Dokumentacija dinamičnega usmerjanja z i18n
Ker Astro privzeto še ne podpira lokalizacije URL-jev, je bil potreben drugačen pristop za omogočitev večjezičnih strani. Ta projekt uporablja dinamične parametre ([...pages]) za implementacijo lokaliziranih poti.
🏗️ Pregled projektne arhitekture
Projekt implementira sistem internacionalizacije (i18n), ki omogoča:
- ✅ SEO-prijazne URL-je v več jezikih
- ✅ Avtomatsko statično generiranje ob času gradnje
- ✅ Jezikovno specifično nalaganje vsebine
- ✅ Dosledno integracijo s sistemom prevodov
- ✅ Pametno preklapljanje jezikov z ohranjanjem konteksta
📁 Struktura datotek
src/
├── components/ # Komponente (Header, Footer, LanguagePicker)
├── content/ # Vsebina (blogi, avtorji)
│ ├── blog/
│ │ ├── en/ # Angleške objave
│ │ └── sl/ # Slovenske objave
│ └── authors/ # Podatki o avtorjih
├── data/ # Navigacijski podatki
│ └── navigationData.ts
├── i18n/ # Sistem internacionalizacije
│ ├── routes.ts # Prevodi poti
│ ├── ui.ts # Konfiguracija jezikov
│ └── utils.ts # Pomožne funkcije
├── layouts/ # Osnovni izgled
├── locales/ # Prevodne datoteke
│ ├── en/ # Angleški prevodi
│ └── sl/ # Slovenski prevodi
├── pages/ # Strani z dinamičnim usmerjanjem
│ ├── [about]/ # O nas strani
│ │ ├── [...index].astro
│ │ ├── _about-en.mdx
│ │ └── _about-sl.mdx
│ ├── [blog]/ # Podrobne blog strani
│ │ └── [...slug].astro
│ ├── [dyn_routing]/ # Primeri dinamičnega usmerjanja
│ │ ├── [subpage2]/
│ │ │ └── [...index].astro
│ │ └── [...subpage1].astro
│ ├── [pages]/ # Splošne strani
│ │ ├── [...index].astro
│ │ ├── _pages-en.mdx
│ │ └── _pages-sl.mdx
│ ├── [pagination]/ # Primeri paginacije
│ │ └── [...page].astro
│ ├── [...blog].astro # Seznam blog objav
│ ├── [...contact].astro # Kontakt stran
│ ├── [...index].astro # Domača stran
│ └── 404.astro # Napaka stran
└── styles/ # Stilske datoteke
🔀 Kako deluje dinamično usmerjanje
1. Struktura URL-jev
Primeri predpostavljajo
defaultLang = "en"inshowDefaultLang = false. URL-ji se samodejno prilagodijo, ko te nastavitve spremeniš.
| Vzorec strani | Angleški URL | Slovenski URL | Opis |
|---|---|---|---|
[...index].astro | / | /sl/ | Domača stran |
[about]/[...index].astro | /about | /sl/o-projektu | O nas |
[...contact].astro | /contact | /sl/kontakt | Kontakt |
[blog]/[...slug].astro | /blog/post | /sl/spletni-dnevnik/objava | Blog objave |
2. Vizualen prikaz usmerjanja
Datoteka: [about]/[...index].astro
│
├── Angleška pot
│ ├── URL: /about
│ ├── Parametri: { about: "about", index: undefined }
│ ├── Props: { lang: "en" }
│ └── Vsebina: _about-en.md
│
└── Slovenska pot
├── URL: /sl/o-projektu
├── Parametri: { about: "sl", index: "o-projektu" }
├── Props: { lang: "sl" }
└── Vsebina: _about-sl.md
3. Strukture strani
Obstajata dva glavna vzorca za implementacijo dinamičnih strani:
Vzorec A: [...pages].astro
import { buildLocalizedStaticPaths } from "@i18n/utils";
// Enotna datoteka: [...pages].astro
export function getStaticPaths() {
return buildLocalizedStaticPaths("/pages", ["...pages"]);
}
Vzorec B: [pages]/[...index].astro
import { buildLocalizedStaticPaths } from "@i18n/utils";
// Vzorec mape: [pages]/[...index].astro
export function getStaticPaths() {
return buildLocalizedStaticPaths("/pages", ["pages", "...index"]);
}
⚙️ Ključne komponente sistema
1. src/i18n/routes.ts - Prevodi poti
Ta datoteka definira preslikave med angleškimi in lokaliziranimi potmi:
export const routes: Record<string, Record<string, string>> = {
sl: {
about: "o-projektu",
blog: "spletni-dnevnik",
services: "storitve",
pages: "strani",
contact: "stik",
},
};
Pomembno: Ključi se morajo ujemati s parametri v
getStaticPaths()funkcijah!
2. src/i18n/ui.ts - Konfiguracija jezikov
Ta datoteka vsebuje ključne konfiguracije za jezikovno podporo:
languages- definira podprte jezike in njihove oznake; tukaj dodaš nove jezikedefaultLang- določa privzeti jezik aplikacije; sprememba premakne koren (/) na izbrani jezikshowDefaultLang- določa, ali se privzeti jezik prikaže v URL-jih (/en/...namesto/)
Obe nastavitvi sta podprti na straneh, blogu in paginaciji.
export const languages = {
en: "English",
sl: "Slovenian",
};
export const defaultLang = "en"; // npr. "sl" za slovenski koren
export const showDefaultLang = false; // true → vedno s predpono: /en/..., /sl/...
Kako preklopiti privzeti jezik
- Odpri
src/i18n/ui.tsin nastavi:defaultLang = "sl"(ali drug jezik)showDefaultLang = true | falseglede na to, ali želiš predpono tudi za privzeti jezik
- Koren postane:
/, ko jeshowDefaultLang = false/{defaultLang}/, ko jeshowDefaultLang = true
- Ostalih sprememb ni treba: strani uporabljajo
useTranslatedPath()vgetStaticPaths()in se poti prilagodijo samodejno. - Preveri home, about, blog (seznam/objava) in paginacijo ter preklop jezika.
3. src/i18n/utils.ts - Pomožne funkcije
getLangFromUrl(url: URL)
Pridobiva trenutni jezik iz URL-ja:
const lang = getLangFromUrl(Astro.url); // "sl" ali "en"
useTranslations(lang: string)
Vrača funkcijo za prevajanje besedil:
const t = useTranslations(lang);
const title = t("main:head.title");
const navHome = t("menu.list.home"); // Uporablja "common" imenski prostor
Podprt format ključev:
"namespace:key"→t("main:title")- Neposredni ključi →
t("menu.home")(privzeto “common” imenski prostor) - Parametri →
t("footer.made", { what: "Astro" })
useTranslatedPath(lang: string)
Vrača funkcijo za prevajanje poti:
const translatePath = useTranslatedPath(lang);
<a href={translatePath("/about")}>About</a>; // "/o-projektu" za slovenščino
switchLanguageUrl(currentUrl: URL, targetLang: string)
Omogoča preklapljanje jezikov ob ohranjanju konteksta trenutne strani:
const newUrl = await switchLanguageUrl(Astro.url, "sl");
🧩 Helper: buildLocalizedStaticPaths()
Ustvari lokalizirane vnose za getStaticPaths() iz angleške osnovne poti in enostavnega vzorca parametrov. Za vsak jezik prevede osnovno pot, jo razdeli na segmente in jih preslika v Astro parametre.
Podpis:
function buildLocalizedStaticPaths(
basePath: string,
pattern: string[],
extraProps?: (lang: string) => Record<string, any>
): Array<{ params: Record<string, string | undefined>; props: Record<string, any> }>
Pravila vzorca:
- Fiksna imena: “about”, “dyn_routing”, “subpage2”
- Zaključno “catch‑all”: predpona
...in vedno zadnji (npr. “…index”, “…subpage1”, “…blog”) - Manjkajoči segmenti postanejo
undefined(kar Astro pričakuje) - Upošteva
defaultLanginshowDefaultLang
Primeri:
// O nas mapa: [about]/[...index].astro
export function getStaticPaths() {
return buildLocalizedStaticPaths("/about", ["about", "...index"]);
}
// Dinamično usmerjanje root: [dyn_routing]/[...index].astro
export function getStaticPaths() {
return buildLocalizedStaticPaths("/dynamic-routing", ["dyn_routing", "...index"]);
}
// Dinamično usmerjanje podstran 1: [dyn_routing]/[...subpage1].astro
export function getStaticPaths() {
return buildLocalizedStaticPaths("/dynamic-routing/subpage-1", ["dyn_routing", "...subpage1"]);
}
// Dinamično usmerjanje podstran 2 (gnezdeno): [dyn_routing]/[subpage2]/[...index].astro
export function getStaticPaths() {
return buildLocalizedStaticPaths("/dynamic-routing/subpage-2", ["dyn_routing", "subpage2", "...index"]);
}
// Blog seznam: [...blog].astro
export function getStaticPaths() {
return buildLocalizedStaticPaths("/blog", ["...blog"]);
}
// Opcijsko: dodatni props per jezik
export function getStaticPaths() {
return buildLocalizedStaticPaths("/services", ["services", "...index"], (lang) => ({ section: "services", lang }));
}
Opomba (paginacija): paginacijska pot uporabi ta helper za lokalizacijo osnovnega segmenta (npr. blog-pagination → spletni-dnevnik-paginacija), nato pa preko paginate() razširi strani in ohrani i18n obliko URL‑jev.
4. src/data/navigationData.ts - Navigacija
const navigationData = [
{
label: "menu.list.home", // Ključ za prevod
href: "/", // Angleška pot (privzeta)
children: [],
},
{
label: "menu.list.service",
href: "/services",
children: [
{
label: "menu.list.subpage-1",
href: "/services/service-1",
},
],
},
];
Opomba: URL-ji v navigaciji so vedno v angleščini, ker se avtomatsko lokalizirajo preko
translatePath().
🗣️ Sistem prevodov
1. Struktura prevodnih datotek
src/locales/
├── en/
│ ├── common.json # Skupni prevodi (navigacija, footer)
│ ├── main.json # Glavna stran
│ ├── about.json # O nas stran
│ └── blog.json # Blog stran
└── sl/
├── common.json
├── main.json
├── about.json
└── blog.json
2. Primer prevodne datoteke (common.json)
{
"menu": {
"list": {
"home": "Domov",
"about": "O nas",
"blog": "Blog"
}
},
"footer": {
"made": "Narejeno z {{what}}"
}
}
Opomba: Za datoteke
common.jsonne rabiš predponecommon:. Uporabi direktnot("menu.list.home"), net("common:menu.list.home").
3. Uporaba prevodov v komponentah
---
import { useTranslations } from "@i18n/utils";
const t = useTranslations(lang);
---
<h1>{t("main:hero.title")}</h1>
<p>{t("menu.list.home")}</p>
<footer>{t("footer.made", { what: "Astro" })}</footer>
📝 Blog sistem z linkedContent
1. Povezovanje vsebin med jeziki
Blog sistem omogoča povezovanje objav med različnimi jeziki. Ključno vprašanje je: kako sistem ve, da je uporabnik še vedno na isti objavi, ko preklopi jezik?
🔗 Rešitev: Vsaka blog objava mora imeti linkedContent identifikator v frontmatter-ju. Ta identifikator povezuje objave v različnih jezikih, ki obravnavajo isto temo. Ko uporabnik preklopi jezik, sistem uporabi ta identifikator za iskanje ustrezne objave v ciljnem jeziku.
Angleška objava (en/security-trends.md):
---
title: "Top Security Trends for 2025"
linkedContent: "security-trends-2025"
author: "Nik Klemenc"
---
Slovenska objava (sl/varnostni-trendi-2025.md):
---
title: "Glavni varnostni trendi za leto 2025"
linkedContent: "security-trends-2025" # Isti identifikator!
author: "Nik Klemenc"
---
2. Avtorji z večjezičnimi podatki
Sistem omogoča povezovanje avtorjev z blog objavami. V tem primeru je uporabljen JSON format, kjer ima vsak avtor večjezične podatke. Pod ključem “position” so definirane pozicije v angleščini in slovenščini, ki se nato dinamično prikazujejo glede na izbrani jezik.
{
"nik-klemenc": {
"name": "Nik Klemenc",
"image": "./nik.jpg",
"position": {
"en": "Full-stack Developer",
"sl": "Full-stack razvijalec"
}
}
}
📄 Paginacija
Projekt vključuje primer paginacije v [pagination]/[...page].astro, implementiran z Astro-jevo privzeto funkcijo paginate() in prilagojen za lokalizirane URL-je.
// Paginacija z Astro funkcijo paginate()
export const getStaticPaths = async ({ paginate }) => {
const posts = /* pridobi in uredi objave po jeziku */ [];
return paginate(posts, {
pageSize: 4,
params: { pagination: "blog-pagination" },
props: {
/* lang, authors, totals */
},
});
};
- URL-ji: EN
/blog-pagination,/blog-pagination/2; SL/sl/spletni-dnevnik-paginacija,/sl/spletni-dnevnik-paginacija/2. - Predloga uporablja
page.data,page.currentPageinpage.lastPage.
💻 Primeri uporabe
1. Osnovna stran z dinamičnim usmerjanjem
---
import { useTranslations, buildLocalizedStaticPaths } from "@i18n/utils";
export function getStaticPaths() {
return buildLocalizedStaticPaths("/services", ["services", "...index"]);
}
const { lang } = Astro.props;
const t = useTranslations(lang);
---
<Base title={t("services:head.title")}>
<h1>{t("services:title")}</h1>
</Base>
2. Preklapljanje jezikov
---
// LanguagePicker.astro - dejanska implementacija
import { switchLanguageUrl, getLangFromUrl, useTranslations } from "@i18n/utils";
import { languages } from "@i18n/ui";
// Pridobi trenutni jezik
const currentLang = getLangFromUrl(Astro.url);
const t = useTranslations(currentLang);
// Pripravi URL-je za vse jezike
const languageUrls = await Promise.all(
Object.entries(languages).map(async ([lang, label]) => {
const targetUrl = await switchLanguageUrl(Astro.url, lang);
const translatedLabel = t(`menu.languages.${lang}`);
return { lang, label: translatedLabel, targetUrl };
})
);
---
<!-- Dropdown selektor -->
<select
name="language"
onchange="window.location.href = this.value"
aria-label={t("menu.languagesText.selectLanguage")}
>
{languageUrls.map(({ lang, label, targetUrl }) => (
<option
value={targetUrl}
selected={lang === currentLang}
>
{label}
</option>
))}
</select>
3. Lokalizirane povezave
---
import { getLangFromUrl, useTranslatedPath } from "@i18n/utils";
const lang = getLangFromUrl(Astro.url);
const translatePath = useTranslatedPath(lang);
---
<a href={translatePath("/about")}>
{t("menu.list.about")}
</a>
✨ Najboljše prakse
1. Poimenovanje datotek
- Uporabljajte angleška imena za datoteke v
pages/mapi - Lokalizirajte samo URL-je preko
routes.ts - Primeri:
[about]mapa, ne[o-projektu]
2. Prevodni ključi
- Uporabljajte hierarhično strukturo (
menu.list.home) - Ločite po imenskih prostorih (
main:title,about:description) - Dodajte kontekst v imena ključev
3. Povezovanje vsebin
- Vedno dodajte
linkedContentidentifikator v blog objave - Uporabljajte dosledno poimenovanje med jeziki
- Testirajte preklapljanje jezikov na vseh straneh
4. SEO optimizacija
- Dodajte
titleindescriptionmeta podatke - Uporabljajte
hreflangatribute za večjezične strani - Implementirajte strukturirane podatke
❓ Pogosta vprašanja
Zakaj ne uporabite privzetega Astro i18n?
Astro nima vseh funkcionalnosti za popolnoma lokalizirane segmente URL-jev s povezovanjem vsebine (linked slugi) in natančnim vedenjem predpone (defaultLang/showDefaultLang). Ta projekt uporablja tanek i18n sloj (routes.ts, useTranslatedPath, switchLanguageUrl), ki zagotavlja:
- Prevedene segmente poti (npr.
/about→/o-projektu) - Neobvezno predpono za privzeti jezik
- Preklop jezika z ohranjanjem konteksta, vključno z blog slugi prek
linkedContent
Kako dodam nov jezik?
- Dodaj jezik v
src/i18n/ui.ts(languages). - Dodaj prevodne datoteke v
src/locales/[lang]/.... - Dodaj preslikave poti v
src/i18n/routes.tsza segmente, ki jih lokaliziraš.
Ni treba spreminjati datotek strani — vse getStaticPaths() uporabljajo useTranslatedPath() in samodejno prevzamejo nov jezik.
Kako deluje preklop jezika za blog objave?
Vsaka objava ima linkedContent identifikator. switchLanguageUrl() z njim poišče ustrezno objavo v ciljnem jeziku, prevede osnovno pot (blog ↔ spletni-dnevnik) in uporabi pravilno predpono glede na showDefaultLang.
Ali lahko uporabljam relativne poti?
Uporabi absolutne poti z začetno poševnico (/about, ne about). Prevajanje poti se opira na dosledne absolutne segmente.
🎯 Ta pristop omogoča popolnoma lokalizirane URL-je, ohranja zmogljivosti statičnega generiranja in zagotavlja SEO prijazne poti.