Skip to content
Snippets Groups Projects
Commit 4e3c5c04 authored by Kocher, Manuel's avatar Kocher, Manuel
Browse files

Add support of translation overrides to dbp-translation

parent 4dbce52e
No related branches found
No related tags found
No related merge requests found
Pipeline #176850 failed
...@@ -75,6 +75,7 @@ export class AppShell extends ScopedElementsMixin(DBPLitElement) { ...@@ -75,6 +75,7 @@ export class AppShell extends ScopedElementsMixin(DBPLitElement) {
this.auth = {}; this.auth = {};
this.langFiles = ''; this.langFiles = '';
this.overrideFiles = '';
} }
static get scopedElements() { static get scopedElements() {
...@@ -273,6 +274,7 @@ export class AppShell extends ScopedElementsMixin(DBPLitElement) { ...@@ -273,6 +274,7 @@ export class AppShell extends ScopedElementsMixin(DBPLitElement) {
env: {type: String}, env: {type: String},
auth: {type: Object}, auth: {type: Object},
langFiles: {type: String, attribute: 'lang-files'}, langFiles: {type: String, attribute: 'lang-files'},
overrideFiles: {type: String, attribute: 'override-files'},
}; };
} }
......
...@@ -15,13 +15,14 @@ import { ...@@ -15,13 +15,14 @@ import {
Translation, Translation,
} from './index.js'; } from './index.js';
export class DbpCommonDemo extends ScopedElementsMixin(LitElement) { export class DbpCommonDemo extends ScopedElementsMixin(LitElement) {
constructor() { constructor() {
super(); super();
this._i18n = createInstance(); this._i18n = createInstance();
this.lang = this._i18n.language; this.lang = this._i18n.language;
this.noAuth = false; this.noAuth = false;
this.langFiles = '';
} }
static get scopedElements() { static get scopedElements() {
...@@ -47,7 +48,6 @@ export class DbpCommonDemo extends ScopedElementsMixin(LitElement) { ...@@ -47,7 +48,6 @@ export class DbpCommonDemo extends ScopedElementsMixin(LitElement) {
return { return {
lang: {type: String}, lang: {type: String},
noAuth: {type: Boolean, attribute: 'no-auth'}, noAuth: {type: Boolean, attribute: 'no-auth'},
langFiles: {type: String, attribute: 'lang-files'},
}; };
} }
...@@ -302,8 +302,10 @@ html { ...@@ -302,8 +302,10 @@ html {
</dbp-translated> </dbp-translated>
</div> </div>
<div class="control" id="dbp-translation-demo"> <div class="control" id="dbp-translation-demo">
<dbp-translation key="toolkit-showcase" subscribe="lang, lang-files"></dbp-translation> <p><dbp-translation key="toolkit-showcase" subscribe="lang, lang-files"></dbp-translation></p>
<dbp-translation key="toolkit-showcase-link" var='{"link1": "https://www.i18next.com/translation-function/interpolation"}' subscribe="lang, lang-files" unsafe></dbp-translation> <p><dbp-translation key="toolkit-showcase" subscribe="lang, lang-files, override-files"></dbp-translation></p>
<p><dbp-translation key="toolkit-showcase-link" var='{"link1": "https://www.i18next.com/translation-function/interpolation"}' subscribe="lang, lang-files" unsafe></dbp-translation></p>
<p><dbp-translation key="toolkit-showcase-link" var='{"link1": "https://dbp-demo.tugraz.at/"}' subscribe="lang, lang-files, override-files" unsafe></dbp-translation></p>
</div> </div>
</div> </div>
</section> </section>
......
...@@ -124,3 +124,48 @@ export function setOverrides(i18n, element, overrides) { ...@@ -124,3 +124,48 @@ export function setOverrides(i18n, element, overrides) {
} }
i18n.setDefaultNamespace(hasOverrides ? overrideNamespace : namespace); i18n.setDefaultNamespace(hasOverrides ? overrideNamespace : namespace);
} }
async function fetchOverridesByLanguage(overrides, lng) {
let result = await
fetch(overrides + lng +'/translation.json', {
headers: {'Content-Type': 'application/json'},
});
let json = await result.json();
return json;
}
/**
* Sets translation overrides for the given i18next instance. Any previously
* applied overrides will be removed first. So calling this with an empty overrides
* object is equal to removing all overrides.
*
* @param {i18next.i18n} i18n - The i18next instance
* @param {HTMLElement} element - The element at which the overrides are targeted
* @param {String} overridesFile - Path to the translation file containing the overrides
*/
export async function setOverridesByFile(i18n, element, overridesFile) {
// We add a special namespace which gets used with priority and falls back
// to the original one. This way we an change the overrides at runtime
// and can even remove them.
// The scoped mixin saves the real tag name under data-tag-name
let tagName = ((element.dataset && element.dataset.tagName) || element.tagName).toLowerCase();
let namespace = i18n.options.fallbackNS;
let overrideNamespace = getOverrideNamespace(namespace);
let hasOverrides = false;
for (let lng of i18n.languages) {
// get translation.json for each lang
let response = await fetchOverridesByLanguage(overridesFile, lng);
// remove old language
i18n.removeResourceBundle(lng, overrideNamespace);
// if no new translation is available, skip
if (response === undefined || response[tagName] === undefined) return;
// get new translation
let resources = response[tagName];
hasOverrides = true;
// set new translation
i18n.addResourceBundle(lng, overrideNamespace, resources);
i18n.setDefaultNamespace(hasOverrides ? overrideNamespace : namespace);
}
return i18n;
}
import {createInstance as _createInstance} from '../i18next.js'; import {createInstance as _createInstance, setOverridesByFile} from '../i18next.js';
import de from './i18n/de/translation.json'; import de from './i18n/de/translation.json';
import en from './i18n/en/translation.json'; import en from './i18n/en/translation.json';
...@@ -12,6 +12,7 @@ export async function createInstanceAsync(langFile, namespace) { ...@@ -12,6 +12,7 @@ export async function createInstanceAsync(langFile, namespace) {
namespace = 'translation' namespace = 'translation'
// check if a path to language files is given // check if a path to language files is given
if(langFile) { if(langFile) {
// request german lang file asynchronously // request german lang file asynchronously
let result = await let result = await
fetch(langFile + 'de/' + namespace +'.json', { fetch(langFile + 'de/' + namespace +'.json', {
...@@ -31,3 +32,5 @@ export async function createInstanceAsync(langFile, namespace) { ...@@ -31,3 +32,5 @@ export async function createInstanceAsync(langFile, namespace) {
return _createInstance({en: en, de: de}, 'de', 'en', namespace); return _createInstance({en: en, de: de}, 'de', 'en', namespace);
} }
export {setOverridesByFile};
...@@ -7,6 +7,5 @@ ...@@ -7,6 +7,5 @@
"api-documentation-server": "Verbindung zum apiDocumentation API Server {{apiDocUrl}} fehlgeschlagen!", "api-documentation-server": "Verbindung zum apiDocumentation API Server {{apiDocUrl}} fehlgeschlagen!",
"error-api-server": "Verbindung zum API Server {{apiUrl}} 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!" "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."
} }
...@@ -7,6 +7,5 @@ ...@@ -7,6 +7,5 @@
"api-documentation-server": "Connection to apiDocumentation server {{apiDocUrl}} failed!", "api-documentation-server": "Connection to apiDocumentation server {{apiDocUrl}} failed!",
"error-api-server": "Connection to api server {{apiUrl}} 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}}!" "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."
} }
...@@ -2,7 +2,20 @@ import {css, html} from 'lit'; ...@@ -2,7 +2,20 @@ import {css, html} from 'lit';
import {until} from 'lit/directives/until.js'; import {until} from 'lit/directives/until.js';
import {unsafeHTML} from 'lit/directives/unsafe-html.js'; import {unsafeHTML} from 'lit/directives/unsafe-html.js';
import DBPLitElement from '../dbp-lit-element'; import DBPLitElement from '../dbp-lit-element';
import {createInstanceAsync} from './i18n.js'; import {createInstanceAsync, setOverridesByFile} from './i18n.js';
let OVERRIDE = {
'de': {
'dbp-translation': {
'toolkit-showcase': 'testText'
}
},
'en': {
'dbp-translation': {
'toolkit-showcase': 'testText2'
}
}
}
export class Translation extends DBPLitElement { export class Translation extends DBPLitElement {
constructor() { constructor() {
...@@ -12,6 +25,7 @@ export class Translation extends DBPLitElement { ...@@ -12,6 +25,7 @@ export class Translation extends DBPLitElement {
this.langFiles = ''; this.langFiles = '';
this.interpolation = ''; this.interpolation = '';
this.namespace = ''; this.namespace = '';
this.overrideFiles = '';
} }
static get properties() { static get properties() {
...@@ -23,6 +37,7 @@ export class Translation extends DBPLitElement { ...@@ -23,6 +37,7 @@ export class Translation extends DBPLitElement {
interpolation: {type: Object, attribute: 'var'}, interpolation: {type: Object, attribute: 'var'},
unsafe: {type: Boolean, attribute: 'unsafe'}, unsafe: {type: Boolean, attribute: 'unsafe'},
namespace: {type: String, attribute: 'ns'}, namespace: {type: String, attribute: 'ns'},
overrideFiles: {type: String, attribute: 'override-files'},
}; };
} }
...@@ -37,11 +52,24 @@ export class Translation extends DBPLitElement { ...@@ -37,11 +52,24 @@ export class Translation extends DBPLitElement {
connectedCallback() { connectedCallback() {
super.connectedCallback(); super.connectedCallback();
if (this.namespace == '') if (this.namespace == '') {
this._i18n = createInstanceAsync(this.langFiles); this._i18n = createInstanceAsync(this.langFiles);
}
else { else {
this._i18n = createInstanceAsync(this.langFiles, this.namespace); this._i18n = createInstanceAsync(this.langFiles, this.namespace);
} }
let local = this;
let overrideFiles = this.overrideFiles;
if (this.overrideFiles) {
this._i18n.then(function(response) {
setOverridesByFile(response, local, overrideFiles).then(function(response) {
local._i18n = response;
local.requestUpdate();
});
})
}
} }
update(changedProperties) { update(changedProperties) {
...@@ -49,7 +77,7 @@ export class Translation extends DBPLitElement { ...@@ -49,7 +77,7 @@ export class Translation extends DBPLitElement {
changedProperties.forEach((oldValue, propName) => { changedProperties.forEach((oldValue, propName) => {
switch (propName) { switch (propName) {
case 'lang': case 'lang':
this._i18n.then(function(response) { Promise.resolve(this._i18n).then(function(response) {
response.changeLanguage(lang); response.changeLanguage(lang);
}); });
break; break;
...@@ -66,7 +94,7 @@ export class Translation extends DBPLitElement { ...@@ -66,7 +94,7 @@ export class Translation extends DBPLitElement {
let unsafe = this.unsafe; let unsafe = this.unsafe;
// async request to i18n translation // async request to i18n translation
const translation = this._i18n.then(function(response){ const translation = Promise.resolve(this._i18n).then(function(response){
if (interpolation && unsafe) if (interpolation && unsafe)
return unsafeHTML(response.t(key, interpolation)); return unsafeHTML(response.t(key, interpolation));
else if (interpolation) else if (interpolation)
......
...@@ -114,6 +114,7 @@ ...@@ -114,6 +114,7 @@
provider-root provider-root
lang="de" lang="de"
lang-files="<%= getPrivateUrl('i18n/') %>" lang-files="<%= getPrivateUrl('i18n/') %>"
override-files="<%= getPrivateUrl('i18n/overrides/') %>"
entry-point-url="<%= entryPointURL %>" entry-point-url="<%= entryPointURL %>"
nextcloud-auth-url="<%= nextcloudWebAppPasswordURL %>" nextcloud-auth-url="<%= nextcloudWebAppPasswordURL %>"
nextcloud-web-dav-url="<%= nextcloudWebDavURL %>" nextcloud-web-dav-url="<%= nextcloudWebDavURL %>"
......
{
"dbp-translation": {
"toolkit-showcase": "Überschriebener i18n toolkit-showcase Text",
"toolkit-showcase-link": "Überschriebener i18n toolkit-showcase-link Text mit <a href=\"{{- link1}}\">TestLink</a>"
}
}
{
"dbp-translation": {
"toolkit-showcase": "Overriden i18n toolkit-showcase text",
"toolkit-showcase-link": "Overriden i18n toolkit-showcase-link text with a <a href=\"{{- link1}}\">test link</a>"
}
}
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment