From fc0749184ebab7df8977e3cd22d6a3e3eb65c7d2 Mon Sep 17 00:00:00 2001 From: Rubin Bhandari Date: Fri, 18 Aug 2023 12:29:23 +0545 Subject: [PATCH 1/4] fix: docs and tsconfig updates --- docs/quick-start.mdx | 37 +++++++++++++++++++++++++ samples/simple/src/app.controller.ts | 4 +-- tsconfig.json | 3 ++- tslint.json | 40 ---------------------------- 4 files changed, 41 insertions(+), 43 deletions(-) delete mode 100644 tslint.json diff --git a/docs/quick-start.mdx b/docs/quick-start.mdx index 9c5b462c..bc8e46fc 100644 --- a/docs/quick-start.mdx +++ b/docs/quick-start.mdx @@ -283,5 +283,42 @@ export class AppService { } ``` + +Translate options + +```typescript + +export type TranslateOptions = { + + /** + * Language to translate to + */ + + lang?: string; + + + /** + * Arguments to pass to the translation + */ + + args?: ({ [k: string]: any } | string)[] | { [k: string]: any }; + + + /** + * Default value to return when no translation is found + */ + + defaultValue?: string; + + + /** + * Debug mode + */ + + debug?: boolean; +}; + +``` + ## Example A working example is available [here](https://github.com/toonvanstrijp/nestjs-i18n/tree/main/samples/simple). diff --git a/samples/simple/src/app.controller.ts b/samples/simple/src/app.controller.ts index c1dc3569..02b6b3ea 100644 --- a/samples/simple/src/app.controller.ts +++ b/samples/simple/src/app.controller.ts @@ -1,6 +1,6 @@ -import { Body, Controller, Get, Post } from '@nestjs/common'; -import { AppService } from './app.service'; +import { Controller, Get } from '@nestjs/common'; import { I18n, I18nContext } from 'nestjs-i18n'; +import { AppService } from './app.service'; @Controller() export class AppController { diff --git a/tsconfig.json b/tsconfig.json index 774abc95..ffd34744 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -5,7 +5,8 @@ "removeComments": true, "emitDecoratorMetadata": true, "experimentalDecorators": true, - "target": "es2017", + "skipLibCheck": true, + "target": "es2021", "sourceMap": true, "baseUrl": "./", "incremental": true diff --git a/tslint.json b/tslint.json deleted file mode 100644 index b6a2e1f0..00000000 --- a/tslint.json +++ /dev/null @@ -1,40 +0,0 @@ -{ - "defaultSeverity": "error", - "extends": ["tslint:recommended"], - "jsRules": {}, - "rules": { - "quotemark": [true, "single", "avoid-escape"], - "no-console": [true, "log"], - "arrow-parens": false, - "no-consecutive-blank-lines": [true, 2], - "member-access": [false], - "ordered-imports": [false], - "object-literal-sort-keys": [false], - "trailing-comma": [false], - "semicolon": [true, "always", "ignore-bound-class-methods"], - "max-line-length": [false], - "one-line": [ - true, - "check-catch", - "check-finally", - "check-else", - "check-open-brace" - ], - "whitespace": [ - true, - "check-decl", - "check-operator", - "check-module", - "check-type", - "check-typecast" - ], - "interface-name": false, - "member-ordering": false, - "no-floating-promises": true, - "object-literal-key-quotes": [true, "as-needed"] - }, - "linterOptions": { - "exclude": ["src/generated/**/*"] - }, - "rulesDirectory": [] -} From ab1d808fb1c591568e7b21dd0e350ee65ba3d0da Mon Sep 17 00:00:00 2001 From: Rubin Bhandari Date: Fri, 18 Aug 2023 12:36:03 +0545 Subject: [PATCH 2/4] Update quick-start.mdx --- docs/quick-start.mdx | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/docs/quick-start.mdx b/docs/quick-start.mdx index bc8e46fc..cf2e80c0 100644 --- a/docs/quick-start.mdx +++ b/docs/quick-start.mdx @@ -284,7 +284,8 @@ export class AppService { ``` -Translate options + +## Translate options ```typescript @@ -318,6 +319,7 @@ export type TranslateOptions = { debug?: boolean; }; +``` ``` ## Example From ce7f097e9397fbe1b5a69c2f1539e67d13f33123 Mon Sep 17 00:00:00 2001 From: Rubin Bhandari Date: Fri, 18 Aug 2023 14:11:20 +0545 Subject: [PATCH 3/4] doc: Update quick-start.mdx Add formatting --- docs/quick-start.mdx | 6 ------ 1 file changed, 6 deletions(-) diff --git a/docs/quick-start.mdx b/docs/quick-start.mdx index cf2e80c0..5e1794af 100644 --- a/docs/quick-start.mdx +++ b/docs/quick-start.mdx @@ -283,8 +283,6 @@ export class AppService { } ``` - - ## Translate options ```typescript @@ -297,21 +295,18 @@ export type TranslateOptions = { lang?: string; - /** * Arguments to pass to the translation */ args?: ({ [k: string]: any } | string)[] | { [k: string]: any }; - /** * Default value to return when no translation is found */ defaultValue?: string; - /** * Debug mode */ @@ -319,7 +314,6 @@ export type TranslateOptions = { debug?: boolean; }; -``` ``` ## Example From 113c4b1c8a85502c37f057741aaa54a74a7c2715 Mon Sep 17 00:00:00 2001 From: Rubin Bhandari Date: Fri, 18 Aug 2023 19:36:44 +0545 Subject: [PATCH 4/4] fix: add barrel files and sort imports --- .../i18n-resolver-options.decorator.ts | 2 +- src/decorators/index.ts | 4 ++++ .../i18n-validation-exception.filter.ts | 9 +++---- src/i18n.constants.ts | 2 +- src/i18n.context.ts | 5 ++-- src/i18n.module.ts | 12 ++++++---- src/index.ts | 21 ++++------------ src/interfaces/i18n-options.interface.ts | 2 +- src/interfaces/index.ts | 7 ++++++ src/loaders/i18n.abstract.loader.ts | 6 ++--- src/loaders/i18n.loader.ts | 2 +- src/loaders/index.ts | 4 ++++ src/middlewares/i18n.middleware.ts | 4 ++-- src/pipes/i18n-validation.pipe.ts | 2 +- src/resolvers/cookie.resolver.ts | 2 +- src/resolvers/header.resolver.ts | 2 +- src/resolvers/index.ts | 6 +++++ src/resolvers/query.resolver.ts | 2 +- src/services/i18n.service.ts | 24 +++++++++++-------- src/types/index.ts | 2 ++ src/utils/index.ts | 6 +++++ src/utils/merge.ts | 2 +- src/utils/util.ts | 6 ++--- 23 files changed, 77 insertions(+), 57 deletions(-) create mode 100644 src/decorators/index.ts create mode 100644 src/interfaces/index.ts create mode 100644 src/loaders/index.ts create mode 100644 src/resolvers/index.ts create mode 100644 src/types/index.ts create mode 100644 src/utils/index.ts diff --git a/src/decorators/i18n-resolver-options.decorator.ts b/src/decorators/i18n-resolver-options.decorator.ts index 10e1e397..fc52efbf 100644 --- a/src/decorators/i18n-resolver-options.decorator.ts +++ b/src/decorators/i18n-resolver-options.decorator.ts @@ -1,5 +1,5 @@ import { Inject } from '@nestjs/common'; -import { I18N_RESOLVER_OPTIONS } from '..'; +import { I18N_RESOLVER_OPTIONS } from '../i18n.constants'; export function getI18nResolverOptionsToken(target: () => void) { return `${target.name}${I18N_RESOLVER_OPTIONS}`; diff --git a/src/decorators/index.ts b/src/decorators/index.ts new file mode 100644 index 00000000..9b52c72b --- /dev/null +++ b/src/decorators/index.ts @@ -0,0 +1,4 @@ +export * from './i18n-lang.decorator'; +export * from './i18n-languages.decorator'; +export * from './i18n-resolver-options.decorator'; +export * from './i18n.decorator'; diff --git a/src/filters/i18n-validation-exception.filter.ts b/src/filters/i18n-validation-exception.filter.ts index 32fa612c..d1b4b614 100644 --- a/src/filters/i18n-validation-exception.filter.ts +++ b/src/filters/i18n-validation-exception.filter.ts @@ -8,14 +8,11 @@ import iterate from 'iterare'; import { I18nContext } from '../i18n.context'; import { I18nValidationError, - I18nValidationException, -} from '../interfaces/i18n-validation-error.interface'; -import { I18nValidationExceptionFilterDetailedErrorsOption, I18nValidationExceptionFilterErrorFormatterOption, -} from '../interfaces/i18n-validation-exception-filter.interface'; -import { mapChildrenToValidationErrors } from '../utils/format'; -import { formatI18nErrors } from '../utils/util'; + I18nValidationException, +} from '../interfaces'; +import { mapChildrenToValidationErrors, formatI18nErrors } from '../utils'; type I18nValidationExceptionFilterOptions = | I18nValidationExceptionFilterDetailedErrorsOption diff --git a/src/i18n.constants.ts b/src/i18n.constants.ts index 86204bed..85a679dc 100644 --- a/src/i18n.constants.ts +++ b/src/i18n.constants.ts @@ -5,6 +5,6 @@ export const I18N_RESOLVER_OPTIONS = 'I18nResolverOptions'; export const I18N_RESOLVERS = 'I18nResolvers'; export const I18N_LOADER_OPTIONS = 'I18nLoaderOptions'; -// private consts +// private constants export const I18N_LANGUAGES_SUBJECT = 'I18nLanguagesSubject'; export const I18N_TRANSLATIONS_SUBJECT = 'I18nTranslationsSubject'; diff --git a/src/i18n.context.ts b/src/i18n.context.ts index 83c12aba..14101b97 100644 --- a/src/i18n.context.ts +++ b/src/i18n.context.ts @@ -1,10 +1,9 @@ import { ArgumentsHost } from '@nestjs/common'; import { AsyncLocalStorage } from 'async_hooks'; -import { I18nTranslator } from './interfaces/i18n-translator.interface'; -import { I18nValidationError } from './interfaces/i18n-validation-error.interface'; +import { I18nTranslator, I18nValidationError } from './interfaces'; import { I18nService, TranslateOptions } from './services/i18n.service'; import { Path, PathValue } from './types'; -import { getContextObject } from './utils/context'; +import { getContextObject } from './utils'; export class I18nContext> implements I18nTranslator diff --git a/src/i18n.module.ts b/src/i18n.module.ts index 6044d855..058e8e19 100644 --- a/src/i18n.module.ts +++ b/src/i18n.module.ts @@ -32,15 +32,19 @@ import { } from '@nestjs/common'; import { I18nLanguageInterceptor } from './interceptors/i18n-language.interceptor'; import { APP_INTERCEPTOR, HttpAdapterHost } from '@nestjs/core'; -import { getI18nResolverOptionsToken } from './decorators/i18n-resolver-options.decorator'; -import { isNestMiddleware, shouldResolve, usingFastify } from './utils/util'; +import { getI18nResolverOptionsToken } from './decorators'; +import { + isNestMiddleware, + shouldResolve, + usingFastify, + mergeDeep, +} from './utils'; import { I18nTranslation } from './interfaces/i18n-translation.interface'; import { I18nLoader } from './loaders/i18n.loader'; import { Observable, BehaviorSubject, Subject, takeUntil } from 'rxjs'; import * as format from 'string-format'; -import { I18nJsonLoader } from './loaders/i18n.json.loader'; +import { I18nJsonLoader } from './loaders'; import { I18nMiddleware } from './middlewares/i18n.middleware'; -import { mergeDeep } from './utils/merge'; import * as fs from 'fs'; import * as path from 'path'; import * as chalk from 'chalk'; diff --git a/src/index.ts b/src/index.ts index 61d1e6d9..5c674417 100644 --- a/src/index.ts +++ b/src/index.ts @@ -13,29 +13,16 @@ export * from './i18n.context'; export * from './services/i18n.service'; // interfaces -export * from './interfaces/i18n-options.interface'; -export * from './interfaces/i18n-language-resolver.interface'; -export * from './interfaces/i18n-translation.interface'; -export * from './interfaces/i18n-validation-error.interface'; +export * from './interfaces'; // decorators -export * from './decorators/i18n-lang.decorator'; -export * from './decorators/i18n-languages.decorator'; -export * from './decorators/i18n-resolver-options.decorator'; -export * from './decorators/i18n.decorator'; +export * from './decorators'; // build in resolvers -export * from './resolvers/header.resolver'; -export * from './resolvers/accept-language.resolver'; -export * from './resolvers/query.resolver'; -export * from './resolvers/cookie.resolver'; -export * from './resolvers/graphql-websocket.resolver'; -export * from './resolvers/grpc-metadata.resolver'; +export * from './resolvers'; // build in loaders -export * from './loaders/i18n.loader'; -export * from './loaders/i18n.json.loader'; -export * from './loaders/i18n.yaml.loader'; +export * from './loaders'; // interceptor export * from './interceptors/i18n-language.interceptor'; diff --git a/src/interfaces/i18n-options.interface.ts b/src/interfaces/i18n-options.interface.ts index 0f942193..4b38fcb0 100644 --- a/src/interfaces/i18n-options.interface.ts +++ b/src/interfaces/i18n-options.interface.ts @@ -7,7 +7,7 @@ import { ValueProvider, } from '@nestjs/common/interfaces'; import { I18nResolver } from './i18n-language-resolver.interface'; -import { I18nLoader } from '../loaders/i18n.loader'; +import { I18nLoader } from '../loaders'; import { ValidatorOptions } from 'class-validator'; export interface OptionsProvider { diff --git a/src/interfaces/index.ts b/src/interfaces/index.ts new file mode 100644 index 00000000..76b27540 --- /dev/null +++ b/src/interfaces/index.ts @@ -0,0 +1,7 @@ +export * from './i18n-language-resolver.interface'; +export * from './i18n-options.interface'; +export * from './i18n-plural.interface'; +export * from './i18n-translation.interface'; +export * from './i18n-translator.interface'; +export * from './i18n-validation-error.interface'; +export * from './i18n-validation-exception-filter.interface'; diff --git a/src/loaders/i18n.abstract.loader.ts b/src/loaders/i18n.abstract.loader.ts index eedc72fb..09d937d2 100644 --- a/src/loaders/i18n.abstract.loader.ts +++ b/src/loaders/i18n.abstract.loader.ts @@ -3,16 +3,16 @@ import { I18N_LOADER_OPTIONS } from '../i18n.constants'; import { Inject, OnModuleDestroy } from '@nestjs/common'; import * as path from 'path'; import { readFile } from 'fs/promises'; -import { exists, getDirectories, getFiles } from '../utils/file'; -import { I18nTranslation } from '../interfaces/i18n-translation.interface'; +import { exists, getDirectories, getFiles } from '../utils'; +import { I18nTranslation } from '../interfaces'; import { Observable, Subject, merge as ObservableMerge, of as ObservableOf, + switchMap, } from 'rxjs'; import * as chokidar from 'chokidar'; -import { switchMap } from 'rxjs/operators'; export interface I18nAbstractLoaderOptions { path: string; diff --git a/src/loaders/i18n.loader.ts b/src/loaders/i18n.loader.ts index 3e11d07c..cdf35d66 100644 --- a/src/loaders/i18n.loader.ts +++ b/src/loaders/i18n.loader.ts @@ -1,4 +1,4 @@ -import { I18nTranslation } from '../interfaces/i18n-translation.interface'; +import { I18nTranslation } from '../interfaces'; import { Observable } from 'rxjs'; export abstract class I18nLoader { diff --git a/src/loaders/index.ts b/src/loaders/index.ts new file mode 100644 index 00000000..6d488e66 --- /dev/null +++ b/src/loaders/index.ts @@ -0,0 +1,4 @@ +export * from './i18n.abstract.loader'; +export * from './i18n.json.loader'; +export * from './i18n.loader'; +export * from './i18n.yaml.loader'; diff --git a/src/middlewares/i18n.middleware.ts b/src/middlewares/i18n.middleware.ts index dbab900b..bdbb9760 100644 --- a/src/middlewares/i18n.middleware.ts +++ b/src/middlewares/i18n.middleware.ts @@ -8,7 +8,7 @@ import { WsArgumentsHost, } from '@nestjs/common/interfaces'; import { ModuleRef } from '@nestjs/core'; -import { shouldResolve } from '../utils/util'; +import { shouldResolve } from '../utils'; import { I18N_OPTIONS, I18N_RESOLVERS } from '../i18n.constants'; import { I18nContext, @@ -17,7 +17,7 @@ import { ResolverWithOptions, } from '../index'; import { I18nService } from '../services/i18n.service'; -import { I18nOptionResolver } from '../interfaces/i18n-options.interface'; +import { I18nOptionResolver } from '../interfaces'; const ExecutionContextMethodNotImplemented = new Error( "Method not implemented. nestjs-i18n creates a fake Http context since it's using middleware to resolve your language. Nestjs middlewares don't have access to the ExecutionContext.", diff --git a/src/pipes/i18n-validation.pipe.ts b/src/pipes/i18n-validation.pipe.ts index 1af0f060..1a36c3c1 100644 --- a/src/pipes/i18n-validation.pipe.ts +++ b/src/pipes/i18n-validation.pipe.ts @@ -4,7 +4,7 @@ import { ValidationPipeOptions, } from '@nestjs/common'; import { I18nContext } from '../i18n.context'; -import { i18nValidationErrorFactory } from '../utils/util'; +import { i18nValidationErrorFactory } from '../utils'; export type I18nValidationPipeOptions = Omit< ValidationPipeOptions, diff --git a/src/resolvers/cookie.resolver.ts b/src/resolvers/cookie.resolver.ts index e825b798..f940ee59 100644 --- a/src/resolvers/cookie.resolver.ts +++ b/src/resolvers/cookie.resolver.ts @@ -1,7 +1,7 @@ import * as cookie from 'cookie'; import { Injectable, ExecutionContext } from '@nestjs/common'; import { I18nResolver } from '..'; -import { I18nResolverOptions } from '../decorators/i18n-resolver-options.decorator'; +import { I18nResolverOptions } from '../decorators'; /** * Simple resolver to fetch language/locale from cookie diff --git a/src/resolvers/header.resolver.ts b/src/resolvers/header.resolver.ts index 81e7b3ff..2b318fae 100644 --- a/src/resolvers/header.resolver.ts +++ b/src/resolvers/header.resolver.ts @@ -1,6 +1,6 @@ import { I18nResolver } from '../index'; import { Injectable, ExecutionContext, Logger } from '@nestjs/common'; -import { I18nResolverOptions } from '../decorators/i18n-resolver-options.decorator'; +import { I18nResolverOptions } from '../decorators'; @Injectable() export class HeaderResolver implements I18nResolver { diff --git a/src/resolvers/index.ts b/src/resolvers/index.ts new file mode 100644 index 00000000..e2f171cd --- /dev/null +++ b/src/resolvers/index.ts @@ -0,0 +1,6 @@ +export * from './accept-language.resolver'; +export * from './cookie.resolver'; +export * from './graphql-websocket.resolver'; +export * from './grpc-metadata.resolver'; +export * from './header.resolver'; +export * from './query.resolver'; diff --git a/src/resolvers/query.resolver.ts b/src/resolvers/query.resolver.ts index ff88bcaa..4d2cf993 100644 --- a/src/resolvers/query.resolver.ts +++ b/src/resolvers/query.resolver.ts @@ -1,6 +1,6 @@ import { I18nResolver } from '../index'; import { Injectable, ExecutionContext } from '@nestjs/common'; -import { I18nResolverOptions } from '../decorators/i18n-resolver-options.decorator'; +import { I18nResolverOptions } from '../decorators'; @Injectable() export class QueryResolver implements I18nResolver { diff --git a/src/services/i18n.service.ts b/src/services/i18n.service.ts index 33f437ae..880a624e 100644 --- a/src/services/i18n.service.ts +++ b/src/services/i18n.service.ts @@ -1,21 +1,25 @@ import { Inject, Injectable, Logger, OnModuleDestroy } from '@nestjs/common'; +import { validate } from 'class-validator'; +import { + BehaviorSubject, + Observable, + Subject, + lastValueFrom, + take, + takeUntil, +} from 'rxjs'; +import { I18nOptions, I18nTranslation, I18nValidationError } from '..'; import { - I18N_OPTIONS, - I18N_TRANSLATIONS, I18N_LANGUAGES, I18N_LANGUAGES_SUBJECT, + I18N_OPTIONS, + I18N_TRANSLATIONS, I18N_TRANSLATIONS_SUBJECT, } from '../i18n.constants'; -import { I18nOptions, I18nValidationError } from '..'; -import { I18nTranslation } from '../interfaces/i18n-translation.interface'; -import { Observable, BehaviorSubject, lastValueFrom, Subject } from 'rxjs'; import { I18nLoader } from '../loaders/i18n.loader'; -import { take, takeUntil } from 'rxjs/operators'; -import { I18nPluralObject } from 'src/interfaces/i18n-plural.interface'; -import { validate } from 'class-validator'; -import { formatI18nErrors } from '../utils/util'; import { IfAnyOrNever, Path, PathValue } from '../types'; -import { I18nTranslator } from '../interfaces/i18n-translator.interface'; +import { formatI18nErrors } from '../utils'; +import { I18nTranslator, I18nPluralObject } from '../interfaces'; const pluralKeys = ['zero', 'one', 'two', 'few', 'many', 'other']; diff --git a/src/types/index.ts b/src/types/index.ts new file mode 100644 index 00000000..8b9afa60 --- /dev/null +++ b/src/types/index.ts @@ -0,0 +1,2 @@ +export * from './either.type'; +export * from './only.type'; diff --git a/src/utils/index.ts b/src/utils/index.ts new file mode 100644 index 00000000..ec2e1c86 --- /dev/null +++ b/src/utils/index.ts @@ -0,0 +1,6 @@ +export * from './context'; +export * from './file'; +export * from './format'; +export * from './merge'; +export * from './typescript'; +export * from './util'; diff --git a/src/utils/merge.ts b/src/utils/merge.ts index 671bffba..7f68b5b4 100644 --- a/src/utils/merge.ts +++ b/src/utils/merge.ts @@ -1,4 +1,4 @@ -import { I18nTranslation } from '../interfaces/i18n-translation.interface'; +import { I18nTranslation } from '../interfaces'; function isObject(item: any) { return item && typeof item === 'object' && !Array.isArray(item); diff --git a/src/utils/util.ts b/src/utils/util.ts index 25775a6b..45356bb5 100644 --- a/src/utils/util.ts +++ b/src/utils/util.ts @@ -1,9 +1,9 @@ -import { I18nOptionResolver } from '../interfaces/i18n-options.interface'; -import { ValidationArguments, ValidationError } from 'class-validator'; import { + I18nOptionResolver, I18nValidationError, I18nValidationException, -} from '../interfaces/i18n-validation-error.interface'; +} from '../interfaces'; +import { ValidationArguments, ValidationError } from 'class-validator'; import { I18nService, TranslateOptions } from '../services/i18n.service'; import { HttpStatus, MiddlewareConsumer } from '@nestjs/common'; import { NestMiddlewareConsumer, Path } from '../types';