commit bda3fa80b703901f6f29e2bcafabd4859d9a1fce
parent f2418cfb9fa221460780fd7dae9301bde494f756
Author: triesap <triesap@radroots.dev>
Date: Mon, 19 Jan 2026 07:57:32 +0000
app-lib: add locale helper
- add locale resolution helper
- add wasm locale lookup fallback
- add unit tests for locale resolution
- re-export locale helpers from app lib
Diffstat:
2 files changed, 64 insertions(+), 0 deletions(-)
diff --git a/crates/app-lib/src/lib.rs b/crates/app-lib/src/lib.rs
@@ -3,6 +3,7 @@
pub mod browser;
pub mod fetch;
pub mod geo;
+pub mod locale;
pub mod path;
pub mod query;
pub mod sleep;
@@ -12,6 +13,7 @@ pub mod symbols;
pub use browser::{browser_platform, BrowserPlatformInfo};
pub use fetch::{fetch_json, FetchJsonError, FetchJsonErrorKind, FetchJsonResult};
pub use geo::{geop_init, geop_is_valid, AppGeolocationPoint};
+pub use locale::{get_locale, resolve_locale};
pub use path::{normalize_path, sanitize_path, trim_slashes};
pub use query::{encode_query_params, encode_route};
pub use sleep::sleep;
diff --git a/crates/app-lib/src/locale.rs b/crates/app-lib/src/locale.rs
@@ -0,0 +1,62 @@
+#![forbid(unsafe_code)]
+
+const DEFAULT_LOCALE: &str = "en";
+
+pub fn resolve_locale(locales: &[&str], navigator_locale: Option<&str>) -> String {
+ let fallback = locales.first().copied().unwrap_or(DEFAULT_LOCALE);
+ let fallback_lower = fallback.to_ascii_lowercase();
+ let Some(nav_locale) = navigator_locale else {
+ return fallback_lower;
+ };
+ let nav_lower = nav_locale.to_ascii_lowercase();
+ if locales
+ .iter()
+ .any(|locale| locale.eq_ignore_ascii_case(&nav_lower))
+ {
+ return nav_lower;
+ }
+ let prefix = nav_lower.chars().take(2).collect::<String>();
+ if !prefix.is_empty()
+ && locales
+ .iter()
+ .any(|locale| locale.eq_ignore_ascii_case(&prefix))
+ {
+ return prefix;
+ }
+ fallback_lower
+}
+
+pub fn get_locale(locales: &[&str]) -> String {
+ #[cfg(target_arch = "wasm32")]
+ {
+ let navigator_locale = web_sys::window().map(|window| window.navigator().language());
+ resolve_locale(locales, navigator_locale.as_deref())
+ }
+ #[cfg(not(target_arch = "wasm32"))]
+ {
+ resolve_locale(locales, None)
+ }
+}
+
+#[cfg(test)]
+mod tests {
+ use super::resolve_locale;
+
+ #[test]
+ fn resolve_locale_prefers_exact_match() {
+ let locales = ["en", "fr"];
+ assert_eq!(resolve_locale(&locales, Some("fr")), "fr");
+ }
+
+ #[test]
+ fn resolve_locale_prefers_prefix_match() {
+ let locales = ["en", "fr"];
+ assert_eq!(resolve_locale(&locales, Some("fr-CA")), "fr");
+ }
+
+ #[test]
+ fn resolve_locale_falls_back() {
+ let locales = ["en", "fr"];
+ assert_eq!(resolve_locale(&locales, Some("es-ES")), "en");
+ }
+}