Passing Symfony Translation to Symfony Webpack Encore - javascript

With Symfony, I use translation, Twig and Webpack encore components.
I can translate in frontend Twig with :
'my_key'|trans
I use command yarn encore dev for generate my app.js, but PHP translation component it's not accessible in Javascript.
I have a lot of things to translate in javascript.

Unfortunately since JS is not handled by PHP and by extension also not by Symfony, you will not have access to Symfony's Translation component inside your js files.
A workaround that could work when you don't have too many translations you need to pass is create a JS data object inside your twig template as part of your Symfony application and then access it from your js files. So roughly like this:
# inside your twig template, e.g. index.html.twig
{% block javascripts %}
<script type="text/javascript">
const TRANSLATION_MAP = {
'my_key': "{{ 'some_key '|trans }}",
'my_other_key': "{{ 'other_key '|trans }}"
};
</script>
{{ parent() }} # This loads all your js files which can then access the translation map defined above
{% endblock %}
The downside to this solution is, that you have to decide which keys to put in your translation map without really knowing whether they are used, so this might become a bit inefficient and hard to follow. Also you have to be careful that your translated content is valid json. You can apply (custom) escaping/filtering to ensure that, but still makes it a bit fragile.
All in all, this might not be the best solution but can be a decent workaround for smaller projects until you find it becomes more of a nuisance and you have to find something more sophisticated.

You have to use BazingaJsTranslationBundle which allows you to access translations you have exposed through javascript:
Translator.trans('key', {}, 'DOMAIN_NAME');
Translator.transChoice('key', 1, {}, 'DOMAIN_NAME');

You could transform translations yaml to json with webpack or a task manager (gulp | grunt).
Put built json translation files to assets.
Require them inside js script.
Emit locale value to frontend to choose proper translation json object inside js script.
Note: in webpack case you should run 2 steps consequently:
first step will build translations to assets to be required in js scripts.
second step will compile js scripts afterwards.
Here is a part of webpack encore config which transforms translations *.yaml files to json with 'js-yaml' and puts them into assets directory:
.addPlugin(new CopyWebpackPlugin(
{
patterns: [
{
from: './translations/*.yaml',
to: './[name].json',
transform(content) {
return Buffer.from(
JSON.stringify(
yaml.safeLoad(content.toString('utf8'), {schema: yaml.JSON_SCHEMA})
),
'utf8'
)
}
}
],
}));

Related

Laravel mix with PHP helpers

In my homepage I have defined a section to forward variables to JS side in a way that I call PHP helper methods, for example:
<script>
var base_url = "{{ url('/') }}";
</script>
This works within blade template, however if I put that to a script and try to minify through Laravel mix, it just gets compressed as a string literal. How could I force resolving this before somehow? Or keep it working within a minified file?
According to Laravel Docs we may inject environment variables into Mix by prefixing a key in your .env file with MIX_ After the variable has been defined in your .env file, you may access via the process.env object. if you using Vue you can use This Package A Simple plugin for loading an environment file.

How to concat and bundle JS files using webpack to be loaded in a script tag

I'm struggling to find a "quick" way to transition some legacy javascript files from assetic to webpack. I (think I) need to find a way to bundle a set of javascript files by simply concatenating them and not having webpack wrap it in a jsonp.
Our platform is built in Symfony, using twig templates and we've got most of our legacy javascript files loaded using assetic (it simply concatenates, and minifies), we've got newer javascript/typescript files bundled with webpack.
Naturally I want to remove all assetic dependencies and use webpack to bundle the old javascript. However, due to the way that webpack produces the bundles, it gets wrapped in the jsonp and any functions/variables defined in those javascript files are not available in global scope.
In an assetic world, our twig files are littered with these:
{%- javascripts
'%kernel.root_dir%/../vendor/twbs/bootstrap/js/bootstrap-transition.js'
'#MyBundle/Resources/js/legacy-1.js'
'#MyBundle/Resources/js/legacy-2.js'
filter="?uglifyjs2"
output="js/compiled/page-one.js" %}
<script type="text/javascript" src="{{ asset_url }}"></script>
{% endjavascripts %}
Which concatenates and minifies them all. I'd like to replace this with a similar webpack version.
I've identified some approaches, but I'm not sure any of them are the best.
Use script-loader to get the scripts to load in global scope - Since script-loader uses eval which is disabled using CSP, this is out of the question
Rewrite each javascript file, exposing the members on the window. - This would probably be the easiest option but I really don't like the idea, and it goes against best practices.
Rewrite each javascript file, exporting the members, then use the expose-loader to load the files, then go through all the twig files and update the references to the members with the global namespace. This would take a lot of time, and I'd prefer not to do this if at all possible
I figured that I could simply configure a queryParameter and the file-loader to load each one, but I'd need a script tag per resource, and would lose out on the bundling nature. (I use a Symfony webpack bundle to generate an entries list for webpack, and provide twig functions for loading entries at runtime from the cdn)
<script src='{{ webpack_asset('%kernel.root_dir%/../vendor/twbs/bootstrap/js/bootstrap-transition.js?asFile') }}"></script>
<script src='{{ webpack_asset('#MyBundle/Resources/js/legacy-1.js?asFile') }}"></script>
<script src='{{ webpack_asset('#MyBundle/Resources/js/legacy-2.js?asFile') }}"></script>
So I'm looking for a way to concatenate the files together. Creating a separate entry file, and loading that as a file wouldn't be good enough as the requires/imports wouldn't be resolved.
I understand the webpack-concat-plugin would require me to define all of the various files to concat up front in the webpack config, which isn't really possible.
I haven't come across a loader that would replace the import '#MyBundle/Resources/js/legacy-2.js' with the content of that js file - but maybe I'm missing something.
I wonder if after all of this effort trying to find an easy solution is greater than the effort to rewrite the legacy javascript to use modules...
I found the following answers but they don't really help me:
Webpack - How to load non module scripts into global scope | window - This assumes I can define the files to concatenate up front. I have almost 100 different includes using assetic, almost all of them use different files.
Webpack - How to load non module scripts into global scope | window - eval is disabled by our CSP
Expose javascript globals bundled via webpack - As I mentioned, I don't really want to create 100 'entry' files just for this, and the webpack-raw-bundler also requires everything to be defined up front in the webpack config file.

Get js variable transpiled by Symfony4 Webpack Encore in twig / FosJsRouting bundle

I use fos_js_routing bundle on symfony4 . I need to get the Routing object reachable in my twig view. I defined Routing in assets/js/app.js, a transpiled js file with Webpack Encore.
Because my Routing object is correctly built in this file,
I want to access it in a Twig view.
// assets/js/app.js
const routes = require('../../web/js/fos_js_routes.json');
import Routing from '../../vendor/friendsofsymfony/jsrouting-bundle/Resources/public/js/router.min.js';
Routing.setRoutingData(routes);
I got this line in my webpack config:
Encore
.setOutputPath('public/build/')
.setPublicPath('/build')
.cleanupOutputBeforeBuild()
.enableSourceMaps(!Encore.isProduction())
.addEntry('js/app', './assets/js/app.js')
I get my twig view, where the transpiled app.js is reachable and succesfully transpiled by webpack Encore. But the variable Routing is not reachable ( I got client error : Routing is not defined),
// in my twig view, inside a script block
var redirectionUrl = Routing.generate('my_route', {arg: arg}); // Routing is not defined
probably because is defined as let in the transpilation process, and I want it as a var, to be reachable in each twig view where I include transpiled app.js ( public/build/js/app.js ). My Twig view is including the transpiled file in this line, and succesfully imported in my twig sources
<script src="{{ asset('build/js/app.js') }}"></script>
How can I get Routing in my twig view using Webpack encore ?
AUTO ANSWER :
As far as i understood ( please comment if I'm wrong ), because Webpack is transpiling ES6 syntax to ES5 syntax and because in this process all useless and unrevelant data is erased in your production js transpiled files
( when you call the command: node_modules/.bin/encore production ). So as far as i know, you cannot pass unused data between ES6 js file transpiled to an ES5 js file with Encore. Maybe entering in advanced configuration of Webpack but i didnt spend the time for that ( and this is not usually what you want to deal with transpilers ). My using of fosjsrouting bundle in my project wasn't essential, so I just removed it. But today i will explain how to properly load fosjsrouting Routing object from your twig templates ( instead of trying to access it from your Encore transpiled js files ).
1- Read documentation of FosJsRouting Bundle here
in order to :
--- A/ Install the bundle
--- B/ Build your routes
I don't remember the commands, but everything is well explained in the documentation link provided.
At the end of your bundle installation, you must get :
1 - router.min.js file in your public/ folder => ./public/bundles/fosjsrouting/js/router.min.js
2 - fos_js_routes.json in your public/ folder =>
./public/js/fos_js_routes.json
Now I show you how to generate a Route on a basic twig template view.
{% extends "base.html.twig" %}
{% block javascripts %}
<script src="{{ asset('bundles/fosjsrouting/js/router.min.js')}}">
</script>
<script>
var r = Routing; // loaded from router.min.js
// because ES5 doesnt support require syntax,
// we use jquery getJSON function in order to set
// routing data to our Routing object
$.getJSON("{{ asset('js/fos_js_routes.json')}}", function(routes) {
console.log(routes);
r.setRoutingData(routes);
console.log(r.generate('my_route'));
});
</script>
{% endblock %}

JS Marko (raptor templates) load dynamic template path in browser

I am trying to use Marko templates in a web application, and would prefer to be able to load pre-compiled templates dynamically. My (weak) understanding is that the suggested raptor-optimizer does static analysis to load all of the templates (as does browserify), and so wouldn't be able to bundle templates only referenced dynamically.
Is it possible to do this without having to hard code every possible template path that I might be interested in? Is it possible to not have to surrender the concat and minify steps to raptor-optimizer/browserify?
first_tmpl = require('marko').load(require.resolve('./tmpl/first.marko'))
second_tmpl = require('marko').load(require.resolve('./tmpl/second.marko'))
https://github.com/raptorjs3/marko#browser-side-rendering
Out of the box, Browserify only supports static code analysis for discovering and bundling dependencies. The RaptorJS Optimizer supports both static code analysis and declarative dependencies inside optimizer.json files. The RaptorJS Optimizer also supports glob patterns so that you can do the following inside an optimizer.json file:
{
"dependencies": [
"**/*.marko"
]
}
In most cases it is better to rely on discovering required templates via static code analysis.
I hope that helps.
--Patrick

Is it possible to include external files when using Jade’s :markdown filter?

I'm building an Express.js Node app and using Jade templates. Jade provides a :markdown filter which enables embedding Markdown code inside Jade:
h1 This is Jade
:markdown
## And this is Markdown
h3 Back in Jade
(Note: In order to use this filter you have to npm install a Markdown engine, e.g. npm install marked --save. You don't have to require() this module within your Express app, but it has to be installed.)
So, embedding Markdown within Jade works fine. However, I would like to keep my Markdown in separate files and include them in Jade templates dynamically. I've tried this and it does not work:
:markdown
include ../path/to/markdown/file.md
The include command is treated as source code instead of being interpreted as a command. Is it possible to inject Markdown from external files within the :markdown filter?
Please don't provide workarounds! I know how to work around this issue. I want to know if the :markdown filter is compatible with external Markdown files.
You can include markdown files using the :md filter modifier.
eg.
html
body
include:md ../path/to/markdown/file.md
Language Reference: https://pugjs.org/language/includes.html#including-filtered-text
the :md modifier does not work for me either, but this works:
html
body
// works:
include file.md
//- does not work:
include:markdown file.md
include:md file.md
I am using docpad with the HTML5 Boilerplate template.
You also should consider the problem of no auto-generation of including *.html.jade files of such includes:
How to auto-generate html from jade file when only included markdown file has changed in livereload development environment?
First, run this command:
npm install marked --save
Then, do this:
include:md ../path/to/markdown/file.md

Categories

Resources