(新年快樂)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文件:

這里,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文件:

圖片

這里的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的資源文件中。我們可以為每種語言創建一個資源文件,如下所示:

MyTexts.resx包含了默認的語言文本, MyTexts.tr.resx包含了土耳其語言的文本。當我們打開MyTexts.resx時,我們可以看到所有文本:

在這種情況下,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文件:

默認的(英語)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實現了一個 基於數據庫各租戶的本地化資源。 點擊查看文檔


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM