
以下文章来源于CodeSpirit-码灵,作者magiccodes
CodeSpirit 框架现已支持完整的前后端多语言国际化功能,提供中英文双语支持,基于 .NET 资源文件和 AMIS locale,通过 Settings 组件实现全局、租户、用户三级语言配置。
版本: v1.0.0 支持语言: 简体中文(zh-CN)、英文(en) 更新日期: 2025年12月28日

Cookie(用户手动切换)
↓ (未设置)
User Settings(用户偏好)
↓ (未设置)
Tenant Settings(租户默认)
↓ (未设置)
Global Settings(系统默认)
↓ (未设置)
zh-CN(最终回退)// 全局默认语言
Module: "Localization"
Key: "DefaultLanguage"
Value: "zh-CN"
Scope: Global
// 租户默认语言
Module: "Localization"
Key: "DefaultLanguage"
Value: "en"
Scope: Tenant
ScopeId: "{tenantId}"
// 用户偏好语言
Module: "Localization"
Key: "PreferredLanguage"
Value: "en"
Scope: User
ScopeId: "{userId}"本地化服务已在 ServiceDefaults 中自动注册,无需额外配置。
using CodeSpirit.Localization.Resources;
using Microsoft.Extensions.Localization;
publicclassMyController : ApiControllerBase
{
privatereadonly IStringLocalizer<SharedResources> _localizer;
public MyController(IStringLocalizer<SharedResources> localizer)
{
_localizer = localizer;
}
[HttpPost]
public IActionResult Create()
{
return Ok(new ApiResponse
{
Status = 1,
Msg = _localizer["Common.Save"].Value
});
}
}// 使用资源键
throw new BusinessException("Errors.InvalidStartTime");
// 带参数
throw new ValidationException("Errors.NotFound", resourceId);using CodeSpirit.Localization.Resources;
public class CreateQuestionDto
{
[Display(Name = "Content", ResourceType = typeof(DisplayResources))]
[Required(ErrorMessageResourceType = typeof(ValidationResources),
ErrorMessageResourceName = "Required")]
[StringLength(2000,
ErrorMessageResourceType = typeof(ValidationResources),
ErrorMessageResourceName = "StringLengthMax")]
public string Content { get; set; } = string.Empty;
}验证错误示例:
中文环境:题目内容不能为空
英文环境:Content is required

DTO 字段的描述信息(Description)也支持多语言,通过 LocalizedDescriptionAttribute 实现。
各服务应创建自己的资源文件,保持服务独立性:
资源文件结构:
CodeSpirit.ExamApi/Resources/
├── ExamDisplayResources.cs # 资源占位类(包含ResourceManager)
├── ExamDisplay.resx # 中文资源
└── ExamDisplay.en.resx # 英文资源资源键命名规范:
Description.{EntityName}.{PropertyName}Description.Question.Options、Description.Question.CorrectAnswerusing CodeSpirit.Core.Attributes;
using CodeSpirit.ExamApi.Resources;
public class CreateQuestionDto
{
[LocalizedDescription(
"根据题目内容生成合适的选项", // 回退文本(可选)
ResourceKey = "Description.Question.Options",
ResourceType = typeof(ExamDisplayResources)
)]
public List<string> Options { get; set; }
}使用方式:
现有的 DescriptionAttribute 仍然可以正常使用,系统会优先检查 LocalizedDescriptionAttribute,如果不存在则回退到 DescriptionAttribute。
CodeSpirit.Localization/Resources/ - 存放真正通用的、跨服务的资源ApiServices/{ServiceName}/Resources/ - 存放服务特有的业务资源最佳实践:
Description.{EntityName}.{PropertyName} 命名约定描述多语言的资源解析由 AMIS 表单生成时统一处理:
GetLocalizedDescription 方法在表单生成时解析资源// 获取翻译文本
const message = CodeSpirit.i18n.t('Common.Save');
// 带参数
const message = CodeSpirit.i18n.t('Validation.Required', { 0: '用户名' });
// 切换语言
CodeSpirit.i18n.switchLanguage('en');@using CodeSpirit.Localization.Resources
@using Microsoft.Extensions.Localization
@inject IStringLocalizer<SharedResources> Localizer
<h1>@Localizer["Common.Save"]</h1>系统已自动集成 Settings 组件,可以通过 Settings API 管理语言配置:
await _settingsService.SetUserSettingAsync(
module: "Localization",
key: "PreferredLanguage",
value: "en",
userId: currentUserId
);await _settingsService.SetTenantSettingAsync(
module: "Localization",
key: "DefaultLanguage",
value: "en",
tenantId: currentTenantId
);await _settingsService.SetGlobalSettingAsync(
module: "Localization",
key: "DefaultLanguage",
value: "en"
);用户可以在导航栏的语言切换器中选择语言,切换后会:
.AspNetCore.Culture)资源文件 | 用途 | 示例键 |
|---|---|---|
Shared.resx | 通用 UI 文本 | Common.Save, Common.Cancel |
Errors.resx | 错误消息 | Errors.NotFound, Errors.Unauthorized |
Validation.resx | 验证消息模板 | Required, StringLengthMax |
Display.resx | 字段显示名称 | Content, Type, Difficulty |
每个资源文件都有对应的英文版本(如 Shared.en.resx)。
各服务应创建自己的资源文件,保持服务独立性:
命名规范:
{ServiceName}DisplayResources.cs{ServiceName}Display.resx、{ServiceName}Display.en.resx示例:
CodeSpirit.ExamApi/Resources/
├── ExamDisplayResources.cs # 资源占位类(包含ResourceManager)
├── ExamDisplay.resx # 中文资源
└── ExamDisplay.en.resx # 英文资源
CodeSpirit.SurveyApi/Resources/
├── SurveyDisplayResources.cs
├── SurveyDisplay.resx
└── SurveyDisplay.en.resx资源键命名约定:
Description.{EntityName}.{PropertyName}Description.Question.Options、Description.Survey.TitleCodeSpirit.i18n.switchLanguage('en')
// 中文环境
throw new BusinessException("Errors.NotFound");
// API 返回: { "status": 0, "msg": "未找到资源" }
// 英文环境
throw new BusinessException("Errors.NotFound");
// API 返回: { "status": 0, "msg": "Resource not found" }AMIS 组件会自动根据当前语言加载对应的 locale 文件:
sdk/6.13.0/locale/en-US.jsAMIS 内置组件(日期选择器、分页器等)会自动显示对应语言。

导航组件(CodeSpirit.Navigation)提供了完整的多语言支持,用于实现动态导航菜单的多语言切换。

导航组件使用专用的资源文件:
资源文件位置:
CodeSpirit.Navigation/Resources/
├── NavigationResources.cs # 资源占位类
├── NavigationResources.resx # 中文资源
└── NavigationResources.en.resx # 英文资源资源键命名规范:
Module.{ModuleName}(如 Module.Identity、Module.Survey)Controller.{ControllerName}(如 Controller.Users、Controller.Roles)导航组件支持两种特性来配置多语言:Module 特性和 NavigationAttribute 特性。
Module 特性用于定义模块级别的多语言配置,通常放在 ApiControllerBase 上:
using CodeSpirit.Core.Attributes;
using CodeSpirit.Core.Enums;
using CodeSpirit.Navigation.Resources;
// 模块级配置(在 ApiControllerBase 上)
[Module("identity",
displayName: "用户中心", // 回退文本
DisplayNameResourceKey = "Module.Identity", // 资源键
DisplayNameResourceType = typeof(NavigationResources), // 资源类型
Icon = "fa-solid fa-user-group")]
[Navigation(
Icon = "fa-solid fa-user-group",
PlatformType = PlatformType.Both,
TitleResourceKey = "Module.Identity", // 与 Module 的资源键保持一致
TitleResourceType = typeof(NavigationResources)
)]
public abstract class ApiControllerBase : CodeSpirit.Shared.Controllers.ApiControllerBase
{
}NavigationAttribute 用于控制器级别的多语言配置:
using CodeSpirit.Core.Attributes;
using CodeSpirit.Navigation.Resources;
using System.ComponentModel;
// 控制器级配置
[DisplayName("用户管理")]
[Navigation(
Icon = "fa-solid fa-users",
PlatformType = PlatformType.Tenant,
TitleResourceKey = "Controller.Users",
TitleResourceType = typeof(NavigationResources)
)]
public class UsersController : ApiControllerBase
{
}Module 特性:
typeof(NavigationResources)(必填)NavigationAttribute 特性:
typeof(NavigationResources)(推荐填写)最佳实践:在模块级配置中,建议在
Navigation特性中也设置TitleResourceKey和TitleResourceType,与Module特性的资源键保持一致,确保导航多语言功能完整可靠。
NavigationAttributeCultureInfo.CurrentUICulture)解析对应的资源文本导航组件使用分布式缓存来提升性能,但在以下情况下可能导致多语言不生效:
症状:切换语言后,导航菜单仍显示旧语言
原因:导航树已缓存,未重新解析多语言资源
解决方案:清空导航缓存
/cacheManagement)CodeSpirit:Navigation:All注意:缓存管理功能仅系统管理员可访问,属于系统平台功能。
// 清空所有导航缓存
await _navigationService.ClearAllNavigationCacheAsync();
// 清空特定模块缓存(实际上也会清空整个缓存)
await _navigationService.ClearModuleNavigationCacheAsync("Identity");
// 重新初始化导航树(清空并重建缓存)
await _navigationService.InitializeNavigationTree();# 清空所有导航缓存
DELETE /api/navigation/cache
# 清空特定模块缓存
DELETE /api/navigation/cache?moduleName=Identity
# 重新初始化导航树(清空并重建缓存)
POST /api/navigation/initialize确保资源文件正确配置为嵌入式资源:
<ItemGroup>
<EmbeddedResource Include="Resources\NavigationResources.resx">
<Generator>PublicResXFileCodeGenerator</Generator>
</EmbeddedResource>
<EmbeddedResource Include="Resources\NavigationResources.en.resx" />
</ItemGroup>Module 和 Navigation 特性配置模块级多语言displayName、Title),确保资源不可用时仍能显示Module 的 DisplayNameResourceKey 和 Navigation 的 TitleResourceKey 通常使用相同的资源键以下是用户中心模块的完整多语言配置示例(来自实际代码):
using CodeSpirit.Core;
using CodeSpirit.Core.Attributes;
using CodeSpirit.Core.Enums;
using CodeSpirit.Navigation.Resources;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using System.ComponentModel;
namespaceCodeSpirit.IdentityApi.Controllers
{
/// <summary>
/// 身份认证API控制器基类
/// </summary>
[ApiController]
[Authorize(policy: "DynamicPermissions")]
[Route("api/identity/[controller]")]
// 模块级配置(使用 Module 和 Navigation 特性)
[Module("identity",
displayName: "用户中心", // 回退文本
DisplayNameResourceKey = "Module.Identity", // 资源键
DisplayNameResourceType = typeof(NavigationResources), // 资源类型
Icon = "fa-solid fa-user-group")]
[Navigation(
Icon = "fa-solid fa-user-group",
PlatformType = PlatformType.Both,
TitleResourceKey = "Module.Identity",
TitleResourceType = typeof(NavigationResources)
)]
publicabstractclassApiControllerBase : CodeSpirit.Shared.Controllers.ApiControllerBase
{
}
/// <summary>
/// 用户管理控制器
/// </summary>
[DisplayName("用户管理")]
[Navigation(
Icon = "fa-solid fa-users",
PlatformType = PlatformType.Tenant,
TitleResourceKey = "Controller.Users",
TitleResourceType = typeof(NavigationResources)
)]
publicclassUsersController : ApiControllerBase
{
privatereadonly IUserService _userService;
public UsersController(IUserService userService)
{
_userService = userService;
}
/// <summary>
/// 获取用户列表
/// </summary>
[HttpGet]
[DisplayName("获取用户列表")]
publicasync Task<ActionResult<ApiResponse<PageList<UserDto>>>> GetUsers([FromQuery] UserQueryDto queryDto)
{
PageList<UserDto> users = await _userService.GetUsersAsync(queryDto);
return SuccessResponse(users);
}
}
}资源文件内容:
<!-- NavigationResources.resx (中文) -->
<data name="Module.Identity">
<value>用户中心</value>
</data>
<data name="Controller.Users">
<value>用户管理</value>
</data>
<!-- NavigationResources.en.resx (英文) -->
<data name="Module.Identity">
<value>User Center</value>
</data>
<data name="Controller.Users">
<value>User Management</value>
</data>导航组件使用以下缓存键:
CodeSpirit:Navigation:Allappsettings.json 中添加{
"Localization":{
"SupportedCultures":[
{"Code":"zh-CN","DisplayName":"简体中文"},
{"Code":"en","DisplayName":"English"},
{"Code":"ja","DisplayName":"日本語"}
]
}
}Shared.ja.resxErrors.ja.resxValidation.ja.resxDisplay.ja.resxja-JP.js 放到 wwwroot/sdk/6.13.0/locale/MainNav.razor 中添加日语选项{
"Localization":{
"DefaultCulture":"zh-CN",
"SupportedCultures":[
{"Code":"zh-CN","DisplayName":"简体中文"},
{"Code":"en","DisplayName":"English"}
],
"EnableTenantLevelLanguage":true,
"EnableUserLevelLanguage":true,
"FallbackToParentCultures":true,
"SettingsModule":"Localization",
"SettingsKeys":{
"GlobalDefault":"DefaultLanguage",
"TenantDefault":"DefaultLanguage",
"UserPreference":"PreferredLanguage"
}
}
}位置:Src/CodeSpirit.Web/Components/Shared/MainNav.razor
用户点击下拉框选择语言,系统会:
Errors.NotFound, Common.SaveStringLengthMax, ValidationErrorErrors.NotFound ❌ Errors.ErrorsNotFound// 资源文件
<data name="UserCreated"><value>用户 {0} 创建成功</value></data>
// 使用
_localizer["UserCreated", username]现有硬编码中文的代码继续正常工作:
// 旧代码(继续工作)
throw new BusinessException("未找到资源");
// 新代码(支持多语言)
throw new BusinessException("Errors.NotFound");如果资源键不存在,系统会返回键名本身,不会抛出异常。
确保项目文件中配置了资源文件生成器:
<EmbeddedResource Update="Resources\Shared.resx">
<Generator>PublicResXFileCodeGenerator</Generator>
</EmbeddedResource>A: 通过 Settings API 设置用户级配置,系统会自动持久化到数据库。
A: 系统会根据当前语言自动加载对应的 AMIS locale 文件(如 en-US.js),AMIS 内置组件会自动显示对应语言。
A: 可以。通过 Settings API 为每个租户设置 Localization.DefaultLanguage,该租户下的所有用户默认使用该语言(用户仍可自定义)。
A:
appsettings.json 添加语言配置Shared.ja.resx)无需修改任何代码逻辑。
A: 使用 LocalizedDescriptionAttribute,指定 ResourceKey 和 ResourceType:
[LocalizedDescription(
ResourceKey = "Description.Question.Options",
ResourceType = typeof(ExamDisplayResources)
)]
public List<string> Options { get; set; }A: 检查以下几点:
.csproj 配置)A: 可以。LocalizedDescriptionAttribute 会根据 CultureInfo.CurrentUICulture 自动获取对应语言的资源。语言切换由 CodeSpirit.Localization 组件的中间件处理。
A: 导航组件支持两种配置方式:
NavigationAttribute 的 TitleResourceKey 和 TitleResourceType[Navigation(
TitleResourceKey = "Controller.Users",
TitleResourceType = typeof(NavigationResources)
)]DisplayAttribute 的 Name 和 ResourceType[Display(
Name = "Controller.Users",
ResourceType = typeof(NavigationResources)
)]如果同时配置了两者,NavigationAttribute 的配置优先级更高。
本文分享自 CodeSpirit-码灵 微信公众号,前往查看
如有侵权,请联系 cloudcommunity@tencent.com 删除。
本文参与 腾讯云自媒体同步曝光计划 ,欢迎热爱写作的你一起参与!