UNPKG

@schukai/monster

Version:

Monster is a simple library for creating fast, robust and lightweight websites.

126 lines (112 loc) 3.65 kB
/** * Copyright © Volker Schukai and all contributing authors, {{copyRightYear}}. All rights reserved. * Node module: @schukai/monster * * This source code is licensed under the GNU Affero General Public License version 3 (AGPLv3). * The full text of the license can be found at: https://www.gnu.org/licenses/agpl-3.0.en.html * * For those who do not wish to adhere to the AGPLv3, a commercial license is available. * Acquiring a commercial license allows you to use this software without complying with the AGPLv3 terms. * For more information about purchasing a commercial license, please contact Volker Schukai. * * SPDX-License-Identifier: AGPL-3.0 */ import { instanceSymbol, internalSymbol } from "../constants.mjs"; import { extend } from "../data/extend.mjs"; import { Formatter as TextFormatter } from "../text/formatter.mjs"; import { validateInstance, validateString } from "../types/validate.mjs"; import { Translations } from "./translations.mjs"; export { Formatter }; /** * @private * @type {symbol} */ const internalTranslationSymbol = Symbol("internalTranslation"); /** * The Formatter extends the Text.Formatter with the possibility to replace the key by a translation. * * @fragments /fragments/libraries/i18n/formatter/ * * @license AGPLv3 * @since 1.26.0 * @copyright Volker Schukai */ class Formatter extends TextFormatter { /** * Default values for the markers are `${` and `}` * * @param {object} object * @param {Translations} translation * @param {object} [options] * @throws {TypeError} value is not an object */ constructor(object, translation, options) { super(object, options); this[internalTranslationSymbol] = validateInstance( translation, Translations, ); } /** * This method is called by the `instanceof` operator. * @return {symbol} * @since 3.27.0 */ static get [instanceSymbol]() { return Symbol.for("@schukai/monster/i18n/formatter@@instance"); } /** * @property {object} marker * @property {array} marker.open=["i18n{","${"] * @property {array} marker.close=["${"] * @property {object} parameter * @property {string} parameter.delimiter="::" * @property {string} parameter.assignment="=" * @property {object} callbacks * @property {function} callbacks.i18n=()=>{} */ get defaults() { return extend({}, super.defaults, { callbacks: { i18n: (value) => { return this[internalTranslationSymbol].getText(validateString(value)); }, }, marker: { open: ["i18n{", "${"], close: ["}"], }, }); } /** * * @param {string} text * @return {string} * @throws {TypeError} value is not a string * @throws {Error} too deep nesting * @throws {Error} key not found * @throws {Error} the closing marker is missing */ format(text) { validateString(text); const openMarker = this[internalSymbol]["marker"]["open"]?.[0]; const closeMarker = this[internalSymbol]["marker"]["close"]?.[0]; if (text.indexOf(openMarker) === 0) { text = text.substring(openMarker.length); if (text.indexOf(closeMarker) === text.length - closeMarker.length) { text = text.substring(0, text.length - closeMarker.length); } else { throw new Error("the closing marker is missing"); } } const parts = validateString(text).split("::"); const translationKey = parts.shift().trim(); // key value delimiter const parameter = parts.join("::").trim(); let assembledText = `${openMarker}static:${translationKey} | call:i18n`; if (parameter.length > 0) { assembledText += `::${parameter}`; } assembledText += closeMarker; return super.format(assembledText); } }