Signalé sur /de/appareils/bornes/goecharger/ : la page était allemande sur deux
lignes et anglaise sur tout le reste. Le diagnostic a séparé deux choses que
j'avais mises dans le même sac le 2026-10-12.
LA LIGNE DE PARTAGE, écrite en tête du générateur :
• les LIBELLÉS QUE NOUS ÉCRIVONS (en-têtes de colonnes, titres de section,
oui/non, « Documentation fabricant ») → TRADUITS. Ils ne désignent rien à
l'écran, l'anglais n'y était qu'une friction. REF devient REF_I18N, trois
jeux de 20 libellés.
• le CONTENU VENANT DE NYMEA (noms de modèles, clés de registres, libellés de
champs comme « MAC address », unités) → VERBATIM, en anglais. Ce sont
exactement les chaînes que l'installateur lit DANS L'APP, qui tourne en
en_US. Les traduire rendrait la doc inutilisable pour retrouver un champ à
l'écran.
Et la pièce qui change l'expérience plus que la traduction elle-même : une note
`verbatim`, générée dans la langue de la page, en tête du bloc de référence.
Sans elle le mélange FR/EN passe pour une traduction bâclée ; avec elle c'est
une convention que le lecteur comprend.
L'injection devient sensible à la langue : `locale_of()` déduit la langue du
suffixe du fichier et `process()` rend le bloc en conséquence. Sans ça le même
bloc partait à l'identique dans les trois versions.
TAGLINES ET TITRES — i18n/devices.yaml, 52 entrées
Le tagline vivait en paragraphe libre, écrit UNE SEULE FOIS à la création de la
page : il restait donc en anglais sur les trois versions, y compris la
FRANÇAISE. Il entre dans le bloc badges, donc il est régénéré, donc traduisible
pour toujours. 52 taglines en fr et de ; l'anglais retombe sur l'amont.
Les traductions vivent dans le dépôt de DOC et pas dans les meta.json : trois
des cinq dépôts plugins sont des dépôts nymea amont que l'AGENTS.md interdit de
modifier. Repli sur l'amont pour toute entrée absente.
Deux titres s'affichaient en FRANÇAIS sous /de/ et /en/ — « Compteur ABB B2x »,
« Borne ABB Terra AC » : corrigés, dans la page et dans la nav.
Deux coquilles AMONT corrigées au passage, repérées en relisant les 57 entrées :
meta.json annonçait « Bestel » pour Vestel et « Easeex » pour Easee. Signalées
comme corrections, pas comme traductions, dans i18n/devices.yaml.
48 FICHES FR ÉTAIENT DANS UN ANCIEN FORMAT, ET MENTAIENT
Trouvé en vérifiant : 48 fiches FR n'avaient pas de marqueur `__badges_*__`.
Leurs badges étaient du HTML figé, écrit une fois — goecharger affichait
« NIGHTLY » alors que PORTING_STATUS dit `stable` depuis. Elles sont migrées au
format canonique : badges générés, donc exacts, plus titre localisé, tagline et
intertitre. Reste `hvac/homeconnect.md`, orpheline (plugin hors PORTING_STATUS).
NAV : UN CONSTAT QUI ÉVITE DE REFAIRE L'ESSAI
`literate-nav` ne lit que son `nav_file` : la substitution par suffixe de
mkdocs-static-i18n ne s'y applique PAS. Un `SUMMARY.de.md` généré n'était lu par
personne et la barre latérale allemande gardait les noms français. Testé, puis
retiré — et la raison est écrite dans le générateur pour que personne n'y
revienne. Les libellés de nav passent par `nav_translations` : 31 éléments
traduits par langue au lieu de 29.
RÉFÉRENCE API : LAISSÉE EN ANGLAIS, DÉLIBÉRÉMENT
Les 23 pages de l'API n'ont pas de variante par langue. Leur traduire les
libellés aurait fait voir du FRANÇAIS aux lecteurs /de/ et /en/ par le repli —
une régression. Créer 46 variantes pour traduire dix libellés aurait triplé
28 000 mots de contenu généré pour un gain nul : le reste de la page (noms de
méthodes, de champs, de types, descriptions) est l'API elle-même, et ce sont les
chaînes qu'on écrit dans ses requêtes. À la place, une note explique dans les
trois langues POURQUOI cette référence est en anglais.
Vérifié : build --strict sans avertissement, les deux générateurs rendent « Doc à
jour » en --check, et la fiche goecharger relue dans les trois langues.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
powersync-docs
Documentation ETM PowerSync — docs.etm-powersync.fr
MkDocs Material, docs-as-code, trois langues : fr (défaut) · de · en.
Licence : CC-BY-4.0 pour la documentation, GPL-3.0 pour les exemples de code.
La règle qui explique tout le reste
95 % du volume est GÉNÉRÉ, pas rédigé. Mesure du 2026-10-12 : 74 013 mots générés contre 3 506 mots de prose, sur 107 pages. Tout ce qui est entre
<!-- BEGIN GENERATED: <clé> --> … <!-- END GENERATED -->
est produit par un script depuis une source de vérité, et écrasé à chaque passage. Éditer à l'intérieur de ces marqueurs, c'est écrire quelque chose qui disparaîtra.
| Générateur | Source de vérité | Produit |
|---|---|---|
scripts/gen_device_reference.py |
PORTING_STATUS.yaml + les integrationplugin*.json et meta.json des 5 dépôts de plugins |
fiches appareils, index de catégorie, matrice de compatibilité, appareils/SUMMARY.md |
scripts/gen_api_reference.py |
introspect.json (introspection nymea) |
référence JSON-RPC (23 pages), api/SUMMARY.md |
Multi-langue — où passe la frontière
Deux régimes, et ils ne se traitent pas pareil :
- Le bloc de référence est en ANGLAIS, une seule fois, servi identique dans les trois
langues. Décision du 2026-10-12. La raison est une mesure, pas un goût : nymea ne sert
aucune traduction sur notre parc — vérifié sur le banc
hems, oùIntegrations.GetThingClassesrend la même charge utile au sha256 près (243 340 octets) pourfr_FR,de_DEeten_US. Les noms de modèles, de registres et d'unités sont donc déjà en anglais ; traduire les seuls en-têtes de colonnes autour aurait fabriqué des pages bilingues sans rien gagner. Le vocabulaire vit dans le dictREFde chaque générateur. - La prose et la navigation se TRADUISENT. Fichiers suffixés (
page.md= fr,page.de.md,page.en.md), libellés de nav parnav_translationsdansmkdocs.yml, échafaudage des pages créées par le dictSCAFFOLDdu générateur appareils.
Pourquoi
SCAFFOLDexiste. Sans lui, chaque nouvel appareil ajouté créerait sa fiche dans la seule langue par défaut, etfallback_to_defaultla servirait en français sous/de/et/en/. Le générateur crée donc les trois variantes — et ne réécrit jamais une page existante, pour que la prose traduite à la main survive aux régénérations.
Localisation ≠ traduction
docs/tarifs.md est l'exemple à connaître : la version FR parle de Tempo et du signal RTE,
la DE de §14a EnWG et de SMARD, l'EN des deux. Ce ne sont pas des traductions l'une de
l'autre, et c'est volontaire — les signaux tarifaires sont nationaux. Même traitement pour la
ligne correspondante de docs/fonctionnalites/index.md.
Travailler dessus
python3 -m venv .venv && . .venv/bin/activate
pip install -r requirements.txt
# Prévisualiser (les trois langues sont construites ensemble)
mkdocs serve
# Régénérer la référence appareils — exige les JSON des 5 dépôts de plugins
# dans .plugins-src/ (la CI les clone ; en local, copiez-les depuis le workspace)
python3 scripts/gen_device_reference.py --src .plugins-src --docs docs \
--lang fr --locales fr,de,en
# Régénérer la référence API
python3 scripts/gen_api_reference.py --src introspect.json --docs docs
# Ce que vérifie la CI : rien ne doit changer après régénération
mkdocs build --strict
python3 scripts/gen_device_reference.py --src .plugins-src --docs docs \
--lang fr --locales fr,de,en --check
⚠️ mkdocs build --strict échoue sur un simple avertissement. C'est pour cela que
navigation.instant est retiré de theme.features : il est incompatible avec le lien
contextuel du sélecteur de langue de mkdocs-static-i18n. Le commentaire est dans
mkdocs.yml, à l'endroit du retrait.
Déploiement
CI Gitea (.gitea/workflows/docs.yml) sur push vers main, plus un passage nocturne à 3 h
(les meta.json des dépôts de plugins bougent sans que ce dépôt-ci bouge). Pipeline :
clone des 5 dépôts de plugins → génération appareils → génération API → mkdocs build --strict → contrôle d'idempotence → rsync vers le serveur.
Le build produit site/ (fr), site/de/ et site/en/ en une passe ; le rsync --delete
pousse l'ensemble.