Задайте питання та отримайте підсумок документа, вказавши цю сторінку та обраного вами постачальника штучного інтелекту
Вміст цієї сторінки перекладено за допомогою штучного інтелекту.
Переглянути останню версію оригінального вмісту англійськоюЯкщо у вас є ідея щодо покращення цієї документації, будь ласка, долучіться, надіславши pull request на GitHub.
Посилання на документацію на GitHubСкопіювати документацію у форматі Markdown в буфер обміну
Astro i18n: routing is built in, translations are not
Astro ships an i18n config that handles locale prefixes, redirects and fallbacks. It does not ship a message catalog, an interpolation syntax or plural handling: you bring those. This post covers what Astro gives you natively, where it stops, and the specific problem nobody warns you about: getting translations into an island.
Table of Contents
What Astro actually gives you
Enable it in astro.config.mjs:
Скопіюйте код у буфер обміну
That buys you four things:
Відкрийте таблицю в модальному вікні, щоб чітко переглянути всі дані
| Feature | What it does |
|---|---|
Astro.currentLocale | The locale parsed from the URL, or defaultLocale |
Astro.preferredLocale | Best match from the Accept-Language header (SSR only) |
getRelativeLocaleUrl / getRelativeLocaleUrlList | Build links to the same page in another language |
i18n.fallback | Redirect or rewrite a missing locale to another one |
And that's the list. There is no t(). Astro's own documentation is explicit about this: you supply the translation layer. The usual starting point is a hand-rolled object plus a helper.
Скопіюйте код у буфер обміну
In a .astro file this is genuinely fine. The frontmatter runs at build time, the strings are inlined into static HTML, and nothing ships to the browser. Astro is a good place to do i18n badly and get away with it.
Then you add an island
Here is the same helper inside a React island:
Скопіюйте код у буфер обміну
On /fr/panier this renders "3 items". The reason is the part of Astro people skip: an island is a separate client entry point, not a child of the page. Astro.currentLocale exists in frontmatter and nowhere else. The island gets props and its own bundle, and that is all.
There are three ways out and each one costs something:
- Pass the locale as a prop.
<Cart count={3} lang={Astro.currentLocale} client:load />. Correct, but nowimport { ui }pulls every language's strings into the island's client bundle, because a static import isn't split by locale. - Pass the resolved strings as props. No catalog in the bundle, but you hand-plumb every string through the page, and the props aren't checked against anything. Add a string to the island, forget the page, ship English.
- Detect the locale in the island at runtime. Read
document.documentElement.langon mount. Withclient:loadthe island is also rendered on the server, where there is no DOM to read, so the prerendered markup is in the default language and swaps on hydration. That's the flash of untranslated content.
Add a second island framework and it compounds: a React island and a Vue island each need their own i18n runtime, their own initialization, and their own copy of the catalog. Same strings, two sources of truth.
How the common libraries handle it
i18next / react-i18next / vue-i18n. Mature, well documented, plurals via Intl.PluralRules, interpolation, an enormous plugin ecosystem. In Astro you initialize an instance in the .astro frontmatter and an instance inside each island. Namespace loading is manual, so it is easy to over-ship: if the island imports common.json and common.json is 40 KB across all locales, that's what the browser downloads. This is the safest choice if your team already knows i18next.
astro-i18n (community). Astro-native, gives you a t() with interpolation and variants on the page side. The island side is still yours to solve.
Paraglide. Compiles each message into a tree-shakeable function, so import { cart_items } from "./paraglide/messages" works identically in .astro frontmatter and in a React island, and the bundler drops what you don't call. Genuinely well suited to Astro's shape. The trade-off is the one described in Compiler vs. declarative i18n: the message layer lives in generated code rather than in your source.
Astro native + hand-rolled. Zero dependencies, and for a five-page marketing site with no islands it's the right answer. It stops scaling the moment you need plurals in Polish or a translator who isn't you.
How Intlayer handles it
Intlayer declares content per component in a .content.ts file and compiles it at build time. Astro is one of the few frameworks where that maps cleanly onto the runtime model: an island is already a component boundary, so the dictionary boundary and the bundle boundary are the same line.
Скопіюйте код у буфер обміну
Скопіюйте код у буфер обміну
Declare the content once, next to the component that uses it:
Скопіюйте код у буфер обміну
The .astro page reads it with getIntlayer and passes only the locale down:
Скопіюйте код у буфер обміну
And the island resolves its own content from the locale it was handed:
Скопіюйте код у буфер обміну
Two things follow from this. The island receives the page's locale as a prop, so it hydrates in the right language instead of flashing the default one. And the same cart dictionary is readable from astro-intlayer on the page and from vue-intlayer or svelte-intlayer in another island, so you do not run one i18n library per island runtime.
Trade-offs and common mistakes
Don't run two routing layers. If you set Astro's i18n.locales and Intlayer's routing.mode, you get two components deciding what /fr/about means, and prefix redirects can bounce. Pick one owner for the URL shape.
client:only islands are invisible to crawlers. The Intlayer Astro guides use client:only="react", which means that island renders nothing at build time. Keep anything you want indexed (headings, body copy, links) in .astro frontmatter, and reserve islands for interactive UI.
hreflang has to be built from the unlocalized path. Generating alternates from Astro.url.pathname on /fr/about will produce /es/fr/about unless you strip the prefix first. getLocalizedUrl handles this; a hand-rolled version usually doesn't. See the hreflang guide.
Intlayer's honest downsides. It needs a build step: dictionaries are generated into .intlayer/, which you add to tsconfig.json include and to .gitignore. Its ecosystem is much smaller than i18next.s, with fewer StackOverflow answers and fewer third-party plugins, and the project is younger. If you already have JSON catalogs and a working i18next setup, migrating buys you bundle size and typing, not correctness. Check the benchmark before deciding that trade is worth it.
Migration doesn't have to be a rewrite. The compat adapters expose the same public API as your current library and delegate to Intlayer underneath, so useTranslation() calls keep working while the content layer moves.
Going further
- Translate your Astro website with Intlayer: routing, sitemap, robots.txt and locale switcher for
.astropages - Astro + React islands setup, and the equivalents for Vue and Svelte
- Configuration reference:
routing.mode, domains, locale storage - i18next compat adapter and the i18next migration guide
- Bundle optimization: what actually reaches the browser
- What is internationalization (i18n)? and the meaning of i18n
- Same series, other runtimes: React i18n, Vue i18n, Svelte i18n
Коментарі
Поки що немає коментарів. Будьте першим, хто поділиться своїми думками.
