Sorunuzu sorun ve bu sayfaya ve seçtiğiniz AI sağlayıcısına referans vererek belgenin bir özetini alın
Sürüm Geçmişi
- "Initial history"v9.4.129.08.2026
Bu sayfanın içeriği bir yapay zeka kullanılarak çevrildi.
Orijinal içeriğin İngilizce son sürümünü görüntüleyinBu dokümantasyonu geliştirmek için bir fikriniz varsa, lütfen GitHub'da bir çekme isteği göndererek katkıda bulunmaktan çekinmeyin.
Dokümantasyon için GitHub bağlantısıBelge Markdown'ını panoya kopyala
Intlayer kullanarak htmx uygulamanızı çevirin | Uluslararasılaştırma (i18n)
htmx kendi içeriğini render etmez. Bir ziyaretçinin okuduğu her etiket sunucunuzun ürettiği HTML'dir ve her swap ayrı bir HTTP isteğidir. Bu nedenle, bir htmx uygulamasını uluslararasılaştırmak bir sunucu sorumluluğudur: locale her istekte çözülmeli ve her fragment o locale'de render edilmelidir.
Intlayer bunu backend entegrasyonları aracılığıyla kapsar ve bu entegrasyonlar her istekte locale'yi algılar ve bildirilen içeriğinizi HTML'yi oluşturan handler'a sunar.
İçindekiler
htmx uygulamasında i18n'nin üç kuralı
Tek bir sayfa düzinelerce swap tetikleyebilir. Her biri, onu başlatan sayfanın belleğine sahip olmayan yeni bir istek. Eğer yerel ayar, ilk render sırasında ayarlanan bir değişkende bulunuyorsa, sonraki her fragment varsayılan dile geri döner.
Intlayer middleware, yerel ayarı istek kendisinden çözer, bu nedenle onuncu dakikada sunulan bir fragment, sıfırıncı dakikada sunulan sayfa ile aynı dilde cevap verir.
htmx ile iki taşıyıcı çalışır. Bir cookie (INTLAYER_LOCALE), tarayıcı tarafından htmx olanlar da dahil olmak üzere her istek üzerinde otomatik olarak gönderilir. Bir başlık (x-intlayer-locale), hx-headers özniteliği ile htmx isteklerine eklenebilir. Her ikisi de varsayılan olarak okunur.
Bir parçaya interpole edilen çevrilmiş bir değer markup'tır. Bunu diğer dinamik değerler gibi tam olarak escape edin, böylece < içeren bir çeviri, değiştirildiği belgeyi bozamaz.
Adım Adım Rehber
GitHub'ta Uygulama Şablonu sayfasına bakın.
Bağımlılıkları Yükleyin
intlayerve sunucunuz için entegrasyonu yükleyin.bashKodu kopyalaKodu panoya kopyala
bashKodu kopyalaKodu panoya kopyala
bashKodu kopyalaKodu panoya kopyala
bashKodu kopyalaKodu panoya kopyala
bashKodu kopyalaKodu panoya kopyala
Express ve Fastify, locale cookie'sini kendi cookie parser'ları aracılığıyla okurlar, bu nedenle bunlar yanında yüklenmelidir. Hono ve Elysia, cookie'leri yerel olarak ayrıştırırlar.
htmx'in kendisi, adım 4'te eklenen tek bir script tag'idir.
Projenizin Konfigürasyonu
Proje kökinizde bir
intlayer.config.tsdosyası oluşturun:intlayer.config.tsKodu kopyalaKodu panoya kopyala
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;Konfigürasyonun tam seçenekleri için yapılandırma belgelerine bakın.
İçeriğinizi Bildirin
Sunucunun oluşturacağı tüm etiketleri bildirin; bunlar yalnızca bir fragment içinde görünen etiketleri de içerir:
src/app.content.tsKodu kopyalaKodu panoya kopyala
import { insert, t, type Dictionary } from "intlayer"; const appContent = { key: "app", content: { pageTitle: "Intlayer + htmx", localeLabel: t({ tr: "Dil", en: "Language", fr: "Langue", es: "Idioma", ar: "اللغة", }), cartSummary: insert( t({ tr: "Sepetinizde yer alan ürünler: {{count}}", en: "Items in your cart: {{count}}", fr: "Articles dans votre panier : {{count}}", es: "Artículos en tu carrito: {{count}}", ar: "المنتجات في سلتك: {{count}}", }) ), addItem: t({ tr: "Bir öğe ekle", en: "Add an item", fr: "Ajouter un article", es: "Añadir un artículo", ar: "أضف منتجًا", }), }, } satisfies Dictionary; export default appContent;İçerik bildirimleri
contentDiraltında herhangi bir yerde bulunabilir (varsayılan olarak./src) ve.content.{json,ts,tsx,js,jsx,mjs,cjs,md,mdx,yaml,yml}ile eşleşir. Bkz. içerik bildirimi belgeleri.Intlayer middleware'ini kaydet
Middleware, her isteğin locale'ini çözer ve handler'larınıza açığa çıkarır.
src/index.tsKodu kopyalaKodu panoya kopyala
import cookieParser from "cookie-parser"; import express from "express"; import { intlayer } from "express-intlayer"; const app = express(); // Cookie parser önce çalışmalıdır: `express-intlayer` locale // cookie'sini `req.cookies` aracılığıyla okur. app.use(cookieParser()); app.use(express.urlencoded({ extended: false })); app.use(intlayer());Çözümlenen locale
res.locals.localeüzerindedir.src/index.tsKodu kopyalaKodu panoya kopyala
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);Çözümlenen yerel ayar
req.intlayer.localeüzerindedir.src/index.tsKodu kopyalaKodu panoya kopyala
import { Hono } from "hono"; import { intlayer } from "hono-intlayer"; const app = new Hono(); app.use("*", intlayer());Çözümlenen yerel ayar
c.get("locale")üzerindedir.src/index.tsKodu kopyalaKodu panoya kopyala
import { Elysia } from "elysia"; import { intlayer } from "elysia-intlayer"; const app = new Elysia().use(intlayer());Çözümlenen locale, route context'inde
intlayer!.localeüzerindedir.Varsayılan olarak locale,
INTLAYER_LOCALEcookie'sinden, sonrax-intlayer-localeheader'ından, sonraAccept-Languagegörüşmesinden alınır.İstek locale'i ile fragment'ları render edin
Fragment renderer'larınızı bir locale'in saf fonksiyonları olarak yazın ve middleware'in çözümlediği locale'i geçin. Bunu açık bir şekilde geçmek, fragment'ı hangi sunucuda olursa olsun, onu isteyen istekle bağlı tutar.
src/views.tsKodu kopyalaKodu panoya kopyala
import { currency, getIntlayer, type Locale } from "intlayer"; const HTML_ENTITIES: Record<string, string> = { "&": "&", "<": "<", ">": ">", '"': """, "'": "'", }; /** Çevirilen bir değeri markup'tan çıkamaması için escape eder. */ 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>`; };Bunu bir route'tan sunun:
src/index.tsKodu kopyalaKodu panoya kopyala
app.post("/cart/items", (req, res) => { const itemCount = Number(req.body?.itemCount ?? 0) + 1; res.type("html").send(renderCart(res.locals.locale, itemCount)); });src/index.tsKodu kopyalaKodu panoya kopyala
fastify.post("/cart/items", async (req, reply) => { const itemCount = Number((req.body as { itemCount?: string })?.itemCount ?? 0) + 1; return reply .type("text/html") .send(renderCart(req.intlayer.locale, itemCount)); });src/index.tsKodu kopyalaKodu panoya kopyala
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.tsKodu kopyalaKodu panoya kopyala
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" }, }); });Aynı fragment artık
frdiyen bir ziyaretçi için Fransızca,ardiyen bir ziyaretçi için Arapça olarak yanıt veriyor, çağrı işaretlemesinde herhangi bir değişiklik olmaksızın.İlk sayfayı sunun
<body>öğesini kendi başına render edin, böylece 7. adımdaki locale anahtarı onu tamamen değiştirebilsin, sonra htmx yükleyen belgeyle sarın:src/views.tsKodu kopyalaKodu panoya kopyala
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 içinltr,rtlveyaautodöndürür, bu da Arapça ve İbranice'nin doğru şekilde düzenlenmesini sağlar.Dili değiştir
Dil değiştirmek diğer herhangi bir istek gibidir. Sunucu seçimi middleware'in okuduğu cookie'de depolar, ardından sayfayı yeni locale'de yeniden render ederek döndürür.
Anahtarı bir
selectolarak işleyin ve tüm<body>'yi değiştirecek şekilde kendinizi gönderip takas edin, böylece statik etiketleriniz de değişsin:src/views.tsKodu kopyalaKodu panoya kopyala
import { getIntlayer, getLocaleName, type Locale, locales } from "intlayer"; const renderLocaleSwitcher = (locale: Locale): string => { const content = getIntlayer("app", locale); const options = locales .map( (availableLocale: Locale) => `<option value="${availableLocale}"${availableLocale === locale ? " selected" : ""}>${escapeHtml(getLocaleName(availableLocale, locale))}</option>` ) .join(""); 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)her dilde mevcut olan dili yazar. Bunun yerine her birini kendi dilinde yazmak için ikinci bir argüman iletmeyin.POST'u, değeri doğrulayarak, cookie'yi ayarlayarak ve yeni body'yi döndürerek işleyin:
src/index.tsKodu kopyalaKodu panoya kopyala
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.tsKodu kopyalaKodu panoya kopyala
import { isDeclaredLocale } from "intlayer"; fastify.post("/locale", async (req, reply) => { const requestedLocale = String((req.body as { locale?: string })?.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.tsKodu kopyalaKodu panoya kopyala
import { setCookie } from "hono/cookie"; import { isDeclaredLocale } from "intlayer"; app.post("/locale", async (c) => { const body = await c.req.parseBody(); // İstenen locale'i body'den al const requestedLocale = String(body["locale"]); if (!isDeclaredLocale(requestedLocale)) { return c.text("Unknown locale", 400); } setCookie(c, "INTLAYER_LOCALE", requestedLocale, { sameSite: "Lax", path: "/", }); return c.html(renderBody(requestedLocale, 0)); });src/index.tsKodu kopyalaKodu panoya kopyala
import { isDeclaredLocale } from "intlayer"; app.post("/locale", ({ body, cookie, status }) => { const requestedLocale = String((body as { locale?: string })?.locale); // Yerel ayarın bildirilmiş bir yerel ayar olup olmadığını kontrol et if (!isDeclaredLocale(requestedLocale)) { return status(400, "Unknown locale"); } // INTLAYER_LOCALE cookie'sini ayarla cookie["INTLAYER_LOCALE"]!.set({ value: requestedLocale, sameSite: "lax", path: "/", }); // Yanıt olarak HTML gövdesini döndür return new Response(renderBody(requestedLocale, 0), { headers: { "content-type": "text/html" }, }); });isDeclaredLocale, keyfi bir string'i yapılandırılmış yerel ayarlarınızdan birine daraltır, bu nedenle beklenmeyen bir değer hiçbir zaman rendererlarınıza ulaşmaz.Swap sonrasında lang ve dir'i senkronize tut
İsteğe bağlıBir swap
<body>öğesini değiştirebilir, etrafındaki<html>öğesini asla değiştiremez. Swap yapılan body öğesindelangvedirözniteliklerini render edin ve bunları başlık bölümünden kök öğeye bir kez geri kopyalayın:src/views.tsKodu kopyalaKodu panoya kopyala
Bunu yapmazsanız, Arapçaya geçiş body içinde sağdan sola doğru render olurken, belge hala önceki dili yardımcı teknolojilere ve tarayıcılara bildirir.
Cookie yerine locale'i header olarak gönder
İsteğe bağlıBir cookie size uygun değilse, bir üst öğedeki
hx-headersile her htmx isteğine locale'i ekleyin. Alt öğeler bunu miras alır:htmlKodu kopyalaKodu panoya kopyala
Middleware varsayılan olarak
x-intlayer-localeokur. Her iki taşıyıcıyı da yapılandırmanızda yeniden adlandırabilirsiniz:intlayer.config.tsKodu kopyalaKodu panoya kopyala
import { Locales, type IntlayerConfig } from "intlayer"; const config: IntlayerConfig = { // ... Diğer yapılandırma seçenekleri routing: { storage: [ { type: "header", name: "my-locale-header" }, { type: "cookie", name: "my-locale-cookie" }, ], }, }; export default config;
TypeScript'i Yapılandırın
Otomatik olarak oluşturulan türleri ekleyerek, bildirilmemiş bir anahtarın runtime'da boş bir string yerine compile hatası olmasını sağlayın.
Kodu panoya kopyala
Git Konfigürasyonu
Intlayer tarafından oluşturulan dosyaları yok saymak önerilir:
Kodu panoya kopyala
VS Code Uzantısı
Intlayer ile geliştirme deneyiminizi iyileştirmek için resmi Intlayer VS Code Uzantısı'nı yükleyebilirsiniz.
VS Code Marketplace'ten Yükleyin
Bu extension şunları sağlar:
- Çeviri anahtarları için otomatik tamamlama.
- Eksik çeviriler için gerçek zamanlı hata algılama.
- Çevrilmiş içeriğin satır içi önizlemeleri.
- Çevirileri kolayca oluşturmak ve güncellemek için hızlı eylemler.
Extension'ın nasıl kullanılacağı hakkında daha fazla bilgi için Intlayer VS Code Extension belgelerine bakın.
Daha İleri Gidin
Daha ileri gitmek için, içeriğinizi CMS kullanarak dışsallaştırabilirsiniz, böylece çevirmenler bir deployment olmadan metni değiştirebilir.
Sıkça Sorulan Sorular
Çünkü fragment isteği hiçbir locale taşımadı. htmx istekleri onu yayınlayan sayfadan bağımsızdır, bu nedenle locale her birinde seyahat etmelidir, INTLAYER_LOCALE cookie'si veya hx-headers ile ayarlanan x-intlayer-locale başlığı aracılığıyla. Cookie parser'ın Express ve Fastify'de Intlayer middleware'den önce çalıştığını kontrol edin, aksi takdirde cookie hiçbir zaman okunmaz ve her istek Accept-Language'e geri döner.
Bunu geçin. Entegrasyonlar çözümlenmiş locale'i (res.locals.locale, req.intlayer.locale, c.get("locale"), intlayer!.locale) açığa çıkarır ve bunu getIntlayer'a vermek her renderer'ı bir locale'in saf fonksiyonu yapar. Bu test etmeyi kolaylaştırır ve sunucuyu değiştirirseniz fragment renderer'larınızı taşınabilir tutar.
Hayır. Ziyaretçinin gördüğü her şey sunucu tarafından üretilir, bu nedenle tarayıcıda çevirisi yapılacak bir şey yoktur. Bu ayrıca htmx uygulamasında i18n'nin sayfa ağırlığı maliyetinin neden sıfıra yakın olduğunun sebebidir: hiçbir katalog client'e gönderilmez.
Sayfalarınızı bir yerel ön eki altında sunun (/fr/cart) ve tam sayfa renderi için yerel kodu yoldan okuyun, tanımlama bilgisinden değil, rota işleyicinizde. Parçalar tanımlama bilgisini veya başlığı kullanmaya devam edebilir. konfigürasyon için yönlendirme seçeneklerine ve özel URL yeniden yazımları bölümüne bakın.
getHTMLTextDir(locale) ltr, rtl veya auto döndürür. İlk render için belgeye ayarlayın ve adım 8'in gösterdiği gibi bir takas sonrasında yeniden uygulayın. CSS mantıksal özellikleri kullanın (margin-left yerine margin-inline-start) böylece düzeniniz buna uyar.
Evet, bir şablon dizesine interpolate ettiğiniz herhangi bir şey için, diğer dinamik değerler gibi tam olarak aynı şekilde. CMS'den veya çevirmen tarafından gelen içerik kontrol ettiğiniz markup değildir. Adım 5 minimal bir escaper göstermektedir.
