Hardkodirani engleski stringovi mogu biti problem kada vaša React aplikacija počne dobivati korisnike iz drugih zemalja. Umjesto "Save", vaši hrvatski korisnici žele vidjeti "Spremi". "Speichern" je ono što vaši njemački korisnici žele. Tu dolazi i18n, odnosno internacionalizacija.
Radili smo nekoliko tjedana na pripremi prijevoda za produkcijsku aplikaciju koja podržava četiri jezika. Ovaj vodič pokazuje kako postaviti stvari na način koji koristimo svaki dan.
Zašto koristiti i18next?
Postoji nekoliko i18n biblioteka za React, kao što su LinguiJS, react-i18next i react-intl. Postoji nekoliko razloga zašto smo odabrali i18next.
Za početak, postoji od 2011. godine. Dokumentacija je dobra, zajednica je aktivna, a većina rubnih slučajeva je već riješena. Kada smo imali problema s pluralizacijom u slavenskim jezicima, pronašli smo rješenja u minutama.
Druga stvar je da se React integracija osjeća kao da pripada. Korištenje translation hooka savršeno se uklapa u način na koji pišemo moderne komponente. Bez wrappera, bez HOC-ova i bez context gimnastike.
Treće, podrška za namespace pomaže održavati stvari u redu. Prijevode za svaku značajku stavljamo u vlastitu datoteku. Prijevodi za login ostaju u auth.json. dashboard.json je mjesto gdje su prijevodi za dashboard. Dodajemo više namespace-ova aplikaciji kako raste, ali ne mijenjamo one koji već postoje.
Instalacija
npm install i18next react-i18next i18next-browser-languagedetector
Dodajte generator tipova za TypeScript projekte:
npm install -D i18next-resources-for-ts
- i18next – brine se o prebacivanju jezika, interpolaciji varijabli i logici prijevoda.
- react-i18next – omogućuje korištenje translation hooka kako bi komponente dobile prijevode.
- i18next-browser-languagedetector – gleda postavke preglednika i localStorage kako bi pronašao pravi jezik.
- i18next-resources-for-ts – stvara TypeScript tipove iz JSON datoteka kako bi autocomplete i provjera grešaka mogli raditi.
Struktura projekta
Ovako postavljamo prijevode:
src/
├── i18n/
│ └── i18n.ts # Konfiguracija
└── locales/
├── en/
│ ├── common.json # Dijeljeni prijevodi
│ ├── auth.json # Login/signup
│ └── home.json # Home modul
├── hr/ # Ista struktura
└── de/ # Ista struktura
Postoji zasebna mapa za svaki jezik. Iste JSON datoteke s istim ključevima su u svakoj mapi. Te JSON datoteke nazivamo "namespace-ovima". Common namespace ima prijevode za riječi koje se koriste posvuda, kao "Save", "Cancel" i "Delete". Feature namespace-ovi drže samo prijevode za tu značajku.
Konfiguracija
U src/i18n/i18n.ts, upišite sljedeće:
import { initReactI18next } from 'react-i18next';
import i18n from 'i18next';
import LanguageDetector from 'i18next-browser-languagedetector';
import authEn from '@/locales/en/auth.json';
import commonEn from '@/locales/en/common.json';
import authHr from '@/locales/hr/auth.json';
import commonHr from '@/locales/hr/common.json';
i18n
.use(LanguageDetector)
.use(initReactI18next)
.init({
resources: {
en: {
common: commonEn,
auth: authEn,
},
hr: {
common: commonHr,
auth: authHr,
},
},
fallbackLng: 'en',
defaultNS: 'common',
ns: ['common', 'auth'],
detection: {
order: ['localStorage', 'navigator'],
caches: ['localStorage'],
},
});
Kada prijevod nije dostupan, fallbackLng je jezik koji se koristi. Ako zaboravimo prevesti jedan string i netko prebaci na njemački, vidjet će engleski umjesto pokvarenog ključa.
Detektor zna gdje prvo tražiti zbog redoslijeda detekcije. Prvo gledamo localStorage (tako da korisnici koji se vraćaju mogu zadržati svoj izbor), a zatim gledamo postavke preglednika.
Prije renderiranja bilo čega, importajte ovu datoteku u entry point vaše aplikacije.
Datoteke prijevoda
Datoteke prijevoda su obični JSON:
// en/common.json
{
"save": "Save",
"cancel": "Cancel",
"welcome": "Welcome back, {{name}}!",
"items": "You have {{count}} item",
"items_other": "You have {{count}} items",
"errors": {
"required": "This field is required"
}
}
// hr/common.json
{
"save": "Spremi",
"cancel": "Odustani",
"welcome": "Dobrodošli natrag, {{name}}!",
"items": "Imate {{count}} stavku",
"items_few": "Imate {{count}} stavke",
"items_other": "Imate {{count}} stavki",
"errors": {
"required": "Ovo polje je obavezno"
}
}
Placeholderi za varijable su dvostruke vitičaste zagrade, kao {{name}}. Nastavci _one, _few i _other koriste se za pluralizaciju. Engleski ima dva oblika: jedninu i množinu. Hrvatski ima tri: 1, 2–4 i 5+. i18next odabire pravi oblik na temelju vrijednosti count.
errors.required i drugi ugniježđeni objekti drže povezane prijevode zajedno. Koristite dot notaciju da im pristupite.
Dohvaćanje prijevoda
import { useTranslation } from 'react-i18next';
function Dashboard({ user, notifications }) {
const { t } = useTranslation();
return (
<div>
<h1>{t('welcome', { name: user.firstName })}</h1>
<p>{t('items', { count: notifications.length })}</p>
<span>{t('errors.required')}</span>
<button>{t('save')}</button>
</div>
);
}
Više namespace-ova
Kada komponenta treba prijevode iz više namespace-ova:
const { t } = useTranslation(['dashboard', 'common']);
t('chart-title'); // iz 'dashboard' (prvi = default)
t('common:save'); // iz 'common' (prefiks je potreban)
Prekidač jezika
function LanguageSwitcher() {
const { i18n } = useTranslation();
return (
<select value={i18n.language} onChange={e => i18n.changeLanguage(e.target.value)}>
<option value="en">English</option>
<option value="hr">Hrvatski</option>
</select>
);
}
Funkcija changeLanguage() mijenja jezik i sprema ga u localStorage. Svojstvo i18n.language govori vam koji jezični kod trenutno koristite.
TypeScript podrška
Ako nemate tipove, greška u tipkanju poput t('sav') neće javiti poruku. Možete vidjeti sirovi ključ u UI-ju. TypeScript compiler pronalazi te greške.
npx i18next-resources-for-ts
Gleda vašu en/ mapu i stvara definicije tipova. Vaš IDE sada automatski popunjava ključeve prijevoda. Nakon dodavanja ili preimenovanja ključeva, pokrenite ovo. Kao "i18n:types", stavljamo to u naše npm skripte.
Dodavanje više jezika
Kada dodajete novu značajku, napravite zasebni namespace za nju:
- Napravite
invoices.jsonu svakoj locale mapi s istim ključevima. - Za svaki jezik, importajte datoteke u
i18n.ts. - Dodajte ih u resources objekt za taj jezik.
- Dodajte
"invoices"unsarray. - Pokrenite TypeScript generator.
Dodavanje ključeva u postojeće namespace-ove je lakše: samo dodajte ključ u sve locale JSON datoteke i zatim regenerirajte tipove.
Najčešći problemi i rješenja
Neki jezici nemaju ključeve
Dodali smo gumb, preveli ga na engleski i poslali van. Tjedan dana kasnije, hrvatski korisnici su vidjeli "delete-permanently" kao običan tekst. Rješenje je ažurirati sve locale datoteke odjednom. To se ne događa zbog pre-commit hooka koji provjerava podudaranje ključeva među localima.
Nazivi ključeva koji nisu jasni uzrokuju probleme
Kada imate 50 ključeva, teško je pratiti one poput "title". Promijenili smo nazive da budu opisniji, poput "invoice-page-title" i "user-settings-title". Dulje ključeve je lakše pretraživati i razumjeti.
Stringovi koji su hardkodirani i nikad se ne prevode
Lažemo sami sebi kada kažemo: "Dodat ćemo prijevod kasnije." Sada je svaki string koji korisnici vide umotan u t() od početka, čak i kada je još u fazi prototipa.
Prijevodi rade na vašem računalu ali ne na serveru
Import i18n je nedostajao iz entry pointa. Prije nego što se aplikacija može prikazati, konfiguracijska datoteka mora biti importana. Provjerite main.tsx ili index.tsx ponovno.
Ljudi ne primjećuju kada postoje greške u tipkanju ključeva prijevoda
Ako ne koristite TypeScript tipove, t('sav') će prikazati sirovi ključ umjesto "Save". Postavljanje generiranja tipova traje samo pet minuta, ali štedi sate debugiranja. Nakon promjena u datotekama prijevoda, pokrenite:
npx i18next-resources-for-ts







