From ea5dfae95fcd1434d3496c14b71d949cce783d52 Mon Sep 17 00:00:00 2001 From: bflatastic Date: Fri, 9 Oct 2026 23:15:54 +0700 Subject: [PATCH] i18n: interface translations are looked up by hash, not by scanning the catalog (#398) tr() scanned the whole catalog (about 380 rows) for every string a panel draws, every frame, in Chinese. Each catalog is now indexed once, on first use, in a HashMap keyed by the English source; English and languages without rows still skip the lookup. The catalogs already have no duplicate sources (catalogs_are_unique_and_keep_the_english_shape), so the index finds the same rows. Test: the i18n tests (pass-through, tr_args, catalog shape). Co-Authored-By: Claude Opus 5.5 --- crates/ui-egui/src/i18n/ui.rs | 24 ++++++++++++++++-------- 1 file changed, 16 insertions(+), 8 deletions(-) diff --git a/crates/ui-egui/src/i18n/ui.rs b/crates/ui-egui/src/i18n/ui.rs index 8e173385..9057c82d 100644 --- a/crates/ui-egui/src/i18n/ui.rs +++ b/crates/ui-egui/src/i18n/ui.rs @@ -14,7 +14,9 @@ //! drop a value, and `tr_args` itself cannot panic. use std::cell::Cell; +use std::collections::HashMap; use std::fmt::Display; +use std::sync::OnceLock; thread_local! { /// The resolved interface language ("en", "ja", "zh-hans", "zh-hant"); "en" until the first @@ -33,18 +35,24 @@ pub(crate) fn current() -> &'static str { CURRENT.with(Cell::get) } -/// The catalog for `language`: empty for English and for any language that has no rows yet. -fn catalog(language: &str) -> &'static [(&'static str, &'static str)] { - match language { - "zh-hans" => SIMPLIFIED_CHINESE, - "zh-hant" => TRADITIONAL_CHINESE, - _ => &[], - } +type Index = HashMap<&'static str, &'static str>; + +/// The catalog for `language`, indexed by English source (built on first use: panels translate +/// hundreds of strings a frame); `None` for English and any language that has no rows yet. +fn catalog(language: &str) -> Option<&'static Index> { + static HANS: OnceLock = OnceLock::new(); + static HANT: OnceLock = OnceLock::new(); + let (index, rows) = match language { + "zh-hans" => (&HANS, SIMPLIFIED_CHINESE), + "zh-hant" => (&HANT, TRADITIONAL_CHINESE), + _ => return None, + }; + Some(index.get_or_init(|| rows.iter().copied().collect())) } /// Translate one interface string: the current language's row for `text`, or `text` itself. pub(crate) fn tr(text: &str) -> &str { - catalog(current()).iter().find(|(en, _)| *en == text).map_or(text, |(_, translated)| *translated) + catalog(current()).and_then(|c| c.get(text)).copied().unwrap_or(text) } /// Translate an interface string that carries values: each `{}` takes the next argument. A missing