@flusys/nestjs-localization 9.1.2 → 9.2.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 +8 -4
- package/controllers/language.controller.d.ts +5 -7
- package/controllers/translation-key.controller.d.ts +1 -0
- package/controllers/translation.controller.d.ts +3 -2
- package/dtos/dto-transforms.d.ts +4 -0
- package/dtos/language.dto.d.ts +3 -0
- package/fesm/349.js +102 -51
- package/fesm/{692.js → 901.js} +127 -52
- package/fesm/controllers/index.js +1 -1
- package/fesm/docs/index.js +1 -3
- package/fesm/dtos/index.js +79 -45
- package/fesm/index.js +9 -7
- package/fesm/modules/index.js +3 -3
- package/fesm/services/index.js +2 -2
- package/package.json +3 -3
- package/services/language.service.d.ts +8 -2
- package/services/translation-key.service.d.ts +1 -0
- package/services/translation.service.d.ts +1 -0
package/fesm/{692.js → 901.js}
RENAMED
|
@@ -5,8 +5,9 @@ import * as __rspack_external__flusys_nestjs_shared_modules_24702bee from "@flus
|
|
|
5
5
|
import * as __rspack_external__nestjs_common_a04939da from "@nestjs/common";
|
|
6
6
|
import * as __rspack_external_typeorm from "typeorm";
|
|
7
7
|
import * as __rspack_external__nestjs_core_d7b00a1a from "@nestjs/core";
|
|
8
|
-
|
|
9
|
-
export const
|
|
8
|
+
import * as __rspack_external__flusys_nestjs_shared_constants_81f33d5b from "@flusys/nestjs-shared/constants";
|
|
9
|
+
export const __rspack_esm_id = 901;
|
|
10
|
+
export const __rspack_esm_ids = [901];
|
|
10
11
|
export const __webpack_modules__ = {
|
|
11
12
|
102(__unused_rspack_module, __webpack_exports__, __webpack_require__) {
|
|
12
13
|
/**
|
|
@@ -459,7 +460,7 @@ function getLocalizationEntities() {
|
|
|
459
460
|
|
|
460
461
|
|
|
461
462
|
},
|
|
462
|
-
|
|
463
|
+
140(__unused_rspack_module, __webpack_exports__, __webpack_require__) {
|
|
463
464
|
|
|
464
465
|
// EXPORTS
|
|
465
466
|
__webpack_require__.d(__webpack_exports__, {
|
|
@@ -601,6 +602,8 @@ LocalizationDataSourceProvider = localization_datasource_provider_ts_decorate([
|
|
|
601
602
|
|
|
602
603
|
// EXTERNAL MODULE: external "@flusys/nestjs-shared/classes"
|
|
603
604
|
var classes_ = __webpack_require__(937);
|
|
605
|
+
;// CONCATENATED MODULE: external "@flusys/nestjs-shared/constants"
|
|
606
|
+
|
|
604
607
|
// EXTERNAL MODULE: external "typeorm"
|
|
605
608
|
var external_typeorm_ = __webpack_require__(158);
|
|
606
609
|
// EXTERNAL MODULE: ./projects/nestjs-localization/src/config/event-actions.ts
|
|
@@ -638,19 +641,30 @@ function language_service_ts_param(paramIndex, decorator) {
|
|
|
638
641
|
|
|
639
642
|
|
|
640
643
|
|
|
644
|
+
|
|
641
645
|
class LanguageService extends classes_.ApiService {
|
|
642
646
|
cacheManager;
|
|
643
647
|
utilsService;
|
|
644
648
|
constructor(cacheManager, utilsService, dataSourceProvider){
|
|
645
649
|
super(event_actions/* .LOCALIZATION_EVENT_ENTITIES.LANGUAGE */.JX.LANGUAGE, cacheManager, utilsService, LanguageService.name, true, event_actions/* .LOCALIZATION_EVENT_MODULE */.ec, dataSourceProvider, src_entities/* .Language */.TM), this.cacheManager = cacheManager, this.utilsService = utilsService;
|
|
646
650
|
}
|
|
647
|
-
async
|
|
651
|
+
async beforeInsertOperation(dto, _user, queryRunner) {
|
|
652
|
+
const dtos = Array.isArray(dto) ? dto : [
|
|
653
|
+
dto
|
|
654
|
+
];
|
|
655
|
+
if (dtos.some((d)=>d.isDefault === true)) {
|
|
656
|
+
await this.lockLanguages(queryRunner.manager);
|
|
657
|
+
}
|
|
658
|
+
}
|
|
659
|
+
async beforeUpdateOperation(dto, _user, queryRunner) {
|
|
648
660
|
const dtos = Array.isArray(dto) ? dto : [
|
|
649
661
|
dto
|
|
650
662
|
];
|
|
651
663
|
const ids = dtos.map((d)=>d.id);
|
|
652
|
-
|
|
653
|
-
|
|
664
|
+
if (dtos.some((d)=>d.isDefault === true)) {
|
|
665
|
+
await this.lockLanguages(queryRunner.manager);
|
|
666
|
+
}
|
|
667
|
+
const existingLanguages = await queryRunner.manager.find(src_entities/* .Language */.TM, {
|
|
654
668
|
where: {
|
|
655
669
|
id: (0,external_typeorm_.In)(ids),
|
|
656
670
|
deletedAt: (0,external_typeorm_.IsNull)()
|
|
@@ -673,12 +687,52 @@ class LanguageService extends classes_.ApiService {
|
|
|
673
687
|
}
|
|
674
688
|
}
|
|
675
689
|
}
|
|
676
|
-
async
|
|
690
|
+
async afterInsertOperation(entities, _dto, _user, queryRunner) {
|
|
691
|
+
await this.keepSingleDefault(entities, queryRunner);
|
|
692
|
+
}
|
|
693
|
+
async afterUpdateOperation(entities, _dto, _user, queryRunner) {
|
|
694
|
+
await this.keepSingleDefault(entities, queryRunner);
|
|
695
|
+
}
|
|
696
|
+
async keepSingleDefault(entities, queryRunner) {
|
|
697
|
+
const newDefault = entities.findLast((entity)=>entity.isDefault);
|
|
698
|
+
if (!newDefault) {
|
|
699
|
+
return;
|
|
700
|
+
}
|
|
701
|
+
await queryRunner.manager.update(src_entities/* .Language */.TM, {
|
|
702
|
+
isDefault: true,
|
|
703
|
+
id: (0,external_typeorm_.Not)(newDefault.id)
|
|
704
|
+
}, {
|
|
705
|
+
isDefault: false
|
|
706
|
+
});
|
|
707
|
+
for (const entity of entities){
|
|
708
|
+
if (entity !== newDefault) {
|
|
709
|
+
entity.isDefault = false;
|
|
710
|
+
}
|
|
711
|
+
}
|
|
712
|
+
}
|
|
713
|
+
// Every default switch and delete takes this lock first (same row order), so concurrent
|
|
714
|
+
// callers serialize: none can demote a stale snapshot and leave two defaults, or delete
|
|
715
|
+
// a language that has just become the default.
|
|
716
|
+
async lockLanguages(manager) {
|
|
717
|
+
await manager.find(src_entities/* .Language */.TM, {
|
|
718
|
+
select: {
|
|
719
|
+
id: true
|
|
720
|
+
},
|
|
721
|
+
order: {
|
|
722
|
+
id: 'ASC'
|
|
723
|
+
},
|
|
724
|
+
withDeleted: true,
|
|
725
|
+
lock: {
|
|
726
|
+
mode: 'pessimistic_write'
|
|
727
|
+
}
|
|
728
|
+
});
|
|
729
|
+
}
|
|
730
|
+
async beforeDeleteOperation(dto, _user, queryRunner) {
|
|
677
731
|
const ids = Array.isArray(dto.id) ? dto.id : [
|
|
678
732
|
dto.id
|
|
679
733
|
];
|
|
680
|
-
await this.
|
|
681
|
-
const languages = await
|
|
734
|
+
await this.lockLanguages(queryRunner.manager);
|
|
735
|
+
const languages = await queryRunner.manager.find(src_entities/* .Language */.TM, {
|
|
682
736
|
where: {
|
|
683
737
|
id: (0,external_typeorm_.In)(ids),
|
|
684
738
|
deletedAt: (0,external_typeorm_.IsNull)()
|
|
@@ -707,6 +761,14 @@ class LanguageService extends classes_.ApiService {
|
|
|
707
761
|
});
|
|
708
762
|
}
|
|
709
763
|
}
|
|
764
|
+
// A permanent delete cascades (FK) to the language's translations
|
|
765
|
+
async delete(option, user) {
|
|
766
|
+
const result = await super.delete(option, user);
|
|
767
|
+
if (option.type === 'permanent') {
|
|
768
|
+
await this.utilsService.clearCache(event_actions/* .LOCALIZATION_EVENT_ENTITIES.TRANSLATION */.JX.TRANSLATION, this.cacheManager, this.cacheTenant());
|
|
769
|
+
}
|
|
770
|
+
return result;
|
|
771
|
+
}
|
|
710
772
|
async getDefaultLanguage() {
|
|
711
773
|
await this.ensureDataSourceRepository();
|
|
712
774
|
return this.repository.findOne({
|
|
@@ -731,36 +793,38 @@ class LanguageService extends classes_.ApiService {
|
|
|
731
793
|
}
|
|
732
794
|
async setAsDefault(id) {
|
|
733
795
|
await this.ensureDataSourceRepository();
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
|
|
796
|
+
await this.repository.manager.transaction(async (manager)=>{
|
|
797
|
+
await this.lockLanguages(manager);
|
|
798
|
+
const target = await manager.findOne(src_entities/* .Language */.TM, {
|
|
799
|
+
where: {
|
|
800
|
+
id,
|
|
801
|
+
deletedAt: (0,external_typeorm_.IsNull)()
|
|
802
|
+
}
|
|
803
|
+
});
|
|
804
|
+
if (!target) {
|
|
805
|
+
throw new common_.NotFoundException({
|
|
806
|
+
message: 'Language not found',
|
|
807
|
+
messageKey: __rspack_external__flusys_nestjs_shared_constants_81f33d5b.ERROR_MESSAGES.NOT_FOUND
|
|
808
|
+
});
|
|
737
809
|
}
|
|
810
|
+
await manager.update(src_entities/* .Language */.TM, {
|
|
811
|
+
isDefault: true,
|
|
812
|
+
id: (0,external_typeorm_.Not)(id)
|
|
813
|
+
}, {
|
|
814
|
+
isDefault: false
|
|
815
|
+
});
|
|
816
|
+
await manager.update(src_entities/* .Language */.TM, {
|
|
817
|
+
id
|
|
818
|
+
}, {
|
|
819
|
+
isDefault: true
|
|
820
|
+
});
|
|
738
821
|
});
|
|
739
|
-
await this.
|
|
740
|
-
|
|
741
|
-
}, {
|
|
742
|
-
isDefault: false
|
|
743
|
-
});
|
|
744
|
-
await this.repository.update({
|
|
745
|
-
id
|
|
746
|
-
}, {
|
|
747
|
-
isDefault: true
|
|
748
|
-
});
|
|
749
|
-
const language = await this.repository.findOne({
|
|
822
|
+
await this.invalidateCache();
|
|
823
|
+
return this.repository.findOne({
|
|
750
824
|
where: {
|
|
751
825
|
id
|
|
752
826
|
}
|
|
753
827
|
});
|
|
754
|
-
await Promise.all([
|
|
755
|
-
this.clearCacheForAll(),
|
|
756
|
-
this.clearCacheForId([
|
|
757
|
-
...previousDefaults,
|
|
758
|
-
{
|
|
759
|
-
id
|
|
760
|
-
}
|
|
761
|
-
])
|
|
762
|
-
]);
|
|
763
|
-
return language;
|
|
764
828
|
}
|
|
765
829
|
}
|
|
766
830
|
LanguageService = language_service_ts_decorate([
|
|
@@ -886,6 +950,14 @@ class TranslationKeyService extends classes_.ApiService {
|
|
|
886
950
|
});
|
|
887
951
|
}
|
|
888
952
|
}
|
|
953
|
+
// A permanent delete cascades (FK) to the key's translations
|
|
954
|
+
async delete(option, user) {
|
|
955
|
+
const result = await super.delete(option, user);
|
|
956
|
+
if (option.type === 'permanent') {
|
|
957
|
+
await this.utilsService.clearCache(event_actions/* .LOCALIZATION_EVENT_ENTITIES.TRANSLATION */.JX.TRANSLATION, this.cacheManager, this.cacheTenant());
|
|
958
|
+
}
|
|
959
|
+
return result;
|
|
960
|
+
}
|
|
889
961
|
async findByModule(module) {
|
|
890
962
|
await this.ensureDataSourceRepository();
|
|
891
963
|
return this.repository.find({
|
|
@@ -987,6 +1059,14 @@ class TranslationService extends classes_.ApiService {
|
|
|
987
1059
|
constructor(cacheManager, utilsService, dataSourceProvider){
|
|
988
1060
|
super(event_actions/* .LOCALIZATION_EVENT_ENTITIES.TRANSLATION */.JX.TRANSLATION, cacheManager, utilsService, TranslationService.name, true, event_actions/* .LOCALIZATION_EVENT_MODULE */.ec, dataSourceProvider, src_entities/* .Translation */.wD), this.cacheManager = cacheManager, this.utilsService = utilsService, this.dataSourceProvider = dataSourceProvider;
|
|
989
1061
|
}
|
|
1062
|
+
// The module filter joins translation_key; a permanent language/key delete cascades rows away
|
|
1063
|
+
cacheDependencies() {
|
|
1064
|
+
return [
|
|
1065
|
+
event_actions/* .LOCALIZATION_EVENT_ENTITIES.TRANSLATION */.JX.TRANSLATION,
|
|
1066
|
+
event_actions/* .LOCALIZATION_EVENT_ENTITIES.TRANSLATION_KEY */.JX.TRANSLATION_KEY,
|
|
1067
|
+
event_actions/* .LOCALIZATION_EVENT_ENTITIES.LANGUAGE */.JX.LANGUAGE
|
|
1068
|
+
];
|
|
1069
|
+
}
|
|
990
1070
|
async getFilterQuery(query, filter, _user) {
|
|
991
1071
|
// Handle module filter by joining to translation_key table
|
|
992
1072
|
if (filter.module) {
|
|
@@ -1051,11 +1131,10 @@ class TranslationService extends classes_.ApiService {
|
|
|
1051
1131
|
const queryRunner = ds.createQueryRunner();
|
|
1052
1132
|
await queryRunner.connect();
|
|
1053
1133
|
await queryRunner.startTransaction();
|
|
1134
|
+
const resultIds = [];
|
|
1054
1135
|
try {
|
|
1055
|
-
const resultIds = [];
|
|
1056
1136
|
for (const update of updates){
|
|
1057
1137
|
if (update.id) {
|
|
1058
|
-
// Update existing translation
|
|
1059
1138
|
await queryRunner.manager.update(src_entities/* .Translation */.wD, {
|
|
1060
1139
|
id: update.id
|
|
1061
1140
|
}, {
|
|
@@ -1064,7 +1143,6 @@ class TranslationService extends classes_.ApiService {
|
|
|
1064
1143
|
});
|
|
1065
1144
|
resultIds.push(update.id);
|
|
1066
1145
|
} else {
|
|
1067
|
-
// Create new translation
|
|
1068
1146
|
const newTranslation = queryRunner.manager.create(src_entities/* .Translation */.wD, {
|
|
1069
1147
|
languageId: update.languageId,
|
|
1070
1148
|
translationKeyId: update.translationKeyId,
|
|
@@ -1077,28 +1155,25 @@ class TranslationService extends classes_.ApiService {
|
|
|
1077
1155
|
}
|
|
1078
1156
|
}
|
|
1079
1157
|
await queryRunner.commitTransaction();
|
|
1080
|
-
const saved = await this.repository.find({
|
|
1081
|
-
where: {
|
|
1082
|
-
id: (0,external_typeorm_.In)(resultIds)
|
|
1083
|
-
}
|
|
1084
|
-
});
|
|
1085
|
-
await Promise.all([
|
|
1086
|
-
this.clearCacheForAll(),
|
|
1087
|
-
this.clearCacheForId(saved)
|
|
1088
|
-
]);
|
|
1089
|
-
await this.publishDomainAction(event_actions/* .LOCALIZATION_EVENT_ACTIONS.BULK_UPDATED */.f_.BULK_UPDATED, {
|
|
1090
|
-
ids: saved.map((translation)=>translation.id),
|
|
1091
|
-
metadata: {
|
|
1092
|
-
count: saved.length
|
|
1093
|
-
}
|
|
1094
|
-
});
|
|
1095
|
-
return saved;
|
|
1096
1158
|
} catch (error) {
|
|
1097
1159
|
await queryRunner.rollbackTransaction();
|
|
1098
1160
|
throw error;
|
|
1099
1161
|
} finally{
|
|
1100
1162
|
await queryRunner.release();
|
|
1101
1163
|
}
|
|
1164
|
+
await this.invalidateCache();
|
|
1165
|
+
const saved = await this.repository.find({
|
|
1166
|
+
where: {
|
|
1167
|
+
id: (0,external_typeorm_.In)(resultIds)
|
|
1168
|
+
}
|
|
1169
|
+
});
|
|
1170
|
+
await this.publishDomainAction(event_actions/* .LOCALIZATION_EVENT_ACTIONS.BULK_UPDATED */.f_.BULK_UPDATED, {
|
|
1171
|
+
ids: saved.map((translation)=>translation.id),
|
|
1172
|
+
metadata: {
|
|
1173
|
+
count: saved.length
|
|
1174
|
+
}
|
|
1175
|
+
});
|
|
1176
|
+
return saved;
|
|
1102
1177
|
}
|
|
1103
1178
|
async getMissingTranslations(languageCode) {
|
|
1104
1179
|
await this.ensureAllReposInitialized();
|
|
@@ -3,7 +3,7 @@ export const __rspack_esm_ids = [24];
|
|
|
3
3
|
export const __webpack_modules__ = {};
|
|
4
4
|
import { __webpack_require__ } from '../runtime.js';
|
|
5
5
|
var __webpack_exec__ = function(moduleId) { return __webpack_require__(__webpack_require__.s = moduleId); }
|
|
6
|
-
import * as __rspack_chunk_1 from '../
|
|
6
|
+
import * as __rspack_chunk_1 from '../901.js';
|
|
7
7
|
__webpack_require__.C(__rspack_chunk_1);
|
|
8
8
|
import * as __rspack_chunk_2 from '../349.js';
|
|
9
9
|
__webpack_require__.C(__rspack_chunk_2);
|
package/fesm/docs/index.js
CHANGED
|
@@ -37,7 +37,6 @@ Database-driven internationalization system. Manage languages, translation keys,
|
|
|
37
37
|
- Store translated values per language per key
|
|
38
38
|
- Mark translations as verified
|
|
39
39
|
- Bulk upsert in a single transaction — create or update in one call
|
|
40
|
-
- Detect missing translations for a given language
|
|
41
40
|
- Track translation counts per key
|
|
42
41
|
|
|
43
42
|
### 🚀 Angular Lazy Loading
|
|
@@ -79,11 +78,10 @@ Keys are grouped by \`module\` so the frontend loads translations on demand:
|
|
|
79
78
|
- POST /localization/language/get-active
|
|
80
79
|
- POST /localization/language/get-default
|
|
81
80
|
- POST /localization/translation/get-by-language
|
|
82
|
-
- POST /localization/translation/get-by-module
|
|
83
81
|
|
|
84
82
|
**Authenticated** (require JWT + permission):
|
|
85
83
|
- All create, update, delete operations
|
|
86
|
-
- get-
|
|
84
|
+
- get-modules, get-counts, update-many-values, set-default
|
|
87
85
|
|
|
88
86
|
## Bulk Update
|
|
89
87
|
|