i18n — react-i18next bilan ko'p tilli ilova

~20 daq · React · Ekotizim va Amaliyot

Muammodan boshlaylik

Internet do'kon o'zbek tilida tayyor. Endi rus va ingliz tillari ham kerak. Birinchi fikr:

<button>
  {til === 'uz' ? "Savatga qo'shish" : til === 'ru' ? 'В корзину' : 'Add to cart'}
</button>

300 ta matn uchun shunday — kod o'qib bo'lmaydigan bo'ladi. Va yana muammolar:

  • Ko'plik: "1 ta mahsulot", "5 ta mahsulot" — o'zbekchada oddiy, rus tilida esa uch xil shakl: "1 товар", "3 товара", "5 товаров"
  • O'zgaruvchilar: "Salom, Ali!" — ism jumlaning o'rtasida, har tilda boshqa joyda
  • Sana va son: 07.11.2026 yoki 11/7/2026; 1 500 000 yoki 1,500,000
  • Tarjimon kod ichiga kirmasligi kerak

i18n (internationalization — "i", 18 ta harf, "n") — ilovani ko'p tillilikka tayyorlash. React da eng mashhur vosita — react-i18next.


O'rnatish

npm install i18next react-i18next i18next-browser-languagedetector

Tarjima fayllari

src/locales/
├── uz/translation.json
├── ru/translation.json
└── en/translation.json
// src/locales/uz/translation.json
{
  "header": {
    "katalog": "Katalog",
    "savat": "Savat"
  },
  "mahsulot": {
    "savatga": "Savatga qo'shish",
    "narx": "Narxi: {{narx}} so'm"
  },
  "salom": "Salom, {{ism}}!"
}
// src/locales/ru/translation.json
{
  "header": {
    "katalog": "Каталог",
    "savat": "Корзина"
  },
  "mahsulot": {
    "savatga": "В корзину",
    "narx": "Цена: {{narx}} сум"
  },
  "salom": "Здравствуйте, {{ism}}!"
}

Matnlar koddan ajratilgan. Tarjimon faqat JSON fayllar bilan ishlaydi.

Sozlash

// src/i18n.js
import i18n from 'i18next';
import { initReactI18next } from 'react-i18next';
import LanguageDetector from 'i18next-browser-languagedetector';

import uz from './locales/uz/translation.json';
import ru from './locales/ru/translation.json';
import en from './locales/en/translation.json';

i18n
  .use(LanguageDetector)              // brauzer tili, localStorage
  .use(initReactI18next)
  .init({
    resources: {
      uz: { translation: uz },
      ru: { translation: ru },
      en: { translation: en },
    },
    fallbackLng: 'uz',                // tarjima topilmasa
    supportedLngs: ['uz', 'ru', 'en'],
    interpolation: {
      escapeValue: false,             // React o'zi XSS dan himoya qiladi
    },
  });

export default i18n;
// main.jsx
import './i18n';                      // App dan oldin

useTranslation

import { useTranslation } from 'react-i18next';

function MahsulotKartochka({ mahsulot }) {
  const { t } = useTranslation();

  return (
    <article>
      <h3>{mahsulot.nomi}</h3>
      <p>{t('mahsulot.narx', { narx: mahsulot.narx.toLocaleString() })}</p>
      <button>{t('mahsulot.savatga')}</button>
    </article>
  );
}
  • t('kalit') — joriy tildagi matn
  • Ichma-ich kalitlar — nuqta bilan: 'header.savat'
  • {{narx}} — o'zgaruvchi; t ning ikkinchi argumenti orqali

Til o'zgarganda useTranslation ishlatadigan barcha komponentlar avtomatik qayta render bo'ladi.

Tilni almashtirish

function TilTanlash() {
  const { i18n } = useTranslation();

  return (
    <select
      value={i18n.resolvedLanguage}
      onChange={(e) => i18n.changeLanguage(e.target.value)}
      aria-label="Til"
    >
      <option value="uz">O'zbekcha</option>
      <option value="ru">Русский</option>
      <option value="en">English</option>
    </select>
  );
}

LanguageDetector tanlangan tilni localStorage ga saqlaydi — keyingi tashrifda o'sha til. <html lang> ni ham yangilang (HTML kursi, 2 va 20-darslar):

i18n.on('languageChanged', (til) => {
  document.documentElement.lang = til;
});

Ko'plik shakllari

// uz
{
  "savatda_one": "Savatda {{count}} ta mahsulot",
  "savatda_other": "Savatda {{count}} ta mahsulot"
}
// ru
{
  "savatda_one": "В корзине {{count}} товар",
  "savatda_few": "В корзине {{count}} товара",
  "savatda_many": "В корзине {{count}} товаров",
  "savatda_other": "В корзине {{count}} товара"
}
// en
{
  "savatda_one": "{{count}} item in cart",
  "savatda_other": "{{count}} items in cart"
}
t('savatda', { count: 5 });
// uz: "Savatda 5 ta mahsulot"
// ru: "В корзине 5 товаров"
// en: "5 items in cart"
  • O'zgaruvchi nomi aynan count bo'lishi kerak
  • Qo'shimchalar (_one, _few, _many, _other) — har bir til qoidalari brauzerning Intl.PluralRules dan olinadi
  • Qaysi tilda qaysi shakllar borligini bilish shart emas — i18next to'g'risini tanlaydi

Matn ichida komponentlar — Trans

{
  "shartlar": "Davom etish orqali <link>foydalanish shartlariga</link> rozilik bildirasiz"
}
import { Trans } from 'react-i18next';

<Trans
  i18nKey="shartlar"
  components={{ link: <a href="/shartlar" /> }}
/>

Havola matnning o'rtasida — va har tilda boshqa joyda bo'lishi mumkin. Jumlani t('qism1') + <a> + t('qism2') ga bo'lmang — tarjimon butun jumlani ko'rishi kerak.


Sana va son — Intl

const { i18n } = useTranslation();
const til = i18n.resolvedLanguage;

new Intl.NumberFormat(til).format(1500000);
// uz: "1 500 000", en: "1,500,000"

new Intl.NumberFormat(til, { style: 'currency', currency: 'UZS', maximumFractionDigits: 0 }).format(1500000);

new Intl.DateTimeFormat(til, { dateStyle: 'long' }).format(new Date());
// ru: "7 ноября 2026 г.", en: "November 7, 2026"

new Intl.RelativeTimeFormat(til, { numeric: 'auto' }).format(-1, 'day');
// ru: "вчера", en: "yesterday"

Brauzerning o'rnatilgan Intl API si — qo'shimcha kutubxonasiz (72-dars).

[!INFO] Brauzerlarning o'zbek tili uchun ma'lumotlari (oy nomlari, formatlar) boshqa tillarga qaraganda to'liq bo'lmasligi va brauzerdan brauzerga farq qilishi mumkin. Muhim joylarda natijani tekshiring va kerak bo'lsa o'z formatlash funksiyangizni yozing.


Katta ilovalar uchun

Namespace lar — bo'lish

locales/uz/
├── common.json      // tugmalar, umumiy so'zlar
├── katalog.json
├── savat.json
└── admin.json
const { t } = useTranslation('savat');
t('jami');                               // savat.json dan
t('common:saqlash');                     // boshqa namespace dan

Kerak bo'lganda yuklash

Barcha tillarning barcha tarjimalarini bundle ga qo'shish — ortiqcha hajm. i18next-http-backend bilan tarjimalar public/locales/ dan kerak bo'lganda yuklanadi:

import HttpBackend from 'i18next-http-backend';

i18n.use(HttpBackend).init({
  backend: { loadPath: '/locales/{{lng}}/{{ns}}.json' },
  // ...
});

Yuklanayotganda — Suspense (81-dars).

[!TIP] TypeScript bilan kalitlarni tekshirish mumkin: t('mahsulot.savtga') (xato yozilgan) — kompilyatsiya xatosi beradi. Shuningdek i18next-parser kabi vositalar koddagi barcha t() chaqiruvlarini topib, tarjima fayllarida yetishmayotgan kalitlarni ko'rsatadi.


Nima uchun shunday?

Nima uchun tarjima uchun maxsus kutubxona kerak — oddiy { uz: {...}, ru: {...} } obyekti yetmaydimi?

Oddiy matnlar uchun yetadi. Lekin tillar bir-biridan juda farq qiladi, va bu farqlar kutilmagan joylarda chiqadi. O'zbek tilida ko'plik shakli bitta ("5 ta mahsulot"), ingliz tilida ikkita, rus tilida uchta, arab tilida oltita. So'zlar tartibi har xil: o'zgaruvchi jumlaning boshida, o'rtasida yoki oxirida bo'lishi mumkin. Sanalar, sonlar va valyutalar har bir mintaqada boshqacha yoziladi. i18next kabi kutubxonalar bu qoidalarni — o'nlab yillik lingvistik tajribani — tayyor holda beradi va sizga faqat "nima demoqchi ekanimni" yozish imkonini qoldiradi. "Qanday yozilishi" esa har bir tilning o'z qoidalariga topshiriladi.


Xulosa

Eslab qoling:

  • i18n — matnlarni koddan ajratish: locales/{til}/translation.json
  • i18next + react-i18next + LanguageDetector; fallbackLng, escapeValue: false
  • const { t, i18n } = useTranslation(); t('kalit', { o'zgaruvchi })
  • i18n.changeLanguage('ru'); <html lang> ni yangilang
  • Ko'plik: kalit_one, kalit_few, kalit_many, kalit_other + { count }
  • Matn ichida komponent — <Trans components={{ link: <a /> }}>; jumlalarni bo'lmang
  • Sana va son — Intl.NumberFormat, Intl.DateTimeFormat
  • Katta ilova: namespace lar, i18next-http-backend bilan kerak bo'lganda yuklash

Keyingi dars

87-dars: PWA — React da Progressive Web App