SAPUI5 (21) - 如何实现多语言

在 SAPUI5 中,通过两种方法来实现多语言,一是 SAPUI5 提供 Resource Model,Resource Model 读取资源包 (Resource Bundle) 并与 View 中的控件绑定。第二种方法是使用 jQuery.sap.resources 相关的 API 读取资源包。两种方法都需要资源包文件并且在配置中设置。

先介绍两个知识点:语言代码和资源包文件。

语言代码

OpenUI5 对语言使用字符串标识,遵循 BCP-47 标准。比如 en 表示英语,en-US 表示美国英语。zh-Hans 表示中文简体,zh-Hant 表示中文繁体。

SAP ABAP 使用的是另外一套专门的编码,比如 ZH 表示中文简体,ZF 表示中文繁体。OpenUI5 能识别 ABAP 的编码并转换成 BCP-47 编码。

OpenUI5 对页面的显示,有一个 当前语言( Current Language ) 的概念,按照当前语言,读取相应的资源包文件,按当前语言显示。那么,当前语言如何确定呢?OpenUI5 按照如下顺序顺序(从高到低),如果都没有找到,最后读取通用设置(比如 i18n.properties)。

  1. URL中的 locale 参数(即在 url 后面加上 ?sap-ui-language=xx )
  2. 应用程序代码的 locale 设置,比如:
sap.ui.getCore().getConfiguration().applySettings({
    language: 'de',
    calendarType: sap.ui.core.CalendarType.Gregorian,
    formatSettings: {
        legacyDateFormat: '1',
        legacyTimeFormat: '1',
        legacyNumberFormat: '1'
    }
});
  1. Android 平台的用户代理字符串设置
  2. 浏览器的一般语言设置,可以用 window.navigator.language 查看
  3. 浏览器中用户语言配置。这个与浏览器相关,比如 IE 通过 window.navigator.userLanguage 查看。
  4. 浏览器语言配置。这个业余浏览器相关,比如 IE 通过 window.navigator.browserLanguage 查看
  5. OpenUI5中硬编码,默认为 en

资源包文件

资源包文件就是 Java 的属性文件( Properties 文件)。文件中包含与语言相关的文本。资源包文件的特征:

  1. 文件的扩展名总是 .properties
  2. 文件名包括固定部分和语言相关部分。比如在 OpenUI5 中,大家习惯将 Resource Bundle 的文件名叫做 i18n(来源于 internationalization 这个单词,取首位两个字母,中间字母数为18)。那么 i18n.properties 是默认的文件(如果没有其他文件,就用这个文件作为默认设置),i18n_zh_CN.properties 是中文简体的资源文件,依此类推。
  3. 资源包文件为扁平结构,不能嵌套。每一行要么是 key-value pair ,要么是 # 开头的注释。也可以可以空行。key-value pair 没有引号。后面有资源包文件的例子。
  4. 如果 Properties 文件的文本为 Unicode 字符,文件使用16进制的编码来存储,而不是明文。这样对开发人员来说,友好性较差。

Resource Model 及数据绑定

我们继续对前面显示供应商 Master-detail 的程序进行重构,增加程序多语言的功能。Eclipse 的项目文件结构如下:

使用 Resource Model 绑定数据需要三步:

  1. 添加资源包文件,将不同的语言放在不同的资源包文件中。本示例给出两个资源包文件:
  • i18n.properties(默认)
  • i18n_zh_CN.properties。(中文简体)

i18n.properties 文件的内容:

#-------------------------------------
# In master page
#-------------------------------------
# master page title
masterTitle=Supplier Overview

# Count of supplier
supplierCount=Supplier count
id=ID
name=Supplier Name

#-------------------------------------
# In detail page
#-------------------------------------

# detail page title
detailTitle=Supplier detail

i18n_zh_CN.properties:

#-------------------------------------
# In master page
#-------------------------------------
# master page title: Supplier Overview
masterTitle=\u4F9B\u5E94\u5546\u6982\u89C8

# Count of supplier
supplierCount=\u4F9B\u5E94\u5546\u6570\u91CF
id=ID
name=\u4F9B\u5E94\u5546\u540D\u79F0

#-------------------------------------
# In detail page
#-------------------------------------

# detail page title: Supplier Detail
detailTitle=\u4F9B\u5E94\u5546\u660E\u7EC6
  1. 在 Component.js 文件中,创建 Resource model 的实例 。Component.js 的完整代码如下:
sap.ui.define([
        "sap/ui/core/UIComponent",
        "sap/ui/model/json/JSONModel",
        "sap/ui/model/resource/ResourceModel"
    ], 
        
    function(UIComponent, JSONModel, ResourceModel){
        return UIComponent.extend("webapp.Component", { 
            // meta-data
            metadata: {
                "rootView": "webapp.view.App",
                "config": {
                    "serviceUrl": "service/data.json",
                    "i18nBundle": "webapp.i18n.i18n"
                }
            },
            
            // initialization
            init: function(){               
                UIComponent.prototype.init.apply(this, arguments);
                
                var mConfig = this.getMetadata().getConfig();
                
                // resource bundle
                var oResourceModel = new ResourceModel({
                    bundleName: mConfig.i18nBundle
                });
                this.setModel(oResourceModel, "i18n");              

                // application data
                var oModel = new JSONModel(mConfig.serviceUrl);
                this.setModel(oModel);
            },
            
            createContent: function() {                 
                // root view
                var oRootView = UIComponent.prototype.createContent.apply(this, arguments);
                oApp = oRootView.byId("app");
                
                return oRootView;               
            }
        });
    }
);

和前一篇的代码相比,代码有如下变更:

  • 添加对 sap.ui.model.resource.ResourceModel 的依赖:
sap.ui.define([
        "sap/ui/core/UIComponent",
        "sap/ui/model/json/JSONModel",
        "sap/ui/model/resource/ResourceModel"
    ], ...
  • 在 metadata 配置中,指定 i18n 文件的位置为 app root->webapp->i18n 。最后一个 i18n 表示文件名。文件的扩展名总是 .properties。
metadata: {
    "rootView": "webapp.view.App",
    "config": {
        "serviceUrl": "service/data.json",
        "i18nBundle": "webapp.i18n.i18n"
    }        
  • 实例化 Resource Model
// initialization
init: function(){               
    UIComponent.prototype.init.apply(this, arguments);
    
    var mConfig = this.getMetadata().getConfig();
    
    // resource bundle
    var oResourceModel = new ResourceModel({
        bundleName: mConfig.i18nBundle
    });
    this.setModel(oResourceModel, "i18n");              

    // application data
    var oModel = new JSONModel(mConfig.serviceUrl);
    this.setModel(oModel);
},

读取配置 config->i18nBundle ,然后使用 setModel() 方法,设置 Component 的 Model 为 ResourceModel ,并且将其命名为i18n。

3)在 View 中参照 Resource Model 中定义的 key。以 Master View 为例:

<core:View xmlns:core="sap.ui.core" 
           xmlns:mvc="sap.ui.core.mvc" xmlns="sap.m"
        controllerName="webapp.controller.Master" 
        xmlns:html="http://www.w3.org/1999/xhtml">
        
    <Page title="{i18n>masterTitle}">
        <content>
            <Table items="{/Suppliers}">
            
                <headerToolbar>
                    <Toolbar>
                        <Label text="{i18n>supplierCount}: {/CountOfSuppliers}" />
                    </Toolbar>
                </headerToolbar>
                
                <columns>
                    <Column>
                        <header><Text text="{i18n>id}" /> </header>
                    </Column>
                    <Column>
                        <header><Text text="{i18n>name}" /> </header>
                    </Column>
                </columns>
                
                <items>
                    <ColumnListItem type="Navigation" press="onListPress">
                        <cells>
                            <ObjectIdentifier text="{ID}" />
                            <ObjectIdentifier text="{Name}" />
                        </cells>
                    </ColumnListItem>
                </items>
                
            </Table>
        </content>
        
        <footer>
            <Toolbar>
                <Button text="language information" press="onBtnPress"/>
            </Toolbar>
        </footer>
    </Page>
    
</core:View>

Master页面标题,之前是硬编码,现在变为:Page title="{i18n>masterTitle}"。其它的控件,属性设置相同。

detail view(Detail.view.xml)

<core:View xmlns:core="sap.ui.core" 
           xmlns:mvc="sap.ui.core.mvc" xmlns="sap.m"
        controllerName="webapp.controller.Detail" 
        xmlns:html="http://www.w3.org/1999/xhtml">
        
    <Page title="{i18n>detailTitle}" showNavButton="true" navButtonPress="onNavPress">
        <content>
            <ObjectHeader title="{Name}" number="ID: {ID}">
                <ObjectAttribute text="{Address/Street}, {Address/City}"/>
            </ObjectHeader>
        </content>
    </Page>
    
</core:View>

启动程序,界面和上篇相同。但我们可在 url 后面添加?sap-ui-language=XXX,实现语言的切换。比如:?sap-ui-language=en切换为英语:

简体中文
英语

使用 jQuery.sap.resources

如果要在代码中直接使用资源包的文本,OpenUI5 提供了 jQuery.sap.resources 方法。比如我们需要在页面中根据不同的语言,显示不同的提示消息。接下来我们在 Master View 中添加一个按钮( sap.m.Button ),当点击的时候读取资源包文件的 msgCurrLanguage ,然后 alert 这个消息给用户。

  1. 先在 i18n.properties 文件中添加 key-value:
msgCurrLanguage=Current Language is {0}

在 i18n_zh_CN.properties 中添加 msgCurrLanguage 的中文显示:

msgCurrLanguage=\u5F53\u524D\u8BED\u8A00\u662F {0}
  1. 在 Master view 的 Page 中添加 Button
<footer>
    <Toolbar>
        <Button text="{i18n>languageTitle}" press="onLanButtonPress" />
    </Toolbar>
</footer>

Master View 的完整代码:

<core:View xmlns:core="sap.ui.core" 
           xmlns:mvc="sap.ui.core.mvc" 
           xmlns="sap.m"
        controllerName="webapp.controller.Master" 
        xmlns:html="http://www.w3.org/1999/xhtml">
    <Page id="master" title="{i18n>masterTitle}">
        <content>
            <Table class="sapUiResponsiveMargin" width="auto" items="{/Suppliers}">
                
       <!--  Table的细节省略,请参考之前代码--> 
                
            </Table>            
        </content>
        
        <footer>
            <Toolbar>
                <Button text="{i18n>languageTitle}" press="onLanButtonPress" />
            </Toolbar>
        </footer>
    </Page>
</core:View>

3)在 Master controller 中添加 event handler: onLanButtonPress:

onLanButtonPress: function(oEvent){
    // 添加依赖包
    jQuery.sap.require("jquery.sap.resources");
    
    var sLocale = sap.ui.getCore().getConfiguration().getLanguage();
    var oBundle = jQuery.sap.resources({
        url: "i18n/i18n.properties",
        locale: sLocale
    })
    
    var sMessage = oBundle.getText("msgCurrLanguage", [sLocale]);
    alert(oBundle.getText("msgCurrLanguage", [sLocale]));
}

代码说明:

  • sap.ui.getCore().getConfiguration().getLanguage() 获得当前语言。

  • jQuery.sap.resources(...) 根据指定的 URL 和 Locale,创建一个新的资源包实例(Creates and returns a new instance of jQuery.sap.util.ResourceBundle using the given URL and locale to determine what to load.)。

  • getText() 根据资源包文件的 key,获取与语言相关的 value。

界面效果(Edge 浏览器),当在中文环境中,显示:

中文

当在英文环境中,显示:

英文

源代码

21_zui5_resource_model

参考

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 206,126评论 6 481
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 88,254评论 2 382
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 152,445评论 0 341
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 55,185评论 1 278
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 64,178评论 5 371
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,970评论 1 284
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,276评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,927评论 0 259
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 43,400评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,883评论 2 323
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,997评论 1 333
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,646评论 4 322
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,213评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,204评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,423评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,423评论 2 352
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,722评论 2 345

推荐阅读更多精彩内容