@softkit/i18n
Version:
This library is a simple wrapper based on [nestjs-i18n](https://nestjs-i18n.com/)
244 lines • 10.3 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.I18nService = void 0;
const tslib_1 = require("tslib");
/* eslint-disable @typescript-eslint/ban-ts-comment */
const common_1 = require("@nestjs/common");
const i18n_constants_1 = require("../i18n.constants");
const class_validator_1 = require("class-validator");
const utils_1 = require("../utils");
const loaders_utils_1 = require("../utils/loaders-utils");
const i18n_error_1 = require("../i18n.error");
const pluralKeys = ['zero', 'one', 'two', 'few', 'many', 'other'];
let I18nService = class I18nService {
constructor(i18nOptions, translations, supportedLanguages, logger, loaders) {
this.i18nOptions = i18nOptions;
this.translations = translations;
this.supportedLanguages = supportedLanguages;
this.logger = logger;
this.loaders = loaders;
this.pluralRules = new Map();
this.hbsHelper = (key, args, options) => {
if (!options) {
options = args;
}
const lang = options.lookupProperty(options.data.root, 'i18nLang');
return this.t(key, { lang, args });
};
}
translate(key, options) {
options = {
lang: this.i18nOptions.fallbackLanguage,
...options,
};
const { defaultValue } = options;
let { lang } = options;
if (lang === 'debug') {
return key;
}
const previousFallbackLang = lang;
lang = lang ?? this.i18nOptions.fallbackLanguage;
lang = this.resolveLanguage(lang);
const translationsByLanguage = this.translations[lang];
const translation = this.translateObject(key, (translationsByLanguage ?? key), lang, options, translationsByLanguage);
if (translationsByLanguage == undefined || !translation) {
const translationKeyMissing = `Translation "${key}" in "${lang}" does not exist.`;
if (lang !== this.i18nOptions.fallbackLanguage || !!defaultValue) {
if (this.i18nOptions.logging && this.i18nOptions.throwOnMissingKey) {
throw new i18n_error_1.I18nError(translationKeyMissing);
}
const nextFallbackLanguage = this.getFallbackLanguage(lang);
if (previousFallbackLang !== nextFallbackLanguage) {
return this.translate(key, {
...options,
lang: nextFallbackLanguage,
});
}
}
this.logger.error(translationKeyMissing);
}
return (translation ?? key);
}
t(key, options) {
return this.translate(key, options);
}
getSupportedLanguages() {
return this.supportedLanguages;
}
getTranslations() {
return this.translations;
}
async refresh() {
if (Object.keys(this.translations).length === 0) {
this.translations = await (0, loaders_utils_1.processTranslations)(this.loaders);
}
if (this.supportedLanguages.length === 0) {
this.supportedLanguages = await (0, loaders_utils_1.processLanguages)(this.loaders);
}
}
resolveLanguage(lang) {
if (this.i18nOptions.fallbacks && !this.supportedLanguages.includes(lang)) {
const sanitizedLang = lang.includes('-')
? [...lang.slice(0, Math.max(0, lang.indexOf('-'))), '-*']
: lang;
for (const key in this.i18nOptions.fallbacks) {
if (key === lang || key === sanitizedLang) {
lang = this.i18nOptions.fallbacks[key];
break;
}
}
}
return lang;
}
async validate(value, options) {
const errors = await (0, class_validator_1.validate)(value, this.i18nOptions.validatorOptions);
return (0, utils_1.formatI18nErrors)(errors, this, options);
}
getFallbackLanguage(lang) {
let regionSepIndex = -1;
if (lang.includes('-')) {
regionSepIndex = lang.lastIndexOf('-');
}
if (lang.includes('_')) {
regionSepIndex = lang.lastIndexOf('_');
}
return regionSepIndex === -1
? this.i18nOptions.fallbackLanguage
: lang.slice(0, regionSepIndex);
}
translateObject(key, translations, lang, options, rootTranslations) {
const keys = key.split('.');
const [firstKey] = keys;
const args = options?.args;
if (keys.length > 1 &&
translations instanceof Object &&
!translations[key]) {
const newKey = keys.slice(1, keys.length).join('.');
if (translations && translations[firstKey]) {
return this.translateObject(newKey, translations[firstKey], lang, options, rootTranslations);
}
}
let translation;
if (typeof translations === 'object' && translations[key]) {
translation = translations[key];
}
else {
this.logger.error(`Error: Translation key not found, fix it! Key: ${key}`);
translation = options?.defaultValue;
}
if (translation && args !== undefined && args !== null) {
const pluralObject = this.getPluralObject(translation);
if (pluralObject && !Array.isArray(args) && args['count'] !== undefined) {
const count = Number(args['count']);
if (!this.pluralRules.has(lang)) {
this.pluralRules.set(lang, new Intl.PluralRules(lang));
}
const pluralRules = this.pluralRules.get(lang);
if (pluralRules) {
const pluralCategory = pluralRules.select(count);
const translationValue = pluralObject[pluralCategory];
if (count === 0 && pluralObject['zero']) {
translation = pluralObject['zero'];
}
else if (translationValue) {
translation = translationValue;
}
}
}
else if (translation instanceof Object) {
const translations = translation;
const result = Object.keys(translation).reduce((obj, nestedKey) => {
return {
...obj,
[nestedKey]: this.translateObject(nestedKey, translations, lang, options, rootTranslations),
};
}, {});
if (Array.isArray(translation)) {
return Object.values(result);
}
return result;
}
if (typeof translation === 'string') {
const formatter = this.i18nOptions.formatter;
if (formatter) {
translation = formatter(translation, ...(Array.isArray(args) ? args : [args]));
}
}
if (typeof translation === 'string') {
const nestedTranslations = this.getNestedTranslations(translation);
if (nestedTranslations && nestedTranslations.length > 0) {
let offset = 0;
for (const nestedTranslation of nestedTranslations) {
const result = rootTranslations
? this.translateObject(nestedTranslation.key, rootTranslations, lang, {
...options,
args: {
parent: options && options.args,
...nestedTranslation.args,
},
}) ?? ''
: '';
translation =
translation.slice(0,
// @ts-ignore
Math.max(0, nestedTranslation.index - offset)) +
result +
translation.slice(Math.max(0,
// @ts-ignore
nestedTranslation.index + nestedTranslation.length - offset));
// @ts-ignore
offset = offset + (nestedTranslation.length - result.length);
}
}
}
}
return translation;
}
getPluralObject(translation) {
for (const k of pluralKeys) {
if (translation[k]) {
return translation;
}
}
return undefined;
}
getNestedTranslations(translation) {
const list = [];
const regex = /\$t\((.*?)(,(.*?))?\)/g;
let result;
while ((result = regex.exec(translation))) {
let key = undefined;
let args = {};
let index = undefined;
let length = undefined;
if (result && result.length > 0) {
key = result[1].trim();
index = result.index;
length = result[0].length;
if (result.length >= 3 && result[3]) {
try {
args = JSON.parse(result[3]);
}
catch (error) {
this.logger.error(`Error while parsing JSON`, error);
}
}
}
if (key) {
list.push({ index, length, key, args });
}
result = undefined;
}
return list.length > 0 ? list : undefined;
}
};
exports.I18nService = I18nService;
exports.I18nService = I18nService = tslib_1.__decorate([
(0, common_1.Injectable)(),
tslib_1.__param(0, (0, common_1.Inject)(i18n_constants_1.I18N_OPTIONS)),
tslib_1.__param(1, (0, common_1.Inject)(i18n_constants_1.I18N_TRANSLATIONS)),
tslib_1.__param(2, (0, common_1.Inject)(i18n_constants_1.I18N_LANGUAGES)),
tslib_1.__param(4, (0, common_1.Inject)(i18n_constants_1.I18N_LOADERS)),
tslib_1.__metadata("design:paramtypes", [Object, Object, Array, common_1.Logger, Array])
], I18nService);
//# sourceMappingURL=i18n.service.js.map