1use i18n_embed::{
2 fluent::{fluent_language_loader, FluentLanguageLoader},
3 LanguageLoader,
4};
5use rust_embed::RustEmbed;
6use std::sync::OnceLock;
7use unic_langid::LanguageIdentifier;
8
9include!(concat!(env!("OUT_DIR"), "/locales.rs"));
10
11pub const DEFAULT_LOCALE: &str = "en-GB";
12
13#[derive(RustEmbed)]
14#[folder = "i18n/"]
15struct Localizations;
16
17pub trait I18nDisplay {
19 fn to_i18n_string(&self) -> String;
21}
22
23pub fn language_loader() -> &'static FluentLanguageLoader {
28 static LOADER: OnceLock<FluentLanguageLoader> = OnceLock::new();
29
30 LOADER.get_or_init(|| {
31 let loader = fluent_language_loader!();
32 loader
33 .load_fallback_language(&Localizations)
34 .expect("fallback language (en-GB) FTL assets must be present at compile time");
35 loader
36 })
37}
38
39pub fn init(locale: Option<&LanguageIdentifier>) {
46 let requested: Vec<LanguageIdentifier> = locale.cloned().into_iter().collect();
47
48 i18n_embed::select(language_loader(), &Localizations, &requested)
49 .expect("failed to select i18n language");
50}
51
52#[macro_export]
61macro_rules! fl {
62 ($message_id:literal) => {{
63 i18n_embed_fl::fl!($crate::i18n::language_loader(), $message_id)
64 }};
65 ($message_id:literal, $($key:ident = $value:expr),* $(,)?) => {{
66 i18n_embed_fl::fl!($crate::i18n::language_loader(), $message_id, $($key = $value),*)
67 }};
68}