fix(i18n): the web build's Auto language follows the browser (#2954) (#2998)

On wasm, detect_system_lang() always returned English, so Auto showed
English whatever the browser's language. It now reads
navigator.languages in order, then navigator.language, and takes the
first tag with a catalog, as the desktop builds do with the system
languages and as VectorCraft and FilmCraft do in their browser builds.

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Kurtulus Gemici
2026-10-18 03:14:01 -04:00
committed by GitHub
parent 233767d181
commit 70d7df548e
2 changed files with 31 additions and 2 deletions
+1 -1
View File
@@ -71,7 +71,7 @@ rfd = { workspace = true, features = ["file-handle-inner"] }
wasm-bindgen-futures = "0.4"
wasm-bindgen = "0.2"
js-sys = "0.3"
web-sys = { version = "0.3", features = ["Window", "Document", "Element", "HtmlElement", "HtmlAnchorElement", "Blob", "BlobPropertyBag", "Url", "HtmlIFrameElement", "HtmlImageElement", "HtmlCollection", "Node", "EventTarget"] }
web-sys = { version = "0.3", features = ["Window", "Document", "Element", "HtmlElement", "HtmlAnchorElement", "Blob", "BlobPropertyBag", "Url", "HtmlIFrameElement", "HtmlImageElement", "HtmlCollection", "Node", "EventTarget", "Navigator"] }
[dev-dependencies]
pdfcraft-xfa = { workspace = true }
+30 -1
View File
@@ -390,9 +390,23 @@ fn first_supported(list: &str) -> Option<Lang> {
list.split(['(', ')', ',', '"', '\n']).map(str::trim).filter(|s| !s.is_empty()).find_map(lang_from_tag)
}
/// The first supported language among BCP 47 tags in preference order, as the browser lists them
/// in `navigator.languages` (#2954). Only the first few tags are looked at: the list is short in
/// practice, and a page can't make Auto walk an unbounded one.
#[cfg_attr(not(target_arch = "wasm32"), allow(dead_code))]
fn first_supported_tag<S: AsRef<str>>(tags: impl IntoIterator<Item = S>) -> Option<Lang> {
tags.into_iter().take(32).find_map(|tag| lang_from_tag(tag.as_ref().trim()))
}
/// The browser's preferred languages, as the desktop builds use the system's: `navigator.languages`
/// in order, then `navigator.language` (kept within the 32 tags `first_supported_tag` reads).
/// English when the browser offers none we have a catalog for.
#[cfg(target_arch = "wasm32")]
fn detect_system_lang() -> Lang {
Lang::EN
let Some(navigator) = web_sys::window().map(|w| w.navigator()) else { return Lang::EN };
let mut tags: Vec<String> = navigator.languages().iter().take(31).filter_map(|v| v.as_string()).collect();
tags.extend(navigator.language());
first_supported_tag(&tags).unwrap_or(Lang::EN)
}
thread_local! {
@@ -650,6 +664,21 @@ mod tests {
assert_eq!(first_supported("("), None);
}
/// #2954: the web build's Auto language follows the browser's preferred languages in order,
/// skipping ones without a catalog, and is English only when none is supported.
#[test]
fn browser_language_list_is_followed_in_order() {
assert_eq!(first_supported_tag(["it-IT", "en-US"]), Lang::from_code("it"));
assert_eq!(first_supported_tag(["xx-YY", "ja", "fr-FR"]), Some(JA()));
assert_eq!(first_supported_tag(["zh-TW"]), Lang::from_code("zh-hant"));
assert_eq!(first_supported_tag(["en-GB", "fr-FR"]), Some(Lang::EN));
assert_eq!(first_supported_tag([" pt-PT "]), Lang::from_code("pt-br"));
assert_eq!(first_supported_tag(["", "xx"]), None);
assert_eq!(first_supported_tag(Vec::<String>::new()), None);
let long = std::iter::repeat_n("xx", 10_000).chain(["fr"]);
assert_eq!(first_supported_tag(long), None, "only the first tags are read");
}
#[test]
fn windows_display_language_resolves_chinese_scripts() {
for tag in ["zh-CN", "zh-SG", "zh-Hans", "zh-Hans-CN"] {