feat: add AI-generated translations for 9 languages - #79
Merged
Conversation
Adds German, French, Dutch, Spanish, Italian, Polish, Brazilian Portuguese and Czech translations, generated from translations/en.json using GitHub Models and validated automatically before being written. scripts/translate.py only sends keys that are missing or whose English source was reworded, so a run after adding a few strings costs a few strings' worth of tokens rather than the whole file. It records a fingerprint of both the English source and the translation it wrote in .github/translation-state.json, which lets it distinguish its own output from a manual correction: hand-edited translations are never overwritten, and are reported for review instead when the English behind them changes. scripts/verify_translations.py re-checks the files on disk as a CI gate. Placeholder mismatches, empty values and unknown keys fail the build. Translations use impersonal phrasing to stay neutral about the familiar/polite distinction (German du/Sie, French tu/vous), and a heuristic warns when one slips through. Also rewords the url_not_allowed message to drop "your", which was propagating a second-person possessive into every translation. Uses only the Python standard library, so requirements.txt is unchanged, and the built-in GITHUB_TOKEN with models: read, so there is no API key to manage and no billing.
Home Assistant select entities use their option values as translation keys, and those legitimately contain dots. The existing de.json on main has entity.select.subghzchannel.state with keys like "100 - 864.000 Mhz (Europe, etc)", which a dot separator silently mis-nests on the way back out. The current en.json happens to contain no such entity, so this never surfaced. Switch the separator to NUL, which cannot appear in a key that came from a real translation file, and add show() to render paths with dots for logs and PR summaries. Keys sent to the model are now plain indices rather than full paths. They no longer carry the separator, and dropping the path text from the response saves output tokens on every request.
Home Assistant treats pt and pt-BR as separate locales, and they differ in everyday interface vocabulary: 73 of the 181 strings came out differently (ligacao/conectar, encriptacao/criptografia, em progresso/em andamento). main already shipped a pt.json, so covering both avoids regressing European Portuguese users when the rewrite lands. Documents in the README that translations other than English are machine generated and that corrections are welcome, including the guarantee that a hand-corrected string is never overwritten. Contributors have little reason to submit a fix if they expect a bot to revert it. Also reports pending review items in the per-language status line, which previously read "up to date" even when a string was flagged because its English source changed under a manual correction.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Adds Czech, Dutch, French, German, Italian, Polish, Portuguese (European and Brazilian), and Spanish translations, plus the workflow that keeps them current.
Cost: nothing. Uses GitHub Models via the built-in
GITHUB_TOKENwithmodels: read. No API key, no secret, no billing. Runs on the Python standard library, sorequirements.txtis unchanged.How it avoids waste
A key is sent to a model only when it is missing, or when its English source was reworded. Everything else is left alone. A full backfill of 9 languages is 27 requests against a 150/day budget; a run after adding three strings is 9 tiny ones. Verified: re-running immediately after a full run makes zero API calls.
Manual corrections are permanent
.github/translation-state.jsonfingerprints both the English source and the translation the workflow wrote, so it can tell its own output from a human edit.Verified end to end on the real files, not just unit-tested.
Validation
scripts/verify_translations.pyruns as a workflow step and fails the build on placeholder mismatches, empty values, or unknown keys. It runs inside the translate workflow because PRs opened byGITHUB_TOKENdo not trigger other workflows, so hassfest will not run on the automated PR.Placeholders like
{name}are enforced exactly: a translation that drops or renames one is rejected and retried rather than committed. Moving a placeholder is allowed, since German word order requires it (Taste {number}).Register
Translations avoid the familiar/polite distinction (German du/Sie, French tu/vous) using impersonal phrasing:
{name} einrichten?, notMöchten Sie {name} einrichten?. A heuristic warns on slips.This surfaced one issue in the English source, which said "in your configuration.yaml" and propagated a second-person possessive into every language. Reworded here.
Notes for review
ptandpt-BRare both included: 73 of 181 strings differ (ligação/conectar,encriptação/criptografia).selectentities use their option values as translation keys, and the existingde.jsononmainhas"100 - 864.000 Mhz (Europe, etc)", which a dot separator mis-nests.feat/clean-portintomainwill need manual resolution. The merge base contains notranslations/directory, soen.json,de.json, andpl.jsonare add/add conflicts. Thede/pl/ptfiles onmaintranslate the superseded string set (177-226 keys that no longer exist) and should be deleted during that merge, orverify_translations.pywill fail on unknown keys.