Lokalisoinnin mukaiset metatiedot, sivustokartat ja hreflang
Tuottakaa kielialuekohtaiset otsikot, kuvaukset ja hreflang-vaihtoehdot kansainvälisen hakukoneoptimoinnin varmistamiseksi.
Lokalisoinnin mukaiset metatiedot, sivustokartat ja hreflang on ilmainen Next.js 15 -fullstack-kehitys (App Router + Server Actions)-oppitunti CoddyKitissä. Tämä on oppitunti 3/4. Voit lukea tästä oppimispolusta kokonaan mitkä tahansa 3 oppituntia ilmaiseksi — sen jälkeen CoddyKit PRO avaa kaikki oppitunnit sekä käytännön harjoittelun sisäänrakennetulla koodieditorilla ja ympäri vuorokauden toimivalla tekoälytuutorilla. Oppitunti kuuluu Next.js 15 -fullstack-kehitys (App Router + Server Actions)-oppimispolkuun, ja edistymisesi synkronoituu verkon ja CoddyKit-sovelluksen välillä. Next.js 15 -fullstack-kehitys (App Router + Server Actions)-kurssilla on yhteensä 4 oppituntia.
Miksi lokalisoiduilla metatiedoilla on merkitystä
Kun Next.js 15 -sovelluksenne palvelee useita kieliä, hakukoneet tarvitsevat kaksi asiaa sijoittaakseen sivustonne oikein: kieliversiokohtaiset metatiedot (oikeankieliset otsikot ja kuvaukset) sekä hreflang-linkit, jotka kertovat Googlelle, mikä URL-osoite palvelee mitäkin kieltä tai aluetta.
Ilman näitä Google saattaa indeksoida vain oletuskieliversion, näyttää hakutuloksissa väärän kielen tai rangaista päällekkäisestä sisällöstä osoitteissa /en/about, /fr/about ja /de/about.
- Lokalisoitu otsikko ja kuvaus — parantavat napsautussuhdetta kullakin markkina-alueella
- hreflang-vaihtoehdot — estävät päällekkäisen sisällön aiheuttamat rangaistukset ja ohjaavat käyttäjät heidän kieliversioonsa
- Canonical-URL-osoite — vahvistaa kunkin kieliversiosivun ensisijaisen URL-osoitteen
Next.js 15:n App Router tarjoaa tähän tarkoitukseen ensiluokkaisen generateMetadata-rajapinnan, joka tekee kaikesta tästä suoraviivaista.
Projektin määritys: kieliversioasetukset ja sanakirjatyypit
Ennen metatietojen luomista määritä tuetut kieliversiot ja tyypitetyn sanakirjan rakenne. Keskitetty i18n.ts-määritystiedosto pitää metatiedot, reitityksen ja sivustokartat yhdenmukaisina.
Tässä oppitunnissa käytetyt käytännöt:
locales— BCP 47 -tunnisteiden taulukko (en,fr,de)defaultLocale— oletuskieliversiogetDictionary— asynkroninen lataaja, joka palauttaa tyypitetyt merkkijonot kieliversiokohtaisesti
// lib/i18n.ts
export const locales = ['en', 'fr', 'de'] as const;
export type Locale = (typeof locales)[number];
export const defaultLocale: Locale = 'en';
export function isValidLocale(value: string): value is Locale {
return (locales as readonly string[]).includes(value);
}
// Typed shape for SEO-related strings
export interface SeoStrings {
title: string;
description: string;
}
export interface Dictionary {
home: SeoStrings;
about: SeoStrings;
}
Sanakirjalataajan rakentaminen
Sanakirjalataaja tuo kunkin kieliversion JSON-tiedoston dynaamisesti. Dynaamisen import()-lausekkeen käyttö pitää bundlen koon pienenä — kunkin kieliversion merkkijonot ladataan vain tarvittaessa.
Tallenna sanakirjat projektin juuren messages/-hakemistoon. Lataajaa kutsutaan generateMetadata-funktion sisällä ja palvelinkomponenteissa, joten sen on oltava asynkroninen.
// lib/getDictionary.ts
import type { Locale, Dictionary } from './i18n';
// Next.js caches the result of dynamic imports automatically in the
// App Router, so repeated calls in the same request are free.
const dictionaries: Record<string, () => Promise<Dictionary>> = {
en: () => import('../messages/en.json').then((m) => m.default as Dictionary),
fr: () => import('../messages/fr.json').then((m) => m.default as Dictionary),
de: () => import('../messages/de.json').then((m) => m.default as Dictionary),
};
export async function getDictionary(locale: Locale): Promise<Dictionary> {
const loader = dictionaries[locale];
if (!loader) throw new Error(`No dictionary for locale: ${locale}`);
return loader();
}
Esimerkkiviestitiedostot
Jokainen kieliversio tarvitsee SEO-merkkijonot sisältävän JSON-tiedoston. Nämä ovat raakoja merkkijonoja, jotka generateMetadata käyttää. Pidä ne lyhyinä ja avainsanoja sisältävinä — ne näkyvät suoraan <title>- ja <meta name="description">-tageissa.
// messages/en.json
{
"home": {
"title": "CoddyKit — Learn to Code",
"description": "Master programming with interactive lessons on CoddyKit."
},
"about": {
"title": "About CoddyKit",
"description": "Learn who we are and our mission to make coding accessible."
}
}
// messages/fr.json
{
"home": {
"title": "CoddyKit — Apprendre la Programmation",
"description": "Maîtrisez la programmation avec des leçons interactives sur CoddyKit."
},
"about": {
"title": "À propos de CoddyKit",
"description": "Découvrez qui nous sommes et notre mission pour rendre le code accessible."
}
}
generateMetadata kieliversiokohtaisella otsikolla ja kuvauksella
generateMetadata on asynkroninen funktio, joka viedään ulos page.tsx- tai layout.tsx-tiedostosta. Next.js 15 kutsuu sitä pyyntöhetkellä (tai koontiversion luonnin aikana staattisilla reiteillä) ja lisää tuloksen <head>-elementtiin.
params-argumentti antaa dynaamisen segmentin — [locale]-reitissä params.locale sisältää BCP 47 -tunnisteen. Lataa oikea sanakirja ja palauta sitten Metadata-objekti.
// app/[locale]/page.tsx
import type { Metadata } from 'next';
import { getDictionary } from '@/lib/getDictionary';
import { isValidLocale, defaultLocale, type Locale } from '@/lib/i18n';
type Props = {
params: Promise<{ locale: string }>;
};
export async function generateMetadata({ params }: Props): Promise<Metadata> {
const { locale: rawLocale } = await params;
const locale: Locale = isValidLocale(rawLocale) ? rawLocale : defaultLocale;
const dict = await getDictionary(locale);
return {
title: dict.home.title,
description: dict.home.description,
};
}
export default async function HomePage({ params }: Props) {
const { locale: rawLocale } = await params;
const locale: Locale = isValidLocale(rawLocale) ? rawLocale : defaultLocale;
const dict = await getDictionary(locale);
return <h1>{dict.home.title}</h1>;
}
hreflang-vaihtoehtojen lisääminen metatietoihin
Metadata-objektin alternates-avain vastaa suoraan <link rel="alternate" hreflang="...">-tageja. Teidän on sisällytettävä:
- yksi merkintä kutakin kieliversiota kohti täydellisenä absoluuttisena URL-osoitteena
x-default-merkintä, joka osoittaa oletuskieliversion URL-osoitteeseen (tai kielenvalintasivulle)
Next.js muodostaa näistä automaattisesti <link>-tagit <head>-elementin sisään — mallipohjia ei tarvitse muokata käsin.
// app/[locale]/page.tsx (generateMetadata, extended)
import type { Metadata } from 'next';
import { getDictionary } from '@/lib/getDictionary';
import { locales, isValidLocale, defaultLocale, type Locale } from '@/lib/i18n';
const BASE_URL = process.env.NEXT_PUBLIC_BASE_URL ?? 'https://www.coddykit.com';
type Props = { params: Promise<{ locale: string }> };
export async function generateMetadata({ params }: Props): Promise<Metadata> {
const { locale: rawLocale } = await params;
const locale: Locale = isValidLocale(rawLocale) ? rawLocale : defaultLocale;
const dict = await getDictionary(locale);
// Build one alternate entry per supported locale
const languages: Record<string, string> = {};
for (const loc of locales) {
languages[loc] = `${BASE_URL}/${loc}`;
}
languages['x-default'] = `${BASE_URL}/${defaultLocale}`;
return {
title: dict.home.title,
description: dict.home.description,
alternates: {
canonical: `${BASE_URL}/${locale}`,
languages,
},
};
}
Vaihtoehtojen muodostuslogiikan jakaminen sivujen kesken
Vaihtoehtojen muodostuslogiikan toistaminen jokaisessa generateMetadata-funktiossa altistaa virheille. Irrottakaa se pieneksi apuohjelmaksi, joka saa nykyisen polkusegmentin ja palauttaa alternates-objektin.
Tämä apuohjelma voi sijaita tiedostossa lib/metadata.ts, josta sen voi tuoda käyttöön missä tahansa sovelluksen osassa.
// lib/metadata.ts
import { locales, defaultLocale, type Locale } from './i18n';
const BASE_URL = process.env.NEXT_PUBLIC_BASE_URL ?? 'https://www.coddykit.com';
/**
* Builds the `alternates` block for Next.js Metadata.
* @param path - path after the locale segment, e.g. '' for home, '/about' for about
*/
export function buildAlternates(path = '') {
const languages: Record<string, string> = {};
for (const locale of locales) {
languages[locale] = `${BASE_URL}/${locale}${path}`;
}
languages['x-default'] = `${BASE_URL}/${defaultLocale}${path}`;
return { languages };
}
// Usage in app/[locale]/about/page.tsx:
// alternates: {
// canonical: `${BASE_URL}/${locale}/about`,
// ...buildAlternates('/about'),
// }
Juuriasettelu: oletusmetatiedot ja OpenGraph-kieliversio
app/[locale]/-hakemiston juuren layout.tsx on oikea paikka oletusmetatiedoille, jotka alisivut voivat korvata. Siellä asetetaan myös <html lang> -attribuutti ja OpenGraphin locale-kenttä.
openGraph.localekäyttää alaviivalla eroteltuja BCP 47 -tunnisteita (esimerkiksifr_FR)openGraph.alternateLocalesisältää muut tuetut kieliversiot
// app/[locale]/layout.tsx
import type { Metadata } from 'next';
import { locales, isValidLocale, defaultLocale, type Locale } from '@/lib/i18n';
import { buildAlternates } from '@/lib/metadata';
const BASE_URL = process.env.NEXT_PUBLIC_BASE_URL ?? 'https://www.coddykit.com';
type Props = {
children: React.ReactNode;
params: Promise<{ locale: string }>;
};
export async function generateMetadata({ params }: Props): Promise<Metadata> {
const { locale: rawLocale } = await params;
const locale: Locale = isValidLocale(rawLocale) ? rawLocale : defaultLocale;
const ogLocale = locale.replace('-', '_'); // 'fr' → 'fr', 'pt-BR' → 'pt_BR'
const alternateLocales = locales.filter((l) => l !== locale).map((l) => l.replace('-', '_'));
return {
metadataBase: new URL(BASE_URL),
alternates: {
canonical: `${BASE_URL}/${locale}`,
...buildAlternates(),
},
openGraph: {
locale: ogLocale,
alternateLocale: alternateLocales,
},
};
}
export default async function LocaleLayout({ children, params }: Props) {
const { locale: rawLocale } = await params;
const locale: Locale = isValidLocale(rawLocale) ? rawLocale : defaultLocale;
return (
<html lang={locale}>
<body>{children}</body>
</html>
);
}
generateStaticParams kaikille kieliversiosivuille
Staattisilla tai ISR-sivuilla vie generateStaticParams ulos, jotta Next.js esirenderöi koontiversion luonnin aikana version jokaiselle kieliversiolle. Ilman tätä täysin staattinen vienti renderöisi vain oletuskieliversion.
Tämä funktio toimii yhdessä generateMetadata-funktion kanssa — Next.js kutsuu molempia jokaiselle generateStaticParams-funktion tuottamalle yhdistelmälle.
// app/[locale]/about/page.tsx
import type { Metadata } from 'next';
import { locales, isValidLocale, defaultLocale, type Locale } from '@/lib/i18n';
import { getDictionary } from '@/lib/getDictionary';
import { buildAlternates } from '@/lib/metadata';
const BASE_URL = process.env.NEXT_PUBLIC_BASE_URL ?? 'https://www.coddykit.com';
// Pre-render /en/about, /fr/about, /de/about at build time
export function generateStaticParams() {
return locales.map((locale) => ({ locale }));
}
type Props = { params: Promise<{ locale: string }> };
export async function generateMetadata({ params }: Props): Promise<Metadata> {
const { locale: rawLocale } = await params;
const locale: Locale = isValidLocale(rawLocale) ? rawLocale : defaultLocale;
const dict = await getDictionary(locale);
return {
title: dict.about.title,
description: dict.about.description,
alternates: {
canonical: `${BASE_URL}/${locale}/about`,
...buildAlternates('/about'),
},
};
}
export default async function AboutPage({ params }: Props) {
const { locale: rawLocale } = await params;
const locale: Locale = isValidLocale(rawLocale) ? rawLocale : defaultLocale;
const dict = await getDictionary(locale);
return <main><h1>{dict.about.title}</h1></main>;
}
Lokalisoidun sivustokartan rakentaminen
Next.js 15 tukee erityistä app/sitemap.ts-tiedostoa, joka palauttaa joukon MetadataRoute.Sitemap-merkintöjä. Kansainvälistetyssä sovelluksessa jokainen sivu esiintyy kerran kutakin kieliversiota kohti, ja jokainen merkintä sisältää alternates.languages-kartan — käytännössä sivustokarttaan upotetut hreflang-tiedot.
Googlen Sitemap hreflang -määritys ja <link rel="alternate"> -head-tagien lähestymistapa ovat päällekkäisiä — tarvitsette vain toisen, mutta molempien käyttö on turvallista ja suurille sivustoille suositeltavaa.
// app/sitemap.ts
import type { MetadataRoute } from 'next';
import { locales, defaultLocale } from '@/lib/i18n';
const BASE_URL = process.env.NEXT_PUBLIC_BASE_URL ?? 'https://www.coddykit.com';
// Static paths (add dynamic paths from your DB as needed)
const staticPaths = ['', '/about', '/courses'];
export default function sitemap(): MetadataRoute.Sitemap {
const entries: MetadataRoute.Sitemap = [];
for (const path of staticPaths) {
for (const locale of locales) {
const url = `${BASE_URL}/${locale}${path}`;
// Build hreflang alternates for this path
const languages: Record<string, string> = {};
for (const loc of locales) {
languages[loc] = `${BASE_URL}/${loc}${path}`;
}
languages['x-default'] = `${BASE_URL}/${defaultLocale}${path}`;
entries.push({
url,
lastModified: new Date(),
changeFrequency: 'weekly',
priority: path === '' ? 1 : 0.8,
alternates: { languages },
});
}
}
return entries;
}
Dynaamiset sivustokarttamerkinnät tietokannasta
Useimmilla tuotantosivustoilla on dynaamisia sivuja — blogikirjoituksia, kurssisivuja ja tuotelistauksia. Hae niiden slug-tunnisteet sitemap.ts-tiedostossa (se on vain palvelimella suoritettava tiedosto) ja luo kullekin lokalisoidut merkinnät.
Pidä sivustokartta nopeana: hae vain tunnisteet ja slug-tunnisteet, älä koko sisältöä. Jos sivuja on tuhansia, käytä Next.js:n sivustokartan jakamisominaisuutta viemällä ulos useita sitemap-N.ts-tiedostoja tai käyttämällä generateSitemaps-funktiota.
// app/sitemap.ts (extended with dynamic course pages)
import type { MetadataRoute } from 'next';
import { locales, defaultLocale } from '@/lib/i18n';
const BASE_URL = process.env.NEXT_PUBLIC_BASE_URL ?? 'https://www.coddykit.com';
async function getCourseSlugs(): Promise<string[]> {
// Replace with your actual data-fetch (Supabase, fetch, ORM, etc.)
const res = await fetch(`${BASE_URL}/api/courses/slugs`, {
next: { revalidate: 3600 }, // revalidate every hour
});
const data: { slug: string }[] = await res.json();
return data.map((c) => c.slug);
}
function buildEntry(path: string): MetadataRoute.Sitemap[number] {
const languages: Record<string, string> = {};
for (const loc of locales) {
languages[loc] = `${BASE_URL}/${loc}${path}`;
}
languages['x-default'] = `${BASE_URL}/${defaultLocale}${path}`;
return { url: `${BASE_URL}/${defaultLocale}${path}`, lastModified: new Date(),
changeFrequency: 'weekly', priority: 0.7, alternates: { languages } };
}
export default async function sitemap(): Promise<MetadataRoute.Sitemap> {
const courseSlugs = await getCourseSlugs();
const coursePaths = courseSlugs.flatMap((slug) =>
locales.map((locale) => ({ locale, path: `/courses/${slug}` }))
);
return [
buildEntry(''),
buildEntry('/about'),
...coursePaths.map(({ path }) => buildEntry(path)),
];
}
Mikä alternates-kenttä on oikea Next.js 15:n hreflang-määritykseen?
Next.js 15 -sovelluksenne tukee englantia, ranskaa ja saksaa. Haluatte, että Google näyttää ranskankielisen version osoitteessa /fr/about, kun ranskankielinen käyttäjä tekee haun. Mikä generateMetadata-funktion palautusarvo toteuttaa hreflang-määrityksen oikein ranskankieliselle About-sivulle?
Kertaus: lokalisoidut metatiedot, sivustokartat ja hreflang
Tässä oppitunnissa rakensitte täydellisen kansainvälistetyn SEO-kerroksen Next.js 15:n App Router -projektille. Muistettavat asiat:
- Sanakirjalataaja — kieliversiokohtaiset dynaamiset tuonnit, jotka tyypitetään jaetulla
Dictionary-rajapinnalla ja jotka Next.js tallentaa automaattisesti välimuistiin - generateMetadata — asynkroninen funktio, joka saa
params.locale-arvon, lataa oikean sanakirjan ja palauttaa arvottitle,description,alternates.canonicaljaalternates.languages - hreflang-vaihtoehdot — aina absoluuttisia URL-osoitteita; sisällytä kaikki tuetut kieliversiot sekä
x-default, joka osoittaa oletuskieliversion URL-osoitteeseen - OpenGraph-kieliversio — asetetaan juuriasettelussa alaviivamuodossa (
fr_FR), ja muiden kielten luettelo annetaanalternateLocale-kentässä - generateStaticParams — käytä yhdessä
generateMetadata-funktion kanssa, jotta jokainen kieliversio esirenderöidään koontiversion luonnin aikana - sitemap.ts — käytä kunkin merkinnän kanssa
MetadataRoute.Sitemap-tyyppiä jaalternates.languages-kenttää; hae dynaamiset slug-tunnisteet tiedostossa (se suoritetaan palvelimella) - buildAlternates-apuohjelma — irrota kielikartan logiikka jaettuun utiliteettiin, jotta sivutiedostot pysyvät selkeinä
Kun nämä osat ovat paikoillaan, jokainen kieliversiosivu sisältää oikeat metatiedot ja ristiviittaukset, Google voi indeksoida kaikki kieliversiot itsenäisesti ja käyttäjät päätyvät aina oikeaan kieliversioon.
Opi TypeScript tekoälytuutorin avulla — ilmaiseksi
Kirjoita ja suorita oikeaa koodia selaimessa, saa välitöntä apua tekoälytuutorilta ympäri vuorokauden ja jatka siitä, mihin jäit, verkossa tai sovelluksessa.
- Kurssit
- 22
- Oppitunnit
- 88
Usein kysytyt kysymykset
Onko oppitunti ”Lokalisoinnin mukaiset metatiedot, sivustokartat ja hreflang” ilmainen?
Kyllä — voit lukea täällä verkossa kokonaan ilmaiseksi mitkä tahansa Next.js 15 -fullstack-kehitys (App Router + Server Actions)-oppimispolun 3 oppituntia, myös oppitunnin “Lokalisoinnin mukaiset metatiedot, sivustokartat ja hreflang”. Sen jälkeen CoddyKit PRO avaa kaikki oppitunnit sekä interaktiiviset harjoitukset sisäänrakennetulla koodieditorilla ja ympäri vuorokauden toimivalla tekoälytuutorilla. Next.js 15 -fullstack-kehitys (App Router + Server Actions)-kurssilla on yhteensä 4 oppituntia.
Mitä opin oppitunnilla ”Lokalisoinnin mukaiset metatiedot, sivustokartat ja hreflang”?
Tuottakaa kielialuekohtaiset otsikot, kuvaukset ja hreflang-vaihtoehdot kansainvälisen hakukoneoptimoinnin varmistamiseksi. Harjoittelet Next.js 15 -fullstack-kehitys (App Router + Server Actions)-aihetta koodilla, jonka suoritat suoraan selaimessa. Ympäri vuorokauden käytettävissä oleva tekoälytuutori vastaa kysymyksiisi oppitunnin aikana.
Tarvitsenko kokemusta aloittaakseni Next.js 15 -fullstack-kehitys (App Router + Server Actions)-opiskelun?
Aiempi kokemus ei ole tarpeen. CoddyKitin Next.js 15 -fullstack-kehitys (App Router + Server Actions)-oppimispolku sopii vasta-alkajista edistyneisiin, joten voit aloittaa tästä tai alusta ja edetä omaan tahtiisi. Tämä on oppitunti 3/4.
Kuinka kauan ”Lokalisoinnin mukaiset metatiedot, sivustokartat ja hreflang”-oppitunnin suorittaminen kestää?
Useimmat CoddyKitin oppitunnit kestävät noin 5–10 minuuttia. Jokainen oppitunti on lyhyt ja interaktiivinen, joten edistyt tasaisesti ja voit jatkaa siitä, mihin jäit – sekä verkossa että sovelluksessa.
Voinko kirjoittaa ja suorittaa koodia tällä Next.js 15 -fullstack-kehitys (App Router + Server Actions)-oppitunnilla?
Kyllä. Jokainen Next.js 15 -fullstack-kehitys (App Router + Server Actions)-oppitunti sisältää sisäänrakennetun koodieditorin, joten voit kirjoittaa ja suorittaa oikeaa koodia suoraan selaimessa ja saada välitöntä palautetta tekoälyltä – paikallista asennusta ei tarvita.
Kaikki tämän kurssin oppitunnit
- Kielialueen tunnistus ja alihakemistoreititys middlewaressa
- Käännösten lataaminen palvelimella ja viestiluettelot
- Lokalisoinnin mukaiset metatiedot, sivustokartat ja hreflang
- Päivämäärien, lukujen ja monikkojen muotoilu kielialueen mukaan