在 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)。
- URL中的 locale 参数(即在 url 后面加上
?sap-ui-language=xx
) - 应用程序代码的 locale 设置,比如:
sap.ui.getCore().getConfiguration().applySettings({
language: 'de',
calendarType: sap.ui.core.CalendarType.Gregorian,
formatSettings: {
legacyDateFormat: '1',
legacyTimeFormat: '1',
legacyNumberFormat: '1'
}
});
- Android 平台的用户代理字符串设置
- 浏览器的一般语言设置,可以用
window.navigator.language
查看 - 浏览器中用户语言配置。这个与浏览器相关,比如 IE 通过
window.navigator.userLanguage
查看。 - 浏览器语言配置。这个业余浏览器相关,比如 IE 通过
window.navigator.browserLanguage
查看 - OpenUI5中硬编码,默认为 en
资源包文件
资源包文件就是 Java 的属性文件( Properties 文件)。文件中包含与语言相关的文本。资源包文件的特征:
- 文件的扩展名总是
.properties
- 文件名包括固定部分和语言相关部分。比如在 OpenUI5 中,大家习惯将 Resource Bundle 的文件名叫做 i18n(来源于 internationalization 这个单词,取首位两个字母,中间字母数为18)。那么 i18n.properties 是默认的文件(如果没有其他文件,就用这个文件作为默认设置),i18n_zh_CN.properties 是中文简体的资源文件,依此类推。
- 资源包文件为扁平结构,不能嵌套。每一行要么是 key-value pair ,要么是
#
开头的注释。也可以可以空行。key-value pair 没有引号。后面有资源包文件的例子。 - 如果 Properties 文件的文本为 Unicode 字符,文件使用16进制的编码来存储,而不是明文。这样对开发人员来说,友好性较差。
Resource Model 及数据绑定
我们继续对前面显示供应商 Master-detail 的程序进行重构,增加程序多语言的功能。Eclipse 的项目文件结构如下:
使用 Resource Model 绑定数据需要三步:
- 添加资源包文件,将不同的语言放在不同的资源包文件中。本示例给出两个资源包文件:
- 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
- 在 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 这个消息给用户。
- 先在 i18n.properties 文件中添加 key-value:
msgCurrLanguage=Current Language is {0}
在 i18n_zh_CN.properties 中添加 msgCurrLanguage
的中文显示:
msgCurrLanguage=\u5F53\u524D\u8BED\u8A00\u662F {0}
- 在 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 浏览器),当在中文环境中,显示:
当在英文环境中,显示: