--- phase: 14.2.1-translate-plugin plan: 01 subsystem: i18n tags: [translate, sm-translate-plugin, gormigrate, LocaleResolver, towel.WithLocale, golem15_translate] requires: - phase: 01-framework-kernel-foundation provides: "compiled party.Register plugins, backpack Publish/Lookup" - phase: 04-cli-scaffolding-i18n-and-mail provides: "towel.WithLocale request locale bag" - phase: 06-http-middleware-and-openapi provides: "surf wrap locale() house middleware and BuildRouter" provides: - "Module git.golem15.com/golem15/sm-translate-plugin, package translate, plugin ID golem15.translate" - "gormigrate tables golem15_translate_locales, golem15_translate_attributes, golem15_translate_indexes, empty golem15_translate_messages" - "Idempotent en/pl Locale seed (en default sort_order 1, pl Polski enabled non-default sort_order 2)" - "classes.Translator context-only Locale(ctx); surf.LocaleResolver published at Boot" - "Optional surf resolver seam: URL prefix rewrite before routing, validated towel.WithLocale; Accept-Language fallback when unpublished" affects: [14.2.1-02 Translatable API and Locales admin, 14.2.1-03 proof host mount, 15-journal-plugin] actuals: tokens: 29734 tasks: 3 commits: 3 plan_head_before: e88007aa7620c8c5067d0276e8a1a9fc6dcdd97c plan_head_after: b2ce986604521234140adcc3d02226676435ee1e plugin_repo_head_after: f6cf99c00b2d2b1951d2971e574acf0ff724daad tech-stack: added: [] patterns: - "Go translate tables are golem15_translate_*; PHP pin keeps winter_translate_* and is not copied into Go DDL" - "surf.LocaleResolver lives in the framework; the plugin Boot-publishes it; hosts without translate keep Accept-Language" - "Request locale is context-only (KERN-07); locale_manually_set is a flag cookie with value 1" key-files: created: - ../sm-translate-plugin/go.mod - ../sm-translate-plugin/plugin.go - ../sm-translate-plugin/README.md - ../sm-translate-plugin/config/config.yaml - ../sm-translate-plugin/models/registry.go - ../sm-translate-plugin/models/locale.go - ../sm-translate-plugin/updates/registry.go - ../sm-translate-plugin/updates/202610060001_create_golem15_translate_locales.go - ../sm-translate-plugin/updates/202610060002_create_golem15_translate_attributes.go - ../sm-translate-plugin/updates/202610060003_create_golem15_translate_indexes.go - ../sm-translate-plugin/updates/202610060004_create_golem15_translate_messages.go - ../sm-translate-plugin/updates/202610060005_seed_en_pl_locales.go - ../sm-translate-plugin/classes/translator.go - modules/surf/locale_resolver.go - modules/surf/locale_resolver_test.go modified: - modules/surf/router.go - modules/surf/README.md - docs/architecture/request-lifecycle.md - docs/services/localization.md - docs/services/routing.md key-decisions: - "Task 2 locked golem15-prefix: Go tables are golem15_translate_locales, golem15_translate_attributes, golem15_translate_indexes, golem15_translate_messages. PHP at SHA 725d547 still uses winter_translate_*; those names are not copied into Go DDL. No rainlab_translate_* tables. No JSON-column storage." - "Sibling plugin go.mod replaces the framework with ../summercms.go so isolated go test in the D-16 checkout passes; a host mount supplies its own go.work replace." patterns-established: - "Squashed gormigrate files named 202610060001_create_golem15_translate_* through 005 seed; never AutoMigrate" - "Translator.Rewrite strips an enabled first path segment before ServeMux; Resolve order is URL, principal, remembered cookie, cookie-gated Accept-Language, default" - "surf stores app on Router at BuildRouter and Lookup[LocaleResolver] without importing the plugin" requirements-completed: [D-01, D-02, D-03, D-04, D-07, D-08, D-10, D-12, D-15, D-16] coverage: - id: D1 description: "Compiled sm-translate-plugin module with ID golem15.translate publishes surf.LocaleResolver at Boot" verification: - kind: unit ref: "sm-translate-plugin#TestPluginID" status: pass - kind: other ref: "go -C ../sm-translate-plugin vet ./... && go -C ../sm-translate-plugin test ./... -short -count=1" status: pass human_judgment: false - id: D2 description: "Four golem15_translate_* tables plus empty messages and idempotent en/pl seed via gormigrate" verification: - kind: other ref: "rg golem15_translate_ updates/202610060001..004; seed file has English/Polski and no Deutsch" status: pass human_judgment: false - id: D3 description: "Optional surf LocaleResolver seam writes a validated code with towel.WithLocale and keeps Accept-Language when unpublished" verification: - kind: unit ref: "modules/surf#TestLocaleResolverWritesValidatedCodeAndStripsPrefix" status: pass - kind: unit ref: "modules/surf#TestLocaleAbsentResolverKeepsAcceptLanguage" status: pass - kind: other ref: "go vet ./modules/surf/... && go test ./modules/surf -short -count=1" status: pass human_judgment: false - id: D4 description: "PHP translate tree at SHA 725d547 is unchanged" verification: - kind: other ref: "git -C /media/nvme/dev/golem15/fonoteka/plugins/golem15/translate status --short (empty) and rev-parse HEAD" status: pass human_judgment: false duration: 16min completed: 2026-10-06 status: complete --- # Phase 14.2.1 Plan 01: Translate plugin repository, schema, and locale seam Summary **Sibling `sm-translate-plugin` with squashed `golem15_translate_*` tables, en/pl Locale seed, context-only Translator, and an optional surf `LocaleResolver` seam** ## Performance - **Duration:** 16 min - **Started:** 2026-10-06T09:37:49Z - **Completed:** 2026-10-06T09:54:21Z - **Tasks:** 3 - **Files modified:** 23 ## Accomplishments - Created the compiled plugin module `git.golem15.com/golem15/sm-translate-plugin` (package `translate`, ID `golem15.translate`) in the D-16 sibling checkout, with config, Locale model, five gormigrate steps, and Boot publication of `surf.LocaleResolver`. - Squashed the PHP pin's columns and indexes onto `golem15_translate_locales`, `golem15_translate_attributes`, `golem15_translate_indexes`, and empty `golem15_translate_messages`. Seeded enabled default `en` at sort_order 1 and enabled non-default `pl` (Polski) at sort_order 2. No Deutsch row. No AutoMigrate. No `winter_translate_*` or `rainlab_translate_*` in Go source. - Implemented `classes.Translator` with D-07 order (URL prefix → principal preferred_locale → remembered `golem15.translate.locale` → cookie-gated Accept-Language → default), `ResolveAPI` without persistence, and `Translator.Locale(ctx)` only. `locale_manually_set` is a flag with value `1`. - Added framework `surf.LocaleResolver`; `BuildRouter` stores the app; prefix rewrite runs before CORS/routing; `locale()` writes `towel.WithLocale`. Hosts without the plugin keep raw Accept-Language. ## Task Commits Each task was committed atomically: 1. **Task 1: Create the public Gitea remote and authorize local repository setup** - `d878a67` (sm-translate-plugin, chore) / `28bd45f` (summercms.go, docs) 2. **Task 2: Confirm the one-way table contract** - recorded in this summary (`golem15-prefix`); no code commit 3. **Task 3: Resolve one URL-prefixed request through the plugin, enabled Locale row, surf, and context** - `f6cf99c` (sm-translate-plugin, feat) / `b2ce986` (summercms.go, feat) **Plan metadata:** (this commit) _Note: Task 2 was a checkpoint:decision. The locked answer `golem15-prefix` was applied before Task 3._ ## Files Created/Modified - `../sm-translate-plugin/plugin.go` - compiled Plugin, HasConfig/HasMigrations/HasModels, Boot publishes LocaleResolver - `../sm-translate-plugin/models/locale.go` - Locale, no timestamps, Fillable code/name/is_enabled - `../sm-translate-plugin/updates/202610060001_create_golem15_translate_locales.go` through `004` messages and `005` en/pl seed - `../sm-translate-plugin/classes/translator.go` - Rewrite, Resolve, ResolveAPI, Locale(ctx) - `../sm-translate-plugin/README.md` - application-neutral plugin contract - `modules/surf/locale_resolver.go` - framework LocaleResolver interface - `modules/surf/router.go` - app on Router, prefix strip, resolver lookup - `modules/surf/README.md`, `docs/services/routing.md`, `docs/services/localization.md`, `docs/architecture/request-lifecycle.md` - exported seam ## Decisions Made - **golem15-prefix (Task 2, locked):** Go persistent tables are `golem15_translate_locales`, `golem15_translate_attributes`, `golem15_translate_indexes`, `golem15_translate_messages`. PHP SHA `725d547ec839f02b5fdc0f0a6faaed601a414d50` still uses `winter_translate_*`; those names are not copied into Go DDL. `winter-final` and `host-json` were not implemented. - Sibling `go.mod` replace is `../summercms.go` so `go -C ../sm-translate-plugin test` works in the D-16 checkout. A host that mounts the plugin at `plugins/golem15/translate` should replace the framework from the host `go.work`. ## Deviations from Plan ### Auto-fixed Issues **1. [Rule 3 - Blocking] Sibling go.mod replace path** - **Found during:** Task 3 (module init) - **Issue:** PATTERNS copies the submodule replace `../../../../summercms.go`, which does not resolve from `/media/nvme/dev/golem15/summercms.io/summercms/sm-translate-plugin`. The plan's verify command is `go -C ../sm-translate-plugin vet ./...`. - **Fix:** `replace git.golem15.com/golem15/summercms => ../summercms.go`, documented in the plugin README. - **Files modified:** `../sm-translate-plugin/go.mod`, `../sm-translate-plugin/README.md` - **Verification:** `go -C ../sm-translate-plugin vet ./...` and `go test ./... -short` pass - **Committed in:** `f6cf99c` (Task 3 plugin commit) --- **Total deviations:** 1 auto-fixed (1 blocking) **Impact on plan:** Required for the isolated sibling verify command. No schema or resolver-order change. ## Issues Encountered None beyond the replace-path deviation. ## Authentication Gates None. ## User Setup Required None - no external service configuration required. ## Next Phase Readiness Ready for 14.2.1-02 (Translatable API, Locales admin, fixture). Schema names and the request-locale seam are the import contract. Messages admin, CMS components, and cabana ML fields stay later plans. ## Self-Check: PASSED --- *Phase: 14.2.1-translate-plugin* *Completed: 2026-10-06*