feat(i18n): make site bilingual (DE + EN) via Craft multi-site #5

Merged
Phil merged 4 commits from feat/multilingual-en into main 2026-05-31 06:10:31 +00:00
Owner

Was

Die ganze Seite ist jetzt zweisprachig (DE + EN) — natives Craft-5-Multi-Site, kein Plugin. Englisch lebt unter /en/, Deutsch bleibt auf Root.

Schichten (4 Commits)

  1. chore(docker)icu-data-full in alle 3 Dockerfiles. Root-Cause: php:8.3-fpm-alpine bringt nur icu-data-en, dadurch rendert der Craft-Formatter jedes Datum englisch. Jetzt lokalisiert ICU korrekt.
  2. feat(i18n) EN-Site — zweite Site en (baseUrl: $EN_SITE_URL) im bestehenden Site-Group, alle 4 Sections + Category/Tag-Groups für beide Sites aktiv. EN_SITE_URL in .env + allen getrackten env-Templates (from-scratch reproduzierbar). Das /en-Präfix kommt aus der baseUrl, nicht aus den URI-Formaten.
  3. feat(i18n) FeldertranslationMethod: none→site für die Prosa-Felder (articleLead, articleTldr, articleBody, articleFootnotes, articleHeroCaption, articleStats, seo). Referenz-Felder bleiben sprachneutral. DE-Werte seeden die EN-Variante.
  4. feat(i18n) Templates — statische DE-Strings → translations/{de,en}/site.php + |t('site'); hreflang + x-default in seo.twig; Language-Switcher in header.twig (Fallback auf Site-Home statt 404); locale-aware Datum; dynamische Blog-URL/Feed-Sprache.

Verifikation (lokal, alle grün)

  • Routing: /, /en/, /blog, /en/blog, Artikel DE+EN → alle 200
  • hreflang: DE + EN + x-default auf beiden Sites
  • Switcher: verlinkt auf die jeweils andere Sprachvariante (Artikel-aware)
  • Strings: DE deutsch / EN englisch, 0 hardcoded Leftovers
  • Datum: DE „19. Mai 2026" / EN „May 19, 2026"
  • Felder pro Sprache getrennt editierbar (DB-bestätigt)

Hinweis zur Config-Diff

Einige config/project/-YAMLs (entryTypes, einzelne fields) wurden von Crafts project-config-Writer kosmetisch re-quotet (kein semantischer Change) — Craft-5.8.19-Serializer-Normalisierung.

Noch offen (kein Code, separater Schritt)

EN-Inhalte sind aktuell mit DE-Text geseedet. Redaktionelle Übersetzung der Globals/Singles/Unterseiten/Blog folgt im CP. Prod-Deploy braucht EN_SITE_URL in der prod-.env + Image-Rebuild für icu-data-full.

Closes #4

🤖 Generated with Claude Code

## Was Die ganze Seite ist jetzt zweisprachig (DE + EN) — natives Craft-5-Multi-Site, kein Plugin. Englisch lebt unter `/en/`, Deutsch bleibt auf Root. ## Schichten (4 Commits) 1. **`chore(docker)`** — `icu-data-full` in alle 3 Dockerfiles. Root-Cause: `php:8.3-fpm-alpine` bringt nur `icu-data-en`, dadurch rendert der Craft-Formatter jedes Datum englisch. Jetzt lokalisiert ICU korrekt. 2. **`feat(i18n)` EN-Site** — zweite Site `en` (`baseUrl: $EN_SITE_URL`) im bestehenden Site-Group, alle 4 Sections + Category/Tag-Groups für beide Sites aktiv. `EN_SITE_URL` in `.env` + allen getrackten env-Templates (from-scratch reproduzierbar). Das `/en`-Präfix kommt aus der baseUrl, nicht aus den URI-Formaten. 3. **`feat(i18n)` Felder** — `translationMethod: none→site` für die Prosa-Felder (`articleLead`, `articleTldr`, `articleBody`, `articleFootnotes`, `articleHeroCaption`, `articleStats`, `seo`). Referenz-Felder bleiben sprachneutral. DE-Werte seeden die EN-Variante. 4. **`feat(i18n)` Templates** — statische DE-Strings → `translations/{de,en}/site.php` + `|t('site')`; hreflang + x-default in `seo.twig`; Language-Switcher in `header.twig` (Fallback auf Site-Home statt 404); locale-aware Datum; dynamische Blog-URL/Feed-Sprache. ## Verifikation (lokal, alle grün) - Routing: `/`, `/en/`, `/blog`, `/en/blog`, Artikel DE+EN → alle **200** - hreflang: DE + EN + `x-default` auf beiden Sites - Switcher: verlinkt auf die jeweils andere Sprachvariante (Artikel-aware) - Strings: DE deutsch / EN englisch, **0** hardcoded Leftovers - Datum: DE „19. Mai 2026" / EN „May 19, 2026" - Felder pro Sprache getrennt editierbar (DB-bestätigt) ## Hinweis zur Config-Diff Einige `config/project/`-YAMLs (entryTypes, einzelne fields) wurden von Crafts project-config-Writer kosmetisch re-quotet (kein semantischer Change) — Craft-5.8.19-Serializer-Normalisierung. ## Noch offen (kein Code, separater Schritt) EN-**Inhalte** sind aktuell mit DE-Text geseedet. Redaktionelle Übersetzung der Globals/Singles/Unterseiten/Blog folgt im CP. Prod-Deploy braucht `EN_SITE_URL` in der prod-`.env` + Image-Rebuild für `icu-data-full`. Closes #4 🤖 Generated with [Claude Code](https://claude.com/claude-code)
php:8.3-fpm-alpine ships only icu-data-en, so Craft's formatter rendered
every date in English regardless of site locale. Adding icu-data-full
gives ICU the German (and all other) locale data, so de-DE renders
"19. Mai 2026" and en renders "May 19, 2026".

Refs #4
Add a second Craft site `en` (language en, baseUrl $EN_SITE_URL) in the
existing site group. English lives under the /en/ subdirectory; German
stays at root. All four sections plus the blog category/tag groups are
enabled for both sites with identical URI formats — the /en prefix comes
from the site baseUrl, not the URI formats.

EN_SITE_URL is wired into the local .env and all tracked env templates so
a from-scratch build reproduces the two-site setup.

Remaining config files were re-serialized by Craft's project-config
writer (cosmetic re-quoting only).

Refs #4
Switch translationMethod none -> site on the article prose fields and the
SEO field so each language holds its own copy: articleLead, articleTldr,
articleBody, articleFootnotes, articleHeroCaption, articleStats, seo.

Reference fields (author, dates, category, tags, hero asset) stay
language-neutral. Existing DE values seed the EN variant on first save.

Refs #4
Make the front-end fully bilingual:

- extract hardcoded German UI strings into translations/{de,en}/site.php
  and render them via |t('site') (404, footnotes, progress, marginalia,
  category nav, related, blog pagination + empty states)
- emit hreflang alternates + x-default for every URL-enabled site in
  _system/seo.twig
- add a language switcher (langSwitcher.twig) in the header that links to
  the current page in the other language, falling back to the site home
  when no localized variant exists
- format dates locale-aware via the Craft formatter (de "19. Mai 2026",
  en "May 19, 2026") in blog meta and card
- derive blog URLs and the feed language dynamically so the EN baseUrl
  and language resolve correctly

Closes #4
Phil merged commit 4bc882b6c3 into main 2026-05-31 06:10:31 +00:00
Phil deleted branch feat/multilingual-en 2026-05-31 06:10:32 +00:00
Sign in to join this conversation.
No reviewers
No labels
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
Phil/philflow.io!5
No description provided.