Author:
    Creation:2026-09-26Last update:2026-09-26

    2026 рдореЗрдВ Lingui рдХрд╛ рдЙрдкрдпреЛрдЧ рдХрд░рдХреЗ рдЕрдкрдиреЗ Next.js рдПрдкреНрд▓рд┐рдХреЗрд╢рди рдХрд╛ рдЕрдВрддрд░реНрд░рд╛рд╖реНрдЯреНрд░реАрдпрдХрд░рдг рдХреИрд╕реЗ рдХрд░реЗрдВ

    рд╡рд┐рд╖рдп рд╕реВрдЪреА

    Lingui рдХреНрдпрд╛ рд╣реИ?

    Lingui рдПрдХ i18n рд▓рд╛рдЗрдмреНрд░реЗрд░реА рд╣реИ рдЬреЛ macros рдФрд░ message extraction рдХреЗ рдЗрд░реНрдж-рдЧрд┐рд░реНрдж рдмрдирд╛рдИ рдЧрдИ рд╣реИред рдЖрдк рдЕрдкрдиреЗ рдШрдЯрдХреЛрдВ (components) рдореЗрдВ рд╕реНрд░реЛрдд рдЯреЗрдХреНрд╕реНрдЯ рд▓рд┐рдЦрддреЗ рд╣реИрдВ ( t`Hello` , <Trans>Hello</Trans>), lingui extract рдкреНрд░рддреНрдпреЗрдХ рд╕рдВрджреЗрд╢ рдХреЛ рдХреИрдЯрд▓реЙрдЧ (рдбрд┐рдлрд╝реЙрд▓реНрдЯ рд░реВрдк рд╕реЗ PO рдлрд╝рд╛рдЗрд▓реЗрдВ) рдореЗрдВ рдПрдХрддреНрд░ рдХрд░рддрд╛ рд╣реИ, рдФрд░ рдПрдХ рд▓реЛрдбрд░ рдЙрдиреНрд╣реЗрдВ рдХреЙрдореНрдкреИрдХреНрдЯ рдЬрд╛рд╡рд╛рд╕реНрдХреНрд░рд┐рдкреНрдЯ рдореЗрдВ рд╕рдВрдХрд▓рд┐рдд (compile) рдХрд░рддрд╛ рд╣реИред рд╕рдВрджреЗрд╢ ICU MessageFormat рдХрд╛ рдЙрдкрдпреЛрдЧ рдХрд░рддреЗ рд╣реИрдВ, рдФрд░ Lingui App Router рдореЗрдВ React Server Components рдХрд╛ рд╕рдорд░реНрдерди рдХрд░рддрд╛ рд╣реИред

    рдпрд╣ рдЧрд╛рдЗрдб рдПрдХ Next.js 16 App Router рдкреНрд░реЛрдЬреЗрдХреНрдЯ рдореЗрдВ Lingui рдХреЛ рд╕реЗрдЯ рдЕрдк рдХрд░рддреА рд╣реИ, рдЬрд┐рд╕рдореЗрдВ рд╢рд╛рдорд┐рд▓ рд╣реИрдВ:

    • SWC рджреНрд╡рд╛рд░рд╛ рд╕рдВрдХрд▓рд┐рдд Macros, рддрд╛рдХрд┐ Turbopack рдЕрдкрдиреА рдЧрддрд┐ рдмрдирд╛рдП рд░рдЦреЗред
    • Server рдФрд░ Client Components рдЬреЛ рд╕рдорд╛рди Trans рдФрд░ useLingui API рд╕рд╛рдЭрд╛ рдХрд░рддреЗ рд╣реИрдВред
    • proxy.ts рдХреЗ рдорд╛рдзреНрдпрдо рд╕реЗ Locale routing: рдбрд┐рдлрд╝реЙрд▓реНрдЯ рд▓реЛрдХреЗрд▓ рдХреЗ рд▓рд┐рдП /about, рджреВрд╕рд░реЛрдВ рдХреЗ рд▓рд┐рдП /fr/about, рдФрд░ рдкрд╣рд▓реА рдмрд╛рд░ рдЖрдиреЗ рд╡рд╛рд▓реЗ рдЙрдкрдпреЛрдЧрдХрд░реНрддрд╛рдУрдВ рдХреЗ рд▓рд┐рдП рднрд╛рд╖рд╛ рдкрд╣рдЪрд╛рдиред
    • generateStaticParams рдХреЗ рд╕рд╛рде рдкреНрд░рддреНрдпреЗрдХ рд▓реЛрдХреЗрд▓ рдХрд╛ Static renderingред
    • рдкреВрд░реНрдг рдмрд╣реБрднрд╛рд╖реА SEO: рдЕрдиреБрд╡рд╛рджрд┐рдд generateMetadata, canonical, x-default рдХреЗ рд╕рд╛рде hreflang, Open Graph locales, JSON-LD, sitemap.ts, robots.ts рдФрд░ рд╕реНрдерд╛рдиреАрдпрдХреГрдд (localized) 404 рдкреЗрдЬред
    рдХрд┐рд╕реА рдЕрдиреНрдп рд▓рд╛рдЗрдмреНрд░реЗрд░реА рдХреА рддрд▓рд╛рд╢ рд╣реИ? next-intl рдЧрд╛рдЗрдб, next-i18next рдЧрд╛рдЗрдб, рдпрд╛ Next.js + Intlayer рдЧрд╛рдЗрдб рджреЗрдЦреЗрдВред
    TanStack Start рдХрд╛ рдЙрдкрдпреЛрдЧ рдХрд░ рд░рд╣реЗ рд╣реИрдВ? TanStack Start + Lingui рдЧрд╛рдЗрдб рджреЗрдЦреЗрдВред рдкреБрд╕реНрддрдХрд╛рд▓рдпреЛрдВ рдХреА рддреБрд▓рдирд╛ рдХрд░ рд░рд╣реЗ рд╣реИрдВ? Lingui рдмрдирд╛рдо Intlayer рдФрд░ next-i18next рдмрдирд╛рдо next-intl рдмрдирд╛рдо Intlayer рдкрдврд╝реЗрдВред

    Next.js рдкрд░ Lingui рдХреЗ рдмрд╛рд░реЗ рдореЗрдВ рдмреЗрдВрдЪрдорд╛рд░реНрдХ рдХреНрдпрд╛ рдХрд╣рддрд╛ рд╣реИ

    i18n рдмреЗрдВрдЪрдорд╛рд░реНрдХ рдкреНрд░рддреНрдпреЗрдХ рдкреНрд░рдореБрдЦ рд▓рд╛рдЗрдмреНрд░реЗрд░реА рдХреЗ рд╕рд╛рде рд╕рдорд╛рди 10-рдкреЗрдЬ, 10-рд▓реЛрдХреЗрд▓ Next.js рдРрдк рдЪрд▓рд╛рддрд╛ рд╣реИ рдФрд░ рдорд╛рдкрддрд╛ рд╣реИ рдХрд┐ рдмреНрд░рд╛рдЙрдЬрд╝рд░ рд╡рд╛рд╕реНрддрд╡ рдореЗрдВ рдХреНрдпрд╛ рдбрд╛рдЙрдирд▓реЛрдб рдХрд░рддрд╛ рд╣реИред

    рдбрд╛рдпрдирд╛рдорд┐рдХ JSON рд▓реЛрдбрд┐рдВрдЧ

    рд░рдирдЯрд╛рдЗрдо рдкрд░ рдЖрд▓рд╕реНрдп рд╕реЗ рдЕрдиреБрд╡рд╛рдж рд▓реЛрдб рдХрд░рддрд╛ рд╣реИ

    рд╕реНрдХреЛрдкреНрдб JSON (рдиреЗрдорд╕реНрдкреЗрд╕рд┐рдВрдЧ)

    рдкреНрд░рддрд┐-рдкреГрд╖реНрда рдЕрдиреБрд╡рд╛рдж рдиреЗрдорд╕реНрдкреЗрд╕

    I18n рдкреНрд░рджрд░реНрд╢рди рдмреЗрдВрдЪрдорд╛рд░реНрдХ

    рдпрд╣ рдореАрдЯреНрд░рд┐рдХ рдХреНрдпрд╛ рд╣реИ?

    рдЕрдВрддрд░реНрд░рд╛рд╖реНрдЯреНрд░реАрдпрдХрд░рдг рд▓рд╛рдЗрдмреНрд░реЗрд░реА рдмрдВрдбрд▓ рдХрд╛ рдХреБрд▓ gzip-рд╕рдВрдкреАрдбрд╝рд┐рдд рдЖрдХрд╛рд░ред рдЗрд╕рдореЗрдВ рдХреЗрд╡рд▓ рдЯреНрд░реА-рд╢реЗрдХрд┐рдВрдЧ рдФрд░ рдорд┐рдирд┐рдлрд┐рдХреЗрд╢рди рдХреЗ рдмрд╛рдж рдкреНрд░рджрд╛рддрд╛ рдФрд░ рд╕рд╛рдордЧреНрд░реА рдкреБрдирд░реНрдкреНрд░рд╛рдкреНрддрд┐ рддрд░реНрдХ рд╢рд╛рдорд┐рд▓ рд╣реИрдВред

    рдпрд╣ рдорд╣рддреНрд╡рдкреВрд░реНрдг рдХреНрдпреЛрдВ рд╣реИ?

    рдПрдХ рдЫреЛрдЯрд╛ рд▓рд╛рдЗрдмреНрд░реЗрд░реА рдЖрдХрд╛рд░ рдкреНрд░рд╛рд░рдВрднрд┐рдХ рдЬрд╛рд╡рд╛рд╕реНрдХреНрд░рд┐рдкреНрдЯ рдкреЗрд▓реЛрдб рдХреЛ рдХрдо рдХрд░рддрд╛ рд╣реИ, рдЬрд┐рд╕рд╕реЗ рдХреНрд▓рд╛рдЗрдВрдЯ рдкрд░ рддреЗрдЬрд╝ рдбрд╛рдЙрдирд▓реЛрдб рдФрд░ рдирд┐рд╖реНрдкрд╛рджрди рд╕рдордп рд╣реЛрддрд╛ рд╣реИред

    рджреЗрдЦрдиреЗ рдХрд╛ рддрд░реАрдХрд╛

    Next.js 16 рдкрд░ @lingui/core@6.6.0 рдХреЗ рд▓рд┐рдП рдореБрдЦреНрдп рдЖрдВрдХрдбрд╝реЗ, 2026-09-26 рдХреЛ рдорд╛рдкреЗ рдЧрдП (gzip):

    рд╕реЗрдЯрдЕрдкрд▓рд╛рдЗрдмреНрд░реЗрд░реА рдХрд╛ рдЖрдХрд╛рд░рдкреНрд░рддрд┐ рдкреЗрдЬ JSрдЕрдиреНрдп-рд▓реЛрдХреЗрд▓ рд▓реАрдХрдЕрдиреНрдп-рдкреЗрдЬ рд▓реАрдХ
    рдмрд┐рдирд╛ i18n (рдмреЗрд╕ рдРрдк)-141.0 KB0%0%
    Lingui, рдкреНрд░рддрд┐ рд▓реЛрдХреЗрд▓ рдПрдХ рдХреИрдЯрд▓реЙрдЧ72.1 KB145.4 KB2.8%89.9%
    @intlayer/lingui (рдХрдореНрдкреИрдЯ)10.7 KB221.6 KB50%90%
    next-intlayer (рдореВрд▓ Intlayer)4.9 KB141.5 KB0%0%

    рдореБрдЦреНрдп рдирд┐рд╖реНрдХрд░реНрд╖:

    • рдкреНрд░рддрд┐ рд▓реЛрдХреЗрд▓ рдПрдХ рдПрдХрд▓ рдХреИрдЯрд▓реЙрдЧ рдЕрднреА рднреА рдЕрдиреНрдп рдкреЗрдЬреЛрдВ рдХреЗ рд╕рдВрджреЗрд╢реЛрдВ рдХреЛ рдХреНрд▓рд╛рдЗрдВрдЯ рдкреНрд░реЛрд╡рд╛рдЗрдбрд░ рдореЗрдВ рд▓реАрдХ рдХрд░рддрд╛ рд╣реИред рдЬрд┐рддрдирд╛ рд╕рдВрднрд╡ рд╣реЛ рд╕рдХреЗ рдЯреЗрдХреНрд╕реНрдЯ рдХреЛ Server Components рдореЗрдВ рд░рдЦреЗрдВ, рдЬреЛ рд░реЗрдВрдбрд░ рдХрд┐рдпрд╛ рдЧрдпрд╛ HTML рднреЗрдЬрддреЗ рд╣реИрдВ, рдХреИрдЯрд▓реЙрдЧ рдирд╣реАрдВред
    • Lingui рд░рдирдЯрд╛рдЗрдо рдХрд╛ рд╡рдЬрди ~72 KB gzip рд╣реИред @intlayer/lingui рдХрдореНрдкреИрдЯ рдЕрдбреИрдкреНрдЯрд░ рд░рдирдЯрд╛рдЗрдо рдХреЛ рдШрдЯрд╛рдХрд░ ~11 KB рдХрд░ рджреЗрддрд╛ рд╣реИ, рд▓реЗрдХрд┐рди рдЗрд╕ рдмреЗрдВрдЪрдорд╛рд░реНрдХ рдореЗрдВ Next.js рдХрдореНрдкреИрдЯ рд╕реЗрдЯрдЕрдк рдЕрднреА рднреА рдкреВрд░реЗ рдХреИрдЯрд▓реЙрдЧ рдХреЛ рдкреЗрдЬ рдкрд░ рднреЗрдЬрддрд╛ рд╣реИред рдореВрд▓ next-intlayer API рд╡рд╣ рд╕реЗрдЯрдЕрдк рд╣реИ рдЬреЛ рдмреЗрд╕ рдРрдк рдХреЗ рдЖрдХрд╛рд░ рдкрд░ рдмрдирд╛ рд░рд╣рддрд╛ рд╣реИред
    рдкреВрд░рд╛ рдбреЗрдЯрд╛ рджреЗрдЦреЗрдВ: Next.js рдмреЗрдВрдЪрдорд╛рд░реНрдХ рд░рд┐рдкреЛрд░реНрдЯ, рдФрд░ рдмреЗрдВрдЪрдорд╛рд░реНрдХ рд░рд┐рдкреЛрдЬрд┐рдЯрд░реАред

    Next.js рдкрд░ рд╕реБрд╡рд┐рдзрд╛рдУрдВ рдХреА рддреБрд▓рдирд╛

    Next.js App Router рдкреНрд░реЛрдЬреЗрдХреНрдЯ рдореЗрдВ рдЖрдорддреМрд░ рдкрд░ рдЖрд╡рд╢реНрдпрдХ рд╕реБрд╡рд┐рдзрд╛рдУрдВ рдкрд░ Lingui рдХреА рддреБрд▓рдирд╛ next-intl рдФрд░ Intlayer рд╕реЗ рдХреИрд╕реЗ рд╣реЛрддреА рд╣реИ:

    рд╕реБрд╡рд┐рдзрд╛next-intlayer (Intlayer)Linguinext-intl
    рдШрдЯрдХреЛрдВ рдХреЗ рдкрд╛рд╕ рдЕрдиреБрд╡рд╛рджтЬЕ рд╕рд╛рдордЧреНрд░реА рдкреНрд░рддреНрдпреЗрдХ рдШрдЯрдХ рдХреЗ рд╕рд╛рде рд╕рд╣-рд╕реНрдерд┐рдд рд╣реЛрддреА рд╣реИтЪая╕П рдШрдЯрдХреЛрдВ рдореЗрдВ рд╕реНрд░реЛрдд рдкрд╛рда, рдХреЗрдВрджреНрд░реАрдХреГрдд рдХреИрдЯрд▓реЙрдЧтЭМ рдХреЗрдВрджреНрд░реАрдХреГрдд JSON
    TypeScript рдПрдХреАрдХрд░рдгтЬЕ рд╕реНрд╡рддрдГ-рдЬрдирд░реЗрдЯреЗрдб рд╕рдЦреНрдд рдкреНрд░рдХрд╛рд░тЪая╕П рдореИрдХреНрд░реЛрдЬрд╝ рдЯрд╛рдЗрдк рдХрд┐рдП рдЧрдП рд╣реИрдВ, рд╕рдВрджреЗрд╢ рдХреИрдЯрд▓реЙрдЧ рдирд╣реАрдВтЬЕ рдЕрдЪреНрдЫрд╛, AppConfig рд╡реГрджреНрдзрд┐ рдХреЗ рдорд╛рдзреНрдпрдо рд╕реЗ
    рд▓рд╛рдкрддрд╛ рдЕрдиреБрд╡рд╛рдж рдкрд╣рдЪрд╛рдитЬЕ TypeScript рддреНрд░реБрдЯрд┐рдпрд╛рдВ рдФрд░ рдмрд┐рд▓реНрдб-рдЯрд╛рдЗрдо рдЪреЗрддрд╛рд╡рдирд┐рдпрд╛рдВтЪая╕П рд╕реНрд░реЛрдд рдкрд╛рда рдкрд░ рд░рдирдЯрд╛рдЗрдо рдлрд╝реЙрд▓рдмреИрдХтЪая╕П рд░рдирдЯрд╛рдЗрдо рдлрд╝реЙрд▓рдмреИрдХ
    рд░рд┐рдЪ рдХрдВрдЯреЗрдВрдЯ (JSX, Markdown)тЬЕ рдкреНрд░рддреНрдпрдХреНрд╖ рд╕рдорд░реНрдердитЬЕ <Trans> рдХреЗ рдЕрдВрджрд░ JSX, рдХреЛрдИ Markdown рдирд╣реАрдВтЪая╕П t.rich рдХреЗ рдорд╛рдзреНрдпрдо рд╕реЗ рдЯреИрдЧ, рдХреЛрдИ Markdown рдирд╣реАрдВ
    AI рдЕрдиреБрд╡рд╛рджтЬЕ рдРрдк рд╕рдВрджрд░реНрдн рдХреЗ рд╕рд╛рде рдЖрдкрдХрд╛ рдЕрдкрдирд╛ рдкреНрд░рджрд╛рддрд╛ рдФрд░ API рдХреБрдВрдЬреАтЭМ рдирд╣реАрдВтЭМ рдирд╣реАрдВ
    рд╡рд┐рдЬреБрдЕрд▓ рдПрдбрд┐рдЯрд░ / CMSтЬЕ рд╕реНрдерд╛рдиреАрдп рд╡рд┐рдЬреБрдЕрд▓ рдПрдбрд┐рдЯрд░ + рд╡реИрдХрд▓реНрдкрд┐рдХ CMSтЭМ рдмрд╛рд╣рд░реА рдкреНрд▓реЗрдЯрдлреЙрд░реНрдо рдХреЗ рдорд╛рдзреНрдпрдо рд╕реЗтЭМ рдмрд╛рд╣рд░реА рдкреНрд▓реЗрдЯрдлреЙрд░реНрдо рдХреЗ рдорд╛рдзреНрдпрдо рд╕реЗ
    рд╕реНрдерд╛рдиреАрдпрдХреГрдд рд░реВрдЯрд┐рдВрдЧтЬЕ рдЗрди-рдмрд┐рд▓реНрдЯтЭМ рдЕрдкрдиреА рдЦреБрдж рдХреА proxy.ts рд▓рд┐рдЦреЗрдВтЬЕ рдЗрди-рдмрд┐рд▓реНрдЯ [locale] рд╕реЗрдЧрдореЗрдВрдЯ
    рдмрд╣реБрд╡рдЪрди (Pluralization)тЬЕ рдЧрдгрдирд╛-рдЖрдзрд╛рд░рд┐рддтЬЕ ICU, <Plural> рдореИрдХреНрд░реЛтЬЕ ICU
    рд╕рд╛рдордЧреНрд░реА рдкреНрд░рд╛рд░реВрдктЬЕ .ts, .tsx, .js, .json, .md, .yamlтЬЕ PO, JSON, CSVтЬЕ .json, .js, .ts
    ICU MessageFormatтЬЕ format: "icu" рдХреЗ рдорд╛рдзреНрдпрдо рд╕реЗтЬЕ рдиреЗрдЯрд┐рд╡тЬЕ рдиреЗрдЯрд┐рд╡
    SEO рд╣реЗрд▓реНрдкрд░реНрд╕ (hreflang, sitemap)тЬЕ рдореЗрдЯрд╛рдбреЗрдЯрд╛, рд╕рд╛рдЗрдЯрдореИрдк рдФрд░ robots.txt рд╣реЗрд▓реНрдкрд░реНрд╕тЭМ рдореИрдиреБрдЕрд▓тЬЕ рдЕрдЪреНрдЫрд╛
    Server ComponentsтЬЕ рдХрд┐рд╕реА рднреА Server Component рдореЗрдВ рд╕реАрдзрд╛ рдПрдХреНрд╕реЗрд╕тЪая╕П рдкреНрд░рддреНрдпреЗрдХ рд▓реЗрдЖрдЙрдЯ рдФрд░ рдкреЗрдЬ рдореЗрдВ setI18nтЪая╕П рдкреНрд░рддрд┐ рдШрдЯрдХ await getTranslations()
    рдкреНрд░рддрд┐-рдШрдЯрдХ рдЯреНрд░реА-рд╢реЗрдХрд┐рдВрдЧтЬЕ рдмрд┐рд▓реНрдб рдЯрд╛рдЗрдо рдкрд░ (Babel / SWC)тЪая╕П рдкреНрд░рддрд┐ рд▓реЛрдХреЗрд▓ рдПрдХ рдХреИрдЯрд▓реЙрдЧ, рдкреНрд░рддрд┐-рдкреЗрдЬ рдПрдХреНрд╕рдЯреНрд░реИрдХреНрдЯрд░ рдкреНрд░рдпреЛрдЧрд╛рддреНрдордХ рд╣реИтЪая╕П рдореИрдиреБрдЕрд▓, рдкреНрд░рддрд┐ рд░реВрдЯ pick() рдХреЗ рд╕рд╛рде
    рд░рдирдЯрд╛рдЗрдо рдЖрдХрд╛рд░ (gzip, рдмреЗрдВрдЪрдорд╛рд░реНрдХ)4.9 KB72.1 KB14.7 KB
    CI рдореЗрдВ рд▓рд╛рдкрддрд╛ рдЕрдиреБрд╡рд╛рджтЬЕ npx intlayer testтЬЕ lingui compile --strictтЪая╕П рдЗрди-рдмрд┐рд▓реНрдЯ рдирд╣реАрдВ
    рдЗрдХреЛрд╕рд┐рд╕реНрдЯрдо / рд╕рдореБрджрд╛рдптЪая╕П рдЫреЛрдЯрд╛, рддреЗрдЬреА рд╕реЗ рдмрдврд╝ рд░рд╣рд╛ рд╣реИтЬЕ рдкрд░рд┐рдкрдХреНрд╡тЬЕ рдмрдбрд╝рд╛
    рд░рдирдЯрд╛рдЗрдо рдЖрдХрд╛рд░ Next.js рдмреЗрдВрдЪрдорд╛рд░реНрдХ рд╕реЗ рд▓рд┐рдП рдЧрдП рд╣реИрдВред рд╡рд┐рд╕реНрддреГрдд рдЪрд░реНрдЪрд╛ рдХреЗ рд▓рд┐рдП, Lingui рдмрдирд╛рдо Intlayer рдкрдврд╝реЗрдВред
    рдЕрдиреНрдп Next.js рдЧрд╛рдЗрдб: next-intl, next-i18next, рдФрд░ Intlayerред

    рд╡реЗ рдЕрднреНрдпрд╛рд╕ рдЬрд┐рдирдХрд╛ рдЖрдкрдХреЛ рдкрд╛рд▓рди рдХрд░рдирд╛ рдЪрд╛рд╣рд┐рдП

    • [locale] рд▓реЗрдЖрдЙрдЯ рдореЗрдВ <html> рдкрд░ lang рдФрд░ dir рд╕реЗрдЯ рдХрд░реЗрдВред
    • рдЯреЗрдХреНрд╕реНрдЯ рдХреЗ рд▓рд┐рдП Server Components рдХреЛ рдкреНрд░рд╛рдердорд┐рдХрддрд╛ рджреЗрдВ: рд╡реЗ рд╕рд░реНрд╡рд░ рдкрд░ HTML рд░реЗрдВрдбрд░ рдХрд░рддреЗ рд╣реИрдВ рдФрд░ рдХреНрд▓рд╛рдЗрдВрдЯ рдкрд░ рдХреИрдЯрд▓реЙрдЧ рдХреА рдЖрд╡рд╢реНрдпрдХрддрд╛ рдирд╣реАрдВ рд╣реЛрддреА рд╣реИред
    • рдкреНрд░рддреНрдпреЗрдХ рд▓реЗрдЖрдЙрдЯ рдФрд░ рдкреЗрдЬ рдореЗрдВ initLingui(locale) рдХреЛ рдХреЙрд▓ рдХрд░реЗрдВред рдиреЗрд╡рд┐рдЧреЗрд╢рди рдкрд░ рд▓реЗрдЖрдЙрдЯ рдлрд┐рд░ рд╕реЗ рд░реЗрдВрдбрд░ рдирд╣реАрдВ рд╣реЛрддреЗ рд╣реИрдВ, рдЗрд╕рд▓рд┐рдП рдХреЛрдИ рдкреЗрдЬ рдЕрдкрдиреЗ рд▓реЗрдЖрдЙрдЯ рджреНрд╡рд╛рд░рд╛ рд▓реЛрдХреЗрд▓ рд╕реЗрдЯ рдХрд┐рдП рдЬрд╛рдиреЗ рдкрд░ рдирд┐рд░реНрднрд░ рдирд╣реАрдВ рд░рд╣ рд╕рдХрддрд╛ рд╣реИред
    • рдкреНрд░рддрд┐ рд▓реЛрдХреЗрд▓ рдПрдХ URL рд░рдЦреЗрдВ рдФрд░ generateStaticParams рдХреЗ рд╕рд╛рде рдкреНрд░рддреНрдпреЗрдХ рд▓реЛрдХреЗрд▓ рдХреЛ рдкреНрд░реА-рд░реЗрдВрдбрд░ рдХрд░реЗрдВред
    • canonical, hreflang рдФрд░ x-default рдХреЗ рд╕рд╛рде generateMetadata рдореЗрдВ рдЕрдкрдиреЗ рдореЗрдЯрд╛рдбреЗрдЯрд╛ рдХрд╛ рдЕрдиреБрд╡рд╛рдж рдХрд░реЗрдВред
    • sitemap.ts рдФрд░ robots.ts рд╕рдореНрдореЗрд▓рдиреЛрдВ рдХреЗ рд╕рд╛рде рдПрдХ рдмрд╣реБрднрд╛рд╖реА рд╕рд╛рдЗрдЯрдореИрдк рдФрд░ robots.txt рдЬрдирд░реЗрдЯ рдХрд░реЗрдВред
    • рд▓реЛрдХреЗрд▓ рд╕реНрд╡рд┐рдЪрд░ рдХреЗ рд▓рд┐рдП рд╡рд╛рд╕реНрддрд╡рд┐рдХ рд▓рд┐рдВрдХ рдХрд╛ рдЙрдкрдпреЛрдЧ рдХрд░реЗрдВ, рддрд╛рдХрд┐ рдХреНрд░реЙрд▓рд░ рдкреНрд░рддреНрдпреЗрдХ рднрд╛рд╖рд╛ рдХреЛ рдЦреЛрдЬ рд╕рдХреЗрдВред
    • CI рдореЗрдВ lingui extract рдЪрд▓рд╛рдПрдВ рддрд╛рдХрд┐ рдХреЛрдИ рдирдпрд╛ рд╕рдВрджреЗрд╢ рдХрднреА рднреА рдмрд┐рдирд╛ рдЕрдиреБрд╡рд╛рдж рдХреЗ рди рдЬрд╛рдПред
    рдЕрдВрддрд░реНрд░рд╛рд╖реНрдЯреНрд░реАрдпрдХрд░рдг рдФрд░ SEO, hreflang рдЧрд╛рдЗрдб рдФрд░ Next.js рдмрд╣реБрднрд╛рд╖реА SEO рддреБрд▓рдирд╛ рдкрд░ рд╣рдорд╛рд░рд╛ рдЧрд╛рдЗрдб рджреЗрдЦреЗрдВред

    Next.js рдПрдкреНрд▓рд┐рдХреЗрд╢рди рдореЗрдВ Lingui рд╕реЗрдЯ рдЕрдк рдХрд░рдиреЗ рдХреЗ рд▓рд┐рдП рдЪрд░рдг-рджрд░-рдЪрд░рдг рдЧрд╛рдЗрдб

    рдпрд╣рд╛рдБ рд╡рд╣ рдкреНрд░реЛрдЬреЗрдХреНрдЯ рд╕рдВрд░рдЪрдирд╛ рд╣реИ рдЬрд┐рд╕реЗ рд╣рдо рдмрдирд╛рдПрдВрдЧреЗ:

    bash
    .
    тФЬтФАтФА lingui.config.ts
    тФЬтФАтФА next.config.ts
    тФФтФАтФА src
        тФЬтФАтФА proxy.ts                    # Locale routing and detection
        тФЬтФАтФА locales
        тФВ   тФЬтФАтФА en
        тФВ   тФВ   тФФтФАтФА messages.po         # Generated by `lingui extract`
        тФВ   тФЬтФАтФА fr
        тФВ   тФВ   тФФтФАтФА messages.po
        тФВ   тФФтФАтФА es
        тФВ       тФФтФАтФА messages.po
        тФЬтФАтФА i18n
        тФВ   тФЬтФАтФА config.ts               # Locales, URL helpers
        тФВ   тФЬтФАтФА appRouterI18n.ts        # Server-only catalogs and instances
        тФВ   тФЬтФАтФА initLingui.ts
        тФВ   тФЬтФАтФА negotiateLocale.ts
        тФВ   тФФтФАтФА metadata.ts             # generateMetadata builder
        тФЬтФАтФА components
        тФВ   тФЬтФАтФА LinguiClientProvider.tsx
        тФВ   тФЬтФАтФА LocaleSwitcher.tsx
        тФВ   тФФтФАтФА LocalizedLink.tsx
        тФФтФАтФА app
            тФЬтФАтФА sitemap.ts
            тФЬтФАтФА robots.ts
            тФФтФАтФА [locale]
                тФЬтФАтФА layout.tsx
                тФЬтФАтФА page.tsx
                тФЬтФАтФА not-found.tsx
                тФЬтФАтФА [...rest]
                тФВ   тФФтФАтФА page.tsx        # Localized 404 for unknown paths
                тФФтФАтФА about
                    тФФтФАтФА page.tsx
    
    1. рдирд┐рд░реНрднрд░рддрд╛рдПрдВ рд╕реНрдерд╛рдкрд┐рдд рдХрд░реЗрдВ (Install Dependencies)

      bash
      npm install @lingui/core @lingui/react
      npm install -D @lingui/cli @lingui/swc-plugin @lingui/loader @lingui/format-po
      
      • @lingui/core / @lingui/react: рд░рдирдЯрд╛рдЗрдо, I18nProvider, Server Components рдХреЗ рд▓рд┐рдП setI18n, рдФрд░ рдореИрдХреНрд░реЛрдЬрд╝ (@lingui/core/macro, @lingui/react/macro)ред
      • @lingui/swc-plugin: Next.js SWC рдкрд╛рдЗрдкрд▓рд╛рдЗрди рдХреЗ рдЕрдВрджрд░ рдореИрдХреНрд░реЛрдЬрд╝ рдХреЛ рд╕рдВрдХрд▓рд┐рдд рдХрд░рддрд╛ рд╣реИред
      • @lingui/loader: рдЖрдпрд╛рдд рдкрд░ .po рдХреИрдЯрд▓реЙрдЧ рд╕рдВрдХрд▓рд┐рдд рдХрд░рддрд╛ рд╣реИ, рдЗрд╕рд▓рд┐рдП lingui compile рдХреА рдЖрд╡рд╢реНрдпрдХрддрд╛ рдирд╣реАрдВ рд╣реИред
      • @lingui/cli: рдХреИрдЯрд▓реЙрдЧ рдореЗрдВ рд╕рдВрджреЗрд╢ рдПрдХрддреНрд░ рдХрд░рдиреЗ рдХреЗ рд▓рд┐рдП lingui extractред
      @lingui/swc-plugin рдПрдХ WebAssembly рдкреНрд▓рдЧрдЗрди рд╣реИ рдЬреЛ Next.js рдХреЗ SWC рд╕рдВрд╕реНрдХрд░рдг рд╕реЗ рдЬреБрдбрд╝рд╛ рд╣реБрдЖ рд╣реИред рдпрджрд┐ Next.js рдЕрдкрдЧреНрд░реЗрдб рдХреЗ рдмрд╛рдж рдмрд┐рд▓реНрдб рд╡рд┐рдлрд▓ рд╣реЛ рдЬрд╛рддрд╛ рд╣реИ, рддреЛ рдкреНрд▓рдЧрдЗрди рдХреЛ рдЙрд╕рдХреЗ README рдореЗрдВ рд╕рдВрдЧрдд рдХреЗ рд░реВрдк рдореЗрдВ рд╕реВрдЪреАрдмрджреНрдз рд╕рдВрд╕реНрдХрд░рдг рдореЗрдВ рдЕрдкрдбреЗрдЯ рдХрд░реЗрдВред
    2. рдЕрдкрдиреЗ рд▓реЛрдХреЗрд▓ рдХреЙрдиреНрдлрд╝рд┐рдЧрд░реЗрд╢рди рдХреЛ рдХреЗрдВрджреНрд░реАрдХреГрдд рдХрд░реЗрдВ

      рдПрдХ рдПрдХрд▓ рдлрд╝рд╛рдЗрд▓ рд▓реЛрдХреЗрд▓ рдФрд░ URL рд╣реЗрд▓реНрдкрд░реНрд╕ рдХреЛ рдкрд░рд┐рднрд╛рд╖рд┐рдд рдХрд░рддреА рд╣реИред рд░реВрдЯрд┐рдВрдЧ, рдореЗрдЯрд╛рдбреЗрдЯрд╛, рд╕рд╛рдЗрдЯрдореИрдк рдФрд░ Lingui рд╕рднреА рдЗрд╕рд╕реЗ рдкрдврд╝рддреЗ рд╣реИрдВред

      src/i18n/config.ts
      export const locales = ["en", "fr", "es"] as const;
      
      export type Locale = (typeof locales)[number];
      
      export const defaultLocale: Locale = "en";
      
      /** Public origin, used for canonical URLs, hreflang and the sitemap. */
      export const siteUrl = "https://example.com";
      
      /** Cookie storing the locale explicitly chosen by the visitor. */
      export const localeCookieName = "NEXT_LOCALE";
      
      /** Open Graph expects `language_TERRITORY` codes. */
      export const openGraphLocales: Record<Locale, string> = {
        en: "en_US",
        fr: "fr_FR",
        es: "es_ES",
      };
      
      export const isLocale = (value: unknown): value is Locale =>
        typeof value === "string" && (locales as readonly string[]).includes(value);
      
      export const resolveLocale = (value: string | undefined): Locale =>
        isLocale(value) ? value : defaultLocale;
      
      const rightToLeftLanguages = new Set(["ar", "fa", "he", "ur", "ps", "yi"]);
      
      export const getTextDirection = (locale: string): "ltr" | "rtl" =>
        rightToLeftLanguages.has(new Intl.Locale(locale).language) ? "rtl" : "ltr";
      
      /** `localizePath("/about", "fr")` тЖТ `/fr/about`, default locale unprefixed. */
      export const localizePath = (path: string, locale: Locale): string => {
        if (locale === defaultLocale) return path;
      
        return path === "/" ? `/${locale}` : `/${locale}${path}`;
      };
      
      /** `/fr/about` тЖТ `/about` */
      export const stripLocale = (pathname: string): string => {
        const [, firstSegment, ...rest] = pathname.split("/");
      
        return isLocale(firstSegment) ? `/${rest.join("/")}` : pathname;
      };
      
      export const getAbsoluteUrl = (path: string, locale: Locale): string =>
        `${siteUrl}${localizePath(path, locale)}`;
      
      export const getLocaleName = (locale: Locale): string =>
        new Intl.DisplayNames([locale], { type: "language" }).of(locale) ?? locale;
      
    3. Lingui рдФрд░ Next.js рдХреЙрдиреНрдлрд╝рд┐рдЧрд░ рдХрд░реЗрдВ

      lingui.config.ts
      import { defineConfig } from "@lingui/cli";
      import { formatter } from "@lingui/format-po";
      import { defaultLocale, locales } from "./src/i18n/config";
      
      export default defineConfig({
        sourceLocale: defaultLocale,
        locales: [...locales],
        catalogs: [
          {
            path: "<rootDir>/src/locales/{locale}/messages",
            include: ["src"],
          },
        ],
        format: formatter({ lineNumbers: false }),
      });
      

      SWC рдкреНрд▓рдЧрдЗрди рдореИрдХреНрд░реЛрдЬрд╝ рдХреЛ рд╕рдВрдХрд▓рд┐рдд рдХрд░рддрд╛ рд╣реИ, рдФрд░ рд▓реЛрдбрд░ Turbopack (Next.js 16 рдореЗрдВ рдбрд┐рдлрд╝реЙрд▓реНрдЯ) рдФрд░ webpack рджреЛрдиреЛрдВ рдХреЗ рд▓рд┐рдП .po рдлрд╝рд╛рдЗрд▓реЛрдВ рдХреЛ рд╕рдВрдХрд▓рд┐рдд рдХрд░рддрд╛ рд╣реИ:

      next.config.ts
      import type { NextConfig } from "next";
      
      const nextConfig: NextConfig = {
        experimental: {
          swcPlugins: [["@lingui/swc-plugin", {}]],
        },
        turbopack: {
          rules: {
            "*.po": { loaders: ["@lingui/loader"], as: "*.js" },
          },
        },
        webpack: (config) => {
          config.module.rules.push({ test: /\.po$/, use: "@lingui/loader" });
      
          return config;
        },
      };
      
      export default nextConfig;
      

      рдПрдХреНрд╕рдЯреНрд░реИрдХреНрд╢рди рд╕реНрдХреНрд░рд┐рдкреНрдЯ рдЬреЛрдбрд╝реЗрдВ:

      package.json
      {
        "scripts": {
          "i18n:extract": "lingui extract --clean",
          "i18n:check": "lingui extract --clean && git diff --exit-code src/locales"
        }
      }
      
    4. рдХреИрдЯрд▓реЙрдЧ рд▓реЛрдб рдХрд░реЗрдВ рдФрд░ рд╕рд░реНрд╡рд░ рдЗрдВрд╕реНрдЯреЗрдВрд╕ рдмрдирд╛рдПрдВ

      Server Components рдореЗрдВ рдХреЛрдИ React context рдирд╣реАрдВ рд╣реЛрддрд╛ рд╣реИ, рдЗрд╕рд▓рд┐рдП Lingui рд╡рд░реНрддрдорд╛рди рд░реЗрдВрдбрд░ рдХреЗ рд▓рд┐рдП рдЗрдВрд╕реНрдЯреЗрдВрд╕ рдкрдВрдЬреАрдХреГрдд рдХрд░рдиреЗ рдХреЗ рд▓рд┐рдП setI18n рдкреНрд░рджрд╛рди рдХрд░рддрд╛ рд╣реИред рдпрд╣ рдореЙрдбреНрдпреВрд▓ рдкреНрд░рддреНрдпреЗрдХ рдХреИрдЯрд▓реЙрдЧ рдХреЛ рдкреНрд░рддрд┐ рд╕рд░реНрд╡рд░ рдкреНрд░рдХреНрд░рд┐рдпрд╛ рдореЗрдВ рдПрдХ рдмрд╛рд░ рд▓реЛрдб рдХрд░рддрд╛ рд╣реИ рдФрд░ рдкреНрд░рддрд┐ рд▓реЛрдХреЗрд▓ рдПрдХ I18n рдЗрдВрд╕реНрдЯреЗрдВрд╕ рдмрдирд╛рддрд╛ рд╣реИред рдпрд╣ server-only рд╣реИ: рдЕрдиреНрдп рд▓реЛрдХреЗрд▓ рдХреЗ рдХреИрдЯрд▓реЙрдЧ рдХрднреА рднреА рдХреНрд▓рд╛рдЗрдВрдЯ рдмрдВрдбрд▓ рддрдХ рдирд╣реАрдВ рдкрд╣реБрдВрдЪрддреЗ рд╣реИрдВред

      src/i18n/appRouterI18n.ts
      import "server-only";
      import { type I18n, type Messages, setupI18n } from "@lingui/core";
      import { type Locale, locales } from "./config";
      
      const loadCatalog = async (locale: Locale): Promise<[Locale, Messages]> => {
        const { messages } = await import(`../locales/${locale}/messages.po`);
      
        return [locale, messages];
      };
      
      const catalogs = Object.fromEntries(
        await Promise.all(locales.map(loadCatalog))
      ) as Record<Locale, Messages>;
      
      const i18nInstances = Object.fromEntries(
        locales.map((locale) => [
          locale,
          setupI18n({ locale, messages: { [locale]: catalogs[locale] } }),
        ])
      ) as Record<Locale, I18n>;
      
      export const getMessages = (locale: Locale): Messages => catalogs[locale];
      
      export const getI18nInstance = (locale: Locale): I18n => i18nInstances[locale];
      
      src/i18n/initLingui.ts
      import { setI18n } from "@lingui/react/server";
      import { getI18nInstance } from "./appRouterI18n";
      import type { Locale } from "./config";
      
      /**
       * Registers the instance for the current Server Component render.
       * Call it in every layout and page.
       */
      export const initLingui = (locale: Locale) => {
        const i18n = getI18nInstance(locale);
      
        setI18n(i18n);
      
        return i18n;
      };
      

      TypeScript рджреНрд╡рд╛рд░рд╛ .po рдЖрдпрд╛рдд рдХреЛ рд╕реНрд╡реАрдХрд╛рд░ рдХрд░рдиреЗ рдХреЗ рд▓рд┐рдП, рдореЙрдбреНрдпреВрд▓ рдХреЛ рдПрдХ рдмрд╛рд░ рдШреЛрд╖рд┐рдд рдХрд░реЗрдВ:

      src/i18n/po.d.ts
      declare module "*.po" {
        import type { Messages } from "@lingui/core";
      
        export const messages: Messages;
      }
      
    5. рдХреНрд▓рд╛рдЗрдВрдЯ рдкреНрд░реЛрд╡рд╛рдЗрдбрд░ рдмрдирд╛рдПрдВ

      Client Components рдПрдХ React context рд╕реЗ рдЕрдиреБрд╡рд╛рдж рдкрдврд╝рддреЗ рд╣реИрдВред рдкреНрд░реЛрд╡рд╛рдЗрдбрд░ рд╕рд░реНрд╡рд░ рд▓реЗрдЖрдЙрдЯ рд╕реЗ рд╕рдХреНрд░рд┐рдп рд▓реЛрдХреЗрд▓ рдХрд╛ рдХреИрдЯрд▓реЙрдЧ рдкреНрд░рд╛рдкреНрдд рдХрд░рддрд╛ рд╣реИ, рдФрд░ рдЕрдкрдирд╛ рдЦреБрдж рдХрд╛ рдЗрдВрд╕реНрдЯреЗрдВрд╕ рдПрдХ рдмрд╛рд░ рдмрдирд╛рддрд╛ рд╣реИред

      src/components/LinguiClientProvider.tsx
      "use client";
      
      import { type Messages, setupI18n } from "@lingui/core";
      import { I18nProvider } from "@lingui/react";
      import { type ReactNode, useState } from "react";
      
      type LinguiClientProviderProps = {
        children: ReactNode;
        initialLocale: string;
        initialMessages: Messages;
      };
      
      export const LinguiClientProvider = ({
        children,
        initialLocale,
        initialMessages,
      }: LinguiClientProviderProps) => {
        const [i18n] = useState(() =>
          setupI18n({
            locale: initialLocale,
            messages: { [initialLocale]: initialMessages },
          })
        );
      
        return <I18nProvider i18n={i18n}>{children}</I18nProvider>;
      };
      
    6. рдбрд╛рдпрдиреЗрдорд┐рдХ рд▓реЛрдХреЗрд▓ рд░реВрдЯреНрд╕ рдкрд░рд┐рднрд╛рд╖рд┐рдд рдХрд░реЗрдВ

      [locale] рд╕реЗрдЧрдореЗрдВрдЯ рд░реВрдЯ рд▓реЗрдЖрдЙрдЯ рдХреЛ рд░рдЦрддрд╛ рд╣реИред generateStaticParams рдмрд┐рд▓реНрдб рдЯрд╛рдЗрдо рдкрд░ рдкреНрд░рддреНрдпреЗрдХ рд▓реЛрдХреЗрд▓ рдХреЛ рдкреНрд░реА-рд░реЗрдВрдбрд░ рдХрд░рддрд╛ рд╣реИ, рдФрд░ dynamicParams = false рдХрд┐рд╕реА рдЕрдиреНрдп рдкреНрд░реАрдлрд╝рд┐рдХреНрд╕ рдХреЗ рд▓рд┐рдП 404 рд▓реМрдЯрд╛рддрд╛ рд╣реИред

      src/app/[locale]/layout.tsx
      import type { Metadata } from "next";
      import { notFound } from "next/navigation";
      import { LinguiClientProvider } from "@/components/LinguiClientProvider";
      import { LocaleSwitcher } from "@/components/LocaleSwitcher";
      import { getMessages } from "@/i18n/appRouterI18n";
      import { getTextDirection, isLocale, locales, siteUrl } from "@/i18n/config";
      import { initLingui } from "@/i18n/initLingui";
      
      export const generateStaticParams = () => locales.map((locale) => ({ locale }));
      
      // Unknown prefixes (/xx/about) тЖТ 404
      export const dynamicParams = false;
      
      export const metadata: Metadata = {
        // Resolves relative canonical and Open Graph URLs
        metadataBase: new URL(siteUrl),
      };
      
      const LocaleLayout = async ({ children, params }: LayoutProps<"/[locale]">) => {
        const { locale } = await params;
      
        if (!isLocale(locale)) notFound();
      
        initLingui(locale);
      
        return (
          <html lang={locale} dir={getTextDirection(locale)}>
            <body>
              <LinguiClientProvider
                initialLocale={locale}
                initialMessages={getMessages(locale)}
              >
                <header>
                  <LocaleSwitcher />
                </header>
                <main>{children}</main>
              </LinguiClientProvider>
            </body>
          </html>
        );
      };
      
      export default LocaleLayout;
      
      рдХреНрд▓рд╛рдЗрдВрдЯ рдкреНрд░реЛрд╡рд╛рдЗрдбрд░ рд╕рдХреНрд░рд┐рдп рд▓реЛрдХреЗрд▓ рдХрд╛ рдкреВрд░рд╛ рдХреИрдЯрд▓реЙрдЧ рдкреНрд░рд╛рдкреНрдд рдХрд░рддрд╛ рд╣реИред рдмреЗрдВрдЪрдорд╛рд░реНрдХ рдЗрд╕реЗ "рдЕрдиреНрдп-рдкреЗрдЬ рд▓реАрдХ" рдХреЗ рд░реВрдк рдореЗрдВ рдорд╛рдкрддрд╛ рд╣реИред Server Components рдореЗрдВ рдЯреЗрдХреНрд╕реНрдЯ рд░рдЦрдиреЗ рд╕реЗ рд╡рд╣ рд╕реАрдорд┐рдд рд╣реЛ рдЬрд╛рддрд╛ рд╣реИ рдЬрд┐рд╕рдХреА рдХреНрд▓рд╛рдЗрдВрдЯ рдХреЛ рд╡рд╛рд╕реНрддрд╡ рдореЗрдВ рдЖрд╡рд╢реНрдпрдХрддрд╛ рд╣реЛрддреА рд╣реИред рдмрдбрд╝реЗ рдРрдкреНрд╕ рдХреЗ рд▓рд┐рдП, Lingui рдХрд╛ рдкреНрд░рдпреЛрдЧрд╛рддреНрдордХ рдкреНрд░рддрд┐-рдкреЗрдЬ рдПрдХреНрд╕рдЯреНрд░реИрдХреНрдЯрд░ (lingui.config.ts рдореЗрдВ experimental.extractor) рдПрдВрдЯреНрд░реА рдкреЙрдЗрдВрдЯ рджреНрд╡рд╛рд░рд╛ рдХреИрдЯрд▓реЙрдЧ рдХреЛ рд╡рд┐рднрд╛рдЬрд┐рдд рдХрд░рддрд╛ рд╣реИред
    7. Server Components рдореЗрдВ рдЕрдиреБрд╡рд╛рдж рдХрд╛ рдЙрдкрдпреЛрдЧ рдХрд░реЗрдВ

      Server Components, Client Components рдХреЗ рд╕рдорд╛рди рд╣реА рдореИрдХреНрд░реЛрдЬрд╝ рдХрд╛ рдЙрдкрдпреЛрдЧ рдХрд░рддреЗ рд╣реИрдВред initLingui рдХреЛ рдкреЗрдЬ рдореЗрдВ рднреА рдЪрд▓рдирд╛ рдЪрд╛рд╣рд┐рдП, рдХреНрдпреЛрдВрдХрд┐ рдПрдХ рд▓реЗрдЖрдЙрдЯ рдЕрдкрдиреЗ рдкреЗрдЬреЛрдВ рдХреЗ рдмреАрдЪ рдиреЗрд╡рд┐рдЧреЗрдЯ рдХрд░рддреЗ рд╕рдордп рдлрд┐рд░ рд╕реЗ рд░реЗрдВрдбрд░ рдирд╣реАрдВ рд╣реЛрддрд╛ рд╣реИред

      src/app/[locale]/about/page.tsx
      import { Trans, useLingui } from "@lingui/react/macro";
      import { Counter } from "@/components/Counter";
      import { resolveLocale } from "@/i18n/config";
      import { initLingui } from "@/i18n/initLingui";
      
      const AboutPage = async ({ params }: PageProps<"/[locale]/about">) => {
        const { locale } = await params;
      
        initLingui(resolveLocale(locale));
      
        return <AboutContent />;
      };
      
      const AboutContent = () => {
        const { t } = useLingui();
      
        return (
          <section aria-label={t`About section`}>
            <h1>
              <Trans>About us</Trans>
            </h1>
            <p>
              <Trans>
                We build <strong>fast</strong>, multilingual applications.
              </Trans>
            </p>
            <Counter />
          </section>
        );
      };
      
      export default AboutPage;
      
    8. Client Components рдореЗрдВ рдЕрдиреБрд╡рд╛рдж рдХрд╛ рдЙрдкрдпреЛрдЧ рдХрд░реЗрдВ

      Client Components рд╕рдорд╛рди рдЖрдпрд╛рдд рдХрд╛ рдЙрдкрдпреЛрдЧ рдХрд░рддреЗ рд╣реИрдВред рдореИрдХреНрд░реЛрдЬрд╝ LinguiClientProvider рд╕реЗ рдЗрдВрд╕реНрдЯреЗрдВрд╕ рдкрдврд╝рддреЗ рд╣реИрдВред

      src/components/Counter.tsx
      "use client";
      
      import { Plural, Trans, useLingui } from "@lingui/react/macro";
      import { useState } from "react";
      
      export const Counter = () => {
        const { t, i18n } = useLingui();
        const [count, setCount] = useState(0);
      
        return (
          <div>
            <p>
              <Plural
                value={count}
                _0="No clicks yet"
                one="# click"
                other="# clicks"
              />
            </p>
            <p>{i18n.number(count)}</p>
            <button
              type="button"
              aria-label={t`Counter`}
              onClick={() => setCount((value) => value + 1)}
            >
              <Trans>Increment</Trans>
            </button>
          </div>
        );
      };
      
    9. рдЕрдкрдиреЗ рд╕рдВрджреЗрд╢ рдирд┐рдХрд╛рд▓реЗрдВ рдФрд░ рдЕрдиреБрд╡рд╛рдж рдХрд░реЗрдВ

      рдПрдХреНрд╕рдЯреНрд░реИрдХреНрд╢рди рдЪрд▓рд╛рдПрдВред Lingui src рдореЗрдВ рдкрд╛рдП рдЧрдП рдкреНрд░рддреНрдпреЗрдХ рд╕рдВрджреЗрд╢ рдХреЛ рдкреНрд░рддреНрдпреЗрдХ рд▓реЛрдХреЗрд▓ рдХреИрдЯрд▓реЙрдЧ рдореЗрдВ рд▓рд┐рдЦрддрд╛ рд╣реИ:

      bash
      npm run i18n:extract
      

      рдлрд┐рд░ рдкреНрд░рддреНрдпреЗрдХ рдкреНрд░рд╡рд┐рд╖реНрдЯрд┐ рдХреЗ msgstr рдХрд╛ рдЕрдиреБрд╡рд╛рдж рдХрд░реЗрдВ:

      src/locales/fr/messages.po
      msgid "About us"
      msgstr "├А propos"
      
      msgid "We build <0>fast</0>, multilingual applications."
      msgstr "Nous cr├йons des applications <0>rapides</0> et multilingues."
      
      msgid "Learn who we are and why we built this application."
      msgstr "D├йcouvrez qui nous sommes et pourquoi nous avons cr├й├й cette application."
      
      msgid "{count, plural, =0 {No clicks yet} one {# click} other {# clics}}"
      msgstr "{count, plural, =0 {Aucun clic} one {# clic} other {# clics}}"
      
      src/locales/es/messages.po
      msgid "About us"
      msgstr "Sobre nosotros"
      
      msgid "We build <0>fast</0>, multilingual applications."
      msgstr "Creamos aplicaciones <0>r├бpidas</0> y multiling├╝es."
      
      msgid "Learn who we are and why we built this application."
      msgstr "Descubre qui├йnes somos y por qu├й creamos esta aplicaci├│n."
      
      msgid "{count, plural, =0 {No clicks yet} one {# click} other {# clics}}"
      msgstr "{count, plural, =0 {Ning├║n clic} one {# clic} other {# clics}}"
      
      <0> рдкреНрд▓реЗрд╕рд╣реЛрд▓реНрдбрд░ <Trans> рдХреЗ JSX рддрддреНрд╡реЛрдВ рдХреЛ рдпрдерд╛рд╕реНрдерд╛рди рд░рдЦрддреЗ рд╣реИрдВ, рддрд╛рдХрд┐ рдЕрдиреБрд╡рд╛рджрдХ рдорд╛рд░реНрдХрдЕрдк рдХреЛ рдЫреБрдП рдмрд┐рдирд╛ рдЙрдиреНрд╣реЗрдВ рд╕реНрдерд╛рдирд╛рдВрддрд░рд┐рдд рдХрд░ рд╕рдХреЗрдВред
    10. рд▓реЛрдХреЗрд▓ рд░реВрдЯрд┐рдВрдЧ рдХреЗ рд▓рд┐рдП рдкреНрд░реЙрдХреНрд╕реА рд╕реЗрдЯ рдХрд░реЗрдВ

      рд╡реИрдХрд▓реНрдкрд┐рдХ

      Next.js 16 рдиреЗ middleware.ts рдХрд╛ рдирд╛рдо рдмрджрд▓рдХрд░ proxy.ts рдХрд░ рджрд┐рдпрд╛ред рдкреНрд░реЙрдХреНрд╕реА "рдЖрд╡рд╢реНрдпрдХрддрд╛рдиреБрд╕рд╛рд░" рдкреНрд░реАрдлрд╝рд┐рдХреНрд╕ рд░рдгрдиреАрддрд┐ рд▓рд╛рдЧреВ рдХрд░рддрд╛ рд╣реИ:

      • /fr/about рдХреЛ рд╡реИрд╕реЗ рд╣реА рд╕рд░реНрд╡ рдХрд┐рдпрд╛ рдЬрд╛рддрд╛ рд╣реИ;
      • /en/about /about рдкрд░ рдкреБрдирд░реНрдирд┐рд░реНрджреЗрд╢рд┐рдд (redirect) рдХрд░рддрд╛ рд╣реИ, рдЗрд╕рд▓рд┐рдП рдбрд┐рдлрд╝реЙрд▓реНрдЯ рд▓реЛрдХреЗрд▓ рдХрд╛ рдПрдХ рд╣реА URL рд╣реЛрддрд╛ рд╣реИ;
      • /about рдХреЛ URL рдмрджрд▓реЗ рдмрд┐рдирд╛ рдЖрдВрддрд░рд┐рдХ рд░реВрдк рд╕реЗ /en/about рдкрд░ рдлрд┐рд░ рд╕реЗ рд▓рд┐рдЦрд╛ (rewrite) рдЬрд╛рддрд╛ рд╣реИ;
      • / рдкрд░ рдкрд╣рд▓реА рд╡рд┐рдЬрд╝рд┐рдЯ рдкрд╕рдВрджреАрджрд╛ рднрд╛рд╖рд╛ рдкрд░ рдкреБрдирд░реНрдирд┐рд░реНрджреЗрд╢рд┐рдд рдХрд░рддреА рд╣реИ (рдкрд╣рд▓реЗ рдХреБрдХреА, рдлрд┐рд░ Accept-Language)ред
      src/i18n/negotiateLocale.ts
      import { isLocale, type Locale } from "./config";
      
      /** "fr-CA,fr;q=0.9,en;q=0.8" тЖТ "fr" */
      export const negotiateLocale = (
        acceptLanguage: string | null | undefined
      ): Locale | undefined => {
        if (!acceptLanguage) return undefined;
      
        return acceptLanguage
          .split(",")
          .map((part) => {
            const [tag = "", quality] = part.trim().split(";q=");
      
            return {
              language: tag.toLowerCase().split("-")[0],
              quality: quality ? Number(quality) : 1,
            };
          })
          .sort((first, second) => second.quality - first.quality)
          .map(({ language }) => language)
          .find(isLocale);
      };
      
      src/proxy.ts
      import { type NextRequest, NextResponse } from "next/server";
      import {
        defaultLocale,
        isLocale,
        localeCookieName,
        localizePath,
        stripLocale,
      } from "@/i18n/config";
      import { negotiateLocale } from "@/i18n/negotiateLocale";
      
      export const proxy = (request: NextRequest) => {
        const { pathname } = request.nextUrl;
        const firstSegment = pathname.split("/")[1];
        const url = request.nextUrl.clone();
      
        if (isLocale(firstSegment)) {
          // /en/about тЖТ /about: one URL for the default locale
          if (firstSegment === defaultLocale) {
            url.pathname = stripLocale(pathname);
      
            return NextResponse.redirect(url, 308);
          }
      
          return NextResponse.next();
        }
      
        // First visit on "/": send the visitor to their language
        if (pathname === "/") {
          const cookieLocale = request.cookies.get(localeCookieName)?.value;
          const preferredLocale = isLocale(cookieLocale)
            ? cookieLocale
            : negotiateLocale(request.headers.get("accept-language"));
      
          if (preferredLocale && preferredLocale !== defaultLocale) {
            url.pathname = localizePath("/", preferredLocale);
      
            return NextResponse.redirect(url, 307);
          }
        }
      
        // /about тЖТ served by /en/about, URL unchanged
        url.pathname = `/${defaultLocale}${pathname === "/" ? "" : pathname}`;
      
        return NextResponse.rewrite(url);
      };
      
      export const config = {
        // Skip API routes, Next.js internals and files (sitemap.xml, robots.txt...)
        matcher: ["/((?!api|_next|.*\\..*).*)"],
      };
      
    11. рдЕрдкрдиреА рд╕рд╛рдордЧреНрд░реА рдХреА рднрд╛рд╖рд╛ рдмрджрд▓реЗрдВ

      рд╡реИрдХрд▓реНрдкрд┐рдХ

      usePathname рдмреНрд░рд╛рдЙрдЬрд╝рд░ рджреНрд╡рд╛рд░рд╛ рджреЗрдЦреЗ рдЧрдП URL (/about рдпрд╛ /fr/about) рдХреЛ рд▓реМрдЯрд╛рддрд╛ рд╣реИред рд▓реЛрдХреЗрд▓ рдХреЛ рд╣рдЯрд╛ рджреЗрдВ, рдлрд┐рд░ рдкреНрд░рддреНрдпреЗрдХ рднрд╛рд╖рд╛ рдХрд╛ рд▓рд┐рдВрдХ рдмрдирд╛рдПрдВред рд╕реНрд╡рд┐рдЪрд░ рд╡рд╛рд╕реНрддрд╡рд┐рдХ рд▓рд┐рдВрдХ рд░реЗрдВрдбрд░ рдХрд░рддрд╛ рд╣реИ, рдЬрд┐рд╕рд╕реЗ рдХреНрд░реЙрд▓рд░ рдкреНрд░рддреНрдпреЗрдХ рднрд╛рд╖рд╛ рд╕рдВрд╕реНрдХрд░рдг рддрдХ рдкрд╣реБрдВрдЪ рд╕рдХрддреЗ рд╣реИрдВ, рдФрд░ рдХреБрдХреА рд╕реНрдкрд╖реНрдЯ рд╡рд┐рдХрд▓реНрдк рдХреЛ рдпрд╛рдж рд░рдЦрддреА рд╣реИред

      src/components/LocaleSwitcher.tsx
      "use client";
      
      import { useLingui } from "@lingui/react/macro";
      import Link from "next/link";
      import { usePathname } from "next/navigation";
      import {
        getLocaleName,
        type Locale,
        localeCookieName,
        locales,
        localizePath,
        stripLocale,
      } from "@/i18n/config";
      
      const persistLocale = (locale: Locale) => {
        document.cookie = `${localeCookieName}=${locale}; Path=/; Max-Age=31536000; SameSite=Lax`;
      };
      
      export const LocaleSwitcher = () => {
        const { i18n, t } = useLingui();
        const basePath = stripLocale(usePathname());
      
        return (
          <nav aria-label={t`Change language`}>
            <ul>
              {locales.map((locale) => (
                <li key={locale}>
                  <Link
                    href={localizePath(basePath, locale)}
                    hrefLang={locale}
                    lang={locale}
                    aria-current={locale === i18n.locale ? "page" : undefined}
                    onClick={() => persistLocale(locale)}
                  >
                    {getLocaleName(locale)}
                  </Link>
                </li>
              ))}
            </ul>
          </nav>
        );
      };
      
    12. рдПрдХ рд╕реНрдерд╛рдиреАрдпрдХреГрдд рд▓рд┐рдВрдХ рдШрдЯрдХ рдмрдирд╛рдПрдВ

      рд╡реИрдХрд▓реНрдкрд┐рдХ
      src/components/LocalizedLink.tsx
      "use client";
      
      import { useLingui } from "@lingui/react";
      import Link from "next/link";
      import type { ComponentProps } from "react";
      import { type Locale, localizePath } from "@/i18n/config";
      
      type LocalizedLinkProps = Omit<ComponentProps<typeof Link>, "href"> & {
        /** Path without locale prefix, e.g. "/about" */
        href: string;
      };
      
      export const LocalizedLink = ({ href, ...props }: LocalizedLinkProps) => {
        const { i18n } = useLingui();
      
        return <Link href={localizePath(href, i18n.locale as Locale)} {...props} />;
      };
      

      рдпрд╣ Server Components рд╕реЗ рднреА рдХрд╛рдо рдХрд░рддрд╛ рд╣реИ, рдХреНрдпреЛрдВрдХрд┐ рдпрд╣ LinguiClientProvider рдХреЗ рдЕрдВрджрд░ рд░реЗрдВрдбрд░ рд╣реЛрддрд╛ рд╣реИ:

      tsx
      <LocalizedLink href="/about">
        <Trans>About us</Trans>
      </LocalizedLink>
      
    13. рдЕрдкрдиреЗ рдореЗрдЯрд╛рдбреЗрдЯрд╛ рдХрд╛ рдЕрдВрддрд░реНрд░рд╛рд╖реНрдЯреНрд░реАрдпрдХрд░рдг рдХрд░реЗрдВ

      рд╡реИрдХрд▓реНрдкрд┐рдХ

      рдкреНрд░рддреНрдпреЗрдХ рднрд╛рд╖рд╛ рд╕рдВрд╕реНрдХрд░рдг рдЕрдкрдиреЗ рджрдо рдкрд░ рд░реИрдВрдХ рдХрд░ рд╕рдХрддрд╛ рд╣реИ, рдмрд╢рд░реНрддреЗ рдкреНрд░рддреНрдпреЗрдХ рдкреЗрдЬ рдирд┐рдореНрдирд▓рд┐рдЦрд┐рдд рдкреНрд░рджрд░реНрд╢рд┐рдд рдХрд░реЗ:

      • рдПрдХ рдЕрдиреБрд╡рд╛рджрд┐рдд title рдФрд░ description;
      • рд╕реНрд╡рдпрдВ рдХреА рдУрд░ рдЗрд╢рд╛рд░рд╛ рдХрд░рдиреЗ рд╡рд╛рд▓рд╛ рдПрдХ canonical URL;
      • рдкреНрд░рддрд┐ рд▓реЛрдХреЗрд▓ рдПрдХ hreflang alternate, рд╕рд╛рде рд╣реА x-default;
      • Open Graph locale, alternateLocale рдФрд░ url;
      • inLanguage рдХреЗ рд╕рд╛рде JSON-LDред

      generateMetadata React рдЯреНрд░реА рдХреЗ рдмрд╛рд╣рд░ рдЪрд▓рддрд╛ рд╣реИ, рдЗрд╕рд▓рд┐рдП рдпрд╣ msg рдореИрдХреНрд░реЛ рдХреЗ рд╕рд╛рде рд╕реАрдзреЗ рд╕рд░реНрд╡рд░ рдЗрдВрд╕реНрдЯреЗрдВрд╕ рдХрд╛ рдЙрдкрдпреЛрдЧ рдХрд░рддрд╛ рд╣реИ:

      src/i18n/metadata.ts
      import type { Metadata } from "next";
      import {
        defaultLocale,
        getAbsoluteUrl,
        type Locale,
        locales,
        openGraphLocales,
      } from "./config";
      
      type LocalizedMetadataOptions = {
        /** Path without locale prefix, e.g. "/about" */
        path: string;
        locale: Locale;
        title: string;
        description: string;
      };
      
      export const buildLocalizedMetadata = ({
        path,
        locale,
        title,
        description,
      }: LocalizedMetadataOptions): Metadata => {
        const url = getAbsoluteUrl(path, locale);
      
        return {
          title,
          description,
          alternates: {
            canonical: url,
            languages: {
              ...Object.fromEntries(
                locales.map((alternateLocale) => [
                  alternateLocale,
                  getAbsoluteUrl(path, alternateLocale),
                ])
              ),
              "x-default": getAbsoluteUrl(path, defaultLocale),
            },
          },
          openGraph: {
            type: "website",
            title,
            description,
            url,
            locale: openGraphLocales[locale],
            alternateLocale: locales
              .filter((alternateLocale) => alternateLocale !== locale)
              .map((alternateLocale) => openGraphLocales[alternateLocale]),
          },
        };
      };
      
      src/app/[locale]/about/page.tsx
      import { msg } from "@lingui/core/macro";
      import type { Metadata } from "next";
      import { getI18nInstance } from "@/i18n/appRouterI18n";
      import { resolveLocale } from "@/i18n/config";
      import { buildLocalizedMetadata } from "@/i18n/metadata";
      
      export const generateMetadata = async ({
        params,
      }: PageProps<"/[locale]/about">): Promise<Metadata> => {
        const locale = resolveLocale((await params).locale);
        const i18n = getI18nInstance(locale);
      
        return buildLocalizedMetadata({
          path: "/about",
          locale,
          title: i18n._(msg`About us`),
          description: i18n._(
            msg`Learn who we are and why we built this application.`
          ),
        });
      };
      
      // ... page component from step 7
      

      JSON-LD рд╕реНрд╡рдпрдВ рдкреЗрдЬ рджреНрд╡рд╛рд░рд╛ рд░реЗрдВрдбрд░ рдХрд┐рдпрд╛ рдЬрд╛рддрд╛ рд╣реИред рдкреЗрдЬ рдлрд╝рд╛рдЗрд▓реЗрдВ рдХреЗрд╡рд▓ Next.js рдлрд╝реАрд▓реНрдб рдирд┐рд░реНрдпрд╛рдд рдХрд░ рд╕рдХрддреА рд╣реИрдВ, рдЗрд╕рд▓рд┐рдП рдШрдЯрдХ рдХреЛ рдЙрд╕рдХреА рдЕрдкрдиреА рдлрд╝рд╛рдЗрд▓ рдореЗрдВ рд░рдЦреЗрдВ:

      src/components/WebPageJsonLd.tsx
      import { getAbsoluteUrl, type Locale } from "@/i18n/config";
      
      type WebPageJsonLdProps = {
        path: string;
        locale: Locale;
        title: string;
      };
      
      export const WebPageJsonLd = ({ path, locale, title }: WebPageJsonLdProps) => (
        <script
          type="application/ld+json"
          dangerouslySetInnerHTML={{
            __html: JSON.stringify({
              "@context": "https://schema.org",
              "@type": "WebPage",
              name: title,
              url: getAbsoluteUrl(path, locale),
              inLanguage: locale,
            }),
          }}
        />
      );
      
      src/app/[locale]/about/page.tsx
      // In AboutContent
      <WebPageJsonLd
        path="/about"
        locale={i18n.locale as Locale}
        title={t`About us`}
      />
      
    14. рдЕрдкрдиреЗ рд╕рд╛рдЗрдЯрдореИрдк рдХрд╛ рдЕрдВрддрд░реНрд░рд╛рд╖реНрдЯреНрд░реАрдпрдХрд░рдг рдХрд░реЗрдВ

      рд╡реИрдХрд▓реНрдкрд┐рдХ

      sitemap.ts рдкрд░рдВрдкрд░рд╛ alternates.languages рдХрд╛ рд╕рдорд░реНрдерди рдХрд░рддреА рд╣реИ, рдЬрд┐рд╕реЗ Next.js xhtml:link рдЕрд▓реНрдЯрд░рдиреЗрдЯ рдХреЗ рд░реВрдк рдореЗрдВ рд░реЗрдВрдбрд░ рдХрд░рддрд╛ рд╣реИред рдкреНрд░рддреНрдпреЗрдХ рд▓реЛрдХреЗрд▓ рдХреЗ рдкреНрд░рддреНрдпреЗрдХ URL рдХреЛ рд╕реВрдЪреАрдмрджреНрдз рдХрд░реЗрдВ:

      src/app/sitemap.ts
      import type { MetadataRoute } from "next";
      import { defaultLocale, getAbsoluteUrl, locales } from "@/i18n/config";
      
      type SitemapPage = {
        path: string;
        changeFrequency: "daily" | "weekly" | "monthly";
        priority: number;
      };
      
      const sitemapPages: SitemapPage[] = [
        { path: "/", changeFrequency: "daily", priority: 1.0 },
        { path: "/about", changeFrequency: "monthly", priority: 0.8 },
      ];
      
      const getAlternateLanguages = (path: string) => ({
        ...Object.fromEntries(
          locales.map((locale) => [locale, getAbsoluteUrl(path, locale)])
        ),
        "x-default": getAbsoluteUrl(path, defaultLocale),
      });
      
      const sitemap = (): MetadataRoute.Sitemap =>
        sitemapPages.flatMap(({ path, changeFrequency, priority }) =>
          locales.map((locale) => ({
            url: getAbsoluteUrl(path, locale),
            lastModified: new Date(),
            changeFrequency,
            priority,
            alternates: { languages: getAlternateLanguages(path) },
          }))
        );
      
      export default sitemap;
      
    15. рдЕрдкрдиреЗ robots.txt рдХрд╛ рдЕрдВрддрд░реНрд░рд╛рд╖реНрдЯреНрд░реАрдпрдХрд░рдг рдХрд░реЗрдВ

      рд╡реИрдХрд▓реНрдкрд┐рдХ

      рдирд┐рдЬреА рд░реВрдЯреНрд╕ рдкреНрд░рддреНрдпреЗрдХ рднрд╛рд╖рд╛ рдореЗрдВ рдореМрдЬреВрдж рд╣реЛрддреЗ рд╣реИрдВ, рдЗрд╕рд▓рд┐рдП disallow рдХреЛ рдкреНрд░рддреНрдпреЗрдХ рд╕реНрдерд╛рдиреАрдпрдХреГрдд рдкрде рдХреЛ рдХрд╡рд░ рдХрд░рдирд╛ рдЪрд╛рд╣рд┐рдП:

      src/app/robots.ts
      import type { MetadataRoute } from "next";
      import { locales, localizePath, siteUrl } from "@/i18n/config";
      
      const privatePaths = ["/dashboard", "/admin"];
      
      const robots = (): MetadataRoute.Robots => ({
        rules: {
          userAgent: "*",
          allow: "/",
          // /dashboard, /fr/dashboard, /es/dashboard...
          disallow: privatePaths.flatMap((path) =>
            locales.map((locale) => localizePath(path, locale))
          ),
        },
        sitemap: `${siteUrl}/sitemap.xml`,
      });
      
      export default robots;
      
    16. рд╕реНрдерд╛рдиреАрдпрдХреГрдд 404 рдкреЗрдЬреЛрдВ рдХреЛ рд╕рдВрднрд╛рд▓реЗрдВ

      рд╡реИрдХрд▓реНрдкрд┐рдХ

      not-found.tsx [locale] рд▓реЗрдЖрдЙрдЯ рдХреЗ рдЕрдВрджрд░ рд░реЗрдВрдбрд░ рд╣реЛрддрд╛ рд╣реИ, рдЗрд╕рд▓рд┐рдП рдЗрд╕рдХреЗ рдкрд╛рд╕ рдХреНрд▓рд╛рдЗрдВрдЯ рдкреНрд░реЛрд╡рд╛рдЗрдбрд░ рддрдХ рдкрд╣реБрдВрдЪ рд╣реЛрддреА рд╣реИред рдХреИрдЪ-рдСрд▓ рд░реВрдЯ рдХрд┐рд╕реА рд▓реЛрдХреЗрд▓ рдХреЗ рдЕрдВрджрд░ рдЕрдЬреНрдЮрд╛рдд рдкрдереЛрдВ рдХреЛ рдЗрд╕ рдкрд░ рднреЗрдЬрддрд╛ рд╣реИред Next.js 404 рдкреНрд░рддрд┐рдХреНрд░рд┐рдпрд╛рдУрдВ рдореЗрдВ рд╕реНрд╡рдЪрд╛рд▓рд┐рдд рд░реВрдк рд╕реЗ noindex рдЬреЛрдбрд╝рддрд╛ рд╣реИред

      src/app/[locale]/not-found.tsx
      "use client";
      
      import { Trans } from "@lingui/react/macro";
      import { LocalizedLink } from "@/components/LocalizedLink";
      
      const NotFound = () => (
        <div>
          <h1>
            <Trans>Page not found</Trans>
          </h1>
          <LocalizedLink href="/">
            <Trans>Back to home</Trans>
          </LocalizedLink>
        </div>
      );
      
      export default NotFound;
      
      src/app/[locale]/[...rest]/page.tsx
      import { notFound } from "next/navigation";
      
      // /fr/does/not/exist тЖТ localized not-found.tsx
      const CatchAllPage = () => notFound();
      
      export default CatchAllPage;
      
    17. Server Actions рдореЗрдВ рд▓реЛрдХреЗрд▓ рддрдХ рдкрд╣реБрдВрдЪреЗрдВ

      рд╡реИрдХрд▓реНрдкрд┐рдХ

      Server Actions рдХреЛ рд░реВрдЯ рдкреИрд░рд╛рдореАрдЯрд░ рдкреНрд░рд╛рдкреНрдд рдирд╣реАрдВ рд╣реЛрддреЗ рд╣реИрдВред рд╕рдмрд╕реЗ рд╡рд┐рд╢реНрд╡рд╕рдиреАрдп рддрд░реАрдХрд╛ рдпрд╣ рд╣реИ рдХрд┐ рдЙрд╕ рдкреЗрдЬ рд╕реЗ рдлрд╝реЙрд░реНрдо рдХреЗ рд╕рд╛рде рд▓реЛрдХреЗрд▓ рднреЗрдЬреЗрдВ рдЬрд┐рд╕реЗ рдпрд╣ рдкрддрд╛ рд╣реИ:

      src/app/[locale]/contact/page.tsx
      import { Trans } from "@lingui/react/macro";
      import { sendContactMessage } from "@/app/actions/sendContactMessage";
      import { resolveLocale } from "@/i18n/config";
      import { initLingui } from "@/i18n/initLingui";
      
      const ContactPage = async ({ params }: PageProps<"/[locale]/contact">) => {
        const locale = resolveLocale((await params).locale);
      
        initLingui(locale);
      
        return (
          <form action={sendContactMessage}>
            <input type="hidden" name="locale" value={locale} />
            <textarea name="message" />
            <button type="submit">
              <Trans>Send</Trans>
            </button>
          </form>
        );
      };
      
      export default ContactPage;
      
      src/app/actions/sendContactMessage.ts
      "use server";
      
      import { msg } from "@lingui/core/macro";
      import { getI18nInstance } from "@/i18n/appRouterI18n";
      import { resolveLocale } from "@/i18n/config";
      
      export const sendContactMessage = async (formData: FormData) => {
        const locale = resolveLocale(formData.get("locale")?.toString());
        const i18n = getI18nInstance(locale);
      
        const subject = i18n._(msg`Thanks for your message`);
      
        // await mailer.send({ subject, locale, ... });
        console.log(`[${locale}] ${subject}`);
      };
      
    18. рдЕрдкрдиреЗ рдореИрдХреНрд░реЛрдЬрд╝ рдмрдирд╛рдП рд░рдЦреЗрдВ, Intlayer рдХреЗ рд╕рд╛рде рд░рдирдЯрд╛рдЗрдо рдШрдЯрд╛рдПрдВ

      рд╡реИрдХрд▓реНрдкрд┐рдХ

      @intlayer/lingui рдХрдореНрдкреИрдЯ рдЕрдбреИрдкреНрдЯрд░ рдЖрдкрдХреЗ рд╕реНрд░реЛрдд рдХреЛ рдЕрдЫреВрддрд╛ рд░рдЦрддрд╛ рд╣реИ: рдореИрдХреНрд░реЛрдЬрд╝ рдкрд╣рд▓реЗ рдХреА рддрд░рд╣ рд╕рдВрдХрд▓рд┐рдд рд╣реЛрддреЗ рд╣реИрдВ, рдФрд░ рдкрд░рд┐рдгрд╛рдореА i18n._(), useLingui() рдФрд░ <Trans> рдХреЙрд▓ Intlayer рд╢рдмреНрджрдХреЛрд╢реЛрдВ рджреНрд╡рд╛рд░рд╛ рд╕реЗрд╡рд┐рдд рдХрд┐рдП рдЬрд╛рддреЗ рд╣реИрдВред Next.js рдмреЗрдВрдЪрдорд╛рд░реНрдХ рдореЗрдВ, рд░рдирдЯрд╛рдЗрдо ~72.1 KB рд╕реЗ рдШрдЯрдХрд░ ~10.7 KB gzip рд╣реЛ рдЬрд╛рддрд╛ рд╣реИред

      Next.js рдкрд░, рдЕрдбреИрдкреНрдЯрд░ рдХреЛ next.config.ts (webpack рдФрд░ Turbopack) рдореЗрдВ @lingui/core рдФрд░ @lingui/react рдХреЛ @intlayer/lingui рдореЗрдВ рдЙрдкрдирд╛рдо (alias) рджреЗрдХрд░, рдФрд░ рдХреЙрдиреНрдлрд╝рд┐рдЧ рдХреЛ next-intlayer/server рд╕реЗ withIntlayer рдХреЗ рд╕рд╛рде рд▓рдкреЗрдЯрдХрд░ рдЬреЛрдбрд╝рд╛ рдЬрд╛рддрд╛ рд╣реИред @lingui/swc-plugin рдмрдирд╛рдП рд░рдЦреЗрдВ рддрд╛рдХрд┐ рдореИрдХреНрд░реЛрдЬрд╝ рдЕрднреА рднреА рдкрд╣рд▓реЗ рд╕рдВрдХрд▓рд┐рдд рд╣реЛрдВред рдкреВрд░рд╛ рдХреЙрдиреНрдлрд╝рд┐рдЧрд░реЗрд╢рди Lingui рдХрдореНрдкреИрдЯ рдЧрд╛рдЗрдб рдореЗрдВ рд╣реИред

      рдЬреИрд╕рд╛ рдХрд┐ рдмреЗрдВрдЪрдорд╛рд░реНрдХ рддрд╛рд▓рд┐рдХрд╛ рд╕реЗ рдкрддрд╛ рдЪрд▓рддрд╛ рд╣реИ, рдЕрдбреИрдкреНрдЯрд░ рд░рдирдЯрд╛рдЗрдо рдХреЛ рдХрдо рдХрд░рддрд╛ рд╣реИ рд▓реЗрдХрд┐рди Next.js рдкрд░ рдкреНрд░рддреНрдпреЗрдХ рдкреЗрдЬ рдкрд░ рднреЗрдЬреЗ рдЬрд╛рдиреЗ рд╡рд╛рд▓реЗ рдХреИрдЯрд▓реЙрдЧ рдХреЛ рдЕрднреА рдХрдо рдирд╣реАрдВ рдХрд░рддрд╛ рд╣реИред рдЗрд╕рдХрд╛ рд╕рдмрд╕реЗ рдЕрдЪреНрдЫрд╛ рдЙрдкрдпреЛрдЧ рдкреНрд░рд╡рд╛рд╕рди рд╕реЗрддреБ (migration bridge) рдХреЗ рд░реВрдк рдореЗрдВ рдХрд┐рдпрд╛ рдЬрд╛рддрд╛ рд╣реИ: рдПрдХ рдмрд╛рд░ рдЬрдм рдпрд╣ рдХрд╛рдо рдХрд░рдиреЗ рд▓рдЧреЗ, рддреЛ рдШрдЯрдХреЛрдВ рдХреЛ рдПрдХ-рдПрдХ рдХрд░рдХреЗ рдореВрд▓ useIntlayer API рдореЗрдВ рд╕реНрдерд╛рдирд╛рдВрддрд░рд┐рдд рдХрд░реЗрдВ, рдЬреЛ рдХреЗрд╡рд▓ рд╡рд╣реА рд╕рд╛рдордЧреНрд░реА рднреЗрдЬрддрд╛ рд╣реИ рдЬреЛ рдкреНрд░рддреНрдпреЗрдХ рдШрдЯрдХ рд░реЗрдВрдбрд░ рдХрд░рддрд╛ рд╣реИред Next.js + Intlayer рдЧрд╛рдЗрдб, Lingui рдмрдирд╛рдо @intlayer/lingui рдФрд░ рд╕рднреА рдХрдореНрдкреИрдЯ рдЕрдбреИрдкреНрдЯрд░ рджреЗрдЦреЗрдВред

    19. Intlayer рдХрд╛ рдЙрдкрдпреЛрдЧ рдХрд░рдХреЗ рдЕрдкрдиреЗ рдЕрдиреБрд╡рд╛рдж рд╕реНрд╡рдЪрд╛рд▓рд┐рдд рдХрд░реЗрдВ

      рд╡реИрдХрд▓реНрдкрд┐рдХ

      Lingui рд╕рдВрджреЗрд╢ рдирд┐рдХрд╛рд▓рддрд╛ рд╣реИ, рд▓реЗрдХрд┐рди рджрд░реНрдЬрдиреЛрдВ рдХреИрдЯрд▓реЙрдЧ рдХреЛ рд╣рд╛рде рд╕реЗ рднрд░рдирд╛ рд╡рд╣ рдЬрдЧрд╣ рд╣реИ рдЬрд╣рд╛рдБ рдЕрдзрд┐рдХрд╛рдВрд╢ рд╕рдордп рд╡реНрдпрддреАрдд рд╣реЛрддрд╛ рд╣реИред Intlayer рдирд┐рдГрд╢реБрд▓реНрдХ рдФрд░ рдУрдкрди рд╕реЛрд░реНрд╕ рд╣реИ, рдФрд░ рдЗрд╕рдХреЗ рдЯреВрд▓ Lingui рдХреЗ рд╕рд╛рде рдорд┐рд▓рдХрд░ рдХрд╛рдо рдХрд░рддреЗ рд╣реИрдВ:

      • рдЕрдкрдиреА рд╕реНрд╡рдпрдВ рдХреА API рдХреБрдВрдЬреА рдФрд░ рдкреНрд░рджрд╛рддрд╛ рдХрд╛ рдЙрдкрдпреЛрдЧ рдХрд░рдХреЗ AI рдХреЗ рд╕рд╛рде рдЕрдиреБрд╡рд╛рдж рдХрд░реЗрдВред рдСрдЯреЛ рдлрд┐рд▓ рдФрд░ CLI рджреЗрдЦреЗрдВред
      • sync PO рдкреНрд▓рдЧрдЗрди рдХреЗ рд╕рд╛рде рд╕рддреНрдп рдХреЗ рд╕реНрд░реЛрдд рдХреЗ рд░реВрдк рдореЗрдВ рдЕрдкрдиреА PO рдлрд╝рд╛рдЗрд▓реЗрдВ рд░рдЦреЗрдВред
      • CI рдореЗрдВ рд▓рд╛рдкрддрд╛ рдЕрдиреБрд╡рд╛рджреЛрдВ рдХрд╛ рдкрд░реАрдХреНрд╖рдг рдХрд░реЗрдВред рдЕрдкрдиреЗ рдЕрдиреБрд╡рд╛рджреЛрдВ рдХрд╛ рдкрд░реАрдХреНрд╖рдг рдХрд░рдирд╛ рджреЗрдЦреЗрдВред
      • scan рдХрдорд╛рдВрдб рдХреЗ рд╕рд╛рде рд▓рд╛рдкрддрд╛ hreflang, рдЧрд▓рдд canonical рдФрд░ рд▓реЛрдХреЗрд▓ рд▓реАрдХ рдХреЗ рд▓рд┐рдП рдЕрдкрдиреА рддреИрдирд╛рдд рд╕рд╛рдЗрдЯ рдХрд╛ рдСрдбрд┐рдЯ рдХрд░реЗрдВред

    рдЕрдХреНрд╕рд░ рдкреВрдЫреЗ рдЬрд╛рдиреЗ рд╡рд╛рд▓реЗ рдкреНрд░рд╢реНрди

    рд╣рд╛рдБред @lingui/react React Server Components рдХрд╛ рд╕рдорд░реНрдерди рдХрд░рддрд╛ рд╣реИред Server Components @lingui/react/server рд╕реЗ setI18n рдХреЗ рд╕рд╛рде рдЗрдВрд╕реНрдЯреЗрдВрд╕ рдкрдВрдЬреАрдХреГрдд рдХрд░рддреЗ рд╣реИрдВ, Client Components рдЗрд╕реЗ I18nProvider рд╕реЗ рдкрдврд╝рддреЗ рд╣реИрдВ, рдФрд░ рджреЛрдиреЛрдВ рд╕рдорд╛рди Trans рдФрд░ useLingui рдореИрдХреНрд░реЛрдЬрд╝ рдХрд╛ рдЙрдкрдпреЛрдЧ рдХрд░рддреЗ рд╣реИрдВред

    Server Components рдореЗрдВ рдХреЛрдИ context рдирд╣реАрдВ рд╣реЛрддрд╛ рд╣реИ, рдЗрд╕рд▓рд┐рдП рдЗрдВрд╕реНрдЯреЗрдВрд╕ рдкреНрд░рддрд┐ рд░реЗрдВрдбрд░ рдкрдВрдЬреАрдХреГрдд рд╣реЛрддрд╛ рд╣реИред рд▓реЗрдЖрдЙрдЯ рдкреВрд░реЗ рдиреЗрд╡рд┐рдЧреЗрд╢рди рдореЗрдВ рд╕реБрд░рдХреНрд╖рд┐рдд рд░рд╣рддреЗ рд╣реИрдВ рдФрд░ рджреЛрдмрд╛рд░рд╛ рд░реЗрдВрдбрд░ рдирд╣реАрдВ рд╣реЛрддреЗ рд╣реИрдВ, рдЗрд╕рд▓рд┐рдП рдХреЛрдИ рдкреЗрдЬ рд▓реЛрдХреЗрд▓ рд╕реЗрдЯ рдХрд░рдиреЗ рдХреЗ рд▓рд┐рдП рдЕрдкрдиреЗ рд▓реЗрдЖрдЙрдЯ рдкрд░ рдирд┐рд░реНрднрд░ рдирд╣реАрдВ рд░рд╣ рд╕рдХрддрд╛ рд╣реИред рдкреНрд░рддреНрдпреЗрдХ рд▓реЗрдЖрдЙрдЯ рдФрд░ рдкреЗрдЬ рдХреЗ рд╢реАрд░реНрд╖ рдкрд░ initLingui(locale) рдХреЛ рдХреЙрд▓ рдХрд░рдиреЗ рд╕реЗ рд╡реЗ рд╕реНрд╡рддрдВрддреНрд░ рд░рд╣рддреЗ рд╣реИрдВред

    @lingui/swc-plugin рдХрд╛ рдЙрдкрдпреЛрдЧ рдХрд░реЗрдВред рдпрд╣ SWC рдкрд╛рдЗрдкрд▓рд╛рдЗрди рдФрд░ Turbopack рдХреЛ рдмрдирд╛рдП рд░рдЦрддрд╛ рд╣реИред Babel рдХреЙрдиреНрдлрд╝рд┐рдЧ рдЬреЛрдбрд╝рдиреЗ рд╕реЗ Next.js рдореЗрдВ SWC рдЕрдХреНрд╖рдо рд╣реЛ рдЬрд╛рддрд╛ рд╣реИ рдФрд░ рдмрд┐рд▓реНрдб рдзреАрдорд╛ рд╣реЛ рдЬрд╛рддрд╛ рд╣реИред рдПрдХрдорд╛рддреНрд░ рд╢рд░реНрдд рдпрд╣ рд╣реИ рдХрд┐ рдкреНрд▓рдЧрдЗрди рд╕рдВрд╕реНрдХрд░рдг рдХреЛ рдЖрдкрдХреЗ Next.js рд░рд┐рд▓реАрдЬрд╝ рдХреЗ SWC рд╕рдВрд╕реНрдХрд░рдг рдХреЗ рд╕рд╛рде рд╕рдВрдЧрдд рд░рдЦрд╛ рдЬрд╛рдПред

    getI18nInstance(locale) рдХреЗ рд╕рд╛рде рд╕рд░реНрд╡рд░ рдЗрдВрд╕реНрдЯреЗрдВрд╕ рдкреНрд░рд╛рдкреНрдд рдХрд░реЗрдВ рдФрд░ msg рдореИрдХреНрд░реЛ рдХреЗ рд╕рд╛рде рдШреЛрд╖рд┐рдд рдбрд┐рд╕реНрдХреНрд░рд┐рдкреНрдЯрд░ рдХрд╛ рдЕрдиреБрд╡рд╛рдж рдХрд░реЗрдВ: i18n._(msg`About us`)ред alternates.canonical, x-default рдХреЗ рд╕рд╛рде alternates.languages, рдФрд░ openGraph.locale рд▓реМрдЯрд╛рдПрдВред рдЪрд░рдг 13 рдПрдХ рдкреБрди: рдкреНрд░рдпреЛрдЬреНрдп рд╣реЗрд▓реНрдкрд░ рдкреНрд░рджрд╛рди рдХрд░рддрд╛ рд╣реИред

    рдмреЗрдВрдЪрдорд╛рд░реНрдХ рд░рдирдЯрд╛рдЗрдо рдХреЗ рд▓рд┐рдП ~72 KB gzip рдорд╛рдкрддрд╛ рд╣реИред рдкреНрд░рддрд┐ рд▓реЛрдХреЗрд▓ рдПрдХ рдХреИрдЯрд▓реЙрдЧ рдХреЗ рд╕рд╛рде, рдмрд┐рдирд╛ i18n рд╡рд╛рд▓реЗ 141 KB рдХреЗ рдореБрдХрд╛рдмрд▓реЗ рдкреЗрдЬреЛрдВ рдХрд╛ рд╡рдЬрди ~145 KB рд╣реЛрддрд╛ рд╣реИ, рд▓реЗрдХрд┐рди рдкреНрд░рддреНрдпреЗрдХ рдкреЗрдЬ рдЕрднреА рднреА рдХреНрд▓рд╛рдЗрдВрдЯ рдкреНрд░реЛрд╡рд╛рдЗрдбрд░ рдХреЗ рдорд╛рдзреНрдпрдо рд╕реЗ рдЕрдиреНрдп рдкреЗрдЬреЛрдВ рдХреЗ рд╕рдВрджреЗрд╢ рдкреНрд░рд╛рдкреНрдд рдХрд░рддрд╛ рд╣реИред

    Lingui рдЙрди рдЯреАрдореЛрдВ рдХреЗ рд▓рд┐рдП рдЙрдкрдпреБрдХреНрдд рд╣реИ рдЬреЛ рдШрдЯрдХреЛрдВ рдореЗрдВ рд╕реНрд░реЛрдд рдЯреЗрдХреНрд╕реНрдЯ рд▓рд┐рдЦрдирд╛ рдФрд░ PO рдлрд╝рд╛рдЗрд▓реЛрдВ рддрдерд╛ рдЕрдиреБрд╡рд╛рджрдХреЛрдВ рдХреЗ рд╕рд╛рде рдХрд╛рдо рдХрд░рдирд╛ рдкрд╕рдВрдж рдХрд░рддреА рд╣реИрдВред next-intl рдЙрди рдЯреАрдореЛрдВ рдХреЗ рд▓рд┐рдП рдЙрдкрдпреБрдХреНрдд рд╣реИ рдЬреЛ JSON рдХреИрдЯрд▓реЙрдЧ рдФрд░ Next.js рдХреЗ рд╕рд╛рде рдПрдХреАрдХреГрдд t("key") API рдкрд╕рдВрдж рдХрд░рддреА рд╣реИрдВред next-i18next i18next рдкреНрд▓рдЧрдЗрди рдЗрдХреЛрд╕рд┐рд╕реНрдЯрдо рд▓рд╛рддрд╛ рд╣реИред next-i18next рдмрдирд╛рдо next-intl рдмрдирд╛рдо Intlayer рдФрд░ Next.js рдмреЗрдВрдЪрдорд╛рд░реНрдХ рджреЗрдЦреЗрдВред

    рдЯрд┐рдкреНрдкрдгрд┐рдпрд╛рдБ

    рдЕрднреА рддрдХ рдХреЛрдИ рдЯрд┐рдкреНрдкрдгреА рдирд╣реАрдВред рдЕрдкрдиреЗ рд╡рд┐рдЪрд╛рд░ рд╕рд╛рдЭрд╛ рдХрд░рдиреЗ рд╡рд╛рд▓реЗ рдкрд╣рд▓реЗ рд╡реНрдпрдХреНрддрд┐ рдмрдиреЗрдВред

    рд╕рдВрдмрдВрдзрд┐рдд рдкреЛрд╕реНрдЯ

    рдирд╡реАрдирддрдо рдкреЛрд╕реНрдЯ

    рдХреНрдпрд╛ Intlayer, Paraglide рд╕реЗ рд╣рд▓реНрдХрд╛ рд╣реИ?

    Paraglide i18n рдмреЗрдВрдЪрдорд╛рд░реНрдХ рдореЗрдВ рд▓рдЧрднрдЧ рд╢реВрдиреНрдп рднрд╛рд░ рд╡рд╛рд▓рд╛ рдкреНрд░рддреАрдд рд╣реЛрддрд╛ рд╣реИ рдХреНрдпреЛрдВрдХрд┐ рдЗрд╕рдХрд╛ рдХреЛрдб рд╕реАрдзреЗ рдЖрдкрдХреЗ рд░рд┐рдкреЙрдЬрд┐рдЯрд░реА рдореЗрдВ рдЬреЗрдирд░реЗрдЯ рд╣реЛрддрд╛ рд╣реИред рдЬрд╛рдирд┐рдП рдпрд╣ рд╡рдЬрди рд╡рд╛рд╕реНрддрд╡ рдореЗрдВ рдХрд╣рд╛рдБ рдЬрд╛рддрд╛ рд╣реИ, рдкреНрд░рддрд┐-рдиреЛрдб рд▓реЛрдХреЗрд▓ рдкрдврд╝рдирд╛ рдкреНрд░рджрд░реНрд╢рди рдХреЛ рдХреИрд╕реЗ рдкреНрд░рднрд╛рд╡рд┐рдд рдХрд░рддрд╛ рд╣реИ, рдФрд░ Intlayer рдХрд╛ рдбрд╛рдпрдирд╛рдорд┐рдХ рд▓реЛрдбрд┐рдВрдЧ рд╕рднреА рдХреЗ рдмрдЬрд╛рдп рдХреЗрд╡рд▓ рдПрдХ рд▓реЛрдХреЗрд▓ рдХреИрд╕реЗ рд▓реЛрдб рдХрд░рддрд╛ рд╣реИред

    Aymeric PINEAU