Vérifier la Skill L10n
Objectif
Détecter les deux façons dont la localisation se casse dans ce repo avant que les utilisateurs ne voient de l'anglais dans une build non-anglaise :
- Clés non traduites. Une clé ajoutée à
app_en.arbmais jamais traduite dans l'une des 16 locales non-anglaises (ar,am,bg,de,es,fr,id,it,ja,ko,nl,pl,pt,ro,sv,tr). - Anglais en dur visible à l'utilisateur. Des chaînes rendues directement à l'utilisateur depuis le code widget sans passer par
context.l10n.<key>. Elles n'apparaissent jamais dans les fichiers.arbcar elles n'ont jamais été extraites, donc aucune quantité de travail de traduction ne les corrige.
Exécutez ceci avant chaque push de PR qui touche mobile/lib/.
Flux de travail
Étape 1 : Déterminer le périmètre
Si l'utilisateur a passé des chemins après /check-l10n, analysez-les. Sinon, analysez l'union des changements en staging + unstaging par rapport à l'arborescence de travail.
# Par défaut : fichiers modifiés
git -C mobile status --porcelain | awk '{print $NF}' | grep '\.dart$'
# Ou explicite : chemins en arguments
Étape 2 : Exécuter le test de cohérence arb (s'il existe)
cd mobile && flutter test test/l10n/arb_consistency_test.dart
Ce test compare tous les fichiers .arb par rapport à app_en.arb et échoue si une locale manque des clés qui ne sont pas sur la liste d'exemption explicite _knownUntranslatedDebt.
Si le fichier de test n'existe pas sur cette branche, ignorez cette étape et fiez-vous entièrement à l'étape 3 plus la vérification en ligne ci-dessous. Notez dans le rapport que la cohérence arb n'a pas été vérifiée.
Repli en ligne quand le test n'existe pas
Si mobile/test/l10n/arb_consistency_test.dart est absent, faites la vérification équivalente manuellement :
cd mobile/lib/l10n
python3 - <<'PY'
import json, glob
en = json.load(open('app_en.arb'))
en_keys = {k for k in en if not k.startswith('@') and k != '@@locale'}
for f in sorted(glob.glob('app_*.arb')):
if f == 'app_en.arb':
continue
other = json.load(open(f))
other_keys = {k for k in other if not k.startswith('@') and k != '@@locale'}
missing = en_keys - other_keys
if missing:
print(f"{f}: {len(missing)} missing key(s)")
for k in sorted(missing)[:20]:
print(f" - {k}")
PY
Étape 3 : Analyser l'anglais en dur dans les fichiers modifiés
python3 .claude/skills/check-l10n/scan_strings.py
Ou avec des chemins explicites :
python3 .claude/skills/check-l10n/scan_strings.py mobile/lib/screens/auth/foo.dart
Le scanner émet une ligne par candidat, au format <path>:<line>:<col> [<rule>] '<literal>'. Le code de sortie est 1 quand il y a des trouvailles, 0 sinon.
Le scanner n'inspecte que les fichiers sous mobile/lib/. Il exclut les fichiers générés (*.g.dart, *.freezed.dart, *.mocks.dart), le répertoire l10n/, et tout arborescence test/ ou integration_test/. Il saute également les lignes à l'intérieur de Log.*(), developer.log(), print(), assert(), throw <Type>Exception(), et les constantes de noms de routes — ces littérales ne sont pas visibles par l'utilisateur.
Étape 4 : Signaler comme une checklist
Produisez une section par catégorie. Utilisez la sortie littérale des outils sous-jacents plutôt que de paraphraser — l'utilisateur doit pouvoir copier un chemin et sauter directement à la ligne.
## Localization check — <branch>
### 1. ARB consistency
- ✅ All 17 locales have every key in app_en.arb
(or)
- ❌ app_de.arb missing 7 keys: authConfirmPasswordLabel, ...
(or)
- ⚠️ arb_consistency_test.dart not present on this branch — used inline
fallback. Verify before merge.
### 2. Hardcoded English in changed UI files
- ✅ No likely user-visible English literals found.
(or)
- ❌ 5 candidate(s):
mobile/lib/screens/auth/login_options_screen.dart
L147:26 [Text-literal] 'Amber app is not installed'
L316:27 [label-arg] 'Sign in'
...
Terminez par l'un de :
- ✅ OK to push — les deux vérifications sont passées.
- ❌ Do not push — lister les actions requises.
- ⚠️ Push with caveat — seulement après que l'utilisateur ait explicitement renoncé à une trouvaille, en documentant pourquoi dans le rapport.
Corriger les trouvailles
Clés non traduites
Si la locale manquante en est une que nous livrons à des locuteurs natifs (l'utilisateur peut confirmer la liste de lancement actuelle), traduisez. Sinon, ajoutez la clé à l'ensemble _knownUntranslatedDebt dans mobile/test/l10n/arb_consistency_test.dart avec un commentaire nommant les locales qui ont encore besoin d'une passe. N'étendez pas silencieusement l'ensemble de dettes — il devrait toujours être révisable comme « la liste des trucs qui ne sont pas traduits, exprès ».
Anglais en dur
Chaque trouvaille a trois résolutions, dans l'ordre de préférence :
- Ajouter une clé l10n à
mobile/lib/l10n/app_en.arb, puis router le widget à traverscontext.l10n.<key>. Si la valeur existe déjà sous un nom légèrement différent, réutilisez-la au lieu de créer un doublon. - Marquer comme non visible pour l'utilisateur. Si le littéral ne rejoint vraiment pas l'utilisateur (par ex., un widget debug-only, un flag développeur-only, un identificateur de test sémantique), considérez si le scanner a besoin d'un motif de ligne de saut supplémentaire. Une règle de saut doit être justifiée par au moins 3 exemples distincts ; les cas isolés ne valent pas l'effort de maintenance regex.
- Renoncer avec raison. Les chaînes de marque qui NE DOIVENT JAMAIS être traduites ("OpenVine", "Divine") sont de l'anglais en dur légitime. Notez la renonciation dans la description de la PR plutôt que de réduire au silence le scanner — les futurs lecteurs doivent pouvoir voir pourquoi cette trouvaille a été acceptée.
Significations courantes des règles
| Règle | Détecte |
|---|---|
Text-literal |
Text('Foo') et const Text("Bar") |
AppBar-title-Text |
title: Text('Foo') (spécialisation de Text-literal) |
label-arg |
label: 'Foo' paramètre nommé à n'importe quel widget |
title-arg |
title: 'Foo' paramètre nommé à n'importe quel widget |
hintText-arg |
hintText: 'Foo' (champs de formulaire) |
helperText-arg |
helperText: 'Foo' (champs de formulaire) |
tooltip-arg / Tooltip-message |
texte d'infobulle |
semanticLabel-arg / semanticsLabel-arg |
étiquettes d'accessibilité |
user-message-call |
premier argument positionnel d'une méthode dont le nom inclut Error/Message/Snackbar/Toast/Dialog/Banner/Notification |
Limitations
- Le scanner est basé sur des expressions régulières et manquera le code fortement templé (string builders,
.padLeft(...), constructions'$prefix - $suffix'). Traitez une exécution propre comme « aucune fuite évidente », pas « toutes les fuites exclues ». - Les chaînes de marque ("Divine", "OpenVine", "Vine") déclencheront parfois l'heuristique de visibilité utilisateur. Renoncez-y dans la description de la PR plutôt que de tenter de les réduire au silence dans le scanner.
- Le scanner signale seulement les chaînes commençant par une majuscule et contenant un espace — les copies UI purement minuscules ou monosyllabiques ("ok", "submit") ne seront pas détectées. C'est un compromis délibéré pour le signal-bruit ; l'examen manuel reste nécessaire pour les étiquettes courtes.