Translation with Smartyer and KD2\Translate
Smartyer has localization support using KD2\Translate module.
Setting up Translate with Smartyer is easy, you just need to use the 'extendSmartyer' method. Here is a simple usage example:
use KD2\Translate;
use KD2\Smartyer;
// Set directories for Smartyer
Smartyer::setTemplateDir(__DIR__ . '/templates');
Smartyer::setCompileDir(__DIR__ . '/cache/compiled');
$tpl = new Smartyer;
// Link Translate to Smartyer
Translate::extendSmartyer($tpl);
// Get browser language
$lang = Translate::getHttpLang();
// Set translate locale from browser language
Translate::setLocale($lang);
// Set the directory where the .po/.mo files are stored
Translate::registerDomain('*', __DIR__ . '/data/lang');
Using Translate blocks in Smartyer
Translate adds a special type of blocks to Smartyer that you can use for translation. These blocks have double curly brackets instead of simple curly brackets.
Here is one very simple usage example:
{{ Hello world! }}
Will look up for the "Hello world!" string in the gettext messages for a translation.
Note that any spaces, new lines or tabs at the beginning or end of the block is ignored. For example, this is also valid:
{{
Hello world!
}}
Passing arguments
You can pass arguments to your string, similarly to sprintf, but with the ability to use named arguments. All arguments have to be passed after the first closing curly bracket, like this:
{{ String to translate } arguments}
Let's look at a simple sprintf-like example:
{{ My dog is called %s and has %d legs! } dog="Emmanuel Macron" legs=2}
Will appear as, for example in French:
Mon chien s'appelle Emmanuel Macron et il a 2 jambes !
In this example, the order of arguments matters, and if you invert them, the result will be invalid. This is why you might want to prefer using named arguments:
{{ My dog is called %dog and has %legs legs! } dog="Emmanuel Macron" legs=2}
You can also pass variables to those arguments, just like any Smartyer function call:
{{ My dog is called %dog and has %legs legs! } dog=$dog->name legs=$dog::LEGS}
Using plural forms
In the above example, the translation should be able to change according to the number of legs of the dog. For example in Czesh, the plural is different if you have between 2 and 4 legs. And in Russian, Arabic, Slovenian, etc. plural forms are much more complex.
But because English only has two plural forms, we only use two in the translate block. But after translation using the gettext engine, there might be 5 or 6 different plural forms. Gettext is doing all the necessary abstraction for us.
One simple example:
{
{You have one new message.}
{You have %n new messages.}
n=$nb_messages
}
As you can see you just need to supply an argument called 'n' to the block, and a second message between curly brackets. You can even use the 'n' variable inside the string, and it will be replaced by the value specified.
Using translation context
Sometimes the same message might be used in different contexts. In English the message will be the same, but it might change for other languages, and you want to use a different translation. Or you might just want to give an hint to your translators to help them understand in which context this message is used.
For this you will need to use the 'context' parameter.
{{ Please log in. } context="Home page" }
This is equivalent to using the pgettext function:
pgettext('Home page', 'Please login.');
Escaping
For security reasons, by default translate blocks are HTML-escaped. If you want to allow HTML or use a different escaping method, you can use the 'escape' parameter.
If is recommended to manually escape any argument then.
Avoiding parsing conflicts with javascript
Just like with Smartyer, you can disable Smartyer parsing in inline javascript using {literal}...{/literal}
. This is a good practice to keep in mind. Or better, just keep you javascript code separated from your templates!
Using dates
By calling the extendSmartyer
method of Translate on a Smartyer object, a new locale-aware modifier is replacing the default Smartyer date_format
.
If the specified locale is not installed on the system, this will be using the PHP IntlDateFormatter class. On Debian and Ubuntu it requires to have installed the php-intl
package or dates won't be translated.
The usage of date_format
doesn't change, it is still accepting all strftime arguments. The only difference is that day and month names are now localized.
Here is a simple usage example:
{{ This blog has been created on %date. } date=$blog->date|date_format:"%x"}
In en_US this will return:
This blog has been created on Tue Feb 5 00:45:10 2009
And in fr_FR it will return:
Ce blog a été créé le Mar 5 Fév 2009 00:45:10
Extracting messages (PO strings) from templates
To create gettext PO files containing all the messages to be translated, directly from Smartyer templates, you will first have to compile all your templates to PHP code.
One example of script to do that:
Smartyer::setTemplateDir(__DIR__ . '/templates');
Smartyer::setCompileDir(__DIR__ . '/cache/compiled');
Smartyer::precompileAll();
Then use the script provided in KD2fw: src/tools/extract-gettext-strings.sh
like this:
sh extract-gettext-strings.sh ~/my_project/locales ~/my_project/cache/compiled