Internationalization is an opt-in feature. Without any configuration, the site behaves as a single-language site with unprefixed URLs (/about, etc.). AstroTable is designed to work for non-ecommerce sites too (portfolios, for example), so multilingual support isn't a core assumption.
This site itself opts into two locales: ja (Japanese) and en (English). The rest of this page describes the opted-in setup.
Configuration
Add Astro's built-in i18n routing to astro.config.mjs.
// astro.config.mjs
export default defineConfig({
i18n: {
defaultLocale: 'ja',
locales: ['ja', 'en'],
routing: { prefixDefaultLocale: true },
},
});
With routing.prefixDefaultLocale: true, every page is locale-prefixed (/ja/..., /en/...), including pages in the default locale (ja). There is no unprefixed URL. Requests to the root / are redirected to /ja/ by the deployment platform (this site uses Cloudflare's public/_redirects, supported on both Pages and Workers static assets — see the deployment page).
Content layout
When i18n is enabled, content is split into locale-named directories.
Directory structure
content/pages/ja/*.md/content/pages/en/*.md— page bodiescontent/nav/ja.md/content/nav/en.md— header navigationcontent/footer/ja.md/content/footer/en.md— footer
Page content is a completely separate file per locale. There's no automatic sync mechanism (machine translation or otherwise), so keeping translations up to date is an operational task.
How routing works
For content pages (content/pages/), src/pages/[...slug].astro maps the file path (a locale-prefixed id like ja/about) directly to a URL. A file at ja/index routes to /ja/.
Pages that exist as fixed templates (such as member pages, which live as real files rather than going through Content Collections) use an actual src/pages/[locale]/... directory structure, generating static paths per locale via getStaticPaths().
Language switcher UI
src/components/LanguageSwitcher.astro generates the URL for each available locale using Astro's official getRelativeLocaleUrl() helper. The shared layout (Base.astro) places it next to the header navigation automatically.
import { getRelativeLocaleUrl } from 'astro:i18n';
const LOCALES = ['ja', 'en'] as const; // keep in sync with i18n.locales in astro.config.mjs
The LOCALES constant duplicates i18n.locales from astro.config.mjs. Both currently need to be kept in sync by hand, so update both when adding or removing a locale.
Adding a translated page
To translate an existing page into another locale, create a file at the same relative path under the corresponding locale directory. For example, to translate content/pages/ja/about.md into English, create content/pages/en/about.md and translate both the frontmatter (title/description/pageType) and the body's Blocks. Keeping file names and directory structure aligned across locales ensures the URLs LanguageSwitcher generates actually resolve to real files.
Adding a new locale
- Add it to
i18n.localesinastro.config.mjs - Add it to the
LOCALESconstant insrc/components/LanguageSwitcher.astro - Create
content/pages/<locale>/,content/nav/<locale>.md, andcontent/footer/<locale>.md
Removing i18n
Remove the i18n setting from astro.config.mjs, flatten the locale directories under content/pages/<locale>/ back into content/pages/*.md, and merge nav.md/footer.md back into single files instead of per-locale ones. Also remove the LanguageSwitcher component usage from the shared layout.