UNPKG

@nextcloud/l10n

Version:

Nextcloud localization and translation helpers for apps and libraries

354 lines (353 loc) 10.5 kB
import { generateFilePath } from "@nextcloud/router"; import DOMPurify from "dompurify"; import escapeHTML from "escape-html"; /*! * SPDX-FileCopyrightText: 2019 Nextcloud GmbH and Nextcloud contributors * SPDX-License-Identifier: GPL-3.0-or-later */ function getLocale() { return globalThis._nc_l10n_locale; } function getCanonicalLocale() { return getLocale().replaceAll(/_/g, "-"); } function setLocale(locale) { globalThis._nc_l10n_locale = locale; if (typeof document !== "undefined") { document.documentElement.dataset.locale = locale; } } function getLanguage() { return globalThis._nc_l10n_language; } function setLanguage(lang) { globalThis._nc_l10n_language = lang; if (typeof document !== "undefined") { document.documentElement.lang = lang; } } function isRTL(language) { const languageCode = language || getLanguage(); const rtlLanguages = [ "ae", // Avestan "ar", // 'العربية', Arabic "arc", // Aramaic "arz", // 'مصرى', Egyptian "bcc", // 'بلوچی مکرانی', Southern Balochi "bqi", // 'بختياري', Bakthiari "ckb", // 'Soranî / کوردی', Sorani "dv", // Dhivehi "fa", // 'فارسی', Persian "glk", // 'گیلکی', Gilaki "ha", // 'هَوُسَ', Hausa "he", // 'עברית', Hebrew "khw", // 'کھوار', Khowar "ks", // 'कॉशुर / کٲشُر', Kashmiri "ku", // 'Kurdî / كوردی', Kurdish "mzn", // 'مازِرونی', Mazanderani "nqo", // 'ߒߞߏ', N’Ko "pnb", // 'پنجابی', Western Punjabi "ps", // 'پښتو', Pashto, "sd", // 'سنڌي', Sindhi "ug", // 'Uyghurche / ئۇيغۇرچە', Uyghur "ur", // 'اردو', Urdu "ur-PK", // 'اردو', Urdu (nextcloud BCP47 variant) "uz-AF", // 'اوزبیکی', Uzbek Afghan "yi" // 'ייִדיש', Yiddish ]; return rtlLanguages.includes(languageCode); } globalThis._nc_l10n_locale ??= typeof document !== "undefined" && document.documentElement.dataset.locale || Intl.DateTimeFormat().resolvedOptions().locale.replaceAll(/-/g, "_"); globalThis._nc_l10n_language ??= typeof document !== "undefined" && document.documentElement.lang || (globalThis.navigator?.language ?? "en"); /*! * SPDX-FileCopyrightText: 2022 Nextcloud GmbH and Nextcloud contributors * SPDX-License-Identifier: GPL-3.0-or-later */ function hasAppTranslations(appId) { return appId in globalThis._oc_l10n_registry_translations && appId in globalThis._oc_l10n_registry_plural_functions; } function registerAppTranslations(appId, translations, pluralFunction) { if (appId === "__proto__" || appId === "constructor" || appId === "prototype") { throw new Error("Invalid appId"); } globalThis._oc_l10n_registry_translations[appId] = { ...globalThis._oc_l10n_registry_translations[appId] || {}, ...translations }; globalThis._oc_l10n_registry_plural_functions[appId] = pluralFunction; } function unregisterAppTranslations(appId) { delete globalThis._oc_l10n_registry_translations[appId]; delete globalThis._oc_l10n_registry_plural_functions[appId]; } function getAppTranslations(appId) { return { translations: globalThis._oc_l10n_registry_translations[appId] ?? {}, pluralFunction: globalThis._oc_l10n_registry_plural_functions[appId] ?? ((number) => number) }; } globalThis._oc_l10n_registry_translations ??= {}; globalThis._oc_l10n_registry_plural_functions ??= {}; /*! * SPDX-FileCopyrightText: 2019 Nextcloud GmbH and Nextcloud contributors * SPDX-License-Identifier: GPL-3.0-or-later */ function translate(app, text, placeholdersOrNumber, optionsOrNumber, options) { const vars = typeof placeholdersOrNumber === "object" ? placeholdersOrNumber : void 0; const number = typeof optionsOrNumber === "number" ? optionsOrNumber : typeof placeholdersOrNumber === "number" ? placeholdersOrNumber : void 0; const allOptions = { // defaults escape: true, sanitize: true, // overwrite with user config ...typeof options === "object" ? options : typeof optionsOrNumber === "object" ? optionsOrNumber : {} }; const identity = (value) => value; const optSanitize = (allOptions.sanitize ? DOMPurify.sanitize : identity) || identity; const optEscape = allOptions.escape ? escapeHTML : identity; const isValidReplacement = (value) => typeof value === "string" || typeof value === "number"; const _build = (text2, vars2, number2) => { return text2.replace(/%n/g, "" + number2).replace(/{([^{}]*)}/g, (match, key) => { if (vars2 === void 0 || !(key in vars2)) { return optEscape(match); } const replacement = vars2[key]; if (isValidReplacement(replacement)) { return optEscape(`${replacement}`); } else if (typeof replacement === "object" && isValidReplacement(replacement.value)) { const escape = replacement.escape !== false ? escapeHTML : identity; return escape(`${replacement.value}`); } else { return optEscape(match); } }); }; const bundle = options?.bundle ?? getAppTranslations(app); let translation = bundle.translations[text] || text; translation = Array.isArray(translation) ? translation[0] : translation; if (typeof vars === "object" || number !== void 0) { return optSanitize(_build( translation, vars, number )); } else { return optSanitize(translation); } } function translatePlural(app, textSingular, textPlural, number, vars, options) { const identifier = "_" + textSingular + "_::_" + textPlural + "_"; const bundle = options?.bundle ?? getAppTranslations(app); const value = bundle.translations[identifier]; if (typeof value !== "undefined") { const translation = value; if (Array.isArray(translation)) { const plural = bundle.pluralFunction(number); return translate(app, translation[plural], vars, number, options); } } if (number === 1) { return translate(app, textSingular, vars, number, options); } else { return translate(app, textPlural, vars, number, options); } } async function loadTranslations(appName, callback) { if (hasAppTranslations(appName) || getLanguage() === "en") { const bundle = getAppTranslations(appName); callback?.(bundle); return bundle; } let response; try { const url = generateFilePath(appName, "l10n", getLanguage() + ".json"); response = await fetch(url); } catch (error) { throw new Error("Network error", { cause: error }); } if (response.ok) { try { const bundle = await response.json(); if (typeof bundle.translations === "object") { register(appName, bundle.translations); callback?.(bundle); return bundle; } } catch { } throw new Error("Invalid content of translation bundle"); } else { throw new Error(response.statusText); } } function register(appName, bundle) { registerAppTranslations(appName, bundle, getPlural); } function unregister(appName) { return unregisterAppTranslations(appName); } function getPlural(number, language = getLanguage()) { if (language === "pt-BR") { language = "xbr"; } if (language.length > 3) { language = language.substring(0, language.lastIndexOf("-")); } switch (language) { case "az": case "bo": case "dz": case "id": case "ja": case "jv": case "ka": case "km": case "kn": case "ko": case "ms": case "th": case "tr": case "vi": case "zh": return 0; case "af": case "bn": case "bg": case "ca": case "da": case "de": case "el": case "en": case "eo": case "es": case "et": case "eu": case "fa": case "fi": case "fo": case "fur": case "fy": case "gl": case "gu": case "ha": case "he": case "hu": case "is": case "it": case "ku": case "lb": case "ml": case "mn": case "mr": case "nah": case "nb": case "ne": case "nl": case "nn": case "no": case "oc": case "om": case "or": case "pa": case "pap": case "ps": case "pt": case "so": case "sq": case "sv": case "sw": case "ta": case "te": case "tk": case "ur": case "zu": return number === 1 ? 0 : 1; case "am": case "bh": case "fil": case "fr": case "gun": case "hi": case "hy": case "ln": case "mg": case "nso": case "xbr": case "ti": case "wa": return number === 0 || number === 1 ? 0 : 1; case "be": case "bs": case "hr": case "ru": case "sh": case "sr": case "uk": return number % 10 === 1 && number % 100 !== 11 ? 0 : number % 10 >= 2 && number % 10 <= 4 && (number % 100 < 10 || number % 100 >= 20) ? 1 : 2; case "cs": case "sk": return number === 1 ? 0 : number >= 2 && number <= 4 ? 1 : 2; case "ga": return number === 1 ? 0 : number === 2 ? 1 : 2; case "lt": return number % 10 === 1 && number % 100 !== 11 ? 0 : number % 10 >= 2 && (number % 100 < 10 || number % 100 >= 20) ? 1 : 2; case "sl": return number % 100 === 1 ? 0 : number % 100 === 2 ? 1 : number % 100 === 3 || number % 100 === 4 ? 2 : 3; case "mk": return number % 10 === 1 ? 0 : 1; case "mt": return number === 1 ? 0 : number === 0 || number % 100 > 1 && number % 100 < 11 ? 1 : number % 100 > 10 && number % 100 < 20 ? 2 : 3; case "lv": return number === 0 ? 0 : number % 10 === 1 && number % 100 !== 11 ? 1 : 2; case "pl": return number === 1 ? 0 : number % 10 >= 2 && number % 10 <= 4 && (number % 100 < 12 || number % 100 > 14) ? 1 : 2; case "cy": return number === 1 ? 0 : number === 2 ? 1 : number === 8 || number === 11 ? 2 : 3; case "ro": return number === 1 ? 0 : number === 0 || number % 100 > 0 && number % 100 < 20 ? 1 : 2; case "ar": return number === 0 ? 0 : number === 1 ? 1 : number === 2 ? 2 : number % 100 >= 3 && number % 100 <= 10 ? 3 : number % 100 >= 11 && number % 100 <= 99 ? 4 : 5; default: return 0; } } export { getLanguage as a, getLocale as b, setLanguage as c, translatePlural as d, getPlural as e, getCanonicalLocale as g, isRTL as i, loadTranslations as l, register as r, setLocale as s, translate as t, unregister as u }; //# sourceMappingURL=translation-DoG5ZELJ.mjs.map