• .NET Core如何使用资源文件实现多语言功能?
  • 发布于 2个月前
  • 194 热度
    0 评论
我们在实际的工作中会经常使用ASP.NET Core的Globalization & Localization 特性以及资源文件,资源文件(.resx)是用来从代码中分隔语言字符串的方法,这些文件包含key/values项,可以使用vs来创建该文件,例如: 你的website支持3种文化-法语,西班牙,英语,因此你必须创建2个类型的资源文件,例如法语和西班牙语。

在这节我将展示从website不同的区域如何实现本地化和全球化:
1 控制器使用IStringLocalizer对象
2 Data Annotations 错误消息
3 客户自定义验证错误消息
4 视图使用IViewLocalizer

我们基于英语,法语和西班牙语实现工作申请表单多语言。


一. 配置启动项
第一步告诉应用程序website将支持的语言文化,并且在应用程序中添加Localization服务和另外一些设置,进入Program类添加下面命名空间:
using Microsoft.AspNetCore.Localization;
using Microsoft.AspNetCore.Mvc.Razor;
using Microsoft.Extensions.Options;
using System.Globalization;
接下来添加下面代码:
builder.Services.AddControllersWithViews()
                .AddViewLocalization(LanguageViewLocationExpanderFormat.Suffix)
                .AddDataAnnotationsLocalization();
// 堆代码 duidaima.com
builder.Services.Configure<RequestLocalizationOptions>(options =>
{
    var supportedCultures = new[]
    {
            new CultureInfo("en-US"),
            new CultureInfo("fr"),
            new CultureInfo("es")
        };
    options.DefaultRequestCulture = new RequestCulture(culture: "en-US", uiCulture: "en-US");
    options.SupportedCultures = supportedCultures;
    options.SupportedUICultures = supportedCultures;
});
这段代码做了2件事情:
1. 给Data Annotations和Views添加本地化特性
2. 你网站支持3种文化-法语,西班牙语,英语被定义为默认
在应用程序中允许使用Localization 服务
var locOptions = app.ApplicationServices.GetService<IOptions<RequestLocalizationOptions>>();
app.UseRequestLocalization(locOptions.Value); 

使用资源文件在ASP.NET Core中创建全球化&本地化特性。


二.使用资源文件DataAnnotations本地化
首先创建一个Model用来呈现应用程序工作申请表单,接着在Models文件夹下创建一个新的类并且命名为JobApplication.cs,这个类包含如下属性:Name, Email Address, DOB 等,将特性应用到这些字段上,像 [Required],[RegularExpression],[Range],[Display]

代码如下:
using AspNetCore.GlobalLocalResFiles.Infrastructure;
using System.ComponentModel.DataAnnotations;
using System.Xml.Linq;

namespace AspNetCore.GlobalLocalResFiles.Models
{
    public class JobApplication
    {
        [Required(ErrorMessage = "Please provide your name")]
        [Display(Name = "Job applicant name")]
        public string Name { get; set; }

        [RegularExpression("^[a-zA-Z0-9_\\.-]+@([a-zA-Z0-9-]+\\.)+[a-zA-Z]{2,6}$", ErrorMessage = "E-mail is not valid")]
        [Display(Name = "Job applicant email")]
        public string Email { get; set; }
        [CustomDate]
        [Display(Name = "Date of Birth")]
        public DateTime DOB { get; set; }
        [Required(ErrorMessage = "Please select your sex")]
        [Display(Name = "Job applicant sex")]
        public string Sex { get; set; }
        [Range(2, 4, ErrorMessage = "{0} must be a number between {1} and {2}")]
        [Display(Name = "Job applicant experience")]
        public int Experience { get; set; }
        [Range(typeof(bool), "true", "true", ErrorMessage = "You must accept the Terms")]
        [Display(Name = "Terms")]
        public bool TermsAccepted { get; set; }
    }
}
接下来,创建资源文件用来存储错误消息和显示名称,因此在Models文件夹下创建两个资源文件并且和JobApplication类在相同的目录下:JobApplication.es.resx,JobApplication.fr.resx
注意:资源文件的名称我使用.es作为西班牙语和.fr作为法语

在这些资源文件你可以添加字符串(例如:错误消息和显示的名称)和他们各自法语和西班牙语字符串

如下图所示,展示两个资源文件:

三. 客户自定义验证本地化字符
我们创建了客户自定义特性叫[CustomDate]针对出生日期,在DOB字段上指定客户验证
[CustomDate]
[Display(Name = "Date of Birth")]
public DateTime DOB { get; set; }
在Infrastructure文件夹创建一个新类叫CustomDate.cs,该类负责验证因此你必须继承自ValidationAttribute类,代码如下:
public class CustomDate : ValidationAttribute
{
    protected override ValidationResult IsValid(object value, ValidationContext validationContext)
    {
        var _localizationService = (IStringLocalizer<CustomDate>)validationContext.GetService(typeof(IStringLocalizer<CustomDate>));
        if ((DateTime)value > DateTime.Now)
            return new ValidationResult(_localizationService["Date of Birth cannot be in the future"]);
        else if ((DateTime)value < new DateTime(1980, 1, 1))
            return new ValidationResult(_localizationService["Date of Birth should not be before 1980"]);
        return ValidationResult.Success;
    }
}
在运行时使用了IStringLocalizer对象获取资源类,它是一个服务用来提供存储在资源文件中的本地化字符串
var _localizationService = (IStringLocalizer<CustomDate>)validationContext.GetService(typeof(IStringLocalizer<CustomDate>));
注意我使用下面代码将获取语言指定的字符串从资源文件中
_localizationService["Date of Birth cannot be in the future"]
_localizationService["Date of Birth should not be before 1980"]
接下来,在Custom验证类的目录下创建2个资源文件并且命名为:
CustomDate.es.resx
CustomDate.fr.resx

在资源文件里添加法语和西班牙语文本字符串- Date of Birth cannot be in the future & Date of Birth should not be before 1980 .


四.控制器使用资源文件进行本地化
当表单提交时控制器调用action方法,提交成功后显示信息在这三种语言中基于用户选择的语言文化,因此,首先我们使用asp.net core依赖注入特性在控制器的构造函数中注入IStringLocalizer对象,然后从资源文件中遍历字符串关联的语言文化,这个消息将展示到用户提交的表单。
代码如下:
public class HomeController : Controller
{
    private readonly IStringLocalizer<HomeController> _localizer;
    public HomeController(IStringLocalizer<HomeController> localizer)
    {
        _localizer = localizer;
    }
    public IActionResult Index()
    {
        return View();
    }
    [HttpPost]
    public IActionResult Index(JobApplication jobApplication)
    {
        if (ModelState.IsValid)
            ViewBag.Message = _localizer["Your application is accepted"];
        return View();
    }
}
_localizer 是一个变量包含IStringLocalizer对象,它主要提供给我存储在资源文件中的特定文化字符串
接下来,创建2个资源文件在Controllers文件夹,命名为:HomeController.es.resx,HomeController.fr.resx
由于我只有一个字符串“Your application is accepted”,因此两个资源文件都只有一个条目
下面两张图片是我控制器资源文件:

五. 视图使用资源文件进行本地化
IViewLocalizer服务提供针对视图文件进行本地化,可以将它注入到你的视图中,显示如下:
@inject IViewLocalizer Localizer
Job申请表单位于Index视图,首先你添加必要命名空间在你的视图:
@using Microsoft.AspNetCore.Builder
@using Microsoft.AspNetCore.Localization
@using Microsoft.Extensions.Options
@using Microsoft.AspNetCore.Mvc.Localization
然后在视图中注入 IViewLocalizer & IOptions ,代码如下:
@inject IViewLocalizer Localizer
@inject IOptions<RequestLocalizationOptions> LocOptions
通过RequestLocalizationOptions 对象,我将使用网站支持的语言文化填充选择控件,用户可以选择他们的文化并且表单将基于选择的文化呈现给用户
接下来添加下面代码创建和填充选择控件项
@{
    var requestCulture = Context.Features.Get<IRequestCultureFeature>();
    var cultureItems = LocOptions.Value.SupportedUICultures
        .Select(c => new SelectListItem { Value = c.Name, Text = c.DisplayName })
        .ToList();
}
<label>Language:</label>
<select onchange="SetCulture(this.value)" asp-for="@requestCulture.RequestCulture.UICulture.Name" asp-items="cultureItems">
</select>
这一小段的JavaScript代码将跳转到用户选择文化的job申请表单
<script>
    function SetCulture(selectedValue) {
        var url = window.location.href.split('?')[0];
        var culture = "?culture=" + selectedValue + "&ui-culture=" + selectedValue;
        window.location.href = url + culture;
    }
</script>
这里使用 QueryStringRequestCultureProvider,其中用户选择的文化被添加到 url 的查询字符串中,因此法语版本的应用程序表单将给与如下url:
https://localhost:44356/?culture=fr&ui-culture=fr
西班牙版本的表单url如下:
https://localhost:44356/?culture=es&ui-culture=es
英语版本我们不需要在url中添加语言文化
https://localhost:44356
接下来我们将把表单添加到视图:
<form class="m-1 p-1" asp-action="Index" asp-route-culture="@culture" asp-route-ui-culture="@uiculture" method="post">
    <div class="form-group">
        <label asp-for="Name"></label>
        <input asp-for="Name" class="form-control" />
        <span asp-validation-for="Name" class="text-danger"></span>
    </div>
    <div class="form-group">
        <label asp-for="DOB"></label>
        <input asp-for="DOB" type="text" asp-format="{0:d}" class="form-control" />
        <span asp-validation-for="DOB" class="text-danger"></span>
    </div>
    <div class="form-group">
        <label asp-for="Sex"></label>
        <div>
            <input asp-for="Sex" type="radio" value="M" />@Localizer["Male"]
            <input asp-for="Sex" type="radio" value="F" />@Localizer["Female"]
        </div>
        <span asp-validation-for="Sex" class="text-danger"></span>
    </div>
    <div class="form-group">
        <label asp-for="Experience"></label>
        <select asp-for="Experience" class="form-control">
            <option value="Select">@Localizer["Select"]</option>
            <option value="0">Fresher</option>
            <option value="1">0-1 years</option>
            <option value="2">1-2 years</option>
            <option value="3">2-3 years</option>
            <option value="4">3-4 years</option>
            <option value="5">4-5 years</option>
        </select>
        <span asp-validation-for="Experience" class="text-danger"></span>
    </div>
    <div class="form-group">
        <input asp-for="TermsAccepted" />
        <label asp-for="TermsAccepted" class="form-check-label">
            @Localizer["I accept the terms & conditions"]
        </label>
        <span asp-validation-for="TermsAccepted" class="text-danger"></span>
    </div>
    <button name="formsubmit" value="Button Control" type="submit" class="btn btn-primary">@Localizer["Submit Application"]</button>
</form>
在视图中我们使用了IViewLocalizer来获取文化关联的字符串,使用代码-@Localizer["SomeString"]

下面为radio控件代码:
<input asp-for="Sex" type="radio" value="M" />@Localizer["Male"]
<input asp-for="Sex" type="radio" value="F" />@Localizer["Female"]
同样,terms标签也做同样的事情:
<label asp-for="TermsAccepted" class="form-check-label">
    @Localizer["I accept the terms & conditions"]
</label>
接下来在与视图相同的目录下创建2个资源文件. 如下:
Index.es.resx
Index.fr.resx
在文件中添加必要的字符串,如下所示

六. 测试
接下来让我们做个测试,第一个将显示用户选择的文化,并且和其关联的文化表单将显示在浏览器:

接下来将展示data annotation信息用西班牙&法语版本

七.资源文件位置
你可以在启动项中使用如下代码改变资源文件的位置
services.AddLocalization(options => options.ResourcesPath = "Resources");
在这种情况下,HomeController 的资源文件应位于以下 2 个位置中的任意一个:
1. Resources/Controllers/ 目录里面:
    Resources/Controllers/HomeController.es.resx
    Resources/Controllers/HomeController.fr.resx
2. Resources目录里面:
    Resources/Controllers.HomeController.es.resx
    Resources/Controllers.HomeController.fr.resx
Data Annotations 资源文件位于以下2个位置中的任意一个:
Resources/Models.JobApplication.es.resx
Resources/Models.JobApplication.fr.resx
或者
Resources/Models/JobApplication.es.resx
Resources/Models/JobApplication.fr.resx
视图资源文件位于以下2个位置中的任意一个:
Resources/Views.Home.Index.es.resx
Resources/Views.Home.Index.fr.resx
或者
Resources/Views/Home/Index.es.resx
Resources/Views/Home/Index.fr.resx

用户评论