Vérifier la Skill L10n
Objectif
Déterminer les deux façons dont la localisation se brise 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 codé en dur et visible par l'utilisateur. Des chaînes rendues directement à l'utilisateur à partir du code widget sans passer par
context.l10n.<key>. Elles ne figurent 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/.
Workflow
Étape 1 : Déterminer le scope
Si l'utilisateur a passé des chemins après /check-l10n, scannez ceux-ci. Sinon, scannez l'union des changements indexés + non indexés par rapport à l'arbre de travail.
# Par défaut : fichiers modifiés
git -C mobile status --porcelain | awk '{print $NF}' | grep '\.dart$'
# Ou explicite : chemins en argument
Étape 2 : Exécuter le test de cohérence des arb (s'il est présent)
cd mobile && flutter test test/l10n/arb_consistency_test.dart
Ce test compare chaque fichier .arb par rapport à app_en.arb et échoue si une locale manque des clés qui ne sont pas sur la liste d'autorisation explicite _knownUntranslatedDebt.
Si le fichier test n'existe pas sur cette branche, passez 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 des arb n'a pas été vérifiée.
Fallback en ligne quand le test n'existe pas
Si mobile/test/l10n/arb_consistency_test.dart est manquant, effectuez 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 : Scanner pour l'anglais codé en dur dans les fichiers modifiés
python3 .agents/skills/check-l10n/scan_strings.py
Ou avec des chemins explicites :
python3 .agents/skills/check-l10n/scan_strings.py mobile/lib/screens/auth/foo.dart
Le scanner émet une ligne par candidat, formatée comme <path>:<line>:<col> [<rule>] '<literal>'. Le code de sortie est 1 s'il y a des résultats, 0 sinon.
Le scanner inspecte uniquement 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 arbre test/ ou integration_test/. Il ignore également les lignes à l'intérieur de Log.*(), developer.log(), print(), assert(), throw <Type>Exception(), et les constantes de noms de routes — ces littéraux ne sont pas visibles par l'utilisateur.
Étape 4 : Reporter sous forme de checklist
Affichez 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 des énoncés suivants :
- ✅ 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 lève explicitement un résultat, en documentant pourquoi dans le rapport.
Corriger les résultats
Clés non traduites
Si la locale manquante figure parmi celles que nous envoyons aux 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 relecture. N'étendez pas silencieusement l'ensemble de la dette — il devrait toujours être examinable comme « la liste des trucs qui ne sont pas traduits, à dessein ».
Anglais codé en dur
Chaque résultat 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 viacontext.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 par l'utilisateur. Si le littéral n'atteint vraiment pas l'utilisateur (par exemple, un widget debug-only, un drapeau developer-only, un identifiant de test sémantique), considérez si le scanner a besoin d'une 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 la peine de la maintenance regex.
- Lever avec raison. Les chaînes de marque qui NE DOIVENT JAMAIS être traduites (« OpenVine », « Divine ») sont un anglais codé en dur légitime. Notez la levée dans la description de la PR plutôt que de réduire au silence le scanner — les futurs lecteurs doivent pouvoir voir pourquoi ce résultat a été accepté.
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é de tout widget |
title-arg |
title: 'Foo' paramètre nommé de tout widget |
hintText-arg |
hintText: 'Foo' (champs de formulaire) |
helperText-arg |
helperText: 'Foo' (champs de formulaire) |
tooltip-arg / Tooltip-message |
texte de tooltip |
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 regex et manquera le code fortement templaterisé (constructeurs de chaînes,
.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. Levez-les dans la description de la PR plutôt que d'essayer de les réduire au silence dans le scanner.
- Le scanner ne signale que les chaînes commençant par une lettre majuscule et contenant un espace — le texte d'interface utilisateur purement minuscule ou composé d'un seul mot (« ok », « submit ») ne sera pas attrapé. C'est un choix délibéré pour le rapport signal-bruit ; un examen manuel reste nécessaire pour les étiquettes courtes.