11 KiB
Phase 14.2.1: Translate plugin - Context
Gathered: 2026-10-06 Status: Ready for planning
## Phase BoundaryGolem15.Translate is ported to Go as sm-translate-plugin so Journal (Phase 15) and later blogs can keep translatable fields. This phase ships the lean core Journal needs: the Locale model and its admin screen, the TranslatableModel behavior, cabana ML field types, and PHP's request-locale resolution.
The proof host is sm-grzybyfunkcjonalne-app, booted with sm-user-plugin + sm-translate-plugin (Journal mounts in Phase 15). Proof: the app boots, Locales admin works, and a fixture model can save and read en+pl through ML fields and WithLocale.
summercms.go is touched for the ML field types (mltext, mlmarkdown) and any Translator/request-locale seam the plugin cannot own alone.
Out of scope: the Messages catalogue and manage_messages admin, locale-picker / hreflang / suggestion-banner CMS components, AI and theme console commands, message import/export, and any edit to wn-translate-plugin. Those wait for a later translate follow-up if a site needs them.
PHP source of truth
- D-01: The port contract is
github.com/golem15com/wn-translate-pluginat git SHA725d547(2026-08-26), the pin currently checked out in Płytarium. That tree is newer than grzybyfunkcjonalne.pl (6e0c146). — Reversibility: costly — Locale tables, attribute storage and Translator behaviour are copied from this tree. - D-02: Freeze at
725d547. Planner and researcher must not silently pick a newermaster. — Reversibility: reversible — a later discussion can re-pin. - D-03: Downstream agents read the frozen source from
/media/nvme/dev/golem15/fonoteka/plugins/golem15/translate(already at that SHA). Do not clone a second copy unless that path is missing or the SHA does not match. - D-04: This phase does not edit or PR
wn-translate-plugin. An urgent PHP upgrade on a live Winter site before that site migrates is allowed as operations. If it lands before plan or execute, re-pin the SHA rather than mixing trees.
What ships this phase
- D-05: Lean only: Locale model + Locales admin (
golem15.translate.manage_locales), and a TranslatableModel behavior Journal can implement. Messages catalogue, locale-picker components, AI/theme commands and message import/export are deferred. — Reversibility: reversible — later plans can add the rest without rewriting Locale. - D-06: Cabana gains ML field types (
mltext,mlmarkdown) matching PHP'sml*widgets, so a Journal form stays one screen with a locale switch. Phase 15's markdown field (15 D-11) and these ML types must compose (mlmarkdownis markdown plus locale). — Reversibility: costly — new field types in the typed admin schema and generated TS types. - D-07: Port PHP
Translatorlocale resolution: URL prefix / session / cookie / default. Do not reduce this phase toAccept-Languageor a query param only. — Reversibility: costly — public blog URLs and the Journal API will depend on this order. - D-08: The plugin seeds English and Polish locales, matching Journal 15 D-06.
Model API
- D-09: A Go model declares translatable attributes with a
Translatable() []string(or equivalent tagged list) the way PHP uses$translatable. Journal copies the PHP Post/Category lists. — Reversibility: costly — every translatable model in shared plugins will call this. - D-10: Storage uses the same tables and columns as this PHP pin (
golem15_translate_*attributes/indexes/locales — researcher confirms exact names from the frozen updates). No JSON-column alternative. — Reversibility: one-way — Winter import and Journal cutover expect those tables. - D-11: A missing translation falls back to the default locale, as PHP does.
- D-12: The exported API other plugins call this phase is the minimum:
WithLocaleon queries, get/set an attribute in a locale, and current locale from Translator. Do not 1:1 every PHPTranslatableModel/Translatormethod name unless research shows Journal needs it.
Where it boots
- D-13: The host is
sm-grzybyfunkcjonalne-app(same as Phase 15 D-18). This phase mountssm-user-plugin+sm-translate-plugin; Journal is added in Phase 15. — Reversibility: one-way — submodule layout lands in that app. - D-14: Local app checkout:
/media/nvme/dev/golem15/summercms.io/summercms/sm-grzybyfunkcjonalne-app/(sibling ofsummercms.go). - D-15: The user creates the public Gitea remote
git@git.golem15.com:golem15/sm-translate-plugin.gitas a manual plan step (it does not exist yet). Module pathgit.golem15.com/golem15/sm-translate-plugin, packagetranslate, plugin IDgolem15.translate. - D-16: Local plugin checkout:
/media/nvme/dev/golem15/summercms.io/summercms/sm-translate-plugin/(sibling ofsummercms.go). The host mounts it as a submodule atplugins/golem15/translatewith ago.workreplace. - D-17: Phase proof: the app boots with user+translate; Locales admin works; a fixture model can save and read
en+plthrough ML fields andWithLocale.
Claude's Discretion
- Exact
golem15_translate_*table/column names and migration IDs, taken from the frozen PHP updates (D-10), not invented. - ML field YAML type names and SPA locale-switcher chrome, provided they follow cabana fail-loud rules and update the module README,
docs/, admin OpenAPI, generated TS types and committeddist/in the same change. - How
mlmarkdownshares implementation with Phase 15's markdown field (land the shared primitive here if Journal is otherwise blocked). - Plan count and split, subject to the plan-count checkpoint and "unit tests are the last plan".
- Run the security-review agent: locale switching on public requests and admin ML writes are authorization-adjacent.
<canonical_refs>
Canonical References
Downstream agents MUST read these before planning or implementing.
Roadmap and prior decisions
.planning/ROADMAP.md§ Phase 14.2.1 — stub goal; this file is the real scope.planning/ROADMAP.md§ Phase 15 — Journal depends on this plugin.planning/phases/15-journal-plugin/15-CONTEXT.md— D-09/D-10 (Journal requires translate; translatable title/slug/content), D-06 (en+pl), D-11 (markdown field), D-18–D-21 (grzyby host).planning/notes/core-plugins-own-repos.md—sm-<name>-pluginlayout, module path, submodule mount.planning/phases/12.1-user-plugin-admin-screens/12.1-CONTEXT.md— YAML admin screens, framework-first field types.planning/phases/04-cli-scaffolding-i18n-and-mail/04-CONTEXT.md— phrasebook; this plugin is model-level translation, not UI strings.planning/phases/09-backend-admin-authentication-and-schema-pipeline/09-CONTEXT.md— typed schema, fail-loud YAML
PHP contract (read-only) — /media/nvme/dev/golem15/fonoteka/plugins/golem15/translate at SHA 725d547
Plugin.php— permissions (manage_locales,manage_messages), navigation, components (components are out of scope)models/Locale.php,models/Attribute.php,models/Message.php(Message is out of scope except as a table that must not be broken)behaviors/— TranslatableModel (or equivalent) and how$translatableis appliedclasses/Translator.php— URL prefix / session / cookie / default (D-07)controllers/Locales.phpand YAML undercontrollers/locales/,models/locale/formwidgets/— PHPml*widgets that D-06 replacesupdates/— Locale / attributes / indexes table names and migration IDs (D-10)README.md,UPGRADE.mdtests/— use as landmines; lean scope means not every PHP test maps 1:1
Go references
../fonoteka.go/plugins/golem15/user/— mount pattern for the sibling pluginmodules/cabana/— field registry;mltext/mlmarkdownland heremodules/phrasebook/— UI strings; do not conflate with model translationsdocs/backend/forms.md,docs/backend/admin-controllers.md
Remotes
git@git.golem15.com:golem15/sm-translate-plugin.git— does not exist yet; user creates it, publicgit@git.golem15.com:golem15/sm-grzybyfunkcjonalne-app.git— empty proof host, already createdgit@git.golem15.com:golem15/sm-journal-plugin.git— public, empty; mounted in Phase 15
</canonical_refs>
<code_context>
Existing Code Insights
Reusable Assets
sm-user-pluginmount pattern:summer.yaml, submodule atplugins/golem15/user,go.workreplace,pact.HasAdminControllers.- cabana field registry (Phase 12.1
permissioneditor, Phase 12.2fileupload/datepicker): the pattern formltext/mlmarkdown. - phrasebook
en/plfor plugin UI strings — separate from Locale rows and attribute translations. sm-grzybyfunkcjonalne-appremote already exists; local directory may not.
Established Patterns
- Shared plugins: module
git.golem15.com/golem15/sm-<name>-plugin, package plain (translate), plugin IDgolem15.translate. - Unknown YAML keys fail boot. Framework field types update README,
docs/, OpenAPI, TS types andboardwalkdist/in the same change. - PHP originals are not changed. Migrations are append-only. Unit tests are the last plan.
Integration Points
- New repo
sm-translate-plugin: Locale model, updates, Locales admin, Translatable behavior, Translator. summercms.gocabana + admin SPA:mltext,mlmarkdown, locale switcher on forms.sm-grzybyfunkcjonalne-app: first mount of user + translate.- Phase 15 Journal:
Requirestranslate; Post/CategoryTranslatable()lists;mltext/mlmarkdownon YAML forms.
</code_context>
## Specific Ideas- Same freeze/read-only/re-pin rules as Journal, applied to Translate.
- "Start fast" on
sm-grzybyfunkcjonalne-appstill holds; this phase boots that app without Journal. - Messages, CMS locale components and AI/theme commands were explicitly left out so Journal is not blocked.
- Messages catalogue and
golem15.translate.manage_messagesadmin. - Locale picker, alternate hreflang, and locale-suggestion-banner CMS components (Phase 16 views may need a successor).
- AI/theme console commands and message import/export.
- Remaining PHP locales beyond the
en+plseed (D-08 is seed rows, not phrasebook files). - Phase 15 should list Depends on: Phase 14.2.1 in ROADMAP (
$gsd-phase --edit 15); not done in this discussion.
Reviewed Todos (not folded)
sitemap-plugin-port.md,mailblocker-with-mailing.md, and the other keyword matches fromtodo.match-phase 14.2.1: unrelated housekeeping.
Phase: 14.2.1-translate-plugin Context gathered: 2026-10-06