Next.js 15 -fullstack-kehitys (App Router + Server Actions) · Oppitunti

Kielialueen tunnistus ja alihakemistoreititys middlewaressa

Tunnistakaa käyttäjän kielialue ja uudelleenkirjoittakaa pyynnöt lokalisoituihin alihakemistoihin middlewaressa.

Oppitunti 1/413 vaihetta

Kielialueen tunnistus ja alihakemistoreititys middlewaressa on ilmainen Next.js 15 -fullstack-kehitys (App Router + Server Actions)-oppitunti CoddyKitissä. Tämä on oppitunti 1/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.

Mitä lokaalin automaattinen tunnistus tarkoittaa

Next.js 15:n kansainvälistäminen (i18n) alkaa locale-tunnistuksesta: selvitetään, minkä kielen ja alueen kävijä haluaa, ennen kuin hänelle tarjotaan sisältöä.

Tunnistus lukee tiedot yleensä kolmesta lähteestä seuraavassa tärkeysjärjestyksessä:

  • URL-polku — /en/about, /tr/about (selkein)
  • Cookie — aiemmin tallennettu asetus, kuten NEXT_LOCALE=fr
  • Accept-Language-otsake — selaimen lähettämä automaattisesti

Next.js 15 poisti sisäänrakennetun i18n-määrityksen App Routerin aikakaudella, joten toteutatte tämän logiikan itse middleware-koodissa. Näin saatte täyden hallinnan vastaavuussäännöistä, varavaihtoehdoista ja uudelleenohjauksista.

Projektin määritys: tuetut localet

Ennen middleware-koodin kirjoittamista määrittäkää tuetut localet ja oletuslocale jaetussa vakiotiedostossa. Tämä keskitetty totuuden lähde tuodaan middleware-koodiin, layout-komponentteihin ja kaikkiin locale-tietoisiin apuohjelmiin.

Yleinen käytäntö on lib/i18n.ts-tiedosto, joka vie käyttöön seuraavat:

  • locales — kaikkien tuettujen locale-merkkijonojen luettelo
  • defaultLocale — varavaihtoehto, kun vastaavuutta ei löydy
  • Apumäärittely tyyppiturvallisia locale-arvoja varten
// lib/i18n.ts
export const locales = ['en', 'tr', 'de', 'fr'] 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);
}

Accept-Language-otsakkeen jäsentäminen

Accept-Language-otsakkeen, joka voi olla esimerkiksi tr-TR,tr;q=0.9,en;q=0.8. Teidän on jäsennettävä se ja löydettävä paras vastaavuus tuettujen localejen joukosta.

Kokonaisen jäsentimen kirjoittamisen sijaan suosittu @formatjs/intl-localematcher-kirjasto hoitaa painotetun neuvottelun oikein. Yhdistäkää se negotiator-kirjastoon, jotta voitte muodostaa järjestetyn luettelon raakaotsakkeen merkkijonosta.

Asentakaa molemmat paketit:

  • npm i @formatjs/intl-localematcher negotiator
  • npm i -D @types/negotiator
// lib/locale-detection.ts
import Negotiator from 'negotiator';
import { match } from '@formatjs/intl-localematcher';
import { locales, defaultLocale, type Locale } from './i18n';

export function getLocaleFromHeader(acceptLanguage: string | null): Locale {
  if (!acceptLanguage) return defaultLocale;

  const headers = { 'accept-language': acceptLanguage };
  const languages = new Negotiator({ headers }).languages();

  try {
    return match(languages, [...locales], defaultLocale) as Locale;
  } catch {
    return defaultLocale;
  }
}

Locale-cookien lukeminen

Kun käyttäjä vaihtaa kielen manuaalisesti, tallennatte valinnan cookieen, jotta tulevilla käynneillä sitä noudatetaan heti — Accept-Language-otsaketta ei tarvitse lukea uudelleen.

Middleware-koodissa luette cookiet suoraan saapuvasta NextRequest-pyynnöstä. Cookien nimenä käytetään yleensä nimeä NEXT_LOCALE, mutta voitte käyttää mitä tahansa nimeä, kunhan käytätte sitä johdonmukaisesti myös cookiea kirjoitettaessa (tämä tehdään yleensä Server Actionissa tai API-reitillä).

Tunnistuksen tärkeysjärjestyksen tulisi olla: ensin cookie, sitten otsake ja lopuksi oletus.

// lib/locale-detection.ts  (continued)
import { type NextRequest } from 'next/server';
import { isValidLocale, defaultLocale, type Locale } from './i18n';
import { getLocaleFromHeader } from './locale-detection';

export const LOCALE_COOKIE = 'NEXT_LOCALE';

export function detectLocale(request: NextRequest): Locale {
  // 1. Check explicit cookie preference
  const cookieValue = request.cookies.get(LOCALE_COOKIE)?.value;
  if (cookieValue && isValidLocale(cookieValue)) {
    return cookieValue;
  }

  // 2. Negotiate from Accept-Language header
  const acceptLanguage = request.headers.get('accept-language');
  return getLocaleFromHeader(acceptLanguage);
}

Alipolkujen reitityskäytäntö

Alipolkujen reititys tarkoittaa, että jokaisen URL-osoitteen alussa on locale: /en/dashboard, /tr/dashboard, /de/dashboard. Tämä lähestymistapa on:

  • hakukoneystävällinen — hakukoneet indeksoivat jokaisen lokalen erillisenä URL-osoitteena
  • helposti jaettava — linkki osoittaa aina samaan localeen
  • välimuistissa säilytettävä — CDN voi tallentaa kunkin lokalen erikseen ilman cookien tarkistamista

App Routerin kansiorakenne vastaa tätä:

  • app/[lang]/page.tsx — kunkin lokalen kotisivu
  • app/[lang]/layout.tsx — locale-tietoinen juurilayout
  • app/[lang]/dashboard/page.tsx — sisäkkäiset reitit

[lang]-dynaaminen segmentti poimii locale-merkkijonon URL-osoitteesta.

Middlewaren kirjoittaminen: peruslogiikka

Projektin juurihakemistossa oleva middleware.ts-tiedosto sieppaa jokaisen pyynnön. Sen tehtävänä on:

  1. ohittaa pyynnöt, joissa on jo kelvollinen locale-etuliite, ja tarjota ne sellaisinaan
  2. tunnistaa locale pyynnöistä, joissa ei ole locale-etuliitettä, ja tehdä uudelleenohjaus lokalisoituun URL-osoitteeseen

Käyttäkää NextResponse.redirect-menetelmää (pysyvä 308 SEO-tarkoituksiin tai väliaikainen 307 dynaamisia asetuksia varten) tai NextResponse.rewrite-menetelmää (piilottaa etuliitteen selaimen osoiteriviltä — alipolkujen reitityksessä harvinaisempi).

// middleware.ts
import { NextResponse, type NextRequest } from 'next/server';
import { locales, defaultLocale, isValidLocale } from './lib/i18n';
import { detectLocale } from './lib/locale-detection';

export function middleware(request: NextRequest): NextResponse {
  const { pathname } = request.nextUrl;

  // Check if the path already starts with a valid locale
  const pathnameLocale = locales.find(
    (locale) => pathname.startsWith(`/${locale}/`) || pathname === `/${locale}`
  );

  if (pathnameLocale) {
    // Already localized — pass through
    return NextResponse.next();
  }

  // Detect locale and redirect
  const locale = detectLocale(request);
  const newUrl = new URL(`/${locale}${pathname}`, request.url);
  newUrl.search = request.nextUrl.search; // preserve query params

  return NextResponse.redirect(newUrl, { status: 307 });
}

matcher-määritys: staattisten resurssien poissulkeminen

Ilman matcher-määritystä middleware suoritetaan jokaiselle pyynnölle — myös pyynnöille, jotka koskevat polkuja /_next/static ja /favicon.ico, sekä API-reiteille. Tämä tuhlaa laskentaresursseja ja voi rikkoa staattisten resurssien toimituksen.

Viekää käyttöön config-objekti, jossa on matcher-taulukko, ja rajoittakaa sen avulla middleware-koodin käsittelemät polut. Alla oleva kuvio sulkee pois:

  • Next.js:n sisäiset polut (/_next)
  • staattisten tiedostojen päätteet (kuvat, fontit ja manifestit)
  • API-reitit, jos käsittelette niiden i18n:n erikseen
// middleware.ts (add at the bottom)
export const config = {
  matcher: [
    /*
     * Match all request paths EXCEPT:
     * - _next/static  (static files)
     * - _next/image   (image optimization)
     * - favicon.ico, sitemap.xml, robots.txt
     * - Files with extensions (e.g. .png, .svg, .woff2)
     */
    '/((?!_next/static|_next/image|favicon\.ico|sitemap\.xml|robots\.txt|.*\.(?:png|jpg|jpeg|gif|svg|ico|woff|woff2|ttf|otf|webp)).*)',
  ],
};

[lang]-layout: lokalen välittäminen eteenpäin

Kun middleware takaa, että jokaisessa URL-osoitteessa on locale-etuliite, app/[lang]/layout.tsx vastaanottaa params.lang-arvon propina. Käyttäkää sitä seuraaviin tarkoituksiin:

  • määrittäkää lang-attribuutti elementille <html> esteettömyttä ja hakukoneoptimointia varten
  • määrittäkää RTL-kielille dir-attribuutti (arabia, heprea)
  • ladatkaa oikea käännössanakirja

Next.js 15:ssä layout-parametrit ovat nyt asynkronisia — teidän on tehtävä await params ennen ominaisuuksien lukemista.

// app/[lang]/layout.tsx
import { type Locale, isValidLocale, defaultLocale } from '@/lib/i18n';
import { getDictionary } from '@/lib/dictionaries';

interface RootLayoutProps {
  children: React.ReactNode;
  params: Promise<{ lang: string }>;
}

export default async function RootLayout({ children, params }: RootLayoutProps) {
  const { lang } = await params;
  const locale: Locale = isValidLocale(lang) ? lang : defaultLocale;
  const dict = await getDictionary(locale);

  return (
    <html lang={locale} dir={locale === 'ar' ? 'rtl' : 'ltr'}>
      <body>{children}</body>
    </html>
  );
}

Käännössanakirjojen lataaminen

S anakirja on JSON-tiedosto, joka sisältää kaikki yhden lokalen käännetyt merkkijonot. Kun sanakirjat ladataan laiskasti (yksi tiedosto localea kohden), alkuperäinen bundle pysyy pienenä — vain aktiivisen lokalen merkkijonot haetaan.

getDictionary-funktio käyttää dynaamista import()-kutsua, jotta Next.js voi jakaa kunkin lokalen build-aikana omaksi chunkikseen.

// lib/dictionaries.ts
import type { Locale } from './i18n';

const dictionaries = {
  en: () => import('../dictionaries/en.json').then((m) => m.default),
  tr: () => import('../dictionaries/tr.json').then((m) => m.default),
  de: () => import('../dictionaries/de.json').then((m) => m.default),
  fr: () => import('../dictionaries/fr.json').then((m) => m.default),
};

export type Dictionary = Awaited<ReturnType<(typeof dictionaries)['en']>>;

export async function getDictionary(locale: Locale): Promise<Dictionary> {
  return dictionaries[locale]();
}

// dictionaries/en.json (example shape)
// {
//   "nav": { "home": "Home", "about": "About" },
//   "hero": { "title": "Welcome", "subtitle": "Start learning today" }
// }

Käyttäjän locale-asetuksen tallentaminen Server Actionilla

Kun käyttäjä valitsee kielenvaihtajan käyttöliittymästä toisen kielen, tallentakaa valinta cookieen Server Action-toiminnolla. Seuraavassa pyynnössä cookie on tällöin saatavilla middleware-koodissa ja ohittaa Accept-Language-otsakkeen.

Käyttäkää next/headers-moduulin cookies()-funktiota Server Actionin sisällä. Kun olette asettaneet cookien, kutsukaa redirect()-funktiota siirtyäksenne samalle polulle uuden lokalen alla.

// app/actions/set-locale.ts
'use server';

import { cookies } from 'next/headers';
import { redirect } from 'next/navigation';
import { isValidLocale, type Locale } from '@/lib/i18n';
import { LOCALE_COOKIE } from '@/lib/locale-detection';

export async function setLocaleAction(
  locale: string,
  currentPath: string
): Promise<void> {
  if (!isValidLocale(locale)) {
    throw new Error(`Unsupported locale: ${locale}`);
  }

  const cookieStore = await cookies();
  cookieStore.set(LOCALE_COOKIE, locale, {
    path: '/',
    maxAge: 60 * 60 * 24 * 365, // 1 year
    sameSite: 'lax',
  });

  // Strip existing locale prefix and redirect to new locale path
  const pathWithoutLocale = currentPath.replace(/^\/[a-z]{2}(\/|$)/, '/');
  redirect(`/${locale}${pathWithoutLocale}`);
}

Kielenvaihtajakomponentin rakentaminen

Kielenvaihtaja on Client Component, joka kutsuu setLocaleAction-Server Actionia, kun käyttäjä valitsee uuden lokalen. Se käyttää usePathname()-funktiota nykyisen URL-osoitteen selvittämiseen, jotta toiminto voi ohjata vastaavalle sivulle uudessa localessa.

Huomioikaa, että myös lomakelähestymistapa (käyttämällä rakennetta <form action={...}>) toimii ja on esteetön — se toimii hallitusti myös ilman JavaScriptiä.

// components/language-switcher.tsx
'use client';

import { usePathname } from 'next/navigation';
import { useTransition } from 'react';
import { setLocaleAction } from '@/app/actions/set-locale';
import { locales, type Locale } from '@/lib/i18n';

const labels: Record<Locale, string> = {
  en: 'English',
  tr: 'Turkce',
  de: 'Deutsch',
  fr: 'Francais',
};

export function LanguageSwitcher({ currentLocale }: { currentLocale: Locale }) {
  const pathname = usePathname();
  const [isPending, startTransition] = useTransition();

  function handleChange(locale: Locale) {
    startTransition(() => {
      setLocaleAction(locale, pathname);
    });
  }

  return (
    <select
      value={currentLocale}
      onChange={(e) => handleChange(e.target.value as Locale)}
      disabled={isPending}
      aria-label="Select language"
    >
      {locales.map((locale) => (
        <option key={locale} value={locale}>
          {labels[locale]}
        </option>
      ))}
    </select>
  );
}

Tietotesti: Middlewaren uudelleenohjaus vai rewrite

Kävijä saapuu osoitteeseen /dashboard ilman locale-etuliitettä. Middleware tunnistaa hänen suosikkilocaleekseen fr. Mikä toimintatapa sopii parhaiten julkiseen ja SEO-optimoituun alipolkujen i18n-toteutukseen?

Oppitunnin kertaus: Locale-tunnistus ja alipolkujen reititys

Tässä oppitunnissa rakensitte Next.js 15:n App Routerille täydellisen i18n-reititystason:

  • Vakiotiedosto (lib/i18n.ts) — keskitetty totuuden lähde tuetuille localeille ja oletuslocalelle sekä tyyppiturvallinen Locale-unionityyppi.
  • Tunnistusapuohjelmat (lib/locale-detection.ts) — tärkeysjärjestys: ensin cookie, sitten Accept-Language-otsakkeen neuvottelu @formatjs/intl-localematcher-kirjastolla ja lopuksi varavaihtoehtona oletuslocale.
  • Middleware (middleware.ts) — päästää valmiiksi lokalisoidut polut läpi ja ohjaa paljaat polut tunnistetun lokalen alipolkuun; matcher rajoittaa käsittelyä ja ohittaa staattiset resurssit.
  • App Router -rakenne — app/[lang]/layout.tsx vastaanottaa Next.js 15:ssä params-arvon awaitattavana Promisena, asettaa html lang-arvon ja lataa laiskasti sanakirjan.
  • Server Action — kirjoittaa käyttäjän tekemän valinnan pitkäikäiseen cookieen ja ohjaa vastaavalle lokalisoidulle polulle.
  • Kielenvaihtaja — Client Component, joka kutsuu Server Actionia useTransition-funktion sisällä estämättömän käyttökokemuksen takaamiseksi.

Tämä malli tarjoaa kaikki SEO-hyödyt (erilliset URL-osoitteet localea kohden), hallitun toiminnan heikennetyissä tilanteissa ja käyttäjän asetuksen säilymisen — ilman kolmannen osapuolen i18n-reitityskirjastoa.

Aloita maksutta

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 ”Kielialueen tunnistus ja alihakemistoreititys middlewaressa” 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 “Kielialueen tunnistus ja alihakemistoreititys middlewaressa”. 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 ”Kielialueen tunnistus ja alihakemistoreititys middlewaressa”?

Tunnistakaa käyttäjän kielialue ja uudelleenkirjoittakaa pyynnöt lokalisoituihin alihakemistoihin middlewaressa. 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 1/4.

Kuinka kauan ”Kielialueen tunnistus ja alihakemistoreititys middlewaressa”-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

  1. Kielialueen tunnistus ja alihakemistoreititys middlewaressa
  2. Käännösten lataaminen palvelimella ja viestiluettelot
  3. Lokalisoinnin mukaiset metatiedot, sivustokartat ja hreflang
  4. Päivämäärien, lukujen ja monikkojen muotoilu kielialueen mukaan
← Takaisin: Next.js 15 -fullstack-kehitys (App Router + Server Actions)