Translating the card¶
So you want the card to speak another language β to keep for yourself, or to
contribute back. A translation is data, not code: a JSON file of
key β string. You can drop one in, restart, and pick it from the header globe β
no rebuild. This page is about making one good; the machinery underneath is in
the i18n system reference.
Where strings come from¶
English (src/i18n/en.js) is the source of truth and the complete key list β
~2,090 keys. A locale is a subset: anything you leave out (or get wrong)
falls back to English, so a partial translation is perfectly valid and ships
fine. Start from the generated English reference β it carries a context comment
on every ambiguous key, so you can translate without reading the source:
β Open en.reference.jsonc β click Raw (or β― β Download) to grab it, translate the values, and save as <code>.json.
(Repo path: custom_components/eufy_vacuum/frontend/locales/en.reference.jsonc. The
shipped locales β de / fr / es / nl / it / pt / ru.json in that same folder β are good models for structure.)
Authoring format¶
Files are authored nested β grouped by section, with a commons block for
words shared across sections β and flattened against the English manifest at load
time. A flat { "rooms.empty": "β¦" } file is the degenerate case and also works.
{
"commons": { "save": "Speichern", "cancel": "Abbrechen" },
"rooms": {
"empty": "Keine RΓ€ume",
"n_selected": { "one": "{count} ausgewΓ€hlt", "other": "{count} ausgewΓ€hlt" }
}
}
- Keep
{placeholders}exactly β same braces, same name. A dropped{count}is rejected for that key (it would render wrong); an extra one is a harmless warning. - Plurals are objects of your language's CLDR cardinal categories β exactly
what
Intl.PluralRules('<your-code>')produces (Russian needsone/few/many/other; most languagesone/other). Every form keeps the placeholders. - Don't translate product names ("Home Assistant"), memorial pet names (Mittens / your own pets β proper names), or the model-family codes. Generic animal-companion names (Cat/Dog/β¦) and the "Rainbow Bridge" idiom do translate to their established renderings.
Dropping one in¶
- Put
<code>.json(e.g.de.json,pt-BR.jsonβ the filename stem is the locale code) inconfig/eufy_vacuum/locales/on your HA instance. - Restart Home Assistant (the integration regenerates the served index at startup), then hard-refresh the dashboard (Ctrl+Shift+R).
- Pick it from the header globe. A drop-in is treated as custom and is reachable only by that explicit choice (it never auto-activates from the system language until it's native-reviewed).
What the intake gate allows¶
A dropped locale is untrusted β some of its values are rendered as markup β so it passes through a sanitize-or-quarantine gate before it loads. Knowing the rules keeps your file from being rejected:
- Allowed markup:
<strong>,<em>,<code>, and<a href="β¦">pointing atgithub.comorkingchddg901.github.io. That covers every place the card renders authored markup. - Inert junk is scrubbed, not fatal. A
<span>, a<div>, an off-site link β the file still loads, but those render as visible literal text (e.g. you'll see<span>in the UI). That's your cue to fix it; it isn't a security problem. - Active content quarantines the whole file. A
<script>/<iframe>, anonclick=/onerror=, ajavascript:link β any of these is treated as tampering and the entire file is rejected (the other ~1,900 keys share its source, so none are trusted). Translations never need any of this.
If a file is quarantined it's remembered by its content hash and skipped silently until you change it β fix the offending value, restart, and it re-evaluates fresh.
Getting it reviewed / contributed¶
A new AI-assisted or unreviewed translation ships as a draft β usable by
explicit pick, but it won't auto-activate. Once a native speaker has reviewed it,
promote it to stable (a one-line LOCALE_STATUS change) so it can follow the HA
system language automatically. draft and stable are the two review states a
shipped locale moves between; the
review-status taxonomy
defines exactly what each status controls (and how it differs from the intake
gate's safety outcomes).
The per-language native-review worklist lists the
specific judgment-calls flagged for each shipped locale
(de/fr/es/nl/it/pt/ru) β a native speaker's to-do list. Confirm or
correct an item there or in the
translate discussion.
To contribute a language upstream, open a PR adding <code>.json to the served
locales folder and its status to the locale tables in src/i18n/index.js. The
i18n reference covers the manifest, plural mechanism,
and the check:i18n gate your file is validated against.