1#![doc = include_str!("../README.md")]
2
3#[doc(hidden)]
4pub mod meta;
5
6#[doc(hidden)]
7pub mod registry;
8
9#[cfg(feature = "derive")]
10pub use es_fluent_derive::{EsFluent, EsFluentChoice, EsFluentThis, EsFluentVariants};
11
12#[doc(hidden)]
13pub use fluent_bundle::FluentValue;
14
15#[doc(hidden)]
16pub use inventory as __inventory;
17
18#[doc(hidden)]
21pub const fn __namespace_from_file_path(path: &str) -> &str {
22 let bytes = path.as_bytes();
23 let len = bytes.len();
24
25 let mut last_slash = 0;
27 let mut i = 0;
28 while i < len {
29 if bytes[i] == b'/' || bytes[i] == b'\\' {
30 last_slash = i + 1;
31 }
32 i += 1;
33 }
34
35 let mut last_dot = len;
37 i = last_slash;
38 while i < len {
39 if bytes[i] == b'.' {
40 last_dot = i;
41 }
42 i += 1;
43 }
44
45 if last_dot == last_slash {
47 last_dot = len;
48 }
49
50 let result_len = last_dot - last_slash;
53 if result_len == 0 {
54 return "unknown";
55 }
56
57 unsafe {
59 core::str::from_utf8_unchecked(core::slice::from_raw_parts(
60 bytes.as_ptr().add(last_slash),
61 result_len,
62 ))
63 }
64}
65
66#[doc(hidden)]
69pub const fn __namespace_from_file_path_relative(path: &str) -> &str {
70 let bytes = path.as_bytes();
71 let len = bytes.len();
72
73 let mut last_dot = len;
75 let mut i = 0;
76 while i < len {
77 if bytes[i] == b'.' {
78 last_dot = i;
79 }
80 i += 1;
81 }
82
83 let start = if len >= 4
85 && bytes[0] == b's'
86 && bytes[1] == b'r'
87 && bytes[2] == b'c'
88 && (bytes[3] == b'/' || bytes[3] == b'\\')
89 {
90 4
91 } else {
92 0
93 };
94
95 let result_len = last_dot - start;
96 if result_len == 0 {
97 return "unknown";
98 }
99
100 unsafe {
102 core::str::from_utf8_unchecked(core::slice::from_raw_parts(
103 bytes.as_ptr().add(start),
104 result_len,
105 ))
106 }
107}
108
109#[doc(hidden)]
110pub use rust_embed as __rust_embed;
111
112#[doc(hidden)]
113pub use es_fluent_manager_core as __manager_core;
114
115#[doc(hidden)]
116pub use unic_langid;
117
118use arc_swap::ArcSwap;
119use es_fluent_manager_core::FluentManager;
120use std::sync::{Arc, OnceLock};
121
122mod traits;
123pub use traits::{EsFluentChoice, FluentDisplay, ThisFtl, ToFluentString};
124
125#[doc(hidden)]
126static CONTEXT: OnceLock<ArcSwap<FluentManager>> = OnceLock::new();
127
128#[doc(hidden)]
129static CUSTOM_LOCALIZER: OnceLock<
130 Box<
131 dyn Fn(&str, Option<&std::collections::HashMap<&str, FluentValue>>) -> Option<String>
132 + Send
133 + Sync,
134 >,
135> = OnceLock::new();
136
137#[doc(hidden)]
146pub fn set_context(manager: FluentManager) {
147 CONTEXT
148 .set(ArcSwap::from_pointee(manager))
149 .map_err(|_| "Context already set")
150 .expect("Failed to set context");
151}
152
153#[doc(hidden)]
162pub fn set_shared_context(manager: Arc<FluentManager>) {
163 CONTEXT
164 .set(ArcSwap::new(manager))
165 .map_err(|_| "Context already set")
166 .expect("Failed to set shared context");
167}
168
169#[doc(hidden)]
179pub fn set_custom_localizer<F>(localizer: F)
180where
181 F: Fn(&str, Option<&std::collections::HashMap<&str, FluentValue>>) -> Option<String>
182 + Send
183 + Sync
184 + 'static,
185{
186 CUSTOM_LOCALIZER
187 .set(Box::new(localizer))
188 .map_err(|_| "Custom localizer already set")
189 .expect("Failed to set custom localizer");
190}
191
192#[doc(hidden)]
194pub fn select_language(lang: &unic_langid::LanguageIdentifier) {
195 if let Some(context) = CONTEXT.get() {
196 context.load().select_language(lang);
197 }
198}
199
200#[doc(hidden)]
209pub fn localize<'a>(
210 id: &str,
211 args: Option<&std::collections::HashMap<&str, FluentValue<'a>>>,
212) -> String {
213 if let Some(custom_localizer) = CUSTOM_LOCALIZER.get()
214 && let Some(message) = custom_localizer(id, args)
215 {
216 return message;
217 }
218
219 if let Some(context) = CONTEXT.get()
220 && let Some(message) = context.load().localize(id, args)
221 {
222 return message;
223 }
224
225 tracing::warn!("Translation for '{}' not found or context not set.", id);
226 id.to_string()
227}