рдЕрдкрдиреЗ рдкреНрд░рд╢реНрди рдХреЛ рдкреВрдЫреЗрдВ рдФрд░ рджрд╕реНрддрд╛рд╡реЗрдЬрд╝ рдХрд╛ рд╕рд╛рд░рд╛рдВрд╢ рдкреНрд░рд╛рдкреНрдд рдХрд░реЗрдВ, рдЗрд╕ рдкреГрд╖реНрда рдФрд░ рдЖрдкрдХреЗ рдЪреБрдиреЗ рд╣реБрдП AI рдкреНрд░рджрд╛рддрд╛ рдХрд╛ рдЙрдкрдпреЛрдЧ рдХрд░рдХреЗ
рд╕рдВрд╕реНрдХрд░рдг рдЗрддрд┐рд╣рд╛рд╕
- "Initial history"v9.4.129/8/2026
рдЗрд╕ рдкреГрд╖реНрда рдХреА рд╕рд╛рдордЧреНрд░реА рдПрдХ AI рджреНрд╡рд╛рд░рд╛ рдЕрдиреБрд╡рд╛рджрд┐рдд рдХреА рдЧрдИ рд╣реИред
рдЕрдВрдЧреНрд░реЗрдЬреА рдореЗрдВ рдореВрд▓ рд╕рд╛рдордЧреНрд░реА рдХреЗ рдЕрдВрддрд┐рдо рд╕рдВрд╕реНрдХрд░рдг рджреЗрдЦреЗрдВрдЕрдЧрд░ рдЖрдкрдХреЗ рдкрд╛рд╕ рдЗрд╕ рджрд╕реНрддрд╛рд╡реЗрдЬрд╝ рдХреЛ рд╕реБрдзрд╛рд░рдиреЗ рдХреЗ рд▓рд┐рдП рдХреЛрдИ рд╡рд┐рдЪрд╛рд░ рд╣реИ, рддреЛ рдХреГрдкрдпрд╛ GitHub рдкрд░ рдПрдХ рдкреБрд▓ рдЕрдиреБрд░реЛрдз рд╕рдмрдорд┐рдЯ рдХрд░рдХреЗ рдпреЛрдЧрджрд╛рди рджреЗрдиреЗ рдореЗрдВ рд╕рдВрдХреЛрдЪ рди рдХрд░реЗрдВред
рджрд╕реНрддрд╛рд╡реЗрдЬрд╝ рдХреЗ рд▓рд┐рдП GitHub рд▓рд┐рдВрдХрджрд╕реНрддрд╛рд╡реЗрдЬрд╝ рдХрд╛ Markdown рдХреЛ рдХреНрд▓рд┐рдкрдмреЛрд░реНрдб рдкрд░ рдХреЙрдкреА рдХрд░реЗрдВ
Intlayer рдХрд╛ рдЙрдкрдпреЛрдЧ рдХрд░рдХреЗ рдЕрдкрдиреЗ htmx рдРрдкреНрд▓рд┐рдХреЗрд╢рди рдХрд╛ рдЕрдиреБрд╡рд╛рдж рдХрд░реЗрдВ | рдЕрдВрддрд░реНрд░рд╛рд╖реНрдЯреНрд░реАрдпрдХрд░рдг (i18n)
htmx рдЕрдкрдиреА рдХреЛрдИ рд╕рд╛рдордЧреНрд░реА render рдирд╣реАрдВ рдХрд░рддрд╛ рд╣реИред рд╣рд░ рд▓реЗрдмрд▓ рдЬреЛ рдХреЛрдИ рдЖрдЧрдВрддреБрдХ рдкрдврд╝рддрд╛ рд╣реИ, рд╡рд╣ HTML рд╣реИ рдЬреЛ рдЖрдкрдХрд╛ server рддреИрдпрд╛рд░ рдХрд░рддрд╛ рд╣реИ, рдФрд░ рд╣рд░ swap рдПрдХ рдЕрд▓рдЧ HTTP request рд╣реИред рдЗрд╕рд▓рд┐рдП, htmx рдРрдк рдХреЛ internationalize рдХрд░рдирд╛ рдПрдХ server concern рд╣реИ: locale рдХреЛ рд╣рд░ request рдкрд░ resolve рдХрд░рдирд╛ рд╣реЛрддрд╛ рд╣реИ, рдФрд░ рд╣рд░ fragment рдХреЛ рдЙрд╕ locale рдореЗрдВ render рдХрд░рдирд╛ рд╣реЛрддрд╛ рд╣реИред
Intlayer рдЗрд╕реЗ рдЕрдкрдиреЗ backend integrations рдХреЗ рдорд╛рдзреНрдпрдо рд╕реЗ рдХрд╡рд░ рдХрд░рддрд╛ рд╣реИ, рдЬреЛ рд╣рд░ request рдкрд░ locale рдХреЛ detect рдХрд░рддреЗ рд╣реИрдВ рдФрд░ рдЖрдкрдХреА рдШреЛрд╖рд┐рдд content рдХреЛ рдЙрд╕ handler рдХреЗ рд▓рд┐рдП expose рдХрд░рддреЗ рд╣реИрдВ рдЬреЛ HTML рдмрдирд╛рддрд╛ рд╣реИред
рд╡рд┐рд╖рдп рд╕реВрдЪреА
htmx рдРрдк рдореЗрдВ i18n рдХреЗ рддреАрди рдирд┐рдпрдо
рдПрдХ рднреА рдкреГрд╖реНрда рджрд░реНрдЬрдиреЛрдВ swaps рдХреЛ рдЯреНрд░рд┐рдЧрд░ рдХрд░ рд╕рдХрддрд╛ рд╣реИред рдкреНрд░рддреНрдпреЗрдХ рдПрдХ рддрд╛рдЬрд╝реА request рд╣реИ рдЬрд┐рд╕реЗ рдЙрд╕ рдкреГрд╖реНрда рдХреА рдХреЛрдИ рд╕реНрдореГрддрд┐ рдирд╣реАрдВ рд╣реИ рдЬреЛ рдЗрд╕реЗ рдЬрд╛рд░реА рдХрд░рддрд╛ рд╣реИред рдпрджрд┐ locale рдкреНрд░рд╛рд░рдВрднрд┐рдХ render рдХреЗ рджреМрд░рд╛рди рд╕реЗрдЯ рдХрд┐рдП рдЧрдП variable рдореЗрдВ рд░рд╣рддрд╛ рд╣реИ, рддреЛ рдЗрд╕рдХреЗ рдмрд╛рдж рдХрд╛ рдкреНрд░рддреНрдпреЗрдХ fragment рдбрд┐рдлрд╝реЙрд▓реНрдЯ рднрд╛рд╖рд╛ рдореЗрдВ рд╡рд╛рдкрд╕ рдЖрддрд╛ рд╣реИред
Intlayer middleware request рд╕реЗ рд╣реА locale рдХреЛ resolve рдХрд░рддрд╛ рд╣реИ, рдЗрд╕рд▓рд┐рдП рдорд┐рдирдЯ рджрд╕ рдкрд░ рджрд┐рдпрд╛ рдЧрдпрд╛ рдПрдХ fragment рдЙрд╕реА рднрд╛рд╖рд╛ рдореЗрдВ рдЙрддреНрддрд░ рджреЗрддрд╛ рд╣реИ рдЬреИрд╕реЗ рдорд┐рдирдЯ рд╢реВрдиреНрдп рдкрд░ рджрд┐рдпрд╛ рдЧрдпрд╛ рдкреГрд╖реНрдаред
htmx рдХреЗ рд╕рд╛рде рджреЛ carriers рдХрд╛рдо рдХрд░рддреЗ рд╣реИрдВред рдПрдХ cookie (INTLAYER_LOCALE) рдХреЛ browser рджреНрд╡рд╛рд░рд╛ рд╕реНрд╡рдЪрд╛рд▓рд┐рдд рд░реВрдк рд╕реЗ рд╣рд░ request рдкрд░ рднреЗрдЬрд╛ рдЬрд╛рддрд╛ рд╣реИ, рдЬрд┐рд╕рдореЗрдВ htmx рднреА рд╢рд╛рдорд┐рд▓ рд╣реИрдВред рдПрдХ header (x-intlayer-locale) рдХреЛ htmx requests рдХреЗ рд╕рд╛рде hx-headers attribute рдХреЗ рд╕рд╛рде attach рдХрд┐рдпрд╛ рдЬрд╛ рд╕рдХрддрд╛ рд╣реИред рджреЛрдиреЛрдВ рдХреЛ рдбрд┐рдлрд╝реЙрд▓реНрдЯ рд░реВрдк рд╕реЗ рдкрдврд╝рд╛ рдЬрд╛рддрд╛ рд╣реИред
рдПрдХ рдЕрдиреБрд╡рд╛рджрд┐рдд рдорд╛рди рдЬреЛ рдПрдХ fragment рдореЗрдВ рдкреНрд░рдХреНрд╖реЗрдкрд┐рдд рдХрд┐рдпрд╛ рдЧрдпрд╛ рд╣реИ, markup рд╣реИред рдЗрд╕реЗ escape рдХрд░реЗрдВ, рдмрд┐рд▓реНрдХреБрд▓ рд╡реИрд╕реЗ рд╣реА рдЬреИрд╕реЗ рдЖрдк рдХрд┐рд╕реА рдЕрдиреНрдп dynamic value рдХрд░реЗрдВрдЧреЗ, рддрд╛рдХрд┐ рдПрдХ рдЕрдиреБрд╡рд╛рдж рдЬрд┐рд╕рдореЗрдВ < рд╣реИ, рд╡рд╣ document рдХреЛ рддреЛрдбрд╝ рди рд╕рдХреЗ рдЬрд┐рд╕рдореЗрдВ рдЗрд╕реЗ рд╕реНрд╡реИрдк рдХрд┐рдпрд╛ рдЬрд╛ рд░рд╣рд╛ рд╣реИред
Step-by-Step Guide
рдПрдкреНрд▓рд┐рдХреЗрд╢рди рдЯреЗрдореНрдкрд▓реЗрдЯ рдХреЛ GitHub рдкрд░ рджреЗрдЦреЗрдВред
рдирд┐рд░реНрднрд░рддрд╛рдПрдВ рд╕реНрдерд╛рдкрд┐рдд рдХрд░реЗрдВ
intlayerрдкреНрд▓рд╕ рдЕрдкрдиреЗ рд╕рд░реНрд╡рд░ рдХреЗ рд▓рд┐рдП рдЗрдВрдЯреАрдЧреНрд░реЗрд╢рди рд╕реНрдерд╛рдкрд┐рдд рдХрд░реЗрдВредbashрдХреЛрдб рдХреЙрдкреА рдХрд░реЗрдВрдХреЛрдб рдХреЛ рдХреНрд▓рд┐рдкрдмреЛрд░реНрдб рдкрд░ рдХреЙрдкреА рдХрд░реЗрдВ
bashрдХреЛрдб рдХреЙрдкреА рдХрд░реЗрдВрдХреЛрдб рдХреЛ рдХреНрд▓рд┐рдкрдмреЛрд░реНрдб рдкрд░ рдХреЙрдкреА рдХрд░реЗрдВ
bashрдХреЛрдб рдХреЙрдкреА рдХрд░реЗрдВрдХреЛрдб рдХреЛ рдХреНрд▓рд┐рдкрдмреЛрд░реНрдб рдкрд░ рдХреЙрдкреА рдХрд░реЗрдВ
bashрдХреЛрдб рдХреЙрдкреА рдХрд░реЗрдВрдХреЛрдб рдХреЛ рдХреНрд▓рд┐рдкрдмреЛрд░реНрдб рдкрд░ рдХреЙрдкреА рдХрд░реЗрдВ
bashрдХреЛрдб рдХреЙрдкреА рдХрд░реЗрдВрдХреЛрдб рдХреЛ рдХреНрд▓рд┐рдкрдмреЛрд░реНрдб рдкрд░ рдХреЙрдкреА рдХрд░реЗрдВ
Express рдФрд░ Fastify рдЕрдкрдиреЗ рд╕реНрд╡рдпрдВ рдХреЗ cookie parsers рдХреЗ рдорд╛рдзреНрдпрдо рд╕реЗ locale cookie рдХреЛ рдкрдврд╝рддреЗ рд╣реИрдВ, рдЗрд╕рд▓рд┐рдП рдЙрдиреНрд╣реЗрдВ рд╕рд╛рде рдореЗрдВ install рдХрд░рдирд╛ рд╣реЛрдЧрд╛ред Hono рдФрд░ Elysia рдиреЗрдЯрд┐рд╡рд▓реА cookies рдХреЛ parse рдХрд░рддреЗ рд╣реИрдВред
htmx рд╕реНрд╡рдпрдВ рдПрдХ рдПрдХрд▓ script tag рд╣реИ, рдЬреЛ step 4 рдореЗрдВ рдЬреЛрдбрд╝рд╛ рдЬрд╛рддрд╛ рд╣реИред
рдЕрдкрдиреА project рдХрд╛ Configuration
рдЕрдкрдиреА project root рдкрд░ рдПрдХ
intlayer.config.tsрдмрдирд╛рдПрдВ:intlayer.config.tsрдХреЛрдб рдХреЙрдкреА рдХрд░реЗрдВрдХреЛрдб рдХреЛ рдХреНрд▓рд┐рдкрдмреЛрд░реНрдб рдкрд░ рдХреЙрдкреА рдХрд░реЗрдВ
import { Locales, type IntlayerConfig } from "intlayer"; const config: IntlayerConfig = { internationalization: { locales: [Locales.ENGLISH, Locales.FRENCH, Locales.SPANISH, Locales.ARABIC], defaultLocale: Locales.ENGLISH, }, }; export default config;рдкреВрд░реНрдг рд╡рд┐рдХрд▓реНрдкреЛрдВ рдХреА рд╕реВрдЪреА рдХреЗ рд▓рд┐рдП, рдХреЙрдиреНрдлрд╝рд┐рдЧрд░реЗрд╢рди рджрд╕реНрддрд╛рд╡реЗрдЬрд╝ рджреЗрдЦреЗрдВред
рдЕрдкрдиреА рд╕рд╛рдордЧреНрд░реА рдШреЛрд╖рд┐рдд рдХрд░реЗрдВ
рд╕рднреА рд▓реЗрдмрд▓ рдШреЛрд╖рд┐рдд рдХрд░реЗрдВ рдЬреЛ рд╕рд░реНрд╡рд░ рд░реЗрдВрдбрд░ рдХрд░реЗрдЧрд╛, рдЬрд┐рдирдореЗрдВ рд╡реЗ рднреА рд╢рд╛рдорд┐рд▓ рд╣реИрдВ рдЬреЛ рдХреЗрд╡рд▓ рдПрдХ fragment рдХреЗ рдЕрдВрджрд░ рджрд┐рдЦрд╛рдИ рджреЗрддреЗ рд╣реИрдВ:
src/app.content.tsрдХреЛрдб рдХреЙрдкреА рдХрд░реЗрдВрдХреЛрдб рдХреЛ рдХреНрд▓рд┐рдкрдмреЛрд░реНрдб рдкрд░ рдХреЙрдкреА рдХрд░реЗрдВ
import { insert, t, type Dictionary } from "intlayer"; const appContent = { key: "app", content: { pageTitle: "Intlayer + htmx", localeLabel: t({ hi: "рднрд╛рд╖рд╛", en: "Language", fr: "Langue", es: "Idioma", ar: "╪з┘Д┘Д╪║╪й", }), cartSummary: insert( t({ hi: "рдЖрдкрдХреА рдХрд╛рд░реНрдЯ рдореЗрдВ рдЖрдЗрдЯрдо: {{count}}", en: "Items in your cart: {{count}}", fr: "Articles dans votre panier : {{count}}", es: "Art├нculos en tu carrito: {{count}}", ar: "╪з┘Д┘Е┘Ж╪к╪м╪з╪к ┘Б┘К ╪│┘Д╪к┘Г: {{count}}", }) ), addItem: t({ hi: "рдПрдХ рдЖрдЗрдЯрдо рдЬреЛрдбрд╝реЗрдВ", en: "Add an item", fr: "Ajouter un article", es: "A├▒adir un art├нculo", ar: "╪г╪╢┘Б ┘Е┘Ж╪к╪м┘Л╪з", }), }, } satisfies Dictionary; export default appContent;Content declarations
contentDirрдХреЗ рдЕрдВрддрд░реНрдЧрдд рдХрд┐рд╕реА рднреА рд╕реНрдерд╛рди рдкрд░ рд░рд╣ рд╕рдХрддреЗ рд╣реИрдВ (рдбрд┐рдлрд╝реЙрд▓реНрдЯ рд░реВрдк рд╕реЗ./src) рдФрд░.content.{json,ts,tsx,js,jsx,mjs,cjs,md,mdx,yaml,yml}рд╕реЗ рдореЗрд▓ рдЦрд╛рддреЗ рд╣реИрдВред content declaration documentation рджреЗрдЦреЗрдВредIntlayer middleware рдХреЛ рд░рдЬрд┐рд╕реНрдЯрд░ рдХрд░реЗрдВ
middleware рдкреНрд░рддреНрдпреЗрдХ request рдХреА locale рдХреЛ resolve рдХрд░рддрд╛ рд╣реИ рдФрд░ рдЗрд╕реЗ рдЖрдкрдХреЗ handlers рдореЗрдВ expose рдХрд░рддрд╛ рд╣реИред
src/index.tsрдХреЛрдб рдХреЙрдкреА рдХрд░реЗрдВрдХреЛрдб рдХреЛ рдХреНрд▓рд┐рдкрдмреЛрд░реНрдб рдкрд░ рдХреЙрдкреА рдХрд░реЗрдВ
import cookieParser from "cookie-parser"; import express from "express"; import { intlayer } from "express-intlayer"; const app = express(); // cookie parser рдХреЛ рдкрд╣рд▓реЗ рдЪрд▓рдирд╛ рд╣реЛрдЧрд╛: `express-intlayer` locale рдХреЛ // `req.cookies` рдХреЗ рдорд╛рдзреНрдпрдо рд╕реЗ рдкрдврд╝рддрд╛ рд╣реИред app.use(cookieParser()); app.use(express.urlencoded({ extended: false })); app.use(intlayer());resolve рдХреА рдЧрдИ locale
res.locals.localeрдкрд░ рд╣реИредsrc/index.tsрдХреЛрдб рдХреЙрдкреА рдХрд░реЗрдВрдХреЛрдб рдХреЛ рдХреНрд▓рд┐рдкрдмреЛрд░реНрдб рдкрд░ рдХреЙрдкреА рдХрд░реЗрдВ
import cookie from "@fastify/cookie"; import formbody from "@fastify/formbody"; import Fastify from "fastify"; import { intlayer } from "fastify-intlayer"; const fastify = Fastify(); await fastify.register(cookie); await fastify.register(formbody); await fastify.register(intlayer);рд╕рдорд╛рдзрд╛рдирд┐рдд locale
req.intlayer.localeрдкрд░ рд╣реИредsrc/index.tsрдХреЛрдб рдХреЙрдкреА рдХрд░реЗрдВрдХреЛрдб рдХреЛ рдХреНрд▓рд┐рдкрдмреЛрд░реНрдб рдкрд░ рдХреЙрдкреА рдХрд░реЗрдВ
import { Hono } from "hono"; import { intlayer } from "hono-intlayer"; const app = new Hono(); app.use("*", intlayer());рд╕рдорд╛рдзрд╛рдирд┐рдд locale
c.get("locale")рд╣реИредsrc/index.tsрдХреЛрдб рдХреЙрдкреА рдХрд░реЗрдВрдХреЛрдб рдХреЛ рдХреНрд▓рд┐рдкрдмреЛрд░реНрдб рдкрд░ рдХреЙрдкреА рдХрд░реЗрдВ
import { Elysia } from "elysia"; import { intlayer } from "elysia-intlayer"; const app = new Elysia().use(intlayer());The resolved locale is
intlayer!.localeon the route context.рдбрд┐рдлрд╝реЙрд▓реНрдЯ рд░реВрдк рд╕реЗ locale рдХреЛ
INTLAYER_LOCALEcookie рд╕реЗ рд▓рд┐рдпрд╛ рдЬрд╛рддрд╛ рд╣реИ, рдлрд┐рд░x-intlayer-localeheader рд╕реЗ, рдлрд┐рд░Accept-Languagenegotiation рд╕реЗредрд░рд┐рдХреНрд╡реЗрд╕реНрдЯ locale рдХреЗ рд╕рд╛рде fragments рдХреЛ render рдХрд░реЗрдВ
рдЕрдкрдиреЗ fragment renderers рдХреЛ рдПрдХ locale рдХреЗ pure functions рдХреЗ рд░реВрдк рдореЗрдВ рд▓рд┐рдЦреЗрдВ, рдФрд░ middleware рджреНрд╡рд╛рд░рд╛ resolve рдХрд┐рдП рдЧрдП locale рдХреЛ рдкрд╛рд╕ рдХрд░реЗрдВред рдЗрд╕реЗ explicitly рдкрд╛рд╕ рдХрд░рдиреЗ рд╕реЗ рдПрдХ fragment рдЙрд╕ request рд╕реЗ рдЬреБрдбрд╝рд╛ рд░рд╣рддрд╛ рд╣реИ рдЬрд┐рд╕рдиреЗ рдЗрд╕реЗ рдорд╛рдВрдЧрд╛ рдерд╛, рдЖрдк рдХрд┐рд╕реА рднреА server рдкрд░ рд╣реЛрдВред
src/views.tsрдХреЛрдб рдХреЙрдкреА рдХрд░реЗрдВрдХреЛрдб рдХреЛ рдХреНрд▓рд┐рдкрдмреЛрд░реНрдб рдкрд░ рдХреЙрдкреА рдХрд░реЗрдВ
import { currency, getIntlayer, type Locale } from "intlayer"; const HTML_ENTITIES: Record<string, string> = { "&": "&", "<": "<", ">": ">", '"': """, "'": "'", }; /** рдорд╛рд░реНрдХрдЕрдк рд╕реЗ рдмрд╛рд╣рд░ рди рдирд┐рдХрд▓рдиреЗ рдХреЗ рд▓рд┐рдП рдЕрдиреБрд╡рд╛рджрд┐рдд рдорд╛рди рдХреЛ рдПрд╕реНрдХреЗрдк рдХрд░рддрд╛ рд╣реИред */ const escapeHtml = (value: string): string => value.replace( /[&<>"']/g, (character) => HTML_ENTITIES[character] ?? character ); export const renderCart = (locale: Locale, itemCount: number): string => { const content = getIntlayer("app", locale); return `<section id="cart"> <p>${escapeHtml(String(content.cartSummary({ count: itemCount })))}</p> <p>${escapeHtml(currency(itemCount * 12.5, { locale, currency: "EUR" }))}</p> <button hx-post="/cart/items" hx-vals='{"itemCount": ${itemCount}}' hx-target="#cart" hx-swap="outerHTML" >${escapeHtml(String(content.addItem))}</button> </section>`; };рдЗрд╕реЗ рдПрдХ route рд╕реЗ serve рдХрд░реЗрдВ:
src/index.tsрдХреЛрдб рдХреЙрдкреА рдХрд░реЗрдВрдХреЛрдб рдХреЛ рдХреНрд▓рд┐рдкрдмреЛрд░реНрдб рдкрд░ рдХреЙрдкреА рдХрд░реЗрдВ
app.post("/cart/items", (req, res) => { // itemCount рдХреЛ request body рд╕реЗ рдкреНрд░рд╛рдкреНрдд рдХрд░реЗрдВ, рдбрд┐рдлрд╝реЙрд▓реНрдЯ 0 рд╣реИ, рдлрд┐рд░ 1 рдЬреЛрдбрд╝реЗрдВ const itemCount = Number(req.body?.itemCount ?? 0) + 1; // HTML рдкреНрд░рдХрд╛рд░ рд╕реЗрдЯ рдХрд░реЗрдВ рдФрд░ renderCart рдХрд╛ рдкрд░рд┐рдгрд╛рдо рднреЗрдЬреЗрдВ res.type("html").send(renderCart(res.locals.locale, itemCount)); });src/index.tsрдХреЛрдб рдХреЙрдкреА рдХрд░реЗрдВрдХреЛрдб рдХреЛ рдХреНрд▓рд┐рдкрдмреЛрд░реНрдб рдкрд░ рдХреЙрдкреА рдХрд░реЗрдВ
fastify.post("/cart/items", async (req, reply) => { // itemCount рдХреЛ request body рд╕реЗ рдкреНрд░рд╛рдкреНрдд рдХрд░реЗрдВ, рдбрд┐рдлрд╝реЙрд▓реНрдЯ 0 рд╣реИ, рдлрд┐рд░ 1 рдЬреЛрдбрд╝реЗрдВ const itemCount = Number((req.body as { itemCount?: string })?.itemCount ?? 0) + 1; // text/html content-type рдХреЗ рд╕рд╛рде renderCart рдХрд╛ рдкрд░рд┐рдгрд╛рдо рднреЗрдЬреЗрдВ return reply .type("text/html") .send(renderCart(req.intlayer.locale, itemCount)); });src/index.tsрдХреЛрдб рдХреЙрдкреА рдХрд░реЗрдВрдХреЛрдб рдХреЛ рдХреНрд▓рд┐рдкрдмреЛрд░реНрдб рдкрд░ рдХреЙрдкреА рдХрд░реЗрдВ
app.post("/cart/items", async (c) => { const body = await c.req.parseBody(); const itemCount = Number(body["itemCount"] ?? 0) + 1; return c.html(renderCart(c.get("locale"), itemCount)); });src/index.tsрдХреЛрдб рдХреЙрдкреА рдХрд░реЗрдВрдХреЛрдб рдХреЛ рдХреНрд▓рд┐рдкрдмреЛрд░реНрдб рдкрд░ рдХреЙрдкреА рдХрд░реЗрдВ
app.post("/cart/items", ({ body, intlayer }) => { const itemCount = Number((body as { itemCount?: string })?.itemCount ?? 0) + 1; return new Response(renderCart(intlayer!.locale, itemCount), { headers: { "content-type": "text/html" }, }); });рдПрдХ рд╣реА рдлреНрд░реИрдЧрдореЗрдВрдЯ рдЕрдм рдлреНрд░рд╛рдВрд╕реАрд╕реА рдореЗрдВ рдЙрддреНрддрд░ рджреЗрддрд╛ рд╣реИ рдЬрд┐рд╕ рд╡рд┐рдЬрд╝рд┐рдЯрд░ рдХрд╛ рдХреБрдХреА
frрдХрд╣рддрд╛ рд╣реИ, рдФрд░ рдЕрд░рдмреА рдореЗрдВ рдЬрд┐рд╕рдХрд╛ рдХреБрдХреАarрдХрд╣рддрд╛ рд╣реИ, рдХреЙрд▓рд┐рдВрдЧ рдорд╛рд░реНрдХрдЕрдк рдореЗрдВ рдХреЛрдИ рдмрджрд▓рд╛рд╡ рдирд╣реАрдВредрдкрд╣рд▓реЗ рдкреГрд╖реНрда рдХреЛ рд╕рд░реНрд╡ рдХрд░реЗрдВ
<body>рдХреЛ рдЕрдкрдиреЗ рдЖрдк рдХреЛ рд░реЗрдВрдбрд░ рдХрд░реЗрдВ, рддрд╛рдХрд┐ рдЪрд░рдг 7 рдореЗрдВ рд▓реЛрдХреЗрд▓ рд╕реНрд╡рд┐рдЪрд░ рдЗрд╕реЗ рдкреВрд░реА рддрд░рд╣ рд╕реЗ рд╕реНрд╡реИрдк рдХрд░ рд╕рдХреЗ, рдлрд┐рд░ рдЗрд╕реЗ рдЙрд╕ рджрд╕реНрддрд╛рд╡реЗрдЬрд╝ рдореЗрдВ рд▓рдкреЗрдЯреЗрдВ рдЬреЛ htmx рд▓реЛрдб рдХрд░рддрд╛ рд╣реИ:src/views.tsрдХреЛрдб рдХреЙрдкреА рдХрд░реЗрдВрдХреЛрдб рдХреЛ рдХреНрд▓рд┐рдкрдмреЛрд░реНрдб рдкрд░ рдХреЙрдкреА рдХрд░реЗрдВ
import { getHTMLTextDir, getIntlayer, type Locale } from "intlayer"; export const renderBody = (locale: Locale, itemCount: number): string => { const content = getIntlayer("app", locale); return `<body lang="${locale}" dir="${getHTMLTextDir(locale)}"> <main> <h1>${escapeHtml(String(content.pageTitle))}</h1> ${renderLocaleSwitcher(locale)} ${renderCart(locale, itemCount)} </main> </body>`; }; export const renderPage = (locale: Locale, itemCount: number): string => `<!doctype html> <html lang="${locale}" dir="${getHTMLTextDir(locale)}"> <head> <meta charset="utf-8" /> <title>${escapeHtml(String(getIntlayer("app", locale).pageTitle))}</title> <script src="https://unpkg.com/htmx.org@2.0.4"></script> </head> ${renderBody(locale, itemCount)} </html>`;getHTMLTextDirlocale рдХреЗ рд▓рд┐рдПltr,rtlрдпрд╛autoreturn рдХрд░рддрд╛ рд╣реИ, рдЬреЛ рдЕрд░рдмреА рдФрд░ рд╣рд┐рдмреНрд░реВ рдХреЛ рд╕рд╣реА рддрд░реАрдХреЗ рд╕реЗ рд▓реЗрдЖрдЙрдЯ рдХрд░рдиреЗ рдХреЗ рд▓рд┐рдП рдЖрд╡рд╢реНрдпрдХ рд╣реИредрднрд╛рд╖рд╛ рд╕реНрд╡рд┐рдЪ рдХрд░реЗрдВ
рднрд╛рд╖рд╛ рд╕реНрд╡рд┐рдЪ рдХрд░рдирд╛ рдХрд┐рд╕реА рдЕрдиреНрдп request рдЬреИрд╕реЗ рд╣реА рд╣реИред рд╕рд░реНрд╡рд░ middleware рдЬреЛ рдкрдврд╝рддрд╛ рд╣реИ рдЙрд╕ cookie рдореЗрдВ рдЪреБрдирд╛рд╡ рдХреЛ рд╕реНрдЯреЛрд░ рдХрд░рддрд╛ рд╣реИ, рдлрд┐рд░ рдирдП locale рдореЗрдВ рдлрд┐рд░ рд╕реЗ render рдХрд┐рдП рдЧрдП рдкреГрд╖реНрда рдХреЛ return рдХрд░рддрд╛ рд╣реИред
<select>рдХреЛ рд░реЗрдВрдбрд░ рдХрд░реЗрдВ рдЬреЛ рд╕реНрд╡рдпрдВ рдХреЛ рдкреЛрд╕реНрдЯ рдХрд░реЗ рдФрд░ рдкреВрд░реЗ<body>рдХреЛ рд╕реНрд╡реИрдк рдХрд░реЗ, рддрд╛рдХрд┐ рдЖрдкрдХреЗ рдлреНрд░реИрдЧрдореЗрдВрдЯреНрд╕ рдХреЗ рдЪрд╛рд░реЛрдВ рдУрд░ рд╕реНрдерд┐рд░ рд▓реЗрдмрд▓ рднреА рдмрджрд▓ рдЬрд╛рдПрдВ:src/views.tsрдХреЛрдб рдХреЙрдкреА рдХрд░реЗрдВрдХреЛрдб рдХреЛ рдХреНрд▓рд┐рдкрдмреЛрд░реНрдб рдкрд░ рдХреЙрдкреА рдХрд░реЗрдВ
import { getIntlayer, getLocaleName, type Locale, locales } from "intlayer"; const renderLocaleSwitcher = (locale: Locale): string => { // рд╡рд░реНрддрдорд╛рди locale рдХреЗ рд▓рд┐рдП content рдкреНрд░рд╛рдкреНрдд рдХрд░реЗрдВ const content = getIntlayer("app", locale); // рд╕рднреА рдЙрдкрд▓рдмреНрдз locales рдХреЗ рд▓рд┐рдП рд╡рд┐рдХрд▓реНрдк рддреИрдпрд╛рд░ рдХрд░реЗрдВ const options = locales .map( (availableLocale: Locale) => `<option value="${availableLocale}"${availableLocale === locale ? " selected" : ""}>${escapeHtml(getLocaleName(availableLocale, locale))}</option>` ) .join(""); // locale switcher form рдХреЛ HTML рдХреЗ рд░реВрдк рдореЗрдВ рд░рд┐рдЯрд░реНрди рдХрд░реЗрдВ return `<form> <label for="locale">${escapeHtml(String(content.localeLabel))}</label> <select id="locale" name="locale" hx-post="/locale" hx-trigger="change" hx-target="body" hx-swap="outerHTML" >${options}</select> </form>`; };getLocaleName(availableLocale, locale)рдкреНрд░рддреНрдпреЗрдХ рднрд╛рд╖рд╛ рдХреЛ рд╡рд░реНрддрдорд╛рди рдореЗрдВ рдкреНрд░рджрд░реНрд╢рд┐рдд рднрд╛рд╖рд╛ рдореЗрдВ рд▓рд┐рдЦрддрд╛ рд╣реИред рдЗрд╕рдХреЗ рдмрдЬрд╛рдп рдкреНрд░рддреНрдпреЗрдХ рдХреЛ рдЕрдкрдиреА рднрд╛рд╖рд╛ рдореЗрдВ рд▓рд┐рдЦрдиреЗ рдХреЗ рд▓рд┐рдП рдХреЛрдИ рджреВрд╕рд░рд╛ argument рдкрд╛рд╕ рди рдХрд░реЗрдВредpost рдХреЛ handle рдХрд░реЗрдВ value рдХреЛ validate рдХрд░рдХреЗ, cookie рд╕реЗрдЯ рдХрд░рдХреЗ, рдФрд░ рдирдпрд╛ body return рдХрд░рдХреЗ:
src/index.tsрдХреЛрдб рдХреЙрдкреА рдХрд░реЗрдВрдХреЛрдб рдХреЛ рдХреНрд▓рд┐рдкрдмреЛрд░реНрдб рдкрд░ рдХреЙрдкреА рдХрд░реЗрдВ
import { isDeclaredLocale } from "intlayer"; app.post("/locale", (req, res) => { const requestedLocale = String(req.body?.locale); if (!isDeclaredLocale(requestedLocale)) { res.status(400).send("Unknown locale"); return; } res.cookie("INTLAYER_LOCALE", requestedLocale, { sameSite: "lax", path: "/", }); res.type("html").send(renderBody(requestedLocale, 0)); });src/index.tsрдХреЛрдб рдХреЙрдкреА рдХрд░реЗрдВрдХреЛрдб рдХреЛ рдХреНрд▓рд┐рдкрдмреЛрд░реНрдб рдкрд░ рдХреЙрдкреА рдХрд░реЗрдВ
import { isDeclaredLocale } from "intlayer"; fastify.post("/locale", async (req, reply) => { const requestedLocale = String((req.body as { locale?: string })?.locale); // рдЬрд╛рдВрдЪреЗрдВ рдХрд┐ рдХреНрдпрд╛ locale рдШреЛрд╖рд┐рдд рд╣реИ if (!isDeclaredLocale(requestedLocale)) { return reply.status(400).send("Unknown locale"); } return reply .setCookie("INTLAYER_LOCALE", requestedLocale, { sameSite: "lax", path: "/", }) .type("text/html") .send(renderBody(requestedLocale, 0)); });src/index.tsрдХреЛрдб рдХреЙрдкреА рдХрд░реЗрдВрдХреЛрдб рдХреЛ рдХреНрд▓рд┐рдкрдмреЛрд░реНрдб рдкрд░ рдХреЙрдкреА рдХрд░реЗрдВ
import { setCookie } from "hono/cookie"; import { isDeclaredLocale } from "intlayer"; app.post("/locale", async (c) => { // рдЕрдиреБрд░реЛрдз рдмреЙрдбреА рдХреЛ рдкрд╛рд░реНрд╕ рдХрд░реЗрдВ const body = await c.req.parseBody(); // рдЕрдиреБрд░реЛрдзрд┐рдд locale рдХреЛ рд╕реНрдЯреНрд░рд┐рдВрдЧ рдореЗрдВ рдХрдирд╡рд░реНрдЯ рдХрд░реЗрдВ const requestedLocale = String(body["locale"]); // рдЬрд╛рдВрдЪреЗрдВ рдХрд┐ рдХреНрдпрд╛ locale рдШреЛрд╖рд┐рдд рд╣реИ if (!isDeclaredLocale(requestedLocale)) { return c.text("Unknown locale", 400); } // INTLAYER_LOCALE рдХреБрдХреА рд╕реЗрдЯ рдХрд░реЗрдВ setCookie(c, "INTLAYER_LOCALE", requestedLocale, { sameSite: "Lax", path: "/", }); // HTML рд░реЗрдВрдбрд░ рдХрд░рдХреЗ рднреЗрдЬреЗрдВ return c.html(renderBody(requestedLocale, 0)); });src/index.tsрдХреЛрдб рдХреЙрдкреА рдХрд░реЗрдВрдХреЛрдб рдХреЛ рдХреНрд▓рд┐рдкрдмреЛрд░реНрдб рдкрд░ рдХреЙрдкреА рдХрд░реЗрдВ
import { isDeclaredLocale } from "intlayer"; app.post("/locale", ({ body, cookie, status }) => { const requestedLocale = String((body as { locale?: string })?.locale); if (!isDeclaredLocale(requestedLocale)) { return status(400, "Unknown locale"); } cookie["INTLAYER_LOCALE"]!.set({ value: requestedLocale, sameSite: "lax", path: "/", }); return new Response(renderBody(requestedLocale, 0), { headers: { "content-type": "text/html" }, }); });isDeclaredLocaleрдЖрдкрдХреА рдХреЙрдиреНрдлрд╝рд┐рдЧрд░ рдХреА рдЧрдИ locales рдореЗрдВ рд╕реЗ рдПрдХ рдХреЛ рдПрдХ рдЖрд░реНрдмрд┐рдЯреНрд░реЗрд░реА string рдХреЛ narrow рдХрд░рддрд╛ рд╣реИ, рдЗрд╕рд▓рд┐рдП рдПрдХ рдЕрдкреНрд░рддреНрдпрд╛рд╢рд┐рдд value рдХрднреА рднреА рдЖрдкрдХреЗ renderers рддрдХ рдирд╣реАрдВ рдкрд╣реБрдВрдЪрддрд╛ рд╣реИредswap рдХреЗ рдмрд╛рдж lang рдФрд░ dir рдХреЛ рд╕рд┐рдВрдХ рдореЗрдВ рд░рдЦреЗрдВ
рд╡реИрдХрд▓реНрдкрд┐рдХрдПрдХ swap
<body>рдХреЛ replace рдХрд░ рд╕рдХрддрд╛ рд╣реИ, рдХрднреА рднреА рдЗрд╕рдХреЗ рдЪрд╛рд░реЛрдВ рдУрд░<html>рдХреЛ рдирд╣реАрдВред Swapped body рдкрд░langрдФрд░dirрдХреЛ render рдХрд░реЗрдВ рдФрд░ рдЙрдиреНрд╣реЗрдВ head рд╕реЗ рдПрдХ рдмрд╛рд░ root element рдкрд░ рд╡рд╛рдкрд╕ рдХреЙрдкреА рдХрд░реЗрдВ:src/views.tsрдХреЛрдб рдХреЙрдкреА рдХрд░реЗрдВрдХреЛрдб рдХреЛ рдХреНрд▓рд┐рдкрдмреЛрд░реНрдб рдкрд░ рдХреЙрдкреА рдХрд░реЗрдВ
рдЗрд╕рдХреЗ рдмрд┐рдирд╛, рдЕрд░рдмреА рдореЗрдВ switch рдХрд░рдиреЗ рд╕реЗ body рдХреЗ рдЕрдВрджрд░ рджрд╛рдПрдВ рд╕реЗ рдмрд╛рдПрдВ render рд╣реЛрддрд╛ рд╣реИ рдЬрдмрдХрд┐ document рдЕрднреА рднреА рдкрд┐рдЫрд▓реА рднрд╛рд╖рд╛ рдХреЛ assistive technology рдФрд░ crawlers рдХреЛ advertise рдХрд░рддрд╛ рд╣реИред
Cookie рдХреА рдЬрдЧрд╣ header рдХреЗ рд░реВрдк рдореЗрдВ locale рднреЗрдЬреЗрдВ
рд╡реИрдХрд▓реНрдкрд┐рдХрдпрджрд┐ рдХреБрдХреА рдЖрдкрдХреЗ рд▓рд┐рдП рдЙрдкрдпреБрдХреНрдд рдирд╣реАрдВ рд╣реИ, рддреЛ
hx-headersрдХреЗ рд╕рд╛рде рдПрдХ ancestor element рдкрд░ рд╣рд░ htmx request рдореЗрдВ locale рдХреЛ attach рдХрд░реЗрдВред Descendants рдЗрд╕реЗ inherit рдХрд░рддреЗ рд╣реИрдВ:htmlрдХреЛрдб рдХреЙрдкреА рдХрд░реЗрдВрдХреЛрдб рдХреЛ рдХреНрд▓рд┐рдкрдмреЛрд░реНрдб рдкрд░ рдХреЙрдкреА рдХрд░реЗрдВ
Middleware рдбрд┐рдлрд╝реЙрд▓реНрдЯ рд░реВрдк рд╕реЗ
x-intlayer-localeрдХреЛ рдкрдврд╝рддрд╛ рд╣реИред рдЖрдк рдЕрдкрдиреЗ configuration рдореЗрдВ рджреЛрдиреЛрдВ carriers рдХреЛ rename рдХрд░ рд╕рдХрддреЗ рд╣реИрдВ:intlayer.config.tsрдХреЛрдб рдХреЙрдкреА рдХрд░реЗрдВрдХреЛрдб рдХреЛ рдХреНрд▓рд┐рдкрдмреЛрд░реНрдб рдкрд░ рдХреЙрдкреА рдХрд░реЗрдВ
import { Locales, type IntlayerConfig } from "intlayer"; // рдЕрдкрдиреЗ configuration рдХреЗ рд▓рд┐рдП constant const config: IntlayerConfig = { // ... рдЕрдиреНрдп configuration рд╡рд┐рдХрд▓реНрдк routing: { storage: [ { type: "header", name: "my-locale-header" }, { type: "cookie", name: "my-locale-cookie" }, ], }, }; export default config;
TypeScript рдХреЛ Configure рдХрд░реЗрдВ
рдСрдЯреЛрдЬреЗрдирд░реЗрдЯреЗрдб рдЯрд╛рдЗрдкреНрд╕ рдХреЛ рд╢рд╛рдорд┐рд▓ рдХрд░реЗрдВ рддрд╛рдХрд┐ рдПрдХ рдЕрдШреЛрд╖рд┐рдд рдХреБрдВрдЬреА рд░рдирдЯрд╛рдЗрдо рдкрд░ рдЦрд╛рд▓реА рд╕реНрдЯреНрд░рд┐рдВрдЧ рдХреЗ рдмрдЬрд╛рдп рдПрдХ compile error рд╣реЛред
рдХреЛрдб рдХреЛ рдХреНрд▓рд┐рдкрдмреЛрд░реНрдб рдкрд░ рдХреЙрдкреА рдХрд░реЗрдВ
Git рдХреЙрдиреНрдлрд╝рд┐рдЧрд░реЗрд╢рди
Intlayer рджреНрд╡рд╛рд░рд╛ рдЬреЗрдирд░реЗрдЯ рдХреА рдЧрдИ рдлрд╛рдЗрд▓реЛрдВ рдХреЛ рдЕрдирджреЗрдЦрд╛ рдХрд░рдирд╛ рдЕрдиреБрд╢рдВрд╕рд┐рдд рд╣реИ:
рдХреЛрдб рдХреЛ рдХреНрд▓рд┐рдкрдмреЛрд░реНрдб рдкрд░ рдХреЙрдкреА рдХрд░реЗрдВ
VS Code Extension
Intlayer рдХреЗ рд╕рд╛рде рдЕрдкрдиреЗ development experience рдХреЛ рдмреЗрд╣рддрд░ рдмрдирд╛рдиреЗ рдХреЗ рд▓рд┐рдП, рдЖрдк official Intlayer VS Code Extension рдХреЛ install рдХрд░ рд╕рдХрддреЗ рд╣реИрдВред
VS Code Marketplace рд╕реЗ Install рдХрд░реЗрдВ
рдпрд╣ extension рдирд┐рдореНрдирд▓рд┐рдЦрд┐рдд рдкреНрд░рджрд╛рди рдХрд░рддрд╛ рд╣реИ:
- Autocompletion рдЕрдиреБрд╡рд╛рдж keys рдХреЗ рд▓рд┐рдПред
- Real-time error detection рд▓рд╛рдкрддрд╛ рдЕрдиреБрд╡рд╛рджреЛрдВ рдХреЗ рд▓рд┐рдПред
- Inline previews рдЕрдиреБрд╡рд╛рджрд┐рдд content рдХрд╛ред
- Quick actions рдЖрд╕рд╛рдиреА рд╕реЗ рдЕрдиреБрд╡рд╛рдж рдмрдирд╛рдиреЗ рдФрд░ рдЕрдкрдбреЗрдЯ рдХрд░рдиреЗ рдХреЗ рд▓рд┐рдПред
Extension рдХрд╛ рдЙрдкрдпреЛрдЧ рдХреИрд╕реЗ рдХрд░реЗрдВ рдЗрд╕рдХреЗ рдмрд╛рд░реЗ рдореЗрдВ рдЕрдзрд┐рдХ рд╡рд┐рд╡рд░рдг рдХреЗ рд▓рд┐рдП, Intlayer VS Code Extension documentation рджреЗрдЦреЗрдВред
рдЖрдЧреЗ рдмрдврд╝реЗрдВ
рдЖрдЧреЗ рдмрдврд╝рдиреЗ рдХреЗ рд▓рд┐рдП, рдЖрдк рдЕрдкрдиреЗ content рдХреЛ CMS рдХрд╛ рдЙрдкрдпреЛрдЧ рдХрд░рдХреЗ externalize рдХрд░ рд╕рдХрддреЗ рд╣реИрдВ, рдЗрд╕рд▓рд┐рдП рдЕрдиреБрд╡рд╛рджрдХ deployment рдХреЗ рдмрд┐рдирд╛ copy рдмрджрд▓ рд╕рдХрддреЗ рд╣реИрдВред
рдЕрдХреНрд╕рд░ рдкреВрдЫреЗ рдЬрд╛рдиреЗ рд╡рд╛рд▓реЗ рдкреНрд░рд╢реНрди
рдХреНрдпреЛрдВрдХрд┐ рдлреНрд░реИрдЧрдореЗрдВрдЯ рд░рд┐рдХреНрд╡реЗрд╕реНрдЯ рдореЗрдВ рдХреЛрдИ рд▓реЛрдХреЗрд▓ рдирд╣реАрдВ рдерд╛ред htmx рд░рд┐рдХреНрд╡реЗрд╕реНрдЯ рдЙрди рдкреЗрдЬ рд╕реЗ рд╕реНрд╡рддрдВрддреНрд░ рд╣реИрдВ рдЬрд┐рдиреНрд╣реЛрдВрдиреЗ рдЙрдиреНрд╣реЗрдВ рдЬрд╛рд░реА рдХрд┐рдпрд╛, рдЗрд╕рд▓рд┐рдП рд▓реЛрдХреЗрд▓ рдХреЛ рдкреНрд░рддреНрдпреЗрдХ рдкрд░ travel рдХрд░рдирд╛ рдкрдбрд╝рддрд╛ рд╣реИ, INTLAYER_LOCALE cookie рдпрд╛ hx-headers рдХреЗ рд╕рд╛рде рд╕реЗрдЯ рдХрд┐рдП рдЧрдП x-intlayer-locale рд╣реЗрдбрд░ рдХреЗ рдорд╛рдзреНрдпрдо рд╕реЗред рдЬрд╛рдВрдЪреЗрдВ рдХрд┐ Express рдФрд░ Fastify рдкрд░ Intlayer middleware рд╕реЗ рдкрд╣рд▓реЗ cookie parser рдЪрд▓рддрд╛ рд╣реИ, рдЕрдиреНрдпрдерд╛ cookie рдХрднреА рдирд╣реАрдВ рдкрдврд╝реА рдЬрд╛рддреА рд╣реИ рдФрд░ рд╣рд░ рд░рд┐рдХреНрд╡реЗрд╕реНрдЯ Accept-Language рдкрд░ fallback рд╣реЛ рдЬрд╛рддреА рд╣реИред
рдЗрд╕реЗ рдкрд╛рд╕ рдХрд░реЗрдВред integrations resolved locale рдХреЛ expose рдХрд░рддреЗ рд╣реИрдВ (res.locals.locale, req.intlayer.locale, c.get("locale"), intlayer!.locale), рдФрд░ рдЗрд╕реЗ getIntlayer рдХреЛ hand рдХрд░рдирд╛ рдкреНрд░рддреНрдпреЗрдХ renderer рдХреЛ рдПрдХ locale рдХрд╛ pure function рдмрдирд╛рддрд╛ рд╣реИред рдпрд╣ test рдХрд░рдирд╛ рдЖрд╕рд╛рди рд╣реИ, рдФрд░ рдпрд╣ рдЖрдкрдХреЗ fragment renderers рдХреЛ portable рд░рдЦрддрд╛ рд╣реИ рдпрджрд┐ рдЖрдк server рдмрджрд▓рддреЗ рд╣реИрдВред
рдирд╣реАрдВред рд╕рдм рдХреБрдЫ рдЬреЛ рдПрдХ visitor рджреЗрдЦрддрд╛ рд╣реИ server рджреНрд╡рд╛рд░рд╛ produced рд╣реЛрддрд╛ рд╣реИ, рдЗрд╕рд▓рд┐рдП browser рдореЗрдВ translate рдХрд░рдиреЗ рдХреЗ рд▓рд┐рдП рдХреБрдЫ рдирд╣реАрдВ рд╣реИред рдпрд╣ рднреА рд╣реИ рдХрд┐ рдХреНрдпреЛрдВ рдПрдХ htmx app рдореЗрдВ i18n рдХреА page weight cost рд▓рдЧрднрдЧ zero рд╣реИ: рдХреЛрдИ рднреА catalog рдХрднреА client рдХреЛ shipped рдирд╣реАрдВ рд╣реЛрддрд╛ рд╣реИред
рдЕрдкрдиреЗ рдкреГрд╖реНрдареЛрдВ рдХреЛ locale prefix рдХреЗ рддрд╣рдд рдкрд░реЛрд╕реЗрдВ (/fr/cart) рдФрд░ рдЕрдкрдиреЗ route handler рдореЗрдВ path рд╕реЗ locale рдХреЛ рдкрдврд╝реЗрдВ, рдмрдЬрд╛рдп cookie рд╕реЗ, рдкреВрд░реНрдг рдкреГрд╖реНрда render рдХреЗ рд▓рд┐рдПред Fragments cookie рдпрд╛ header рдХрд╛ рдЙрдкрдпреЛрдЧ рдХрд░рддреЗ рд░рд╣ рд╕рдХрддреЗ рд╣реИрдВред Routing options рдХреЗ рд▓рд┐рдП configuration рдФрд░ custom URL rewrites рджреЗрдЦреЗрдВред
getHTMLTextDir(locale) ltr, rtl рдпрд╛ auto return рдХрд░рддрд╛ рд╣реИред рдЗрд╕реЗ document рдкрд░ initial render рдХреЗ рд▓рд┐рдП рд╕реЗрдЯ рдХрд░реЗрдВ, рдФрд░ swap рдХреЗ рдмрд╛рдж рдЗрд╕реЗ рдлрд┐рд░ рд╕реЗ рд▓рд╛рдЧреВ рдХрд░реЗрдВ рдЬреИрд╕рд╛ рдХрд┐ step 8 рджрд┐рдЦрд╛рддрд╛ рд╣реИред CSS logical properties рдХрд╛ рдЙрдкрдпреЛрдЧ рдХрд░реЗрдВ (margin-inline-start рдмрдЬрд╛рдп margin-left рдХреЗ) рддрд╛рдХрд┐ рдЖрдкрдХрд╛ layout follow рдХрд░реЗред
рд╣рд╛рдВ, рдХрд┐рд╕реА рднреА рдЪреАрдЬрд╝ рдХреЗ рд▓рд┐рдП рдЬреЛ рдЖрдк рдПрдХ template string рдореЗрдВ interpolate рдХрд░рддреЗ рд╣реИрдВ, рдмрд┐рд▓реНрдХреБрд▓ рдХрд┐рд╕реА рднреА рдЕрдиреНрдп dynamic value рдХреА рддрд░рд╣ред CMS рд╕реЗ рдпрд╛ translator рд╕реЗ рдЖрдиреЗ рд╡рд╛рд▓реА content markup рдирд╣реАрдВ рд╣реИ рдЬрд┐рд╕реЗ рдЖрдк рдирд┐рдпрдВрддреНрд░рд┐рдд рдХрд░рддреЗ рд╣реИрдВред Step 5 рдПрдХ minimal escaper рджрд┐рдЦрд╛рддрд╛ рд╣реИред
