Une présentation de Christophe Porteneuve à #FranceJS 2013
Paris Web, toussa…
Terme | Définition |
---|---|
L10n | Localization (régionalisation) : traduction et adaptation à la culture pour une certaine région, un certain pays |
i18n | Internationalization : adaptation technique ouvrant la voie à de multiples régionalisations |
g11n | Globalization : i18n + L10n |
Les développeurs s’en foutent…
…ou alors ils n’y pensent pas…
…ou alors ils ont la flemme…
…ou alors leurs outils puent.
vos 1 invités sont prévenus
Aucune personnalisation (masculin pluriel pour tout)
vos 1 invité(e)(s) est (sont) prévenu(e)(s)
« Je sais que c’est pourri mais ni le temps ni l’envie »
(on se cantonne en général au masculin ou à du partiel)
votre invité(e) est prévenu(e)
Effort sur le nombre, mais pas sur le genre
La majorité des efforts s'arrête là :-(
votre invitée est prévenue
La classe à Dallas : déclinaison genre + nombre
(on ne sait toutefois pas à quel degré de pluralisation)
votre invitée a bien reçu les 1 042 messages renvoyés ce lundi
Achievement unlocked: g11n Wizard!
Déclinaison à contextes multiples + formatage
(spéciale dédicace à @notabene et @ElieSl)
Les dates et les nombres
samedi, mai 18 2013 à 5:00, le thé…
Il y a 2,345 propositions à évaluer
Un T-shirt à seulement €7.95 !
Le TC39 a compris qu’il faut quelque chose…
Le standard ECMA-402, à savoir la ECMAScript Internationalization API, comble un manque, mais attention !
Uniquement pour le formatage et la collation (comparaison/tri de textes, etc.)
C’est déjà pas mal !
Adoptez une approche à priorité croissante
Accept-Language
(~ navigator.language
)fr.wikipedia.org
apple.com/fr/…
example.com/?locale=fr&…
lang=
de <html>
(que côté client, a posteriori)La langue active, ce n’est pas anodin…
Quand tu l’oublies, ça peut trop foutre la honte
lang=
(codes ISO-3166-2)dir=
(ltr
, rtl
)bdo
pour les cas foireux ou pré-orientésvar TRANSLATIONS = {
fr: {
label: 'Changez la langue :',
'#great': 'C’est génial !',
// …
},
…
};
function applyLocale(loc) {
loc = 'string' == typeof loc ? loc : document.documentElement.getAttribute('lang');
var repo = TRANSLATIONS[loc];
for (var sel in repo)
document.querySelector(sel).innerText = repo[sel];
}
https://github.com/tdd/sudweb-g11n/tree/master/public/demos/silex/
*.po
Gettext est à peu près partout…
…mais il est limité à deux contextes : le nombre et un contexte libre
(en revanche il gère très bien les formes de pluriel)
Il est facile de convertir ses fichiers .po
(voire .mo
) vers d’autres formats, notamment JSON, pour migrer vers d’autres outils
Le principal problème vient des pluriels irréguliers et des invariables, particulièrement fréquents en anglais
Principale solution JS : Lingo
Module Node, enrobable côté client (ex. avec browserify)
var lang = new Language('en', 'English');
lang.pluralize('data') // => 'data'
lang.pluralize('sheep') // => 'sheep'
lang.singularize('octopi') // => 'octopus'
lang.singularize('axes') // => 'axis'
Les nombres (simples, monnaies, pourcentages…) et les dates/heures
Portage JS du module Google Closure
(lui-même issu du JDK je crois)
var parisCash = new NumberFormat('fr_FR', NumberFormat.Format.CURRENCY);
var phillyCash = new NumberFormat('en_US', NumberFormat.Format.CURRENCY);
var montrealCash = new NumberFormat('fr_CA', NumberFormat.Format.CURRENCY);
var unambiguousCash = new NumberFormat('fr_CA', NumberFormat.Format.CURRENCY,
null, NumberFormat.CurrencyStyle.GLOBAL);
parisCash.format(Math.PI * 1000) // => '3 141,59 €' — avec les insécables…
phillyCash.format(Math.PI * 1000) // => '$3,141.59'
montrealCash.format(Math.PI * 1000) // => '3 141,59 $'
unambiguousCash.format(Math.PI * 1000) // => '3 141,59 CAD $'
157 locales pris en charge !
Toutes sortes de formatages/analyses utiles…
numeral.language('fr');
var pim = numeral(Math.PI * 1000 * 1000 * 1000);
var pik = numeral(Math.PI * 1000);
var pi = numeral(Math.PI * 1000);
pi.format('0.000') // => '3,142'
pi.format('0.0%') // => '314,2%'
pik.format('0,0.00 $') // => '3141,59 €' -- presque…
pim.format('0.00b') // => '2.93GB'
pik.format('0.00a') // => '3.14k'
numeral(1).format('o') // => '1er'
numeral(18394).format(':') // => '5:06:34'
numeral().unformat('3 142,17 €') // => 3142.17
Pas parfait, mais ça aide…
Calculs + formatage et analyse… en multilingue
Chaînes de format détaillées (JDK, pas strftime
)
moment.lang('fr');
var today = moment('19/05/2013', 'DD/MM/YYYY');
today.week() // => 20
today.add(2, 'months').format('dddd D MMMM YYYY') // => 'jeudi 18 juillet 2013'
today.endOf('month').fromNow() // => 'dans 3 mois'
moment().add(1, 'day').calendar() // => 'Demain à 10:40'
moment().endOf('month').diff(moment(), 'days') // => 12
// Et PLEIN d’autres trucs !
Beaucoup de fonctionnalités, dispo client (détection de langue) et Node, réutilisation de clé, pluralisation riche, deux niveaux de contexte, post-processing et notifs serveur traductions manquantes, etc.
// Dans locales/fr/translation.json ou JSON local chargé :
// { "greeting": "Salut __name__ !",
// "greet_friends_male": "Salut copain !",
// "greet_friends_male_plural": "Salut les copains !",
// "greet_friends_female": "Salut copine !",
// "greet_friends_female_plural": "Salut les copines !"}
i18n.init({ lng: 'fr' });
i18n.t('greeting', { name: 'John') // => 'Salut John !'
i18n.t('greet_friends', { count: 3, context: 'male' }) // => 'Salut les copains !'
Bon équilibre entre simplicité fonctionnelle et API.
Pluralisation personnalisable, réutilisation de clé, interpolation, chargement initial ou AJAX, fallback…
Dépendance jQuery, en revanche.
// locales/fr.json (ou datastore intégré au JS) :
// { "greeting": "Bonjour __name__",
// "inbox": "Vous avez un e-mail",
// "inbox_plural": "Vous avez __count__ e-mails" }
$.jsperanto.init(applyLocale, { lang: 'fr' });
$.t('greeting', { name: 'Robert' }) // => 'Bonjour Robert'
$.t('inbox', { count: 17 }) // => 'Vous avez 17 e-mails'
Multi-contexte avec imbrication. Le top, mais syntaxe un peu lourde, forcément…
var mf = new MessageFormat('fr');
var f = mf.compile('Il \
{RES, plural, =0{n’y a aucun} one{y a un} other{y a #}} \
{RES, plural, one{produit trouvé} other{produits trouvés}} \
dans {CAT, plural, one{une catégorie} other{# catégories}}');
f({ RES: 0, CAT: 0 }) // => 'Il n’y a aucun produit trouvé dans une catégorie'
f({ RES: 1, CAT: 1 }) // => 'Il y a un produit trouvé dans une catégorie'
f({ RES: 3, CAT: 1 }) // => 'Il y a 3 produits trouvés dans une catégorie'
f({ RES: 3, CAT: 2 }) // => 'Il y a 3 produits trouvés dans 2 catégories'
Par Mozilla, initialement pour Firefox OS.
Responsive. Mais bordel côté format & mise en œuvre…
<name "L20n">
<welcome "{{ name }} by Mozilla">
<position() { @screen.width.px < 1140 ?
"default" : "right" }>
<step4[position()] {
default: """
4. Start localizing a resource file
as you see below.
""",
right: """
4. Start localizing a resource file
as you see on the right.
"""
}>
Approche engine : Tolk
Approche SaaS : Locale
Les 2 : YAML + sérialisation JSON dans la vue initiale
Service en ligne multi-formats, type Locale mais avec une insistance sur les workflows de relecture/approbation.
Outil open-source pour simplifier les saisies MessageFormat avec des exemples concrets. Encore balbutiant mais très, très bonne contextualisation pour assister la traduction.
Petit tour de l'interface vite fait…
Plugin i18n
Archi intéressante avec fallbacks progressifs, etc. mais une limite à connaître : basé modules → langue déterminée une seule fois
Angular propose le formatage pour les dates/heures, nombres et monnaies, ainsi que la pluralisation, mais rien de spécial pour la traduction.
Extensions Handlebars intégrées basées sur CLDR.js pour la traduction et la pluralisation, qu’on charge comme on veut. Juste la pluralisation comme contexte, mais CLDR.
Interfaçage avancé Express, détection/persistence de langue, helpers de traduction et de pluralisation, compatibilité Webtranslateit…
Toutes les fonctions du côté client + sa propre UI de gestion et un bon interfaçage Express.
Similaire à i18n-2 mais plus flexible sur les stockages de traductions. Déjà pas mal de choses mais (comme d’hab) juste 2 niveaux de contexte.
Prise en charge « façon Rails » CompoundJS, TowerJS, etc.