Move localization to ESPHome compile-time substitutions#58
Conversation
## Summary
Moves language selection and all UI strings from the Blueprint (runtime, Jinja) to ESPHome
firmware (compile-time substitutions). Only the strings for the selected language are compiled
into the firmware — no locale table is loaded at runtime.
## Changes
### ESPHome
- Add `esphome/nspanel_esphome_localization.yaml` with full locale table for 54 languages
- Add `language` substitution (default: `en`) with validation and fallback
- Add `render_date` script to `nspanel_esphome_datetime.yaml` — reusable date rendering
with localized weekday/month names, used by home page and weather pages
- Replace all `mui_*` globals receiving values via Blueprint `mem` calls with compile-time
string literals from `${LOCALIZATION[LANG].*}`
- Remove `mui_bytes_per_char`, `mui_please_confirm`, `mui_unavailable`, `mui_meridiem_am`,
`mui_meridiem_pm` globals and their `action_component_text`/`action_component_val` handlers
- Remove `mui_alarm` parameter from `page_alarm` API action
- Add `SENTINEL_UNAVAILABLE`, `SENTINEL_UNKNOWN`, `SENTINEL_NO_NAME` substitutions for
Blueprint-side placeholder strings
### Blueprint
- Remove language selector `!input`
- Remove full `mui` locale table
- Remove all `component_text` calls sending locale strings via `page: mem`
- Update `page_alarm` call to remove `mui_alarm` parameter
- Send sentinel strings (`__unavailable__`, `__unknown__`, `__no_name__`) where needed
### Docs
- Add `docs/localization.md` with language code table and font support notes
- Update `docs/blueprint.md` — language section now points to ESPHome substitution
- Update `docs/install.md` — add `language:` substitution to all YAML examples
- Update `docs/migration_from_blackymas.md` — language migration note and FAQ entry
## ⚠️ Breaking Change
This release requires a **coordinated update** of both ESPHome firmware and Blueprint.
Users must also add `language: xx` to their ESPHome `substitutions` block before flashing.
Without it, the panel will default to English regardless of the previous Blueprint language setting.
See [docs/localization.md](docs/localization.md) for the full list of supported language codes.
## Supported Languages (54)
`af` `ar` `bg` `bn` `bs` `ca` `cs` `cy` `da` `de` `el` `en` `es` `et` `eu` `fa` `fi` `fr`
`gl` `he` `hi` `hr` `hu` `id` `is` `it` `ja` `jv` `ko` `lt` `lv` `mk` `ms` `nb` `nl` `pa`
`pl` `pt` `ro` `ru` `sk` `sl` `sq` `sr` `sv` `sw` `ta` `th` `tr` `uk` `ur` `vi` `zh-cn` `zh-tw`
📝 WalkthroughWalkthroughAdds compile-time localization via an ESPHome Changes
Sequence Diagram(s)sequenceDiagram
participant HA as Home Assistant
participant API as esphome API (component_text)
participant Pre as Preprocessor (lambda)
participant Local as Localization substitutions
participant HMI as HMI/display script
HA->>API: call component_text(txt)
API->>Pre: forward txt
Pre->>Local: lookup SENTINEL or LOCALIZATION[LANG]
Local-->>Pre: replaced txt_str
Pre->>API: execute action_component_text with txt_str
API->>HMI: update display component text
HMI-->>HA: ack/update visual
sequenceDiagram
participant Page as Page controller (page_weather)
participant Render as render_date script
participant Local as Localization substitutions
participant Display as HMI/display component
Page->>Render: execute(component="home.date", timestamp)
Render->>Local: substitute %A/%a/%B/%b tokens from LOCALIZATION[LANG]
Local-->>Render: formatted tokens
Render->>Display: set component text (rendered date)
Display-->>Page: done
Estimated code review effort🎯 4 (Complex) | ⏱️ ~60 minutes Possibly related PRs
Poem
🚥 Pre-merge checks | ✅ 3✅ Passed checks (3 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment Tip You can disable the changed files summary in the walkthrough.Disable the |
There was a problem hiding this comment.
Actionable comments posted: 3
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
docs/addon_climate.md (1)
82-82:⚠️ Potential issue | 🟠 MajorFix markdown table pipe style to unblock CI.
Line 82 has spacing before a pipe that violates MD060 (
table-column-style) and currently fails validation.Proposed fix
-temp_min|Optional|Number representing a temperature in the selected unit|*cool: `15` (°C) or `60` (°F)*<br>*heat: `7` (°C) or `45` (°F)*<br>*dual: `7` (°C) or `45` (°F)* |The minimum temperature the climate device can reach. Used to set the range of the frontend gauge. +temp_min|Optional|Number representing a temperature in the selected unit|*cool: `15` (°C) or `60` (°F)*<br>*heat: `7` (°C) or `45` (°F)*<br>*dual: `7` (°C) or `45` (°F)*|The minimum temperature the climate device can reach. Used to set the range of the frontend gauge.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@docs/addon_climate.md` at line 82, The table row for the "temp_min" field has stray spaces before pipe characters which violates MD060; edit the markdown row containing "temp_min" so all pipe separators are flush (no spaces immediately before '|'), ensuring the columns use consistent pipe style (e.g., change "temp_min | Optional | ..." to "temp_min|Optional|..."). Keep the cell content unchanged other than removing the extra spaces around the pipes so the table validates.
🧹 Nitpick comments (3)
esphome/nspanel_esphome_api.yaml (1)
75-82: Consider usingelse ifchain for clarity and minor efficiency.The sentinel replacement logic uses three independent
ifstatements. Sincetxt_strcan only match one sentinel value at a time, anelse ifchain would be slightly clearer about the mutually exclusive nature and avoid unnecessary string comparisons.♻️ Suggested optimization
std::string txt_str = std::string(txt); - if (txt_str == "${SENTINEL_UNAVAILABLE}") txt_str = "${LOCALIZATION[LANG].unavailable}"; - if (txt_str == "${SENTINEL_UNKNOWN}") txt_str = "${LOCALIZATION[LANG].unknown}"; - if (txt_str == "${SENTINEL_NO_NAME}") txt_str = "${LOCALIZATION[LANG].no_name}"; + if (txt_str == "${SENTINEL_UNAVAILABLE}") { + txt_str = "${LOCALIZATION[LANG].unavailable}"; + } else if (txt_str == "${SENTINEL_UNKNOWN}") { + txt_str = "${LOCALIZATION[LANG].unknown}"; + } else if (txt_str == "${SENTINEL_NO_NAME}") { + txt_str = "${LOCALIZATION[LANG].no_name}"; + } action_component_text->execute( page.c_str(), id.c_str(), txt_str.c_str() );🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@esphome/nspanel_esphome_api.yaml` around lines 75 - 82, The sentinel-replacement block that sets txt_str currently uses three independent if checks (txt_str == "${SENTINEL_UNAVAILABLE}", "${SENTINEL_UNKNOWN}", "${SENTINEL_NO_NAME}"); change these to an if / else if / else if chain so only the first matching sentinel triggers replacement (leave the replacement strings using LOCALIZATION[LANG] and the subsequent call to action_component_text->execute(page.c_str(), id.c_str(), txt_str.c_str()) unchanged), preserving behavior but avoiding redundant comparisons.docs/migration_from_blackymas.md (1)
126-126: Inconsistent quoting in table.The
languagerow uses single quotes ('language') while other rows use backticks for code formatting. This should be consistent with the rest of the table.📝 Suggested fix
-| 'language' | Selected via Blueprint dropdown | Set as `language: xx` substitution in ESPHome YAML - see [Localization](localization.md) | +| `language` | Selected via Blueprint dropdown | Set as `language: xx` substitution in ESPHome YAML - see [Localization](localization.md) |🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@docs/migration_from_blackymas.md` at line 126, The table row is inconsistent: change the single-quoted cell 'language' to use backticks like the other rows (i.e., replace 'language' with `language`) so the code formatting matches the rest of the table; update the same table line containing the string 'language' to use backtick quoting.docs/localization.md (1)
92-93: Add newline at end of file.The file is missing a trailing newline after the last line. Per coding guidelines, files should end with a newline character.
📝 Suggested fix
If your language is not listed, please [open a feature request](https://github.com/edwardtfn/NSPanel-Easy/issues/new) and it will be added in a future release. +🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@docs/localization.md` around lines 92 - 93, The file ending with the line "If your language is not listed, please [open a feature request](https://github.com/edwardtfn/NSPanel-Easy/issues/new) and it will be added in a future release." is missing a trailing newline; open the file containing that exact sentence and add a single newline character at the end of the file so the file ends with a newline (ensure your editor or pre-commit trims/retains trailing newline correctly).
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@esphome/nspanel_esphome_datetime.yaml`:
- Around line 169-178: The replace_token helper only replaces the first
occurrence of a token in date_str; update the lambda used around
id(mui_date_format) so it repeatedly finds and replaces all occurrences (e.g.,
loop while str.find(token) != npos and replace each found instance) for each
call (tokens "%A", "%a", "%B", "%b") so formats like "%A %d (%A)" or "%b / %b"
are fully expanded.
In `@esphome/nspanel_esphome_page_weather.yaml`:
- Around line 63-69: The timestamp calculation currently adds page_index *
${SECONDS_PER_DAY} to the current epoch which can cross DST boundaries; instead
compute a stable local-day anchor at local noon and add page_index days to that
anchor. Locate the code around PAGE_WEATHER01_ID and SECONDS_PER_DAY where
timestamp is set (uses id(time_provider).now().timestamp), convert the current
instant to local date/time, build an anchor timestamp for that local date at
12:00 (noon) and then compute timestamp = anchor_noon + (page_index *
${SECONDS_PER_DAY}); keep the rest of the logic (page_index bounds check and
logging) unchanged.
In `@hmi/dev/nspanel_CJK_eu_code/boot.txt`:
- Line 63: Update the boot variant's min_tft_version by changing the "Text
: 10" entry in hmi/dev/nspanel_easy_landscape/boot.txt to "Text : 11"
so it matches the other updated variants and passes the version compatibility
check.
---
Outside diff comments:
In `@docs/addon_climate.md`:
- Line 82: The table row for the "temp_min" field has stray spaces before pipe
characters which violates MD060; edit the markdown row containing "temp_min" so
all pipe separators are flush (no spaces immediately before '|'), ensuring the
columns use consistent pipe style (e.g., change "temp_min | Optional | ..." to
"temp_min|Optional|..."). Keep the cell content unchanged other than removing
the extra spaces around the pipes so the table validates.
---
Nitpick comments:
In `@docs/localization.md`:
- Around line 92-93: The file ending with the line "If your language is not
listed, please [open a feature
request](https://github.com/edwardtfn/NSPanel-Easy/issues/new) and it will be
added in a future release." is missing a trailing newline; open the file
containing that exact sentence and add a single newline character at the end of
the file so the file ends with a newline (ensure your editor or pre-commit
trims/retains trailing newline correctly).
In `@docs/migration_from_blackymas.md`:
- Line 126: The table row is inconsistent: change the single-quoted cell
'language' to use backticks like the other rows (i.e., replace 'language' with
`language`) so the code formatting matches the rest of the table; update the
same table line containing the string 'language' to use backtick quoting.
In `@esphome/nspanel_esphome_api.yaml`:
- Around line 75-82: The sentinel-replacement block that sets txt_str currently
uses three independent if checks (txt_str == "${SENTINEL_UNAVAILABLE}",
"${SENTINEL_UNKNOWN}", "${SENTINEL_NO_NAME}"); change these to an if / else if /
else if chain so only the first matching sentinel triggers replacement (leave
the replacement strings using LOCALIZATION[LANG] and the subsequent call to
action_component_text->execute(page.c_str(), id.c_str(), txt_str.c_str())
unchanged), preserving behavior but avoiding redundant comparisons.
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro
Run ID: 1ec2f139-c741-408e-93da-51be8498c62c
📒 Files selected for processing (54)
docs/addon_climate.mddocs/addon_upload_tft.mddocs/api.mddocs/blueprint.mddocs/customization.mddocs/install.mddocs/localization.mddocs/migration_from_blackymas.mdesphome/nspanel_esphome_api.yamlesphome/nspanel_esphome_datetime.yamlesphome/nspanel_esphome_hw_display.yamlesphome/nspanel_esphome_localization.yamlesphome/nspanel_esphome_page_alarm.yamlesphome/nspanel_esphome_page_buttons.yamlesphome/nspanel_esphome_page_climate.yamlesphome/nspanel_esphome_page_confirm.yamlesphome/nspanel_esphome_page_cover.yamlesphome/nspanel_esphome_page_home.yamlesphome/nspanel_esphome_page_media_player.yamlesphome/nspanel_esphome_page_notification.yamlesphome/nspanel_esphome_page_settings.yamlesphome/nspanel_esphome_page_weather.yamlesphome/nspanel_esphome_version.yamlhmi/dev/nspanel_CJK_eu_code/boot.txthmi/dev/nspanel_CJK_eu_code/qrcode.txthmi/dev/nspanel_CJK_eu_code/settings.txthmi/dev/nspanel_CJK_us_code/boot.txthmi/dev/nspanel_CJK_us_code/qrcode.txthmi/dev/nspanel_CJK_us_code/settings.txthmi/dev/nspanel_CJK_us_land_code/boot.txthmi/dev/nspanel_CJK_us_land_code/qrcode.txthmi/dev/nspanel_CJK_us_land_code/settings.txthmi/dev/nspanel_eu_code/boot.txthmi/dev/nspanel_eu_code/qrcode.txthmi/dev/nspanel_eu_code/settings.txthmi/dev/nspanel_us_code/boot.txthmi/dev/nspanel_us_code/qrcode.txthmi/dev/nspanel_us_code/settings.txthmi/dev/nspanel_us_land_code/boot.txthmi/dev/nspanel_us_land_code/qrcode.txthmi/dev/nspanel_us_land_code/settings.txthmi/nspanel_CJK_eu.HMIhmi/nspanel_CJK_eu.tfthmi/nspanel_CJK_us.HMIhmi/nspanel_CJK_us.tfthmi/nspanel_CJK_us_land.HMIhmi/nspanel_CJK_us_land.tfthmi/nspanel_eu.HMIhmi/nspanel_eu.tfthmi/nspanel_us.HMIhmi/nspanel_us.tfthmi/nspanel_us_land.HMIhmi/nspanel_us_land.tftnspanel_easy_blueprint.yaml
💤 Files with no reviewable changes (4)
- docs/api.md
- esphome/nspanel_esphome_hw_display.yaml
- esphome/nspanel_esphome_page_cover.yaml
- esphome/nspanel_esphome_page_home.yaml
There was a problem hiding this comment.
Actionable comments posted: 2
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@docs/addon_climate.md`:
- Line 82: The table row for the "temp_min" column contains an unescaped pipe
inside the cell text ("device | can reach") which splits the row into an extra
column and triggers MD056; update the "temp_min" table cell to escape the pipe
(e.g., replace "device | can reach" with "device \| can reach") or wrap that
phrase in code/backticks so the pipe is treated as literal, ensuring the row
remains the intended number of columns and the table renders correctly.
- Around line 72-89: The table in the PR uses non-compact pipe spacing which
triggers MD060; update the table rows to use compact pipe spacing (single space
around pipes) so the header/separator and all data rows are consistent —
specifically modify the header row "| Key | Required | Supported values |
Default | Description |", the separator row currently formatted as "| :- | :-:
| :-: | :-: | :- |" to a compact form like "| :- | :-:
| :-: | :-: | :- |", and normalize every data row (including the "~temp_step~"
deprecated row and rows containing multi-line defaults like temp_min/temp_max)
to have single spaces around pipes and no extra internal spacing so markdownlint
MD060 passes.
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro
Run ID: c32969ae-af25-461d-bd58-78a26d547320
📒 Files selected for processing (3)
docs/addon_climate.mddocs/api.mddocs/blueprint.md
✅ Files skipped from review due to trivial changes (1)
- docs/blueprint.md
🚧 Files skipped from review as they are similar to previous changes (1)
- docs/api.md
…into v9999.99.9
There was a problem hiding this comment.
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
esphome/nspanel_esphome_datetime.yaml (1)
74-112:⚠️ Potential issue | 🟡 MinorHandle repeated time tokens the same way as repeated date tokens.
mui_time_formatis user-editable, but%-H,%-I, and%pare each processed only once here. Formats like%-I:%M (%-I)or%p %I:%M %pwill still leave a raw/untranslated token in the rendered output, and thehome.timepath still keeps leading whitespace when%pis used as a prefix. Please switch these to the same looped replacement strategy you already use inrender_date, and trim both ends after stripping%pforhome.time.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@esphome/nspanel_esphome_datetime.yaml` around lines 74 - 112, The code only replaces %-H, %-I and %p once, causing leftover tokens for repeated occurrences; update the replacement logic in the blocks using time_format_str (and its copies time_format_str_sleep and time_format_str_home) to repeatedly replace all occurrences of "%-H" and "%-I" and to replace all "%p" instances the same way render_date does (i.e., loop until find returns npos), and after removing/replacing "%p" for time_format_str_home and the final home.time text trim both leading and trailing whitespace before calling disp1->set_component_text("home.meridiem", ...); reference symbols: time_format_str, time_format_str_sleep, time_format_str_home, "%-H", "%-I", "%p", render_date, home.time, home.meridiem.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Outside diff comments:
In `@esphome/nspanel_esphome_datetime.yaml`:
- Around line 74-112: The code only replaces %-H, %-I and %p once, causing
leftover tokens for repeated occurrences; update the replacement logic in the
blocks using time_format_str (and its copies time_format_str_sleep and
time_format_str_home) to repeatedly replace all occurrences of "%-H" and "%-I"
and to replace all "%p" instances the same way render_date does (i.e., loop
until find returns npos), and after removing/replacing "%p" for
time_format_str_home and the final home.time text trim both leading and trailing
whitespace before calling disp1->set_component_text("home.meridiem", ...);
reference symbols: time_format_str, time_format_str_sleep, time_format_str_home,
"%-H", "%-I", "%p", render_date, home.time, home.meridiem.
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro
Run ID: 8db41b6c-9355-4f4b-8c6d-ddf08a2652c3
📒 Files selected for processing (7)
docs/migration_from_blackymas.mdesphome/nspanel_esphome_api.yamlesphome/nspanel_esphome_datetime.yamlesphome/nspanel_esphome_page_weather.yamlhmi/dev/nspanel_easy_landscape/boot.txthmi/nspanel_easy_landscape.hmihmi/nspanel_easy_landscape.tft
✅ Files skipped from review due to trivial changes (3)
- hmi/dev/nspanel_easy_landscape/boot.txt
- docs/migration_from_blackymas.md
- esphome/nspanel_esphome_page_weather.yaml
There was a problem hiding this comment.
Actionable comments posted: 1
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@esphome/nspanel_esphome_boot.yaml`:
- Around line 153-160: The lambda in boot_nextion emits step 5 before
boot_progress is initialized, causing the progress bar to jump; remove the
boot_progress->execute(5) call from the lambda (the block that contains
boot_log, disp1->send_command_printf(...), and
get_page_id(wakeup_page_name->current_option())) and instead invoke
boot_progress->execute(5) from the caller after the boot script (boot) has reset
progress to 0; locate the call by looking for the boot_nextion lambda and the
boot_progress symbol and move the step update into the caller immediately after
the initial progress reset.
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro
Run ID: 01c99351-4a41-4331-800a-40ac7891ffd8
📒 Files selected for processing (4)
esphome/nspanel_esphome_boot.yamlesphome/nspanel_esphome_datetime.yamlesphome/nspanel_esphome_page_settings.yamlnspanel_easy_blueprint.yaml
🚧 Files skipped from review as they are similar to previous changes (1)
- esphome/nspanel_esphome_datetime.yaml
|
@CodeRabbit review |
✅ Actions performedReview triggered.
|
|
@coderabbitai resume |
There was a problem hiding this comment.
🧹 Nitpick comments (1)
esphome/nspanel_esphome_datetime.yaml (1)
170-178: Consider extracting duplicatedreplace_tokenhelper.The
replace_tokenlambda is defined identically in bothrefresh_datetime(lines 76-84) andrender_date(lines 170-178). While functional, this duplication increases maintenance burden.You could define a reusable C++ helper function in a shared location or accept the duplication given the lambdas are self-contained.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@esphome/nspanel_esphome_datetime.yaml` around lines 170 - 178, The duplicated replace_token lambda used in refresh_datetime and render_date should be extracted into a single helper function to avoid duplication; create a free/static helper like replace_token(std::string& str, const char* token, const char* value) (or put it in the appropriate namespace) and replace both lambda definitions in refresh_datetime and render_date with calls to that helper, keeping the same behavior (find/replace loop using strlen and pos advancement) so callers remain unchanged.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Nitpick comments:
In `@esphome/nspanel_esphome_datetime.yaml`:
- Around line 170-178: The duplicated replace_token lambda used in
refresh_datetime and render_date should be extracted into a single helper
function to avoid duplication; create a free/static helper like
replace_token(std::string& str, const char* token, const char* value) (or put it
in the appropriate namespace) and replace both lambda definitions in
refresh_datetime and render_date with calls to that helper, keeping the same
behavior (find/replace loop using strlen and pos advancement) so callers remain
unchanged.
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro
Run ID: ceaeff20-db51-4d2f-8193-1d0352f169b5
📒 Files selected for processing (4)
esphome/nspanel_esphome_boot.yamlesphome/nspanel_esphome_datetime.yamlesphome/nspanel_esphome_page_settings.yamlnspanel_easy_blueprint.yaml
🚧 Files skipped from review as they are similar to previous changes (1)
- esphome/nspanel_esphome_page_settings.yaml
✅ Actions performedReviews resumed. |
Summary
Moves language selection and all UI strings from the Blueprint (runtime, Jinja) to ESPHome firmware (compile-time substitutions). Only the strings for the selected language are compiled into the firmware — no locale table is loaded at runtime.
Changes
ESPHome
esphome/nspanel_esphome_localization.yamlwith full locale table for 54 languageslanguagesubstitution (default:en) with validation and fallbackrender_datescript tonspanel_esphome_datetime.yaml— reusable date rendering with localized weekday/month names, used by home page and weather pagesmui_*globals receiving values via Blueprintmemcalls with compile-time string literals from${LOCALIZATION[LANG].*}mui_bytes_per_char,mui_please_confirm,mui_unavailable,mui_meridiem_am,mui_meridiem_pmglobals and theiraction_component_text/action_component_valhandlersmui_alarmparameter frompage_alarmAPI actionSENTINEL_UNAVAILABLE,SENTINEL_UNKNOWN,SENTINEL_NO_NAMEsubstitutions for Blueprint-side placeholder stringsBlueprint
!inputmuilocale tablecomponent_textcalls sending locale strings viapage: mempage_alarmcall to removemui_alarmparameter__unavailable__,__unknown__,__no_name__) where neededDocs
docs/localization.mdwith language code table and font support notesdocs/blueprint.md— language section now points to ESPHome substitutiondocs/install.md— addlanguage:substitution to all YAML examplesdocs/migration_from_blackymas.md— language migration note and FAQ entryThis release requires a coordinated update of both ESPHome firmware and Blueprint.
Users must also add
language: xxto their ESPHomesubstitutionsblock before flashing.Without it, the panel will default to English regardless of the previous Blueprint language setting.
See docs/localization.md for the full list of supported language codes.
Supported Languages
afarbgbnbscacscydadeeleneseteufafifrglhehihrhuidisitjajvkoltlvmkmsnbnlpaplptroruskslsqsrsvswtathtrukurvizh-cnzh-twSummary by CodeRabbit
New Features
Documentation
User-facing changes
Chores