0.简介

如果你所开发的需要走向世界的话,那么肯定需要针对每一个用户进行不同的本地化处理,有可能你的客户在日本,需要使用日语作为显示文本,也有可能你的客户在美国,需要使用英语作为显示文本。如果你还是一样的写死错误信息,或者描述信息,那么就无法做到多语言适配。

Abp 框架本身提供了一套多语言机制来帮助我们实现本地化,基本思路是 Abp 本身维护一个键值对集合。只需要将展示给客户的文字信息处都使用一个语言 Key 来进行填充,当用户登录系统之后,会取得当前用户的区域文化信息进行文本渲染。

0.1 如何使用

我们首先来看一下如何定义一个多语言资源并使用。首先 Abp 自身支持三种类型的本地化资源来源,第一种是 XML 文件,第二种则是 JSON 文件,第三种则是内嵌资源文件,如果这三种都不能满足你的需求,你可以自行实现 ILocalizationSource 接口来返回多语言资源。

小提示:

Abp Zero 模块就提供了数据库持久化存储多语言资源的功能。

0.1.1 定义应用程序支持的语言

如果你需要为你的应用程序添加不同语言的支持,就必须在你任意模块的预加载方法当中添加语言来进行配置:

Configuration.Localization.Languages.Add(new LanguageInfo("en", "English", "famfamfam-flag-england", true));

Configuration.Localization.Languages.Add(new LanguageInfo("tr", "Türkçe", "famfamfam-flag-tr"));

例如以上代码,就能够让我们的程序拥有针对英语与土耳其语的多语言处理能力。

这里的 famfamfam-flag-england 与 famfamfam-flag-tr 是一个 CSS 类型,是 Abp 为前端展示所封装的小国旗图标。

0.1.2 建立多语言资源文件

有了语言之后,Abp 还需要你提供标准的多语言资源文件,这里我们以 自带的 XML 资源文件为例,其文件名称为 Abp-zh-Hans.xml ,路径为 Abp\Localization\Sources\AbpXmlSource。

SMTP主机

SMTP端口

用户名

密码

域名

使用SSL

使用默认验证

默认发件人邮箱地址

默认发件人名字

预设语言

接收通知

当前用户没有登录到系统!

时区

您没有权限进行此操作,您需要以下权限: {0}

您没有权限进行此操作,您至少需要下列权限的其中一项: {0}

主菜单

每个文件内部,会有一个 节点用于说明当前文件是针对于哪个区域适用的,而在其 内部则就是结合键值对的形式,name 里面的内容就是多语言文本项的键,在标签内部的就是其真正的值。

打开一个针对俄语国家的 XML 资源文件,文件名称叫做 Abp-ru.xml。

SMTP сервер

SMTP порт

Имя пользователя

Пароль

Домен

Использовать SSL

Использовать учетные данные по умолчанию

Электронный адрес отправителя по умолчанию

Имя отправителя по умолчанию

Язык по умолчанию

Получать уведомления

Текущий пользователь не вошёл в приложение!

可以看到 Key 值都是一样的,只是其 内部的值根据区域国家的不同值不一样而已。

其次从文件名我们就可以看到需要使用 XML 资源文件对于文件的命名格式会有一定要求,还是以 Abp 自带的资源文件为例,可以看一下他们基本上都是由 {SourceName}-{CultureInfo}.xml 这样构成的。

0.1.3 注册本地化的 XML 资源

那么如果我们需要注册之前的两个 XML 资源到 Abp 框架当中的话,则需要在预加载模块处通过如下代码来执行注册,并且需要右键 XML 文件,更改其构建操作为 内嵌资源。

Configuration.Localization.Sources.Add(

new DictionaryBasedLocalizationSource(

// 本地化资源名称

AbpConsts.LocalizationSourceName,

// 数据源提供者,这里使用的是 XML ,除了 XML 提供者,还有 JSON 等

new XmlEmbeddedFileLocalizationDictionaryProvider(

typeof(AbpKernelModule).GetAssembly(), "Abp.Localization.Sources.AbpXmlSource"

)));

0.1.4 获取多语言文本

如果你需要在某处获取指定 Key 所对应的具体显示文本,只需要注入 ILocalizationManager ,通过其 GetString() 方法就可以获得具体的值。如果你需要获取本地化资源的地方不能够使用依赖注入,你可以使用 LocalizationHelper 静态类来进行操作。

var @string = _localizationManager.GetString("Abp", "MainMenu");

它默认是从 Thread.CurrentThread.CurrentUICulture 获取到的当前区域信息,从而来取得某个 Key 所对应的显示值,而当前区域信息是由 Abp 注入的一系列 RequestCultureProviders 所提供的,他按照以下顺序来进行设置。

QueryStringRequestCultureProvider(ASP .NET Core 默认提供):该默认提供器使用的是 QueryString 的 culture&ui-culture 所提供的区域文化信息来初始化该值,例如:culture=es-MX&ui-culture=es-MX。

AbpUserRequestCultureProvider (Abp 提供):该提供器会读取当前用户的 IAbpSession 信息,并且从 ISettingManager 中获取用户所配置的 "Abp.Localization.DefaultLanguageName" 属性,将其作为默认的区域文化信息。

**AbpLocalizationHeaderRequestCultureProvider ** (Abp 提供):使用每次请求头当中的 .AspNetCore.Culture 值作为当前的区域文化信息,例如 c=en|uic=en-US。

CookieRequestCultureProvider (ASP .NET Core 提供):使用每次请求的 Cookie 当中 Key 为 .AspNetCore.Culture 值作为当前区域文化信息。

AbpDefaultRequestCultureProvider (Abp 提供):如果之前这些提供器都没有为当前区域文化赋值,则从 ISettingMananger 当中取得 Abp.Localization.DefaultLanguageName 的默认值。

AcceptLanguageHeaderRequestCultureProvider (ASP .NET Core 默认提供):该提供器最终会使用用户每次请求时传递的 Accept-Language 头部作为当前区域文化信息。

小提示:

这里 Abp 注入的提供器是有顺序的,注入这么多提供器就是为了最后确定当前用户的区域文化信息以便展示相应的语言文本。

1.启动流程

1.1 启动流程图

1.2 代码流程

根据使用方法我们可以得知,要配置 Abp 的多语言,必须得等 IAbpStartupConfiguration 初始化完毕才可以。即在 AbpBootstrapper 的 Initialize() 方法之中:

public virtual void Initialize()

{

// ... 其他代码

// 注入 IAbpStartupConfiguration 配置与本地化资源配置

IocManager.IocContainer.Install(new AbpCoreInstaller());

// ... 其他代码

// 初始化 AbpStartupConfiguration 类型

IocManager.Resolve().Initialize();

// ... 其他代码

}

配置类里面包含了用户所配置的所有语言与多语言资源信息,在被成功注入到 Ioc 容器之后,Abp 就开始使用本地化资源管理器来初始化这些多语言数据了。

public override void PostInitialize()

{

// 注册缺少的组件,防止遗漏注册组件

RegisterMissingComponents();

IocManager.Resolve().Initialize();

IocManager.Resolve().Initialize();

IocManager.Resolve().Initialize();

// 重点在这里,这个 PostInitialize 方法是存放在核心模块当中的,在这里调用了本地化资源管理器的初始化方法

IocManager.Resolve().Initialize();

IocManager.Resolve().Initialize();

IocManager.Resolve().Initialize();

if (Configuration.BackgroundJobs.IsJobExecutionEnabled)

{

var workerManager = IocManager.Resolve();

workerManager.Start();

workerManager.Add(IocManager.Resolve());

}

}

具体 LocalizationManager 及其内部的实现我们在下一节代码分析中详细进行讲述。

这些动作仅仅是在注入 Abp 框架的时候所需要执行的一些步骤,如果你要启用多语言,需要在 ASP .NET Core 程序的 Startup 类中的 Configure() 处通过更改 UseAbpRequestLocalization 状态为 True,才会将区域文化识别中间件注入到程序当中。

public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)

{

app.UseAbp(options =>

{

options.UseAbpRequestLocalization = false; //disable automatic adding of request localization

});

//...authentication middleware(s)

app.UseAbpRequestLocalization(); //manually add request localization

//...other middlewares

app.UseMvc(routes =>

{

//...

});

}

其实这里的 UseAbpRequestLocalization() 就已经将上文说的那些 RequestProvider 按照顺序依次注入到 MVC 之中了。

2.代码分析

Abp 框架针对本地化处理相关的类型与方法定义都存放在 Abp 库的 Localization 文件夹下。关系还是相对复杂的,这里我们先从其核心的 Abp 库针对于多语言的处理开始讲起。

2.1 多语言模块配置

Abp 需要使用的所有信息都是由用户在自己启动模块的 PreInitialize() 当中,通过 ILocalizationConfiguration 进行注入配置。也就是说在 ILocalizationConfiguration 内部,主要是包含了语言,与多语言资源提供者两种重点信息。

public interface ILocalizationConfiguration

{

// 当前应用程序可配置的语言列表

IList Languages { get; }

// 本地化资源列表

ILocalizationSourceList Sources { get; }

// 是否启用多语言(本地化) 系统

bool IsEnabled { get; set; }

// 以下四个布尔类型的参数主要用于确定当没有找到多语言文本时的处理逻辑,默认都为 True

bool ReturnGivenTextIfNotFound { get; set; }

bool WrapGivenTextIfNotFound { get; set; }

bool HumanizeTextIfNotFound { get; set; }

bool LogWarnMessageIfNotFound { get; set; }

}

2.2 语言信息

当前应用程序能够支持哪一些语言,取决于用户在预加载的时候给多语言模块配置对象分配了哪些语言。通过第 0.1.1 节我们看到用户可以直接通过初始化一个新的 LanguageInfo 对象,将其添加到 Languages 属性之中。

public class LanguageInfo

{

///

/// 区域文化代码名称

/// 应该是一个有效的区域文化代码名称,更多的可以通过 CultureInfo 静态类获得所有文化代码。

/// 例如: "en-US" 是北美适用的, "tr-TR" 适用于土耳其。

///

public string Name { get; set; }

///

/// 该语言默认应该展示的语言名称。

/// 例如: 英语应该展示为 "English", "zh-Hans" 应该展示为 "简体中文"

///

public string DisplayName { get; set; }

///

/// 用于展示的图标 CSS 类名,可选参数

///

public string Icon { get; set; }

///

/// 是否为默认语言

///

public bool IsDefault { get; set; }

///

/// 该语言是否被禁用

///

public bool IsDisabled { get; set; }

///

/// 语言的展示方式是自左向右还是自右向左

///

public bool IsRightToLeft

{

get

{

try

{

return CultureInfo.GetCultureInfo(Name).TextInfo?.IsRightToLeft ?? false;

}

catch

{

return false;

}

}

}

public LanguageInfo(string name, string displayName, string icon = null, bool isDefault = false, bool isDisabled = false)

{

Name = name;

DisplayName = displayName;

Icon = icon;

IsDefault = isDefault;

IsDisabled = isDisabled;

}

}

关于语言的定义还是相当简单的,主要参数就是语言的 区域文化代码 与 展示的名称,其余的都可以是可选参数。

小提示:

关于当前系统所支持的区域文化代码,可以通过执行 CultureInfo.GetCultures(CultureTypes.AllCultures); 得到。

2.3 语言管理器

Abp 针对语言也提供了一个管理器,接口叫做 ILanguageManager,定义简单,两个方法。

public interface ILanguageManager

{

// 获得当前语言

LanguageInfo CurrentLanguage { get; }

// 获得所有语言

IReadOnlyList GetLanguages();

}

实现也不复杂,它内部的实现就是从一个 ILanguageProvider 拿取有哪一些语言数据。

private readonly ILanguageProvider _languageProvider;

public IReadOnlyList GetLanguages()

{

return _languageProvider.GetLanguages();

}

// 获取当前语言,其实就是获取的 CultureInfo.CurrentUICulture.Name 的信息,然后去查询语言集合。

private LanguageInfo GetCurrentLanguage()

{

var languages = _languageProvider.GetLanguages();

// ... 省略了的代码

var currentCultureName = CultureInfo.CurrentUICulture.Name;

var currentLanguage = languages.FirstOrDefault(l => l.Name == currentCultureName);

if (currentLanguage != null)

{

return currentLanguage;

}

// ... 省略了的代码

return languages[0];

}

默认实现就是直接读取之前通过 Configuration 的 Languages 里面的数据。

在 Abp.Zero 模块还有两外一个实现,叫做 ApplicationLanguageProvider ,这个提供者则是从数据库表 ApplicationLanguage 获取的这些语言列表数据,并且这些语言信息还与租户有关,不同的租户他所能够获得到的语言数据也不一样。

public IReadOnlyList GetLanguages()

{

// 可以看到这里传入的当前登录用户的租户 Id,通过这个参数去查询的语言表数据

var languageInfos = AsyncHelper.RunSync(() => _applicationLanguageManager.GetLanguagesAsync(AbpSession.TenantId))

.OrderBy(l => l.DisplayName)

.Select(l => l.ToLanguageInfo())

.ToList();

SetDefaultLanguage(languageInfos);

return languageInfos;

}

2.4 本地化资源

2.4.1 本地化资源列表

在多语言模块配置内部使用的是 ILocalizationSourceList 类型的一个 Sources 属性,该类型其实就是继承自 IList 的一个具体实现而已,一个类型为 ILocalizationSource 的集合,不过其扩展了一个

Extensions 属性用于存放扩展的多语言数据字段。

2.4.2 本地化资源

其接口定义为 ILocalizationSource ,Abp 默认为我们实现了四种本地化资源的实现。

第一个是空实现,可以跳过,第二个则是针对资源文件进行读取的的本地化资源,第三个是基于字典的的本地化资源定义,最后一个是由 Abp Zero 模块所提供的数据库版本的多语言资源定义。

首先看一下该接口的定义:

public interface ILocalizationSource

{

// 本地化资源唯一的名称

string Name { get; }

// 用于初始化本地化资源,在 Abp 框架初始化的时候被调用

void Initialize(ILocalizationConfiguration configuration, IIocResolver iocResolver);

// 从当前本地化资源中获取给定关键字的多语言文本项,为用户当前语言

string GetString(string name);

// 从当前本地化资源中获取给定关键字与区域文化的多语言文本项

string GetString(string name, CultureInfo culture);

// 作用同上,只不过不存在会返回 NULL

string GetStringOrNull(string name, bool tryDefaults = true);

// 作用同上,只不过不存在会返回 NULL

string GetStringOrNull(string name, CultureInfo culture, bool tryDefaults = true);

// 获得当前语言所有的多语言文本项集合

IReadOnlyList GetAllStrings(bool includeDefaults = true);

// 获得给定区域文化的所有多语言文本项集合

IReadOnlyList GetAllStrings(CultureInfo culture, bool includeDefaults = true);

}

也就可以这么来看,我们有几套本地化资源,他们通过 Name 来进行标识,如果你需要在本地化管理器获取某一套本地化资源,那么你可以直接通过 Name 来进行定位。而每一套本地化资源,自身都拥有具体的多语言数据,这些多语言数据有可能来自文件也有可能来自数据库,这取决于你具体的实现。

2.4.3 基于字典的本地化资源

最开始我们在使用范例当中,通过 DictionaryBasedLocalizationSource 来建立我们的本地化资源对象。该对象实现了 ILocalizationSource 与 IDictionaryBasedLocalizationSource 接口,内部定义了一个本地化资源字典提供器。

当调用本地化资源的 Initialize() 方法的时候,会使用具体的本地化资源字典提供器来获取数据,而这个字典提供器可以为 XmlFileLocalizationDictionaryProvider、JsonEmbeddedFileLocalizationDictionaryProvider 等。

这些内部字典提供器在初始化的时候,会将自身的数据按照 语言/多语言项 的形式将多语言信息存放在一个字典之中,而这个字典又可以分为 XML、JSON 等等等等...

// 内部字典提供器

public interface ILocalizationDictionaryProvider

{

// 语言/多语言项字典

IDictionary Dictionaries { get; }

// 本地化资源初始化时被调用

void Initialize(string sourceName);

}

而这里的 ILocalizationDictionary 其实就是一个键值对,键关联的是多语言项的标识 KEY,例如 "Home",而 Value 就是具体的展示文本信息了。

而是用字典本地化资源对象获取数据的时候,其实也就是从其内部的字典提供器来获取数据。

例如本地化资源有一个 GetString() 方法,它内部拥有一个字典提供器 DictionaryProvider,我要获取某个 KEY 为 "Home" 所需要经过的步骤如下。

public ILocalizationDictionaryProvider DictionaryProvider { get; }

public string GetString(string name)

{

// 获取当前用户区域文化,标识为 "Home" 的展示文本

return GetString(name, CultureInfo.CurrentUICulture);

}

public string GetString(string name, CultureInfo culture)

{

// 获取值

var value = GetStringOrNull(name, culture);

// 判断值为空的话,根据配置的要求是否抛出异常

if (value == null)

{

return ReturnGivenNameOrThrowException(name, culture);

}

return value;

}

// 获得 KEY 关联的文本

public string GetStringOrNull(string name, CultureInfo culture, bool tryDefaults = true)

{

var cultureName = culture.Name;

var dictionaries = DictionaryProvider.Dictionaries;

// 在这里就开始从初始化所加载完成的语言字典里面,获取具体的多语言项字典

ILocalizationDictionary originalDictionary;

if (dictionaries.TryGetValue(cultureName, out originalDictionary))

{

// 多语言项字典拿取具体的多语言文本值

var strOriginal = originalDictionary.GetOrNull(name);

if (strOriginal != null)

{

return strOriginal.Value;

}

}

if (!tryDefaults)

{

return null;

}

//Try to get from same language dictionary (without country code)

if (cultureName.Contains("-")) //Example: "tr-TR" (length=5)

{

ILocalizationDictionary langDictionary;

if (dictionaries.TryGetValue(GetBaseCultureName(cultureName), out langDictionary))

{

var strLang = langDictionary.GetOrNull(name);

if (strLang != null)

{

return strLang.Value;

}

}

}

//Try to get from default language

var defaultDictionary = DictionaryProvider.DefaultDictionary;

if (defaultDictionary == null)

{

return null;

}

var strDefault = defaultDictionary.GetOrNull(name);

if (strDefault == null)

{

return null;

}

return strDefault.Value;

}

2.3.4 基于数据库的本地化资源

如果你有集成 Abp.Zero 模块的话,可以通过在启动模块的预加载方法编写以下代码启用 Zero 的多语言机制。

Configuration.Modules.Zero().LanguageManagement.EnableDbLocalization();

Abp.Zero 针对原有的本地化资源进行了扩展,新增的本地化资源类叫做 MultiTenantLocalizationSource,该类同语言管理器一样,是一个基于多租户实现的本地化资源,内部字典的值是从数据库当中获取的,其大体逻辑与字典本地化资源一样,都是内部维护有一个字典提供器。

在通过 EnableDbLocalization() 方法的时候就直接替换掉了 ILanguageProvider 的默认实现,并且在配置的 Sources 源里面也增加了 MultiTenantLocalizationSource 作为一个本地化资源。

2.5 本地化资源管理器

扯了这么多,让我们来看一下最为核心的 ILocalizationManager 接口,如果我们需要获取某个数据源的某个 Key 所对应的多语言值肯定是要注入这个本地化资源管理器来进行操作的。

public interface ILocalizationManager

{

// 根据名称获得本地化数据源

ILocalizationSource GetSource(string name);

// 获取所有的本地化数据源

IReadOnlyList GetAllSources();

}

这里的数据源标识的就是一个命名空间的作用,比如我在 A 模块当中有一个 Key 为 "Home" 的多语言项,在 B 模块也有一个 Key 为 "Home" 的多语言项,这个时候就可以用数据源标识来区分这两个 "Home" 。

本地化资源管理器通过在初始化的时候调用其 Initialize() 来初始化所有被注入的本地化资源,最后并将其放在一个字典之中,以便后续使用。

private readonly IDictionary _sources;

foreach (var source in _configuration.Sources)

{

// ... 其他代码

_sources[source.Name] = source;

source.Initialize(_configuration, _iocResolver);

// ... 其他代码

}

3.结语

针对 Abp 的多语言处理本篇文章不太适合作为入门了解,其中大部分知识需要结合 Abp 源码进行阅读才能够加深理解,此文仅作抛砖引玉之用,如有任何意见或建议欢迎大家在评论当中指出。

mysql 多语言处理_[Abp 源码分析]十三、多语言(本地化)处理相关推荐

  1. 【转】ABP源码分析十二:本地化

    本文逐个分析ABP中涉及到localization的接口和类,以及他们之间的关系.本地化主要涉及两个方面:一个是语言(Language)的管理,这部分相对简单.另一个是语言对应得本地化资源(Local ...

  2. 【转】ABP源码分析十三:缓存Cache实现

    ABP中有两种cache的实现方式:MemoryCache 和 RedisCache. 如下图,两者都继承自ICache接口(准确说是CacheBase抽象类).ABP核心模块封装了MemoryCac ...

  3. [Abp 源码分析]ASP.NET Core 集成

    点击上方蓝字关注我们 0. 简介 整个 Abp 框架最为核心的除了 Abp 库之外,其次就是 Abp.AspNetCore 库了.虽然 Abp 本身是可以用于控制台程序的,不过那样的话 Abp 就基本 ...

  4. 【转】ABP源码分析一:整体项目结构及目录

    ABP是一套非常优秀的web应用程序架构,适合用来搭建集中式架构的web应用程序. 整个Abp的Infrastructure是以Abp这个package为核心模块(core)+15个模块(module ...

  5. ABP源码分析三十四:ABP.Web.Mvc

    ABP.Web.Mvc模块主要完成两个任务: 第一,通过自定义的AbpController抽象基类封装ABP核心模块中的功能,以便利的方式提供给我们创建controller使用. 第二,一些常见的基础 ...

  6. 【转】ABP源码分析四十三:ZERO的本地化

    ABP Zero模块扩展了ABP基础框架中的本地化功能,实现了通过数据库对本地化功能进行管理.其通过数据库保存本地化语言及其资源. ApplicationLanguage:代表本地化语言的实体类.一种 ...

  7. 【转】ABP源码分析四十四:ZERO的配置

    ABP Zero模块中需要配置的地方主要集中在三块:1.配置静态的role:2.配置外部认证源:3.配置本地化语言和资源. UserManagementConfig/IUserManagementCo ...

  8. 【转】ABP源码分析三十四:ABP.Web.Mvc

    ABP.Web.Mvc模块主要完成两个任务: 第一,通过自定义的AbpController抽象基类封装ABP核心模块中的功能,以便利的方式提供给我们创建controller使用. 第二,一些常见的基础 ...

  9. 【转】ABP源码分析四:Configuration

    核心模块的配置 Configuration是ABP中设计比较巧妙的地方.其通过AbpStartupConfiguration,Castle的依赖注入,Dictionary对象和扩展方法很巧妙的实现了配 ...

最新文章

  1. 理解I/O Completion Port(完成端口)
  2. 特斯拉车主「作死」成真:炫耀「主驾无人」,自动驾驶导致车毁人亡
  3. 学习编程的25个“坑”,你踩到了吗?
  4. Android+Java中使用Aes对称加密的工具类与使用
  5. 编译Qt“NMAKE:fatal error U1077”错误的解决方法
  6. io python_python学习笔记 - StringIO以及BytesIO
  7. 不同网段互PING,对不?
  8. 提取网页里面全部所有链接的方法
  9. HDU - 3364 Lanterns(高斯消元解方程(取模))
  10. Shell替换:Shell变量替换,命令替换,转义字符
  11. 调试.NET CORE代码
  12. 软件设计的火花——Brooks新作及《人月神话》三十五周年讨论会纪要
  13. CISSP的成长之路(十六):复习访问控制(1)
  14. Java对象表示方式1:序列化、反序列化和 transient 关键字的作用
  15. MongoDB学习day10--数据库导入导出
  16. MyEclipse服务器远程调试
  17. 雄迈摄像头ffmpeg转码推送至websocket
  18. 学习.NET好书推荐
  19. Mac下用android studio创建安卓模拟器
  20. 青龙面板基本脚本运行必装依赖 一键式安装脚本安装依赖 2023年3月28日

热门文章

  1. 【ChatGPT】大语言模型排行榜:Vicuna 夺冠,清华 ChatGLM 进前五
  2. html 浏览器存储方式
  3. realsenseD435i ros auto_exposure设置
  4. select下拉框事件操作
  5. 重载运算符operater用法
  6. jQuery实例:图片展示效果
  7. 【机器学习实验五】逻辑斯蒂回归
  8. IPC(IP CAMERA)
  9. 线性表两种存储结构的不同特点及其使用场合
  10. 自顶向下 谢希仁计算机网络第七版课后答案