ASP.NET Core搭建多層網站架構【13-擴展之支持全球化和本地化多語言】

2020/02/03, ASP.NET Core 3.1, VS2019, ResXManagerhtml

摘要:基於ASP.NET Core 3.1 WebApi搭建後端多層網站架構【13-擴展之支持全球化和本地化多語言】
使用資源管理多語言文件實現網站本地化支持多語言顯示git

文章目錄github

此分支項目代碼web

官方文檔請點擊:ASP.NET Core 全球化和本地化數據庫

本章節介紹了使用資源管理多語言文件實現網站本地化支持多語言顯示json

說明:本文的方法是採起全部的語言資源都在同一個地方,例如在MS.WebCore路徑下有SharedResource.zh-Hans.resxSharedResource.zh-Hant.resx兩個語言資源文件,裏面包含了整個網站全部的翻譯,無論是Controller中的翻譯仍是業務Service的翻譯都從SharedResource中讀取。
而官方的作法是:HomeController的語言資源文件在Resources/Controllers.HomeController.fr.resxResources/Controllers/HomeController.fr.resx中,HomeService的語言資源文件則可能在Resources/Services.HomeService.fr.resxResources/Services/HomeService.fr.resx中,各個語言資源文件分散在各處,沒有統一管理,官方語言資源文件命名的規則請查閱文檔後端

網站添加默認語言

MS.WebApi應用程序的appsettings.jsonSiteSetting節點中,添加DefaultLanguage子節點:"DefaultLanguage": "zh-Hans"
api

MS.WebCore類庫的SiteSetting.cs類中,對應添加網站默認語言的成員變量public string DefaultLanguage { get; set; }
架構

說明:mvc

  • 網站配置中添加了默認語言(DefaultLanguage)配置,默認爲簡體中文zh-Hans
  • zh-Hans表示簡體中文,未區分地區,若是要精確可使用zh-Hans-CN表示中國大陸地區使用的簡體中文,zh-Hans-SG則表示新加坡地區使用的簡體中文,標準文檔
  • 本文還將創建其餘兩種語言:zh-Hant繁體中文、en英語(也都是未區分地區的)

添加語言資源文件

MS.WebCore類庫中新建MultiLanguages文件夾,在該文件夾下新建SharedResource.cs類:

namespace MS.WebCore.MultiLanguages
{
    public class SharedResource
    {
    }
}

注意是public類型

在MultiLanguages文件夾下新建資源文件SharedResource.zh-Hans.resx:

若是新建項中找不到資源文件的選項,把MS.WebCore類庫中的Project Sdk從Microsoft.NET.Sdk改成Microsoft.NET.Sdk.Web再試試(記得添加完資源文件再把sdk改回來)
一樣的方式再添加SharedResource.en.resxSharedResource.zh-Hant.resx
新建完成後,以下圖所示:

後面的操做中,會主要編輯簡體中文的翻譯內容,而後使用工具同步翻譯繁體中文、英語的翻譯內容

封裝註冊

在MultiLanguages文件夾下新建MultiLangExtensions.cs類:

using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Localization;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Localization;
using System.Collections.Generic;
using System.Globalization;

namespace MS.WebCore.MultiLanguages
{
    public static class MultiLangExtensions
    {
        /// <summary>
        /// 支持的語言類型
        /// 此處內容要與真實的文件對應
        /// </summary>
        public static readonly List<string> supportLangs = new List<string>
        {
            "zh-Hans",
            "zh-Hant",
            "en"
        };

        /// <summary>
        /// 更改當前UI線程語言
        /// </summary>
        /// <param name="name"></param>
        public static void SetCurrentUICulture(string name)
        {
            CultureInfo.CurrentUICulture = new CultureInfo(name, false);
        }
        /// <summary>
        /// 獲取指定語言的文字內容
        /// </summary>
        /// <param name="localizer"></param>
        /// <param name="specificLang"></param>
        /// <param name="key"></param>
        /// <returns></returns>
        public static string GetSpecificLanguageString(this IStringLocalizer localizer, string specificLang, string key)
        {
#pragma warning disable CS0618 // 類型或成員已過期
            return localizer.WithCulture(new CultureInfo(specificLang))[key].ToString();
#pragma warning restore CS0618 // 類型或成員已過期
        }


        /// <summary>
        /// 添加多語言本地化支持
        /// </summary>
        /// <param name="services"></param>
        /// <returns></returns>
        public static IServiceCollection AddMultiLanguages(this IServiceCollection services)
        {

            services.AddLocalization();

            services.AddSingleton<IStringLocalizer>((sp) =>
            {
                var sharedLocalizer = sp.GetRequiredService<IStringLocalizer<SharedResource>>();
                return sharedLocalizer;
            });
            /*
             //須要在startup中,AddControllers(webapi)的後面或者AddMVC(webmvc)註冊
            .AddDataAnnotationsLocalization(options =>
                {
                    options.DataAnnotationLocalizerProvider = (type, factory) =>
                        factory.Create(typeof(SharedResource));//給註解添加本地化資源提供器Localizerprovider
                })
             
             */

            return services;
        }

        /// <summary>
        /// 使用多語言本地化中間件
        /// 默認語言在appsetting中設置
        /// </summary>
        /// <param name="app"></param>
        /// <param name="configuration"></param>
        /// <returns></returns>
        public static IApplicationBuilder UseMultiLanguage(this IApplicationBuilder app, IConfiguration configuration)
        {
            List<CultureInfo> supportedCultures = new List<CultureInfo>();
            foreach (var item in supportLangs)
            {
                supportedCultures.Add(new CultureInfo(item));
            }
            return app.UseRequestLocalization(new RequestLocalizationOptions
            {
                DefaultRequestCulture = new RequestCulture(configuration.GetSection("SiteSetting:DefaultLanguage").Value),
                // Formatting numbers, dates, etc.
                SupportedCultures = supportedCultures,
                // UI strings that we have localized.
                SupportedUICultures = supportedCultures
            });
        }
    }
}

說明:

  • 指定了支持的語言列表:"zh-Hans","zh-Hant","en"
  • SetCurrentUICulture方法用於修改當前UI線程語言,例如一開始默認設定了中文,而後想切換成英文,就使用該方法
  • GetSpecificLanguageString方法是不切換UI線程語言的狀況下,獲取其餘語言的翻譯內容
  • AddMultiLanguages是封裝了多語言服務
    • 其中AddLocalization是給服務添加本地化
    • AddSingleton 是註冊獲取語言資源提供器爲單例,默認取的是SharedResource資源內容,因此後文在構造器中獲取IStringLocalizer,用它取到的語言都是從SharedResource語言資源文件中讀取到的
  • UseMultiLanguage方法是註冊多語言中間件,其中設定了支持的語言列表,根據網站配置設定了默認的語言

使用

註冊服務

MS.WebApi應用程序的Startup.cs類ConfigureServices中:

//添加多語言本地化支持
services.AddMultiLanguages();

services
    .AddControllers(options =>
    {
        options.Filters.Add<ApiResultFilter>();
        options.Filters.Add<ApiExceptionFilter>();
    })
    .AddDataAnnotationsLocalization(options =>
    {
        options.DataAnnotationLocalizerProvider = (type, factory) =>
        factory.Create(typeof(SharedResource));//給註解添加本地化資源提供器Localizerprovider
    });

說明:

  • services.AddMultiLanguages();是添加多語言本地化支持
  • services.AddControllers本來就有了,但要在後面追加AddDataAnnotationsLocalization方法,是給DataAnnotations本地化,依舊是註冊SharedResource語言文件,這樣一來,ViewModel中的註解也都能支持多語言了

MS.WebApi應用程序的Startup.cs類Configure中添加app.UseMultiLanguage(Configuration);

添加語言

以LoginViewModel中的翻譯爲例,我暫時添加了LoginViewModel字段註解上的簡體中文、繁體中文翻譯(英語未添加)

啓動項目,打開Postman,選取登錄接口進行測試: localhost:5000/account 、
接着修改接口地址爲: localhost:5000/account?culture=zh-hant 、
再繼續測試未添加的語言:localhost:5000/account?culture=en(圖中是us,打錯了,可是不影響結果)

說明:

  • 只維護了LoginViewModel註解上的簡中和繁中,未維護英文
  • 資源文件resx中,左邊是名稱,即獲取語言的代號,右邊是值,獲取對應的語言值,也就是意味着各個語言資源文件的名稱都是相同的,僅僅是值不同
  • 默認不附帶語言時,獲取到的是網站默認語言 簡體中文
  • 使用culture來指定須要顯示的語言
  • 使用culture=zh-hant來指定顯示 繁體中文,在維護了繁體中文時,則會顯示出來
  • 指定顯示爲英文時,因爲英文資源未維護,因此culture=us結果顯示依然是默認語言中文

至此,ViewModel數據註解已經實現了本地化

調用語言

繼續維護LoginViewModel中剩下的翻譯到簡體中文、繁體中文資源文件中:

使用IStringLocalizer來獲取語言資源
LoginValidate方法中添加參數IStringLocalizer localizer,而後使用GetString方法獲取對應的語言:

public async Task<ExecuteResult<UserData>> LoginValidate(IUnitOfWork<MSDbContext> unitOfWork, IMapper mapper, SiteSetting siteSetting, IStringLocalizer localizer)
{
    ExecuteResult<UserData> result = new ExecuteResult<UserData>();
    //將登陸用戶查出來
    var loginUserInDB = await unitOfWork.GetRepository<UserLogin>().FindAsync(Account);

    //用戶不存在
    if (loginUserInDB is null)
    {
        return result.SetFailMessage(localizer.GetString("DataAnnotations_ErrorMessage_NotExist", localizer.GetString("LoginViewModel_DisplayName_Account")));
    }

    //用戶被鎖定
    if (loginUserInDB.IsLocked &&
        loginUserInDB.LockedTime.HasValue &&
        (DateTime.Now - loginUserInDB.LockedTime.Value).Minutes < siteSetting.LoginLockedTimeout)
    {
        return result.SetFailMessage(localizer.GetString("LoginViewModel_ServiceError_UserLocked", siteSetting.LoginLockedTimeout));
    }

    //密碼正確
    if (Crypto.VerifyHashedPassword(loginUserInDB.HashedPassword, Password))
    {
        //密碼正確後才加載用戶信息、角色信息
        var userInDB = await unitOfWork.GetRepository<User>().GetFirstOrDefaultAsync(
            predicate: a => a.Id == loginUserInDB.UserId,
            include: source => source
             .Include(u => u.Role));

        //若是用戶已失效
        if (userInDB.StatusCode != StatusCode.Enable)
        {
            return result.SetFailMessage(localizer.GetString("LoginViewModel_ServiceError_UserDisabled"));
        }

        //用戶正常、密碼正確,更新相應字段
        loginUserInDB.IsLocked = false;
        loginUserInDB.AccessFailedCount = 0;
        loginUserInDB.LastLoginTime = DateTime.Now;
        //提交到數據庫
        await unitOfWork.SaveChangesAsync();

        //獲得userdata
        UserData userData = mapper.Map<UserData>(userInDB);
        return result.SetData(userData);
    }
    //密碼錯誤
    else
    {
        loginUserInDB.AccessFailedCount++;//失敗次數累加
        result.SetFailMessage(localizer.GetString("LoginViewModel_ServiceError_LoginError"));
        //超出失敗次數限制
        if (loginUserInDB.AccessFailedCount >= siteSetting.LoginFailedCountLimits)
        {
            loginUserInDB.IsLocked = true;
            loginUserInDB.LockedTime = DateTime.Now;
            result.SetFailMessage(localizer.GetString("LoginViewModel_ServiceError_UserLocked", siteSetting.LoginLockedTimeout));
        }
        //提交到數據庫
        await unitOfWork.SaveChangesAsync();
        return result;
    }
}

IStringLocalizer的使用方法如上所示。
LoginValidate方法是在AccountService中被調用的,因此AccountService中經過構造函數依賴注入的方式獲得IStringLocalizer localizer

啓動項目,打開Postman調用登錄接口,能夠看到登錄驗證的返回內容已經取得多語言:

維護多語言資源

安裝插件

這裏我推薦一個叫ResXManager的VS插件,用它管理多語言資源:

對着resx文件右擊使用ResXManager打開(打開的前提是文件內至少要有一條語言記錄):

打開後以下圖,多種語言都在一個頁面中顯示出來,選擇某一行能夠直接編輯:

導出爲Excel

選擇將全部資源導出,保存在本地

使用谷歌翻譯

打開Excel,將簡體中文那部分選擇,複製出來,粘貼到谷歌翻譯中:

選取好要翻譯的目標語言後,將翻譯後的內容複製回Excel:

最後在ResXManager插件中,把Excel文件導入回來:

能夠看到須要翻譯的資源都出來了:

說明:

  • 這個插件也支持調用api自動翻譯,可是須要api key,能夠自行研究下

多語言都維護好後,啓動項目,打開Postman調用登錄接口,能夠看到英文被正確翻譯了:

相關文章
相關標籤/搜索