@nextcloud/l10n
Advanced tools
Comparing version 1.6.0 to 2.0.0-beta.0
@@ -5,2 +5,18 @@ # Changelog | ||
## 2.0.0-beta.0 - 2023-01-11 | ||
[Full Changelog](https://github.com/nextcloud/nextcloud-l10n/compare/v1.6.0...v2.0.0-beta.0) | ||
### Fixed | ||
- Provide all translation related functions [\#542](https://github.com/nextcloud/nextcloud-l10n/pull/542) ([susnux](https://github.com/susnux)) | ||
- Fix building and deploying documentaton [\#546](https://github.com/nextcloud/nextcloud-l10n/pull/546) ([susnux](https://github.com/susnux)) | ||
## 1.6.0 - 2022-05-10 | ||
### Changed | ||
- Dependency updates | ||
- Remove dependency on OC for `getLocale` and `getLanguage` | ||
## 1.5.0 - 2022-05-10 | ||
Superseeded by v1.6.0 as the release was empty. | ||
## 1.4.1 - 2020-09-07 | ||
@@ -7,0 +23,0 @@ ### Changed |
@@ -1,120 +0,98 @@ | ||
"use strict"; | ||
'use strict'; | ||
Object.defineProperty(exports, "__esModule", { | ||
value: true | ||
}); | ||
exports.getGettextBuilder = getGettextBuilder; | ||
var GetText = require('node-gettext'); | ||
require('@nextcloud/router'); | ||
require('dompurify'); | ||
require('escape-html'); | ||
require("core-js/modules/es.regexp.exec.js"); | ||
/** | ||
* Returns the user's language | ||
*/ | ||
function getLanguage() { | ||
return document.documentElement.lang || 'en'; | ||
} | ||
require("core-js/modules/es.string.replace.js"); | ||
/** | ||
* This module provides functionality to translate applications independent from Nextcloud | ||
* | ||
* @packageDocumentation | ||
* @module @nextcloud/l10n/gettext | ||
* @example | ||
* ```js | ||
import { getGettextBuilder } from '@nextcloud/l10n/gettext' | ||
require("core-js/modules/es.object.define-property.js"); | ||
const gt = getGettextBuilder() | ||
.detectLocale() // or use setLanguage() | ||
.addTranslation(/* ... *\/) | ||
.build() | ||
require("core-js/modules/es.object.to-string.js"); | ||
require("core-js/modules/es.regexp.to-string.js"); | ||
var _nodeGettext = _interopRequireDefault(require("node-gettext")); | ||
var _ = require("."); | ||
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } | ||
function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } | ||
function _defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } | ||
function _createClass(Constructor, protoProps, staticProps) { if (protoProps) _defineProperties(Constructor.prototype, protoProps); if (staticProps) _defineProperties(Constructor, staticProps); Object.defineProperty(Constructor, "prototype", { writable: false }); return Constructor; } | ||
var GettextBuilder = /*#__PURE__*/function () { | ||
function GettextBuilder() { | ||
_classCallCheck(this, GettextBuilder); | ||
this.translations = {}; | ||
this.debug = false; | ||
} | ||
_createClass(GettextBuilder, [{ | ||
key: "setLanguage", | ||
value: function setLanguage(language) { | ||
this.locale = language; | ||
return this; | ||
gt.gettext('some string to translate') | ||
``` | ||
*/ | ||
/** | ||
* @notExported | ||
*/ | ||
class GettextBuilder { | ||
constructor() { | ||
this.translations = {}; | ||
this.debug = false; | ||
} | ||
}, { | ||
key: "detectLocale", | ||
value: function detectLocale() { | ||
return this.setLanguage((0, _.getLanguage)().replace('-', '_')); | ||
setLanguage(language) { | ||
this.locale = language; | ||
return this; | ||
} | ||
}, { | ||
key: "addTranslation", | ||
value: function addTranslation(language, data) { | ||
this.translations[language] = data; | ||
return this; | ||
/** Try to detect locale from context with `en` as fallback value */ | ||
detectLocale() { | ||
return this.setLanguage(getLanguage().replace('-', '_')); | ||
} | ||
}, { | ||
key: "enableDebugMode", | ||
value: function enableDebugMode() { | ||
this.debug = true; | ||
return this; | ||
addTranslation(language, data) { | ||
this.translations[language] = data; | ||
return this; | ||
} | ||
}, { | ||
key: "build", | ||
value: function build() { | ||
return new GettextWrapper(this.locale || 'en', this.translations, this.debug); | ||
enableDebugMode() { | ||
this.debug = true; | ||
return this; | ||
} | ||
}]); | ||
return GettextBuilder; | ||
}(); | ||
var GettextWrapper = /*#__PURE__*/function () { | ||
function GettextWrapper(locale, data, debug) { | ||
_classCallCheck(this, GettextWrapper); | ||
this.gt = new _nodeGettext.default({ | ||
debug: debug, | ||
sourceLocale: 'en' | ||
}); | ||
for (var key in data) { | ||
this.gt.addTranslations(key, 'messages', data[key]); | ||
build() { | ||
return new GettextWrapper(this.locale || 'en', this.translations, this.debug); | ||
} | ||
this.gt.setLocale(locale); | ||
} | ||
_createClass(GettextWrapper, [{ | ||
key: "subtitudePlaceholders", | ||
value: function subtitudePlaceholders(translated, vars) { | ||
return translated.replace(/{([^{}]*)}/g, function (a, b) { | ||
var r = vars[b]; | ||
if (typeof r === 'string' || typeof r === 'number') { | ||
return r.toString(); | ||
} else { | ||
return a; | ||
} | ||
/** | ||
* @notExported | ||
*/ | ||
class GettextWrapper { | ||
constructor(locale, data, debug) { | ||
this.gt = new GetText({ | ||
debug, | ||
sourceLocale: 'en', | ||
}); | ||
for (let key in data) { | ||
this.gt.addTranslations(key, 'messages', data[key]); | ||
} | ||
}); | ||
this.gt.setLocale(locale); | ||
} | ||
}, { | ||
key: "gettext", | ||
value: function gettext(original) { | ||
var placeholders = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {}; | ||
return this.subtitudePlaceholders(this.gt.gettext(original), placeholders); | ||
subtitudePlaceholders(translated, vars) { | ||
return translated.replace(/{([^{}]*)}/g, (a, b) => { | ||
const r = vars[b]; | ||
if (typeof r === 'string' || typeof r === 'number') { | ||
return r.toString(); | ||
} | ||
else { | ||
return a; | ||
} | ||
}); | ||
} | ||
}, { | ||
key: "ngettext", | ||
value: function ngettext(singular, plural, count) { | ||
var placeholders = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : {}; | ||
return this.subtitudePlaceholders(this.gt.ngettext(singular, plural, count).replace(/%n/g, count.toString()), placeholders); | ||
/** Get translated string (singular form), optionally with placeholders */ | ||
gettext(original, placeholders = {}) { | ||
return this.subtitudePlaceholders(this.gt.gettext(original), placeholders); | ||
} | ||
}]); | ||
return GettextWrapper; | ||
}(); | ||
/** Get translated string with plural forms */ | ||
ngettext(singular, plural, count, placeholders = {}) { | ||
return this.subtitudePlaceholders(this.gt.ngettext(singular, plural, count).replace(/%n/g, count.toString()), placeholders); | ||
} | ||
} | ||
function getGettextBuilder() { | ||
return new GettextBuilder(); | ||
return new GettextBuilder(); | ||
} | ||
//# sourceMappingURL=gettext.js.map | ||
exports.getGettextBuilder = getGettextBuilder; |
@@ -1,26 +0,80 @@ | ||
"use strict"; | ||
'use strict'; | ||
require("core-js/modules/es.object.define-property.js"); | ||
var router = require('@nextcloud/router'); | ||
var DOMPurify = require('dompurify'); | ||
var escapeHTML = require('escape-html'); | ||
Object.defineProperty(exports, "__esModule", { | ||
value: true | ||
}); | ||
exports.getCanonicalLocale = getCanonicalLocale; | ||
exports.getDayNames = getDayNames; | ||
exports.getDayNamesMin = getDayNamesMin; | ||
exports.getDayNamesShort = getDayNamesShort; | ||
exports.getFirstDay = getFirstDay; | ||
exports.getLanguage = getLanguage; | ||
exports.getLocale = getLocale; | ||
exports.getMonthNames = getMonthNames; | ||
exports.getMonthNamesShort = getMonthNamesShort; | ||
exports.translate = translate; | ||
exports.translatePlural = translatePlural; | ||
/** | ||
* Check if translations and plural function are set for given app | ||
* @param {string} appId the app id | ||
* @return {boolean} | ||
*/ | ||
function hasAppTranslations(appId) { | ||
return (window._oc_l10n_registry_translations?.[appId] !== undefined && | ||
window._oc_l10n_registry_plural_functions?.[appId] !== undefined); | ||
} | ||
/** | ||
* Register new, or extend available, translations for an app | ||
* @param {string} appId the app id | ||
* @param {object} translations the translations list | ||
* @param {Function} pluralFunction the plural function | ||
*/ | ||
function registerAppTranslations(appId, translations, pluralFunction) { | ||
if (!hasAppTranslations(appId)) { | ||
setAppTranslations(appId, translations, pluralFunction); | ||
} | ||
else { | ||
extendAppTranslations(appId, translations, pluralFunction); | ||
} | ||
} | ||
/** | ||
* Unregister all translations and plural function for given app | ||
* @param {string} appId the app id | ||
*/ | ||
function unregisterAppTranslations(appId) { | ||
delete window._oc_l10n_registry_translations[appId]; | ||
delete window._oc_l10n_registry_plural_functions[appId]; | ||
} | ||
/** | ||
* Get translations bundle for given app and current locale | ||
* @param {string} appId the app id | ||
* @return {object} | ||
*/ | ||
function getAppTranslations(appId) { | ||
if (typeof window._oc_l10n_registry_translations === 'undefined' || | ||
typeof window._oc_l10n_registry_plural_functions === 'undefined') { | ||
console.warn('No OC L10N registry found'); | ||
return { | ||
translations: {}, | ||
pluralFunction: (number) => number, | ||
}; | ||
} | ||
return { | ||
translations: window._oc_l10n_registry_translations[appId] || {}, | ||
pluralFunction: window._oc_l10n_registry_plural_functions[appId], | ||
}; | ||
} | ||
/** | ||
* Set new translations and plural function for an app | ||
* @param {string} appId the app id | ||
* @param {object} translations the translations list | ||
* @param {Function} pluralFunction the plural function | ||
*/ | ||
function setAppTranslations(appId, translations, pluralFunction) { | ||
window._oc_l10n_registry_translations[appId] = translations; | ||
window._oc_l10n_registry_plural_functions[appId] = pluralFunction; | ||
} | ||
/** | ||
* Extend translations for an app | ||
* @param {string} appId the app id | ||
* @param {object} translations the translations list | ||
* @param {Function} [pluralFunction] the plural function (will override old value if given) | ||
*/ | ||
function extendAppTranslations(appId, translations, pluralFunction) { | ||
window._oc_l10n_registry_translations[appId] = Object.assign(window._oc_l10n_registry_translations[appId], translations); | ||
if (typeof pluralFunction === 'function') { | ||
window._oc_l10n_registry_plural_functions[appId] = pluralFunction; | ||
} | ||
} | ||
require("core-js/modules/es.regexp.exec.js"); | ||
require("core-js/modules/es.string.replace.js"); | ||
/// <reference types="@nextcloud/typings" /> | ||
/** | ||
@@ -30,7 +84,6 @@ * Returns the user's locale | ||
function getLocale() { | ||
return document.documentElement.dataset.locale || 'en'; | ||
return document.documentElement.dataset.locale || 'en'; | ||
} | ||
function getCanonicalLocale() { | ||
return getLocale().replace(/_/g, '-'); | ||
return getLocale().replace(/_/g, '-'); | ||
} | ||
@@ -40,8 +93,5 @@ /** | ||
*/ | ||
function getLanguage() { | ||
return document.documentElement.lang || 'en'; | ||
return document.documentElement.lang || 'en'; | ||
} | ||
/** | ||
@@ -57,9 +107,36 @@ * Translate a string | ||
*/ | ||
function translate(app, text, vars, count, options) { | ||
if (typeof OC === 'undefined') { | ||
console.warn('No OC found'); | ||
return text; | ||
} | ||
return OC.L10N.translate(app, text, vars, count, options); | ||
function translate(app, text, vars, number, options) { | ||
const defaultOptions = { | ||
escape: true, | ||
sanitize: true, | ||
}; | ||
const allOptions = Object.assign({}, defaultOptions, options || {}); | ||
const identity = (value) => value; | ||
const optSanitize = allOptions.sanitize ? DOMPurify.sanitize : identity; | ||
const optEscape = allOptions.escape ? escapeHTML : identity; | ||
// TODO: cache this function to avoid inline recreation | ||
// of the same function over and over again in case | ||
// translate() is used in a loop | ||
const _build = (text, vars, number) => { | ||
return text.replace(/%n/g, '' + number).replace(/{([^{}]*)}/g, (match, key) => { | ||
if (vars === undefined || !(key in vars)) { | ||
return optSanitize(match); | ||
} | ||
const r = vars[key]; | ||
if (typeof r === 'string' || typeof r === 'number') { | ||
return optSanitize(optEscape(r)); | ||
} | ||
else { | ||
return optSanitize(match); | ||
} | ||
}); | ||
}; | ||
const bundle = getAppTranslations(app); | ||
const translation = bundle.translations[text] || text; | ||
if (typeof vars === 'object' || number !== undefined) { | ||
return optSanitize(_build(translation, vars, number)); | ||
} | ||
else { | ||
return optSanitize(translation); | ||
} | ||
} | ||
@@ -72,3 +149,3 @@ /** | ||
* @param {string} textPlural the string to translate for n objects | ||
* @param {number} count number to determine whether to use singular or plural | ||
* @param {number} number number to determine whether to use singular or plural | ||
* @param {Object} vars of placeholder key to value | ||
@@ -78,13 +155,276 @@ * @param {object} options options object | ||
*/ | ||
function translatePlural(app, textSingular, textPlural, count, vars, options) { | ||
if (typeof OC === 'undefined') { | ||
console.warn('No OC found'); | ||
return textSingular; | ||
} | ||
return OC.L10N.translatePlural(app, textSingular, textPlural, count, vars, options); | ||
function translatePlural(app, textSingular, textPlural, number, vars, options) { | ||
const identifier = '_' + textSingular + '_::_' + textPlural + '_'; | ||
const 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); | ||
} | ||
} | ||
/** | ||
* Load an app's translation bundle if not loaded already. | ||
* | ||
* @param {string} appName name of the app | ||
* @param {Function} callback callback to be called when | ||
* the translations are loaded | ||
* @return {Promise} promise | ||
*/ | ||
function loadTranslations(appName, callback) { | ||
// already available ? | ||
if (hasAppTranslations(appName) || getLocale() === 'en') { | ||
const deferred = $.Deferred(); | ||
const promise = deferred.promise(); | ||
promise.then(callback); | ||
deferred.resolve(); | ||
return promise; | ||
} | ||
const url = router.generateFilePath(appName, 'l10n', getLocale() + '.json'); | ||
const promise = new Promise((resolve, reject) => { | ||
const request = new XMLHttpRequest(); | ||
request.open('GET', url, false); | ||
request.onerror = (event) => { | ||
reject({ status: request.status, statusText: request.statusText }); | ||
}; | ||
request.onload = (event) => { | ||
if (request.status >= 200 && request.status < 300) { | ||
resolve(JSON.parse(request.responseText)); | ||
} | ||
else { | ||
reject({ | ||
status: request.status, | ||
statusText: request.statusText, | ||
}); | ||
} | ||
}; | ||
request.send(); | ||
}); | ||
// load JSON translation bundle per AJAX | ||
return promise | ||
.then((result) => { | ||
if (result.translations) { | ||
register(appName, result.translations); | ||
} | ||
}) | ||
.then(callback); | ||
} | ||
/** | ||
* Register an app's translation bundle. | ||
* | ||
* @param {string} appName name of the app | ||
* @param {object<string, string>} bundle translation bundle | ||
*/ | ||
function register(appName, bundle) { | ||
registerAppTranslations(appName, bundle, getPlural); | ||
} | ||
/** | ||
* @private | ||
*/ | ||
const _unregister = unregisterAppTranslations; | ||
/** | ||
* Get array index of translations for a plural form | ||
* | ||
* | ||
* @param {number} number the number of elements | ||
* @return {number} 0 for the singular form(, 1 for the first plural form, ...) | ||
*/ | ||
function getPlural(number) { | ||
let language = getLanguage(); | ||
if (language === 'pt-BR') { | ||
// temporary set a locale for brazilian | ||
language = 'xbr'; | ||
} | ||
if (typeof language === 'undefined' || language === '') { | ||
return number === 1 ? 0 : 1; | ||
} | ||
if (language.length > 3) { | ||
language = language.substring(0, language.lastIndexOf('-')); | ||
} | ||
/* | ||
* The plural rules are derived from code of the Zend Framework (2010-09-25), | ||
* which is subject to the new BSD license (http://framework.zend.com/license/new-bsd). | ||
* Copyright (c) 2005-2010 Zend Technologies USA Inc. (http://www.zend.com) | ||
*/ | ||
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; | ||
} | ||
} | ||
/// <reference types="@nextcloud/typings" /> | ||
/** | ||
* Get the first day of the week | ||
@@ -94,11 +434,8 @@ * | ||
*/ | ||
function getFirstDay() { | ||
if (typeof window.firstDay === 'undefined') { | ||
console.warn('No firstDay found'); | ||
return 1; | ||
} | ||
return window.firstDay; | ||
if (typeof window.firstDay === 'undefined') { | ||
console.warn('No firstDay found'); | ||
return 1; | ||
} | ||
return window.firstDay; | ||
} | ||
@@ -110,11 +447,16 @@ /** | ||
*/ | ||
function getDayNames() { | ||
if (typeof window.dayNames === 'undefined') { | ||
console.warn('No dayNames found'); | ||
return ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday']; | ||
} | ||
return window.dayNames; | ||
if (typeof window.dayNames === 'undefined') { | ||
console.warn('No dayNames found'); | ||
return [ | ||
'Sunday', | ||
'Monday', | ||
'Tuesday', | ||
'Wednesday', | ||
'Thursday', | ||
'Friday', | ||
'Saturday', | ||
]; | ||
} | ||
return window.dayNames; | ||
} | ||
@@ -126,11 +468,8 @@ /** | ||
*/ | ||
function getDayNamesShort() { | ||
if (typeof window.dayNamesShort === 'undefined') { | ||
console.warn('No dayNamesShort found'); | ||
return ['Sun.', 'Mon.', 'Tue.', 'Wed.', 'Thu.', 'Fri.', 'Sat.']; | ||
} | ||
return window.dayNamesShort; | ||
if (typeof window.dayNamesShort === 'undefined') { | ||
console.warn('No dayNamesShort found'); | ||
return ['Sun.', 'Mon.', 'Tue.', 'Wed.', 'Thu.', 'Fri.', 'Sat.']; | ||
} | ||
return window.dayNamesShort; | ||
} | ||
@@ -142,11 +481,8 @@ /** | ||
*/ | ||
function getDayNamesMin() { | ||
if (typeof window.dayNamesMin === 'undefined') { | ||
console.warn('No dayNamesMin found'); | ||
return ['Su', 'Mo', 'Tu', 'We', 'Th', 'Fr', 'Sa']; | ||
} | ||
return window.dayNamesMin; | ||
if (typeof window.dayNamesMin === 'undefined') { | ||
console.warn('No dayNamesMin found'); | ||
return ['Su', 'Mo', 'Tu', 'We', 'Th', 'Fr', 'Sa']; | ||
} | ||
return window.dayNamesMin; | ||
} | ||
@@ -158,11 +494,21 @@ /** | ||
*/ | ||
function getMonthNames() { | ||
if (typeof window.monthNames === 'undefined') { | ||
console.warn('No monthNames found'); | ||
return ['January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December']; | ||
} | ||
return window.monthNames; | ||
if (typeof window.monthNames === 'undefined') { | ||
console.warn('No monthNames found'); | ||
return [ | ||
'January', | ||
'February', | ||
'March', | ||
'April', | ||
'May', | ||
'June', | ||
'July', | ||
'August', | ||
'September', | ||
'October', | ||
'November', | ||
'December', | ||
]; | ||
} | ||
return window.monthNames; | ||
} | ||
@@ -174,12 +520,37 @@ /** | ||
*/ | ||
function getMonthNamesShort() { | ||
if (typeof window.monthNamesShort === 'undefined') { | ||
console.warn('No monthNamesShort found'); | ||
return ['Jan.', 'Feb.', 'Mar.', 'Apr.', 'May.', 'Jun.', 'Jul.', 'Aug.', 'Sep.', 'Oct.', 'Nov.', 'Dec.']; | ||
} | ||
if (typeof window.monthNamesShort === 'undefined') { | ||
console.warn('No monthNamesShort found'); | ||
return [ | ||
'Jan.', | ||
'Feb.', | ||
'Mar.', | ||
'Apr.', | ||
'May.', | ||
'Jun.', | ||
'Jul.', | ||
'Aug.', | ||
'Sep.', | ||
'Oct.', | ||
'Nov.', | ||
'Dec.', | ||
]; | ||
} | ||
return window.monthNamesShort; | ||
} | ||
return window.monthNamesShort; | ||
} | ||
//# sourceMappingURL=index.js.map | ||
exports._unregister = _unregister; | ||
exports.getCanonicalLocale = getCanonicalLocale; | ||
exports.getDayNames = getDayNames; | ||
exports.getDayNamesMin = getDayNamesMin; | ||
exports.getDayNamesShort = getDayNamesShort; | ||
exports.getFirstDay = getFirstDay; | ||
exports.getLanguage = getLanguage; | ||
exports.getLocale = getLocale; | ||
exports.getMonthNames = getMonthNames; | ||
exports.getMonthNamesShort = getMonthNamesShort; | ||
exports.getPlural = getPlural; | ||
exports.loadTranslations = loadTranslations; | ||
exports.register = register; | ||
exports.translate = translate; | ||
exports.translatePlural = translatePlural; |
@@ -0,1 +1,18 @@ | ||
/** | ||
* This module provides functionality to translate applications independent from Nextcloud | ||
* | ||
* @packageDocumentation | ||
* @module @nextcloud/l10n/gettext | ||
* @example | ||
* ```js | ||
import { getGettextBuilder } from '@nextcloud/l10n/gettext' | ||
const gt = getGettextBuilder() | ||
.detectLocale() // or use setLanguage() | ||
.addTranslation(/* ... *\/) | ||
.build() | ||
gt.gettext('some string to translate') | ||
``` | ||
*/ | ||
import GetText from "node-gettext" | ||
@@ -5,2 +22,5 @@ | ||
/** | ||
* @notExported | ||
*/ | ||
class GettextBuilder { | ||
@@ -17,2 +37,3 @@ | ||
/** Try to detect locale from context with `en` as fallback value */ | ||
detectLocale(): GettextBuilder { | ||
@@ -38,2 +59,5 @@ return this.setLanguage(getLanguage().replace('-', '_')) | ||
/** | ||
* @notExported | ||
*/ | ||
class GettextWrapper { | ||
@@ -56,3 +80,3 @@ | ||
private subtitudePlaceholders(translated: string, vars: object): string { | ||
private subtitudePlaceholders(translated: string, vars: Record<string, string | number>): string { | ||
return translated.replace(/{([^{}]*)}/g, (a, b) => { | ||
@@ -68,3 +92,4 @@ const r = vars[b] | ||
gettext(original: string, placeholders: object = {}): string { | ||
/** Get translated string (singular form), optionally with placeholders */ | ||
gettext(original: string, placeholders: Record<string, string | number> = {}): string { | ||
return this.subtitudePlaceholders( | ||
@@ -76,3 +101,4 @@ this.gt.gettext(original), | ||
ngettext(singular: string, plural: string, count: number, placeholders: object = {}): string { | ||
/** Get translated string with plural forms */ | ||
ngettext(singular: string, plural: string, count: number, placeholders: Record<string, string | number> = {}): string { | ||
return this.subtitudePlaceholders( | ||
@@ -79,0 +105,0 @@ this.gt.ngettext(singular, plural, count).replace(/%n/g, count.toString()), |
161
lib/index.ts
@@ -1,152 +0,17 @@ | ||
/// <reference types="@nextcloud/typings" /> | ||
declare var OC: Nextcloud.v16.OC | Nextcloud.v17.OC | Nextcloud.v18.OC | Nextcloud.v19.OC | | ||
Nextcloud.v20.OC | Nextcloud.v21.OC | Nextcloud.v22.OC | Nextcloud.v23.OC | | ||
Nextcloud.v24.OC; | ||
declare var window: Nextcloud.v16.WindowWithGlobals | Nextcloud.v17.WindowWithGlobals | Nextcloud.v18.WindowWithGlobals | Nextcloud.v19.WindowWithGlobals; | ||
/** | ||
* Returns the user's locale | ||
*/ | ||
export function getLocale(): string { | ||
return document.documentElement.dataset.locale || 'en' | ||
} | ||
* This module provides all functions for the `OC.L10N` namespace | ||
* | ||
* @packageDocumentation | ||
* @module @nextcloud/l10n | ||
* @example | ||
* ```js | ||
import { translate as t, translatePlural as n } from '@nextcloud/l10n' | ||
export function getCanonicalLocale(): string { | ||
return getLocale().replace(/_/g, '-') | ||
} | ||
/** | ||
* Returns the user's language | ||
console.log(t('my-app', 'Hello {name}', { name: 'J. Doe' })); | ||
const count = 2; | ||
console.warn(n('my-app', 'Got an error', 'Got multiple errors', 2)); | ||
``` | ||
*/ | ||
export function getLanguage(): string { | ||
return document.documentElement.lang || 'en' | ||
} | ||
interface TranslationOptions { | ||
escape?: boolean | ||
} | ||
/** | ||
* Translate a string | ||
* | ||
* @param {string} app the id of the app for which to translate the string | ||
* @param {string} text the string to translate | ||
* @param {object} vars map of placeholder key to value | ||
* @param {number} number to replace %n with | ||
* @param {object} [options] options object | ||
* @return {string} | ||
*/ | ||
export function translate(app: string, text: string, vars?: object, count?: number, options?: TranslationOptions): string { | ||
if (typeof OC === 'undefined') { | ||
console.warn('No OC found') | ||
return text | ||
} | ||
return OC.L10N.translate(app, text, vars, count, options) | ||
} | ||
/** | ||
* Translate a plural string | ||
* | ||
* @param {string} app the id of the app for which to translate the string | ||
* @param {string} textSingular the string to translate for exactly one object | ||
* @param {string} textPlural the string to translate for n objects | ||
* @param {number} count number to determine whether to use singular or plural | ||
* @param {Object} vars of placeholder key to value | ||
* @param {object} options options object | ||
* @return {string} | ||
*/ | ||
export function translatePlural(app: string, textSingular: string, textPlural: string, count: number, vars?: object, options?: TranslationOptions): string { | ||
if (typeof OC === 'undefined') { | ||
console.warn('No OC found') | ||
return textSingular | ||
} | ||
return OC.L10N.translatePlural(app, textSingular, textPlural, count, vars, options) | ||
} | ||
/** | ||
* Get the first day of the week | ||
* | ||
* @return {number} | ||
*/ | ||
export function getFirstDay(): number { | ||
if (typeof window.firstDay === 'undefined') { | ||
console.warn('No firstDay found') | ||
return 1 | ||
} | ||
return window.firstDay | ||
} | ||
/** | ||
* Get a list of day names (full names) | ||
* | ||
* @return {string[]} | ||
*/ | ||
export function getDayNames(): string[] { | ||
if (typeof window.dayNames === 'undefined') { | ||
console.warn('No dayNames found') | ||
return ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday'] | ||
} | ||
return window.dayNames | ||
} | ||
/** | ||
* Get a list of day names (short names) | ||
* | ||
* @return {string[]} | ||
*/ | ||
export function getDayNamesShort(): string[] { | ||
if (typeof window.dayNamesShort === 'undefined') { | ||
console.warn('No dayNamesShort found') | ||
return ['Sun.', 'Mon.', 'Tue.', 'Wed.', 'Thu.', 'Fri.', 'Sat.'] | ||
} | ||
return window.dayNamesShort | ||
} | ||
/** | ||
* Get a list of day names (minified names) | ||
* | ||
* @return {string[]} | ||
*/ | ||
export function getDayNamesMin(): string[] { | ||
if (typeof window.dayNamesMin === 'undefined') { | ||
console.warn('No dayNamesMin found') | ||
return ['Su', 'Mo', 'Tu', 'We', 'Th', 'Fr', 'Sa'] | ||
} | ||
return window.dayNamesMin | ||
} | ||
/** | ||
* Get a list of month names (full names) | ||
* | ||
* @return {string[]} | ||
*/ | ||
export function getMonthNames(): string[] { | ||
if (typeof window.monthNames === 'undefined') { | ||
console.warn('No monthNames found') | ||
return ['January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December'] | ||
} | ||
return window.monthNames | ||
} | ||
/** | ||
* Get a list of month names (short names) | ||
* | ||
* @return {string[]} | ||
*/ | ||
export function getMonthNamesShort(): string[] { | ||
if (typeof window.monthNamesShort === 'undefined') { | ||
console.warn('No monthNamesShort found') | ||
return ['Jan.', 'Feb.', 'Mar.', 'Apr.', 'May.', 'Jun.', 'Jul.', 'Aug.', 'Sep.', 'Oct.', 'Nov.', 'Dec.'] | ||
} | ||
return window.monthNamesShort | ||
} | ||
export * from './translation' | ||
export * from './date' |
{ | ||
"name": "@nextcloud/l10n", | ||
"version": "1.6.0", | ||
"version": "2.0.0-beta.0", | ||
"description": "", | ||
"main": "dist/index.js", | ||
"types": "dist/index.d.ts", | ||
"exports": { | ||
".": { | ||
"import": "./dist/index.mjs", | ||
"require": "./dist/index.js" | ||
}, | ||
"./gettext": { | ||
"import": "./dist/gettext.mjs", | ||
"require": "./dist/gettext.js" | ||
} | ||
}, | ||
"scripts": { | ||
"build": "babel ./lib --out-dir dist --extensions '.ts,.tsx' --source-maps && tsc --emitDeclarationOnly", | ||
"build:doc": "typedoc --excludeNotExported --mode file --out dist/doc lib && touch dist/doc/.nojekyll", | ||
"check-types": "tsc", | ||
"dev": "babel ./lib --out-dir dist --extensions '.ts,.tsx' --watch", | ||
"build": "rollup -c", | ||
"build:doc": "typedoc && touch dist/doc/.nojekyll", | ||
"check-es-compat": "check-es-compat dist/*.js", | ||
"check-types": "tsc --noEmit", | ||
"dev": "rollup -c -w", | ||
"test": "jest", | ||
@@ -26,23 +37,30 @@ "test:watch": "jest --watchAll" | ||
"dependencies": { | ||
"core-js": "^3.6.4", | ||
"@nextcloud/router": "^2.0.0", | ||
"dompurify": "^2.4.1", | ||
"escape-html": "^1.0.3", | ||
"node-gettext": "^3.0.0" | ||
}, | ||
"devDependencies": { | ||
"@babel/cli": "^7.8.4", | ||
"@babel/core": "^7.9.0", | ||
"@babel/preset-env": "^7.9.5", | ||
"@babel/preset-typescript": "^7.9.0", | ||
"@nextcloud/browserslist-config": "^2.2.0", | ||
"@nextcloud/typings": "^1.0.0", | ||
"@nextcloud/browserslist-config": "^2.3.0", | ||
"@nextcloud/typings": "^1.6.0", | ||
"@rollup/plugin-typescript": "^11.0.0", | ||
"@types/jest": "^29.2.5", | ||
"@types/node-gettext": "^3.0", | ||
"babel-jest": "^27.2.1", | ||
"babel-plugin-transform-class-properties": "^6.24.1", | ||
"gettext-parser": "^4.0.3", | ||
"jest": "^27.2.1", | ||
"typedoc": "^0.22.4", | ||
"typescript": "^4.0.2" | ||
"@zamiell/typedoc-plugin-not-exported": "^0.2.0", | ||
"check-es-compat": "^2.2.0", | ||
"gettext-parser": "^6.0.0", | ||
"jest": "^29.3.1", | ||
"jest-environment-jsdom": "^29.3.1", | ||
"rollup": "^3.9.1", | ||
"ts-jest": "^29.0.3", | ||
"tslib": "^2.4.1", | ||
"typedoc": "^0.23.23" | ||
}, | ||
"browserslist": [ | ||
"extends @nextcloud/browserslist-config" | ||
] | ||
], | ||
"engines": { | ||
"node": "^16.0.0", | ||
"npm": "^7.0.0 || ^8.0.0" | ||
} | ||
} |
# @nextcloud/l10n | ||
[![Build Status](https://travis-ci.com/nextcloud/nextcloud-l10n.svg?branch=master)](https://travis-ci.com/nextcloud/nextcloud-l10n) | ||
[![Build Status](https://img.shields.io/github/actions/workflow/status/nextcloud/nextcloud-l10n/node.yml?branch=master)](https://github.com/nextcloud/nextcloud-l10n/actions/workflows/node.yml) | ||
[![npm](https://img.shields.io/npm/v/@nextcloud/l10n.svg)](https://www.npmjs.com/package/@nextcloud/l10n) | ||
@@ -5,0 +5,0 @@ [![Documentation](https://img.shields.io/badge/Documentation-online-brightgreen)](https://nextcloud.github.io/nextcloud-l10n/) |
{ | ||
"$schema": "https://json.schemastore.org/tsconfig", | ||
"compilerOptions": { | ||
"target": "es5", | ||
"module": "commonjs", | ||
"target": "ES2020", | ||
"module": "ES2020", | ||
"declaration": true, | ||
"esModuleInterop": true, | ||
"declaration": true, | ||
"moduleResolution": "node", | ||
"noImplicitAny": false, | ||
"outDir": "./dist", | ||
@@ -12,5 +15,5 @@ "strict": true, | ||
"dom" | ||
], | ||
"noImplicitAny": false, | ||
} | ||
} | ||
] | ||
}, | ||
"exclude": ["./dist"] | ||
} |
Sorry, the diff of this file is not supported yet
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
Major refactor
Supply chain riskPackage has recently undergone a major refactor. It may be unstable or indicate significant internal changes. Use caution when updating to versions that include significant changes.
Found 1 instance in 1 package
New author
Supply chain riskA new npm collaborator published a version of the package for the first time. New collaborators are usually benign additions to a project, but do indicate a change to the security surface area of a package.
Found 1 instance in 1 package
No v1
QualityPackage is not semver >=1. This means it is not stable and does not support ^ ranges.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
119565
32
2501
4
14
1
2
+ Added@nextcloud/router@^2.0.0
+ Addeddompurify@^2.4.1
+ Addedescape-html@^1.0.3
+ Added@nextcloud/router@2.2.1(transitive)
+ Added@nextcloud/typings@1.9.1(transitive)
+ Added@types/jquery@3.5.16(transitive)
+ Added@types/sizzle@2.3.9(transitive)
+ Addeddompurify@2.5.8(transitive)
+ Addedescape-html@1.0.3(transitive)
- Removedcore-js@^3.6.4