| 1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677 |
- //! Process-global locale store + locale-aware task spawner.
- //!
- //! `fluent_i18n::set_locale` writes to a `thread_local!` `RefCell`, so
- //! `tokio::spawn` worker threads do NOT inherit the locale set on the main
- //! thread — every `t!` call inside a spawned task falls back to English.
- //! This module closes that gap: [`set_global_locale`] captures the locale
- //! once at startup (process-global `OnceLock`), and [`spawn_with_locale`]
- //! re-applies it at the start of each spawned task so `t!` renders in the
- //! user's locale, not the fallback.
- use std::future::Future;
- use std::sync::OnceLock;
- use tokio::task::JoinHandle;
- /// Process-global locale string (e.g. `"ru-RU"`), set once on the main
- /// thread at startup. Read by [`spawn_with_locale`] before each task.
- static GLOBAL_LOCALE: OnceLock<Option<String>> = OnceLock::new();
- /// Capture the locale once at startup. Called from `main` after
- /// `set_locale`. Subsequent calls are no-ops (first wins).
- pub fn set_global_locale(locale: Option<String>) {
- let _ = GLOBAL_LOCALE.set(locale);
- }
- /// Like `tokio::spawn`, but re-applies the process-global locale to the
- /// worker thread's thread-local before polling the future, so `t!` calls
- /// inside the task render in the user's locale.
- pub fn spawn_with_locale<F, T>(future: F) -> JoinHandle<T>
- where
- F: Future<Output = T> + Send + 'static,
- T: Send + 'static,
- {
- let locale = GLOBAL_LOCALE.get().and_then(|opt| opt.clone());
- tokio::spawn(async move {
- if let Some(ref loc) = locale {
- let _ = fluent_i18n::set_locale(Some(loc.as_str()));
- }
- future.await
- })
- }
- /// Re-apply the process-global locale to the CURRENT thread. Call at the
- /// top of a `tokio::task::spawn_blocking` closure (those threads are
- /// fresh and do not inherit the thread-local locale either).
- pub fn apply_global_locale() {
- if let Some(loc) = GLOBAL_LOCALE.get().and_then(|opt| opt.clone()) {
- let _ = fluent_i18n::set_locale(Some(loc.as_str()));
- }
- }
- #[cfg(test)]
- mod tests {
- use super::*;
- #[tokio::test]
- async fn spawn_with_locale_inherits_locale() {
- set_global_locale(Some("ru-RU".to_string()));
- let result = spawn_with_locale(async {
- fluent_i18n::t!(
- "composed-dir-built",
- {
- "host" => "/tmp/x".to_string(),
- "node" => "n".to_string(),
- "sources" => 3u32,
- "files" => 1u32,
- "local" => 2u32,
- }
- )
- })
- .await
- .unwrap();
- assert!(
- result.contains("источник"),
- "expected Russian output (источник), got: {result}"
- );
- }
- }
|