How to localise a polymer based application?

985 views
Skip to first unread message

Nicolas Bouthors

unread,
Nov 15, 2013, 2:08:50 AM11/15/13
to polym...@googlegroups.com
Do we have à preferred approach to support internationalization of polymer element.
For now I see using the binding API as an option, passing dictionaries as a parameter to each element. But doing so is cumbersome for example for elements containing other elements directly or via the content tag.

Any better idea?

Shouldn't we have a common way, so that all elements inherit from a common dictionary and have some support to retrieve dictionary values from a string, inside a mustache {{}}?

arien...@gmail.com

unread,
Nov 15, 2013, 5:25:48 AM11/15/13
to polym...@googlegroups.com


On Friday, November 15, 2013 8:08:50 AM UTC+1, Nicolas Bouthors wrote:
passing dictionaries as a parameter to each element. But doing so is cumbersome for example for elements containing other elements directly or via the content tag.
I'm interested in what people suggest. There is a similar issue in theming/styling. Certain things need to be shared in a common state for all components without having to pass along attributes.
Style encapsulation is great, to a certain extent, then it becomes annoying. Similarly, passing along labels for localization is much better handled through some shared state.


Nicolas Bouthors

unread,
Nov 16, 2013, 9:21:41 AM11/16/13
to polym...@googlegroups.com
It is simple to create a new polymer tag, which loads dictionaries from the Web for example. 
Then it is tempting to create a Fiter do do the conversion.
{{ "sCancel" | localizeFilter}}  for example.

 PolymerExpressions.filters.localizeFilter = function() {
return {
   toDOM: function(value) {
var lang=getLanguage(); // from navigator.language for ex
return dict[lang][value];  // dict being a gloabl variable
   }
};
   };
Now the issue is to load the dictionary before the new Filter is created and used in other tags.

How can we do so?

Nicolas

Nicolas Bouthors

unread,
Nov 17, 2013, 4:25:00 AM11/17/13
to polym...@googlegroups.com


There is no way to make sure a specific polymer tag is created/inserted before others. 

Still it is possible to use a tab as a wrapper, and to use the time when it is loaded in, to setup the internationalization infrastructure:
- load a bunch of dictionary files. make sure to do it synchonously
- set them up some global variables
- defined a polymer filter, that will then be used by other tags to translate strings, based on the desided language.

The wrapping polymer tag will not play an active role for the setup,  It can still be used to manage a language pref cookie value 
for example. So the app can controle which language should be used next time the application is reloaded.

Not perfect, but operative.


Le vendredi 15 novembre 2013 08:08:50 UTC+1, Nicolas Bouthors a écrit :

Scott Miles

unread,
Nov 17, 2013, 1:05:06 PM11/17/13
to Nicolas Bouthors, polymer-dev
> There is no way to make sure a specific polymer tag is created/inserted before others. 

I'm not sure what this means, can you be more specific?

In particular, tag registration is required by us to be deterministic for exactly this type of thing. One must be able to declare x-foo before x-bar and be confident that x-foo is prepared and can be used inside of x-bar.

Can you provide even just some pseudo code that gives a clearer picture of where you are encountering a problem?

Scott

Follow Polymer on Google+: plus.google.com/107187849809354688692
---
You received this message because you are subscribed to the Google Groups "Polymer" group.
To unsubscribe from this group and stop receiving emails from it, send an email to polymer-dev...@googlegroups.com.
For more options, visit https://groups.google.com/groups/opt_out.

Nicolas Bouthors

unread,
Nov 17, 2013, 3:54:37 PM11/17/13
to polym...@googlegroups.com
Tag registration will certainly be usefull. I am not certain it will be enough for this particular case.
(mainly as an internationalization lib should be usable inside elements and for plain old javascript
parts of the application )
We don't want to have 2 libs on the client side, and server side kits (like for django) are not compatible with polymer templates (because they also use mustache notation and get confused when they hit one)

Here is some code that shows what I want to do.
My internationalization polymer element is x-lang.

The application.
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>Polymer test/title>
  <script type="text/javascript" src="polymer/polymer.js"></script>
 <link rel="import" href="x-lang.html" />
</head>
<body>

  <polymer-element name="match-example">
    <template>
       <div> internat:(msg_hello) {{  "msg_hello"| upObject }}</div> 
       <div> internat:(sApply) {{  "sApply"| upObject }}</div> 
    </template>
    <script>
      Polymer('match-example', {
  created: function() {
    console.log("match-ex created");
  } ,
      });
  
    </script>
  </polymer-element>

  <match-example id="me"></match-example>
  <x-lang id='aid' ></x-lang>
 
</body>
</html>

Note: 
match-example uses a polymer-expression filter : upObject, which is created
as a result of loading the x-lang polymer file.





The code for x-lang:

<link rel="import"  href="polymer_cookie.html">
<polymer-element name="x-lang">
  <template bind>
   <polymer-cookie id="kuki" name="map">   </polymer-cookie>
   <polymer-cookie id="kukil" name="lang">   </polymer-cookie>
    </template>
  <script>

(function() {  
    debug=false;
    
    function getLanguage() {
        kukies=document.cookies;
var pairs = document.cookie.split(/\s*;\s*/);
var map = pairs.map(function(kv) {
            var eq = kv.indexOf('=');
            return {
name: unescape(kv.slice(0, eq)),
value: unescape(kv.slice(eq + 1))
            };
        });

        var lang= map.filter(function(kv){ return kv.name === "lang"; })[0];
console.log("found lang kuki", lang.value);
var langp=lang.value;
if (["fr","en"].indexOf(lang.value) == -1 ) {
    if (navigator.language.indexOf("fr")> -1) langp = "fr";
    if (navigator.language.indexOf("en")> -1)   langp = "en";
}
return langp;
    }
    var endict = { color:"color"}
    var frdict = { color:"couleur"}
    var dict = { en: endict, fr:frdict, default:{color:"Kolor"}}
    var urls=["Messages.properties" ,"Messages_en.properties"
      ,"Messages_fr.properties"];
//
// function to prepare the dictionary upon dll completion
    function process(resp,url) {
var lang="default";
if (url.contains("_fr")) lang = "fr" ;
if (url.contains("_en")) lang = "en" ;
console.log("work on lang",lang);
var arr = resp.split("\n");
arr.forEach(function(e) {
    if ( ! e.startsWith("#") ) {
var wordlist=e.split("=");
var key = wordlist[0].trim();
var removed = wordlist.splice(0, 1);
console.log(key , wordlist.join("="));
dict[lang][key]= wordlist.join("=");
    }
});
    }
// 
// Load the dictionaries synchronously. as the dict
// must be setup before the upObject gets to be used.
    urls.forEach(function(u) {
var xhr = new XMLHttpRequest();
xhr.open("GET", u, false);  // synchronous request
xhr.send(null);
console.log(xhr.responseText);
process(xhr.responseText,u);
    });

    PolymerExpressions.filters.upObject = function() {
return {
    toDOM: function(value) {
var lang=getLanguage();
console.log("I see value", value, dict,lang);
console.log("--->",dict[lang][value]);
return dict[lang][value];
    }
};
    };
// Here we define the polymer element. All it does is allow to have access
// to the loaded dictionary and display the language used for internationalization
//
    Polymer('x-lang', {
dict: {en:{},fr:{},default:{}},
urls:["Messages.properties" ,"Messages_en.properties" ,"Messages_fr.properties"],

enteredView: function() {
    console.log("x-lang inserted ");
            this.dict =dict;
            this.lang=getLanguage();
},



    });
})();
</script>
</polymer-element>


Note:
 At load time, the filter is defined, so it can be used by the other polymer elements when their template is 
created/instanciated.

I tried to define the filter inside x-lang;;enteredView or x-lang-created. It comes too late. I get some errors or the text shown is not translated because the dictionary is not loaded yet.


In practise, we want to have something ready as soon as possible. An internationalization library should be loaded so that it can be used inside polymer-elements, but also in plain javascript (relying on OnReady callbacks for example)

Wrapping the lib in a polymer element would make the init/config and control (change language) nicely colocated and reusable. 

Kelly St. John

unread,
Sep 11, 2014, 4:03:47 PM9/11/14
to polym...@googlegroups.com
This was an interesting topic.  Have there been any updates on best practices for polymer localization support?  What are people successfully using for polymer apps in this regard?

Eric Bidelman

unread,
Sep 11, 2014, 5:02:39 PM9/11/14
to Kelly St. John, polymer-dev
This is an area we're still fleshing out, but Scott did some initial work on https://github.com/Polymer/i18next-element a while back. You might find it useful.

Follow Polymer on Google+: plus.google.com/107187849809354688692
---
You received this message because you are subscribed to the Google Groups "Polymer" group.
To unsubscribe from this group and stop receiving emails from it, send an email to polymer-dev...@googlegroups.com.

Alex Wilder

unread,
Oct 14, 2014, 4:50:24 AM10/14/14
to polym...@googlegroups.com
I used this method to translate my future website:

I created a HTML dictionnary and imported it (so it's loaded before Polymer renders the page):

<link id="localization" rel="import" href="dict.html"/>

dict.html:
  <translation>
  ...
      <key value="new">
          <val lang="en">New</val>
          <val lang="fr">Nouveau</val>
      </key>  
  ... 
  </translation>

Then parsed this dictionnary as a document with javascript and used a Polymer expression to handle localization:
  ....
  PolymerExpressions.prototype.translate = function(key) {
    var dictkey = dictionnaries[key];
    return dictkey[currentLanguage];
  };

I only need to surround my main document with an autobinded template to get it translated:
<template is="auto-binding">
  <core-item label="{{translate('new')}}"></core-item>
</template>

I can also using it as an attribute for a polymer-element so.

I'm sure there's a best way to do it, but this one works like a charm for me. 


Le vendredi 15 novembre 2013 08:08:50 UTC+1, Nicolas Bouthors a écrit :

pascal....@gmail.com

unread,
Jan 6, 2015, 10:10:24 AM1/6/15
to polym...@googlegroups.com
Hi,

I'm trying to use work from Scott on https://github.com/Polymer/i18next-element to translate my Polymer app.

However, I'd like to use i18next with translations from json files, instead of having them defined in the code (as it is done here https://github.com/Polymer/i18next-element/blob/master/i18n.html).

The problem is that i18next loads translation asynchronously, and they are available after elements in my page are rendered.

I've tried to fix this issue by creating a wrapping element, using a conditional template that will display the content only when the translations are loaded.

This element looks like the following :

<polymer-element name="i18n-element">
 
<template>
 
<template if="{{translationLoaded}}">
 
<content></content>
 
</template>
 
<template if="{{!translationLoaded}}">
 
<div>
 Please wait while translations are loading...
 
</div>
 
</template>
 
</template>
 
<script>
PolymerExpressions.prototype._ = function (args) {
 
return i18n.t(args);
};

window
._ = i18n.t;

Polymer('i18n-element', {
 translationLoaded
: false,
 created
: function () {
 
var i18nElement = this;
 i18n
.init({
 lng
: "en"
 
}, function (t) {
 console
.log('i18n initialized', i18n.t('Hello'));
 i18nElement
.translationLoaded = true;
 
});
 
}


});
</script>
</polymer-element>


I can then use this element to wrap other elements, for example

 <i18n-element>
 
<my-element></my-element>
 
<template is="auto-binding">
 Inline content: {{_('Hello')}}.
 
</template>
 
</i18n-element>

my-element is a simple element that renders a text using {{_('Hello')}}

My assertion was that as long as the "translationLoaded" property is false, nothing will be done with child elements of i18n-element.

I was apparently wrong as the {{}} expressions in my-element and in the auto-binding template are still evaluated before the translations are loaded.

I can fix this issue by telling i18next to load translations synchronously.

This setup is actually what Nicolas Bouthors suggested earlier in this thread (wrapping element + synchronous load of dictionaries).

I was however wondering if it is possible to have the {{}} expressions evaluated after the translations have been loaded.

Sample code (with synchronous loading) can be found here https://github.com/plequang/polymer-i18n, and viewed here http://plequang.github.io/polymer-i18n/

Niels Grewe

unread,
Jan 6, 2015, 10:38:19 AM1/6/15
to pascal....@gmail.com, polym...@googlegroups.com
Hi Pacal,

> Am 06.01.2015 um 16:10 schrieb pascal....@gmail.com:
>
> I was however wondering if it is possible to have the {{}} expressions evaluated after the translations have been loaded.

It could be done with a custom filter bound to translationLoaded. For example instead of having ‘{{_('Hello')}}’ you would have {{'Hello‘ | _(translationLoaded)}}. As long as translationLoaded is false, you would just return the argument unchanged from the filter, and once it’s updated to be true, you run the argument through the translation mechanism.

Cheers,

Niels




Eric Bidelman

unread,
Jan 6, 2015, 4:27:23 PM1/6/15
to Niels Grewe, pascal....@gmail.com, polym...@googlegroups.com
You may also be interested in https://ebidel.github.io/i18n-msg/components/i18n-msg/, which uses translations from json files.

Follow Polymer on Google+: plus.google.com/107187849809354688692
---
You received this message because you are subscribed to the Google Groups "Polymer" group.
To unsubscribe from this group and stop receiving emails from it, send an email to polymer-dev...@googlegroups.com.

pascal....@gmail.com

unread,
Jan 9, 2015, 10:10:53 AM1/9/15
to polym...@googlegroups.com, pascal....@gmail.com
Thanks for your suggestion Niels.

It's working. It has a nice "side effect" : you can easily load a new language and switch language dynamically, by setting translationLoaded to false then to true.

However, I've add difficulties to make translationLoaded kind of global. 

So now all my polymer elements should look like this  (always including the i18n-element to gain access to the global translation loaded parameter.

<polymer-element name="my-element">
 
<template>
 
<i18n-element id="translations"></i18n-element>
   
<div>
     Translation in my-element: {{ 'Hello' | _($.translations.data.loaded) }}
 
</div>
 
</template>


I need to think a bit more about it, to see if I prefer this way of doing, or a more simple syntax but without async loading.

Thanks a lot.

Niels Grewe

unread,
Jan 9, 2015, 12:06:49 PM1/9/15
to pascal....@gmail.com, polym...@googlegroups.com
Hi Pascal!

> Am 09.01.2015 um 16:10 schrieb pascal....@gmail.com:
>
> Thanks for your suggestion Niels.
>
> It's working. It has a nice "side effect" : you can easily load a new language and switch language dynamically, by setting translationLoaded to false then to true.

That’s cool. Data binding rocks ;-)

> However, I've add difficulties to make translationLoaded kind of global.
> I eventually managed it by using this https://www.polymer-project.org/docs/polymer/polymer.html#global

What I do in my current project is to have a common element that all my custom elements extend. I’ve added a ‘g’ attribute on it that gets populated with an object containing global state in the ready callback. That way, I can still easily override values for quick testing of individual elements by setting ‘g’ to something sensible (<my-element g='{"foo“: "bar“}’></my-element>).

> So now all my polymer elements should look like this (always including the i18n-element to gain access to the global translation loaded parameter.
>
> <polymer-element name="my-element">
> <template>
> <i18n-element id="translations"></i18n-element>
> <div>
> Translation in my-element: {{ 'Hello' | _($.translations.data.loaded) }}
> </div>
> </template>
>
>
> I need to think a bit more about it, to see if I prefer this way of doing, or a more simple syntax but without async loading.

The biggest problem I see with asynchronously loading translations is that having a flash of untranslated content is not very desirable.

Cheers,

Niels
Reply all
Reply to author
Forward
0 new messages