New documents follow the system region: A4, centimetres and decimal comma outside the US (#838) (#857)

* #838: new documents follow the system region (A4/cm/decimal comma outside the US)

- geom: Region (from a locale tag; CLDR Letter and US-measurement territories,
  decimal separator), Unit::format_with; parse_measure takes either separator
  and a Unicode minus.
- engine: Session.region (plain value, en-US headless), Session::units()
  (explicit Measurement units choice wins, else the region's),
  blank_document(); file.new blank uses the region's paper; file.options
  takes "region" and units "auto" and reports unitsFromRegion/region.
  Prefs.units is now Option and saved as measurementUnits (old saved values
  were the inch default, not a choice).
- ui: region from WORDCRAFT_REGION, LC_PAPER/LC_MEASUREMENT/LC_NUMERIC, the
  platform locale (browser on the web) and Windows' home location; length
  fields show and read the region's decimal separator (MeasureExt); Size
  menu lists sizes in the measurement unit.
- Tests: engine new_documents_follow_the_region, geom region/format/parse,
  ui length_fields_follow_the_region. Docs: localization parity, roadmap.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* Review fixes for #857: keep a measurement unit chosen before the rename

Settings saved before #838 kept the unit under `editing.units`, defaulting to inches. On load,
a value there other than inches was the user's own choice, so it now carries over to
`measurementUnits` (unless that key is already present); inches, junk and non-strings follow
the region. Test: `a_unit_chosen_before_regions_survives_the_rename`.

Also merged main (ROADMAP.md, engine lib.rs re-exports: `blank_document` and main's
`set_system_locale`). The Windows region code (`GlobalizationPreferences::HomeGeographicRegion`)
was cross-checked with clippy -D warnings for x86_64-pc-windows-gnu.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* #838 review: saved region choice, Unix conventions apart, the browser's locale

- File › Options › Advanced › Region: Automatic (the system's) or a chosen region, saved with
  the preferences (`Prefs::region`; `file.options {"region": "pl-PL" | "auto"}`). The session
  keeps the platform's region as `system_region`; `Session::region()` is the one in effect.
  Measurement units gains an Automatic entry (follows the region).
- Unix: numbers follow LC_NUMERIC (else LANG), paper LC_PAPER and measurement LC_MEASUREMENT,
  each on its own (`Region::with_paper_and_measurement`); WORDCRAFT_REGION and LC_ALL still set
  all three.
- Web: sys-locale's `js` feature, so the browser's language reaches the region (and the
  interface language, as its comment already said).
- Runs without preferences (WORDCRAFT_NO_PREFS) stay en-US unless WORDCRAFT_REGION is set.
- docs/localization-parity.md: where the region comes from, the Windows home location vs. Word's
  regional format, and where the decimal separator comes from.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* #838 review: rustfmt

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* #838: rustfmt after merging main

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* i18n: Hungarian and French strings for this change

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

---------

Co-authored-by: Dmitrii Khlapov <19991457+dkhlapov@users.noreply.github.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Co-authored-by: Brandon Thomas <echelon@gmail.com>
This commit is contained in:
authored and GitHub committed 2026-10-12 08:23:43 -04:00
1 parent 411ce31de7
commit 00ed50515e
44 files changed
+727 -86

No files matched your search

+11 -2
View File
@@ -51,8 +51,17 @@ pub fn start() {
if let Err(e) = listen_for_pasted_pictures(inbox.clone(), cc.egui_ctx.clone()) {
log::error!("pasting pictures is unavailable: {e}");
}
let doc = if query().contains("sample") { wordcraft_engine::sample::sample_document() } else { wordcraft_doc::Document::new() };
let mut app = WordApp::new(Session::new(doc), services(inbox.clone(), cc.egui_ctx.clone(), dirty, saves.clone()));
// The browser's locale picks the new document's paper, the units and the
// decimal separator (#838).
let region = wordcraft_ui_egui::i18n::system_region();
let doc = if query().contains("sample") {
wordcraft_engine::sample::sample_document()
} else {
wordcraft_engine::blank_document(&region)
};
let mut session = Session::new(doc);
session.system_region = region;
let mut app = WordApp::new(session, services(inbox.clone(), cc.egui_ctx.clone(), dirty, saves.clone()));
app.autosave = false;
Ok(Box::new(WebShell { app, inbox, saves }))
}),
+3 -2
View File
@@ -57,8 +57,9 @@ wl-clipboard-rs = "0.9"
# The system's Animation effects setting (Settings › Accessibility › Visual effects), read through
# the Windows Runtime's UISettings: a safe API, where the Win32 call needs unsafe, which our
# workspace forbids. Already in the build through wgpu and AccessKit, at this version; this adds
# only the `UI_ViewManagement` bindings.
windows = { version = "0.62", features = ["UI_ViewManagement"] }
# only the `UI_ViewManagement` bindings, and `System_UserProfile` for the home region new
# documents take their paper and units from (#838).
windows = { version = "0.62", features = ["UI_ViewManagement", "System_UserProfile"] }
# The clipboard's "Rich Text Format" and the names of its formats (#687, #833), which arboard
# doesn't read. Already in the build through arboard, at this version.
clipboard-win = { version = "5.4", features = ["std"] }
+14 -2
View File
@@ -20,6 +20,7 @@ mod file_dialogs;
mod graphics;
mod logging;
mod print;
mod region;
use wordcraft_engine::Session;
use wordcraft_ui_egui::{
@@ -302,11 +303,22 @@ fn main() -> eframe::Result {
"WordCraft",
options,
Box::new(move |cc| {
let doc = if sample { wordcraft_engine::sample::sample_document() } else { wordcraft_doc::Document::new() };
// The system region picks the new document's paper, the units and the decimal
// separator (#838).
let region = region::system();
let doc = if sample { wordcraft_engine::sample::sample_document() } else { wordcraft_engine::blank_document(&region) };
let (file_dialog, dialogs) = file_dialogs::hook(&cc.egui_ctx);
let mut app = WordApp::new(Session::new(doc), services(file_dialog, &cc.egui_ctx));
let mut session = Session::new(doc);
session.system_region = region;
let mut app = WordApp::new(session, services(file_dialog, &cc.egui_ctx));
load_prefs(&mut app);
load_user_dictionary(&mut app);
// A region chosen in File › Options replaces the system's for the first blank
// document too (#838).
if !sample && app.session.prefs.region.is_some() {
let doc = app.session.blank_document();
app.session.set_document(doc);
}
app.watch_desktop_theme(&cc.egui_ctx);
app.ui.window = restored;
app.integrated_titlebar = cfg!(target_os = "macos");
+30
View File
@@ -0,0 +1,30 @@
//! The system region new documents follow (#838): the paper File › New starts on, the default
//! measurement unit and the decimal separator.
//!
//! - Windows: the preferred language with the home location from Settings › Time & language ›
//! Region (`GlobalizationPreferences.HomeGeographicRegion`, a safe Windows Runtime API), since
//! the language list alone says `en-US` for an English interface in Poland.
//! - Elsewhere: the locale the UI crate reads ([`wordcraft_ui_egui::i18n::system_region`]).
/// The system region.
pub fn system() -> wordcraft_engine::Region {
// Runs without preferences (agents' and screenshot runs) behave the same on every machine:
// en-US unless `WORDCRAFT_REGION` says otherwise.
if std::env::var_os("WORDCRAFT_NO_PREFS").is_some() && std::env::var_os("WORDCRAFT_REGION").is_none() {
return wordcraft_engine::Region::default();
}
let region = wordcraft_ui_egui::i18n::system_region();
#[cfg(target_os = "windows")]
{
if std::env::var_os("WORDCRAFT_REGION").is_none() {
return match windows::System::UserProfile::GlobalizationPreferences::HomeGeographicRegion() {
Ok(home) => region.with_territory(&home.to_string()),
Err(e) => {
log::debug!("system home region: {e}");
region
}
};
}
}
region
}