Les locales dans Angular

Les locales sont toujours une problématique complexe à mettre en place. Sur Overblog, j'avais été obligé d'écrire mon propre système de locales afin de pouvoir gérer plusieurs concepts : rechargement des traducations à la volée, prise en compte de paramètres dans la locale, locales dans les locales, etc.

AngularJS n'a pas de système de localisation intégré. Il propose bien un système d'internationalisation afin de gérer les formats de nombres, monnaies, dates, etc, mais rien pour la traducation de texte. Ils partent du principe que chacun peut se coder son moteur de locale très simplement à l'aide d'un service. En effet, c'est très simple. J'ai utilisé le service proposé par Jim Lavin qui sait charger le fichier de resources propre à la langue du navigateur et propose une directive et un filtre.

Une fois le service chargé on peut alors afficher du texte de la façon suivante :

<p data-i18n="montexte"></p>  

ou

<p>{{"montexte"|i18n}}</p>  

Tout cela marche fort bien. Mais je me suis retrouvé face à une problématique qui était récurente sur Overblog et que je voudrais donc anticiper : remplacer des paramètres dans la traduction. Un simple exemple :

"10 articles filtrés sur un total de 120"

10 et 120 sont des variables. 10 va changer au cours de la frappe dans le champs de tri, 120 va changer selon ce que contient la base de données. Le développeur un peu pressé résoudra ce problème de la façon suivante :

<p>{{filtered}} {{'posts filtered on'|i18n}} {{total}}</p>  

Sauf que selon les langues et la tournure de phrase, filtered et total ne seront peut être pas à la même place. On pourrait aussi vouloir rajouter des mots avant ou après ces variables, par exemple en anglais : Filter 10 posts on 120.

J'ai trouvé une solution, très moche, mais qui a le mérite de fonctionner :

<p>{{('filtered on total'|i18n).replace('@@filtered@@', filtered.length).replace('@@total@@', all.length)}}</p>  

où "filtered on total" vaut "Filter @@filtered@@ posts on @@total@@".

Si vous connaissez un moyen plus joli ou plus approprié, n'hésitez pas à m'en faire part en commentaire.

Edit

J'ai fait un truc un peu plus joli avec un filtre Substitute très simple :

'use strict';

angular.module('myapp')  
.filter('substitute', function () {
  return function (text, substitutions) {

    angular.forEach(substitutions, function(v, k) {
      text = text.replace(k, v);
    });
    return text;
  };
});

Ainsi, on peut écrire dans le template :

{{ 'filtered on total'|i18n|substitute:{'@@filtered@@': filtered.length, '@@total@@': all.length} }}

Il faut juste penser à ne pas oublier l'espace entre la dernière parenthèse du json du filtre substitute et les doubles accolades de fin de block sinon ça plante.

Hadrien

Hi, I'm a french Javascript Lead Developer, Web Architect from Toulouse, France. I've worked for 12 years for many projects with YUI, AngularJS, Aurelia.io and now React and React native.

Toulouse, France https://hadrien.eu