Problems developers face when localizing Astro apps—and how IntlPull helps.
Built-in routing vs astro-i18next vs manual. Choosing the right approach takes research.
Some approaches require duplicating content files per language. Can get verbose.
Static generation works best. Dynamic/SSR i18n needs more setup.
Why astro-i18next is a great choice for Astro localization.
Astro 4 has native i18n routing. /es/, /de/ prefixes. No plugin for basic routing.
vs manual route setup
Pre-render all language versions at build time. Fast, CDN-friendly pages.
vs runtime translation
Organize content by language in collections. Markdown/MDX with locale folders.
vs single-language content
Use React, Vue, Svelte islands. Each can use its own i18n library if needed.
vs locked-in framework
Static HTML with proper lang attributes. Hreflang tags. Search engine friendly.
vs client-rendered content
Mix static and server pages. Dynamic routes can still use translations.
vs static-only
Get astro-i18next running in your Astro project.
Configure Astro's i18n in astro.config.mjs.
// astro.config.mjs
import { defineConfig } from 'astro/config';
export default defineConfig({
i18n: {
defaultLocale: 'en',
locales: ['en', 'es', 'de', 'fr'],
routing: {
prefixDefaultLocale: false
}
}
});Set up locale files.
// src/i18n/ui.ts
export const languages = {
en: 'English',
es: 'Espanol',
de: 'Deutsch',
};
export const defaultLang = 'en';
export const ui = {
en: {
'nav.home': 'Home',
'nav.about': 'About',
'nav.contact': 'Contact',
},
es: {
'nav.home': 'Inicio',
'nav.about': 'Acerca',
'nav.contact': 'Contacto',
},
de: {
'nav.home': 'Startseite',
'nav.about': 'Uber uns',
'nav.contact': 'Kontakt',
},
} as const;Helper function to get translations.
// src/i18n/utils.ts
import { ui, defaultLang } from './ui';
export function getLangFromUrl(url: URL) {
const [, lang] = url.pathname.split('/');
if (lang in ui) return lang as keyof typeof ui;
return defaultLang;
}
export function useTranslations(lang: keyof typeof ui) {
return function t(key: keyof typeof ui[typeof defaultLang]) {
return ui[lang][key] || ui[defaultLang][key];
}
}Use translations in Astro components.
---
// src/components/Nav.astro
import { getLangFromUrl, useTranslations } from '../i18n/utils';
const lang = getLangFromUrl(Astro.url);
const t = useTranslations(lang);
---
<nav>
<a href="/">{t('nav.home')}</a>
<a href="/about">{t('nav.about')}</a>
<a href="/contact">{t('nav.contact')}</a>
</nav>Generate pages for each locale.
// src/pages/[lang]/index.astro
---
import { languages } from '../../i18n/ui';
import Layout from '../../layouts/Layout.astro';
import { useTranslations } from '../../i18n/utils';
export function getStaticPaths() {
return Object.keys(languages).map((lang) => ({
params: { lang },
}));
}
const { lang } = Astro.params;
const t = useTranslations(lang as keyof typeof languages);
---
<Layout>
<h1>{t('nav.home')}</h1>
</Layout>Connect IntlPull to manage translations professionally.
Add the CLI for translation management.
npm install -D @intlpull/cliSet up the project configuration.
{
"projectId": "your-project-id",
"format": "json",
"sourceLocale": "en",
"localesDir": "./src/i18n/locales",
"filePattern": "{locale}.json"
}Pull translations and convert to TypeScript.
# Pull translations
npx intlpull pull
# Generate TypeScript (optional)
npx intlpull generate --format typescript --output ./src/i18n/ui.tsIntegrate with your build process.
// package.json
{
"scripts": {
"i18n:pull": "intlpull pull && intlpull generate --format typescript --output ./src/i18n/generated.ts",
"build": "npm run i18n:pull && astro build"
}
}Complete guide to Next.js App Router localization with next-intl. Server components, routing, metadata, and IntlPull integration.
Complete guide to Nuxt 3 localization with @nuxtjs/i18n. SSR, hybrid rendering, SEO, and IntlPull integration.