Languages
Wish of the owner (22-10-2026): "We moeten alle Europese talen ondersteunen, inclusief Turks." ("We must support all European languages, including Turkish.") Phase 1 is described here: the translation system, all user texts as keys, a first translation in 34 languages, the typeface, the language choice and the design of the keyboard layouts.
The API of libs/i18n (Language, t, tv, tn, uppercase, format::date, …) and the translation keys are English (since 27-10-2026).
The languages
The 24 official languages of the EU, and Turkish, Ukrainian, Serbian (Latin and Cyrillic), Albanian, Macedonian, Bosnian, Norwegian (Bokmål), Icelandic and Catalan. Not (now): Russian, Belarusian.
| Code | Own name | English | Script | Status |
|---|---|---|---|---|
nl | Nederlands | Dutch | Latin | reviewed (development language) |
en | English | English | Latin | reviewed (fallback) |
bg | Български | Bulgarian | Cyrillic | draft |
bs | Bosanski | Bosnian | Latin | draft |
ca | Català | Catalan | Latin | draft |
cs | Čeština | Czech | Latin | draft |
da | Dansk | Danish | Latin | draft |
de | Deutsch | German | Latin | draft |
el | Ελληνικά | Greek | Greek | draft |
es | Español | Spanish | Latin | draft |
et | Eesti | Estonian | Latin | draft |
fi | Suomi | Finnish | Latin | draft |
fr | Français | French | Latin | draft |
ga | Gaeilge | Irish | Latin | draft |
hr | Hrvatski | Croatian | Latin | draft |
hu | Magyar | Hungarian | Latin | draft |
is | Íslenska | Icelandic | Latin | draft |
it | Italiano | Italian | Latin | draft |
lt | Lietuvių | Lithuanian | Latin | draft |
lv | Latviešu | Latvian | Latin | draft |
mk | Македонски | Macedonian | Cyrillic | draft |
mt | Malti | Maltese | Latin | draft |
nb | Norsk bokmål | Norwegian (Bokmål) | Latin | draft |
pl | Polski | Polish | Latin | draft |
pt | Português | Portuguese (Portugal) | Latin | draft |
ro | Română | Romanian | Latin | draft |
sk | Slovenčina | Slovak | Latin | draft |
sl | Slovenščina | Slovenian | Latin | draft |
sq | Shqip | Albanian | Latin | draft |
sr-Latn | Srpski | Serbian (latinica, default for sr) | Latin | draft |
sr-Cyrl | Српски | Serbian (ćirilica) | Cyrillic | draft |
sv | Svenska | Swedish | Latin | draft |
tr | Türkçe | Turkish | Latin | draft |
uk | Українська | Ukrainian | Cyrillic | draft |
All languages write from left to right (Language::direction; the type leaves room for a right-to-left language).
Draft ("concept") means: translated by Claude (phase 1), not yet reviewed by a native speaker. In Settings › Language the explanation is then followed by "(Deutsch: Entwurf)" and the contact sheet (make ui, build/ui/talen/talen.png) puts "(concept)" after the name. The doubts of the translators are under "Review".
How it works
libs/i18n (crate euzos-i18n, no_std + alloc, without float, no external crate):
Language: the 34 languages withcode()(BCP-47),name()(own name),script(),direction(),from_code()("no" →nb, "sr" →sr-Latn, a region is dropped),accept_language()andCHOICE_LIST(alphabetical by own name: first Latin script, then Greek, then Cyrillic).translations/<code>.txt: per languagekey = text, with# status: draftor# status: reviewed.nl.txtdetermines which keys exist. build.rs turns them into static tables: per language one long text and a table of u32 start offsets (u16 until 27-10-2026, when Greek passed 64 KB; no thousands of&strpointers with relocations in the kernel), and per key a constant:s::LOGOUT, and for a pluralm::ITEMS. A typo in a key does not compile; a key in a language that is not innl.txt, a duplicate key or an unknown plural form stops the build with file and line number.t(lang, s::X): the text; if it is missing in that language, the English, and otherwise the name of the key (visibly wrong, never empty).tv(lang, s::X, &[..])fills in{0},{1}(the word order may differ per language),tn(lang, m::X, n)chooses the plural form and fills in{n}as a number of the language.- Plurals: the CLDR forms for integers (
zero,one,two,few,many,other), in the file asitems[one] = {n} item.plural_form(lang, n)knows the rules of all 34 languages (Polish 2-4/5+, Ukrainian, Croatian/Serbian/Bosnian, Slovenian with dual, Lithuanian, Latvian with zero, Romanian, Irish and Maltese with five forms, Icelandic and Macedonian 1/21/31, French and Portuguese 0 and 1). - Capitals:
uppercase(lang, ..)andlowercase(lang, ..): Turkish i ↔ İ and ı ↔ I, Greek in capitals without tonos ("Ρυθμίσεις" → "ΡΥΘΜΙΣΕΙΣ"), a Greek Σ at the end of a word becomes ς, German ß → SS. The headings (APPS, NETWORK, …) are written normally in the files and are drawn withdesktop::main. The browser doestext-transformaccording to<html lang>of the page. - Formatting (
format):time(24-hour clock, "14:05"; Finnish "14.05"),date("22-10-2026" nl, "22/10/2026" en/fr/es/it/pt/el/ga/mt, "22.10.2026" de/pl/tr/…, "2026. 10. 22." hu, "2026-10-22" sv/lt, "22. 10. 2026." hr/bs/sr, "22.10.2026 г." bg),number(thousands from 10 000 with a point, comma or narrow space) anddecimal(decimal sign: point in en/ga/mt, otherwise comma). The sizes in the apps ("7,8 GB" / "7.8 GB") and the duration ("2 u 05 min" / "2 h 05 min") follow the language. find_nl(nl): the key for a Dutch text that comes from outside (the profile server sends "Gisteren", the kernel "Interne SSD", "toetsenbord (PS/2)");desktop::t_nltranslates such a text if there is a key for it and otherwise leaves it as it is.
Where the language comes from (kernel, kernel/src/lang.rs): the language of the profile as long as someone is logged in, otherwise that of the PC. The language of the PC is in PROFILE.TXT as language=<code> (before 27-10-2026 taal=<code>, still read; default nl; docs/PROFILE-PROTOCOL.md) and applies to the login screen (also the power menu there) and the boot screen (until PROFILE.TXT has been read: Dutch). Toasts of sshd and the Wi-Fi, the browser (start page, about:browser, error pages, messages in the bar) and the Accept-Language header of every HTTP request (sites and the profile server) ask lang::current(). The kernel log and the console are in English (since the English conversion of 27-10-2026; before that Dutch). On the login screen anyone can choose the language of the PC with the language card at the bottom (docs/DESKTOP-DESIGN.md 13); that choice is in \EOS\FW\LANG.TXT (language=<code>, nothing else; the old name TAAL.TXT and the old line taal=<code> are still read) and wins over language= in PROFILE.TXT.
Choosing the language: Settings › Language shows all 34 languages in their own name as buttons in a grid (Language::CHOICE_LIST). A click applies immediately: desktop, bar, menu, the titles and content of open apps and the terminal bar are redrawn (redraw_all). The choice goes with profile::setting_changed("taal", code) to the profile server (taal= in the profile, all 34 codes).
Typeface
The subset of Inter (make letters, tools/ui/src/subset.rs) now also has Greek (U+0384-U+03CE), Cyrillic (U+0400-U+045F and Ґ ґ), the digraphs DŽDždžLJLjljNJNjnj, the narrow no-break space (U+202F, thousands) and the Ukrainian apostrophe (U+02BC), next to ASCII, Latin-1, Latin Extended-A and Ș ș Ț ț. Inter has all those characters. A test (desktop::tests::all_chars_in_the_typeface) checks that every character of every translation, of the own names, of the headings in capitals and of the dates in both weights is in the typeface.
| before | after | |
|---|---|---|
| INTER-R.TTF | 28 832 B | 41 072 B |
| INTER-B.TTF | 28 644 B | 40 864 B |
| kernel x86-64 | 2 525 000 B | 2 836 608 B (+311 608, +12.3 %) |
| kernel ARM64 | 2 190 112 B | 2 506 256 B (+316 144) |
(main at de01b30 against this branch.) Of the growth ~24 KB is typeface and ~299 KB translations (270 KB text in 34 languages, ~23 KB start offsets, ~6 KB key names); the old nl/en table and the Dutch texts in the code come off that. Can be smaller later: load the tables of only the chosen language of the stick, or compress. The glyph cache (Fonts::in_cache) only grows with what is drawn: per language the characters of that script are added, not the whole typeface.
The bitmap font of the console stays ASCII; the terminal already draws non-ASCII with Inter.
Translating and reviewing
- A new text: key and Dutch text in
nl.txt(in the right section), English inen.txt, in the codet(lang, s::KEY)(or in an apptt(st, s::KEY), a headingmain_tt). A text with a value:{0}in the text andtv; with a count: a plural andtn. - The other languages: the test
all_languages_complete(cargo test -p euzos-i18n, also inmake ui) lists per language what is missing, which placeholders differ and which plural forms are missing. If a text is missing, the user sees the English. - Checking whether it fits:
make uirenders menu, Settings (Language and Appearance), Files, Hardware, the login screen (with power menu) and the power menu with the Log out question in nl, en, de, fr, tr, pl, el, bg, uk, fi, hu and mt tobuild/ui/talen/<code>-*.png, and a contact sheet with all 34 languages (talen.png). Other languages:build/target/release/euzos-ui-preview talen <dir> <code> .... Texts that do not fit are shortened with "…" (buttons, labels, menu items, task buttons, the explanation in Settings); the explanation on the login screen runs over two lines. - Review by a native speaker: read the file next to
nl.txt/en.txtand the PNGs, improve, and replace the header# status: draft — to be reviewed by a native speakerwith# status: reviewed (<name>, <date>). Then "concept" disappears from Settings and the contact sheet.
Review: doubts of the translators
- Form of address: informal in nl (je/jij), de (du), da/nb/sv/is, es, it, fi, et, pl, ga, mt; polite form in fr (vous), pt, ca (vós in sentences), cs, sk, sl, lt, lv, el, ro, sq, tr, bg, mk, uk, hr, bs, sr and, since the review of 28-10-2026, hu (Ön, as Windows and macOS do). Please confirm German "du" or "Sie" and Estonian informal address. The terms per language are in docs/i18n/GLOSSARY.md; the expert review of 28-10-2026 and its open questions in docs/i18n/REVIEW-2026-10-28.md.
- Words: German "WLAN" (the rest "Wi-Fi"); Maltese keeps English loanwords (Settings, Boot, Input); Ukrainian "Застосунки", "ШІ-асистенти"; Macedonian the least certain ("USB-меморија", "Безбедно по основно"); Irish mutations after numbers ("huaire", "n-ordú").
- Numbers in sentences: where a word would have to inflect with the number ("{0} nu actief", "{0} gekozen": "{0} now active", "{0} selected") many languages chose "label: {0}".
- Welcome without gender in es/it/pt/ca ("Te damos la bienvenida"); Icelandic "Velkomin" depends on gender.
Open points
- Keyboard layouts (see below): the conversion layer is in place, the drivers do not use it yet; a choice in Settings and
toetsenbord=in the profile will follow. - Phone app (Flutter,
app/lib/texts.dart): now a classTwith_('nl', 'en'). Approach: the same fileslibs/i18n/translations/*.txtas source, with a small Dart script (inapp/tool/) that converts them toapp/lib/teksten_<code>.dart(aMap<String, String>per language) and the keys of the app (an own section# --- Appinnl.txt, or an ownapp/vertalingen/).T.of(context)then chooses bystaat.taal(the same 34 codes), with fallback to English; plurals with the same CLDR rules (a Dart copy ofeuzos_i18n::plural_form, with a test that compares the outcomes). Not built yet. - Mails of the profile server (
server/profile/src/mail.rs): now nl or en (the language of the account). Approach: the server reads the same translation files (it is Rust:euzos-i18nas a dependency, with an own section# --- Mailin the files) and chooses the language of the account (taal=, all 34 codes); unknown → English. - Browser content: the
Accept-Languageheader now goes along (the language of the user, then English with q=0.5), so sites that support it answer in that language. The interface of the browser is translated; the pages themselves are not (no translation service: own-code rule). - Installer (
libs/ui/src/installer.rs): still Dutch only; the installer runs before there is aPROFILE.TXTwithlanguage=. A choice of the language as the first step of the installer, which immediately writeslanguage=toPROFILE.TXT, is the obvious approach. - The boot screen is Dutch until
PROFILE.TXThas been read (the stick is then not always readable yet); the bootloader can passlanguage=later. - Smaller: the translations cost ~299 KB in the kernel; later load only the chosen languages or compress.
- Texts of the kernel that come in as data in Dutch (hardware info "Aanraakscherm (I2C-HID, …)", "UEFI (EUZOS-lader), proefstart", places "poort 1.2") are only translated if they are exactly a key; better to have those come from the kernel as keys.
Keyboards
A layout belongs to the user (profile: toetsenbord=, proposal) and, before login, to the PC (PROFILE.TXT: toetsenbord=). Needed for these languages:
| Layout | For |
|---|---|
| QWERTY US, US International (dead ' ` ^ ~ ") | nl, en, programmers; also pt/es/it/ca without own keyboard |
| QWERTY UK | en (Ireland, Malta: UK and Maltese 47/48) |
| QWERTZ German, Austrian, Swiss (German/French) | de |
| QWERTZ Czech, Slovak, Hungarian, Slovenian/Croatian/Bosnian/Serbian (Latin) | cs, sk, hu, sl, hr, bs, sr-Latn |
| AZERTY French, Belgian | fr, nl (Belgium) |
| Turkish Q and Turkish F | tr |
| Greek | el |
| Cyrillic Bulgarian (phonetic and BDS), Ukrainian, Serbian, Macedonian | bg, uk, sr-Cyrl, mk |
| Nordic: Danish, Norwegian, Swedish/Finnish, Icelandic | da, nb, sv, fi, is |
| Estonian, Latvian (QWERTY with dead key), Lithuanian | et, lv, lt |
| Spanish, Portuguese, Italian, Catalan (Spanish), Polish (programmer), Romanian (standard), Albanian | es, pt, it, ca, pl, ro, sq |
| Irish, Maltese | ga, mt |
Design (libs/input/src/layout.rs, tested with cargo test -p euzos-input):
- A key is a HID usage (page 0x07), as USB sends it; PS/2 (set 1) goes via
from_set1to the same usage. One table per layout for both drivers. - Per layout four layers (plain, Shift, AltGr, AltGr+Shift) of 49 characters in a fixed order (a-z, 1-0, the punctuation, the ISO keys
#and<). A combining accent (U+0300 …) in a layer is a dead key. Converter::key(usage, shift, altgr)keeps track of Caps Lock (Turkish: i → İ) and a pending dead key: dead key + letter → composed character (á à â ã ä ç …), + space or twice → the accent itself, + something else → accent and character separately. US International: ' + c → ç.- Built now: US, US International, German, French (AZERTY) and Turkish Q. The rest follows the same pattern (tables only).
What the drivers still need (phase 2): the input buffer of the kernel (kernel/src/input.rs) now carries u32 (done for the on-screen keyboard): 0x00-0x7F ASCII, 0x80-0x9F the special codes (arrows, volume, windows; in Unicode the C1 control characters), from 0xA0 a Unicode character (input::push_char); the browser, the installer and the shell already take characters. Still to do: the PS/2 driver (arch/x86_64/keyboard.rs) and the USB driver pass usage + Shift/AltGr (right Alt; Ctrl+Alt as AltGr) to Converter and put the character in the buffer with input::push_char. The font of the console only knows ASCII.
EUZOS