Next.js 15 fullstack (App Router + Server Actions) · leksjon

Formatere datoer, tall og flertall etter lokalisering

Bruk Intl-API-ene til å gjengi datoer, valutaer og flertallsformer som er korrekte for lokaliseringen.

Leksjon 4 av 413 trinn

Formatere datoer, tall og flertall etter lokalisering er en gratis leksjon i Next.js 15 fullstack (App Router + Server Actions) på CoddyKit. Dette er leksjon 4 av 4. Du kan lese valgfritt 3 leksjoner fra denne læringsstien gratis i sin helhet – deretter låser CoddyKit PRO opp alle leksjoner, samt praktisk øving med en innebygd kodeeditor og en AI-veileder som er tilgjengelig døgnet rundt. Den er en del av læringsløpet i Next.js 15 fullstack (App Router + Server Actions), og fremdriften din synkroniseres mellom nettet og CoddyKit-appen. Kurset i Next.js 15 fullstack (App Router + Server Actions) inneholder totalt 4 leksjoner.

Hvorfor locale-bevisst formatering er viktig

En dato som 06/07/2025 betyr 7. juni for en amerikaner, men 6. juli for en europeer. Et tall som 1.234 betyr ett tusen to hundre og trettifire i USA, men én komma to tre fire i Tyskland.

Hvis formatlogikken hardkodes, slutter appen å fungere riktig for internasjonale brukere. I stedet leveres både nettleseren og Node.js med Intl-navnerommet — et standardbasert sett med API-er som håndterer lokalitetstilpasset formatering uten tredjepartsbiblioteker.

  • Intl.DateTimeFormat — formaterer datoer og klokkeslett
  • Intl.NumberFormat — formaterer tall, valutaer og enheter
  • Intl.PluralRules — velger riktig flertallsform

I et Next.js 15-prosjekt med App Router kjøres disse på serveren (i Server Components og Server Actions) så vel som på klienten. Dermed får du et konsistent resultat uten å sende med ekstra JavaScript.

Finne aktiv lokalitet i App Router

Next.js 15 med App Router lagrer den aktive lokaliteten i URL-segmentet. En vanlig konvensjon er /en/dashboard eller /de/dashboard. layout.tsx mottar en params-prop som inneholder den.

Utdraget nedenfor viser hvordan du leser params.locale i en asynkron Server Component og sender den videre til formateringshjelpere.

// app/[locale]/layout.tsx
import { ReactNode } from 'react';

interface Props {
  children: ReactNode;
  params: Promise<{ locale: string }>;
}

export default async function LocaleLayout({ children, params }: Props) {
  const { locale } = await params; // e.g. 'en', 'de', 'tr'

  return (
    <html lang={locale}>
      <body>{children}</body>
    </html>
  );
}

Formatere datoer med Intl.DateTimeFormat

Intl.DateTimeFormat mottar en lokalitetsstreng og et options-objekt. Du kan styre hvilke dato- og klokkeslettdeler som skal vises, og hvilken stil de skal ha.

  • dateStyle: 'short', 'medium', 'long', 'full'
  • timeStyle: de samme alternativene
  • Enkeltfelter: year, month, day, hour, minute og så videre

Kallet .format(date) returnerer en lokalitetsriktig streng. Siden Intl er innebygd i Node.js, kan dette kjøres trygt i en Server Component uten ekstra kostnad for klientens bundle.

// lib/format-date.ts
export function formatDate(
  date: Date,
  locale: string,
  options: Intl.DateTimeFormatOptions = { dateStyle: 'long' }
): string {
  return new Intl.DateTimeFormat(locale, options).format(date);
}

// Usage examples:
// formatDate(new Date(), 'en-US') => 'June 7, 2025'
// formatDate(new Date(), 'de-DE') => '7. Juni 2025'
// formatDate(new Date(), 'tr-TR') => '7 Haziran 2025'
// formatDate(new Date(), 'ja-JP') => '2025年6月7日'

Bruke formatDate i en Server Component

Siden Server Components kjører på Node.js, kan du kalle formatDate-hjelperen direkte — ingen useEffect, ingen hydreringsavvik og ingen ekstra bundle-størrelse for klienten.

Lokaliteten hentes fra params og sendes direkte til formateringsfunksjonen. Resultatet er en vanlig streng som bygges inn i HTML.

// app/[locale]/blog/[slug]/page.tsx
import { formatDate } from '@/lib/format-date';

interface Props {
  params: Promise<{ locale: string; slug: string }>;
}

export default async function BlogPost({ params }: Props) {
  const { locale, slug } = await params;

  // In a real app you would fetch the post from a DB or CMS
  const publishedAt = new Date('2025-06-07T09:00:00Z');

  return (
    <article>
      <time dateTime={publishedAt.toISOString()}>
        {formatDate(publishedAt, locale, { dateStyle: 'full', timeStyle: 'short' })}
      </time>
      <h1>Post: {slug}</h1>
    </article>
  );
}

Formatere tall og valutaer

Intl.NumberFormat håndterer tre hovedbruksområder:

  • Vanlige tall — desimalskilletegn og grupperinger varierer etter lokalitet (1,234.56 mot 1.234,56)
  • Valuta — plasseringen av symbolet, antallet desimaler og til og med valutanavnet varierer
  • Enheter — kilogram, miles, byte og så videre, formatert i henhold til lokale konvensjoner

Send alltid med currency sammen med style: 'currency'. Valutakoden (ISO 4217) forteller formateringsfunksjonen hvilket symbol som skal brukes, mens lokaliteten forteller hvor det skal plasseres.

// lib/format-number.ts
export function formatCurrency(
  amount: number,
  currency: string,
  locale: string
): string {
  return new Intl.NumberFormat(locale, {
    style: 'currency',
    currency,
    minimumFractionDigits: 2,
    maximumFractionDigits: 2,
  }).format(amount);
}

export function formatNumber(
  value: number,
  locale: string,
  options?: Intl.NumberFormatOptions
): string {
  return new Intl.NumberFormat(locale, options).format(value);
}

// formatCurrency(1234.5, 'USD', 'en-US') => '$1,234.50'
// formatCurrency(1234.5, 'EUR', 'de-DE') => '1.234,50 €'
// formatCurrency(1234.5, 'TRY', 'tr-TR') => '₺1.234,50'

En frittstående Intl-demo (kjørbar)

Utdraget nedenfor er et komplett, selvstendig TypeScript-program som demonstrerer Intl.DateTimeFormat og Intl.NumberFormat på tvers av flere lokaliteter. Du trenger ingen rammeverk eller ekstern pakke — programmet kan kjøres med ts-node eller limes inn i en TypeScript-lekeplass.

const locales = ['en-US', 'de-DE', 'tr-TR', 'ja-JP', 'ar-SA'];
const date = new Date('2025-06-07T14:30:00Z');
const price = 9999.99;

for (const locale of locales) {
  const formattedDate = new Intl.DateTimeFormat(locale, {
    dateStyle: 'medium',
    timeStyle: 'short',
  }).format(date);

  const formattedPrice = new Intl.NumberFormat(locale, {
    style: 'currency',
    currency: 'USD',
  }).format(price);

  console.log(`[${locale}]  date: ${formattedDate}  |  price: ${formattedPrice}`);
}

Flertallsregler — den skjulte kompleksiteten

Engelsk har to flertallsformer: 1 item mot 2 items. Arabisk har derimot seks, russisk har tre og polsk har fire. Hvis du implementerer flertallsbøying med en enkel sjekk som count === 1 ? 'item' : 'items', blir appen feil for det meste av verden.

Intl.PluralRules løser dette ved å returnere riktig CLDR-flertallskategori for et gitt tall og en gitt lokalitet:

  • zero
  • one
  • two
  • few
  • many
  • other

Du knytter hver kategori til den riktige oversatte strengen i meldingskatalogen, og lar deretter PluralRules velge riktig nøkkel under kjøring.

Implementere en pluralize-hjelper

Hjelperen nedenfor mottar et antall, en lokalitet og et oppslag med strenger for flertallskategoriene. Den bruker Intl.PluralRules til å velge riktig form, og erstatter deretter plassholderen {{count}} med det faktiske tallet.

Dermed holdes flertallslogikken utenfor oversettelsesfilene — filene trenger bare malene for hver kategori, ikke forgreningskode.

// lib/pluralize.ts
type PluralForms = Partial<Record<Intl.LDMLPluralRule, string>>;

export function pluralize(
  count: number,
  locale: string,
  forms: PluralForms
): string {
  const rules = new Intl.PluralRules(locale);
  const category = rules.select(count); // 'zero'|'one'|'two'|'few'|'many'|'other'
  const template = forms[category] ?? forms['other'] ?? '';
  return template.replace('{{count}}', String(count));
}

// English
// pluralize(1, 'en', { one: '{{count}} item', other: '{{count}} items' })
// => '1 item'

// pluralize(5, 'en', { one: '{{count}} item', other: '{{count}} items' })
// => '5 items'

// Arabic (6 forms)
// pluralize(11, 'ar', { zero:'...', one:'...', two:'...', few:'...', many:'{{count}} عنصرًا', other:'...' })
// => '11 عنصرًا'

Bruke pluralize i en Server Component

Kombiner pluralize-hjelperen med lokalitetstilpasset tallformatering for en komplett løsning som er klar til bruk i produksjon. Handlekurvsammendraget nedenfor viser antallet varer og totalprisen, begge korrekt formatert for alle lokaliteter.

// app/[locale]/cart/page.tsx
import { pluralize } from '@/lib/pluralize';
import { formatCurrency } from '@/lib/format-number';

interface Props {
  params: Promise<{ locale: string }>;
}

// Minimal translation catalog — in real projects use next-intl or similar
const itemForms: Record<string, Record<string, string>> = {
  'en-US': { one: '{{count}} item',     other: '{{count}} items' },
  'de-DE': { one: '{{count}} Artikel',  other: '{{count}} Artikel' },
  'tr-TR': { one: '{{count}} ürün',     other: '{{count}} ürün' },
};

export default async function CartPage({ params }: Props) {
  const { locale } = await params;
  const itemCount = 3;
  const total = 129.99;
  const forms = itemForms[locale] ?? itemForms['en-US'];

  return (
    <section>
      <p>{pluralize(itemCount, locale, forms)}</p>
      <p>Total: {formatCurrency(total, 'USD', locale)}</p>
    </section>
  );
}

Formatere relativ tid med Intl.RelativeTimeFormat

Intl.RelativeTimeFormat formaterer tidsrom relativt til nå — 3 days ago, in 2 hours — for alle lokaliteter og numeriske stiler.

  • numeric: 'always' — bruker alltid tall: 1 day ago
  • numeric: 'auto' — bruker naturlig språk når det er mulig: yesterday

Hjelperen nedenfor beregner automatisk den beste enheten og overlater strengformateringen til API-et.

// lib/format-relative.ts
export function formatRelative(date: Date, locale: string): string {
  const rtf = new Intl.RelativeTimeFormat(locale, { numeric: 'auto' });
  const diffMs = date.getTime() - Date.now();
  const diffSec = Math.round(diffMs / 1000);

  const thresholds: [number, Intl.RelativeTimeFormatUnit][] = [
    [60, 'second'],
    [3600, 'minute'],
    [86400, 'hour'],
    [2592000, 'day'],
    [31536000, 'month'],
  ];

  for (const [limit, unit] of thresholds) {
    if (Math.abs(diffSec) < limit) {
      const divisor = unit === 'second' ? 1
        : unit === 'minute' ? 60
        : unit === 'hour' ? 3600
        : unit === 'day' ? 86400
        : 2592000;
      return rtf.format(Math.round(diffSec / divisor), unit);
    }
  }

  return rtf.format(Math.round(diffSec / 31536000), 'year');
}

// formatRelative(new Date(Date.now() - 90000), 'en-US') => '2 minutes ago'
// formatRelative(new Date(Date.now() - 90000), 'de-DE') => 'vor 2 Minuten'

Opprette en gjenbrukbar FormattedDate-klientkomponent

Noen ganger trenger du datoformatering på klienten — for eksempel når datoen endres etter en brukerinteraksjon. En tynn klientkomponent som leser navigator.language, unngår hydreringsavvik ved å la nettleseren velge sin egen lokalitet.

Legg merke til 'use client'-direktivet. Selve komponenten leveres uten en Intl-polyfill, fordi alle moderne nettlesere og Node 18+ har innebygd støtte for hele Intl-spesifikasjonen.

'use client';

import { useMemo } from 'react';

interface Props {
  date: string; // ISO string — safe to serialize from Server to Client
  options?: Intl.DateTimeFormatOptions;
}

export function FormattedDate({ date, options = { dateStyle: 'medium' } }: Props) {
  // navigator.language gives the browser's locale, e.g. 'en-GB'
  const formatted = useMemo(() => {
    return new Intl.DateTimeFormat(navigator.language, options).format(
      new Date(date)
    );
  }, [date, options]);

  return <time dateTime={date}>{formatted}</time>;
}

Kunnskapssjekk: Flertallsformer og Intl.PluralRules

Bruk det du har lært i denne leksjonen, og svar på følgende spørsmål om Intl.PluralRules og lokalitetstilpasset flertallsbøying.

Oppsummering: Lokalitetstilpasset formatering i Next.js 15

I denne leksjonen har du lært å bruke de innebygde Intl-API-ene til å formatere datoer, tall, valutaer, relativ tid og strenger med riktig flertallsform for alle lokaliteter — uten behov for tredjepartsbiblioteker.

  • Intl.DateTimeFormat — formaterer datoer og klokkeslett. Bruk dateStyle/timeStyle for rask formatering eller alternativer for enkeltfelter når du trenger detaljert kontroll.
  • Intl.NumberFormat — håndterer vanlige tall, valutaer (style: 'currency') og enheter. Knytt alltid en valutakode sammen med lokaliteten.
  • Intl.RelativeTimeFormat — produserer lokalitetsriktige relative strenger som yesterday eller vor 2 Minuten.
  • Intl.PluralRules — velger CLDR-flertallskategorien (zero / one / two / few / many / other), slik at oversettelseskatalogen kan levere riktig form for hvert språk.
  • I Next.js 15 App Router bør du kjøre disse formateringsfunksjonene i Server Components for å unngå hydreringsavvik og bundle-kostnader. Bruk en 'use client'-omslutning med navigator.language bare når det er nødvendig å finne lokaliteten på klienten.

Disse fire Intl-API-ene dekker de aller fleste behov for i18n-formatering uten at du trenger et tungt bibliotek.

Gratis å komme i gang

Lær deg TypeScript med en AI-veileder – gratis

Skriv og kjør ekte kode i nettleseren, få umiddelbar hjelp fra en AI-veileder som er tilgjengelig døgnet rundt, og fortsett der du slapp – på nettet eller i appen.

Kurs
22
Leksjoner
88

Ofte stilte spørsmål

Er leksjonen «Formatere datoer, tall og flertall etter lokalisering» gratis?

Ja – du kan lese valgfritt 3 av leksjonene i læringsstien Next.js 15 fullstack (App Router + Server Actions), inkludert «Formatere datoer, tall og flertall etter lokalisering», gratis i sin helhet her på nettet. Deretter låser CoddyKit PRO opp alle leksjoner, samt interaktiv øving med en innebygd kodeeditor og en AI-veileder som er tilgjengelig døgnet rundt. Kurset i Next.js 15 fullstack (App Router + Server Actions) inneholder totalt 4 leksjoner.

Hva lærer jeg i «Formatere datoer, tall og flertall etter lokalisering»?

Bruk Intl-API-ene til å gjengi datoer, valutaer og flertallsformer som er korrekte for lokaliseringen. Du øver på Next.js 15 fullstack (App Router + Server Actions) med praktisk kode som du kjører direkte i nettleseren, mens en AI-veileder som er tilgjengelig døgnet rundt, svarer på spørsmålene dine mens du jobber deg gjennom leksjonen.

Trenger jeg erfaring for å begynne med Next.js 15 fullstack (App Router + Server Actions)?

Ingen tidligere erfaring er nødvendig. Next.js 15 fullstack (App Router + Server Actions) på CoddyKit er lagt opp for både nybegynnere og viderekomne, så De kan begynne her eller helt fra start og lære i Deres eget tempo. Dette er leksjon 4 av 4.

Hvor lang tid tar leksjonen «Formatere datoer, tall og flertall etter lokalisering»?

De fleste CoddyKit-leksjoner tar omtrent 5–10 minutter. Hver leksjon er kort og interaktiv, slik at De gjør jevne fremskritt og kan fortsette akkurat der De slapp – både på nettet og i appen.

Kan jeg skrive og kjøre kode i denne Next.js 15 fullstack (App Router + Server Actions)-leksjonen?

Ja. Alle Next.js 15 fullstack (App Router + Server Actions)-leksjoner har en innebygd kodeeditor, slik at De kan skrive og kjøre ekte kode direkte i nettleseren og få umiddelbar tilbakemelding fra AI – uten lokal konfigurering.

Alle leksjonene i dette kurset

  1. Lokalisering og subruting med middleware
  2. Lasting av oversettelser på serversiden og meldingskataloger
  3. Lokalisert metadata, sitemaps og hreflang
  4. Formatere datoer, tall og flertall etter lokalisering
← Tilbake til Next.js 15 fullstack (App Router + Server Actions)