Translations
People see a widget in the language they use Tiledash in. You write it in one language, and add translations for the others: the app’s eight, English, Nederlands, Deutsch, Español, Français, Norsk bokmål, Svenska and Dansk. Open the Translations tab in the editor, or have your AI assistant do it.
Texts in a template
A text you want translated becomes a message with a key, and the template asks for it with the t filter:
<p>{{ 'battery_low' | t: level: cap.measure_battery.value }}</p>The message is what you wrote, with a place for each value:
Battery at {level, number}%In the editor, select a text in the template and choose Make translatable: it becomes a message, and each plain {{ value }} in the selection becomes a {name} in it.
Or write the t yourself: until its key has a text, the editor underlines the key and the widget shows the key itself. Hover it and choose Add to translations to give it a row in the Translations tab.
In an expression, use t() with each value as a name and a value:
{{= t('battery_low', 'level', cap.measure_battery.value) }}{% let line = t('hello', 'name', device.name) %}To keep a text for later, assign it like any other value:
{% assign line = 'battery_low' | t: level: cap.measure_battery.value %}{% let line = t('battery_low', 'level', cap.measure_battery.value) %}{% capture line %}{{ 'battery_low' | t: level: 82 }} · {{ device.name }}{% endcapture %}t works in templates, not in handlers. To put a text in a handler, write it there from the template: data-hd-on-click="toast({{ 'saved' | t | quote }})".
The key must be written out: {{ name | t }} can’t be checked or translated, so a save refuses it. A key with no text yet only gets a warning: you can save, and it shows as its key until it has one. To choose between texts, put a t in each branch:
{% if charging %}{% assign line = 'charging' | t %}{% else %}{% assign line = 'idle' | t %}{% endif %}Pass a value, not a whole capability: cap.measure_battery.value or .text, not cap.measure_battery.
A tag in a message, like <b>, is left out. Put markup in the template, around the t.
The Translations tab
The tab is a table: a row per text, and a column per language, the one you wrote it in first (marked Original). Click a cell to type in it, the key too: changing a key renames it in the template as well.
- Add a text adds a row at the bottom. Type its key, then its text in any language: it doesn’t have to start in the original one.
- Add a language adds a column. The cross on a column takes that language away.
- Note for translators, the last column, says what a translator can’t see, like where a text shows and how much room it has. Viewers never see it.
- The bin at the end of a row deletes that text in every language.
- To do shows only the rows that still need something: an empty cell, a changed text or one that doesn’t fit.
The name, description and settings are rows too. Their own text is changed where it is set, on the Settings tab or under Details; the table only translates them.
A key no t reads gets a warning under the editor; click it to find its row.
What a message can say
| In a message | What it does |
|---|---|
{name} | The value as it is |
{level, number} | A number, written the way the viewer’s region does |
{count, plural, one {# light} other {# lights}} | The form for the number, with # for the number itself |
{count, plural, =0 {None} one {# light} other {…}} | =0 for exactly that number |
{mode, select, eco {Saving} other {Normal}} | A text per value, always with other |
Each language has its own plural forms: Dutch, German, English, Norwegian, Swedish and Danish use one and other; Spanish and French can add many, for whole millions.
A translation keeps the values and =N forms of the text it translates.
The name and settings
A widget’s name, description and settings are translated without a message, by their own key. The Translations tab lists them. An assistant writes them as:
| Key | What it translates |
|---|---|
name, description | The widget’s name and line in the picker |
setting.<name>.label, .help, .placeholder, .unit | A setting’s texts |
setting.<name>.option.<value> | One choice of a choice setting |
setting.<name>.spot.<value> | One place of a values setting |
param.<name>.label, .help, .unit (a partial) | A parameter’s texts |
style.<name>.label, .section, .hint, .option.<value> | A style variable’s texts |
tip.<name>.title, .text | One of its tips |
A unit that goes in the style, like px, isn’t translated: it is code. A text setting’s default stays as you wrote it: it is a value, not a label.
Which language shows
A viewer gets their own language if the widget has it, else English if it has that, else the language you wrote it in. A text of the template that isn’t in any of those shows in any language that has it, else as its key.
The filters on_off, relative_time and duration follow the viewer’s language by themselves, and so do cap.*.title, cap.*.units and a capability’s choices, unless the device’s driver or its owner chose their own. So do the words inside hd-* components, and the price words prices.now.word, prices.today.label, a window’s when and prices.pending: compare prices.now.level in a template, never the word.
If you change a text after translating it, the translation still shows, and is marked Text changed until you change it or choose Still right.
While you type {{ ' or t( in the template, the editor suggests the keys of the widget’s texts, each with its text, and after | t: or the key in t(…) the values that text takes.
Show in above the editor’s preview draws the widget in one of its languages, without changing the app’s. Only the widget’s texts and the words of its filters follow; the template’s locale stays yours.
Partials
A partial has its own messages: its t never reads the widget’s, and a widget’s never reads a partial’s.
To show a widget’s text in a partial, pass it as a parameter. A render takes no filters, so assign it first:
{% assign title = 'charging' | t %}{% render 'device-head', title: title %}With an AI assistant
create_widget, save_widget and save_partial take i18n, and get_widget and get_partial return it:
{ "source": "en", "messages": { "battery_low": { "text": "Battery at {level, number}%", "note": "Under the icon. Short." } }, "translations": { "nl": { "battery_low": "Batterij op {level, number}%", "name": "Accu", "setting.charger.label": "Lader" } }}- Write a translation as its text alone: Tiledash notes which text it was made from. One read back has
{ text, source }; send it back as it is, so one that went stale stays marked until it is translated again. - Send the whole block. Leaving out
i18nkeeps what the widget has;nulltakes it all away. translationsToDoin the answer says, for every language Tiledash speaks but the one the widget is written in, what ismissingandstale: a language the widget has nothing in yet lists all its texts.check_widgetlists what doesn’t fit.- The languages are
en,nl,de,esandfr; the server’s instructions name them too, so an assistant can offer to translate into each. render_widgetandrender_partialtakelanguage, to see the result in one.- A
notetells a translator what they can’t see: where a text shows, and how much room it has. - A message’s own
textmay be""while it is only in other languages so far.
Publishing
Translations are part of the version you publish, so a new language or a corrected text is a new version, and a patch is enough: nothing changes for anyone who doesn’t read them. The Community lists the languages an item shows in. In a package folder they are files beside the template:
locales/messages.json { "source": "en", "messages": { "battery_low": { "text": "…" } } }locales/nl.json { "battery_low": "Batterij op {level, number}%", "name": "Accu" }In a published widget, name and description translate the listing’s title and summary, for the widget picker.
A bundled partial keeps its own under i18n in partials/<name>.json.