How to use custom labels in Akeneo UI, translate components, or replace default strings?

If you’re a Frontend Developer, you are aware that multilingual user interface must be a fundamental part of development as it enables clients to take their products global. The multilingual software has to support localization (l10n) and internationalization (i18n). This means also paying attention to translation as a process of text transformation into another language to help people understand it in their native language. Developers refer to translations as t9n. My blog post is a step-by-step tutorial to show you how to add translations in RequireJS modules and React components. Let's learn more about how this feature works in Akeneo PIM (Product Information Management).


My blog post series provides some context and explanation of the principles used to build UI in the PIM. Check the other articles:


How to add translation in RequireJS modules

To start working with translations you have to add a translator as a dependency to the chosen module as follows:

define(["oro/translator"], function (_) {
   // code goes here...
});

As a next step, let’s create a directory with translations in .yml files.

According to the Symfony documentation, the translation should be placed in the Resources directory of our bundle. Wondering how to create new Symfony bundles? For more information, see my article Getting started with creating frontend modules in Akeneo PIM.

Now it’s time to create .yml files with translations. The .yml files responsible for translations must follow all the same naming: starting with the phrase jsmessages / . (dot) / code of the language / . (dot) / yml extension

Example: jsmessages.de_DE.yml

If you’re not sure about language codes, you can just open any translations directory in Akeneo source code and check the rest of the codes.

As you can see in the example below, there are Polish, English, French, and German translation files:

At this point, you can start adding translations in the .yml files. For this case, only one translation for saving product will be added:

macopedia:
 product:
   save:
     success: "Product has been saved successfully",
     failure: "Product hasn't been saved successfully"

In the code above, you can see that macopedia is a root key (it’s easy to recognize that it is not Akeneo native translation) and inside the root key there is a product key (to recognize that translation is related to product). Inside it you can observe success and failure key-value pairs.

In the same way, you need to add the rest of the translations for French, German, and Polish .yml files.

The last step will be adding new translations in the JS module.

We will change the translation in save.js which you can find in the source files of Akeneo right here.

What is the best way to override or extend existing files? 📚 I also explained it in the previous articles. I hope you will find them helpful:

How to override a RequireJS module?

How to extend existing modules based on RequireJS?

Finally, in the editing file we can change translations:

'use strict';

define([
   'jquery',
   'underscore',
   'oro/translator',
   'pim/form/common/save',
   'oro/messenger',
   'pim/saver/product',
   'pim/field-manager',
   'pim/i18n',
   'pim/user-context',
   'pim/analytics',
], function ($, _, __, BaseSave, messenger, ProductSaver, FieldManager, i18n, UserContext, analytics) {
   return BaseSave.extend({
       updateSuccessMessage: __('macopedia.product.save.success'),
       updateFailureMessage: __('macopedia.product.save.failure'),

       configure: function () {
           this.listenTo(this.getRoot(), ':form:change-family:after', this.save);
           this.listenTo(this.getRoot(), 'pim_enrich:form:update-association', this.save);

           return BaseSave.prototype.configure.apply(this, arguments);
       },

       /**
        * {@inheritdoc}
        */
       save: function (options) {
           var product = $.extend(true, {}, this.getFormData());
           var productId = product.meta.id;

           delete product.meta;

           var notReadyFields = FieldManager.getNotReadyFields();

           if (0 < notReadyFields.length) {
               var fieldLabels = _.map(notReadyFields, function (field) {
                   return i18n.getLabel(field.attribute.label, UserContext.get('catalogLocale'), field.attribute.code);
               });

               messenger.notify(
                   'error',
                   __('pim_enrich.entity.product.flash.update.fields_not_ready', {
                       fields: fieldLabels.join(', '),
                   })
               );

               return;
           }

           this.showLoadingMask();
           this.getRoot().trigger('pim_enrich:form:entity:pre_save');

           analytics.track('product:form:saved', {
               name: product.identifier,
           });

           return ProductSaver.save(productId, product)
               .fail(this.fail.bind(this))
               .then(
                   function (data) {
                       this.postSave();

                       this.setData(data, options);

                       this.getRoot().trigger('pim_enrich:form:entity:post_fetch', data);
                   }.bind(this)
               )
               .always(this.hideLoadingMask.bind(this));
       },
   });
});

Take notice of lines 16-17:

 updateSuccessMessage: __('macopedia.product.save.success'),
       updateFailureMessage: __('macopedia.product.save.failure'),

Function __() comes from the translator which was added to the module in previous steps and takes only one argument – a string responsible for finding a correct translation. Bear in mind that macopedia.product.save.success string comes from the structure of the translation .yml file.

Tip! If any problems occurred while changing translations, it is probably because of an incorrect string argument which doesn't match to the key-value structure in the .yml file.

How to add translation in React components

You can add translations for React components in Akeneo two ways. The first method is very similar to adding translation in JS modules. Let’s start by explaining this method.

You can import a translator using import keyword:

import __ from "akeneoassetmanager/tools/translator";

Then, you are able to use __() function to translate chosen phrases in the editing component:

<div className="AknFullPage-subTitle">
   {__("pim_asset_manager.asset_family.create.subtitle")}
</div>

Translations with useTranslate hook

An alternative way to translate phrases in React components is useTranslate hook which is a custom hook available in Akeneo source files. It’s similar to the custom hook useMediator which we talked about in the previous post.

To use this hook, you need to import it and assign to a variable in the following way:

import {useTranslate} from '@akeneo-pim-community/shared';
const translate = useTranslate();

useTranslate takes only one argument which is a string responsible for finding a correct translation. In this simple example, you can observe using translation for a label in the TextField component:

translate('pim_common.required_label')

The whole source code of this component you will find here.

Hopefully, my article helps you better understand what the process of adding translations looks like and you’re ready to face new challenges related to multilingual interfaces. Soon, I will share other interesting insights about Akeneo PIM for Frontend Developers. In the upcoming post, I will detail how to use the UserContext module in Akeneo projects. 💻

Great ideas require great technology solutions