Skip to content

Repository files navigation

ESPLang

ESPLang is a small translation table helper for ESP32 firmware. The library owns only active-language state, fallback-language state, registered table pointers, and lookup logic, while users keep control of their own language IDs, key enums, and translation data.

CI / Release / License

CI Release License: MIT

Features

  • No hardcoded languages or translation keys in the library API.
  • Pointer-only registration of user-owned translation tables.
  • Linear lookup by default, with optional sorted-key tables for binary-search key lookup.
  • Fallback-language lookup for missing translations.
  • No heap allocation required by the library.
  • Caller-buffer formatting through format(...).
  • Public macro and helper for compact table definitions.

Installation

  • PlatformIO: add https://github.com/ESPToolKit/esp-lang.git to lib_deps.
  • Arduino IDE: install as ZIP from this repository.

Single Include

#include <ESPLang.h>

Quick Start

#include <Arduino.h>
#include <ESPLang.h>

namespace AppLanguage {
static constexpr LangLanguageId EN = 1;
static constexpr LangLanguageId HU = 2;
}

enum class AppText : uint16_t {
	TestMessage = 1,
	BatteryLow = 2,
};

static const LangTranslationEntry kEnglish[] = {
    ESP_LANG_ENTRY(AppText::TestMessage, "Test message"),
    ESP_LANG_ENTRY(AppText::BatteryLow, "Battery low: %d%%"),
};

static const LangTranslationEntry kHungarian[] = {
    ESP_LANG_ENTRY(AppText::TestMessage, "Teszt uzenet"),
    ESP_LANG_ENTRY(AppText::BatteryLow, "Alacsony akkuszint: %d%%"),
};

static const LangTranslationTable kTables[] = {
    makeLangTable(AppLanguage::EN, kEnglish),
    makeLangTable(AppLanguage::HU, kHungarian),
};

ESPLang lang;

void setup() {
    Serial.begin(115200);

    ESPLangConfig config{};
    config.tables = kTables;
    config.tableCount = sizeof(kTables) / sizeof(kTables[0]);
    config.defaultLanguage = AppLanguage::EN;
    config.useDefaultFallback = false;
    config.fallbackLanguage = AppLanguage::EN;
    config.missingText = "[missing]";

    if (!lang.init(config)) {
        Serial.println("ESPLang init failed");
        return;
    }

    lang.setLanguage(AppLanguage::HU);
    Serial.println(lang.translate(AppText::TestMessage));

    char buffer[32];
    if (lang.format(buffer, sizeof(buffer), AppText::BatteryLow, 17)) {
        Serial.println(buffer);
    }
}

Examples

  • basic_translate: initialize ESPLang, switch languages, and print direct translations.
  • fallback_and_format: demonstrate fallback lookup, caller-buffer formatting, and runtime missing-text overrides.
  • sorted_lookup: register sorted tables with makeSortedLangTable(...) and use binary-search key lookup.
  • mixed_lookup_modes: combine sorted and linear tables in one config and rely on fallback across lookup modes.

API Reference

Public types:

  • using LangLanguageId = uint16_t
  • using LangKeyId = uint16_t
  • enum class LangLookupMode : uint8_t { Linear, SortedByKey } selects per-table key lookup strategy.
  • LangTranslationEntry { key, text } maps one user-defined key to one translation string.
  • LangTranslationTable { language, entries, entryCount, lookupMode } groups one language with its translation entries and lookup mode.
  • ESPLangConfig { tables, tableCount, defaultLanguage, useDefaultFallback, fallbackLanguage, missingText } configures the pointer-owned table set, startup language, fallback behavior, and missing-text string.

Helpers:

  • ESP_LANG_ENTRY(key, text) creates a LangTranslationEntry from an enum key.
  • makeLangTable(language, entries) builds a linear LangTranslationTable with the correct entry count.
  • makeSortedLangTable(language, entries) builds a sorted-key LangTranslationTable that uses binary search for key lookup.

Main API:

  • bool init(const ESPLangConfig& config) / void deinit() / bool isInitialized() const
  • bool setLanguage(LangLanguageId language) / LangLanguageId getLanguage() const
  • bool setFallbackLanguage(LangLanguageId language) / LangLanguageId getFallbackLanguage() const
  • void setMissingText(const char* text) / const char* getMissingText() const
  • bool hasLanguage(LangLanguageId language) const
  • bool hasKey(LangKeyId key) const
  • bool hasKeyInLanguage(LangLanguageId language, LangKeyId key) const
  • const char* translate(LangKeyId key) const
  • const char* translateFrom(LangLanguageId language, LangKeyId key) const
  • bool format(char* outBuffer, size_t outBufferSize, LangKeyId key, ...) const

Validation and lookup behavior:

  • init(...) returns false for null tables, zero table count, missing default language, missing explicit fallback language, invalid table storage, duplicate language tables, duplicate keys within the same language, or null text pointers.
  • makeSortedLangTable(...) requires entries to already be sorted by ascending key; init(...) rejects unsorted or duplicate keys in sorted tables.
  • translate(key) checks the selected language first, then the fallback language if it differs, then returns the configured missing text.
  • translateFrom(language, key) only searches the requested language.
  • missingText defaults to "?" when omitted or reset with setMissingText(nullptr).
  • Language-table resolution remains linear even when individual tables use sorted-key lookup.

Gotchas

  • translateFrom(...) does not use fallback lookup.
  • format(...) treats the resolved translation text as a vsnprintf format string. Placeholder mistakes in translation data, or mismatched argument types at the call site, become runtime formatting bugs.
  • format(...) returns false when the formatted output does not fully fit, even though the buffer may still contain truncated output.
  • useDefaultFallback = true resolves the effective fallback language to defaultLanguage.
  • Duplicate keys are only invalid within the same language table; reusing the same key across different languages is expected.
  • Sorted-key lookup is opt-in per table. The library never sorts caller data for you.

Restrictions

  • Packaged as an ESP32 / Arduino-targeted library for ESPToolKit.
  • C++17 is required.
  • Translation tables and strings are caller-owned and must remain valid for the lifetime of the ESPLang instance.

Standalone CMake

include(FetchContent)

FetchContent_Declare(
  esp_lang
  GIT_REPOSITORY https://github.com/ESPToolKit/esp-lang.git
  GIT_TAG main
)

FetchContent_MakeAvailable(esp_lang)

target_link_libraries(your_target PRIVATE ESPLang::esp_lang)

If the source is vendored locally, add_subdirectory(path/to/esp-lang) exposes the same ESPLang::esp_lang target.

Tests

  • Host-side tests in test/test_esplang cover config validation, lifecycle, linear and sorted lookup rules, mixed fallback behavior, missing-text handling, and formatting behavior.
  • CI also builds the Arduino examples through both PlatformIO and Arduino CLI on the standard ESP32 board matrix.

Formatting Baseline

This repository follows the firmware formatting baseline from esptoolkit-template:

  • .clang-format is the source of truth for C/C++/INO layout.
  • .editorconfig enforces tabs (tab_width = 4), LF endings, and final newline.
  • Format all tracked firmware sources with bash scripts/format_cpp.sh.

License

MIT - see LICENSE.md.

ESPToolKit

About

A small translation table helper for ESP32

Topics

Resources

Code of conduct

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages