最近有一个外国友人通过邮件联系我,想用我的活动室预约,但是还没支持多语言,基本上都是写死的中文,所以最近想支持一下更多语言,于是有了多语言方面的一些实践
国际化/本地化介绍国际化(Globalization)和本地化(Localization)是要实现的多语言支持的基础
Globalization is the process of designing and developing applications that function for multiple cultures.
Localization is the process of customizing your application for a given culture and locale.
国际化是要支持处理多种文化,而本地化是要根据某一个文化和区域的来展示相应的处理。
更多关于国际化与本地化的不同可以参考 Stack Overflow 上的讨论 https://stackoverflow.com/questions/2074869/globalization-vs-localization
Localization In Asp.NET Core微软官方的 Localization 的实现是基于资源文件实现的 (*.resx),我们也可以扩展支持更多方式,如 JSON/数据库 都是可以的,社区已经有实现的示例,只要是可以提供一个文本源的都是可以的,我们先使用默认的基于资源文件的,下一篇再讲一个自定义实现一个 Localization Provider。
.NET Core Localization 的 核心是 IStringLocalizer,asp.net core 里扩展定义了 IViewLocalizer 和 IHtmlLocalizer,IViewLocalizer 和 IHtmlLocalizer 主要是为了处理包含 html 的资源,他们不会对资源进行 html encode,相当于 @Html.Raw 的效果,而 IStringLocalizer 则会被 html encode,除此之外 IViewLocalizer 还会根据当前视图的路径寻找资源文件
来看一个示例:
Razor 页面
浏览器效果:
查看网页源代码:
实际案例 服务注册注册 Localization 相关服务:
var supportedCultures = new[] { new CultureInfo("zh"), new CultureInfo("en"), }; services.Configure<RequestLocalizationOptions>(options => { options.DefaultRequestCulture = new RequestCulture("zh"); // Formatting numbers, dates, etc. options.SupportedCultures = supportedCultures; // UI strings that we have localized. options.SupportedUICultures = supportedCultures; }); services.AddLocalization(options => options.ResourcesPath = Configuration.GetAppSetting("ResourcesPath"));配置视图 Localization(根据需要如果是 WebAPI 就不需要了)
services.AddControllersWithViews() .AddNewtonsoftJson(options => { options.SerializerSettings.ContractResolver = new DefaultContractResolver(); options.SerializerSettings.DateTimeZoneHandling = DateTimeZoneHandling.Utc; // 设置时区为 UTC options.SerializerSettings.NullValueHandling = NullValueHandling.Ignore; options.SerializerSettings.ReferenceLoopHandling = ReferenceLoopHandling.Ignore; }) .AddViewLocalization(LanguageViewLocationExpanderFormat.Suffix, opts => { opts.ResourcesPath = Configuration.GetAppSetting("ResourcesPath"); }) .AddDataAnnotationsLocalization() .SetCompatibilityVersion(CompatibilityVersion.Latest);中间件配置:
app.UseRequestLocalization();在控制器中使用示例:
IStringLocalizer 和 IHtmlLocalizer /IViewLocalizer 都可以直接从依赖注入服务中获取,IStringLocalizer 和 IHtmlLocalizer 推荐使用强类型的方式,也就是下面示例的使用方式,使用方式和 ILogger 类似
public async Task<ActionResult> MakeReservation( [FromBody]ReservationViewModel model, [FromHeader]string captcha, [FromHeader]string captchaType, [FromServices]IStringLocalizer<HomeController> localizer) { var result = new ResultModel<bool>(); var isCodeValid = await HttpContext.RequestServices.GetService<CaptchaVerifyHelper>() .ValidateVerifyCodeAsync(captchaType, captcha); if (!isCodeValid) { result.Status = ResultStatus.RequestError; result.ErrorMsg = localizer["InvalidCaptchaInfo"]; return Json(result); }在视图中使用示例:
localizer["data"] 返回的是一个 LocalizedString,实现了隐式转换为 string, 有的时候可能需要强制转一下string, 或者使用 Value 属性
@inject IViewLocalizer viewLocalizer viewLocalizer["About"] @Html.ActionLink((string)viewLocalizer["About"], "About", "Home") @Html.ActionLink(viewLocalizer["About"].Value, "About", "Home")资源文件配置:
资源文件的配置和文件的结构类似,下面是一个示例