Lingo is a strongly typed, feature-oriented translation library for Arduino ESP32.
It is designed for firmware where each feature owns a small translation table, lookup must stay allocation-free, and runtime registry storage should prefer PSRAM when available.
- Feature-owned tables - register many small translation domains per language instead of one global dictionary.
- Strongly typed keys - each enum type is an isolated translation domain, so identical numeric values cannot collide across features.
- PSRAM-first registry -
preferPsramdefaults totrueand falls back to normal ESP32 heap when needed. - Allocation-free lookup - translation tables and strings are referenced directly;
get()andfind()do not allocate. - No RTTI - enum domains use unique static type tokens without
typeidorstd::type_index. - Safe C strings -
get()always returns a validconst char *, suitable for logging and embedded display APIs. - Explicit fallback - requested-language lookup can fall back to the configured default language within the same feature domain.
[env:esp32dev]platform = espressif32
board = esp32dev
framework = arduino
lib_deps =
https://github.com/ZekStack/lingo.git
build_flags =
-std=gnu++20
build_unflags =
-std=gnu++11Lingo is not published to Arduino Library Manager yet.
Install it by downloading the repository ZIP or cloning it into:
Arduino/libraries/Lingo#include<Arduino.h>
#include<Lingo.h>
Lingo lingo;
enumclassLanguage : uint16_t {
Hu,
En,
};
enumclassSoftwareKey : uint16_t {
UpdateAvailable,
Install,
};
constexpr LingoTable SOFTWARE_HU{
LingoEntry(SoftwareKey::UpdateAvailable, "Frissítés érhető el"),
LingoEntry(SoftwareKey::Install, "Telepítés"),
};
constexpr LingoTable SOFTWARE_EN{
LingoEntry(SoftwareKey::UpdateAvailable, "Update available"),
LingoEntry(SoftwareKey::Install, "Install"),
};
voidsetup() {
LingoConfig config;
config.defaultLanguage = Language::Hu;
config.preferPsram = true;
if (!lingo.init(config)) {
return;
}
lingo.addTable(Language::Hu, SOFTWARE_HU);
lingo.addTable(Language::En, SOFTWARE_EN);
constchar *current = lingo.get(SoftwareKey::Install);
constchar *english = lingo.get(SoftwareKey::Install, Language::En);
lingo.setDefaultLanguage(Language::En);
}A second feature can define its own enum starting at the same numeric values without collisions:
enumclassTimeKey : uint16_t {
Save = 0,
Timezone = 1,
};
lingo.addTable(Language::Hu, TIME_HU);
lingo.addTable(Language::En, TIME_EN);Important
Registered tables and their translation strings are not copied. Keep them alive until lingo.end(). constexpr or static tables containing string literals are the intended pattern.
- Exactly one table may be registered for a
{language, key enum type}pair. - Size
maxTablesfor the total number of registered language/domain pairs; each feature table in each language consumes one slot. get()never returnsnullptr;find()is the strict nullable lookup API.get(key, language)falls back to the default language only within the same enum domain.init()allocates a bounded registry once. WithpreferPsram = true, ESP32 PSRAM is attempted first and normal heap is the fallback.- After startup registration, concurrent
get()/find()calls are supported. Do not mutate the table registry concurrently with lookups. - Translation bytes are passed through unchanged, including UTF-8 text.
| Example | Description |
|---|---|
Basic | Initialize Lingo, register HU/EN tables, and switch the default language. |
FeatureTables | Register multiple feature domains per language with overlapping numeric key values. |
Fallback | Demonstrate strict lookup and same-domain default-language fallback. |
Start with:
examples/Basic| Document | Description |
|---|---|
docs/getting-started.md | First integration and table registration. |
docs/api.md | Public types and methods. |
docs/configuration.md | Registry sizing, fallback string, and PSRAM preference. |
docs/tables-and-domains.md | Feature-domain identity and registration rules. |
docs/fallback.md | Exact get() and find() resolution behavior. |
docs/memory.md | Static translations, bounded allocation, and PSRAM behavior. |
docs/thread-safety.md | Supported runtime concurrency and lifecycle rules. |
docs/troubleshooting.md | Common registration and lookup failures. |
LingoResult init(const LingoConfig &config);
lingo.addTable(Language::Hu, SOFTWARE_HU);
lingo.addTable(Language::En, SOFTWARE_EN);
constchar *value = lingo.get(SoftwareKey::Install);
constchar *hu = lingo.get(SoftwareKey::Install, Language::Hu);
constchar *strict = lingo.find(SoftwareKey::Install, Language::Hu);
lingo.setDefaultLanguage(Language::En);| Item | Support |
|---|---|
| Framework | Arduino ESP32 |
| Platform | espressif32 |
| Language | C++20 |
| Dependencies | none |
| RTTI | not required |
| Exceptions | not used for public error handling |
| PSRAM | preferred for registry allocation by default |
| Runtime lookup allocation | none |
| Status | initial 0.1.0 development |
MIT - see LICENSE.md.
Part of the ZekStack ESP32 library stack.