diff --git a/packages/base/src/ResourceBundle.js b/packages/base/src/ResourceBundle.js index 06fead301d1c..ac41e240ff73 100644 --- a/packages/base/src/ResourceBundle.js +++ b/packages/base/src/ResourceBundle.js @@ -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 @@ -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); @@ -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 }; diff --git a/packages/main/bundle.esm.js b/packages/main/bundle.esm.js index 85dc1cbdf198..ed809be12355 100644 --- a/packages/main/bundle.esm.js +++ b/packages/main/bundle.esm.js @@ -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"; diff --git a/packages/main/src/ResourceBundleProvider.js b/packages/main/src/MessageBundleAssets.js similarity index 93% rename from packages/main/src/ResourceBundleProvider.js rename to packages/main/src/MessageBundleAssets.js index 2ec1c7652341..eab38db9c66d 100644 --- a/packages/main/src/ResourceBundleProvider.js +++ b/packages/main/src/MessageBundleAssets.js @@ -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"; @@ -97,5 +97,3 @@ Suggested pattern: "i18n\\\/.*\\\.json"`); /* eslint-enable */ registerMessageBundles("@ui5/webcomponents", bundleMap); - -export { fetchResourceBundle, getResourceBundle }; diff --git a/packages/main/src/Panel.js b/packages/main/src/Panel.js index 4578d26b276f..e0ed65647575 100644 --- a/packages/main/src/Panel.js +++ b/packages/main/src/Panel.js @@ -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"; @@ -193,14 +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 = () => {}; @@ -213,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; } diff --git a/packages/main/src/TextArea.js b/packages/main/src/TextArea.js index 9706b1fbf8cd..7badbb5509df 100644 --- a/packages/main/src/TextArea.js +++ b/packages/main/src/TextArea.js @@ -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"; @@ -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() { @@ -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 { diff --git a/packages/main/src/Tokenizer.js b/packages/main/src/Tokenizer.js index 5c22be49eea1..236a1257fec6 100644 --- a/packages/main/src/Tokenizer.js +++ b/packages/main/src/Tokenizer.js @@ -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"; @@ -93,8 +95,9 @@ class Tokenizer extends UI5Element { return this._getTokens(); }; - this._delegates.push(this._itemNav); this.resourceBundle = getResourceBundle("@ui5/webcomponents"); + + this._delegates.push(this._itemNav); } onBeforeRendering() { @@ -105,7 +108,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() { @@ -211,6 +214,7 @@ class Tokenizer extends UI5Element { static async define(...params) { await fetchResourceBundle("@ui5/webcomponents"); + super.define(...params); } } diff --git a/packages/main/src/i18n/defaults.js b/packages/main/src/i18n/defaults.js new file mode 100644 index 000000000000..00dc801500bb --- /dev/null +++ b/packages/main/src/i18n/defaults.js @@ -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, +}; diff --git a/packages/main/test/sap/ui/webcomponents/main/qunit/Panel.qunit.js b/packages/main/test/sap/ui/webcomponents/main/qunit/Panel.qunit.js index 67c3269819ec..ca007ec7b125 100644 --- a/packages/main/test/sap/ui/webcomponents/main/qunit/Panel.qunit.js +++ b/packages/main/test/sap/ui/webcomponents/main/qunit/Panel.qunit.js @@ -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"); });