UserFrosting provides a complete framework for translating strings and sentences easily and efficiently. UserFrosting allows you to define the default locale your UI should present to guest users, while allowing resgistered users to choose their prefered locale.
It's important to talk about why this system is important and why you should use it too in your sprinkles, even if you plan on supporting only a single locale in your app.
Let's face it, it would be waayyyy easier to simply hard code every public facing string in your common code. While using the translation system will make it easier to support additional languages in the future, it will also help separate your code from your site content.
Imagine one day you decide to change the term Users to Members. If you hardcoded everything inside your code, you now have to go through all of the files, look for everwhere the word User (and Users !) is written and change it, one by one, everywhere. Keep in mind, this word is also used in the core code of UserFrosting! This means, if we were to hardcode everything too, that you would have to update all UserFrosting provided files too!
However, having all the content in a single location can help in this situation. Since a common messages key is used in your code, you now have to fix only the localized message. The same goes if you made a typo somewhere...
As you'll see later, the translation system is also very powerful when it comes to pluralisation. The translation system already has the necessary logic to handle the difference between "1 item" and "10 items".
Finaly, it can also makes it easier for non-coders to review your content. Whether to check for spelling errors or to actually update some fixed content displayed to your user, people with little technical knowledge can understand and edit language files.
To better understand how to support multiple locales within your site, we need to start by looking at how UserFrosting handles translation of text.
UserFrosting i18n module is composed of three objects that work together to handle translation duties. Those objects are:
A Locale is used to create a Dictionary, which is then used by the Translator. The overall flow can be visualized as :
The Locale object contains all there is to know about the locale itself: The name of the locale (in both English and the localized variant), the authors who provided the translations, the plural rule the locale needs to follow, etc.
All this information can be found in the locale configuration file. Like everything with UserFrosting, a locale can be overwritten by any Sprinkle.
See Creating a custom locale for more information.
The Dictionary is tied to a specific locale. The dictionary's purpose is to return a data matrix composed of keys shared between all locales, called messages keys, and the associated translated phrases, called localized messages.
The system uses a KEY
and VALUE
system, which is stored in standard PHP arrays:
return [
'MESSAGE_KEY' => 'Localized message',
];
This information is stored in languages files. These are normal PHP files typically located in app/sprinkles/{sprinkleName}/locale/{locale}/messages.php
and grouped into folders named after the locale code, as pictured below. Each locale can have as many files as needed (eg. messages.php
, foo.php
, bar.php
, etc.) for easier maintenance. Those files will be merged together at runtime to create a compiled dictionary of all the keys available for the translator to use.
locale/es_ES/example.php
return[
"ACCOUNT_SPECIFY_USERNAME" => "Introduce tu nombre de usuario.",
"ACCOUNT_SPECIFY_DISPLAY_NAME" => "Introduce tu nombre público.",
];
locale/en_US/example.php
return [
"ACCOUNT_SPECIFY_USERNAME" => "Please enter your user name.",
"ACCOUNT_SPECIFY_DISPLAY_NAME" => "Please enter your display name.",
"ACCOUNT_SPECIFY_AGE" => "Please enter your age.",
];
Some locales have a parent locale, and each locale's language files will be loaded on top of the parent's one. So for example, since the Spanish version above doesn't have any value for the ACCOUNT_SPECIFY_AGE
key, the English value would be returned for that key if en_ES
has en_US
for parent.
Just like routes, the names of the files don't matter as they won't overwrite each other. This means two sprinkles can have a locale/en_US/messages.php
file and both will be loaded and merged togeter. This means that each subsequently loaded Sprinkle can override translations from previous Sprinkles, or define new ones entirely.
For example, if you want your sprinkle to overwrite a value in the core, you can redefine the same key in your sprinkle :
app/sprinkles/core/locale/en_US/example.php
return[
...
"ACCOUNT_SPECIFY_USERNAME" => "Please enter your user name.",
...
];
app/sprinkles/MySite/locale/en_US/MyCustomLanguage.php
return [
"ACCOUNT_SPECIFY_USERNAME" => "Enter your name!",
];
When processed using the English locale, ACCOUNT_SPECIFY_USERNAME
will be linked to the phrase Enter your name!
.
Finally, the Translator use the information from a specific Dictionary to perform the actual association, aka finding the proper localized messages and returning it to the system, while replacing the placeholder with the specified values.
In the next pages, we will see how to actually use the translator in your app.