Localize Polymer apps with translation platform

<app-localize-behavior> and Chrome i18n format allow for a streamlined and de-centralized translation workflow.

Ronny Roeller
NEXT Engineering
4 min readAug 4, 2017

--

Rosetta Stone replica

Translating applications is essential to reach a broad audience. Translation platforms like Transifex smoothen the workflow between developers and translators (disclosure: I’ve no affiliation with Transifex beyond being a happy customer). Polymer itself provides bits and pieces for an effective integrated solution.

The first part of this article describes how we glued those pieces together. The second part explains why we choose to replace our XLIFF based solution with Chrome i18n files.

Add translation infrastructure to a project

First, create a simple Chrome i18n file as the skeleton file for the source translations (further examples assume the skeleton to be at locales/en.json).

{
"hello": {
"message": "Hello world!",
"description": "Welcome message of the application"
}
}

Hint: It’s strongly advised to describe the context of each message in the “description” field. This helps the translator to optimally translate the message to the target language.

Next, create the Transifex configuration in .tx/config:

[main]
host = https://www.transifex.com
[your-transifex-project.transifex-resource]
file_filter = locales/<lang>.json
source_file = locales/en.json
source_lang = en
type = CHROME

The type CHROME tells Transifex that the resource is a Chrome i18n file.

Finally, you can push the new translation file to Transifex on the command line from your project directory (learn here how to install the Transifex client):

$ tx push -st

Once your translators localized the file to another language, you can easily pull them from the command line:

$ tx pull -a

Use translations in Polymer

OK, now we have our translation files set up. Let’s use them!

First, add to your project the Polymer mixin that provides localization support via Chrome i18n files:

bower install app-localize-chrome-i18n-mixin --save

Now, you can use the mixin in your Polymer elements:

<link rel="import" href="bower_components/app-localize-chrome-i18n-mixin/app-localize-chrome-i18n-mixin.html">...<template>
<div>[[localize('hello')]]</div>
</template>
<script>
class MyElement extends
Polymer.AppLocalizeChromeI18nMixin(Polymer.Element) {
... static get properties() {
return {
language: {
value: 'en'
}

};
}
}

See the app-localize-chrome-i18n-mixin demo for the complete “Hello World” example.

Scale to large applications

Larger applications typically structure elements in various sub folders. Hence, the relative path resolution of AppLocalizeChromeI18nMixin won’t work if there is a central localization file. For those scenarios, AppLocalizeChromeI18nMixin allows defining the path of the localization files:

class MyElement extends
Polymer.AppLocalizeChromeI18nMixin(Polymer.Element, '/') {

In this example, the mixin will fetch the localization file always from the root of the application.

Another challenge is keeping the current language of all element consistent. Today, <app-localize-behavior> doesn’t support changing the language globally. Instead, each element must be informed separately. Most solutions like Polymer data binding or polymer-redux quickly become a mess.

Instead, you can use the following mixin to keep the language properties in sync (credit for inspiration):

<link rel="import" href="../../bower_components/app-localize-chrome-i18n-mixin/app-localize-chrome-i18n-mixin.html"><script>  var MyApp = MyApp || {};
MyApp.LocalizeMixin = parent => class LocalizeMixin extends
Polymer.AppLocalizeChromeI18nMixin(parent, '/locales/') {
ready() {
super.ready();
MyApp.LocalizeMixin.localizedElements.push(this);
this.language = MyApp.LocalizeMixin.language;

}
changeLanguage(language) {
MyApp.LocalizeMixin.language = language;
MyApp.LocalizeMixin.localizedElements.forEach(element => {
element.language = language;
});

}
}// Global list of all localized elements
MyApp.LocalizeMixin.localizedElements = [];
// Current language
MyApp.LocalizeMixin.language = 'en';
</script>

With this mixin, each element will receive the current language at its initialization. Whenever the language changes, the property of the element will be automatically adjusted.

Why Chrome i18n files?

<app-localize-behavior> didn’t exist when we started localizing our application. We therefore created the <carbon-i18n-behavior> and fed it with XLIFF files, the most widely used standard to exchange translations.

We experienced two issues with the XLIFF part of the solution:

  • XLIFF contains tons of features for the workflow between translations. Yet, as developers we don’t care about what translators do internally. In other words: lots of complexity without any tangible benefit.
  • XLIFF is XML-based. Not really a match in heaven with JavaScript... Reading the XLIFF files in runtime was a clear no-go. This forced us to add a conversion step to our build pipeline. The additional build step not only made running Polymer code via polymer serve impossible but also added lots of complexity to the build process itself.

Looking for an alternative, we wanted a JSON-based de-facto standard format (this ruled out the <app-localize-behavior> format). Ideally, we were looking for a format that our translation tool could output directly, i.e. we wouldn’t need a conversation step in our build process.

We first considered Transifex key/value format but ultimately decided against it because our translators required hints about the context of the message. So, we finally ended up with the Chrome i18n format, which ticked all the boxes.

Happy coding!

Want to learn more about Polymer? Have a look to all our Medium posts on Polymer.

--

--

Ronny Roeller
NEXT Engineering

CTO at nextapp.co # Product discovery platform for high performing teams that bring their customers into every decision