collective

Browse / github.com/storytold/lightcraft / patch

LR-BEHAV-LOCALE: the desktop app starts in the system's language

LR-BEHAV-LOCALE: the desktop app starts in the system's language ### Problem On the desktop, "Auto" never looked at the system's language: `set_default_language` is called only by the web host, so a library with no saved settings opened in English unless `LIGHTCRAFT_LANGUAGE` was set, even on a system set to Russian, German or any other language LightCraft has a catalog for. Found on macOS with the system language `ru-SK`. ### Change - `apps/lightcraft/src/main.rs`: before the settings load, the desktop host passes the system's preferred languages (`sys_locale::get_locales()`) to `locale_from_tags`, as the web host does with `navigator.languages`, and in

Status
open
Score
0.00
Adopters
0 other operators
Author
JaffarSk24 (imported from pull request #3146, open)
Base
662df66f1c08bdb1b1f1d0c1c638c4cef936d0b0
Size
+43 −9 in 8 files
Review
pending
Shared
2026-10-28
Id
a015de762283b4ef6bbe98ad

How it went

No other operator has reported using it yet.

Diff

raw · Cargo.lock Cargo.toml apps/lightcraft/Cargo.toml apps/lightcraft/src/main.rs crates/ui-egui/src/i18n.rs crates/ui-egui/src/i18n/tests.rs docs/localization.md docs/whats-new.md

diff --git a/Cargo.lock b/Cargo.lock--- a/Cargo.lock+++ b/Cargo.lock@@ -3311,6 +3311,7 @@ dependencies = [  "pollster 1.0.1",  "rfd",  "serde_json",+ "sys-locale",  "winit",  "winresource",  "zbus",@@ -6145,6 +6146,15 @@ dependencies = [  "syn 3.0.6", ] +[[package]]+name = "sys-locale"+version = "0.3.2"+source = "registry+https://github.com/rust-lang/crates.io-index"+checksum = "8eab9a99a024a169fe8a903cf9d4a3b3601109bcc13bd9e3c6fff259138626c4"+dependencies = [+ "libc",+]+ [[package]] name = "sysctl" version = "0.5.5"diff --git a/Cargo.toml b/Cargo.toml--- a/Cargo.toml+++ b/Cargo.toml@@ -51,6 +51,8 @@ sha2 = "0.10" serde = { version = "1", features = ["derive", "rc"] } serde_json = { version = "1", features = ["float_roundtrip"] } log = "0.4"+# the operating system's preferred languages, for the desktop "Auto" language+sys-locale = "0.3" # All date/time conversion goes through chrono (no hand-rolled calendar code beyond the 400-year range extension; AGENTS.md). chrono = { version = "0.4.45", default-features = false, features = ["std"] } rayon = "1"diff --git a/apps/lightcraft/Cargo.toml b/apps/lightcraft/Cargo.toml--- a/apps/lightcraft/Cargo.toml+++ b/apps/lightcraft/Cargo.toml@@ -42,6 +42,8 @@ winit = { version = "0.30", default-features = false, features = ["x11"] }  [target.'cfg(not(target_arch = "wasm32"))'.dependencies] rfd = { workspace = true }+# the system's preferred languages for the "Auto" interface language (`main`)+sys-locale = { workspace = true }  [target.'cfg(target_os = "linux")'.dependencies] zbus = { workspace = true }diff --git a/apps/lightcraft/src/main.rs b/apps/lightcraft/src/main.rs--- a/apps/lightcraft/src/main.rs+++ b/apps/lightcraft/src/main.rs@@ -193,6 +193,11 @@ fn main() -> eframe::Result {     // what the arguments name, resolved now: an empty or relative argument never becomes a drive     // root, and a whole drive or home folder is not walked (issue #374)     let (files, launch_notices) = launch_imports(&files);+    // "Auto" follows the system's preferred languages, as the web build follows the browser's:+    // installed before the settings load, since a library without saved settings starts in it.+    if let Some(language) = lightcraft_ui_egui::i18n::locale_from_tags(&sys_locale::get_locales().collect::<Vec<_>>()) {+        lightcraft_ui_egui::i18n::set_default_language(language);+    }     let (prefs, prefs_warning, keep_prefs_file) = load_prefs(in_memory);     // the saved language from the start, so a failed start is reported in it too     if let Some(ui) = &prefs {diff --git a/crates/ui-egui/src/i18n.rs b/crates/ui-egui/src/i18n.rs--- a/crates/ui-egui/src/i18n.rs+++ b/crates/ui-egui/src/i18n.rs@@ -193,23 +193,23 @@ thread_local! {     static DEFAULT_LANGUAGE: std::cell::Cell<Option<Locale>> = const { std::cell::Cell::new(None) }; } -/// Install the language the "Auto" setting resolves to when nothing is stored. The browser host-/// calls this once at startup with the browser's own languages ([`locale_from_tags`]); the-/// desktop host doesn't: there Auto keeps following the environment. Must run before the UI state-/// is first constructed.+/// Install the language the "Auto" setting resolves to when nothing is stored. Each host calls+/// this once at startup through [`locale_from_tags`]: the browser host with the browser's own+/// languages, the desktop host with the operating system's preferred languages. Must run before+/// the UI state is first constructed. pub fn set_default_language(language: Locale) {     DEFAULT_LANGUAGE.with(|slot| slot.set(Some(language))); } -/// The first of `tags` (BCP-47, most preferred first, as `navigator.languages` lists them) that-/// names a shipped language; None when none does.+/// The first of `tags` (BCP-47, most preferred first, as `navigator.languages` and the system's+/// language list give them) that names a shipped language; None when none does. pub fn locale_from_tags(tags: &[String]) -> Option<Locale> {     tags.iter().find_map(|tag| Locale::parse_tag(tag)) }  /// The UI language "Auto" resolves to: the environment's (`LIGHTCRAFT_LANGUAGE=zh-hans`, for-/// headless runs), or the host-installed default ([`set_default_language`], the browser's languages-/// on the web), else English.+/// headless runs), or the host-installed default ([`set_default_language`]: the system's languages+/// on the desktop, the browser's on the web), else English. pub fn default_language() -> Locale {     std::env::var("LIGHTCRAFT_LANGUAGE")         .ok()diff --git a/crates/ui-egui/src/i18n/tests.rs b/crates/ui-egui/src/i18n/tests.rs--- a/crates/ui-egui/src/i18n/tests.rs+++ b/crates/ui-egui/src/i18n/tests.rs@@ -184,7 +184,7 @@ fn locale_tags_normalize() {     assert_eq!(Locale::parse_tag("xx"), None); } -/// The web host's "Auto" (issue #2085): the browser's `navigator.languages` in order, first+/// The hosts' "Auto" (issue #2085): the browser's `navigator.languages`, or the system's languages, in order, first /// shipped language wins, unsupported tags are skipped, an empty or foreign list stays English /// ([`default_language`] fallback). #[test]@@ -196,6 +196,9 @@ fn locale_from_tags_takes_the_first_shipped_language() {     assert_eq!(locale_from_tags(&["zh-TW".into(), "en".into()]), Some(Locale::ZhHant));     assert_eq!(locale_from_tags(&["ja_JP.UTF-8".into(), "en".into()]), Some(Locale::Ja));     assert_eq!(locale_from_tags(&["pt".into(), "pt-BR".into()]), Some(Locale::PtBr));+    // A language set up with a region it has no catalog of its own for (Russian in Slovakia).+    assert_eq!(locale_from_tags(&["ru-SK".into(), "en-SK".into(), "sk-SK".into()]), Some(Locale::Ru));+    assert_eq!(locale_from_tags(&["en-US".into(), "ru-RU".into()]), Some(Locale::En)); }  #[test]diff --git a/docs/localization.md b/docs/localization.md--- a/docs/localization.md+++ b/docs/localization.md@@ -113,6 +113,10 @@ in code without invalidating anyone's saved preference. `LIGHTCRAFT_LANGUAGE` ac locale looks like (`zh_Hans`, `zh-CN`, `en_US`, `ja_JP.UTF-8`) and falls back to the base language when a region has no dedicated entry. +With no saved setting, the app starts in the first of the system's preferred languages it has a+catalog for (the browser's languages in the web build), else English. `LIGHTCRAFT_LANGUAGE`+overrides that, and a language chosen in Edit ▸ Language is saved and kept.+ ## Release notes  Help ▸ What's New shows `docs/whats-new.md`, and each of its lines (a heading, or one wrapped line of adiff --git a/docs/whats-new.md b/docs/whats-new.md--- a/docs/whats-new.md+++ b/docs/whats-new.md@@ -1,5 +1,13 @@ # What's new in LightCraft +## 2026-10-27++### The desktop app starts in the system's language+- On macOS, Windows and Linux, a library with no saved settings now opens in the first of the system's preferred+  languages that LightCraft has a catalog for (Russian for `ru-SK`, German for `de-AT`), as the web build follows the+  browser's languages. Before, it opened in English unless `LIGHTCRAFT_LANGUAGE` was set. A language chosen in Edit ▸+  Language is kept as before.+ ## 2026-10-24  ### Relative-date filters and smart albums follow the clock