@nextcloud/l10n
Version:
Nextcloud localization and translation helpers for apps and libraries
346 lines (345 loc) • 10.1 kB
JavaScript
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
*/
const environmentLocale = Intl.DateTimeFormat().resolvedOptions().locale;
function getLocale() {
return document.documentElement.dataset.locale || environmentLocale.replaceAll(/-/g, "_");
}
function getCanonicalLocale() {
return getLocale().replaceAll(/_/g, "-");
}
function getLanguage() {
return document.documentElement.lang || navigator.language;
}
function isRTL(language) {
const languageCode = language || getLanguage();
const rtlLanguages = [
/* eslint-disable no-multi-spaces */
"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
/* eslint-enable no-multi-spaces */
];
return rtlLanguages.includes(languageCode);
}
/*!
* SPDX-FileCopyrightText: 2022 Nextcloud GmbH and Nextcloud contributors
* SPDX-License-Identifier: GPL-3.0-or-later
*/
function hasAppTranslations(appId) {
return window._oc_l10n_registry_translations?.[appId] !== void 0 && window._oc_l10n_registry_plural_functions?.[appId] !== void 0;
}
function registerAppTranslations(appId, translations, pluralFunction) {
if (appId === "__proto__" || appId === "constructor" || appId === "prototype") {
throw new Error("Invalid appId");
}
window._oc_l10n_registry_translations = Object.assign(
window._oc_l10n_registry_translations || {},
{
[appId]: Object.assign(window._oc_l10n_registry_translations?.[appId] || {}, translations)
}
);
window._oc_l10n_registry_plural_functions = Object.assign(
window._oc_l10n_registry_plural_functions || {},
{
[appId]: pluralFunction
}
);
}
function unregisterAppTranslations(appId) {
delete window._oc_l10n_registry_translations?.[appId];
delete window._oc_l10n_registry_plural_functions?.[appId];
}
function getAppTranslations(appId) {
return {
translations: window._oc_l10n_registry_translations?.[appId] ?? {},
pluralFunction: window._oc_l10n_registry_plural_functions?.[appId] ?? ((number) => number)
};
}
/*!
* 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;
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");
}
if (response.ok) {
try {
const bundle = await response.json();
if (typeof bundle.translations === "object") {
register(appName, bundle.translations);
callback?.(bundle);
return bundle;
}
} catch (error) {
}
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,
translatePlural as c,
getPlural as d,
getCanonicalLocale as g,
isRTL as i,
loadTranslations as l,
register as r,
translate as t,
unregister as u
};
//# sourceMappingURL=translation-DUYoTdjY.mjs.map