@opra/common
Version:
Opra common package
135 lines (134 loc) • 5.28 kB
JavaScript
import i18next from '@browsery/i18next';
import { splitString, tokenize } from 'fast-tokenizer';
import { unescapeString } from './string-utils.js';
// eslint-disable-next-line import-x/no-named-as-default-member
export const BaseI18n = Object.getPrototypeOf(i18next.createInstance())
.constructor;
export class I18n extends BaseI18n {
async init(arg0, arg1) {
const options = typeof arg0 === 'object' ? arg0 : {};
const callback = typeof arg0 === 'function' ? arg0 : arg1;
try {
const t = await super.init(options, callback);
// Add formatters
const formatter = this.services.formatter;
formatter.add('lowercase', (value, lng) => value.toLocaleLowerCase(lng));
formatter.add('uppercase', (value, lng) => value.toLocaleUpperCase(lng));
formatter.add('upperFirst', (value, lng) => value.charAt(0).toLocaleUpperCase(lng) + value.substring(1));
// overwrite existing resources with options.resources
if (options?.resources) {
for (const lang of Object.keys(options.resources)) {
const langObj = options.resources[lang];
for (const ns of Object.keys(langObj)) {
this.addResourceBundle(lang, ns, langObj[ns], false, true);
}
}
}
if (callback)
callback(null, t);
return t;
}
catch (err) {
if (callback)
callback(err, this.t);
throw err;
}
}
deep(input, options) {
if (input == null)
return input;
const objectStack = new WeakMap();
return this._deepTranslate(input, objectStack, options);
}
createInstance(options, callback) {
return new I18n(options, callback);
}
static createInstance(options, callback) {
return new I18n(options, callback);
}
_deepTranslate(input, objectStack, options) {
if (input == null)
return input;
if (options?.ignore && options.ignore(input, this))
return input;
if (typeof input === 'object' && objectStack.has(input))
return objectStack.get(input);
if (typeof input === 'string') {
let s = '';
for (let token of tokenize(input, {
brackets: { '$t(': ')' },
quotes: true,
keepQuotes: true,
keepBrackets: true,
keepDelimiters: true,
})) {
if (token.startsWith('$t(') && token.endsWith(')')) {
token = token.substring(3, token.length - 1);
const a = splitString(token, {
delimiters: '?',
quotes: true,
brackets: { '{': '}' },
});
const fallback = unescapeString(token.substring((a[0] || '').length + 1));
token = a[0] || '';
const keys = [];
let opts = null;
for (const token2 of tokenize(token, {
delimiters: ',',
quotes: true,
brackets: { '{': '}' },
})) {
if (token2.startsWith('{')) {
opts = JSON.parse(token2);
continue;
}
keys.push(token2);
}
const k = keys.length > 1 ? '$t(' + keys.join(',') + ')' : keys[0];
s += fallback
? this.t(k, fallback, { ...options, ...opts })
: this.t(k, { ...options, ...opts });
continue;
}
s += token;
}
return s;
}
if (Array.isArray(input)) {
const out = Array(input.length);
objectStack.set(input, out);
for (let i = 0, l = input.length; i < l; i++) {
out[i] = this._deepTranslate(input[i], objectStack, options);
}
objectStack.delete(input);
return out;
}
if (typeof input === 'object') {
if (Buffer.isBuffer(input))
return input;
if (Buffer.isBuffer(input) ||
input instanceof Symbol ||
input instanceof RegExp ||
input instanceof Map ||
input instanceof Set ||
input instanceof WeakMap ||
input instanceof WeakSet) {
return input;
}
const out = {};
objectStack.set(input, out);
const keys = Object.keys(input);
for (let i = 0, l = keys.length; i < l; i++) {
const k = keys[i];
out[k] = this._deepTranslate(input[k], objectStack, options);
}
objectStack.delete(input);
return out;
}
return input;
}
static get defaultInstance() {
return defaultInstance;
}
}
const defaultInstance = I18n.createInstance();