Tasks completed: 3/3 - Load a namespaced translation from an embedded plugin - Select CLDR and Laravel plural forms with PHP parameters - Apply configured locale fallback and missing-key behavior SUMMARY: .planning/phases/04-cli-scaffolding-i18n-and-mail/04-02-SUMMARY.md Co-authored-by: Cursor <cursoragent@cursor.com>
7.2 KiB
phase, plan, subsystem, tags, requires, provides, affects, tech-stack, key-files, key-decisions, patterns-established, requirements-completed, duration, completed
| phase | plan | subsystem | tags | requires | provides | affects | tech-stack | key-files | key-decisions | patterns-established | requirements-completed | duration | completed | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 04-cli-scaffolding-i18n-and-mail | 02 | i18n |
|
|
|
|
|
|
|
|
|
10min | 2026-09-18 |
Phase 4 Plan 02: i18n phrasebook Summary
Plugin-owned English and Polish YAML catalogs resolve vendor.plugin::group.key with CLDR and Laravel plurals, :name case variants, and pl-PL→pl→fallback→raw lookup
Performance
- Duration: 10 min
- Started: 2026-09-18T11:43:00Z
- Completed: 2026-09-18T11:52:55Z
- Tasks: 3
- Files modified: 11
Accomplishments
phrasebookloads embeddedlang/<locale>/<group>.yaml, flattens nested maps tovendor.plugin::group.dot.path, and rejects malformed catalogs with plugin/file/key contextparty.Activatepublishes one*phrasebook.Translatorafter Register and before Boot, usingapp.locale/app.fallback_localewith framework defaultsen/en- Polish and English plural maps and Laravel pipe strings select the specified forms;
:name,:Name, and:NAMEsubstitute after selection without sending YAML through go-i18n templates - Requested locale walks parent then configured fallback then returns the raw key; missing keys log once outside production
Task Commits
Each task was committed atomically:
- Task 1: Load a namespaced translation from an embedded plugin -
f824e55(feat) - Task 2: Select CLDR and Laravel plural forms with PHP parameters -
6d8786a(feat) - Task 3: Apply configured locale fallback and missing-key behavior -
8a4e0ef(feat)
Plan metadata: (this commit)
Files Created/Modified
phrasebook/loader.go- WalkDir catalog load, YAML flatten, plural-map detection, pipe parsephrasebook/translator.go- Get/Choice, CLDR category selection, fallback chain, missing-key logphrasebook/translator_test.go- translation, plural, and locale-fallback smokesparty/registry.go- HasLang scan and translator publish before Bootparty/registry_test.go- translator available at Boot; malformed lang fails with plugin idexamples/hello/plugins/base/plugin.go- embeds LangFS and implements HasLangexamples/hello/plugins/base/lang/en/lang.yaml- English nested keys, plurals, pipes, parametersexamples/hello/plugins/base/lang/pl/lang.yaml- Polish nested keys, CLDR map and pipe formsexamples/hello/hello_test.go- Activate + en/pl/pl-PL/fallback/raw-key smokego.mod/go.sum- pin go-i18n/v2 v2.6.1
Decisions Made
- go-i18n receives only category-name message variants and
IdentityParser, so PHP:nameplaceholders and{{.Count}}YAML stay literal - Each locale gets its own
i18n.Bundledefault language so Polishfew/manyare real categories, not Englishother - Fallback stops at configured
app.fallback_localethen the raw key; there is no extra core-locale step beyond that default
Deviations from Plan
Auto-fixed Issues
1. [Rule 3 - Blocking] phrasebook tests cannot import party
- Found during: Task 1 (TestTranslationSmoke Activate)
- Issue:
phrasebook_test→party→phrasebookis an import cycle in tests - Fix: Call
phrasebook.Activatefrom phrasebook tests; keepparty.ActivateHasLang wiring tests inparty/registry_test.go - Files modified:
phrasebook/translator_test.go,party/registry_test.go - Verification:
TestTranslationSmokeandTestActivatePublishesTranslatorBeforeBootpass - Committed in:
f824e55(Task 1 commit)
2. [Rule 1 - Bug] go-i18n rejects float PluralCount
- Found during: Task 2 (fractional plural samples)
- Issue:
plural.NewOperandsrequires integers or decimal strings, notfloat64 - Fix: Coerce float counts to decimal strings before category selection
- Files modified:
phrasebook/translator.go - Verification:
TestPluralSmokecovers1.5for pl/en - Committed in:
6d8786a(Task 2 commit)
3. [Rule 1 - Bug] English default bundle hid Polish CLDR forms
- Found during: Task 2 (Polish
fewrejected as invalid forpl) - Issue: A single
NewBundle(language.English)made Localizer match English plural rules - Fix: Cache one bundle per locale tag with that tag as default language
- Files modified:
phrasebook/translator.go - Verification: Polish counts 1/2/5/22 select one/few/many/few
- Committed in:
6d8786a(Task 2 commit)
Total deviations: 3 auto-fixed (1 blocking, 2 bugs) Impact on plan: No behavior divergence from D-01–D-05. Fixes were required for catalog tests to compile and for Polish CLDR selection to be real.
Issues Encountered
None
User Setup Required
None - no external service configuration required.
Next Phase Readiness
Ready for 04-03 (postcard / mail). Translator is app-scoped and available at plugin Boot. Unit-test coverage remains the last plan (04-04). Per-request preferred-locale lookup stays Phase 7.
Verification
go test ./phrasebook -run 'TestTranslationSmoke|TestPluralSmoke|TestLocaleFallbackSmoke' -short -count=1— PASSgo -C examples/hello test ./...— PASSgo vet ./... && go test ./...— PASS- No second context key; phrasebook reads
towel.Localeonly - No package-level current-locale variable
Self-Check: PASSED
Phase: 04-cli-scaffolding-i18n-and-mail Completed: 2026-09-18