es_fluent/
lib.rs

1#![doc = include_str!("../README.md")]
2
3#[cfg(feature = "derive")]
4pub use es_fluent_derive::{EsFluent, EsFluentChoice, EsFluentKv};
5
6#[doc(hidden)]
7pub use es_fluent_manager_core::{FluentManager, I18nModule, LocalizationError, Localizer};
8
9#[doc(hidden)]
10pub use fluent_bundle::FluentValue;
11
12#[doc(hidden)]
13pub use inventory as __inventory;
14
15#[doc(hidden)]
16pub use rust_embed as __rust_embed;
17
18#[doc(hidden)]
19pub use es_fluent_manager_core as __manager_core;
20
21#[doc(hidden)]
22pub use unic_langid;
23
24mod traits;
25pub use traits::{EsFluentChoice, FluentDisplay, ThisFtl, ToFluentString};
26
27use std::sync::{Arc, OnceLock, RwLock};
28
29#[doc(hidden)]
30static CONTEXT: OnceLock<Arc<RwLock<FluentManager>>> = OnceLock::new();
31
32#[doc(hidden)]
33static CUSTOM_LOCALIZER: OnceLock<
34    Box<
35        dyn Fn(&str, Option<&std::collections::HashMap<&str, FluentValue>>) -> Option<String>
36            + Send
37            + Sync,
38    >,
39> = OnceLock::new();
40
41/// Sets the global `FluentManager` context.
42///
43/// This function should be called once at the beginning of your application's
44/// lifecycle.
45///
46/// # Panics
47///
48/// This function will panic if the context has already been set.
49#[doc(hidden)]
50pub fn set_context(manager: FluentManager) {
51    CONTEXT
52        .set(Arc::new(RwLock::new(manager)))
53        .map_err(|_| "Context already set")
54        .expect("Failed to set context");
55}
56
57/// Sets the global `FluentManager` context with a shared `Arc<RwLock<FluentManager>>`.
58///
59/// This function is useful when you want to share the `FluentManager` between
60/// multiple threads.
61///
62/// # Panics
63///
64/// This function will panic if the context has already been set.
65#[doc(hidden)]
66pub fn set_shared_context(manager: Arc<RwLock<FluentManager>>) {
67    CONTEXT
68        .set(manager)
69        .map_err(|_| "Context already set")
70        .expect("Failed to set shared context");
71}
72
73/// Sets a custom localizer function.
74///
75/// The custom localizer will be called before the global context's `localize`
76/// method. If the custom localizer returns `Some(message)`, the message will be
77/// returned. Otherwise, the global context will be used.
78///
79/// # Panics
80///
81/// This function will panic if the custom localizer has already been set.
82#[doc(hidden)]
83pub fn set_custom_localizer<F>(localizer: F)
84where
85    F: Fn(&str, Option<&std::collections::HashMap<&str, FluentValue>>) -> Option<String>
86        + Send
87        + Sync
88        + 'static,
89{
90    CUSTOM_LOCALIZER
91        .set(Box::new(localizer))
92        .map_err(|_| "Custom localizer already set")
93        .expect("Failed to set custom localizer");
94}
95
96/// Updates the global `FluentManager` context.
97#[doc(hidden)]
98pub fn update_context<F>(f: F)
99where
100    F: FnOnce(&mut FluentManager),
101{
102    if let Some(context_arc) = CONTEXT.get() {
103        let mut context = context_arc
104            .write()
105            .expect("Failed to acquire write lock on context");
106        f(&mut context);
107    }
108}
109
110/// Localizes a message by its ID.
111///
112/// This function will first try to use the custom localizer if it has been set.
113/// If the custom localizer returns `None`, it will then try to use the global
114/// context.
115///
116/// If the message is not found, a warning will be logged and the ID will be
117/// returned as the message.
118#[doc(hidden)]
119pub fn localize<'a>(
120    id: &str,
121    args: Option<&std::collections::HashMap<&str, FluentValue<'a>>>,
122) -> String {
123    if let Some(custom_localizer) = CUSTOM_LOCALIZER.get()
124        && let Some(message) = custom_localizer(id, args)
125    {
126        return message;
127    }
128
129    if let Some(context_arc) = CONTEXT.get() {
130        let context = context_arc
131            .read()
132            .expect("Failed to acquire read lock on context");
133
134        if let Some(message) = context.localize(id, args) {
135            return message;
136        }
137    }
138
139    log::warn!("Translation for '{}' not found or context not set.", id);
140    id.to_string()
141}