Internationalisation (i18n)
Runique includes a lightweight i18n system with no external dependencies.
Supported languages
Lang variant | Code | Recognised locale examples |
|---|---|---|
Lang::Fr | fr | fr, fr-FR, fr-CA, fr-BE, fr-CH |
Lang::En | en | en, en-US, en-GB, en-CA |
Lang::De | de | de, de-DE, de-AT, de-CH |
Lang::Es | es | es, es-ES, es-MX, es-AR |
Lang::It | it | it, it-IT, it-CH |
Lang::Pt | pt | pt, pt-PT, pt-BR |
Lang::Ja | ja | ja, ja-JP |
Lang::Zh | zh | zh, zh-CN, zh-TW, zh-HK |
Lang::Ru | ru | ru, ru-RU, ru-BY, ru-UA |
The default language is Lang::En when no explicit configuration is provided.
Available API
use runique::utils::trad::{set_lang, current_lang, t, tf, Lang};
// Set the global language
set_lang(Lang::Fr);
// Read the active language
let lang = current_lang(); // → Lang::Fr
// Translate a key
let msg = t("forms.required"); // → "This field is required"
// Translate a key with parameters (replaces {} with arguments)
let msg = tf("forms.too_short", &[3]); // → "Too short (min 3)"
| Function | Signature | Description |
|---|---|---|
set_lang | fn set_lang(lang: Lang) | Sets the global application language |
current_lang | fn current_lang() -> Lang | Returns the active language (default: En) |
t | fn t(key: &str) -> Cow<'static, str> | Translates a simple key |
tf | fn tf<T: Display>(key: &str, args: &[T]) -> String | Translates a key with {} substitutions |
Key format
Keys follow dot notation "section.sub_section.key" matching the hierarchy of the translation JSON.
"forms.required" → { "forms": { "required": "..." } }
"forms.too_short" → { "forms": { "too_short": "Too short (min {})" } }
"error.not_found" → { "error": { "not_found": "..." } }
Each {} in a value is replaced sequentially by the arguments passed to tf.
Fallback behaviour
If a key is missing in the target language, Runique applies the following fallback:
- Look up the key in the configured language (e.g.
Lang::Fr) - If absent, try
Lang::En - If still absent, return the raw key (e.g.
"forms.required")
This mechanism guarantees that an incomplete translation never causes a panic.
Configuration in `main.rs`
Call set_lang() before building the app, typically right after loading the configuration:
use runique::prelude::*;
use runique::utils::trad::{set_lang, Lang};
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
let config = RuniqueConfig::from_env();
// Set the application language
set_lang(Lang::Fr);
RuniqueApp::builder(config)
.routes(urls::routes())
.with_database(db)
.build()
.await?
.run()
.await?;
Ok(())
}
The language set here applies to user-facing messages: form validation and error messages. Internal logs (tracing::warn!) are hardcoded in French in the source and are not translated.
`LANG` variable for the CLI
The runique CLI reads the LANG environment variable (or LC_ALL / LC_MESSAGES as fallback) to choose its own display language.
Important:
LANGin.envonly applies to the CLI (runique start,runique new, etc.). The web application language is controlled byset_lang()inmain.rs.
Priority for the CLI:
LANGvariable in.env(loaded viadotenvy)- System
LANG/LC_ALL/LC_MESSAGESvariable Enby default if no variable is defined or recognised
# .env — CLI only
LANG=fr
Full locale strings are normalised automatically: fr_FR.UTF-8 is interpreted as fr.