1#[cfg(feature = "handlebars")]
6mod handlebars;
7
8#[cfg(feature = "tera")]
9mod tera;
10
11mod multi_loader;
12mod shared;
13
14use std::borrow::Cow;
15use std::collections::HashMap;
16
17use crate::FluentBundle;
18use fluent_bundle::{FluentArgs, FluentResource, FluentValue};
19use fluent_langneg::negotiate_languages;
20
21pub use unic_langid::{langid, langids, LanguageIdentifier};
22
23mod arc_loader;
24mod static_loader;
25
26pub use arc_loader::{ArcLoader, ArcLoaderBuilder};
27pub use multi_loader::MultiLoader;
28pub use static_loader::StaticLoader;
29
30pub trait Loader {
32 fn lookup(&self, lang: &LanguageIdentifier, text_id: &str) -> String {
34 self.lookup_complete(lang, text_id, None)
35 }
36
37 fn lookup_with_args(
39 &self,
40 lang: &LanguageIdentifier,
41 text_id: &str,
42 args: &HashMap<Cow<'static, str>, FluentValue>,
43 ) -> String {
44 self.lookup_complete(lang, text_id, Some(args))
45 }
46
47 fn lookup_complete(
49 &self,
50 lang: &LanguageIdentifier,
51 text_id: &str,
52 args: Option<&HashMap<Cow<'static, str>, FluentValue>>,
53 ) -> String;
54
55 fn try_lookup(&self, lang: &LanguageIdentifier, text_id: &str) -> Option<String> {
57 self.try_lookup_complete(lang, text_id, None)
58 }
59
60 fn try_lookup_with_args(
62 &self,
63 lang: &LanguageIdentifier,
64 text_id: &str,
65 args: &HashMap<Cow<'static, str>, FluentValue>,
66 ) -> Option<String> {
67 self.try_lookup_complete(lang, text_id, Some(args))
68 }
69
70 fn try_lookup_complete(
72 &self,
73 lang: &LanguageIdentifier,
74 text_id: &str,
75 args: Option<&HashMap<Cow<'static, str>, FluentValue>>,
76 ) -> Option<String>;
77
78 fn locales(&self) -> Box<dyn Iterator<Item = &LanguageIdentifier> + '_>;
80}
81
82impl<L> Loader for std::sync::Arc<L>
83where
84 L: Loader,
85{
86 fn lookup_complete(
87 &self,
88 lang: &LanguageIdentifier,
89 text_id: &str,
90 args: Option<&HashMap<Cow<'static, str>, FluentValue>>,
91 ) -> String {
92 L::lookup_complete(self, lang, text_id, args)
93 }
94
95 fn try_lookup_complete(
96 &self,
97 lang: &LanguageIdentifier,
98 text_id: &str,
99 args: Option<&HashMap<Cow<'static, str>, FluentValue>>,
100 ) -> Option<String> {
101 L::try_lookup_complete(self, lang, text_id, args)
102 }
103
104 fn locales(&self) -> Box<dyn Iterator<Item = &LanguageIdentifier> + '_> {
105 L::locales(self)
106 }
107}
108
109impl<'a, L> Loader for &'a L
110where
111 L: Loader,
112{
113 fn lookup_complete(
114 &self,
115 lang: &LanguageIdentifier,
116 text_id: &str,
117 args: Option<&HashMap<Cow<'static, str>, FluentValue>>,
118 ) -> String {
119 L::lookup_complete(self, lang, text_id, args)
120 }
121
122 fn try_lookup_complete(
123 &self,
124 lang: &LanguageIdentifier,
125 text_id: &str,
126 args: Option<&HashMap<Cow<'static, str>, FluentValue>>,
127 ) -> Option<String> {
128 L::try_lookup_complete(self, lang, text_id, args)
129 }
130
131 fn locales(&self) -> Box<dyn Iterator<Item = &LanguageIdentifier> + '_> {
132 L::locales(self)
133 }
134}
135
136pub struct FluentLoader<L> {
139 loader: L,
140 #[allow(unused)]
141 default_lang: Option<LanguageIdentifier>,
142}
143
144impl<L> FluentLoader<L> {
145 pub fn new(loader: L) -> Self {
147 Self {
148 loader,
149 default_lang: None,
150 }
151 }
152
153 pub fn with_default_lang(self, lang: LanguageIdentifier) -> Self {
157 Self {
158 loader: self.loader,
159 default_lang: Some(lang),
160 }
161 }
162}
163
164pub fn build_fallbacks(
166 locales: &[LanguageIdentifier],
167) -> HashMap<LanguageIdentifier, Vec<LanguageIdentifier>> {
168 let mut map = HashMap::new();
169
170 for locale in locales.iter() {
171 map.insert(
172 locale.to_owned(),
173 negotiate_languages(
174 &[locale],
175 locales,
176 None,
177 fluent_langneg::NegotiationStrategy::Filtering,
178 )
179 .into_iter()
180 .cloned()
181 .collect::<Vec<_>>(),
182 );
183 }
184
185 map
186}
187
188fn create_bundle(
192 lang: LanguageIdentifier,
193 resources: &'static [FluentResource],
194 core_resource: Option<&'static FluentResource>,
195 customizer: &impl Fn(&mut FluentBundle<&'static FluentResource>),
196) -> FluentBundle<&'static FluentResource> {
197 let mut bundle: FluentBundle<&'static FluentResource> =
198 FluentBundle::new_concurrent(vec![lang]);
199 if let Some(core) = core_resource {
200 bundle
201 .add_resource(core)
202 .expect("Failed to add core resource to bundle");
203 }
204 for res in resources {
205 bundle
206 .add_resource(res)
207 .expect("Failed to add FTL resources to the bundle.");
208 }
209
210 customizer(&mut bundle);
211 bundle
212}
213
214pub fn build_bundles(
217 resources: &'static HashMap<LanguageIdentifier, Vec<FluentResource>>,
218 core_resource: Option<&'static FluentResource>,
219 customizer: impl Fn(&mut FluentBundle<&'static FluentResource>),
220) -> HashMap<LanguageIdentifier, FluentBundle<&'static FluentResource>> {
221 let mut bundles = HashMap::new();
222 for (k, v) in resources.iter() {
223 bundles.insert(
224 k.clone(),
225 create_bundle(k.clone(), v, core_resource, &customizer),
226 );
227 }
228 bundles
229}
230
231fn map_to_fluent_args<'map, T: AsRef<str>>(map: &'map HashMap<T, FluentValue>) -> FluentArgs<'map> {
232 map.iter()
233 .map(|(key, value)| (key.as_ref(), value.clone()))
234 .collect()
235}