(新年快乐)ABP理论学习之本地化(2016第一篇)

返回总目录


本篇目录

  • 应用语言
  • 本地化资源
  • 获取本地化文本
  • 扩展本地化资源
  • 最佳实践

应用语言

一个应用至少有一种UI语言,许多应用不止有一种语言。ABP为应用提供了一个灵活的本地化系统。

第一件事情就是声明支持哪些语言。这个是在模块的PreInitialize方法中完成的,如下所示:

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"));

在服务端,可以先注入ILocalizationManager,然后使用它。在客户端,可以使用 abp.localizationJavascript API来获得所有可使用的语言和当前的语言。famfamfam-flag-england (和tr)只是一个Css类而已,你可以根据自己的需要改变它。然后在UI上使用它来展示相关的旗帜(比如各种国旗)。

ABP模板使用了本地化系统给用户呈现的是一个切换语言的下拉列表。你可以创建一个模板项目然后学习一下源代码。

本地化资源

本地化文本可以存储在不同的资源中。甚至你可以在相同的应用中使用不止一种语言(如果你有不止一个模块,每个模块可以定义一个单独的本地化资源)。应该为本地化资源实现ILocalizationSource接口,然后将它注册到ABP的本地化配置中。

每个本地化资源必须有一个唯一的资源名XML文件资源文件是预定义的本地化资源类型。

XML文件

本地化文本可以存储在XML文件中,XML文件的内容就像下面展示的那样:

<?xml version="1.0" encoding="utf-8" ?>
<localizationDictionary culture="en">
  <texts>
    <text name="TaskSystem" value="Task System" />
    <text name="TaskList" value="Task List" />
    <text name="NewTask" value="New Task" />
    <text name="Xtasks" value="{0} tasks" />
    <text name="CompletedTasks" value="Completed tasks" />
    <text name="EmailWelcomeMessage">Hi,
Welcome to Simple Task System! This is a sample
email content.</text>
  </texts>
</localizationDictionary>

XML文件必须是utf-8编码, culture="en"声明该XML文件包含了英语文本。对于文本节点,name特性用于标识一个文本。你可以使用 value特性或者 inner text(如上面的最后一个)给本地化文本赋值。如下所示,我们为每种语言创建了一个单独的XML文件:

(新年快乐)ABP理论学习之本地化(2016第一篇)

这里,SimpleTaskSystem资源名称,SimpleTaskSystem定义了 默认的语言。当请求一个文本时,ABP会从当前语言的XML文件中获取文本(使用Thread.CurrentThread.CurrentUICulture找到当前的语言)。如果不存在当前语言的文本,就会从默认语言的XML文件中获得文本。

注册XML本地化资源

XML文件可以存储在文件系统中或者可以内嵌在一个程序集中。

对于文件系统存储的XML,我们可以注册一个XML本地化资源,如下所示:

Configuration.Localization.Sources.Add(
    new DictionaryBasedLocalizationSource(
        "SimpleTaskSystem",
        new XmlFileLocalizationDictionaryProvider(
            HttpContext.Current.Server.MapPath("~/Localization/SimpleTaskSystem")
            )
        )
    );

这个是在模块的PreInitialize事件中完成的(看模块系统获取更多信息)。ABP会找到所有给定目录的XML文件并注册这些本地化资源。

对于内嵌的XML文件,我们应该将所有的本地化XML文件标记为内嵌的资源(选中xml文件,打开属性窗口,将生成操作的值改为‘内嵌的资源’)。然后像下面那样注册该本地化资源:

Configuration.Localization.Sources.Add(
    new DictionaryBasedLocalizationSource(
        "SimpleTaskSystem",
        new XmlEmbeddedFileLocalizationDictionaryProvider(
            Assembly.GetExecutingAssembly(),
            "MyCompany.MyProject.Localization.Sources"
            )
        )
    );

XmlEmbeddedFileLocalizationDictionaryProvider会获得包含XML文件的程序集(GetExecutingAssembly简单地指向当前的程序集)和XML文件的 命名空间(程序集名称+xml文件的文件夹层次)。

注意:当给内嵌的XML文件起名字时,要加上语言后缀,但是不要使用“.”,比如“MySource.ch.xml”,而要使用短号“-”,比如“MySource-en.xml”。因为当寻找资源时,“.”会造成问题。

JSON文件

JSON文件可以用于存储本地化资源的文本。JSON本地化文件的一个简单样例如下所示:

{
  "culture": "en",
  "texts": {
    "TaskSystem": "Task system",
    "Xtasks": "{0} tasks"
  }
}

JSON文件的格式应该是unicode(utf-8)格式。"culture":"en"表示该Json文件包含了英文文本。如下所示,我们为每种语言创建了一个单独的Json文件:

(新年快乐)ABP理论学习之本地化(2016第一篇)

这里的MySourceName是资源名称, MySourceName.json定义了 默认的语言,json文件和XML文件很相似。

注册JSON本地化资源
Json文件可以存储在文件系统中,也可以 内嵌到一个程序集中。
对于文件系统存储JSON,我们可以注册一个Json本地化资源,如下所示:

Configuration.Localization.Sources.Add(
    new DictionaryBasedLocalizationSource(
        "MySourceName",
        new JsonFileLocalizationDictionaryProvider(
            HttpContext.Current.Server.MapPath("~/Localization/MySourceName")
            )
        )
    );

这是在模块的PerInitialize事件中完成的。ABP会在给定的目录中寻找所有的Json文件,并注册到本地化资源。

对于内嵌的Json文件,首先我们应该将所有的本地化Json文件标记为 内嵌的资源(选中Json文件,然后打开属性窗口,更改生成操作为内嵌的资源即可)。然后我们就可以像下面那样注册本地化资源了:

 Configuration.Localization.Sources.Add(
    new DictionaryBasedLocalizationSource(
        "MySourceName",
        new JsonEmbeddedFileLocalizationDictionaryProvider(
            Assembly.GetExecutingAssembly(),
            "MyCompany.MyProject.Localization.Sources"
            )
        )
    );

JsonEmbeddedFileLocalizationDictionaryProvider需要一个包含Json文件的程序集(GetExecutingAssembly指向当前的程序集)和Json文件的命名空间(命名空间是通过程序集名称+json文件的文件夹层次计算出来的)。

注意:当我们给json文件添加了语言后缀时,不要使用“.”标记符号,如“MySource.tr.json”,而要使用分隔符如“MySource-tr.json”,因为“.”标记符号在寻找资源时可能会发生问题。

资源文件

本地化文本也可以存储在.NET的资源文件中。我们可以为每种语言创建一个资源文件,如下所示:

(新年快乐)ABP理论学习之本地化(2016第一篇)

MyTexts.resx包含了默认的语言文本, MyTexts.tr.resx包含了土耳其语言的文本。当我们打开MyTexts.resx时,我们可以看到所有文本:

(新年快乐)ABP理论学习之本地化(2016第一篇)

在这种情况下,ABP使用了.NET中内置的本地化资源管理者。你应该为该资源配置一个本地化资源:

Configuration.Localization.Sources.Add(
    new ResourceFileLocalizationSource(
        "MySource",
        MyTexts.ResourceManager
        ));

这里,MySource是资源的 唯一名字,而且 MyTexts.ResourceManager是获取本地化文本的资源管理者的引用。这个是在模块的 Initialize事件中完成的。

自定义资源

自定义本地化资源实现了在不同的资源中(比如数据库)存储文本。你可以直接实现ILocalizationSource接口或者从DictionaryBasedLocalizationSource类中派生可以使得实现更容易。

获取本地化文本

当创建了资源并把它注册到ABP的本地化系统之后,文本就能轻易地本地化了。

服务端

在服务端,我们可以注入ILocalizationManager,然后使用它的 GetString方法。

var s1 = _localizationManager.GetString("SimpleTaskSystem", "NewTask");

GetString方法会基于当前线程的UI文化(culture)获取字符串。如果没有找到,就会返回 默认语言对应的字符串。如果任何地方都没有定义该字符串,就会默认返回使用 "[]" 包装的 给定字符串(而不是抛出异常)。这种行为是可以配置的(你可以在模块的PreInitialize中使用Configuration.Loacalization.ReturnGivenTextIfNotFound属性进行配置)。

记得不要重复资源的名字(运行时会报错),你可以首先获得该资源,然后从该资源中获得字符串:

var source = _localizationManager.GetSource("SimpleTaskSystem");
var s1 = source.GetString("NewTask");

这会返回当前语言的文本。GetString方法也有重载方法通过参数获取不同语言和格式的文本。

如果我们不能注入ILocalizationManager(也许在一个不能到达依赖注入系统的静态上下文中),那么可以简单地使用LocalizationHelper静态类。但是尽可能地注入并使用ILocalizationManager,因为LocalizationHelper是静态的而且静态不好测试(对于写单元测试的人来说)。

如果你需要在应用服务、MVC控制器、Razor视图或者其他派生自AbpServiceBase的类中本地化,那么可以使用快捷的 L方法。

在MVC控制器中

一般在MVC控制器和视图中需要本地化文本。这里有一个快捷方式,如下所示:

public class HomeController : SimpleTaskSystemControllerBase
{
    public ActionResult Index()
    {
        var helloWorldText = L("HelloWorld");
        return View();
    }
}

L方法用于本地化一个字符串。当然,你必须提供一个资源名,这里的HelloWorld就是从资源中找到的。它是在控制器基类SimpleTaskSystemControllerBase(以ControllerBase为后缀的控制器)中完成的,如下所示:

public abstract class SimpleTaskSystemControllerBase : AbpController
{
    protected SimpleTaskSystemControllerBase()
    {
        LocalizationSourceName = "SimpleTaskSystem";
    }
}

注意L派生自AbpController。因此,你可以使用 L方法轻松地本地化文本。

在MVC视图中

在视图中也可以使用相同的L方法:

<div>
    <form id="NewTaskForm" role="form">
        <div class="form-group">
            <label for="TaskDescription">@L("TaskDescription")</label>
            <textarea id="TaskDescription" data-bind="value: task.description" class="form-control" rows="3" placeholder="@L("EnterDescriptionHere")" required></textarea>
        </div>
        <div class="form-group">
            <label for="TaskAssignedPerson">@L("AssignTo")</label>
            <select id="TaskAssignedPerson" data-bind="options: people, optionsText: 'name', optionsValue: 'id', value: task.assignedPersonId, optionsCaption: '@L("SelectPerson")'" class="form-control"></select>
        </div>
        <button data-bind="click: saveTask" type="submit" class="btn btn-primary">@L("CreateTheTask")</button>
    </form>
</div>

为了能够这样使用,你应该让你的视图派生自设置了资源名的一个基类:

public abstract class SimpleTaskSystemWebViewPageBase : SimpleTaskSystemWebViewPageBase<dynamic>
{

}

public abstract class SimpleTaskSystemWebViewPageBase<TModel> : AbpWebViewPage<TModel>
{
    protected SimpleTaskSystemWebViewPageBase()
    {
        LocalizationSourceName = "SimpleTaskSystem";
    }
}

而且要在web.config中设置这个视图基类:

<pages pageBaseType="SimpleTaskSystem.Web.Views.SimpleTaskSystemWebViewPageBase">

当你从ABP模板创建解决方案时,对于视图和控制器的所有这些都已经准备好了。

客户端

ABP也使得在javascript代码中使用相同的本地化文本成为了可能。首先,你应该将动态的ABP脚本添加到页面中:

<script src="/AbpScripts/GetScripts" type="text/javascript"></script>

ABP在客户端会自动生成需要的javascript代码来获得本地化的文本。然后,你就可以轻松地使用javascript获得一个本地化的文本,如下所示:

var s1 = abp.localization.localize('NewTask', 'SimpleTaskSystem');

这里,NewTask是文本名,SimpleTaskSystem是资源名。记住不要重复资源名。你也可以先获得资源名,然后获得文本:

var source = abp.localization.getSource('SimpleTaskSystem');
var s1 = source('NewTask');

格式化参数

本地化方法也可以有额外的格式参数,例子:

abp.localization.localize('RoleDeleteWarningMessage', 'MySource', 'Admin');

//如果使用上面的getSource找到了资源,就使用source作为快捷方式
source('RoleDeleteWarningMessage', 'Admin');

如果RoleDeleteWarningMessage = 'Role {0} will be deleted', 那么本地化后的文本就是'Role Admin will be deleted'。

默认的本地化资源
可以设置默认的本地化资源,然后就能够不使用资源名称也可以使用abp.localization.localize方法了。

abp.localization.defaultSourceName = 'SimpleTaskSystem';
var s1 = abp.localization.localize('NewTask');

defaultSourceName是全局的,并且一次只能用于一个资源名称。

扩展本地化资源

假设我们已经有一个定义了自己本地化资源的模块。我们可能需要更改它的本地化文本,或者添加一些新的文本或者翻译为其他的语言。如何解决这个呢?ABP允许扩展一个本地化资源,当前只对XML文件有效(实际上任何本地化资源都实现了IDictionaryBasedLocalizationSource接口)。

ABP也定义了一些本地化资源。比如,Abp.Webnuget包定义了一个叫做 AbpWeb的本地化资源作为内嵌XML文件:

(新年快乐)ABP理论学习之本地化(2016第一篇)

默认的(英语)XML文件像下面这个样子(这里只展示一部分文本):

<?xml version="1.0" encoding="utf-8" ?>
<localizationDictionary culture="en">
  <texts>
    <text name="InternalServerError" value="An internal error occurred during your request!" />
    <text name="ValidationError" value="Your request is not valid!" />
    ...
  </texts>
</localizationDictionary>

要扩展AbpWeb资源,我们可以定义XML文件。假设我们只想更改InternalServerError文本,我们就可以像下面那样定义一个XML文件:

<?xml version="1.0" encoding="utf-8" ?>
<localizationDictionary culture="en">
  <texts>
    <text name="InternalServerError" value="Sorry :( It seems there is a problem. Let us to solve it and please try again later." />
  </texts>
</localizationDictionary>

然后我们在模块的PreInitialize方法中注册它:

Configuration.Localization.Sources.Extensions.Add(
    new LocalizationSourceExtensionInfo("AbpWeb",
        new XmlFileLocalizationDictionaryProvider(
            HttpContext.Current.Server.MapPath("~/Localization/AbpWebExtensions")
            )
        )
    );

如果我们想创建内嵌的资源XML文件(上面有讲),那么可以使用XmlEmbeddedFileLocalizationDictionaryProvider。ABP使用XML文件合并了基本的本地化资源。我们也可以添加新的语言文件。

注意:我们可以使用Json文件来扩展XMl文件,反过来也一样。

结论

ABP提供了使用不同资源进行本地化的功能,也提供了在服务端和客户端代码中使用相同的本地化文本的基础设施。

XML文件,Json文件和资源文件都有自己的长处和弱势,我们建议使用XML文件或者Json文件代替资源文件。因为:

  • XML/JSON文件更容易编辑,扩展或者移植。
  • 当获取本地化文本时,XML/JSON文件要求string类型的key,而不是像资源文件需要编译时的属性。这个可以被认为是缺点,但是以后更改资源时相对容易一些。甚至我们将本地化不需要更改就可以移动到数据库或者使用了该本地化资源的代码中了(Module-zero已经实现了创建一个基于数据库和每个租户的本地化资源,查看《Module Zero之语言管理》)。

此外,如果你使用XML或者Json,建议不要按照name进行排序文本,而要按照创建日期进行排序。这样,当别人要将文本翻译为其他语言时,ta就可以轻松地看到哪一个文本是新添加的。

通过实现ILocalizationSource接口,你也可以创建你自己的本地化资源并集成到ABP中。 Module-zero实现了一个 基于数据库各租户的本地化资源。 点击查看文档

更多相关文章
  • ABP理论学习之JavascriptAPI(理论完结篇)
    返回总目录 本篇目录 Ajax Notification Message UI block和busy 事件总线 Logging 其他工具功能 说在前面的话 不知不觉,我们送走了2015,同时迎来了2016.我相信,每一个人,都会在过去的一年有所失,但我更相信,我们所得到的更多.过去的就过去了,不要再 ...
  • 返回总目录 为了和2016年春节赛跑,完成该系列博客,我牺牲了今天中午的时间来完成该系列的第一篇----开篇介绍.开篇介绍嘛,读过大学教材的同学都知道,这玩意总是那么无聊,跟考试没关系,干脆直接跳过,呵呵,多么美好的大学时光啊!不过,现在想想,开篇介绍确实不怎么重要,它只是告诉我们今后我要开始讲什么 ...
  • 第一篇博文
      2016年1月29日我的第一篇博文算正式开通!!!!今后不定期更新中~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  • Linux系统详解第一篇:Linux简介
    Linux系统详解第一篇:Linux简介 导语:注意:本文章取材广泛,有来自于互联网的,有来自教科书的,有来自自己的笔记的,经过长时间的整理并多次修改后发布.本文章的目的是为了让刚入门Linux的读者对Linux的发展历史和发行版本有个很好的了解.如果发现本文章有什么疏漏的地方前指出,让我们共同努力 ...
  • 今天决定开始逐步记录总结自己的学习之路,本Blog主要做读书随笔类的一些文字,思考,总结自己的一些心得:另在CSDN上的为技术博客,作为一名不合格,不入流的菜鸟程序员,希望自己能够通过不断的努力,每天的小进步,一步步从小菜鸟演变成大菜鸟. 读书: 胡适先生曾提倡“多研究些问题,少谈些主义”,在193 ...
  • hadoop作业分片处理以及任务本地性分析源码分析第一篇
    (一) Map输入数据块的切分算法(基于hadoop源码 1.0.1):  (1)分片算法    MapTask的个数据主要取决于InputFormat通过对输入数据调用getSplit()方法分割为若干个分片数据,即InputSplit数.hadoop中切片大小主要由以下几个因素:blockSiz ...
  • Python3中级玩家:淘宝天猫商品搜索爬虫自动化工具第一篇
    Python3中级玩家:淘宝天猫商品搜索爬虫自动化工具(第一篇) 由于Github 打包的exe某些文件上传被.gitignore了,所以欢迎从这里下载工具:http://pan.baidu.com/s/1bpHPjDt 一.前言 大家好,今天我要来讲讲一个比较实用的爬虫工具,抓取淘宝的关键字商品信 ...
  • 2016年01月14日,第一篇博客,我暂时在北京工作,做MySQL DBA,水平一般,目前正在跟吴炳锡和叶金荣两位MySQL高人学习. 请大家多来我这里哦,哈哈哈.
一周排行
  • zabbix部署server与agent安装
    安装编译环境yum -y install *gcc*安装LAMPyum install - ...
  • Windows8企业部署系列一
                                      Windows 8 企 ...
  • 前面的一篇文章简要说了一下linux网卡的ip地址结构,可是写完之后觉得说的不够彻底,我一般写文章喜欢从大的方面着手而不喜欢钻到细节中,因为细节往往容易把握,而大体的设计往往对于我们这些学习的人是不可见的,比如lin ...
  • while  循环:while CONDITION :do  statement1  ...doneuntil CONDITION:do statement1 ...done互动添加一个用户的shell脚本: ech ...
  • firefox 更新到44或45,发现原来的更改快捷键的扩展没了!!! 最近刚找到个比较好的
  •     仅可使用客户端集成插件部署 vCenter Server Appliance:该插件是一个适用于 Windows 的 HTML 安装程序,您可以用它直接连接到 ESXi 5.1.x.ESXi 5.5.x 或 ...
  • 量词的局限l 量词只能规定之前字符或字符组的出现次数l 如果要规定一个字符串的出现次数,必须使用括号(),在括号内填写字符串,在闭括号之后添加量词例子:publicclass GeneralThree {public ...
  • 百度搜索网站登录口:http://www.baidu.com/search/url_submit.html 百度单个网页提交入口:http://zhanzhang.baidu.com/sitesubmit 360搜索 ...
  • 如何做好团队非技术建设 [IT168 技术]这次谈谈的是如何做好团队建设.这其实是个很大范畴的,但这次主要想谈下团队建设中的非技术因素,以及分析下在团队建设中,特别重要的成员的心理建设方面的问题.      首先,要 ...
  • using System;using System.Collections.Generic;using System.Linq;using System.Text;using System.Threading.Tas ...