ASP.NET Core 使用 JSON 文件作为国际化/多语言/ I18N 处理方式

yufei       4 年, 11 月 前       6503

今天复习了下之前写的 ASP.NET Core 基础教程 发现少了国际化这个章节。而最近的项目有可能要用到这个特性,于是花点时间来了解下。

因为我喜欢使用轻量级的编辑器,比如 Sublime Text 4 或者 Visual Studio Code 做开发,所以对官方给丁的默认资源文件格式 resx 实在是不好编辑和添加。因此,本文我们使用 JSON 文件作为 I18N 国际化的文件格式。

1. 创建项目

我们使用命令行来创建使用 webapp 模板,一个名为 i18n 的项目

dotnet new webapp -n i18n

输出结果如下

正在准备...
已成功创建模板“ASP.NET Core Web App”。
此模板包含除 Microsoft 以外其他方的技术,请参阅 https://aka.ms/aspnetcore/5.0-third-party-notices 以获取详细信息。

正在处理创建后操作...
在 i18n/i18n.csproj 上运行 “dotnet restore”...
  正在确定要还原的项目…
  已还原 /Users/yufei/Downloads/dotnet/i18n/i18n.csproj (用时 102 ms)。
已成功还原。

然后使用 tree i18n 看看目录结果,输出结果如下

.
├── Pages
│   ├── Error.cshtml
│   ├── Error.cshtml.cs
│   ├── Index.cshtml
│   ├── Index.cshtml.cs
│   ├── Privacy.cshtml
│   ├── Privacy.cshtml.cs
│   ├── Shared
│   │   ├── _Layout.cshtml
│   │   └── _ValidationScriptsPartial.cshtml
│   ├── _ViewImports.cshtml
│   └── _ViewStart.cshtml
├── Program.cs
├── Properties
│   └── launchSettings.json
├── Startup.cs
├── appsettings.Development.json
├── appsettings.json
├── i18n.csproj
├── obj
│   ├── i18n.csproj.nuget.dgspec.json
│   ├── i18n.csproj.nuget.g.props
│   ├── i18n.csproj.nuget.g.targets
│   ├── project.assets.json
│   └── project.nuget.cache
└── wwwroot
    ├── css
    │   └── site.css
    ├── favicon.ico
    ├── js
    │   └── site.js
    └── lib
        ├── bootstrap
        │   ├── LICENSE
        │   └── dist
        │       ├── css
        │       │   ├── bootstrap-grid.css
        │       │   ├── bootstrap-grid.css.map
        │       │   ├── bootstrap-grid.min.css
        │       │   ├── bootstrap-grid.min.css.map
        │       │   ├── bootstrap-reboot.css
        │       │   ├── bootstrap-reboot.css.map
        │       │   ├── bootstrap-reboot.min.css
        │       │   ├── bootstrap-reboot.min.css.map
        │       │   ├── bootstrap.css
        │       │   ├── bootstrap.css.map
        │       │   ├── bootstrap.min.css
        │       │   └── bootstrap.min.css.map
        │       └── js
        │           ├── bootstrap.bundle.js
        │           ├── bootstrap.bundle.js.map
        │           ├── bootstrap.bundle.min.js
        │           ├── bootstrap.bundle.min.js.map
        │           ├── bootstrap.js
        │           ├── bootstrap.js.map
        │           ├── bootstrap.min.js
        │           └── bootstrap.min.js.map
        ├── jquery
        │   ├── LICENSE.txt
        │   └── dist
        │       ├── jquery.js
        │       ├── jquery.min.js
        │       └── jquery.min.map
        ├── jquery-validation
        │   ├── LICENSE.md
        │   └── dist
        │       ├── additional-methods.js
        │       ├── additional-methods.min.js
        │       ├── jquery.validate.js
        │       └── jquery.validate.min.js
        └── jquery-validation-unobtrusive
            ├── LICENSE.txt
            ├── jquery.validate.unobtrusive.js
            └── jquery.validate.unobtrusive.min.js

17 directories, 57 files

大家在使用 Visual Studio 或者 Visual Studio Code 创建项目的时候,在模板页面选择 Web 应用程序 这个模板。其它的则默认即可。

2. 添加 My.Extensions.Localization.Json 包

可以使用 Nuget 搜索 My.Extensions.Localization.Json 并点击安装即可。或者你可以像我一样使用下面的命令行添加

dotnet add package  My.Extensions.Localization.Json

3. 创建 Resources 文件和 Startup.en.json 和 Startup.zh.json

在 118n 目录下创建子目录 Resources,并在 Resources 目录下创建 Startup.en.json 和 Startup.zh.json 文件

mkdir Resources
touch Resources/Startup.en.json
touch Resources/Startup.zh.json

创建好后的目录结构如下

.
├── Pages
│   ├── ...
├── Program.cs
├── Properties
│   └── launchSettings.json
├── Resources
│   ├── Startup.en.json
│   ├── Startup.zh.json
├── Startup.cs
├── appsettings.Development.json
├── appsettings.json
├── bin
│   └── Debug
│       └── net5.0
│           └── ref
├── i18n.csproj
├── i18n.sln
├── obj
│   ....
└── wwwroot
    ├── css
    │   └── site.css
    ├── favicon.ico
    ├── js
    │   └── site.js
    └── lib
        ├── ....

4. 编辑 Startup.en.json 和 Startup.zh.json 分别输入两个条目

Startup.en.json

{
  "hello": "Hello"
}

Startup.zh.json

{
  "hello":  "你好"
}

5. 编辑 Startup.cs 添加相关配置

接下来我们要编辑 Startup.cs 文件添加国际化相关的配置

首先引入相关命名空间

using System.Globalization;
using Microsoft.Extensions.Localization;
using Microsoft.AspNetCore.Localization;
using System.Collections.Generic;

告诉系统 JSON 国际化资源文件位置

编辑 public void ConfigureServices(IServiceCollection services) 方法添加国际化资源文件位置

// This method gets called by the runtime. Use this method to add services to the container.
public void ConfigureServices(IServiceCollection services)
{
    services.AddRazorPages();

    // 添加下面这行
    // 告诉国际化引擎,所有的国际化资源文件都在 Resources 目录
    services.AddJsonLocalization(options => options.ResourcesPath = "Resources");
}

扩展 Configure() 方法配置国际化

把原来的 public void Configure(IApplicationBuilder app, IWebHostEnvironment env) 方法扩展成 public void Configure(IApplicationBuilder app, IHostEnvironment env, IStringLocalizer localizer1, IStringLocalizer<Startup> localizer2){

然后添加我们可能要切换到的语言选项

// 添加可能要用到的语言选项列表
var supportedCultures = new List<CultureInfo>
{
    new CultureInfo("en"),
    new CultureInfo("zh")
};

对了,所有的国际化语言选项都是 CultureInfo 的实例。每个区域都有自己的文化,所以 CultureInfo 这个名字还是蛮适合的

下一步,我们要把这个语言选项列表赋值给请求相关的配置上并且设置默认的语言选项

// 设置发起请求可能要用到的语言适配
var options = new RequestLocalizationOptions
{
    DefaultRequestCulture = new RequestCulture("en"),
    SupportedCultures = supportedCultures,
    SupportedUICultures = supportedCultures
};

最后,我们使用 app.UseRequestLocalization(options) 把国际化的配置设置到应用程序上。 并注释掉所有模板相关的请求。

上面所有的配置完成后,Startup.cs 类的所有代码如下

using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;

using System.Globalization;
using Microsoft.Extensions.Localization;
using Microsoft.AspNetCore.Localization;
using System.Collections.Generic;
using Microsoft.AspNetCore.Http;

namespace i18n
{
    public class Startup
    {
        public Startup(IConfiguration configuration)
        {
            Configuration = configuration;
        }

        public IConfiguration Configuration { get; }

        // This method gets called by the runtime. Use this method to add services to the container.
        public void ConfigureServices(IServiceCollection services)
        {
            // services.AddRazorPages();
            // 告诉国际化引擎,所有的国际化资源文件都在 Resources 目录
            services.AddJsonLocalization(options => options.ResourcesPath = "Resources");
        }

        // This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
        public void Configure(IApplicationBuilder app, IHostEnvironment env, IStringLocalizer localizer1, IStringLocalizer<Startup> localizer2)
        {

            // 添加可能要用到的语言选项列表
            var supportedCultures = new List<CultureInfo>
            {
                new CultureInfo("en"),
                new CultureInfo("zh")
            };

            // 设置发起请求可能要用到的语言适配
            var options = new RequestLocalizationOptions
            {
                DefaultRequestCulture = new RequestCulture("en"),
                SupportedCultures = supportedCultures,
                SupportedUICultures = supportedCultures
            };

            // 将国际化的配置添加到应用程序上
            app.UseRequestLocalization(options);


            if (env.IsDevelopment())
            {
                app.UseDeveloperExceptionPage();
            }
            else
            {
                app.UseExceptionHandler("/Error");
                // The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts.
                app.UseHsts();
            }

            // app.UseHttpsRedirection();
            // app.UseStaticFiles();

            // app.UseRouting();

            // app.UseAuthorization();

            app.Run(async (context) =>
            {
                await context.Response.WriteAsync($"{localizer1["hello"]}!!");
                await context.Response.WriteAsync($"{localizer2["hello"]}!!");
            });

            /*
            app.UseEndpoints(endpoints =>
            {
                endpoints.MapRazorPages();
            });*/
        }
    }
}

输出结果如下

hello!!你好!!

如果我们全部切换到英文,则输出结果如下

hello!!Hello!!%                                                                           

很多同学会问 Configure() 方法中的 IStringLocalizer localizer1 和 IStringLocalizer<Startup> localizer2 有什么不同?

这里,我们就要讲讲 ASP.NET Core 如何查找国际化语言文件了,比如对于 en_US 和 zh_CN 两个语言序列和 Startup 这个类。

  1. 带泛型和不带泛型的区别就是,带泛型首先会查找 [类名].[语言].json 和非泛型则查找 [语言].json 文件。所以 localizer1 首先查找的是 en_US.json 或 zh_CN.json 类,而 localizer2 查找的是 Startup.zh_CN.json 或 Startup.en_US.json 文件。

  2. 对于语言类,比如 en_US 和 zh_CN 如果找不到精确匹配的语言文件,则会尝试查找 _ 前面那部分对应的语言文件,比如 en.json 或 zh.json 文件。

    en_US 这种是 [语言]_[国家代码] 的组合形式。

泛型类的好处

IStringLocalizer<Startup> localizer2 这种机制的好处,给予我们按功能、按模块组织国际化的机会。比如我们可以把所有和用户相关的国际化资源都放到 User.json 中,然后在方法入口使用 IStringLocalizer<User> userLocalizer 来访问对应的资源

2 回复  |  直到 Jan 04, 2022

yufei

#1   •   4 年, 8 月 前   •  

这我就不懂了

cc520

#2   •   4 年, 8 月 前   •  

html界面可以适用嘛,用的easyui的框架搭的页面

简单教程 = 简单教程,简单编程
简单教程 是一个关于技术和学习的地方
现在注册
已注册用户请 登入
关于   |   FAQ   |   我们的愿景   |   广告投放   |  博客

♥   简单教程,简单编程 - IT 入门首选站

Copyright © 2013-2022 简单教程 twle.cn All Rights Reserved.