A pretty nice way to expose your translated messages to your JavaScript.
Install the bundle:
composer require "willdurand/js-translation-bundle:@stable"
Protip: you should browse the
willdurand/js-translation-bundle
page to choose a stable version to use, avoid the @stable
meta constraint.
Register the bundle in app/AppKernel.php
:
<?php
// app/AppKernel.php
public function registerBundles()
{
return array(
// ...
new Bazinga\ExposeTranslationBundle\BazingaExposeTranslationBundle(),
);
}
Register the routing in app/config/routing.yml
:
# app/config/routing.yml
_bazinga_exposetranslation:
resource: "@BazingaExposeTranslationBundle/Resources/config/routing/routing.yml"
Publish assets:
php app/console assets:install --symlink web
Add this line in your layout:
<script type="text/javascript" src="{{ asset('bundles/bazingaexposetranslation/js/translator.min.js') }}"></script>
Now, you have to specify which translation files to load.
By adding a line as below:
<script type="text/javascript" src="{{ url('bazinga_exposetranslation_js') }}"></script>
This will use the current locale
and will provide translated messages found in each messages.CURRENT_LOCALE.*
files of your project.
<script type="text/javascript" src="{{ url('bazinga_exposetranslation_js', { 'domain_name': 'DOMAIN_NAME' }) }}"></script>
This will use the current locale
and will provide translated messages found in each DOMAIN_NAME.CURRENT_LOCALE.*
files of your project.
You can specify a locale
to use for translation if you want, just add the _locale
parameter:
<script type="text/javascript" src="{{ url('bazinga_exposetranslation_js', { 'domain_name': 'DOMAIN_NAME', '_locale' : 'MY_LOCALE' }) }}"></script>
This will provide translated messages found in each DOMAIN_NAME.MY_LOCALE.*
files of your project.
Alternatively, you can load your translated messages via JSON (e.g. using jQuery's ajax()
or RequireJS's text plugin).
Just amend the above mentioned URLs to also contain the '_format': 'json'
parameter like so:
{{ url('bazinga_exposetranslation_js', { '_format': 'json' }) }}
Then, feed the translator via Translator.fromJSON(myRetrievedJSONString)
.
Dump the translation files to the web/js
folder:
php app/console bazinga:expose-translation:dump web/js
You can use the optional --symlink
option. The target
(web/js
in the
example above) argument is also optionally, web
is the default target
.
{% javascripts
'bundles/bazingaexposetranslation/js/translator.js'
'js/i18n/*/*.js' %}
<script type="text/javascript" src="{{ asset_url }}"></script>
{% endjavascripts %}
With 'js/i18n/*/*.js'
, you load all the translation files from all of the
translation domains. Of couse, you can load domains one by one
'js/i18n/admin/*.js'
.
Translator.has('DOMAIN_NAME:key');
// true or false
Translator.get('DOMAIN_NAME:key');
// the translated message or undefined
Note: The JavaScript is AMD ready.
If you don't specify any domain, a guesser is provided to find the best translated message for the given key
.
To configure the guesser, you have to set the defaultDomains
attribute. By default, the configured default domain is messages
.
Translator.get('key');
// will try to find a translated message in default domains.
Note: this will only work if default domains are previously loaded (see the Load translation domains first section).
Read the official documentation about Symfony2 message placeholders.
The get()
method accepts a second argument that takes placeholders without %
delimiters:
Translator.get('DOMAIN_NAME:key', { "foo" : "bar" });
// will replace each "%foo%" in the message by "bar".
You can override the placeholder delimiters by setting the placeHolderSuffix
and placeHolderPrefix
attributes.
Probably the best feature provided in this bundle ! It allows you to use pluralization exactly like you can do in Symfony2.
Read the official doc about pluralization.
A third parameter can be added to the get()
method, it's the number of objects being described. Here is an example:
# app/Resources/messages.en.yml
apples: "{0} There is no apples|{1} There is one apple|]1,19] There are %count% apples|[20,Inf] There are many apples"
Translator.locale = 'en';
Translator.get('apples', {"count" : 0}, 0);
// will return "There is no apples"
Translator.get('apples', {"count" : 1}, 1);
// will return "There is one apple"
Translator.get('apples', {"count" : 2}, 2);
// will return "There are 2 apples"
Translator.get('apples', {"count" : 10}, 10);
// will return "There are 10 apples"
Translator.get('apples', {"count" : 19}, 19);
// will return "There are 19 apples"
Translator.get('apples', {"count" : 20}, 20);
// will return "There are many apples"
Translator.get('apples', {"count" : 100}, 100);
// will return "There are many apples"
Note: This is not tested at the moment. It works fine for english/french translations.
You can get the current locale by accessing the locale
attribute:
Translator.locale;
// will return the current locale.
Consider the following translation files:
# app/Resources/translations/Hello.fr.yml
foo: "Bar"
ba:
bar: "Hello world"
placeholder: "Hello %username% !"
# app/Resources/translations/messages.fr.yml
placeholder: "Hello %username%, how are you ?"
You can do:
Translator.get('Hello:foo');
// will return 'Bar' if the current locale is set to 'fr', undefined otherwise.
Translator.get('Hello:ba.bar');
// will return 'Hello world' if the current locale is set to 'fr', undefined otherwise.
Translator.get('Hello:placeholder');
// will return 'Hello %username% !' if the current locale is set to 'fr', undefined otherwise.
Translator.get('Hello:placeholder', { "username" : "will" });
// will return 'Hello will !' if the current locale is set to 'fr', undefined otherwise.
Translator.get('placeholder', { "username" : "will" });
// will return 'Hello will, how are you ?' if the current locale is set to 'fr', undefined otherwise.
Translator.get('placeholder');
// will return 'Hello %username%, how are you ?' if the current locale is set to 'fr', undefined otherwise.
You can easily add your own default domains by adding these lines in your app/config/config*.yml
files:
bazinga_expose_translation:
default_domains: [ messages ]
Note: You still have to include a <script>
tag to expose messages but you avoid writing domain names before each of your keys.
In a similar way, if some of your translations are not complete you can enable a fallback for untranslated messages:
bazinga_expose_translation:
locale_fallback: "en" # put here locale code of some complete translation, I recommend the value used for translator fallback
# app/config/config*.yml
bazinga_expose_translation:
locale_fallback: ''
default_domains: []
Setup the test suite using Composer:
$ composer install --dev
Run it using PHPUnit:
$ phpunit
You can run the JavaScript test suite using PhantomJS:
$ phantomjs Resources/js/run-qunit.js file://`pwd`/Resources/js/index.html