如何为next-intl的<T>组件实现作用域关联的类型化翻译键?
解决next-intl异步组件翻译的类型安全问题
方案一:泛型关联scope与翻译键类型
先定义翻译文件的类型,再通过泛型让组件的k参数仅提示对应scope下的有效键:
// 导入你的翻译文件类型,假设英文翻译位于messages/en.json type Messages = typeof import('./messages/en.json'); // 定义组件Props的泛型类型 type TProps<S extends keyof Messages, K extends keyof Messages[S]> = { scope: S; k: K; values?: Record<string, string | number>; // 可选的插值参数 }; export default function T<S extends keyof Messages, K extends keyof Messages[S]>({ scope, k, values, }: TProps<S, K>) { const t = useTranslations(scope); return <>{t(k, values)}</>; }
使用示例:
// 此时k会自动提示"user" scope下的所有翻译键,比如"name"、"email"等 <T scope="user" k="name" />
如果需要支持无scope的全局翻译,可调整泛型适配:
type TProps< S extends keyof Messages | undefined = undefined, K extends S extends undefined ? keyof Messages : keyof Messages[S] = S extends undefined ? keyof Messages : keyof Messages[S] > = { scope?: S; k: K; values?: Record<string, string | number>; }; export default function T< S extends keyof Messages | undefined = undefined, K extends S extends undefined ? keyof Messages : keyof Messages[S] = S extends undefined ? keyof Messages : keyof Messages[S] >({ scope, k, values, }: TProps<S, K>) { const t = scope ? useTranslations(scope) : useTranslations(); return <>{t(k as any, values)}</>; }
方案二:使用完整翻译键路径
如果不想拆分scope和k,可以直接传入完整键路径(如"user.name"),通过模板字符串类型约束输入:
type Messages = typeof import('./messages/en.json'); // 生成所有合法的完整翻译键:全局键 + 带scope的嵌套键 type FullTranslationKey = | keyof Messages | { [S in keyof Messages]: `${S & string}.${keyof Messages[S] & string}` }[keyof Messages]; type TProps = { key: FullTranslationKey; values?: Record<string, string | number>; }; export default function T({ key, values }: TProps) { const t = useTranslations(); return <>{t(key, values)}</>; }
使用示例:
// key会提示所有全局键和嵌套键,比如"title"、"user.name"等 <T key="user.name" />
方案说明
- 方案一贴合
useTranslations的原生使用逻辑,类型提示精准,适合频繁切换不同scope的场景。 - 方案二更简洁,无需拆分scope和键,适合翻译结构相对简单的项目。
两种方案都能解决「任意键值传入」的类型不安全问题,同时适配异步组件无法直接调用useTranslations的场景。
内容的提问来源于stack exchange,提问作者Ahmed Abdelbaset
相关产品推荐
相关产品推荐

