Repository navigation
Canadian French (fr-CA) as its own locale, not a fallback - #39
Conversation
`fr-CA` currently resolves to the France copy, and the differences are not stylistic. `courriel` rather than `e-mail`, `reporter`/`report` rather than `reprogrammer`/`reprogrammation`, `renseignements personnels` rather than `données personnelles` (the Quebec statutory term, so the wrong one is wrong in a legal sense rather than a register one), and no space before `!` `?` `;` where France puts one. CLDR itself disagrees on an abbreviation: July is `juill.` in fr-CA against `juil.` in fr. It is the first REGIONAL locale, which is the only structural thing here: the matcher already prefers an exact tag, so `fr-FR` and plain `fr` are untouched by this file existing. TestResolve now pins that in both directions rather than only the new one, since the half that would break silently is the French visitor who never asked for any of this. The three guards the other eight locales already pass apply unchanged — same key set as English, matching printf verbs, and date tables cross-checked against CLDR — so the structure is verified even though the wording is not.⚠️ The copy is an unreviewed draft, like every non-English locale here. Nobody who speaks Canadian French has read it. That is said plainly in the CHANGELOG so a correction arrives as an ordinary pull request rather than as a bug report. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
There was a problem hiding this comment.
ℹ️ No critical issues — two stale locale counts outside the files this PR already updated.
Reviewed changes One commit adding Canadian French as a first-class regional locale (data + Resolve pins + doc counts). No matcher or handler changes.
fr-CA.jsonlocale table New 158-key file with the claimed fr vs fr-CA differences (courriel,reporter/report,renseignements personnels, punctuation spacing, CLDRjuill.). Key set matchesen; package tests pass.TestResolvepins both directions Exactfr-CA, Europeanfr-FR/frunaffected, and?lang=fr-CAoverride — the half that would break silently if the matcher preferred the new regional tag too broadly.- Doc/CHANGELOG counts
CLAUDE.md,CONTRIBUTING.md,ARCHITECTURE§23, and the Unreleased CHANGELOG entry now say 9 locales / listfr-CA.
ℹ️ Locale counts still say 8 in two places this PR did not touch
README.md still markets "8 languages" and lists English through Swedish with no Canadian French — the most visible user-facing surface for the feature. docs/ARCHITECTURE.md (~L809, LiveKit "not translated" known issue) still says the booking surfaces "ship in 8 languages (§23)", which now contradicts §23's "9 locales". Historical "8 locales" changelog entries are fine to leave.
Technical details
# Stale locale counts outside the PR's doc set
## Affected sites
- `README.md` ~L193–195 — still "**8 languages**" and the bullet list without Canadian French
- `docs/ARCHITECTURE.md` ~L808–809 — LiveKit known-issue still "ship in 8 languages (§23)" while §23 now says 9
## Required outcome
- User-facing and cross-referenced live counts match the shipped locale set (`en es fr fr-CA de it pt nl sv` / 9)
- Do not rewrite historical CHANGELOG / ARCHITECTURE dated entries that correctly described the state at ship time
## Suggested approach (optional)
- Bump README to 9 languages and add Canadian French to the list (same register as the other names)
- Change ARCHITECTURE L809 "8 languages" → "9 languages" (or "§23's locales") so it stays consistent with §23Grok | 𝕏
shockalotti
left a comment
There was a problem hiding this comment.
The locale itself follows the documented pattern (one JSON file, three guards green, honest unreviewed-draft label in CHANGELOG per §23's marketing rule) — no issue with the fr-CA copy as a draft. But this PR puts the docs in a self-contradictory state:
It updates the count to 9 in ARCHITECTURE §23, CONTRIBUTING, and CLAUDE.md, while README.md ("8 languages" bullet) and ARCHITECTURE §18 ("ship in 8 languages") still say 8. Merged as-is, the repo claims 8 and 9 locales at the same time. Please update those two spots to 9 in this PR.
Standing note (no action needed here): per §23's translation-quality rule, fr-CA stays unmarketed until a native speaker reads the booking page, manage page, and four emails end to end — the CHANGELOG already says this, good.
The locale commit moved the count to nine in ARCHITECTURE §23, CONTRIBUTING and CLAUDE.md but left the README's feature bullet and the LiveKit known issue in §18 at eight, so the repository claimed both numbers at once. Both now say nine, and the README's list names Canadian French. The dated 2026-08-20 entries in the CHANGELOG and the ARCHITECTURE log keep "8 locales": they describe what shipped that day. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
There was a problem hiding this comment.
✅ No new issues found.
Reviewed changes One docs commit since the prior review, filling the two live locale-count gaps that review called out. No code or locale-table changes in this delta.
- README language bullet Count is now 9 and the list includes Canadian French alongside French.
- ARCHITECTURE LiveKit known-issue The §18 line now says the booking surfaces ship in 9 languages, matching §23.
Grok | 𝕏
|
Fixed in 6bd9e48: the README feature bullet now says 9 languages and lists Canadian French, and the LiveKit entry in §18 says 9. I left the two dated 2026-08-20 entries (the CHANGELOG and the ARCHITECTURE log) at "8 locales", since they describe what shipped that day. Understood on the translation rule: fr-CA stays an unreviewed draft until a native speaker has read the booking page, the manage page and the four emails, and the CHANGELOG line keeps saying so. |
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
The branch was cut before 0.9.0, when its entry sat under [Unreleased]. 0.9.0 renamed that section, and merging main moved the entry with it without a conflict, so it read as something 0.9.0 shipped. Moved back under [Unreleased], text unchanged. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
#40 was squash-merged on 2026-09-17 from a branch cut before 0.9.0 (2026-09-10), and its entry landed under 0.9.0's Added the same way, without a conflict. 0.9.0 shipped without the setting. Moved under [Unreleased], text unchanged. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
There was a problem hiding this comment.
✅ No new issues found.
Reviewed changes Changelog hygiene after merging main (which cut 0.9.0). No locale-table or matcher changes in this delta.
fr-CAchangelog placement The Canadian French entry now lives under[Unreleased], not[0.9.0].FRAME_ANCESTORSchangelog placement Same correction for the adminframe-ancestorsnote, which landed onmainafter the 0.9.0 tag.
Grok | 𝕏
|
Merged #40 hit the same thing when it was squash-merged: |

First of the features split out of #30, in the order of appetite you described on that thread.
fr-CAcurrently resolves to the France copy through the primary-subtag fallback. The differences are not stylistic:e-mailcourrielreprogrammer/reprogrammationreporter/reportdonnées personnellesrenseignements personnels!?;juil.juill.The third row is the one that matters most:
renseignements personnelsis the statutory term in Quebec, so the France word is wrong in a legal sense rather than a register one. The last row is CLDR's own disagreement, not a preference, which is whyTestDateTablesMatchCLDRcatches it.The only structural point
This is the first regional locale.
Resolvealready prefers an exact tag before falling back to the primary subtag, so no matcher change was needed — but that means the risk is not in the new locale, it is in the eight that already work. A visitor sendingfr-FRor plainfrmust be untouched by this file existing.TestResolvenow pins both directions:{"Canadian French resolves to its own locale", "fr-CA,fr;q=0.9", "", "fr-CA"}, {"European French is unaffected by fr-CA", "fr-FR,fr;q=0.9", "", "fr"}, {"plain fr is unaffected by fr-CA", "fr", "", "fr"}, {"fr-CA can be selected by the ?lang override", "en", "fr-CA", "fr-CA"},Mutation-checked rather than assumed: with the locale file moved aside, the two positive cases fail (
Resolve("fr-CA,fr;q=0.9", "") = "fr", want "fr-CA") while the twofr/fr-FRcases stay green, which is what they are for — they assert an absence of change and would be worthless if removing the feature moved them.What is verified and what is not
The three guards the other eight locales pass apply unchanged: same key set as English (
TestAllLocalesHaveTheSameKeys), matching printf verbs including the%[1]s/%[2]spositional pairno_available_times_hostneeds (TestAllLocalesHaveMatchingFormatVerbs), and date tables cross-checked against CLDR (TestDateTablesMatchCLDR). So the structure is verified.Currency and percent take a non-breaking space before
$and%in Canadian French. No key carries either today, so that rule is recorded in ARCHITECTURE §23 rather than applied — worth writing down before someone adds a priced-event string.Scope
Nine files: the locale, four test cases, and the doc/CHANGELOG counts that say "8 locales". No handler, no matcher, no schema.
internal/i18npasses; the full suite passes on this branch.Adding a locale is still just adding
internal/i18n/locales/<code>.json—init()globs the directory, and nothing else needed a list updating.🤖 Generated with Claude Code