返回

别再把字符串包进函数调用里了

Ernest McCarter avatarErnest McCarter
gt-reacti18ntagged-templatemacrodeveloper-experience

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@latest

2. 使用 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 的原生能力,而不是某种特定框架的变通方案。自然地编写字符串,剩下的交给工具链即可。