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<L> Loader for &L
110where
111 L: Loader + ?Sized,
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
136impl<T: Loader + ?Sized> Loader for Box<T> {
137 fn lookup_complete(
138 &self,
139 lang: &LanguageIdentifier,
140 text_id: &str,
141 args: Option<&HashMap<Cow<'static, str>, FluentValue>>,
142 ) -> String {
143 self.as_ref().lookup_complete(lang, text_id, args)
144 }
145
146 fn try_lookup_complete(
147 &self,
148 lang: &LanguageIdentifier,
149 text_id: &str,
150 args: Option<&HashMap<Cow<'static, str>, FluentValue>>,
151 ) -> Option<String> {
152 self.as_ref().try_lookup_complete(lang, text_id, args)
153 }
154
155 fn locales(&self) -> Box<dyn Iterator<Item = &LanguageIdentifier> + '_> {
156 self.as_ref().locales()
157 }
158}
159
160pub struct FluentLoader<L> {
163 loader: L,
164 #[allow(unused)]
165 default_lang: Option<LanguageIdentifier>,
166}
167
168impl<L> FluentLoader<L> {
169 pub fn new(loader: L) -> Self {
171 Self {
172 loader,
173 default_lang: None,
174 }
175 }
176
177 pub fn with_default_lang(self, lang: LanguageIdentifier) -> Self {
181 Self {
182 loader: self.loader,
183 default_lang: Some(lang),
184 }
185 }
186}
187
188pub fn build_fallbacks(
190 locales: &[LanguageIdentifier],
191) -> HashMap<LanguageIdentifier, Vec<LanguageIdentifier>> {
192 let mut map = HashMap::new();
193
194 for locale in locales.iter() {
195 map.insert(
196 locale.to_owned(),
197 negotiate_languages(
198 &[locale],
199 locales,
200 None,
201 fluent_langneg::NegotiationStrategy::Filtering,
202 )
203 .into_iter()
204 .cloned()
205 .collect::<Vec<_>>(),
206 );
207 }
208
209 map
210}
211
212fn create_bundle(
216 lang: LanguageIdentifier,
217 resources: &'static [FluentResource],
218 core_resource: Option<&'static FluentResource>,
219 customizer: &impl Fn(&mut FluentBundle<&'static FluentResource>),
220) -> FluentBundle<&'static FluentResource> {
221 let mut bundle: FluentBundle<&'static FluentResource> =
222 FluentBundle::new_concurrent(vec![lang]);
223 if let Some(core) = core_resource {
224 bundle
225 .add_resource(core)
226 .expect("Failed to add core resource to bundle");
227 }
228 for res in resources {
229 bundle
230 .add_resource(res)
231 .expect("Failed to add FTL resources to the bundle.");
232 }
233
234 customizer(&mut bundle);
235 bundle
236}
237
238pub fn build_bundles(
241 resources: &'static HashMap<LanguageIdentifier, Vec<FluentResource>>,
242 core_resource: Option<&'static FluentResource>,
243 customizer: impl Fn(&mut FluentBundle<&'static FluentResource>),
244) -> HashMap<LanguageIdentifier, FluentBundle<&'static FluentResource>> {
245 let mut bundles = HashMap::new();
246 for (k, v) in resources.iter() {
247 bundles.insert(
248 k.clone(),
249 create_bundle(k.clone(), v, core_resource, &customizer),
250 );
251 }
252 bundles
253}
254
255fn map_to_fluent_args<'map, T: AsRef<str>>(map: &'map HashMap<T, FluentValue>) -> FluentArgs<'map> {
256 map.iter()
257 .map(|(key, value)| (key.as_ref(), value.clone()))
258 .collect()
259}