/ bin / app / src / util / i18n.rs
i18n.rs
 1  /* This file is part of DarkFi (https://dark.fi)
 2   *
 3   * Copyright (C) 2020-2025 Dyne.org foundation
 4   *
 5   * This program is free software: you can redistribute it and/or modify
 6   * it under the terms of the GNU Affero General Public License as
 7   * published by the Free Software Foundation, either version 3 of the
 8   * License, or (at your option) any later version.
 9   *
10   * This program is distributed in the hope that it will be useful,
11   * but WITHOUT ANY WARRANTY; without even the implied warranty of
12   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13   * GNU Affero General Public License for more details.
14   *
15   * You should have received a copy of the GNU Affero General Public License
16   * along with this program.  If not, see <https://www.gnu.org/licenses/>.
17   */
18  
19  use fluent::{concurrent::FluentBundle, FluentResource};
20  use parking_lot::RwLock;
21  use std::sync::Arc;
22  use unic_langid::langid;
23  
24  pub type I18nResource = Arc<FluentResource>;
25  
26  pub struct I18nBabelFish {
27      bundle: RwLock<Arc<FluentBundle<I18nResource>>>,
28  }
29  
30  impl I18nBabelFish {
31      pub fn new(src: String, lang: &str) -> Self {
32          let mut langs = vec![lang.parse().unwrap()];
33          if lang != "en-US" {
34              langs.push(langid!("en-US"));
35          }
36  
37          let res = Arc::new(FluentResource::try_new(src).unwrap());
38          let mut bundle = FluentBundle::new_concurrent(langs);
39          bundle.add_resource(res).unwrap();
40          Self { bundle: RwLock::new(Arc::new(bundle)) }
41      }
42  
43      pub fn set(&self, other: &Self) {
44          let bundle = other.bundle.read();
45          *self.bundle.write() = Arc::clone(&*bundle);
46      }
47  
48      pub fn tr(&self, id: &str) -> Option<String> {
49          let bundle = self.bundle.read();
50          let msg = bundle.get_message(id)?;
51          let patt = msg.value()?;
52          // See FluentBundle::format_pattern()
53          // this is where we can also pass args into the ftl str
54          let mut errs = vec![];
55          let res = bundle.format_pattern(&patt, None, &mut errs);
56          Some(res.into_owned())
57      }
58  }
59  
60  impl Clone for I18nBabelFish {
61      fn clone(&self) -> Self {
62          let bundle = self.bundle.read();
63          Self { bundle: RwLock::new(Arc::clone(&*bundle)) }
64      }
65  }