Notation like Home Assistant, each screen in its own language, and the language in every build

From the end-to-end test on the bench boards and an independent review:
- Units spaced as Home Assistant does (54% / 54 % in German and French, 18°, 21.5 °C), in the firmware and the app.
- Grouping from five digits where the language does (1234 but 12.345 in Spanish, Polish, Portuguese): the layout
  message carries group_min and percent_space; an unknown number style no longer refuses a layout.
- Automatic follows Home Assistant's own language for the clock and numbers, also without a translation (Swedish:
  24 hours, 1 234,5; pt-BR: 1.234,5): regions.json from the CLDR for all 63 languages Home Assistant offers.
- 12-hour clock: the top bar says 3:48 PM, the clock card 3:48 (not 03:48), Last runs follow the clock.
- The climate setpoint font has the decimal comma; the value fonts have ², ³ and µ (m³ was a box).
- Weather days: at most three letters without the dot (mié, sáb, czw); the screen took three bytes.
- Texts fall back per key through the base language (pt-BR -> pt -> en) in the firmware and the app.
- Each screen gets the app's words in the language its firmware speaks; firmware from before the languages gets
  English and its own letters, not boxes.
- set_language re-reads what it writes (indentation, comments, quoted keys, CRLF, !include) and every build writes
  the language first; the 24-hour pin no longer hits a fresh install and waits for the old clock switches.
- Tests: region and notation, plural parity with the C++ rules, set_language edge cases, the pin, PUT /api/language.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
Max Gramser
2026-09-19 16:52:34 +02:00
parent ad8a8ccf56
commit 125346d94e
37 changed files with 1419 additions and 448 deletions
+381
View File
@@ -0,0 +1,381 @@
{
"_about": "GENERATED by tools/i18n.py cldr: how every Home Assistant language writes the clock and numbers (Unicode CLDR, Home Assistant's own rule for the space before %). The app takes it for Settings -> Language & region set to Automatic.",
"af": {
"clock": "24",
"numbers": "space",
"group_min": 1,
"percent_space": false
},
"ar": {
"clock": "12",
"numbers": "point",
"group_min": 1,
"percent_space": false
},
"bg": {
"clock": "24",
"numbers": "space",
"group_min": 2,
"percent_space": false
},
"bn": {
"clock": "12",
"numbers": "point",
"group_min": 1,
"percent_space": false
},
"bs": {
"clock": "24",
"numbers": "comma",
"group_min": 1,
"percent_space": false
},
"ca": {
"clock": "24",
"numbers": "comma",
"group_min": 1,
"percent_space": false
},
"cs": {
"clock": "24",
"numbers": "space",
"group_min": 1,
"percent_space": true
},
"cy": {
"clock": "24",
"numbers": "point",
"group_min": 1,
"percent_space": false
},
"da": {
"clock": "24",
"numbers": "comma",
"group_min": 1,
"percent_space": false
},
"de": {
"clock": "24",
"numbers": "comma",
"group_min": 1,
"percent_space": true
},
"el": {
"clock": "12",
"numbers": "comma",
"group_min": 1,
"percent_space": false
},
"en": {
"clock": "12",
"numbers": "point",
"group_min": 1,
"percent_space": false
},
"en-GB": {
"clock": "24",
"numbers": "point",
"group_min": 1,
"percent_space": false
},
"eo": {
"clock": "24",
"numbers": "space",
"group_min": 1,
"percent_space": false
},
"es": {
"clock": "24",
"numbers": "comma",
"group_min": 2,
"percent_space": false
},
"et": {
"clock": "24",
"numbers": "space",
"group_min": 2,
"percent_space": false
},
"eu": {
"clock": "24",
"numbers": "comma",
"group_min": 1,
"percent_space": false
},
"fa": {
"clock": "24",
"numbers": "point",
"group_min": 1,
"percent_space": false
},
"fi": {
"clock": "24",
"numbers": "space",
"group_min": 1,
"percent_space": true
},
"fy": {
"clock": "24",
"numbers": "comma",
"group_min": 1,
"percent_space": false
},
"fr": {
"clock": "24",
"numbers": "space",
"group_min": 1,
"percent_space": true
},
"ga": {
"clock": "24",
"numbers": "point",
"group_min": 1,
"percent_space": false
},
"gl": {
"clock": "24",
"numbers": "comma",
"group_min": 1,
"percent_space": false
},
"gsw": {
"clock": "24",
"numbers": "point",
"group_min": 1,
"percent_space": false
},
"he": {
"clock": "24",
"numbers": "point",
"group_min": 1,
"percent_space": false
},
"hi": {
"clock": "12",
"numbers": "point",
"group_min": 1,
"percent_space": false
},
"hr": {
"clock": "24",
"numbers": "comma",
"group_min": 1,
"percent_space": false
},
"hu": {
"clock": "24",
"numbers": "space",
"group_min": 2,
"percent_space": false
},
"hy": {
"clock": "24",
"numbers": "space",
"group_min": 1,
"percent_space": false
},
"id": {
"clock": "24",
"numbers": "comma",
"group_min": 1,
"percent_space": false
},
"it": {
"clock": "24",
"numbers": "comma",
"group_min": 2,
"percent_space": false
},
"is": {
"clock": "24",
"numbers": "comma",
"group_min": 1,
"percent_space": false
},
"ja": {
"clock": "24",
"numbers": "point",
"group_min": 1,
"percent_space": false
},
"ka": {
"clock": "24",
"numbers": "space",
"group_min": 2,
"percent_space": false
},
"ko": {
"clock": "12",
"numbers": "point",
"group_min": 1,
"percent_space": false
},
"lb": {
"clock": "24",
"numbers": "comma",
"group_min": 1,
"percent_space": false
},
"lt": {
"clock": "24",
"numbers": "space",
"group_min": 1,
"percent_space": false
},
"lv": {
"clock": "24",
"numbers": "space",
"group_min": 2,
"percent_space": false
},
"mk": {
"clock": "24",
"numbers": "comma",
"group_min": 1,
"percent_space": false
},
"ml": {
"clock": "12",
"numbers": "point",
"group_min": 1,
"percent_space": false
},
"nl": {
"clock": "24",
"numbers": "comma",
"group_min": 1,
"percent_space": false
},
"nb": {
"clock": "24",
"numbers": "space",
"group_min": 1,
"percent_space": false
},
"nn": {
"clock": "24",
"numbers": "space",
"group_min": 1,
"percent_space": false
},
"pl": {
"clock": "24",
"numbers": "space",
"group_min": 2,
"percent_space": false
},
"pt": {
"clock": "24",
"numbers": "space",
"group_min": 2,
"percent_space": false
},
"pt-BR": {
"clock": "24",
"numbers": "comma",
"group_min": 1,
"percent_space": false
},
"ro": {
"clock": "24",
"numbers": "comma",
"group_min": 1,
"percent_space": false
},
"ru": {
"clock": "24",
"numbers": "space",
"group_min": 1,
"percent_space": false
},
"sk": {
"clock": "24",
"numbers": "space",
"group_min": 1,
"percent_space": true
},
"sl": {
"clock": "24",
"numbers": "comma",
"group_min": 2,
"percent_space": false
},
"sr": {
"clock": "24",
"numbers": "comma",
"group_min": 1,
"percent_space": false
},
"sr-Latn": {
"clock": "24",
"numbers": "comma",
"group_min": 1,
"percent_space": false
},
"sv": {
"clock": "24",
"numbers": "space",
"group_min": 1,
"percent_space": true
},
"sq": {
"clock": "12",
"numbers": "space",
"group_min": 2,
"percent_space": false
},
"ta": {
"clock": "12",
"numbers": "point",
"group_min": 1,
"percent_space": false
},
"te": {
"clock": "12",
"numbers": "point",
"group_min": 1,
"percent_space": false
},
"th": {
"clock": "24",
"numbers": "point",
"group_min": 1,
"percent_space": false
},
"tr": {
"clock": "24",
"numbers": "comma",
"group_min": 1,
"percent_space": false
},
"uk": {
"clock": "24",
"numbers": "space",
"group_min": 1,
"percent_space": false
},
"ur": {
"clock": "12",
"numbers": "point",
"group_min": 1,
"percent_space": false
},
"vi": {
"clock": "24",
"numbers": "comma",
"group_min": 1,
"percent_space": false
},
"zh-Hans": {
"clock": "24",
"numbers": "point",
"group_min": 1,
"percent_space": false
},
"zh-Hant": {
"clock": "12",
"numbers": "point",
"group_min": 1,
"percent_space": false
}
}