# node: 字符串翻译模式 URL: https://generaltranslation.com/zh/docs/node/guides/strings.mdx --- title: 字符串翻译模式 description: 在 Node.js 中翻译字符串的两种方式——内联和预注册 --- 在 `gt-node` 中,翻译字符串有三种方式: 1. **使用 `getGT()` 进行内联翻译** —— 直接在请求处理器中翻译字符串 (构建时) 2. **使用 `msg()` / `getMessages()` 预注册** —— 在模块级作用域中定义字符串,并在 runtime 时解析 (构建时) 3. **使用 `tx()` 进行 on-demand 翻译** —— 在 runtime 时翻译字符串,包括构建时未知的内容 ## 使用 `getGT()` 进行内联翻译 当某个字符串只在一个地方使用,或者内容依赖于请求特定的数据时,请使用 [`getGT()`](/docs/node/api/get-gt): ```js title="routes/greeting.js" import { getGT } from 'gt-node'; app.get('/api/greeting', async (req, res) => { const gt = await getGT(); res.json({ message: gt('Hello, world!'), }); }); ``` ### 变量插值 使用 `{name}` 占位符,将变量作为第二个参数传入: ```js const gt = await getGT(); gt('Welcome, {name}!', { name: user.displayName }); gt('You have {count} new messages.', { count: unreadCount }); gt('{city} weather: {temp}°F', { city: 'Tokyo', temp: 72 }); ``` ## 通过 `msg()` / `getMessages()` 预注册字符串 使用 [`msg()`](/docs/node/api/get-messages) 在模块级作用域中注册字符串,也就是在任何请求处理函数之外。然后在处理函数内使用 [`getMessages()`](/docs/node/api/get-messages) 将它们解析为当前区域设置下的文本: ```js title="messages.js" import { msg } from 'gt-node'; export const GREETING = msg('Hello, world!'); export const WELCOME = msg('Welcome, {name}!'); export const NOT_FOUND = msg('Resource not found.'); export const UNAUTHORIZED = msg('You must be logged in.'); ``` ```js title="routes/api.js" import { getMessages } from 'gt-node'; import { GREETING, WELCOME, NOT_FOUND } from './messages.js'; app.get('/api/greeting', async (req, res) => { const m = await getMessages(); res.json({ greeting: m(GREETING), welcome: m(WELCOME, { name: 'Alice' }), }); }); app.use(async (req, res) => { const m = await getMessages(); res.status(404).json({ error: m(NOT_FOUND) }); }); ``` 这种方式最适合以下情况: * 同一个字符串会在多个处理函数中使用 * 字符串作为共享常量使用 (如错误消息、标签、枚举) * 你希望将所有可翻译的字符串集中放在一个文件中,便于审阅 ## 使用 `tx()` 进行按需翻译 如果内容在构建时还无法确定——例如用户生成的内容或无法预先翻译的动态数据——请使用 [`tx()`](/docs/node/api/strings/tx): ```js title="routes/translate.js" import { tx } from 'gt-node'; app.post('/api/translate', async (req, res) => { const translated = await tx(req.body.text); res.json({ translated }); }); ``` `tx` 是异步的,会通过网络请求按需执行翻译,因此比构建时方案更慢。只有在 `getGT()` 或 `msg()` 无法满足该使用场景时,才应使用它。 ## 何时使用哪种方式 | Pattern | 最适合 | | ------------------------- | ----------------------------- | | `getGT()` | 一次性字符串、请求相关内容、仅出现在单个处理函数中的字符串 | | `msg()` / `getMessages()` | 共享常量、错误消息、枚举标签、集中管理字符串 | | `tx()` | 构建时无法预知的动态内容或用户生成内容 | 这两种方式生成的译文完全一致。区别只在于代码组织方式——选择更符合你的项目结构的即可。 ## 使用 `$context` 消歧 有歧义的字符串可能会导致译文不准确。添加 `$context` 以帮助翻译器理解上下文: ```js // 内联 const gt = await getGT(); gt('Apple', { $context: 'the technology company' }); gt('Spring', { $context: 'the season, not a coil' }); // 预注册 const APPLE = msg('Apple', { $context: 'the fruit' }); const SPRING = msg('Spring', { $context: 'a metal coil' }); ``` ## 完整示例 ```js title="server.js" import express from 'express'; import { initializeGT, withGT, getGT, msg, getMessages } from 'gt-node'; initializeGT({ defaultLocale: 'en', locales: ['en', 'es', 'fr', 'ja'], projectId: process.env.GT_PROJECT_ID, }); // 预注册共享字符串 const ERRORS = { notFound: msg('Resource not found.'), unauthorized: msg('You must be logged in.'), forbidden: msg('You do not have permission to access this resource.'), }; const app = express(); app.use((req, res, next) => { const locale = req.headers['accept-language']?.split(',')[0] || 'en'; withGT(locale, () => next()); }); // 内联翻译 app.get('/api/dashboard', async (req, res) => { const gt = await getGT(); res.json({ title: gt('Dashboard'), subtitle: gt('Welcome back, {name}!', { name: req.user.name }), }); }); // 预注册消息 app.use(async (req, res) => { const m = await getMessages(); res.status(404).json({ error: m(ERRORS.notFound) }); }); app.listen(3000); ``` ## 后续步骤 * [`getGT` API 参考](/docs/node/api/get-gt) * [`msg` 和 `getMessages` API 参考](/docs/node/api/get-messages) * [`tx` API 参考](/docs/node/api/strings/tx) — runtime 翻译 * [区域设置检测与中间件](/docs/node/guides/middleware) — 了解区域设置上下文的工作原理