Skip to content

M12: regional formats (typed and displayed numbers, dates and times) - #447

Closed
quantmind-br wants to merge 1 commit into
storytold:mainfrom
quantmind-br:regional-formats
Closed

quantmind-br wants to merge 1 commit into
storytold:mainfrom
quantmind-br:regional-formats

Conversation

@quantmind-br

@quantmind-br quantmind-br commented Oct 11, 2026 •

Copy link
Copy Markdown

Closes #361. Second of the focused PRs announced in #191: a Regional format setting next to the interface language, the way Excel follows the Windows/macOS region. Typed numbers, dates and times, and how they are displayed, follow it (1.234,56, 25/09/2026, 15.03.2026). Stored values, formulas, number format codes and files stay en-US.

What changes

  • 15 regions in crates/core/src/region.rs: en-US, en-GB, en-IN, es-ES, de-DE, fr-FR, it-IT, pt-BR, pt-PT, ru-RU, cs-CZ, ja-JP, ko-KR, zh-CN and zh-TW. Each has decimal and group separators (with Indian 3;2 grouping), date order, short and long date and time codes, month and day names (genitive months for ru and cs), AM/PM words and currency. The data is from CLDR; the module doc lists the deliberate deviations. Other system tags fall back to the language's main region (en-AU → en-GB, pt-AO → pt-PT).
  • Typing: parse_input_in(s, sys, &Region) reads regional numbers, currency, percent, day/month order, two-digit years, local month names, ISO dates and local AM/PM. The returned format codes stay canonical (m/d/yyyy), so a region change never rewrites a cell.
  • Display: format_value_in applies regional separators, names and AM/PM. Built-in formats 14 and 22 show the region's short date, and [$-F800]/[$-F400] its long date and time, as Excel does. format_value is unchanged (en-US).
  • Setting:
    • File › Options › Regional format (label translated in every shipped language).
    • Command app.region {region?}: strict tag; an unknown tag is an error that lists the valid ones; without a param it reports the current region. The command is journaled but not undoable, and a region change neither dirties a workbook nor recalculates.
    • The region is saved in prefs.json. The desktop app starts from the system region on first run, the web app from navigator.language (CJK regions keep en-US there, like the language), and CLI, MCP and tests use en-US.
  • Ctrl+; and Ctrl+Shift+; insert the date and time in the region's form, and the grid's General fit uses the regional decimal.

The engine boundary stays canonical

Command text is en-US unless the call says otherwise. cell.set (input), range.fill, range.setValues (string cells), edit.paste (text), data.validate (input) and data.forecastSheet (end) accept region: "<tag>", meaning "this text was typed in this region". The UI passes the workbook's region on every edit it commits. So:

  • Scripts replay the same. A recorded script or the journal gives the same result whatever the current preference is.
  • Canonical producers stay canonical. Numbers given as JSON values (cell.set {value: 1.234} stores 1.234 in de-DE), Replace, scenarios and Circle Invalid Data never reparse their text in the region.
  • Files and copies. CSV/TSV files and Get Data from a range are written in en-US, because CSV import reads en-US. The system clipboard and HTML copy carry the displayed text, as Excel does.

docs/control-protocol.md documents app.region and the region param.

Behaviour change to note

The Spanish interface used to read decimal-comma constants (-1,5) in the formula bar on its own. Now the region decides: Spanish interface with the es-ES region (the first-run default on a Spanish system) works as before, and with en-US -1,5 is text. crates/ui-egui/tests/formula_locale.rs is updated to that.

Not in this PR (documented in localization-parity.md)

The following stay en-US:

With #443

The code merges cleanly with #443 (formula languages). Only ROADMAP.md and docs/localization-parity.md conflict, and I'll rebase whichever lands second. With both merged, the formula, calc, core, numfmt, ui-egui and engine tests pass. The only failures are the CSV tests below, which also fail on main.

Tests

cargo xtask ci locally:

New tests cover:

  • regional parsing and display, including formats 14 and 22 per region, and en-US format_value_in matching format_value;
  • the command: set, report, unknown tags, undo, new documents;
  • the en-GB and decimal-comma flows, validation and the status bar;
  • the region param on every command that takes it;
  • script and journal replay after a region switch;
  • canonical producers in a decimal-comma region;
  • CSV round trips in de-DE and en-GB, and Get Data from a range;
  • edit-text fallbacks in date formats;
  • the Options combo, and -R$ 5 committing as a number with a pt-BR region and the Spanish interface.

Smoke on the release build through the control channel:

Region Typed Stored Shown F2
de-DE 1.234,5 1234.5 1.234,50 1234,5
de-DE 15.3.2026 46096 15.03.2026 15.03.2026
de-DE 12,5% 0.125 12,50% 0,125
de-DE 1.234,50 € 1234.5 1.234,50 € 1234,5
en-GB 25/09/2026 46290 25/09/2026 —
  • The same de-DE date shows as 15/03/2026 after switching to en-GB.
  • Through the API in de-DE, cell.set {input: "1.5"} stores 1.5 and shows 1,5, and "1,234" without region stores 1234 (en-US grouping).
  • CLI, de-DE: 1000 in #,##0, 1.5 and a date are shown as 1.000, 1,5, 25.09.2026 and saved to CSV as "1,000",1.5,9/25/2026; reopening the file gives the same values.

Follow-up, #1398 (draft, stacked on this and #443): formulas typed in the editor use this region's list and decimal separators, as Excel does, whatever the interface language. This PR is unchanged.

@Dejan-Port

Copy link
Copy Markdown
Contributor

This looks like exactly the right shape for the problem — the Region struct and the engine-boundary design (canonical storage, region: param on the text-accepting commands so scripts/journal replay the same) are well thought through. I'd been starting a narrower, display-only NumberLocale (just decimal/group separators) for Serbian before finding this PR; happy to drop that in favor of this.

Serbian (sr-Latn-RS) isn't in the 15 regions yet. I can put together the Region entry in your exact format if useful:

static SR_RS: Region = Region {
    tag: "sr-Latn-RS",
    name: "Srpski (Srbija)",
    lcid: 0x081A, // Serbian (Latin, Serbia)
    decimal: ',',
    group: '.',
    grouping: Grouping::Thousands,
    date_order: DateOrder::Dmy,
    date_sep: '.',
    time_sep: ':',
    short_date: "dd.mm.yyyy",
    long_date: "dddd, dd. mmmm yyyy",
    short_time: "hh:mm",
    long_time: "hh:mm:ss",
    datetime: "dd.mm.yyyy hh:mm",
    months: ["Januar", "Februar", "Mart", "April", "Maj", "Jun", "Jul", "Avgust", "Septembar", "Oktobar", "Novembar", "Decembar"],
    months_abbr: ["Jan", "Feb", "Mar", "Apr", "Maj", "Jun", "Jul", "Avg", "Sep", "Okt", "Nov", "Dec"],
    months_genitive: None, // Serbian doesn't inflect month names like ru/cs
    weekdays: ["Nedelja", "Ponedeljak", "Utorak", "Sreda", "Četvrtak", "Petak", "Subota"],
    weekdays_abbr: ["Ned", "Pon", "Uto", "Sre", "Čet", "Pet", "Sub"],
    am_pm: ["AM", "PM"], // no native AM/PM convention; 24-hour clock is standard
    currency: "RSD",
    currency_format: "#,##0.00\" RSD\"",
    currency_format_int: "#,##0\" RSD\"",
};

A few judgment calls I made (correct me if CLDR/Windows disagree): Serbian uses a 24-hour clock almost exclusively, so short_time/long_time have no AM/PM; the currency symbol follows the number (1.234,56 RSD), not precedes it, unlike the $/£/₹ regions above.

Let me know if you'd rather I open this as a PR once #447 lands (adding SR_RS + the lookup entry + an i18n label), or fold it in here directly — whichever is less friction for you.

@quantmind-br

Copy link
Copy Markdown
Author

@Dejan-Port thanks, Serbian would be a good addition. A separate PR after #447 lands would be less friction: #447 is already large, so I'd rather keep its scope fixed for review. You can branch from regional-formats on quantmind-br/gridcraft now and rebase once it's merged.

I checked the entry against CLDR 48 (ICU 78) and glibc's sr_RS@latin:

  • LCID: sr-Latn-RS is 0x241A; 0x081A is the legacy sr-Latn-CS (Serbia and Montenegro) in [MS-LCID].
  • Month and weekday names are lowercase in both sources (januar, mart, nedelja, ponedeljak; abbreviations jan, ned). The cs-CZ entry is lowercase for the same reason.
  • Dates end with a period in both: glibc %d.%m.%Y. gives 15.03.2026., CLDR short is 15. 3. 2026. and long 15. mart 2026.. Excel takes these from Windows' region settings, so the Windows sr-Latn-RS short date is the one to match. If it keeps the trailing period, please also check that typing 15.03.2026. is read as a date.
  • Currency: 1.234,56 RSD matches CLDR, so the symbol after the number is right. The 24-hour times are right too.
  • AM/PM: the file uses the shared AM_PM constant, so am_pm: AM_PM.
  • Lookup: lookup drops the @latin modifier before matching. On Linux, sr_RS.UTF-8 (Cyrillic) and sr_RS.UTF-8@latin would both resolve to sr-Latn-RS, and so would sr-Cyrl-RS. That may be acceptable while there is no Cyrillic region, but it's worth a test either way.

Dejan-Port pushed a commit to Dejan-Port/gridcraft that referenced this pull request Oct 12, 2026
1. Target branch unchanged (regional-formats) — staying here for
   review as asked; will rebase onto storytold/gridcraft main and
   reopen there once storytold#447 merges.

2. The trailing-separator fix was wrong in scope: trim_end_matches(seps)
   stripped any run of '/', '-' and '.' in every region, not one
   separator in regions whose own short date ends with it. That turned
   "3/4/" into a date in en-US, "25.09.2026.." into one in sr-Latn-RS,
   and similar for every other region, none of which have a trailing-
   separator convention. Replaced with the suggested, narrower check:
   strip exactly one instance of the region's own date_sep, and only
   when r.short_date.ends_with(r.date_sep). Added regression tests
   pinning en-US "3/4/", "1-2-", "2026-09-25-", "12.2026." and fr-FR
   "25.09.2026/" as text, and sr-Latn-RS "25.09.2026.." (a second
   trailing period) as text too.

3. long_date now ends with the period: "dddd, d. mmmm yyyy." — CLDR 48
   (ICU 78) gives it for both the full and the long pattern, and
   glibc's sr_RS@latin uses %A, %d. %B %Y.; missed that the quoted
   "15. mart 2026." already had it.

4. Docs: ROADMAP.md (Languages paragraph, 2026-10-11 regional-formats
   row) and docs/localization-parity.md (count, region list, a new
   revision-history row for this change) now say 16 regions.

Minor: short_date "dd.mm.yyyy." is cited from glibc's %d.%m.%Y.
(sr_RS@latin), not Windows — I don't have a source for the Windows
sr-Latn-RS short date pattern and shouldn't have claimed one.

Tests: cargo test -p gridcraft-core 33/33 (including the 5 new
regression cases above). cargo fmt --all -- --check and cargo clippy
--workspace --all-targets -- -D warnings both clean.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
quantmind-br added a commit to quantmind-br/gridcraft that referenced this pull request Oct 12, 2026
Dejan-Port pushed a commit to Dejan-Port/gridcraft that referenced this pull request Oct 12, 2026
Adds Region::sr-Latn-RS (crates/core/src/region.rs) to the Regional
format system from storytold#447, plus a general parsing fix it exposed.

Corrections from review on storytold#447 (thanks @quantmind-br):
- lcid 0x241A (sr-Latn-RS), not 0x081A (the legacy sr-Latn-CS, Serbia
  and Montenegro).
- Month and weekday names lowercase (januar, mart, nedelja,
  ponedeljak; jan, ned), matching cs-CZ's convention and CLDR/glibc.
- am_pm: AM_PM (the shared constant) — Serbian uses the 24-hour clock,
  so this is unused in practice but keeps the struct uniform.
- short_date "dd.mm.yyyy." keeps its trailing period, matching glibc
  sr_RS@latin's %d.%m.%Y. and Windows' sr-Latn-RS. long_date drops it
  ("dddd, d. mmmm yyyy"), matching the CLDR long (non-full) pattern
  quoted in review, with the weekday prepended the way every other
  region's long_date already does (per the module doc: "CLDR's *full*
  date, with the weekday").
- currency "1.234,56 RSD" (symbol after the number, period grouping)
  confirmed against CLDR.
- Registered "sr" => "sr-Latn-RS" in lookup()'s language fallback —
  without it, sr/sr-RS/sr_RS.UTF-8/sr_RS@latin/sr-Cyrl-RS would all
  still resolve to en-US regardless of the new Region entry, since
  lookup() only reaches a Region through this explicit match. sr-Cyrl
  variants fold into the same Latin region for now, as the review
  comment flagged (lookup drops the @latin/script modifier before
  matching; no separate Cyrillic region exists yet).

The trailing period that was specifically being verified exposed a
real gap: parse_date's `t.split(seps)` left a trailing empty part
whenever input ended with a separator, so a Serbian short date typed
back exactly as shown ("25.09.2026.") fell through every match arm
and failed to parse as a date at all. Fixed by trimming one trailing
separator before splitting — a one-line, region-agnostic fix; no
currently-shipped region's short_date ends with a separator, so this
changes no existing behaviour (covered by the existing
hostile_input_never_panics_in_any_region fuzz test, which still
passes).

Tests: extended dates_in_the_regions_order (plain and trailing-period
Serbian dates, including the round-trip through `.format`),
month_names_in_local_words, currency_follows_the_region,
lookup_accepts_system_and_browser_tags (sr, sr-RS, sr_RS.UTF-8,
sr_RS@latin, sr-Cyrl-RS), and month_names_match_loosely. Full
cargo test -p gridcraft-core: 33/33. cargo fmt --all -- --check and
cargo clippy --workspace --all-targets -- -D warnings both clean.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Dejan-Port pushed a commit to Dejan-Port/gridcraft that referenced this pull request Oct 12, 2026
1. Target branch unchanged (regional-formats) — staying here for
   review as asked; will rebase onto storytold/gridcraft main and
   reopen there once storytold#447 merges.

2. The trailing-separator fix was wrong in scope: trim_end_matches(seps)
   stripped any run of '/', '-' and '.' in every region, not one
   separator in regions whose own short date ends with it. That turned
   "3/4/" into a date in en-US, "25.09.2026.." into one in sr-Latn-RS,
   and similar for every other region, none of which have a trailing-
   separator convention. Replaced with the suggested, narrower check:
   strip exactly one instance of the region's own date_sep, and only
   when r.short_date.ends_with(r.date_sep). Added regression tests
   pinning en-US "3/4/", "1-2-", "2026-09-25-", "12.2026." and fr-FR
   "25.09.2026/" as text, and sr-Latn-RS "25.09.2026.." (a second
   trailing period) as text too.

3. long_date now ends with the period: "dddd, d. mmmm yyyy." — CLDR 48
   (ICU 78) gives it for both the full and the long pattern, and
   glibc's sr_RS@latin uses %A, %d. %B %Y.; missed that the quoted
   "15. mart 2026." already had it.

4. Docs: ROADMAP.md (Languages paragraph, 2026-10-11 regional-formats
   row) and docs/localization-parity.md (count, region list, a new
   revision-history row for this change) now say 16 regions.

Minor: short_date "dd.mm.yyyy." is cited from glibc's %d.%m.%Y.
(sr_RS@latin), not Windows — I don't have a source for the Windows
sr-Latn-RS short date pattern and shouldn't have claimed one.

Tests: cargo test -p gridcraft-core 33/33 (including the 5 new
regression cases above). cargo fmt --all -- --check and cargo clippy
--workspace --all-targets -- -D warnings both clean.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
devius added a commit to devius/gridcraft that referenced this pull request Oct 12, 2026
Brings in ka-catalog (upstream main + storytold#447 + Georgian table). Keeps the fork-only Windows preview workflow; the ported ka-fixes follow in the next merge.

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

quantmind-br commented Oct 13, 2026 •

Copy link
Copy Markdown
Author

Update: 04ced7b was dropped in the next rebase; #796 fixed it on main (see below).

Rebased onto 45221f5. This PR now has a second commit, 04ced7b, which fixes a bug that is on main today.

home.rowHeight can hide rows (height 0) or show them (any other height), and home.autofitRowHeight shows them. Neither command marked the change as structural the way home.hideRows does, so SUBTOTAL(109,…) kept skipping the rows until the next full recalc. Minimal repro on main, from the sales sample:

s.execute("selection.set", json!({"range":"E8:F13"})).ok();
s.execute("home.hideRows", json!({"rows":"9:11"})).ok();
s.execute("home.rowHeight", json!({"height":60.0})).ok();
// Sales!C11 =SUBTOTAL(109,Sales[Q1]) stays 203670; a full recalc gives 304820

The command-sequence fuzz on main doesn't hit it with its default seeds. This PR adds command parameters, which changes the random sequence, and seed 3 then fails in command_sequences_keep_the_workbook_consistent. The commit only sets structural when a row's hidden state actually changes, and adds a regression test that fails without it. It is independent of the regional formats, so it can also be cherry-picked on its own.

Also in this rebase:

@quantmind-br

quantmind-br commented Oct 13, 2026 •

Copy link
Copy Markdown
Author

Rebased onto 6405b00. 04ced7b is gone: #796 (2be0452) put the same fix on main, and it also covers Ungroup. The PR is back to one commit, and the command-sequence fuzz passes with every seed without it.

Also in this rebase:

Adds an Excel-style Region setting that is independent of the interface
language and of the formula language (FormulaLocale is untouched).

- core: region.rs with 15 regions (en-US, en-GB, en-IN, es-ES, de-DE,
  fr-FR, it-IT, pt-BR, pt-PT, ru-RU, cs-CZ, ja-JP, ko-KR, zh-CN, zh-TW):
  separators, grouping, date order, short/long date and time codes, month
  and day names, AM/PM words, currency. Data from CLDR.
- core: parse_input_in(s, sys, &Region) reads regional numbers, currency,
  percent, dates (D/M/Y, M/D/Y, Y/M/D, local month names) and times;
  parse_input stays the en-US wrapper.
- numfmt: format_value_in renders separators, month/day names and AM/PM
  per region; built-in formats 14 and 22 show the region's short date,
  [$-F800]/[$-F400] its long date and time. format_value stays en-US.
- model: Workbook.region, a #[serde(skip)] runtime field (en-US default).
- engine: Prefs.region is the session preference; it is applied to every
  open and new document and re-applied on undo/redo. New noundo command
  app.region {region?} sets it (strict tags, error lists the valid ones)
  or reports it. Cell input, the editor text, data validation, what-if
  and forecast inputs and display use the workbook region.
- ui-egui: Backstage Options "Regional format" combo, i18n labels in all
  shipped languages, Ctrl+; and Ctrl+Shift+; follow the region, constants
  are no longer re-read by the formula language.
- apps: desktop starts from the system region on first run, then the
  saved pref; web from navigator.language; headless sessions use en-US.
- docs: control-protocol.md, localization-parity.md, ROADMAP.md.

Command text is canonical (en-US) unless the call carries region: "<tag>"
(cell.set input, range.fill, range.setValues, edit.paste text, data.validate,
data.forecastSheet end). The UI passes the workbook's region on every edit it
commits, so a script, a journal or a canonical producer (JSON values, Replace,
scenarios, Circle Invalid Data) never depends on the preference. CSV/TSV files
and Get Data from a range are canonical en-US; only the system clipboard and
HTML copy carry the displayed text.

Stored values, formulas and format codes are never rewritten, so changing
region only changes how they are typed and shown. Worksheet-function text
coercion (VALUE, DATEVALUE, TEXT, criteria) and chart axes stay en-US.
@quantmind-br

Copy link
Copy Markdown
Author

Rebased onto 84acf62 (#848). Only the doc headers conflicted. #848's AutoComplete and #833's row fit on open don't read typed text, so neither needs a region.

On this branch, command_sequences_keep_the_workbook_consistent fails at seed 0 with the default budget. The cause is a bug that is already on main, #964: when two dynamic arrays want the same cells, a full recalculation or reopening the XLSX gives the area to whichever comes first in reading order. This PR adds command parameters, which shifts the fuzz's random sequence onto that case. The same shrunk sequence fails on main. #984 fixes it, and with #984 applied on top of this branch the fuzz passes with the default seeds and with 200 seeds × 60 steps.

@vedran-dobos

Copy link
Copy Markdown
Contributor

@quantmind-br I tried this PR together with #443, merged on main (d9d09f0, 0.7.0), in a real window on Windows 11 with Croatian regional settings, and compared it with Excel 365 (English interface) on the same machine.

What works. With a Croatian region added (see below), typed 1.234,56, 3,5, 12,5%, 1.234,56 € and dates are numbers, cells show them the Croatian way, and switching the region back and forth leaves the stored values alone. The tests match what you reported: only command_sequences_keep_the_workbook_consistent fails (seed 0), and it passes with #984's code merged on top.

Croatian falls back to en-US. hr isn't in lookup, so a Croatian system gets en-US. I have hr-HR ready on top of this branch, in your format, one commit: Windows' own hr-HR data (via .NET CultureInfo("hr-HR"), since that's what Excel shows), which differs from CLDR in places:

Windows hr-HR CLDR hr
Short date 25.9.2026. 25. 09. 2026.
Long date 25. rujna 2026. (no weekday) petak, 25. rujna 2026.

The short date ends with a period, so parse_date gets one small change: where the region's short date ends with a period, a typed date may too (25.9.2026., 25.9., 25. rujna 2026.). Other regions are unchanged, and a test checks that de-DE still keeps 25.9.2026. as text. As you suggested to the Serbian contributor, I'll open it as a separate PR once this one lands, unless you'd rather take it in here.

Question: should formula separators follow the region? In Excel with an English interface and Croatian regional settings:

Typed Excel 365 (English UI, hr-HR region) This PR + #443 (English UI, hr-HR region)
=1,5*1,5 2,25 refused: unexpected ,
=SUM(1;2) 3 refused: expected , or )
=ZBROJ(1;2) (Croatian name) error refused (the ;)

So Excel takes function names from its interface language, but the decimal and argument separators in formulas from the regional settings. Here the region is kept out of formulas, and #443 ties the separators to the interface language, so this combination can't type a decimal comma in a formula. Would you consider taking the formula decimal and list separators from the region, with #443's dialects supplying the names? I only measured this combination (English interface, Croatian region), so whether a German interface with a US region gives =SUMME(1.5,2) in Excel is untested.

A suggestion: a "System" region. Excel doesn't use a fixed table; it reads the Windows regional settings, including what the user changed there. A customised short date such as dd.MM.yyyy., a changed currency symbol or a different list separator all show up in Excel, and no built-in region can match them. On Windows the current values are in HKCU\Control Panel\International (sDecimal, sThousand, sList, sShortDate, sLongDate, sTimeFormat, sCurrency, iCurrency, iNegCurr, LocaleName) or from GetLocaleInfoEx. A "System" entry at the top of the Regional format list (the desktop default) could take the separators, date and time patterns and currency from there, and use the table entry for LocaleName for month and day names. The built-in regions would stay for the web app and for anyone who picks one. That might be a follow-up rather than part of this PR; it's a question for you and the maintainers.

@quantmind-br

Copy link
Copy Markdown
Author

@vedran-dobos thank you, this is exactly the measurement that was missing.

Separators. Your English UI + hr-HR result agrees with an independent one on #191: a French Excel 2024 used , between arguments and . as the decimal, while the same user's French Excel 2013 used ; and ,. Both point the same way, so I agree that the decimal and list separators in formulas should come from the region, and only the names, booleans, errors and table specifiers from the interface language.

How I'd stage it, without reshaping either PR under review:

Your open question stays open in that plan: whether a German UI with a US region gives =SUMME(1.5,2). If you or anyone can measure it, I'll add it as a test case.

hr-HR. Yes please, as a separate PR once #447 lands, as with the Serbian region. Taking Windows' data (via CultureInfo) rather than CLDR is right, since that's what Excel shows. The trailing-period rule, limited to regions whose short date ends with a period and with the de-DE test keeping 25.9.2026. as text, sounds right to me.

"System" region. I like it as a desktop default, as a follow-up after #447. The fixed regions stay for the web app and for anyone who picks one explicitly. It needs a per-OS reader: on Windows the HKCU\Control Panel\International values / GetLocaleInfoEx you listed. On macOS and Linux it needs the equivalent, or a fallback to the table entry for the system locale. The maintainers should decide whether they want that, so I won't fold it into #447.

Also thanks for confirming the fuzz result (seed 0 fails without #984, passes with it).

@quantmind-br

Copy link
Copy Markdown
Author

Closing in favour of a stack of smaller PRs, same final tree (rebased onto current main):

  1. M12: regional format data (Region, 15 regions) #1524 region data: Region, the 15 regions and Workbook::region; nothing reads them yet. +691/-1.
  2. typed input: parse_input_in. +360/-91.
  3. display: numfmt and engine display in a region. +331/-67.
  4. engine: session region, app.region, the region parameter on typed commands. +599/-54. It needs Calc: a contested spill area stays with its array on full recalc and reopening (#964) #984 first: the new command shifts the command-sequence fuzz onto the Calc: a contested spill area changes hands on Calculate Now and on reopening #964 spill bug.
  5. Options UI: File › Options › Regional format and the system default. +112/-9.

I'll open each one as the previous lands.

Dejan-Port pushed a commit to Dejan-Port/gridcraft that referenced this pull request Oct 15, 2026
Adds Region::sr-Latn-RS (crates/core/src/region.rs) to the Regional
format system from storytold#447, plus a general parsing fix it exposed.

Corrections from review on storytold#447 (thanks @quantmind-br):
- lcid 0x241A (sr-Latn-RS), not 0x081A (the legacy sr-Latn-CS, Serbia
  and Montenegro).
- Month and weekday names lowercase (januar, mart, nedelja,
  ponedeljak; jan, ned), matching cs-CZ's convention and CLDR/glibc.
- am_pm: AM_PM (the shared constant) — Serbian uses the 24-hour clock,
  so this is unused in practice but keeps the struct uniform.
- short_date "dd.mm.yyyy." keeps its trailing period, matching glibc
  sr_RS@latin's %d.%m.%Y. and Windows' sr-Latn-RS. long_date drops it
  ("dddd, d. mmmm yyyy"), matching the CLDR long (non-full) pattern
  quoted in review, with the weekday prepended the way every other
  region's long_date already does (per the module doc: "CLDR's *full*
  date, with the weekday").
- currency "1.234,56 RSD" (symbol after the number, period grouping)
  confirmed against CLDR.
- Registered "sr" => "sr-Latn-RS" in lookup()'s language fallback —
  without it, sr/sr-RS/sr_RS.UTF-8/sr_RS@latin/sr-Cyrl-RS would all
  still resolve to en-US regardless of the new Region entry, since
  lookup() only reaches a Region through this explicit match. sr-Cyrl
  variants fold into the same Latin region for now, as the review
  comment flagged (lookup drops the @latin/script modifier before
  matching; no separate Cyrillic region exists yet).

The trailing period that was specifically being verified exposed a
real gap: parse_date's `t.split(seps)` left a trailing empty part
whenever input ended with a separator, so a Serbian short date typed
back exactly as shown ("25.09.2026.") fell through every match arm
and failed to parse as a date at all. Fixed by trimming one trailing
separator before splitting — a one-line, region-agnostic fix; no
currently-shipped region's short_date ends with a separator, so this
changes no existing behaviour (covered by the existing
hostile_input_never_panics_in_any_region fuzz test, which still
passes).

Tests: extended dates_in_the_regions_order (plain and trailing-period
Serbian dates, including the round-trip through `.format`),
month_names_in_local_words, currency_follows_the_region,
lookup_accepts_system_and_browser_tags (sr, sr-RS, sr_RS.UTF-8,
sr_RS@latin, sr-Cyrl-RS), and month_names_match_loosely. Full
cargo test -p gridcraft-core: 33/33. cargo fmt --all -- --check and
cargo clippy --workspace --all-targets -- -D warnings both clean.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Dejan-Port pushed a commit to Dejan-Port/gridcraft that referenced this pull request Oct 15, 2026
1. Target branch unchanged (regional-formats) — staying here for
   review as asked; will rebase onto storytold/gridcraft main and
   reopen there once storytold#447 merges.

2. The trailing-separator fix was wrong in scope: trim_end_matches(seps)
   stripped any run of '/', '-' and '.' in every region, not one
   separator in regions whose own short date ends with it. That turned
   "3/4/" into a date in en-US, "25.09.2026.." into one in sr-Latn-RS,
   and similar for every other region, none of which have a trailing-
   separator convention. Replaced with the suggested, narrower check:
   strip exactly one instance of the region's own date_sep, and only
   when r.short_date.ends_with(r.date_sep). Added regression tests
   pinning en-US "3/4/", "1-2-", "2026-09-25-", "12.2026." and fr-FR
   "25.09.2026/" as text, and sr-Latn-RS "25.09.2026.." (a second
   trailing period) as text too.

3. long_date now ends with the period: "dddd, d. mmmm yyyy." — CLDR 48
   (ICU 78) gives it for both the full and the long pattern, and
   glibc's sr_RS@latin uses %A, %d. %B %Y.; missed that the quoted
   "15. mart 2026." already had it.

4. Docs: ROADMAP.md (Languages paragraph, 2026-10-11 regional-formats
   row) and docs/localization-parity.md (count, region list, a new
   revision-history row for this change) now say 16 regions.

Minor: short_date "dd.mm.yyyy." is cited from glibc's %d.%m.%Y.
(sr_RS@latin), not Windows — I don't have a source for the Windows
sr-Latn-RS short date pattern and shouldn't have claimed one.

Tests: cargo test -p gridcraft-core 33/33 (including the 5 new
regression cases above). cargo fmt --all -- --check and cargo clippy
--workspace --all-targets -- -D warnings both clean.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

No way to define default region settings

3 participants