From 958f5580b6e761c584707a93ea45c1d492a42f0a Mon Sep 17 00:00:00 2001 From: oech3 <79379754+oech3@users.noreply.github.com> Date: Mon, 9 Mar 2026 15:48:35 +0900 Subject: [PATCH] uucore: share a FluentResource between threads --- src/uucore/src/lib/mods/locale.rs | 72 ++++++++++++++++++++----------- 1 file changed, 47 insertions(+), 25 deletions(-) diff --git a/src/uucore/src/lib/mods/locale.rs b/src/uucore/src/lib/mods/locale.rs index e7d05f4c723..f247c87c89a 100644 --- a/src/uucore/src/lib/mods/locale.rs +++ b/src/uucore/src/lib/mods/locale.rs @@ -65,19 +65,19 @@ include!(concat!(env!("OUT_DIR"), "/embedded_locales.rs")); // A struct to handle localization with optional English fallback struct Localizer { - primary_bundle: FluentBundle, - fallback_bundle: Option>, + primary_bundle: FluentBundle<&'static FluentResource>, + fallback_bundle: Option>, } impl Localizer { - fn new(primary_bundle: FluentBundle) -> Self { + fn new(primary_bundle: FluentBundle<&'static FluentResource>) -> Self { Self { primary_bundle, fallback_bundle: None, } } - fn with_fallback(mut self, fallback_bundle: FluentBundle) -> Self { + fn with_fallback(mut self, fallback_bundle: FluentBundle<&'static FluentResource>) -> Self { self.fallback_bundle = Some(fallback_bundle); self } @@ -107,7 +107,10 @@ impl Localizer { } } -// Global localizer stored in thread-local OnceLock +// Cache localizer. FluentBundle is not Sync while FluentResource is Sync +static UUCORE_FLUENT: OnceLock = OnceLock::new(); +static CHECKSUM_FLUENT: OnceLock = OnceLock::new(); +static UTIL_FLUENT: OnceLock = OnceLock::new(); thread_local! { static LOCALIZER: OnceLock = const { OnceLock::new() }; } @@ -136,8 +139,8 @@ fn create_bundle( locale: &LanguageIdentifier, locales_dir: &Path, util_name: &str, -) -> Result, LocalizationError> { - let mut bundle = FluentBundle::new(vec![locale.clone()]); +) -> Result, LocalizationError> { + let mut bundle: FluentBundle<&'static FluentResource> = FluentBundle::new(vec![locale.clone()]); // Disable Unicode directional isolate characters bundle.set_use_isolating(false); @@ -148,7 +151,7 @@ fn create_bundle( .and_then(|locale_path| fs::read_to_string(locale_path).ok()) .and_then(|ftl| FluentResource::try_new(ftl).ok()) { - bundle.add_resource_overriding(resource); + bundle.add_resource_overriding(Box::leak(Box::new(resource))); } }; @@ -193,10 +196,11 @@ fn init_localization( .expect("Default locale should always be valid"); // Try to create a bundle that combines common and utility-specific strings - let english_bundle = create_bundle(&default_locale, locales_dir, util_name).or_else(|_| { - // Fallback to embedded utility-specific and common strings - create_english_bundle_from_embedded(&default_locale, util_name) - })?; + let english_bundle: FluentBundle<&'static FluentResource> = + create_bundle(&default_locale, locales_dir, util_name).or_else(|_| { + // Fallback to embedded utility-specific and common strings + create_english_bundle_from_embedded(&default_locale, util_name) + })?; let loc = if locale == &default_locale { // If requesting English, just use English as primary (no fallback needed) @@ -220,8 +224,18 @@ fn init_localization( } /// Helper function to parse FluentResource from content string -fn parse_fluent_resource(content: &str) -> Result { - FluentResource::try_new(content.to_string()).map_err( +fn parse_fluent_resource( + content: &str, + cache: &'static OnceLock, +) -> Result<&'static FluentResource, LocalizationError> { + // globa cache breaks unit tests + if cfg!(not(test)) { + if let Some(res) = cache.get() { + return Ok(res); + } + } + + let resource = FluentResource::try_new(content.to_string()).map_err( |(_partial_resource, errs): (FluentResource, Vec)| { if let Some(first_err) = errs.into_iter().next() { let snippet = first_err @@ -238,14 +252,20 @@ fn parse_fluent_resource(content: &str) -> Result Result, LocalizationError> { +) -> Result, LocalizationError> { // Only support English from embedded files if *locale != "en-US" { return Err(LocalizationError::LocalesDirNotFound( @@ -253,19 +273,19 @@ fn create_english_bundle_from_embedded( )); } - let mut bundle = FluentBundle::new(vec![locale.clone()]); + let mut bundle: FluentBundle<&'static FluentResource> = FluentBundle::new(vec![locale.clone()]); bundle.set_use_isolating(false); // First, try to load common uucore strings if let Some(uucore_content) = get_embedded_locale("uucore/en-US.ftl") { - let uucore_resource = parse_fluent_resource(uucore_content)?; + let uucore_resource = parse_fluent_resource(uucore_content, &UUCORE_FLUENT)?; bundle.add_resource_overriding(uucore_resource); } // Checksum algorithms need locale messages from checksum_common if util_name.ends_with("sum") { if let Some(uucore_content) = get_embedded_locale("checksum_common/en-US.ftl") { - let uucore_resource = parse_fluent_resource(uucore_content)?; + let uucore_resource = parse_fluent_resource(uucore_content, &CHECKSUM_FLUENT)?; bundle.add_resource_overriding(uucore_resource); } } @@ -273,7 +293,7 @@ fn create_english_bundle_from_embedded( // Then, try to load utility-specific strings let locale_key = format!("{util_name}/en-US.ftl"); if let Some(ftl_content) = get_embedded_locale(&locale_key) { - let resource = parse_fluent_resource(ftl_content)?; + let resource = parse_fluent_resource(ftl_content, &UTIL_FLUENT)?; bundle.add_resource_overriding(resource); } @@ -428,7 +448,8 @@ pub fn setup_localization(p: &str) -> Result<(), LocalizationError> { // No locales directory found, use embedded English with common strings directly let default_locale = LanguageIdentifier::from_str(DEFAULT_LOCALE) .expect("Default locale should always be valid"); - let english_bundle = create_english_bundle_from_embedded(&default_locale, p)?; + let english_bundle: FluentBundle<&'static FluentResource> = + create_english_bundle_from_embedded(&default_locale, p)?; let localizer = Localizer::new(english_bundle); LOCALIZER.with(|lock| { @@ -593,14 +614,15 @@ mod tests { fn create_test_bundle( locale: &LanguageIdentifier, test_locales_dir: &Path, - ) -> Result, LocalizationError> { - let mut bundle = FluentBundle::new(vec![locale.clone()]); + ) -> Result, LocalizationError> { + let mut bundle: FluentBundle<&'static FluentResource> = + FluentBundle::new(vec![locale.clone()]); bundle.set_use_isolating(false); // Only load from the test directory - no common strings or utility-specific paths let locale_path = test_locales_dir.join(format!("{locale}.ftl")); if let Ok(ftl_content) = fs::read_to_string(&locale_path) { - let resource = parse_fluent_resource(&ftl_content)?; + let resource = parse_fluent_resource(&ftl_content, &UUCORE_FLUENT)?; bundle.add_resource_overriding(resource); return Ok(bundle); } @@ -763,7 +785,7 @@ invalid-syntax = This is { $missing #[test] fn test_localizer_format_primary_bundle() { let temp_dir = create_test_locales_dir(); - let en_bundle = create_test_bundle( + let en_bundle: FluentBundle<&'static FluentResource> = create_test_bundle( &LanguageIdentifier::from_str("en-US").unwrap(), temp_dir.path(), )