UNPKG

nestjs-i18n

Version:
467 lines 21.3 kB
"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