[Sugester V2](https://pomoc.sugester2.pl.md) / [CMS](https://pomoc.sugester2.pl/cms-1.md)

# [Strony wielojęzyczne w CMS](https://pomoc.sugester2.pl/strony-wielojezyczne-w-cms-MAycsTRB.md)

Jak tworzyć wielojęzyczne strony w CMS z automatycznym przełączaniem języka i poprawnym SEO.

## Architektura

Wielojęzyczność opiera się na trzech elementach:
1. **Master page** (PL) z locale-keyed fields
2. **Slave pages** (EN, ES, FR...) — puste, dziedziczą content z mastera
3. **Domeny** z ustawionym locale (np. mojafirma.pl=pl, mojafirma.com=en)

```
Site: mojafirma-marketing (multilang: true)
├── Master: path="cennik", locale=pl
│   fields: { "pl": {"hero": "Cennik"}, "en": {"hero": "Pricing"} }
│   content: <h1>{{ hero }}</h1>...
├── Slave EN: path="pricing", locale=en, based_on=master
│   content: "" (pusty — dziedziczy z mastera)
└── Slave FR: path="tarifs", locale=fr, based_on=master
    content: "" (pusty)
```

## Locale-keyed fields

Wszystkie teksty na masterze PL w strukturze per język:

```json
{
  "pl": {
    "hero_title": "Cennik",
    "hero_lead": "Wybierz plan dla siebie"
  },
  "en": {
    "hero_title": "Pricing",
    "hero_lead": "Choose your plan"
  }
}
```

CMS automatycznie dobiera odpowiedni język na podstawie domeny.

## Slave pages — zasady

Slave to strona per język z trzema polami:
- **path** — ścieżka w docelowym języku (np. `pricing`, `tarifs`)
- **locale** — kod języka (`en`, `fr`, `es`)
- **based_on_page_id** — ID mastera PL

Content i fields **pozostają puste** — CMS dziedziczy je z mastera.

### Path bez prefixu locale

Slave ma path w docelowym języku, **bez prefixu**:
- `pricing` (nie `en/pricing`)
- `tarifs` (nie `fr/tarifs`)

**Wyjątek:** gdy path koliduje z masterem PL (ten sam tekst w obu językach):
- Master PL: `crm` → Slave EN: `en/crm` (bo `crm` = `crm`)
- Master PL: `blog` → Slave EN: `blog-en`

## Homepage

Homepage (path="") to **jeden master** z locale-keyed fields, bez slave'ów.

Strony `/en`, `/fr` to puste **redirect pages** (redirect_to: "/") — fallback gdy ktoś wejdzie ręcznie.

## Layout

Layout używa `{{ html_lang }}` (nie `{{ locale }}`) do conditionals:

```html
{% if html_lang == "en" %}
  <a href="/pricing">Pricing</a>
{% else %}
  <a href="/cennik">Cennik</a>
{% endif %}
```

`html_lang` jest ustawiany automatycznie z locale domeny.

Zmienne layoutowe dostępne przez `{{ layout.nazwa }}` (nie `{{ nazwa }}`).

## Blog wielojęzyczny

Tag `<cms type="article">` **nie filtruje po locale** — pokazuje wszystkie artykuły z danej kategorii.

Rozwiązanie: osobny `category_code` per język + Liquid conditional:

```html
{% if html_lang == "en" %}
<cms type="article" category_code="blog-en" per_page="12" site="current">
  <list><!-- karty artykułów --></list>
  <show><!-- pełny artykuł --></show>
</cms>
{% else %}
<cms type="article" category_code="blog-pl" per_page="12" site="current">
  <list><!-- karty artykułów --></list>
  <show><!-- pełny artykuł --></show>
</cms>
{% endif %}
```

**Ważne:** każdy branch musi mieć **kompletny** blok `<cms>...<list>...<show>...</cms>`. Nie wolno dzielić otwierającego i zamykającego tagu między branche — CMS zwróci błąd 500.

## SEO

- **Canonical:** `<link rel="canonical" href="{{ url }}">` w layoucie
- **Hreflang:** `{{ seo_head }}` w layoucie generuje hreflang automatycznie (wymaga multilang: true na site)
- **Schema.org:** JSON-LD Organization + SoftwareApplication w layoucie

## Domeny

Każda domena ma przypisany locale:

| Domena | locale |
|--------|--------|
| mojafirma.pl | pl |
| mojafirma.com | en |
| mojafirma.es | es |

Ustawienie: **Konta → Domeny** (Account::Domain z subject_type=Cms::Site).

## Locale codes

- Czech: **cs** (nie cz)
- Ukrainian: **uk** (nie ua)
- Pozostałe: pl, en, fr, sk, de, es

## Checklist

- [ ] Master PL z locale-keyed fields (pl, en, es...)
- [ ] Content z Liquid variables — bez hardkodowanych tekstów
- [ ] Slave per język: path + locale + based_on_page_id, content i fields puste
- [ ] Path slave'a bez prefixu (chyba że koliduje z masterem)
- [ ] Artykuły blogowe z category_code per język
- [ ] Layout z html_lang conditionals
- [ ] multilang: true na site
- [ ] Domeny z locale