在全球化时代,越来越多的 Web 应用需要支持多种语言,以吸引不同区域的用户。对于基于 ASP.NET Core 的项目来说,通过使用 i18n(Internationalization + Localization)机制可以优雅地实现多语言支持。本文将带你从零构建 ASP.NET Core 多语言方案,包括基本配置、资源管理、语言切换、策略扩展以及一些实践建议。
基础概念:国际化 vs 本地化
在深入实现之前,我们先明确几个常见术语:
国际化 (I18N):使系统具有支持多语言/多区域的能力。
本地化 (L10N):针对某个语言/区域把界面或文本进行翻译与调整,使其适合特定文化环境。
区域性 / 文化 (Culture / CultureInfo):一种语言以及可选的国家/区域组合,比如 en-US、zh-CN。
默认语言 / 支持语言:系统设计时默认展示的语言,以及系统所支持的语言列表。
在 ASP.NET Core 中,你需要同时处理“让系统能支持多语言”(国际化)与“提供每种语言的资源与翻译”(本地化)两个层面。
在 ASP.NET Core 中启用本地化支持
1. 在服务容器中添加本地化服务
通常在 Program.cs 或 Startup.cs(取决于项目风格)中配置:
services.AddLocalization(options => {
// 指定资源文件(.resx)存放的目录(相对于项目根目录)
options.ResourcesPath = "Resources";
});
services.AddControllersWithViews()
.AddViewLocalization() // 视图局部化(Razor 视图中的本地化)
.AddDataAnnotationsLocalization(); // 支持 DataAnnotation 特性本地化
这里通过 AddLocalization 注入核心本地化服务,之后通过 AddViewLocalization 和 AddDataAnnotationsLocalization 扩展视图和模型验证的本地化能力。
2. 配置请求文化和中间件
要让系统根据请求上下文(如 URL 参数、Cookie、Accept‐Language 头)选择语言,需要启用 RequestLocalizationMiddleware:
var supportedCultures = new[] { "en-US", "zh-CN", "ja-JP" /*可根据需要扩展*/ };
var localizationOptions = new RequestLocalizationOptions()
.SetDefaultCulture("en-US")
.AddSupportedCultures(supportedCultures)
.AddSupportedUICultures(supportedCultures);
// 可插入自定义 RequestCultureProvider(例如从 QueryString 或 Cookie 读取语言设置)
app.UseRequestLocalization(localizationOptions);
请务必在 UseRouting() 等中间件之前调用 UseRequestLocalization,使得后续处理(如 MVC)能感知当前文化环境。
资源文件与本地化字符串
1. 资源文件 (.resx) 组织方式
通常推荐在项目中建立一个 Resources 文件夹,按命名空间/类名组织资源文件:
Resources/Controllers.HomeController.en-US.resx
Resources/Controllers.HomeController.zh-CN.resx
Resources/SharedResource.en-US.resx
Resources/SharedResource.zh-CN.resx
你还可以使用一个通用资源类(例如 SharedResource)来放置跨视图、共用的文案。
资源文件的命名和层级要与命名空间或使用的 IStringLocalizer
2. 在控制器 / 服务中注入本地化
在控制器、服务、API 层等处使用本地化功能:
public class HomeController : Controller
{
private readonly IStringLocalizer
public HomeController(IStringLocalizer
{
_localizer = localizer;
}
public IActionResult Index()
{
ViewData["Greeting"] = _localizer["HelloMessage"];
return View();
}
}
你可以直接使用 IStringLocalizer
在视图(Razor)中,如果你引入 IViewLocalizer 或 @inject IStringLocalizer
@localizer["WelcomeTitle"]
3. 本地化数据注解错误消息
对于模型验证特性(如 [Required], [Display(Name = "...")]),你也可以将其本地化。示例如下:
public class MyModel
{
[Required(ErrorMessage = nameof(SharedResource.RequiredError))]
public string Name { get; set; }
}
在服务配置时,你可以指定如何查找 DataAnnotation 的资源:
services.AddControllersWithViews()
.AddDataAnnotationsLocalization(options =>
{
options.DataAnnotationLocalizerProvider = (type, factory) =>
{
// 可统一使用 SharedResource 或者根据类型映射资源类
return factory.Create(typeof(SharedResource));
};
});
语言切换机制与策略
多语言支持不仅仅是资源翻译,还需要用户在界面中切换语言,同时系统记住用户选择。以下是几种常见机制:
1. 查询字符串 (QueryString)
最简单:在 URL 中传递 ?culture=zh-CN&ui-culture=zh-CN,系统通过 QueryStringRequestCultureProvider 读取并设置当前文化环境。
优点:实现简单、调试方便。
缺点:URL 会显得冗长,也不便于记忆或 SEO。
2. Cookie 存储
可以使用 CookieRequestCultureProvider,在用户切换语言时将语言信息写入 Cookie(格式如 c=zh-CN|uic=zh-CN)。下次访问时中间件优先读取 Cookie。
优点:用户体验好(记住偏好)。
缺点:跨设备不一致、有隐私/同意机制要求。
3. Accept-Language 请求头
默认的 AcceptLanguageHeaderRequestCultureProvider 可自动从浏览器发来的 Accept-Language 头解析用户偏好。当用户没有明确切换语言时,此机制可以作为后备方案。
4. 混合策略与自定义 Provider
你可以组合上述方式(优先 QueryString → 再 Cookie → 再 Header),也可以自定义 RequestCultureProvider(例如从用户数据库配置读取、URL 路由前缀读取等)。自定义的 Provider 需要继承 RequestCultureProvider 并重写 DetermineProviderCultureResult。
5. 切换语言接口与 UI
常见做法是创建一个控制器动作(如 ChangeLanguage),接收语言参数,设置 Cookie 或重定向带参数 URL,然后跳回原页面。
在视图层可以做一个下拉菜单、语言切换链接、旗帜图标等。切换后你可能希望通过 ReturnUrl 参数返回当前页面。
例如:
public IActionResult ChangeLanguage(string culture, string returnUrl)
{
Response.Cookies.Append(
CookieRequestCultureProvider.DefaultCookieName,
CookieRequestCultureProvider.MakeCookieValue(new RequestCulture(culture)),
new CookieOptions { Expires = DateTimeOffset.UtcNow.AddYears(1) }
);
return LocalRedirect(returnUrl);
}
进阶 & 扩展方案
运行时资源动态更新(数据库、外部翻译平台)
如果你希望在运行时更新翻译内容(无需重编译 .resx),可以考虑将资源存储在数据库或翻译平台中。你可以实现一个自定义 IStringLocalizer 或 IStringLocalizerFactory,在其内部优先查库或缓存,再回退到 .resx。这样就能支持后台翻译编辑、热更新等功能。
资源导入 / 导出工具
对于大规模翻译项目,手动编辑 .resx 很繁琐。你可以使用工具将翻译内容导出到 Excel、PO 文件、JSON、CSV 等,或者从翻译平台导入回 .resx。这样便于翻译人员集中管理与协作。
兼容前端 JS/i18n
在有前端(如 React、Vue、Angular、纯 JS)并且前后端共语言文本的情形下,你可能需要把资源内容以 JSON 或 JS 模块形式暴露给前端。你可以写一个 API 接口,根据用户语言返回一组键值对给前端 i18n 库。
SEO 和 URL 前缀语言
对于公开网页(非纯 API / 管理后台场景),你可能希望在 SEO 友好的 URL 中引入语言前缀,如 example.com/zh-CN/products、example.com/en-US/products。你可以在路由配置里添加语言作为路由片段,然后在中间件或过滤器中解析语言,设置当前文化。这样每个语言版本都有独立 URL,有利于 SEO 和内容索引。
常见问题与调试建议
资源未能正确加载:检查资源文件的命名规范、命名空间、Build Action 是否为 EmbeddedResource,资源类 Custom Tool 是否正确;确认 ResourcesPath 配置是否正确。
语言切换无效 / 总是显示默认语言:检查 UseRequestLocalization 是否在合适位置调用;确认 RequestLocalizationOptions 中是否包含目标语言;检查自定义 Provider 的执行顺序。
DataAnnotations 本地化失败:确保你在 AddDataAnnotationsLocalization 中配置了正确的 DataAnnotationLocalizerProvider,并且资源里存在该条目的翻译。
表单验证错误信息不是本地化:表单提交后若模型验证失败,你可能需要在前端或控制器中显式触发验证错误本地化,确保当前线程的 Culture/UiCulture 已经被设置。
前端与后端语言不一致:如果前端(JS)与后端(Razor / API)都有需要本地化的内容,要保证二者使用同一语言配置源或同步语言切换机制。
性能 / 缓存:对于动态(数据库)加载资源,要做好缓存或内存缓存策略,避免频繁访问数据库影响性能。
总结
利用 ASP.NET Core 内建的国际化与本地化机制(Microsoft.Extensions.Localization、RequestLocalizationMiddleware、.resx 资源文件等),你可以在项目中构建健壮、灵活、可扩展的多语言支持体系。从基础注入服务、配置中间件,到资源管理与语言切换,再到运行时扩展和前端兼容,本文给出了完整思路与实践路径。