Mundarija (34)
- Bu darsda
- 1. Nega bu kerak?
- 2. Tuzilma
- 2.1 [locale] segmenti
- 2.2 routing.ts
- 2.3 request.ts va next/root-params
- 3. Tarjima xabarlari
- 3.1 JSON fayllar
- 3.2 Serverda — getTranslations
- 3.3 Client komponentlarga — prop bilan
- 4. Navigatsiya va til almashtirgich
- 4.1 Tilni biladigan Link
- 4.2 Natija oynasida: o'z Link va til almashtirgich
- 4.3 Biz topdik: <Link locale> va ikkita header
- 5. Metadata va SEO
- 5.1 hreflang
- 5.2 Tuzoq takrorlandi: openGraph.locale
- 5.3 Kontent tili o'zbekcha qoladi
- 6. Proxy va Server Action
- 6.1 Proxy: ikki vazifa bitta faylda
- 6.2 Server Action'da til
- 7. Portfolio qadami: ikki til va commit
- 7.1 Asosiy o'zgarishlar (holat 34)
- 7.2 Tekshiruv
- 7.3 Commit
- 8. Ko'p uchraydigan xatolar
- 9. Mashqlar
- 1-mashq (oson): manzillar
- 2-mashq (o'rta): rus tili
- 3-mashq (qiyin): kalitlar mosligi
- 4-mashq: Portfolio qadami
- 10. Real ishda
- Xulosa
- Manbalar
Next.js i18n: next-intl bilan [locale] segmenti, tarjimalar va hreflang
Qisqacha: Next.js'da til URL'da yashaydi:
app/[locale]/…papkasi/haqimda(o'zbekcha) va/en/haqimda(inglizcha) ni bitta kod bilan beradi.next-intluch qismdan iborat:routing(qaysi tillar, prefiks qanday), proxy (manzilni tilga moslash) vagetTranslations(serverda tarjima xabarlari). Hamma tarjima serverda bajarilsa, brauzerga tarjima kodi umuman bormaydi va sahifalar statik qoladi. Har sahifaga canonical vahreflangberiladi — Google qaysi til qaysi manzilda ekanini biladi.
Bu darsda
portfolioga ingliz tilini qo'shasiz:[locale]segmenti,routing,request.ts, tarjima fayllari.localePrefix: "as-needed"valocaleDetection: falsenima berishini haqiqiy javoblar bilan ko'rasiz.- Tarjimani serverda olib, client komponentlarga prop bilan berasiz — va bu brauzer JS'iga qanday ta'sir qilishini o'lchov bilan bilasiz.
- Har sahifaga hreflang va til bo'yicha Open Graph qo'shasiz.
- Uchta haqiqiy tuzoqni bilasiz:
<Link locale>ikki header,openGraph.localealmashishi, Server Action'da til.
Oldin bilishingiz kerak: i18n I: react-i18next, proxy.ts, Metadata API, generateStaticParams, Navigatsiya.
1. Nega bu kerak?
i18n I darsida vazifalar ni react-i18next bilan ko'p tilli qilgansiz. U SPA edi: til localStorage da, tarjimalar brauzerda yuklanadi, URL bir xil. SEO uchun bu yomon: Google /haqimda ni bir marta ko'radi — qaysi tilda?
portfolio — ochiq sayt, uni xorijiy ish beruvchi ham o'qishi mumkin. Talablar:
- har tilning o'z manzili:
/haqimdava/en/haqimda— ulashsa, o'sha tilda ochiladi; - o'zbekcha — prefikssiz (asosiy auditoriya), inglizcha —
/en; - sahifalar statik qoladi (
○), brauzerga ortiqcha JS ketmaydi; - Google ikki versiya bir-birining tarjimasi ekanini bilsin (
hreflang).
Hayotiy o'xshatish: ikki tilli muzey. Har zalda bitta eksponat, lekin ikki xil yozuv taxtachasi. Kirishda — til tanlovi, va zal raqamida til belgisi: «12» — o'zbekcha yo'lak, «EN-12» — inglizcha. Eksponat (komponent kodi) bitta, taxtachalar (tarjima fayllari) ikkita.
2. Tuzilma
2.1 [locale] segmenti
Root layout endi app/[locale]/layout.tsx da — chunki <html lang> tilga bog'liq:
src/
├─ app/
│ ├─ [locale]/
│ │ ├─ layout.tsx ← root layout, <html lang={til}>
│ │ ├─ not-found.tsx
│ │ ├─ error.tsx
│ │ ├─ [...yoq]/page.tsx ← mos kelmagan yo'l → notFound()
│ │ └─ (sayt)/… ← hamma sahifalar
│ ├─ sitemap.ts, robots.ts, manifest.ts, opengraph-image.tsx
│ ├─ blog/rss.xml/route.ts, api/…
├─ i18n/
│ ├─ routing.ts, request.ts, navigation.ts
│ ├─ metadata.ts, yol.ts, tur.d.ts
└─ proxy.ts
messages/
├─ uz.json, en.json ← 106 kalit, ikkalasida bir xilTilga bog'liq bo'lmagan fayllar (sitemap, RSS, API) root'da qoldi. [...yoq] — hammasini ushlovchi segment (catch-all): /en/umuman-yoq kabi yo'llar ham til layout'i bilan 404 sahifaga tushishi uchun (next-intl hujjatidagi naqsh).
2.2 routing.ts
import { defineRouting } from "next-intl/routing";
export const routing = defineRouting({
locales: ["uz", "en"],
defaultLocale: "uz",
localePrefix: "as-needed",
localeDetection: false,
});| Sozlama | Ma'nosi |
|---|---|
localePrefix: "as-needed" |
sukut tili prefikssiz (/haqimda), boshqasi /en/… |
localeDetection: false |
brauzer tiliga qarab yo'naltirmaslik, NEXT_LOCALE cookie yo'q |
localeDetection: false — ongli qaror. Yoqilsa, next-intl Accept-Language sarlavhasini o'qib, ingliz tilidagi brauzerni /en ga yo'naltiradi va tanlovni cookie'ga yozadi. Natija: sahifa javobi so'rovga bog'liq — cookie banner kerak bo'lishi mumkin, kesh murakkablashadi. Kanon sodda yo'lni tanladi: til faqat URL'dan, foydalanuvchi o'zi almashtiradi.
Haqiqiy natija (natijalar/34-i18n.txt): /uz/… ga kirgan odam kanonik manzilga yo'naltiriladi:
/uz: 307 /
/uz/haqimda: 307 /haqimda2.3 request.ts va next/root-params
request.ts — next-intl har so'rovda qaysi til va qaysi xabarlarni ishlatishini shu yerdan oladi:
import { hasLocale } from "next-intl";
import { getRequestConfig } from "next-intl/server";
import { notFound } from "next/navigation";
import * as rootParams from "next/root-params";
import { routing } from "./routing";
export default getRequestConfig(async ({ locale }) => {
if (!locale) {
const qiymat = await rootParams.locale();
if (!hasLocale(routing.locales, qiymat)) notFound();
locale = qiymat;
}
return {
locale,
messages: (await import(`../../messages/${locale}.json`)).default,
};
});next/root-params — Next 16.3 da qo'shilgan API: root segment parametri ([locale]) ni istalgan Server Component'dan (layout, sahifa, ichma-ich komponent) prop'siz o'qish (rootParams.locale()). Hujjatga ko'ra u client komponentda, Server Action'da va Route Handler'da ishlamaydi — «Server Action'da til» bo'limida buning oqibatini ko'ramiz. Avval next-intl'da har sahifa boshida setRequestLocale(locale) yozish kerak edi — aks holda statik render buzilardi. Endi kerak emas. hasLocale — "de" kabi noma'lum tilni ushlaydi.
next.config.ts da plagin ulanadi:
import createNextIntlPlugin from "next-intl/plugin";
const withNextIntl = createNextIntlPlugin("./src/i18n/request.ts");
export default withNextIntl(nextConfig);Statik render uchun root layout ikkala tilni build'ga beradi:
export function generateStaticParams() {
return routing.locales.map((locale) => ({ locale }));
}Build jadvali (tekshiruv-34.txt, ko'chirma):
├ /[locale]/haqimda
│ ├ ◐ /[locale]/haqimda
│ ├ ○ /uz/haqimda 1h 1d
│ └ ○ /en/haqimda 1h 1dIkkala til ham ○ — build'da tayyor. So'rov yo'li:
flowchart LR
A["GET /haqimda"] --> P["proxy: slug tekshiruvi,<br/>next-intl"]
P -->|"rewrite /uz/haqimda"| L["[locale]/layout<br/>lang=uz"]
L --> T["getTranslations<br/>messages/uz.json"]
B["GET /en/haqimda"] --> P
P -->|"o'zgarishsiz"| L2["[locale]/layout<br/>lang=en"]
L2 --> T2["messages/en.json"]Ikkala yo'l ham build'da tayyorlangan HTML'ga tushadi — server tarjimani har so'rovda qayta bajarmaydi. Jami 63 sahifa prerender. /haqimda manzili ichkarida /uz/haqimda ga rewrite qilinadi (proxy).
3. Tarjima xabarlari
3.1 JSON fayllar
messages/uz.json va en.json — bir xil kalitlar, guruhlarga (namespace) bo'lingan:
{
"Menyu": {
"nomi": "Asosiy menyu",
"loyihalar": "Loyihalar",
"boshqaTil": "English"
},
"Loyihalar": {
"kirish": "{soni} ta loyiha: har birida qisqa tavsif, sana va texnologiyalar."
}
}{soni} — ICU formatidagi o'zgaruvchi (react-i18next dagi {{soni}} ning bitta qavsli ko'rinishi). 106 kalit ikkala faylda bir xil — kanon buni skript bilan tekshirdi.
3.2 Serverda — getTranslations
export default async function LoyihalarSahifasi() {
const t = await getTranslations("Loyihalar");
return (
<main id="asosiy">
<h1>{t("sarlavha")}</h1>
<p>{t("kirish", { soni: LOYIHALAR.length })}</p>
</main>
);
}Server Component'da tarjima — oddiy funksiya chaqiruvi. Natija HTML'ga tayyor matn bo'lib tushadi; tarjima kodi ham, JSON ham brauzerga bormaydi.
Type-safe kalitlar — tur.d.ts (react-i18next dagi declare module ning next-intl versiyasi):
declare module "next-intl" {
interface AppConfig {
Locale: (typeof routing.locales)[number];
Messages: typeof uz;
}
}Endi t("Menyu.loyhalar") (xato kalit) — tsc xatosi, saytda bo'sh joy emas.
3.3 Client komponentlarga — prop bilan
Interaktiv komponentlar (TemaTanlov, TegFiltri, AloqaFormasi …) brauzerda ishlaydi. Ularda useTranslations ishlatsa bo'ladi — lekin buning uchun hamma xabarlar brauzerga yuborilishi kerak. Kanon boshqa yo'l tanladi: matnni serverda olib, prop bilan beradi:
// Server Component (SaytSarlavhasi)
<TilAlmashtirgich yorliq={t("til")} matn={t("boshqaTil")} />Client komponent faqat ikkita satr oladi — butun lug'at emas. Bu Server va Client kompozitsiyasi qoidasining o'zi: ma'lumotni serverda tayyorlab, client'ga minimal narsa berish.
Tekshirib ko'ring:
AloqaFormasida 15 ta matn bor (yorliqlar, xatolar, tugma). Prop bilan berish noqulay emasmi? Muqobil nima?
Javob
Noqulayroq, lekin arzon: 15 satr — bir necha yuz bayt. Muqobil — NextIntlClientProvider messages={pick(messages, "Aloqa")}: faqat bitta namespace'ni brauzerga berib, formada useTranslations("Aloqa"). Bu ham to'g'ri yo'l. Lekin u next-intl'ning client kodini (IntlMessageFormat) brauzerga olib keladi — keyingi bo'limdagi o'lchov uning narxini ko'rsatadi.
4. Navigatsiya va til almashtirgich
4.1 Tilni biladigan Link
/en/haqimda sahifasidagi menyuda <Link href="/loyihalar"> — /loyihalar ga (o'zbekchaga) olib boradi. Kerakli manzil — /en/loyihalar. Prefiksni qo'shadigan toza funksiya (src/i18n/yol.ts):
const DEFAULT = "uz";
function tilYoli(til, yol) {
if (til === DEFAULT) return yol;
return yol === "/" ? `/${til}` : `/${til}${yol}`;
}
console.log(tilYoli("uz", "/loyihalar")); // /loyihalar
console.log(tilYoli("en", "/loyihalar")); // /en/loyihalar
console.log(tilYoli("en", "/")); // /en
console.log(tilYoli("uz", "/")); // // uchun alohida shart: /en/ emas, /en (oxirida slash'siz — canonical bilan bir xil).
Holat 34 da kanon next-intl'ning tayyor yechimini ishlatdi — createNavigation(routing): u Link, usePathname, useRouter ning tilni biladigan versiyalarini beradi. Bu client komponentlar uchun next-intl konteksti talab qildi. Birinchi build'da sababi yozilmagan Error: chiqdi, dev'da esa aniq xabar:
No intl context foundYechim — root layout'da <NextIntlClientProvider messages={null}>: kontekst bor, xabarlar yo'q. Narx o'lchandi:
- holat 33 (bir til)146,5 KB
- holat 34: next-intl Link+15.2161,7 KB
- holat 38: o'z Link−12.1149,6 KB
Manba: IlmHamroh kanoni, portfolio, headless Chrome 155, next start; tekshiruv-33, tekshiruv-34, tekshiruv-38, 2026-10-21
+15.2 KB — next-intl'ning client qismi (IntlMessageFormat), garchi xabarlar null bo'lsa ham. Kanon keyinroq (Next.js performance darsida) createNavigation ni ~30 qatorli o'z Link va usePathname bilan almashtirdi: next/link + tilYoli. Tarjima (getTranslations) serverda qoldi.
Kursdagi yakuniy qaror: next-intl — faqat serverda, navigatsiya — o'zimizniki. createNavigation esa shu darsdagi tajriba bo'lib qoladi: u ishlaydi, lekin narxini o'lchab, undan voz kechdik.
4.2 Natija oynasida: o'z Link va til almashtirgich
O'z Link ning g'oyasini next/link o'quv modeli bilan ko'ramiz. Model sozlamasi: ih.boshla("/en", [...shablonlar]) — /en sahifasidan boshlaymiz, useParams esa [locale] ni shablondan oladi (bu Next API emas — modelning tugmasi). Modelda manzil satri yo'q — joriy yo'lni ekranga chiqaramiz:
import NextLink from "next/link";
import { ih, useParams, usePathname } from "next/navigation";
import type { ReactNode } from "react";
ih.boshla("/en", ["/[locale]/loyihalar", "/[locale]"]);
const TILLAR = ["uz", "en"];
const MATN: Record<string, Record<string, string>> = {
uz: {
bosh: "Bosh sahifa",
loyihalar: "Loyihalar",
boshqa: "English",
},
en: { bosh: "Home", loyihalar: "Projects", boshqa: "O'zbekcha" },
};
function tilYoli(til: string, yol: string) {
if (til === "uz") return yol;
return yol === "/" ? `/${til}` : `/${til}${yol}`;
}
function useTil(): string {
const t = useParams().locale;
return typeof t === "string" && TILLAR.includes(t) ? t : "uz";
}
// Tilni biladigan Link (kanondagi holat 38 g'oyasi)
type LinkProps = { href: string; children: ReactNode };
function Link({ href, children }: LinkProps) {
const til = useTil();
return <NextLink href={tilYoli(til, href)}>{children}</NextLink>;
}
// Prefikssiz yo'l: /en/loyihalar → /loyihalar
function useIchkiYol(): string {
const yol = usePathname();
const m = /^\/(en|uz)(\/.*)?$/.exec(yol);
return m ? (m[2] ?? "/") : yol;
}
function Switcher() {
const til = useTil();
const boshqa = til === "en" ? "uz" : "en";
const href = tilYoli(boshqa, useIchkiYol());
return <NextLink href={href}>{MATN[til].boshqa}</NextLink>;
}
export default function App() {
const til = useTil();
return (
<>
<nav>
<Link href="/">{MATN[til].bosh}</Link>{" "}
<Link href="/loyihalar">{MATN[til].loyihalar}</Link>{" "}
<Switcher />
</nav>
<p>Manzil: {usePathname()}</p>
<p>Til: {til} | ichki yo'l: {useIchkiYol()}</p>
</>
);
}Ekranda:
Bosh sahifa Loyihalar English
Manzil: /loyihalar
Til: uz | ichki yo'l: /loyihalarQadamma-qadam:
| Qadam | Manzil | Menyu tili | Almashtirgich |
|---|---|---|---|
| ochildi | /en |
Home, Projects | O'zbekcha → / |
| «Projects» | /en/loyihalar |
inglizcha | O'zbekcha → /loyihalar |
| «O'zbekcha» | /loyihalar |
o'zbekcha | English → /en/loyihalar |
Almashtirgich o'sha sahifaning boshqa tildagi nusxasiga olib bordi — bosh sahifaga emas. useTil dagi TILLAR.includes tekshiruvi — hasLocale ning kichik nusxasi: modelda /loyihalar ni /[locale] shabloni "locale = loyihalar" deb o'qiydi, tekshiruv uni sukut tiliga qaytaradi. Haqiqiy Next'da proxy /loyihalar ni /uz/loyihalar ga rewrite qiladi va locale doim to'g'ri.
4.3 Biz topdik: <Link locale> va ikkita header
Modelda almashtirgich — <Link>. Kanonda esa — oddiy <a href>. Nega? Holat 34 ning oraliq variantida almashtirgich next-intl'ning <Link locale="en"> edi. Client navigatsiyada root layout almashadi ([locale] — root segment). Next 16.4 eski daraxtni tashlamadi (natijalar/tajriba/34-link-ikki-header.txt):
almashgandan keyin: ["en|BODY|Aziz KarimovHomeAboutProjectsB|hidden=0","en|BODY|Aziz KarimovBosh sahifaMen haq|hidden=0"] | navigatsiya turi: /loyihalar/vazifalar
# ya'ni: hujjat yangi yuklanmagan (navigation entry eski URL), <body> da ikkala til header'i — eski daraxt tashlanmagan<body> da ikkita header: inglizcha va o'zbekcha. Tuzatish — oddiy <a href={tilYoli(boshqa, yol)}>: til almashganda sahifa to'liq yuklanadi. Keyin (natijalar/34-header.txt):
almashgandan keyin: ["en|BODY|Aziz KarimovHomeAboutProjectsB|hidden=0"] | navigatsiya turi: /en/loyihalar/vazifalarBitta header. Til almashtirish kamdan-kam bo'ladi — to'liq yuklashning narxi sezilmaydi, ishonchlilik esa muhimroq. Kanon komponenti hrefLang va lang atributlarini ham qo'yadi — ekran o'quvchi "English" so'zini inglizcha talaffuz qiladi.
5. Metadata va SEO
5.1 hreflang
hreflang (SPA uchun SEO) — Google'ga "bu sahifaning boshqa tildagi nusxasi mana bu manzilda" deyish. Kanon yordamchisi (src/i18n/metadata.ts) har sahifa uchun canonical va tillar ro'yxatini yasaydi. Uning mantiqini toza JS'da:
const TILLAR = ["uz", "en"];
const DEFAULT = "uz";
const tilYoli = (til, yol) =>
til === DEFAULT ? yol : yol === "/" ? `/${til}` : `/${til}${yol}`;
function alternativlar(joriyTil, yol) {
const languages = {};
for (const l of TILLAR) languages[l] = tilYoli(l, yol);
languages["x-default"] = tilYoli(DEFAULT, yol);
return { canonical: tilYoli(joriyTil, yol), languages };
}
console.log(alternativlar("en", "/haqimda"));Konsolda:
{
canonical: '/en/haqimda',
languages: { uz: '/haqimda', en: '/en/haqimda', 'x-default': '/haqimda' }
}x-default — "til mos kelmasa, shu". Kanonda til rootParams.locale() dan olinadi, sahifada esa: alternates: await alternativlar("/haqimda"). Haqiqiy natija (natijalar/34-i18n.txt, qisqartirilgan):
/en/haqimda 200 {"lang":"en","title":"About me — Aziz Karimov","canonical":"https://example.com/en/haqimda","hreflang":"uz=/haqimda en=/en/haqimda x-default=/haqimda","ogLocale":"en_US"}Sitemap'da ham: 22 URL, 44 hreflang havolasi.
5.2 Tuzoq takrorlandi: openGraph.locale
Root layout openGraph: { siteName, locale: "en_US" } beradi, loyiha sahifasi esa o'z openGraph ini. Metadata API darsidagi tuzoq qaytdi: sahifaning openGraph i layout'nikini almashtirdi — inglizcha sahifada og:locale yo'qoldi. Yechim ham o'sha: OG_ASOS o'rniga endi funksiya — ogAsos(), joriy tildan og:locale ni yasaydi va har openGraph da yoyiladi:
export async function ogAsos() {
const til = await rootParams.locale();
const locale = til === "en" ? "en_US" : "uz_UZ";
return { siteName: "Aziz Karimov", locale };
}5.3 Kontent tili o'zbekcha qoladi
Menyu, sarlavhalar, forma — tarjima qilindi. Loyiha va blog matni esa faqat o'zbekcha: tarjimasi — alohida katta ish. Kursdagi qaror: /en sahifalarida bu matn o'zbekcha qoladi, lekin halol belgilanadi:
- matn bloki
lang="uz"ichida — ekran o'quvchi uni o'zbekcha talaffuz qiladi (WCAG 3.1.2, "qismlar tili"); - ustida ko'rinadigan izoh: "This text is in Uzbek.";
- oy nomlari —
oyniYoz(sana, til): ChromeIntlo'zbek lotinini to'g'ri chiqarmaydi (21-qism topilmasi), shuning uchun qo'lda jadval.
Tarjima — sizning mashqingiz (4-mashq).
6. Proxy va Server Action
6.1 Proxy: ikki vazifa bitta faylda
proxy.ts dagi slug tekshiruvi endi /en/ prefiksini ham taniydi, keyin next-intl'ning proxy funksiyasiga uzatadi:
const tilYonaltir = createMiddleware(routing);
const SLUGLI = /^(?:\/(en|uz))?\/(loyihalar|blog)\/([a-z0-9-]+)(?:\/rasm)?$/;
export async function proxy(request: NextRequest) {
const m = SLUGLI.exec(request.nextUrl.pathname);
if (m) {
const [, til = routing.defaultLocale, bolim, slug = ""] = m;
const bormi = /* … LOYIHALAR yoki postlar … */ true;
if (!bormi) {
const ichki = new URL(`/${til}/_topilmadi`, request.url);
return NextResponse.rewrite(ichki);
}
}
return tilYonaltir(request);
}
export const config = {
matcher: "/((?!api|_next|opengraph-image|.*\\..*).*)",
};createMiddleware — nomi eski (next-intl hali "middleware" deydi), ishi proxy. U /haqimda ni ichkarida /uz/haqimda ga rewrite qiladi, /uz/… ni 307 bilan prefikssizga yo'naltiradi. Matcher endi "inkor" shaklida: API, _next, OG rasm va nuqtali har qanday fayl proxy'dan o'tmaydi — next/image darsidagi saboq.
404 natijalari (34-i18n.txt):
404 /en/loyihalar/yoq: 404 lang=en h1=Page not found
404 /blog/yoq: 404 lang=uz h1=Sahifa topilmadi
404 /en/umuman-yoq: 404 lang=en h1=Page not found404 sahifasi ham to'g'ri tilda.
6.2 Server Action'da til
Hujjat bo'yicha next/root-params Server Action ichida ishlamaydi: action — sahifa render'i emas, alohida POST. Kanon yechimi — formada yashirin maydon:
<input type="hidden" name="til" value={til} />Action'da u tekshiriladi (forma ma'lumoti — foydalanuvchi kiritgan narsa):
const kelganTil = String(forma.get("til") ?? "");
const locale = hasLocale(routing.locales, kelganTil)
? kelganTil
: routing.defaultLocale;
const tx = await getTranslations({ locale, namespace: "Xato" });Zod xabarlari ham endi matn emas, tarjima kaliti: sxema "ismQisqa" qaytaradi, action uni tx(kalit) bilan tilga aylantiradi. Inglizcha forma natijasi:
en server xatolari: ["Please check the highlighted fields.","Please enter your name (at least 2 letters)","Email address is invalid","Message must be at least 10 characters"]
en yuborildi: Thank you, John! Your message was received — I usually reply within a day.7. Portfolio qadami: ikki til va commit
7.1 Asosiy o'zgarishlar (holat 34)
| Qism | Fayllar |
|---|---|
| Paket | next-intl 4.14.9 (--save-exact) |
| Sozlama | src/i18n/routing.ts, request.ts, tur.d.ts, next.config.ts plagini |
| Tuzilma | sahifalar app/[locale]/(sayt)/ ga; [locale]/layout.tsx — root layout |
| Matnlar | messages/uz.json, en.json (106 kalit); sahifalarda getTranslations |
| Navigatsiya | navigation.ts (createNavigation), TilAlmashtirgich — <a href> |
| SEO | metadata.ts (alternativlar, ogAsos), sitemap'da ikki til |
| Proxy | slug tekshiruvi + createMiddleware(routing) |
| Forma | yashirin til, Zod — kalitlar |
7.2 Tekshiruv
npm run lint
npm run tip
npm run build
npm run startKanon o'lchovi: 63 sahifa prerender, axe 0 (9 sahifa), hydration 0; regressiya (filtr, modal, tema, forma) — holat 23 bilan bir xil. JS: / 161.7 KB gzip (+15.2 — next-intl client qismi; holat 38 da −12.1).
7.3 Commit
git add -A
git commitCommit (kanon): i18n: next-intl (uz prefikssiz, /en), root-params, tarjima xabarlari, hreflang, til almashtirgich.
8. Ko'p uchraydigan xatolar
| Belgi | Sabab | Davosi |
|---|---|---|
No intl context found |
next-intl client hook'i provider'siz | NextIntlClientProvider yoki o'z Link |
/en sahifalari ƒ bo'lib qoldi |
Til headers()/cookie'dan |
generateStaticParams + next/root-params |
Menyu havolasi /en dan o'zbekchaga olib ketadi |
Oddiy next/link |
Tilni biladigan Link (tilYoli) |
| Til almashganda ikki header | <Link> bilan root layout almashdi |
<a href> — to'liq yuklash |
og:locale yo'q |
Sahifa openGraph i almashtirdi |
...(await ogAsos()) |
| Forma xatolari doim o'zbekcha | Action'da root-params yo'q |
Yashirin til + hasLocale |
| Bitta tilda kalit yo'q — bo'sh joy | JSON fayllar farq qiladi | AppConfig turlari + kalit tekshiruvi skripti |
/en/umuman-yoq — tilsiz 404 |
Hammasini ushlovchi segment yo'q | [locale]/[...yoq]/page.tsx → notFound() |
9. Mashqlar
Mashqlar kurs/mashqlar/23/34-next-intl/ da. Jonli yechimlarni «Ishga tushir» bilan sinang.
1-mashq (oson): manzillar
localePrefix: "as-needed", sukut uz:
- O'zbekcha «Men haqimda» —
- Inglizcha «Men haqimda» —
/uz/haqimdaga kirilsa — 307 bilan- Inglizcha bosh sahifa —
Yechim
- /haqimda — sukut tili prefikssiz.
- /en/haqimda — boshqa til prefiks bilan.
- /haqimda — bir sahifaning ikki manzili bo'lmasligi uchun next-intl kanonik manzilga yo'naltiradi.
- /en —
/en/emas: oxirida slash yo'q, canonical bilan bir xil.
2-mashq (o'rta): rus tili
tilYoli va alternativlar ni uch tilga kengaytiring: ["uz", "ru", "en"]. /loyihalar uchun rus tilidagi sahifaning alternativlarini chiqaring. Keyin bitta qo'shimcha: x-default ingliz tili bo'lsin (xorijiy auditoriya uchun) — natija qanday o'zgaradi?
Yechim
const TILLAR = ["uz", "ru", "en"];
const DEFAULT = "uz";
const tilYoli = (til, yol) =>
til === DEFAULT ? yol : yol === "/" ? `/${til}` : `/${til}${yol}`;
function alternativlar(joriyTil, yol, xDefault = DEFAULT) {
const languages = {};
for (const l of TILLAR) languages[l] = tilYoli(l, yol);
languages["x-default"] = tilYoli(xDefault, yol);
return { canonical: tilYoli(joriyTil, yol), languages };
}
console.log(alternativlar("ru", "/loyihalar"));
const enSukut = alternativlar("ru", "/loyihalar", "en");
console.log(enSukut.languages["x-default"]);Konsolda:
{
canonical: '/ru/loyihalar',
languages: {
uz: '/loyihalar',
ru: '/ru/loyihalar',
en: '/en/loyihalar',
'x-default': '/loyihalar'
}
}
/en/loyihalarYangi til — TILLAR ga bitta qator va bitta JSON fayl; funksiyalar o'zgarmaydi. x-default — sukut tilidan alohida qaror: sayt o'zbekcha prefikssiz bo'lishi mumkin, lekin tilni bilmaydigan mehmon uchun inglizcha yaxshiroq bo'lishi mumkin. Kanonda ikkalasi uz.
3-mashq (qiyin): kalitlar mosligi
Kanon uz.json va en.json da 106 kalit bir xilligini skript bilan tekshirgan. Shunday funksiya yozing: kalitlar(obyekt) — ichma-ich obyektdan "Menyu.nomi" kabi to'liq kalitlar ro'yxati; farq(a, b) — a da bor, b da yo'q kalitlar. Ikki kichik lug'at bilan sinang. Rekursiya — rekursiya asoslari.
Yechim
function kalitlar(obyekt, oldi = "") {
const natija = [];
for (const [k, v] of Object.entries(obyekt)) {
const tolik = oldi ? `${oldi}.${k}` : k;
if (typeof v === "object" && v !== null) {
natija.push(...kalitlar(v, tolik));
} else {
natija.push(tolik);
}
}
return natija;
}
function farq(a, b) {
const bor = new Set(kalitlar(b));
return kalitlar(a).filter((k) => !bor.has(k));
}
const uz = {
Menyu: { nomi: "Asosiy menyu", blog: "Blog", aloqa: "Aloqa" },
Sayt: { ism: "Aziz Karimov" },
};
const en = {
Menyu: { nomi: "Main menu", blog: "Blog" },
Sayt: { ism: "Aziz Karimov", kasb: "frontend developer" },
};
console.log("uz da bor, en da yo'q:", farq(uz, en));
console.log("en da bor, uz da yo'q:", farq(en, uz));
console.log("uz kalitlari:", kalitlar(uz).length);Konsolda:
uz da bor, en da yo'q: [ 'Menyu.aloqa' ]
en da bor, uz da yo'q: [ 'Sayt.kasb' ]
uz kalitlari: 4Ikki yo'nalishda ham tekshirish kerak: bir tilda ortiqcha kalit ham xato (eskirgan yoki imlo xatoli). Bunday skript npm test ga qo'shilsa, tarjimasiz kalit CI'da ushlanadi. AppConfig turi faqat kod ishlatgan kalitlarni tekshiradi — JSON fayllar orasidagi farqni esa shu skript.
4-mashq: Portfolio qadami
portfolio ga ikki tilni qo'shing («Portfolio qadami» jadvali). Tekshiring: /en/haqimda — lang="en", inglizcha sarlavha, hreflang uchala qatori; /uz/haqimda — 307; almashtirgich o'sha sahifaga olib boradi va header bitta. Qo'shimcha (kanonda ataylab qilinmagan — sizning mashqingiz): bitta loyihaning case study matnini inglizchaga tarjima qiling va /en da lang="uz" izohini shu loyiha uchun olib tashlang.
Yechim
Kutilgan natija — «hreflang» bo'limidagi 34-i18n.txt qatoriga o'xshash. Tarjima uchun eng sodda yo'l: LOYIHALAR dagi matn maydonlarini { uz: "…", en: "…" } ko'rinishiga keltirish va sahifada loyiha.qisqa[til] ?? loyiha.qisqa.uz — tarjima bo'lmasa o'zbekcha va lang="uz". Har loyiha uchun kontentLang() mantiqini shu bilan bog'lang: tarjima bor — lang kerak emas, yo'q — lang="uz" va izoh. Commit — o'zingizniki.
10. Real ishda
- next-intl — App Router uchun eng keng tarqalgan i18n kutubxonasi; muqobillari — Paraglide (kompilyatsiya qilinadigan, juda yengil),
next-i18next(Pages Router merosi). - O'zbekistondagi saytlar odatda uch tilli: uz, ru, en (ba'zan o'zbek kirill ham). Prefiks strategiyasi va
x-default— har loyihada alohida qaror. - Katta jamoalarda tarjimalar TMS'da (Crowdin, Lokalise) yuritiladi va JSON fayllar avtomatik sinxronlanadi — 3-mashqdagi tekshiruv CI'da turadi.
- Intervyu savoli: "Next'da i18n'ni statik saqlab qanday qilasiz?" —
[locale]+generateStaticParams+ server tarjimasi; "hreflang nima?"; "NegaAccept-Languagebo'yicha yo'naltirish SEO va keshga ta'sir qiladi?"
Xulosa
- Til — URL'da:
app/[locale]/,localePrefix: "as-needed"(uz prefikssiz,/en),localeDetection: false— cookie va yo'naltirishsiz. request.ts+next/root-params—setRequestLocalesiz statik render; 63 sahifa○.- Tarjima serverda (
getTranslations), client komponentlarga — prop; type-safe kalitlarAppConfigbilan. - next-intl client navigatsiyasi +15.2 KB; kanon keyin o'z
Linkbilan −12.1 KB qildi.<Link locale>ikki header berdi — almashtirgich<a href>. - hreflang va canonical har sahifada,
og:locale—ogAsos()bilan; Server Action'da til — yashirin maydon +hasLocale.
Keyingi dars: Xatolar, loglar va instrumentation — server xatolarini onRequestError bilan tuzilgan JSON log qilamiz va maxfiy tafsilot brauzerga chiqmasligini tekshiramiz.
Manbalar
- next-intl 4 hujjati: "App Router setup", "Routing" (
defineRouting,localePrefix,localeDetection), "Middleware/proxy", "Navigation" (createNavigation), "Server & Client Components" — next-intl.dev/docs (2026-10-21 da o'qildi) - Next.js hujjati (16.4.0):
next/root-paramsAPI reference — nextjs.org/docs/app/api-reference/functions/next-root-params - Kanon natijalari:
natijalar/34-i18n.txt,natijalar/34-header.txt,natijalar/tajriba/34-link-ikki-header.txt,natijalar/tekshiruv-33.txt,tekshiruv-34.txt,natijalar/tekshiruv-38.txt - WCAG 2.2, 3.1.2 "Language of Parts" — w3.org/WAI/WCAG22/Understanding/language-of-parts
Izohlar (0)
Izoh yozish uchun kiring.
- Hozircha izoh yo'q. Birinchi bo'ling!