# How OpenLogi Handles Locale Negotiation with Rust i18n in GPUI Processes

> Discover how OpenLogi's Rust i18n uses GPUI processes for efficient locale negotiation. Learn about build-time catalog compilation and runtime BCP-47 tag resolution for seamless localization.

- Repository: [Xuan Zhang/OpenLogi](https://github.com/AprilNEA/OpenLogi)
- Tags: internals
- Published: 2026-09-05

---

**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`](https://github.com/AprilNEA/OpenLogi/blob/main/crates/openlogi-desktop/src/main.rs):

```rust
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`](https://github.com/AprilNEA/OpenLogi/blob/main/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`](https://github.com/AprilNEA/OpenLogi/blob/main/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`](https://github.com/AprilNEA/OpenLogi/blob/main/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`](https://github.com/AprilNEA/OpenLogi/blob/main/crates/openlogi-desktop/src/services/i18n.rs) calls `openlogi_core::locale::activate(settings.language.as_deref())` to set the global atomic:

```rust
// 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:

```rust
// 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`](https://github.com/AprilNEA/OpenLogi/blob/main/crates/openlogi-core/src/locale.rs) implements the core negotiation algorithm:

1. **Parse** the optional BCP-47 language tag (e.g., `fr-CA`)
2. **Reduce** the tag to broader matches (e.g., `fr-CA` → `fr`) to find the closest available catalog compiled into the binary
3. **Fallback** to `"en"` when no matching catalog exists
4. **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`](https://github.com/AprilNEA/OpenLogi/blob/main/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:

```rust
// 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 in [`crates/openlogi-desktop/src/main.rs`](https://github.com/AprilNEA/OpenLogi/blob/main/crates/openlogi-desktop/src/main.rs) embeds 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 `activate` function in [`crates/openlogi-core/src/locale.rs`](https://github.com/AprilNEA/OpenLogi/blob/main/crates/openlogi-core/src/locale.rs) resolves language tags to the nearest available catalog, falling back to English when necessary.
- **Runtime application**: The `i18n::apply` function sets the locale at startup, while `AppState::set_language` handles live switching with GPUI window refreshes.
- **Integrity guarantees**: Tests in [`crates/openlogi-ui/tests/catalog_parity.rs`](https://github.com/AprilNEA/OpenLogi/blob/main/crates/openlogi-ui/tests/catalog_parity.rs) ensure 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`](https://github.com/AprilNEA/OpenLogi/blob/main/catalog_parity.rs) test suite enforces that every locale file contains the same keys as [`en.toml`](https://github.com/AprilNEA/OpenLogi/blob/main/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`](https://github.com/AprilNEA/OpenLogi/blob/main/en.toml) file serves as the source of truth, with other language files (e.g., [`fr.toml`](https://github.com/AprilNEA/OpenLogi/blob/main/fr.toml), [`de.toml`](https://github.com/AprilNEA/OpenLogi/blob/main/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`](https://github.com/AprilNEA/OpenLogi/blob/main/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.