Les textes d'interface restent cohérents quand chaque chaîne visible par l'utilisateur se trouve dans un seul catalogue du dépôt, est référencée par clé et ne change que via un diff relu. C'est la même discipline que celle que vous appliquez déjà à la logique, appliquée aux formulations. Tout le reste de cette page décrit les pratiques qui l'ancrent durablement.

Voici le guide opérationnel. L'architecture sous-jacente, et l'argument pour faire de la codebase la source de vérité de la copie produit, se trouvent dans la copie produit native au code. Lisez-le pour le pourquoi. Poursuivez ici pour le comment.

Où doit vivre la copie d'interface ?

Dans un fichier de catalogue par langue source, à l'intérieur du dépôt, à côté du code qui l'affiche. Pas dans un tableur, pas dans un fichier de design, pas dans une page de wiki qui était exacte en mars. Le catalogue est l'endroit où un développeur, un relecteur, un traducteur et un agent de code regardent tous quand ils veulent savoir ce que dit un écran.

Les règles pratiques qui en découlent :

  • Un catalogue, une locale source. Un seul en.json (ou .po, .ftl, .strings, selon ce que lit votre bibliothèque i18n) fait foi. Si vous avez plusieurs apps dans un monorepo, chaque app a le sien, et les chaînes partagées vont dans un catalogue partagé que les deux importent. Ce que vous évitez : deux fichiers pouvant contenir le même message.
  • Regroupez par écran ou par fonctionnalité, pas par composant. Des clés comme checkout.summary.total vieillissent mieux que SummaryCard.totalLabel, car les composants sont renommés et découpés alors que l'écran et le message restent en place.
  • Nommez la clé d'après le message, pas d'après la formulation. checkout.submit survit à un passage de « Passer commande » à « Acheter maintenant ». checkout.placeOrderButton non, et le décalage entre la clé et le texte est ce qui conduit ensuite un relecteur à croire qu'il s'agit de deux chaînes différentes.
  • Gardez le catalogue dans la même pull request que le code qui l'utilise. Une chaîne ajoutée dans une PR et son composant dans une autre, c'est une chaîne qui sera manquée, oubliée ou dupliquée.

Si vos chaînes sont encore des littéraux dans les composants, le catalogue est la première chose à créer. Le faire à la main est un projet à part entière, d'où l'intérêt de connecter le dépôt et de laisser la conversion s'en charger, détaillé plus bas.

Pourquoi référencer par clé plutôt que chercher le littéral ?

Parce qu'une clé ne peut résoudre qu'une seule chaîne, alors qu'un littéral peut être saisi d'autant de façons qu'il y a de développeurs. « Sign in », « Sign In », « Log in » et « Login » sont quatre chaînes pour un outil de recherche et une seule intention pour l'utilisateur. Quand chaque composant appelle t("auth.signIn"), la question de la bonne formulation est tranchée une fois, à un seul endroit, et la réponse s'applique partout où la clé est utilisée.

Deux habitudes font tenir le tout :

  • Réutilisez avant d'ajouter. Avant de créer une clé, cherchez le message dans le catalogue. Si common.cancel existe, une nouvelle dialog.cancelButton avec le même texte est le début de la dérive, pas un doublon inoffensif. Les deux seront modifiées indépendamment dès que quelqu'un touchera à l'une d'elles.
  • Ne concaténez pas. t("cart.items") + " " + count produit un texte qu'aucune ligne du catalogue ne décrit. Utilisez l'interpolation et les formes plurielles de votre bibliothèque pour que la phrase entière soit une unité relisable.

L'exception qu'on invoque volontiers, c'est « cette chaîne n'apparaît qu'une fois, un littéral suffit ». Elle n'apparaît qu'une fois aujourd'hui. Le composant sera copié, l'écran aura un jumeau, et le littéral voyagera avec avant de diverger.

Comment relire les changements de copie dans une pull request ?

Traitez une ligne de catalogue modifiée comme une signature de fonction modifiée : quelque chose qu'un relecteur lit, questionne et approuve. C'est la pratique qui transforme la cohérence d'un problème de mémoire en problème de relecture, et la relecture est le seul moment où quelqu'un est fiablement attentif.

Ce qui fait fonctionner la relecture du catalogue :

  • Un changement de formulation est sa propre ligne dans le diff. Cela ne vaut que si la chaîne est dans le catalogue. Un changement de formulation dans un composant est aussi une ligne de diff, mais il se noie parmi les changements de logique et on le survole comme « juste du texte ».
  • Routez les changements du catalogue vers un responsable de la copie. Une entrée CODEOWNERS sur le chemin du catalogue signifie que la personne qui possède la formulation — responsable produit, content designer ou développeur le plus attentif au sujet — est sollicitée à chaque changement. Tout le reste de la PR suit la relecture normale.
  • Posez trois questions par ligne modifiée. Est-ce le terme validé pour cet élément ? Est-ce cohérent avec les chaînes voisines en ton et en casse ? La clé décrit-elle toujours le message ? Un relecteur qui se les pose rattrape l'essentiel de ce qu'un guide de style est censé prévenir.
  • Refusez les renommages silencieux. Une PR qui fait passer auth.signIn de « Sign in » à « Log in » sans explication est une décision de copie prise par la personne qui éditait à ce moment-là. Elle exige une phrase dans la description de la PR expliquant pourquoi, ou elle doit être annulée.

Le mode de défaillance que cela prévient a un nom. La dérive de copie, c'est le texte visible par l'utilisateur qui s'écarte de sa source validée selon les surfaces et au fil du temps, sans source de vérité unique dont s'écarter. Qu'est-ce que la dérive de copie en détaille les causes ; la relecture du catalogue dans le diff est la pratique qui en neutralise la plupart.

Comment empêcher l'arrivée de nouvelles chaînes codées en dur ?

Faites échouer le build dès qu'un nouveau littéral apparaît. Les règles et les relectures attrapent ce que les gens pensent à chercher. Une règle de lint attrape le reste, sur chaque pull request, sans que personne ait à y penser.

La mise en place est légère :

  • Activez une règle no-literal-string. eslint-plugin-i18next en fournit une pour les projets JavaScript et TypeScript. Limitez-la au texte JSX et aux attributs visibles par l'utilisateur, comme les indications de champ, title, aria-label et alt, et exemptez les fichiers de test et les stories Storybook pour que la règle reste crédible.
  • Exécutez-la là où se font les merges. Un hook de pre-commit est pratique ; une étape de CI est celle qui compte, car elle s'exécute sur la pull request, que l'éditeur de l'auteur soit configuré ou non.
  • Ajoutez un contrôle des clés inutilisées et manquantes. La plupart des bibliothèques i18n ont un outil compagnon qui liste les clés référencées dans le code mais absentes du catalogue, et les clés du catalogue référencées nulle part. Exécutez-le aussi en CI. Les clés inutilisées sont l'endroit où se cachent les formulations périmées.
  • Commencez strict, puis autorisez les exceptions par commentaire. Une règle désactivée pour tout le dossier components/ n'est pas une règle. Une règle désactivée sur une seule ligne, avec une raison, fait office de documentation.

Les équipes qui développent avec des outils de code IA sont plus exposées, car ces outils régénèrent des composants à partir du contexte local et ramènent les littéraux avec eux. Pourquoi Cursor AI continue d'ajouter des chaînes codées en dur détaille la règle de lint, y compris la configuration en trois couches : fichier de règles, linter et CI.

Que doit contenir un guide de style de la copie pour développeurs ?

Un court fichier dans le dépôt, qui tient sur un écran, énonce la voix, fixe les termes et indique où vont les chaînes. Les longs guides de style vivent dans un wiki et sont lus une fois. Un guide court vit à côté du code et est lu par les personnes comme par les outils à chaque ajout de chaîne.

Un COPY.md efficace (ou une section de votre guide de contribution existant) couvre :

  1. La voix, en trois phrases. À qui le produit ressemble, son niveau de formalité, s'il dit « vous » ou « l'utilisateur ». Assez pour trancher la plupart des débats.
  2. Les termes qui ne varient jamais. Le nom de chaque fonctionnalité, les verbes des actions principales (« Enregistrer » ou « Mettre à jour » ? « Supprimer » ou « Retirer » ?), et les mots que vous avez décidé de ne pas employer.
  3. Les règles de casse et de ponctuation. Casse de phrase ou casse de titre dans les boutons et les titres, point final ou non dans les infobulles, façon d'écrire les nombres et les dates.
  4. Où vont les chaînes et comment nommer les clés. Le chemin du catalogue, la règle de regroupement, la règle de nommage des clés, et « cherchez avant d'ajouter ».
  5. Que faire en cas de doute. À qui demander, ou quelle clé réutiliser en attendant.

Gardez-le dans le dépôt pour qu'un fichier de règles puisse y pointer, qu'un relecteur puisse le citer en commentaire et qu'un agent de code puisse le lire avant d'écrire un libellé.

Comment empêcher les agents de code IA de casser la cohérence ?

Donnez à l'agent une source de vérité à lire et un garde-fou qui le rattrape quand il ne la lit pas. Un agent qui génère un formulaire écrira sans scrupule « Submit » sur un écran et « Send » sur le suivant, car chacun était le mot localement le plus probable. Il n'est pas négligent : il n'a simplement rien par rapport à quoi rester cohérent.

Deux mesures règlent l'essentiel. D'abord, un fichier de règles (AGENTS.md, .cursor/rules, CLAUDE.md, selon ce que lisent vos outils) qui dit : le texte visible par l'utilisateur va dans le catalogue, réutilisez les clés existantes, et lisez COPY.md avant d'écrire un libellé. Ensuite, la règle de lint de la section précédente, car un fichier de règles réduit les approximations et une règle de lint attrape celles qui sont passées.

C'est tout ce que cette page a à dire sur les agents. Le problème précis d'un agent qui reformule des chaînes déjà existantes, et la façon de rendre cette modification visible plutôt que silencieuse, est un sujet à part : comment empêcher les agents de code IA de réécrire votre copie d'interface.

Quel est le rôle de la localisation ?

La cohérence et la localisation dépendent de la même chose, une source validée : le catalogue qui vous donne l'une vous donne l'autre. Une équipe incapable de dire laquelle de trois formulations est la bonne ne peut en traduire aucune correctement non plus. Corrigez la source, et la traduction devient un artefact dérivé du catalogue plutôt qu'un projet distinct avec sa propre version de la vérité.

Concrètement, le catalogue que vous avez construit pour la cohérence est déjà le fichier à partir duquel travaille un traducteur ou un outil de traduction. Chaque clé reçoit ses équivalents dans chaque langue cible, les mêmes noms de clés s'appliquent, et une modification de formulation dans le catalogue source apparaît comme un changement que les traductions doivent suivre. Quand ces traductions sont laissées à la traîne de la source, vous obtenez la classe de problèmes décrite dans pourquoi les fichiers de traduction se désynchronisent, et la solution est la même discipline : un catalogue, des clés, des changements dans le diff.

Où se place globalize.now

globalize.now est un système de gestion des textes natif du code, avec la localisation intégrée, et chaque pratique de cette page correspond à ce qu'il suppose de votre dépôt. Vous connectez un dépôt GitHub ou GitLab dans l'app. Un scan détermine ce qui s'y trouve. Si les chaînes sont des littéraux dans les composants, une conversion ponctuelle les déplace derrière des clés dans un catalogue et ajoute la configuration i18n s'il n'y en a pas. Si vous utilisez déjà next-intl, react-i18next, i18next ou Lingui, il s'appuie sur l'existant. Le résultat arrive sous forme de pull request sur sa propre branche : le premier catalogue passe donc exactement par la relecture décrite plus haut. Ensuite, les nouvelles chaînes sources que vous ajoutez sont traduites par des jobs de push qui ouvrent une pull request avec le résultat. Le dépôt reste la seule source de vérité.

Nous travaillons à étendre cette même discipline au site marketing, à la landing page et au produit à partir d'une seule source, pour qu'une fonctionnalité soit décrite de la même façon partout où un utilisateur la rencontre. C'est la direction que nous prenons, pas quelque chose à acheter aujourd'hui. Les tarifs sont sur la page de tarification.

Une checklist à adopter cette semaine

  1. Créez un catalogue par langue source, dans le dépôt, et fixez la règle de nommage des clés.
  2. Déplacez-y les chaînes des écrans que vous touchez le plus ; référencez-les par clé.
  3. Activez une règle de lint no-literal-string, limitée au texte visible par l'utilisateur, et exécutez-la en CI.
  4. Ajoutez une ligne CODEOWNERS sur le chemin du catalogue.
  5. Rédigez COPY.md : voix, termes fixes, règles de casse, où vont les chaînes. Un écran.
  6. Faites pointer le fichier de règles de votre agent vers le catalogue et vers COPY.md.
  7. Rejetez en review tout changement de formulation dont la description de la PR ne donne pas la raison.

Si la deuxième étape est celle qui ressemble à un mois de travail, connectez un dépôt, lisez le plan qu'il propose et passez en revue la pull request qu'il ouvre.

globalize.now transforme le texte codé en dur de votre application en fichiers de traduction prêts à l'emploi et les maintient à jour au fur et à mesure de vos déploiements.

Essayer globalize.now gratuitement