Skip to main content

es_fluent_manager_core/
localization.rs

1//! This module provides the core types for managing translations.
2
3use es_fluent_derive_core::EsFluentError;
4use fluent_bundle::FluentValue;
5use std::collections::HashMap;
6use unic_langid::LanguageIdentifier;
7
8pub type LocalizationError = EsFluentError;
9
10pub trait Localizer: Send + Sync {
11    /// Selects a language for the localizer.
12    fn select_language(
13        &self,
14        lang: &LanguageIdentifier,
15    ) -> es_fluent_derive_core::EsFluentResult<()>;
16    /// Localizes a message by its ID.
17    fn localize<'a>(
18        &self,
19        id: &str,
20        args: Option<&HashMap<&str, FluentValue<'a>>>,
21    ) -> Option<String>;
22}
23
24pub trait I18nModule: Send + Sync {
25    /// Returns the name of the module.
26    fn name(&self) -> &'static str;
27    /// Creates a localizer for the module.
28    fn create_localizer(&self) -> Box<dyn Localizer>;
29}
30
31inventory::collect!(&'static dyn I18nModule);
32
33/// A manager for Fluent translations.
34#[derive(Default)]
35pub struct FluentManager {
36    localizers: Vec<Box<dyn Localizer>>,
37}
38
39impl Clone for FluentManager {
40    fn clone(&self) -> Self {
41        Self::new_with_discovered_modules()
42    }
43}
44
45impl FluentManager {
46    /// Creates a new `FluentManager` with discovered i18n modules.
47    pub fn new_with_discovered_modules() -> Self {
48        let mut manager = Self::default();
49        for module in inventory::iter::<&'static dyn I18nModule>() {
50            tracing::info!("Discovered and loading i18n module: {}", module.name());
51            manager.localizers.push(module.create_localizer());
52        }
53        manager
54    }
55
56    /// Selects a language for all localizers.
57    pub fn select_language(&self, lang: &LanguageIdentifier) {
58        for localizer in &self.localizers {
59            if let Err(e) = localizer.select_language(lang) {
60                tracing::warn!("Module failed to set language '{}': {}", lang, e);
61            }
62        }
63    }
64
65    /// Localizes a message by its ID.
66    pub fn localize<'a>(
67        &self,
68        id: &str,
69        args: Option<&HashMap<&str, FluentValue<'a>>>,
70    ) -> Option<String> {
71        for localizer in &self.localizers {
72            if let Some(message) = localizer.localize(id, args) {
73                return Some(message);
74            }
75        }
76        None
77    }
78}