Huge News!Announcing our $40M Series B led by Abstract Ventures.Learn More
Socket
Sign inDemoInstall
Socket

@nextcloud/l10n

Package Overview
Dependencies
Maintainers
12
Versions
23
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

@nextcloud/l10n - npm Package Compare versions

Comparing version 2.0.0-beta.0 to 2.0.0

.eslintrc.json

8

CHANGELOG.md

@@ -5,9 +5,11 @@ # Changelog

## 2.0.0-beta.0 - 2023-01-11
## 2.0.0 - 2023-01-12
[Full Changelog](https://github.com/nextcloud/nextcloud-l10n/compare/v1.6.0...v2.0.0-beta.0)
[Full Changelog](https://github.com/nextcloud/nextcloud-l10n/compare/v1.6.0...v2.0.0)
### Changed
From 2.0.0, this package is standalone and do not rely on window OC variables to function.
### 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))
- Fix building and deploying documentation [\#546](https://github.com/nextcloud/nextcloud-l10n/pull/546) ([susnux](https://github.com/susnux))

@@ -14,0 +16,0 @@ ## 1.6.0 - 2022-05-10

@@ -23,3 +23,2 @@ 'use strict';

import { getGettextBuilder } from '@nextcloud/l10n/gettext'
const gt = getGettextBuilder()

@@ -29,3 +28,2 @@ .detectLocale() // or use setLanguage()

.build()
gt.gettext('some string to translate')

@@ -71,3 +69,3 @@ ```

});
for (let key in data) {
for (const key in data) {
this.gt.addTranslations(key, 'messages', data[key]);

@@ -88,7 +86,19 @@ }

}
/** Get translated string (singular form), optionally with placeholders */
/**
* Get translated string (singular form), optionally with placeholders
*
* @param original original string to translate
* @param placeholders map of placeholder key to value
*/
gettext(original, placeholders = {}) {
return this.subtitudePlaceholders(this.gt.gettext(original), placeholders);
}
/** Get translated string with plural forms */
/**
* Get translated string with plural forms
*
* @param singular Singular text form
* @param plural Plural text form to be used if `count` requires it
* @param count The number to insert into the text
* @param placeholders optional map of placeholder key to value
*/
ngettext(singular, plural, count, placeholders = {}) {

@@ -98,2 +108,5 @@ return this.subtitudePlaceholders(this.gt.ngettext(singular, plural, count).replace(/%n/g, count.toString()), placeholders);

}
/**
* Create a new GettextBuilder instance
*/
function getGettextBuilder() {

@@ -100,0 +113,0 @@ return new GettextBuilder();

@@ -9,2 +9,3 @@ 'use strict';

* Check if translations and plural function are set for given app
*
* @param {string} appId the app id

@@ -14,7 +15,9 @@ * @return {boolean}

function hasAppTranslations(appId) {
return (window._oc_l10n_registry_translations?.[appId] !== undefined &&
window._oc_l10n_registry_plural_functions?.[appId] !== undefined);
var _a, _b;
return (((_a = window._oc_l10n_registry_translations) === null || _a === void 0 ? void 0 : _a[appId]) !== undefined
&& ((_b = window._oc_l10n_registry_plural_functions) === null || _b === void 0 ? void 0 : _b[appId]) !== undefined);
}
/**
* Register new, or extend available, translations for an app
*
* @param {string} appId the app id

@@ -34,2 +37,3 @@ * @param {object} translations the translations list

* Unregister all translations and plural function for given app
*
* @param {string} appId the app id

@@ -43,2 +47,3 @@ */

* Get translations bundle for given app and current locale
*
* @param {string} appId the app id

@@ -48,4 +53,4 @@ * @return {object}

function getAppTranslations(appId) {
if (typeof window._oc_l10n_registry_translations === 'undefined' ||
typeof window._oc_l10n_registry_plural_functions === 'undefined') {
if (typeof window._oc_l10n_registry_translations === 'undefined'
|| typeof window._oc_l10n_registry_plural_functions === 'undefined') {
console.warn('No OC L10N registry found');

@@ -64,2 +69,3 @@ return {

* Set new translations and plural function for an app
*
* @param {string} appId the app id

@@ -75,2 +81,3 @@ * @param {object} translations the translations list

* Extend translations for an app
*
* @param {string} appId the app id

@@ -93,2 +100,6 @@ * @param {object} translations the translations list

}
/**
* Returns user's locale in canonical form
* E.g. `en-US` instead of `en_US`
*/
function getCanonicalLocale() {

@@ -149,3 +160,3 @@ return getLocale().replace(/_/g, '-');

/**
* Translate a plural string
* Translate a string containing an object which possibly requires a plural form
*

@@ -156,5 +167,4 @@ * @param {string} app the id of the app for which to translate the string

* @param {number} number number to determine whether to use singular or plural
* @param {Object} vars of placeholder key to value
* @param {object} vars of placeholder key to value
* @param {object} options options object
* @return {string}
*/

@@ -190,7 +200,3 @@ function translatePlural(app, textSingular, textPlural, number, vars, options) {

if (hasAppTranslations(appName) || getLocale() === 'en') {
const deferred = $.Deferred();
const promise = deferred.promise();
promise.then(callback);
deferred.resolve();
return promise;
return Promise.resolve().then(callback);
}

@@ -201,6 +207,6 @@ const url = router.generateFilePath(appName, 'l10n', getLocale() + '.json');

request.open('GET', url, false);
request.onerror = (event) => {
reject({ status: request.status, statusText: request.statusText });
request.onerror = () => {
reject(new Error(request.statusText));
};
request.onload = (event) => {
request.onload = () => {
if (request.status >= 200 && request.status < 300) {

@@ -210,6 +216,3 @@ resolve(JSON.parse(request.responseText));

else {
reject({
status: request.status,
statusText: request.statusText,
});
reject(new Error(request.statusText));
}

@@ -225,2 +228,3 @@ };

}
return result;
})

@@ -233,3 +237,3 @@ .then(callback);

* @param {string} appName name of the app
* @param {object<string, string>} bundle translation bundle
* @param {Object<string, string>} bundle translation bundle
*/

@@ -359,5 +363,5 @@ function register(appName, bundle) {

? 0
: number % 10 >= 2 &&
number % 10 <= 4 &&
(number % 100 < 10 || number % 100 >= 20)
: number % 10 >= 2
&& number % 10 <= 4
&& (number % 100 < 10 || number % 100 >= 20)
? 1

@@ -403,5 +407,5 @@ : 2;

? 0
: number % 10 >= 2 &&
number % 10 <= 4 &&
(number % 100 < 12 || number % 100 > 14)
: number % 10 >= 2
&& number % 10 <= 4
&& (number % 100 < 12 || number % 100 > 14)
? 1

@@ -408,0 +412,0 @@ : 2;

@@ -11,3 +11,3 @@ /**

detectLocale(): GettextBuilder;
addTranslation(language: string, data: any): GettextBuilder;
addTranslation(language: string, data: object): GettextBuilder;
enableDebugMode(): GettextBuilder;

@@ -21,10 +21,25 @@ build(): GettextWrapper;

private gt;
constructor(locale: string, data: any, debug: boolean);
constructor(locale: string, data: object, debug: boolean);
private subtitudePlaceholders;
/** Get translated string (singular form), optionally with placeholders */
/**
* Get translated string (singular form), optionally with placeholders
*
* @param original original string to translate
* @param placeholders map of placeholder key to value
*/
gettext(original: string, placeholders?: Record<string, string | number>): string;
/** Get translated string with plural forms */
/**
* Get translated string with plural forms
*
* @param singular Singular text form
* @param plural Plural text form to be used if `count` requires it
* @param count The number to insert into the text
* @param placeholders optional map of placeholder key to value
*/
ngettext(singular: string, plural: string, count: number, placeholders?: Record<string, string | number>): string;
}
/**
* Create a new GettextBuilder instance
*/
export declare function getGettextBuilder(): GettextBuilder;
export {};

@@ -9,3 +9,2 @@ /**

import { translate as t, translatePlural as n } from '@nextcloud/l10n'
console.log(t('my-app', 'Hello {name}', { name: 'J. Doe' }));

@@ -12,0 +11,0 @@ const count = 2;

@@ -9,2 +9,3 @@ export type Translations = Record<string, string | undefined>;

* Check if translations and plural function are set for given app
*
* @param {string} appId the app id

@@ -16,2 +17,3 @@ * @return {boolean}

* Register new, or extend available, translations for an app
*
* @param {string} appId the app id

@@ -24,2 +26,3 @@ * @param {object} translations the translations list

* Unregister all translations and plural function for given app
*
* @param {string} appId the app id

@@ -30,2 +33,3 @@ */

* Get translations bundle for given app and current locale
*
* @param {string} appId the app id

@@ -32,0 +36,0 @@ * @return {object}

@@ -1,2 +0,1 @@

/// <reference types="jquery" />
import { unregisterAppTranslations } from './registry';

@@ -15,2 +14,6 @@ import type { Translations } from './registry';

export declare function getLocale(): string;
/**
* Returns user's locale in canonical form
* E.g. `en-US` instead of `en_US`
*/
export declare function getCanonicalLocale(): string;

@@ -31,5 +34,5 @@ /**

*/
export declare function translate(app: string, text: string, vars?: Record<string, any>, number?: number, options?: TranslationOptions): string;
export declare function translate(app: string, text: string, vars?: Record<string, string | number>, number?: number, options?: TranslationOptions): string;
/**
* Translate a plural string
* Translate a string containing an object which possibly requires a plural form
*

@@ -40,7 +43,6 @@ * @param {string} app the id of the app for which to translate the string

* @param {number} number number to determine whether to use singular or plural
* @param {Object} vars of placeholder key to value
* @param {object} vars of placeholder key to value
* @param {object} options options object
* @return {string}
*/
export declare function translatePlural(app: string, textSingular: string, textPlural: string, number: number, vars?: object, options?: TranslationOptions): string;
export declare function translatePlural(app: string, textSingular: string, textPlural: string, number: number, vars?: Record<string, string | number>, options?: TranslationOptions): string;
/**

@@ -54,3 +56,3 @@ * Load an app's translation bundle if not loaded already.

*/
export declare function loadTranslations(appName: string, callback: (...args: any[]) => any): JQueryPromise<unknown> | Promise<any>;
export declare function loadTranslations(appName: string, callback: (...args: []) => unknown): Promise<unknown>;
/**

@@ -60,3 +62,3 @@ * Register an app's translation bundle.

* @param {string} appName name of the app
* @param {object<string, string>} bundle translation bundle
* @param {Object<string, string>} bundle translation bundle
*/

@@ -63,0 +65,0 @@ export declare function register(appName: string, bundle: Translations): void;

@@ -11,5 +11,5 @@ module.exports = {

},
]
],
},
transformIgnorePatterns: [],
}
/// <reference types="@nextcloud/typings" />
declare var window: Nextcloud.v24.WindowWithGlobals
declare let window: Nextcloud.v24.WindowWithGlobals

@@ -5,0 +5,0 @@ /**

/**
* This module provides functionality to translate applications independent from Nextcloud
*
*
* @packageDocumentation

@@ -9,14 +9,12 @@ * @module @nextcloud/l10n/gettext

import { getGettextBuilder } from '@nextcloud/l10n/gettext'
const gt = getGettextBuilder()
.detectLocale() // or use setLanguage()
.addTranslation(/* ... *\/)
.build()
.detectLocale() // or use setLanguage()
.addTranslation(/* ... *\/)
.build()
gt.gettext('some string to translate')
```
*/
import GetText from "node-gettext"
import GetText from 'node-gettext'
import { getLanguage } from "."
import { getLanguage } from '.'

@@ -28,29 +26,29 @@ /**

private locale?: string
private translations = {}
private debug = false
private locale?: string
private translations = {}
private debug = false
setLanguage(language: string): GettextBuilder {
this.locale = language
return this
}
setLanguage(language: string): GettextBuilder {
this.locale = language
return this
}
/** Try to detect locale from context with `en` as fallback value */
detectLocale(): GettextBuilder {
return this.setLanguage(getLanguage().replace('-', '_'))
}
/** Try to detect locale from context with `en` as fallback value */
detectLocale(): GettextBuilder {
return this.setLanguage(getLanguage().replace('-', '_'))
}
addTranslation(language: string, data: any): GettextBuilder {
this.translations[language] = data
return this
}
addTranslation(language: string, data: object): GettextBuilder {
this.translations[language] = data
return this
}
enableDebugMode(): GettextBuilder {
this.debug = true
return this
}
enableDebugMode(): GettextBuilder {
this.debug = true
return this
}
build(): GettextWrapper {
return new GettextWrapper(this.locale || 'en', this.translations, this.debug)
}
build(): GettextWrapper {
return new GettextWrapper(this.locale || 'en', this.translations, this.debug)
}

@@ -64,48 +62,63 @@ }

private gt: GetText
private gt: GetText
constructor(locale: string, data: any, debug: boolean) {
this.gt = new GetText({
debug,
sourceLocale: 'en',
})
constructor(locale: string, data: object, debug: boolean) {
this.gt = new GetText({
debug,
sourceLocale: 'en',
})
for (let key in data) {
this.gt.addTranslations(key, 'messages', data[key])
}
for (const key in data) {
this.gt.addTranslations(key, 'messages', data[key])
}
this.gt.setLocale(locale)
}
this.gt.setLocale(locale)
}
private subtitudePlaceholders(translated: string, vars: Record<string, string | number>): string {
return translated.replace(/{([^{}]*)}/g, (a, b) => {
const r = vars[b]
if (typeof r === 'string' || typeof r === 'number') {
return r.toString()
} else {
return a
}
})
}
private subtitudePlaceholders(translated: string, vars: Record<string, string | number>): string {
return translated.replace(/{([^{}]*)}/g, (a, b) => {
const r = vars[b]
if (typeof r === 'string' || typeof r === 'number') {
return r.toString()
} else {
return a
}
})
}
/** Get translated string (singular form), optionally with placeholders */
gettext(original: string, placeholders: Record<string, string | number> = {}): string {
return this.subtitudePlaceholders(
this.gt.gettext(original),
placeholders
)
}
/**
* Get translated string (singular form), optionally with placeholders
*
* @param original original string to translate
* @param placeholders map of placeholder key to value
*/
gettext(original: string, placeholders: Record<string, string | number> = {}): string {
return this.subtitudePlaceholders(
this.gt.gettext(original),
placeholders
)
}
/** Get translated string with plural forms */
ngettext(singular: string, plural: string, count: number, placeholders: Record<string, string | number> = {}): string {
return this.subtitudePlaceholders(
this.gt.ngettext(singular, plural, count).replace(/%n/g, count.toString()),
placeholders
)
}
/**
* Get translated string with plural forms
*
* @param singular Singular text form
* @param plural Plural text form to be used if `count` requires it
* @param count The number to insert into the text
* @param placeholders optional map of placeholder key to value
*/
ngettext(singular: string, plural: string, count: number, placeholders: Record<string, string | number> = {}): string {
return this.subtitudePlaceholders(
this.gt.ngettext(singular, plural, count).replace(/%n/g, count.toString()),
placeholders
)
}
}
/**
* Create a new GettextBuilder instance
*/
export function getGettextBuilder() {
return new GettextBuilder()
return new GettextBuilder()
}
/**
* This module provides all functions for the `OC.L10N` namespace
*
*
* @packageDocumentation

@@ -9,3 +9,2 @@ * @module @nextcloud/l10n

import { translate as t, translatePlural as n } from '@nextcloud/l10n'
console.log(t('my-app', 'Hello {name}', { name: 'J. Doe' }));

@@ -12,0 +11,0 @@ const count = 2;

export type Translations = Record<string, string | undefined>
export type PluralFunction = (number: number) => number
declare var window: {
declare let window: {
_oc_l10n_registry_translations: Record<string, Translations>

@@ -16,2 +16,3 @@ _oc_l10n_registry_plural_functions: Record<string, PluralFunction>

* Check if translations and plural function are set for given app
*
* @param {string} appId the app id

@@ -22,4 +23,4 @@ * @return {boolean}

return (
window._oc_l10n_registry_translations?.[appId] !== undefined &&
window._oc_l10n_registry_plural_functions?.[appId] !== undefined
window._oc_l10n_registry_translations?.[appId] !== undefined
&& window._oc_l10n_registry_plural_functions?.[appId] !== undefined
)

@@ -30,2 +31,3 @@ }

* Register new, or extend available, translations for an app
*
* @param {string} appId the app id

@@ -49,2 +51,3 @@ * @param {object} translations the translations list

* Unregister all translations and plural function for given app
*
* @param {string} appId the app id

@@ -59,2 +62,3 @@ */

* Get translations bundle for given app and current locale
*
* @param {string} appId the app id

@@ -65,4 +69,4 @@ * @return {object}

if (
typeof window._oc_l10n_registry_translations === 'undefined' ||
typeof window._oc_l10n_registry_plural_functions === 'undefined'
typeof window._oc_l10n_registry_translations === 'undefined'
|| typeof window._oc_l10n_registry_plural_functions === 'undefined'
) {

@@ -84,2 +88,3 @@ console.warn('No OC L10N registry found')

* Set new translations and plural function for an app
*
* @param {string} appId the app id

@@ -100,2 +105,3 @@ * @param {object} translations the translations list

* Extend translations for an app
*
* @param {string} appId the app id

@@ -102,0 +108,0 @@ * @param {object} translations the translations list

@@ -28,2 +28,6 @@ import {

/**
* Returns user's locale in canonical form
* E.g. `en-US` instead of `en_US`
*/
export function getCanonicalLocale(): string {

@@ -53,3 +57,3 @@ return getLocale().replace(/_/g, '-')

text: string,
vars?: Record<string, any>,
vars?: Record<string, string | number>,
number?: number,

@@ -71,3 +75,3 @@ options?: TranslationOptions

// translate() is used in a loop
const _build = (text: string, vars?: Record<string, any>, number?: number) => {
const _build = (text: string, vars?: Record<string, string | number>, number?: number) => {
return text.replace(/%n/g, '' + number).replace(/{([^{}]*)}/g, (match, key) => {

@@ -98,3 +102,3 @@ if (vars === undefined || !(key in vars)) {

/**
* Translate a plural string
* Translate a string containing an object which possibly requires a plural form
*

@@ -105,7 +109,5 @@ * @param {string} app the id of the app for which to translate the string

* @param {number} number number to determine whether to use singular or plural
* @param {Object} vars of placeholder key to value
* @param {object} vars of placeholder key to value
* @param {object} options options object
* @return {string}
*/
export function translatePlural(

@@ -116,3 +118,3 @@ app: string,

number: number,
vars?: object,
vars?: Record<string, string | number>,
options?: TranslationOptions

@@ -147,10 +149,6 @@ ): string {

*/
export function loadTranslations(appName: string, callback: (...args: any[]) => any) {
export function loadTranslations(appName: string, callback: (...args: []) => unknown) {
// already available ?
if (hasAppTranslations(appName) || getLocale() === 'en') {
const deferred = $.Deferred()
const promise = deferred.promise()
promise.then(callback)
deferred.resolve()
return promise
return Promise.resolve().then(callback)
}

@@ -166,13 +164,10 @@

request.open('GET', url, false)
request.onerror = (event) => {
reject({ status: request.status, statusText: request.statusText })
request.onerror = () => {
reject(new Error(request.statusText))
}
request.onload = (event) => {
request.onload = () => {
if (request.status >= 200 && request.status < 300) {
resolve(JSON.parse(request.responseText))
} else {
reject({
status: request.status,
statusText: request.statusText,
})
reject(new Error(request.statusText))
}

@@ -189,2 +184,3 @@ }

}
return result
})

@@ -198,3 +194,3 @@ .then(callback)

* @param {string} appName name of the app
* @param {object<string, string>} bundle translation bundle
* @param {Object<string, string>} bundle translation bundle
*/

@@ -238,185 +234,185 @@ export function register(appName: string, bundle: Translations) {

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 '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 '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 '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)
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 'cs':
case 'sk':
return number === 1 ? 0 : number >= 2 && number <= 4 ? 1 : 2
case 'ga':
return number === 1 ? 0 : number === 2 ? 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)
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
case 'sl':
return number % 100 === 1
? 0
: number % 100 === 2
? 1
: number % 100 === 3 || number % 100 === 4
? 2
: 3
? 2
: 3
case 'mk':
return number % 10 === 1 ? 0 : 1
case 'mk':
return number % 10 === 1 ? 0 : 1
case 'mt':
return number === 1
? 0
: number === 0 || (number % 100 > 1 && number % 100 < 11)
case 'mt':
return number === 1
? 0
: number === 0 || (number % 100 > 1 && number % 100 < 11)
? 1
: number % 100 > 10 && number % 100 < 20
? 2
: 3
? 2
: 3
case 'lv':
return number === 0
? 0
: number % 10 === 1 && number % 100 !== 11
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)
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
case 'cy':
return number === 1
? 0
: number === 2
? 1
: number === 8 || number === 11
? 2
: 3
? 2
: 3
case 'ro':
return number === 1
? 0
: number === 0 || (number % 100 > 0 && number % 100 < 20)
case 'ro':
return number === 1
? 0
: number === 0 || (number % 100 > 0 && number % 100 < 20)
? 1
: 2
case 'ar':
return number === 0
? 0
: number === 1
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
? 2
: number % 100 >= 3 && number % 100 <= 10
? 3
: number % 100 >= 11 && number % 100 <= 99
? 4
: 5
default:
return 0
default:
return 0
}
}
{
"name": "@nextcloud/l10n",
"version": "2.0.0-beta.0",
"version": "2.0.0",
"description": "",

@@ -23,2 +23,4 @@ "main": "dist/index.js",

"dev": "rollup -c -w",
"lint": "eslint .",
"lint:fix": "eslint --fix lib tests",
"test": "jest",

@@ -45,2 +47,3 @@ "test:watch": "jest --watchAll"

"@nextcloud/browserslist-config": "^2.3.0",
"@nextcloud/eslint-config": "^8.2.0",
"@nextcloud/typings": "^1.6.0",

@@ -47,0 +50,0 @@ "@rollup/plugin-typescript": "^11.0.0",

@@ -0,111 +1,112 @@

/* eslint-disable @typescript-eslint/ban-ts-comment */
/// <reference types="@nextcloud/typings" />
declare var window: Nextcloud.v24.WindowWithGlobals
import { getDayNames, getDayNamesMin, getDayNamesShort, getFirstDay, getMonthNames, getMonthNamesShort } from '../lib/date'
declare let window: Nextcloud.v24.WindowWithGlobals
describe('date', () => {
const orginalWarn = console.warn
const orginalWarn = console.warn
afterAll(() => {
console.warn = orginalWarn
})
beforeEach(() => {
console.warn = jest.fn()
})
afterAll(() => {
console.warn = orginalWarn
})
beforeEach(() => {
console.warn = jest.fn()
})
describe('getFirstDay', () => {
// @ts-ignore
afterAll(() => { delete window.firstDay })
describe('getFirstDay', () => {
// @ts-ignore
afterAll(() => { delete window.firstDay })
it('works without `OC`', () => {
expect(getFirstDay()).toBe(1)
// Warning as fallback is being used
expect(console.warn).toBeCalled()
})
it('works without `OC`', () => {
expect(getFirstDay()).toBe(1)
// Warning as fallback is being used
expect(console.warn).toBeCalled()
})
it('works with `OC`', () => {
window.firstDay = 3
expect(getFirstDay()).toBe(3)
})
})
it('works with `OC`', () => {
window.firstDay = 3
expect(getFirstDay()).toBe(3)
})
})
describe('getDayNames', () => {
// @ts-ignore
afterAll(() => { delete window.dayNames })
describe('getDayNames', () => {
// @ts-ignore
afterAll(() => { delete window.dayNames })
it('works without `OC`', () => {
expect(getDayNames().length).toBe(7)
// Warning as fallback is being used
expect(console.warn).toBeCalled()
})
it('works without `OC`', () => {
expect(getDayNames().length).toBe(7)
// Warning as fallback is being used
expect(console.warn).toBeCalled()
})
it('works with `OC`', () => {
window.dayNames = 'a'.repeat(7).split('')
expect(getDayNames()).toBe(window.dayNames)
})
})
it('works with `OC`', () => {
window.dayNames = 'a'.repeat(7).split('')
expect(getDayNames()).toBe(window.dayNames)
})
})
describe('getDayNamesShort', () => {
// @ts-ignore
afterAll(() => { delete window.dayNamesShort })
describe('getDayNamesShort', () => {
// @ts-ignore
afterAll(() => { delete window.dayNamesShort })
it('works without `OC`', () => {
expect(getDayNamesShort().length).toBe(7)
// Warning as fallback is being used
expect(console.warn).toBeCalled()
})
it('works without `OC`', () => {
expect(getDayNamesShort().length).toBe(7)
// Warning as fallback is being used
expect(console.warn).toBeCalled()
})
it('works with `OC`', () => {
window.dayNamesShort = 'b'.repeat(7).split('')
expect(getDayNamesShort()).toBe(window.dayNamesShort)
})
})
it('works with `OC`', () => {
window.dayNamesShort = 'b'.repeat(7).split('')
expect(getDayNamesShort()).toBe(window.dayNamesShort)
})
})
describe('getDayNamesMin', () => {
// @ts-ignore
afterAll(() => { delete window.dayNamesMin })
describe('getDayNamesMin', () => {
// @ts-ignore
afterAll(() => { delete window.dayNamesMin })
it('works without `OC`', () => {
expect(getDayNamesMin().length).toBe(7)
// Warning as fallback is being used
expect(console.warn).toBeCalled()
})
it('works without `OC`', () => {
expect(getDayNamesMin().length).toBe(7)
// Warning as fallback is being used
expect(console.warn).toBeCalled()
})
it('works with `OC`', () => {
window.dayNamesMin = 'c'.repeat(7).split('')
expect(getDayNamesMin()).toBe(window.dayNamesMin)
})
})
it('works with `OC`', () => {
window.dayNamesMin = 'c'.repeat(7).split('')
expect(getDayNamesMin()).toBe(window.dayNamesMin)
})
})
describe('getMonthNames', () => {
// @ts-ignore
afterAll(() => { delete window.monthNames })
describe('getMonthNames', () => {
// @ts-ignore
afterAll(() => { delete window.monthNames })
it('works without `OC`', () => {
expect(getMonthNames().length).toBe(12)
// Warning as fallback is being used
expect(console.warn).toBeCalled()
})
it('works without `OC`', () => {
expect(getMonthNames().length).toBe(12)
// Warning as fallback is being used
expect(console.warn).toBeCalled()
})
it('works with `OC`', () => {
window.monthNames = 'd'.repeat(12).split('')
expect(getMonthNames()).toBe(window.monthNames)
})
})
it('works with `OC`', () => {
window.monthNames = 'd'.repeat(12).split('')
expect(getMonthNames()).toBe(window.monthNames)
})
})
describe('getMonthNamesShort', () => {
// @ts-ignore
afterAll(() => { delete window.monthNamesShort })
describe('getMonthNamesShort', () => {
// @ts-ignore
afterAll(() => { delete window.monthNamesShort })
it('works without `OC`', () => {
expect(getMonthNamesShort().length).toBe(12)
// Warning as fallback is being used
expect(console.warn).toBeCalled()
})
it('works without `OC`', () => {
expect(getMonthNamesShort().length).toBe(12)
// Warning as fallback is being used
expect(console.warn).toBeCalled()
})
it('works with `OC`', () => {
window.monthNamesShort = 'e'.repeat(12).split('')
expect(getMonthNamesShort()).toBe(window.monthNamesShort)
})
})
it('works with `OC`', () => {
window.monthNamesShort = 'e'.repeat(12).split('')
expect(getMonthNamesShort()).toBe(window.monthNamesShort)
})
})
})
import { po } from 'gettext-parser'
import { getGettextBuilder } from '../lib/gettext'
import { getGettextBuilder } from '../lib/gettext.ts'
describe('gettext', () => {
beforeEach(() => {
jest.spyOn(console, 'warn')
})
beforeEach(() => {
jest.spyOn(console, 'warn')
})
afterEach(() => {
console.warn.mockRestore()
})
afterEach(() => {
console.warn.mockRestore()
})
it('falls back to the original string', () => {
const gt = getGettextBuilder()
.setLanguage('de')
.build()
it('falls back to the original string', () => {
const gt = getGettextBuilder()
.setLanguage('de')
.build()
const translation = gt.gettext('Settings')
const translation = gt.gettext('Settings')
expect(translation).toEqual('Settings')
})
expect(translation).toEqual('Settings')
})
it('does not log in production', () => {
const gt = getGettextBuilder()
.setLanguage('de')
.build()
it('does not log in production', () => {
const gt = getGettextBuilder()
.setLanguage('de')
.build()
gt.gettext('Settings')
gt.gettext('Settings')
expect(console.warn).not.toHaveBeenCalled()
})
expect(console.warn).not.toHaveBeenCalled()
})
it('has optional debug logs', () => {
const gt = getGettextBuilder()
.setLanguage('de')
.enableDebugMode()
.build()
it('has optional debug logs', () => {
const gt = getGettextBuilder()
.setLanguage('de')
.enableDebugMode()
.build()
gt.gettext('Settings')
gt.gettext('Settings')
expect(console.warn).toHaveBeenCalled()
})
expect(console.warn).toHaveBeenCalled()
})
it('falls back to the original singular string', () => {
const gt = getGettextBuilder()
.setLanguage('en')
.build()
it('falls back to the original singular string', () => {
const gt = getGettextBuilder()
.setLanguage('en')
.build()
const translated = gt.ngettext('%n Setting', '%n Settings', 1)
const translated = gt.ngettext('%n Setting', '%n Settings', 1)
expect(translated).toEqual('1 Setting')
})
expect(translated).toEqual('1 Setting')
})
it('falls back to the original plural string', () => {
const gt = getGettextBuilder()
.setLanguage('en')
.build()
it('falls back to the original plural string', () => {
const gt = getGettextBuilder()
.setLanguage('en')
.build()
const translated = gt.ngettext('%n Setting', '%n Settings', 2)
const translated = gt.ngettext('%n Setting', '%n Settings', 2)
expect(translated).toEqual('2 Settings')
})
expect(translated).toEqual('2 Settings')
})
it('detects en as default locale/language', () => {
const detected = getGettextBuilder()
.detectLocale()
.build()
it('detects en as default locale/language', () => {
const detected = getGettextBuilder()
.detectLocale()
.build()
const manual = getGettextBuilder()
.setLanguage('en')
.build()
const manual = getGettextBuilder()
.setLanguage('en')
.build()
expect(detected).toEqual(manual)
})
expect(detected).toEqual(manual)
})
it('used nextcloud-style placeholder replacement', () => {
const gt = getGettextBuilder()
.setLanguage('de')
.build()
it('used nextcloud-style placeholder replacement', () => {
const gt = getGettextBuilder()
.setLanguage('de')
.build()
const translation = gt.gettext('I wish Nextcloud were written in {lang}', {
lang: 'Rust'
})
const translation = gt.gettext('I wish Nextcloud were written in {lang}', {
lang: 'Rust',
})
expect(translation).toEqual('I wish Nextcloud were written in Rust')
})
expect(translation).toEqual('I wish Nextcloud were written in Rust')
})
it('is fault tolerant to invalid placeholders', () => {
const gt = getGettextBuilder()
.setLanguage('de')
.build()
it('is fault tolerant to invalid placeholders', () => {
const gt = getGettextBuilder()
.setLanguage('de')
.build()
const translation = gt.gettext('This is {value}', {
value: false
})
const translation = gt.gettext('This is {value}', {
value: false,
})
expect(translation).toEqual('This is {value}')
})
expect(translation).toEqual('This is {value}')
})
it('used nextcloud-style placeholder replacement for plurals', () => {
const gt = getGettextBuilder()
.setLanguage('de')
.build()
it('used nextcloud-style placeholder replacement for plurals', () => {
const gt = getGettextBuilder()
.setLanguage('de')
.build()
const translation = gt.ngettext('%n {what} Setting', '%n {what} Settings', 2, {
what: 'test',
})
const translation = gt.ngettext('%n {what} Setting', '%n {what} Settings', 2, {
what: 'test',
})
expect(translation).toEqual('2 test Settings')
})
expect(translation).toEqual('2 test Settings')
})
it('translates', () => {
const pot = `msgid ""
it('translates', () => {
const pot = `msgid ""
msgstr ""

@@ -125,15 +125,15 @@ "Last-Translator: Translator, 2020\n"

`
const gt = getGettextBuilder()
.setLanguage('sv')
.addTranslation('sv', po.parse(pot))
.build()
const gt = getGettextBuilder()
.setLanguage('sv')
.addTranslation('sv', po.parse(pot))
.build()
const translation = gt.gettext('abc')
const translation = gt.gettext('abc')
expect(translation).toEqual('def')
})
expect(translation).toEqual('def')
})
it('translates plurals', () => {
// From https://www.gnu.org/software/gettext/manual/html_node/Translating-plural-forms.html
const pot = `msgid ""
it('translates plurals', () => {
// From https://www.gnu.org/software/gettext/manual/html_node/Translating-plural-forms.html
const pot = `msgid ""
msgstr ""

@@ -151,14 +151,14 @@ "Last-Translator: Translator, 2020\n"

`
const gt = getGettextBuilder()
.setLanguage('sv')
.addTranslation('sv', po.parse(pot))
.build()
const gt = getGettextBuilder()
.setLanguage('sv')
.addTranslation('sv', po.parse(pot))
.build()
const translation = gt.ngettext('One file removed', '%n files removed', 2)
const translation = gt.ngettext('One file removed', '%n files removed', 2)
expect(translation).toEqual('2 slika uklonjenih')
})
expect(translation).toEqual('2 slika uklonjenih')
})
it('falls back to english', () => {
const pot = `msgid ""
it('falls back to english', () => {
const pot = `msgid ""
msgstr ""

@@ -173,22 +173,22 @@ "Last-Translator: Translator, 2023\n"

`
// Do not set local explicitly, so 'en' should be used
const gt = getGettextBuilder()
.addTranslation('en', po.parse(pot))
.build()
// Do not set local explicitly, so 'en' should be used
const gt = getGettextBuilder()
.addTranslation('en', po.parse(pot))
.build()
const translation = gt.gettext('abc')
const translation = gt.gettext('abc')
expect(translation).toEqual('xyz')
})
expect(translation).toEqual('xyz')
})
it('does not escape special chars', () => {
const gt = getGettextBuilder()
.setLanguage('de')
.build()
it('does not escape special chars', () => {
const gt = getGettextBuilder()
.setLanguage('de')
.build()
const translation = gt.gettext('test & stuff')
const translation = gt.gettext('test & stuff')
expect(translation).toEqual('test & stuff')
})
expect(translation).toEqual('test & stuff')
})
})
import {
getCanonicalLocale,
getFirstDay,
getDayNames,
getDayNamesShort,
getDayNamesMin,
getMonthNames,
getMonthNamesShort,
translate,
translatePlural
getCanonicalLocale,
translate,
translatePlural,
} from '../lib/index'

@@ -16,72 +10,72 @@

describe('translate', () => {
const mockWindowDE = () => {
window._oc_l10n_registry_translations = {
core: {
'Hello world!': 'Hallo Welt!',
'Hello {name}': 'Hallo {name}',
'_download %n file_::_download %n files_': [
'Lade %n Datei herunter',
'Lade %n Dateien herunter'
],
}
}
window._oc_l10n_registry_plural_functions = {
core: (t) => 1 === t ? 0 : 1
}
setLocale('de')
}
const mockWindowDE = () => {
window._oc_l10n_registry_translations = {
core: {
'Hello world!': 'Hallo Welt!',
'Hello {name}': 'Hallo {name}',
'_download %n file_::_download %n files_': [
'Lade %n Datei herunter',
'Lade %n Dateien herunter',
],
},
}
window._oc_l10n_registry_plural_functions = {
core: (t) => t === 1 ? 0 : 1,
}
setLocale('de')
}
beforeAll(mockWindowDE)
beforeAll(mockWindowDE)
it('singular', () => {
const text = 'Hello world!'
const translation = translate('core', text)
expect(translation).toBe('Hallo Welt!')
})
it('singular', () => {
const text = 'Hello world!'
const translation = translate('core', text)
expect(translation).toBe('Hallo Welt!')
})
it('with variable', () => {
const text = 'Hello {name}'
const translation = translate('core', text, {name: 'J. Doe'})
expect(translation).toBe('Hallo J. Doe')
})
it('with variable', () => {
const text = 'Hello {name}'
const translation = translate('core', text, { name: 'J. Doe' })
expect(translation).toBe('Hallo J. Doe')
})
it('plural', () => {
const text = ['download %n file', 'download %n files']
it('plural', () => {
const text = ['download %n file', 'download %n files']
expect(translatePlural('core', ...text, 1)).toBe('Lade 1 Datei herunter')
expect(translatePlural('core', ...text, 1)).toBe('Lade 1 Datei herunter')
expect(translatePlural('core', ...text, 2)).toBe('Lade 2 Dateien herunter')
})
expect(translatePlural('core', ...text, 2)).toBe('Lade 2 Dateien herunter')
})
it('missing text', () => {
const text = 'Good bye!'
const translation = translate('core', text)
expect(translation).toBe('Good bye!')
})
it('missing text', () => {
const text = 'Good bye!'
const translation = translate('core', text)
expect(translation).toBe('Good bye!')
})
it('missing application', () => {
const text = 'Good bye!'
const translation = translate('unavailable', text)
expect(translation).toBe('Good bye!')
})
it('missing application', () => {
const text = 'Good bye!'
const translation = translate('unavailable', text)
expect(translation).toBe('Good bye!')
})
})
describe('getCanonicalLocale', () => {
afterEach(() => {
setLocale('')
})
afterEach(() => {
setLocale('')
})
it('Returns primary locales as is', () => {
setLocale('de')
expect(getCanonicalLocale()).toEqual('de')
setLocale('zu')
expect(getCanonicalLocale()).toEqual('zu')
})
it('Returns primary locales as is', () => {
setLocale('de')
expect(getCanonicalLocale()).toEqual('de')
setLocale('zu')
expect(getCanonicalLocale()).toEqual('zu')
})
it('Returns extended locales with hyphens', () => {
setLocale('az_Cyrl_AZ')
expect(getCanonicalLocale()).toEqual('az-Cyrl-AZ')
setLocale('de_DE')
expect(getCanonicalLocale()).toEqual('de-DE')
})
it('Returns extended locales with hyphens', () => {
setLocale('az_Cyrl_AZ')
expect(getCanonicalLocale()).toEqual('az-Cyrl-AZ')
setLocale('de_DE')
expect(getCanonicalLocale()).toEqual('de-DE')
})
})
{
"$schema": "https://json.schemastore.org/tsconfig",
"compilerOptions": {
"target": "ES2020",
"target": "ES2019", // ES2020 requires Safari 13+
"module": "ES2020",

@@ -6,0 +6,0 @@ "declaration": true,

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc