How do you implement internationalization (i18n) in Astro? How do you configure a multilingual website?
Astro's internationalization (i18n) feature allows developers to easily build multilingual websites. Understanding how to configure and use Astro's i18n functionality is crucial for projects targeting global users.
Basic Configuration:
javascript// astro.config.mjs import { defineConfig } from 'astro/config'; import { i18n } from 'astro-i18next'; export default defineConfig({ integrations: [ i18n({ defaultLocale: 'en', locales: ['en', 'zh', 'ja', 'es'], fallbackLocale: 'en', routing: { prefixDefaultLocale: false, }, }), ], });
Using astro-i18next Integration:
bashnpm install astro-i18next i18next
javascript// astro.config.mjs import { defineConfig } from 'astro/config'; import i18next from 'astro-i18next'; export default defineConfig({ integrations: [ i18next({ defaultLocale: 'en', locales: ['en', 'zh'], routingStrategy: 'prefix', }), ], });
Translation File Structure:
shellsrc/ ├── i18n/ │ ├── en/ │ │ ├── common.json │ │ └── home.json │ ├── zh/ │ │ ├── common.json │ │ └── home.json │ └── ja/ │ ├── common.json │ └── home.json
Translation File Examples:
json// src/i18n/en/common.json { "nav": { "home": "Home", "about": "About", "contact": "Contact" }, "buttons": { "submit": "Submit", "cancel": "Cancel" } }
json// src/i18n/zh/common.json { "nav": { "home": "首页", "about": "关于", "contact": "联系" }, "buttons": { "submit": "提交", "cancel": "取消" } }
Using Translations in Components:
astro--- import { useTranslation } from 'astro-i18next'; const { t } = useTranslation(); --- <nav> <a href="/">{t('nav.home')}</a> <a href="/about">{t('nav.about')}</a> <a href="/contact">{t('nav.contact')}</a> </nav> <button>{t('buttons.submit')}</button>
Language Switcher:
astro--- import { useTranslation, useLocale } from 'astro-i18next'; const { t } = useTranslation(); const locale = useLocale(); const locales = ['en', 'zh', 'ja', 'es']; --- <div class="language-switcher"> {locales.map(loc => ( <a href={`/${loc === 'en' ? '' : loc}`} class={locale === loc ? 'active' : ''} > {loc.toUpperCase()} </a> ))} </div> <style> .language-switcher { display: flex; gap: 0.5rem; } .active { font-weight: bold; text-decoration: underline; } </style>
Dynamic Route Internationalization:
astro--- // src/pages/[lang]/blog/[slug].astro import { useTranslation, useLocale } from 'astro-i18next'; import { getCollection } from 'astro:content'; const { t } = useTranslation(); const locale = useLocale(); const { slug } = Astro.params; const post = await getEntry('blog', slug); const { Content } = await post.render(); --- <h1>{post.data.title}</h1> <Content />
Content Collection Internationalization:
markdown--- # src/content/en/blog/my-post.md title: "My English Post" publishDate: 2024-01-15 --- This is the English version of the post.
markdown--- # src/content/zh/blog/my-post.md title: "我的中文文章" publishDate: 2024-01-15 --- 这是文章的中文版本。
astro--- // src/pages/blog/[slug].astro import { getCollection } from 'astro:content'; import { useLocale } from 'astro-i18next'; const locale = useLocale(); const { slug } = Astro.params; const post = await getEntry(`blog-${locale}`, slug); const { Content } = await post.render(); --- <h1>{post.data.title}</h1> <Content />
Date and Number Formatting:
astro--- import { useTranslation } from 'astro-i18next'; const { t, i18n } = useTranslation(); const date = new Date(); const number = 1234567.89; --- <p>Date: {date.toLocaleDateString(i18n.language)}</p> <p>Number: {number.toLocaleString(i18n.language)}</p> <p>Currency: {number.toLocaleString(i18n.language, { style: 'currency', currency: 'USD' })}</p>
SEO Optimization:
astro--- import { useTranslation, useLocale } from 'astro-i18next'; const { t } = useTranslation(); const locale = useLocale(); --- <html lang={locale}> <head> <meta charset="UTF-8" /> <title>{t('meta.title')}</title> <meta name="description" content={t('meta.description')} /> <!-- Language switch links --> <link rel="alternate" hreflang="en" href="/en" /> <link rel="alternate" hreflang="zh" href="/zh" /> <link rel="alternate" hreflang="ja" href="/ja" /> <link rel="alternate" hreflang="x-default" href="/" /> </head> <body> <slot /> </body> </html>
Server-Side Rendering (SSR) Internationalization:
typescript// src/middleware.ts import { defineMiddleware } from 'astro:middleware'; export const onRequest = defineMiddleware((context, next) => { const url = new URL(context.request.url); const pathSegments = url.pathname.split('/').filter(Boolean); // Detect language const detectedLocale = detectLocale(context.request); // Redirect to detected language if no language prefix if (!pathSegments[0] || !['en', 'zh', 'ja'].includes(pathSegments[0])) { return context.redirect(`/${detectedLocale}${url.pathname}`); } // Store language in locals context.locals.locale = pathSegments[0]; return next(); }); function detectLocale(request: Request): string { const acceptLanguage = request.headers.get('accept-language'); const browserLocale = acceptLanguage?.split(',')[0].split('-')[0] || 'en'; const supportedLocales = ['en', 'zh', 'ja']; return supportedLocales.includes(browserLocale) ? browserLocale : 'en'; }
RTL (Right-to-Left) Language Support:
astro--- import { useTranslation, useLocale } from 'astro-i18next'; const { t } = useTranslation(); const locale = useLocale(); const rtlLocales = ['ar', 'he', 'fa']; const isRTL = rtlLocales.includes(locale); --- <html lang={locale} dir={isRTL ? 'rtl' : 'ltr'}> <head> <style> body { direction: {isRTL ? 'rtl' : 'ltr'}; } </style> </head> <body> <slot /> </body> </html>
Best Practices:
-
Translation Management:
- Use professional translation tools (like Crowdin, Locize)
- Keep translation file structure consistent
- Regularly review and update translations
-
Performance Optimization:
- Load translation files on demand
- Use caching to reduce duplicate requests
- Preload commonly used languages
-
User Experience:
- Provide clear language switcher
- Remember user language preference
- Handle missing translations gracefully
-
SEO Considerations:
- Set correct hreflang for each language
- Use appropriate language tags
- Avoid duplicate content issues
-
Development Workflow:
- Use type-safe translation keys
- Automate translation checks
- Integrate into CI/CD pipeline
Astro's internationalization feature provides flexible multilingual support to help developers build applications for global users.