Sunday, December 22, 2013

A Slightly Better i18n Polymer

Last night, I got application-wide localization working in my Polymer application. It is not real internationalization and localization—it only allows for a single application-wide localization, but it is a start. Tonight, I hope to use some of Dart's intl package to come up with something better. Specifically, I would like to be able to dynamically change the locale at runtime.

There is an example of Polymer internationalization in the Dart repository. I am not quite following along with that, but it does prove invaluable.

Yesterday's solution relied on attributes and <content> tags to include information from other locales:
      <hello-you hello="Bonjour" done="Fin">
        <p>Présentez-vous une expérience personnalisée incroyable!</p>
Those attributes and <content> are then included in the template as:
<polymer-element name="hello-you">
      <h2>{{hello}} {{your_name}}</h2>
        <input value="{{your_name}}">
        <input type=submit value="{{done}}!" on-click="{{feelingLucky}}">
      <p class=instructions>
  <script type="application/dart" src="hello_you.dart"></script>
As I mentioned, the attributes like hello and done have to to hard-coded in the page for this approach to work. Instead, I want those properties to be dynamic getters.

In the end, I do not even use the intl package, which seems useful for strings requiring parameters (numbers or dates). I find that I can achieve what I want with a Labels class like:
class Labels {
  var locale;

  operator [](label) {
    var lookup;
    switch (locale) {
      case 'fr' : lookup = fr; break;
      default: lookup = en;

    return lookup[label];

  static Map get en => {
    'hello': 'Hello',
    'done': 'Done',
    'instructions': 'Introduce yourself for an amazing personalized experience!'

  static Map get fr => {
    'hello': 'Bonjour',
    'done': 'Fin',
    'instructions': 'Présentez-vous une expérience personnalisée incroyable!'
The localized labels are included as static methods. An instance of the Labels class provides an [] lookup, switching the labels based on the current locale.

The various getters in the Polymer can then be written as lookups of Labels:
class HelloYou extends PolymerElement {
  // ...
  @published String locale = 'en';

  HelloYou.created(): super.created();

  String get hello => _labels['hello'];
  String get done => _labels['done'];
  String get instructions => _labels['instructions'];

  var __labels;
  get _labels {
    if (__labels == null) __labels = new Labels(locale);
    return __labels;
  // ...
That still will not quite work if I wanted to allow a human to change the locale on the fly, but it allows the Polymer to change locales with a simple attribute:
<hello-you locale="fr"></hello-you>
Which results in:

I will pick back up tomorrow trying to allow a human to hot swap locales. I would also like to come up with a contrived example to use for intl messages. I do not think that I will end up using them in Patterns in Polymer (there's not an easy JavaScript equivalent), but I misunderstood them badly enough tonight to confuse myself into thinking that I needed them. This seems a fine chance to understand what they really can do.

Day #973


  1. I played with this a while ago and like the approach using filters

    1. That is pretty nice. The filters make the Polymer template self-documenting for which fields get i18n. I'll give that a try in a day or two. Thanks!