【SAPUI5】i18nをライブラリ化する

##i18nのライブラリ化
SAPUI5のi18nは、多言語対応用のテキストを格納するファイルです。
参考：[【SAPUI5】i18nで多言語対応](https://qiita.com/tami/items/139e312b1ec047814411)

通常はアプリケーションごとにi18nファイルを持ちますが、複数のアプリケーションで共通のテキストを使いたい場合もあると思います。そんなとき、ライブラリにi18nを定義し、ライブラリのi18nとアプリケーションのi18をマージして使うことができます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/276021/f0111df3-c931-6b11-1123-5d4093b616c4.png)

以下のブログを参考に、i18nをライブラリ化してアプリケーションから使ってみます。

- [SAPUI5アプリで再利用可能なライブラリを作る（ABAPリポジトリ）](https://qiita.com/yonekura-h/items/053fc34acba91fe472d2)
- [Merging Two I18n Text Bundles](https://jasper07.secondphase.com.au/blog/2016/04/09/merge-translatable-texts/)
- [Implementing Re-use Components in SAPUI5 libraries and consuming them in SAPUI5 apps](https://www.nabisoft.com/tutorials/sapui5/implementing-re-use-components-in-sapui5-libraries-and-consuming-them-in-sapui5-apps)

※2020/2/5 追記
よりよいやり方
[【SAPUI5】ライブラリのmessagebundleをアプリケーションから使う方法](https://qiita.com/tami/items/09d824317bb3c486aec1)

##ライブラリの登録
以下の記事を参考に、ライブラリを登録します。
[SAPUI5アプリで再利用可能なライブラリを作る（ABAPリポジトリ）](https://qiita.com/yonekura-h/items/053fc34acba91fe472d2)

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/276021/bbc32081-dac4-1025-9386-a01f8aeca9cf.png)

つづいて、zdemolibraryの直下にreuseTextsというフォルダを作成します。
中にはi18n、Component.js、manifest.jsonを格納します。Component.jsがついているので、reuseTextsが小さなアプリケーションのように機能します。このため、独自のi18nを持つことができます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/276021/8e947d8c-a9a3-13ea-9174-5a0cdd9671d3.png)

**Component.js**
ここでは特別な処理はありません。

```javascript
sap.ui.define([
	"sap/ui/core/UIComponent"
], function (UIComponent, Device, models) {
	"use strict";

	return UIComponent.extend("demo.library.zdemolibrary.reuseTexts.Component", {

		metadata: {
			manifest: "json"
		},

		init: function () {
			UIComponent.prototype.init.apply(this, arguments);

		}
	});
});
```

**manifest.json**
通常のアプリケーションと違うのは、sap.app.typeが"component"であることと、embeddedByにlibrary.jsへの相対パスを記載するところです。

```json
{
	"_version": "1.12.0",
	"sap.app": {
		"id": "demo.library.zdemolibrary.reuseTexts",
		"type": "component",
		"embeddedBy": "../",
		"i18n": "i18n/i18n.properties",
		"title": "{{compTitle}}",
		"description": "{{compDescription}}",
		"applicationVersion": {
			"version": "1.0.0"
		},
		"resources": "resources.json"
	},
	"sap.ui": {
		"technology": "UI5",
		"icons": {
			"icon": "sap-icon://task",
			"favIcon": "",
			"phone": "",
			"phone@2": "",
			"tablet": "",
			"tablet@2": ""
		},
		"deviceTypes": {
			"desktop": true,
			"tablet": true,
			"phone": true
		}
	},
	"sap.ui5": {
		"dependencies": {
			"minUI5Version": "1.66.0",
			"libs": {
				"sap.ui.core": {}
			}
		},
		"contentDensities": {
			"compact": true,
			"cozy": true
		},
		"models": {
			"i18n": {
				"type": "sap.ui.model.resource.ResourceModel",
				"settings": {
					"bundleName": "demo.library.zdemolibrary.reuseTexts.i18n.i18n"
				}
			}
		},
		"resources": {
			"css": [],
			"js" : []
		}
	}
}
```

**i18n**

```text
compTitle=Reuse i18n
compDescription=Library for reuse i18n

reuseText.comment1=Reuse Text 1
reuseText.comment2=Reuse Text 2

```

##ライブラリを使用するアプリケーションを登録
SAPUI5のテンプレートを使って、簡単なアプリケーションを登録します。

**App.view.xml**
2つのテキストを表示します。1つ目はローカルのi18nを参照し、2つ目はライブラリのi18を参照します。

```xml
<mvc:View controllerName="demo.Train_17_UseLibraryText.controller.App" xmlns:mvc="sap.ui.core.mvc" displayBlock="true" xmlns="sap.m">
	<Shell id="shell">
		<App id="app">
			<pages>
				<Page id="page" title="{i18n>title}">
					<content>
						<VBox>
							<Text text="{i18n>local.comment1}"/>
							<Text text="{i18n>reuseText.comment1}"/>							
						</VBox>
					</content>
				</Page>
			</pages>
		</App>
	</Shell>
</mvc:View>
```

**i18n**

```text
title=Title
appTitle=Train_17_UseLibraryText
appDescription=App Description

local.comment1=Local Text 1
local.comment2=Local Text 2

```

この状態で実行すると、以下のようになります。上のテキストにはローカルのi18で定義したテキストが入っていますが、ライブラリのi18nを参照しているほうはまだ変換されていません。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/276021/2cc26a65-11eb-6ace-5043-8dc55ce2c437.png)

###ライブラリのi18nを使用する
プロジェクトを右クリックし、Project->Add Reference to Libraryでライブラリを参照します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/276021/cd329f77-d43a-57de-6e5e-56218ba2a1e5.png)

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/276021/6478d66f-438e-969a-fcb6-3ae7ae0e4029.png)

manifest.jsonに以下を設定します。

```json
	"sap.ui5": {
		"flexEnabled": false,
		"rootView": {
			"viewName": "demo.Train_17_UseLibraryText.view.App",
			"type": "XML",
			"async": true,
			"id": "App"
		},
		"dependencies": {
			"minUI5Version": "1.65.6",
			"libs": {
				"sap.ui.layout": {},
				"sap.ui.core": {},
				"sap.m": {},
				"demo.library.zdemolibrary": {
					"minVersion": "1.0.0"
				}
			},
			//追加
			"components": {
				"demo.library.zdemolibrary.reuseTexts": {
					"lazy": true				}
			}
		}
```

Component.jsの中で、ライブラリのコンポーネント（reuseTexts）からi18nをロードし、自身のi18nにマージします。

```javascript
		init: function () {
			//...既存のコードは省略
			
			sap.ui.component({
				name: "demo.library.zdemolibrary.reuseTexts",
				settings: {},
				componentData: {},
				async: true,
				manifest: true
			}).then(function(oComponent){
				var oBundle = oComponent.getModel("i18n").getResourceBundle();
				this.getModel("i18n").enhance(oBundle);
			}.bind(this));
		}
```

###結果
ライブラリで定義したi18nも表示されました。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/276021/4482c5c5-e208-d700-cb0f-9ed46f931510.png)

ただ、1行目のテキストに比べて2行目のテキストが遅れてロードされます。これは、コンポーネントを作る処理がasync（非同期）になっているためです。

```javascript
            sap.ui.component({
                name: "demo.library.zdemolibrary.reuseTexts",
                settings: {},
                componentData: {},
                async: true,
                manifest: true
            })
```

以下のようにすると両方のテキストが同じタイミングで表示されます。

```javascript
			var oComponent = sap.ui.component({
				name: "demo.library.zdemolibrary.reuseTexts",
				settings: {},
				componentData: {},
				async: false,
				manifest: true
			});
			
			var oBundle = oComponent.getModel("i18n").getResourceBundle();
			this.getModel("i18n").enhance(oBundle);
```

今回はビューにテキストしかないので画面の表示が速く、テキストの表示遅れが気になりましたが、実際はビューの初期処理などもあるので、非同期で問題ないと思います。