diff --git a/packages/app-shell/src/app-shell.js b/packages/app-shell/src/app-shell.js index 95ea104586969c1fdc91950bb936d3141132227d..53397c4e4edd3ae6a8c8d12aaa31d53ecf9276f0 100644 --- a/packages/app-shell/src/app-shell.js +++ b/packages/app-shell/src/app-shell.js @@ -74,6 +74,7 @@ export class AppShell extends ScopedElementsMixin(DBPLitElement) { this.initateOpenMenu = false; this.auth = {}; + this.langFile = ''; } static get scopedElements() { @@ -271,6 +272,7 @@ export class AppShell extends ScopedElementsMixin(DBPLitElement) { buildTime: {type: String, attribute: 'build-time'}, env: {type: String}, auth: {type: Object}, + langFile: {type: String, attribute: 'lang-file'}, }; } @@ -701,7 +703,7 @@ export class AppShell extends ScopedElementsMixin(DBPLitElement) { background-position:center center; margin: 0 0.5% 0 1.5%; font-size:94%; - } + } */ .menu a { diff --git a/packages/common/components.js b/packages/common/components.js index 6a42c6afc7809394b9e260abd2f512b61ddec743..ac6917b0663c2a19190e69bb5b7653a8a3f681e6 100644 --- a/packages/common/components.js +++ b/packages/common/components.js @@ -7,6 +7,7 @@ import { MiniSpinner, Spinner, Translated, + Translation } from './index'; commonUtils.defineCustomElement('dbp-mini-spinner', MiniSpinner); @@ -16,3 +17,4 @@ commonUtils.defineCustomElement('dbp-button', Button); commonUtils.defineCustomElement('dbp-loading-button', LoadingButton); commonUtils.defineCustomElement('dbp-inline-notification', InlineNotification); commonUtils.defineCustomElement('dbp-translated', Translated); +commonUtils.defineCustomElement('dbp-translation', Translation); diff --git a/packages/common/dbp-common-demo.js b/packages/common/dbp-common-demo.js index fad1b686ab060edc66778e4bd13431b2c895c616..5f5b9bc0f94bd8b218ca979e82d531b869e46193 100644 --- a/packages/common/dbp-common-demo.js +++ b/packages/common/dbp-common-demo.js @@ -12,6 +12,7 @@ import { Spinner, InlineNotification, Translated, + Translation, } from './index.js'; export class DbpCommonDemo extends ScopedElementsMixin(LitElement) { @@ -20,6 +21,7 @@ export class DbpCommonDemo extends ScopedElementsMixin(LitElement) { this._i18n = createInstance(); this.lang = this._i18n.language; this.noAuth = false; + this.langFile = ''; } static get scopedElements() { @@ -31,6 +33,7 @@ export class DbpCommonDemo extends ScopedElementsMixin(LitElement) { 'dbp-loading-button': LoadingButton, 'dbp-inline-notification': InlineNotification, 'dbp-translated': Translated, + 'dbp-translation': Translation }; if (customElements.get('dbp-auth')) { @@ -44,6 +47,7 @@ export class DbpCommonDemo extends ScopedElementsMixin(LitElement) { return { lang: {type: String}, noAuth: {type: Boolean, attribute: 'no-auth'}, + langFile: {type: String, attribute: 'lang-file'}, }; } @@ -297,6 +301,9 @@ html { </div> </dbp-translated> </div> + <div class="control" id="dbp-translation-demo"> + <dbp-translation key="toolkit-showcase" subscribe="lang, lang-file"></dbp-translation> + </div> </div> </section> `; diff --git a/packages/common/index.js b/packages/common/index.js index 0009a2f09e6baac0872c90addb531afd3ed31203..3d7467fc55c3290890d31f4c2164949c6f726323 100644 --- a/packages/common/index.js +++ b/packages/common/index.js @@ -6,6 +6,7 @@ import {Button, LoadingButton} from './src/button.js'; import {Spinner} from './src/spinner.js'; import {InlineNotification} from './src/inline-notification.js'; import {Translated} from './src/translated'; +import {Translation} from './src/translation'; import {AdapterLitElement} from './src/adapter-lit-element.js'; export {EventBus, createLinkedAbortController, createTimeoutAbortSignal}; @@ -14,7 +15,7 @@ export {MiniSpinner}; export {Button, LoadingButton}; export {Spinner}; export {InlineNotification}; -export {Translated}; +export {Translated, Translation}; export * from './src/logger.js'; export * from './src/utils.js'; export {AdapterLitElement}; diff --git a/packages/common/jsonld.js b/packages/common/jsonld.js index 021a62dbe3bb734db0b62eeb4d6d2222bfd38351..525e432b3f4a17424f2cb9453ebedacdc9ad7a67 100644 --- a/packages/common/jsonld.js +++ b/packages/common/jsonld.js @@ -297,7 +297,7 @@ export default class JSONLD { } } -JSONLD._i18n = createInstance(); +JSONLD._i18n = await createInstance(); JSONLD.instances = {}; JSONLD.successFunctions = {}; JSONLD.failureFunctions = {}; diff --git a/packages/common/src/i18n.js b/packages/common/src/i18n.js index 6623ebdc2d5144e117f9df22d98a8b866ae3c1ba..0374a825419d2f2dea5fd68c5b9b0f76c019b9bb 100644 --- a/packages/common/src/i18n.js +++ b/packages/common/src/i18n.js @@ -6,3 +6,26 @@ import en from './i18n/en/translation.json'; export function createInstance() { return _createInstance({en: en, de: de}, 'de', 'en'); } + +export async function createInstanceAsync(langFile) { + // check if a path to language files is given + if(langFile) { + // request german lang file asynchronously + let result = await + fetch(langFile + 'de/translation.json', { + headers: {'Content-Type': 'application/json'}, + }); + const dynDe = await result.json(); + + // request english lang file asynchronously + result = await + fetch(langFile + 'en/translation.json', { + headers: {'Content-Type': 'application/json'}, + }); + const dynEn = await result.json(); + + return _createInstance({en: dynEn, de: dynDe}, 'de', 'en'); + } + + return _createInstance({en: en, de: de}, 'de', 'en'); +} diff --git a/packages/common/src/i18n/de/translation.json b/packages/common/src/i18n/de/translation.json index 51cf793c6cb4108b18d8cab47b63959663ec8590..b9daed00bc4e840bf1e885e4cff6f5a5e9c4d229 100644 --- a/packages/common/src/i18n/de/translation.json +++ b/packages/common/src/i18n/de/translation.json @@ -7,5 +7,6 @@ "api-documentation-server": "Verbindung zum apiDocumentation API Server {{apiDocUrl}} fehlgeschlagen!", "error-api-server": "Verbindung zum API Server {{apiUrl}} fehlgeschlagen!", "error-hydra-documentation-url-not-set": "Hydra apiDocumentation URL wurden für server {{apiUrl}} nicht gesetzt!" - } + }, + "toolkit-showcase": "Dieser Text wird mithilfe von i18n Englisch wenn man die Sprache auf Englisch stellt." } diff --git a/packages/common/src/i18n/en/translation.json b/packages/common/src/i18n/en/translation.json index e1036a6580fd180af131a4c2744c8c4fef2745fb..1e17839ab74d17ebb29bd8bf73c721a13533280a 100644 --- a/packages/common/src/i18n/en/translation.json +++ b/packages/common/src/i18n/en/translation.json @@ -7,5 +7,6 @@ "api-documentation-server": "Connection to apiDocumentation server {{apiDocUrl}} failed!", "error-api-server": "Connection to api server {{apiUrl}} failed!", "error-hydra-documentation-url-not-set": "Hydra apiDocumentation url was not set for server {{apiUrl}}!" - } + }, + "toolkit-showcase": "This text will be translated to german using i18n when the user changes the language to german." } diff --git a/packages/common/src/translation.js b/packages/common/src/translation.js new file mode 100644 index 0000000000000000000000000000000000000000..782dd3e278f423ed284a6063423ae1ba35eaf6f2 --- /dev/null +++ b/packages/common/src/translation.js @@ -0,0 +1,67 @@ +import {css, html} from 'lit'; +import {classMap} from 'lit/directives/class-map.js'; +import {until} from 'lit/directives/until.js'; +import DBPLitElement from '../dbp-lit-element'; +import {createInstanceAsync} from './i18n.js'; + +export class Translation extends DBPLitElement { + constructor() { + super(); + this.key = ''; + this.lang = ''; + this.langFile = ''; + } + + static get properties() { + return { + ...super.properties, + key: {type: String}, + lang: {type: String}, + langFile: {type: String, attribute: 'lang-file'}, + }; + } + + static get styles() { + // language=css + return css` + .hidden { + display: none; + } + `; + } + + connectedCallback() { + super.connectedCallback(); + this._i18n = createInstanceAsync(this.langFile); + } + + update(changedProperties) { + changedProperties.forEach((oldValue, propName) => { + switch (propName) { + case 'lang': + let lang = this.lang; + this._i18n.then(function(response) { + response.changeLanguage(lang); + }); + break; + } + }); + + super.update(changedProperties); + } + + render() { + // save global key in local variable for async use + let key = this.key; + + // async request to i18n translation + const translation = this._i18n.then(function(response){ + return response.t(key); + }); + + // load translation text when available, otherweise display "Loading.." + return html` + ${until(translation, html`<span>Loading..</span>`)} + `; + } +} diff --git a/toolkit-showcase/assets/common.metadata.json b/toolkit-showcase/assets/common.metadata.json index de1f70c7ede8728cc3cd6b0b8922da8137310763..51e4892cb53180deb82a6224e9dd0c836c9f6452 100644 --- a/toolkit-showcase/assets/common.metadata.json +++ b/toolkit-showcase/assets/common.metadata.json @@ -14,5 +14,5 @@ "de": "Gemeinsame Web Components", "en": "Common web components" }, - "subscribe": "lang,entry-point-url" + "subscribe": "lang,entry-point-url,lang-file" } diff --git a/toolkit-showcase/assets/dbp-toolkit-showcase.html.ejs b/toolkit-showcase/assets/dbp-toolkit-showcase.html.ejs index ef38009f126c9bdba6e6f9009e443eb59867f93d..1cf3784a8bcfde73d276e6951838c2b011cbf373 100644 --- a/toolkit-showcase/assets/dbp-toolkit-showcase.html.ejs +++ b/toolkit-showcase/assets/dbp-toolkit-showcase.html.ejs @@ -11,7 +11,7 @@ <!-- PWA manifest file --> <link rel="manifest" href="<%= getUrl(name + '.manifest.json') %>"> - + <!-- PWA iphone --> <link rel="apple-touch-icon" sizes="180x180" href="<%= getPrivateUrl('apple-touch-icon.png') %>"> <link rel="icon" type="image/png" sizes="32x32" href="<%= getPrivateUrl('icon-32x32.png') %>"> @@ -113,6 +113,7 @@ <<%= name %> provider-root lang="de" + lang-file="<%= getUrl('/src/i18n/') %>" entry-point-url="<%= entryPointURL %>" nextcloud-auth-url="<%= nextcloudWebAppPasswordURL %>" nextcloud-web-dav-url="<%= nextcloudWebDavURL %>" diff --git a/toolkit-showcase/src/dbp-common-demo-activity.js b/toolkit-showcase/src/dbp-common-demo-activity.js index fc633b9d37f5ba3362f6bbe99f6b75f8ab107010..6735845541387b0098d6f61bf329470508bf84c3 100644 --- a/toolkit-showcase/src/dbp-common-demo-activity.js +++ b/toolkit-showcase/src/dbp-common-demo-activity.js @@ -13,6 +13,7 @@ class DbpCommonDemoActivity extends ScopedElementsMixin(AdapterLitElement) { super(); this.lang = 'en'; this.entryPointUrl = ''; + this.langFile = ''; } static get scopedElements() { @@ -25,6 +26,7 @@ class DbpCommonDemoActivity extends ScopedElementsMixin(AdapterLitElement) { return { ...super.properties, lang: {type: String}, + langFile: {type: String, attribute: 'lang-file'}, entryPointUrl: {type: String, attribute: 'entry-point-url'}, }; } @@ -63,6 +65,7 @@ class DbpCommonDemoActivity extends ScopedElementsMixin(AdapterLitElement) { <dbp-common-demo id="demo" lang="${this.lang}" + lang-file="${this.langFile}" entry-point-url="${this.entryPointUrl}"></dbp-common-demo> `; } diff --git a/toolkit-showcase/src/i18n/de/translation.json b/toolkit-showcase/src/i18n/de/translation.json new file mode 100644 index 0000000000000000000000000000000000000000..d51b20bd684577823c83c44264231d626cbcfdbf --- /dev/null +++ b/toolkit-showcase/src/i18n/de/translation.json @@ -0,0 +1,3 @@ +{ + "toolkit-showcase": "Dieser Text wird mithilfe von i18n aus einer benutzerdefinierten Sprachdatei gelesen und ins Englische übersetzt wenn man die Sprache auf Englisch stellt." +} diff --git a/toolkit-showcase/src/i18n/en/translation.json b/toolkit-showcase/src/i18n/en/translation.json new file mode 100644 index 0000000000000000000000000000000000000000..789bde6fc2f8a1ae8df720d3f736c19b5bb2315e --- /dev/null +++ b/toolkit-showcase/src/i18n/en/translation.json @@ -0,0 +1,12 @@ +{ + "error": { + "connection-to-server-refused": "Connection to server refused!", + "summary": "An error occurred" + }, + "jsonld": { + "api-documentation-server": "Connection to apiDocumentation server {{apiDocUrl}} failed!", + "error-api-server": "Connection to api server {{apiUrl}} failed!", + "error-hydra-documentation-url-not-set": "Hydra apiDocumentation url was not set for server {{apiUrl}}!" + }, + "toolkit-showcase": "This text will be translated to german using i18n with a user defined language file when the language is changed to german." +}