如果你的网站需要支持多种语言访问,那么就需要实现国际化功能。
这样,中文用户访问时看到的是中文界面,英文用户访问时看到的是英文界面。
不仅前端需要国际化,后端也需要国际化。否则,当英文用户使用英文界面登录时,突然遇到一个“用户不存在”的错误提示,会感到非常困惑。
本文将介绍如何在 Nest 框架中实现国际化功能。
初始化项目
首先,创建一个新的 Nest 项目:
nest new nest-i18n-test -p pnpm
安装 nestjs-i18n 包:
pnpm install nestjs-i18n
配置 I18nModule:
在 AppModule 中引入 I18nModule:
import { Module } from '@nestjs/common';
import { AppController } from './app.controller';
import { AppService } from './app.service';
import { I18nModule, QueryResolver } from 'nestjs-i18n';
import * as path from 'path';
@Module({
// 导入模块
imports: [
// 配置国际化模块
I18nModule.forRoot({
// 设置默认语言为英语
fallbackLanguage: 'en',
// 加载器选项
loaderOptions: {
// 指定国际化文件路径
path: path.join(__dirname, '/i18n/'),
// 监视文件变化
watch: true,
},
// 配置解析器,支持通过查询参数 'lang' 或 'l' 来指定语言
resolvers: [new QueryResolver(['lang', 'l'])],
}),
],
controllers: [AppController],
providers: [AppService],
})
export class AppModule {}
添加国际化资源包
在 src 目录创建 i18n 目录,然后创建语言资源文件:
i18n/en/test.json:
{
"hello": "Hello World"
}
i18n/zh/test.json:
{
"hello": "你好世界"
}
同时,在 nest-cli.json 中配置资源文件的自动复制:
"assets": [
{ "include": "i18n/**/*", "watchAssets": true }
]
修改 AppService 以使用国际化
import { Inject, Injectable } from '@nestjs/common';
import { I18nContext, I18nService } from 'nestjs-i18n';
@Injectable()
export class AppService {
@Inject()
private readonly i18n: I18nService;
getHello(): string {
return this.i18n.t('test.hello', { lang: I18nContext.current().lang });
}
}
I18nService 从资源文件中获取 test.hello 的值,使用当前语言。
运行项目
pnpm run start:dev
在浏览器中访问,可以看到界面根据语言环境自动切换:
使用其他语言解析器
除了 QueryResolver,还可以使用其他解析器,
例如根据自定义 header、cookie 或 accept-language 头来解析语言:
import { Module } from '@nestjs/common';
import { AppController } from './app.controller';
import { AppService } from './app.service';
import {
AcceptLanguageResolver,
CookieResolver,
HeaderResolver,
I18nModule,
QueryResolver,
} from 'nestjs-i18n';
import * as path from 'path';
@Module({
imports: [
// 配置国际化模块
I18nModule.forRoot({
// 设置默认语言为英语
fallbackLanguage: 'en',
// 配置语言文件加载选项
loaderOptions: {
// 设置语言文件的路径
path: path.join(__dirname, '/i18n/'),
// 启用文件监视,自动重载语言文件
watch: true,
},
// 配置语言解析器
resolvers: [
// 解析 URL 查询参数中的语言设置,支持 "lang" 和 "l" 参数
new QueryResolver(['lang', 'l']),
// 解析 HTTP 头部中的自定义语言设置,支持 "x-custom-lang" 头部
new HeaderResolver(['x-custom-lang']),
// 解析 Cookie 中的语言设置,支持 "lang" Cookie
new CookieResolver(['lang']),
// 解析请求头中的 Accept-Language 设置
AcceptLanguageResolver,
],
}),
],
// 设置控制器
controllers: [AppController],
// 设置服务提供者
providers: [AppService],
})
export class AppModule {}
可以在 Postman 中测试 cookie 解析器,通过添加 cookie 来切换语言:
加一个 lang=zh:
返回就变成中文了:
在 DTO 中使用国际化
由于 DTO 不在 IoC 容器中,无法直接注入 I18nService。可以使用 I18nValidationPipe 来实现国际化验证消息。
安装验证相关的包
pnpm install class-validator class-transformer
修改 CreateUserDto 以使用国际化验证消息
加一个模块:
nest g resource user
import { IsNotEmpty, MinLength } from "class-validator";
export class CreateUserDto {
@IsNotEmpty({
message: "validate.usernameNotEmpty"
})
username: string;
@IsNotEmpty({
message: 'validate.passwordNotEmpty'
})
@MinLength(6, {
message: 'validate.passwordNotLessThan6'
})
password: string;
}
全局启用 I18nValidationPipe
在 main.ts 中配置:
import { NestFactory } from '@nestjs/core';
import { AppModule } from './app.module';
import { I18nValidationExceptionFilter, I18nValidationPipe } from 'nestjs-i18n';
async function bootstrap() {
// 创建 Nest 应用实例
const app = await NestFactory.create(AppModule);
// 使用全局验证管道,进行国际化验证
app.useGlobalPipes(new I18nValidationPipe());
// 使用全局异常过滤器,处理国际化验证异常
app.useGlobalFilters(
new I18nValidationExceptionFilter({
detailedErrors: false, // 设置是否显示详细错误信息
}),
);
// 启动应用,监听 3000 端口
await app.listen(3000);
}
// 调用 bootstrap 函数启动应用
bootstrap();
添加验证资源包
i18n/zh/validate.json:
{
"usernameNotEmpty": "用户名不能为空",
"passwordNotEmpty": "密码不能为空",
"passwordNotLessThan6": "密码不能少于 6 位"
}
i18n/en/validate.json:
{
"usernameNotEmpty": "The username cannot be empty",
"passwordNotEmpty": "Password cannot be empty",
"passwordNotLessThan6": "The password cannot be less than 6 characters"
}
测试
通过上述配置,可以实现根据语言环境返回不同的验证消息:
修改 cookie:
报错信息为英文:
使用占位符实现动态消息
可以在文案中使用占位符:
然后传入参数:
@MinLength(6, {
message: i18nValidationMessage("validate.passwordNotLessThan6", {
len: 66
})
})
在 I18nService 的 API 中同样支持占位符:
import { Inject, Injectable } from '@nestjs/common';
import { I18nContext, I18nService } from 'nestjs-i18n';
@Injectable()
export class AppService {
@Inject()
private readonly i18n: I18nService;
getHello(): string {
return this.i18n.t('test.hello', {
lang: I18nContext.current().lang,
args: {
name: '云牧',
},
});
}
}
通过这些配置,Nest 项目可以灵活支持多种语言,满足国际化需求。