Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: inline english texts if no translation is fetched #479

Merged
merged 8 commits into from
Jun 4, 2019
65 changes: 33 additions & 32 deletions packages/base/src/ResourceBundle.js
Original file line number Diff line number Diff line change
@@ -1,35 +1,11 @@
import "./shims/jquery-shim.js";
import ResourceBundle from "@ui5/webcomponents-core/dist/sap/base/i18n/ResourceBundle.js";
import formatMessage from "@ui5/webcomponents-core/dist/sap/base/strings/formatMessage.js";
import { getLanguage } from "./LocaleProvider.js";
import { registerModuleContent } from "./ResourceLoaderOverrides.js";
import { fetchJsonOnce } from "./util/FetchHelper.js";

const bundleURLs = new Map();
const singletonPromises = new Map();

/**
* Creates a new promise for the specified key (or returns a new one and stores it for next calls).
* The same promise is always returned for multiple calls to this method for the same key.
* This promise can also be resolved so that all usages that await its resolution can continue.
* @param {key} key the unique key identifying the promise
* @private
*/
const _getSingletonPromise = key => {
const prevPromise = singletonPromises.get(key);
if (prevPromise) {
return prevPromise;
}

let resolveFn;
const newPromise = new Promise(resolve => {
resolveFn = resolve;
});
// private usage for making a deferred-like API to avoid storing resolve functions in a second map
newPromise._deferredResolve = resolveFn;

singletonPromises.set(key, newPromise);
return newPromise;
};

/**
* This method preforms the asyncronous task of fething the actual text resources. It will fetch
Expand All @@ -41,10 +17,14 @@ const _getSingletonPromise = key => {
* @public
*/
const fetchResourceBundle = async packageId => {
// depending on the module resolution order, the fetch might run before the bundle URLs are registered - sync them here
await _getSingletonPromise(packageId);
const bundlesForPackage = bundleURLs.get(packageId);

if (!bundlesForPackage) {
console.warn(`Message bundle assets are not configured. Falling back to english texts.`, /* eslint-disable-line */
` You need to import @ui5/webcomponents/dist/MessageBundleAssets.js with a build tool that supports JSON imports.`); /* eslint-disable-line */
return;
}

const language = getLanguage();

let localeId = ResourceBundle.__normalize(language);
Expand Down Expand Up @@ -72,13 +52,34 @@ const fetchResourceBundle = async packageId => {
*/
const registerMessageBundles = (packageId, bundlesMap) => {
bundleURLs.set(packageId, bundlesMap);
_getSingletonPromise(packageId)._deferredResolve();
};

const getResourceBundle = library => {
return ResourceBundle.create({
url: `${library}.properties`,
});
class ResourceBundleFallback {
getText(textObj, ...params) {
return formatMessage(textObj.defaultText, params);
}
}

class ResourceBundleWrapper {
constructor(resouceBundle) {
this._resourceBundle = resouceBundle;
}

getText(textObj, ...params) {
return this._resourceBundle.getText(textObj.key, ...params);
}
}

const getResourceBundle = packageId => {
const bundleLoaded = bundleURLs.has(packageId);

if (bundleLoaded) {
return new ResourceBundleWrapper(ResourceBundle.create({
url: `${packageId}.properties`,
}));
}

return new ResourceBundleFallback();
};

export { fetchResourceBundle, registerMessageBundles, getResourceBundle };
2 changes: 2 additions & 0 deletions packages/main/bundle.esm.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@ import "@ui5/webcomponents-base/src/browsersupport/Edge.js";
import "@ui5/webcomponents-base/src/shims/jquery-shim.js";
import "./dist/ThemePropertiesProvider.js";

import "@ui5/webcomponents/dist/MessageBundleAssets.js";

import Gregorian from "@ui5/webcomponents-core/dist/sap/ui/core/date/Gregorian.js";
import Buddhist from "@ui5/webcomponents-core/dist/sap/ui/core/date/Buddhist.js";
import Islamic from "@ui5/webcomponents-core/dist/sap/ui/core/date/Islamic.js";
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { fetchResourceBundle, registerMessageBundles, getResourceBundle } from "@ui5/webcomponents-base/src/ResourceBundle.js";
import { registerMessageBundles } from "@ui5/webcomponents-base/src/ResourceBundle.js";

import ar from "./i18n/messagebundle_ar.json";
import bg from "./i18n/messagebundle_bg.json";
Expand Down Expand Up @@ -97,5 +97,3 @@ Suggested pattern: "i18n\\\/.*\\\.json"`);
/* eslint-enable */

registerMessageBundles("@ui5/webcomponents", bundleMap);

export { fetchResourceBundle, getResourceBundle };
9 changes: 5 additions & 4 deletions packages/main/src/Panel.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,12 @@ import { getIconURI } from "@ui5/webcomponents-base/src/IconPool.js";
import slideDown from "@ui5/webcomponents-base/src/animations/slideDown.js";
import slideUp from "@ui5/webcomponents-base/src/animations/slideUp.js";
import { isSpace, isEnter } from "@ui5/webcomponents-base/src/events/PseudoEvents.js";
import { fetchResourceBundle, getResourceBundle } from "@ui5/webcomponents-base/src/ResourceBundle.js";
import Icon from "./Icon.js";
import PanelAccessibleRole from "./types/PanelAccessibleRole.js";
import PanelRenderer from "./build/compiled/PanelRenderer.lit.js";
import { fetchResourceBundle, getResourceBundle } from "./ResourceBundleProvider.js";

import { PANEL_ICON } from "./i18n/defaults.js";

// Styles
import panelCss from "./themes/Panel.css.js";
Expand Down Expand Up @@ -193,15 +195,13 @@ class Panel extends UI5Element {
constructor() {
super();

this.resourceBundle = getResourceBundle("@ui5/webcomponents");

this._header = {};

this._icon = {};
this._icon.id = `${this.id}-CollapsedImg`;
this._icon.src = getIconURI("navigation-right-arrow");
this._icon.title = this.resourceBundle.getText("PANEL_ICON");
this._icon.functional = true;
this.resourceBundle = getResourceBundle("@ui5/webcomponents");

this._toggle = event => { event.preventDefault(); this._toggleOpen(); };
this._noOp = () => {};
Expand All @@ -214,6 +214,7 @@ class Panel extends UI5Element {
}

const toggleWithInternalHeader = !this.header;
this._icon.title = this.resourceBundle.getText(PANEL_ICON);
this._header.press = toggleWithInternalHeader ? this._toggle : this._noOp;
this._icon.press = !toggleWithInternalHeader ? this._toggle : this._noOp;
}
Expand Down
12 changes: 7 additions & 5 deletions packages/main/src/TextArea.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,10 @@ import Bootstrap from "@ui5/webcomponents-base/src/Bootstrap.js";
import UI5Element from "@ui5/webcomponents-base/src/UI5Element.js";
import CSSSize from "@ui5/webcomponents-base/src/types/CSSSize.js";
import Integer from "@ui5/webcomponents-base/src/types/Integer.js";
import { fetchResourceBundle, getResourceBundle } from "@ui5/webcomponents-base/src/ResourceBundle.js";
import TextAreaRenderer from "./build/compiled/TextAreaRenderer.lit.js";
import { fetchResourceBundle, getResourceBundle } from "./ResourceBundleProvider.js";

import { TEXTAREA_CHARACTERS_LEFT, TEXTAREA_CHARACTERS_EXCEEDED } from "./i18n/defaults.js";

// Styles
import styles from "./themes/TextArea.css.js";
Expand Down Expand Up @@ -231,11 +233,11 @@ class TextArea extends UI5Element {
constructor() {
super();

this.resourceBundle = getResourceBundle("@ui5/webcomponents");

this._listeners = {
change: this._handleChange.bind(this),
};

this.resourceBundle = getResourceBundle("@ui5/webcomponents");
}

onBeforeRendering() {
Expand Down Expand Up @@ -319,9 +321,9 @@ class TextArea extends UI5Element {
leftCharactersCount = maxLength - this.value.length;

if (leftCharactersCount >= 0) {
exceededText = this.resourceBundle.getText("TEXTAREA_CHARACTERS_LEFT", [leftCharactersCount]);
exceededText = this.resourceBundle.getText(TEXTAREA_CHARACTERS_LEFT, [leftCharactersCount]);
} else {
exceededText = this.resourceBundle.getText("TEXTAREA_CHARACTERS_EXCEEDED", [Math.abs(leftCharactersCount)]);
exceededText = this.resourceBundle.getText(TEXTAREA_CHARACTERS_EXCEEDED, [Math.abs(leftCharactersCount)]);
}
}
} else {
Expand Down
10 changes: 7 additions & 3 deletions packages/main/src/Tokenizer.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,15 @@ import Bootstrap from "@ui5/webcomponents-base/src/Bootstrap.js";
import UI5Element from "@ui5/webcomponents-base/src/UI5Element.js";
import ResizeHandler from "@ui5/webcomponents-base/src/delegate/ResizeHandler.js";
import ItemNavigation from "@ui5/webcomponents-base/src/delegate/ItemNavigation.js";
import { fetchResourceBundle, getResourceBundle } from "@ui5/webcomponents-base/src/ResourceBundle.js";

import { fetchResourceBundle, getResourceBundle } from "./ResourceBundleProvider.js";
import TokenizerRenderer from "./build/compiled/TokenizerRenderer.lit.js";
import { MULTIINPUT_SHOW_MORE_TOKENS } from "./i18n/defaults.js";

// Styles
import styles from "./themes/Tokenizer.css.js";


// all themes should work via the convenience import (inlined now, switch to json when elements can be imported individyally)
import "./ThemePropertiesProvider.js";

Expand Down Expand Up @@ -97,8 +99,9 @@ class Tokenizer extends UI5Element {
return this._getTokens();
};

this._delegates.push(this._itemNav);
this.resourceBundle = getResourceBundle("@ui5/webcomponents");

this._delegates.push(this._itemNav);
}

onBeforeRendering() {
Expand All @@ -109,7 +112,7 @@ class Tokenizer extends UI5Element {
}

this._lastTokenCount = this.tokens.length;
this._nMoreText = this.resourceBundle.getText("MULTIINPUT_SHOW_MORE_TOKENS", [this._hiddenTokens.length]);
this._nMoreText = this.resourceBundle.getText(MULTIINPUT_SHOW_MORE_TOKENS, [this._hiddenTokens.length]);
}

onAfterRendering() {
Expand Down Expand Up @@ -215,6 +218,7 @@ class Tokenizer extends UI5Element {

static async define(...params) {
await fetchResourceBundle("@ui5/webcomponents");

super.define(...params);
}
}
Expand Down
26 changes: 26 additions & 0 deletions packages/main/src/i18n/defaults.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
const MULTIINPUT_SHOW_MORE_TOKENS = {
key: "MULTIINPUT_SHOW_MORE_TOKENS",
defaultText: "{0} More",
};

const TEXTAREA_CHARACTERS_LEFT = {
key: "TEXTAREA_CHARACTERS_LEFT",
defaultText: "{0} characters remaining",
};

const TEXTAREA_CHARACTERS_EXCEEDED = {
key: "TEXTAREA_CHARACTERS_EXCEEDED",
defaultText: "{0} characters over limit",
};

const PANEL_ICON = {
key: "PANEL_ICON",
defaultText: "Expand/Collapse",
};

export {
MULTIINPUT_SHOW_MORE_TOKENS,
TEXTAREA_CHARACTERS_LEFT,
TEXTAREA_CHARACTERS_EXCEEDED,
PANEL_ICON,
};
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ TestHelper.ready(function () {
QUnit.test("The default 'headerText' is empty string", function (assert) {
var panel = this.getPanelRoot(),
sExpected = "";

assert.equal(panel.querySelector(".sapMPanelHdr").innerText.trim(), sExpected, "headerText is empty string");
});

Expand Down