82 pages .de.md et 82 .en.md. La volumétrie explique la forme : 95 % du volume
est GÉNÉRÉ (74 013 mots contre 3 506 rédigés, mesuré sur 107 pages). On ne
traduisait donc pas 77 500 mots — on traduit la prose, et le vocabulaire des
générateurs fait le reste.
Prose traduite à la main : accueil, installation (4), fonctionnalités (4),
tarifs, référence, dépannage, FAQ, intégrations (2), api/index, et les 6 fiches
appareils qui portent une vraie prose + compatibilite.md. Les blocs générés ne
sont JAMAIS recopiés dans une traduction : le script de pose les reprend
verbatim du fichier français via des placeholders, pour qu'une traduction ne
puisse pas désynchroniser un bloc généré.
Les 49 fiches légères n'ont rien à traduire : leur tagline vient d'amont et est
DÉJÀ en anglais (« Connects nymea to powerfox. »). Constat, pas renoncement.
Localisation ≠ traduction, et tarifs.md en 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. Aucune des deux n'est
annoncée comme livrée : les deux sont Roadmap, et les pages le disent.
Référence régénérée : en-têtes et libellés en anglais (Field/Type/Notes,
Supported models, Settings, Read-only, Brand / Model…). Conséquence assumée et
signalée : les pages FR en ligne changent aussi.
README.md (il était VIDE) : la règle du 95 % généré en tête, la frontière de
décision du multi-langue avec sa mesure, pourquoi SCAFFOLD existe, et le piège
--strict/navigation.instant.
TODO : multi-langue clos, et trois points ouverts qui en restent — les tagline
vivent dans les meta.json de 5 autres dépôts (forme à ajouter : tagline_de /
tagline_en), les stubs traduits restent des stubs (la dette éditoriale a triplé
en surface, pas en fond), api/types.md n'a pas besoin de variante. Le P1
openmeteo est corrigé sur constat : il EST dans PORTING_STATUS depuis, et une
ligne commentée contradictoire y traîne encore.
Vérifié : mkdocs build --strict vert sur les trois langues, 322 pages HTML,
contenu traduit relu dans le HTML produit, et les deux générateurs rendent
« Doc à jour » en --check.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
mkdocs.yml : plugin i18n en mode `suffix` (page.md = fr, page.de.md,
page.en.md), fr par défaut, fallback_to_default, un index de recherche par
langue, et nav_translations — 29 éléments traduits par langue, vérifié au
build. i18n est placé AVANT literate-nav et search : il réécrit l'arbre des
fichiers par langue, les autres doivent voir l'arbre déjà localisé.
navigation.instant est RETIRÉ de theme.features. Ce n'est pas un arbitrage de
confort : il est incompatible avec le lien contextuel du sélecteur de langue de
mkdocs-static-i18n, et l'avertissement fait ÉCHOUER `mkdocs build --strict`.
Le sélecteur l'emporte — atterrir sur la même page dans l'autre langue vaut
plus qu'un chargement instantané qui renvoie à l'accueil. Le commentaire est à
l'endroit du retrait, pas ailleurs.
Vérifié que literate-nav et static-i18n cohabitent, plutôt que supposé : build
construit, nav de /de/ relue dans le HTML produit (« Geräte », « Kompatibilität »,
liste des appareils bien dépliée).
CI : --locales fr,de,en sur la génération ET sur le contrôle d'idempotence. Et
une étape qui manquait — gen_api_reference.py n'était pas dans le pipeline, sa
sortie ne se propageait donc qu'au prochain passage manuel.
requirements.txt : mkdocs-static-i18n >= 1.3.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Le vocabulaire des deux générateurs sortait en français en dur. Il est
externalisé dans un dict REF par générateur, EN ANGLAIS, un seul jeu servi
identique dans les trois langues.
La raison est une MESURE, pas un goût : nymea ne sert aucune traduction sur
notre parc. Vérifié le 2026-10-12 sur le banc `hems` —
Integrations.GetThingClasses rend la MÊME charge utile au sha256 près
(243 340 octets) pour fr_FR, de_DE et en_US, et nymea-plugins-translations
n'est pas installé. 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. Côté API c'est plus direct
encore : méthodes, champs, types et `description` viennent d'introspect.json.
Ce qui se TRADUIT, en revanche, c'est la prose et la navigation. D'où deux
ajouts côté fiches appareils :
- SCAFFOLD : l'échafaudage de prose par langue (titre de catégorie, intertitre
« Référence », mention de guide à venir). Sans lui, chaque nouvel appareil
créerait sa fiche dans la seule langue par défaut, et fallback_to_default la
servirait EN FRANÇAIS sous /de/ et /en/ — par le simple fait d'être créée.
- CATEGORY_LABELS_I18N pour les titres de pages d'index de catégorie.
`--lang` existait depuis la CI mais n'était PAS lu (« Conservé pour
compatibilité CI »). Il désigne maintenant la langue par défaut — celle dont
les fichiers n'ont pas de suffixe — et `--locales` dit lesquelles générer.
ensure_device_pages() et ensure_category_indexes() bouclent dessus, et ne
réécrivent JAMAIS une page existante : la prose traduite à la main survit aux
régénérations. Vérifié : second passage, « Doc à jour », 0 page régénérée.
Les messages de diagnostic (ERREUR, AVERTISSEMENT, BLOQUANT) restent en
français : ils s'adressent au mainteneur, pas au site.
Note de méthode : la validation par ast.parse() ne voit pas une erreur de table
des symboles (`global` après usage). compile() la voit.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Correction 2 — canal par défaut selon l'origine :
- Tous les plugins origin:nymea passent de nightly→stable (upstream redistribué tel quel)
- Plugins ETM conservent leur canal manuel (eastron=stable, abb-b2x/abb-terra/waveshare=testing)
- Ajout name: sur 5 plugins sans meta.json (v2xeamberelectric→Amber Electric, mtec,
genericenergy, genericheatingcooling, genericcar)
Correction 3 — Keba fork ETM :
- repo: etm-powersync-plugins (fork corrigé KC-P30), channel: testing, origin: etm
- La version nymea-plugins (bugguée) n'est plus déclarée → écartée de facto
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- render_header_badges() fonction partagée pour canal + origine + stabilité
- Marqueur __badges_<plugin>__ dans les 9 fiches (5 manuelles + 4 auto)
- Marqueur __index_<cat>__ dans les 3 index manuels (compteurs, bornes, smart)
- Front matter hide: [toc] sur tous les index de catégorie
- .badge.experimental ajouté dans extra.css (waveshare)
- process() gère __badges_ keys via render_header_badges()
- build_category_index_page() inclut hide:toc front matter
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Le générateur crée désormais entièrement les fiches inexistantes (titre,
badges, section générée BEGIN/END) et les index.md de catégorie manquants.
Les fiches rédigées manuellement (eastron, abb-b2x, abb-terra, keba,
waveshare) sont préservées — seul le contenu entre marqueurs est réinjecté.
- gen_device_reference.py : ensure_device_pages() + ensure_category_indexes()
créent les .md manquants dans le format exact de process() → idempotent
- render_matrix() : inclut tous les canaux (nightly sans JSON = ligne stub)
- generate_summary() : intègre check mode + inclut toutes les entrées nav
- process()/repl() : gère __index_<cat>__ et nightly sans JSON → stub
- load_plugins/load_meta : logging explicite pour JSON vides ou non parsables
- Supprimer stubs manuels hvac/{daikinairco,sgready,simpleheatpump,index}.md
et onduleurs/{fronius,index}.md → remplacés par fichiers générés
- 9 appareils dans la matrice et dans le SUMMARY.md, mkdocs build --strict OK
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>