pslink_locales/
lib.rs

1//! This modules contains the parts for making the app translatable.
2use std::sync::Arc;
3
4use fluent::{FluentArgs, FluentBundle, FluentResource};
5use pslink_shared::datatypes::Lang;
6use unic_langid::LanguageIdentifier;
7
8/// A struct containing the data, functions and the current language to query the localized strings.
9#[derive(Clone)]
10pub struct I18n {
11    lang: Lang,
12    ftl_bundle: Arc<FluentBundle<FluentResource>>,
13}
14
15impl std::fmt::Debug for I18n {
16    /// On debug print skip the bundle
17    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
18        write!(f, "{:?}", self.lang)
19    }
20}
21
22impl I18n {
23    /// Create a new translator struct
24    #[must_use]
25    pub fn new(lang: Lang) -> Self {
26        let ftl_bundle = Arc::new(Self::create_ftl_bundle(lang));
27        Self { lang, ftl_bundle }
28    }
29
30    /// Get the current language
31    #[must_use]
32    pub const fn lang(&self) -> &Lang {
33        &self.lang
34    }
35
36    /// Set the current language
37    pub fn set_lang(&mut self, lang: Lang) {
38        self.lang = lang;
39        self.ftl_bundle = Arc::new(Self::create_ftl_bundle(lang));
40    }
41
42    /// Get a localized string. Optionally with parameters provided in `args`.
43    pub fn translate(&self, key: impl AsRef<str>, args: Option<&FluentArgs>) -> String {
44        // log!(key.as_ref());
45        let msg = self
46            .ftl_bundle
47            .get_message(key.as_ref())
48            .expect("Failed to get fluent message for key {}");
49
50        let pattern = msg.value().expect("Failed to parse pattern");
51
52        self.ftl_bundle
53            .format_pattern(pattern, args, &mut vec![])
54            .to_string()
55    }
56
57    /// Prettyprint the language name
58    #[must_use]
59    pub const fn label(&self) -> &'static str {
60        match self.lang {
61            Lang::EnUS => "English (US)",
62            Lang::DeDE => "Deutsch (Deutschland)",
63        }
64    }
65
66    /// include the fluent messages into the binary
67    #[must_use]
68    pub const fn ftl_messages(lang: Lang) -> &'static str {
69        macro_rules! include_ftl_messages {
70            ( $lang_id:literal ) => {
71                include_str!(concat!("../", $lang_id, "/main.ftl"))
72            };
73        }
74        match lang {
75            Lang::EnUS => include_ftl_messages!("en"),
76            Lang::DeDE => include_ftl_messages!("de"),
77        }
78    }
79
80    #[must_use]
81    pub fn language_identifier(lang: Lang) -> LanguageIdentifier {
82        lang.as_ref()
83            .parse()
84            .expect("parse Lang to LanguageIdentifier")
85    }
86
87    /// Create and initialize a fluent bundle.
88    #[must_use]
89    pub fn create_ftl_bundle(lang: Lang) -> FluentBundle<FluentResource> {
90        let ftl_resource = FluentResource::try_new(Self::ftl_messages(lang).to_owned())
91            .expect("parse FTL messages");
92
93        let mut bundle = FluentBundle::new(vec![Self::language_identifier(lang)]);
94        bundle.add_resource(ftl_resource).expect("add FTL resource");
95        bundle
96    }
97}