Words
A component has no words of its own. Every label, name and message it draws or says is your product's — given on the component, once for the whole product, or taken from the English word pack you import.
A component has no words of its own. No component has a default text in any language: not “Required”, not “Close”, not “OK”. A word a component draws or says is one your product gave: written by you, or taken from the English word pack, which you import.
A default hides the failure it causes. A product in Japanese that forgets one word shows English beside its own text, and nothing reports it. A product in English has its own voice too (“Mandatory”, “Done”, “Nothing found”). So a component has no word to fall back on, and it tells you when one is missing. The English word pack is not such a fallback: nothing applies it but your own import.
A component looks in three places, in this order, and nowhere else.
| Place | What it gives | Example |
|---|---|---|
| The component itself | Words about this one thing, and any word this one says differently | label, error-text; close-label="Close filters" |
| Your product’s words | Words you say the same way wherever the part is drawn, given once per language, or the English word pack you import | requiredText, closeLabel, previousMonthLabel |
| The platform | What the browser already has in the reader’s language | Month and weekday names, digits, “AM”, the browser’s own message for a missing value |
After these there is nothing: no fallback language, and no English “for now”.
Tanrei writes one set of your product’s words for you: a word in plain, neutral English for every key, such as “Required”, “Close” and “Previous month”. Import it once:
import '@tanrei/web/words/en';It gives the words of the whole page, as one call of setWords() would. Nothing imports it for you: not @tanrei/web, not @tanrei/react, not a component, and not the lang of the page. The pack’s word for each key is in the table of keys.
- Your own words still win. Call
setWords()after the import, and the keys you give replace the pack’s; a word on a component wins over both. The import sets every key when it runs, so a word you gave before it is replaced. - Start from its words. The module exports them too:
import { englishWords } from '@tanrei/web/words/en'. - In a product in another language, a key that your language’s set does not have is read from the page’s set — with the pack imported, that is English. Import it there only where English is the word you want for a key you did not write; without it, the missing word is reported instead.
- It hides nothing it should not. With the pack imported, every key has a word, so
missingWords()is empty. To find the words your product has not written itself, ask it in a test that does not import the pack.
In React, the import is the same line, in the module that renders first.
On the web, call setWords() once, before or after the components load. Elements already on the page draw the words at once.
import { setWords } from '@tanrei/web';
setWords({
requiredText: 'Required',
optionalText: 'Optional',
closeLabel: 'Close',
clearLabel: 'Clear',
targetLabel: 'opens in a new tab',
});Give only the words of the components you use. A second call adds to the first, key by key; a key given as an empty string is taken away. @tanrei/web/words exports the same function without defining any element, and @tanrei/react exports it too.
An attribute on one component always wins over your product’s words:
The word on the component itself: one field says it differently
<tanrei-text-field label="Email" required required-text="Required"></tanrei-text-field>
<tanrei-text-field label="Company" required required-text="Needed for invoices"></tanrei-text-field>Give one set per language. A component reads the set of the nearest lang attribute around it, so a page in Japanese, or a Japanese part of an English page, reads the Japanese words.
setWords({ requiredText: 'Required', closeLabel: 'Close' }, { lang: 'en' });
setWords({ requiredText: '必須', closeLabel: '閉じる' }, { lang: 'ja' });- A set for
jaalso answersja-JP, unless you giveja-JPa set of its own. - A key that the language’s set does not have is read from the set given without
lang. - The words are plain strings. If your product has a localization library, hand it the strings from there:
setWords()is not a translation system, and has no plural rules. A few words have places a component fills, and nothing else is read in a word.
Some sentences hold a value that only the component knows, at the moment it says them: where a row stands after a move, or how many entries a paste made. Your product still writes the whole sentence. It marks where the value goes with a name between braces, and the component puts the value there.
setWords({ movedText: '{headline}: {position} of {count}' }, { lang: 'en' });
setWords({ movedText: '{headline}:{count} 件中 {position} 番目' }, { lang: 'ja' });The order of the words is your language’s. The component fills the places and does nothing else to the sentence.
- The component decides the names. Each key has its own places, listed in the table of keys, and only those are filled. You may write a place once, several times, or not at all.
- A number is written in the language of the element.
{count}and{position}are whole numbers, written with the digits and the grouping of the nearestlang. - A text keeps its own direction.
{headline}and{entry}are texts the component holds: a row’s headline, an entry a person typed. They are put in as plain text, never as markup. Where the sentence is drawn or announced, the text is isolated, so a Latin name inside an Arabic sentence stays in its own order. - To write a brace, write it twice.
{{is drawn as{and}}as}. A word without places is not read at all, so there a brace is written once. - One place is needed.
entryRemoveLabelnames the remove button of each entry of a chip field. Without{entry}every button has the same name.
In development the console reports a place the key does not have ({entri}), a single brace and the missing {entry}. The sentence is still used exactly as you wrote it. setWords() reports them when you give the word, and the component reports them when it uses a word given on the element.
The component has no grammar: no plural forms, no ordinals and no choice between two sentences. You have three ways to deal with a number, and none needs anything from Tanrei.
- Write a sentence that is right for every number:
{position} of {count},Added: {count}. - Give the word on the component, and change it when the count changes. Your product knows the count, and a word on the component always wins. Your own plural rules choose the sentence.
- Where your product holds the numbers itself, the word has no places: a table’s status (“1–20 of 132”) is one whole string that you write.
A missing word is a fault of the product, as a text field without a label is. The component makes it small for the person and visible to you.
- Words that would be drawn are not drawn. Where a mark says the same thing, the mark stands in their place: an asterisk for the requirement tag of a required field, the close and check icons for a picker’s Cancel and OK,
+1for “next day”. Where no mark does, nothing is drawn and the place keeps its size. - A control that needs a name is still drawn and still works. A calendar’s navigation buttons, a text field’s resize handle and a picker’s actions keep working without a name. The one exception is a number field’s steppers: an unnamed one is hidden from assistive technology, because the spin button beside it steps by itself.
- It is reported. In development the console names the attribute, once per word and page:
<tanrei-field>: required-text is empty and the product's words have no requiredText.
A control without an accessible name fails people who use a screen reader. The report is there so that you fix it, not so that you may leave it.
missingWords() returns the keys that were needed since the page loaded and that nobody gave. Ask it at the end of a test that walks through your product. A page that imports the English word pack misses none.
import { missingWords } from '@tanrei/web';
test('every word the product needs is given', async () => {
// …render the screens of the product…
expect(missingWords()).toEqual([]);
});A glyph, an asterisk, a dash, a digit and a separator the locale gives are not words, and a component still draws them. Nor are the names in the API (a prop, a value), or what the console tells a developer, which is English. The legends a keyboard shortcut draws (“Ctrl”, “Enter”, ⌘) are what the keys themselves carry; what is read aloud for a shortcut is the label you give it.
Every key, the props that read it, the places the component fills in its sentence, and its word in the English word pack. Most keys have the name of their prop. Three differ: a copy button’s label and failedLabel read copyLabel and copyFailedLabel, and the breadcrumbs’ overflowLabel reads breadcrumbsOverflowLabel.
Words that are about one thing — a field’s label, a date-time picker’s label, a remove button’s “Remove Ada Lovelace”, a status text with a count — have no key: give them on the component.
The rule is the same on every platform. Only the web is built so far; SwiftUI, Jetpack Compose, Flutter and React Native will take the product’s words, and the same English word pack, in their own idiom.