Author:
    Creation:2026-08-29Last update:2026-08-29

    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

    ide.intlayer.org

    एप्लिकेशन टेम्पलेट को GitHub पर देखें।

    1. निर्भरताएं स्थापित करें

      intlayer प्लस अपने सर्वर के लिए इंटीग्रेशन स्थापित करें।

      bash
      npm install intlayer express-intlayer cookie-parser
      
      bash
      npm install intlayer fastify-intlayer @fastify/cookie @fastify/formbody
      
      bash
      npm install intlayer hono-intlayer
      
      bash
      npm install intlayer elysia-intlayer
      
      bash
      bun add intlayer elysia-intlayer
      
      Express और Fastify अपने स्वयं के cookie parsers के माध्यम से locale cookie को पढ़ते हैं, इसलिए उन्हें साथ में install करना होगा। Hono और Elysia नेटिवली cookies को parse करते हैं।

      htmx स्वयं एक एकल script tag है, जो step 4 में जोड़ा जाता है।

    2. अपनी 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;
      
      पूर्ण विकल्पों की सूची के लिए, कॉन्फ़िगरेशन दस्तावेज़ देखें।
    3. अपनी सामग्री घोषित करें

      सभी लेबल घोषित करें जो सर्वर रेंडर करेगा, जिनमें वे भी शामिल हैं जो केवल एक 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 देखें।
    4. 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!.locale on the route context.

      डिफ़ॉल्ट रूप से locale को INTLAYER_LOCALE cookie से लिया जाता है, फिर x-intlayer-locale header से, फिर Accept-Language negotiation से।

    5. रिक्वेस्ट 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> = {
        "&": "&amp;",
        "<": "&lt;",
        ">": "&gt;",
        '"': "&quot;",
        "'": "&#39;",
      };
      
      /** मार्कअप से बाहर न निकलने के लिए अनुवादित मान को एस्केप करता है। */
      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 कहता है, कॉलिंग मार्कअप में कोई बदलाव नहीं।

    6. पहले पृष्ठ को सर्व करें

      <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>`;
      

      getHTMLTextDir locale के लिए ltr, rtl या auto return करता है, जो अरबी और हिब्रू को सही तरीके से लेआउट करने के लिए आवश्यक है।

    7. भाषा स्विच करें

      भाषा स्विच करना किसी अन्य 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 तक नहीं पहुंचता है।
    8. swap के बाद lang और dir को सिंक में रखें

      वैकल्पिक

      एक swap <body> को replace कर सकता है, कभी भी इसके चारों ओर <html> को नहीं। Swapped body पर lang और dir को render करें और उन्हें head से एक बार root element पर वापस कॉपी करें:

      src/views.ts
      <script>
        document.addEventListener("htmx:afterSwap", () => {
          document.documentElement.lang = document.body.lang;
          document.documentElement.dir = document.body.dir;
        });
      </script>
      

      इसके बिना, अरबी में switch करने से body के अंदर दाएं से बाएं render होता है जबकि document अभी भी पिछली भाषा को assistive technology और crawlers को advertise करता है।

    9. वैकल्पिक

      यदि कुकी आपके लिए उपयुक्त नहीं है, तो hx-headers के साथ एक ancestor element पर हर htmx request में locale को attach करें। Descendants इसे inherit करते हैं:

      html
      <body hx-headers='{"x-intlayer-locale": "fr"}'>
        ...
      </body>
      

      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 हो।

    tsconfig.json
    {
      // ... आपके मौजूदा TypeScript कॉन्फ़िगरेशन
      "include": [
        // ... आपके मौजूदा TypeScript कॉन्फ़िगरेशन
        ".intlayer/**/*.ts", // ऑटो-जेनरेटेड टाइप्स शामिल करें
      ],
    }
    

    Git कॉन्फ़िगरेशन

    Intlayer द्वारा जेनरेट की गई फाइलों को अनदेखा करना अनुशंसित है:

    .gitignore
    # Intlayer द्वारा जेनरेट की गई फाइलों को अनदेखा करें
    .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 दिखाता है।