Astro 集成 (ts-intl-astro)
ts-intl-astro 是面向 Astro 的国际化集成包,基于核心库 @aaakul/ts-intl 构建。通过异步上下文管理(AsyncLocalStorage)存储当前请求的语言状态,避免组件树中逐层传递语言参数(Prop Drilling)。
核心特性
- 避免 Prop Drilling:基于 Node.js 与标准运行时的
AsyncLocalStorage和 Astro 中间件机制,在组件树中直接调用useTranslations()获取当前请求语言的翻译函数。 - 中间件自动注入:在
astro.config.mjs中注册集成后,会自动向 Astro 渲染管道注入前置中间件(Pre-middleware),解析当前页面的语言标识。支持context.currentLocale、路由参数([lang][locale])及 URL 路径分析。 - 严格类型安全:继承
ts-intl的编译期全类型推断,包含命名空间约束、键名自动补全、ICU 参数校验与跨语言词典结构对齐校验。
安装
在 Astro 项目中安装 ts-intl-astro 及其依赖:
pnpm add ts-intl-astro
# 或
npm install ts-intl-astro
# 或
yarn add ts-intl-astro
快速配置
1. 初始化 i18n 实例
在项目目录 src/i18n/index.ts 中创建并导出翻译 Hooks 与实例配置:
// src/i18n/index.ts
import { createAstroI18n } from "ts-intl-astro";
import enUS from "./messages/en-US";
import zhHans from "./messages/zh-Hans";
export const {
useTranslations,
useLocale,
useFormatter,
languages,
defaultLanguage,
} = createAstroI18n({
defaultLanguage: "zh-Hans",
messages: {
"zh-Hans": zhHans,
"en-US": enUS,
},
});
export type SupportedLanguage = (typeof languages)[number];
2. 注册 Astro 集成
在项目的 astro.config.mjs 中引入并启用 tsIntl:
// astro.config.mjs
import { defineConfig } from "astro/config";
import tsIntl from "ts-intl-astro";
export default defineConfig({
integrations: [tsIntl()],
});
该集成在 Astro 构建与运行阶段自动注册上下文中间件。当页面处理请求或生成静态页面时,中间件解析路由语言并将当前上下文注入请求作用域。
在组件中使用
通过上下文中间件,组件内部无需声明或接收 lang 属性:
基础用法
---
// src/components/Header.astro
import { useTranslations, useLocale } from "@/i18n";
const t = useTranslations("nav");
const lang = useLocale();
---
<header class="flex items-center justify-between p-4">
<nav class="space-x-4">
<a href={`/${lang}`}>{t("home")}</a>
<a href={`/${lang}/docs`}>{t("docs")}</a>
<a href={`/${lang}/pricing`}>{t("pricing")}</a>
</nav>
</header>
显式覆盖语言
如果某个独立组件需要强制渲染为特定语言(例如语言切换下拉列表的预览),可传入 { locale } 选项进行覆盖:
---
import { useTranslations } from "@/i18n";
const tEn = useTranslations("hero", { locale: "en-US" });
---
<p>{tEn("title")}</p>
使用格式化器
调用 useFormatter() 获取当前语言环境下的 Web Intl 格式化器:
---
import { useFormatter } from "@/i18n";
const fmt = useFormatter();
const formattedPrice = fmt.number(1299, { style: "currency", currency: "CNY" });
const formattedDate = fmt.dateTime(new Date(), { dateStyle: "medium" });
---
<div>
<span>{formattedPrice}</span>
<time>{formattedDate}</time>
</div>
路由段配置与使用(Route Segments)
推荐使用动态路由段来组织多语言页面结构,例如 src/pages/[lang]/index.astro 或 src/pages/[locale]/about.astro。
静态路由生成(SSG)
在静态站点生成模式下,结合导出的 languages 数组定义 getStaticPaths():
---
// src/pages/[lang]/index.astro
import { languages, useTranslations, useLocale } from "@/i18n";
export function getStaticPaths() {
return languages.map((lang) => ({
params: { lang },
}));
}
const t = useTranslations("nav");
const lang = useLocale();
---
<nav>
<a href={`/${lang}`}>{t("home")}</a>
<a href={`/${lang}/docs`}>{t("docs")}</a>
</nav>
路由段解析机制
- 默认路由段参数名:中间件默认自动检测动态路由参数
context.params.lang与context.params.locale。只要目录名为[lang]或[locale],即可零配置自动识别。 - 免传参上下文注入:中间件在进入页面和子组件渲染前,便已从路由段提取出语言并存入异步作用域。页面内所有深层子组件直接调用
useTranslations()或useLocale()即可获得对应语言,彻底消除从页面到子组件的逐层属性透传(Prop Drilling)。 - 自定义路由段参数名:若路由目录使用了其他参数名(例如
src/pages/[localeCode]/...),可通过createAstroI18n的paramNames选项进行配置:
export const {
/* ... */
} = createAstroI18n({
defaultLanguage: "zh-Hans",
paramNames: ["localeCode", "lang", "locale"],
messages: {
/* ... */
},
});
语言解析策略
ts-intl-astro 会按以下优先级依次解析当前请求的语言:
- Astro 内置
context.currentLocale(Astro i18n 路由) - 路由动态参数(默认检测
context.params.lang和context.params.locale,可通过paramNames自定义) - URL 路径前缀分析(例如
/zh-Hans/...提取首段路径) - 默认语言回退(配置中指定的
defaultLanguage)
API 参考
| API | 类型 | 说明 |
|---|---|---|
createAstroI18n(config) |
Function |
初始化 Astro 专用 i18n 实例,支持配置 defaultLanguage、messages 与 paramNames |
useTranslations(ns?, opts?) |
Function |
获取绑定至当前渲染上下文的翻译函数 t |
useLocale() |
() => string |
获取当前渲染上下文的活动语言代码 |
useFormatter(opts?) |
() => Formatter |
获取当前语言绑定的缓存 Web Intl 格式化器 |