Translation Component


    The component Phalcon\Translate offers multilingual capabilities to applications. This component allows you to display content in different languages, based on the user’s choice of language, available by the application.

    Usage

    Introducing translations in your application is a relatively simple task. However no two implementations are the same and of course the implementation will depend on the needs of your application. Some of the options available can be an automatic detection of the visitor’s language using the server headers (parsing the contents or using the getBestLanguage() method of the [Phalcon\Http\Request][Phalcon_Http#request] object).

    The getTranslator() method is available in the controller for all actions that require it. You could of course introduce a caching mechanism to store the translation adapter in your cache (based on the language selected i.e. en.cache, de-cache etc.)

    The t variable is passed then in the view and with it we can perform translations in the view layer.

    1. <!-- welcome -->
    2. <!-- String: hi => 'Hello' -->
    3. <p><?php echo $t->_('hi'), ' ', $name; ?></p>

    and for Volt:

    1. <p>{{ t._('hi') }} {{ name }}</p>

    The _() method will return the translated string of the key passed. In the above example, it will return the value stored for the key hi. The component can also parse placeholders using [interpolation][#interpolation]. Therefore for a translation of:

    1. Hello %name%!

    you will need to pass the $name variable in the _() call and the component will perform the replacement for you.

    1. <!-- welcome -->
    2. <!-- String: hi-name => 'Hello %name%' -->
    3. <p><?php echo $t->_('hi-name', ['name' => $name]); ?></p>

    and for Volt:

    1. <p>{{ t._('hi-name', ['name' => name]) }}</p>

    Plugin

    The implementation above can be extended to offer translation capabilities throughout the application. We can of course move the getTranslator() method in a base controller and change its visibility to protected. However, we might want to use translations in other components that are outside the scope of a controller.

    To achieve this, we can implement a new component as a Plugin and register it in our container.

    1. <?php
    2. namespace MyApp;
    3. use Phalcon\Di\Injectable;
    4. use Phalcon\Translate\Adapter\NativeArray;
    5. use Phalcon\Translate\InterpolatorFactory;
    6. use Phalcon\Translate\TranslateFactory;
    7. class Locale extends Injectable
    8. {
    9. /**
    10. * @return NativeArray
    11. */
    12. public function getTranslator(): NativeArray
    13. {
    14. // Ask browser what is the best language
    15. $language = $this->request->getBestLanguage();
    16. $messages = [];
    17. $translationFile = 'app/messages/' . $language . '.php';
    18. if (true !== file_exists($translationFile)) {
    19. $translationFile = 'app/messages/en.php';
    20. }
    21. require $translationFile;
    22. $interpolator = new InterpolatorFactory();
    23. $factory = new TranslateFactory($interpolator);
    24. return $factory->newInstance(
    25. 'array',
    26. [
    27. 'content' => $messages,
    28. ]
    29. );
    30. }
    31. }

    Then we can register it in the Di container, when setting up services during bootstrap:

    1. <?php
    2. use MyApp\Locale;
    3. $container->set('locale', new Locale());

    And now you can access the Locale plugin from your controllers and anywhere you need to.

    1. <?php
    2. use Phalcon\Mvc\Controller;
    3. /**
    4. * @property MyApp\Locale $locale
    5. */
    6. class MyController extends Controller
    7. {
    8. public function indexAction()
    9. {
    10. $name = 'Mike';
    11. $text = $this->locale->_(
    12. 'hi-name',
    13. [
    14. 'name' => $name,
    15. ]
    16. );
    17. $this->view->text = $text;
    18. }
    19. }
    1. <?php echo $locale->_('hi-name', ['name' => 'Mike']);

    and for Volt:

    Routing

    Some applications use the URL of the request to distinguish content based on different languages, in order to help with SEO. A sample URL is:

    1. https://mozilla.org/es-ES/firefox/

    Phalcon can implement this functionality by using a .

    Loads Translate Adapter class using adapter option, the remaining options will be passed to the adapter constructor.

    1. <?php
    2. use Phalcon\Translate\InterpolatorFactory;
    3. use Phalcon\Translate\TranslateFactory;
    4. $interpolator = new InterpolatorFactory();
    5. $factory = new TranslateFactory($interpolator);
    6. $options = [
    7. 'content' => [
    8. 'hi' => 'Hello',
    9. 'bye' => 'Good Bye',
    10. ],
    11. ];
    12. $translator = $factory->newInstance('array', $options);

    Adapters

    This component makes use of adapters to read translation messages from different sources in a unified way.

    This adapter stores the translated strings in a PHP array. This adapter is clearly the fastest of all since strings are stored in memory. Additionally the fact that it uses PHP arrays makes maintenance easier. The strings can also be stored in JSON files which in turn can be translated back to the native PHP array format when retrieved.

    1. <?php
    2. use Phalcon\Translate\InterpolatorFactory;
    3. use Phalcon\Translate\TranslateFactory;
    4. $interpolator = new InterpolatorFactory();
    5. $factory = new TranslateFactory($interpolator);
    6. 'content' => [
    7. 'hi' => 'Hello',
    8. 'bye' => 'Good Bye',
    9. ],
    10. ];
    11. $translator = $factory->newInstance('array', $options);

    The recommended usage would be to create one file per language and store it in the file system. After that you can load the relevant file, based on the language selected. A sample structure can be:

    1. app/messages/en.php
    2. app/messages/es.php
    3. app/messages/fr.php
    4. app/messages/zh.php

    or in JSON format

    1. app/messages/en.json
    2. app/messages/es.json
    3. app/messages/fr.json
    4. app/messages/zh.json

    Each file contains PHP arrays, where key is the key of the translated string and value the translated message. Each file contains the same keys but the values are of course the message translated in the respective language.

    1. <?php
    2. // app/messages/en.php
    3. $messages = [
    4. 'hi' => 'Hello',
    5. 'bye' => 'Good Bye',
    6. 'hi-name' => 'Hello %name%',
    7. 'song' => 'This song is %song%',
    8. ];
    1. <?php
    2. // app/messages/fr.php
    3. $messages = [
    4. 'hi' => 'Bonjour',
    5. 'bye' => 'Au revoir',
    6. 'hi-name' => 'Bonjour %name%',
    7. 'song' => 'La chanson est %song%',
    8. ];

    Creating this adapter can be achieved by using the , but you can instantiate it directly:

    1. <?php
    2. use Phalcon\Translate\InterpolatorFactory;
    3. use Phalcon\Translate\Adapter\NativeArray;
    4. $interpolator = new InterpolatorFactory();
    5. $options = [
    6. 'content' => [
    7. 'hi' => 'Hello',
    8. 'bye' => 'Good Bye',
    9. ],
    10. ];
    11. $translator = new NativeArray($interpolator, $options);

    Csv

    If your translation strings are stored in a .csv file. The adapter accepts the interpolator factory and an array with options necessary for loading the translations. The options array accepts:

    • content: The location of the CSV file on the file system
    • delimiter: The delimiter the CSV file uses (optional - defaults to ;)
    • enclosure: The character that surrounds the text (optional - defaults to ")
    1. <?php
    2. use Phalcon\Translate\InterpolatorFactory;
    3. use Phalcon\Translate\TranslateFactory;
    4. $interpolator = new InterpolatorFactory();
    5. $factory = new TranslateFactory($interpolator);
    6. // `sample-key`|`sample-translated-text`
    7. $options = [
    8. 'content' => '/path/to/translation-file.csv',
    9. 'delimiter' => '|',
    10. 'enclosure' => '`',
    11. ];
    12. $translator = $factory->newInstance('csv', $options);

    In the above example you can see the usage of delimiter and enclosure. In most cases you will not need to supply these options but in case your CSV files are somewhat different, you have the option to instruct the adapter as to how it will parse the contents of the translation file.

    Gettext

    The format has been around for years and many applications are using it because it has become a standard and it is easy to use. The translations are stored in .po and .mo files, and content can be easily added or changed using online editors or tools such as POEdit. This adapter requires files to be in specific folders so it can locate the translation files. The options array accepts:

    • locale: The language locale you need
    • defaultDomain: The domain for the files. This is the actual name of the files. Both po and mo files must have the same name.
    • directory: The directory where the translation files are located
    • category: A LC* PHP variable defining what category should be used. This maps to a folder (as seen below in the sample directory structure).
    1. <?php
    2. use Phalcon\Translate\InterpolatorFactory;
    3. use Phalcon\Translate\TranslateFactory;
    4. $interpolator = new InterpolatorFactory();
    5. $factory = new TranslateFactory($interpolator);
    6. $options = [
    7. 'locale' => 'de_DE.UTF-8',
    8. 'defaultDomain' => 'translations',
    9. 'directory' => '/path/to/application/locales',
    10. 'category' => LC_MESSAGES,
    11. ];
    12. $translator = $factory->newInstance('gettext', $options);

    A sample directory structure for the translation files is:

    1. translations/
    2. en_US.UTF-8/
    3. LC_MESSAGES/
    4. translations.mo
    5. translations.po
    6. de_DE.UTF-8
    7. LC_MESSAGES/
    8. translations.mo
    9. translations.po

    Creating this adapter can be achieved by using the , but you can instantiate it directly:

    1. <?php
    2. use Phalcon\Translate\InterpolatorFactory;
    3. use Phalcon\Translate\Adapter\Gettext;
    4. $interpolator = new InterpolatorFactory();
    5. $options = [
    6. 'locale' => 'de_DE.UTF-8',
    7. 'defaultDomain' => 'translations',
    8. 'directory' => '/path/to/application/locales',
    9. 'category' => LC_MESSAGES,
    10. ];
    11. $translator = new Gettext($interpolator, $options);

    The Phalcon\Translate\Adapter\AdapterInterface interface must be implemented in order to create your own translate adapters or extend the existing ones:

    1. <?php
    2. use Phalcon\Translate\Adapter\AdapterInterface;
    3. class MyTranslateAdapter implements AdapterInterface
    4. {
    5. /**
    6. * @param array $options
    7. */
    8. public function __construct(array $options);
    9. /**
    10. * @param string $translateKey
    11. * @param array|null $placeholders
    12. * @return string
    13. */
    14. public function t($translateKey, $placeholders = null);
    15. /**
    16. * @param string $translateKey
    17. * @param array $placeholders
    18. * @return string
    19. */
    20. public function _(string $translateKey, $placeholders = null): string;
    21. /**
    22. * @param string $index
    23. * @param array $placeholders
    24. * @return string
    25. */
    26. public function query(string $index, $placeholders = null): string;
    27. /**
    28. * @param string $index
    29. * @return bool
    30. */
    31. public function exists(string $index): bool;
    32. }

    There are more adapters available for this components in the

    Interpolation

    In many cases, the translated strings need to be with data. With interpolation you can inject a variable from your code to the translated message at a specific place. The placeholder in the message is enclosed with % characters.

    1. Hello %name, good %time%!
    2. Salut %name%, bien %time%!

    Assuming that the context will not change based on each language’s strings, you can add these placeholders to your translated strings. The Translate component with its adapters will then correctly perform the interpolation for you.

    To change the interpolator that your adapter uses, all you have to do is pass the name of the interpolator in the options using the defaultInterpolator key.

    1. <?php
    2. use Phalcon\Translate\InterpolatorFactory;
    3. use Phalcon\Translate\TranslateFactory;
    4. $interpolator = new InterpolatorFactory();
    5. $factory = new TranslateFactory($interpolator);
    6. $options = [
    7. 'defaultInterpolator' => 'indexedArray',
    8. 'content' => [
    9. 'hi-name' => 'Hello %1$s, it\'s %2$d o\'clock',
    10. ],
    11. ];
    12. $translator = $factory->newInstance('array', $options);

    AssociatedArray

    Phalcon\Translate\Interpolator\AssociativeArray is the default interpolator. It allows you to do a key/value replacement of the placeholders.

    1. <?php
    2. use Phalcon\Translate\InterpolatorFactory;
    3. use Phalcon\Translate\TranslateFactory;
    4. $interpolator = new InterpolatorFactory();
    5. $factory = new TranslateFactory($interpolator);
    6. $options = [
    7. 'content' => [
    8. 'hi-name' => 'Hello %name%, good %time% !',
    9. ],
    10. ];
    11. $translator = $factory->newInstance('array', $options);
    12. $name = 'Henry';
    13. $translator->_(
    14. 'hi-name',
    15. [
    16. 'name' => $name,
    17. 'time' => 'day',
    18. ]
    19. ); // Hello Henry, good day !
    20. $translator->_(
    21. 'hi-name',
    22. [
    23. 'name' => $name,
    24. 'time' => 'night',
    25. ]
    26. ); // Hello Henry, good night !

    AssociatedArray

    Phalcon\Translate\Interpolator\IndexedArray is another option that you can use as the interpolator. This interpolator follows the convention.

    1. <?php
    2. use Phalcon\Translate\InterpolatorFactory;
    3. use Phalcon\Translate\TranslateFactory;
    4. $interpolator = new InterpolatorFactory();
    5. $factory = new TranslateFactory($interpolator);
    6. $options = [
    7. 'defaultInterpolator' => 'indexedArray',
    8. 'content' => [
    9. 'hi-name' => 'Hello %1$s, it\'s %2$d o\'clock',
    10. ],
    11. ];
    12. $translator = $factory->newInstance('array', $options);
    13. $name = 'Henry';
    14. $translator->_(
    15. 'hi-name',
    16. [
    17. $name,
    18. 8,
    19. ]
    20. ); // Hello Henry, it's 8 o'clock

    The Phalcon\Translate\Interpolator\InterpolatorInterface interface must be implemented in order to create your own interpolators or extend the existing ones:

    Interpolator Factory

    1. <?php
    2. use Phalcon\Translate\InterpolatorFactory;
    3. use Phalcon\Translate\TranslateFactory;
    4. $interpolator = new InterpolatorFactory();
    5. $factory = new TranslateFactory($interpolator);
    6. $translator = $factory->newInstance(
    7. 'array',
    8. [
    9. 'content' => [
    10. 'hi' => 'Hello',
    11. 'bye' => 'Good Bye',
    12. ],
    13. );