Skip to content
Snippets Groups Projects
Verified Commit fbbee53d authored by Volker Schukai's avatar Volker Schukai :alien:
Browse files

feat: document translations

parent de48d5f8
No related branches found
No related tags found
No related merge requests found
...@@ -165,6 +165,9 @@ function transform(value) { ...@@ -165,6 +165,9 @@ function transform(value) {
let key; let key;
let defaultValue; let defaultValue;
let element;
let attribute;
switch (this.command) { switch (this.command) {
case "static": case "static":
return this.args.join(":"); return this.args.join(":");
...@@ -189,9 +192,11 @@ function transform(value) { ...@@ -189,9 +192,11 @@ function transform(value) {
validateInteger(n); validateInteger(n);
return n; return n;
case "to-json":
case "tojson": case "tojson":
return JSON.stringify(value); return JSON.stringify(value);
case "from-json":
case "fromjson": case "fromjson":
return JSON.parse(value); return JSON.parse(value);
...@@ -456,6 +461,44 @@ function transform(value) { ...@@ -456,6 +461,44 @@ function transform(value) {
throw new Error("type not supported"); throw new Error("type not supported");
// case "element-by-id":
// return getGlobal("document").getElementById(convertToString(value));
//
// case "element-query":
// case "element-query-selector":
// return getGlobal("document").querySelector(convertToString(value));
//
// case "element-value":
// return getGlobal("document").getElementById(convertToString(value))?.value;
//
// case "element-text":
// return getGlobal("document").getElementById(convertToString(value))?.innerText;
//
// case "element-html":
// return getGlobal("document").getElementById(convertToString(value))?.innerHTML;
//
// case "element-attribute":
// let element = getGlobal("document").getElementById(convertToString(value));
// let attribute = args.shift();
// if (attribute === undefined) {
// throw new Error("missing attribute parameter");
// }
// return element?.getAttribute(attribute);
case "translation":
element = getGlobal("document").getElementById(convertToString(value));
if (element === undefined) {
throw new Error("missing element parameter");
}
attribute = args.shift();
if (attribute === undefined) {
throw new Error("missing attribute parameter");
}
return element?.getAttribute(attribute);
default: default:
throw new Error(`unknown command ${this.command}`); throw new Error(`unknown command ${this.command}`);
} }
......
...@@ -11,7 +11,16 @@ import {BaseWithOptions} from "../types/basewithoptions.mjs"; ...@@ -11,7 +11,16 @@ import {BaseWithOptions} from "../types/basewithoptions.mjs";
import {Locale} from "./locale.mjs"; import {Locale} from "./locale.mjs";
import {Translations} from "./translations.mjs"; import {Translations} from "./translations.mjs";
export { Provider }; export {Provider, translationsLinkSymbol};
/**
* @memberOf Monster.I18n
* @type {symbol}
* @license AGPLv3
* @since 3.9.0
* @private
*/
const translationsLinkSymbol = Symbol.for("@schukai/monster/i18n/translations@@link");
/** /**
* A provider makes a translation object available. * A provider makes a translation object available.
...@@ -28,6 +37,11 @@ class Provider extends BaseWithOptions { ...@@ -28,6 +37,11 @@ class Provider extends BaseWithOptions {
* @return {Promise} * @return {Promise}
*/ */
getTranslations(locale) { getTranslations(locale) {
if (locale === undefined) {
locale = getLocaleOfDocument();
}
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
try { try {
resolve(new Translations(locale)); resolve(new Translations(locale));
...@@ -36,4 +50,56 @@ class Provider extends BaseWithOptions { ...@@ -36,4 +50,56 @@ class Provider extends BaseWithOptions {
} }
}); });
} }
/**
* @param {Locale|string} locale
* @param {HTMLElement} element
* @return {Provider}
*/
assignToElement(locale, element) {
if (locale === undefined) {
locale = getLocaleOfDocument();
}
if (!(locale instanceof Locale)) {
throw new Error("Locale is not an instance of Locale");
}
if (!(element instanceof HTMLElement)) {
element = document.querySelector("body");
}
if (!(element instanceof HTMLElement)) {
throw new Error("Element is not an HTMLElement");
}
return this.getTranslations(locale).then((obj) => {
let translations = null;
if (hasObjectLink(element, translationsLinkSymbol)) {
const objects = getLinkedObjects(element, translationsLinkSymbol);
for (const o of objects) {
if (o instanceof Translations) {
translations = o;
break;
}
}
if (!(translations instanceof Translations)) {
throw new Error("Object is not an instance of Translations");
}
translations.assignTranslations(obj);
} else {
addToObjectLink(element, translationsLinkSymbol, obj);
}
return obj;
});
}
} }
...@@ -42,22 +42,27 @@ class Embed extends Provider { ...@@ -42,22 +42,27 @@ class Embed extends Provider {
* new Embed('translations') * new Embed('translations')
* ``` * ```
* *
* @param {string} id * @param {HTMLElement|string} elementOrId
* @param {Object} options * @param {Object} options
*/ */
constructor(id, options) { constructor(elementOrId, options) {
super(options); super(options);
if (options === undefined) { if (options === undefined) {
options = {}; options = {};
} }
validateString(id); if (elementOrId instanceof HTMLElement) {
/**
* @property {HTMLElement|string}
*/
this.translateElement = elementOrId;
} else {
/** /**
* @property {string} * @property {HTMLElement|string}
*/ */
this.textId = id; this.translateElement = getDocument().getElementById(validateString(elementOrId));
}
/** /**
* @private * @private
...@@ -86,16 +91,25 @@ class Embed extends Provider { ...@@ -86,16 +91,25 @@ class Embed extends Provider {
} }
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
let text = getGlobalObject("document").getElementById(this.textId);
if (text === null) { if (this.translateElement === null) {
reject(new Error("Text not found")); reject(new Error("Text not found"));
return; return;
} }
if (!(this.translateElement instanceof HTMLScriptElement)) {
reject(new Error("Element is not a script tag"));
return;
}
if (this.translateElement.type !== "application/json") {
reject(new Error("Element is not a script tag with type application/json"));
return;
}
let translations = null; let translations = null;
try { try {
translations = JSON.parse(text.innerHTML); translations = JSON.parse(this.translateElement.innerHTML);
} catch (e) { } catch (e) {
reject(e); reject(e);
return; return;
...@@ -112,4 +126,38 @@ class Embed extends Provider { ...@@ -112,4 +126,38 @@ class Embed extends Provider {
resolve(t); resolve(t);
}); });
} }
/**
* Initializes the translations for the current document.
*
* `script[data-monster-role=translations]` is searched for and the translations are assigned to the element.
*
* @param element
* @returns {Promise<unknown[]>}
*/
static assignTranslationsToElement(element) {
const d = getDocument()
if (!(element instanceof HTMLElement)) {
element = d.querySelector("body");
}
const list = d.querySelectorAll("script[data-monster-role=translations]");
if (list === null) {
return;
}
const promises = [];
let result
list.forEach((translationElement) => {
const p = new Embed(translationElement);
promises.push(p.assignToElement(undefined, element));
});
return Promise.all(promises);
}
} }
...@@ -34,11 +34,12 @@ class Translations extends Base { ...@@ -34,11 +34,12 @@ class Translations extends Base {
constructor(locale) { constructor(locale) {
super(); super();
if (isString(locale)) { if (locale instanceof Locale) {
locale = parseLocale(locale); this.locale = locale;
} else {
this.locale = parseLocale(validateString(locale));
} }
this.locale = validateInstance(locale, Locale);
this.storage = new Map(); this.storage = new Map();
} }
...@@ -170,6 +171,13 @@ class Translations extends Base { ...@@ -170,6 +171,13 @@ class Translations extends Base {
assignTranslations(translations) { assignTranslations(translations) {
validateObject(translations); validateObject(translations);
if (translations instanceof Translations) {
translations.storage.forEach((v, k) => {
this.setText(k, v);
});
return this;
}
for (const [k, v] of Object.entries(translations)) { for (const [k, v] of Object.entries(translations)) {
this.setText(k, v); this.setText(k, v);
} }
...@@ -177,3 +185,41 @@ class Translations extends Base { ...@@ -177,3 +185,41 @@ class Translations extends Base {
return this; return this;
} }
} }
/**
* Returns the translations for the current document.
*
* @param element
* @returns {*}
* @throws {Error} Element is not an HTMLElement
* @throws {Error} Missing translations
*/
function getDocumentTranslations(element) {
const d = getDocument()
if (!(element instanceof HTMLElement)) {
element = d.querySelector("body");
}
if (!(element instanceof HTMLElement)) {
throw new Error("Element is not an HTMLElement");
}
if (!hasObjectLink(element, translationsLinkSymbol)) {
throw new Error("Missing translations");
}
let obj = getLinkedObjects(element, translationsLinkSymbol);
for (const t of obj) {
if (t instanceof Translations) {
return t;
}
}
throw new Error("Missing translations");
}
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment