Je construis une application qui devra être disponible dans plusieurs langues et paramètres régionaux.
Ma question n'est pas purement technique, mais plutôt sur l'architecture et les modèles que les gens utilisent réellement en production pour résoudre ce problème. Je n'ai trouvé nulle part de "livre de recettes" pour ça, alors je me tourne vers mon site de questions / réponses préféré :)
Voici mes exigences (elles sont vraiment "standard"):
- L'utilisateur peut choisir la langue (trivial)
- Lors du changement de langue, l'interface doit se traduire automatiquement dans la nouvelle langue sélectionnée
- Je ne suis pas trop préoccupé par le formatage des nombres, des dates, etc. pour le moment, je veux une solution simple pour simplement traduire des chaînes
Voici les solutions possibles auxquelles je pourrais penser:
Chaque composant traite la traduction de manière isolée
Cela signifie que chaque composant a par exemple un ensemble de fichiers en.json, fr.json etc. à côté de lui avec les chaînes traduites. Et une fonction d'aide pour aider à lire les valeurs de celles qui dépendent de la langue sélectionnée.
- Pro: plus respectueux de la philosophie React, chaque composant est "autonome"
- Inconvénients: vous ne pouvez pas centraliser toutes les traductions dans un fichier (pour que quelqu'un d'autre ajoute une nouvelle langue par exemple)
- Inconvénients: vous devez toujours passer la langue actuelle comme accessoire, dans chaque composant sanglant et leurs enfants
Chaque composant reçoit les traductions via les accessoires
Donc, ils ne sont pas conscients de la langue actuelle, ils prennent juste une liste de chaînes comme accessoires qui correspondent à la langue actuelle
- Pro: puisque ces chaînes viennent "du haut", elles peuvent être centralisées quelque part
- Inconvénients: chaque composant est maintenant lié au système de traduction, vous ne pouvez pas simplement en réutiliser un, vous devez spécifier les bonnes chaînes à chaque fois
Vous contournez un peu les accessoires et utilisez éventuellement le truc de contexte pour transmettre la langue actuelle
- Pro: c'est principalement transparent, pas besoin de passer la langue actuelle et / ou les traductions via des accessoires tout le temps
- Inconvénients: il semble compliqué à utiliser
Si vous avez une autre idée, dites-le!
Comment faites-vous?
la source
Réponses:
Après avoir essayé plusieurs solutions, je pense en avoir trouvé une qui fonctionne bien et devrait être une solution idiomatique pour React 0.14 (c'est-à-dire qu'elle n'utilise pas de mixins, mais des composants d'ordre supérieur) ( modifier : aussi parfaitement bien avec React 15 bien sûr! ).
Voici donc la solution, en commençant par le bas (les composants individuels):
Le composant
La seule chose dont votre composant aurait besoin (par convention) est un
strings
accessoire. Il doit s'agir d'un objet contenant les différentes chaînes dont votre composant a besoin, mais la forme en dépend en réalité.Il contient les traductions par défaut, vous pouvez donc utiliser le composant ailleurs sans avoir à fournir de traduction (cela fonctionnerait hors de la boîte avec la langue par défaut, l'anglais dans cet exemple)
Le composant d'ordre supérieur
Sur l'extrait de code précédent, vous avez peut-être remarqué ceci sur la dernière ligne:
translate('MyComponent')(MyComponent)
translate
dans ce cas, il s'agit d'un composant d'ordre supérieur qui enveloppe votre composant et fournit des fonctionnalités supplémentaires (cette construction remplace les mixins des versions précédentes de React).Le premier argument est une clé qui sera utilisée pour rechercher les traductions dans le fichier de traduction (j'ai utilisé le nom du composant ici, mais cela pourrait être n'importe quoi). Le second (notez que la fonction est curry, pour permettre aux décorateurs ES7) est le composant lui-même à envelopper.
Voici le code du composant translate:
Ce n'est pas magique: il lira simplement la langue actuelle à partir du contexte (et ce contexte ne saignera pas partout dans la base de code, juste utilisé ici dans ce wrapper), puis obtiendra l'objet de chaînes approprié à partir des fichiers chargés. Ce morceau de logique est assez naïf dans cet exemple, pourrait être fait comme vous le souhaitez vraiment.
L'élément important est qu'il prend la langue actuelle du contexte et la convertit en chaînes, étant donné la clé fournie.
Tout en haut de la hiérarchie
Sur le composant racine, il vous suffit de définir la langue actuelle à partir de votre état actuel. L'exemple suivant utilise Redux comme implémentation de type Flux, mais il peut facilement être converti en utilisant n'importe quel autre framework / modèle / bibliothèque.
Et pour finir, les fichiers de traduction:
Fichiers de traduction
Qu'en pensez-vous?
Je pense que cela résout tout le problème que j'essayais d'éviter dans ma question: la logique de traduction ne saigne pas partout dans le code source, elle est assez isolée et permet de réutiliser les composants sans elle.
Par exemple, MyComponent n'a pas besoin d'être encapsulé par translate () et pourrait être séparé, permettant sa réutilisation par toute autre personne souhaitant fournir le
strings
par ses propres moyens.[Edit: 31/03/2016]: J'ai récemment travaillé sur un tableau rétrospectif (pour Agile Retrospectives), construit avec React & Redux, et est multilingue. Comme beaucoup de gens ont demandé un exemple réel dans les commentaires, le voici:
Vous pouvez trouver le code ici: https://github.com/antoinejaussoin/retro-board/tree/master
la source
dangerouslySetInnerHTML
prop, soyez juste conscient des implications (nettoyez manuellement l'entrée). Voir facebook.github.io/react/tips/dangerously-set-inner-html.htmlconst formStrings = { cancel, create, required }; export default { fooForm: { ...formStrings, foo: 'foo' }, barForm: { ...formStrings, bar: 'bar' } }
D'après mon expérience, la meilleure approche consiste à créer un état redux i18n et à l'utiliser, pour de nombreuses raisons:
1- Cela vous permettra de passer la valeur initiale depuis la base de données, le fichier local ou même depuis un moteur de template tel que EJS ou jade
2- Lorsque l'utilisateur change la langue, vous pouvez changer toute la langue de l'application sans même actualiser l'interface utilisateur.
3- Lorsque l'utilisateur change la langue, cela vous permettra également de récupérer la nouvelle langue à partir de l'API, du fichier local ou même des constantes
4- Vous pouvez également enregistrer d'autres choses importantes avec les chaînes telles que le fuseau horaire, la devise, la direction (RTL / LTR) et la liste des langues disponibles
5- Vous pouvez définir la langue de changement comme une action redux normale
6- Vous pouvez avoir vos chaînes backend et front end au même endroit, par exemple dans mon cas, j'utilise i18n-node pour la localisation et lorsque l'utilisateur change la langue de l'interface utilisateur, je fais juste un appel API normal et dans le backend, je viens de retourner
i18n.getCatalog(req)
cela renverra toutes les chaînes utilisateur uniquement pour la langue actuelleMa suggestion pour l'état initial de l'i18n est:
Modules utiles supplémentaires pour i18n:
1- string-template cela vous permettra d'injecter des valeurs entre vos chaînes de catalogue par exemple:
2- au format humain ce module vous permettra de convertir un nombre en / à partir d'une chaîne lisible par l'homme, par exemple:
3- momentjs les dates et heures les plus célèbres de la bibliothèque npm, vous pouvez traduire moment mais il a déjà une traduction intégrée juste dont vous avez besoin pour passer la langue de l'état actuel par exemple:
Mise à jour (14/06/2019)
Actuellement, il existe de nombreux frameworks implémentant le même concept en utilisant l'API de contexte de réaction (sans redux), j'ai personnellement recommandé I18next
la source
La solution d'Antoine fonctionne bien, mais il y a quelques mises en garde:
C'est pourquoi nous avons construit redux-polyglot au-dessus de Redux et AirBNB's Polyglot .
(Je suis l'un des auteurs)
Il offre :
setLanguage(lang, messages)
getP(state)
sélecteur qui récupère unP
objet qui expose 4 méthodes:t(key)
: fonction polyglotte T d'originetc(key)
: traduction en majusculetu(key)
: traduction en majusculetm(morphism)(key)
: traduction morphée personnaliséegetLocale(state)
sélecteur pour obtenir la langue courantetranslate
composant d'ordre supérieur pour améliorer vos composants React en injectant l'p
objet dans des accessoiresExemple d'utilisation simple:
envoyer une nouvelle langue:
dans le composant:
Veuillez me dire si vous avez des questions / suggestions!
la source
_()
fonctions, par exemple pour obtenir toutes ces chaînes. Ainsi, vous pouvez dans un fichier de langue le traduire plus facilement et ne pas jouer avec des variables folles. Dans certains cas, les pages de destination nécessitent une partie spécifique de la mise en page pour être affichées différemment. Ainsi, une fonction intelligente de sélection par défaut par rapport à d'autres choix possibles devrait également être disponible.D'après mes recherches à ce sujet, il semble y avoir deux approches principales utilisées pour i18n en JavaScript, ICU et gettext .
Je n'ai jamais utilisé que gettext, donc je suis partial.
Ce qui m'étonne, c'est la faiblesse du soutien. Je viens du monde PHP, que ce soit CakePHP ou WordPress. Dans ces deux situations, c'est une norme de base que toutes les chaînes sont simplement entourées
__('')
, puis plus loin dans la ligne, vous obtenez des traductions à l'aide de fichiers PO très facilement.gettext
Vous obtenez la familiarité de sprintf pour le formatage des chaînes et les fichiers PO seront traduits facilement par des milliers d'agences différentes.
Il existe deux options populaires:
Les deux prennent en charge le style gettext, le formatage des chaînes de style sprintf et l'importation / exportation vers des fichiers PO.
i18next a une extension React développée par eux-mêmes. Jed ne le fait pas. Sentry.io semble utiliser une intégration personnalisée de Jed avec React. Le post React + Redux suggère d'utiliser
Cependant, Jed semble être une implémentation plus centrée sur gettext - c'est son intention exprimée, alors que i18next l'a juste comme option.
ICU
Cela a plus de support pour les cas marginaux autour des traductions, par exemple pour traiter le genre. Je pense que vous en verrez les avantages si vous avez des langues plus complexes à traduire.
Une option populaire pour cela est messageformat.js . Discuté brièvement dans ce tutoriel de blog sentry.io . messageformat.js est en fait développé par la même personne qui a écrit Jed. Il fait des déclarations assez sévères pour utiliser l'ICU :
Comparaison approximative
gettext avec sprintf:
messageformat.js (ma meilleure estimation en lisant le guide ):
la source
Si ce n'est pas encore fait, jetez un œil à https://react.i18next.com/ pourrait être un bon conseil. Il est basé sur i18next: apprenez une fois - traduisez partout.
Votre code ressemblera à quelque chose comme:
Livré avec des échantillons pour:
https://github.com/i18next/react-i18next/tree/master/example
En plus de cela, vous devriez également considérer le flux de travail pendant le développement et plus tard pour vos traducteurs -> https://www.youtube.com/watch?v=9NOzJhgmyQE
la source
Je voudrais proposer une solution simple en utilisant create-react-app .
L'application sera construite pour chaque langue séparément, donc toute la logique de traduction sera déplacée hors de l'application.
Le serveur Web servira automatiquement la langue correcte, en fonction de l'en - tête Accept-Language , ou manuellement en définissant un cookie .
La plupart du temps, nous ne changeons pas de langue plus d'une fois, voire jamais du tout)
Les données de traduction sont placées dans le même fichier composant, qui l'utilise, le long des styles, du html et du code.
Et ici, nous avons un composant totalement indépendant qui est responsable de son propre état, vue, traduction:
Ajoutez une variable d'environnement de langage à votre package.json
C'est ça!
Ma réponse originale incluait également une approche plus monolithique avec un seul fichier json pour chaque traduction:
lang / ru.json
lib / lang.js
src / App.jsx
la source