@flusys/nestjs-localization 4.0.1 → 4.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -1,152 +1,90 @@
1
- # Localization Package Guide
1
+ # @flusys/nestjs-localization
2
2
 
3
- > **Package:** `@flusys/nestjs-localization`
4
- > **Version:** 4.0.1
5
- > **Type:** Multi-language backend support with TypeORM entities, caching, and translation management
3
+ > Multi-language backend for NestJS — database-driven translations, `{{variable}}` interpolation, RTL support, missing-key detection, verification workflow, and module-scoped partial loading.
4
+
5
+ [![npm version](https://img.shields.io/npm/v/@flusys/nestjs-localization.svg)](https://www.npmjs.com/package/@flusys/nestjs-localization)
6
+ [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
7
+ [![NestJS](https://img.shields.io/badge/NestJS-11.x-red.svg)](https://nestjs.com/)
8
+ [![TypeScript](https://img.shields.io/badge/TypeScript-5.x-blue.svg)](https://www.typescriptlang.org/)
9
+ [![Node.js](https://img.shields.io/badge/Node.js-%3E%3D18.x-green.svg)](https://nodejs.org/)
10
+
11
+ ---
12
+
13
+ ## Table of Contents
14
+
15
+ - [Overview](#overview)
16
+ - [Features](#features)
17
+ - [Compatibility](#compatibility)
18
+ - [Installation](#installation)
19
+ - [Quick Start](#quick-start)
20
+ - [Module Registration](#module-registration)
21
+ - [forRoot (Sync)](#forroot-sync)
22
+ - [forRootAsync (Factory)](#forrootasync-factory)
23
+ - [Configuration Reference](#configuration-reference)
24
+ - [Feature Toggles](#feature-toggles)
25
+ - [API Endpoints](#api-endpoints)
26
+ - [Entities](#entities)
27
+ - [Translation Workflow](#translation-workflow)
28
+ - [Variable Interpolation](#variable-interpolation)
29
+ - [Fallback Chain](#fallback-chain)
30
+ - [RTL Support](#rtl-support)
31
+ - [Caching](#caching)
32
+ - [Exported Services](#exported-services)
33
+ - [Integration with API Responses](#integration-with-api-responses)
34
+ - [Troubleshooting](#troubleshooting)
35
+ - [License](#license)
6
36
 
7
37
  ---
8
38
 
9
39
  ## Overview
10
40
 
11
- `@flusys/nestjs-localization` provides a complete localization backend system:
41
+ `@flusys/nestjs-localization` manages the backend side of multi-language support. Translations are stored in the database and served to the frontend via API. The package provides a full admin workflow: create languages, define translation keys (grouped by module), add translations per language, verify them, and detect missing ones.
12
42
 
13
- - **Language Management** - CRUD operations for languages with RTL support
14
- - **Translation Keys** - Module-based key organization with variables
15
- - **Translations** - Per-language translations with verification status
16
- - **Caching** - Optional hybrid caching for performance
17
- - **Dynamic DataSource** - Request-scoped repository pattern
18
- - **Variable Interpolation** - `{{variable}}` syntax support
19
- - **Bulk Operations** - Batch update translations
20
- - **Missing Translation Detection** - Find untranslated keys
43
+ All API responses in the FLUSYS ecosystem return a `messageKey` field (e.g., `'auth.login_success'`). The frontend looks up this key in the translations returned by this package.
21
44
 
22
- ### Package Hierarchy
45
+ ---
23
46
 
24
- ```
25
- @flusys/nestjs-core <- Foundation (IDataSourceServiceOptions)
26
- |
27
- @flusys/nestjs-shared <- Base classes (RequestScopedApiService)
28
- |
29
- @flusys/nestjs-localization <- Localization system (THIS PACKAGE)
30
- ```
47
+ ## Features
48
+
49
+ - **Language management** — Create languages with ISO 639-1 codes, native names, and RTL/LTR direction
50
+ - **Translation keys** — Module-scoped key registry with variable declarations and readonly protection
51
+ - **Translations** — Per-language values with a verification workflow (draft → verified)
52
+ - **Variable interpolation** — `{{variableName}}` syntax in messages with `messageVariables` field in API responses
53
+ - **Missing key detection** — Find which keys have no translation for a given language
54
+ - **Bulk operations** — Atomic bulk update of multiple translations in one transaction
55
+ - **Default language** — Configurable fallback language (e.g., `'en'`)
56
+ - **Module scoping** — Keys grouped by module for partial frontend loading
57
+ - **Caching** — Optional HybridCache for translation lookups with configurable TTL
58
+ - **Multi-tenant** — Per-tenant DataSource isolation
31
59
 
32
60
  ---
33
61
 
34
- ## Package Architecture
62
+ ## Compatibility
35
63
 
36
- ```
37
- nestjs-localization/
38
- ├── config/
39
- │ ├── localization.constants.ts # LOCALIZATION_MODULE_OPTIONS
40
- │ └── index.ts
41
- ├── interfaces/
42
- │ ├── language.interface.ts # ILanguage
43
- │ ├── translation-key.interface.ts # ITranslationKey
44
- │ ├── translation.interface.ts # ITranslation, ITranslationsMap
45
- │ ├── localization-module-options.interface.ts # Module config
46
- │ └── index.ts
47
- ├── entities/
48
- │ ├── language.entity.ts # Language entity
49
- │ ├── translation-key.entity.ts # TranslationKey entity
50
- │ ├── translation.entity.ts # Translation entity
51
- │ └── index.ts
52
- ├── dtos/
53
- │ ├── language.dto.ts # Language DTOs
54
- │ ├── translation-key.dto.ts # TranslationKey DTOs
55
- │ ├── translation.dto.ts # Translation DTOs
56
- │ └── index.ts
57
- ├── services/
58
- │ ├── localization-config.service.ts # Config management
59
- │ ├── localization-datasource.provider.ts # DataSource provider
60
- │ ├── localization-helper.service.ts # Interpolation utilities
61
- │ ├── language.service.ts # Language CRUD
62
- │ ├── translation-key.service.ts # TranslationKey CRUD
63
- │ ├── translation.service.ts # Translation CRUD + queries
64
- │ └── index.ts
65
- ├── controllers/
66
- │ ├── language.controller.ts # Language endpoints
67
- │ ├── translation-key.controller.ts # TranslationKey endpoints
68
- │ ├── translation.controller.ts # Translation endpoints
69
- │ └── index.ts
70
- ├── modules/
71
- │ └── localization.module.ts # Module definition
72
- └── index.ts
73
- ```
64
+ | Package | Version |
65
+ |---------|---------|
66
+ | `@flusys/nestjs-core` | `^4.0.0` |
67
+ | `@flusys/nestjs-shared` | `^4.0.0` |
68
+ | `@nestjs/core` | `^11.0.0` |
69
+ | `typeorm` | `^0.3.0` |
70
+ | Node.js | `>= 18.x` |
74
71
 
75
72
  ---
76
73
 
77
- ## Database Schema
78
-
79
- ### Language Table
80
-
81
- | Column | Type | Nullable | Description |
82
- |--------|------|----------|-------------|
83
- | `id` | uuid | No | Primary key |
84
- | `code` | varchar(10) | No | ISO 639-1 code (e.g., 'en', 'ar') |
85
- | `name` | varchar(100) | No | English name |
86
- | `native_name` | varchar(100) | Yes | Native name (e.g., 'العربية') |
87
- | `direction` | varchar(3) | Yes | 'ltr' or 'rtl' |
88
- | `is_active` | boolean | No | Active status (default: true) |
89
- | `is_default` | boolean | No | Default language flag |
90
- | `serial` | int | Yes | Sort order |
91
- | `created_at` | timestamp | No | Creation timestamp |
92
- | `updated_at` | timestamp | No | Update timestamp |
93
- | `deleted_at` | timestamp | Yes | Soft delete timestamp |
94
-
95
- **Indexes:**
96
- - `UNIQUE (code)`
97
- - `INDEX (is_active)`
98
- - `INDEX (is_default)`
99
-
100
- ### TranslationKey Table
101
-
102
- | Column | Type | Nullable | Description |
103
- |--------|------|----------|-------------|
104
- | `id` | uuid | No | Primary key |
105
- | `module` | varchar(100) | No | Module grouping (e.g., 'auth') |
106
- | `key` | varchar(255) | No | Translation key |
107
- | `default_message` | varchar(500) | No | Default/fallback message |
108
- | `description` | text | Yes | Description for translators |
109
- | `variables` | simple-array | Yes | Required variables |
110
- | `is_active` | boolean | No | Active status |
111
- | `serial` | int | Yes | Sort order |
112
- | `created_at` | timestamp | No | Creation timestamp |
113
- | `updated_at` | timestamp | No | Update timestamp |
114
- | `deleted_at` | timestamp | Yes | Soft delete timestamp |
115
-
116
- **Indexes:**
117
- - `UNIQUE (key)`
118
- - `INDEX (module)`
119
- - `INDEX (module, key)`
120
- - `INDEX (is_active)`
121
-
122
- ### Translation Table
123
-
124
- | Column | Type | Nullable | Description |
125
- |--------|------|----------|-------------|
126
- | `id` | uuid | No | Primary key |
127
- | `language_id` | uuid | No | FK to language |
128
- | `translation_key_id` | uuid | No | FK to translation_key |
129
- | `value` | text | No | Translated value |
130
- | `is_verified` | boolean | No | Verified by translator |
131
- | `is_active` | boolean | No | Active status |
132
- | `created_at` | timestamp | No | Creation timestamp |
133
- | `updated_at` | timestamp | No | Update timestamp |
134
- | `deleted_at` | timestamp | Yes | Soft delete timestamp |
135
-
136
- **Indexes:**
137
- - `UNIQUE (language_id, translation_key_id)`
138
- - `INDEX (language_id)`
139
- - `INDEX (translation_key_id)`
140
- - `INDEX (is_active)`
74
+ ## Installation
75
+
76
+ ```bash
77
+ npm install @flusys/nestjs-localization @flusys/nestjs-shared @flusys/nestjs-core
78
+ ```
141
79
 
142
80
  ---
143
81
 
144
- ## Module Setup
82
+ ## Quick Start
145
83
 
146
- ### Basic Setup
84
+ ### Minimal Setup (Single Database)
147
85
 
148
86
  ```typescript
149
- // app.module.ts
87
+ import { Module } from '@nestjs/common';
150
88
  import { LocalizationModule } from '@flusys/nestjs-localization';
151
89
 
152
90
  @Module({
@@ -155,11 +93,20 @@ import { LocalizationModule } from '@flusys/nestjs-localization';
155
93
  global: true,
156
94
  includeController: true,
157
95
  bootstrapAppConfig: {
158
- dataSource: AppDataSource,
96
+ databaseMode: 'single',
97
+ enableCompanyFeature: false,
159
98
  },
160
99
  config: {
100
+ defaultDatabaseConfig: {
101
+ type: 'postgres',
102
+ host: process.env.DB_HOST,
103
+ port: Number(process.env.DB_PORT ?? 5432),
104
+ username: process.env.DB_USER,
105
+ password: process.env.DB_PASSWORD,
106
+ database: process.env.DB_NAME,
107
+ },
161
108
  defaultLanguageCode: 'en',
162
- enableCaching: true,
109
+ enableCaching: false,
163
110
  cacheTtlSeconds: 300,
164
111
  },
165
112
  }),
@@ -168,703 +115,421 @@ import { LocalizationModule } from '@flusys/nestjs-localization';
168
115
  export class AppModule {}
169
116
  ```
170
117
 
171
- ### Async Setup
118
+ After startup, create languages and translation keys via the API. The frontend fetches translations on init and uses the `messageKey` returned by each API response to look up the localized message.
172
119
 
173
- ```typescript
174
- // app.module.ts
175
- import { LocalizationModule } from '@flusys/nestjs-localization';
176
- import { ConfigModule, ConfigService } from '@nestjs/config';
120
+ ---
177
121
 
178
- @Module({
179
- imports: [
180
- LocalizationModule.forRootAsync({
181
- global: true,
182
- includeController: true,
183
- imports: [ConfigModule],
184
- bootstrapAppConfig: {
185
- dataSource: AppDataSource,
186
- },
187
- useFactory: (configService: ConfigService) => ({
188
- defaultLanguageCode: configService.get('DEFAULT_LANGUAGE', 'en'),
189
- enableCaching: configService.get('ENABLE_CACHE', true),
190
- cacheTtlSeconds: configService.get('CACHE_TTL', 300),
191
- }),
192
- inject: [ConfigService],
193
- }),
194
- ],
195
- })
196
- export class AppModule {}
197
- ```
122
+ ## Module Registration
198
123
 
199
- ### Module Options
124
+ ### forRoot (Sync)
200
125
 
201
126
  ```typescript
202
- interface ILocalizationModuleConfig {
203
- defaultLanguageCode?: string; // Default: 'en'
204
- enableCaching?: boolean; // Enable hybrid cache
205
- cacheTtlSeconds?: number; // Cache TTL in seconds
206
- }
207
-
208
- interface LocalizationModuleOptions {
209
- global?: boolean; // Register as global module
210
- includeController?: boolean; // Include controllers (default: true)
211
- bootstrapAppConfig: IBootstrapAppConfig;
212
- config?: ILocalizationModuleConfig;
213
- }
127
+ LocalizationModule.forRoot({
128
+ global?: boolean;
129
+ includeController?: boolean; // Default: true
130
+ bootstrapAppConfig?: {
131
+ databaseMode: 'single' | 'multi-tenant';
132
+ enableCompanyFeature: boolean; // No company variants — shared data only
133
+ };
134
+ config: ILocalizationModuleConfig;
135
+ })
214
136
  ```
215
137
 
216
- ---
217
-
218
- ## Entities
219
-
220
- ### Language Entity
138
+ ### forRootAsync (Factory)
221
139
 
222
140
  ```typescript
223
- @Entity({ name: 'language' })
224
- export class Language extends Identity {
225
- @Column({ type: 'varchar', length: 10 })
226
- code!: string;
227
-
228
- @Column({ type: 'varchar', length: 100 })
229
- name!: string;
230
-
231
- @Column({ type: 'varchar', length: 100, nullable: true, name: 'native_name' })
232
- nativeName!: string | null;
233
-
234
- @Column({ type: 'varchar', length: 3, nullable: true })
235
- direction!: 'ltr' | 'rtl' | null;
236
-
237
- @Column({ type: 'boolean', default: true, name: 'is_active' })
238
- isActive!: boolean;
239
-
240
- @Column({ type: 'boolean', default: false, name: 'is_default' })
241
- isDefault!: boolean;
242
-
243
- @Column({ type: 'int', nullable: true })
244
- serial!: number | null;
245
-
246
- @OneToMany('Translation', 'language')
247
- translations?: Translation[];
248
- }
141
+ import { ConfigService } from '@nestjs/config';
142
+
143
+ LocalizationModule.forRootAsync({
144
+ global: true,
145
+ includeController: true,
146
+ bootstrapAppConfig: {
147
+ databaseMode: 'single',
148
+ enableCompanyFeature: false,
149
+ },
150
+ imports: [ConfigModule],
151
+ useFactory: (configService: ConfigService) => ({
152
+ defaultDatabaseConfig: {
153
+ type: 'postgres',
154
+ host: configService.get('DB_HOST'),
155
+ port: configService.get<number>('DB_PORT'),
156
+ username: configService.get('DB_USER'),
157
+ password: configService.get('DB_PASSWORD'),
158
+ database: configService.get('DB_NAME'),
159
+ },
160
+ defaultLanguageCode: configService.get('DEFAULT_LANGUAGE', 'en'),
161
+ enableCaching: configService.get<boolean>('LOCALIZATION_CACHE', false),
162
+ cacheTtlSeconds: 300,
163
+ }),
164
+ inject: [ConfigService],
165
+ })
249
166
  ```
250
167
 
251
- ### TranslationKey Entity
168
+ ### Multi-Tenant Setup
252
169
 
253
170
  ```typescript
254
- @Entity({ name: 'translation_key' })
255
- export class TranslationKey extends Identity {
256
- @Column({ type: 'varchar', length: 100 })
257
- module!: string;
258
-
259
- @Column({ type: 'varchar', length: 255 })
260
- key!: string;
171
+ LocalizationModule.forRootAsync({
172
+ global: true,
173
+ bootstrapAppConfig: {
174
+ databaseMode: 'multi-tenant',
175
+ enableCompanyFeature: false,
176
+ },
177
+ useFactory: (): ILocalizationModuleConfig => ({
178
+ tenantDefaultDatabaseConfig: {
179
+ type: 'postgres',
180
+ host: process.env.DB_HOST,
181
+ port: Number(process.env.DB_PORT),
182
+ username: process.env.DB_USER,
183
+ password: process.env.DB_PASSWORD,
184
+ },
185
+ tenants: [
186
+ { id: 'tenant1', database: 'tenant1_db' },
187
+ { id: 'tenant2', database: 'tenant2_db' },
188
+ ],
189
+ defaultLanguageCode: 'en',
190
+ }),
191
+ })
192
+ ```
261
193
 
262
- @Column({ type: 'varchar', length: 500, name: 'default_message' })
263
- defaultMessage!: string;
194
+ **Exported services:**
195
+ - `LocalizationConfigService`
196
+ - `LocalizationDataSourceProvider`
197
+ - `LanguageService`
198
+ - `TranslationKeyService`
199
+ - `TranslationService`
264
200
 
265
- @Column({ type: 'text', nullable: true })
266
- description!: string | null;
201
+ ---
267
202
 
268
- @Column({ type: 'simple-array', nullable: true })
269
- variables!: string[] | null;
203
+ ## Configuration Reference
270
204
 
271
- @Column({ type: 'boolean', default: true, name: 'is_active' })
272
- isActive!: boolean;
205
+ ```typescript
206
+ interface ILocalizationModuleConfig extends IDataSourceServiceOptions {
207
+ /** Default language code for fallback (default: 'en') */
208
+ defaultLanguageCode?: string;
273
209
 
274
- @Column({ type: 'int', nullable: true })
275
- serial!: number | null;
210
+ /** Enable translation caching (default: false) */
211
+ enableCaching?: boolean;
276
212
 
277
- @OneToMany('Translation', 'translationKey')
278
- translations?: Translation[];
213
+ /** Cache TTL in seconds (default: 300) */
214
+ cacheTtlSeconds?: number;
279
215
  }
280
216
  ```
281
217
 
282
- ### Translation Entity
283
-
284
- ```typescript
285
- @Entity({ name: 'translation' })
286
- export class Translation extends Identity {
287
- @Column({ type: 'uuid', name: 'language_id' })
288
- languageId!: string;
289
-
290
- @Column({ type: 'uuid', name: 'translation_key_id' })
291
- translationKeyId!: string;
292
-
293
- @Column({ type: 'text' })
294
- value!: string;
295
-
296
- @Column({ type: 'boolean', default: false, name: 'is_verified' })
297
- isVerified!: boolean;
218
+ ---
298
219
 
299
- @Column({ type: 'boolean', default: true, name: 'is_active' })
300
- isActive!: boolean;
220
+ ## Feature Toggles
301
221
 
302
- @ManyToOne(() => Language)
303
- @JoinColumn({ name: 'language_id' })
304
- language?: Language;
222
+ | Feature | Config | Default | Effect |
223
+ |---------|--------|---------|--------|
224
+ | Caching | `enableCaching: true` | `false` | Translations cached in HybridCache with `cacheTtlSeconds` TTL |
225
+ | Multi-tenant | `databaseMode: 'multi-tenant'` | `'single'` | Per-tenant DataSource connections |
305
226
 
306
- @ManyToOne(() => TranslationKey)
307
- @JoinColumn({ name: 'translation_key_id' })
308
- translationKey?: TranslationKey;
309
- }
310
- ```
227
+ > **Note:** Localization entities do **not** have company variants (`enableCompanyFeature` is ignored). All translation data is shared across companies.
311
228
 
312
229
  ---
313
230
 
314
- ## Services
231
+ ## API Endpoints
315
232
 
316
- ### LanguageService
233
+ All endpoints use **POST**. Authentication is required unless noted.
317
234
 
318
- CRUD operations for languages:
235
+ ### Languages — `POST /localization/language/*`
319
236
 
320
- ```typescript
321
- @Injectable({ scope: Scope.REQUEST })
322
- export class LanguageService extends RequestScopedApiService<...> {
323
- // Inherited CRUD from RequestScopedApiService
324
- // insert(), update(), delete(), findById(), findAll()
325
-
326
- // Get active languages (public, no auth)
327
- async getActiveLanguages(): Promise<Language[]>;
237
+ | Endpoint | Permission | Description |
238
+ |----------|-----------|-------------|
239
+ | `POST /localization/language/insert` | `language.create` | Create a language |
240
+ | `POST /localization/language/get-all` | Public | List all active languages |
241
+ | `POST /localization/language/get/:id` | `language.read` | Get language by ID |
242
+ | `POST /localization/language/update` | `language.update` | Update language |
243
+ | `POST /localization/language/delete` | `language.delete` | Delete language |
244
+ | `POST /localization/language/set-default` | `language.update` | Set as default language |
328
245
 
329
- // Get default language
330
- async getDefaultLanguage(): Promise<Language | null>;
246
+ **Create language:**
331
247
 
332
- // Set default language (unsets previous default)
333
- async setDefaultLanguage(id: string): Promise<Language>;
248
+ ```json
249
+ POST /localization/language/insert
250
+ {
251
+ "name": "English",
252
+ "nativeName": "English",
253
+ "code": "en",
254
+ "direction": "ltr",
255
+ "isActive": true,
256
+ "isDefault": true
334
257
  }
335
258
  ```
336
259
 
337
- ### TranslationKeyService
338
-
339
- CRUD operations for translation keys:
340
-
341
- ```typescript
342
- @Injectable({ scope: Scope.REQUEST })
343
- export class TranslationKeyService extends RequestScopedApiService<...> {
344
- // Inherited CRUD from RequestScopedApiService
345
-
346
- // Get keys by module
347
- async getByModule(module: string): Promise<TranslationKey[]>;
348
-
349
- // Get all unique modules
350
- async getModules(): Promise<string[]>;
260
+ ```json
261
+ POST /localization/language/insert
262
+ {
263
+ "name": "Arabic",
264
+ "nativeName": "العربية",
265
+ "code": "ar",
266
+ "direction": "rtl",
267
+ "isActive": true
351
268
  }
352
269
  ```
353
270
 
354
- ### TranslationService
355
-
356
- Translation operations with queries:
271
+ ### Translation Keys — `POST /localization/translation-key/*`
357
272
 
358
- ```typescript
359
- @Injectable({ scope: Scope.REQUEST })
360
- export class TranslationService extends RequestScopedApiService<...> {
361
- // Inherited CRUD from RequestScopedApiService
273
+ | Endpoint | Permission | Description |
274
+ |----------|-----------|-------------|
275
+ | `POST /localization/translation-key/insert` | `translation-key.create` | Register a new translation key |
276
+ | `POST /localization/translation-key/get-all` | `translation-key.read` | List all keys |
277
+ | `POST /localization/translation-key/get/:id` | `translation-key.read` | Get key by ID |
278
+ | `POST /localization/translation-key/get-by-module` | `translation-key.read` | Get all keys for a module |
279
+ | `POST /localization/translation-key/update` | `translation-key.update` | Update key (not readonly keys) |
280
+ | `POST /localization/translation-key/delete` | `translation-key.delete` | Delete key (not readonly) |
281
+ | `POST /localization/translation-key/get-missing` | `translation-key.read` | Get keys with no translation for a language |
362
282
 
363
- // Get all translations for a language (public, no auth)
364
- async getByLanguageCode(languageCode: string): Promise<ITranslationsMap>;
283
+ **Register a key:**
365
284
 
366
- // Get translations by module and language (public, no auth)
367
- async getByModuleAndLanguage(module: string, languageCode: string): Promise<ITranslationsMap>;
285
+ ```json
286
+ POST /localization/translation-key/insert
287
+ {
288
+ "key": "auth.login_success",
289
+ "module": "auth",
290
+ "description": "Shown after successful login",
291
+ "defaultValue": "Login successful",
292
+ "variables": [],
293
+ "isReadonly": true
294
+ }
295
+ ```
368
296
 
369
- // Bulk update translation values
370
- async bulkUpdate(updates: BulkUpdateTranslationDto[]): Promise<Translation[]>;
297
+ **Key with variables:**
371
298
 
372
- // Get translation keys missing translations for a language
373
- async getMissingTranslations(languageCode: string): Promise<TranslationKey[]>;
299
+ ```json
300
+ {
301
+ "key": "user.welcome",
302
+ "module": "auth",
303
+ "description": "Welcome message with user name",
304
+ "defaultValue": "Welcome, {{name}}!",
305
+ "variables": ["name"],
306
+ "isReadonly": false
374
307
  }
375
308
  ```
376
309
 
377
- ### LocalizationHelperService
310
+ ### Translations — `POST /localization/translation/*`
378
311
 
379
- Variable interpolation utilities:
312
+ | Endpoint | Permission | Description |
313
+ |----------|-----------|-------------|
314
+ | `POST /localization/translation/insert` | `translation.create` | Add a translation value |
315
+ | `POST /localization/translation/get-all` | `translation.read` | List all translations |
316
+ | `POST /localization/translation/get-by-language` | Public | Get all translations for a language |
317
+ | `POST /localization/translation/get-by-language-module` | Public | Get translations for a language + module |
318
+ | `POST /localization/translation/update` | `translation.update` | Update a translation value |
319
+ | `POST /localization/translation/bulk-update` | `translation.update` | Bulk update translations (atomic) |
320
+ | `POST /localization/translation/delete` | `translation.delete` | Delete a translation |
321
+ | `POST /localization/translation/verify` | `translation.update` | Mark a translation as verified |
322
+ | `POST /localization/translation/unverify` | `translation.update` | Mark a translation as unverified |
380
323
 
381
- ```typescript
382
- @Injectable()
383
- export class LocalizationHelperService {
384
- // Replace {{variable}} placeholders with values
385
- interpolate(template: string, variables?: Record<string, string | number>): string;
324
+ **Add a translation:**
386
325
 
387
- // Extract variable names from template
388
- extractVariables(template: string): string[];
326
+ ```json
327
+ POST /localization/translation/insert
328
+ {
329
+ "keyId": "uuid-of-translation-key",
330
+ "languageId": "uuid-of-language",
331
+ "value": "Connexion réussie"
389
332
  }
390
333
  ```
391
334
 
392
- **Example:**
393
-
394
- ```typescript
395
- const helper = inject(LocalizationHelperService);
396
-
397
- const result = helper.interpolate('Hello, {{name}}!', { name: 'John' });
398
- // Output: "Hello, John!"
335
+ **Bulk update (atomic transaction):**
399
336
 
400
- const vars = helper.extractVariables('Welcome {{user}}, you have {{count}} items');
401
- // Output: ['user', 'count']
337
+ ```json
338
+ POST /localization/translation/bulk-update
339
+ {
340
+ "languageId": "uuid-of-french",
341
+ "translations": [
342
+ { "keyId": "uuid-1", "value": "Connexion réussie" },
343
+ { "keyId": "uuid-2", "value": "Bienvenue, {{name}}!" },
344
+ { "keyId": "uuid-3", "value": "Mot de passe incorrect" }
345
+ ]
346
+ }
402
347
  ```
403
348
 
404
349
  ---
405
350
 
406
- ## Controllers
407
-
408
- All endpoints follow **POST-only RPC** style (not REST).
409
-
410
- ### Language Controller
411
-
412
- | Endpoint | Auth | Permission | Description |
413
- |----------|------|------------|-------------|
414
- | `POST /localization/language/insert` | JWT | `language.create` | Create language |
415
- | `POST /localization/language/update` | JWT | `language.update` | Update language |
416
- | `POST /localization/language/delete` | JWT | `language.delete` | Delete language |
417
- | `POST /localization/language/get-by-id` | JWT | `language.read` | Get by ID |
418
- | `POST /localization/language/get-all` | JWT | `language.read` | Get all with pagination |
419
- | `POST /localization/language/get-active` | None | - | Get active languages |
420
- | `POST /localization/language/get-default` | None | - | Get default language |
421
- | `POST /localization/language/set-default` | JWT | `language.update` | Set default language |
422
-
423
- ### TranslationKey Controller
424
-
425
- | Endpoint | Auth | Permission | Description |
426
- |----------|------|------------|-------------|
427
- | `POST /localization/translation-key/insert` | JWT | `translation-key.create` | Create key |
428
- | `POST /localization/translation-key/update` | JWT | `translation-key.update` | Update key |
429
- | `POST /localization/translation-key/delete` | JWT | `translation-key.delete` | Delete key |
430
- | `POST /localization/translation-key/get-by-id` | JWT | `translation-key.read` | Get by ID |
431
- | `POST /localization/translation-key/get-all` | JWT | `translation-key.read` | Get all with pagination |
432
- | `POST /localization/translation-key/get-by-module` | JWT | `translation-key.read` | Get keys by module |
433
- | `POST /localization/translation-key/get-modules` | JWT | `translation-key.read` | Get all unique module names |
434
-
435
- ### Translation Controller
436
-
437
- | Endpoint | Auth | Permission | Description |
438
- |----------|------|------------|-------------|
439
- | `POST /localization/translation/insert` | JWT | `translation.create` | Create translation |
440
- | `POST /localization/translation/update` | JWT | `translation.update` | Update translation |
441
- | `POST /localization/translation/delete` | JWT | `translation.delete` | Delete translation |
442
- | `POST /localization/translation/get-by-id` | JWT | `translation.read` | Get by ID |
443
- | `POST /localization/translation/get-all` | JWT | `translation.read` | Get all with pagination |
444
- | `POST /localization/translation/get-by-language` | None | - | Get translations by language code |
445
- | `POST /localization/translation/get-by-module` | None | - | Get translations by module + language |
446
- | `POST /localization/translation/update-many-values` | JWT | `translation.update` | Bulk update translations |
447
- | `POST /localization/translation/get-missing` | JWT | `translation.read` | Get missing translations |
448
-
449
- ---
351
+ ## Entities
450
352
 
451
- ## DTOs
353
+ | Entity | Table | Description |
354
+ |--------|-------|-------------|
355
+ | `Language` | `localization_language` | Language with code, direction, default flag |
356
+ | `TranslationKey` | `localization_translation_key` | Key registry with module, variables, readonly flag |
357
+ | `Translation` | `localization_translation` | Per-language translation values with verification status |
452
358
 
453
- ### Language DTOs
359
+ > Localization entities have **no company variants**. All translation data is shared globally.
454
360
 
455
361
  ```typescript
456
- // Create
457
- interface CreateLanguageDto {
458
- code: string; // Required, ISO 639-1
459
- name: string; // Required
460
- nativeName?: string;
461
- direction?: 'ltr' | 'rtl';
462
- isActive?: boolean;
463
- isDefault?: boolean;
464
- serial?: number;
465
- }
466
-
467
- // Update
468
- interface UpdateLanguageDto extends Partial<CreateLanguageDto> {
469
- id: string; // Required
470
- }
362
+ import { LocalizationModule } from '@flusys/nestjs-localization';
471
363
 
472
- // Response
473
- interface LanguageResponseDto {
474
- id: string;
475
- code: string;
476
- name: string;
477
- nativeName: string | null;
478
- direction: 'ltr' | 'rtl' | null;
479
- isActive: boolean;
480
- isDefault: boolean;
481
- serial: number | null;
482
- createdAt: Date;
483
- updatedAt: Date;
484
- }
364
+ TypeOrmModule.forRoot({
365
+ entities: [
366
+ ...LocalizationModule.getEntities(),
367
+ // other entities
368
+ ],
369
+ })
485
370
  ```
486
371
 
487
- ### TranslationKey DTOs
372
+ ---
488
373
 
489
- ```typescript
490
- // Create
491
- interface CreateTranslationKeyDto {
492
- module: string; // Required
493
- key: string; // Required
494
- defaultMessage: string; // Required
495
- description?: string;
496
- variables?: string[];
497
- isActive?: boolean;
498
- serial?: number;
499
- }
374
+ ## Translation Workflow
500
375
 
501
- // Update
502
- interface UpdateTranslationKeyDto extends Partial<CreateTranslationKeyDto> {
503
- id: string; // Required
504
- }
376
+ ```
377
+ 1. Create Language → POST /localization/language/insert
378
+ 2. Register Key → POST /localization/translation-key/insert
379
+ 3. Add Translation → POST /localization/translation/insert
380
+ 4. Verify Translation → POST /localization/translation/verify
381
+ 5. Detect Missing → POST /localization/translation-key/get-missing
382
+ 6. Bulk Update → POST /localization/translation/bulk-update
505
383
  ```
506
384
 
507
- ### Translation DTOs
508
-
509
- ```typescript
510
- // Create
511
- interface CreateTranslationDto {
512
- languageId: string; // Required
513
- translationKeyId: string; // Required
514
- value: string; // Required
515
- isVerified?: boolean;
516
- isActive?: boolean;
517
- }
385
+ Translation statuses:
386
+ - **Unverified** (default) — Draft translation, may not be production-ready
387
+ - **Verified** — Approved by a translator or admin
518
388
 
519
- // Update
520
- interface UpdateTranslationDto extends Partial<CreateTranslationDto> {
521
- id: string; // Required
522
- }
389
+ ---
523
390
 
524
- // Bulk Update
525
- interface BulkUpdateTranslationDto {
526
- id: string;
527
- value: string;
528
- isVerified?: boolean;
529
- }
391
+ ## Variable Interpolation
530
392
 
531
- interface BulkUpdateTranslationsDto {
532
- translations: BulkUpdateTranslationDto[];
533
- }
393
+ Translation values use `{{variableName}}` syntax. The `messageVariables` field in API responses carries the variable values that should be interpolated by the frontend:
534
394
 
535
- // Query DTOs
536
- interface GetTranslationsByLanguageDto {
537
- languageCode: string;
538
- }
395
+ **Server response:**
539
396
 
540
- interface GetTranslationsByModuleDto {
541
- module: string;
542
- languageCode: string;
397
+ ```json
398
+ {
399
+ "success": true,
400
+ "message": "Welcome, John!",
401
+ "messageKey": "user.welcome",
402
+ "messageVariables": { "name": "John" }
543
403
  }
544
404
  ```
545
405
 
546
- ---
547
-
548
- ## Interfaces
549
-
550
- ### ILanguage
406
+ **Frontend interpolation:**
551
407
 
552
408
  ```typescript
553
- interface ILanguage {
554
- id: string;
555
- code: string;
556
- name: string;
557
- nativeName: string | null;
558
- direction: 'ltr' | 'rtl' | null;
559
- isActive: boolean;
560
- isDefault: boolean;
561
- serial: number | null;
562
- }
409
+ // Fetch translation for 'user.welcome': "Bienvenue, {{name}}!"
410
+ const template = translations['user.welcome'];
411
+ const message = template.replace(/\{\{(\w+)\}\}/g, (_, key) => variables[key] ?? key);
412
+ // Result: "Bienvenue, John!"
563
413
  ```
564
414
 
565
- ### ITranslationKey
415
+ ---
566
416
 
567
- ```typescript
568
- interface ITranslationKey {
569
- id: string;
570
- module: string;
571
- key: string;
572
- defaultMessage: string;
573
- description: string | null;
574
- variables: string[] | null;
575
- isActive: boolean;
576
- serial: number | null;
577
- }
578
- ```
417
+ ## Fallback Chain
579
418
 
580
- ### ITranslation
419
+ When a translation is not found for the requested language, the resolution falls back in this order:
581
420
 
582
- ```typescript
583
- interface ITranslation {
584
- id: string;
585
- languageId: string;
586
- translationKeyId: string;
587
- value: string;
588
- isVerified: boolean;
589
- isActive: boolean;
590
- }
591
421
  ```
592
-
593
- ### ITranslationsMap
594
-
595
- ```typescript
596
- type ITranslationsMap = Record<string, string>;
597
-
598
- // Example
599
- {
600
- 'shared.save': 'Save',
601
- 'shared.cancel': 'Cancel',
602
- 'auth.login.title': 'Login',
603
- }
422
+ Requested language → Default language (e.g., 'en') → key.defaultValue → key itself
604
423
  ```
605
424
 
606
425
  ---
607
426
 
608
- ## Permissions
609
-
610
- | Resource | Permission | Description |
611
- |----------|------------|-------------|
612
- | Language | `language.create` | Create languages |
613
- | Language | `language.read` | Read languages |
614
- | Language | `language.update` | Update languages |
615
- | Language | `language.delete` | Delete languages |
616
- | TranslationKey | `translation-key.create` | Create keys |
617
- | TranslationKey | `translation-key.read` | Read keys |
618
- | TranslationKey | `translation-key.update` | Update keys |
619
- | TranslationKey | `translation-key.delete` | Delete keys |
620
- | Translation | `translation.create` | Create translations |
621
- | Translation | `translation.read` | Read translations |
622
- | Translation | `translation.update` | Update translations |
623
- | Translation | `translation.delete` | Delete translations |
427
+ ## RTL Support
624
428
 
625
- ---
429
+ Each language has a `direction` field: `'ltr'` (left-to-right) or `'rtl'` (right-to-left).
626
430
 
627
- ## Data Flow
431
+ The frontend uses this to switch layout direction:
628
432
 
629
- ### Public Translation Request
630
-
631
- ```
632
- Frontend requests translations
633
- ↓
634
- POST /localization/translation/get-by-language
635
- { languageCode: 'en' }
636
- ↓
637
- TranslationController.getByLanguage()
638
- ↓
639
- TranslationService.getByLanguageCode('en')
640
- ↓
641
- Find language by code (active only)
642
- ↓
643
- Query translations with JOIN on translation_key
644
- ↓
645
- Build ITranslationsMap { key: value }
646
- ↓
647
- Return SingleResponseDto<ITranslationsMap>
648
- ```
649
-
650
- ### Admin Translation Update
651
-
652
- ```
653
- Admin updates translations
654
- ↓
655
- POST /localization/translation/update-many-values
656
- { translations: [{ id, value, isVerified }] }
657
- ↓
658
- JWT Auth + permission check (translation.update)
659
- ↓
660
- TranslationController.updateManyValues()
661
- ↓
662
- TranslationService.bulkUpdate(translations)
663
- ↓
664
- Update each translation in database
665
- ↓
666
- Return BulkResponseDto with updated count
433
+ ```typescript
434
+ // Angular
435
+ const lang = await this.http.post('/localization/language/get-all', {}).toPromise();
436
+ const current = lang.data.find(l => l.code === userLanguageCode);
437
+ document.dir = current.direction; // 'ltr' or 'rtl'
667
438
  ```
668
439
 
669
440
  ---
670
441
 
671
- ## Usage Examples
672
-
673
- ### Inject Services
674
-
675
- ```typescript
676
- import { TranslationService, LanguageService } from '@flusys/nestjs-localization';
677
-
678
- @Injectable()
679
- export class MyService {
680
- constructor(
681
- private readonly translationService: TranslationService,
682
- private readonly languageService: LanguageService,
683
- ) {}
684
-
685
- async getTranslationsForUser(languageCode: string) {
686
- return this.translationService.getByLanguageCode(languageCode);
687
- }
688
-
689
- async getActiveLanguages() {
690
- return this.languageService.getActiveLanguages();
691
- }
692
- }
693
- ```
442
+ ## Caching
694
443
 
695
- ### Variable Interpolation
444
+ When `enableCaching: true`, translation lookups are cached with the key pattern `loc:{languageCode}:{module}`. Cache is invalidated when translations are updated via the bulk-update or individual update endpoints.
696
445
 
697
446
  ```typescript
698
- import { LocalizationHelperService } from '@flusys/nestjs-localization';
699
-
700
- @Injectable()
701
- export class NotificationService {
702
- constructor(private readonly helper: LocalizationHelperService) {}
703
-
704
- formatMessage(template: string, user: User) {
705
- return this.helper.interpolate(template, {
706
- name: user.name,
707
- count: user.notifications.length,
708
- });
709
- }
447
+ config: {
448
+ enableCaching: true,
449
+ cacheTtlSeconds: 600, // 10 minutes
710
450
  }
711
451
  ```
712
452
 
713
- ### Seeding Languages
453
+ Requires `HybridCache` (Redis or in-memory) to be configured via `CacheModule` from `@flusys/nestjs-shared`.
714
454
 
715
- ```typescript
716
- // seed/languages.seed.ts
717
- const languages = [
718
- { code: 'en', name: 'English', nativeName: 'English', direction: 'ltr', isDefault: true, isActive: true },
719
- { code: 'ar', name: 'Arabic', nativeName: 'العربية', direction: 'rtl', isActive: true },
720
- { code: 'fr', name: 'French', nativeName: 'Français', direction: 'ltr', isActive: true },
721
- ];
722
-
723
- // Use LanguageService to seed
724
- for (const lang of languages) {
725
- await languageService.insert(lang);
726
- }
727
- ```
455
+ ---
728
456
 
729
- ### Seeding Translation Keys
457
+ ## Exported Services
730
458
 
731
- ```typescript
732
- const keys = [
733
- { module: 'shared', key: 'shared.save', defaultMessage: 'Save' },
734
- { module: 'shared', key: 'shared.cancel', defaultMessage: 'Cancel' },
735
- { module: 'auth', key: 'auth.login.title', defaultMessage: 'Login' },
736
- { module: 'auth', key: 'auth.welcome', defaultMessage: 'Welcome, {{name}}!', variables: ['name'] },
737
- ];
738
- ```
459
+ | Service | Description |
460
+ |---------|-------------|
461
+ | `LanguageService` | Language CRUD, default language management |
462
+ | `TranslationKeyService` | Key CRUD, missing-key detection, module filtering |
463
+ | `TranslationService` | Translation CRUD, bulk update, verification |
464
+ | `LocalizationConfigService` | Runtime config (default language, caching settings) |
465
+ | `LocalizationDataSourceProvider` | Dynamic DataSource per request |
739
466
 
740
467
  ---
741
468
 
742
- ## Caching
469
+ ## Integration with API Responses
743
470
 
744
- When `enableCaching: true`:
471
+ All FLUSYS response DTOs include `messageKey` and optionally `messageVariables`. The frontend uses these to display localized messages:
745
472
 
746
- - Translations are cached using HybridCache
747
- - Cache key format: `localization:translations:{languageCode}`
748
- - TTL configured via `cacheTtlSeconds`
749
- - Cache invalidated on translation updates
750
-
751
- ---
473
+ **Backend (any service):**
752
474
 
753
- ## Frontend Integration
475
+ ```typescript
476
+ return {
477
+ success: true,
478
+ message: 'Welcome, John!',
479
+ messageKey: 'user.welcome',
480
+ messageVariables: { name: 'John' },
481
+ data: userDto,
482
+ };
483
+ ```
754
484
 
755
- The backend serves translations to the frontend using **module-wise loading**:
485
+ **Frontend flow:**
756
486
 
757
- ### Module-Wise Loading Pattern
487
+ 1. On app init: `GET /localization/translation/get-by-language` for the user's language
488
+ 2. Cache translations in a map: `{ 'user.welcome': 'Bienvenue, {{name}}!' }`
489
+ 3. On every API response: look up `messageKey`, interpolate `messageVariables`
490
+ 4. Display the localized message
758
491
 
759
- Frontend loads translations **per-module** as users navigate, NOT all at once:
492
+ ---
760
493
 
761
- ```
762
- App Init
763
- ↓
764
- POST /localization/language/get-active → Load available languages
765
- ↓
766
- User navigates to protected route
767
- ↓
768
- POST /localization/translation/get-by-module
769
- { module: 'shared', languageCode: 'en' }
770
- ↓
771
- POST /localization/translation/get-by-module
772
- { module: 'layout', languageCode: 'en' }
773
- ↓
774
- User navigates to /storage
775
- ↓
776
- POST /localization/translation/get-by-module
777
- { module: 'storage', languageCode: 'en' }
778
- ```
494
+ ## Troubleshooting
779
495
 
780
- ### Available Modules
496
+ **`Language not found` for a valid code**
781
497
 
782
- > **Key Format:** All translation keys use **full dot-case** — every word separated by a dot, all lowercase. No camelCase anywhere in keys.
783
- > ```
784
- > ✅ event.manager.event.create.success
785
- > ❌ eventManager.eventCreateSuccess
786
- > ```
498
+ Use the language's UUID (`id`), not the ISO code, in most endpoints. Use `get-by-language` with the code only for the public translation endpoints.
787
499
 
788
- | Module | Keys Prefix | Description |
789
- |--------|-------------|-------------|
790
- | `shared` | `shared.*` | Common UI labels, actions, validation messages |
791
- | `layout` | `layout.*` | Layout, navigation, topbar labels |
792
- | `auth` | `auth.*` | Login, register, password, user management |
793
- | `iam` | `iam.*` | Roles, permissions, actions |
794
- | `storage` | `storage.*` | File upload, folders, storage config |
795
- | `formBuilder` | `formBuilder.*` | Form builder UI |
796
- | `email` | `email.*` | Email templates, config |
797
- | `notification` | `notification.*` | Notifications |
798
- | `eventManager` | `event.manager.*` | Calendar, events |
799
- | `localization` | `localization.*` | Language & translation admin UI |
500
+ ---
800
501
 
801
- ### When Frontend Localization is ENABLED
502
+ **Translation not returned for a module**
802
503
 
803
- 1. **App Init:** Frontend calls `POST /localization/language/get-active`
804
- 2. **Route Navigation:** Frontend calls `POST /localization/translation/get-by-module` for each feature
805
- 3. **Language Switch:** Frontend reloads translations for already-loaded modules
504
+ Check the `module` field on the `TranslationKey`. The `get-by-language-module` endpoint filters strictly by module name. Ensure it matches exactly (case-sensitive).
806
505
 
807
- ### When Frontend Localization is DISABLED
506
+ ---
808
507
 
809
- 1. Frontend skips all API calls to localization endpoints
810
- 2. Frontend uses hardcoded fallback messages only
811
- 3. Backend localization module is not utilized (can still be included for admin use)
508
+ **Missing keys not detected**
812
509
 
813
- ### Response Format
510
+ `get-missing` returns keys that have no `Translation` record for the given language. If you created a translation but left `value` empty, it is still counted as "present" (not missing). Delete or update the empty translation to see it in the missing list.
814
511
 
815
- All public endpoints return consistent format:
512
+ ---
816
513
 
817
- ```typescript
818
- // GET Active Languages
819
- {
820
- success: true,
821
- data: [
822
- { id: '...', code: 'en', name: 'English', nativeName: 'English', direction: 'ltr', isDefault: true },
823
- { id: '...', code: 'ar', name: 'Arabic', nativeName: 'العربية', direction: 'rtl', isDefault: false }
824
- ]
825
- }
514
+ **Caching returning stale translations**
826
515
 
827
- // GET Translations by Module
828
- {
829
- success: true,
830
- data: {
831
- 'storage.folders.title': 'Folders',
832
- 'storage.upload.success': 'File uploaded successfully',
833
- 'storage.delete.confirm': 'Are you sure you want to delete this file?'
834
- }
835
- }
836
- ```
516
+ When `enableCaching: true`, call `POST /localization/translation/bulk-update` (which invalidates the cache) instead of individual updates if you need immediate cache refresh.
837
517
 
838
518
  ---
839
519
 
840
- ## Best Practices
520
+ **`No metadata for entity`**
841
521
 
842
- | Practice | Description |
843
- |----------|-------------|
844
- | **Module Organization** | Group keys by module (auth, shared, storage) |
845
- | **Key Naming** | Use full dot-case: `module.feature.action` — all lowercase, dots only, no camelCase |
846
- | **Default Messages** | Always provide meaningful defaults |
847
- | **Document Variables** | Use `variables` array to document required placeholders |
848
- | **Description Field** | Add context for translators |
849
- | **Soft Delete** | Use soft delete for translations (recoverable) |
850
- | **Bulk Operations** | Use bulk update for efficiency |
522
+ Register entities:
523
+ ```typescript
524
+ entities: [...LocalizationModule.getEntities()]
525
+ ```
851
526
 
852
527
  ---
853
528
 
854
- ## Public API Exports
529
+ ## License
855
530
 
856
- | Category | Exports |
857
- |----------|---------|
858
- | **Entities** | `Language`, `TranslationKey`, `Translation` |
859
- | **Interfaces** | `ILanguage`, `ITranslationKey`, `ITranslation`, `ITranslationsMap`, `ILocalizationModuleConfig`, `LocalizationModuleOptions` |
860
- | **DTOs** | `CreateLanguageDto`, `UpdateLanguageDto`, `LanguageResponseDto`, `CreateTranslationKeyDto`, `UpdateTranslationKeyDto`, `TranslationKeyResponseDto`, `CreateTranslationDto`, `UpdateTranslationDto`, `TranslationResponseDto`, `BulkUpdateTranslationsDto`, `GetTranslationsByLanguageDto`, `GetTranslationsByModuleDto` |
861
- | **Services** | `LanguageService`, `TranslationKeyService`, `TranslationService`, `LocalizationHelperService`, `LocalizationConfigService`, `LocalizationDataSourceProvider` |
862
- | **Controllers** | `LanguageController`, `TranslationKeyController`, `TranslationController` |
863
- | **Module** | `LocalizationModule` |
864
- | **Constants** | `LOCALIZATION_MODULE_OPTIONS` |
531
+ MIT © FLUSYS
865
532
 
866
533
  ---
867
534
 
868
- **Last Updated:** 2026-03-01
869
- **Version:** 3.0.2
870
- **NestJS Version:** 11
535
+ > Part of the **FLUSYS** framework — a full-stack monorepo powering Angular 21 + NestJS 11 applications.
@@ -4,12 +4,17 @@ import { CreateLanguageDto, LanguageResponseDto, UpdateLanguageDto } from '../dt
4
4
  import { ILanguage } from '../interfaces';
5
5
  import { LanguageService } from '../services';
6
6
  declare const LanguageController_base: abstract new (service: LanguageService) => {
7
+ readonly enabledEndpoints: import("@flusys/nestjs-shared/classes").ApiEndpoint[] | "all";
7
8
  service: LanguageService;
9
+ isEnabled(endpoint: import("@flusys/nestjs-shared/classes").ApiEndpoint): boolean;
8
10
  insert(addDto: CreateLanguageDto, user: ILoggedUserInfo | null): Promise<SingleResponseDto<LanguageResponseDto>>;
9
11
  insertMany(addDto: CreateLanguageDto[], user: ILoggedUserInfo | null): Promise<import("@flusys/nestjs-shared/dtos").BulkResponseDto<LanguageResponseDto>>;
10
12
  getById(id: string, body: import("@flusys/nestjs-shared/dtos").GetByIdBodyDto, user: ILoggedUserInfo | null): Promise<SingleResponseDto<LanguageResponseDto>>;
13
+ getByIds(body: import("@flusys/nestjs-shared/dtos").GetByIdsDto, user: ILoggedUserInfo | null): Promise<ListResponseDto<LanguageResponseDto>>;
11
14
  update(updateDto: UpdateLanguageDto, user: ILoggedUserInfo | null): Promise<SingleResponseDto<LanguageResponseDto>>;
12
15
  updateMany(updateDtos: UpdateLanguageDto[], user: ILoggedUserInfo | null): Promise<import("@flusys/nestjs-shared/dtos").BulkResponseDto<LanguageResponseDto>>;
16
+ bulkUpsert(dtos: (CreateLanguageDto | UpdateLanguageDto)[], user: ILoggedUserInfo | null): Promise<import("@flusys/nestjs-shared/dtos").BulkResponseDto<LanguageResponseDto>>;
17
+ getByFilter(filter: Record<string, any>, user: ILoggedUserInfo | null): Promise<SingleResponseDto<LanguageResponseDto>>;
13
18
  getAll(filterAndPaginationDto: import("@flusys/nestjs-shared/dtos").FilterAndPaginationDto, user: ILoggedUserInfo | null, search?: string): Promise<ListResponseDto<LanguageResponseDto>>;
14
19
  delete(deleteDto: import("@flusys/nestjs-shared/dtos").DeleteDto, user: ILoggedUserInfo | null): Promise<import("@flusys/nestjs-shared/dtos").MessageResponseDto>;
15
20
  };
@@ -4,12 +4,17 @@ import { CreateTranslationKeyDto, GetByModuleDto, TranslationKeyResponseDto, Upd
4
4
  import { ITranslationKey } from '../interfaces';
5
5
  import { TranslationKeyService } from '../services';
6
6
  declare const TranslationKeyController_base: abstract new (service: TranslationKeyService) => {
7
+ readonly enabledEndpoints: import("@flusys/nestjs-shared/classes").ApiEndpoint[] | "all";
7
8
  service: TranslationKeyService;
9
+ isEnabled(endpoint: import("@flusys/nestjs-shared/classes").ApiEndpoint): boolean;
8
10
  insert(addDto: CreateTranslationKeyDto, user: ILoggedUserInfo | null): Promise<SingleResponseDto<TranslationKeyResponseDto>>;
9
11
  insertMany(addDto: CreateTranslationKeyDto[], user: ILoggedUserInfo | null): Promise<import("@flusys/nestjs-shared/dtos").BulkResponseDto<TranslationKeyResponseDto>>;
10
12
  getById(id: string, body: import("@flusys/nestjs-shared/dtos").GetByIdBodyDto, user: ILoggedUserInfo | null): Promise<SingleResponseDto<TranslationKeyResponseDto>>;
13
+ getByIds(body: import("@flusys/nestjs-shared/dtos").GetByIdsDto, user: ILoggedUserInfo | null): Promise<ListResponseDto<TranslationKeyResponseDto>>;
11
14
  update(updateDto: UpdateTranslationKeyDto, user: ILoggedUserInfo | null): Promise<SingleResponseDto<TranslationKeyResponseDto>>;
12
15
  updateMany(updateDtos: UpdateTranslationKeyDto[], user: ILoggedUserInfo | null): Promise<import("@flusys/nestjs-shared/dtos").BulkResponseDto<TranslationKeyResponseDto>>;
16
+ bulkUpsert(dtos: (CreateTranslationKeyDto | UpdateTranslationKeyDto)[], user: ILoggedUserInfo | null): Promise<import("@flusys/nestjs-shared/dtos").BulkResponseDto<TranslationKeyResponseDto>>;
17
+ getByFilter(filter: Record<string, any>, user: ILoggedUserInfo | null): Promise<SingleResponseDto<TranslationKeyResponseDto>>;
13
18
  getAll(filterAndPaginationDto: import("@flusys/nestjs-shared/dtos").FilterAndPaginationDto, user: ILoggedUserInfo | null, search?: string): Promise<ListResponseDto<TranslationKeyResponseDto>>;
14
19
  delete(deleteDto: import("@flusys/nestjs-shared/dtos").DeleteDto, user: ILoggedUserInfo | null): Promise<import("@flusys/nestjs-shared/dtos").MessageResponseDto>;
15
20
  };
@@ -4,12 +4,17 @@ import { BulkUpdateTranslationsDto, CreateTranslationDto, GetTranslationsByLangu
4
4
  import { ITranslation, ITranslationKey, ITranslationsMap } from '../interfaces';
5
5
  import { TranslationService } from '../services';
6
6
  declare const TranslationController_base: abstract new (service: TranslationService) => {
7
+ readonly enabledEndpoints: import("@flusys/nestjs-shared/classes").ApiEndpoint[] | "all";
7
8
  service: TranslationService;
9
+ isEnabled(endpoint: import("@flusys/nestjs-shared/classes").ApiEndpoint): boolean;
8
10
  insert(addDto: CreateTranslationDto, user: ILoggedUserInfo | null): Promise<SingleResponseDto<TranslationResponseDto>>;
9
11
  insertMany(addDto: CreateTranslationDto[], user: ILoggedUserInfo | null): Promise<BulkResponseDto<TranslationResponseDto>>;
10
12
  getById(id: string, body: import("@flusys/nestjs-shared/dtos").GetByIdBodyDto, user: ILoggedUserInfo | null): Promise<SingleResponseDto<TranslationResponseDto>>;
13
+ getByIds(body: import("@flusys/nestjs-shared/dtos").GetByIdsDto, user: ILoggedUserInfo | null): Promise<ListResponseDto<TranslationResponseDto>>;
11
14
  update(updateDto: UpdateTranslationDto, user: ILoggedUserInfo | null): Promise<SingleResponseDto<TranslationResponseDto>>;
12
15
  updateMany(updateDtos: UpdateTranslationDto[], user: ILoggedUserInfo | null): Promise<BulkResponseDto<TranslationResponseDto>>;
16
+ bulkUpsert(dtos: (CreateTranslationDto | UpdateTranslationDto)[], user: ILoggedUserInfo | null): Promise<BulkResponseDto<TranslationResponseDto>>;
17
+ getByFilter(filter: Record<string, any>, user: ILoggedUserInfo | null): Promise<SingleResponseDto<TranslationResponseDto>>;
13
18
  getAll(filterAndPaginationDto: import("@flusys/nestjs-shared/dtos").FilterAndPaginationDto, user: ILoggedUserInfo | null, search?: string): Promise<ListResponseDto<TranslationResponseDto>>;
14
19
  delete(deleteDto: import("@flusys/nestjs-shared/dtos").DeleteDto, user: ILoggedUserInfo | null): Promise<import("@flusys/nestjs-shared/dtos").MessageResponseDto>;
15
20
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@flusys/nestjs-localization",
3
- "version": "4.0.1",
3
+ "version": "4.1.0",
4
4
  "description": "Localization module for multi-language support",
5
5
  "main": "cjs/index.js",
6
6
  "module": "fesm/index.js",
@@ -75,7 +75,7 @@
75
75
  "express": "^4.18.0"
76
76
  },
77
77
  "dependencies": {
78
- "@flusys/nestjs-core": "4.0.1",
79
- "@flusys/nestjs-shared": "4.0.1"
78
+ "@flusys/nestjs-core": "4.1.0",
79
+ "@flusys/nestjs-shared": "4.1.0"
80
80
  }
81
81
  }