La localisation d'une application dynamique comme Sprout Social dans plusieurs langues est une entreprise complexe. Traduire le texte qui apparaît dans l'application n'est que la moitié de l'histoire. Cela implique également de développer notre application de manière à faciliter l'extraction et l'échange de ce texte pour les traductions. Chez Sprout, nous nous appuyons sur des fournisseurs tiers pour les traductions. Mais nous avons toujours besoin d'outils pour extraire, regrouper et soumettre des demandes de traduction à ces fournisseurs, puis servir et restituer les traductions aux utilisateurs finaux.



Pendant des années, l'équipe d'ingénierie de Sprout s'est débrouillée avec une solution de localisation personnalisée, car les solutions open source étaient encore en pleine maturation. Cela nous a permis d'accueillir nos plus gros clients dans nos langues prises en charge, mais il manquait certaines fonctionnalités utiles. Dans cet article, je vais décrire notre nouveau système de localisation, comment il s'attaque aux scénarios de localisation les plus complexes et comment nous avons progressivement introduit ces changements dans l'ensemble de l'organisation d'ingénierie Web.



Notre ancien système

Pour comprendre notre nouveau système de localisation, vous devez d'abord comprendre comment notre ancien système fonctionnait et les domaines où nous pourrions l'améliorer.

Syntaxe des messages

La localisation des applications consiste à résumer le texte visible par l'utilisateur final en unités de chaîne, appelées messages. Ces messages sont extraits et soumis à des traducteurs. En faisant abstraction de ces chaînes, nous pouvons facilement les échanger en fonction de la langue préférée de l'utilisateur final.

Ces messages peuvent être de simples chaînes statiques comme 'Hello, world' ou avoir des espaces réservés comme 'Hello, {name}' ou une mise en forme de texte enrichi comme 'Hello, world'. Étant donné que ces fonctionnalités doivent être sérialisées en chaînes, vous avez besoin d'une syntaxe que les traducteurs et le code de l'application comprennent pour traduire et restituer correctement le texte.

Une partie de ce qui rendait notre ancien système de localisation difficile à utiliser était que nous avions créé notre propre syntaxe et maintenu un « analyseur » maison pour ladite syntaxe. Ce code prenait du temps à maintenir et la syntaxe était assez minimale. Nous voulions des fonctionnalités supplémentaires pour rendre les messages plus complexes.


numéro d'ange 23

Exemple : dans l'application Sprout, nous avons besoin d'un moyen de rendre 'Vous avez X messages' où X est une valeur numérique dynamique.



Considérez le cas pluriel, 'Vous avez 5 des postes ”. Considérons le cas singulier, 'Vous avez 1 poste ”. Prenons le cas '0'. Considérez les langues qui pourraient avoir une grammaire pour le cas '1' comme le chinois et le japonais. Considérez les langues qui ont une grammaire pour le cas où X est un « grand nombre » comme l'arabe, le polonais et le russe.

Gestion des messages

Nous avons des messages que nous pouvons envoyer aux traducteurs et échanger dans notre application. Notre application a besoin d'un moyen de stocker ces messages et de les servir à nos utilisateurs finaux.

Notre ancien système stockait tous nos messages dans des fichiers JSON (que nous appelions 'fichiers lang'), qui étaient gérés manuellement. Nous avons référencé les messages dans ces fichiers en utilisant des identifiants dans notre code javascript source. Lorsqu'un utilisateur voulait l'application en espagnol, nous servions nos fichiers en espagnol, puis le javascript rendait le message espagnol correspondant en utilisant l'ID.



Pour des raisons de performances, nous avons essayé de ne servir que les messages utilisateur qui se trouvaient sur cette page, nous avions donc des fichiers lang séparés pour les différentes pages de l'application. C'était un système valide, mais à mesure que notre équipe et notre application évoluaient, cela signifiait plus de temps de développement manuel pour créer et gérer ces identifiants et ces fichiers lang.

  Capture d'écran de JavaScript précédemment utilisé pour gérer manuellement les messages et la traduction dans Sprout's codebase.

Pour ajouter un nouveau message à l'application, les développeurs devaient les ajouter manuellement au bon fichier lang avec un identifiant unique pour référencer ce message. Parfois, nous rencontrions des problèmes de collisions d'ID et de fautes de frappe entraînant une langue manquante dans l'application. L'ajout de texte à l'application Web était fastidieux avec de nombreuses étapes qui n'étaient pas intuitives.

Notre nouvelle solution

Connaissant ces lacunes, les ingénieurs Web de toute l'organisation Produit ont créé un groupe de travail sur la localisation pour développer une solution. Nous nous rencontrions régulièrement pour réfléchir. Après un processus de recherche approfondi, nous avons décidé de migrer l'application Sprout de notre système de localisation maison pour utiliser FormatJS réagir-intl bibliothèque et construire une infrastructure autour d'elle pour gérer nos messages. React-intl était la bibliothèque de localisation open source la plus riche en fonctionnalités et la plus populaire de l'écosystème javascript et s'intégrait bien dans notre base de code.

Syntaxe des messages

Nous voulions une solution plus robuste et ne voulions pas créer quelque chose à partir de rien. Nous avons adopté le Syntaxe des messages ICU , une syntaxe standardisée utilisée dans les applications Java, PHP et C, et capture les complexités des messages d'application dynamiques. Le réagir-intl La bibliothèque prend également en charge l'analyse et le rendu des messages de syntaxe de message ICU.

  Un exemple côte à côte de la façon dont la syntaxe des messages ICU capture plusieurs cas. A gauche se trouve le message en anglais, avant d'être traduit en russe. Sur la droite se trouve le message traduit en russe. Remarquez que lorsque les traducteurs convertissent ce message dans d'autres langues, ils peuvent ajouter et supprimer des cas si nécessaire pour prendre correctement en charge la langue. La traduction russe de ce message ajoute « peu » et « beaucoup » de cas.

Ceci est un exemple de la façon dont la syntaxe des messages ICU capture plusieurs cas. C'est le message en anglais et en russe. Remarquez que lorsque les traducteurs convertissent ce message dans d'autres langues, ils peuvent ajouter et supprimer des cas si nécessaire pour prendre correctement en charge la langue. La traduction russe de ce message ajoute « peu » et « beaucoup » de cas.

La syntaxe des messages ICU a été testée par de nombreuses applications dans d'innombrables langues. Nous pouvions être sûrs qu'il pouvait répondre aux besoins sophistiqués de nos clients et qu'il existait de nombreuses solutions et/ou ressources pédagogiques pour toutes les questions de localisation que nous rencontrions.

Gestion des messages

Nous avons développé un système à l'aide d'outils fournis par FormatJS qui automatiserait le processus d'ajout, de suppression et de stockage des messages. Cela impliquait des changements philosophiques dans la façon dont nous abordions le stockage et le référencement des messages.

Un changement majeur par rapport à notre ancien système que FormatJS encourage était d'utiliser notre code d'interface utilisateur comme source de vérité pour les messages. Dans notre système précédent, la source des messages et l'utilisation des messages se trouvaient à deux endroits différents, ce qui signifiait que nous devions les synchroniser. Notre nouveau système conserve les sources des messages avec le reste du code de l'interface utilisateur. Nous avons simplement besoin d'exécuter un script qui extraira tous les messages des fichiers d'interface utilisateur pour générer nos fichiers lang, et le contenu du message devient les identifiants uniques à l'aide d'une fonction de hachage.

  Capture d'écran de JavaScript précédemment utilisé pour gérer automatiquement les messages et la traduction dans Sprout's codebase.

Cette modification colocalise les messages avec le code de l'interface utilisateur et présente plusieurs avantages :

  • Plus lisible : Plus d'identifiants conçus pour les robots dans notre code d'interface utilisateur. Nous pouvons maintenant lire les messages en anglais dans le code de l'interface utilisateur et comprendre le texte que l'utilisateur verra.
  • ID non manuels : Ces identifiants qui n'étaient utilisés que par les machines sont désormais générés par les machines, et par définition, uniques par message.
  • Aucun fichier lang géré manuellement : Les développeurs ne devraient pas avoir besoin de toucher à ces fichiers lang. Nos scripts gèrent l'ajout et la suppression des messages.

Comment avons-nous migré ?

Mais comment avons-nous migré toute notre équipe d'ingénierie Web et notre base de code vers ce nouveau système ? Nous avons divisé cela en quatre étapes : piloter le nouveau système, former notre équipe, rendre obsolète l'ancien système et migrer vers notre nouvelle solution.

Pilotage du nouveau système

Le groupe de travail a piloté le nouveau système dans des sections spécifiques de l'application pour avoir une idée de ses meilleures pratiques et de la portée complète de la migration. Cela a permis de configurer le nouveau système côté client (poly-remplissages, etc.) et du côté construction de l'application. Cela nous a permis d'itérer sur l'expérience du développeur et d'atténuer les risques.

Éducation

Nous avons pris ce que nous avons appris du pilote et l'avons utilisé pour éduquer toute l'équipe d'ingénierie Web. Nous avons développé une FAQ et d'autres documents et présentations pédagogiques pour aider les développeurs à utiliser la nouvelle bibliothèque. Il est facile de sous-évaluer cette étape, mais cette partie d'une migration est extrêmement importante. Peu importe la qualité de votre nouveau système, les gens doivent savoir comment et pourquoi ils doivent l'utiliser.

Nous avons également développé un programme d'ambassadeurs dans lequel chaque équipe de fonctionnalités Web de Sprout avait un ambassadeur de la localisation nommé, qui était chargé d'aider à éduquer son équipe sur le nouveau système et de signaler les problèmes ou les points faibles au groupe de travail.

Cela nous a permis de déléguer les responsabilités en matière d'éducation et d'identifier les problèmes spécifiques à chaque équipe.

Dépréciation de l'ancien système

Après nous être sentis confiants dans l'expérience des développeurs, les connaissances partagées et le potentiel d'échelle du nouveau système, nous avons abandonné l'ancien système. Nous avons créé des règles eslint personnalisées et utilisé l'outil de peluchage, attelle , pour bloquer l'utilisation de l'ancien système tout en autorisant les utilisations existantes. À partir de ce moment, les ingénieurs Web devaient utiliser le nouveau système lors de l'écriture de nouveau code.


symbolisme du numéro 19

Migration vers notre nouveau système

Avec confiance dans notre nouveau système et un nombre fixe d'anciens usages, nous avons commencé la migration.

De nombreuses utilisations avaient des équivalents un à un dans le nouveau système. Là où ces équivalents existent, nous avons pu automatiser la migration en écrivant un code-mod utilisant jscodeshift . Nous avons pu exécuter le code-mod de manière itérative sur des sections de la base de code, en apprenant et en corrigeant les problèmes au fur et à mesure. Il y avait suffisamment de cas marginaux restants qui ne pouvaient pas être facilement codés pour que nous nous sentions à l'aise de les réparer manuellement.

Sortir

Pourquoi avons-nous opté pour une telle approche itérative au lieu d'essayer de tout migrer en même temps ? L'utilisation d'une approche itérative fait partie de la culture d'ingénierie de Sprout, et nous croyons en l'apprentissage et l'amélioration constants.

En abordant la migration de cette manière, nous avons pu apprendre au fur et à mesure, en ajustant et en résolvant les problèmes en temps réel. Nous pourrions également annuler les modifications si la migration commençait à bloquer le développement d'applications. Notre approche itérative nous a permis de progresser tout en travaillant sur d'autres initiatives et nous a permis de signaler les changements majeurs avec un groupe plus restreint avant de les déployer à tout le monde. Les mêmes principes de développement de fonctionnalités pour une application s'appliquent au développement d'outils de développement internes.

Apprentissages et plats à emporter

Réinventer notre système de localisation a été une entreprise colossale pour l'ensemble de l'organisation d'ingénierie Web. Mon conseil aux autres personnes confrontées à des projets ou à des défis similaires serait de :

  • Utiliser des normes largement adoptées : Pourquoi créer une syntaxe de message personnalisée alors que les ingénieurs qui ont passé des années à réfléchir à cet espace problématique ont déjà développé une syntaxe de message ICU ?
  • Envisagez de regrouper les éléments connexes : Cela facilitera grandement leur ajout, leur modification et leur suppression.
  • Adoptez un déploiement itératif : Concevez le déploiement de votre changement d'une manière qui vous permet d'apprendre au fur et à mesure. Vous ne pouvez pas tout anticiper, alors prévoyez un espace de recours dans votre plan.
  • Partagez vos apprentissages : L'éducation est la moitié d'un déploiement. Peu importe la qualité de votre nouveau système si les gens ne savent pas comment l'utiliser ou pourquoi il est meilleur.

Pour plus d'informations sur la culture d'ingénierie de Sprout, consultez notre page carrières aujourd'hui.

Partage Avec Tes Amis: