From 5dd7ec486a4ff3fca502ebf4334a83b7c8f6f1e9 Mon Sep 17 00:00:00 2001 From: Thiago Ramalho Date: Thu, 8 Aug 2024 16:54:28 -0300 Subject: [PATCH 01/11] feat: create internalization module --- packages/i18n/README.md | 14 + packages/i18n/package.json | 21 ++ .../__fixtures__/locales/en/my-namespace.json | 3 + .../__fixtures__/locales/en/translation.json | 3 + .../__fixtures__/locales/pt/my-namespace.json | 3 + .../__fixtures__/locales/pt/translation.json | 3 + .../i18n/src/__fixtures__/moduleA/index.ts | 13 + .../moduleA/locales/en/ModuleAFixture.ts | 4 + .../src/__fixtures__/moduleA/locales/index.ts | 17 ++ .../moduleA/locales/pt/ModuleAFixture.ts | 4 + .../moduleA/moduleA.module.fixture.ts | 29 ++ .../i18n/src/__fixtures__/moduleB/index.ts | 12 + .../moduleB/locales/en/ModuleBFixture.ts | 4 + .../src/__fixtures__/moduleB/locales/index.ts | 17 ++ .../moduleB/locales/pt/ModuleBFixture.ts | 4 + .../moduleB/moduleB.module.fixture.ts | 18 ++ .../i18n/src/__fixtures__/moduleC/index.ts | 12 + .../moduleC/locales/en/ModuleCFixture.ts | 5 + .../src/__fixtures__/moduleC/locales/index.ts | 17 ++ .../moduleC/locales/pt/ModuleCFixture.ts | 4 + .../moduleC/moduleC.module.fixture.ts | 12 + packages/i18n/src/constants.ts | 1 + packages/i18n/src/i18n.e2e-spec.ts | 75 ++++++ packages/i18n/src/i18n.spec.ts | 195 ++++++++++++++ packages/i18n/src/i18n.ts | 252 ++++++++++++++++++ packages/i18n/src/index.ts | 9 + .../i18n-key-exists-params.interface.ts | 5 + .../src/interfaces/i18n-locales.interface.ts | 7 + .../src/interfaces/i18n-module.interface.ts | 6 + .../src/interfaces/i18n-options.interface.ts | 3 + .../src/interfaces/i18n-settings.interface.ts | 10 + .../i18n-translate-params.interface.ts | 9 + packages/i18n/src/interfaces/i18n.types.ts | 7 + packages/i18n/tsconfig.json | 15 ++ packages/i18n/typedoc.json | 3 + packages/nestjs-cache/package.json | 1 + .../nestjs-cache/src/cache.module.spec.ts | 3 + packages/nestjs-cache/src/cache.module.ts | 10 +- .../src/locales/en/CacheModule.ts | 4 + packages/nestjs-cache/src/locales/index.ts | 16 ++ .../src/locales/pt/CacheModule.ts | 4 + .../src/services/cache.service.spec.ts | 28 +- .../src/services/cache.service.ts | 5 +- packages/typeorm-common/package.json | 1 + packages/typeorm-common/src/constants.ts | 4 + .../reference-id-no-match.exception.ts | 13 +- .../exceptions/reference-lookup.exception.ts | 14 +- .../exceptions/reference-mutate.exception.ts | 13 +- .../reference-validation.exception.ts | 13 +- packages/typeorm-common/src/index.ts | 6 + .../src/locales/en/translation.ts | 7 + packages/typeorm-common/src/locales/index.ts | 14 + .../src/locales/pt/translation.ts | 7 + .../src/services/mutate.service.spec.ts | 41 ++- tsconfig.json | 3 + yarn.lock | 36 ++- 56 files changed, 1041 insertions(+), 18 deletions(-) create mode 100644 packages/i18n/README.md create mode 100644 packages/i18n/package.json create mode 100644 packages/i18n/src/__fixtures__/locales/en/my-namespace.json create mode 100644 packages/i18n/src/__fixtures__/locales/en/translation.json create mode 100644 packages/i18n/src/__fixtures__/locales/pt/my-namespace.json create mode 100644 packages/i18n/src/__fixtures__/locales/pt/translation.json create mode 100644 packages/i18n/src/__fixtures__/moduleA/index.ts create mode 100644 packages/i18n/src/__fixtures__/moduleA/locales/en/ModuleAFixture.ts create mode 100644 packages/i18n/src/__fixtures__/moduleA/locales/index.ts create mode 100644 packages/i18n/src/__fixtures__/moduleA/locales/pt/ModuleAFixture.ts create mode 100644 packages/i18n/src/__fixtures__/moduleA/moduleA.module.fixture.ts create mode 100644 packages/i18n/src/__fixtures__/moduleB/index.ts create mode 100644 packages/i18n/src/__fixtures__/moduleB/locales/en/ModuleBFixture.ts create mode 100644 packages/i18n/src/__fixtures__/moduleB/locales/index.ts create mode 100644 packages/i18n/src/__fixtures__/moduleB/locales/pt/ModuleBFixture.ts create mode 100644 packages/i18n/src/__fixtures__/moduleB/moduleB.module.fixture.ts create mode 100644 packages/i18n/src/__fixtures__/moduleC/index.ts create mode 100644 packages/i18n/src/__fixtures__/moduleC/locales/en/ModuleCFixture.ts create mode 100644 packages/i18n/src/__fixtures__/moduleC/locales/index.ts create mode 100644 packages/i18n/src/__fixtures__/moduleC/locales/pt/ModuleCFixture.ts create mode 100644 packages/i18n/src/__fixtures__/moduleC/moduleC.module.fixture.ts create mode 100644 packages/i18n/src/constants.ts create mode 100644 packages/i18n/src/i18n.e2e-spec.ts create mode 100644 packages/i18n/src/i18n.spec.ts create mode 100644 packages/i18n/src/i18n.ts create mode 100644 packages/i18n/src/index.ts create mode 100644 packages/i18n/src/interfaces/i18n-key-exists-params.interface.ts create mode 100644 packages/i18n/src/interfaces/i18n-locales.interface.ts create mode 100644 packages/i18n/src/interfaces/i18n-module.interface.ts create mode 100644 packages/i18n/src/interfaces/i18n-options.interface.ts create mode 100644 packages/i18n/src/interfaces/i18n-settings.interface.ts create mode 100644 packages/i18n/src/interfaces/i18n-translate-params.interface.ts create mode 100644 packages/i18n/src/interfaces/i18n.types.ts create mode 100644 packages/i18n/tsconfig.json create mode 100644 packages/i18n/typedoc.json create mode 100644 packages/nestjs-cache/src/locales/en/CacheModule.ts create mode 100644 packages/nestjs-cache/src/locales/index.ts create mode 100644 packages/nestjs-cache/src/locales/pt/CacheModule.ts create mode 100644 packages/typeorm-common/src/constants.ts create mode 100644 packages/typeorm-common/src/locales/en/translation.ts create mode 100644 packages/typeorm-common/src/locales/index.ts create mode 100644 packages/typeorm-common/src/locales/pt/translation.ts diff --git a/packages/i18n/README.md b/packages/i18n/README.md new file mode 100644 index 00000000..baae2c21 --- /dev/null +++ b/packages/i18n/README.md @@ -0,0 +1,14 @@ +# Rockets Typescript Core + +Core Typescript interfaces, classes and utilities. + +## Project + +[![NPM Latest](https://img.shields.io/npm/v/@concepta/ts-core)](https://www.npmjs.com/package/@concepta/ts-core) +[![NPM Downloads](https://img.shields.io/npm/dw/@conceptadev/ts-core)](https://www.npmjs.com/package/@concepta/ts-core) +[![GH Last Commit](https://img.shields.io/github/last-commit/conceptadev/rockets?logo=github)](https://github.com/conceptadev/rockets) +[![GH Contrib](https://img.shields.io/github/contributors/conceptadev/rockets?logo=github)](https://github.com/conceptadev/rockets/graphs/contributors) + +## Installation + +`yarn add @concepta/ts-core` diff --git a/packages/i18n/package.json b/packages/i18n/package.json new file mode 100644 index 00000000..319a508a --- /dev/null +++ b/packages/i18n/package.json @@ -0,0 +1,21 @@ +{ + "name": "@concepta/i18n", + "version": "4.0.0-alpha.48", + "description": "Rockets i18n", + "main": "dist/index.js", + "types": "dist/index.d.ts", + "license": "BSD-3-Clause", + "publishConfig": { + "access": "public" + }, + "files": [ + "dist/**/!(*.spec|*.e2e-spec|*.fixture).{js,d.ts}" + ], + "dependencies": { + "i18next": "^23.12.2" + }, + "devDependencies": { + "i18next-fs-backend": "^2.3.1", + "i18next-http-middleware": "^3.6.0" + } +} diff --git a/packages/i18n/src/__fixtures__/locales/en/my-namespace.json b/packages/i18n/src/__fixtures__/locales/en/my-namespace.json new file mode 100644 index 00000000..1d901363 --- /dev/null +++ b/packages/i18n/src/__fixtures__/locales/en/my-namespace.json @@ -0,0 +1,3 @@ +{ + "hello": "Hi" +} diff --git a/packages/i18n/src/__fixtures__/locales/en/translation.json b/packages/i18n/src/__fixtures__/locales/en/translation.json new file mode 100644 index 00000000..89e126ec --- /dev/null +++ b/packages/i18n/src/__fixtures__/locales/en/translation.json @@ -0,0 +1,3 @@ +{ + "hello": "Hi from translation" +} diff --git a/packages/i18n/src/__fixtures__/locales/pt/my-namespace.json b/packages/i18n/src/__fixtures__/locales/pt/my-namespace.json new file mode 100644 index 00000000..98655875 --- /dev/null +++ b/packages/i18n/src/__fixtures__/locales/pt/my-namespace.json @@ -0,0 +1,3 @@ +{ + "hello": "Olá" +} diff --git a/packages/i18n/src/__fixtures__/locales/pt/translation.json b/packages/i18n/src/__fixtures__/locales/pt/translation.json new file mode 100644 index 00000000..ee871bd1 --- /dev/null +++ b/packages/i18n/src/__fixtures__/locales/pt/translation.json @@ -0,0 +1,3 @@ +{ + "hello": "Olá do translation" +} diff --git a/packages/i18n/src/__fixtures__/moduleA/index.ts b/packages/i18n/src/__fixtures__/moduleA/index.ts new file mode 100644 index 00000000..92898ebc --- /dev/null +++ b/packages/i18n/src/__fixtures__/moduleA/index.ts @@ -0,0 +1,13 @@ + +import { I18n } from '../../i18n'; +import LOCALES from './locales'; + +let loaded = false; +export const initModuleATranslation = () => { + if (!loaded) { + I18n.addTranslations(LOCALES); + loaded = true; + } +} + +export * from './moduleA.module.fixture'; diff --git a/packages/i18n/src/__fixtures__/moduleA/locales/en/ModuleAFixture.ts b/packages/i18n/src/__fixtures__/moduleA/locales/en/ModuleAFixture.ts new file mode 100644 index 00000000..50901b9c --- /dev/null +++ b/packages/i18n/src/__fixtures__/moduleA/locales/en/ModuleAFixture.ts @@ -0,0 +1,4 @@ +const enUS = { + "hello": "Hi from module A" +} +export default enUS; \ No newline at end of file diff --git a/packages/i18n/src/__fixtures__/moduleA/locales/index.ts b/packages/i18n/src/__fixtures__/moduleA/locales/index.ts new file mode 100644 index 00000000..f20270c7 --- /dev/null +++ b/packages/i18n/src/__fixtures__/moduleA/locales/index.ts @@ -0,0 +1,17 @@ + +import { LocalesInterface } from "../../../interfaces/i18n-locales.interface"; +import { ModuleAFixture } from "../moduleA.module.fixture"; +import enUS from "./en/ModuleAFixture"; +import ptBR from "./pt/ModuleAFixture"; + +const LOCALES: LocalesInterface[] = [{ + namespace: ModuleAFixture.name, + language: 'en', + resource: enUS, +}, { + namespace: ModuleAFixture.name, + language: 'pt', + resource: ptBR, +}] + +export default LOCALES; \ No newline at end of file diff --git a/packages/i18n/src/__fixtures__/moduleA/locales/pt/ModuleAFixture.ts b/packages/i18n/src/__fixtures__/moduleA/locales/pt/ModuleAFixture.ts new file mode 100644 index 00000000..c6f37656 --- /dev/null +++ b/packages/i18n/src/__fixtures__/moduleA/locales/pt/ModuleAFixture.ts @@ -0,0 +1,4 @@ +const ptBR = { + "hello": "Olá do modulo A" +} +export default ptBR; \ No newline at end of file diff --git a/packages/i18n/src/__fixtures__/moduleA/moduleA.module.fixture.ts b/packages/i18n/src/__fixtures__/moduleA/moduleA.module.fixture.ts new file mode 100644 index 00000000..6f8caa16 --- /dev/null +++ b/packages/i18n/src/__fixtures__/moduleA/moduleA.module.fixture.ts @@ -0,0 +1,29 @@ +import { initModuleATranslation } from "./"; +import { I18n } from "../../i18n"; + +import { ModuleBFixture, initModuleBTranslation } from "../moduleB"; +import { ModuleCFixture, initModuleCTranslation } from "../moduleC"; + +// Non-NestJS module +export class ModuleAFixture { + constructor(private moduleB: ModuleBFixture, private moduleC: ModuleCFixture) { + initModuleATranslation(); + initModuleBTranslation(); + initModuleCTranslation(); + } + translationModuleA() { + return I18n.translate({ namespace: ModuleAFixture.name, key: 'hello', language: 'en' }); + } + + translationModuleB() { + return I18n.translate({ namespace: ModuleBFixture.name, key: 'hello', language: 'en' }); + } + + translationCFromModuleB() { + return this.moduleB.translationC(); + } + + translationC(key:string = 'hello', language: string = 'en') { + return I18n.translate({ namespace: ModuleCFixture.name, key: key, language: language }); + } +} \ No newline at end of file diff --git a/packages/i18n/src/__fixtures__/moduleB/index.ts b/packages/i18n/src/__fixtures__/moduleB/index.ts new file mode 100644 index 00000000..53f6ebe6 --- /dev/null +++ b/packages/i18n/src/__fixtures__/moduleB/index.ts @@ -0,0 +1,12 @@ +import { I18n } from '../../i18n'; +import LOCALES from './locales'; + +let loaded = false; +export const initModuleBTranslation = () => { + if (!loaded) { + I18n.addTranslations(LOCALES); + loaded = true; + } +} + +export * from './moduleB.module.fixture'; diff --git a/packages/i18n/src/__fixtures__/moduleB/locales/en/ModuleBFixture.ts b/packages/i18n/src/__fixtures__/moduleB/locales/en/ModuleBFixture.ts new file mode 100644 index 00000000..cce9c536 --- /dev/null +++ b/packages/i18n/src/__fixtures__/moduleB/locales/en/ModuleBFixture.ts @@ -0,0 +1,4 @@ +const enUS = { + "hello": "Hi from module B" +} +export default enUS; \ No newline at end of file diff --git a/packages/i18n/src/__fixtures__/moduleB/locales/index.ts b/packages/i18n/src/__fixtures__/moduleB/locales/index.ts new file mode 100644 index 00000000..1c7fed54 --- /dev/null +++ b/packages/i18n/src/__fixtures__/moduleB/locales/index.ts @@ -0,0 +1,17 @@ + +import { LocalesInterface } from "../../../interfaces/i18n-locales.interface"; +import { ModuleBFixture } from "../moduleB.module.fixture"; +import enUS from "./en/ModuleBFixture"; +import ptBR from "./pt/ModuleBFixture"; + +const LOCALES: LocalesInterface[] = [{ + namespace: ModuleBFixture.name, + language: 'en', + resource: enUS, +}, { + namespace: ModuleBFixture.name, + language: 'pt', + resource: ptBR, +}] + +export default LOCALES; \ No newline at end of file diff --git a/packages/i18n/src/__fixtures__/moduleB/locales/pt/ModuleBFixture.ts b/packages/i18n/src/__fixtures__/moduleB/locales/pt/ModuleBFixture.ts new file mode 100644 index 00000000..45d570ce --- /dev/null +++ b/packages/i18n/src/__fixtures__/moduleB/locales/pt/ModuleBFixture.ts @@ -0,0 +1,4 @@ +const ptBR = { + "hello": "Olá do modulo B" +} +export default ptBR; \ No newline at end of file diff --git a/packages/i18n/src/__fixtures__/moduleB/moduleB.module.fixture.ts b/packages/i18n/src/__fixtures__/moduleB/moduleB.module.fixture.ts new file mode 100644 index 00000000..75f2f825 --- /dev/null +++ b/packages/i18n/src/__fixtures__/moduleB/moduleB.module.fixture.ts @@ -0,0 +1,18 @@ +import { initModuleBTranslation } from "./"; +import { I18n } from "../../i18n"; +import { ModuleCFixture, initModuleCTranslation } from "../moduleC"; + +// Non-NestJS module +export class ModuleBFixture { + constructor(private moduleC: ModuleCFixture) { + initModuleBTranslation(); + initModuleCTranslation(); + } + translationB() { + return I18n.translate({ namespace: ModuleBFixture.name, key: 'hello', language: 'en' }); + } + + translationC() { + return I18n.translate({ namespace: ModuleCFixture.name, key: 'hello', language: 'en' }); + } +} \ No newline at end of file diff --git a/packages/i18n/src/__fixtures__/moduleC/index.ts b/packages/i18n/src/__fixtures__/moduleC/index.ts new file mode 100644 index 00000000..c3bd2f6e --- /dev/null +++ b/packages/i18n/src/__fixtures__/moduleC/index.ts @@ -0,0 +1,12 @@ +import { I18n } from '../../i18n'; +import LOCALES from './locales'; + +let loaded = false; +export const initModuleCTranslation = () => { + if (!loaded) { + I18n.addTranslations(LOCALES); + loaded = true; + } +} + +export * from './moduleC.module.fixture'; diff --git a/packages/i18n/src/__fixtures__/moduleC/locales/en/ModuleCFixture.ts b/packages/i18n/src/__fixtures__/moduleC/locales/en/ModuleCFixture.ts new file mode 100644 index 00000000..cfd9dad8 --- /dev/null +++ b/packages/i18n/src/__fixtures__/moduleC/locales/en/ModuleCFixture.ts @@ -0,0 +1,5 @@ +const enUS = { + "hello": "Hi from module C", + "bye": "Bye from module C" +} +export default enUS; \ No newline at end of file diff --git a/packages/i18n/src/__fixtures__/moduleC/locales/index.ts b/packages/i18n/src/__fixtures__/moduleC/locales/index.ts new file mode 100644 index 00000000..b4f17254 --- /dev/null +++ b/packages/i18n/src/__fixtures__/moduleC/locales/index.ts @@ -0,0 +1,17 @@ + +import { LocalesInterface } from "../../../interfaces/i18n-locales.interface"; +import { ModuleCFixture } from "../moduleC.module.fixture"; +import enUS from "./en/ModuleCFixture"; +import ptCR from "./pt/ModuleCFixture"; + +const LOCALES: LocalesInterface[] = [{ + namespace: ModuleCFixture.name, + language: 'en', + resource: enUS, +}, { + namespace: ModuleCFixture.name, + language: 'pt', + resource: ptCR, +}] + +export default LOCALES; \ No newline at end of file diff --git a/packages/i18n/src/__fixtures__/moduleC/locales/pt/ModuleCFixture.ts b/packages/i18n/src/__fixtures__/moduleC/locales/pt/ModuleCFixture.ts new file mode 100644 index 00000000..f7287d71 --- /dev/null +++ b/packages/i18n/src/__fixtures__/moduleC/locales/pt/ModuleCFixture.ts @@ -0,0 +1,4 @@ +const ptCR = { + "hello": "Olá do modulo C" +} +export default ptCR; \ No newline at end of file diff --git a/packages/i18n/src/__fixtures__/moduleC/moduleC.module.fixture.ts b/packages/i18n/src/__fixtures__/moduleC/moduleC.module.fixture.ts new file mode 100644 index 00000000..e3666f10 --- /dev/null +++ b/packages/i18n/src/__fixtures__/moduleC/moduleC.module.fixture.ts @@ -0,0 +1,12 @@ +import { initModuleCTranslation } from "./"; +import { I18n } from "../../i18n"; + +// Non-NestJS module +export class ModuleCFixture { + constructor() { + initModuleCTranslation(); + } + translationC() { + return I18n.translate({ namespace: ModuleCFixture.name, key: 'hello', language: 'en' }); + } +} \ No newline at end of file diff --git a/packages/i18n/src/constants.ts b/packages/i18n/src/constants.ts new file mode 100644 index 00000000..a1ef7c0a --- /dev/null +++ b/packages/i18n/src/constants.ts @@ -0,0 +1 @@ +export const DEFAULT_NAMESPACE = 'translation'; \ No newline at end of file diff --git a/packages/i18n/src/i18n.e2e-spec.ts b/packages/i18n/src/i18n.e2e-spec.ts new file mode 100644 index 00000000..3f62478e --- /dev/null +++ b/packages/i18n/src/i18n.e2e-spec.ts @@ -0,0 +1,75 @@ +import { ModuleAFixture } from './__fixtures__/moduleA'; +import { ModuleBFixture } from './__fixtures__/moduleB'; +import { ModuleCFixture } from './__fixtures__/moduleC'; +import { I18n } from './i18n'; +import { LocalesInterface } from './interfaces/i18n-locales.interface'; + +describe('i18n module', () => { + + describe('ModuleAFixture E2E', () => { + let moduleA: ModuleAFixture; + let moduleB: ModuleBFixture; + let moduleC: ModuleCFixture; + + beforeAll(() => { + jest.spyOn(console, 'log').mockImplementation(() => {}); + jest.spyOn(console, 'warn').mockImplementation(() => {}); + // Initialize translations + I18n.init({ + options: { + initImmediate: false, + } + }); + // Create instances of the modules + moduleC = new ModuleCFixture(); + moduleB = new ModuleBFixture(new ModuleCFixture()); + moduleA = new ModuleAFixture(moduleB, moduleC); + }); + + it('should translate for module A', () => { + const result = moduleA.translationModuleA(); + expect(result).toBe('Hi from module A'); + }); + + it('should translate for module B', () => { + const result = moduleA.translationModuleB(); + expect(result).toBe('Hi from module B'); + }); + + it('should translate C from module B', () => { + const result = moduleA.translationCFromModuleB(); + expect(result).toBe('Hi from module C'); + }); + + it('should translate for module C', () => { + const result = moduleA.translationC(); + expect(result).toBe('Hi from module C'); + }); + + it('should translate for module C', () => { + const LOCALES: LocalesInterface[] = [{ + namespace: ModuleCFixture.name, + resource: { + 'hello': 'overwrite english', + 'hi': 'new hi' + }, + deep: false, + overwrite: false + }]; + I18n.addTranslations(LOCALES); + + const result = moduleA.translationC('hello', 'pt'); + expect(result).toBe('Olá do modulo C'); + + const result1 = moduleA.translationC('hello', 'en'); + expect(result1).toBe('overwrite english'); + + const resultExtend = moduleA.translationC('hi', 'en'); + expect(resultExtend).toBe('new hi'); + + const resultBye = moduleA.translationC('bye', 'en'); + expect(resultBye).toBe('Bye from module C'); + + }); + }); +}); \ No newline at end of file diff --git a/packages/i18n/src/i18n.spec.ts b/packages/i18n/src/i18n.spec.ts new file mode 100644 index 00000000..e4571695 --- /dev/null +++ b/packages/i18n/src/i18n.spec.ts @@ -0,0 +1,195 @@ +import I18NexFsBackend, { FsBackendOptions } from 'i18next-fs-backend'; +import { join } from 'path'; +import { ModuleAFixture } from './__fixtures__/moduleA'; +import { ModuleBFixture } from './__fixtures__/moduleB'; +import { ModuleCFixture } from './__fixtures__/moduleC'; +import { I18n } from './i18n'; +import { LocalesInterface } from './interfaces/i18n-locales.interface'; + +describe('i18n module', () => { + + beforeAll(() => { + jest.spyOn(console, 'log').mockImplementation(() => {}); + jest.spyOn(console, 'warn').mockImplementation(() => {}); + }) + + it('should not initialize ', () => { + const LOCALES: LocalesInterface[] = [{ + namespace: 'my-namespace', + language: 'en', + resource: require(__dirname + '/__fixtures__/locales/en/my-namespace.json'), + }]; + + const consoleErrorSpy = jest.spyOn(console, 'error').mockReturnValueOnce(); + + I18n.addTranslations(LOCALES); + expect(consoleErrorSpy).toBeCalledWith('i18next was not isInitialized, please call init'); + }); + + describe('i18n configuration', () => { + + afterEach(() => { + I18n.reset(); + }) + + it('should initialize translations from constant', async () => { + + const enUS = { + hello: "Hi" + }; + + const ptBR = { + hello: "Olá" + }; + + // TODO: maybe the interface can be improved + const LOCALES: LocalesInterface[] = [{ + namespace: 'my-namespace', + language: 'en', + resource: enUS, + }, { + namespace: 'my-namespace', + language: 'pt', + resource: ptBR, + }]; + I18n.init({ + options: { + initImmediate: false, + } + }); + I18n.addTranslations(LOCALES); + + const enTranslation = I18n.translate({ namespace: 'my-namespace', key: 'hello', language: 'en' }); + const ptTranslation = I18n.translate({ namespace: 'my-namespace', key: 'hello', language: 'pt' }); + + expect(enTranslation).toBe('Hi'); + expect(ptTranslation).toBe('Olá'); + }); + + it('should initialize translations from json', async () => { + const LOCALES: LocalesInterface[] = [{ + namespace: 'my-namespace', + language: 'en', + resource: require(__dirname + '/__fixtures__/locales/en/my-namespace.json'), + }, { + namespace: 'my-namespace', + language: 'pt', + resource: require(__dirname + '/__fixtures__/locales/pt/my-namespace.json'), + }]; + + I18n.init({ + options: { + initImmediate: false, + } + }); + I18n.addTranslations(LOCALES); + + const enTranslation = I18n.translate({ namespace: 'my-namespace', key: 'hello', language: 'en' }); + const ptTranslation = I18n.translate({ namespace: 'my-namespace', key: 'hello', language: 'pt' }); + + expect(enTranslation).toBe('Hi'); + expect(ptTranslation).toBe('Olá'); + }); + + it('should initialize translations from constant', async () => { + + const enUS = { + hello: "Hi" + }; + + const ptBR = { + hello: "Olá" + }; + + I18n.init({ + options: { + initImmediate: false, + ns: [ 'translation', 'my-namespace' ], + resources: { + en: { + 'my-namespace': enUS + }, + pt: { + 'my-namespace': ptBR + } + } + } + }); + + const enTranslation = I18n.translate({ namespace: 'my-namespace', key: 'hello', language: 'en' }); + const ptTranslation = I18n.translate({ namespace: 'my-namespace', key: 'hello', language: 'pt' }); + + expect(enTranslation).toBe('Hi'); + expect(ptTranslation).toBe('Olá'); + }); + + it('should initialize translations from settings', async () => { + + I18n.init({ + modules: [I18NexFsBackend], + options: { + initImmediate: false, + ns: ['translation', 'my-namespace'], + backend: { + loadPath: join(__dirname, './__fixtures__/locales/{{lng}}/{{ns}}.json') + }, + supportedLngs: ['en', 'pt'], + preload: ['en', 'pt'], + fallbackLng: 'en' + } + }, (e) => { + console.log('Loading translations', e); + }); + + const enTranslation = I18n.translate({ namespace: 'my-namespace', key: 'hello', language: 'en' }); + const ptTranslation = I18n.translate({ namespace: 'my-namespace', key: 'hello', language: 'pt' }); + const defaultEnTranslation = I18n.translate({ namespace: 'translation', key: 'hello', language: 'en' }); + const defaultPtTranslation = I18n.translate({ namespace: 'translation', key: 'hello', language: 'pt' }); + + expect(enTranslation).toBe('Hi'); + expect(ptTranslation).toBe('Olá'); + expect(defaultEnTranslation).toBe('Hi from translation'); + expect(defaultPtTranslation).toBe('Olá do translation'); + }); + }); + + describe('ModuleAFixture E2E', () => { + let moduleA: ModuleAFixture; + let moduleB: ModuleBFixture; + let moduleC: ModuleCFixture; + + beforeAll(() => { + // Initialize translations + I18n.init({ + options: { + initImmediate: false, + } + }); + + // Create instances of the modules + moduleB = new ModuleBFixture(new ModuleCFixture()); + moduleC = new ModuleCFixture(); + moduleA = new ModuleAFixture(moduleB, moduleC); + }); + + it('should translate for module A', () => { + const result = moduleA.translationModuleA(); + expect(result).toBe('Hi from module A'); + }); + + it('should translate for module B', () => { + const result = moduleA.translationModuleB(); + expect(result).toBe('Hi from module B'); + }); + + it('should translate C from module B', () => { + const result = moduleA.translationCFromModuleB(); + expect(result).toBe('Hi from module C'); + }); + + it('should translate for module C', () => { + const result = moduleA.translationC(); + expect(result).toBe('Hi from module C'); + }); + }); +}); \ No newline at end of file diff --git a/packages/i18n/src/i18n.ts b/packages/i18n/src/i18n.ts new file mode 100644 index 00000000..49233179 --- /dev/null +++ b/packages/i18n/src/i18n.ts @@ -0,0 +1,252 @@ +// TODO: should we create a wrapper for this? +import i18next, { Callback } from 'i18next'; +import { + I18nCallback, + I18nObjectModule, + i18nextModuleType +} from './interfaces/i18n.types'; +import { I18Settings } from './interfaces/i18n-settings.interface'; +import { TranslateParams } from './interfaces/i18n-translate-params.interface'; +import { LocalesInterface } from './interfaces/i18n-locales.interface'; +import { KeyExistsParams } from './interfaces/i18n-key-exists-params.interface'; +import { I18nextOptions } from './interfaces/i18n-options.interface'; + +export class I18n { + static instance = i18next.createInstance(); + + /** + * Adds a module to the i18next instance. + * + * @template T - The type of the module. + * @param module - The module to add. + * @returns The i18next instance. + * + * @example + * // Example of adding a custom backend module + * import Backend from 'i18next-custom-backend'; + * + * I18n.addModule(Backend); + */ + static addModule = (module: i18nextModuleType) => { + return I18n.instance.use(module) + } + + /** + * Adds multiple modules to the i18next instance. + * + * @template T - The type of the modules. + * @param modules - An array of modules to add. + * + * @example + * // Example of adding multiple custom backend modules + * import Backend1 from 'i18next-custom-backend1'; + * import Backend2 from 'i18next-custom-backend2'; + * + * I18n.addModules([Backend1, Backend2]); + */ + static addModules = (modules?: i18nextModuleType[]) => { + try { + if (modules && modules.length > 0 ) + modules.forEach((m) => { + I18n.addModule(m) + }) + } catch (e) { + console.error('Error adding module'); + } + } + + /** + * Retrieves the i18next instance. + * + * @returns The i18next instance. + * + * @example + * const i18nInstance = I18n.getInstance(); + */ + static getInstance = () => { + return I18n.instance; + } + + /** + * Changes the language. The callback will be called as soon translations were + * loaded or an error occurs while loading. HINT: For easy testing - setting + * lng to 'cimode' will set t function to always return the key. + * + * @param language - The language to change to. + * @returns A promise that resolves when the language change is complete. + * + * @example + * await I18n.changeLanguage('fr'); + */ + static changeLanguage = async (language: string, callback?: Callback): Promise => { + try { + await I18n.instance.changeLanguage(language, callback); + console.log(`Language changed to ${language}`); + } catch (error) { + console.error('Error changing language:', error); + } + } + + /** + * Resets the i18next instance by creating a new instance. + * + * @example + * I18n.reset(); + */ + static reset = () => { + I18n.instance = i18next.createInstance(); + } + + /** + * Initializes the i18next instance with the provided settings. + * + * @template T - The type of the settings. + * @param settings - The settings to initialize i18next with. + * @param callback - Optional callback to be called after initialization. + * + * @example + * const settings = { + * modules: [Backend], + * options: { + * lng: 'en', + * resources: { + * en: { + * translation: { + * hello: "hello world" + * } + * } + * } + * } + * }; + * I18n.init(settings); + */ + static init = (settings?: I18Settings, callback?: I18nCallback) => { + I18n.addModules(settings?.modules) + + const i18nextOptions: I18nextOptions = { + debug: true, + preload: ['en'], + fallbackLng: 'en', + ns: ['translation'], + defaultNS: 'translation', + ...settings?.options + }; + + I18n.instance.init(i18nextOptions, callback); + } + + /** + * Translates a given key using the provided parameters. + * + * @param params - The parameters for translation. + * @returns The translated string. + * + * @example + * const translation = I18n.translate({ + * key: 'hello', + * namespace: 'common', + * language: 'fr', + * defaultMessage: 'Bonjour', + * }); + */ + static translate = (params: TranslateParams): string => { + const { + namespace = I18n.getDefaultNamespace(), + language = I18n.getCurrentLanguage(), + options = {}, + key, + defaultMessage = '' + } = params; + const doesKeyExists = I18n.keyExists({ + namespace, + key, + language + }) + + const t = I18n.instance.getFixedT(language); + const result = doesKeyExists ? t(`${namespace}:${key}`, options) : defaultMessage; + return result; + } + + /** + * Retrieves the default namespace from the i18next instance options. + * + * @returns The default namespace as a string. + * + * @example + * const defaultNamespace = I18n.getDefaultNamespace(); + */ + static getDefaultNamespace = (): string => { + // TODO: need to validate what to do if defaultNS is set to false + const defaultNS = I18n.instance.options.defaultNS; + if (typeof defaultNS === 'string') { + return defaultNS; + } else if (Array.isArray(defaultNS) && defaultNS.length > 0) { + return defaultNS[0]; + } else { + return 'translation'; // Fallback to a default namespace + } + } + + /** + * Retrieves the current language detected from the i18next instance. + * + * @returns The default language as a string. + * + * @example + * const defaultLanguage = I18n.getDefaultLanguage(); + */ + static getCurrentLanguage = (): string => { + return I18n.instance.language; + } + + /** + * Adds translations to the i18next instance. + * Adds a complete bundle. Setting deep param to true will extend existing + * translations in that file. Setting overwrite to true it will overwrite + * existing key translations in that file. + * + * @param locales - An array of locale objects containing translation data. + * + * @example + * const locales = [ + * { + * namespace: 'common', + * resource: { key: 'value' }, + * language: 'en', + * deep: true, + * overwrite: true + * } + * ]; + * I18n.addTranslations(locales); + */ + static addTranslations = (locales: LocalesInterface[]) => { + + if (!I18n.instance.isInitialized) { + console.error('i18next was not isInitialized, please call init'); + return; + } + locales.forEach(locale => { + const { namespace = I18n.getDefaultNamespace(), resource, language = I18n.getCurrentLanguage(), deep, overwrite } = locale; + I18n.instance.addResourceBundle(language, namespace, resource, deep, overwrite ); + }); + }; + + /** + * Checks if a translation key exists in the i18next instance. + * + * @param params - The parameters to check if the key exists. + * @returns A boolean indicating whether the key exists. + * + * @example + * const exists = I18n.keyExists({ + * key: 'hello', + * namespace: 'common', + * language: 'fr' + * }); + */ + static keyExists = (params: KeyExistsParams): boolean => { + const { namespace = I18n.getDefaultNamespace(), key, language = I18n.getCurrentLanguage() } = params; + return I18n.instance.exists(`${namespace}:${key}`, { lng: language }); + } +} \ No newline at end of file diff --git a/packages/i18n/src/index.ts b/packages/i18n/src/index.ts new file mode 100644 index 00000000..0f1d22e1 --- /dev/null +++ b/packages/i18n/src/index.ts @@ -0,0 +1,9 @@ +import { I18n } from './i18n'; +import { TranslateParams } from './interfaces/i18n-translate-params.interface'; + +export { I18n } from './i18n'; +export { LocalesInterface } from './interfaces/i18n-locales.interface'; + +export const t = (args: TranslateParams) => { + return I18n.translate(args); +} \ No newline at end of file diff --git a/packages/i18n/src/interfaces/i18n-key-exists-params.interface.ts b/packages/i18n/src/interfaces/i18n-key-exists-params.interface.ts new file mode 100644 index 00000000..1aa93ed2 --- /dev/null +++ b/packages/i18n/src/interfaces/i18n-key-exists-params.interface.ts @@ -0,0 +1,5 @@ +export interface KeyExistsParams { + namespace?: string; + key: string; + language?: string; +} \ No newline at end of file diff --git a/packages/i18n/src/interfaces/i18n-locales.interface.ts b/packages/i18n/src/interfaces/i18n-locales.interface.ts new file mode 100644 index 00000000..a76e4efa --- /dev/null +++ b/packages/i18n/src/interfaces/i18n-locales.interface.ts @@ -0,0 +1,7 @@ +export interface LocalesInterface { + namespace?: string; + language?: string; + resource: Record + deep?: boolean; + overwrite?: boolean; +} \ No newline at end of file diff --git a/packages/i18n/src/interfaces/i18n-module.interface.ts b/packages/i18n/src/interfaces/i18n-module.interface.ts new file mode 100644 index 00000000..2287583b --- /dev/null +++ b/packages/i18n/src/interfaces/i18n-module.interface.ts @@ -0,0 +1,6 @@ +import { Newable, NewableModule } from "i18next"; +import { I18nObjectModule } from "./i18n.types"; + +export interface i18nextModule { + module: (T | NewableModule | Newable) +} \ No newline at end of file diff --git a/packages/i18n/src/interfaces/i18n-options.interface.ts b/packages/i18n/src/interfaces/i18n-options.interface.ts new file mode 100644 index 00000000..7801b630 --- /dev/null +++ b/packages/i18n/src/interfaces/i18n-options.interface.ts @@ -0,0 +1,3 @@ +import { InitOptions } from "i18next"; + +export interface I18nextOptions extends InitOptions {} \ No newline at end of file diff --git a/packages/i18n/src/interfaces/i18n-settings.interface.ts b/packages/i18n/src/interfaces/i18n-settings.interface.ts new file mode 100644 index 00000000..e612a8d6 --- /dev/null +++ b/packages/i18n/src/interfaces/i18n-settings.interface.ts @@ -0,0 +1,10 @@ +import { I18nextOptions } from "./i18n-options.interface" +import { I18nObjectModule, i18nextModuleType } from "./i18n.types" + +export interface I18Settings { + // TODO: validate this how to receive the other possible types + // any custom configuration should be under settings + newInstance?: boolean; + modules?: i18nextModuleType[] + options?: I18nextOptions +} diff --git a/packages/i18n/src/interfaces/i18n-translate-params.interface.ts b/packages/i18n/src/interfaces/i18n-translate-params.interface.ts new file mode 100644 index 00000000..43eb0f46 --- /dev/null +++ b/packages/i18n/src/interfaces/i18n-translate-params.interface.ts @@ -0,0 +1,9 @@ +import { I18nTranslationOptions } from "./i18n.types"; + +export interface TranslateParams { + namespace?: string; + key: string; + language?: string; + defaultMessage?: string; + options?: I18nTranslationOptions; +} \ No newline at end of file diff --git a/packages/i18n/src/interfaces/i18n.types.ts b/packages/i18n/src/interfaces/i18n.types.ts new file mode 100644 index 00000000..9ee5702c --- /dev/null +++ b/packages/i18n/src/interfaces/i18n.types.ts @@ -0,0 +1,7 @@ +import { Callback, Module, Newable, NewableModule, TOptions } from "i18next"; + +export type I18nObjectModule = Module; +export type I18nTranslationOptions = TOptions; +export type I18nCallback = Callback; +export type i18nextModuleType = T | NewableModule | Newable; + diff --git a/packages/i18n/tsconfig.json b/packages/i18n/tsconfig.json new file mode 100644 index 00000000..ef998095 --- /dev/null +++ b/packages/i18n/tsconfig.json @@ -0,0 +1,15 @@ +{ + "extends": "../../tsconfig", + "compilerOptions": { + "composite": true, + "rootDir": "./src", + "outDir": "./dist", + "typeRoots": [ + "./node_modules/@types", + "../../node_modules/@types" + ] + }, + "include": [ + "src/**/*.ts" + ] +} diff --git a/packages/i18n/typedoc.json b/packages/i18n/typedoc.json new file mode 100644 index 00000000..944fda5a --- /dev/null +++ b/packages/i18n/typedoc.json @@ -0,0 +1,3 @@ +{ + "entryPoints": ["src/index.ts"] +} \ No newline at end of file diff --git a/packages/nestjs-cache/package.json b/packages/nestjs-cache/package.json index 67f9094b..5a89c189 100644 --- a/packages/nestjs-cache/package.json +++ b/packages/nestjs-cache/package.json @@ -12,6 +12,7 @@ "dist/**/!(*.spec|*.e2e-spec|*.fixture).{js,d.ts}" ], "dependencies": { + "@concepta/i18n": "^4.0.0-alpha.48", "@concepta/nestjs-access-control": "^4.0.0-alpha.48", "@concepta/nestjs-common": "^4.0.0-alpha.48", "@concepta/nestjs-core": "^4.0.0-alpha.48", diff --git a/packages/nestjs-cache/src/cache.module.spec.ts b/packages/nestjs-cache/src/cache.module.spec.ts index 9ad0fa6c..ab1ee325 100644 --- a/packages/nestjs-cache/src/cache.module.spec.ts +++ b/packages/nestjs-cache/src/cache.module.spec.ts @@ -14,6 +14,9 @@ describe(CacheModule.name, () => { let cacheDynamicRepo: Record>; beforeEach(async () => { + // to avoid error log from i18next not initialized + jest.spyOn(console, 'error').mockImplementation(() => {}); + const testModule: TestingModule = await Test.createTestingModule({ imports: [AppModuleFixture], }).compile(); diff --git a/packages/nestjs-cache/src/cache.module.ts b/packages/nestjs-cache/src/cache.module.ts index e566fde4..4dbd62aa 100644 --- a/packages/nestjs-cache/src/cache.module.ts +++ b/packages/nestjs-cache/src/cache.module.ts @@ -1,4 +1,4 @@ -import { DynamicModule, Module } from '@nestjs/common'; +import { DynamicModule, Module, OnModuleInit } from '@nestjs/common'; import { CacheAsyncOptions, @@ -8,12 +8,20 @@ import { createCacheImports, createCacheProviders, } from './cache.module-definition'; +import LOCALES from './locales'; +import { I18n } from '@concepta/i18n'; /** * Cache Module */ @Module({}) export class CacheModule extends CacheModuleClass { + + constructor() { + super(); + I18n.addTranslations(LOCALES); + } + static register(options: CacheOptions): DynamicModule { return super.register(options); } diff --git a/packages/nestjs-cache/src/locales/en/CacheModule.ts b/packages/nestjs-cache/src/locales/en/CacheModule.ts new file mode 100644 index 00000000..d909525c --- /dev/null +++ b/packages/nestjs-cache/src/locales/en/CacheModule.ts @@ -0,0 +1,4 @@ +const enUS = { + "REFERENCE_MUTATE_ERROR": "Error while trying to mutate a %s reference" +} +export default enUS; \ No newline at end of file diff --git a/packages/nestjs-cache/src/locales/index.ts b/packages/nestjs-cache/src/locales/index.ts new file mode 100644 index 00000000..b5f48ccf --- /dev/null +++ b/packages/nestjs-cache/src/locales/index.ts @@ -0,0 +1,16 @@ +import { LocalesInterface } from "@concepta/i18n/dist/interfaces/i18n.interfaces"; +import { CacheModule } from "../cache.module"; +import enUS from "./en/CacheModule"; +import ptBR from "./pt/CacheModule"; + +const LOCALES: LocalesInterface[] = [{ + namespace: 'CacheModule', + language: 'en', + resource: enUS, +}, { + namespace: 'CacheModule' , + language: 'pt', + resource: ptBR, +}] + +export default LOCALES; \ No newline at end of file diff --git a/packages/nestjs-cache/src/locales/pt/CacheModule.ts b/packages/nestjs-cache/src/locales/pt/CacheModule.ts new file mode 100644 index 00000000..f4c6d8c9 --- /dev/null +++ b/packages/nestjs-cache/src/locales/pt/CacheModule.ts @@ -0,0 +1,4 @@ +const ptBR = { + "REFERENCE_MUTATE_ERROR": "Erro ao tentar alterar uma referência de %s" +} +export default ptBR; \ No newline at end of file diff --git a/packages/nestjs-cache/src/services/cache.service.spec.ts b/packages/nestjs-cache/src/services/cache.service.spec.ts index 79f887b1..7a3a3e0c 100644 --- a/packages/nestjs-cache/src/services/cache.service.spec.ts +++ b/packages/nestjs-cache/src/services/cache.service.spec.ts @@ -7,11 +7,14 @@ import { ReferenceMutateException, ReferenceValidationException, RepositoryProxy, + initTypeOrmCommonTranslation, } from '@concepta/typeorm-common'; import { CacheService } from './cache.service'; import { CacheSettingsInterface } from '../interfaces/cache-settings.interface'; import { CacheCreateDto } from '../dto/cache-create.dto'; +import { I18n } from '@concepta/i18n'; +import LOCALES from '../locales'; const expirationDate = new Date(); expirationDate.setHours(expirationDate.getHours() + 1); @@ -48,6 +51,10 @@ describe('CacheService', () => { service = new CacheService({ testAssignment: repo }, settings); }); + afterEach(() => { + I18n.reset(); + }) + describe(CacheService.prototype.create, () => { it('should create a cache entry', async () => { Object.assign(cacheCreateDto, cacheDto); @@ -140,14 +147,31 @@ describe('CacheService', () => { ).rejects.toThrow(ReferenceValidationException); }); - it('should throw a ReferenceMutateException on error', async () => { + it('should throw a ReferenceMutateException on error with translations in pt', async () => { + jest.spyOn(console, 'log').mockImplementation(() => {}); + jest.spyOn(console, 'warn').mockImplementation(() => {}); + I18n.init({ + options: { + initImmediate: false, + fallbackLng: 'pt', + } + }); + // load local translations + I18n.addTranslations(LOCALES); + + // load typeorm translations + initTypeOrmCommonTranslation(); + + // need to call this to load the translations const assignment: ReferenceAssignment = 'testAssignment'; const error = new Error('error'); service['mergeEntity'] = jest.fn().mockResolvedValue(error); const t = () => service.update(assignment, cacheDto, queryOptions); - await expect(t).rejects.toThrow(ReferenceMutateException); + await expect(t).rejects.toThrowError( + 'Erro ao tentar alterar uma referência de undefined' + ); }); }); }); diff --git a/packages/nestjs-cache/src/services/cache.service.ts b/packages/nestjs-cache/src/services/cache.service.ts index 481b3b88..db610a6e 100644 --- a/packages/nestjs-cache/src/services/cache.service.ts +++ b/packages/nestjs-cache/src/services/cache.service.ts @@ -113,7 +113,10 @@ export class CacheService implements CacheServiceInterface { expirationDate, }); } catch (e) { - throw new ReferenceMutateException(assignmentRepo.metadata.targetName, e); + throw new ReferenceMutateException( + assignmentRepo.metadata.targetName, + e, + ); } } diff --git a/packages/typeorm-common/package.json b/packages/typeorm-common/package.json index 4cbeb583..dd64ef92 100644 --- a/packages/typeorm-common/package.json +++ b/packages/typeorm-common/package.json @@ -12,6 +12,7 @@ "dist/**/!(*.spec|*.e2e-spec|*.fixture).{js,d.ts}" ], "dependencies": { + "@concepta/i18n": "^4.0.0-alpha.48", "@concepta/ts-core": "^4.0.0-alpha.48", "@faker-js/faker": "^6.0.0-alpha.6", "@nestjs/common": "^9.0.0" diff --git a/packages/typeorm-common/src/constants.ts b/packages/typeorm-common/src/constants.ts new file mode 100644 index 00000000..f6b9f439 --- /dev/null +++ b/packages/typeorm-common/src/constants.ts @@ -0,0 +1,4 @@ +export const REFERENCE_ID_NO_MATCH = 'REFERENCE_ID_NO_MATCH'; +export const REFERENCE_LOOKUP_ERROR = 'REFERENCE_LOOKUP_ERROR'; +export const REFERENCE_MUTATE_ERROR = 'REFERENCE_MUTATE_ERROR'; +export const REFERENCE_VALIDATION_ERROR = 'REFERENCE_VALIDATION_ERROR'; \ No newline at end of file diff --git a/packages/typeorm-common/src/exceptions/reference-id-no-match.exception.ts b/packages/typeorm-common/src/exceptions/reference-id-no-match.exception.ts index c98850cc..979f48f1 100644 --- a/packages/typeorm-common/src/exceptions/reference-id-no-match.exception.ts +++ b/packages/typeorm-common/src/exceptions/reference-id-no-match.exception.ts @@ -1,11 +1,13 @@ import { format } from 'util'; import { ReferenceId, ExceptionInterface } from '@concepta/ts-core'; +import { t } from '@concepta/i18n'; +import { REFERENCE_ID_NO_MATCH } from '../constants'; export class ReferenceIdNoMatchException extends Error implements ExceptionInterface { - errorCode = 'REFERENCE_ID_NO_MATCH'; + errorCode = REFERENCE_ID_NO_MATCH; context: { entityName: string; @@ -15,9 +17,14 @@ export class ReferenceIdNoMatchException constructor( entityName: string, id: ReferenceId, - message = 'No match for %s reference id %s.', + message?: string, ) { - super(format(message, entityName, id)); + super(format(message + ?? t({ + key: REFERENCE_ID_NO_MATCH, + defaultMessage: 'No match for %s reference id %s.' + }) + , entityName, id)); this.context = { entityName, id, diff --git a/packages/typeorm-common/src/exceptions/reference-lookup.exception.ts b/packages/typeorm-common/src/exceptions/reference-lookup.exception.ts index 4fbdacd1..31cba0d7 100644 --- a/packages/typeorm-common/src/exceptions/reference-lookup.exception.ts +++ b/packages/typeorm-common/src/exceptions/reference-lookup.exception.ts @@ -1,11 +1,14 @@ import { format } from 'util'; import { ExceptionInterface, NotAnErrorException } from '@concepta/ts-core'; +import { t } from '@concepta/i18n'; +import { REFERENCE_LOOKUP_ERROR } from '../constants'; + export class ReferenceLookupException extends Error implements ExceptionInterface { - errorCode = 'REFERENCE_LOOKUP_ERROR'; + errorCode = REFERENCE_LOOKUP_ERROR; context: { entityName: string; @@ -15,9 +18,14 @@ export class ReferenceLookupException constructor( entityName: string, originalError: unknown, - message = 'Error while trying to lookup a %s reference', + message?: string, ) { - super(format(message, entityName)); + super(format(message + ?? t({ + key: REFERENCE_LOOKUP_ERROR, + defaultMessage: 'Error while trying to lookup a %s reference' + }) + , entityName)); this.context = { entityName, originalError: diff --git a/packages/typeorm-common/src/exceptions/reference-mutate.exception.ts b/packages/typeorm-common/src/exceptions/reference-mutate.exception.ts index da64453c..c215a1c3 100644 --- a/packages/typeorm-common/src/exceptions/reference-mutate.exception.ts +++ b/packages/typeorm-common/src/exceptions/reference-mutate.exception.ts @@ -1,11 +1,13 @@ import { format } from 'util'; import { ExceptionInterface, NotAnErrorException } from '@concepta/ts-core'; +import { t } from '@concepta/i18n'; +import { REFERENCE_MUTATE_ERROR } from '../constants'; export class ReferenceMutateException extends Error implements ExceptionInterface { - errorCode = 'REFERENCE_MUTATE_ERROR'; + errorCode = REFERENCE_MUTATE_ERROR; context: { entityName: string; @@ -15,9 +17,14 @@ export class ReferenceMutateException constructor( entityName: string, originalError: unknown, - message = 'Error while trying to mutate a %s reference', + message?: string, ) { - super(format(message, entityName)); + super(format(message + ?? t({ + key: REFERENCE_MUTATE_ERROR, + defaultMessage: 'Error Default while trying to mutate a %s reference' + }) + , entityName)); this.context = { entityName, originalError: diff --git a/packages/typeorm-common/src/exceptions/reference-validation.exception.ts b/packages/typeorm-common/src/exceptions/reference-validation.exception.ts index 198daef9..7be7d1c8 100644 --- a/packages/typeorm-common/src/exceptions/reference-validation.exception.ts +++ b/packages/typeorm-common/src/exceptions/reference-validation.exception.ts @@ -1,12 +1,14 @@ import { format } from 'util'; import { ValidationError } from 'class-validator'; +import { t } from '@concepta/i18n'; import { ExceptionInterface } from '@concepta/ts-core'; +import { REFERENCE_VALIDATION_ERROR } from '../constants'; export class ReferenceValidationException extends Error implements ExceptionInterface { - errorCode = 'REFERENCE_VALIDATION_ERROR'; + errorCode = REFERENCE_VALIDATION_ERROR; context: { entityName: string; @@ -16,9 +18,14 @@ export class ReferenceValidationException constructor( entityName: string, validationErrors: ValidationError[], - message = 'Data for the %s reference is not valid', + message?: string, ) { - super(format(message, entityName)); + super(format(message + ?? t({ + key: REFERENCE_VALIDATION_ERROR, + defaultMessage: 'Data for the %s reference is not valid', + }) + , entityName)); this.context = { entityName, validationErrors, diff --git a/packages/typeorm-common/src/index.ts b/packages/typeorm-common/src/index.ts index 74233f0e..e2c243d3 100644 --- a/packages/typeorm-common/src/index.ts +++ b/packages/typeorm-common/src/index.ts @@ -1,3 +1,9 @@ +import { I18n } from '@concepta/i18n'; +import LOCALES from './locales'; + +export const initTypeOrmCommonTranslation = () => { + I18n.addTranslations(LOCALES); +} // interfaces export { QueryOptionsInterface } from './interfaces/query-options.interface'; export { SafeTransactionOptionsInterface } from './interfaces/safe-transaction-options.interface'; diff --git a/packages/typeorm-common/src/locales/en/translation.ts b/packages/typeorm-common/src/locales/en/translation.ts new file mode 100644 index 00000000..e7c02f22 --- /dev/null +++ b/packages/typeorm-common/src/locales/en/translation.ts @@ -0,0 +1,7 @@ +const enUS = { + 'REFERENCE_MUTATE_ERROR': 'Error while trying to mutate a %s reference', + 'REFERENCE_LOOKUP_ERROR': 'Error while trying to lookup a %s reference', + 'REFERENCE_ID_NO_MATCH': 'No match for %s reference id %s.', + 'REFERENCE_VALIDATION_ERROR': 'Data for the %s reference is not valid' +} +export default enUS; diff --git a/packages/typeorm-common/src/locales/index.ts b/packages/typeorm-common/src/locales/index.ts new file mode 100644 index 00000000..4150e3fb --- /dev/null +++ b/packages/typeorm-common/src/locales/index.ts @@ -0,0 +1,14 @@ +import { LocalesInterface } from "@concepta/i18n"; +import enUS from "./en/translation"; +import ptBR from "./pt/translation"; + +// TODO: should we keep this under default namespace? +const LOCALES: LocalesInterface[] = [{ + language: 'en', + resource: enUS, +}, { + language: 'pt', + resource: ptBR, +}] + +export default LOCALES; \ No newline at end of file diff --git a/packages/typeorm-common/src/locales/pt/translation.ts b/packages/typeorm-common/src/locales/pt/translation.ts new file mode 100644 index 00000000..3161109d --- /dev/null +++ b/packages/typeorm-common/src/locales/pt/translation.ts @@ -0,0 +1,7 @@ +const ptBR = { + "REFERENCE_MUTATE_ERROR": "Erro ao tentar alterar uma referência de %s", + 'REFERENCE_LOOKUP_ERROR': 'Erro ao tentar procurar uma referência de %s', + 'REFERENCE_ID_NO_MATCH': 'Nenhuma correspondência da referência %s para o id %s.', + 'REFERENCE_VALIDATION_ERROR': 'Os dados relacionados a %s não são válidos', +} +export default ptBR; \ No newline at end of file diff --git a/packages/typeorm-common/src/services/mutate.service.spec.ts b/packages/typeorm-common/src/services/mutate.service.spec.ts index 37012d09..5d9bf824 100644 --- a/packages/typeorm-common/src/services/mutate.service.spec.ts +++ b/packages/typeorm-common/src/services/mutate.service.spec.ts @@ -14,6 +14,8 @@ import { TestMutateServiceFixture } from '../__fixtures__/services/test-mutate.s import { TestModuleFixture } from '../__fixtures__/test.module.fixture'; import { TestEntityFixture } from '../__fixtures__/test.entity.fixture'; import { TestFactoryFixture } from '../__fixtures__/test.factory.fixture'; +import { initTypeOrmCommonTranslation } from '..'; +import { I18n } from '@concepta/i18n'; describe(MutateService, () => { const WRONG_UUID = '3bfd065e-0c30-11ed-861d-0242ac120002'; @@ -24,6 +26,13 @@ describe(MutateService, () => { let testFactory: TestFactoryFixture; beforeEach(async () => { + jest.spyOn(console, 'log').mockImplementation(() => {}); + jest.spyOn(console, 'warn').mockImplementation(() => {}); + I18n.init({ + options: { + initImmediate: false, + } + }) const moduleFixture: TestingModule = await Test.createTestingModule({ imports: [AppModuleFixture], }).compile(); @@ -244,7 +253,37 @@ describe(MutateService, () => { }); }; - await expect(t).rejects.toThrow(ReferenceIdNoMatchException); + await expect(t).rejects.toThrowError( + `No match for TestEntityFixture reference id ${WRONG_UUID}.` + ); + }); + + it('id does not match with translation pt', async () => { + I18n.changeLanguage('pt'); + initTypeOrmCommonTranslation(); + const t = async () => { + return testMutateService.remove({ + id: WRONG_UUID, + }); + }; + + await expect(t).rejects.toThrowError( + `Nenhuma correspondência da referência TestEntityFixture para o id ${WRONG_UUID}.` + ); + }); + + it('id does not match with translation en', async () => { + I18n.changeLanguage('en'); + initTypeOrmCommonTranslation(); + const t = async () => { + return testMutateService.remove({ + id: WRONG_UUID, + }); + }; + + await expect(t).rejects.toThrowError( + `No match for TestEntityFixture reference id ${WRONG_UUID}.` + ); }); it('exception', async () => { diff --git a/tsconfig.json b/tsconfig.json index 3aaebaaf..dffcde97 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -102,6 +102,9 @@ }, { "path": "packages/nestjs-cache" + }, + { + "path": "packages/i18n" } ] } diff --git a/yarn.lock b/yarn.lock index 0fa206d8..c8303bdf 100644 --- a/yarn.lock +++ b/yarn.lock @@ -401,7 +401,7 @@ __metadata: languageName: node linkType: hard -"@babel/runtime@npm:^7.23.9": +"@babel/runtime@npm:^7.23.2, @babel/runtime@npm:^7.23.9": version: 7.24.8 resolution: "@babel/runtime@npm:7.24.8" dependencies: @@ -670,6 +670,16 @@ __metadata: languageName: node linkType: hard +"@concepta/i18n@npm:^4.0.0-alpha.48, @concepta/i18n@workspace:packages/i18n": + version: 0.0.0-use.local + resolution: "@concepta/i18n@workspace:packages/i18n" + dependencies: + i18next: "npm:^23.12.2" + i18next-fs-backend: "npm:^2.3.1" + i18next-http-middleware: "npm:^3.6.0" + languageName: unknown + linkType: soft + "@concepta/nestjs-access-control@npm:^4.0.0-alpha.48, @concepta/nestjs-access-control@workspace:packages/nestjs-access-control": version: 0.0.0-use.local resolution: "@concepta/nestjs-access-control@workspace:packages/nestjs-access-control" @@ -856,6 +866,7 @@ __metadata: version: 0.0.0-use.local resolution: "@concepta/nestjs-cache@workspace:packages/nestjs-cache" dependencies: + "@concepta/i18n": "npm:^4.0.0-alpha.48" "@concepta/nestjs-access-control": "npm:^4.0.0-alpha.48" "@concepta/nestjs-common": "npm:^4.0.0-alpha.48" "@concepta/nestjs-core": "npm:^4.0.0-alpha.48" @@ -9947,6 +9958,29 @@ __metadata: languageName: node linkType: hard +"i18next-fs-backend@npm:^2.3.1": + version: 2.3.1 + resolution: "i18next-fs-backend@npm:2.3.1" + checksum: 10c0/6a4a6318ee13851436e17232afc6833d4e5c937a0fe15cf9454b4b7dd605154580a990716008ee734d582e8db7dd6a39389eb64474d42950093c938e50a80df9 + languageName: node + linkType: hard + +"i18next-http-middleware@npm:^3.6.0": + version: 3.6.0 + resolution: "i18next-http-middleware@npm:3.6.0" + checksum: 10c0/52fdc7d5f90c6af51300a932e3906910ca30fe0137fb051bec29a83a3e4ca9cd17f79a70ddd20d04f3500617d0e8758d1f482210c197f7229fc03edaa15bf2ef + languageName: node + linkType: hard + +"i18next@npm:^23.12.2": + version: 23.12.2 + resolution: "i18next@npm:23.12.2" + dependencies: + "@babel/runtime": "npm:^7.23.2" + checksum: 10c0/aa7654afefc3f5383513563226401994558675a57eb562f0c4580d573d8410d24954f25112d45f9b6b1ca3f88c7beea1ac802ed50ad4081a06a17d08857e3c5d + languageName: node + linkType: hard + "iconv-lite@npm:0.4.24, iconv-lite@npm:^0.4.24": version: 0.4.24 resolution: "iconv-lite@npm:0.4.24" From ac7b4aa8a29dca99e473acd0546744ba8fe3bd8b Mon Sep 17 00:00:00 2001 From: Thiago Ramalho Date: Fri, 9 Aug 2024 17:01:44 -0300 Subject: [PATCH 02/11] feat: i18n module cleanup --- .eslintrc.js | 6 +- packages/i18n/README.md | 280 +++++++++++++++++- .../__fixtures__/locales/en/translation.json | 2 +- .../i18n/src/__fixtures__/moduleA/index.ts | 3 +- .../moduleA/locales/en/ModuleAFixture.ts | 6 +- .../src/__fixtures__/moduleA/locales/index.ts | 32 +- .../moduleA/locales/pt/ModuleAFixture.ts | 6 +- .../moduleA/moduleA.module.fixture.ts | 35 ++- .../i18n/src/__fixtures__/moduleB/index.ts | 2 +- .../moduleB/locales/en/ModuleBFixture.ts | 6 +- .../src/__fixtures__/moduleB/locales/index.ts | 32 +- .../moduleB/locales/pt/ModuleBFixture.ts | 6 +- .../moduleB/moduleB.module.fixture.ts | 20 +- .../i18n/src/__fixtures__/moduleC/index.ts | 2 +- .../moduleC/locales/en/ModuleCFixture.ts | 8 +- .../src/__fixtures__/moduleC/locales/index.ts | 32 +- .../moduleC/locales/pt/ModuleCFixture.ts | 6 +- .../moduleC/moduleC.module.fixture.ts | 12 +- packages/i18n/src/constants.ts | 1 - packages/i18n/src/i18n.e2e-spec.ts | 40 +-- packages/i18n/src/i18n.spec.ts | 225 +++++++++----- packages/i18n/src/i18n.ts | 204 ++++++++----- packages/i18n/src/index.ts | 4 +- .../i18n-key-exists-params.interface.ts | 5 - .../src/interfaces/i18n-locales.interface.ts | 25 +- .../src/interfaces/i18n-module.interface.ts | 16 +- .../src/interfaces/i18n-options.interface.ts | 7 +- .../src/interfaces/i18n-settings.interface.ts | 23 +- .../i18n-translate-params.interface.ts | 11 +- .../i18n-translation-keys.interface copy.ts | 22 ++ packages/i18n/src/interfaces/i18n.types.ts | 22 +- .../nestjs-cache/src/cache.module.spec.ts | 2 +- packages/nestjs-cache/src/cache.module.ts | 7 +- .../src/locales/en/CacheModule.ts | 6 +- packages/nestjs-cache/src/locales/index.ts | 30 +- .../src/locales/pt/CacheModule.ts | 6 +- .../src/services/cache.service.spec.ts | 10 +- .../src/services/cache.service.ts | 5 +- packages/typeorm-common/src/constants.ts | 2 +- .../reference-id-no-match.exception.ts | 23 +- .../exceptions/reference-lookup.exception.ts | 23 +- .../exceptions/reference-mutate.exception.ts | 23 +- .../reference-validation.exception.ts | 16 +- packages/typeorm-common/src/index.ts | 2 +- .../src/locales/en/translation.ts | 10 +- packages/typeorm-common/src/locales/index.ts | 25 +- .../src/locales/pt/translation.ts | 13 +- .../src/services/mutate.service.spec.ts | 12 +- yarn.lock | 6 +- 49 files changed, 896 insertions(+), 426 deletions(-) delete mode 100644 packages/i18n/src/constants.ts delete mode 100644 packages/i18n/src/interfaces/i18n-key-exists-params.interface.ts create mode 100644 packages/i18n/src/interfaces/i18n-translation-keys.interface copy.ts diff --git a/.eslintrc.js b/.eslintrc.js index 933bc671..78637d1e 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -5,10 +5,7 @@ module.exports = { tsconfigRootDir: __dirname, project: ['./tsconfig.eslint.json'], }, - plugins: [ - 'import', - 'tsdoc', - ], + plugins: ['import', 'tsdoc'], extends: [ '@concepta/eslint-config/nest', 'plugin:jsdoc/recommended-typescript', @@ -21,6 +18,7 @@ module.exports = { '**/tsconfig.json', '**/tsconfig.eslint.json', '**/commitlint.config.js', + 'packages/i18n/src/__fixtures__/locales/**/*.json', ], settings: { jsdoc: { diff --git a/packages/i18n/README.md b/packages/i18n/README.md index baae2c21..becd6bd0 100644 --- a/packages/i18n/README.md +++ b/packages/i18n/README.md @@ -1,14 +1,278 @@ -# Rockets Typescript Core -Core Typescript interfaces, classes and utilities. +# i18n Module for React + +An advanced i18n (internationalization) utility for managing multiple languages in React applications with seamless integration and modularity. ## Project -[![NPM Latest](https://img.shields.io/npm/v/@concepta/ts-core)](https://www.npmjs.com/package/@concepta/ts-core) -[![NPM Downloads](https://img.shields.io/npm/dw/@conceptadev/ts-core)](https://www.npmjs.com/package/@concepta/ts-core) -[![GH Last Commit](https://img.shields.io/github/last-commit/conceptadev/rockets?logo=github)](https://github.com/conceptadev/rockets) -[![GH Contrib](https://img.shields.io/github/contributors/conceptadev/rockets?logo=github)](https://github.com/conceptadev/rockets/graphs/contributors) +[![NPM Latest](https://img.shields.io/npm/v/@concepta/i18n)](https://www.npmjs.com/package/@concepta/i18n) +[![NPM Downloads](https://img.shields.io/npm/dw/@concepta/i18n)](https://www.npmjs.com/package/@concepta/i18n) +[![GH Last Commit](https://img.shields.io/github/last-commit/yourorg/i18n?logo=github)](https://github.com/yourorg/i18n) +[![GH Contrib](https://img.shields.io/github/contributors/yourorg/i18n?logo=github)](https://github.com/yourorg/i18n/graphs/contributors) + +# Table of Contents + +1. [Tutorials](#tutorials) + - [Getting Started with i18n](#getting-started-with-i18n) + - [Installation](#installation) + - [Basic Setup](#basic-setup) + - [Example](#example) + - [Adding a Custom Backend Module](#adding-a-custom-backend-module) + - [Changing Language Dynamically](#changing-language-dynamically) + - [Adding Translations](#adding-translations) +2. [How-To Guides](#how-to-guides) + - [Creating a Custom Translation Module](#creating-a-custom-translation-module) + - [Handling Missing Translations](#handling-missing-translations) + - [Using i18n in Tests](#using-i18n-in-tests) +3. [Reference](#reference) + - [i18n API Methods](#i18n-api-methods) + - [Supported i18n Modules](#supported-i18n-modules) +4. [Explanation](#explanation) + - [What is i18n?](#what-is-i18n) + - [How i18n Module Works](#how-i18n-module-works) + - [Benefits of Using i18n](#benefits-of-using-i18n) + - [Common i18n Pitfalls](#common-i18n-pitfalls) + +# Tutorials + +## Getting Started with i18n + +### Installation + +Install the `@concepta/i18n` package using yarn or npm: + +yarn add @concepta/i18n + +npm install @concepta/i18n + +## Basic Setup + +To set up the i18n module, you need to initialize it with your desired settings and add your translations. For more detailed settings documentation, check [i18next](https://www.i18next.com/). + +```ts +// i18nSetup.js +import { I18n, t } from '@concepta/i18n'; + +export function initI18n() { + I18n.init({ + options: { + fallbackLng: 'en', + resources: { + en: { + 'translation': { + hello: "Hi" + } + }, + pt: { + 'translation': { + hello: "Olá" + } + } + } + } + }); + // initialized and ready to go! + // i18n is already initialized, because the translation resources were passed via init function +} + +export { t }; +``` +```ts +import { initI18n, t } from './i18nSetup'; + +// Initialize i18n when the application loads +initI18n(); + +// result: Hi +console.log(t({ key: 'hello' })); + +// result: Olá +console.log(t({ + key: 'hello', + language: 'pt' +})); +``` +## Example + +These are basic examples to help you get started with the i18n module. + +### Adding a Custom Backend Module with typescript + +To add a custom backend module for fetching translations: +```json +//./locales/en/translation.json +{ + "hello": "Hi" +} +``` +```json +//./locales/pt/translation.json +{ + "hello": "Olá" +} +``` +```ts +import I18NexFsBackend, { FsBackendOptions } from 'i18next-fs-backend'; +import { I18n, t } from '@concepta/i18n'; + +//... +I18n.init({ + modules: [I18NexFsBackend], + options: { + initImmediate: false, + ns: ['translation'], + backend: { + loadPath: join(__dirname, './locales/{{lng}}/{{ns}}.json') + }, + supportedLngs: ['en', 'pt'], + preload: ['en', 'pt'], + fallbackLng: 'en' + } +}); +//... +``` + +# How-To Guides + +## Changing Language Dynamically + +You can change the language at runtime: + +```ts +//... +I18n.init({ + options: { + resources: { + en: { + 'translation': { + hello: "Hi" + } + }, + pt: { + 'translation': { + hello: "Olá" + } + } + } + } +}); +// result: Hi +console.log(t({ key: 'hello' })); + +I18n.changeLanguage('pt'); + +// result: Olá +console.log(t({ key: 'hello' })); +``` + +## Adding Translations + +To add translations dynamically, you can initialize the I18n instance first and add +translation resources at a later time. This approach is useful in scenarios where +translations are fetched from an external source or need to be updated without +reinitializing the entire i18n setup. + +This method is particularly beneficial in the following situations: + +- **Fetching Translations from an API**: When translations are retrieved from a + remote server, you can initialize i18n once and update translations as they + become available. +- **Modular Applications**: In large applications with multiple modules, + translations can be added as each module is loaded, reducing the initial load + time. +- **Real-time Updates**: If your application supports real-time updates, you can + dynamically add new translations without disrupting the user experience. +```ts +//... +I18n.init(); + +const locales = [ + { + namespace: 'common', + language: 'en', + resource: { welcome: 'Welcome' }, + }, + { + namespace: 'common', + language: 'fr', + resource: { welcome: 'Bienvenue' }, + }, +]; + +I18n.addTranslations(locales); + +// Welcome +console.log(t({ + namespace: 'common', + key: 'welcome', + language: 'en' +})); + +// Bienvenue +console.log(t({ + namespace: 'common', + key: 'welcome', + language: 'fr' +})); +``` +## Creating a Custom Translation Module + +You can create custom translation modules to extend the i18n functionality. Here’s how: + +### **Define the Module**: Add modules on initialization + +```ts +import { MyCustomModule } from './my-custom-module'; +import { I18n } from '@concepta/i18n'; + +I18n.init({ + modules: [MyCustomModule], + options: { + //... + } +}); +``` + +## Handling Missing Translations + +To handle missing translations gracefully: +```ts +import { I18n, t } from '@concepta/i18n'; +I18n.translate({ + key: 'missing_key', + namespace: 'common', + defaultMessage: 'This is the default text', +}); + +// or +t({ + key: 'missing_key', + namespace: 'common', + defaultMessage: 'This is the default text', +}); +``` + +# Reference + +Please check (API Reference)[] for more information. + +# Explanation + +## What is i18n? + +i18n, short for internationalization, refers to the process of designing software applications that can be adapted to various languages and regions without engineering changes. + +## How i18n Module Works + +The i18n module in this package provides a wrapper around i18next, allowing seamless integration of translation modules and dynamic language switching. + +### Benefits of Using i18n + +- **Flexibility**: Supports multiple languages and dynamic translation updates. +- **Modularity**: Easily extendable with custom modules for translation management. +- **Ease of Use**: Simple API to manage translations and switch languages. -## Installation +### Common i18n Pitfalls -`yarn add @concepta/ts-core` +- **Missing Translations**: Ensure all necessary translations are provided for each language. +- **Performance Overheads**: Avoid loading unnecessary translation files by using modular imports. diff --git a/packages/i18n/src/__fixtures__/locales/en/translation.json b/packages/i18n/src/__fixtures__/locales/en/translation.json index 89e126ec..8d8f0268 100644 --- a/packages/i18n/src/__fixtures__/locales/en/translation.json +++ b/packages/i18n/src/__fixtures__/locales/en/translation.json @@ -1,3 +1,3 @@ { "hello": "Hi from translation" -} +} \ No newline at end of file diff --git a/packages/i18n/src/__fixtures__/moduleA/index.ts b/packages/i18n/src/__fixtures__/moduleA/index.ts index 92898ebc..0cdc765c 100644 --- a/packages/i18n/src/__fixtures__/moduleA/index.ts +++ b/packages/i18n/src/__fixtures__/moduleA/index.ts @@ -1,4 +1,3 @@ - import { I18n } from '../../i18n'; import LOCALES from './locales'; @@ -8,6 +7,6 @@ export const initModuleATranslation = () => { I18n.addTranslations(LOCALES); loaded = true; } -} +}; export * from './moduleA.module.fixture'; diff --git a/packages/i18n/src/__fixtures__/moduleA/locales/en/ModuleAFixture.ts b/packages/i18n/src/__fixtures__/moduleA/locales/en/ModuleAFixture.ts index 50901b9c..9c4c6d50 100644 --- a/packages/i18n/src/__fixtures__/moduleA/locales/en/ModuleAFixture.ts +++ b/packages/i18n/src/__fixtures__/moduleA/locales/en/ModuleAFixture.ts @@ -1,4 +1,4 @@ const enUS = { - "hello": "Hi from module A" -} -export default enUS; \ No newline at end of file + hello: 'Hi from module A', +}; +export default enUS; diff --git a/packages/i18n/src/__fixtures__/moduleA/locales/index.ts b/packages/i18n/src/__fixtures__/moduleA/locales/index.ts index f20270c7..04a16f00 100644 --- a/packages/i18n/src/__fixtures__/moduleA/locales/index.ts +++ b/packages/i18n/src/__fixtures__/moduleA/locales/index.ts @@ -1,17 +1,19 @@ +import { I18nLocalesInterface } from '../../../interfaces/i18n-locales.interface'; +import { ModuleAFixture } from '../moduleA.module.fixture'; +import enUS from './en/ModuleAFixture'; +import ptBR from './pt/ModuleAFixture'; -import { LocalesInterface } from "../../../interfaces/i18n-locales.interface"; -import { ModuleAFixture } from "../moduleA.module.fixture"; -import enUS from "./en/ModuleAFixture"; -import ptBR from "./pt/ModuleAFixture"; +const LOCALES: I18nLocalesInterface[] = [ + { + namespace: ModuleAFixture.name, + language: 'en', + resource: enUS, + }, + { + namespace: ModuleAFixture.name, + language: 'pt', + resource: ptBR, + }, +]; -const LOCALES: LocalesInterface[] = [{ - namespace: ModuleAFixture.name, - language: 'en', - resource: enUS, -}, { - namespace: ModuleAFixture.name, - language: 'pt', - resource: ptBR, -}] - -export default LOCALES; \ No newline at end of file +export default LOCALES; diff --git a/packages/i18n/src/__fixtures__/moduleA/locales/pt/ModuleAFixture.ts b/packages/i18n/src/__fixtures__/moduleA/locales/pt/ModuleAFixture.ts index c6f37656..b55357f7 100644 --- a/packages/i18n/src/__fixtures__/moduleA/locales/pt/ModuleAFixture.ts +++ b/packages/i18n/src/__fixtures__/moduleA/locales/pt/ModuleAFixture.ts @@ -1,4 +1,4 @@ const ptBR = { - "hello": "Olá do modulo A" -} -export default ptBR; \ No newline at end of file + hello: 'Olá do modulo A', +}; +export default ptBR; diff --git a/packages/i18n/src/__fixtures__/moduleA/moduleA.module.fixture.ts b/packages/i18n/src/__fixtures__/moduleA/moduleA.module.fixture.ts index 6f8caa16..040e4dd8 100644 --- a/packages/i18n/src/__fixtures__/moduleA/moduleA.module.fixture.ts +++ b/packages/i18n/src/__fixtures__/moduleA/moduleA.module.fixture.ts @@ -1,29 +1,44 @@ -import { initModuleATranslation } from "./"; -import { I18n } from "../../i18n"; +import { initModuleATranslation } from './'; +import { I18n } from '../../i18n'; -import { ModuleBFixture, initModuleBTranslation } from "../moduleB"; -import { ModuleCFixture, initModuleCTranslation } from "../moduleC"; +import { ModuleBFixture, initModuleBTranslation } from '../moduleB'; +import { ModuleCFixture, initModuleCTranslation } from '../moduleC'; // Non-NestJS module export class ModuleAFixture { - constructor(private moduleB: ModuleBFixture, private moduleC: ModuleCFixture) { + constructor( + private moduleB: ModuleBFixture, + private moduleC: ModuleCFixture, + ) { initModuleATranslation(); initModuleBTranslation(); initModuleCTranslation(); } translationModuleA() { - return I18n.translate({ namespace: ModuleAFixture.name, key: 'hello', language: 'en' }); + return I18n.translate({ + namespace: ModuleAFixture.name, + key: 'hello', + language: 'en', + }); } translationModuleB() { - return I18n.translate({ namespace: ModuleBFixture.name, key: 'hello', language: 'en' }); + return I18n.translate({ + namespace: ModuleBFixture.name, + key: 'hello', + language: 'en', + }); } translationCFromModuleB() { return this.moduleB.translationC(); } - translationC(key:string = 'hello', language: string = 'en') { - return I18n.translate({ namespace: ModuleCFixture.name, key: key, language: language }); + translationC(key: string = 'hello', language: string = 'en') { + return I18n.translate({ + namespace: ModuleCFixture.name, + key: key, + language: language, + }); } -} \ No newline at end of file +} diff --git a/packages/i18n/src/__fixtures__/moduleB/index.ts b/packages/i18n/src/__fixtures__/moduleB/index.ts index 53f6ebe6..2b054598 100644 --- a/packages/i18n/src/__fixtures__/moduleB/index.ts +++ b/packages/i18n/src/__fixtures__/moduleB/index.ts @@ -7,6 +7,6 @@ export const initModuleBTranslation = () => { I18n.addTranslations(LOCALES); loaded = true; } -} +}; export * from './moduleB.module.fixture'; diff --git a/packages/i18n/src/__fixtures__/moduleB/locales/en/ModuleBFixture.ts b/packages/i18n/src/__fixtures__/moduleB/locales/en/ModuleBFixture.ts index cce9c536..4c5481db 100644 --- a/packages/i18n/src/__fixtures__/moduleB/locales/en/ModuleBFixture.ts +++ b/packages/i18n/src/__fixtures__/moduleB/locales/en/ModuleBFixture.ts @@ -1,4 +1,4 @@ const enUS = { - "hello": "Hi from module B" -} -export default enUS; \ No newline at end of file + hello: 'Hi from module B', +}; +export default enUS; diff --git a/packages/i18n/src/__fixtures__/moduleB/locales/index.ts b/packages/i18n/src/__fixtures__/moduleB/locales/index.ts index 1c7fed54..6d1915bd 100644 --- a/packages/i18n/src/__fixtures__/moduleB/locales/index.ts +++ b/packages/i18n/src/__fixtures__/moduleB/locales/index.ts @@ -1,17 +1,19 @@ +import { I18nLocalesInterface } from '../../../interfaces/i18n-locales.interface'; +import { ModuleBFixture } from '../moduleB.module.fixture'; +import enUS from './en/ModuleBFixture'; +import ptBR from './pt/ModuleBFixture'; -import { LocalesInterface } from "../../../interfaces/i18n-locales.interface"; -import { ModuleBFixture } from "../moduleB.module.fixture"; -import enUS from "./en/ModuleBFixture"; -import ptBR from "./pt/ModuleBFixture"; +const LOCALES: I18nLocalesInterface[] = [ + { + namespace: ModuleBFixture.name, + language: 'en', + resource: enUS, + }, + { + namespace: ModuleBFixture.name, + language: 'pt', + resource: ptBR, + }, +]; -const LOCALES: LocalesInterface[] = [{ - namespace: ModuleBFixture.name, - language: 'en', - resource: enUS, -}, { - namespace: ModuleBFixture.name, - language: 'pt', - resource: ptBR, -}] - -export default LOCALES; \ No newline at end of file +export default LOCALES; diff --git a/packages/i18n/src/__fixtures__/moduleB/locales/pt/ModuleBFixture.ts b/packages/i18n/src/__fixtures__/moduleB/locales/pt/ModuleBFixture.ts index 45d570ce..3065099f 100644 --- a/packages/i18n/src/__fixtures__/moduleB/locales/pt/ModuleBFixture.ts +++ b/packages/i18n/src/__fixtures__/moduleB/locales/pt/ModuleBFixture.ts @@ -1,4 +1,4 @@ const ptBR = { - "hello": "Olá do modulo B" -} -export default ptBR; \ No newline at end of file + hello: 'Olá do modulo B', +}; +export default ptBR; diff --git a/packages/i18n/src/__fixtures__/moduleB/moduleB.module.fixture.ts b/packages/i18n/src/__fixtures__/moduleB/moduleB.module.fixture.ts index 75f2f825..899b21d2 100644 --- a/packages/i18n/src/__fixtures__/moduleB/moduleB.module.fixture.ts +++ b/packages/i18n/src/__fixtures__/moduleB/moduleB.module.fixture.ts @@ -1,6 +1,6 @@ -import { initModuleBTranslation } from "./"; -import { I18n } from "../../i18n"; -import { ModuleCFixture, initModuleCTranslation } from "../moduleC"; +import { initModuleBTranslation } from './'; +import { I18n } from '../../i18n'; +import { ModuleCFixture, initModuleCTranslation } from '../moduleC'; // Non-NestJS module export class ModuleBFixture { @@ -9,10 +9,18 @@ export class ModuleBFixture { initModuleCTranslation(); } translationB() { - return I18n.translate({ namespace: ModuleBFixture.name, key: 'hello', language: 'en' }); + return I18n.translate({ + namespace: ModuleBFixture.name, + key: 'hello', + language: 'en', + }); } translationC() { - return I18n.translate({ namespace: ModuleCFixture.name, key: 'hello', language: 'en' }); + return I18n.translate({ + namespace: ModuleCFixture.name, + key: 'hello', + language: 'en', + }); } -} \ No newline at end of file +} diff --git a/packages/i18n/src/__fixtures__/moduleC/index.ts b/packages/i18n/src/__fixtures__/moduleC/index.ts index c3bd2f6e..21414b0b 100644 --- a/packages/i18n/src/__fixtures__/moduleC/index.ts +++ b/packages/i18n/src/__fixtures__/moduleC/index.ts @@ -7,6 +7,6 @@ export const initModuleCTranslation = () => { I18n.addTranslations(LOCALES); loaded = true; } -} +}; export * from './moduleC.module.fixture'; diff --git a/packages/i18n/src/__fixtures__/moduleC/locales/en/ModuleCFixture.ts b/packages/i18n/src/__fixtures__/moduleC/locales/en/ModuleCFixture.ts index cfd9dad8..f2ffacf9 100644 --- a/packages/i18n/src/__fixtures__/moduleC/locales/en/ModuleCFixture.ts +++ b/packages/i18n/src/__fixtures__/moduleC/locales/en/ModuleCFixture.ts @@ -1,5 +1,5 @@ const enUS = { - "hello": "Hi from module C", - "bye": "Bye from module C" -} -export default enUS; \ No newline at end of file + hello: 'Hi from module C', + bye: 'Bye from module C', +}; +export default enUS; diff --git a/packages/i18n/src/__fixtures__/moduleC/locales/index.ts b/packages/i18n/src/__fixtures__/moduleC/locales/index.ts index b4f17254..814384aa 100644 --- a/packages/i18n/src/__fixtures__/moduleC/locales/index.ts +++ b/packages/i18n/src/__fixtures__/moduleC/locales/index.ts @@ -1,17 +1,19 @@ +import { I18nLocalesInterface } from '../../../interfaces/i18n-locales.interface'; +import { ModuleCFixture } from '../moduleC.module.fixture'; +import enUS from './en/ModuleCFixture'; +import ptCR from './pt/ModuleCFixture'; -import { LocalesInterface } from "../../../interfaces/i18n-locales.interface"; -import { ModuleCFixture } from "../moduleC.module.fixture"; -import enUS from "./en/ModuleCFixture"; -import ptCR from "./pt/ModuleCFixture"; +const LOCALES: I18nLocalesInterface[] = [ + { + namespace: ModuleCFixture.name, + language: 'en', + resource: enUS, + }, + { + namespace: ModuleCFixture.name, + language: 'pt', + resource: ptCR, + }, +]; -const LOCALES: LocalesInterface[] = [{ - namespace: ModuleCFixture.name, - language: 'en', - resource: enUS, -}, { - namespace: ModuleCFixture.name, - language: 'pt', - resource: ptCR, -}] - -export default LOCALES; \ No newline at end of file +export default LOCALES; diff --git a/packages/i18n/src/__fixtures__/moduleC/locales/pt/ModuleCFixture.ts b/packages/i18n/src/__fixtures__/moduleC/locales/pt/ModuleCFixture.ts index f7287d71..4bb2cc32 100644 --- a/packages/i18n/src/__fixtures__/moduleC/locales/pt/ModuleCFixture.ts +++ b/packages/i18n/src/__fixtures__/moduleC/locales/pt/ModuleCFixture.ts @@ -1,4 +1,4 @@ const ptCR = { - "hello": "Olá do modulo C" -} -export default ptCR; \ No newline at end of file + hello: 'Olá do modulo C', +}; +export default ptCR; diff --git a/packages/i18n/src/__fixtures__/moduleC/moduleC.module.fixture.ts b/packages/i18n/src/__fixtures__/moduleC/moduleC.module.fixture.ts index e3666f10..6207d6f4 100644 --- a/packages/i18n/src/__fixtures__/moduleC/moduleC.module.fixture.ts +++ b/packages/i18n/src/__fixtures__/moduleC/moduleC.module.fixture.ts @@ -1,5 +1,5 @@ -import { initModuleCTranslation } from "./"; -import { I18n } from "../../i18n"; +import { initModuleCTranslation } from './'; +import { I18n } from '../../i18n'; // Non-NestJS module export class ModuleCFixture { @@ -7,6 +7,10 @@ export class ModuleCFixture { initModuleCTranslation(); } translationC() { - return I18n.translate({ namespace: ModuleCFixture.name, key: 'hello', language: 'en' }); + return I18n.translate({ + namespace: ModuleCFixture.name, + key: 'hello', + language: 'en', + }); } -} \ No newline at end of file +} diff --git a/packages/i18n/src/constants.ts b/packages/i18n/src/constants.ts deleted file mode 100644 index a1ef7c0a..00000000 --- a/packages/i18n/src/constants.ts +++ /dev/null @@ -1 +0,0 @@ -export const DEFAULT_NAMESPACE = 'translation'; \ No newline at end of file diff --git a/packages/i18n/src/i18n.e2e-spec.ts b/packages/i18n/src/i18n.e2e-spec.ts index 3f62478e..7b8ac582 100644 --- a/packages/i18n/src/i18n.e2e-spec.ts +++ b/packages/i18n/src/i18n.e2e-spec.ts @@ -2,15 +2,14 @@ import { ModuleAFixture } from './__fixtures__/moduleA'; import { ModuleBFixture } from './__fixtures__/moduleB'; import { ModuleCFixture } from './__fixtures__/moduleC'; import { I18n } from './i18n'; -import { LocalesInterface } from './interfaces/i18n-locales.interface'; +import { I18nLocalesInterface } from './interfaces/i18n-locales.interface'; describe('i18n module', () => { - describe('ModuleAFixture E2E', () => { let moduleA: ModuleAFixture; let moduleB: ModuleBFixture; let moduleC: ModuleCFixture; - + beforeAll(() => { jest.spyOn(console, 'log').mockImplementation(() => {}); jest.spyOn(console, 'warn').mockImplementation(() => {}); @@ -18,44 +17,46 @@ describe('i18n module', () => { I18n.init({ options: { initImmediate: false, - } + }, }); // Create instances of the modules moduleC = new ModuleCFixture(); moduleB = new ModuleBFixture(new ModuleCFixture()); moduleA = new ModuleAFixture(moduleB, moduleC); }); - + it('should translate for module A', () => { const result = moduleA.translationModuleA(); expect(result).toBe('Hi from module A'); }); - + it('should translate for module B', () => { const result = moduleA.translationModuleB(); expect(result).toBe('Hi from module B'); }); - + it('should translate C from module B', () => { const result = moduleA.translationCFromModuleB(); expect(result).toBe('Hi from module C'); }); - + it('should translate for module C', () => { const result = moduleA.translationC(); expect(result).toBe('Hi from module C'); }); it('should translate for module C', () => { - const LOCALES: LocalesInterface[] = [{ - namespace: ModuleCFixture.name, - resource: { - 'hello': 'overwrite english', - 'hi': 'new hi' + const LOCALES: I18nLocalesInterface[] = [ + { + namespace: ModuleCFixture.name, + resource: { + hello: 'overwrite english', + hi: 'new hi', + }, + deep: false, + overwrite: false, }, - deep: false, - overwrite: false - }]; + ]; I18n.addTranslations(LOCALES); const result = moduleA.translationC('hello', 'pt'); @@ -63,13 +64,12 @@ describe('i18n module', () => { const result1 = moduleA.translationC('hello', 'en'); expect(result1).toBe('overwrite english'); - + const resultExtend = moduleA.translationC('hi', 'en'); expect(resultExtend).toBe('new hi'); - + const resultBye = moduleA.translationC('bye', 'en'); expect(resultBye).toBe('Bye from module C'); - }); }); -}); \ No newline at end of file +}); diff --git a/packages/i18n/src/i18n.spec.ts b/packages/i18n/src/i18n.spec.ts index e4571695..88bf6e59 100644 --- a/packages/i18n/src/i18n.spec.ts +++ b/packages/i18n/src/i18n.spec.ts @@ -4,147 +4,210 @@ import { ModuleAFixture } from './__fixtures__/moduleA'; import { ModuleBFixture } from './__fixtures__/moduleB'; import { ModuleCFixture } from './__fixtures__/moduleC'; import { I18n } from './i18n'; -import { LocalesInterface } from './interfaces/i18n-locales.interface'; +import { I18nLocalesInterface } from './interfaces/i18n-locales.interface'; +import { t } from './'; describe('i18n module', () => { - beforeAll(() => { jest.spyOn(console, 'log').mockImplementation(() => {}); jest.spyOn(console, 'warn').mockImplementation(() => {}); - }) + }); it('should not initialize ', () => { - const LOCALES: LocalesInterface[] = [{ - namespace: 'my-namespace', - language: 'en', - resource: require(__dirname + '/__fixtures__/locales/en/my-namespace.json'), - }]; - + const LOCALES: I18nLocalesInterface[] = [ + { + namespace: 'my-namespace', + language: 'en', + resource: require(__dirname + + '/__fixtures__/locales/en/my-namespace.json'), + }, + ]; + const consoleErrorSpy = jest.spyOn(console, 'error').mockReturnValueOnce(); I18n.addTranslations(LOCALES); - expect(consoleErrorSpy).toBeCalledWith('i18next was not isInitialized, please call init'); + expect(consoleErrorSpy).toBeCalledWith( + 'i18next was not isInitialized, please call init', + ); }); describe('i18n configuration', () => { - - afterEach(() => { + afterEach(() => { I18n.reset(); - }) - + }); + it('should initialize translations from constant', async () => { - const enUS = { - hello: "Hi" + hello: 'Hi', }; const ptBR = { - hello: "Olá" + hello: 'Olá', }; - + // TODO: maybe the interface can be improved - const LOCALES: LocalesInterface[] = [{ - namespace: 'my-namespace', - language: 'en', - resource: enUS, - }, { - namespace: 'my-namespace', - language: 'pt', - resource: ptBR, - }]; + const LOCALES: I18nLocalesInterface[] = [ + { + namespace: 'my-namespace', + language: 'en', + resource: enUS, + }, + { + namespace: 'my-namespace', + language: 'pt', + resource: ptBR, + }, + ]; I18n.init({ options: { initImmediate: false, - } + }, }); I18n.addTranslations(LOCALES); - - const enTranslation = I18n.translate({ namespace: 'my-namespace', key: 'hello', language: 'en' }); - const ptTranslation = I18n.translate({ namespace: 'my-namespace', key: 'hello', language: 'pt' }); + + const enTranslation = I18n.translate({ + namespace: 'my-namespace', + key: 'hello', + language: 'en', + }); + const ptTranslation = I18n.translate({ + namespace: 'my-namespace', + key: 'hello', + language: 'pt', + }); expect(enTranslation).toBe('Hi'); expect(ptTranslation).toBe('Olá'); }); it('should initialize translations from json', async () => { - const LOCALES: LocalesInterface[] = [{ - namespace: 'my-namespace', - language: 'en', - resource: require(__dirname + '/__fixtures__/locales/en/my-namespace.json'), - }, { - namespace: 'my-namespace', - language: 'pt', - resource: require(__dirname + '/__fixtures__/locales/pt/my-namespace.json'), - }]; - + const LOCALES: I18nLocalesInterface[] = [ + { + namespace: 'my-namespace', + language: 'en', + resource: require(__dirname + + '/__fixtures__/locales/en/my-namespace.json'), + }, + { + namespace: 'my-namespace', + language: 'pt', + resource: require(__dirname + + '/__fixtures__/locales/pt/my-namespace.json'), + }, + ]; + I18n.init({ options: { initImmediate: false, - } + }, }); I18n.addTranslations(LOCALES); - const enTranslation = I18n.translate({ namespace: 'my-namespace', key: 'hello', language: 'en' }); - const ptTranslation = I18n.translate({ namespace: 'my-namespace', key: 'hello', language: 'pt' }); + const enTranslation = I18n.translate({ + namespace: 'my-namespace', + key: 'hello', + language: 'en', + }); + const ptTranslation = I18n.translate({ + namespace: 'my-namespace', + key: 'hello', + language: 'pt', + }); expect(enTranslation).toBe('Hi'); expect(ptTranslation).toBe('Olá'); }); it('should initialize translations from constant', async () => { - - const enUS = { - hello: "Hi" - }; - - const ptBR = { - hello: "Olá" - }; - I18n.init({ options: { - initImmediate: false, - ns: [ 'translation', 'my-namespace' ], resources: { en: { - 'my-namespace': enUS + translation: { + hello: 'Hi', + }, }, pt: { - 'my-namespace': ptBR - } - } - } + translation: { + hello: 'Olá', + }, + }, + }, + }, }); - const enTranslation = I18n.translate({ namespace: 'my-namespace', key: 'hello', language: 'en' }); - const ptTranslation = I18n.translate({ namespace: 'my-namespace', key: 'hello', language: 'pt' }); + const enTranslation = t({ key: 'hello' }); + const ptTranslation = t({ key: 'hello', language: 'pt' }); expect(enTranslation).toBe('Hi'); expect(ptTranslation).toBe('Olá'); }); - it('should initialize translations from settings', async () => { + it('should initialize translations from constant changing language', async () => { + I18n.init({ + options: { + resources: { + en: { + translation: { + hello: 'Hi', + }, + }, + pt: { + translation: { + hello: 'Olá', + }, + }, + }, + }, + }); + + const enTranslation = t({ key: 'hello' }); + expect(enTranslation).toBe('Hi'); + + I18n.changeLanguage('pt'); + const ptTranslation = t({ key: 'hello' }); + expect(ptTranslation).toBe('Olá'); + }); + + it('should initialize translations from settings', async () => { I18n.init({ modules: [I18NexFsBackend], options: { initImmediate: false, ns: ['translation', 'my-namespace'], backend: { - loadPath: join(__dirname, './__fixtures__/locales/{{lng}}/{{ns}}.json') + loadPath: join( + __dirname, + './__fixtures__/locales/{{lng}}/{{ns}}.json', + ), }, supportedLngs: ['en', 'pt'], preload: ['en', 'pt'], - fallbackLng: 'en' - } - }, (e) => { - console.log('Loading translations', e); + fallbackLng: 'en', + }, }); - const enTranslation = I18n.translate({ namespace: 'my-namespace', key: 'hello', language: 'en' }); - const ptTranslation = I18n.translate({ namespace: 'my-namespace', key: 'hello', language: 'pt' }); - const defaultEnTranslation = I18n.translate({ namespace: 'translation', key: 'hello', language: 'en' }); - const defaultPtTranslation = I18n.translate({ namespace: 'translation', key: 'hello', language: 'pt' }); + const enTranslation = t({ + namespace: 'my-namespace', + key: 'hello', + language: 'en', + }); + const ptTranslation = t({ + namespace: 'my-namespace', + key: 'hello', + language: 'pt', + }); + const defaultEnTranslation = t({ + namespace: 'translation', + key: 'hello', + language: 'en', + }); + const defaultPtTranslation = t({ + namespace: 'translation', + key: 'hello', + language: 'pt', + }); expect(enTranslation).toBe('Hi'); expect(ptTranslation).toBe('Olá'); @@ -152,44 +215,44 @@ describe('i18n module', () => { expect(defaultPtTranslation).toBe('Olá do translation'); }); }); - + describe('ModuleAFixture E2E', () => { let moduleA: ModuleAFixture; let moduleB: ModuleBFixture; let moduleC: ModuleCFixture; - + beforeAll(() => { // Initialize translations I18n.init({ options: { initImmediate: false, - } + }, }); - + // Create instances of the modules moduleB = new ModuleBFixture(new ModuleCFixture()); moduleC = new ModuleCFixture(); moduleA = new ModuleAFixture(moduleB, moduleC); }); - + it('should translate for module A', () => { const result = moduleA.translationModuleA(); expect(result).toBe('Hi from module A'); }); - + it('should translate for module B', () => { const result = moduleA.translationModuleB(); expect(result).toBe('Hi from module B'); }); - + it('should translate C from module B', () => { const result = moduleA.translationCFromModuleB(); expect(result).toBe('Hi from module C'); }); - + it('should translate for module C', () => { const result = moduleA.translationC(); expect(result).toBe('Hi from module C'); }); }); -}); \ No newline at end of file +}); diff --git a/packages/i18n/src/i18n.ts b/packages/i18n/src/i18n.ts index 49233179..922d5af8 100644 --- a/packages/i18n/src/i18n.ts +++ b/packages/i18n/src/i18n.ts @@ -3,108 +3,122 @@ import i18next, { Callback } from 'i18next'; import { I18nCallback, I18nObjectModule, - i18nextModuleType + I18nextModuleType, } from './interfaces/i18n.types'; -import { I18Settings } from './interfaces/i18n-settings.interface'; +import { I18nSettings } from './interfaces/i18n-settings.interface'; import { TranslateParams } from './interfaces/i18n-translate-params.interface'; -import { LocalesInterface } from './interfaces/i18n-locales.interface'; -import { KeyExistsParams } from './interfaces/i18n-key-exists-params.interface'; -import { I18nextOptions } from './interfaces/i18n-options.interface'; +import { I18nLocalesInterface } from './interfaces/i18n-locales.interface'; +import { I18nOptions } from './interfaces/i18n-options.interface'; +import { I18nTranslationKeys } from './interfaces/i18n-translation-keys.interface copy'; -export class I18n { +export class I18n { static instance = i18next.createInstance(); - + /** * Adds a module to the i18next instance. - * - * @template T - The type of the module. + * * @param module - The module to add. * @returns The i18next instance. - * + * * @example + * ```typescript * // Example of adding a custom backend module * import Backend from 'i18next-custom-backend'; - * + * * I18n.addModule(Backend); + * ``` */ - static addModule = (module: i18nextModuleType) => { - return I18n.instance.use(module) - } + static addModule = ( + module: I18nextModuleType, + ) => { + return I18n.instance.use(module); + }; /** * Adds multiple modules to the i18next instance. - * - * @template T - The type of the modules. + * * @param modules - An array of modules to add. - * + * * @example + * ```ts * // Example of adding multiple custom backend modules * import Backend1 from 'i18next-custom-backend1'; * import Backend2 from 'i18next-custom-backend2'; - * + * * I18n.addModules([Backend1, Backend2]); + * ``` */ - static addModules = (modules?: i18nextModuleType[]) => { + static addModules = ( + modules?: I18nextModuleType[], + ) => { try { - if (modules && modules.length > 0 ) - modules.forEach((m) => { - I18n.addModule(m) - }) - } catch (e) { + if (modules && modules.length > 0) + modules.forEach((m) => { + I18n.addModule(m); + }); + } catch (e) { console.error('Error adding module'); } - } + }; /** * Retrieves the i18next instance. - * + * * @returns The i18next instance. - * + * * @example + * ```ts * const i18nInstance = I18n.getInstance(); + * ``` */ static getInstance = () => { return I18n.instance; - } + }; /** - * Changes the language. The callback will be called as soon translations were - * loaded or an error occurs while loading. HINT: For easy testing - setting + * Changes the language. The callback will be called as soon translations were + * loaded or an error occurs while loading. HINT: For easy testing - setting * lng to 'cimode' will set t function to always return the key. - * + * * @param language - The language to change to. * @returns A promise that resolves when the language change is complete. - * + * * @example + * ```ts * await I18n.changeLanguage('fr'); + * ``` */ - static changeLanguage = async (language: string, callback?: Callback): Promise => { + static changeLanguage = async ( + language: string, + callback?: Callback, + ): Promise => { try { await I18n.instance.changeLanguage(language, callback); - console.log(`Language changed to ${language}`); } catch (error) { console.error('Error changing language:', error); } - } + }; /** * Resets the i18next instance by creating a new instance. - * + * * @example + * ```ts * I18n.reset(); + * ``` */ static reset = () => { - I18n.instance = i18next.createInstance(); - } + I18n.instance = i18next.createInstance(); + }; /** * Initializes the i18next instance with the provided settings. - * - * @template T - The type of the settings. + * * @param settings - The settings to initialize i18next with. * @param callback - Optional callback to be called after initialization. - * + * * @example + * ```ts * const settings = { * modules: [Backend], * options: { @@ -119,35 +133,38 @@ export class I18n { * } * }; * I18n.init(settings); + * ``` */ - static init = (settings?: I18Settings, callback?: I18nCallback) => { - I18n.addModules(settings?.modules) - - const i18nextOptions: I18nextOptions = { + static init = (settings?: I18nSettings, callback?: I18nCallback) => { + I18n.addModules(settings?.modules); + + const i18nextOptions: I18nOptions = { debug: true, preload: ['en'], fallbackLng: 'en', ns: ['translation'], defaultNS: 'translation', - ...settings?.options + ...settings?.options, }; - + I18n.instance.init(i18nextOptions, callback); - } - + }; + /** * Translates a given key using the provided parameters. - * + * * @param params - The parameters for translation. * @returns The translated string. - * + * * @example + * ```ts * const translation = I18n.translate({ * key: 'hello', * namespace: 'common', * language: 'fr', * defaultMessage: 'Bonjour', * }); + * ``` */ static translate = (params: TranslateParams): string => { const { @@ -155,26 +172,30 @@ export class I18n { language = I18n.getCurrentLanguage(), options = {}, key, - defaultMessage = '' + defaultMessage = '', } = params; const doesKeyExists = I18n.keyExists({ namespace, key, - language - }) - + language, + }); + const t = I18n.instance.getFixedT(language); - const result = doesKeyExists ? t(`${namespace}:${key}`, options) : defaultMessage; + const result = doesKeyExists + ? t(`${namespace}:${key}`, options) + : defaultMessage; return result; - } - + }; + /** * Retrieves the default namespace from the i18next instance options. - * + * * @returns The default namespace as a string. - * + * * @example + * ```ts * const defaultNamespace = I18n.getDefaultNamespace(); + * ``` */ static getDefaultNamespace = (): string => { // TODO: need to validate what to do if defaultNS is set to false @@ -186,29 +207,32 @@ export class I18n { } else { return 'translation'; // Fallback to a default namespace } - } + }; /** * Retrieves the current language detected from the i18next instance. * * @returns The default language as a string. - * + * * @example + * ```ts * const defaultLanguage = I18n.getDefaultLanguage(); + * ``` */ static getCurrentLanguage = (): string => { return I18n.instance.language; - } - - /** + }; + + /** * Adds translations to the i18next instance. - * Adds a complete bundle. Setting deep param to true will extend existing - * translations in that file. Setting overwrite to true it will overwrite + * Adds a complete bundle. Setting deep param to true will extend existing + * translations in that file. Setting overwrite to true it will overwrite * existing key translations in that file. - * + * * @param locales - An array of locale objects containing translation data. - * + * * @example + * ```ts * const locales = [ * { * namespace: 'common', @@ -219,34 +243,52 @@ export class I18n { * } * ]; * I18n.addTranslations(locales); + * ``` */ - static addTranslations = (locales: LocalesInterface[]) => { - + static addTranslations = (locales: I18nLocalesInterface[]) => { if (!I18n.instance.isInitialized) { console.error('i18next was not isInitialized, please call init'); return; } - locales.forEach(locale => { - const { namespace = I18n.getDefaultNamespace(), resource, language = I18n.getCurrentLanguage(), deep, overwrite } = locale; - I18n.instance.addResourceBundle(language, namespace, resource, deep, overwrite ); + locales.forEach((locale) => { + const { + namespace = I18n.getDefaultNamespace(), + resource, + language = I18n.getCurrentLanguage(), + deep, + overwrite, + } = locale; + I18n.instance.addResourceBundle( + language, + namespace, + resource, + deep, + overwrite, + ); }); }; - + /** * Checks if a translation key exists in the i18next instance. - * + * * @param params - The parameters to check if the key exists. * @returns A boolean indicating whether the key exists. - * + * * @example + * ```ts * const exists = I18n.keyExists({ * key: 'hello', * namespace: 'common', * language: 'fr' * }); + * ``` */ - static keyExists = (params: KeyExistsParams): boolean => { - const { namespace = I18n.getDefaultNamespace(), key, language = I18n.getCurrentLanguage() } = params; + static keyExists = (params: I18nTranslationKeys): boolean => { + const { + namespace = I18n.getDefaultNamespace(), + key, + language = I18n.getCurrentLanguage(), + } = params; return I18n.instance.exists(`${namespace}:${key}`, { lng: language }); - } -} \ No newline at end of file + }; +} diff --git a/packages/i18n/src/index.ts b/packages/i18n/src/index.ts index 0f1d22e1..9b6a6f4e 100644 --- a/packages/i18n/src/index.ts +++ b/packages/i18n/src/index.ts @@ -2,8 +2,8 @@ import { I18n } from './i18n'; import { TranslateParams } from './interfaces/i18n-translate-params.interface'; export { I18n } from './i18n'; -export { LocalesInterface } from './interfaces/i18n-locales.interface'; +export { I18nLocalesInterface as LocalesInterface } from './interfaces/i18n-locales.interface'; export const t = (args: TranslateParams) => { return I18n.translate(args); -} \ No newline at end of file +}; diff --git a/packages/i18n/src/interfaces/i18n-key-exists-params.interface.ts b/packages/i18n/src/interfaces/i18n-key-exists-params.interface.ts deleted file mode 100644 index 1aa93ed2..00000000 --- a/packages/i18n/src/interfaces/i18n-key-exists-params.interface.ts +++ /dev/null @@ -1,5 +0,0 @@ -export interface KeyExistsParams { - namespace?: string; - key: string; - language?: string; -} \ No newline at end of file diff --git a/packages/i18n/src/interfaces/i18n-locales.interface.ts b/packages/i18n/src/interfaces/i18n-locales.interface.ts index a76e4efa..1dca9640 100644 --- a/packages/i18n/src/interfaces/i18n-locales.interface.ts +++ b/packages/i18n/src/interfaces/i18n-locales.interface.ts @@ -1,7 +1,22 @@ -export interface LocalesInterface { - namespace?: string; - language?: string; - resource: Record +import { I18nTranslationKeys } from './i18n-translation-keys.interface copy'; + +/** + * Interface representing the structure of localization data. + */ +export interface I18nLocalesInterface + extends Partial> { + /** + * A record of localization keys and their corresponding translations. + */ + resource: Record; + + /** + * Optional flag indicating whether the localization should be extends the other resources. + */ deep?: boolean; + + /** + * Optional flag indicating whether the localization should overwrite existing keys. + */ overwrite?: boolean; -} \ No newline at end of file +} diff --git a/packages/i18n/src/interfaces/i18n-module.interface.ts b/packages/i18n/src/interfaces/i18n-module.interface.ts index 2287583b..ed90e187 100644 --- a/packages/i18n/src/interfaces/i18n-module.interface.ts +++ b/packages/i18n/src/interfaces/i18n-module.interface.ts @@ -1,6 +1,12 @@ -import { Newable, NewableModule } from "i18next"; -import { I18nObjectModule } from "./i18n.types"; +import { Newable, NewableModule } from 'i18next'; +import { I18nObjectModule } from './i18n.types'; -export interface i18nextModule { - module: (T | NewableModule | Newable) -} \ No newline at end of file +/** + * Interface representing an I18n module. + */ +export interface I18nModule { + /** + * The module property can be of type T, NewableModule, or Newable. + */ + module: T | NewableModule | Newable; +} diff --git a/packages/i18n/src/interfaces/i18n-options.interface.ts b/packages/i18n/src/interfaces/i18n-options.interface.ts index 7801b630..44539367 100644 --- a/packages/i18n/src/interfaces/i18n-options.interface.ts +++ b/packages/i18n/src/interfaces/i18n-options.interface.ts @@ -1,3 +1,6 @@ -import { InitOptions } from "i18next"; +import { InitOptions } from 'i18next'; -export interface I18nextOptions extends InitOptions {} \ No newline at end of file +/** + * Interface extending InitOptions from i18next with generic type T. + */ +export interface I18nOptions extends InitOptions {} diff --git a/packages/i18n/src/interfaces/i18n-settings.interface.ts b/packages/i18n/src/interfaces/i18n-settings.interface.ts index e612a8d6..de938e34 100644 --- a/packages/i18n/src/interfaces/i18n-settings.interface.ts +++ b/packages/i18n/src/interfaces/i18n-settings.interface.ts @@ -1,10 +1,17 @@ -import { I18nextOptions } from "./i18n-options.interface" -import { I18nObjectModule, i18nextModuleType } from "./i18n.types" +import { I18nOptions } from './i18n-options.interface'; +import { I18nObjectModule, I18nextModuleType } from './i18n.types'; -export interface I18Settings { - // TODO: validate this how to receive the other possible types - // any custom configuration should be under settings - newInstance?: boolean; - modules?: i18nextModuleType[] - options?: I18nextOptions +/** + * Interface representing the settings for internationalization (i18n). + */ +export interface I18nSettings { + /** + * An array of i18n modules. + */ + modules?: I18nextModuleType[]; + + /** + * Options for configuring i18n. + */ + options?: I18nOptions; } diff --git a/packages/i18n/src/interfaces/i18n-translate-params.interface.ts b/packages/i18n/src/interfaces/i18n-translate-params.interface.ts index 43eb0f46..4d676f2e 100644 --- a/packages/i18n/src/interfaces/i18n-translate-params.interface.ts +++ b/packages/i18n/src/interfaces/i18n-translate-params.interface.ts @@ -1,9 +1,8 @@ -import { I18nTranslationOptions } from "./i18n.types"; +import { I18nTranslationKeys } from './i18n-translation-keys.interface copy'; +import { I18nTranslationOptions } from './i18n.types'; -export interface TranslateParams { - namespace?: string; - key: string; - language?: string; +export interface TranslateParams extends I18nTranslationKeys { + // TODO: change default message to inside options.defaultValue defaultMessage?: string; options?: I18nTranslationOptions; -} \ No newline at end of file +} diff --git a/packages/i18n/src/interfaces/i18n-translation-keys.interface copy.ts b/packages/i18n/src/interfaces/i18n-translation-keys.interface copy.ts new file mode 100644 index 00000000..9c758228 --- /dev/null +++ b/packages/i18n/src/interfaces/i18n-translation-keys.interface copy.ts @@ -0,0 +1,22 @@ +/** + * Parameters for checking if a translation key exists. + */ +export interface I18nTranslationKeys { + /** + * The namespace of the translation key. + * Optional. + */ + namespace?: string; + + /** + * The translation key to check. + * Required. + */ + key: string; + + /** + * The language of the translation key. + * Optional. + */ + language?: string; +} diff --git a/packages/i18n/src/interfaces/i18n.types.ts b/packages/i18n/src/interfaces/i18n.types.ts index 9ee5702c..7e4a61c8 100644 --- a/packages/i18n/src/interfaces/i18n.types.ts +++ b/packages/i18n/src/interfaces/i18n.types.ts @@ -1,7 +1,25 @@ -import { Callback, Module, Newable, NewableModule, TOptions } from "i18next"; +import { Callback, Module, Newable, NewableModule, TOptions } from 'i18next'; +/** + * Represents an i18n module object. + */ export type I18nObjectModule = Module; + +/** + * Options for i18n translation. + */ export type I18nTranslationOptions = TOptions; + +/** + * Callback function for i18n operations. + */ export type I18nCallback = Callback; -export type i18nextModuleType = T | NewableModule | Newable; +/** + * Type representing an i18next module, which can be an instance of the module, + * a newable module, or a newable type. + */ +export type I18nextModuleType = + | T + | NewableModule + | Newable; diff --git a/packages/nestjs-cache/src/cache.module.spec.ts b/packages/nestjs-cache/src/cache.module.spec.ts index ab1ee325..7699e8ca 100644 --- a/packages/nestjs-cache/src/cache.module.spec.ts +++ b/packages/nestjs-cache/src/cache.module.spec.ts @@ -16,7 +16,7 @@ describe(CacheModule.name, () => { beforeEach(async () => { // to avoid error log from i18next not initialized jest.spyOn(console, 'error').mockImplementation(() => {}); - + const testModule: TestingModule = await Test.createTestingModule({ imports: [AppModuleFixture], }).compile(); diff --git a/packages/nestjs-cache/src/cache.module.ts b/packages/nestjs-cache/src/cache.module.ts index 4dbd62aa..4d7fa5dc 100644 --- a/packages/nestjs-cache/src/cache.module.ts +++ b/packages/nestjs-cache/src/cache.module.ts @@ -1,4 +1,4 @@ -import { DynamicModule, Module, OnModuleInit } from '@nestjs/common'; +import { DynamicModule, Module } from '@nestjs/common'; import { CacheAsyncOptions, @@ -16,12 +16,11 @@ import { I18n } from '@concepta/i18n'; */ @Module({}) export class CacheModule extends CacheModuleClass { - constructor() { - super(); + super(); I18n.addTranslations(LOCALES); } - + static register(options: CacheOptions): DynamicModule { return super.register(options); } diff --git a/packages/nestjs-cache/src/locales/en/CacheModule.ts b/packages/nestjs-cache/src/locales/en/CacheModule.ts index d909525c..2edb1294 100644 --- a/packages/nestjs-cache/src/locales/en/CacheModule.ts +++ b/packages/nestjs-cache/src/locales/en/CacheModule.ts @@ -1,4 +1,4 @@ const enUS = { - "REFERENCE_MUTATE_ERROR": "Error while trying to mutate a %s reference" -} -export default enUS; \ No newline at end of file + REFERENCE_MUTATE_ERROR: 'Error while trying to mutate a %s reference', +}; +export default enUS; diff --git a/packages/nestjs-cache/src/locales/index.ts b/packages/nestjs-cache/src/locales/index.ts index b5f48ccf..5a6cc2c5 100644 --- a/packages/nestjs-cache/src/locales/index.ts +++ b/packages/nestjs-cache/src/locales/index.ts @@ -1,16 +1,18 @@ -import { LocalesInterface } from "@concepta/i18n/dist/interfaces/i18n.interfaces"; -import { CacheModule } from "../cache.module"; -import enUS from "./en/CacheModule"; -import ptBR from "./pt/CacheModule"; +import { LocalesInterface } from '@concepta/i18n/dist/interfaces/i18n.interfaces'; +import enUS from './en/CacheModule'; +import ptBR from './pt/CacheModule'; -const LOCALES: LocalesInterface[] = [{ - namespace: 'CacheModule', - language: 'en', - resource: enUS, -}, { - namespace: 'CacheModule' , - language: 'pt', - resource: ptBR, -}] +const LOCALES: LocalesInterface[] = [ + { + namespace: 'CacheModule', + language: 'en', + resource: enUS, + }, + { + namespace: 'CacheModule', + language: 'pt', + resource: ptBR, + }, +]; -export default LOCALES; \ No newline at end of file +export default LOCALES; diff --git a/packages/nestjs-cache/src/locales/pt/CacheModule.ts b/packages/nestjs-cache/src/locales/pt/CacheModule.ts index f4c6d8c9..fdb36f00 100644 --- a/packages/nestjs-cache/src/locales/pt/CacheModule.ts +++ b/packages/nestjs-cache/src/locales/pt/CacheModule.ts @@ -1,4 +1,4 @@ const ptBR = { - "REFERENCE_MUTATE_ERROR": "Erro ao tentar alterar uma referência de %s" -} -export default ptBR; \ No newline at end of file + REFERENCE_MUTATE_ERROR: 'Erro ao tentar alterar uma referência de %s', +}; +export default ptBR; diff --git a/packages/nestjs-cache/src/services/cache.service.spec.ts b/packages/nestjs-cache/src/services/cache.service.spec.ts index 7a3a3e0c..ca0ef085 100644 --- a/packages/nestjs-cache/src/services/cache.service.spec.ts +++ b/packages/nestjs-cache/src/services/cache.service.spec.ts @@ -51,9 +51,9 @@ describe('CacheService', () => { service = new CacheService({ testAssignment: repo }, settings); }); - afterEach(() => { + afterEach(() => { I18n.reset(); - }) + }); describe(CacheService.prototype.create, () => { it('should create a cache entry', async () => { @@ -154,14 +154,14 @@ describe('CacheService', () => { options: { initImmediate: false, fallbackLng: 'pt', - } + }, }); // load local translations I18n.addTranslations(LOCALES); // load typeorm translations initTypeOrmCommonTranslation(); - + // need to call this to load the translations const assignment: ReferenceAssignment = 'testAssignment'; @@ -170,7 +170,7 @@ describe('CacheService', () => { const t = () => service.update(assignment, cacheDto, queryOptions); await expect(t).rejects.toThrowError( - 'Erro ao tentar alterar uma referência de undefined' + 'Erro ao tentar alterar uma referência de undefined', ); }); }); diff --git a/packages/nestjs-cache/src/services/cache.service.ts b/packages/nestjs-cache/src/services/cache.service.ts index db610a6e..481b3b88 100644 --- a/packages/nestjs-cache/src/services/cache.service.ts +++ b/packages/nestjs-cache/src/services/cache.service.ts @@ -113,10 +113,7 @@ export class CacheService implements CacheServiceInterface { expirationDate, }); } catch (e) { - throw new ReferenceMutateException( - assignmentRepo.metadata.targetName, - e, - ); + throw new ReferenceMutateException(assignmentRepo.metadata.targetName, e); } } diff --git a/packages/typeorm-common/src/constants.ts b/packages/typeorm-common/src/constants.ts index f6b9f439..5ae1a488 100644 --- a/packages/typeorm-common/src/constants.ts +++ b/packages/typeorm-common/src/constants.ts @@ -1,4 +1,4 @@ export const REFERENCE_ID_NO_MATCH = 'REFERENCE_ID_NO_MATCH'; export const REFERENCE_LOOKUP_ERROR = 'REFERENCE_LOOKUP_ERROR'; export const REFERENCE_MUTATE_ERROR = 'REFERENCE_MUTATE_ERROR'; -export const REFERENCE_VALIDATION_ERROR = 'REFERENCE_VALIDATION_ERROR'; \ No newline at end of file +export const REFERENCE_VALIDATION_ERROR = 'REFERENCE_VALIDATION_ERROR'; diff --git a/packages/typeorm-common/src/exceptions/reference-id-no-match.exception.ts b/packages/typeorm-common/src/exceptions/reference-id-no-match.exception.ts index 979f48f1..737d6fc7 100644 --- a/packages/typeorm-common/src/exceptions/reference-id-no-match.exception.ts +++ b/packages/typeorm-common/src/exceptions/reference-id-no-match.exception.ts @@ -14,17 +14,18 @@ export class ReferenceIdNoMatchException id: ReferenceId; }; - constructor( - entityName: string, - id: ReferenceId, - message?: string, - ) { - super(format(message - ?? t({ - key: REFERENCE_ID_NO_MATCH, - defaultMessage: 'No match for %s reference id %s.' - }) - , entityName, id)); + constructor(entityName: string, id: ReferenceId, message?: string) { + super( + format( + message ?? + t({ + key: REFERENCE_ID_NO_MATCH, + defaultMessage: 'No match for %s reference id %s.', + }), + entityName, + id, + ), + ); this.context = { entityName, id, diff --git a/packages/typeorm-common/src/exceptions/reference-lookup.exception.ts b/packages/typeorm-common/src/exceptions/reference-lookup.exception.ts index 31cba0d7..8d8198ec 100644 --- a/packages/typeorm-common/src/exceptions/reference-lookup.exception.ts +++ b/packages/typeorm-common/src/exceptions/reference-lookup.exception.ts @@ -3,7 +3,6 @@ import { ExceptionInterface, NotAnErrorException } from '@concepta/ts-core'; import { t } from '@concepta/i18n'; import { REFERENCE_LOOKUP_ERROR } from '../constants'; - export class ReferenceLookupException extends Error implements ExceptionInterface @@ -15,17 +14,17 @@ export class ReferenceLookupException originalError: Error; }; - constructor( - entityName: string, - originalError: unknown, - message?: string, - ) { - super(format(message - ?? t({ - key: REFERENCE_LOOKUP_ERROR, - defaultMessage: 'Error while trying to lookup a %s reference' - }) - , entityName)); + constructor(entityName: string, originalError: unknown, message?: string) { + super( + format( + message ?? + t({ + key: REFERENCE_LOOKUP_ERROR, + defaultMessage: 'Error while trying to lookup a %s reference', + }), + entityName, + ), + ); this.context = { entityName, originalError: diff --git a/packages/typeorm-common/src/exceptions/reference-mutate.exception.ts b/packages/typeorm-common/src/exceptions/reference-mutate.exception.ts index c215a1c3..5f080f65 100644 --- a/packages/typeorm-common/src/exceptions/reference-mutate.exception.ts +++ b/packages/typeorm-common/src/exceptions/reference-mutate.exception.ts @@ -14,17 +14,18 @@ export class ReferenceMutateException originalError: Error; }; - constructor( - entityName: string, - originalError: unknown, - message?: string, - ) { - super(format(message - ?? t({ - key: REFERENCE_MUTATE_ERROR, - defaultMessage: 'Error Default while trying to mutate a %s reference' - }) - , entityName)); + constructor(entityName: string, originalError: unknown, message?: string) { + super( + format( + message ?? + t({ + key: REFERENCE_MUTATE_ERROR, + defaultMessage: + 'Error Default while trying to mutate a %s reference', + }), + entityName, + ), + ); this.context = { entityName, originalError: diff --git a/packages/typeorm-common/src/exceptions/reference-validation.exception.ts b/packages/typeorm-common/src/exceptions/reference-validation.exception.ts index 7be7d1c8..3af50619 100644 --- a/packages/typeorm-common/src/exceptions/reference-validation.exception.ts +++ b/packages/typeorm-common/src/exceptions/reference-validation.exception.ts @@ -20,12 +20,16 @@ export class ReferenceValidationException validationErrors: ValidationError[], message?: string, ) { - super(format(message - ?? t({ - key: REFERENCE_VALIDATION_ERROR, - defaultMessage: 'Data for the %s reference is not valid', - }) - , entityName)); + super( + format( + message ?? + t({ + key: REFERENCE_VALIDATION_ERROR, + defaultMessage: 'Data for the %s reference is not valid', + }), + entityName, + ), + ); this.context = { entityName, validationErrors, diff --git a/packages/typeorm-common/src/index.ts b/packages/typeorm-common/src/index.ts index e2c243d3..c3985e26 100644 --- a/packages/typeorm-common/src/index.ts +++ b/packages/typeorm-common/src/index.ts @@ -3,7 +3,7 @@ import LOCALES from './locales'; export const initTypeOrmCommonTranslation = () => { I18n.addTranslations(LOCALES); -} +}; // interfaces export { QueryOptionsInterface } from './interfaces/query-options.interface'; export { SafeTransactionOptionsInterface } from './interfaces/safe-transaction-options.interface'; diff --git a/packages/typeorm-common/src/locales/en/translation.ts b/packages/typeorm-common/src/locales/en/translation.ts index e7c02f22..724868c2 100644 --- a/packages/typeorm-common/src/locales/en/translation.ts +++ b/packages/typeorm-common/src/locales/en/translation.ts @@ -1,7 +1,7 @@ const enUS = { - 'REFERENCE_MUTATE_ERROR': 'Error while trying to mutate a %s reference', - 'REFERENCE_LOOKUP_ERROR': 'Error while trying to lookup a %s reference', - 'REFERENCE_ID_NO_MATCH': 'No match for %s reference id %s.', - 'REFERENCE_VALIDATION_ERROR': 'Data for the %s reference is not valid' -} + REFERENCE_MUTATE_ERROR: 'Error while trying to mutate a %s reference', + REFERENCE_LOOKUP_ERROR: 'Error while trying to lookup a %s reference', + REFERENCE_ID_NO_MATCH: 'No match for %s reference id %s.', + REFERENCE_VALIDATION_ERROR: 'Data for the %s reference is not valid', +}; export default enUS; diff --git a/packages/typeorm-common/src/locales/index.ts b/packages/typeorm-common/src/locales/index.ts index 4150e3fb..37d63a4c 100644 --- a/packages/typeorm-common/src/locales/index.ts +++ b/packages/typeorm-common/src/locales/index.ts @@ -1,14 +1,17 @@ -import { LocalesInterface } from "@concepta/i18n"; -import enUS from "./en/translation"; -import ptBR from "./pt/translation"; +import { LocalesInterface } from '@concepta/i18n'; +import enUS from './en/translation'; +import ptBR from './pt/translation'; // TODO: should we keep this under default namespace? -const LOCALES: LocalesInterface[] = [{ - language: 'en', - resource: enUS, -}, { - language: 'pt', - resource: ptBR, -}] +const LOCALES: LocalesInterface[] = [ + { + language: 'en', + resource: enUS, + }, + { + language: 'pt', + resource: ptBR, + }, +]; -export default LOCALES; \ No newline at end of file +export default LOCALES; diff --git a/packages/typeorm-common/src/locales/pt/translation.ts b/packages/typeorm-common/src/locales/pt/translation.ts index 3161109d..0e024675 100644 --- a/packages/typeorm-common/src/locales/pt/translation.ts +++ b/packages/typeorm-common/src/locales/pt/translation.ts @@ -1,7 +1,8 @@ const ptBR = { - "REFERENCE_MUTATE_ERROR": "Erro ao tentar alterar uma referência de %s", - 'REFERENCE_LOOKUP_ERROR': 'Erro ao tentar procurar uma referência de %s', - 'REFERENCE_ID_NO_MATCH': 'Nenhuma correspondência da referência %s para o id %s.', - 'REFERENCE_VALIDATION_ERROR': 'Os dados relacionados a %s não são válidos', -} -export default ptBR; \ No newline at end of file + REFERENCE_MUTATE_ERROR: 'Erro ao tentar alterar uma referência de %s', + REFERENCE_LOOKUP_ERROR: 'Erro ao tentar procurar uma referência de %s', + REFERENCE_ID_NO_MATCH: + 'Nenhuma correspondência da referência %s para o id %s.', + REFERENCE_VALIDATION_ERROR: 'Os dados relacionados a %s não são válidos', +}; +export default ptBR; diff --git a/packages/typeorm-common/src/services/mutate.service.spec.ts b/packages/typeorm-common/src/services/mutate.service.spec.ts index 5d9bf824..aec23334 100644 --- a/packages/typeorm-common/src/services/mutate.service.spec.ts +++ b/packages/typeorm-common/src/services/mutate.service.spec.ts @@ -31,8 +31,8 @@ describe(MutateService, () => { I18n.init({ options: { initImmediate: false, - } - }) + }, + }); const moduleFixture: TestingModule = await Test.createTestingModule({ imports: [AppModuleFixture], }).compile(); @@ -254,10 +254,10 @@ describe(MutateService, () => { }; await expect(t).rejects.toThrowError( - `No match for TestEntityFixture reference id ${WRONG_UUID}.` + `No match for TestEntityFixture reference id ${WRONG_UUID}.`, ); }); - + it('id does not match with translation pt', async () => { I18n.changeLanguage('pt'); initTypeOrmCommonTranslation(); @@ -268,7 +268,7 @@ describe(MutateService, () => { }; await expect(t).rejects.toThrowError( - `Nenhuma correspondência da referência TestEntityFixture para o id ${WRONG_UUID}.` + `Nenhuma correspondência da referência TestEntityFixture para o id ${WRONG_UUID}.`, ); }); @@ -282,7 +282,7 @@ describe(MutateService, () => { }; await expect(t).rejects.toThrowError( - `No match for TestEntityFixture reference id ${WRONG_UUID}.` + `No match for TestEntityFixture reference id ${WRONG_UUID}.`, ); }); diff --git a/yarn.lock b/yarn.lock index 0dad6392..e9699522 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3812,11 +3812,11 @@ __metadata: linkType: hard "@types/node@npm:*, @types/node@npm:>= 8, @types/node@npm:>=12": - version: 22.1.0 - resolution: "@types/node@npm:22.1.0" + version: 22.2.0 + resolution: "@types/node@npm:22.2.0" dependencies: undici-types: "npm:~6.13.0" - checksum: 10c0/553dafcb842b889c036d43b390d464e8ffcf3ca455ddd5b1a1ef98396381eafbeb0c112a15cc6bf9662b72bc25fc45efc4b6f604760e1e84c410f1b7936c488b + checksum: 10c0/c17900b34faecfec204f72970bd658d0c217aaf739c1bf7690c969465b6b26b77a8be1cd9ba735aadbd1dd20b5c3e4f406ec33528bf7c6eec90744886c5d5608 languageName: node linkType: hard From b6cdaac4efd38641c77d7df1ed92d1901c466d92 Mon Sep 17 00:00:00 2001 From: Thiago Ramalho Date: Fri, 9 Aug 2024 17:08:14 -0300 Subject: [PATCH 03/11] chore: update readme --- packages/i18n/README.md | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/packages/i18n/README.md b/packages/i18n/README.md index becd6bd0..a3ed88c2 100644 --- a/packages/i18n/README.md +++ b/packages/i18n/README.md @@ -17,22 +17,18 @@ An advanced i18n (internationalization) utility for managing multiple languages - [Installation](#installation) - [Basic Setup](#basic-setup) - [Example](#example) - - [Adding a Custom Backend Module](#adding-a-custom-backend-module) + - [Adding a Custom Backend Module](#adding-a-custom-backend-module) - [Changing Language Dynamically](#changing-language-dynamically) - [Adding Translations](#adding-translations) 2. [How-To Guides](#how-to-guides) - [Creating a Custom Translation Module](#creating-a-custom-translation-module) - [Handling Missing Translations](#handling-missing-translations) - - [Using i18n in Tests](#using-i18n-in-tests) 3. [Reference](#reference) - - [i18n API Methods](#i18n-api-methods) - - [Supported i18n Modules](#supported-i18n-modules) 4. [Explanation](#explanation) - [What is i18n?](#what-is-i18n) - [How i18n Module Works](#how-i18n-module-works) - [Benefits of Using i18n](#benefits-of-using-i18n) - [Common i18n Pitfalls](#common-i18n-pitfalls) - # Tutorials ## Getting Started with i18n @@ -96,7 +92,7 @@ console.log(t({ These are basic examples to help you get started with the i18n module. -### Adding a Custom Backend Module with typescript +### Adding a Custom Backend Module To add a custom backend module for fetching translations: ```json From 8ba875366bb9228aef62d9d7ce423adb7b8c3c11 Mon Sep 17 00:00:00 2001 From: Thiago Ramalho Date: Fri, 9 Aug 2024 17:09:20 -0300 Subject: [PATCH 04/11] chore: lint --- packages/i18n/README.md | 27 ++++++++++++++++++--------- 1 file changed, 18 insertions(+), 9 deletions(-) diff --git a/packages/i18n/README.md b/packages/i18n/README.md index a3ed88c2..26467fc2 100644 --- a/packages/i18n/README.md +++ b/packages/i18n/README.md @@ -17,7 +17,7 @@ An advanced i18n (internationalization) utility for managing multiple languages - [Installation](#installation) - [Basic Setup](#basic-setup) - [Example](#example) - - [Adding a Custom Backend Module](#adding-a-custom-backend-module) + - [Adding a Custom Backend Module](#adding-a-custom-backend-module) - [Changing Language Dynamically](#changing-language-dynamically) - [Adding Translations](#adding-translations) 2. [How-To Guides](#how-to-guides) @@ -29,6 +29,7 @@ An advanced i18n (internationalization) utility for managing multiple languages - [How i18n Module Works](#how-i18n-module-works) - [Benefits of Using i18n](#benefits-of-using-i18n) - [Common i18n Pitfalls](#common-i18n-pitfalls) + # Tutorials ## Getting Started with i18n @@ -73,6 +74,7 @@ export function initI18n() { export { t }; ``` + ```ts import { initI18n, t } from './i18nSetup'; @@ -88,6 +90,7 @@ console.log(t({ language: 'pt' })); ``` + ## Example These are basic examples to help you get started with the i18n module. @@ -95,18 +98,21 @@ These are basic examples to help you get started with the i18n module. ### Adding a Custom Backend Module To add a custom backend module for fetching translations: + ```json //./locales/en/translation.json { "hello": "Hi" } ``` + ```json //./locales/pt/translation.json { "hello": "Olá" } ``` + ```ts import I18NexFsBackend, { FsBackendOptions } from 'i18next-fs-backend'; import { I18n, t } from '@concepta/i18n'; @@ -163,21 +169,22 @@ console.log(t({ key: 'hello' })); ## Adding Translations -To add translations dynamically, you can initialize the I18n instance first and add -translation resources at a later time. This approach is useful in scenarios where -translations are fetched from an external source or need to be updated without +To add translations dynamically, you can initialize the I18n instance first and add +translation resources at a later time. This approach is useful in scenarios where +translations are fetched from an external source or need to be updated without reinitializing the entire i18n setup. This method is particularly beneficial in the following situations: -- **Fetching Translations from an API**: When translations are retrieved from a - remote server, you can initialize i18n once and update translations as they +- **Fetching Translations from an API**: When translations are retrieved from a + remote server, you can initialize i18n once and update translations as they become available. -- **Modular Applications**: In large applications with multiple modules, - translations can be added as each module is loaded, reducing the initial load +- **Modular Applications**: In large applications with multiple modules, + translations can be added as each module is loaded, reducing the initial load time. -- **Real-time Updates**: If your application supports real-time updates, you can +- **Real-time Updates**: If your application supports real-time updates, you can dynamically add new translations without disrupting the user experience. + ```ts //... I18n.init(); @@ -211,6 +218,7 @@ console.log(t({ language: 'fr' })); ``` + ## Creating a Custom Translation Module You can create custom translation modules to extend the i18n functionality. Here’s how: @@ -232,6 +240,7 @@ I18n.init({ ## Handling Missing Translations To handle missing translations gracefully: + ```ts import { I18n, t } from '@concepta/i18n'; I18n.translate({ From 365174d9947241006803895280e8c79fc79fb579 Mon Sep 17 00:00:00 2001 From: Thiago Ramalho Date: Mon, 12 Aug 2024 09:05:45 -0300 Subject: [PATCH 05/11] chore: update imports --- packages/i18n/src/index.ts | 2 +- packages/nestjs-cache/src/locales/index.ts | 5 +++-- packages/typeorm-common/src/locales/index.ts | 5 +++-- 3 files changed, 7 insertions(+), 5 deletions(-) diff --git a/packages/i18n/src/index.ts b/packages/i18n/src/index.ts index 9b6a6f4e..cca9e9ea 100644 --- a/packages/i18n/src/index.ts +++ b/packages/i18n/src/index.ts @@ -2,7 +2,7 @@ import { I18n } from './i18n'; import { TranslateParams } from './interfaces/i18n-translate-params.interface'; export { I18n } from './i18n'; -export { I18nLocalesInterface as LocalesInterface } from './interfaces/i18n-locales.interface'; +export { I18nLocalesInterface } from './interfaces/i18n-locales.interface'; export const t = (args: TranslateParams) => { return I18n.translate(args); diff --git a/packages/nestjs-cache/src/locales/index.ts b/packages/nestjs-cache/src/locales/index.ts index 5a6cc2c5..b4bfd530 100644 --- a/packages/nestjs-cache/src/locales/index.ts +++ b/packages/nestjs-cache/src/locales/index.ts @@ -1,8 +1,9 @@ -import { LocalesInterface } from '@concepta/i18n/dist/interfaces/i18n.interfaces'; + +import { I18nLocalesInterface } from '@concepta/i18n'; import enUS from './en/CacheModule'; import ptBR from './pt/CacheModule'; -const LOCALES: LocalesInterface[] = [ +const LOCALES: I18nLocalesInterface[] = [ { namespace: 'CacheModule', language: 'en', diff --git a/packages/typeorm-common/src/locales/index.ts b/packages/typeorm-common/src/locales/index.ts index 37d63a4c..0c40c7b2 100644 --- a/packages/typeorm-common/src/locales/index.ts +++ b/packages/typeorm-common/src/locales/index.ts @@ -1,9 +1,10 @@ -import { LocalesInterface } from '@concepta/i18n'; + +import { I18nLocalesInterface } from '@concepta/i18n'; import enUS from './en/translation'; import ptBR from './pt/translation'; // TODO: should we keep this under default namespace? -const LOCALES: LocalesInterface[] = [ +const LOCALES: I18nLocalesInterface[] = [ { language: 'en', resource: enUS, From 2d215427b607433b56526bee372b7db19417e8e0 Mon Sep 17 00:00:00 2001 From: Thiago Ramalho Date: Mon, 12 Aug 2024 09:37:10 -0300 Subject: [PATCH 06/11] chore: update reference --- packages/typeorm-common/tsconfig.json | 3 + yarn.lock | 91 ++++++++++++++++++++++----- 2 files changed, 79 insertions(+), 15 deletions(-) diff --git a/packages/typeorm-common/tsconfig.json b/packages/typeorm-common/tsconfig.json index bb399af7..6613d9a1 100644 --- a/packages/typeorm-common/tsconfig.json +++ b/packages/typeorm-common/tsconfig.json @@ -15,6 +15,9 @@ "references": [ { "path": "../nestjs-typeorm-ext" + }, + { + "path": "../i18n" } ] } diff --git a/yarn.lock b/yarn.lock index e9699522..1e3ab26d 100644 --- a/yarn.lock +++ b/yarn.lock @@ -5201,12 +5201,12 @@ __metadata: linkType: hard "avvio@npm:^8.2.1": - version: 8.3.2 - resolution: "avvio@npm:8.3.2" + version: 8.4.0 + resolution: "avvio@npm:8.4.0" dependencies: "@fastify/error": "npm:^3.3.0" fastq: "npm:^1.17.1" - checksum: 10c0/280767ca1259cac1e78fc75ee4f1f6a735914e045015b1ac4c0ce9c5b0933edcb7e9a31fb286a479e51e56562b352dcfa35941e6f555edd341ed5d355e36e4ac + checksum: 10c0/bea7f28e38b57755786852226f380ea087d572f8bbcfe14b59d1239551ef89cecc40229a6ac85e17af44c81a481d03280576586385e93d76bb9f2c5bc75c6067 languageName: node linkType: hard @@ -5871,7 +5871,7 @@ __metadata: languageName: node linkType: hard -"cheerio@npm:1.0.0-rc.12, cheerio@npm:^1.0.0-rc.12": +"cheerio@npm:1.0.0-rc.12": version: 1.0.0-rc.12 resolution: "cheerio@npm:1.0.0-rc.12" dependencies: @@ -5886,6 +5886,25 @@ __metadata: languageName: node linkType: hard +"cheerio@npm:^1.0.0-rc.12": + version: 1.0.0 + resolution: "cheerio@npm:1.0.0" + dependencies: + cheerio-select: "npm:^2.1.0" + dom-serializer: "npm:^2.0.0" + domhandler: "npm:^5.0.3" + domutils: "npm:^3.1.0" + encoding-sniffer: "npm:^0.2.0" + htmlparser2: "npm:^9.1.0" + parse5: "npm:^7.1.2" + parse5-htmlparser2-tree-adapter: "npm:^7.0.0" + parse5-parser-stream: "npm:^7.1.2" + undici: "npm:^6.19.5" + whatwg-mimetype: "npm:^4.0.0" + checksum: 10c0/d0e16925d9c36c879edfaef1c0244c866375a4c7b8d6ccd7ae0ad42da7d26263ea1a3c17b9a1aa5965918deeff2d40ac2e7223824f8e6eca972df3b81316a09f + languageName: node + linkType: hard + "chokidar@npm:3.5.3": version: 3.5.3 resolution: "chokidar@npm:3.5.3" @@ -7472,9 +7491,9 @@ __metadata: linkType: hard "electron-to-chromium@npm:^1.5.4": - version: 1.5.5 - resolution: "electron-to-chromium@npm:1.5.5" - checksum: 10c0/6e5e12f729a74a78d9a7386ea32039262cb8a2f4611ab346da1f162c270d0569194c72169042080a1017220835ed30ee2d77ca5ba13c1acaa5fa0d373fbc0ad5 + version: 1.5.6 + resolution: "electron-to-chromium@npm:1.5.6" + checksum: 10c0/1dfef4feaa9f4e5231b8b0697f1f55623eb2ffd263b50e6d4ff995a0927653997ba116640ae06786661f8d24c7b99fa6727c0796e9c60d748f473674d78ab31e languageName: node linkType: hard @@ -7527,6 +7546,16 @@ __metadata: languageName: node linkType: hard +"encoding-sniffer@npm:^0.2.0": + version: 0.2.0 + resolution: "encoding-sniffer@npm:0.2.0" + dependencies: + iconv-lite: "npm:^0.6.3" + whatwg-encoding: "npm:^3.1.1" + checksum: 10c0/b312e0d67f339bec44e021e5210ee8ee90d7b8f9975eb2c79a36fd467eb07709e88dcf62ee20f62ee0d74a13874307d99557852a2de9b448f1e3fb991fc68257 + languageName: node + linkType: hard + "encoding@npm:^0.1.11, encoding@npm:^0.1.13": version: 0.1.13 resolution: "encoding@npm:0.1.13" @@ -9972,7 +10001,7 @@ __metadata: languageName: node linkType: hard -"iconv-lite@npm:0.6.3, iconv-lite@npm:^0.6.2": +"iconv-lite@npm:0.6.3, iconv-lite@npm:^0.6.2, iconv-lite@npm:^0.6.3": version: 0.6.3 resolution: "iconv-lite@npm:0.6.3" dependencies: @@ -10012,9 +10041,9 @@ __metadata: linkType: hard "ignore@npm:^5.1.1, ignore@npm:^5.2.0, ignore@npm:^5.2.4, ignore@npm:~5.3.1": - version: 5.3.1 - resolution: "ignore@npm:5.3.1" - checksum: 10c0/703f7f45ffb2a27fb2c5a8db0c32e7dee66b33a225d28e8db4e1be6474795f606686a6e3bcc50e1aa12f2042db4c9d4a7d60af3250511de74620fbed052ea4cd + version: 5.3.2 + resolution: "ignore@npm:5.3.2" + checksum: 10c0/f9f652c957983634ded1e7f02da3b559a0d4cc210fca3792cb67f1b153623c9c42efdc1c4121af171e295444459fc4a9201101fb041b1104a3c000bccb188337 languageName: node linkType: hard @@ -14434,6 +14463,15 @@ __metadata: languageName: node linkType: hard +"parse5-parser-stream@npm:^7.1.2": + version: 7.1.2 + resolution: "parse5-parser-stream@npm:7.1.2" + dependencies: + parse5: "npm:^7.0.0" + checksum: 10c0/e236c61000d38ecad369e725a48506b051cebad8abb00e6d4e8bff7aa85c183820fcb45db1559cc90955bdbbdbd665ea94c41259594e74566fff411478dc7fcb + languageName: node + linkType: hard + "parse5@npm:6.0.1, parse5@npm:^6.0.1": version: 6.0.1 resolution: "parse5@npm:6.0.1" @@ -14448,7 +14486,7 @@ __metadata: languageName: node linkType: hard -"parse5@npm:^7.0.0": +"parse5@npm:^7.0.0, parse5@npm:^7.1.2": version: 7.1.2 resolution: "parse5@npm:7.1.2" dependencies: @@ -18009,11 +18047,11 @@ __metadata: linkType: hard "uglify-js@npm:^3.1.4, uglify-js@npm:^3.5.1": - version: 3.19.1 - resolution: "uglify-js@npm:3.19.1" + version: 3.19.2 + resolution: "uglify-js@npm:3.19.2" bin: uglifyjs: bin/uglifyjs - checksum: 10c0/7609ab3f10d54de5ef014770f845c747266a969e9092d2284ca0ba18f10a4488208c1491bd8b52bd4c40cf6687b47a77c495f08e4a625babcdd57f58e34a3976 + checksum: 10c0/51dbe1304a91cac5daa01f6a2d4ecd545fab7b7d0625e11590b923e95a6d2263b3481dcea974abfc0282b33d2c76f74f1196a992df07eae0847175bc39ea45bb languageName: node linkType: hard @@ -18066,6 +18104,13 @@ __metadata: languageName: node linkType: hard +"undici@npm:^6.19.5": + version: 6.19.7 + resolution: "undici@npm:6.19.7" + checksum: 10c0/801d1e66d5bccdd3fcc9ecf1c95b83a593e4867b89e21ed725e35bd4d572b3d3ce1d7feab2a4f2046f65923de70bfafb69ac148c633d1ab30a948d6fec24475a + languageName: node + linkType: hard + "union-value@npm:^1.0.0": version: 1.0.1 resolution: "union-value@npm:1.0.1" @@ -18526,6 +18571,15 @@ __metadata: languageName: node linkType: hard +"whatwg-encoding@npm:^3.1.1": + version: 3.1.1 + resolution: "whatwg-encoding@npm:3.1.1" + dependencies: + iconv-lite: "npm:0.6.3" + checksum: 10c0/273b5f441c2f7fda3368a496c3009edbaa5e43b71b09728f90425e7f487e5cef9eb2b846a31bd760dd8077739c26faf6b5ca43a5f24033172b003b72cf61a93e + languageName: node + linkType: hard + "whatwg-mimetype@npm:^2.3.0": version: 2.3.0 resolution: "whatwg-mimetype@npm:2.3.0" @@ -18533,6 +18587,13 @@ __metadata: languageName: node linkType: hard +"whatwg-mimetype@npm:^4.0.0": + version: 4.0.0 + resolution: "whatwg-mimetype@npm:4.0.0" + checksum: 10c0/a773cdc8126b514d790bdae7052e8bf242970cebd84af62fb2f35a33411e78e981f6c0ab9ed1fe6ec5071b09d5340ac9178e05b52d35a9c4bcf558ba1b1551df + languageName: node + linkType: hard + "whatwg-url@npm:^5.0.0": version: 5.0.0 resolution: "whatwg-url@npm:5.0.0" From fc62baa13ca1350e242a2fbca0844044f5ac7d9e Mon Sep 17 00:00:00 2001 From: Thiago Ramalho Date: Mon, 12 Aug 2024 09:44:40 -0300 Subject: [PATCH 07/11] chore: lint --- packages/nestjs-cache/src/locales/index.ts | 1 - packages/typeorm-common/src/locales/index.ts | 1 - 2 files changed, 2 deletions(-) diff --git a/packages/nestjs-cache/src/locales/index.ts b/packages/nestjs-cache/src/locales/index.ts index b4bfd530..a349c338 100644 --- a/packages/nestjs-cache/src/locales/index.ts +++ b/packages/nestjs-cache/src/locales/index.ts @@ -1,4 +1,3 @@ - import { I18nLocalesInterface } from '@concepta/i18n'; import enUS from './en/CacheModule'; import ptBR from './pt/CacheModule'; diff --git a/packages/typeorm-common/src/locales/index.ts b/packages/typeorm-common/src/locales/index.ts index 0c40c7b2..91af10c3 100644 --- a/packages/typeorm-common/src/locales/index.ts +++ b/packages/typeorm-common/src/locales/index.ts @@ -1,4 +1,3 @@ - import { I18nLocalesInterface } from '@concepta/i18n'; import enUS from './en/translation'; import ptBR from './pt/translation'; From 4917a5fdfa520b643823310eb05d0f043d1a75a8 Mon Sep 17 00:00:00 2001 From: Thiago Ramalho Date: Mon, 12 Aug 2024 09:51:50 -0300 Subject: [PATCH 08/11] chore: md lint --- packages/i18n/README.md | 38 ++++++++++++++++++++++++-------------- 1 file changed, 24 insertions(+), 14 deletions(-) diff --git a/packages/i18n/README.md b/packages/i18n/README.md index 26467fc2..98b5c9a0 100644 --- a/packages/i18n/README.md +++ b/packages/i18n/README.md @@ -44,7 +44,9 @@ npm install @concepta/i18n ## Basic Setup -To set up the i18n module, you need to initialize it with your desired settings and add your translations. For more detailed settings documentation, check [i18next](https://www.i18next.com/). +To set up the i18n module, you need to initialize it with your desired +settings and add your translations. For more detailed settings documentation, +check [i18next](https://www.i18next.com/). ```ts // i18nSetup.js @@ -69,7 +71,8 @@ export function initI18n() { } }); // initialized and ready to go! - // i18n is already initialized, because the translation resources were passed via init function + // i18n is already initialized, because the translation resources were + // passed via init function } export { t }; @@ -169,10 +172,10 @@ console.log(t({ key: 'hello' })); ## Adding Translations -To add translations dynamically, you can initialize the I18n instance first and add -translation resources at a later time. This approach is useful in scenarios where -translations are fetched from an external source or need to be updated without -reinitializing the entire i18n setup. +To add translations dynamically, you can initialize the I18n instance first +and addtranslation resources at a later time. This approach is useful in +scenarios where translations are fetched from an external source or need to be +updated without reinitializing the entire i18n setup. This method is particularly beneficial in the following situations: @@ -182,8 +185,8 @@ This method is particularly beneficial in the following situations: - **Modular Applications**: In large applications with multiple modules, translations can be added as each module is loaded, reducing the initial load time. -- **Real-time Updates**: If your application supports real-time updates, you can - dynamically add new translations without disrupting the user experience. +- **Real-time Updates**: If your application supports real-time updates, you + can dynamically add new translations without disrupting the user experience. ```ts //... @@ -221,7 +224,8 @@ console.log(t({ ## Creating a Custom Translation Module -You can create custom translation modules to extend the i18n functionality. Here’s how: +You can create custom translation modules to extend the i18n functionality. +Here’s how: ### **Define the Module**: Add modules on initialization @@ -265,19 +269,25 @@ Please check (API Reference)[] for more information. ## What is i18n? -i18n, short for internationalization, refers to the process of designing software applications that can be adapted to various languages and regions without engineering changes. +i18n, short for internationalization, refers to the process of designing +software applications that can be adapted to various languages and regions +without engineering changes. ## How i18n Module Works -The i18n module in this package provides a wrapper around i18next, allowing seamless integration of translation modules and dynamic language switching. +The i18n module in this package provides a wrapper around i18next, allowing +seamless integration of translation modules and dynamic language switching. ### Benefits of Using i18n - **Flexibility**: Supports multiple languages and dynamic translation updates. -- **Modularity**: Easily extendable with custom modules for translation management. +- **Modularity**: Easily extendable with custom modules for translation +management. - **Ease of Use**: Simple API to manage translations and switch languages. ### Common i18n Pitfalls -- **Missing Translations**: Ensure all necessary translations are provided for each language. -- **Performance Overheads**: Avoid loading unnecessary translation files by using modular imports. +- **Missing Translations**: Ensure all necessary translations are provided for +each language. +- **Performance Overheads**: Avoid loading unnecessary translation files by +using modular imports. From ddac5c85776373a1146b9970a61eb783f0ad1ff2 Mon Sep 17 00:00:00 2001 From: Thiago Ramalho Date: Thu, 15 Aug 2024 10:21:05 -0300 Subject: [PATCH 09/11] chore: update yarn --- yarn.lock | 504 ++++++++++++++++++++++++++++++------------------------ 1 file changed, 280 insertions(+), 224 deletions(-) diff --git a/yarn.lock b/yarn.lock index 5d18cbb7..cd9aa479 100644 --- a/yarn.lock +++ b/yarn.lock @@ -73,86 +73,58 @@ __metadata: languageName: node linkType: hard -"@babel/compat-data@npm:^7.24.8": - version: 7.24.9 - resolution: "@babel/compat-data@npm:7.24.9" - checksum: 10c0/95a69c9ed00ae78b4921f33403e9b35518e6139a0c46af763c65dea160720cb57c6cc23f7d30249091a0248335b0e39de5c8dfa8e7877c830e44561e0bdc1254 +"@babel/compat-data@npm:^7.25.2": + version: 7.25.2 + resolution: "@babel/compat-data@npm:7.25.2" + checksum: 10c0/5bf1f14d6e5f0d37c19543e99209ff4a94bb97915e1ce01e5334a144aa08cd56b6e62ece8135dac77e126723d63d4d4b96fc603a12c43b88c28f4b5e070270c5 languageName: node linkType: hard "@babel/core@npm:^7.1.0, @babel/core@npm:^7.12.3, @babel/core@npm:^7.7.2, @babel/core@npm:^7.8.0": - version: 7.24.9 - resolution: "@babel/core@npm:7.24.9" + version: 7.25.2 + resolution: "@babel/core@npm:7.25.2" dependencies: "@ampproject/remapping": "npm:^2.2.0" "@babel/code-frame": "npm:^7.24.7" - "@babel/generator": "npm:^7.24.9" - "@babel/helper-compilation-targets": "npm:^7.24.8" - "@babel/helper-module-transforms": "npm:^7.24.9" - "@babel/helpers": "npm:^7.24.8" - "@babel/parser": "npm:^7.24.8" - "@babel/template": "npm:^7.24.7" - "@babel/traverse": "npm:^7.24.8" - "@babel/types": "npm:^7.24.9" + "@babel/generator": "npm:^7.25.0" + "@babel/helper-compilation-targets": "npm:^7.25.2" + "@babel/helper-module-transforms": "npm:^7.25.2" + "@babel/helpers": "npm:^7.25.0" + "@babel/parser": "npm:^7.25.0" + "@babel/template": "npm:^7.25.0" + "@babel/traverse": "npm:^7.25.2" + "@babel/types": "npm:^7.25.2" convert-source-map: "npm:^2.0.0" debug: "npm:^4.1.0" gensync: "npm:^1.0.0-beta.2" json5: "npm:^2.2.3" semver: "npm:^6.3.1" - checksum: 10c0/e104ec6efbf099f55184933e9ab078eb5821c792ddfef3e9c6561986ec4ff103f5c11e3d7d6e5e8929e50e2c58db1cc80e5b6f14b530335b6622095ec4b4124c + checksum: 10c0/a425fa40e73cb72b6464063a57c478bc2de9dbcc19c280f1b55a3d88b35d572e87e8594e7d7b4880331addb6faef641bbeb701b91b41b8806cd4deae5d74f401 languageName: node linkType: hard -"@babel/generator@npm:^7.24.8, @babel/generator@npm:^7.24.9, @babel/generator@npm:^7.7.2": - version: 7.24.10 - resolution: "@babel/generator@npm:7.24.10" +"@babel/generator@npm:^7.25.0, @babel/generator@npm:^7.7.2": + version: 7.25.0 + resolution: "@babel/generator@npm:7.25.0" dependencies: - "@babel/types": "npm:^7.24.9" + "@babel/types": "npm:^7.25.0" "@jridgewell/gen-mapping": "npm:^0.3.5" "@jridgewell/trace-mapping": "npm:^0.3.25" jsesc: "npm:^2.5.1" - checksum: 10c0/abcfd75f625aecc87ce6036ef788b12723fd3c46530df1130d1f00d18e48b462849ddaeef8b1a02bfdcb6e28956389a98c5729dad1c3c5448307dacb6c959f29 + checksum: 10c0/d0e2dfcdc8bdbb5dded34b705ceebf2e0bc1b06795a1530e64fb6a3ccf313c189db7f60c1616effae48114e1a25adc75855bc4496f3779a396b3377bae718ce7 languageName: node linkType: hard -"@babel/helper-compilation-targets@npm:^7.24.8": - version: 7.24.8 - resolution: "@babel/helper-compilation-targets@npm:7.24.8" +"@babel/helper-compilation-targets@npm:^7.25.2": + version: 7.25.2 + resolution: "@babel/helper-compilation-targets@npm:7.25.2" dependencies: - "@babel/compat-data": "npm:^7.24.8" + "@babel/compat-data": "npm:^7.25.2" "@babel/helper-validator-option": "npm:^7.24.8" browserslist: "npm:^4.23.1" lru-cache: "npm:^5.1.1" semver: "npm:^6.3.1" - checksum: 10c0/2885c44ef6aaf82b7e4352b30089bb09fbe08ed5ec24eb452c2bdc3c021e2a65ab412f74b3d67ec1398da0356c730b33a2ceca1d67d34c85080d31ca6efa9aec - languageName: node - linkType: hard - -"@babel/helper-environment-visitor@npm:^7.24.7": - version: 7.24.7 - resolution: "@babel/helper-environment-visitor@npm:7.24.7" - dependencies: - "@babel/types": "npm:^7.24.7" - checksum: 10c0/36ece78882b5960e2d26abf13cf15ff5689bf7c325b10a2895a74a499e712de0d305f8d78bb382dd3c05cfba7e47ec98fe28aab5674243e0625cd38438dd0b2d - languageName: node - linkType: hard - -"@babel/helper-function-name@npm:^7.24.7": - version: 7.24.7 - resolution: "@babel/helper-function-name@npm:7.24.7" - dependencies: - "@babel/template": "npm:^7.24.7" - "@babel/types": "npm:^7.24.7" - checksum: 10c0/e5e41e6cf86bd0f8bf272cbb6e7c5ee0f3e9660414174435a46653efba4f2479ce03ce04abff2aa2ef9359cf057c79c06cb7b134a565ad9c0e8a50dcdc3b43c4 - languageName: node - linkType: hard - -"@babel/helper-hoist-variables@npm:^7.24.7": - version: 7.24.7 - resolution: "@babel/helper-hoist-variables@npm:7.24.7" - dependencies: - "@babel/types": "npm:^7.24.7" - checksum: 10c0/19ee37563bbd1219f9d98991ad0e9abef77803ee5945fd85aa7aa62a67c69efca9a801696a1b58dda27f211e878b3327789e6fd2a6f6c725ccefe36774b5ce95 + checksum: 10c0/de10e986b5322c9f807350467dc845ec59df9e596a5926a3b5edbb4710d8e3b8009d4396690e70b88c3844fe8ec4042d61436dd4b92d1f5f75655cf43ab07e99 languageName: node linkType: hard @@ -166,18 +138,17 @@ __metadata: languageName: node linkType: hard -"@babel/helper-module-transforms@npm:^7.24.9": - version: 7.24.9 - resolution: "@babel/helper-module-transforms@npm:7.24.9" +"@babel/helper-module-transforms@npm:^7.25.2": + version: 7.25.2 + resolution: "@babel/helper-module-transforms@npm:7.25.2" dependencies: - "@babel/helper-environment-visitor": "npm:^7.24.7" "@babel/helper-module-imports": "npm:^7.24.7" "@babel/helper-simple-access": "npm:^7.24.7" - "@babel/helper-split-export-declaration": "npm:^7.24.7" "@babel/helper-validator-identifier": "npm:^7.24.7" + "@babel/traverse": "npm:^7.25.2" peerDependencies: "@babel/core": ^7.0.0 - checksum: 10c0/e27bca43bc113731ee4f2b33a4c5bf9c7eebf4d64487b814c305cbd5feb272c29fcd3d79634ba03131ade171e5972bc7ede8dbc83ba0deb02f1e62d318c87770 + checksum: 10c0/adaa15970ace0aee5934b5a633789b5795b6229c6a9cf3e09a7e80aa33e478675eee807006a862aa9aa517935d81f88a6db8a9f5936e3a2a40ec75f8062bc329 languageName: node linkType: hard @@ -198,15 +169,6 @@ __metadata: languageName: node linkType: hard -"@babel/helper-split-export-declaration@npm:^7.24.7": - version: 7.24.7 - resolution: "@babel/helper-split-export-declaration@npm:7.24.7" - dependencies: - "@babel/types": "npm:^7.24.7" - checksum: 10c0/0254577d7086bf09b01bbde98f731d4fcf4b7c3fa9634fdb87929801307c1f6202a1352e3faa5492450fa8da4420542d44de604daf540704ff349594a78184f6 - languageName: node - linkType: hard - "@babel/helper-string-parser@npm:^7.24.8": version: 7.24.8 resolution: "@babel/helper-string-parser@npm:7.24.8" @@ -228,13 +190,13 @@ __metadata: languageName: node linkType: hard -"@babel/helpers@npm:^7.24.8": - version: 7.24.8 - resolution: "@babel/helpers@npm:7.24.8" +"@babel/helpers@npm:^7.25.0": + version: 7.25.0 + resolution: "@babel/helpers@npm:7.25.0" dependencies: - "@babel/template": "npm:^7.24.7" - "@babel/types": "npm:^7.24.8" - checksum: 10c0/42b8939b0a0bf72d6df9721973eb0fd7cd48f42641c5c9c740916397faa586255c06d36c6e6a7e091860723096281c620f6ffaee0011a3bb254a6f5475d89a12 + "@babel/template": "npm:^7.25.0" + "@babel/types": "npm:^7.25.0" + checksum: 10c0/b7fe007fc4194268abf70aa3810365085e290e6528dcb9fbbf7a765d43c74b6369ce0f99c5ccd2d44c413853099daa449c9a0123f0b212ac8d18643f2e8174b8 languageName: node linkType: hard @@ -250,12 +212,14 @@ __metadata: languageName: node linkType: hard -"@babel/parser@npm:^7.1.0, @babel/parser@npm:^7.14.7, @babel/parser@npm:^7.20.7, @babel/parser@npm:^7.24.7, @babel/parser@npm:^7.24.8, @babel/parser@npm:^7.6.0, @babel/parser@npm:^7.9.6": - version: 7.24.8 - resolution: "@babel/parser@npm:7.24.8" +"@babel/parser@npm:^7.1.0, @babel/parser@npm:^7.14.7, @babel/parser@npm:^7.20.7, @babel/parser@npm:^7.25.0, @babel/parser@npm:^7.25.3, @babel/parser@npm:^7.6.0, @babel/parser@npm:^7.9.6": + version: 7.25.3 + resolution: "@babel/parser@npm:7.25.3" + dependencies: + "@babel/types": "npm:^7.25.2" bin: parser: ./bin/babel-parser.js - checksum: 10c0/ce69671de8fa6f649abf849be262707ac700b573b8b1ce1893c66cc6cd76aeb1294a19e8c290b0eadeb2f47d3f413a2e57a281804ffbe76bfb9fa50194cf3c52 + checksum: 10c0/874b01349aedb805d6694f867a752fdc7469778fad76aca4548d2cc6ce96087c3ba5fb917a6f8d05d2d1a74aae309b5f50f1a4dba035f5a2c9fcfe6e106d2c4e languageName: node linkType: hard @@ -281,7 +245,7 @@ __metadata: languageName: node linkType: hard -"@babel/plugin-syntax-class-properties@npm:^7.8.3": +"@babel/plugin-syntax-class-properties@npm:^7.12.13": version: 7.12.13 resolution: "@babel/plugin-syntax-class-properties@npm:7.12.13" dependencies: @@ -292,7 +256,29 @@ __metadata: languageName: node linkType: hard -"@babel/plugin-syntax-import-meta@npm:^7.8.3": +"@babel/plugin-syntax-class-static-block@npm:^7.14.5": + version: 7.14.5 + resolution: "@babel/plugin-syntax-class-static-block@npm:7.14.5" + dependencies: + "@babel/helper-plugin-utils": "npm:^7.14.5" + peerDependencies: + "@babel/core": ^7.0.0-0 + checksum: 10c0/4464bf9115f4a2d02ce1454411baf9cfb665af1da53709c5c56953e5e2913745b0fcce82982a00463d6facbdd93445c691024e310b91431a1e2f024b158f6371 + languageName: node + linkType: hard + +"@babel/plugin-syntax-import-attributes@npm:^7.24.7": + version: 7.24.7 + resolution: "@babel/plugin-syntax-import-attributes@npm:7.24.7" + dependencies: + "@babel/helper-plugin-utils": "npm:^7.24.7" + peerDependencies: + "@babel/core": ^7.0.0-0 + checksum: 10c0/eccc54d0f03c96d0eec7a6e2fa124dadbc7298345b62ffc4238f173308c4325b5598f139695ff05a95cf78412ef6903599e4b814496612bf39aad4715a16375b + languageName: node + linkType: hard + +"@babel/plugin-syntax-import-meta@npm:^7.10.4": version: 7.10.4 resolution: "@babel/plugin-syntax-import-meta@npm:7.10.4" dependencies: @@ -314,7 +300,7 @@ __metadata: languageName: node linkType: hard -"@babel/plugin-syntax-logical-assignment-operators@npm:^7.8.3": +"@babel/plugin-syntax-logical-assignment-operators@npm:^7.10.4": version: 7.10.4 resolution: "@babel/plugin-syntax-logical-assignment-operators@npm:7.10.4" dependencies: @@ -336,7 +322,7 @@ __metadata: languageName: node linkType: hard -"@babel/plugin-syntax-numeric-separator@npm:^7.8.3": +"@babel/plugin-syntax-numeric-separator@npm:^7.10.4": version: 7.10.4 resolution: "@babel/plugin-syntax-numeric-separator@npm:7.10.4" dependencies: @@ -380,7 +366,18 @@ __metadata: languageName: node linkType: hard -"@babel/plugin-syntax-top-level-await@npm:^7.8.3": +"@babel/plugin-syntax-private-property-in-object@npm:^7.14.5": + version: 7.14.5 + resolution: "@babel/plugin-syntax-private-property-in-object@npm:7.14.5" + dependencies: + "@babel/helper-plugin-utils": "npm:^7.14.5" + peerDependencies: + "@babel/core": ^7.0.0-0 + checksum: 10c0/69822772561706c87f0a65bc92d0772cea74d6bc0911537904a676d5ff496a6d3ac4e05a166d8125fce4a16605bace141afc3611074e170a994e66e5397787f3 + languageName: node + linkType: hard + +"@babel/plugin-syntax-top-level-await@npm:^7.14.5": version: 7.14.5 resolution: "@babel/plugin-syntax-top-level-await@npm:7.14.5" dependencies: @@ -402,52 +399,49 @@ __metadata: languageName: node linkType: hard -"@babel/runtime@npm:^7.23.9": - version: 7.24.8 - resolution: "@babel/runtime@npm:7.24.8" +"@babel/runtime@npm:^7.23.2, @babel/runtime@npm:^7.23.9": + version: 7.25.0 + resolution: "@babel/runtime@npm:7.25.0" dependencies: regenerator-runtime: "npm:^0.14.0" - checksum: 10c0/f24b30af6b3ecae19165b3b032f9bc37b2d1769677bd63b69a6f81061967cfc847aa822518402ea6616b1d301d7eb46986b99c9f69cdb5880834fca2e6b34881 + checksum: 10c0/bd3faf246170826cef2071a94d7b47b49d532351360ecd17722d03f6713fd93a3eb3dbd9518faa778d5e8ccad7392a7a604e56bd37aaad3f3aa68d619ccd983d languageName: node linkType: hard -"@babel/template@npm:^7.24.7, @babel/template@npm:^7.3.3": - version: 7.24.7 - resolution: "@babel/template@npm:7.24.7" +"@babel/template@npm:^7.25.0, @babel/template@npm:^7.3.3": + version: 7.25.0 + resolution: "@babel/template@npm:7.25.0" dependencies: "@babel/code-frame": "npm:^7.24.7" - "@babel/parser": "npm:^7.24.7" - "@babel/types": "npm:^7.24.7" - checksum: 10c0/95b0b3ee80fcef685b7f4426f5713a855ea2cd5ac4da829b213f8fb5afe48a2a14683c2ea04d446dbc7f711c33c5cd4a965ef34dcbe5bc387c9e966b67877ae3 + "@babel/parser": "npm:^7.25.0" + "@babel/types": "npm:^7.25.0" + checksum: 10c0/4e31afd873215744c016e02b04f43b9fa23205d6d0766fb2e93eb4091c60c1b88897936adb895fb04e3c23de98dfdcbe31bc98daaa1a4e0133f78bb948e1209b languageName: node linkType: hard -"@babel/traverse@npm:^7.24.7, @babel/traverse@npm:^7.24.8, @babel/traverse@npm:^7.7.2": - version: 7.24.8 - resolution: "@babel/traverse@npm:7.24.8" +"@babel/traverse@npm:^7.24.7, @babel/traverse@npm:^7.25.2, @babel/traverse@npm:^7.7.2": + version: 7.25.3 + resolution: "@babel/traverse@npm:7.25.3" dependencies: "@babel/code-frame": "npm:^7.24.7" - "@babel/generator": "npm:^7.24.8" - "@babel/helper-environment-visitor": "npm:^7.24.7" - "@babel/helper-function-name": "npm:^7.24.7" - "@babel/helper-hoist-variables": "npm:^7.24.7" - "@babel/helper-split-export-declaration": "npm:^7.24.7" - "@babel/parser": "npm:^7.24.8" - "@babel/types": "npm:^7.24.8" + "@babel/generator": "npm:^7.25.0" + "@babel/parser": "npm:^7.25.3" + "@babel/template": "npm:^7.25.0" + "@babel/types": "npm:^7.25.2" debug: "npm:^4.3.1" globals: "npm:^11.1.0" - checksum: 10c0/67a5cc35824455cdb54fb9e196a44b3186283e29018a9c2331f51763921e18e891b3c60c283615a27540ec8eb4c8b89f41c237b91f732a7aa518b2eb7a0d434d + checksum: 10c0/4c8a1966fa90b53a783a4afd2fcdaa6ab1a912e6621dca9fcc6633e80ccb9491620e88caf73b537da4e16cefd537b548c87d7087868d5b0066414dea375c0e9b languageName: node linkType: hard -"@babel/types@npm:^7.0.0, @babel/types@npm:^7.20.7, @babel/types@npm:^7.24.7, @babel/types@npm:^7.24.8, @babel/types@npm:^7.24.9, @babel/types@npm:^7.3.3, @babel/types@npm:^7.6.1, @babel/types@npm:^7.8.3, @babel/types@npm:^7.9.6": - version: 7.24.9 - resolution: "@babel/types@npm:7.24.9" +"@babel/types@npm:^7.0.0, @babel/types@npm:^7.20.7, @babel/types@npm:^7.24.7, @babel/types@npm:^7.25.0, @babel/types@npm:^7.25.2, @babel/types@npm:^7.3.3, @babel/types@npm:^7.6.1, @babel/types@npm:^7.9.6": + version: 7.25.2 + resolution: "@babel/types@npm:7.25.2" dependencies: "@babel/helper-string-parser": "npm:^7.24.8" "@babel/helper-validator-identifier": "npm:^7.24.7" to-fast-properties: "npm:^2.0.0" - checksum: 10c0/4970b3481cab39c5c3fdb7c28c834df5c7049f3c7f43baeafe121bb05270ebf0da7c65b097abf314877f213baa591109c82204f30d66cdd46c22ece4a2f32415 + checksum: 10c0/e489435856be239f8cc1120c90a197e4c2865385121908e5edb7223cfdff3768cba18f489adfe0c26955d9e7bbb1fb10625bc2517505908ceb0af848989bd864 languageName: node linkType: hard @@ -675,6 +669,16 @@ __metadata: languageName: node linkType: hard +"@concepta/i18n@npm:^4.0.0-alpha.48, @concepta/i18n@workspace:packages/i18n": + version: 0.0.0-use.local + resolution: "@concepta/i18n@workspace:packages/i18n" + dependencies: + i18next: "npm:^23.12.2" + i18next-fs-backend: "npm:^2.3.1" + i18next-http-middleware: "npm:^3.6.0" + languageName: unknown + linkType: soft + "@concepta/nestjs-access-control@npm:^5.0.0-alpha.0, @concepta/nestjs-access-control@workspace:packages/nestjs-access-control": version: 0.0.0-use.local resolution: "@concepta/nestjs-access-control@workspace:packages/nestjs-access-control" @@ -859,6 +863,7 @@ __metadata: version: 0.0.0-use.local resolution: "@concepta/nestjs-cache@workspace:packages/nestjs-cache" dependencies: + "@concepta/i18n": "npm:^4.0.0-alpha.48" "@concepta/nestjs-access-control": "npm:^5.0.0-alpha.0" "@concepta/nestjs-common": "npm:^5.0.0-alpha.0" "@concepta/nestjs-crud": "npm:^5.0.0-alpha.0" @@ -1302,6 +1307,7 @@ __metadata: version: 0.0.0-use.local resolution: "@concepta/typeorm-common@workspace:packages/typeorm-common" dependencies: + "@concepta/i18n": "npm:^4.0.0-alpha.48" "@concepta/nestjs-common": "npm:^5.0.0-alpha.0" "@concepta/nestjs-typeorm-ext": "npm:^5.0.0-alpha.0" "@concepta/ts-core": "npm:^5.0.0-alpha.0" @@ -3584,12 +3590,12 @@ __metadata: linkType: hard "@types/eslint@npm:*": - version: 8.56.10 - resolution: "@types/eslint@npm:8.56.10" + version: 9.6.0 + resolution: "@types/eslint@npm:9.6.0" dependencies: "@types/estree": "npm:*" "@types/json-schema": "npm:*" - checksum: 10c0/674349d6c342c3864d70f4d5a9965f96fb253801532752c8c500ad6a1c2e8b219e01ccff5dc8791dcb58b5483012c495708bb9f3ff929f5c9322b3da126c15d3 + checksum: 10c0/69301356bc73b85e381ae00931291de2e96d1cc49a112c592c74ee32b2f85412203dea6a333b4315fd9839bb14f364f265cbfe7743fc5a78492ee0326dd6a2c1 languageName: node linkType: hard @@ -3753,11 +3759,11 @@ __metadata: linkType: hard "@types/node@npm:*, @types/node@npm:>= 8": - version: 20.14.11 - resolution: "@types/node@npm:20.14.11" + version: 22.3.0 + resolution: "@types/node@npm:22.3.0" dependencies: - undici-types: "npm:~5.26.4" - checksum: 10c0/5306becc0ff41d81b1e31524bd376e958d0741d1ce892dffd586b9ae0cb6553c62b0d62abd16da8bea6b9a2c17572d360450535d7c073794b0cef9cb4e39691e + undici-types: "npm:~6.18.2" + checksum: 10c0/855be3b97f4262a84818f889ff898e147dcef4f72b866e3551a8367380cdb63a45cf3929f09d9a0647f62706f8d4772e91a1ecd1bd7c6a80d6744c2b0cbca608 languageName: node linkType: hard @@ -3922,13 +3928,14 @@ __metadata: linkType: hard "@types/superagent@npm:*": - version: 8.1.7 - resolution: "@types/superagent@npm:8.1.7" + version: 8.1.8 + resolution: "@types/superagent@npm:8.1.8" dependencies: "@types/cookiejar": "npm:^2.1.5" "@types/methods": "npm:^1.1.4" "@types/node": "npm:*" - checksum: 10c0/4676d539f5feaaea9d39d7409c86ae9e15b92a43c28456aff9d9897e47e9fe5ebd3807600c5310f84fe5ebea30f3fe5e2b3b101a87821a478ca79e3a56fd8c9e + form-data: "npm:^4.0.0" + checksum: 10c0/c5fa8fe48e63445317d2e056c93c373a14cd916ac7b6e5a084f8cdecc70419683c89e3245ad47ff3d1f33406cfdc23117e3877651b184257adcd3063b7037feb languageName: node linkType: hard @@ -5127,9 +5134,9 @@ __metadata: linkType: hard "aws4@npm:^1.8.0": - version: 1.13.0 - resolution: "aws4@npm:1.13.0" - checksum: 10c0/4c71398543e432631a226cabafaa138f8070482f99790233840d84847291ec744e739cb18684a68f52125d0e73f82f16f0246d93524ec85167fadb3cf60dfa4f + version: 1.13.1 + resolution: "aws4@npm:1.13.1" + checksum: 10c0/c40a90b998853b92f9d0198e9992f4a94c81f29b02ca02b75952efaef07ff0660e756c7ebd04ff674edfa36c29406abaa8aad84f23dbc8b362d31979a631d3fe languageName: node linkType: hard @@ -5177,24 +5184,27 @@ __metadata: linkType: hard "babel-preset-current-node-syntax@npm:^1.0.0": - version: 1.0.1 - resolution: "babel-preset-current-node-syntax@npm:1.0.1" + version: 1.1.0 + resolution: "babel-preset-current-node-syntax@npm:1.1.0" dependencies: "@babel/plugin-syntax-async-generators": "npm:^7.8.4" "@babel/plugin-syntax-bigint": "npm:^7.8.3" - "@babel/plugin-syntax-class-properties": "npm:^7.8.3" - "@babel/plugin-syntax-import-meta": "npm:^7.8.3" + "@babel/plugin-syntax-class-properties": "npm:^7.12.13" + "@babel/plugin-syntax-class-static-block": "npm:^7.14.5" + "@babel/plugin-syntax-import-attributes": "npm:^7.24.7" + "@babel/plugin-syntax-import-meta": "npm:^7.10.4" "@babel/plugin-syntax-json-strings": "npm:^7.8.3" - "@babel/plugin-syntax-logical-assignment-operators": "npm:^7.8.3" + "@babel/plugin-syntax-logical-assignment-operators": "npm:^7.10.4" "@babel/plugin-syntax-nullish-coalescing-operator": "npm:^7.8.3" - "@babel/plugin-syntax-numeric-separator": "npm:^7.8.3" + "@babel/plugin-syntax-numeric-separator": "npm:^7.10.4" "@babel/plugin-syntax-object-rest-spread": "npm:^7.8.3" "@babel/plugin-syntax-optional-catch-binding": "npm:^7.8.3" "@babel/plugin-syntax-optional-chaining": "npm:^7.8.3" - "@babel/plugin-syntax-top-level-await": "npm:^7.8.3" + "@babel/plugin-syntax-private-property-in-object": "npm:^7.14.5" + "@babel/plugin-syntax-top-level-await": "npm:^7.14.5" peerDependencies: "@babel/core": ^7.0.0 - checksum: 10c0/5ba39a3a0e6c37d25e56a4fb843be632dac98d54706d8a0933f9bcb1a07987a96d55c2b5a6c11788a74063fb2534fe68c1f1dbb6c93626850c785e0938495627 + checksum: 10c0/0b838d4412e3322cb4436f246e24e9c00bebcedfd8f00a2f51489db683bd35406bbd55a700759c28d26959c6e03f84dd6a1426f576f440267c1d7a73c5717281 languageName: node linkType: hard @@ -5386,7 +5396,7 @@ __metadata: languageName: node linkType: hard -"browserslist@npm:^4.21.10": +"browserslist@npm:^4.21.10, browserslist@npm:^4.23.1": version: 4.23.3 resolution: "browserslist@npm:4.23.3" dependencies: @@ -5400,20 +5410,6 @@ __metadata: languageName: node linkType: hard -"browserslist@npm:^4.23.1": - version: 4.23.2 - resolution: "browserslist@npm:4.23.2" - dependencies: - caniuse-lite: "npm:^1.0.30001640" - electron-to-chromium: "npm:^1.4.820" - node-releases: "npm:^2.0.14" - update-browserslist-db: "npm:^1.1.0" - bin: - browserslist: cli.js - checksum: 10c0/0217d23c69ed61cdd2530c7019bf7c822cd74c51f8baab18dd62457fed3129f52499f8d3a6f809ae1fb7bb3050aa70caa9a529cc36c7478427966dbf429723a5 - languageName: node - linkType: hard - "bs-logger@npm:0.x": version: 0.2.6 resolution: "bs-logger@npm:0.2.6" @@ -5692,17 +5688,10 @@ __metadata: languageName: node linkType: hard -"caniuse-lite@npm:^1.0.30001640": - version: 1.0.30001642 - resolution: "caniuse-lite@npm:1.0.30001642" - checksum: 10c0/7366878ecdd482392a741c66fd2b39816b70573d66f64b1f8e5916835faf7a15f116368290170f4d7c4e823ec78eea9b6c0f63bee763a511cc7990afa429d63b - languageName: node - linkType: hard - "caniuse-lite@npm:^1.0.30001646": - version: 1.0.30001649 - resolution: "caniuse-lite@npm:1.0.30001649" - checksum: 10c0/0ca2f3776324acfc36d72a575e72ffd1408b91f0ac462a6f0aa08ea24d0d16e83f85f652e19d40e6d6d82ab0fb588740f948e7c88d2818fe6bcd68f70ca33acf + version: 1.0.30001651 + resolution: "caniuse-lite@npm:1.0.30001651" + checksum: 10c0/7821278952a6dbd17358e5d08083d258f092e2a530f5bc1840657cb140fbbc5ec44293bc888258c44a18a9570cde149ed05819ac8320b9710cf22f699891e6ad languageName: node linkType: hard @@ -5788,7 +5777,7 @@ __metadata: languageName: node linkType: hard -"cheerio@npm:1.0.0-rc.12, cheerio@npm:^1.0.0-rc.12": +"cheerio@npm:1.0.0-rc.12": version: 1.0.0-rc.12 resolution: "cheerio@npm:1.0.0-rc.12" dependencies: @@ -5803,6 +5792,25 @@ __metadata: languageName: node linkType: hard +"cheerio@npm:^1.0.0-rc.12": + version: 1.0.0 + resolution: "cheerio@npm:1.0.0" + dependencies: + cheerio-select: "npm:^2.1.0" + dom-serializer: "npm:^2.0.0" + domhandler: "npm:^5.0.3" + domutils: "npm:^3.1.0" + encoding-sniffer: "npm:^0.2.0" + htmlparser2: "npm:^9.1.0" + parse5: "npm:^7.1.2" + parse5-htmlparser2-tree-adapter: "npm:^7.0.0" + parse5-parser-stream: "npm:^7.1.2" + undici: "npm:^6.19.5" + whatwg-mimetype: "npm:^4.0.0" + checksum: 10c0/d0e16925d9c36c879edfaef1c0244c866375a4c7b8d6ccd7ae0ad42da7d26263ea1a3c17b9a1aa5965918deeff2d40ac2e7223824f8e6eca972df3b81316a09f + languageName: node + linkType: hard + "chokidar@npm:3.6.0, chokidar@npm:^3.0.0, chokidar@npm:^3.5.3": version: 3.6.0 resolution: "chokidar@npm:3.6.0" @@ -6901,9 +6909,9 @@ __metadata: linkType: hard "dayjs@npm:^1.11.9": - version: 1.11.11 - resolution: "dayjs@npm:1.11.11" - checksum: 10c0/0131d10516b9945f05a57e13f4af49a6814de5573a494824e103131a3bbe4cc470b1aefe8e17e51f9a478a22cd116084be1ee5725cedb66ec4c3f9091202dc4b + version: 1.11.12 + resolution: "dayjs@npm:1.11.12" + checksum: 10c0/9673d37f3f9ad8a91caaeae9b3fea9a0010c81c7f58599fb9d860bc3359b86632fbff8eb7dddc86c2acaab01c5e6860bc672952f17b58c9286140c52b077c8e4 languageName: node linkType: hard @@ -6926,14 +6934,14 @@ __metadata: linkType: hard "debug@npm:4, debug@npm:^4.0.0, debug@npm:^4.1.0, debug@npm:^4.1.1, debug@npm:^4.3.1, debug@npm:^4.3.2, debug@npm:^4.3.4, debug@npm:^4.3.5": - version: 4.3.5 - resolution: "debug@npm:4.3.5" + version: 4.3.6 + resolution: "debug@npm:4.3.6" dependencies: ms: "npm:2.1.2" peerDependenciesMeta: supports-color: optional: true - checksum: 10c0/082c375a2bdc4f4469c99f325ff458adad62a3fc2c482d59923c260cb08152f34e2659f72b3767db8bb2f21ca81a60a42d1019605a412132d7b9f59363a005cc + checksum: 10c0/3293416bff072389c101697d4611c402a6bacd1900ac20c0492f61a9cdd6b3b29750fc7f5e299f8058469ef60ff8fb79b86395a30374fbd2490113c1c7112285 languageName: node linkType: hard @@ -7431,17 +7439,10 @@ __metadata: languageName: node linkType: hard -"electron-to-chromium@npm:^1.4.820": - version: 1.4.828 - resolution: "electron-to-chromium@npm:1.4.828" - checksum: 10c0/3c9d101379c952d2ebe62941ba91cd7806ff47d0fc957e3e38c89bf6a3b4a6bbe1e7cf38017144ac28dc4856fe202a06371d969a041a746763ed6a88a2b4fb18 - languageName: node - linkType: hard - "electron-to-chromium@npm:^1.5.4": - version: 1.5.5 - resolution: "electron-to-chromium@npm:1.5.5" - checksum: 10c0/6e5e12f729a74a78d9a7386ea32039262cb8a2f4611ab346da1f162c270d0569194c72169042080a1017220835ed30ee2d77ca5ba13c1acaa5fa0d373fbc0ad5 + version: 1.5.8 + resolution: "electron-to-chromium@npm:1.5.8" + checksum: 10c0/801de2afa0479ffa0cd0e36b7865241dcd3a66a92fca28457431d2dc2bd9c2d066ab07578b419426c504df196f078c63283ee47140c28039d224ec2631acbcee languageName: node linkType: hard @@ -7494,6 +7495,16 @@ __metadata: languageName: node linkType: hard +"encoding-sniffer@npm:^0.2.0": + version: 0.2.0 + resolution: "encoding-sniffer@npm:0.2.0" + dependencies: + iconv-lite: "npm:^0.6.3" + whatwg-encoding: "npm:^3.1.1" + checksum: 10c0/b312e0d67f339bec44e021e5210ee8ee90d7b8f9975eb2c79a36fd467eb07709e88dcf62ee20f62ee0d74a13874307d99557852a2de9b448f1e3fb991fc68257 + languageName: node + linkType: hard + "encoding@npm:^0.1.11, encoding@npm:^0.1.13": version: 0.1.13 resolution: "encoding@npm:0.1.13" @@ -7512,17 +7523,7 @@ __metadata: languageName: node linkType: hard -"enhanced-resolve@npm:^5.0.0, enhanced-resolve@npm:^5.7.0": - version: 5.17.0 - resolution: "enhanced-resolve@npm:5.17.0" - dependencies: - graceful-fs: "npm:^4.2.4" - tapable: "npm:^2.2.0" - checksum: 10c0/90065e58e4fd08e77ba47f827eaa17d60c335e01e4859f6e644bb3b8d0e32b203d33894aee92adfa5121fa262f912b48bdf0d0475e98b4a0a1132eea1169ad37 - languageName: node - linkType: hard - -"enhanced-resolve@npm:^5.17.0": +"enhanced-resolve@npm:^5.0.0, enhanced-resolve@npm:^5.17.0, enhanced-resolve@npm:^5.7.0": version: 5.17.1 resolution: "enhanced-resolve@npm:5.17.1" dependencies: @@ -8747,12 +8748,12 @@ __metadata: linkType: hard "foreground-child@npm:^3.1.0": - version: 3.2.1 - resolution: "foreground-child@npm:3.2.1" + version: 3.3.0 + resolution: "foreground-child@npm:3.3.0" dependencies: cross-spawn: "npm:^7.0.0" signal-exit: "npm:^4.0.1" - checksum: 10c0/9a53a33dbd87090e9576bef65fb4a71de60f6863a8062a7b11bc1cbe3cc86d428677d7c0b9ef61cdac11007ac580006f78bd5638618d564cfd5e6fd713d6878f + checksum: 10c0/028f1d41000553fcfa6c4bb5c372963bf3d9bf0b1f25a87d1a6253014343fb69dfb1b42d9625d7cf44c8ba429940f3d0ff718b62105d4d4a4f6ef8ca0a53faa2 languageName: node linkType: hard @@ -9877,6 +9878,29 @@ __metadata: languageName: node linkType: hard +"i18next-fs-backend@npm:^2.3.1": + version: 2.3.2 + resolution: "i18next-fs-backend@npm:2.3.2" + checksum: 10c0/5760843ade112821e4df62dffcdab769a09550bbe26264bb03babd60a122b4a1520a7ee95de228d3b932d2ac4220fc7669936026c10a729292c98cacdee8c49b + languageName: node + linkType: hard + +"i18next-http-middleware@npm:^3.6.0": + version: 3.6.0 + resolution: "i18next-http-middleware@npm:3.6.0" + checksum: 10c0/52fdc7d5f90c6af51300a932e3906910ca30fe0137fb051bec29a83a3e4ca9cd17f79a70ddd20d04f3500617d0e8758d1f482210c197f7229fc03edaa15bf2ef + languageName: node + linkType: hard + +"i18next@npm:^23.12.2": + version: 23.12.3 + resolution: "i18next@npm:23.12.3" + dependencies: + "@babel/runtime": "npm:^7.23.2" + checksum: 10c0/8c17d448130a1f0ea81d4684bdbb791ebc5df0defdb65e0103e0084fe802d2e36451da8721a39dea29401f709de79e34e6a8079913bf38018a698203f606f2b0 + languageName: node + linkType: hard + "iconv-lite@npm:0.4.24, iconv-lite@npm:^0.4.24": version: 0.4.24 resolution: "iconv-lite@npm:0.4.24" @@ -9886,7 +9910,7 @@ __metadata: languageName: node linkType: hard -"iconv-lite@npm:0.6.3, iconv-lite@npm:^0.6.2": +"iconv-lite@npm:0.6.3, iconv-lite@npm:^0.6.2, iconv-lite@npm:^0.6.3": version: 0.6.3 resolution: "iconv-lite@npm:0.6.3" dependencies: @@ -9926,9 +9950,9 @@ __metadata: linkType: hard "ignore@npm:^5.1.1, ignore@npm:^5.2.0, ignore@npm:^5.2.4, ignore@npm:~5.3.1": - version: 5.3.1 - resolution: "ignore@npm:5.3.1" - checksum: 10c0/703f7f45ffb2a27fb2c5a8db0c32e7dee66b33a225d28e8db4e1be6474795f606686a6e3bcc50e1aa12f2042db4c9d4a7d60af3250511de74620fbed052ea4cd + version: 5.3.2 + resolution: "ignore@npm:5.3.2" + checksum: 10c0/f9f652c957983634ded1e7f02da3b559a0d4cc210fca3792cb67f1b153623c9c42efdc1c4121af171e295444459fc4a9201101fb041b1104a3c000bccb188337 languageName: node linkType: hard @@ -9965,14 +9989,14 @@ __metadata: linkType: hard "import-local@npm:^3.0.2": - version: 3.1.0 - resolution: "import-local@npm:3.1.0" + version: 3.2.0 + resolution: "import-local@npm:3.2.0" dependencies: pkg-dir: "npm:^4.2.0" resolve-cwd: "npm:^3.0.0" bin: import-local-fixture: fixtures/cli.js - checksum: 10c0/c67ecea72f775fe8684ca3d057e54bdb2ae28c14bf261d2607c269c18ea0da7b730924c06262eca9aed4b8ab31e31d65bc60b50e7296c85908a56e2f7d41ecd2 + checksum: 10c0/94cd6367a672b7e0cb026970c85b76902d2710a64896fa6de93bd5c571dd03b228c5759308959de205083e3b1c61e799f019c9e36ee8e9c523b993e1057f0433 languageName: node linkType: hard @@ -10256,11 +10280,11 @@ __metadata: linkType: hard "is-core-module@npm:^2.13.0, is-core-module@npm:^2.13.1, is-core-module@npm:^2.5.0": - version: 2.14.0 - resolution: "is-core-module@npm:2.14.0" + version: 2.15.0 + resolution: "is-core-module@npm:2.15.0" dependencies: hasown: "npm:^2.0.2" - checksum: 10c0/ae8dbc82bd20426558bc8d20ce290ce301c1cfd6ae4446266d10cacff4c63c67ab16440ade1d72ced9ec41c569fbacbcee01e293782ce568527c4cdf35936e4c + checksum: 10c0/da161f3d9906f459486da65609b2f1a2dfdc60887c689c234d04e88a062cb7920fa5be5fb7ab08dc43b732929653c4135ef05bf77888ae2a9040ce76815eb7b1 languageName: node linkType: hard @@ -10796,8 +10820,8 @@ __metadata: linkType: hard "jake@npm:^10.8.5": - version: 10.9.1 - resolution: "jake@npm:10.9.1" + version: 10.9.2 + resolution: "jake@npm:10.9.2" dependencies: async: "npm:^3.2.3" chalk: "npm:^4.0.2" @@ -10805,7 +10829,7 @@ __metadata: minimatch: "npm:^3.1.2" bin: jake: bin/cli.js - checksum: 10c0/dda972431a926462f08fcf583ea8997884216a43daa5cce81cb42e7e661dc244f836c0a802fde23439c6e1fc59743d1c0be340aa726d3b17d77557611a5cd541 + checksum: 10c0/c4597b5ed9b6a908252feab296485a4f87cba9e26d6c20e0ca144fb69e0c40203d34a2efddb33b3d297b8bd59605e6c1f44f6221ca1e10e69175ecbf3ff5fe31 languageName: node linkType: hard @@ -11818,9 +11842,9 @@ __metadata: linkType: hard "libphonenumber-js@npm:^1.10.53": - version: 1.11.4 - resolution: "libphonenumber-js@npm:1.11.4" - checksum: 10c0/0a606da67b4b465e6e157570ad5e70b92f59197cdc1c505d160422a21a894b55a75c9044b863d0eaf4d96884d3fa3e77268adf55afc2d8f11efae7f7a249e7cc + version: 1.11.7 + resolution: "libphonenumber-js@npm:1.11.7" + checksum: 10c0/c22a7d5b479393b71403b9be704669ca03de92adb051a1c5385d237293e2a89385732ad2406053bd72275e8d2e872dc1ee7c46ffc6de632a3b4d582023362635 languageName: node linkType: hard @@ -13578,13 +13602,6 @@ __metadata: languageName: node linkType: hard -"node-releases@npm:^2.0.14": - version: 2.0.17 - resolution: "node-releases@npm:2.0.17" - checksum: 10c0/8b8af0ade683e89e1a9c379e3a2d89604c9ae767ef1e52aa5029ca2db1fab5f741140d04cdbf204f56e4a789e319908988291fb22015f8e9f784b7ea9b1b3dd3 - languageName: node - linkType: hard - "node-releases@npm:^2.0.18": version: 2.0.18 resolution: "node-releases@npm:2.0.18" @@ -14390,6 +14407,15 @@ __metadata: languageName: node linkType: hard +"parse5-parser-stream@npm:^7.1.2": + version: 7.1.2 + resolution: "parse5-parser-stream@npm:7.1.2" + dependencies: + parse5: "npm:^7.0.0" + checksum: 10c0/e236c61000d38ecad369e725a48506b051cebad8abb00e6d4e8bff7aa85c183820fcb45db1559cc90955bdbbdbd665ea94c41259594e74566fff411478dc7fcb + languageName: node + linkType: hard + "parse5@npm:6.0.1, parse5@npm:^6.0.1": version: 6.0.1 resolution: "parse5@npm:6.0.1" @@ -14404,7 +14430,7 @@ __metadata: languageName: node linkType: hard -"parse5@npm:^7.0.0": +"parse5@npm:^7.0.0, parse5@npm:^7.1.2": version: 7.1.2 resolution: "parse5@npm:7.1.2" dependencies: @@ -15135,11 +15161,11 @@ __metadata: linkType: hard "qs@npm:^6.11.0, qs@npm:^6.8.0, qs@npm:^6.9.4": - version: 6.12.3 - resolution: "qs@npm:6.12.3" + version: 6.13.0 + resolution: "qs@npm:6.13.0" dependencies: side-channel: "npm:^1.0.6" - checksum: 10c0/243ddcc8f49dab78fc51041f7f64c500b47c671c45a101a8aca565d8537cb562921da7ef1a831b4a7051596ec88bb35a0d5e25a240025e8b32c6bfb69f00bf2f + checksum: 10c0/62372cdeec24dc83a9fb240b7533c0fdcf0c5f7e0b83343edd7310f0ab4c8205a5e7c56406531f2e47e1b4878a3821d652be4192c841de5b032ca83619d8f860 languageName: node linkType: hard @@ -16042,9 +16068,9 @@ __metadata: linkType: hard "set-cookie-parser@npm:^2.4.1": - version: 2.6.0 - resolution: "set-cookie-parser@npm:2.6.0" - checksum: 10c0/739da029f0e56806a103fcd5501d9c475e19e77bd8274192d7ae5c374ae714a82bba9a7ac00b0330a18227c5644b08df9e442240527be578f5a6030f9bb2bb80 + version: 2.7.0 + resolution: "set-cookie-parser@npm:2.7.0" + checksum: 10c0/5ccb2d0389bda27631d57e44644319f0b77200e7c8bd1515824eb83dbd2d351864a29581f7e7f977a5aeb83c3ec9976e69b706a80ac654152fd26353011ffef4 languageName: node linkType: hard @@ -17092,8 +17118,8 @@ __metadata: linkType: hard "terser@npm:^5.26.0": - version: 5.31.3 - resolution: "terser@npm:5.31.3" + version: 5.31.6 + resolution: "terser@npm:5.31.6" dependencies: "@jridgewell/source-map": "npm:^0.3.3" acorn: "npm:^8.8.2" @@ -17101,7 +17127,7 @@ __metadata: source-map-support: "npm:~0.5.20" bin: terser: bin/terser - checksum: 10c0/eb2b525dada9febd3db74e94bd295f9cd7abd809e4f9c6bbc795a3048ad50fd327c15eab99db383fa820239680eef6d2dbd7dc05361769c204ddee5cf684d41e + checksum: 10c0/b17d02b65a52a5041430572b3c514475820f5e7590fa93773c0f5b4be601ccf3f6d745bf5a79f3ee58187cf85edf61c24ddf4345783839fccb44c9c8fa9b427e languageName: node linkType: hard @@ -17837,11 +17863,11 @@ __metadata: linkType: hard "uglify-js@npm:^3.1.4, uglify-js@npm:^3.5.1": - version: 3.18.0 - resolution: "uglify-js@npm:3.18.0" + version: 3.19.2 + resolution: "uglify-js@npm:3.19.2" bin: uglifyjs: bin/uglifyjs - checksum: 10c0/57f5f6213a2c4e8c551be9c875c085d565dc88af6b7caaab40a197aa639183cdce7c9dc2f858675eca72a5323f850ab7e88b9cc0a52dfbe3e0768aee6ab6e102 + checksum: 10c0/51dbe1304a91cac5daa01f6a2d4ecd545fab7b7d0625e11590b923e95a6d2263b3481dcea974abfc0282b33d2c76f74f1196a992df07eae0847175bc39ea45bb languageName: node linkType: hard @@ -17894,6 +17920,20 @@ __metadata: languageName: node linkType: hard +"undici-types@npm:~6.18.2": + version: 6.18.2 + resolution: "undici-types@npm:6.18.2" + checksum: 10c0/dea28163891a5af7624c120107dc07a74c369ee94c6dd1d0de29af061ee129fac4846f97130589f4436b85f6102c73d30328ca908be02626dd8ab9fec5642aba + languageName: node + linkType: hard + +"undici@npm:^6.19.5": + version: 6.19.7 + resolution: "undici@npm:6.19.7" + checksum: 10c0/801d1e66d5bccdd3fcc9ecf1c95b83a593e4867b89e21ed725e35bd4d572b3d3ce1d7feab2a4f2046f65923de70bfafb69ac148c633d1ab30a948d6fec24475a + languageName: node + linkType: hard + "unicorn-magic@npm:^0.1.0": version: 0.1.0 resolution: "unicorn-magic@npm:0.1.0" @@ -18232,12 +18272,12 @@ __metadata: linkType: hard "watchpack@npm:^2.4.1": - version: 2.4.1 - resolution: "watchpack@npm:2.4.1" + version: 2.4.2 + resolution: "watchpack@npm:2.4.2" dependencies: glob-to-regexp: "npm:^0.4.1" graceful-fs: "npm:^4.1.2" - checksum: 10c0/c694de0a61004e587a8a0fdc9cfec20ee692c52032d9ab2c2e99969a37fdab9e6e1bd3164ed506f9a13f7c83e65563d563e0d6b87358470cdb7309b83db78683 + checksum: 10c0/ec60a5f0e9efaeca0102fd9126346b3b2d523e01c34030d3fddf5813a7125765121ebdc2552981136dcd2c852deb1af0b39340f2fcc235f292db5399d0283577 languageName: node linkType: hard @@ -18352,6 +18392,15 @@ __metadata: languageName: node linkType: hard +"whatwg-encoding@npm:^3.1.1": + version: 3.1.1 + resolution: "whatwg-encoding@npm:3.1.1" + dependencies: + iconv-lite: "npm:0.6.3" + checksum: 10c0/273b5f441c2f7fda3368a496c3009edbaa5e43b71b09728f90425e7f487e5cef9eb2b846a31bd760dd8077739c26faf6b5ca43a5f24033172b003b72cf61a93e + languageName: node + linkType: hard + "whatwg-mimetype@npm:^2.3.0": version: 2.3.0 resolution: "whatwg-mimetype@npm:2.3.0" @@ -18359,6 +18408,13 @@ __metadata: languageName: node linkType: hard +"whatwg-mimetype@npm:^4.0.0": + version: 4.0.0 + resolution: "whatwg-mimetype@npm:4.0.0" + checksum: 10c0/a773cdc8126b514d790bdae7052e8bf242970cebd84af62fb2f35a33411e78e981f6c0ab9ed1fe6ec5071b09d5340ac9178e05b52d35a9c4bcf558ba1b1551df + languageName: node + linkType: hard + "whatwg-url@npm:^5.0.0": version: 5.0.0 resolution: "whatwg-url@npm:5.0.0" From 7aa0e706b14d71026700cf653dc16adcae0335ad Mon Sep 17 00:00:00 2001 From: Thiago Ramalho Date: Mon, 26 Aug 2024 10:50:12 -0300 Subject: [PATCH 10/11] chore: update and linting --- packages/i18n/README.md | 39 ++++++++++++++++++++------------------- 1 file changed, 20 insertions(+), 19 deletions(-) diff --git a/packages/i18n/README.md b/packages/i18n/README.md index 98b5c9a0..c4d4117b 100644 --- a/packages/i18n/README.md +++ b/packages/i18n/README.md @@ -1,14 +1,15 @@ # i18n Module for React -An advanced i18n (internationalization) utility for managing multiple languages in React applications with seamless integration and modularity. +An advanced i18n (internationalization) utility for managing multiple +languages in React applications with seamless integration and modularity. ## Project -[![NPM Latest](https://img.shields.io/npm/v/@concepta/i18n)](https://www.npmjs.com/package/@concepta/i18n) -[![NPM Downloads](https://img.shields.io/npm/dw/@concepta/i18n)](https://www.npmjs.com/package/@concepta/i18n) -[![GH Last Commit](https://img.shields.io/github/last-commit/yourorg/i18n?logo=github)](https://github.com/yourorg/i18n) -[![GH Contrib](https://img.shields.io/github/contributors/yourorg/i18n?logo=github)](https://github.com/yourorg/i18n/graphs/contributors) +[![NPM Latest](https://img.shields.io/npm/v/@concepta/i18n)](https://www.npmjs.com/package/@concepta/i18n) +[![NPM Downloads](https://img.shields.io/npm/dw/@concepta/i18n)](https://www.npmjs.com/package/@concepta/i18n) +[![GH Last Commit](https://img.shields.io/github/last-commit/@concepta/i18n?logo=github)](https://github.com/@concepta/i18n) +[![GH Contrib](https://img.shields.io/github/contributors/@concepta/i18n?logo=github)](https://github.com/@concepta/i18n/graphs/contributors) # Table of Contents @@ -44,8 +45,8 @@ npm install @concepta/i18n ## Basic Setup -To set up the i18n module, you need to initialize it with your desired -settings and add your translations. For more detailed settings documentation, +To set up the i18n module, you need to initialize it with your desired +settings and add your translations. For more detailed settings documentation, check [i18next](https://www.i18next.com/). ```ts @@ -71,7 +72,7 @@ export function initI18n() { } }); // initialized and ready to go! - // i18n is already initialized, because the translation resources were + // i18n is already initialized, because the translation resources were // passed via init function } @@ -172,9 +173,9 @@ console.log(t({ key: 'hello' })); ## Adding Translations -To add translations dynamically, you can initialize the I18n instance first -and addtranslation resources at a later time. This approach is useful in -scenarios where translations are fetched from an external source or need to be +To add translations dynamically, you can initialize the I18n instance first +and addtranslation resources at a later time. This approach is useful in +scenarios where translations are fetched from an external source or need to be updated without reinitializing the entire i18n setup. This method is particularly beneficial in the following situations: @@ -185,7 +186,7 @@ This method is particularly beneficial in the following situations: - **Modular Applications**: In large applications with multiple modules, translations can be added as each module is loaded, reducing the initial load time. -- **Real-time Updates**: If your application supports real-time updates, you +- **Real-time Updates**: If your application supports real-time updates, you can dynamically add new translations without disrupting the user experience. ```ts @@ -224,7 +225,7 @@ console.log(t({ ## Creating a Custom Translation Module -You can create custom translation modules to extend the i18n functionality. +You can create custom translation modules to extend the i18n functionality. Here’s how: ### **Define the Module**: Add modules on initialization @@ -269,25 +270,25 @@ Please check (API Reference)[] for more information. ## What is i18n? -i18n, short for internationalization, refers to the process of designing -software applications that can be adapted to various languages and regions +i18n, short for internationalization, refers to the process of designing +software applications that can be adapted to various languages and regions without engineering changes. ## How i18n Module Works -The i18n module in this package provides a wrapper around i18next, allowing +The i18n module in this package provides a wrapper around i18next, allowing seamless integration of translation modules and dynamic language switching. ### Benefits of Using i18n - **Flexibility**: Supports multiple languages and dynamic translation updates. -- **Modularity**: Easily extendable with custom modules for translation +- **Modularity**: Easily extendable with custom modules for translation management. - **Ease of Use**: Simple API to manage translations and switch languages. ### Common i18n Pitfalls -- **Missing Translations**: Ensure all necessary translations are provided for +- **Missing Translations**: Ensure all necessary translations are provided for each language. -- **Performance Overheads**: Avoid loading unnecessary translation files by +- **Performance Overheads**: Avoid loading unnecessary translation files by using modular imports. From 1f707759a0599beb7559e050dd7d2b7e0ae25e6d Mon Sep 17 00:00:00 2001 From: Thiago Ramalho Date: Tue, 10 Dec 2024 15:33:26 -0300 Subject: [PATCH 11/11] chore: linting --- packages/nestjs-cache/src/locales/pt/CacheModule.ts | 2 +- .../src/exceptions/reference-mutate.exception.ts | 3 +-- packages/typeorm-common/src/locales/en/translation.ts | 7 ++++++- packages/typeorm-common/src/locales/pt/translation.ts | 7 ++++++- 4 files changed, 14 insertions(+), 5 deletions(-) diff --git a/packages/nestjs-cache/src/locales/pt/CacheModule.ts b/packages/nestjs-cache/src/locales/pt/CacheModule.ts index fdb36f00..420b2017 100644 --- a/packages/nestjs-cache/src/locales/pt/CacheModule.ts +++ b/packages/nestjs-cache/src/locales/pt/CacheModule.ts @@ -1,4 +1,4 @@ const ptBR = { - REFERENCE_MUTATE_ERROR: 'Erro ao tentar alterar uma referência de %s', + REFERENCE_MUTATE_ERROR: 'Error ao tentar alterar uma referência de %s', }; export default ptBR; diff --git a/packages/typeorm-common/src/exceptions/reference-mutate.exception.ts b/packages/typeorm-common/src/exceptions/reference-mutate.exception.ts index 51daf9a7..4cdb7b1b 100644 --- a/packages/typeorm-common/src/exceptions/reference-mutate.exception.ts +++ b/packages/typeorm-common/src/exceptions/reference-mutate.exception.ts @@ -14,8 +14,7 @@ export class ReferenceMutateException extends RuntimeException { super({ message: t({ key: REFERENCE_MUTATE_ERROR, - defaultMessage: - 'Error Default while trying to mutate a %s reference', + defaultMessage: 'Error Default while trying to mutate a %s reference', }), messageParams: [entityName], ...options, diff --git a/packages/typeorm-common/src/locales/en/translation.ts b/packages/typeorm-common/src/locales/en/translation.ts index fe6df683..87ef3354 100644 --- a/packages/typeorm-common/src/locales/en/translation.ts +++ b/packages/typeorm-common/src/locales/en/translation.ts @@ -1,4 +1,9 @@ -import { REFERENCE_ID_NO_MATCH, REFERENCE_LOOKUP_ERROR, REFERENCE_MUTATE_ERROR, REFERENCE_VALIDATION_ERROR } from "../../constants"; +import { + REFERENCE_ID_NO_MATCH, + REFERENCE_LOOKUP_ERROR, + REFERENCE_MUTATE_ERROR, + REFERENCE_VALIDATION_ERROR, +} from '../../constants'; const enUS = { [REFERENCE_MUTATE_ERROR]: 'Error while trying to mutate a %s reference', diff --git a/packages/typeorm-common/src/locales/pt/translation.ts b/packages/typeorm-common/src/locales/pt/translation.ts index 25e4fe43..18ac4a00 100644 --- a/packages/typeorm-common/src/locales/pt/translation.ts +++ b/packages/typeorm-common/src/locales/pt/translation.ts @@ -1,4 +1,9 @@ -import { REFERENCE_ID_NO_MATCH, REFERENCE_LOOKUP_ERROR, REFERENCE_MUTATE_ERROR, REFERENCE_VALIDATION_ERROR } from "../../constants"; +import { + REFERENCE_ID_NO_MATCH, + REFERENCE_LOOKUP_ERROR, + REFERENCE_MUTATE_ERROR, + REFERENCE_VALIDATION_ERROR, +} from '../../constants'; const ptBR = { [REFERENCE_MUTATE_ERROR]: 'Erro ao tentar alterar uma referência de %s',