.net5 core Razor 进阶之四:全球化和本地化
在网页开发中常用的一个功能可能就是全球化了,全球化的目的之一就是为网页应用提供多语言的支持,
它的原理并不复杂,都是用一个Key对应多个 value 值,在不同的语言下取相应的 Value 值,形式如下:
key | chinese_value | english_value | korea_value |
hello | 你好 | hello | |
... | ... | ... | |
exit | 登出 | sign out |
当页面切换到中文的时候就取 chinese_value 的值,切换到英文的时候就取 english_value 的值,
其他以此类推。下面我们使用 .NET5 core 自带的全球化功能来实现这样的效果。
场景:
将前面例子中网页的菜单项全部做成多语言的,为简单起见,
只提供中文(简体/繁体)和英文共3个版本(其他语言笔者也不会),效果如下:
前置概念:
1. DotNET5 core中实现多语言的原理是这样的:将不同的语言的 Key-Value 值组织到不同的文件中,这些文件称为【资源文件】,
文件的名称是以 " . " 号分隔的三段式文件名(这是规则),以本例为例,简体中文对应的文件名:Menu.zh-CN.resx,
繁体中文对应的文件名:Menu.zh-TW.resx,英文对应的文件名:Menu.en.resx,
可以看到,第1段 "Menu" 这个名称可以自己命名,第3段是后缀名 resx,第2段则必须取值于下表(节选):
总共352个,具体可以用下面的代码遍历出来看:
CultureInfo[] ArrCulture = CultureInfo.GetCultures(CultureTypes.UserCustomCulture);
和中文有关的几个:
zh | 中文 |
zh-Hans | 中文(简体) |
zh-Hant | 中文(繁体) |
zh-TW | 中文(繁体,台湾) |
zh-CN | 中文(简体,中国) |
zh-SG | 中文(简体,新加坡) |
zh-MO | 中文(繁体,澳门特别行政区) |
2. 如果新建了一个资源文件,一定要在同样的名称空间下建一个相同名称的类,
比如上面新增了 Menu.en.resx 这个资源文件,一定要同时新增一个 Menu.cs 的类,路径可以随意,但是名称空间要是 AuthManagement。
3. 有多语言的页面一般会有一个可选的语言列表供用户去选择对应的语言(如下图),当用户选择某种语言后,
要以某种方式将此语言值传回当前页面,这样在刷新当前页面的时候,框架就会根据设置的语言去找对应的资源文件
(比如用户选择了中文简体,就要将zh-CN传递到当前页面,这样系统就会去找 Menu.zh-CN.resx 这个文件) 。
4. 如何将zh-CN传递到当前页面呢?DotNET5 core为我们提供了3种传值途径,分别是:URL、Cookie、Header。
(1) URL 的表现形式是这样的:https://docs.microsoft.com/?culture=zh-CN,后面接查询参数 culture = zh-CN;
(2) Cookie 对应的 key 是 ".AspNetCore.Culture",设置该值为 .AspNetCore.Culture = c=zh-CN;
(3) Header 对应的 key 是 "Accept-Language",设置该值为:Accept-Language = zh-CN;
有了上面4点基本知识,我们就可以来实现场景中要的效果了。
实现步骤:
第1步:建立资源文件。
在解决方案下新建 Resources 的文件夹,添加3个资源文件,如下:
Menu.en.resx / Menu.zh-CN.resx / Menu.zh-TW.resx 分别如下:
第2步:新增一个 Menu.cs 的类文件(如下图),这个文件什么代码都不用写,
仅仅只新增进来就可以了,这是.NET CORE要求的。(这样的要求是不是有点奇怪?)
Menu.cs 并没有放到解决方案的根目录下,因为不想让类文件杂乱无章,为了满足【前置概念】中第2点的要求,
我们把这个类的名称空间改成 AuthManagement (默认的名称空间是 AuthManagement.ResourcesClass)。
namespace AuthManagement { public class Menu { } }
第3步:在 Startup.cs 中配置资源文件的路径和相应的服务。
public void ConfigureServices(IServiceCollection services) { //指定资源文件的路径
services.AddLocalization(options=>options.ResourcesPath="Resources"); services.AddRazorPages().AddViewLocalization(); // 让页面 xxx.cshtml 也能使用资源文件 //其他代码...... }
在 Configure() 方法中启用全球化
public void Configure(IApplicationBuilder app, IWebHostEnvironment env, ILoggerFactory loggerFactory)
{ if (env.IsDevelopment()) { app.UseDeveloperExceptionPage(); } else { app.UseExceptionHandler("/Error"); } //启用本地化中间件 string[] cultures = new[] { "zh-CN", "zh-TW", "en" }; app.UseRequestLocalization( new RequestLocalizationOptions().SetDefaultCulture(cultures[0]) //设置多语言的默认值是 zh-CN .AddSupportedCultures(cultures).AddSupportedUICultures(cultures) );
//其他代码...... }
第4步:在 _Layout.cshtml 中注入 IHtmlLocalizer<Menu> 使用多语言,并加入多语言列表的链接。
@using Microsoft.AspNetCore.Mvc.Localization; @using AuthManagement; @inject IHtmlLocalizer<Menu> Localizer //在页面顶部注入 IHtmlLocalizer<Menu> ,这里可以看到 Menu 的作用了。
导航菜单使用多语言:
......
<ul class="navbar-nav flex-grow-1"> <li class="nav-item"><a class="nav-link text-dark" asp-area="" asp-page="/Index">【@Localizer["Home"]】</a></li> <li class="nav-item"><a class="nav-link text-dark" asp-area="" asp-page="/Auth/DeptList">【@Localizer["DeptMng"]】</a></li> <li class="nav-item"><a class="nav-link text-dark" asp-area="" asp-page="/Auth/UserList">【@Localizer["UserMng"]】</a></li> <li class="nav-item"><a class="nav-link text-dark" asp-area="" asp-page="/Auth/AuthSetting">【@Localizer["AuthMng"]】</a></li> <li class="nav-item"><a class="nav-link text-dark" asp-area="" asp-page="/Auth/LogList">【@Localizer["OpLog"]】</a></li> <li class="nav-item"><a class="nav-link text-dark" asp-area="" asp-page="/Auth/ScoreMng">【@Localizer["StuScore"]】</a></li> <li class="nav-item"><a class="nav-link text-dark" asp-area="" asp-page="/Privacy">【@Localizer["Privacy"]】</a></li> <li class="nav-item"> @if (Context.User.Identity.IsAuthenticated) { <a class="nav-link text-dark" asp-area="" asp-page="/Auth/Signout">[@Localizer["Exit"]]</a> } else { <a class="nav-link text-dark" asp-area="" asp-page="/Auth/Signin">[@Localizer["Enter"]]</a> } </li> <li> <a style="color:#000;" href="/Index?culture=zh-CN">中文简体</a> |
<a style="color:#000;" href="/Index?culture=zh-TW">繁體</a> |
<a style="color:#000;" href="/Index?culture=en">English</a> | </li> </ul>
......
菜单项都用 @Localizer["Xxxxx"] 这样的形式做多语言,Xxxxx 是在资源文件中定义的 Key ;
语言列表只是简单的跳转到首页并以 URL 的形式将用户选择的语言值传过去(另外2种传值方式略)。
至此,首页的多语言版本就准备好了,我们编译并运行页面的效果如下:
第一次打开页面,默认显示 zh-CN , 这是我们在 Startup.cs 的 Configure() 中配置的:
点 "繁体" 的链接后页面如下:
点 "English" 的链接后页面如下:
可以看到,达到了我们想要的效果。
-----------------------------------------------------------------------
注:如果想在 .cs 页面也使用多语言,只需要在构造函数中注入 IStringLocalizer<T> ,
并用索引的方式使用就可以了,代码如下:
namespace AuthManagement.Pages { public class IndexModel : PageModel { private readonly IStringLocalizer<Menu> _localizer; public IndexModel(IStringLocalizer<Menu> localizer) {
//注入资源文件服务,因为我们用的是 Menu.xxx.resx 这个类型的资源文件,
//所以 IStringLocalizer<T> 对应的泛型类是 Menu 。 _localizer = localizer; } public string LocalizerTest; public void OnGet() { LocalizerTest = _localizer["SysTitle"]; //直接使用 key 值引用就可以了。 } } }
从这里我们也可以看到为什么要定义 Menu.cs 这个什么代码都不用写的类了,从微软设计师的观点来看,
资源文件是不能孤立存在的,它只能依附在某个对象上,把资源和对象强行关联起来,这样把对象管理好,
资源文件也就不会乱,这也就可以理解为什么要叫 “资源文件” 而不是 "语言文件" 或其他什么名字了。