# General Translation Knowledge Base: General Translation 与 nestjs-i18n：该选哪一个？
URL: https://generaltranslation.com/zh/kb/general-translation-vs-nestjs-i18n.mdx
Docs index: https://generaltranslation.com/llms.txt
Description: nestjs-i18n 在 NestJS 应用中为每个请求解析语言，并从按语言划分的 JSON 或 YAML 消息目录中渲染消息，但它本身并不翻译任何内容。General Translation 的 CLI 可以翻译这些消息目录，其 gt-node 库则能翻译仅在运行时才存在的内容。因此，你可以继续使用 nestjs-i18n，同时引入 General Translation。
Category: 国际化
Last updated: 2026-10-16
Authors: Kevin Wu, Jackie Chen
Published: 2026-10-16

nestjs-i18n 是一个面向 NestJS 的开源 i18n 模块：它根据查询字符串、请求头、cookie 或 `Accept-Language` 确定每个请求的语言，再由 `I18nContext` 或 `I18nService` 从你的消息目录中返回该语言对应的消息。它只负责渲染译文，并不生成译文，因此每种语言的消息目录都得由你、你的译者或某个服务来编写和维护。General Translation 从两个方面弥补了这一不足：其 CLI 可以翻译 nestjs-i18n 的消息目录，其 `gt-node` 库则能翻译仅在运行时才存在的内容。

## nestjs-i18n 的优势

* **专为 Nest 打造。** 只需调用一次 `I18nModule.forRoot()`，[即可通过 `@I18n()` 参数装饰器获取 `I18nContext`，并在服务中使用 `I18nContext.current()`](https://nestjs-i18n.com/quick-start)。它支持 HTTP、GraphQL、gRPC 和 WebSockets。
* **语言解析器。** [`QueryResolver`、`HeaderResolver`、`CookieResolver` 和 `AcceptLanguageResolver`](https://nestjs-i18n.com/concepts/resolver) 会按你列出的顺序依次运行；GraphQL 和 gRPC 解析器负责对应的传输协议，你也可以编写自定义解析器。
* **简洁的消息目录。** 每种语言对应一个文件夹 (`src/i18n/en/`、`src/i18n/es/`) ，其中存放 [JSON 或 YAML 文件](https://nestjs-i18n.com/concepts/loader)，文件名即命名空间 (`inbox.title`) ，开发环境下还支持实时重载。
* **复数形式与插值。** 支持 `{name}` 占位符、通过 `$t()` 引用其他键，以及[包含 `zero`、`one`、`two`、`few`、`many` 和 `other` 键的复数对象](https://nestjs-i18n.com/guides/plurals)，具体使用哪个键由 `Intl.PluralRules` 决定。[10.8.0 版本新增](https://github.com/toonvanstrijp/nestjs-i18n/releases/tag/v10.8.0)了需主动启用的 `useICU` 设置，启用后改用 ICU MessageFormat 格式化消息。
* **类型安全。** [`typesOutputPath` 会生成 `I18nTranslations` 和 `I18nPath` 类型](https://nestjs-i18n.com/guides/type-safety)，键名拼写错误会直接导致编译失败；某种语言缺少键时，`nestjs-i18n check` 命令会报错退出。
* **验证消息。** `I18nValidationPipe`、`I18nValidationExceptionFilter` 和 `i18nValidationMessage()` 可以[翻译 DTO 的 class-validator 错误信息](https://nestjs-i18n.com/guides/dto_validation/global-validation)。

它采用 MIT 许可证，[10.8.5 版本要求 Node.js 22 或更高版本](https://github.com/toonvanstrijp/nestjs-i18n/blob/v10.8.5/package.json)。

## nestjs-i18n 留给你自己处理的部分

下面是一个英文的收件箱目录，其中包含一条复数消息：

```json title="src/i18n/en/inbox.json"
{
  "title": "Inbox",
  "unread": {
    "one": "You have {count} unread message.",
    "other": "You have {count} unread messages."
  }
}
```

西班牙语文件夹中存放着该文件的翻译副本，某个控制器返回 `i18n.t('inbox.unread', { args: { count } })`。没有德语文件夹。向一个使用 nestjs-i18n 10.8.5 的最小 Nest 11 应用发送请求，结果如下：

```text title="Output"
?lang=es&count=3  "Tienes 3 mensajes sin leer."
?lang=de&count=3  "You have 3 unread messages."
```

德语之所以回退到英语，是因为其目录尚未翻译。语言检测虽然能正常工作，却无法补上缺失的文本。这正是 nestjs-i18n 留给你的工作：生成消息目录，并在每次英语原文变更时及时同步更新。

## General Translation 如何融入 NestJS 应用

General Translation 的 CLI 可直接在原位置翻译 nestjs-i18n 的消息目录。只需将一个 `json` 条目指向各语言文件夹即可：

```json title="gt.config.json"
{
  "defaultLocale": "en",
  "locales": [
    "es",
    "de"
  ],
  "files": {
    "json": {
      "include": [
        "src/i18n/[locale]/*.json"
      ]
    }
  }
}
```

[`npx gt translate`](/docs/cli/reference/commands/translate) 会读取 `src/i18n/en/inbox.json`，并写入 `src/i18n/es/inbox.json` 和 `src/i18n/de/inbox.json`，且保留所有键。在 CI 中运行时，未改动的条目会直接复用之前的译文。nestjs-i18n 则通过其现有的加载器加载生成的消息目录。

本示例使用英语、西班牙语和德语消息，均包含 `one` 和 `other` 两种形式。请分别以计数 1 和 3 预览生成的消息，并在发布前运行 `nestjs-i18n check -p src/i18n`，检查是否有缺失的键。

对于仅在运行时才存在的内容 (例如队友发布的帖子或数据库记录) ，请使用 General Translation 的 [`gt-node`](/docs/node/quickstart)。只需初始化一次，之后在调用 [`tx`](/docs/node/reference/functions/tx) 时，将 nestjs-i18n 已解析的语言传给 [`withGT`](/docs/node/reference/functions/with-gt) 即可：

```ts title="src/main.ts"
import { NestFactory } from '@nestjs/core';
import { initializeGT } from 'gt-node';
import { AppModule } from './app.module';

// 从环境变量读取 GT_PROJECT_ID 和 GT_API_KEY（需为已启用运行时翻译的项目密钥）。
initializeGT({ defaultLocale: 'en', locales: ['en', 'es', 'de'] });

async function bootstrap() {
  const app = await NestFactory.create(AppModule);
  await app.listen(3000);
}
bootstrap();
```

```ts title="src/posts.controller.ts"
import { Controller, Get, Param } from '@nestjs/common';
import { I18n, I18nContext } from 'nestjs-i18n';
import { tx, withGT } from 'gt-node';
import { PostsService } from './posts.service';

@Controller('posts')
export class PostsController {
  constructor(private readonly posts: PostsService) {}

  @Get(':id')
  async findOne(@Param('id') id: string, @I18n() i18n: I18nContext) {
    return withGT(i18n.lang, async () => {
      const post = await this.posts.findOne(id);
      return { ...post, body: await tx(post.body, { $context: 'A post written by a user' }) };
    });
  }
}
```

现在，这两个工具都会使用你现有 nestjs-i18n 解析器所选定的语言。如果 `?lang=es` 的优先级高于 `Accept-Language: de`，那么目录消息和翻译后的帖子都会是西班牙语。固定的界面文案请使用消息目录，用户创建的内容则使用 [`tx`](/docs/node/reference/functions/tx)。

类型化键、目录查找和验证消息仍由 nestjs-i18n 负责。General Translation 负责生成译文，并提供共享的词汇表、自定义提示词以及[仪表板审阅](/docs/platform/dashboard/guides/reviewing-translations)功能。固定文案沿用上述目录工作流，动态内容则使用 `tx`。

## nestjs-i18n 与 General Translation 概览对比

| 功能     | nestjs-i18n                                             | General Translation                                                                                           |
| ------ | ------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------- |
| 定位     | 解析请求语言，并基于消息目录渲染消息                                      | 翻译消息目录，并在运行时翻译动态内容                                                                                            |
| 翻译你的内容 | 否                                                       | 是，通过 [`npx gt translate`](/docs/cli/reference/commands/translate) 和 [`tx`](/docs/node/reference/functions/tx) |
| 语言检测   | 查询参数、请求头、cookie、`Accept-Language`、GraphQL、gRPC 以及自定义解析器 | 通过 [`withGT`](/docs/node/reference/functions/with-gt) 沿用同一个已解析的语言                                             |
| 复数形式   | 按语言划分的类别键，由 `Intl.PluralRules` 选择                       | 翻译源文件中已有的复数键                                                                                                  |
| 类型安全   | 生成键类型，并检查缺失的键                                           | 保留目录键，可继续使用相同的生成类型                                                                                            |
| 验证消息   | 集成 class-validator                                      | 翻译这些消息所在的消息目录                                                                                                 |
| 费用     | 免费，采用 MIT 许可证                                           | [按用量计费](https://generaltranslation.com/pricing)                                                               |

## 如何选择

nestjs-i18n 的解析器、类型化键和验证集成可以继续保留。如果你希望英文内容变更时在 CI 中自动翻译消息目录，或者 API 返回的用户内容、数据库内容需要实时翻译，就可以引入 General Translation。现有控制器仍沿用原来的键，而 General Translation 负责让这些键对应的文本始终保持最新。

## 同时使用两者

1. 保持 nestjs-i18n 及其文件夹不变。
2. 添加 `gt.config.json` 并在其中配置目录路径，然后设置 `GT_API_KEY` 和 `GT_PROJECT_ID`。 (参见[配置 CLI](/docs/cli/guides/configuring#credentials)。) 
3. 使用 [`npx gt upload`](/docs/cli/reference/commands/upload) 上传现有译文，然后在 CI 中运行 [`npx gt translate`](/docs/cli/reference/commands/translate)。
4. 之后如果在本地修改了译文，请先运行 [`npx gt save-local`](/docs/cli/reference/commands/save-local)，再重新生成。在需要运行时翻译的地方添加 `gt-node`。

## 要点总结

* nestjs-i18n 负责处理 NestJS 中的语言检测、类型化键、复数形式和验证消息，但所有语言的所有消息目录都得由你自己编写，包括各语言的复数形式。
* General Translation 的 CLI 可以原地翻译 nestjs-i18n 的 JSON 消息目录，`gt-node` 则基于同一请求语言翻译动态内容。
* 两者搭配使用：nestjs-i18n 负责渲染，General Translation 负责翻译。

## Related questions

- [General Translation 与 i18next、react-intl 和 next-intl 对比：应该选择哪个国际化库？](https://generaltranslation.com/zh/kb/general-translation-vs-i18n-libraries.mdx): i18next、react-intl 和 next-intl 等开源库本身并不翻译任何内容。它们只负责显示你在各语言目录文件中提供的译文，这些译文由你、你的译者或其他服务编写。General Translation 的库同样开源，承担同样的工作，此外 General Translation 还会以你的代码为上下文完成翻译。
- [为什么选择 General Translation 而不是 TMS（Smartling、Lokalise、Phrase、Crowdin 或 Transifex）？](https://generaltranslation.com/zh/kb/general-translation-vs-tms.mdx): Smartling、Lokalise、Phrase、Crowdin 或 Transifex 等翻译管理系统（TMS）负责协调译员，但工程师仍需提取字符串、发送出去、等待译文，再把译文接回产品中。General Translation 结合上下文直接翻译代码中的内容，每次发布都同步交付所有语言，并基于开源库运行，不存在供应商锁定。
- [应该自建 i18n，还是使用 General Translation？](https://generaltranslation.com/zh/kb/general-translation-vs-in-house-i18n.mdx): 手写的 i18n（每种语言一个字典，外加一个简单的 t() 辅助函数）足以应对一两种语言、少量文案的场景。随着应用规模扩大，你会发现自己在重新实现复数规则、后备内容和文件同步，而且译文仍然得自己产出。General Translation 的开源库负责渲染，General Translation 则根据你的代码生成并更新译文。
- [General Translation 与 Symfony Translation：该选哪一个？](https://generaltranslation.com/zh/kb/general-translation-vs-symfony-translation.mdx): Symfony Translation 负责在 PHP 和 Twig 中渲染消息。General Translation 则在 CI 中翻译其 YAML 消息目录，随源内容的变化持续同步更新，并为审校人员提供可直接编辑译文的仪表板。保留 Symfony 的渲染机制，同时为你的发布工作流加入自动化翻译。
- [不用翻译文件也能做 i18n 吗？](https://generaltranslation.com/zh/kb/i18n-without-translation-files.mdx): 可以。使用 gt-react 或 gt-next 时，内容以内联方式直接写在组件中的 `<T>` 组件内，译文在构建时作为输出生成，因此无需编写键，也无需为每个区域设置编写 JSON 文件并保持同步。

## Previous and next questions

- Previous question: [General Translation 与 Mobility：该选哪一个？](https://generaltranslation.com/zh/kb/general-translation-vs-mobility.mdx)
- Next question: [General Translation 与 Paraglide JS：该选哪一个？](https://generaltranslation.com/zh/kb/general-translation-vs-paraglide.mdx)

## Sitemap

See the [Knowledge Base sitemap](https://generaltranslation.com/zh/kb/sitemap.md) for every article.
