/** * @author schukai GmbH */ import {Base} from "../types/base.mjs"; import {isObject, isString} from "../types/is.mjs"; import {validateInstance, validateInteger, validateObject, validateString} from "../types/validate.mjs"; import {Locale, parseLocale} from "./locale.mjs"; export {Translations} /** * With this class you can manage translations and access the keys. * * ``` * <script type="module"> * import {Translations} from 'https://cdn.jsdelivr.net/npm/@schukai/monster@latest/source/i18n/translations.mjs'; * new Translations() * </script> * ``` * * @example * * import {Translations} from 'https://cdn.jsdelivr.net/npm/@schukai/monster@latest/source/i18n/translations.mjs'; * import {parseLocale} from 'https://cdn.jsdelivr.net/npm/@schukai/monster@latest/source/i18n/locale.mjs'; * * const translation = new Translations(parseLocale('en-GB')); * * translation.assignTranslations({ * text1: "click", * text2: { * 'one': 'click once', * 'other': 'click n times' * } * }); * * console.log(translation.getText('text1')); * // ↦ click * * console.log(translation.getPluralRuleText('text2',1)); * // -> click once * console.log(translation.getPluralRuleText('text2',2)); * // -> click n times * * @since 1.13.0 * @copyright schukai GmbH * @memberOf Monster.I18n * @see https://datatracker.ietf.org/doc/html/rfc3066 */ class Translations extends Base { /** * * @param {Locale} locale */ constructor(locale) { super(); if (isString(locale)) { locale = parseLocale(locale); } this.locale = validateInstance(locale, Locale); this.storage = new Map(); } /** * Fetches a text using the specified key. * If no suitable key is found, `defaultText` is taken. * * @param {string} key * @param {string|undefined} defaultText * @return {string} * @throws {Error} key not found */ getText(key, defaultText) { if (!this.storage.has(key)) { if (defaultText === undefined) { throw new Error('key ' + key + ' not found'); } return validateString(defaultText); } let r = this.storage.get(key); if (isObject(r)) { return this.getPluralRuleText(key, 'other', defaultText); } return this.storage.get(key); } /** * A number `count` can be passed to this method. In addition to a number, one of the keywords can also be passed directly. * "zero", "one", "two", "few", "many" and "other". Remember: not every language has all rules. * * The appropriate text for this number is then selected. If no suitable key is found, `defaultText` is taken. * * @param {string} key * @param {integer|count} count * @param {string|undefined} defaultText * @return {string} */ getPluralRuleText(key, count, defaultText) { if (!this.storage.has(key)) { return validateString(defaultText); } let r = validateObject(this.storage.get(key)); let keyword; if (isString(count)) { keyword = count.toLocaleString(); } else { count = validateInteger(count); if (count === 0) { // special handlig for zero count if (r.hasOwnProperty('zero')) { return validateString(r['zero']); } } keyword = new Intl.PluralRules(this.locale.toString()).select(validateInteger(count)); } if (r.hasOwnProperty(keyword)) { return validateString(r[keyword]); } if (r.hasOwnProperty(DEFAULT_KEY)) { return validateString(r[DEFAULT_KEY]); } return validateString(defaultText); } /** * Set a text for a key * * ``` * translations.setText("text1": "Make my day!"); * // plural rules * translations.setText("text6": { * "zero": "There are no files on Disk.", * "one": "There is one file on Disk.", * "other": "There are files on Disk." * "default": "There are files on Disk." * }); * ``` * * @param {string} key * @param {string|object} text * @return {Translations} * @throws {TypeError} value is not a string or object */ setText(key, text) { if (isString(text) || isObject(text)) { this.storage.set(validateString(key), text); return this; } throw new TypeError('value is not a string or object'); } /** * This method can be used to transfer overlays from an object. The keys are transferred and the values are entered as text. * * The values can either be character strings or, in the case of texts with plural forms, objects. The plural forms must be stored as text via a standard key "zero", "one", "two", "few", "many" and "other". * * Additionally, the key default can be specified, which will be used if no other key fits. * * In some languages, like for example in german, there is no own more number at the value 0. In these languages the function applies additionally zero. * * ``` * translations.assignTranslations({ * "text1": "Make my day!", * "text2": "I'll be back!", * "text6": { * "zero": "There are no files on Disk.", * "one": "There is one file on Disk.", * "other": "There are files on Disk." * "default": "There are files on Disk." * }); * ``` * * @param {object} translations * @return {Translations} */ assignTranslations(translations) { validateObject(translations); for (const [k, v] of Object.entries(translations)) { this.setText(k, v); } return this; } }