Files
summercms/.planning/phases/14.2.1-translate-plugin/14.2.1-CONTEXT.md
2026-10-06 10:41:43 +02:00

11 KiB
Raw Blame History

Phase 14.2.1: Translate plugin - Context

Gathered: 2026-10-06 Status: Ready for planning

## Phase Boundary

Golem15.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.

## Implementation Decisions

PHP source of truth

  • D-01: The port contract is github.com/golem15com/wn-translate-plugin at git SHA 725d547 (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 newer master. — 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's ml* 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 (mlmarkdown is markdown plus locale). — Reversibility: costly — new field types in the typed admin schema and generated TS types.
  • D-07: Port PHP Translator locale resolution: URL prefix / session / cookie / default. Do not reduce this phase to Accept-Language or 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: WithLocale on queries, get/set an attribute in a locale, and current locale from Translator. Do not 1:1 every PHP TranslatableModel / Translator method 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 mounts sm-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 of summercms.go).
  • D-15: The user creates the public Gitea remote git@git.golem15.com:golem15/sm-translate-plugin.git as a manual plan step (it does not exist yet). Module path git.golem15.com/golem15/sm-translate-plugin, package translate, plugin ID golem15.translate.
  • D-16: Local plugin checkout: /media/nvme/dev/golem15/summercms.io/summercms/sm-translate-plugin/ (sibling of summercms.go). The host mounts it as a submodule at plugins/golem15/translate with a go.work replace.
  • D-17: Phase proof: the app boots with user+translate; Locales admin works; a fixture model can save and read en+pl through ML fields and WithLocale.

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 committed dist/ in the same change.
  • How mlmarkdown shares 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>-plugin layout, 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 $translatable is applied
  • classes/Translator.php — URL prefix / session / cookie / default (D-07)
  • controllers/Locales.php and YAML under controllers/locales/, models/locale/
  • formwidgets/ — PHP ml* widgets that D-06 replaces
  • updates/ — Locale / attributes / indexes table names and migration IDs (D-10)
  • README.md, UPGRADE.md
  • tests/ — 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 plugin
  • modules/cabana/ — field registry; mltext / mlmarkdown land here
  • modules/phrasebook/ — UI strings; do not conflate with model translations
  • docs/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, public
  • git@git.golem15.com:golem15/sm-grzybyfunkcjonalne-app.git — empty proof host, already created
  • git@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-plugin mount pattern: summer.yaml, submodule at plugins/golem15/user, go.work replace, pact.HasAdminControllers.
  • cabana field registry (Phase 12.1 permissioneditor, Phase 12.2 fileupload / datepicker): the pattern for mltext / mlmarkdown.
  • phrasebook en/pl for plugin UI strings — separate from Locale rows and attribute translations.
  • sm-grzybyfunkcjonalne-app remote already exists; local directory may not.

Established Patterns

  • Shared plugins: module git.golem15.com/golem15/sm-<name>-plugin, package plain (translate), plugin ID golem15.translate.
  • Unknown YAML keys fail boot. Framework field types update README, docs/, OpenAPI, TS types and boardwalk dist/ 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.go cabana + admin SPA: mltext, mlmarkdown, locale switcher on forms.
  • sm-grzybyfunkcjonalne-app: first mount of user + translate.
  • Phase 15 Journal: Requires translate; Post/Category Translatable() lists; mltext/mlmarkdown on YAML forms.

</code_context>

## Specific Ideas
  • Same freeze/read-only/re-pin rules as Journal, applied to Translate.
  • "Start fast" on sm-grzybyfunkcjonalne-app still 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.
## Deferred Ideas
  • Messages catalogue and golem15.translate.manage_messages admin.
  • 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+pl seed (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 from todo.match-phase 14.2.1: unrelated housekeeping.

Phase: 14.2.1-translate-plugin Context gathered: 2026-10-06