别再把字符串包进函数调用里了
t("Hello, {name}", { name }) 的问题
如果你曾为 React 应用做过国际化,肯定写过类似这样的代码:
const gt = useGT();
return <p>{gt("Hello, {name}! You have {count} items.", { name, count })}</p>;它能用,但很别扭。你得手写 ICU MessageFormat 语法,在选项对象里把每个变量名再写一遍,还得从一个依赖 React 上下文的 Hook 里取出 t。对于一个本应只是给现有字符串加上的轻量封装来说,这些样板步骤未免太多了。
而且还不止如此。只要你需要在 React 组件之外做翻译——比如在工具函数、事件处理函数或 server action 里——就只能开始找各种变通办法,因为 Hook 在这些地方根本用不了。
模板字面量应可直接使用
下面是使用 t 宏后的同一段代码:
import { t } from "gt-react/browser";
return <p>{t`Hello, ${name}! You have ${count} items.`}</p>;就是这样。就是标准的 JavaScript 模板字面量语法。没有 ICU 占位符,没有选项对象,没有 Hook,也不需要上下文 Provider。你只需像写普通模板字面量一样写字符串,其余都由编译器处理。
在构建阶段,GT 编译器会将以下内容转换为:
t`Hello, ${name}!`变为:
t("Hello, {0}!", { "0": name })tagged template 纯粹只是语法糖——在 runtime 时的行为与传入字符串调用 t() 完全一致。但开发体验会好很多.
不再依赖 React 上下文
这里更大的变化不在于语法,而在于架构。gt-react/browser 导出的 t 函数不依赖 React 上下文。不需要 Hook,也不需要在组件内部调用。
这意味着你可以在以下场景中使用 t:
- 事件处理函数:
onClick={() => alert(tSaved!)} - 工具函数:
function formatError(code) { return tError: $} - 常量和配置:
const LABELS = { save: tSave, cancel: tCancel} - 客户端上任何运行 JavaScript 的地方
旧的 pattern——useGT() 返回一个作用域受 React 上下文限制的函数——一直是个瓶颈。它迫使翻译变成 React 层面的事,而它本质上其实是字符串层面的事。
全局注册
如果你不想在每个文件中都导入 t,可以将其全局注册:
// 在你的应用入口点中
import "gt-react/macros";这会设置 globalThis.t,让 t 作为 tagged template 无需导入即可在任何地方使用。编译器足够智能,能够检测到这一点——如果 t 已经从 GT 源导入,它就不会重复注入 import。否则,如果你将 t 用作 tagged template,编译器就会自动为你注入 import。
也支持拼接
宏展开不只支持 tagged template,也能处理作为参数传入的模板字面量和字符串拼接:
// 模板字面量作为参数 — 同样会被转换
t(`Welcome back, ${user}`)
// 字符串拼接 — 同样会被转换
t("Hello, " + name + "! Welcome.")两者在构建阶段都会被统一转换为同一个 t("...", { ... }) 调用。
快速开始
1. 安装最新版的 gt-react
npm install gt-react@latest2. 使用 t 宏
可直接导入:
import { t } from "gt-react/browser";
export function Greeting({ name }) {
return <p>{t`Hello, ${name}!`}</p>;
}或者全局注册,这样就无需导入:
// app/layout.tsx 或入口点
import "gt-react/macros";// 在应用的任意位置
export function Greeting({ name }) {
return <p>{t`Hello, ${name}!`}</p>;
}3. 就这些
GT 编译器会在构建期间自动完成转换,无需额外配置——默认已启用宏展开。
t 宏只是对 API 的一个小调整,但它反映了一个更大的转变:翻译应该像 JavaScript 的原生能力,而不是某种特定框架的变通方案。自然地编写字符串,剩下的交给工具链即可。