You don't need to do any of this yourself. If you'd like the integration translated into your language, just open an issue asking for it — Claude can do the translation. The notes below are for maintainers (and anyone who'd rather submit a PR directly).
Cover Time Based has strings in two places, each with its own file layout. Both surfaces need updating when you add a new language or a new translatable string.
| Surface | Files | Used for |
|---|---|---|
| Home Assistant backend | strings.json and translations/<lang>.json |
Config-flow titles and fields, Repairs issues, service descriptions — anything Home Assistant's own UI renders for us. |
| Lovelace configuration card | frontend/cover-time-based-card.js — the EN object near the top, and the TRANSLATIONS = { en: EN, pt: {...}, pl: {...} } block below it |
Every string the card itself draws. |
Currently supported languages: English (en), Portuguese (pt), Polish (pl).
Say you want to add German (de).
- Copy
custom_components/cover_time_based/translations/en.jsontocustom_components/cover_time_based/translations/de.json. - Translate every value in
de.json. Keep the keys exactly the same — Home Assistant looks them up by name.
Don't touch strings.json — it stays in English and is the developer source of truth.
In custom_components/cover_time_based/frontend/cover-time-based-card.js:
-
Find the
TRANSLATIONSobject:const TRANSLATIONS = { en: EN, pt: { ... }, pl: { ... }, };
-
Add a
de:entry mirroring the existingpt:andpl:blocks. Use theENobject above it as the master list of keys: copy every key across and translate its value.
The card falls back to English for any key you miss, so a partial translation will work — but the audit below will flag what's missing.
When you add a new feature that introduces a new user-facing string:
- Add the key + English value to
strings.json, under the appropriate top-level section (config,issues, orservices). - Mirror the same addition into
translations/en.json. The two files have the same shape;strings.jsonis what ships,translations/en.jsonis what Home Assistant actually reads. - Add the same key with a translated value to every other
translations/<lang>.jsonfile (pt.json,pl.json, …).
- Add the key + English value to the
ENobject near the top ofcover-time-based-card.js. - Add the same key with a translated value to every other language block inside the
TRANSLATIONSobject (pt,pl, …). - Render the string with
this._t("your.key"). It readshass.languageand falls back to English if a key or language is missing.
For substitutions, use {name} placeholders and pass replacements as the second argument:
this._t("hints.movement", { seconds: 3 });
// EN: "Click after about {seconds} seconds."Run this from the repo root. It lists missing and extra keys for every non-English language across both surfaces:
python - <<'PY'
import json, re
from pathlib import Path
base = Path("custom_components/cover_time_based")
def all_keys(d, prefix=""):
out = []
for k, v in d.items():
full = f"{prefix}.{k}" if prefix else k
if isinstance(v, dict): out += all_keys(v, full)
else: out.append(full)
return set(out)
en = json.loads((base / "translations/en.json").read_text())
en_keys = all_keys(en)
for path in sorted((base / "translations").glob("*.json")):
if path.stem == "en": continue
other = all_keys(json.loads(path.read_text()))
print(f"backend {path.stem}: missing={sorted(en_keys - other) or 'none'} extra={sorted(other - en_keys) or 'none'}")
card = (base / "frontend/cover-time-based-card.js").read_text()
def block(text, start):
m = re.search(start, text)
if not m: return ""
s, depth = m.end() - 1, 0
for i in range(s, len(text)):
if text[i] == '{': depth += 1
elif text[i] == '}':
depth -= 1
if depth == 0: return text[s:i+1]
return ""
def keys_of(t): return set(re.findall(r'"([^"]+)":\s*"', t))
en_card = keys_of(block(card, r"const EN\s*=\s*\{"))
trans = block(card, r"const TRANSLATIONS\s*=\s*\{")
for lang in re.findall(r"^\s{2}(\w+):\s*\{", trans, re.M):
if lang == "en": continue
other = keys_of(block(trans, rf"{lang}:\s*\{{"))
print(f"card {lang}: missing={sorted(en_card - other) or 'none'} extra={sorted(other - en_card) or 'none'}")
PYBoth surfaces should report missing=none extra=none for every language.