שאלו את שאלתכם וקבלו סיכום של המסמך על ידי הפניה לדף הזה ולספק ה-AI שתבחרו
היסטוריית גרסאות
- "Initial version"v9.5.1026.9.2026
התוכן של הדף הזה תורגם באמצעות AI.
צפייה בגרסה האחרונה של התוכן המקורי באנגליתאם יש לכם רעיון לשיפור התיעוד הזה, אל תהססו לתרום על ידי הגשת Pull Request ב-GitHub.
קישור GitHub לתיעודהעתקת ה-Markdown של המסמך ללוח
How to internationalize your TanStack Start application using Lingui in 2026
Table of Contents
What is Lingui?
Lingui is an i18n library built around macros and message extraction. You write the source text directly in your components ( t`Hello` , <Trans>Hello</Trans>), lingui extract collects every message into catalogs (PO files by default), translators fill them, and the Vite plugin compiles them to compact JavaScript. Messages use ICU MessageFormat, so plurals and selects are supported.
TanStack Start does not ship an i18n layer, so this guide wires Lingui into it from scratch:
- Macros compiled by Babel through
@rolldown/plugin-babel(required with@vitejs/plugin-reactv6 and Vite 8). - Locale routing with an optional
{-$locale}segment (/about,/fr/about). - One catalog per locale, loaded on demand, and an
I18ninstance per render so concurrent SSR requests never share a locale. - Complete multilingual SEO: translated
<title>and description, canonical URL,hreflangwithx-default, Open Graph locales, JSON-LD, sitemap,robots.txt, pre-rendering and localized 404 pages.
Looking for another stack?
Using Next.js?
Comparing libraries?
To understand where these libraries come from, read the history of JavaScript i18n.
What the benchmark says about Lingui on TanStack Start
The i18n benchmark runs the same 10-page, 10-locale TanStack Start app with every major library and measures what the browser actually downloads.
מדד
טעינת JSON דינמית
טעינה עצלה של תרגומים בזמן ריצה
JSON תחום (namespacing)
מרחבי שמות תרגום לכל דף
מהו המדד הזה?
הגודל הכולל לאחר דחיסת gzip של ה-bundle של ספריית הבינאום. הוא כולל רק את ה-provider ואת לוגיקת שליפת התוכן לאחר tree-shaking ומיזעור.
למה זה חשוב?
ספרייה קטנה יותר מקטינה את מטען ה-JavaScript ההתחלתי, מה שמוביל לזמני הורדה והרצה מהירים יותר בצד הלקוח.
הצגה כ
Key figures for @lingui/core@6.6.0, measured on 2026-09-26 (gzip):
פתיחת הטבלה בחלון כדי לראות את כל תוכן הנתונים בבירור
| Setup | Library size | JS per page | Other-locale leak | Other-page leak |
|---|---|---|---|---|
| No i18n (base app) | - | 111.0 KB | 0% | 0% |
| Lingui (setup of this guide) | 56.7 KB | 115.2 KB | 9.3% | 0% |
@intlayer/lingui (compat) | 9.8 KB | 136.7 KB | 9.9% | 0% |
react-intlayer (native Intlayer) | 4.5 KB | 126.8 KB | 0% | 0% |
What to take away:
- Load one catalog per locale, on demand. It keeps pages close to the base app size.
- The runtime stays heavy (~57 KB gzip). The
@intlayer/linguicompat adapter (step 16) keeps your macros and cuts it to ~10 KB.
See the full data: TanStack Start benchmark report, and the benchmark repository.
Feature comparison on TanStack Start
How Lingui compares with the other libraries commonly used on TanStack Start:
פתיחת הטבלה בחלון כדי לראות את כל תוכן הנתונים בבירור
| Feature | react-intlayer (Intlayer) | use-intl | Paraglide JS | Lingui |
|---|---|---|---|---|
| Translations near components | ✅ Co-located | ❌ Centralized JSON | ❌ One JSON file per locale | ⚠️ Source text in components |
| TypeScript integration | ✅ Auto-generated types | ✅ Via AppConfig | ✅ Typed message functions | ⚠️ Macros only |
| Missing translation detection | ✅ Type errors and build warnings | ⚠️ Runtime fallback | ⚠️ Falls back to the base locale | ⚠️ Falls back to the source text |
| Rich content (JSX, Markdown) | ✅ Direct support | ⚠️ Tags via t.rich | ⚠️ Strings | ✅ JSX inside <Trans> |
| Localized routing | ✅ Built-in | ❌ Manual {-$locale} | ✅ urlPatterns + router rewrite | ❌ Manual {-$locale} |
| Locale switch without reload | ✅ Yes | ✅ Yes | ❌ Full page reload | ✅ Yes |
| Pluralization | ✅ Enumeration-based | ✅ ICU | ✅ Variants | ✅ ICU |
| ICU MessageFormat | ✅ Via format: "icu" | ✅ Native | ⚠️ Via an inlang plugin | ✅ Native |
| Content formats | ✅ .ts, .json, .md, .yaml... | ⚠️ .json | ⚠️ inlang JSON | ✅ PO, JSON, CSV |
| AI translation | ✅ Your own provider and key | ❌ No | ❌ No | ❌ No |
| Visual editor / CMS | ✅ Local editor + optional CMS | ❌ External platforms | ⚠️ inlang ecosystem apps | ❌ External platforms |
| SEO helpers (hreflang, sitemap) | ✅ Built-in | ❌ Manual | ⚠️ Localized URLs, rest manual | ❌ Manual |
| Runtime size (gzip, benchmark) | 4.5 KB | 75.9 KB | 1.8 KB | 56.7 KB |
| Leak, best setup (locale / page) | 0% / 0% | 0% / 0% | 49.7% / 0% | 8.6% / 0% |
| Missing translations in CI | ✅ npx intlayer test | ⚠️ Not built-in | ⚠️ Not built-in | ✅ lingui compile --strict |
Runtime size and leak figures come from the TanStack Start benchmark. Leak is measured on the best setup of each library.
Other TanStack Start guides:
Practices you should follow
- Set
langanddiron<html>from the route locale, so they are correct in the server HTML. - Keep one URL per locale with a prefix, so every language version is indexable.
- Create one
I18ninstance per locale, never mutate a global one during SSR: two concurrent requests would overwrite each other's locale. - Load only the active catalog, never import all of them in client code.
- Pick one macro style (
useLingui+tin components,msgfor lazy descriptors) and stick to it. Mixingt,i18n._,i18n.tand<Trans>makes the code harder to read for humans and AI assistants. - Run
lingui extractin CI so a new message never ships untranslated. - Translate your metadata, and declare
canonical,hreflangandx-defaulton every page. - Generate a multilingual sitemap and robots.txt, and pre-render every locale.
Use real links for the locale switcher, so crawlers discover every language.
- internationalization and SEO
- hreflang guide
Step-by-Step Guide to Set Up Lingui in a TanStack Start Application
Here's the project structure we'll be creating:
העתקת הקוד ללוח
Install Dependencies
bashהעתקת קודהעתקת הקוד ללוח
- @lingui/core / @lingui/react: runtime,
I18nProviderand the macros (@lingui/core/macro,@lingui/react/macro). - @lingui/cli:
lingui extractto collect messages into catalogs. - @lingui/vite-plugin: compiles
.pocatalogs on import, solingui compileis not needed. - @lingui/babel-plugin-lingui-macro + @rolldown/plugin-babel: transform the macros at build time.
- @lingui/core / @lingui/react: runtime,
Centralize Your Locale Configuration
The default locale stays unprefixed (
/about), other locales are prefixed (/fr/about).src/i18n/config.tsהעתקת קודהעתקת הקוד ללוח
Configure Lingui
The Lingui config reuses the same locale list, so the catalogs, the router and the sitemap never disagree.
lingui.config.tsהעתקת קודהעתקת הקוד ללוח
Add the extraction scripts:
package.jsonהעתקת קודהעתקת הקוד ללוח
i18n:checkfails in CI when a component contains a message that was not extracted and committed.Configure Vite
With
@vitejs/plugin-reactv6, Babel is no longer built in.@rolldown/plugin-babelruns the Lingui macro plugin, andlinguiTransformerBabelPresetonly processes files that import a macro, which keeps builds fast.vite.config.tsהעתקת קודהעתקת הקוד ללוח
Load Catalogs per Locale
The template literal in
import()lets Vite emit one chunk per catalog, and the Lingui plugin compiles the.pofile into it. A French visitor downloads the French catalog only.The compiled messages are plain data, so they can be returned by a route loader, serialized into the HTML, and reused on hydration.
src/i18n/lingui.tsהעתקת קודהעתקת הקוד ללוח
For TypeScript to accept the
.poimport, declare the module once:src/i18n/po.d.tsהעתקת קודהעתקת הקוד ללוח
Create the Root Document
The root route reads the optional locale param to set
langanddiron the server-rendered<html>.src/routes/__root.tsxהעתקת קודהעתקת הקוד ללוח
Create the Locale Layout Route
The
{-$locale}folder creates an optional path segment:/aboutand/fr/aboutboth match/{-$locale}/about. The layout rejects unknown prefixes, loads the catalog of the current locale, and provides a dedicatedI18ninstance.src/routes/{-$locale}/route.tsxהעתקת קודהעתקת הקוד ללוח
Utilize Translations in Your Pages
Write the source text in the component. The macros turn it into message IDs at build time, and
lingui extractpicks it up.<Trans>for JSX content, including nested elements;useLingui().tfor strings (attributes, props);<Plural>for ICU plurals.
src/routes/{-$locale}/about.tsxהעתקת קודהעתקת הקוד ללוח
The dynamic
import()of a catalog is cached by the module system, so callingloadI18nin several loaders does not download the catalog twice.Extract and Translate Your Messages
Run the extraction. Lingui writes every message into each locale catalog:
bashהעתקת קודהעתקת הקוד ללוח
Then translate the
msgstrof each entry:src/locales/fr/messages.poהעתקת קודהעתקת הקוד ללוח
src/locales/es/messages.poהעתקת קודהעתקת הקוד ללוח
By default, message IDs are hashes of the source text: changing the English text creates a new message. Use explicit IDs (
<Trans id="about.title">About us</Trans>) for texts that change often.Build a Localized Link Component
אופציונליEvery route lives under
{-$locale}, so links must carry the current locale param.src/components/LocalizedLink.tsxהעתקת קודהעתקת הקוד ללוח
Change the Language of Your Content
אופציונליRender the switcher as links, so crawlers find every language version.
to="."keeps the current page and replaces the locale param. The loader of the locale layout then fetches the new catalog.src/components/LocaleSwitcher.tsxהעתקת קודהעתקת הקוד ללוח
Internationalize Your Metadata
אופציונליEach language version can rank on its own, provided every page exposes a translated
<title>and description, a self-referencing canonical, onehreflangper locale plusx-default, Open Graph locales, and JSON-LD withinLanguage. The metadata is translated in the loader (step 8), and this helper builds the rest:src/i18n/seo.tsהעתקת קודהעתקת הקוד ללוח
Internationalize Your Sitemap and robots.txt
אופציונליThe sitemap lists every URL of every locale, each entry declaring all its alternates with
xhtml:link.robots.txtblocks private routes in every language and points to the sitemap. Removepublic/robots.txtif the starter created one.src/routes/sitemap[.]xml.tsהעתקת קודהעתקת הקוד ללוח
src/routes/robots[.]txt.tsהעתקת קודהעתקת הקוד ללוח
Pre-render Every Locale
אופציונליList every localized path so TanStack Start pre-renders all language versions at build time:
vite.config.tsהעתקת קודהעתקת הקוד ללוח
Redirect First-Time Visitors and Handle 404 Pages
אופציונליA request middleware sends a visitor landing on
/to their preferred language (cookie first, thenAccept-Language). Deep links are never redirected, so crawlers and shared URLs always get the page they asked for.src/i18n/negotiateLocale.tsהעתקת קודהעתקת הקוד ללוח
src/start.tsהעתקת קודהעתקת הקוד ללוח
For 404 pages, a catch-all route renders the localized
notFoundComponentof the layout. Mark itnoindex: React 19 hoists the<meta>into<head>.src/components/NotFound.tsxהעתקת קודהעתקת הקוד ללוח
src/routes/{-$locale}/$.tsxהעתקת קודהעתקת הקוד ללוח
Keep Your Macros, Cut the Runtime with Intlayer
אופציונליThe
@intlayer/linguicompat adapter keeps your source untouched: the macros compile exactly as before, and the resultingi18n._(),useLingui()and<Trans>calls are served by compiled Intlayer dictionaries. In the benchmark, the runtime drops from ~56.7 KB to ~9.8 KB gzip.bashהעתקת קודהעתקת הקוד ללוח
Add the plugin after the macro transform, so it aliases
@lingui/coreand@lingui/reactto the adapter:vite.config.tsהעתקת קודהעתקת הקוד ללוח
Catalogs are synchronized with the sync JSON plugin (JSON catalogs) or the sync PO plugin (PO catalogs). See the full setup in the Lingui compat guide, and a side-by-side comparison in Lingui vs @intlayer/lingui.
Automate Your Translations Using Intlayer
אופציונליLingui extracts messages, but filling dozens of catalogs by hand is where most of the time goes. Intlayer is free and open source, and its tooling works alongside Lingui:
- Translate with AI using your own API key and provider. See auto fill and the CLI.
- Keep your PO files as the source of truth with the sync PO plugin.
- Test missing translations in CI. See testing your translations.
- Audit your deployed site for missing
hreflang, wrong canonicals and locale leaks with the scan command.
Frequently Asked Questions
Yes. Lingui has no dedicated TanStack Start integration, but its Vite plugin and Babel macro plugin work as is. The two points to get right are running the macros through @rolldown/plugin-babel (Vite 8 and @vitejs/plugin-react v6 no longer include Babel), and creating an I18n instance per locale instead of activating a global one during SSR.
On the server, one process renders many requests at the same time. Calling i18n.activate("fr") on a shared object would switch the language of a request rendering in English in parallel. setupI18n creates an isolated instance per locale, which is safe.
No. @lingui/vite-plugin compiles .po catalogs when they are imported. You only run lingui extract to collect new messages.
Declare them with the msg macro, and translate them in the route loader with i18n._(msg`...`). The loader returns plain strings, so head() stays synchronous and the values are serialized for hydration. Step 8 and step 12 show the full setup.
The benchmark measures ~56.7 KB gzip for the runtime. With one catalog per locale loaded on demand, pages weigh ~115 KB against 111 KB without i18n. Importing every catalog statically raises it to ~152 KB.
Yes. The @intlayer/lingui adapter keeps the macros and swaps the runtime. You can then move components to useIntlayer one at a time. See the compat adapters.
תגובות
עדיין אין תגובות. היו הראשונים לשתף את מחשבותיכם.
