Strings¶
Text shown on the dashboards is written in English where it is used, with a key next to it. A translation is a file that maps the same keys to another language. English needs no file, so reading the markup or the script shows the real words.
scripts/check-strings.py enforces what follows, and runs in CI.
Adding a string¶
In markup, key the element that holds the text, and nothing but the text:
<div class="prot-title" data-t="server.checkDaily">Check daily</div>
<button data-t="server.checkNow">Check now</button>
An attribute takes data-t- and its name, with the English in the attribute:
<div role="group" aria-label="Theme" data-t-aria-label="theme">
title, aria-label and placeholder are covered.
In the page's script, call t() with a literal key and literal English. Values
go in through named placeholders:
t('server.install', 'Install v{version}', { version: v })
Keys¶
Lowercase words joined by dots, camelCase within a word:
server.tvUpdates.block.desc. Start with the tab or page (server., adv.),
or common. for words used across it such as common.on.
One key, one English text. Where the same words appear in several places, such as a description on both dashboards, use the same key, so the check notices when one copy is edited and the others are not. Different wording needs a different key.
Rules¶
- Whole sentences. Never build a sentence from pieces such as
n + t('x', ' of ') + total. Other languages order words differently. Put the sentence in one string with placeholders:'{n} of {total} on'. - Never translate a value. Anything compared with what the TV reports, sent
to the server or the TV, or used as a state stays in English. Only what is
shown goes through
t(). Keep state in a variable ordataset, never read it back from displayed text. - Script-owned text is keyed in the script. An element whose text the script
sets gets no
data-t, and holds a placeholder such as—in the markup until the script fills it. - No local variable named
tin a page that loads/assets/i18n.js. It would hidet()in that function.
Messages from the server¶
Text the server sends a page - an error, a screen saver's name, an agreement's
description - is written with msg(), from server/lib/say.js:
error: msg('srv.update.busy', 'an update is already running')
error: msg('srv.saver.stageFailed', 'could not stage the screen saver: {error}', { error: e.message })
msg() returns the English, so the server, Home Assistant, MQTT and the log
all carry on in English. When a page is in another language it says so on its
requests, and the server swaps the messages in its answer for their
translations, from the same file the page uses. Only fields a page shows are
changed - error, label, detail and the rest of DISPLAY_FIELDS in
say.js - and only strings msg() made, so a raw value that reads the same as
a message is left alone.
Errors a page cannot cause, such as a malformed request, stay in English
without msg(). Names that come from the TV itself, such as its picture modes
and inputs, are shown as the TV gives them.
Converting part of a page¶
Key every piece of text in it, then add the element's id to CONVERTED in
scripts/check-strings.py. From then on, text added there without a key fails
the check.
Adding a language¶
./scripts/check-strings.py --template > server/assets/i18n/<lang>.jsonwrites every key with its English asfromand an emptytext.- Fill in
text, keeping each{placeholder}as it is. An entry left out, or left empty, shows in English. - Add the two-letter code to
LANGSinserver/assets/i18n.js, and the file toFILESinserver/deploy.sh.
The page follows the browser's language. ?lang=es picks one for testing.
Keeping a translation current¶
Each entry records the English it was translated from. When the English changes,
the entry is outdated: the page shows the new English, not a translation of
the old words, and ./scripts/check-strings.py lists it with the new English to
translate. Outdated entries never fail the check, so editing English is never
blocked on a translation. --missing lists strings a language has no entry for.