nestjs-i18n
Version:
The i18n module for Nest.
467 lines • 21.3 kB
JavaScript
"use strict";
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
var desc = Object.getOwnPropertyDescriptor(m, k);
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
desc = { enumerable: true, get: function() { return m[k]; } };
}
Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
o["default"] = v;
});
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
return c > 3 && r && Object.defineProperty(target, key, r), r;
};
var __importStar = (this && this.__importStar) || (function () {
var ownKeys = function(o) {
ownKeys = Object.getOwnPropertyNames || function (o) {
var ar = [];
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
return ar;
};
return ownKeys(o);
};
return function (mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
__setModuleDefault(result, mod);
return result;
};
})();
var __metadata = (this && this.__metadata) || function (k, v) {
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
};
var __param = (this && this.__param) || function (paramIndex, decorator) {
return function (target, key) { decorator(target, key, paramIndex); }
};
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
var I18nModule_1;
Object.defineProperty(exports, "__esModule", { value: true });
exports.I18nModule = void 0;
const promises_1 = require("fs/promises");
const path_1 = __importDefault(require("path"));
const common_1 = require("@nestjs/common");
const core_1 = require("@nestjs/core");
const rxjs_1 = require("rxjs");
const string_format_1 = __importDefault(require("string-format"));
const decorators_1 = require("./decorators");
const i18n_constants_1 = require("./i18n.constants");
const i18n_language_interceptor_1 = require("./interceptors/i18n-language.interceptor");
const loaders_1 = require("./loaders");
const i18n_loader_1 = require("./loaders/i18n.loader");
const i18n_middleware_1 = require("./middlewares/i18n.middleware");
const i18n_service_1 = require("./services/i18n.service");
const utils_1 = require("./utils");
const defaultOptions = {
resolvers: [],
formatter: string_format_1.default,
logging: true,
throwOnMissingKey: false,
loader: loaders_1.I18nJsonLoader,
};
let I18nModule = I18nModule_1 = class I18nModule {
constructor(i18n, translations, i18nOptions, adapter, middleware) {
this.i18n = i18n;
this.translations = translations;
this.i18nOptions = i18nOptions;
this.adapter = adapter;
this.middleware = middleware;
this.unsubscribe = new rxjs_1.Subject();
}
async onModuleInit() {
await this.i18n.refresh();
if (this.i18nOptions.viewEngine) {
if (['hbs', 'handlebars'].includes(this.i18nOptions.viewEngine)) {
try {
const hbsModule = this.i18nOptions.viewEngine === 'hbs'
? await Promise.resolve().then(() => __importStar(require('hbs')))
: await Promise.resolve().then(() => __importStar(require('handlebars')));
const hbs = hbsModule.default ?? hbsModule;
hbs.registerHelper('t', this.i18n.hbsHelper);
utils_1.logger.log('Handlebars helper registered');
}
catch (e) {
utils_1.logger.error(this.i18nOptions.viewEngine + ' module failed to load', e);
}
}
if (['pug', 'ejs', 'eta', 'nunjucks'].includes(this.i18nOptions.viewEngine) &&
!this.adapter.httpAdapter.constructor.name.toLowerCase().startsWith('fastify')) {
const app = this.adapter.httpAdapter.getInstance();
app.locals ??= {};
app.locals['t'] = (key, lang, args) => {
return this.i18n.t(key, { lang, args });
};
}
}
if (this.i18nOptions.typesOutputPath) {
try {
const ts = await Promise.resolve().then(() => __importStar(require('./utils/typescript')));
const typesOutputPath = this.i18nOptions.typesOutputPath;
this.translations
.pipe((0, rxjs_1.takeUntil)(this.unsubscribe), (0, rxjs_1.switchMap)(async (t) => {
try {
utils_1.logger.log('Checking translation changes');
const object = Object.keys(t).reduce((result, key) => (0, utils_1.mergeDeep)(result, t[key]), {});
const rawContent = await ts.createTypesFile(object);
if (!rawContent) {
return;
}
const outputFile = ts.annotateSourceCode(rawContent);
await (0, promises_1.mkdir)(path_1.default.dirname(typesOutputPath), {
recursive: true,
});
let currentFileContent = null;
try {
currentFileContent = await (0, promises_1.readFile)(typesOutputPath, 'utf8');
}
catch (err) {
utils_1.logger.error(err);
}
if (currentFileContent != outputFile) {
await (0, promises_1.writeFile)(typesOutputPath, outputFile);
utils_1.logger.log(`Types generated in: ${this.i18nOptions.typesOutputPath}.
Please also add it to ignore files of your linter and formatter to avoid linting and formatting it
`);
}
else {
utils_1.logger.log('No changes detected');
}
}
catch (err) {
utils_1.logger.error('Error generating types file', err);
}
}))
.subscribe();
}
catch {
utils_1.logger.error('Typescript package not found, types generation failed. Please install typescript as a dev dependency to enable this feature.');
}
}
}
onModuleDestroy() {
this.unsubscribe.next();
this.unsubscribe.complete();
}
configure(consumer) {
if (this.i18nOptions.disableMiddleware)
return;
const middlewareRoute = (0, utils_1.usingFastify)(consumer) ? '*' : '*path';
consumer.apply(i18n_middleware_1.I18nMiddleware).forRoutes(middlewareRoute);
if ((0, utils_1.usingFastify)(consumer)) {
consumer.httpAdapter.getInstance().addHook('preHandler', async (request, reply) => {
const locals = reply.locals ?? (reply.locals = {});
if (request.raw.i18nLang) {
locals.i18nLang = request.raw.i18nLang;
}
if (this.i18nOptions.viewEngine &&
['pug', 'ejs', 'eta', 'nunjucks'].includes(this.i18nOptions.viewEngine)) {
locals.t = (key, lang, args) => {
return this.i18n.t(key, { lang, args });
};
}
});
}
if (!(0, utils_1.usingFastify)(consumer) && (0, utils_1.isNestMiddleware)(consumer)) {
const adapterInstance = consumer.httpAdapter.getInstance();
if (typeof adapterInstance?.use === 'function') {
const noop = () => { };
adapterInstance.use(async (err, req, res, next) => {
if (!req.i18nContext) {
await this.middleware.use(req, res, noop);
}
next(err);
});
}
}
}
static forRoot(options) {
options = this.sanitizeI18nOptions(options);
const i18nLanguagesSubject = new rxjs_1.BehaviorSubject([]);
const i18nTranslationSubject = new rxjs_1.BehaviorSubject({});
const i18nOptions = {
provide: i18n_constants_1.I18N_OPTIONS,
useValue: options,
};
const legacyProviders = [];
let i18nLoadersProvider;
if (options.loaders && options.loaders.length > 0) {
i18nLoadersProvider = {
provide: i18n_constants_1.I18N_LOADERS,
useValue: options.loaders,
};
}
else {
const i18nLoaderProvider = {
provide: i18n_loader_1.I18nLoader,
useClass: options.loader,
};
const i18nLoaderOptionsProvider = {
provide: i18n_constants_1.I18N_LOADER_OPTIONS,
useValue: options.loaderOptions,
};
legacyProviders.push(i18nLoaderProvider, i18nLoaderOptionsProvider);
i18nLoadersProvider = {
provide: i18n_constants_1.I18N_LOADERS,
useFactory: (loader) => [loader],
inject: [i18n_loader_1.I18nLoader],
};
}
const i18nLanguagesSubjectProvider = {
provide: i18n_constants_1.I18N_LANGUAGES_SUBJECT,
useValue: i18nLanguagesSubject,
};
const i18nTranslationSubjectProvider = {
provide: i18n_constants_1.I18N_TRANSLATIONS_SUBJECT,
useValue: i18nTranslationSubject,
};
const translationsProvider = this.createMultiLoaderStreamProvider(i18n_constants_1.I18N_TRANSLATIONS, (loaders) => (0, utils_1.processTranslations)(loaders), i18nTranslationSubject);
const languagesProvider = this.createMultiLoaderStreamProvider(i18n_constants_1.I18N_LANGUAGES, (loaders) => (0, utils_1.processLanguages)(loaders), i18nLanguagesSubject);
const resolversProvider = {
provide: i18n_constants_1.I18N_RESOLVERS,
useValue: options.resolvers || [],
};
const i18nMessageFormatProvider = {
provide: utils_1.I18nMessageFormat,
useFactory: (resolvedOptions) => new utils_1.I18nMessageFormat(resolvedOptions),
inject: [i18n_constants_1.I18N_OPTIONS],
};
return {
module: I18nModule_1,
providers: [
{ provide: common_1.Logger, useValue: utils_1.logger },
{
provide: core_1.APP_INTERCEPTOR,
useClass: i18n_language_interceptor_1.I18nLanguageInterceptor,
},
i18n_service_1.I18nService,
i18n_middleware_1.I18nMiddleware,
i18nOptions,
translationsProvider,
languagesProvider,
resolversProvider,
i18nMessageFormatProvider,
i18nLoadersProvider,
...legacyProviders,
i18nLanguagesSubjectProvider,
i18nTranslationSubjectProvider,
...this.createResolverProviders(options.resolvers),
],
exports: [i18n_constants_1.I18N_OPTIONS, i18n_constants_1.I18N_RESOLVERS, i18n_service_1.I18nService, i18n_middleware_1.I18nMiddleware, languagesProvider],
};
}
static forRootAsync(options) {
options = this.sanitizeI18nOptions(options);
const asyncOptionsProvider = this.createAsyncOptionsProvider(options);
const asyncTranslationProvider = this.createAsyncTranslationProvider();
const asyncLanguagesProvider = this.createAsyncLanguagesProvider();
const asyncLoaderOptionsProvider = this.createAsyncLoaderOptionsProvider();
const asyncLoadersProvider = this.createAsyncLoadersProvider(options);
const i18nLanguagesSubject = new rxjs_1.BehaviorSubject([]);
const i18nTranslationSubject = new rxjs_1.BehaviorSubject({});
const resolversProvider = {
provide: i18n_constants_1.I18N_RESOLVERS,
useValue: options.resolvers || [],
};
const i18nLanguagesSubjectProvider = {
provide: i18n_constants_1.I18N_LANGUAGES_SUBJECT,
useValue: i18nLanguagesSubject,
};
const i18nTranslationSubjectProvider = {
provide: i18n_constants_1.I18N_TRANSLATIONS_SUBJECT,
useValue: i18nTranslationSubject,
};
const i18nMessageFormatProvider = {
provide: utils_1.I18nMessageFormat,
useFactory: (resolvedOptions) => new utils_1.I18nMessageFormat(resolvedOptions),
inject: [i18n_constants_1.I18N_OPTIONS],
};
return {
module: I18nModule_1,
imports: options.imports || [],
providers: [
{ provide: common_1.Logger, useValue: utils_1.logger },
{
provide: core_1.APP_INTERCEPTOR,
useClass: i18n_language_interceptor_1.I18nLanguageInterceptor,
},
asyncOptionsProvider,
asyncTranslationProvider,
asyncLanguagesProvider,
asyncLoaderOptionsProvider,
asyncLoadersProvider,
i18nMessageFormatProvider,
i18n_service_1.I18nService,
i18n_middleware_1.I18nMiddleware,
resolversProvider,
i18nLanguagesSubjectProvider,
i18nTranslationSubjectProvider,
...this.createResolverProviders(options.resolvers),
],
exports: [i18n_constants_1.I18N_OPTIONS, i18n_constants_1.I18N_RESOLVERS, i18n_service_1.I18nService, i18n_middleware_1.I18nMiddleware, asyncLanguagesProvider],
};
}
static createAsyncOptionsProvider(options) {
if (options.useFactory) {
return {
provide: i18n_constants_1.I18N_OPTIONS,
useFactory: async (...args) => {
return this.sanitizeI18nOptions((await options.useFactory(...args)));
},
inject: options.inject || [],
};
}
return {
provide: i18n_constants_1.I18N_OPTIONS,
useFactory: async (optionsFactory) => this.sanitizeI18nOptions((await optionsFactory.createI18nOptions())),
inject: [options.useClass ?? options.useExisting],
};
}
static createAsyncLoaderOptionsProvider() {
return {
provide: i18n_constants_1.I18N_LOADER_OPTIONS,
useFactory: async (options) => {
return this.sanitizeI18nOptions(options.loaderOptions);
},
inject: [i18n_constants_1.I18N_OPTIONS],
};
}
static createAsyncLoadersProvider(options) {
if (options.loaders && options.loaders.length > 0) {
return {
provide: i18n_constants_1.I18N_LOADERS,
useValue: options.loaders,
};
}
return {
provide: i18n_constants_1.I18N_LOADERS,
useFactory: (resolvedOptions) => {
if (resolvedOptions.loaders && resolvedOptions.loaders.length > 0) {
return resolvedOptions.loaders;
}
if (resolvedOptions.loader) {
return [new resolvedOptions.loader(resolvedOptions.loaderOptions)];
}
return [];
},
inject: [i18n_constants_1.I18N_OPTIONS],
};
}
static createAsyncTranslationProvider() {
return this.createMultiLoaderStreamProvider(i18n_constants_1.I18N_TRANSLATIONS, (loaders) => (0, utils_1.processTranslations)(loaders), undefined, i18n_constants_1.I18N_TRANSLATIONS_SUBJECT);
}
static createAsyncLanguagesProvider() {
return this.createMultiLoaderStreamProvider(i18n_constants_1.I18N_LANGUAGES, (loaders) => (0, utils_1.processLanguages)(loaders), undefined, i18n_constants_1.I18N_LANGUAGES_SUBJECT);
}
static createMultiLoaderStreamProvider(provide, processLoaders, subject, subjectToken) {
return {
provide,
useFactory: async (loaders, injectedSubject) => {
const streamSubject = injectedSubject ?? subject;
if (!streamSubject) {
throw new Error('Missing BehaviorSubject provider for i18n stream');
}
try {
const value = await processLoaders(loaders);
if (value instanceof rxjs_1.Observable) {
value.subscribe(streamSubject);
}
else {
streamSubject.next(value);
}
}
catch (e) {
utils_1.logger.error('parsing translation error', e);
}
return streamSubject.asObservable();
},
inject: subjectToken ? [i18n_constants_1.I18N_LOADERS, subjectToken] : [i18n_constants_1.I18N_LOADERS],
};
}
static createLoaderStreamProvider(provide, loaderCall, subject, subjectToken) {
return {
provide,
useFactory: async (loader, injectedSubject) => {
const streamSubject = injectedSubject ?? subject;
if (!streamSubject) {
throw new Error('Missing BehaviorSubject provider for i18n stream');
}
try {
const value = await loaderCall(loader);
if (value instanceof rxjs_1.Observable) {
value.subscribe(streamSubject);
}
else {
streamSubject.next(value);
}
}
catch (e) {
utils_1.logger.error('parsing translation error', e);
}
return streamSubject.asObservable();
},
inject: subjectToken ? [i18n_loader_1.I18nLoader, subjectToken] : [i18n_loader_1.I18nLoader],
};
}
static sanitizeI18nOptions(options) {
options = { ...defaultOptions, ...options };
return options;
}
static createResolverProviders(resolvers) {
if (!resolvers || resolvers.length === 0) {
utils_1.logger.log(`No resolvers provided. Set the language manually per request or configure resolvers: https://nestjs-i18n.com/quick-start`);
}
return (resolvers || []).filter(utils_1.shouldResolve).reduce((providers, r) => {
if ('use' in r) {
const { use: resolver, options, ...rest } = r;
const optionsToken = (0, decorators_1.getI18nResolverOptionsToken)(resolver);
providers.push({
provide: resolver,
useClass: resolver,
});
if (options) {
rest.useValue = options;
}
providers.push({
provide: optionsToken,
...rest,
});
}
else {
const optionsToken = (0, decorators_1.getI18nResolverOptionsToken)(r);
providers.push({
provide: r,
useClass: r,
inject: [optionsToken],
});
providers.push({
provide: optionsToken,
useFactory: () => undefined,
});
}
return providers;
}, []);
}
};
exports.I18nModule = I18nModule;
exports.I18nModule = I18nModule = I18nModule_1 = __decorate([
(0, common_1.Global)(),
(0, common_1.Module)({}),
__param(1, (0, common_1.Inject)(i18n_constants_1.I18N_TRANSLATIONS)),
__param(2, (0, common_1.Inject)(i18n_constants_1.I18N_OPTIONS)),
__metadata("design:paramtypes", [i18n_service_1.I18nService,
rxjs_1.Observable, Object, core_1.HttpAdapterHost,
i18n_middleware_1.I18nMiddleware])
], I18nModule);
//# sourceMappingURL=i18n.module.js.map