Skip to content

Add CLDR plural categories with a legacy translation path - #500

Merged
DirkDoes merged 8 commits into
mainfrom
task/localization-plurals
Oct 4, 2026
Merged

DirkDoes merged 8 commits into
mainfrom
task/localization-plurals

Conversation

@DirkDoes

@DirkDoes DirkDoes commented Sep 28, 2026 •

Copy link
Copy Markdown
Member

Purpose of this PR:

Keep global t() and add count-based category lookup for zero/one/two/few/many/other using CLDR cardinal rules for the selectable languages. Named count arguments and positional counts are supported; fallback recomputes the English plural category.

Add t_legacy() for exact numeric/.n variants and move the current numeric-key callers, including tTime(), to it. Imported production YAML is unchanged. Test-only embedded translations exercise the new categories.

Targets main after the settings layers were merged. This is PR 1/3 of the remaining localization stack; works without live localization.

How to Test:

dotnet test WheelWizard.sln

580 tests passed (561 unit, 19 headless UI), including decimal counts, language-specific rules, English fallback, named/positional calls, and legacy behavior.

What Has Been Changed:

See the focused implementation above. The existing settings JSON contract and imported translation sheets are preserved.

Related Issue Link:

No linked issue. PR 1/3 of native GitHub stack #508 (#500 → #501 → #502).

Checklist before merging

  • You have created relevant tests

Summary by CodeRabbit

  • New Features
    • Translations now support plural forms based on numeric counts, with language-specific rules for languages including English, Russian, Polish, and Czech.
    • When a translation is unavailable in the selected language, the app can fall back to English plural forms.
    • Numeric counts work with both named and positional translation arguments.
  • Compatibility
    • Existing number-specific translations remain available through the legacy translation path.

@coderabbitai

coderabbitai Bot commented Sep 28, 2026 •

Copy link
Copy Markdown
Contributor

Review in Change Stack →

Navigate logical layers of code changes, visualize relationships, and explore their blast radius.

Important

Review skipped

Auto incremental reviews are disabled on this repository.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration
  • Configuration used: Organization UI
  • Review profile: ASSERTIVE
  • Plan: Advanced
  • Run ID: 6ec39caa-b1d8-43ba-b732-a2237b983510

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration
  • Configuration used: Organization UI
  • Review profile: ASSERTIVE
  • Plan: Advanced
  • Run ID: 9410ee0e-f157-47bb-8b02-a86376823b4f
📥 Commits

Reviewing files that changed from the base of the PR and between 90fdf02 and ccbbe06.

📒 Files selected for processing (9)
  • WheelWizard.Test/Features/Localization/PluralRulesTests.cs
  • WheelWizard.Test/Features/Settings/SettingsTests.cs
  • WheelWizard.Test/Resources/Languages/plural-fixture.yml
  • WheelWizard.Test/WheelWizard.Test.csproj
  • WheelWizard/Features/Localization/EmbeddedYamlLocalizationService.cs
  • WheelWizard/Features/Localization/ILocalizationService.cs
  • WheelWizard/Features/Localization/PluralRules.cs
  • WheelWizard/Features/Localization/TranslationFunctions.cs
  • WheelWizard/Views/Layout.axaml.cs

Included review availability: This review used your included allowance. Your plan provides up to 4 included reviews per hour; 3 remain after this review.


📝 Walkthrough

Walkthrough

The localization service now selects plural translation forms using language-specific cardinal rules. Numeric translation arguments use this plural lookup when they convert to decimal. The previous numeric-key behavior remains available through t_legacy, which existing call sites now use.

Changes

Plural Localization

Layer / File(s) Summary
Plural rules and lookup
WheelWizard/Features/Localization/PluralRules.cs, WheelWizard/Features/Localization/ILocalizationService.cs, WheelWizard/Features/Localization/EmbeddedYamlLocalizationService.cs
Adds language-specific cardinal category selection and a service method that checks the selected category, .other, and the base key before trying English or returning the key.
Translation integration
WheelWizard/Features/Localization/TranslationFunctions.cs, WheelWizard/Views/Layout.axaml.cs, WheelWizard.Test/Features/Settings/SettingsTests.cs
Routes convertible numeric arguments through plural translation. Retains the previous number-key behavior as t_legacy and uses it for time labels, tooltips, and the settings test.
Plural translation validation
WheelWizard.Test/Features/Localization/PluralRulesTests.cs, WheelWizard.Test/Resources/Languages/plural-fixture.yml, WheelWizard.Test/WheelWizard.Test.csproj
Adds category and translation integration tests. Adds an English and Russian fixture and embeds the language YAML files in the test project.

Priority: ➖ Normal

Estimated code review effort: 3 (Moderate) | ~25 minutes

Change: Feature

Sequence Diagram(s)

sequenceDiagram
  participant Caller
  participant TranslationFunctions
  participant ILocalizationService
  participant EmbeddedYamlLocalizationService
  participant PluralRules
  Caller->>TranslationFunctions: Call t with numeric count
  TranslationFunctions->>ILocalizationService: TranslatePlural with key, count, and language
  ILocalizationService->>EmbeddedYamlLocalizationService: Resolve plural translation
  EmbeddedYamlLocalizationService->>PluralRules: Select category for language and count
  PluralRules-->>EmbeddedYamlLocalizationService: Return category
  EmbeddedYamlLocalizationService-->>TranslationFunctions: Return matching form or fallback
  TranslationFunctions-->>Caller: Return translated text
Loading

Merge Risk: ⚪ Minimal · up to ccbbe

No actionable regression is established for this change; it is mergeable after normal checks.

Security Architecture Review

Security architecture risk: 🔵 Low · up to ccbbe

The checked paths remain application-local translation and display operations. Existing numeric-key consumers retain an explicit compatibility path. No new privileged operation or externally reachable attack path was established, but coverage of all downstream consumers is incomplete.

Retained concerns
No architecture-level concerns identified.

Security review details

Security Blast Radius

  • inferred — The demonstrated effect is application-local translation selection and displayed tooltip text. The inspected paths do not establish additional credential, service, or datastore authority. This bounds the observed exposure, not every possible downstream consumer.

Trust Boundaries and Controls

  • observed — The newly public constructor accepts an Assembly object from an in-process caller and loads matching embedded YAML resources. It does not itself accept a remote resource URL or load an assembly from an attacker-supplied path. No external route to assembly selection was established.

Resilience and Maintainability Implications

  • observed — Production plural lookup reads the current language once and performs local calculations and dictionary reads. It does not replace the provider, change language ownership, reload resources, or introduce a multi-step persistent state transition.
🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 7.69% which is insufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff. Analyzed 26 functions across 7 files. (2 skipped: 2… Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Title check ✅ Passed The title clearly identifies the main change: CLDR plural categories and a legacy translation path.
Description check ✅ Passed The description covers the purpose, test steps and results, changes, related PRs, and required test checklist. It states that no linked issue exists and identifies the related PR stack.
Full details: Docstring Coverage

Explanation

Docstring coverage is 7.69% which is insufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff. Analyzed 26 functions across 7 files. (2 skipped: 2 unsupported.)

✨ Finishing Touches 💡 1
📝 Generate docstrings 💡
  • Commit to this branch
  • Create a new PR
🧪 Generate unit tests (beta)
  • Commit to this branch
  • Create a new PR
  • Autopilot · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

I’m a rabbit; I count each hop,
One, few, or many, I never stop.
English and Russian forms now align,
I nibble the keys and find the right line.
Old number keys still have their place,
I thump my paws at every case.

Comment @coderabbitai help to get the list of available commands.

@DirkDoes
DirkDoes added this pull request to stack #503 September 28, 2026 21:03
@DirkDoes DirkDoes changed the title Add cardinal plural categories and preserve legacy translations Add CLDR plural categories with a legacy translation path Sep 28, 2026
@DirkDoes
DirkDoes force-pushed the task/localization-plurals branch from 434c164 to ccbbe06 Compare October 3, 2026 11:32
@DirkDoes
DirkDoes marked this pull request as ready for review October 3, 2026 11:33

@patchzyy patchzyy left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

reviewed this against #499. the existing tests pass locally: 580 unit tests and 19 ui tests.

[p2] keep the locale region when choosing the plural category.

with a portuguese catalog containing one and other, t("pt-pt.items", count: 0) returns the one form. the rule selector correctly returns other for that locale, but the translation service normalizes pt-pt to pt before calling it, so the regional rule is never reached through the translation api.

please use the normalized language for catalog lookup while retaining the full locale for plural selection, and add an integration test through t() or the service. the current regional test only calls the rule selector directly. the cldr rules distinguish these locales.

location: embeddedyamllocalizationservice.cs, the language normalization at the start of plural translation.

i confirmed this with an extra embedded fixture and a failing regression test in an isolated copy of this commit.

Base automatically changed from task/typed-settings to task/settings-persistence October 4, 2026 09:58
@DirkDoes
DirkDoes deleted the branch main October 4, 2026 10:01
@DirkDoes DirkDoes closed this Oct 4, 2026
@DirkDoes
DirkDoes removed this pull request from stack #503 October 4, 2026 10:04
@DirkDoes DirkDoes reopened this Oct 4, 2026
@DirkDoes
DirkDoes changed the base branch from task/settings-persistence to main October 4, 2026 10:04
@DirkDoes
DirkDoes added this pull request to stack #508 October 4, 2026 10:06
@DirkDoes
DirkDoes merged commit 867c560 into main Oct 4, 2026
3 checks passed
@DirkDoes
DirkDoes deleted the task/localization-plurals branch October 4, 2026 10:34
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.

2 participants