diff --git a/lib/date.ts b/lib/date.ts
new file mode 100644
index 00000000..f616431a
--- /dev/null
+++ b/lib/date.ts
@@ -0,0 +1,121 @@
+///
+
+declare var window: Nextcloud.v24.WindowWithGlobals
+
+/**
+ * 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
+}
diff --git a/lib/index.ts b/lib/index.ts
index 1df0cb92..b8f6bafe 100644
--- a/lib/index.ts
+++ b/lib/index.ts
@@ -1,201 +1,2 @@
-///
-
-declare var window: Nextcloud.v16.WindowWithGlobals
- | Nextcloud.v17.WindowWithGlobals
- | Nextcloud.v18.WindowWithGlobals
- | Nextcloud.v19.WindowWithGlobals;
-
-import DOMPurify from 'dompurify'
-import escapeHTML from 'escape-html'
-import { getAppTranslations } from './registry'
-
-/**
- * Returns the user's locale
- */
-export function getLocale(): string {
- return document.documentElement.dataset.locale || 'en'
-}
-
-export function getCanonicalLocale(): string {
- return getLocale().replace(/_/g, '-')
-}
-
-/**
- * Returns the user's language
- */
-export function getLanguage(): string {
- return document.documentElement.lang || 'en'
-}
-
-interface TranslationOptions {
- /** enable/disable auto escape of placeholders (by default enabled) */
- escape?: boolean,
- /** enable/disable sanitization (by default enabled) */
- sanitize?: 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?: Record, number?: number, options?: TranslationOptions): string {
- 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: string, vars?: Record, number?: 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)
- }
-}
-
-/**
- * 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} number 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, number: number, vars?: object, options?: TranslationOptions): string {
- 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)
- }
-}
-
-/**
- * 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'
diff --git a/lib/registry.ts b/lib/registry.ts
index 872ebc98..7a86bd38 100644
--- a/lib/registry.ts
+++ b/lib/registry.ts
@@ -1,29 +1,109 @@
+export type Translations = Record
+export type PluralFunction = (number: number) => number
+
declare var window: {
- _oc_l10n_registry_translations: Record>
- _oc_l10n_registry_plural_functions: Record number>
+ _oc_l10n_registry_translations: Record
+ _oc_l10n_registry_plural_functions: Record
}
interface AppTranslations {
- translations: Record;
- pluralFunction: (number: number) => number;
+ translations: Translations
+ pluralFunction: PluralFunction
+}
+
+/**
+ * Check if translations and plural function are set for given app
+ * @param {string} appId the app id
+ * @return {boolean}
+ */
+export function hasAppTranslations(appId: string) {
+ 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
+ */
+export function registerAppTranslations(
+ appId: string,
+ translations: Translations,
+ pluralFunction: 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
+ */
+export function unregisterAppTranslations(appId: string) {
+ 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}
*/
export function getAppTranslations(appId: string): AppTranslations {
- 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) => number
- }
- }
-
- return {
- translations: window._oc_l10n_registry_translations[appId] || {},
- pluralFunction: window._oc_l10n_registry_plural_functions[appId],
- }
-}
\ No newline at end of file
+ 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) => 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: string,
+ translations: Translations,
+ pluralFunction: 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: string,
+ translations: Translations,
+ pluralFunction?: 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
+ }
+}
diff --git a/lib/translation.ts b/lib/translation.ts
new file mode 100644
index 00000000..f8100367
--- /dev/null
+++ b/lib/translation.ts
@@ -0,0 +1,410 @@
+import {
+ getAppTranslations,
+ hasAppTranslations,
+ registerAppTranslations,
+ unregisterAppTranslations,
+} from './registry'
+import type { Translations } from './registry'
+import { generateFilePath } from '@nextcloud/router'
+
+import DOMPurify from 'dompurify'
+import escapeHTML from 'escape-html'
+
+interface TranslationOptions {
+ /** enable/disable auto escape of placeholders (by default enabled) */
+ escape?: boolean
+ /** enable/disable sanitization (by default enabled) */
+ sanitize?: boolean
+}
+
+/**
+ * Returns the user's locale
+ */
+export function getLocale(): string {
+ return document.documentElement.dataset.locale || 'en'
+}
+
+export function getCanonicalLocale(): string {
+ return getLocale().replace(/_/g, '-')
+}
+
+/**
+ * Returns the user's language
+ */
+export function getLanguage(): string {
+ return document.documentElement.lang || 'en'
+}
+
+/**
+ * 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?: Record,
+ number?: number,
+ options?: TranslationOptions
+): string {
+ 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: string, vars?: Record, number?: 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)
+ }
+}
+
+/**
+ * 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} number 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,
+ number: number,
+ vars?: object,
+ options?: TranslationOptions
+): string {
+ 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
+ */
+export function loadTranslations(appName: string, callback: (...args: any[]) => any) {
+ // already available ?
+ if (hasAppTranslations(appName) || getLocale() === 'en') {
+ const deferred = $.Deferred()
+ const promise = deferred.promise()
+ promise.then(callback)
+ deferred.resolve()
+ return promise
+ }
+
+ const url = generateFilePath(appName, 'l10n', getLocale() + '.json')
+
+ const promise = new Promise<{
+ translations: Translations
+ pluralForm: string
+ }>((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} bundle translation bundle
+ */
+export function register(appName: string, bundle: Translations) {
+ registerAppTranslations(appName, bundle, getPlural)
+}
+
+/**
+ * @private
+ */
+export 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, ...)
+ */
+export function getPlural(number: 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
+ }
+}
diff --git a/package-lock.json b/package-lock.json
index 0bbe4d51..c2da0806 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -9,6 +9,7 @@
"version": "1.6.0",
"license": "GPL-3.0-or-later",
"dependencies": {
+ "@nextcloud/router": "^2.0.0",
"core-js": "^3.6.4",
"dompurify": "^2.4.1",
"escape-html": "^1.0.3",
@@ -2727,6 +2728,18 @@
"npm": "^7.0.0 || ^8.0.0"
}
},
+ "node_modules/@nextcloud/router": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/@nextcloud/router/-/router-2.0.1.tgz",
+ "integrity": "sha512-qLRxTjZS6y9NUPmU6X3Ega5qHPeEx4kCgqwo0I6Y9wV71EGGi9zPnWDsqmmmJj8RkDp30jcfGNWCTwbPAebTDA==",
+ "dependencies": {
+ "core-js": "^3.6.4"
+ },
+ "engines": {
+ "node": "^16.0.0",
+ "npm": "^7.0.0 || ^8.0.0"
+ }
+ },
"node_modules/@nextcloud/typings": {
"version": "1.5.0",
"resolved": "https://registry.npmjs.org/@nextcloud/typings/-/typings-1.5.0.tgz",
@@ -10410,6 +10423,14 @@
"integrity": "sha512-1Tpkof2e9Q0UicHWahQnXXrubJoqyiaqsH9G52v3cjGeVeH3BCfa1FOa41eBwBSFe2/Jxj/wCH2YVLgIXpWbBg==",
"dev": true
},
+ "@nextcloud/router": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/@nextcloud/router/-/router-2.0.1.tgz",
+ "integrity": "sha512-qLRxTjZS6y9NUPmU6X3Ega5qHPeEx4kCgqwo0I6Y9wV71EGGi9zPnWDsqmmmJj8RkDp30jcfGNWCTwbPAebTDA==",
+ "requires": {
+ "core-js": "^3.6.4"
+ }
+ },
"@nextcloud/typings": {
"version": "1.5.0",
"resolved": "https://registry.npmjs.org/@nextcloud/typings/-/typings-1.5.0.tgz",
diff --git a/package.json b/package.json
index f2e57881..131c7426 100644
--- a/package.json
+++ b/package.json
@@ -33,6 +33,7 @@
"url": "https://github.com/nextcloud/nextcloud-l10n"
},
"dependencies": {
+ "@nextcloud/router": "^2.0.0",
"core-js": "^3.6.4",
"dompurify": "^2.4.1",
"escape-html": "^1.0.3",