1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
use anyhow::{anyhow, Result};
use fluent::{bundle::FluentBundle, FluentArgs, FluentResource};
use include_dir::{include_dir, Dir, DirEntry};
use intl_memoizer::concurrent::IntlLangMemoizer;
use lazy_static::lazy_static;
use std::{
collections::HashMap,
str::{from_utf8, FromStr},
};
use unic_langid::LanguageIdentifier;
pub static RESOURCES: Dir = include_dir!("$CARGO_MANIFEST_DIR/resources/");
lazy_static! {
static ref BUNDLES: HashMap<String, FluentBundle<FluentResource, IntlLangMemoizer>> = {
let mut res = HashMap::new();
for lang in get_available_locales(&RESOURCES).expect("failed to list translations") {
match get_bundle(&RESOURCES, lang.clone(), "global") {
Err(err) => panic!("failed to load translations: {}", err),
Ok(bundle) => {
res.insert(lang.to_string(), bundle);
}
}
}
res
};
}
pub fn read_language_data(
resoure_dir: &Dir,
id: &LanguageIdentifier,
translation_name: &str,
) -> Result<String> {
let file = resoure_dir
.get_file(format!("{}/{}.ftl", id, translation_name))
.ok_or_else(|| anyhow!("'{}' translation not found", translation_name))?;
Ok(from_utf8(file.contents())?.to_string())
}
pub fn get_available_locales(resoure_dir: &Dir) -> Result<Vec<LanguageIdentifier>> {
resoure_dir
.find("*")
.map_err(|err| anyhow!("{}", err))?
.filter_map(|e| {
if let DirEntry::Dir(dir) = e {
Some(
LanguageIdentifier::from_str(dir.path().file_name()?.to_str()?)
.map_err(|err| anyhow!("{}", err)),
)
} else {
None
}
})
.collect()
}
pub fn get_bundle(
resoure_dir: &Dir,
lang: LanguageIdentifier,
translation_name: &str,
) -> Result<FluentBundle<FluentResource, IntlLangMemoizer>> {
let available = get_available_locales(resoure_dir)?;
if !available.contains(&lang) {
return Err(anyhow!("{} was not found in available languages", lang));
}
let mut bundle = FluentBundle::new_concurrent(vec![lang.clone()]);
let data = read_language_data(resoure_dir, &lang, translation_name)?;
let resource = FluentResource::try_new(data)
.map_err(|_| anyhow!("Failed to parse flt file for language {}", lang))?;
bundle.add_resource_overriding(resource);
Ok(bundle)
}
pub fn get_message(
bundle: &FluentBundle<FluentResource, IntlLangMemoizer>,
msgid: &str,
args: Option<FluentArgs>,
) -> Result<String> {
let mut errors = vec![];
let msg = bundle
.get_message(msgid)
.ok_or_else(|| anyhow!("Message `{}` was not found.", msgid))?;
let pattern = msg
.value()
.ok_or_else(|| anyhow!("Message `{}` has no value.", msgid))?;
Ok(bundle
.format_pattern(pattern, args.as_ref(), &mut errors)
.into())
}
pub fn get_message_global(msgid: &str, langid: &str, args: Option<FluentArgs>) -> String {
if let Some(bundle) = BUNDLES.get(langid) {
get_message(bundle, msgid, args).unwrap_or_else(|_| msgid.to_string())
} else {
msgid.to_string()
}
}