- 🚀 Zero Runtime Overhead - Translations stored in PROGMEM (Flash)
- 🔄 Dynamic Locale Switching - Change language on-the-fly
- 📦 Compact - Efficient memory usage for embedded devices
- 🎨 LVGL Integration - Perfect for touchscreen displays
- 🛠️ Easy Setup - Simple YAML configuration
| Feature | Description |
|---|---|
| 🌍 Multi-Language Support | Add locales (en, ru, de, fr, etc.) |
| 💾 PROGMEM Storage | Translations stored in Flash, not RAM |
| 🔄 Runtime Switching | Change locale without reboot |
| 📝 YAML-Based | Simple, readable translation files |
| 🎯 Nested Keys | Organize translations hierarchically |
| 🔌 LVGL Integration | Update labels automatically |
| ⚡ Fast Lookups | Optimized translation retrieval |
| 🛡️ Fallback Support | Returns key if translation missing |
- ESPHome >= 2025.9.0
- LVGL Component (for UI integration)
- Python 3.8+ (for build process)
- PyYAML (automatically installed)
- Add to your ESPHome YAML configuration:
external_components:
- source: github://alaltitov/esphome
components: [i18n]- Create Translation Files (for example):
translations/en.yaml:
hello: "Hello"
goodbye: "Goodbye"
weather:
sunny: "Sunny"
cloudy: "Cloudy"
rainy: "Rainy"translations/ru.yaml:
hello: "Привет"
goodbye: "Пока"
weather:
sunny: "Солнечно"
cloudy: "Облачно"
rainy: "Дождливо"- Add to your ESPHome YAML:
i18n:
id: i18n_translations
sources:
- translations/en.yaml
- translations/ru.yaml
# - translations/de.yaml
# - translations/fr.yaml
# - translations/es.yaml
default_locale: en
- Use in LVGL:
lvgl:
pages:
- id: climate_page
bg_color: color_slate_blue_gray
widgets:
- label:
id: hello_lbl
text: "TEST"
- obj:
align: center
bg_color: color_blue
width: 150
height: 80
widgets:
- label:
id: lang_btn_label
align: center
text_color: color_white
text: "EN"
on_press:
then:
- lambda: |-
std::string current = id(i18n_translations).get_current_locale();
if (current == "ru") {
id(i18n_translations).set_current_locale("en");
} else {
id(i18n_translations).set_current_locale("ru");
}
ESP_LOGI("main", "Locale switched to: %s", id(i18n_translations).get_current_locale().c_str());
- lvgl.label.update:
id: hello_lbl
text: !lambda |-
return id(i18n_translations).translate("weather.cloudy");
- lvgl.label.update:
id: lang_btn_label
text: !lambda |-
std::string current = id(i18n_translations).get_current_locale();
return (current == "ru") ? "EN" : "RU";i18n:
# Component ID (required for automations)
id: i18n_translations
# Translation source files (required)
sources:
- translations/en.yaml
- translations/ru.yaml
- translations/de.yaml
# Default locale (optional, default: "en")
default_locale: en| Option | Type | Required | Description |
|---|---|---|---|
id |
ID | Yes | Component identifier |
sources |
List | Yes | List of YAML translation files |
default_locale |
String | No | Default locale on boot |
- Method 1: Via lambda
button:
- platform: template
name: "Switch to Russian"
on_press:
- lambda: |-
id(i18n_translations).set_current_locale("ru");
- Method 2: Via YAML action
button:
- platform: template
name: "Switch to English"
on_press:
- i18n.set_locale:
id: i18n_translations
locale: "en"
lambda: |-
ESP_LOGI("main", "Current locale: %s", id(i18n_translations).get_current_locale().c_str());- Simple translation:
- lvgl.label.update:
id: some_id
text: !lambda |-
return id(i18n_translations).translate("weather.cloudy");- With dynamic key:
- lvgl.label.update:
id: some_id
text: !lambda |-
return id(i18n_translations).translate("weather." + id(weather_state_sensor_some_id).state));- With value:
ota:
- platform: esphome
password: !secret display_ota
on_progress:
- lvgl.label.update:
id: ota_label
text: !lambda |-
static char buffer[64];
snprintf(buffer, sizeof(buffer), id(i18n_translations).translate("ota.progress").c_str(), x);
// where "x" - value progress from OTA
return buffer;- List translation:
LVGL ESPHome roller object example
- lambda: |-
lv_obj_t *roller_obj = id(backlight_settings_sleep_time_roller).obj;
if (roller_obj == nullptr) {
return;
}
uint16_t old_selection = lv_roller_get_selected(roller_obj);
std::string options =
id(i18n_translations).translate("sleep_time.never") + "\n" +
id(i18n_translations).translate("sleep_time.minute1") + "\n" +
id(i18n_translations).translate("sleep_time.minute5") + "\n" +
id(i18n_translations).translate("sleep_time.minute10") + "\n" +
id(i18n_translations).translate("sleep_time.minute30") + "\n" +
id(i18n_translations).translate("sleep_time.hour1") + "\n" +
id(i18n_translations).translate("sleep_time.hour6") + "\n" +
id(i18n_translations).translate("sleep_time.hour12");
lv_roller_set_options(roller_obj, options.c_str(), LV_ROLLER_MODE_NORMAL);
lv_roller_set_selected(roller_obj, old_selection, LV_ANIM_OFF);| Method | Description | Returns |
|---|---|---|
translate(key) |
Translate key using current locale | std::string |
set_current_locale(locale) |
Change current language | void |
get_current_locale() |
Get current language code | std::string |
Made with ❤️ for the ESPHome community
This project was made in my free time and if it was useful to you, you can support me if you find it necessary 😊:
ETH/USDT (ERC-20): 0x9fF0E16a58229bEcdFDf47d9759f20bE77356994
Or just put ⭐ Thank you