Doménu peterbabic.sk som mal nevyužitú roky. Medzitým bol tento blog len v angličtine. Nedávno som si povedal, prečo nevyužiť obe? Slovenská verzia by mohla pomôcť s lokálnym SEO a sprístupniť môj obsah ľuďom, ktorí radšej čítajú po slovensky.

Výzva bola urobiť to bez duplikácie celého kódu.

Cieľ #

Chcel som:

  1. Jeden repozitár, jeden kód
  2. Dve nasadenia Cloudflare Pages (.com a .sk)
  3. Rovnaké URL, len iný jazyk obsahu
  4. Zdieľané layouty, komponenty, štýly
  5. Strojovo preložený obsah s upozornením

Posledný bod je dôležitý. Nebudem ručne prekladať 250+ príspevkov. Ale chcem, aby čitatelia vedeli, že je to strojový preklad a mali odkaz na originál.

Štruktúra obsahu #

Môj prvý inštinkt boli samostatné adresáre content/en/ a content/sk/. Ale potom by sa slugy mohli rozísť. Preklep v názve slovenského priečinka? Teraz máte nezhodu URL.

Namiesto toho som dal oba jazykové súbory do rovnakého priečinka:

src/content/blog/
├── some-post/
│   ├── en.md
│   └── sk.md
├── another-post/
│   ├── en.md
│   └── sk.md

Názov priečinka JE slug. Nedá sa to pokaziť.

Konfigurácia obsahu zachytí oba s glob vzorom:

loader: glob({ pattern: "**/{en,sk}.md", base: "./src/content/blog" })

Výber jazyka #

Jazyk sa vyberá počas buildu cez premennú prostredia:

// astro.config.mjs
const locale = process.env.LOCALE || "en"
const site =
  locale === "sk" ? "https://peterbabic.sk" : "https://peterbabic.com"

Potom pomocná funkcia filtruje príspevky podľa jazyka:

export function getLocale(): Locale {
  const locale = import.meta.env.LOCALE
  if (locale === "sk") return "sk"
  return "en"
}

Pri načítaní príspevkov ich zoskupím podľa priečinka slugu a vyberiem správny jazykový súbor. Ak slovenčina neexistuje, vráti sa k angličtine a vypíše varovanie počas buildu.

UI reťazce #

Napevno zadaný text ako “Publikované”, “O mne”, “Domov” tiež potreboval preklad. Jednoduchý objekt s oboma jazykmi:

const ui = {
  en: {
    "nav.home": "Home",
    "post.published": "Published",
  },
  sk: {
    "nav.home": "Domov",
    "post.published": "Publikované",
  },
}

export function t(key: keyof typeof ui.en): string {
  return ui[getLocale()][key]
}

Komponenty používajú t("nav.home") namiesto napevno zadaných reťazcov.

Upozornenie na preklad #

Keďže slovenský obsah je strojovo preložený, pridal som banner s odkazom na pôvodnú anglickú verziu. Sleduje rovnaký vzor feature flag ako používam pre komentáre a newsletter:

const featureEnabled =
  runtimeEnv?.FEATURE_TRANSLATION_NOTICE === "true" ||
  import.meta.env.FEATURE_TRANSLATION_NOTICE === "true"
const showNotice = featureEnabled && locale === "sk"

Upozornenie sa zobrazí len na slovenskej stránke, keď je flag zapnutý.

Nastavenie Cloudflare Pages #

Obe nasadenia smerujú na ten istý repozitár s rôznymi premennými prostredia, ale peterbabic.com nepotrebuje žiadne, keďže používa predvolené nastavenie a premenné majú fallbacky nastavené v kóde. Pre peterbabic.sk mám dve premenné prostredia:

  • LOCALE=sk
  • FEATURE_TRANSLATION_NOTICE=true

Toto zabezpečí, že obe nasadenia zobrazujú odlišný, ale správny výstup.

Samotný preklad #

Použil som Clauda na preklad všetkých 250 príspevkov v paralelných dávkach. Preklady zachovávajú štruktúru frontmatteru, nechávajú bloky kódu nedotknuté a udržiavajú rovnaké URL. Tagy zostávajú v angličtine, keďže sa používajú na filtrovanie.

Celý preklad trval asi hodinu reálneho času s paralelnými agentmi.

Čo sa mi na tomto prístupe páči #

  • Žiadna duplikácia kódu - opravím bug raz, obe stránky ho dostanú
  • Atomické nasadenia - pushnem do master, obe stránky sa aktualizujú
  • Nemožné mať nezhodu slugov - názov priečinka je zdroj pravdy
  • Elegantná degradácia - chýbajúci preklad sa vráti k angličtine

Čo by mohlo byť lepšie #

Nerobím žiadnu automatickú detekciu jazyka ani prepínanie. Ak niekto pristane na zlej doméne, musí manuálne prejsť na druhú. Zatiaľ je to v poriadku. Možno neskôr pridám prepínač jazyka do hlavičky.

Tiež sitemap obsahuje len stránky, ktoré existujú v danom jazyku. Nie som si istý, či je to ideálne pre SEO, ale zdá sa to správne.

Záver #

Ak máte nevyužitú doménu vo svojom rodnom jazyku, tento prístup by vám mohol fungovať. Jeden kód, výber jazyka počas buildu, žiadna runtime réžia. Premenná prostredia LOCALE je jediný rozdiel medzi dvoma nasadeniami.

Enjoy!

Odkazy #