# es-fluent Architecture
This document details the architecture of the `es-fluent` crate, which serves as the user-facing facade and entry point for the ecosystem.
## Overview
`es-fluent` ties together the derive macros and manager components into a cohesive API. It provides:
1. **Re-exports**: Easy access to common traits (`EsFluent`, `EsFluentChoice`, `EsFluentVariants`, `EsFluentThis`, `ToFluentString`, `ThisFtl`) and derive macros.
1. **Registry Types**: `FtlTypeInfo`, `FtlVariant`, `RegisteredFtlType`, and inventory collection for FTL file generation (including optional namespaces).
1. **Global Context**: A thread-safe singleton for storing the `FluentManager`, enabling ergonomic localization macros.
1. **Custom Localizer**: A hook for overriding or intercepting the localization process.
1. **Traits**: Standard definitions for how types invoke the localization system.
## Architecture
```mermaid
flowchart TD
subgraph FACADE["es-fluent"]
API["Public API (Derive Macros)"]
CTX["Global Context (OnceLock)"]
CUSTOM["Custom Localizer (OnceLock)"]
TRAIT["Traits (ToFluentString, etc)"]
INTERNAL["Internal localize() fn"]
NS["Internal namespace helpers"]
end
subgraph BACKEND["Backends"]
EMBED["es-fluent-manager-embedded"]
BEVY["es-fluent-manager-bevy"]
end
subgraph CORE["Core"]
MGR["es-fluent-manager-core"]
DERIVE["es-fluent-derive"]
end
API -->|generates code calling| INTERNAL
API -->|uses| NS
INTERNAL --> CUSTOM
CUSTOM -.->|fallback| CTX
CTX --> MGR
EMBED -->|initializes| CTX
BEVY -->|initializes| CTX
TRAIT --> API
```
## Global Context
To allow `Display` implementations (which can't easily pass arguments) to access translations, `es-fluent` uses a `OnceLock`-protected global context with `ArcSwap` for lock-free reads.
```rs
static CONTEXT: OnceLock<ArcSwap<FluentManager>> = OnceLock::new();
```
- **Initialization**: Only one backend (e.g., `embedded::init()` or `bevy` plugin) can initialize this context using `set_context` or `set_shared_context`.
- **Language Selection**: The active language can be changed at runtime using `select_language`, which calls `select_language` on the global `FluentManager`.
- **Consumption**: The internal `localize` function (used by the `FluentDisplay` trait) uses lock-free `ArcSwap::load()` to access the manager, providing better performance in read-heavy localization scenarios.
### Why ArcSwap?
Localization is a classic "read-heavy, write-rare" workload:
- **Reads**: `localize()` is called frequently (every time text is displayed)
- **Writes**: Language changes are rare (user occasionally switches language)
Using `ArcSwap` instead of `Arc<RwLock<...>>` eliminates lock contention on the hot path, making localization calls completely lock-free.
## Custom Localizer
A custom localizer can be registered to intercept translation requests. This is useful for testing, special environments, or implementing fallbacks.
```rs
static CUSTOM_LOCALIZER: OnceLock<Box<dyn Fn(...) -> Option<String> ...>> = OnceLock::new();
```
- **Priority**: The `localize` function first checks the custom localizer. If it returns `Some(string)`, that result is used.
- **Fallback**: If the custom localizer returns `None` (or isn't set), the system falls back to the Global Context.
## Traits
> **Important**: All traits listed below are intended to be implemented automatically by `#[derive(EsFluent)]` or other macros. **Manual implementation is strongly discouraged** and rarely needed (except for wrapping enums that delegate to other derived types).
### `ToFluentString`
The primary trait for converting a type into a localized string.
### `FluentDisplay`
A helper trait that `#[derive(EsFluent)]` implements. It handles the logic of looking up the correct key and passing arguments to the `localize` function.
### `EsFluentChoice`
Used to convert an enum into a string that can be used as a Fluent choice (selector).
### `ThisFtl`
A trait for types that have a "this" fluent key representing the type itself, typically implemented via `#[derive(EsFluent)]` with `#[fluent(this)]`.
## Internal Namespace Helpers
Derive macros can request a namespace based on the file path (e.g., `namespace = file`, `namespace(file(relative))`, `namespace = folder`, or `namespace(folder(relative))`). The namespace rule is stored on `FtlTypeInfo` and resolved by the generator/CLI to drive per-namespace `.ftl` output.
## Integration
Users generally add this crate to their dependencies:
```toml
[dependencies]
es-fluent = { version = "...", features = ["derive"] }
```
And then use the derive macros:
```rs
use es_fluent::{EsFluent, EsFluentChoice, EsFluentVariants, EsFluentThis};
#[derive(EsFluent)]
struct Hello;
#[derive(EsFluentVariants)]
#[fluent_variants(keys = ["label", "placeholder"])]
struct MyForm {
username: String,
password: String,
}
#[derive(EsFluentThis)]
#[fluent_this(origin)]
enum Gender {
Male,
Female,
}
```
> **Note**: Consumers should rely on the `#[derive(EsFluent)]` macro and the `ToFluentString` trait. The trait only needs to be in scope to enable calling `to_fluent_string`. Direct usage of the internal `localize!` macro or function is discouraged and generally not necessary.