How OpenLogi Handles Locale Negotiation with Rust i18n in GPUI Processes
OpenLogi negotiates locales within GPUI processes by compiling translation catalogs into binaries at build time, storing the active locale in a process-global atomic variable managed by the rust_i18n crate, and resolving BCP-47 language tags at runtime through a thin wrapper that selects the nearest matching catalog before triggering GPUI window refreshes.
OpenLogi's desktop interface and overlay UI are built with GPUI, Zed's Rust UI framework, and rely on the rust_i18n crate for all user-visible strings. The implementation ensures that locale changes propagate instantly across all GPUI widgets without requiring process restarts, leveraging compile-time verification to guarantee translation completeness.
Compile-Time Catalog Generation with rust_i18n
OpenLogi embeds localization catalogs directly into each binary (desktop, overlay, and agent) during compilation. The build process expands translation files located in crates/openlogi-ui/locales/*.toml using the rust_i18n macro invocation found in crates/openlogi-desktop/src/main.rs:
rust_i18n::i18n!("../openlogi-ui/locales", fallback = "en");
This macro embeds a complete locale catalog for every supported language plus a fallback English catalog (en.toml) directly into the binary. The compile-time approach eliminates runtime file system dependencies for translations and ensures that all GPUI processes have immediate access to every supported locale.
The translation files follow the TOML format, with en.toml serving as the source of truth for all translation keys.
Process-Wide Locale Storage and the Global Atomic
The rust_i18n crate stores the current locale in a process-global atomic variable. When this atomic updates, every subsequent call to rust_i18n::t! automatically reads the new value, including GPUI's built-in widget strings that depend on the localization system.
OpenLogi abstracts this atomic behind a thin wrapper in crates/openlogi-core/src/locale.rs. The activate function serves as the central entry point for locale changes, handling BCP-47 language tag resolution and atomic updates. This design ensures that both application-specific code and GPUI components (gpui-component) read from the same synchronized state.
Runtime Locale Negotiation in GPUI Processes
Startup Resolution
When the desktop UI launches, it reads the persisted language preference from AppSettings and applies it before any windows render. The initialization flow in crates/openlogi-desktop/src/services/i18n.rs calls openlogi_core::locale::activate(settings.language.as_deref()) to set the global atomic:
// Apply the persisted locale at startup (desktop binary)
use openlogi_core::config::AppSettings;
use openlogi_desktop::services::i18n;
fn start_ui(settings: &AppSettings) {
// Initialise the process-wide locale
i18n::apply(settings);
// …continue with GPUI window creation
}
Setting the locale before window creation guarantees that every UI element renders in the correct language from the first frame, eliminating flickering or delayed translation application.
Live Language Switching
Users can switch languages at runtime without restarting the application. When a new language is selected in the Settings UI, AppState::set_language invokes the same activate routine and forces a GPUI window refresh:
// Switch locale at runtime from a Settings UI action
use openlogi_core::locale::activate;
use crate::state::AppState;
fn on_language_selected(app_state: &mut AppState, new_lang: Option<String>) {
// Update the atomic locale
activate(new_lang.as_deref());
// Tell GPUI to redraw all windows with the new locale
app_state.refresh_ui();
}
The refresh operation causes all open views to re-evaluate their tr! and rust_i18n::t! calls, immediately displaying the new translations across menus, dialogs, and tooltips.
BCP-47 Tag Resolution Logic
The activate function in crates/openlogi-core/src/locale.rs implements the core negotiation algorithm:
- Parse the optional BCP-47 language tag (e.g.,
fr-CA) - Reduce the tag to broader matches (e.g.,
fr-CA→fr) to find the closest available catalog compiled into the binary - Fallback to
"en"when no matching catalog exists - Set the process-global locale via
rust_i18n::set_locale
This resolution strategy ensures that regional variants automatically map to base languages when specific regional catalogs are unavailable, maintaining usability while simplifying translation maintenance.
Catalog Parity and Compile-Time Verification
To prevent runtime failures from missing translation keys, OpenLogi enforces catalog parity through a test suite. The test in crates/openlogi-ui/tests/catalog_parity.rs verifies that every .toml locale file contains the exact same set of keys as the English source catalog.
If a translation key is missing in any locale, the application displays the raw key text rather than panicking, but the test suite catches these omissions during continuous integration. This compile-time verification guarantees that distributed binaries contain complete translation coverage for all supported languages.
Accessing Translations in GPUI Views
Application code and GPUI widgets access localized strings through the standard rust_i18n macro:
// Access a translated string in any GPUI view
use rust_i18n::t;
fn label_for_button() -> String {
t!("action.save").into_owned() // pulls the key from the catalog
}
Because GPUI reads from the same process-global atomic that activate modifies, built-in widgets and custom views alike automatically reflect locale changes without explicit reconfiguration.
Summary
- Compile-time embedding: The
rust_i18n::i18n!macro incrates/openlogi-desktop/src/main.rsembeds all TOML translation files into the binary with English as the fallback. - Global atomic storage: The active locale lives in a process-global atomic variable managed by rust_i18n, shared between OpenLogi code and GPUI components.
- BCP-47 resolution: The
activatefunction incrates/openlogi-core/src/locale.rsresolves language tags to the nearest available catalog, falling back to English when necessary. - Runtime application: The
i18n::applyfunction sets the locale at startup, whileAppState::set_languagehandles live switching with GPUI window refreshes. - Integrity guarantees: Tests in
crates/openlogi-ui/tests/catalog_parity.rsensure translation completeness across all locale files.
Frequently Asked Questions
How does OpenLogi handle missing translations?
When a translation key is absent from the active locale catalog, rust_i18n displays the raw key text instead of crashing. Additionally, the catalog_parity.rs test suite enforces that every locale file contains the same keys as en.toml, catching missing translations at compile time before they reach production binaries.
Can users switch languages without restarting the OpenLogi desktop application?
Yes. When a user selects a different language in the Settings UI, AppState::set_language calls openlogi_core::locale::activate to update the process-global atomic, then triggers a GPUI window refresh. All open views re-render immediately with the new locale without requiring an application restart.
What file format does OpenLogi use for translation catalogs?
OpenLogi stores translations in TOML files located in crates/openlogi-ui/locales/. The en.toml file serves as the source of truth, with other language files (e.g., fr.toml, de.toml) providing translations for the same set of keys. The rust_i18n macro processes these files at compile time to generate embedded catalogs.
How does locale negotiation handle regional language variants like fr-CA?
The activate function in crates/openlogi-core/src/locale.rs implements BCP-47 tag resolution that reduces regional variants to their base language codes. If fr-CA is requested but only fr exists in the compiled catalogs, the system selects fr as the nearest match. If no match exists after reduction, the system falls back to the English catalog embedded via the fallback = "en" macro parameter.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →