@flusys/nestjs-localization 4.0.0-rc

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.
Files changed (89) hide show
  1. package/README.md +870 -0
  2. package/cjs/config/index.js +19 -0
  3. package/cjs/config/localization.constants.js +20 -0
  4. package/cjs/config/message-keys.js +77 -0
  5. package/cjs/controllers/index.js +20 -0
  6. package/cjs/controllers/language.controller.js +188 -0
  7. package/cjs/controllers/translation-key.controller.js +174 -0
  8. package/cjs/controllers/translation.controller.js +254 -0
  9. package/cjs/dtos/index.js +20 -0
  10. package/cjs/dtos/language.dto.js +205 -0
  11. package/cjs/dtos/translation-key.dto.js +233 -0
  12. package/cjs/dtos/translation.dto.js +298 -0
  13. package/cjs/entities/index.js +42 -0
  14. package/cjs/entities/language.entity.js +126 -0
  15. package/cjs/entities/translation-key.entity.js +125 -0
  16. package/cjs/entities/translation.entity.js +120 -0
  17. package/cjs/index.js +24 -0
  18. package/cjs/interfaces/index.js +21 -0
  19. package/cjs/interfaces/language.interface.js +4 -0
  20. package/cjs/interfaces/localization-module-options.interface.js +4 -0
  21. package/cjs/interfaces/translation-key.interface.js +4 -0
  22. package/cjs/interfaces/translation.interface.js +4 -0
  23. package/cjs/modules/index.js +18 -0
  24. package/cjs/modules/localization.module.js +115 -0
  25. package/cjs/services/index.js +22 -0
  26. package/cjs/services/language.service.js +108 -0
  27. package/cjs/services/localization-config.service.js +66 -0
  28. package/cjs/services/localization-datasource.provider.js +122 -0
  29. package/cjs/services/translation-key.service.js +184 -0
  30. package/cjs/services/translation.service.js +222 -0
  31. package/config/index.d.ts +2 -0
  32. package/config/localization.constants.d.ts +2 -0
  33. package/config/message-keys.d.ts +96 -0
  34. package/controllers/index.d.ts +3 -0
  35. package/controllers/language.controller.d.ts +25 -0
  36. package/controllers/translation-key.controller.d.ts +22 -0
  37. package/controllers/translation.controller.d.ts +25 -0
  38. package/dtos/index.d.ts +3 -0
  39. package/dtos/language.dto.d.ts +24 -0
  40. package/dtos/translation-key.dto.d.ts +29 -0
  41. package/dtos/translation.dto.d.ts +42 -0
  42. package/entities/index.d.ts +8 -0
  43. package/entities/language.entity.d.ts +13 -0
  44. package/entities/translation-key.entity.d.ts +13 -0
  45. package/entities/translation.entity.d.ts +12 -0
  46. package/fesm/config/index.js +2 -0
  47. package/fesm/config/localization.constants.js +2 -0
  48. package/fesm/config/message-keys.js +54 -0
  49. package/fesm/controllers/index.js +3 -0
  50. package/fesm/controllers/language.controller.js +178 -0
  51. package/fesm/controllers/translation-key.controller.js +164 -0
  52. package/fesm/controllers/translation.controller.js +244 -0
  53. package/fesm/dtos/index.js +3 -0
  54. package/fesm/dtos/language.dto.js +184 -0
  55. package/fesm/dtos/translation-key.dto.js +209 -0
  56. package/fesm/dtos/translation.dto.js +265 -0
  57. package/fesm/entities/index.js +14 -0
  58. package/fesm/entities/language.entity.js +116 -0
  59. package/fesm/entities/translation-key.entity.js +115 -0
  60. package/fesm/entities/translation.entity.js +110 -0
  61. package/fesm/index.js +7 -0
  62. package/fesm/interfaces/index.js +4 -0
  63. package/fesm/interfaces/language.interface.js +1 -0
  64. package/fesm/interfaces/localization-module-options.interface.js +1 -0
  65. package/fesm/interfaces/translation-key.interface.js +1 -0
  66. package/fesm/interfaces/translation.interface.js +1 -0
  67. package/fesm/modules/index.js +1 -0
  68. package/fesm/modules/localization.module.js +105 -0
  69. package/fesm/services/index.js +5 -0
  70. package/fesm/services/language.service.js +98 -0
  71. package/fesm/services/localization-config.service.js +56 -0
  72. package/fesm/services/localization-datasource.provider.js +112 -0
  73. package/fesm/services/translation-key.service.js +174 -0
  74. package/fesm/services/translation.service.js +212 -0
  75. package/index.d.ts +7 -0
  76. package/interfaces/index.d.ts +4 -0
  77. package/interfaces/language.interface.d.ts +10 -0
  78. package/interfaces/localization-module-options.interface.d.ts +21 -0
  79. package/interfaces/translation-key.interface.d.ts +11 -0
  80. package/interfaces/translation.interface.d.ts +13 -0
  81. package/modules/index.d.ts +1 -0
  82. package/modules/localization.module.d.ts +8 -0
  83. package/package.json +80 -0
  84. package/services/index.d.ts +5 -0
  85. package/services/language.service.d.ts +18 -0
  86. package/services/localization-config.service.d.ts +11 -0
  87. package/services/localization-datasource.provider.d.ts +16 -0
  88. package/services/translation-key.service.d.ts +23 -0
  89. package/services/translation.service.d.ts +30 -0
@@ -0,0 +1,105 @@
1
+ function _ts_decorate(decorators, target, key, desc) {
2
+ var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
3
+ if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
4
+ else for(var i = decorators.length - 1; i >= 0; i--)if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
5
+ return c > 3 && r && Object.defineProperty(target, key, r), r;
6
+ }
7
+ import { CacheModule, UtilsModule } from '@flusys/nestjs-shared/modules';
8
+ import { Module } from '@nestjs/common';
9
+ import { LOCALIZATION_MODULE_OPTIONS } from '../config/localization.constants';
10
+ import { LanguageController, TranslationKeyController, TranslationController } from '../controllers';
11
+ import { LanguageService, LocalizationConfigService, LocalizationDataSourceProvider, TranslationKeyService, TranslationService } from '../services';
12
+ const CONTROLLERS = [
13
+ LanguageController,
14
+ TranslationKeyController,
15
+ TranslationController
16
+ ];
17
+ const EXPORTED_SERVICES = [
18
+ LocalizationConfigService,
19
+ LocalizationDataSourceProvider,
20
+ LanguageService,
21
+ TranslationKeyService,
22
+ TranslationService
23
+ ];
24
+ export class LocalizationModule {
25
+ static forRoot(options) {
26
+ return {
27
+ module: LocalizationModule,
28
+ global: options.global ?? false,
29
+ imports: [
30
+ CacheModule,
31
+ UtilsModule
32
+ ],
33
+ controllers: options.includeController !== false ? CONTROLLERS : [],
34
+ providers: this.buildProviders(options),
35
+ exports: EXPORTED_SERVICES
36
+ };
37
+ }
38
+ static forRootAsync(options) {
39
+ return {
40
+ module: LocalizationModule,
41
+ global: options.global ?? false,
42
+ imports: [
43
+ ...options.imports || [],
44
+ CacheModule,
45
+ UtilsModule
46
+ ],
47
+ controllers: options.includeController !== false ? CONTROLLERS : [],
48
+ providers: [
49
+ ...this.createAsyncProviders(options),
50
+ ...this.buildProviders()
51
+ ],
52
+ exports: EXPORTED_SERVICES
53
+ };
54
+ }
55
+ static buildProviders(options) {
56
+ const providers = [
57
+ ...EXPORTED_SERVICES
58
+ ];
59
+ if (options) {
60
+ providers.unshift({
61
+ provide: LOCALIZATION_MODULE_OPTIONS,
62
+ useValue: options
63
+ });
64
+ }
65
+ return providers;
66
+ }
67
+ static createAsyncProviders(options) {
68
+ if (options.useFactory) {
69
+ return [
70
+ {
71
+ provide: LOCALIZATION_MODULE_OPTIONS,
72
+ useFactory: async (...args)=>({
73
+ ...options,
74
+ config: await options.useFactory(...args)
75
+ }),
76
+ inject: options.inject || []
77
+ }
78
+ ];
79
+ }
80
+ const factoryClass = options.useClass || options.useExisting;
81
+ if (!factoryClass) return [];
82
+ const providers = [
83
+ {
84
+ provide: LOCALIZATION_MODULE_OPTIONS,
85
+ useFactory: async (factory)=>({
86
+ ...options,
87
+ config: await factory.createLocalizationOptions()
88
+ }),
89
+ inject: [
90
+ factoryClass
91
+ ]
92
+ }
93
+ ];
94
+ if (options.useClass) {
95
+ providers.push({
96
+ provide: options.useClass,
97
+ useClass: options.useClass
98
+ });
99
+ }
100
+ return providers;
101
+ }
102
+ }
103
+ LocalizationModule = _ts_decorate([
104
+ Module({})
105
+ ], LocalizationModule);
@@ -0,0 +1,5 @@
1
+ export * from './localization-config.service';
2
+ export * from './localization-datasource.provider';
3
+ export * from './language.service';
4
+ export * from './translation-key.service';
5
+ export * from './translation.service';
@@ -0,0 +1,98 @@
1
+ function _define_property(obj, key, value) {
2
+ if (key in obj) {
3
+ Object.defineProperty(obj, key, {
4
+ value: value,
5
+ enumerable: true,
6
+ configurable: true,
7
+ writable: true
8
+ });
9
+ } else {
10
+ obj[key] = value;
11
+ }
12
+ return obj;
13
+ }
14
+ function _ts_decorate(decorators, target, key, desc) {
15
+ var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
16
+ if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
17
+ else for(var i = decorators.length - 1; i >= 0; i--)if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
18
+ return c > 3 && r && Object.defineProperty(target, key, r), r;
19
+ }
20
+ function _ts_metadata(k, v) {
21
+ if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
22
+ }
23
+ function _ts_param(paramIndex, decorator) {
24
+ return function(target, key) {
25
+ decorator(target, key, paramIndex);
26
+ };
27
+ }
28
+ import { HybridCache, RequestScopedApiService } from '@flusys/nestjs-shared/classes';
29
+ import { UtilsService } from '@flusys/nestjs-shared/modules';
30
+ import { Inject, Injectable, Scope } from '@nestjs/common';
31
+ import { IsNull } from 'typeorm';
32
+ import { Language } from '../entities';
33
+ import { LocalizationDataSourceProvider } from './localization-datasource.provider';
34
+ export class LanguageService extends RequestScopedApiService {
35
+ resolveEntity() {
36
+ return Language;
37
+ }
38
+ getDataSourceProvider() {
39
+ return this.dataSourceProvider;
40
+ }
41
+ async getDefaultLanguage() {
42
+ await this.ensureRepositoryInitialized();
43
+ return this.repository.findOne({
44
+ where: {
45
+ isDefault: true,
46
+ deletedAt: IsNull()
47
+ }
48
+ });
49
+ }
50
+ async getActiveLanguages() {
51
+ await this.ensureRepositoryInitialized();
52
+ return this.repository.find({
53
+ where: {
54
+ isActive: true,
55
+ deletedAt: IsNull()
56
+ },
57
+ order: {
58
+ serial: 'ASC',
59
+ name: 'ASC'
60
+ }
61
+ });
62
+ }
63
+ async setAsDefault(id) {
64
+ await this.ensureRepositoryInitialized();
65
+ await this.repository.update({
66
+ isDefault: true
67
+ }, {
68
+ isDefault: false
69
+ });
70
+ await this.repository.update({
71
+ id
72
+ }, {
73
+ isDefault: true
74
+ });
75
+ return this.repository.findOne({
76
+ where: {
77
+ id
78
+ }
79
+ });
80
+ }
81
+ constructor(cacheManager, utilsService, dataSourceProvider){
82
+ super('language', null, cacheManager, utilsService, LanguageService.name, true, 'localization'), _define_property(this, "cacheManager", void 0), _define_property(this, "utilsService", void 0), _define_property(this, "dataSourceProvider", void 0), this.cacheManager = cacheManager, this.utilsService = utilsService, this.dataSourceProvider = dataSourceProvider;
83
+ }
84
+ }
85
+ LanguageService = _ts_decorate([
86
+ Injectable({
87
+ scope: Scope.REQUEST
88
+ }),
89
+ _ts_param(0, Inject('CACHE_INSTANCE')),
90
+ _ts_param(1, Inject(UtilsService)),
91
+ _ts_param(2, Inject(LocalizationDataSourceProvider)),
92
+ _ts_metadata("design:type", Function),
93
+ _ts_metadata("design:paramtypes", [
94
+ typeof HybridCache === "undefined" ? Object : HybridCache,
95
+ typeof UtilsService === "undefined" ? Object : UtilsService,
96
+ typeof LocalizationDataSourceProvider === "undefined" ? Object : LocalizationDataSourceProvider
97
+ ])
98
+ ], LanguageService);
@@ -0,0 +1,56 @@
1
+ function _define_property(obj, key, value) {
2
+ if (key in obj) {
3
+ Object.defineProperty(obj, key, {
4
+ value: value,
5
+ enumerable: true,
6
+ configurable: true,
7
+ writable: true
8
+ });
9
+ } else {
10
+ obj[key] = value;
11
+ }
12
+ return obj;
13
+ }
14
+ function _ts_decorate(decorators, target, key, desc) {
15
+ var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
16
+ if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
17
+ else for(var i = decorators.length - 1; i >= 0; i--)if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
18
+ return c > 3 && r && Object.defineProperty(target, key, r), r;
19
+ }
20
+ function _ts_metadata(k, v) {
21
+ if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
22
+ }
23
+ function _ts_param(paramIndex, decorator) {
24
+ return function(target, key) {
25
+ decorator(target, key, paramIndex);
26
+ };
27
+ }
28
+ import { Inject, Injectable } from '@nestjs/common';
29
+ import { LOCALIZATION_MODULE_OPTIONS } from '../config/localization.constants';
30
+ import { LocalizationModuleOptions } from '../interfaces';
31
+ export class LocalizationConfigService {
32
+ isCompanyFeatureEnabled() {
33
+ return false;
34
+ }
35
+ getDatabaseMode() {
36
+ return this.options.bootstrapAppConfig?.databaseMode ?? 'single';
37
+ }
38
+ isMultiTenant() {
39
+ return this.getDatabaseMode() === 'multi-tenant';
40
+ }
41
+ getOptions() {
42
+ return this.options;
43
+ }
44
+ constructor(options){
45
+ _define_property(this, "options", void 0);
46
+ this.options = options;
47
+ }
48
+ }
49
+ LocalizationConfigService = _ts_decorate([
50
+ Injectable(),
51
+ _ts_param(0, Inject(LOCALIZATION_MODULE_OPTIONS)),
52
+ _ts_metadata("design:type", Function),
53
+ _ts_metadata("design:paramtypes", [
54
+ typeof LocalizationModuleOptions === "undefined" ? Object : LocalizationModuleOptions
55
+ ])
56
+ ], LocalizationConfigService);
@@ -0,0 +1,112 @@
1
+ function _define_property(obj, key, value) {
2
+ if (key in obj) {
3
+ Object.defineProperty(obj, key, {
4
+ value: value,
5
+ enumerable: true,
6
+ configurable: true,
7
+ writable: true
8
+ });
9
+ } else {
10
+ obj[key] = value;
11
+ }
12
+ return obj;
13
+ }
14
+ function _ts_decorate(decorators, target, key, desc) {
15
+ var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
16
+ if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
17
+ else for(var i = decorators.length - 1; i >= 0; i--)if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
18
+ return c > 3 && r && Object.defineProperty(target, key, r), r;
19
+ }
20
+ function _ts_metadata(k, v) {
21
+ if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
22
+ }
23
+ function _ts_param(paramIndex, decorator) {
24
+ return function(target, key) {
25
+ decorator(target, key, paramIndex);
26
+ };
27
+ }
28
+ import { MultiTenantDataSourceService } from '@flusys/nestjs-shared/modules';
29
+ import { SYSTEM_MESSAGES } from '@flusys/nestjs-shared/constants';
30
+ import { Inject, Injectable, InternalServerErrorException, Optional, Scope } from '@nestjs/common';
31
+ import { REQUEST } from '@nestjs/core';
32
+ import { Request } from 'express';
33
+ import { getLocalizationEntities } from '../entities';
34
+ import { LocalizationConfigService } from './localization-config.service';
35
+ export class LocalizationDataSourceProvider extends MultiTenantDataSourceService {
36
+ static buildParentOptions(options) {
37
+ return {
38
+ bootstrapAppConfig: options.bootstrapAppConfig,
39
+ defaultDatabaseConfig: options.config?.defaultDatabaseConfig,
40
+ tenantDefaultDatabaseConfig: options.config?.tenantDefaultDatabaseConfig,
41
+ tenants: options.config?.tenants
42
+ };
43
+ }
44
+ async createDataSourceFromConfig(config) {
45
+ const entities = getLocalizationEntities();
46
+ return super.createDataSourceFromConfig(config, entities);
47
+ }
48
+ async getSingleDataSource() {
49
+ if (LocalizationDataSourceProvider.singleDataSource?.isInitialized) {
50
+ return LocalizationDataSourceProvider.singleDataSource;
51
+ }
52
+ if (LocalizationDataSourceProvider.singleConnectionLock) {
53
+ return LocalizationDataSourceProvider.singleConnectionLock;
54
+ }
55
+ const config = this.getDefaultDatabaseConfig();
56
+ if (!config) {
57
+ throw new InternalServerErrorException({
58
+ message: 'No database config available. Provide defaultDatabaseConfig or tenantDefaultDatabaseConfig.',
59
+ messageKey: SYSTEM_MESSAGES.DATABASE_CONFIG_NOT_AVAILABLE
60
+ });
61
+ }
62
+ const connectionPromise = this.createDataSourceFromConfig(config);
63
+ LocalizationDataSourceProvider.singleConnectionLock = connectionPromise;
64
+ try {
65
+ const dataSource = await connectionPromise;
66
+ LocalizationDataSourceProvider.singleDataSource = dataSource;
67
+ return dataSource;
68
+ } finally{
69
+ LocalizationDataSourceProvider.singleConnectionLock = null;
70
+ }
71
+ }
72
+ async getOrCreateTenantConnection(tenant) {
73
+ const existing = LocalizationDataSourceProvider.tenantConnections.get(tenant.id);
74
+ if (existing?.isInitialized) {
75
+ return existing;
76
+ }
77
+ const pendingConnection = LocalizationDataSourceProvider.connectionLocks.get(tenant.id);
78
+ if (pendingConnection) {
79
+ return pendingConnection;
80
+ }
81
+ const config = this.buildTenantDatabaseConfig(tenant);
82
+ const connectionPromise = this.createDataSourceFromConfig(config);
83
+ LocalizationDataSourceProvider.connectionLocks.set(tenant.id, connectionPromise);
84
+ try {
85
+ const dataSource = await connectionPromise;
86
+ LocalizationDataSourceProvider.tenantConnections.set(tenant.id, dataSource);
87
+ return dataSource;
88
+ } finally{
89
+ LocalizationDataSourceProvider.connectionLocks.delete(tenant.id);
90
+ }
91
+ }
92
+ constructor(configService, request){
93
+ super(LocalizationDataSourceProvider.buildParentOptions(configService.getOptions()), request);
94
+ }
95
+ }
96
+ _define_property(LocalizationDataSourceProvider, "tenantConnections", new Map());
97
+ _define_property(LocalizationDataSourceProvider, "singleDataSource", null);
98
+ _define_property(LocalizationDataSourceProvider, "connectionLocks", new Map());
99
+ _define_property(LocalizationDataSourceProvider, "singleConnectionLock", null);
100
+ LocalizationDataSourceProvider = _ts_decorate([
101
+ Injectable({
102
+ scope: Scope.REQUEST
103
+ }),
104
+ _ts_param(0, Inject(LocalizationConfigService)),
105
+ _ts_param(1, Optional()),
106
+ _ts_param(1, Inject(REQUEST)),
107
+ _ts_metadata("design:type", Function),
108
+ _ts_metadata("design:paramtypes", [
109
+ typeof LocalizationConfigService === "undefined" ? Object : LocalizationConfigService,
110
+ typeof Request === "undefined" ? Object : Request
111
+ ])
112
+ ], LocalizationDataSourceProvider);
@@ -0,0 +1,174 @@
1
+ function _define_property(obj, key, value) {
2
+ if (key in obj) {
3
+ Object.defineProperty(obj, key, {
4
+ value: value,
5
+ enumerable: true,
6
+ configurable: true,
7
+ writable: true
8
+ });
9
+ } else {
10
+ obj[key] = value;
11
+ }
12
+ return obj;
13
+ }
14
+ function _ts_decorate(decorators, target, key, desc) {
15
+ var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
16
+ if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
17
+ else for(var i = decorators.length - 1; i >= 0; i--)if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
18
+ return c > 3 && r && Object.defineProperty(target, key, r), r;
19
+ }
20
+ function _ts_metadata(k, v) {
21
+ if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
22
+ }
23
+ function _ts_param(paramIndex, decorator) {
24
+ return function(target, key) {
25
+ decorator(target, key, paramIndex);
26
+ };
27
+ }
28
+ import { HybridCache, RequestScopedApiService } from '@flusys/nestjs-shared/classes';
29
+ import { TRANSLATION_KEY_MESSAGES } from '../config';
30
+ import { UtilsService } from '@flusys/nestjs-shared/modules';
31
+ import { BadRequestException, ForbiddenException, Inject, Injectable, Scope } from '@nestjs/common';
32
+ import { In, IsNull } from 'typeorm';
33
+ import { TranslationKey } from '../entities';
34
+ import { LocalizationDataSourceProvider } from './localization-datasource.provider';
35
+ export class TranslationKeyService extends RequestScopedApiService {
36
+ resolveEntity() {
37
+ return TranslationKey;
38
+ }
39
+ getDataSourceProvider() {
40
+ return this.dataSourceProvider;
41
+ }
42
+ async beforeInsertOperation(dto, _user, _queryRunner) {
43
+ const dtos = Array.isArray(dto) ? dto : [
44
+ dto
45
+ ];
46
+ await this.validateNoDuplicateKeys(dtos.map((d)=>({
47
+ module: d.module,
48
+ key: d.key
49
+ })));
50
+ }
51
+ async beforeUpdateOperation(dto, _user, _queryRunner) {
52
+ const dtos = Array.isArray(dto) ? dto : [
53
+ dto
54
+ ];
55
+ const ids = dtos.map((d)=>d.id);
56
+ await this.ensureRepositoryInitialized();
57
+ const existingKeys = await this.repository.find({
58
+ where: {
59
+ id: In(ids),
60
+ deletedAt: IsNull()
61
+ }
62
+ });
63
+ // Check if any readonly keys are being updated with key/module changes
64
+ for (const d of dtos){
65
+ const existing = existingKeys.find((e)=>e.id === d.id);
66
+ if (existing?.isReadonly && (d.key || d.module)) {
67
+ throw new ForbiddenException({
68
+ message: `Cannot modify key or module of readonly translation key "${existing.key}"`,
69
+ messageKey: TRANSLATION_KEY_MESSAGES.READONLY_UPDATE_FORBIDDEN,
70
+ messageParams: {
71
+ key: existing.key
72
+ }
73
+ });
74
+ }
75
+ }
76
+ // Validate no duplicates for keys being changed
77
+ const keysToValidate = dtos.filter((d)=>d.key || d.module).map((d)=>{
78
+ const existing = existingKeys.find((e)=>e.id === d.id);
79
+ return {
80
+ module: d.module ?? existing?.module ?? '',
81
+ key: d.key ?? existing?.key ?? '',
82
+ excludeId: d.id
83
+ };
84
+ });
85
+ if (keysToValidate.length > 0) {
86
+ await this.validateNoDuplicateKeys(keysToValidate);
87
+ }
88
+ }
89
+ async beforeDeleteOperation(option, _user, _queryRunner) {
90
+ const ids = Array.isArray(option.id) ? option.id : [
91
+ option.id
92
+ ];
93
+ await this.ensureRepositoryInitialized();
94
+ const readonlyKeys = await this.repository.find({
95
+ where: {
96
+ id: In(ids),
97
+ isReadonly: true,
98
+ deletedAt: IsNull()
99
+ }
100
+ });
101
+ if (readonlyKeys.length > 0) {
102
+ const keys = readonlyKeys.map((k)=>k.key).join(', ');
103
+ throw new ForbiddenException({
104
+ message: `Cannot delete readonly translation keys: ${keys}`,
105
+ messageKey: TRANSLATION_KEY_MESSAGES.READONLY_DELETE_FORBIDDEN,
106
+ messageParams: {
107
+ keys
108
+ }
109
+ });
110
+ }
111
+ }
112
+ async findByModule(module) {
113
+ await this.ensureRepositoryInitialized();
114
+ return this.repository.find({
115
+ where: {
116
+ module,
117
+ isActive: true,
118
+ deletedAt: IsNull()
119
+ },
120
+ order: {
121
+ serial: 'ASC',
122
+ key: 'ASC'
123
+ }
124
+ });
125
+ }
126
+ async getModules() {
127
+ await this.ensureRepositoryInitialized();
128
+ const result = await this.repository.createQueryBuilder('tk').select('DISTINCT tk.module', 'module').where('tk.deleted_at IS NULL').getRawMany();
129
+ return result.map((r)=>r.module);
130
+ }
131
+ async validateNoDuplicateKeys(keys) {
132
+ await this.ensureRepositoryInitialized();
133
+ for (const { module, key, excludeId } of keys){
134
+ const query = this.repository.createQueryBuilder('tk').where('tk.module = :module', {
135
+ module
136
+ }).andWhere('tk.key = :key', {
137
+ key
138
+ }).andWhere('tk.deleted_at IS NULL');
139
+ if (excludeId) {
140
+ query.andWhere('tk.id != :excludeId', {
141
+ excludeId
142
+ });
143
+ }
144
+ const existing = await query.getOne();
145
+ if (existing) {
146
+ throw new BadRequestException({
147
+ message: `Translation key "${key}" already exists in module "${module}"`,
148
+ messageKey: TRANSLATION_KEY_MESSAGES.DUPLICATE_KEY_IN_MODULE,
149
+ messageParams: {
150
+ key,
151
+ module
152
+ }
153
+ });
154
+ }
155
+ }
156
+ }
157
+ constructor(cacheManager, utilsService, dataSourceProvider){
158
+ super('translationKey', null, cacheManager, utilsService, TranslationKeyService.name, true, 'localization'), _define_property(this, "cacheManager", void 0), _define_property(this, "utilsService", void 0), _define_property(this, "dataSourceProvider", void 0), this.cacheManager = cacheManager, this.utilsService = utilsService, this.dataSourceProvider = dataSourceProvider;
159
+ }
160
+ }
161
+ TranslationKeyService = _ts_decorate([
162
+ Injectable({
163
+ scope: Scope.REQUEST
164
+ }),
165
+ _ts_param(0, Inject('CACHE_INSTANCE')),
166
+ _ts_param(1, Inject(UtilsService)),
167
+ _ts_param(2, Inject(LocalizationDataSourceProvider)),
168
+ _ts_metadata("design:type", Function),
169
+ _ts_metadata("design:paramtypes", [
170
+ typeof HybridCache === "undefined" ? Object : HybridCache,
171
+ typeof UtilsService === "undefined" ? Object : UtilsService,
172
+ typeof LocalizationDataSourceProvider === "undefined" ? Object : LocalizationDataSourceProvider
173
+ ])
174
+ ], TranslationKeyService);