Vous avez demandé à Claude Code d’ajouter un état de chargement au formulaire de connexion. L’état de chargement fonctionne. Le bouton affiche aussi désormais « Log in » au lieu de « Sign in », le message d’erreur est devenu plus aimable et un titre est soudain passé en casse phrase. Personne n’a demandé quoi que ce soit de tout cela, tous les tests passent et, à moins que quelqu’un ne lise le diff mot à mot, tout part en production.
Cet article traite précisément de cet échec : un texte existant et approuvé reformulé en silence par un agent de coding IA, et le workflow qui l’empêche. Les chaînes que les agents créent en dur et sans clé posent un autre problème, traité dans pourquoi les outils de coding IA ajoutent sans cesse des chaînes codées en dur. La question de l’emplacement général des textes produit relève de l’architecture et est traitée dans les textes produit natifs du code. Ici, on reste sur l’incident : ce lundi matin où les mots ont changé sans que personne ne le décide. globalize.now est un système de gestion des textes produit, natif du code, avec la localisation intégrée. Cet incident est l’une des raisons de son existence.
Pourquoi les agents de coding IA réécrivent-ils les textes d’interface ?
Les agents de coding IA réécrivent les textes d’interface parce qu’ils voient de la prose directement dans les composants. Dès que ces textes sont placés derrière des clés dans un fichier de traduction source, les modifier devient une édition explicite et révisable, plutôt qu’un effet secondaire d’une autre tâche.
Dans un composant, « Sign in » n’a aucun statut particulier. C’est une chaîne littérale dans un attribut JSX, impossible à distinguer d’un className ou d’un identifiant de test. L’agent a été entraîné sur des millions de diffs où améliorer le code voisin était considéré comme un bon comportement. Pour un modèle, rendre un libellé « plus clair » revient au même que renommer une variable. Aucune erreur de typage ne l’attend, aucun test n’échoue, aucun propriétaire n’est associé à ces mots. Reformuler ne lui coûte rien.
La modification passe ensuite la review pour la même raison. Les reviewers lisent la logique. Une modification de formulation se retrouve au milieu d’un diff qui concerne légitimement un état de chargement : le code compile, s’affiche et, si un test de snapshot se plaint, la réaction habituelle consiste à mettre à jour le snapshot — qui enregistre alors la nouvelle formulation comme si elle avait été approuvée.
Les fichiers de règles comme AGENTS.md empêchent-ils vraiment ce problème ?
Les fichiers de règles pour les agents réduisent la fréquence du problème, mais ne rendent pas les textes d’interface impossibles à modifier. C’est l’architecture qui rend la modification visible.
Gardez quand même le fichier de règles : c’est la couche la moins coûteuse et elle fonctionne assez souvent pour justifier ses trente secondes de mise en place. Ce bloc fonctionne dans AGENTS.md, CLAUDE.md et les règles Cursor (.cursorrules ou .cursor/rules/) :
## UI copy policy — do not modify user-facing text
- Never change user-visible strings (button labels, headings, empty states,
error messages, tooltips, placeholder text) unless the task explicitly
asks for a copy change.
- User-visible text lives in the source locale file (e.g. locales/en.json).
Reference it by key. Never inline a new user-facing string in a component.
- If a task needs new user-facing text, add a key to the source locale file,
reference it, and list the new key in your summary for copy review.
- Any diff to the source locale file is a copy change. Call it out
separately — it needs its own approval, apart from code review.
Passons à la partie honnête. Une règle fournit du contexte, et le contexte est en concurrence. Sur une longue tâche, l’instruction n’est qu’une ligne parmi des milliers de tokens de code, et elle est parfois perdue : lors d’une compaction, par un sous-agent qui ne l’a jamais vue ou face à un pattern fort dans le fichier environnant. Pire encore, l’agent qui enfreint la règle ne l’annonce pas. Vous ne recevez aucune notification indiquant que votre formulation a changé ; vous recevez un diff dans lequel elle se trouve simplement avoir changé. Une convention n’est pas un contrôle.
Le fichier de règles est donc la première couche, mais aussi une couche à laquelle vous ne devez pas faire confiance seule.
Comment rendre une modification de texte visible plutôt que silencieuse ?
Sortez chaque chaîne visible par l’utilisateur des composants et placez-la derrière une clé dans un fichier de traduction source. Ensuite, un agent qui veut modifier votre formulation doit éditer un fichier dont l’objectif entier est la formulation.
C’est l’étape que le fichier de règles ne peut pas accomplir, et le mécanisme est simple. Quand les composants référencent t('auth.signIn') au lieu de répéter « Sign in », deux choses changent simultanément. Les diffs de composants ne contiennent plus de texte : l’agent qui travaille sur votre état de chargement n’a donc rien à reformuler là où il intervient. Et chaque modification de texte, humaine ou faite par un agent, arrive dans un seul fichier — où une ligne modifiée est l’événement qui vous intéresse, pas du bruit autour. Parcourir le diff d’un fichier de traduction prend quelques secondes. Annuler une mauvaise reformulation tient en une ligne.
Pour être précis sur ce que cela apporte : l’agent peut toujours modifier le fichier de traduction. Le but n’est pas de rendre les mots intouchables, mais de faire en sorte que les toucher cesse d’être un effet secondaire et devienne une action lisible et révisable. C’est la différence entre le découvrir dans le diff et le découvrir par un client.
Le dossier complet en faveur d’une source de vérité conservée dans le codebase — plutôt que dans un outil de design qui se synchronise avec lui — constitue le pilier les textes produit natifs du code. Pour cet article, une seule conséquence suffit : les textes associés à des clés transforment votre fichier de règles, qui n’était qu’une demande, en quelque chose de vérifiable.
À quoi ressemble l’étape de validation ?
Traitez toute modification du fichier de traduction source comme une modification de texte et transmettez-la à la personne responsable des formulations.
Concrètement : placez le répertoire des locales dans CODEOWNERS afin qu’une personne responsable des textes soit automatiquement sollicitée pour toute modification. Ajoutez une ligne au modèle de PR : « Cette PR modifie-t-elle une formulation visible par l’utilisateur, et était-ce l’objectif de la tâche ? » Conservez aussi l’exigence du fichier de règles : l’agent doit lister les clés modifiées dans son résumé. S’il s’y conforme, la review est instantanée ; sinon, le hook CODEOWNERS intercepte quand même la modification.
Dans une petite équipe, c’est moins lourd qu’il n’y paraît. Le diff de locale est court, il se lit en langage courant et « ce bouton doit-il afficher “Log in now” ? » est une décision que quelqu’un peut prendre en cinq secondes — à condition que la question lui soit effectivement posée. C’est tout le rôle de cette étape de validation.
Que faire si l’agent a déjà réécrit vos textes ?
Restaurez d’abord la formulation approuvée, puis associez les chaînes à des clés afin que la prochaine reformulation apparaisse dans un diff.
Pour trouver les dégâts, il faut passer par Git : comparez les composants concernés au dernier commit auquel vous faites confiance et ne lisez que les chaînes littérales. Restaurez ce qui a changé. Si vous ne pouvez pas affirmer avec certitude quelle formulation était approuvée — le bouton affiche « Log in », la page marketing « Sign in » et aucun artefact n’indique laquelle est correcte — vous avez atteint l’état plus profond auquel cet incident contribue. Le copy drift désigne la divergence des textes visibles par l’utilisateur par rapport à leur source approuvée, d’une surface et d’une période à l’autre, sans source de vérité unique. Ce terme, ses causes et ses conséquences en aval font l’objet d’un article dédié : qu’est-ce que le copy drift ?.
Associez ensuite à des clés les textes que vous avez restaurés, en commençant par les chaînes touchées par l’agent. Ce sont manifestement celles qui se trouvent dans la ligne de mire.
Quel est le rôle de la localisation ?
Une reformulation silencieuse est une gêne dans une langue et un risque dans dix : chaque traduction dérivée de l’ancienne formulation est désormais obsolète, et rien ne vous indique qu’il faut la retraduire.
C’est là que l’incident lié à l’agent s’aggrave. Vous livrez la nouvelle formulation anglaise, tandis que vos versions allemande, japonaise et espagnole conservent l’ancienne : les surfaces divergent désormais entre les langues, et plus seulement entre les écrans. Les catalogues qui ne sont plus synchronisés constituent un mode d’échec à part entière, avec ses propres correctifs, traité dans fichiers de traduction désynchronisés.
Les textes associés à des clés ferment aussi cette boucle, car une chaîne source modifiée devient un événement visible auquel ses traductions sont rattachées, plutôt qu’un littéral qui change discrètement. C’est le principe de conception de globalize.now : les textes vivent dans le codebase, sont centralisés derrière des clés et la localisation relève de la même opération, pas d’un ajout après coup. Connectez un repo et chaque langue est dérivée de cette source unique. Les équipes qui livrent avec des outils de coding IA rencontrent ce problème en premier. C’est pourquoi nous écrivons pour les développeurs et pour les personnes qui construisent avec des outils d’IA.
La prochaine fois qu’un agent intervient dans vos composants, la question sera de savoir si un libellé modifié apparaît comme une décision ou comme une surprise. globalize.now centralise vos chaînes visibles par l’utilisateur dans le repo que vous connectez et dérive chaque langue de ce texte source unique.
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