Lokalizacja
Przełącz na dowolny język te nieliczne słowa, które komponenty prfct mówią same od siebie — przyciski zamykania, spinnery, paginację, kalendarze — jednym providerem.
Komponenty prfct nie niosą prawie żadnego tekstu: wszystko, co czytają ludzie, to twoja treść i pozostaje w twoich rękach. Tylko kilka komponentów mówi coś samodzielnie — głównie do czytników ekranu: nazwę przycisku zamykającego okno dialogowe, status spinnera, punkty orientacyjne paginacji i ścieżki nawigacji. LocaleProvider przełącza je wszystkie naraz.
"use client"
import * as React from "react"
import { pl } from "react-day-picker/locale"
import { Calendar } from "@/components/ui/calendar"
import { LocaleProvider, useLocale } from "@/components/ui/locale"
import {
Pagination,
PaginationContent,
PaginationEllipsis,
PaginationItem,
PaginationLink,
PaginationNext,
PaginationPrevious,
} from "@/components/ui/pagination"
import { ToggleGroup, ToggleGroupItem } from "@/components/ui/toggle-group"
const today = new Date(2026, 8, 23)
export default function LocalizationDemo() {
// Start in the language this page isn't in, so the difference shows.
const { locale: page } = useLocale()
const [locale, setLocale] = React.useState(
page.startsWith("pl") ? "en" : "pl"
)
const [date, setDate] = React.useState<Date | undefined>(today)
return (
<div className="flex flex-col items-center gap-6">
<ToggleGroup
variant="outline"
spacing={0}
value={[locale]}
onValueChange={(value) => value[0] && setLocale(value[0])}
aria-label="Język"
>
<ToggleGroupItem value="en" lang="en">
English
</ToggleGroupItem>
<ToggleGroupItem value="pl" lang="pl">
Polski
</ToggleGroupItem>
</ToggleGroup>
<LocaleProvider
locale={locale}
calendar={locale === "pl" ? pl : undefined}
>
<div lang={locale} className="flex flex-col items-center gap-4">
<Calendar
mode="single"
today={today}
defaultMonth={today}
selected={date}
onSelect={setDate}
className="rounded-xl border shadow-raised"
/>
<Pagination>
<PaginationContent>
<PaginationItem>
<PaginationPrevious aria-disabled="true" />
</PaginationItem>
<PaginationItem>
<PaginationLink href="?page=1" isActive>
1
</PaginationLink>
</PaginationItem>
<PaginationItem>
<PaginationLink href="?page=2">2</PaginationLink>
</PaginationItem>
<PaginationItem>
<PaginationEllipsis />
</PaginationItem>
<PaginationItem>
<PaginationNext href="?page=2" />
</PaginationItem>
</PaginationContent>
</Pagination>
</div>
</LocaleProvider>
</div>
)
}Instalacja
$ pnpm dlx shadcn@latest add https://www.prfct.dev/r/locale.jsonKomponenty, które mówią same od siebie, już od niego zależą, więc CLI doda go razem z pierwszym z nich.
Użycie
Owiń aplikację raz, jak najwyżej — zwykle w głównym layoucie:
import { LocaleProvider } from "@/components/ui/locale"
export default function RootLayout({ children }: { children: React.ReactNode }) {
return (
<html lang="pl">
<body>
<LocaleProvider locale="pl">{children}</LocaleProvider>
</body>
</html>
)
}Bez providera komponenty mówią po angielsku. locale to znacznik BCP 47: pl, pl-PL i pl-pl wybierają polskie komunikaty, a pełny znacznik formatuje liczby — podpowiedzi wykresów pokażą 12 345,5 po polsku i 12,345.5 po angielsku.
LocaleProvider decyduje, co mówią komponenty; <html lang> — jak czytnik ekranu to wymówi. Ustaw oba, z tej samej wartości.Wbudowane języki
prfct zawiera angielski i polski. Każdy komunikat napisano z myślą o jego roli — nazwie dostępnej, punkcie orientacyjnym, opisie roli — a nie przetłumaczono słowo w słowo.
Opisy ról (carousel, slide) zaczynają się małą literą, bo czytniki ekranu ogłaszają je w środku zdania: „Wyróżnione realizacje, karuzela”.
Inne języki
Dla każdego innego języka przekaż messages. Pominięte klucze wracają do angielskiego, więc możesz tłumaczyć stopniowo — albo zmienić pojedynczą frazę we wbudowanym języku:
<LocaleProvider
locale="de"
messages={{
close: "Schließen",
loading: "Wird geladen",
previous: "Zurück",
next: "Weiter",
previousPage: "Zur vorherigen Seite",
nextPage: "Zur nächsten Seite",
}}
>
{children}
</LocaleProvider>Komunikaty to zwykłe ciągi znaków. removeItem otrzymuje tekst chipa jako {item}: "Entferne {item}".
Kalendarz
Nazwy miesięcy, dni tygodnia, pierwszy dzień tygodnia i etykiety samego kalendarza pochodzą z jego lokalizacji dat. Zaimportuj ją z react-day-picker/locale i przekaż jako calendar:
"use client"
import { pl } from "react-day-picker/locale"
import { LocaleProvider } from "@/components/ui/locale"
export function Providers({ children }: { children: React.ReactNode }) {
return (
<LocaleProvider locale="pl" calendar={pl}>
{children}
</LocaleProvider>
)
}Lokalizacje dat zawierają funkcje, a tych nie da się przekazać z komponentu serwerowego do klienckiego — dlatego ten provider mieszka we własnym, małym komponencie klienckim. locale przekazane bezpośrednio do Calendar nadal ma pierwszeństwo.
calendar tylko je — nie całą aplikację.Twoja treść
prfct nie narzuca frameworka do tłumaczenia twoich tekstów: użyj next-intl, Lingui albo zwykłych, typowanych słowników — tego, czego twoja aplikacja już używa. Trzymaj język komponentów i język treści w jednym miejscu, żeby nigdy się nie rozjechały.
Ta strona istnieje po angielsku i po polsku — bez żadnej biblioteki:
- Teksty interfejsu żyją w typowanych słownikach. Polska odmiana liczebników korzysta z
Intl.PluralRules, który wie, że 2 to komponenty, a 5 to komponentów. - Każda strona leży w segmencie
[locale]. Angielskie adresy nie mają prefiksu; polskie zaczynają się od/pl. - Strona dokumentacji to osobny plik MDX dla każdego języka. Tłumaczenia zachowują strukturę nagłówków oryginału, więc
#accessibilitydziała w obu wersjach. - Przykłady pisze się raz. Katalog przetłumaczonych ciągów tworzy polską kopię podczas budowania — kod pozostaje identyczny.
Dostępność
- Ustaw
<html lang>na język strony, a fragmenty w innym języku oznacz ich własnymlang, żeby czytnik ekranu zmienił wymowę. - Nazwy dostępne mają być krótkie i konkretne: „Zamknij” nazywa przycisk; „Kliknij tutaj, aby zamknąć to okno” — już nie.
- Testuj z czytnikiem ekranu w każdym języku, który dostarczasz — nazwa, która dobrze brzmi po angielsku, po przetłumaczeniu może stać się dwuznaczna.
Dokumentacja API
LocaleProvider
useLocale
Zwraca { locale, messages, calendar } — dla twoich własnych komponentów, które mówią same od siebie. Poza providerem zwraca angielski.
const { messages } = useLocale()
return <span className="sr-only">{messages.close}</span>