Skip to content

Commit

Permalink
[FEATURE] manifestCreator: i18n section v22
Browse files Browse the repository at this point in the history
With app descriptor v22 (1.21.0)
the i18n sections in the manifest are objects
with:
* bundleUrl
* supportedLocales
  • Loading branch information
tobiasso85 committed Nov 26, 2020
1 parent e9458e7 commit d3b7dda
Show file tree
Hide file tree
Showing 10 changed files with 179 additions and 35 deletions.
105 changes: 97 additions & 8 deletions lib/processors/manifestCreator.js
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ const APP_DESCRIPTOR_V3_SECTION_SAP_APP = new Version("1.2.0");
const APP_DESCRIPTOR_V3_OTHER_SECTIONS = new Version("1.1.0");
const APP_DESCRIPTOR_V5 = new Version("1.4.0");
const APP_DESCRIPTOR_V10 = new Version("1.9.0");
const APP_DESCRIPTOR_V22 = new Version("1.21.0");

// namespaces used in .library files
const XMLNS_UILIB = "http://www.sap.com/sap.ui.library.xsd";
Expand Down Expand Up @@ -122,13 +123,30 @@ class Library {


class LibraryBundle {
/**
*
* @param {string} prefix
* @param {module:@ui5/fs.Resource[]} resources
*/
constructor(prefix, resources) {
this.prefix = prefix;
this.resources = resources.filter((res) => res.getPath().startsWith(prefix));
}

/**
*
* @param {string} name
* @returns {module:@ui5/fs.Resource}
*/
findResource(name) {
return this.resources.find((res) => res.getPath() === this.prefix + name);
}

/**
*
* @param {RegExp} pattern
* @returns {module:@ui5/fs.Resource[]}
*/
getResources(pattern) {
return this.resources.filter((res) => pattern == null || pattern.test(res.getPath()));
}
Expand All @@ -143,6 +161,13 @@ async function createManifest(libraryResource, libBundle, descriptorVersion, _in

// collect information from library.js file
const libraryJSInfo = await analyzeLibraryJS(libBundle.findResource("library.js"));
const includeSupportedLocalesInformation = descriptorVersion.compare(APP_DESCRIPTOR_V22) >= 0;
/**
* cache for supported locales
*
* @see createI18nSection
*/
const i18nToSupportedLocales = new Map();

const manifestAppData = library.getAppData("manifest", XMLNS_MANIFEST);
const sapFioriAppData = findChild(manifestAppData, "sap.fiori");
Expand Down Expand Up @@ -265,13 +290,19 @@ async function createManifest(libraryResource, libBundle, descriptorVersion, _in

return osComponents.length > 0 ? osComponents : undefined;
}

const i18nAppData = findChild(manifestAppData, "i18n");
let i18n;
if (i18nAppData) {
const i18nText = i18nAppData._; // text content
i18n = createI18nSection(i18nText, i18nToSupportedLocales);
log.verbose(`sap.app/i18n taken from .library appData: '%s'`, i18nText);
}
const sapApp = {
_version: sectionVersion(APP_DESCRIPTOR_V3_SECTION_SAP_APP),
id: library.getName(),
type: "library",
embeds: findComponentPaths(),
i18n: getChildTextContent(manifestAppData, "i18n"),
i18n,
applicationVersion: {
version: isValid(library.getVersion()) ? library.getVersion() : getProjectVersion()
},
Expand Down Expand Up @@ -400,24 +431,28 @@ async function createManifest(libraryResource, libBundle, descriptorVersion, _in
// - from library resources (if "messagebundle.properties" exists)
function i18n() {
const i18nElement = findChild(libraryAppData, "i18n");
let i18n = null;
if ( i18nElement ) {
const i18n = i18nElement._;
i18n = i18nElement._; // text content
if ( i18n === "false" ) {
return false;
} else if ( i18n === "true" ) {
return "messagebundle.properties";
} else {
return i18n;
i18n = "messagebundle.properties";
}
// log.verbose(" sap.ui5/library/i18n property taken from .library appData: '%s'", library.i18n);
} else {
if ( libBundle.findResource("messagebundle.properties") != null ) {
// log.verbose(" sap.ui5/library/i18n property determined from resources: '%s'", library.i18n);
return "messagebundle.properties";
i18n = "messagebundle.properties";
} else {
return false;
}
}
// i18n can be an empty string therefore check for not being null
if (i18n !== null) {
return createI18nSection(i18n, i18nToSupportedLocales);
}
return false;
}

// css:
Expand Down Expand Up @@ -468,6 +503,60 @@ async function createManifest(libraryResource, libBundle, descriptorVersion, _in
return sapUI5;
}

/**
* Creates an i18n section:
* - either using bundleUrl and supportedLocales
* - or the i18n String
*
* @param {string} i18n bundle url, e.g. "messagebundle.properties"
* @param {Map<string, Set<string>>} i18nToSupportedLocales cache to determine the supportedLocales only once
* @returns {{bundleUrl: string, supportedLocales: string[]}|null|string} json structure with bundleUrl and supportedLocales or the i18n String if not a ".properties" file.
* <code>null</code> if given i18n String is <code>null</code>
*/
function createI18nSection(i18n, i18nToSupportedLocales) {
if (!i18n) {
return null;
}
if (!i18n.endsWith(".properties")) {
return i18n;
}

// if the supported locales information should not be included use i18n text
if (!includeSupportedLocalesInformation) {
return i18n;
}

let supportedLocales = i18nToSupportedLocales.get(i18n);

if (!supportedLocales) {
supportedLocales = new Set();

if (libBundle.findResource(i18n) != null) {
supportedLocales.add("");
}
const i18nPatternStr = i18n.substring(0, i18n.length - ".properties".length);
const i18nRegexp = new RegExp(i18nPatternStr + "_([^.]+)\\.properties$");


libBundle.getResources().forEach((resource) => {
const resPath = resource.getPath();
const result = i18nRegexp.exec(resPath);

if (result) {
supportedLocales.add(result[1].replace(/_/g, "-"));
}
});
i18nToSupportedLocales.set(i18n, supportedLocales);
}

const supportedLocalesArray = [...supportedLocales];
supportedLocalesArray.sort();
return {
bundleUrl: i18n,
supportedLocales: supportedLocalesArray
};
}

function createSapFiori() {
// collect registrationIds if present
function registrationIds() {
Expand Down Expand Up @@ -556,7 +645,7 @@ async function createManifest(libraryResource, libBundle, descriptorVersion, _in
module.exports = function({libraryResource, resources, options}) {
// merge options with defaults
options = Object.assign({
descriptorVersion: APP_DESCRIPTOR_V10,
descriptorVersion: APP_DESCRIPTOR_V22,
include3rdParty: true,
prettyPrint: true
}, options);
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
{
"_version": "1.9.0",
"_version": "1.21.0",
"sap.app": {
"id": "library.d",
"type": "library",
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
{
"_version": "1.9.0",
"_version": "1.21.0",
"sap.app": {
"id": "library.h",
"type": "library",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -133,7 +133,7 @@
{
"name": "manifest.json",
"module": "library/h/manifest.json",
"size": 739
"size": 740
},
{
"name": "not.js",
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
{
"_version": "1.9.0",
"_version": "1.21.0",
"sap.app": {
"id": "library.h",
"type": "library",
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
{
"_version": "1.9.0",
"_version": "1.21.0",
"sap.app": {
"id": "library.i",
"type": "library",
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
{
"_version": "1.9.0",
"_version": "1.21.0",
"sap.app": {
"id": "library.k",
"type": "library",
Expand All @@ -22,7 +22,14 @@
"libs": {}
},
"library": {
"i18n": "messagebundle.properties",
"i18n": {
"bundleUrl": "messagebundle.properties",
"supportedLocales": [
"",
"de",
"en"
]
},
"content": {
"controls": [
"library.k.AnyControl"
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
{
"_version": "1.9.0",
"_version": "1.21.0",
"sap.app": {
"id": "library.k",
"type": "library",
Expand Down
49 changes: 40 additions & 9 deletions test/lib/processors/manifestCreator.js
Original file line number Diff line number Diff line change
Expand Up @@ -17,14 +17,28 @@ const libraryContent = `<?xml version="1.0" encoding="UTF-8" ?>
<libraryName>sap.ui.core</libraryName>
</dependency>
</dependencies>
<appData>
<manifest xmlns="http://www.sap.com/ui5/buildext/manifest">
<i18n>i18n/i18n.properties</i18n>
</manifest>
</appData>
</library>`;

const expectedManifestContent = `{
"_version": "1.9.0",
"_version": "1.21.0",
"sap.app": {
"id": "library.e",
"type": "library",
"embeds": [],
"i18n": {
"bundleUrl": "i18n/i18n.properties",
"supportedLocales": [
"",
"de",
"en"
]
},
"applicationVersion": {
"version": "1.0.0"
},
Expand Down Expand Up @@ -55,9 +69,10 @@ test.afterEach.always((t) => {
});

test("default manifest creation", async (t) => {
const prefix = "/resources/sap/ui/mine/";
const libraryResource = {
getPath: () => {
return "/resources/sap/ui/mine/.library";
return prefix + ".library";
},
getString: async () => {
return libraryContent;
Expand All @@ -71,11 +86,19 @@ test("default manifest creation", async (t) => {
}
};

const result = await manifestCreator({libraryResource, resources: [], options: {}});
const resources = ["", "_en", "_de"].map((lang) => {
return {
getPath: () => {
return `${prefix}i18n/i18n${lang}.properties`;
}
};
});
const result = await manifestCreator({libraryResource, resources, options: {}});
t.is(await result.getString(), expectedManifestContent, "Correct result returned");
});

test.serial("manifest creation for sap/apf", async (t) => {
const prefix = "/resources/sap/apf/";
const logger = require("@ui5/logger");
const verboseLogStub = sinon.stub();
const myLoggerInstance = {
Expand Down Expand Up @@ -103,16 +126,24 @@ test.serial("manifest creation for sap/apf", async (t) => {

const componentResource = {
getPath: () => {
return "/resources/sap/apf/Component.js";
return prefix + "Component.js";
}
};

const result = await manifestCreatorWithStub({libraryResource, resources: [componentResource], options: {}});
const resources = ["", "_en", "_de"].map((lang) => {
return {
getPath: () => {
return `${prefix}i18n/i18n${lang}.properties`;
}
};
});
resources.push(componentResource);
const result = await manifestCreatorWithStub({libraryResource, resources, options: {}});
t.is(await result.getString(), expectedManifestContent, "Correct result returned");

t.is(verboseLogStub.callCount, 8);
t.is(verboseLogStub.firstCall.args[0],
t.is(verboseLogStub.callCount, 9);
t.is(verboseLogStub.firstCall.args[0], "sap.app/i18n taken from .library appData: '%s'");
t.is(verboseLogStub.secondCall.args[0],
"Package %s contains both '*.library' and 'Component.js'. " +
"This is a known issue but can't be solved due to backward compatibility.");
t.is(verboseLogStub.firstCall.args[1], "/resources/sap/apf/Component.js");
t.is(verboseLogStub.secondCall.args[1], "/resources/sap/apf/Component.js");
});
Loading

0 comments on commit d3b7dda

Please sign in to comment.