@flusys/ng-localization 9.1.0 → 9.1.1

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.
@@ -1,436 +1,506 @@
1
- import * as i0 from '@angular/core';
2
- import { InjectionToken, Service, inject, ApplicationRef, signal, computed, effect, DestroyRef, Component, provideAppInitializer } from '@angular/core';
3
- import { ApiResourceService, PlatformService, TranslateService, TranslatePipe, LANGUAGE_PERMISSIONS, HasPermissionDirective, PermissionValidatorService, TRANSLATION_KEY_PERMISSIONS, IconComponent, permissionGuard, resolveTranslationModule, SHARED_MESSAGES } from '@flusys/ng-shared';
4
- import { HttpClient } from '@angular/common/http';
5
- import { APP_CONFIG, LOCALIZATION_API_SERVICE, LOCALIZATION_STATE_SERVICE } from '@flusys/ng-core';
6
- import { firstValueFrom, from, finalize, forkJoin } from 'rxjs';
7
- import { DOCUMENT } from '@angular/common';
8
- import { takeUntilDestroyed, toSignal } from '@angular/core/rxjs-interop';
9
- import { MenuComponent, UiIconComponent, MessageService, CardComponent, InputTextDirective, SelectComponent, CheckboxComponent, FButtonDirective, ConfirmationService, TableComponent, TagComponent, TooltipDirective, IconFieldComponent, InputIconComponent, TextareaDirective, ProgressSpinnerComponent } from '@flusys/ng-ui';
10
- import { LAYOUT_LANGUAGE_SELECTOR, LAYOUT_LANGUAGE_SELECTOR_PANEL } from '@flusys/ng-layout';
11
- import * as i1 from '@angular/forms';
12
- import { FormsModule } from '@angular/forms';
13
- import { form, required, maxLength, disabled, FormField } from '@angular/forms/signals';
14
- import { Router, ActivatedRoute, RouterOutlet, RouterLink, RouterLinkActive } from '@angular/router';
15
-
16
- const LOCALIZATION_CONFIG = new InjectionToken('LOCALIZATION_CONFIG');
17
- /** Get localization config with sensible defaults applied */
1
+ import * as i0 from "@angular/core";
2
+ import { ApplicationRef, Component, DestroyRef, InjectionToken, Service, computed, effect, inject, provideAppInitializer, signal } from "@angular/core";
3
+ import { ApiResourceService, LANGUAGE_PERMISSIONS, PermissionValidatorService, PlatformService, SHARED_MESSAGES, TRANSLATION_KEY_PERMISSIONS, permissionGuard, resolveTranslationModule } from "@flusys/ng-shared";
4
+ import { HttpClient } from "@angular/common/http";
5
+ import { APP_CONFIG, LOCALIZATION_API_SERVICE, LOCALIZATION_STATE_SERVICE } from "@flusys/ng-core";
6
+ import { firstValueFrom, from } from "rxjs";
7
+ import { DOCUMENT } from "@angular/common";
8
+ import { takeUntilDestroyed } from "@angular/core/rxjs-interop";
9
+ import { MenuComponent, UiIconComponent } from "@flusys/ng-ui";
10
+ import { LAYOUT_LANGUAGE_SELECTOR, LAYOUT_LANGUAGE_SELECTOR_PANEL } from "@flusys/ng-layout";
11
+ const LOCALIZATION_CONFIG = new InjectionToken("LOCALIZATION_CONFIG");
18
12
  function getLocalizationConfig(overrides) {
19
- const defaults = {
20
- defaultLanguageCode: 'en',
21
- loadStrategy: 'all',
22
- initialModules: [],
23
- enableLayoutSelector: false,
24
- };
25
- return {
26
- ...defaults,
27
- ...overrides,
28
- defaultLanguageCode: overrides?.defaultLanguageCode || defaults.defaultLanguageCode,
29
- };
13
+ const defaults = {
14
+ defaultLanguageCode: "en",
15
+ loadStrategy: "all",
16
+ initialModules: [],
17
+ enableLayoutSelector: false
18
+ };
19
+ return {
20
+ ...defaults,
21
+ ...overrides,
22
+ defaultLanguageCode: overrides?.defaultLanguageCode || defaults.defaultLanguageCode
23
+ };
30
24
  }
31
-
32
- const STORAGE_KEYS = {
33
- CURRENT_LANGUAGE: 'flusys.language',
34
- };
35
-
25
+ const STORAGE_KEYS = { CURRENT_LANGUAGE: "flusys.language" };
36
26
  const LOCALIZATION_MESSAGES = {
37
- // Language CRUD (API keys)
38
- 'language.create.success': 'Language created successfully',
39
- 'language.create.many.success': '{{count}} languages created successfully',
40
- 'language.get.success': 'Language retrieved successfully',
41
- 'language.get.by.ids.success': 'Languages retrieved successfully by Ids',
42
- 'language.get.by.filter.success': 'Languages retrieved successfully by filter',
43
- 'language.get.all.success': 'Languages retrieved successfully',
44
- 'language.update.success': 'Language updated successfully',
45
- 'language.update.many.success': '{{count}} languages updated successfully',
46
- 'language.delete.success': 'Language deleted successfully',
47
- 'language.restore.success': 'Language restored successfully',
48
- 'language.readonly.update.forbidden': 'Cannot modify code/name of readonly language "{{name}}"',
49
- 'language.readonly.delete.forbidden': 'Cannot delete readonly language(s): {{names}}',
50
- 'language.default.delete.forbidden': 'Cannot delete the default language(s): {{names}}. Set another language as default first.',
51
- 'language.active.success': 'Active languages retrieved successfully',
52
- 'language.set.default.success': 'Default language updated successfully',
53
- 'language.get.default.success': 'Default language retrieved successfully',
54
- // Translation key CRUD (API keys)
55
- 'translation.key.create.success': 'Translation key created successfully',
56
- 'translation.key.create.many.success': '{{count}} translation keys created successfully',
57
- 'translation.key.get.success': 'Translation key retrieved successfully',
58
- 'translation.key.get.by.ids.success': 'Translation keys retrieved successfully by Ids',
59
- 'translation.key.get.by.filter.success': 'Translation keys retrieved successfully by filter',
60
- 'translation.key.get.all.success': 'Translation keys retrieved successfully',
61
- 'translation.key.update.success': 'Translation key updated successfully',
62
- 'translation.key.update.many.success': '{{count}} translation keys updated successfully',
63
- 'translation.key.delete.success': 'Translation key deleted successfully',
64
- 'translation.key.restore.success': 'Translation key restored successfully',
65
- 'translation.key.modules.success': 'Modules retrieved successfully',
66
- 'translation.key.readonly.delete.forbidden': 'Cannot delete readonly translation keys: {{keys}}',
67
- 'translation.key.readonly.update.forbidden': 'Cannot modify key or module of system translation keys',
68
- 'translation.key.duplicate.key.in.module': 'Translation key "{{key}}" already exists in module "{{module}}"',
69
- // Translation CRUD (API keys)
70
- 'translation.create.success': 'Translation created successfully',
71
- 'translation.create.many.success': '{{count}} translations created successfully',
72
- 'translation.get.success': 'Translation retrieved successfully',
73
- 'translation.get.by.ids.success': 'Translations retrieved successfully by Ids',
74
- 'translation.get.by.filter.success': 'Translations retrieved successfully by filter',
75
- 'translation.get.all.success': 'Translations retrieved successfully',
76
- 'translation.update.success': 'Translation updated successfully',
77
- 'translation.update.many.success': '{{count}} translations updated successfully',
78
- 'translation.delete.success': 'Translation deleted successfully',
79
- 'translation.restore.success': 'Translation restored successfully',
80
- 'translation.by.language.success': 'Translations retrieved successfully',
81
- // pages/localization-container/localization-container.page.ts
82
- 'localization.title': 'Localization',
83
- 'localization.subtitle': 'Manage languages and translations',
84
- 'localization.tabs.languages': 'Languages',
85
- 'localization.tabs.keys': 'Keys',
86
- // pages/language-list/language-list.page.ts
87
- 'localization.language.title': 'Languages',
88
- 'localization.language.subtitle': 'Manage available languages for your application',
89
- 'localization.language.new': 'Add Language',
90
- 'localization.language.code': 'Language Code',
91
- 'localization.language.name': 'Language Name',
92
- 'localization.language.native.name': 'Native Name',
93
- 'localization.language.direction': 'Text Direction',
94
- 'localization.language.rtl': 'RTL',
95
- 'localization.language.ltr': 'LTR',
96
- 'localization.language.default': 'Default',
97
- 'localization.language.set.default': 'Set as Default',
98
- 'localization.language.empty': 'No languages configured. Add a language to get started.',
99
- 'localization.language.confirm.set.default': 'Set "{{name}}" as the default language?',
100
- 'localization.language.delete.title': 'Delete Language',
101
- 'localization.language.default.delete.forbidden': 'Cannot delete the default language. Set another language as default first.',
102
- 'localization.language.readonly.delete.forbidden': 'This is a system language and cannot be deleted',
103
- // pages/language-form/language-form.page.ts
104
- 'localization.language.edit': 'Edit Language',
105
- 'localization.language.readonly.edit.hint': 'This is a system language — its code, name, native name, and direction cannot be changed',
106
- 'localization.placeholder.code': 'en',
107
- 'localization.language.iso.code.hint': 'ISO 639-1 language code (e.g., en, ar, fr)',
108
- 'localization.placeholder.name': 'English',
109
- 'localization.placeholder.native.name': 'English',
110
- 'localization.language.native.name.hint': 'Name in the native language (e.g., العربية for Arabic)',
111
- 'localization.language.display.order': 'Display Order',
112
- 'localization.language.direction.ltr': 'Left to Right (LTR)',
113
- 'localization.language.direction.rtl': 'Right to Left (RTL)',
114
- 'localization.validation.code.required': 'Language code is required',
115
- 'localization.validation.code.max.length': 'Language code must be at most 10 characters',
116
- 'localization.validation.name.required': 'Language name is required',
117
- 'localization.validation.name.max.length': 'Language name must be at most 100 characters',
118
- // pages/translation-key-list/translation-key-list.page.ts
119
- 'localization.key.title': 'Translation Keys',
120
- 'localization.key.subtitle': 'Manage translation keys for your application',
121
- 'localization.key.new': 'Add Key',
122
- 'localization.key.filter.module': 'Filter by module',
123
- 'localization.key.module': 'Module',
124
- 'localization.key.name': 'Key Name',
125
- 'localization.key.default.message': 'Default Message',
126
- 'localization.key.translations': 'Translations',
127
- 'localization.key.empty': 'No translation keys found',
128
- 'localization.key.delete.title': 'Delete Translation Key',
129
- 'localization.key.readonly.delete.forbidden': 'This is a system translation key and cannot be deleted',
130
- 'localization.key.readonly.edit.hint': 'This is a system key — its key name and module cannot be changed',
131
- // pages/translation-key-form/translation-key-form.page.ts
132
- 'localization.key.edit': 'Edit Key',
133
- 'localization.key.about': 'Key Information',
134
- 'localization.placeholder.key.name': 'button.submit',
135
- 'localization.key.select.module': 'Select or enter module',
136
- 'localization.placeholder.default.message': 'Enter default value (English)',
137
- 'localization.key.description': 'Description',
138
- 'localization.placeholder.key.description': 'Optional description for translators',
139
- 'localization.key.variables': 'Variables',
140
- 'localization.key.variables.placeholder': 'e.g., name, count, date',
141
- 'localization.key.variables.hint': 'Enter variable names used in this translation',
142
- 'localization.key.translations.section': 'Translations',
143
- 'localization.key.no.languages': 'No active languages configured',
144
- 'localization.key.enter.translation': 'Enter translation',
145
- 'localization.validation.key.required': 'Translation key is required',
146
- 'localization.validation.default.message.required': 'Default message is required',
27
+ "language.create.success": "Language created successfully",
28
+ "language.create.many.success": "{{count}} languages created successfully",
29
+ "language.get.success": "Language retrieved successfully",
30
+ "language.get.by.ids.success": "Languages retrieved successfully by Ids",
31
+ "language.get.by.filter.success": "Languages retrieved successfully by filter",
32
+ "language.get.all.success": "Languages retrieved successfully",
33
+ "language.update.success": "Language updated successfully",
34
+ "language.update.many.success": "{{count}} languages updated successfully",
35
+ "language.delete.success": "Language deleted successfully",
36
+ "language.restore.success": "Language restored successfully",
37
+ "language.readonly.update.forbidden": "Cannot modify code/name of readonly language \"{{name}}\"",
38
+ "language.readonly.delete.forbidden": "Cannot delete readonly language(s): {{names}}",
39
+ "language.default.delete.forbidden": "Cannot delete the default language(s): {{names}}. Set another language as default first.",
40
+ "language.active.success": "Active languages retrieved successfully",
41
+ "language.set.default.success": "Default language updated successfully",
42
+ "language.get.default.success": "Default language retrieved successfully",
43
+ "translation.key.create.success": "Translation key created successfully",
44
+ "translation.key.create.many.success": "{{count}} translation keys created successfully",
45
+ "translation.key.get.success": "Translation key retrieved successfully",
46
+ "translation.key.get.by.ids.success": "Translation keys retrieved successfully by Ids",
47
+ "translation.key.get.by.filter.success": "Translation keys retrieved successfully by filter",
48
+ "translation.key.get.all.success": "Translation keys retrieved successfully",
49
+ "translation.key.update.success": "Translation key updated successfully",
50
+ "translation.key.update.many.success": "{{count}} translation keys updated successfully",
51
+ "translation.key.delete.success": "Translation key deleted successfully",
52
+ "translation.key.restore.success": "Translation key restored successfully",
53
+ "translation.key.modules.success": "Modules retrieved successfully",
54
+ "translation.key.readonly.delete.forbidden": "Cannot delete readonly translation keys: {{keys}}",
55
+ "translation.key.readonly.update.forbidden": "Cannot modify key or module of system translation keys",
56
+ "translation.key.duplicate.key.in.module": "Translation key \"{{key}}\" already exists in module \"{{module}}\"",
57
+ "translation.create.success": "Translation created successfully",
58
+ "translation.create.many.success": "{{count}} translations created successfully",
59
+ "translation.get.success": "Translation retrieved successfully",
60
+ "translation.get.by.ids.success": "Translations retrieved successfully by Ids",
61
+ "translation.get.by.filter.success": "Translations retrieved successfully by filter",
62
+ "translation.get.all.success": "Translations retrieved successfully",
63
+ "translation.update.success": "Translation updated successfully",
64
+ "translation.update.many.success": "{{count}} translations updated successfully",
65
+ "translation.delete.success": "Translation deleted successfully",
66
+ "translation.restore.success": "Translation restored successfully",
67
+ "translation.by.language.success": "Translations retrieved successfully",
68
+ "localization.title": "Localization",
69
+ "localization.subtitle": "Manage languages and translations",
70
+ "localization.tabs.languages": "Languages",
71
+ "localization.tabs.keys": "Keys",
72
+ "localization.language.title": "Languages",
73
+ "localization.language.subtitle": "Manage available languages for your application",
74
+ "localization.language.new": "Add Language",
75
+ "localization.language.code": "Language Code",
76
+ "localization.language.name": "Language Name",
77
+ "localization.language.native.name": "Native Name",
78
+ "localization.language.direction": "Text Direction",
79
+ "localization.language.rtl": "RTL",
80
+ "localization.language.ltr": "LTR",
81
+ "localization.language.default": "Default",
82
+ "localization.language.set.default": "Set as Default",
83
+ "localization.language.empty": "No languages configured. Add a language to get started.",
84
+ "localization.language.confirm.set.default": "Set \"{{name}}\" as the default language?",
85
+ "localization.language.delete.title": "Delete Language",
86
+ "localization.language.default.delete.forbidden": "Cannot delete the default language. Set another language as default first.",
87
+ "localization.language.readonly.delete.forbidden": "This is a system language and cannot be deleted",
88
+ "localization.language.edit": "Edit Language",
89
+ "localization.language.readonly.edit.hint": "This is a system language — its code, name, native name, and direction cannot be changed",
90
+ "localization.placeholder.code": "en",
91
+ "localization.language.iso.code.hint": "ISO 639-1 language code (e.g., en, ar, fr)",
92
+ "localization.placeholder.name": "English",
93
+ "localization.placeholder.native.name": "English",
94
+ "localization.language.native.name.hint": "Name in the native language (e.g., العربية for Arabic)",
95
+ "localization.language.display.order": "Display Order",
96
+ "localization.language.direction.ltr": "Left to Right (LTR)",
97
+ "localization.language.direction.rtl": "Right to Left (RTL)",
98
+ "localization.validation.code.required": "Language code is required",
99
+ "localization.validation.code.max.length": "Language code must be at most 10 characters",
100
+ "localization.validation.name.required": "Language name is required",
101
+ "localization.validation.name.max.length": "Language name must be at most 100 characters",
102
+ "localization.key.title": "Translation Keys",
103
+ "localization.key.subtitle": "Manage translation keys for your application",
104
+ "localization.key.new": "Add Key",
105
+ "localization.key.filter.module": "Filter by module",
106
+ "localization.key.module": "Module",
107
+ "localization.key.name": "Key Name",
108
+ "localization.key.default.message": "Default Message",
109
+ "localization.key.translations": "Translations",
110
+ "localization.key.empty": "No translation keys found",
111
+ "localization.key.delete.title": "Delete Translation Key",
112
+ "localization.key.readonly.delete.forbidden": "This is a system translation key and cannot be deleted",
113
+ "localization.key.readonly.edit.hint": "This is a system key — its key name and module cannot be changed",
114
+ "localization.key.edit": "Edit Key",
115
+ "localization.key.about": "Key Information",
116
+ "localization.placeholder.key.name": "button.submit",
117
+ "localization.key.select.module": "Select or enter module",
118
+ "localization.placeholder.default.message": "Enter default value (English)",
119
+ "localization.key.description": "Description",
120
+ "localization.placeholder.key.description": "Optional description for translators",
121
+ "localization.key.variables": "Variables",
122
+ "localization.key.variables.placeholder": "e.g., name, count, date",
123
+ "localization.key.variables.hint": "Enter variable names used in this translation",
124
+ "localization.key.translations.section": "Translations",
125
+ "localization.key.no.languages": "No active languages configured",
126
+ "localization.key.enter.translation": "Enter translation",
127
+ "localization.validation.key.required": "Translation key is required",
128
+ "localization.validation.default.message.required": "Default message is required"
147
129
  };
148
-
149
- class LanguageApiService extends ApiResourceService {
150
- constructor() {
151
- super('language', 'localization');
152
- }
153
- setDefault(id) {
154
- return this.http.post(`${this.baseUrl}/set-default`, { id });
155
- }
156
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.5", ngImport: i0, type: LanguageApiService, deps: [], target: i0.ɵɵFactoryTarget.Service });
157
- static ɵprov = i0.ɵɵngDeclareService({ minVersion: "22.0.0", version: "22.1.5", ngImport: i0, type: LanguageApiService });
158
- }
159
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.5", ngImport: i0, type: LanguageApiService, decorators: [{
160
- type: Service
161
- }], ctorParameters: () => [] });
162
-
163
- class LocalizationApiService {
164
- http = inject(HttpClient);
165
- appConfig = inject(APP_CONFIG);
166
- get baseUrl() {
167
- return `${this.appConfig.apiBaseUrl}/localization`;
168
- }
169
- getActiveLanguages() {
170
- return this.http.post(`${this.baseUrl}/language/get-active`, {});
171
- }
172
- getTranslationsByLanguage(languageCode, modules) {
173
- return this.http.post(`${this.baseUrl}/translation/get-by-language`, { languageCode, ...(modules?.length ? { modules } : {}) });
174
- }
175
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.5", ngImport: i0, type: LocalizationApiService, deps: [], target: i0.ɵɵFactoryTarget.Service });
176
- static ɵprov = i0.ɵɵngDeclareService({ minVersion: "22.0.0", version: "22.1.5", ngImport: i0, type: LocalizationApiService });
177
- }
178
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.5", ngImport: i0, type: LocalizationApiService, decorators: [{
179
- type: Service
180
- }] });
181
-
182
- class LocalizationInitializerService {
183
- apiService = inject(LOCALIZATION_API_SERVICE, {
184
- optional: false,
185
- });
186
- stateService = inject(LOCALIZATION_STATE_SERVICE, {
187
- optional: false,
188
- });
189
- async initialize(initialFallbackMessages) {
190
- try {
191
- this.stateService.setLoading(true);
192
- if (initialFallbackMessages) {
193
- this.stateService.updateFallbackMessages(initialFallbackMessages);
194
- }
195
- const languagesResponse = await firstValueFrom(this.apiService.getActiveLanguages());
196
- if (languagesResponse.success && languagesResponse.data) {
197
- this.stateService.setAvailableLanguages(languagesResponse.data);
198
- const savedCode = this.stateService.getSavedLanguageCode();
199
- let selectedLanguage = languagesResponse.data.find((l) => l.code === savedCode);
200
- if (!selectedLanguage) {
201
- selectedLanguage = languagesResponse.data.find((l) => l.isDefault);
202
- }
203
- if (!selectedLanguage && languagesResponse.data.length > 0) {
204
- selectedLanguage = languagesResponse.data[0];
205
- }
206
- if (selectedLanguage) {
207
- this.stateService.setCurrentLanguage(selectedLanguage);
208
- }
209
- }
210
- }
211
- catch (error) {
212
- // Silently fail - use default language
213
- }
214
- finally {
215
- this.stateService.setLoading(false);
216
- }
217
- }
218
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.5", ngImport: i0, type: LocalizationInitializerService, deps: [], target: i0.ɵɵFactoryTarget.Service });
219
- static ɵprov = i0.ɵɵngDeclareService({ minVersion: "22.0.0", version: "22.1.5", ngImport: i0, type: LocalizationInitializerService });
220
- }
221
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.5", ngImport: i0, type: LocalizationInitializerService, decorators: [{
222
- type: Service
223
- }] });
224
-
225
- class LocalizationStateService {
226
- platformService = inject(PlatformService);
227
- document = inject(DOCUMENT);
228
- appRef = inject(ApplicationRef);
229
- _loadedModules = new Set();
230
- _currentLanguage = signal(null, /* @ts-ignore */
231
- ...(ngDevMode ? [{ debugName: "_currentLanguage" }] : /* istanbul ignore next */ []));
232
- _availableLanguages = signal([], /* @ts-ignore */
233
- ...(ngDevMode ? [{ debugName: "_availableLanguages" }] : /* istanbul ignore next */ []));
234
- _translations = signal({}, /* @ts-ignore */
235
- ...(ngDevMode ? [{ debugName: "_translations" }] : /* istanbul ignore next */ []));
236
- _fallbackMessages = signal({}, /* @ts-ignore */
237
- ...(ngDevMode ? [{ debugName: "_fallbackMessages" }] : /* istanbul ignore next */ []));
238
- _isLoading = signal(false, /* @ts-ignore */
239
- ...(ngDevMode ? [{ debugName: "_isLoading" }] : /* istanbul ignore next */ []));
240
- availableLanguages = this._availableLanguages.asReadonly();
241
- translations = this._translations.asReadonly();
242
- fallbackMessages = this._fallbackMessages.asReadonly();
243
- isLoading = this._isLoading.asReadonly();
244
- currentLanguageCode = computed(() => this._currentLanguage()?.code ?? 'en', /* @ts-ignore */
245
- ...(ngDevMode ? [{ debugName: "currentLanguageCode" }] : /* istanbul ignore next */ []));
246
- constructor() {
247
- effect(() => {
248
- const language = this._currentLanguage();
249
- if (language && !this.platformService.isServer) {
250
- this.document.documentElement.dir = language.direction ?? 'ltr';
251
- this.document.documentElement.lang = language.code;
252
- localStorage.setItem(STORAGE_KEYS.CURRENT_LANGUAGE, language.code);
253
- }
254
- });
255
- effect(() => {
256
- this._translations();
257
- setTimeout(() => this.appRef.tick(), 0);
258
- });
259
- }
260
- setCurrentLanguage(language) {
261
- this._currentLanguage.set(language);
262
- }
263
- setAvailableLanguages(languages) {
264
- this._availableLanguages.set(languages);
265
- }
266
- setTranslations(translations) {
267
- this._translations.set(translations);
268
- }
269
- mergeTranslations(translations) {
270
- this._translations.update((current) => ({ ...current, ...translations }));
271
- }
272
- setLoading(loading) {
273
- this._isLoading.set(loading);
274
- }
275
- getSavedLanguageCode() {
276
- if (this.platformService.isServer)
277
- return null;
278
- return localStorage.getItem(STORAGE_KEYS.CURRENT_LANGUAGE);
279
- }
280
- isModuleLoaded(module) {
281
- return this._loadedModules.has(module);
282
- }
283
- markModuleLoaded(module) {
284
- this._loadedModules.add(module);
285
- }
286
- getLoadedModules() {
287
- return Array.from(this._loadedModules);
288
- }
289
- clearLoadedModules() {
290
- this._loadedModules.clear();
291
- }
292
- updateFallbackMessages(messages) {
293
- this._fallbackMessages.update((current) => ({ ...current, ...messages }));
294
- }
295
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.5", ngImport: i0, type: LocalizationStateService, deps: [], target: i0.ɵɵFactoryTarget.Service });
296
- static ɵprov = i0.ɵɵngDeclareService({ minVersion: "22.0.0", version: "22.1.5", ngImport: i0, type: LocalizationStateService });
297
- }
298
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.5", ngImport: i0, type: LocalizationStateService, decorators: [{
299
- type: Service
300
- }], ctorParameters: () => [] });
301
-
302
- class TranslationApiService extends ApiResourceService {
303
- constructor() {
304
- super('translation', 'localization');
305
- }
306
- getByKeyId(translationKeyId) {
307
- return this.http.post(`${this.baseUrl}/get-all`, {
308
- pagination: { currentPage: 0, pageSize: 1000 },
309
- filter: { translationKeyId },
310
- sort: {},
311
- });
312
- }
313
- getCounts() {
314
- return this.http.post(`${this.baseUrl}/get-counts`, {});
315
- }
316
- bulkUpdate(translations) {
317
- return this.http.post(`${this.baseUrl}/update-many-values`, { translations });
318
- }
319
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.5", ngImport: i0, type: TranslationApiService, deps: [], target: i0.ɵɵFactoryTarget.Service });
320
- static ɵprov = i0.ɵɵngDeclareService({ minVersion: "22.0.0", version: "22.1.5", ngImport: i0, type: TranslationApiService });
321
- }
322
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.5", ngImport: i0, type: TranslationApiService, decorators: [{
323
- type: Service
324
- }], ctorParameters: () => [] });
325
-
326
- class TranslationKeyApiService extends ApiResourceService {
327
- constructor() {
328
- super('translation-key', 'localization');
329
- }
330
- getModules() {
331
- return this.http.post(`${this.baseUrl}/get-modules`, {});
332
- }
333
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.5", ngImport: i0, type: TranslationKeyApiService, deps: [], target: i0.ɵɵFactoryTarget.Service });
334
- static ɵprov = i0.ɵɵngDeclareService({ minVersion: "22.0.0", version: "22.1.5", ngImport: i0, type: TranslationKeyApiService });
335
- }
336
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.5", ngImport: i0, type: TranslationKeyApiService, decorators: [{
337
- type: Service
338
- }], ctorParameters: () => [] });
339
-
340
- class TranslationLoaderService {
341
- DEFAULT_INITIAL_MODULES = ['shared', 'layout'];
342
- config = inject(LOCALIZATION_CONFIG, { optional: true });
343
- apiService = inject(LOCALIZATION_API_SERVICE, {
344
- optional: false,
345
- });
346
- stateService = inject(LOCALIZATION_STATE_SERVICE, {
347
- optional: false,
348
- });
349
- async loadTranslations(languageCode, clearModules = false) {
350
- const strategy = this.config?.loadStrategy ?? 'all';
351
- // Save currently loaded modules before clearing (for language switch)
352
- const previouslyLoadedModules = clearModules
353
- ? this.stateService.getLoadedModules()
354
- : [];
355
- if (clearModules) {
356
- this.stateService.clearLoadedModules();
357
- }
358
- this.stateService.setLoading(true);
359
- try {
360
- if (strategy === 'all') {
361
- await this.loadAndSetTranslations(languageCode);
362
- }
363
- else {
364
- const modules = this.config?.initialModules ?? this.DEFAULT_INITIAL_MODULES;
365
- await this.loadAndSetTranslations(languageCode, modules);
366
- // Reload all previously loaded modules for the new language
367
- if (previouslyLoadedModules.length > 0) {
368
- await this.reloadModules(languageCode, previouslyLoadedModules);
369
- }
370
- }
371
- }
372
- finally {
373
- this.stateService.setLoading(false);
374
- }
375
- }
376
- async loadAndSetTranslations(languageCode, modules) {
377
- const response = await firstValueFrom(this.apiService.getTranslationsByLanguage(languageCode, modules));
378
- if (response.success && response.data) {
379
- this.stateService.setTranslations(response.data);
380
- if (modules) {
381
- modules.forEach((m) => this.stateService.markModuleLoaded(m));
382
- }
383
- }
384
- }
385
- async reloadModules(languageCode, modules) {
386
- const initialModules = this.config?.initialModules ?? this.DEFAULT_INITIAL_MODULES;
387
- const additionalModules = modules.filter((m) => !initialModules.includes(m));
388
- if (additionalModules.length === 0)
389
- return;
390
- const response = await firstValueFrom(this.apiService.getTranslationsByLanguage(languageCode, additionalModules));
391
- if (response.success && response.data) {
392
- this.stateService.mergeTranslations(response.data);
393
- additionalModules.forEach((m) => this.stateService.markModuleLoaded(m));
394
- }
395
- }
396
- loadTranslationsAsync(languageCode, clearModules = false) {
397
- return from(this.loadTranslations(languageCode, clearModules));
398
- }
399
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.5", ngImport: i0, type: TranslationLoaderService, deps: [], target: i0.ɵɵFactoryTarget.Service });
400
- static ɵprov = i0.ɵɵngDeclareService({ minVersion: "22.0.0", version: "22.1.5", ngImport: i0, type: TranslationLoaderService });
401
- }
402
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.5", ngImport: i0, type: TranslationLoaderService, decorators: [{
403
- type: Service
404
- }] });
405
-
406
- class LanguageSelectorComponent {
407
- localizationState = inject(LocalizationStateService);
408
- translationLoader = inject(TranslationLoaderService);
409
- destroyRef = inject(DestroyRef);
410
- languages = this.localizationState.availableLanguages;
411
- selectedLanguageCode = this.localizationState.currentLanguageCode;
412
- selectedLanguage = computed(() => this.languages().find((l) => l.code === this.selectedLanguageCode()) ??
413
- null, /* @ts-ignore */
414
- ...(ngDevMode ? [{ debugName: "selectedLanguage" }] : /* istanbul ignore next */ []));
415
- menuItems = computed(() => this.languages().map((lang) => ({
416
- label: lang.nativeName || lang.name,
417
- badge: lang.code.toUpperCase(),
418
- selected: lang.code === this.selectedLanguageCode(),
419
- command: () => this.onLanguageSelect(lang.code),
420
- })), /* @ts-ignore */
421
- ...(ngDevMode ? [{ debugName: "menuItems" }] : /* istanbul ignore next */ []));
422
- onLanguageSelect(code) {
423
- const language = this.languages().find((l) => l.code === code);
424
- if (language) {
425
- this.localizationState.setCurrentLanguage(language);
426
- this.translationLoader
427
- .loadTranslationsAsync(code, true)
428
- .pipe(takeUntilDestroyed(this.destroyRef))
429
- .subscribe();
430
- }
431
- }
432
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.5", ngImport: i0, type: LanguageSelectorComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
433
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "22.1.5", type: LanguageSelectorComponent, isStandalone: true, selector: "lib-language-selector", host: { classAttribute: "relative" }, ngImport: i0, template: `
130
+ var LanguageApiService = class LanguageApiService extends ApiResourceService {
131
+ constructor() {
132
+ super("language", "localization");
133
+ }
134
+ setDefault(id) {
135
+ return this.http.post(`${this.baseUrl}/set-default`, { id });
136
+ }
137
+ static ɵfac = i0.ɵɵngDeclareFactory({
138
+ minVersion: "12.0.0",
139
+ version: "22.2.1",
140
+ ngImport: i0,
141
+ type: LanguageApiService,
142
+ deps: [],
143
+ target: i0.ɵɵFactoryTarget.Service
144
+ });
145
+ static ɵprov = i0.ɵɵngDeclareService({
146
+ minVersion: "22.0.0",
147
+ version: "22.2.1",
148
+ ngImport: i0,
149
+ type: LanguageApiService
150
+ });
151
+ };
152
+ i0.ɵɵngDeclareClassMetadata({
153
+ minVersion: "12.0.0",
154
+ version: "22.2.1",
155
+ ngImport: i0,
156
+ type: LanguageApiService,
157
+ decorators: [{ type: Service }],
158
+ ctorParameters: () => []
159
+ });
160
+ var LocalizationApiService = class LocalizationApiService {
161
+ http = inject(HttpClient);
162
+ appConfig = inject(APP_CONFIG);
163
+ get baseUrl() {
164
+ return `${this.appConfig.apiBaseUrl}/localization`;
165
+ }
166
+ getActiveLanguages() {
167
+ return this.http.post(`${this.baseUrl}/language/get-active`, {});
168
+ }
169
+ getTranslationsByLanguage(languageCode, modules) {
170
+ return this.http.post(`${this.baseUrl}/translation/get-by-language`, {
171
+ languageCode,
172
+ ...modules?.length ? { modules } : {}
173
+ });
174
+ }
175
+ static ɵfac = i0.ɵɵngDeclareFactory({
176
+ minVersion: "12.0.0",
177
+ version: "22.2.1",
178
+ ngImport: i0,
179
+ type: LocalizationApiService,
180
+ deps: [],
181
+ target: i0.ɵɵFactoryTarget.Service
182
+ });
183
+ static ɵprov = i0.ɵɵngDeclareService({
184
+ minVersion: "22.0.0",
185
+ version: "22.2.1",
186
+ ngImport: i0,
187
+ type: LocalizationApiService
188
+ });
189
+ };
190
+ i0.ɵɵngDeclareClassMetadata({
191
+ minVersion: "12.0.0",
192
+ version: "22.2.1",
193
+ ngImport: i0,
194
+ type: LocalizationApiService,
195
+ decorators: [{ type: Service }]
196
+ });
197
+ var LocalizationInitializerService = class LocalizationInitializerService {
198
+ apiService = inject(LOCALIZATION_API_SERVICE, { optional: false });
199
+ stateService = inject(LOCALIZATION_STATE_SERVICE, { optional: false });
200
+ async initialize(initialFallbackMessages) {
201
+ try {
202
+ this.stateService.setLoading(true);
203
+ if (initialFallbackMessages) this.stateService.updateFallbackMessages(initialFallbackMessages);
204
+ const languagesResponse = await firstValueFrom(this.apiService.getActiveLanguages());
205
+ if (languagesResponse.success && languagesResponse.data) {
206
+ this.stateService.setAvailableLanguages(languagesResponse.data);
207
+ const savedCode = this.stateService.getSavedLanguageCode();
208
+ let selectedLanguage = languagesResponse.data.find((l) => l.code === savedCode);
209
+ if (!selectedLanguage) selectedLanguage = languagesResponse.data.find((l) => l.isDefault);
210
+ if (!selectedLanguage && languagesResponse.data.length > 0) selectedLanguage = languagesResponse.data[0];
211
+ if (selectedLanguage) this.stateService.setCurrentLanguage(selectedLanguage);
212
+ }
213
+ } catch (error) {} finally {
214
+ this.stateService.setLoading(false);
215
+ }
216
+ }
217
+ static ɵfac = i0.ɵɵngDeclareFactory({
218
+ minVersion: "12.0.0",
219
+ version: "22.2.1",
220
+ ngImport: i0,
221
+ type: LocalizationInitializerService,
222
+ deps: [],
223
+ target: i0.ɵɵFactoryTarget.Service
224
+ });
225
+ static ɵprov = i0.ɵɵngDeclareService({
226
+ minVersion: "22.0.0",
227
+ version: "22.2.1",
228
+ ngImport: i0,
229
+ type: LocalizationInitializerService
230
+ });
231
+ };
232
+ i0.ɵɵngDeclareClassMetadata({
233
+ minVersion: "12.0.0",
234
+ version: "22.2.1",
235
+ ngImport: i0,
236
+ type: LocalizationInitializerService,
237
+ decorators: [{ type: Service }]
238
+ });
239
+ var LocalizationStateService = class LocalizationStateService {
240
+ platformService = inject(PlatformService);
241
+ document = inject(DOCUMENT);
242
+ appRef = inject(ApplicationRef);
243
+ _loadedModules = /* @__PURE__ */ new Set();
244
+ _currentLanguage = signal(null, ...ngDevMode ? [{ debugName: "_currentLanguage" }] : /* istanbul ignore next */ []);
245
+ _availableLanguages = signal([], ...ngDevMode ? [{ debugName: "_availableLanguages" }] : /* istanbul ignore next */ []);
246
+ _translations = signal({}, ...ngDevMode ? [{ debugName: "_translations" }] : /* istanbul ignore next */ []);
247
+ _fallbackMessages = signal({}, ...ngDevMode ? [{ debugName: "_fallbackMessages" }] : /* istanbul ignore next */ []);
248
+ _isLoading = signal(false, ...ngDevMode ? [{ debugName: "_isLoading" }] : /* istanbul ignore next */ []);
249
+ availableLanguages = this._availableLanguages.asReadonly();
250
+ translations = this._translations.asReadonly();
251
+ fallbackMessages = this._fallbackMessages.asReadonly();
252
+ isLoading = this._isLoading.asReadonly();
253
+ currentLanguageCode = computed(() => this._currentLanguage()?.code ?? "en", ...ngDevMode ? [{ debugName: "currentLanguageCode" }] : /* istanbul ignore next */ []);
254
+ constructor() {
255
+ effect(() => {
256
+ const language = this._currentLanguage();
257
+ if (language && !this.platformService.isServer) {
258
+ this.document.documentElement.dir = language.direction ?? "ltr";
259
+ this.document.documentElement.lang = language.code;
260
+ localStorage.setItem(STORAGE_KEYS.CURRENT_LANGUAGE, language.code);
261
+ }
262
+ });
263
+ effect(() => {
264
+ this._translations();
265
+ setTimeout(() => this.appRef.tick(), 0);
266
+ });
267
+ }
268
+ setCurrentLanguage(language) {
269
+ this._currentLanguage.set(language);
270
+ }
271
+ setAvailableLanguages(languages) {
272
+ this._availableLanguages.set(languages);
273
+ }
274
+ setTranslations(translations) {
275
+ this._translations.set(translations);
276
+ }
277
+ mergeTranslations(translations) {
278
+ this._translations.update((current) => ({
279
+ ...current,
280
+ ...translations
281
+ }));
282
+ }
283
+ setLoading(loading) {
284
+ this._isLoading.set(loading);
285
+ }
286
+ getSavedLanguageCode() {
287
+ if (this.platformService.isServer) return null;
288
+ return localStorage.getItem(STORAGE_KEYS.CURRENT_LANGUAGE);
289
+ }
290
+ isModuleLoaded(module) {
291
+ return this._loadedModules.has(module);
292
+ }
293
+ markModuleLoaded(module) {
294
+ this._loadedModules.add(module);
295
+ }
296
+ getLoadedModules() {
297
+ return Array.from(this._loadedModules);
298
+ }
299
+ clearLoadedModules() {
300
+ this._loadedModules.clear();
301
+ }
302
+ updateFallbackMessages(messages) {
303
+ this._fallbackMessages.update((current) => ({
304
+ ...current,
305
+ ...messages
306
+ }));
307
+ }
308
+ static ɵfac = i0.ɵɵngDeclareFactory({
309
+ minVersion: "12.0.0",
310
+ version: "22.2.1",
311
+ ngImport: i0,
312
+ type: LocalizationStateService,
313
+ deps: [],
314
+ target: i0.ɵɵFactoryTarget.Service
315
+ });
316
+ static ɵprov = i0.ɵɵngDeclareService({
317
+ minVersion: "22.0.0",
318
+ version: "22.2.1",
319
+ ngImport: i0,
320
+ type: LocalizationStateService
321
+ });
322
+ };
323
+ i0.ɵɵngDeclareClassMetadata({
324
+ minVersion: "12.0.0",
325
+ version: "22.2.1",
326
+ ngImport: i0,
327
+ type: LocalizationStateService,
328
+ decorators: [{ type: Service }],
329
+ ctorParameters: () => []
330
+ });
331
+ var TranslationApiService = class TranslationApiService extends ApiResourceService {
332
+ constructor() {
333
+ super("translation", "localization");
334
+ }
335
+ getByKeyId(translationKeyId) {
336
+ return this.http.post(`${this.baseUrl}/get-all`, {
337
+ pagination: {
338
+ currentPage: 0,
339
+ pageSize: 1e3
340
+ },
341
+ filter: { translationKeyId },
342
+ sort: {}
343
+ });
344
+ }
345
+ getCounts() {
346
+ return this.http.post(`${this.baseUrl}/get-counts`, {});
347
+ }
348
+ bulkUpdate(translations) {
349
+ return this.http.post(`${this.baseUrl}/update-many-values`, { translations });
350
+ }
351
+ static ɵfac = i0.ɵɵngDeclareFactory({
352
+ minVersion: "12.0.0",
353
+ version: "22.2.1",
354
+ ngImport: i0,
355
+ type: TranslationApiService,
356
+ deps: [],
357
+ target: i0.ɵɵFactoryTarget.Service
358
+ });
359
+ static ɵprov = i0.ɵɵngDeclareService({
360
+ minVersion: "22.0.0",
361
+ version: "22.2.1",
362
+ ngImport: i0,
363
+ type: TranslationApiService
364
+ });
365
+ };
366
+ i0.ɵɵngDeclareClassMetadata({
367
+ minVersion: "12.0.0",
368
+ version: "22.2.1",
369
+ ngImport: i0,
370
+ type: TranslationApiService,
371
+ decorators: [{ type: Service }],
372
+ ctorParameters: () => []
373
+ });
374
+ var TranslationKeyApiService = class TranslationKeyApiService extends ApiResourceService {
375
+ constructor() {
376
+ super("translation-key", "localization");
377
+ }
378
+ getModules() {
379
+ return this.http.post(`${this.baseUrl}/get-modules`, {});
380
+ }
381
+ static ɵfac = i0.ɵɵngDeclareFactory({
382
+ minVersion: "12.0.0",
383
+ version: "22.2.1",
384
+ ngImport: i0,
385
+ type: TranslationKeyApiService,
386
+ deps: [],
387
+ target: i0.ɵɵFactoryTarget.Service
388
+ });
389
+ static ɵprov = i0.ɵɵngDeclareService({
390
+ minVersion: "22.0.0",
391
+ version: "22.2.1",
392
+ ngImport: i0,
393
+ type: TranslationKeyApiService
394
+ });
395
+ };
396
+ i0.ɵɵngDeclareClassMetadata({
397
+ minVersion: "12.0.0",
398
+ version: "22.2.1",
399
+ ngImport: i0,
400
+ type: TranslationKeyApiService,
401
+ decorators: [{ type: Service }],
402
+ ctorParameters: () => []
403
+ });
404
+ var TranslationLoaderService = class TranslationLoaderService {
405
+ DEFAULT_INITIAL_MODULES = ["shared", "layout"];
406
+ config = inject(LOCALIZATION_CONFIG, { optional: true });
407
+ apiService = inject(LOCALIZATION_API_SERVICE, { optional: false });
408
+ stateService = inject(LOCALIZATION_STATE_SERVICE, { optional: false });
409
+ async loadTranslations(languageCode, clearModules = false) {
410
+ const strategy = this.config?.loadStrategy ?? "all";
411
+ const previouslyLoadedModules = clearModules ? this.stateService.getLoadedModules() : [];
412
+ if (clearModules) this.stateService.clearLoadedModules();
413
+ this.stateService.setLoading(true);
414
+ try {
415
+ if (strategy === "all") await this.loadAndSetTranslations(languageCode);
416
+ else {
417
+ const modules = this.config?.initialModules ?? this.DEFAULT_INITIAL_MODULES;
418
+ await this.loadAndSetTranslations(languageCode, modules);
419
+ if (previouslyLoadedModules.length > 0) await this.reloadModules(languageCode, previouslyLoadedModules);
420
+ }
421
+ } finally {
422
+ this.stateService.setLoading(false);
423
+ }
424
+ }
425
+ async loadAndSetTranslations(languageCode, modules) {
426
+ const response = await firstValueFrom(this.apiService.getTranslationsByLanguage(languageCode, modules));
427
+ if (response.success && response.data) {
428
+ this.stateService.setTranslations(response.data);
429
+ if (modules) modules.forEach((m) => this.stateService.markModuleLoaded(m));
430
+ }
431
+ }
432
+ async reloadModules(languageCode, modules) {
433
+ const initialModules = this.config?.initialModules ?? this.DEFAULT_INITIAL_MODULES;
434
+ const additionalModules = modules.filter((m) => !initialModules.includes(m));
435
+ if (additionalModules.length === 0) return;
436
+ const response = await firstValueFrom(this.apiService.getTranslationsByLanguage(languageCode, additionalModules));
437
+ if (response.success && response.data) {
438
+ this.stateService.mergeTranslations(response.data);
439
+ additionalModules.forEach((m) => this.stateService.markModuleLoaded(m));
440
+ }
441
+ }
442
+ loadTranslationsAsync(languageCode, clearModules = false) {
443
+ return from(this.loadTranslations(languageCode, clearModules));
444
+ }
445
+ static ɵfac = i0.ɵɵngDeclareFactory({
446
+ minVersion: "12.0.0",
447
+ version: "22.2.1",
448
+ ngImport: i0,
449
+ type: TranslationLoaderService,
450
+ deps: [],
451
+ target: i0.ɵɵFactoryTarget.Service
452
+ });
453
+ static ɵprov = i0.ɵɵngDeclareService({
454
+ minVersion: "22.0.0",
455
+ version: "22.2.1",
456
+ ngImport: i0,
457
+ type: TranslationLoaderService
458
+ });
459
+ };
460
+ i0.ɵɵngDeclareClassMetadata({
461
+ minVersion: "12.0.0",
462
+ version: "22.2.1",
463
+ ngImport: i0,
464
+ type: TranslationLoaderService,
465
+ decorators: [{ type: Service }]
466
+ });
467
+ var LanguageSelectorComponent = class LanguageSelectorComponent {
468
+ localizationState = inject(LocalizationStateService);
469
+ translationLoader = inject(TranslationLoaderService);
470
+ destroyRef = inject(DestroyRef);
471
+ languages = this.localizationState.availableLanguages;
472
+ selectedLanguageCode = this.localizationState.currentLanguageCode;
473
+ selectedLanguage = computed(() => this.languages().find((l) => l.code === this.selectedLanguageCode()) ?? null, ...ngDevMode ? [{ debugName: "selectedLanguage" }] : /* istanbul ignore next */ []);
474
+ menuItems = computed(() => this.languages().map((lang) => ({
475
+ label: lang.nativeName || lang.name,
476
+ badge: lang.code.toUpperCase(),
477
+ selected: lang.code === this.selectedLanguageCode(),
478
+ command: () => this.onLanguageSelect(lang.code)
479
+ })), ...ngDevMode ? [{ debugName: "menuItems" }] : /* istanbul ignore next */ []);
480
+ onLanguageSelect(code) {
481
+ const language = this.languages().find((l) => l.code === code);
482
+ if (language) {
483
+ this.localizationState.setCurrentLanguage(language);
484
+ this.translationLoader.loadTranslationsAsync(code, true).pipe(takeUntilDestroyed(this.destroyRef)).subscribe();
485
+ }
486
+ }
487
+ static ɵfac = i0.ɵɵngDeclareFactory({
488
+ minVersion: "12.0.0",
489
+ version: "22.2.1",
490
+ ngImport: i0,
491
+ type: LanguageSelectorComponent,
492
+ deps: [],
493
+ target: i0.ɵɵFactoryTarget.Component
494
+ });
495
+ static ɵcmp = i0.ɵɵngDeclareComponent({
496
+ minVersion: "14.0.0",
497
+ version: "22.2.1",
498
+ type: LanguageSelectorComponent,
499
+ isStandalone: true,
500
+ selector: "lib-language-selector",
501
+ host: { classAttribute: "relative" },
502
+ ngImport: i0,
503
+ template: `
434
504
  <button
435
505
  type="button"
436
506
  class="layout-topbar-action"
@@ -451,15 +521,46 @@ class LanguageSelectorComponent {
451
521
  class="absolute left-[0]"
452
522
  [style]="{ 'inset-inline-end': '0' }"
453
523
  />
454
- `, isInline: true, dependencies: [{ kind: "component", type: MenuComponent, selector: "f-menu", inputs: ["model", "popup", "appendTo", "styleClass", "style"], outputs: ["onShow", "onHide"] }, { kind: "component", type: UiIconComponent, selector: "ui-icon", inputs: ["name", "size", "spin", "fill", "severity"] }] });
455
- }
456
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.5", ngImport: i0, type: LanguageSelectorComponent, decorators: [{
457
- type: Component,
458
- args: [{
459
- selector: 'lib-language-selector',
460
- imports: [MenuComponent, UiIconComponent],
461
- host: { class: 'relative' },
462
- template: `
524
+ `,
525
+ isInline: true,
526
+ dependencies: [{
527
+ kind: "component",
528
+ type: MenuComponent,
529
+ selector: "f-menu",
530
+ inputs: [
531
+ "model",
532
+ "popup",
533
+ "appendTo",
534
+ "styleClass",
535
+ "style"
536
+ ],
537
+ outputs: ["onShow", "onHide"]
538
+ }, {
539
+ kind: "component",
540
+ type: UiIconComponent,
541
+ selector: "ui-icon",
542
+ inputs: [
543
+ "name",
544
+ "size",
545
+ "spin",
546
+ "fill",
547
+ "severity"
548
+ ]
549
+ }]
550
+ });
551
+ };
552
+ i0.ɵɵngDeclareClassMetadata({
553
+ minVersion: "12.0.0",
554
+ version: "22.2.1",
555
+ ngImport: i0,
556
+ type: LanguageSelectorComponent,
557
+ decorators: [{
558
+ type: Component,
559
+ args: [{
560
+ selector: "lib-language-selector",
561
+ imports: [MenuComponent, UiIconComponent],
562
+ host: { class: "relative" },
563
+ template: `
463
564
  <button
464
565
  type="button"
465
566
  class="layout-topbar-action"
@@ -480,2276 +581,184 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.5", ngImpor
480
581
  class="absolute left-[0]"
481
582
  [style]="{ 'inset-inline-end': '0' }"
482
583
  />
483
- `,
484
- }]
485
- }] });
486
-
487
- class LanguageSelectorPanelComponent {
488
- localizationState = inject(LocalizationStateService);
489
- translationLoader = inject(TranslationLoaderService);
490
- destroyRef = inject(DestroyRef);
491
- languages = this.localizationState.availableLanguages;
492
- _selectedLanguageCode = signal(this.localizationState.currentLanguageCode(), /* @ts-ignore */
493
- ...(ngDevMode ? [{ debugName: "_selectedLanguageCode" }] : /* istanbul ignore next */ []));
494
- selectedLanguageCode = this._selectedLanguageCode.asReadonly();
495
- menuItems = computed(() => this.languages().map((lang) => ({
496
- label: lang.nativeName || lang.name,
497
- badge: lang.code.toUpperCase(),
498
- selected: lang.code === this.selectedLanguageCode(),
499
- command: () => this.onLanguageSelect(lang.code),
500
- })), /* @ts-ignore */
501
- ...(ngDevMode ? [{ debugName: "menuItems" }] : /* istanbul ignore next */ []));
502
- constructor() {
503
- effect(() => {
504
- const currentCode = this.localizationState.currentLanguageCode();
505
- if (currentCode !== this._selectedLanguageCode()) {
506
- this._selectedLanguageCode.set(currentCode);
507
- }
508
- });
509
- }
510
- onLanguageSelect(code) {
511
- const language = this.languages().find((l) => l.code === code);
512
- if (language) {
513
- this._selectedLanguageCode.set(code);
514
- this.localizationState.setCurrentLanguage(language);
515
- this.translationLoader
516
- .loadTranslationsAsync(code, true)
517
- .pipe(takeUntilDestroyed(this.destroyRef))
518
- .subscribe();
519
- }
520
- }
521
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.5", ngImport: i0, type: LanguageSelectorPanelComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
522
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "22.1.5", type: LanguageSelectorPanelComponent, isStandalone: true, selector: "lib-language-selector-panel", host: { styleAttribute: "inset-inline-end: 0;", classAttribute: "hidden absolute z-50 top-[24px] p-3 min-w-[180px]" }, ngImport: i0, template: `<f-menu [model]="menuItems()" />`, isInline: true, dependencies: [{ kind: "component", type: MenuComponent, selector: "f-menu", inputs: ["model", "popup", "appendTo", "styleClass", "style"], outputs: ["onShow", "onHide"] }] });
523
- }
524
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.5", ngImport: i0, type: LanguageSelectorPanelComponent, decorators: [{
525
- type: Component,
526
- args: [{
527
- selector: 'lib-language-selector-panel',
528
- imports: [MenuComponent],
529
- host: {
530
- class: 'hidden absolute z-50 top-[24px] p-3 min-w-[180px]',
531
- style: 'inset-inline-end: 0;',
532
- },
533
- template: `<f-menu [model]="menuItems()" />`,
534
- }]
535
- }], ctorParameters: () => [] });
536
-
537
- function provideLocalization(config, initialFallbackMessages) {
538
- const providers = [
539
- // 1. Value/Existing Providers
540
- { provide: LOCALIZATION_CONFIG, useValue: config },
541
- {
542
- provide: LOCALIZATION_STATE_SERVICE,
543
- useExisting: LocalizationStateService,
544
- },
545
- { provide: LOCALIZATION_API_SERVICE, useExisting: LocalizationApiService },
546
- // 2. The Service itself
547
- LocalizationInitializerService,
548
- // 3. The modern Initializer
549
- provideAppInitializer(() => {
550
- const initializer = inject(LocalizationInitializerService);
551
- return initializer.initialize(initialFallbackMessages);
552
- }),
553
- ];
554
- if (config.enableLayoutSelector) {
555
- providers.push({
556
- provide: LAYOUT_LANGUAGE_SELECTOR,
557
- useValue: LanguageSelectorComponent,
558
- }, {
559
- provide: LAYOUT_LANGUAGE_SELECTOR_PANEL,
560
- useValue: LanguageSelectorPanelComponent,
561
- });
562
- }
563
- return providers;
564
- }
565
-
566
- class LanguageFormPage {
567
- router = inject(Router);
568
- route = inject(ActivatedRoute);
569
- languageService = inject(LanguageApiService);
570
- messageService = inject(MessageService);
571
- translateAdapter = inject(TranslateService, {
572
- optional: true,
573
- });
574
- destroyRef = inject(DestroyRef);
575
- routeParams = toSignal(this.route.paramMap);
576
- loadTriggered = false;
577
- isSaving = signal(false, /* @ts-ignore */
578
- ...(ngDevMode ? [{ debugName: "isSaving" }] : /* istanbul ignore next */ []));
579
- existingLanguage = signal(null, /* @ts-ignore */
580
- ...(ngDevMode ? [{ debugName: "existingLanguage" }] : /* istanbul ignore next */ []));
581
- isEditMode = computed(() => !!this.existingLanguage(), /* @ts-ignore */
582
- ...(ngDevMode ? [{ debugName: "isEditMode" }] : /* istanbul ignore next */ []));
583
- isReadonly = computed(() => this.existingLanguage()?.isReadonly ?? false, /* @ts-ignore */
584
- ...(ngDevMode ? [{ debugName: "isReadonly" }] : /* istanbul ignore next */ []));
585
- translate(key, vars) {
586
- return this.translateAdapter?.translate(key, vars) ?? key;
587
- }
588
- directionOptions = computed(() => [
589
- {
590
- label: this.translate('localization.language.direction.ltr'),
591
- value: 'ltr',
592
- },
593
- {
594
- label: this.translate('localization.language.direction.rtl'),
595
- value: 'rtl',
596
- },
597
- ], /* @ts-ignore */
598
- ...(ngDevMode ? [{ debugName: "directionOptions" }] : /* istanbul ignore next */ []));
599
- formModel = signal({
600
- code: '',
601
- name: '',
602
- nativeName: '',
603
- direction: 'ltr',
604
- serial: '',
605
- isActive: true,
606
- isDefault: false,
607
- }, /* @ts-ignore */
608
- ...(ngDevMode ? [{ debugName: "formModel" }] : /* istanbul ignore next */ []));
609
- formTree = form(this.formModel, (f) => {
610
- required(f.code, {
611
- message: this.translate('localization.validation.code.required'),
612
- });
613
- maxLength(f.code, 10, {
614
- message: this.translate('localization.validation.code.max.length'),
615
- });
616
- required(f.name, {
617
- message: this.translate('localization.validation.name.required'),
618
- });
619
- maxLength(f.name, 100, {
620
- message: this.translate('localization.validation.name.max.length'),
621
- });
622
- disabled(f.code, { when: () => this.isReadonly() });
623
- disabled(f.name, { when: () => this.isReadonly() });
624
- disabled(f.nativeName, { when: () => this.isReadonly() });
625
- });
626
- isFormValid = computed(() => {
627
- const model = this.formModel();
628
- return model.code.trim().length > 0 && model.name.trim().length > 0;
629
- }, /* @ts-ignore */
630
- ...(ngDevMode ? [{ debugName: "isFormValid" }] : /* istanbul ignore next */ []));
631
- constructor() {
632
- effect(() => {
633
- const params = this.routeParams();
634
- if (!params || this.loadTriggered)
635
- return;
636
- this.loadTriggered = true;
637
- const id = params.get('id');
638
- if (id && id !== 'new') {
639
- this.loadLanguage(id);
640
- }
641
- });
642
- }
643
- updateDirection(value) {
644
- this.formModel.update((m) => ({ ...m, direction: value }));
645
- }
646
- loadLanguage(id) {
647
- this.isSaving.set(true);
648
- from(this.languageService.findById(id))
649
- .pipe(takeUntilDestroyed(this.destroyRef), finalize(() => this.isSaving.set(false)))
650
- .subscribe({
651
- next: (response) => {
652
- if (response.success && response.data) {
653
- this.existingLanguage.set(response.data);
654
- this.formModel.set(this.toFormModel(response.data));
655
- }
656
- },
657
- error: () => this.router.navigate(['/localization/languages']),
658
- });
659
- }
660
- toFormModel(language) {
661
- return {
662
- code: language.code,
663
- name: language.name,
664
- nativeName: language.nativeName ?? '',
665
- direction: language.direction ?? 'ltr',
666
- serial: language.serial?.toString() ?? '',
667
- isActive: language.isActive,
668
- isDefault: language.isDefault,
669
- };
670
- }
671
- onSubmit() {
672
- if (!this.isFormValid())
673
- return;
674
- this.isSaving.set(true);
675
- const model = this.formModel();
676
- const dto = {
677
- code: model.code,
678
- name: model.name,
679
- nativeName: model.nativeName || undefined,
680
- direction: model.direction,
681
- serial: model.serial ? parseInt(model.serial, 10) : undefined,
682
- isActive: model.isActive,
683
- isDefault: model.isDefault,
684
- };
685
- const operation$ = this.isEditMode()
686
- ? from(this.languageService.update({
687
- id: this.existingLanguage().id,
688
- ...dto,
689
- }))
690
- : from(this.languageService.insert(dto));
691
- operation$
692
- .pipe(takeUntilDestroyed(this.destroyRef), finalize(() => this.isSaving.set(false)))
693
- .subscribe({
694
- next: (response) => {
695
- if (response.success) {
696
- this.messageService.add({
697
- severity: 'success',
698
- summary: this.translate('shared.success'),
699
- detail: response.messageKey
700
- ? this.translate(response.messageKey, response.messageVariables)
701
- : (response.message ?? ''),
702
- });
703
- this.router.navigate(['/localization/languages']);
704
- }
705
- },
706
- });
707
- }
708
- onCancel() {
709
- this.router.navigate(['/localization/languages']);
710
- }
711
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.5", ngImport: i0, type: LanguageFormPage, deps: [], target: i0.ɵɵFactoryTarget.Component });
712
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.1.5", type: LanguageFormPage, isStandalone: true, selector: "lib-language-form-page", providers: [MessageService], ngImport: i0, template: `
713
- <f-card>
714
- <ng-template #header>
715
- <div class="flex justify-between items-center p-4">
716
- <h2 class="text-xl font-semibold m-0">
717
- {{
718
- isEditMode()
719
- ? ('localization.language.edit' | translate)
720
- : ('localization.language.new' | translate)
721
- }}
722
- </h2>
723
- </div>
724
- </ng-template>
725
-
726
- <form
727
- (ngSubmit)="onSubmit()"
728
- class="grid grid-cols-1 md:grid-cols-2 gap-4"
729
- >
730
- <!-- Language Code -->
731
- <div class="flex flex-col gap-2">
732
- <label for="code" class="font-medium"
733
- >{{ 'localization.language.code' | translate }} *</label
734
- >
735
- <input
736
- fInputText
737
- id="code"
738
- [formField]="formTree.code"
739
- [placeholder]="'localization.placeholder.code' | translate"
740
- />
741
- <small class="text-muted-color">{{
742
- 'localization.language.iso.code.hint' | translate
743
- }}</small>
744
- </div>
745
-
746
- <!-- Language Name -->
747
- <div class="flex flex-col gap-2">
748
- <label for="name" class="font-medium"
749
- >{{ 'localization.language.name' | translate }} *</label
750
- >
751
- <input
752
- fInputText
753
- id="name"
754
- [formField]="formTree.name"
755
- [placeholder]="'localization.placeholder.name' | translate"
756
- />
757
- </div>
758
-
759
- <!-- Native Name -->
760
- <div class="flex flex-col gap-2">
761
- <label for="nativeName" class="font-medium">{{
762
- 'localization.language.native.name' | translate
763
- }}</label>
764
- <input
765
- fInputText
766
- id="nativeName"
767
- [formField]="formTree.nativeName"
768
- [placeholder]="'localization.placeholder.native.name' | translate"
769
- />
770
- <small class="text-muted-color">{{
771
- 'localization.language.native.name.hint' | translate
772
- }}</small>
773
- </div>
774
-
775
- <!-- Text Direction -->
776
- <div class="flex flex-col gap-2">
777
- <label for="direction" class="font-medium"
778
- >{{ 'localization.language.direction' | translate }} *</label
779
- >
780
- <f-select
781
- id="direction"
782
- [ngModel]="formModel().direction"
783
- (ngModelChange)="updateDirection($event)"
784
- [ngModelOptions]="{ standalone: true }"
785
- [options]="directionOptions()"
786
- optionLabel="label"
787
- optionValue="value"
788
- [disabled]="isReadonly()"
789
- class="w-full"
790
- />
791
- @if (isReadonly()) {
792
- <small class="text-muted-color">{{
793
- 'localization.language.readonly.edit.hint' | translate
794
- }}</small>
795
- }
796
- </div>
797
-
798
- <!-- Display Order -->
799
- <div class="flex flex-col gap-2">
800
- <label for="serial" class="font-medium">{{
801
- 'localization.language.display.order' | translate
802
- }}</label>
803
- <input
804
- fInputText
805
- id="serial"
806
- type="number"
807
- [formField]="formTree.serial"
808
- [placeholder]="'shared.display.order.placeholder' | translate"
809
- />
810
- </div>
811
-
812
- <!-- Empty cell for alignment -->
813
- <div class="hidden md:block"></div>
814
-
815
- <!-- Checkboxes -->
816
- <div class="flex items-center gap-6 md:col-span-2 pt-4">
817
- <f-checkbox [formField]="formTree.isActive" [label]="'shared.active' | translate" />
818
- <f-checkbox [formField]="formTree.isDefault" [label]="'localization.language.default' | translate" />
819
- </div>
820
-
821
- <!-- Actions -->
822
- <div class="flex justify-end gap-4 md:col-span-2 pt-4">
823
- <button
824
- fButton
825
- type="button"
826
- severity="secondary"
827
- [outlined]="true"
828
- (click)="onCancel()"
829
- >
830
- <span pButtonLabel>{{ 'shared.cancel' | translate }}</span>
831
- </button>
832
- <button
833
- fButton
834
- type="submit"
835
- [disabled]="!isFormValid() || isSaving()"
836
- >
837
- @if (isSaving()) {
838
- <ui-icon pButtonIcon name="spinner" [spin]="true"></ui-icon>
839
- }
840
- <span pButtonLabel>{{
841
- isEditMode()
842
- ? ('shared.update' | translate)
843
- : ('shared.create' | translate)
844
- }}</span>
845
- </button>
846
- </div>
847
- </form>
848
- </f-card>
849
- `, isInline: true, dependencies: [{ kind: "component", type: CardComponent, selector: "f-card", inputs: ["header", "subheader", "styleClass", "style"] }, { kind: "directive", type: InputTextDirective, selector: "[fInputText]", inputs: ["invalid", "touched", "variant", "size", "fluid"] }, { kind: "component", type: SelectComponent, selector: "f-select", inputs: ["options", "optionLabel", "optionValue", "placeholder", "disabled", "filter", "filterBy", "filterMatchMode", "filterLocale", "filterPlaceholder", "resetFilterOnHide", "autofocusFilter", "emptyMessage", "emptyFilterMessage", "showClear", "loading", "loadingIcon", "dropdownIcon", "clearIcon", "inputId", "invalid", "touched", "styleClass", "style", "appendTo", "editable", "readonly", "autofocus", "checkmark", "group", "optionGroupLabel", "optionGroupChildren", "virtualScroll", "virtualScrollItemSize", "lazy", "selectedObject"], outputs: ["onFilter", "onChange", "onShow", "onHide", "onBlur", "onFocus", "onClear", "onLazyLoad"] }, { kind: "component", type: CheckboxComponent, selector: "f-checkbox", inputs: ["label", "binary", "disabled", "readonly", "inputId", "invalid", "indeterminate", "variant", "size", "touched", "styleClass", "style", "checked"], outputs: ["onChange", "onBlur", "touch", "checkedChange"] }, { kind: "directive", type: FButtonDirective, selector: "[fButton]", inputs: ["severity", "text", "outlined", "link", "variant", "rounded", "raised", "fluid", "disabled", "loading", "loadingIcon", "size", "label", "icon", "iconPos", "ariaLabel", "plain"] }, { kind: "component", type: UiIconComponent, selector: "ui-icon", inputs: ["name", "size", "spin", "fill", "severity"] }, { kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i1.ɵNgNoValidate, selector: "form:not([ngNoForm]):not([ngNativeValidate])" }, { kind: "directive", type: i1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1.NgControlStatusGroup, selector: "[formGroupName],[formArrayName],[ngModelGroup],[formGroup],[formArray],form:not([ngNoForm]),[ngForm]" }, { kind: "directive", type: i1.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "directive", type: i1.NgForm, selector: "form:not([ngNoForm]):not([formGroup]):not([formArray]),ng-form,[ngForm]", inputs: ["ngFormOptions"], outputs: ["ngSubmit"], exportAs: ["ngForm"] }, { kind: "directive", type: FormField, selector: "[formField]", inputs: ["formField"], exportAs: ["formField"] }, { kind: "pipe", type: TranslatePipe, name: "translate" }] });
850
- }
851
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.5", ngImport: i0, type: LanguageFormPage, decorators: [{
852
- type: Component,
853
- args: [{
854
- selector: 'lib-language-form-page',
855
- imports: [
856
- CardComponent,
857
- InputTextDirective,
858
- SelectComponent,
859
- CheckboxComponent,
860
- FButtonDirective,
861
- UiIconComponent,
862
- FormsModule,
863
- FormField,
864
- TranslatePipe,
865
- ],
866
- providers: [MessageService],
867
- template: `
868
- <f-card>
869
- <ng-template #header>
870
- <div class="flex justify-between items-center p-4">
871
- <h2 class="text-xl font-semibold m-0">
872
- {{
873
- isEditMode()
874
- ? ('localization.language.edit' | translate)
875
- : ('localization.language.new' | translate)
876
- }}
877
- </h2>
878
- </div>
879
- </ng-template>
880
-
881
- <form
882
- (ngSubmit)="onSubmit()"
883
- class="grid grid-cols-1 md:grid-cols-2 gap-4"
884
- >
885
- <!-- Language Code -->
886
- <div class="flex flex-col gap-2">
887
- <label for="code" class="font-medium"
888
- >{{ 'localization.language.code' | translate }} *</label
889
- >
890
- <input
891
- fInputText
892
- id="code"
893
- [formField]="formTree.code"
894
- [placeholder]="'localization.placeholder.code' | translate"
895
- />
896
- <small class="text-muted-color">{{
897
- 'localization.language.iso.code.hint' | translate
898
- }}</small>
899
- </div>
900
-
901
- <!-- Language Name -->
902
- <div class="flex flex-col gap-2">
903
- <label for="name" class="font-medium"
904
- >{{ 'localization.language.name' | translate }} *</label
905
- >
906
- <input
907
- fInputText
908
- id="name"
909
- [formField]="formTree.name"
910
- [placeholder]="'localization.placeholder.name' | translate"
911
- />
912
- </div>
913
-
914
- <!-- Native Name -->
915
- <div class="flex flex-col gap-2">
916
- <label for="nativeName" class="font-medium">{{
917
- 'localization.language.native.name' | translate
918
- }}</label>
919
- <input
920
- fInputText
921
- id="nativeName"
922
- [formField]="formTree.nativeName"
923
- [placeholder]="'localization.placeholder.native.name' | translate"
924
- />
925
- <small class="text-muted-color">{{
926
- 'localization.language.native.name.hint' | translate
927
- }}</small>
928
- </div>
929
-
930
- <!-- Text Direction -->
931
- <div class="flex flex-col gap-2">
932
- <label for="direction" class="font-medium"
933
- >{{ 'localization.language.direction' | translate }} *</label
934
- >
935
- <f-select
936
- id="direction"
937
- [ngModel]="formModel().direction"
938
- (ngModelChange)="updateDirection($event)"
939
- [ngModelOptions]="{ standalone: true }"
940
- [options]="directionOptions()"
941
- optionLabel="label"
942
- optionValue="value"
943
- [disabled]="isReadonly()"
944
- class="w-full"
945
- />
946
- @if (isReadonly()) {
947
- <small class="text-muted-color">{{
948
- 'localization.language.readonly.edit.hint' | translate
949
- }}</small>
950
- }
951
- </div>
952
-
953
- <!-- Display Order -->
954
- <div class="flex flex-col gap-2">
955
- <label for="serial" class="font-medium">{{
956
- 'localization.language.display.order' | translate
957
- }}</label>
958
- <input
959
- fInputText
960
- id="serial"
961
- type="number"
962
- [formField]="formTree.serial"
963
- [placeholder]="'shared.display.order.placeholder' | translate"
964
- />
965
- </div>
966
-
967
- <!-- Empty cell for alignment -->
968
- <div class="hidden md:block"></div>
969
-
970
- <!-- Checkboxes -->
971
- <div class="flex items-center gap-6 md:col-span-2 pt-4">
972
- <f-checkbox [formField]="formTree.isActive" [label]="'shared.active' | translate" />
973
- <f-checkbox [formField]="formTree.isDefault" [label]="'localization.language.default' | translate" />
974
- </div>
975
-
976
- <!-- Actions -->
977
- <div class="flex justify-end gap-4 md:col-span-2 pt-4">
978
- <button
979
- fButton
980
- type="button"
981
- severity="secondary"
982
- [outlined]="true"
983
- (click)="onCancel()"
984
- >
985
- <span pButtonLabel>{{ 'shared.cancel' | translate }}</span>
986
- </button>
987
- <button
988
- fButton
989
- type="submit"
990
- [disabled]="!isFormValid() || isSaving()"
991
- >
992
- @if (isSaving()) {
993
- <ui-icon pButtonIcon name="spinner" [spin]="true"></ui-icon>
994
- }
995
- <span pButtonLabel>{{
996
- isEditMode()
997
- ? ('shared.update' | translate)
998
- : ('shared.create' | translate)
999
- }}</span>
1000
- </button>
1001
- </div>
1002
- </form>
1003
- </f-card>
1004
- `,
1005
- }]
1006
- }], ctorParameters: () => [] });
1007
-
1008
- var languageForm_page = /*#__PURE__*/Object.freeze({
1009
- __proto__: null,
1010
- LanguageFormPage: LanguageFormPage
1011
- });
1012
-
1013
- class LanguageListPage {
1014
- LANGUAGE_PERMISSIONS = LANGUAGE_PERMISSIONS;
1015
- router = inject(Router);
1016
- languageService = inject(LanguageApiService);
1017
- confirmationService = inject(ConfirmationService);
1018
- messageService = inject(MessageService);
1019
- translateService = inject(TranslateService);
1020
- destroyRef = inject(DestroyRef);
1021
- ngOnInit() {
1022
- this.languageService.fetchList('', {
1023
- pagination: { currentPage: 0, pageSize: 100 },
1024
- filter: {},
1025
- sort: { serial: 'ASC', name: 'ASC' },
1026
- });
1027
- }
1028
- onCreate() {
1029
- this.router.navigate(['/localization/languages/new']);
1030
- }
1031
- onSearchChange(term) {
1032
- this.languageService.fetchList(term, {
1033
- pagination: { currentPage: 0, pageSize: 100 },
1034
- filter: {},
1035
- sort: { serial: 'ASC', name: 'ASC' },
1036
- });
1037
- }
1038
- onEdit(id) {
1039
- this.router.navigate(['/localization/languages', id]);
1040
- }
1041
- onSetDefault(lang) {
1042
- this.confirmationService.confirm({
1043
- message: this.translateService.translate('localization.language.confirm.set.default', { name: lang.name }),
1044
- header: this.translateService.translate('localization.language.set.default'),
1045
- icon: 'star',
1046
- accept: () => {
1047
- this.languageService
1048
- .setDefault(lang.id)
1049
- .pipe(takeUntilDestroyed(this.destroyRef))
1050
- .subscribe({
1051
- next: (response) => {
1052
- if (response.success) {
1053
- this.messageService.add({
1054
- severity: 'success',
1055
- summary: this.translateService.translate('shared.success'),
1056
- detail: response.messageKey
1057
- ? this.translateService.translate(response.messageKey, response.messageVariables)
1058
- : (response.message ?? ''),
1059
- });
1060
- this.languageService.reload();
1061
- }
1062
- },
1063
- });
1064
- },
1065
- });
1066
- }
1067
- onDelete(lang) {
1068
- this.confirmationService.confirm({
1069
- message: this.translateService.translate('shared.confirm.delete'),
1070
- header: this.translateService.translate('localization.language.delete.title'),
1071
- icon: 'exclamation-triangle',
1072
- acceptButtonStyleClass: 'p-button-danger',
1073
- accept: () => {
1074
- from(this.languageService.delete({ id: lang.id, type: 'delete' }))
1075
- .pipe(takeUntilDestroyed(this.destroyRef))
1076
- .subscribe({
1077
- next: (response) => {
1078
- if (response.success) {
1079
- this.messageService.add({
1080
- severity: 'success',
1081
- summary: this.translateService.translate('shared.success'),
1082
- detail: response.messageKey
1083
- ? this.translateService.translate(response.messageKey, response.messageVariables)
1084
- : (response.message ?? ''),
1085
- });
1086
- this.languageService.reload();
1087
- }
1088
- },
1089
- });
1090
- },
1091
- });
1092
- }
1093
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.5", ngImport: i0, type: LanguageListPage, deps: [], target: i0.ɵɵFactoryTarget.Component });
1094
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.1.5", type: LanguageListPage, isStandalone: true, selector: "lib-language-list-page", ngImport: i0, template: `
1095
- <div class="card">
1096
- <div
1097
- class="flex flex-col sm:flex-row justify-between items-start sm:items-center gap-3 mb-4"
1098
- >
1099
- <div>
1100
- <h3 class="text-lg sm:text-xl font-semibold m-0">
1101
- {{ 'localization.language.title' | translate }}
1102
- </h3>
1103
- <p class="text-sm text-muted-color mt-1">
1104
- {{ 'localization.language.subtitle' | translate }}
1105
- </p>
1106
- </div>
1107
- <div
1108
- class="flex flex-col sm:flex-row items-stretch sm:items-center gap-2 w-full sm:w-auto"
1109
- >
1110
- <f-iconfield class="w-full sm:w-64">
1111
- <f-inputicon icon="search" />
1112
- <input
1113
- fInputText
1114
- type="text"
1115
- [ngModel]="languageService.searchTerm()"
1116
- (ngModelChange)="onSearchChange($event)"
1117
- [ngModelOptions]="{ standalone: true }"
1118
- [placeholder]="'shared.search' | translate"
1119
- class="w-full"
1120
- />
1121
- </f-iconfield>
1122
- <button
1123
- fButton
1124
- *hasPermission="LANGUAGE_PERMISSIONS.CREATE"
1125
- (click)="onCreate()"
1126
- class="w-full sm:w-auto"
1127
- >
1128
- <ui-icon pButtonIcon name="plus"></ui-icon>
1129
- <span pButtonLabel>{{
1130
- 'localization.language.new' | translate
1131
- }}</span>
1132
- </button>
1133
- </div>
1134
- </div>
1135
-
1136
- <div class="overflow-x-auto -mx-4 sm:mx-0">
1137
- <f-table
1138
- [value]="languageService.data()"
1139
- [loading]="languageService.isLoading()"
1140
- [tableStyle]="{ 'min-width': '40rem' }"
1141
- >
1142
- <ng-template #header>
1143
- <tr>
1144
- <th>{{ 'localization.language.code' | translate }}</th>
1145
- <th>{{ 'localization.language.name' | translate }}</th>
1146
- <th>{{ 'localization.language.native.name' | translate }}</th>
1147
- <th>{{ 'localization.language.direction' | translate }}</th>
1148
- <th>{{ 'shared.status' | translate }}</th>
1149
- <th class="w-[150px]">{{ 'shared.actions' | translate }}</th>
1150
- </tr>
1151
- </ng-template>
1152
-
1153
- <ng-template #body let-lang>
1154
- <tr>
1155
- <td>
1156
- <span class="uppercase font-semibold">{{ lang.code }}</span>
1157
- </td>
1158
- <td>{{ lang.name }}</td>
1159
- <td>{{ lang.nativeName || ('shared.na' | translate) }}</td>
1160
- <td>
1161
- <f-tag
1162
- [value]="
1163
- lang.direction === 'rtl'
1164
- ? ('localization.language.rtl' | translate)
1165
- : ('localization.language.ltr' | translate)
1166
- "
1167
- [severity]="lang.direction === 'rtl' ? 'warn' : 'info'"
1168
- />
1169
- </td>
1170
- <td>
1171
- <div class="flex gap-1 items-center">
1172
- <f-tag
1173
- [value]="
1174
- lang.isActive
1175
- ? ('shared.active' | translate)
1176
- : ('shared.inactive' | translate)
1177
- "
1178
- [severity]="lang.isActive ? 'success' : 'secondary'"
1179
- />
1180
- @if (lang.isDefault) {
1181
- <f-tag
1182
- [value]="'localization.language.default' | translate"
1183
- severity="contrast"
1184
- />
1185
- }
1186
- @if (lang.isReadonly) {
1187
- <f-tag
1188
- [value]="'shared.read.only' | translate"
1189
- severity="warn"
1190
- />
1191
- }
1192
- </div>
1193
- </td>
1194
- <td>
1195
- <div class="flex gap-1">
1196
- @if (!lang.isDefault) {
1197
- <button
1198
- fButton
1199
- *hasPermission="LANGUAGE_PERMISSIONS.UPDATE"
1200
- [text]="true"
1201
- size="small"
1202
- [fTooltip]="
1203
- 'localization.language.set.default' | translate
1204
- "
1205
- (click)="onSetDefault(lang)"
1206
- >
1207
- <ui-icon pButtonIcon name="star"></ui-icon>
1208
- </button>
1209
- }
1210
- <button
1211
- fButton
1212
- *hasPermission="LANGUAGE_PERMISSIONS.UPDATE"
1213
- [text]="true"
1214
- severity="secondary"
1215
- size="small"
1216
- [fTooltip]="'shared.edit' | translate"
1217
- (click)="onEdit(lang.id)"
1218
- >
1219
- <ui-icon pButtonIcon name="pencil"></ui-icon>
1220
- </button>
1221
- <button
1222
- fButton
1223
- *hasPermission="LANGUAGE_PERMISSIONS.DELETE"
1224
- [text]="true"
1225
- severity="danger"
1226
- size="small"
1227
- [fTooltip]="
1228
- (lang.isDefault
1229
- ? 'localization.language.default.delete.forbidden'
1230
- : lang.isReadonly
1231
- ? 'localization.language.readonly.delete.forbidden'
1232
- : 'shared.delete'
1233
- ) | translate
1234
- "
1235
- [disabled]="lang.isDefault || lang.isReadonly"
1236
- (click)="onDelete(lang)"
1237
- >
1238
- <ui-icon pButtonIcon name="trash"></ui-icon>
1239
- </button>
1240
- </div>
1241
- </td>
1242
- </tr>
1243
- </ng-template>
1244
-
1245
- <ng-template #emptymessage>
1246
- <tr>
1247
- <td colspan="6" class="text-center py-4 text-muted-color">
1248
- {{ 'localization.language.empty' | translate }}
1249
- </td>
1250
- </tr>
1251
- </ng-template>
1252
- </f-table>
1253
- </div>
1254
- </div>
1255
- `, isInline: true, dependencies: [{ kind: "directive", type: FButtonDirective, selector: "[fButton]", inputs: ["severity", "text", "outlined", "link", "variant", "rounded", "raised", "fluid", "disabled", "loading", "loadingIcon", "size", "label", "icon", "iconPos", "ariaLabel", "plain"] }, { kind: "component", type: UiIconComponent, selector: "ui-icon", inputs: ["name", "size", "spin", "fill", "severity"] }, { kind: "component", type: TableComponent, selector: "f-table", inputs: ["value", "lazy", "loading", "rows", "totalRecords", "rowHover", "dataKey", "scrollable", "scrollHeight", "paginator", "first", "rowsPerPageOptions", "showCurrentPageReport", "currentPageReportTemplate", "tableStyle", "styleClass", "globalFilterFields", "filterDelay", "responsiveLayout", "selectionMode", "sortMode", "rowExpandMode", "resizableColumns", "rowStyleClass", "virtualScroll", "virtualScrollItemSize", "sortField", "sortOrder", "multiSortMeta", "filters", "selection", "expandedRowKeys", "contextMenuSelection"], outputs: ["sortFieldChange", "sortOrderChange", "multiSortMetaChange", "filtersChange", "selectionChange", "expandedRowKeysChange", "contextMenuSelectionChange", "onLazyLoad", "onSort", "onFilter", "onRowSelect", "onRowUnselect", "onSelectAllChange", "onRowExpand", "onRowCollapse", "onColResize", "onRowContextMenu"] }, { kind: "component", type: TagComponent, selector: "f-tag", inputs: ["value", "severity", "icon", "rounded", "color", "styleClass", "style"] }, { kind: "directive", type: TooltipDirective, selector: "[fTooltip]", inputs: ["fTooltip", "tooltipPosition", "positionStyle", "tooltipStyleClass", "disabled", "tooltipEvent", "showDelay", "hideDelay", "life", "autoHide", "positionTop", "positionLeft", "escape"] }, { kind: "directive", type: HasPermissionDirective, selector: "[hasPermission]", inputs: ["hasPermission"] }, { kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i1.DefaultValueAccessor, selector: "input:not([type=checkbox]):not([ngNoCva])[formControlName],textarea:not([ngNoCva])[formControlName],input:not([type=checkbox]):not([ngNoCva])[formControl],textarea:not([ngNoCva])[formControl],input:not([type=checkbox]):not([ngNoCva])[ngModel],textarea:not([ngNoCva])[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "component", type: IconFieldComponent, selector: "f-iconfield", inputs: ["iconPosition", "styleClass"] }, { kind: "component", type: InputIconComponent, selector: "f-inputicon", inputs: ["icon", "position", "styleClass", "style"] }, { kind: "directive", type: InputTextDirective, selector: "[fInputText]", inputs: ["invalid", "touched", "variant", "size", "fluid"] }, { kind: "pipe", type: TranslatePipe, name: "translate" }] });
1256
- }
1257
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.5", ngImport: i0, type: LanguageListPage, decorators: [{
1258
- type: Component,
1259
- args: [{
1260
- selector: 'lib-language-list-page',
1261
- imports: [
1262
- FButtonDirective,
1263
- UiIconComponent,
1264
- TableComponent,
1265
- TagComponent,
1266
- TooltipDirective,
1267
- HasPermissionDirective,
1268
- TranslatePipe,
1269
- FormsModule,
1270
- IconFieldComponent,
1271
- InputIconComponent,
1272
- InputTextDirective,
1273
- ],
1274
- template: `
1275
- <div class="card">
1276
- <div
1277
- class="flex flex-col sm:flex-row justify-between items-start sm:items-center gap-3 mb-4"
1278
- >
1279
- <div>
1280
- <h3 class="text-lg sm:text-xl font-semibold m-0">
1281
- {{ 'localization.language.title' | translate }}
1282
- </h3>
1283
- <p class="text-sm text-muted-color mt-1">
1284
- {{ 'localization.language.subtitle' | translate }}
1285
- </p>
1286
- </div>
1287
- <div
1288
- class="flex flex-col sm:flex-row items-stretch sm:items-center gap-2 w-full sm:w-auto"
1289
- >
1290
- <f-iconfield class="w-full sm:w-64">
1291
- <f-inputicon icon="search" />
1292
- <input
1293
- fInputText
1294
- type="text"
1295
- [ngModel]="languageService.searchTerm()"
1296
- (ngModelChange)="onSearchChange($event)"
1297
- [ngModelOptions]="{ standalone: true }"
1298
- [placeholder]="'shared.search' | translate"
1299
- class="w-full"
1300
- />
1301
- </f-iconfield>
1302
- <button
1303
- fButton
1304
- *hasPermission="LANGUAGE_PERMISSIONS.CREATE"
1305
- (click)="onCreate()"
1306
- class="w-full sm:w-auto"
1307
- >
1308
- <ui-icon pButtonIcon name="plus"></ui-icon>
1309
- <span pButtonLabel>{{
1310
- 'localization.language.new' | translate
1311
- }}</span>
1312
- </button>
1313
- </div>
1314
- </div>
1315
-
1316
- <div class="overflow-x-auto -mx-4 sm:mx-0">
1317
- <f-table
1318
- [value]="languageService.data()"
1319
- [loading]="languageService.isLoading()"
1320
- [tableStyle]="{ 'min-width': '40rem' }"
1321
- >
1322
- <ng-template #header>
1323
- <tr>
1324
- <th>{{ 'localization.language.code' | translate }}</th>
1325
- <th>{{ 'localization.language.name' | translate }}</th>
1326
- <th>{{ 'localization.language.native.name' | translate }}</th>
1327
- <th>{{ 'localization.language.direction' | translate }}</th>
1328
- <th>{{ 'shared.status' | translate }}</th>
1329
- <th class="w-[150px]">{{ 'shared.actions' | translate }}</th>
1330
- </tr>
1331
- </ng-template>
1332
-
1333
- <ng-template #body let-lang>
1334
- <tr>
1335
- <td>
1336
- <span class="uppercase font-semibold">{{ lang.code }}</span>
1337
- </td>
1338
- <td>{{ lang.name }}</td>
1339
- <td>{{ lang.nativeName || ('shared.na' | translate) }}</td>
1340
- <td>
1341
- <f-tag
1342
- [value]="
1343
- lang.direction === 'rtl'
1344
- ? ('localization.language.rtl' | translate)
1345
- : ('localization.language.ltr' | translate)
1346
- "
1347
- [severity]="lang.direction === 'rtl' ? 'warn' : 'info'"
1348
- />
1349
- </td>
1350
- <td>
1351
- <div class="flex gap-1 items-center">
1352
- <f-tag
1353
- [value]="
1354
- lang.isActive
1355
- ? ('shared.active' | translate)
1356
- : ('shared.inactive' | translate)
1357
- "
1358
- [severity]="lang.isActive ? 'success' : 'secondary'"
1359
- />
1360
- @if (lang.isDefault) {
1361
- <f-tag
1362
- [value]="'localization.language.default' | translate"
1363
- severity="contrast"
1364
- />
1365
- }
1366
- @if (lang.isReadonly) {
1367
- <f-tag
1368
- [value]="'shared.read.only' | translate"
1369
- severity="warn"
1370
- />
1371
- }
1372
- </div>
1373
- </td>
1374
- <td>
1375
- <div class="flex gap-1">
1376
- @if (!lang.isDefault) {
1377
- <button
1378
- fButton
1379
- *hasPermission="LANGUAGE_PERMISSIONS.UPDATE"
1380
- [text]="true"
1381
- size="small"
1382
- [fTooltip]="
1383
- 'localization.language.set.default' | translate
1384
- "
1385
- (click)="onSetDefault(lang)"
1386
- >
1387
- <ui-icon pButtonIcon name="star"></ui-icon>
1388
- </button>
1389
- }
1390
- <button
1391
- fButton
1392
- *hasPermission="LANGUAGE_PERMISSIONS.UPDATE"
1393
- [text]="true"
1394
- severity="secondary"
1395
- size="small"
1396
- [fTooltip]="'shared.edit' | translate"
1397
- (click)="onEdit(lang.id)"
1398
- >
1399
- <ui-icon pButtonIcon name="pencil"></ui-icon>
1400
- </button>
1401
- <button
1402
- fButton
1403
- *hasPermission="LANGUAGE_PERMISSIONS.DELETE"
1404
- [text]="true"
1405
- severity="danger"
1406
- size="small"
1407
- [fTooltip]="
1408
- (lang.isDefault
1409
- ? 'localization.language.default.delete.forbidden'
1410
- : lang.isReadonly
1411
- ? 'localization.language.readonly.delete.forbidden'
1412
- : 'shared.delete'
1413
- ) | translate
1414
- "
1415
- [disabled]="lang.isDefault || lang.isReadonly"
1416
- (click)="onDelete(lang)"
1417
- >
1418
- <ui-icon pButtonIcon name="trash"></ui-icon>
1419
- </button>
1420
- </div>
1421
- </td>
1422
- </tr>
1423
- </ng-template>
1424
-
1425
- <ng-template #emptymessage>
1426
- <tr>
1427
- <td colspan="6" class="text-center py-4 text-muted-color">
1428
- {{ 'localization.language.empty' | translate }}
1429
- </td>
1430
- </tr>
1431
- </ng-template>
1432
- </f-table>
1433
- </div>
1434
- </div>
1435
- `,
1436
- }]
1437
- }] });
1438
-
1439
- var languageList_page = /*#__PURE__*/Object.freeze({
1440
- __proto__: null,
1441
- LanguageListPage: LanguageListPage
584
+ `
585
+ }]
586
+ }]
1442
587
  });
1443
-
1444
- class LocalizationContainerPage {
1445
- permissionValidator = inject(PermissionValidatorService);
1446
- allTabs = [
1447
- {
1448
- id: 'languages',
1449
- labelKey: 'localization.tabs.languages',
1450
- icon: 'globe',
1451
- route: 'languages',
1452
- permission: LANGUAGE_PERMISSIONS.READ,
1453
- },
1454
- {
1455
- id: 'keys',
1456
- labelKey: 'localization.tabs.keys',
1457
- icon: 'key',
1458
- route: 'keys',
1459
- permission: TRANSLATION_KEY_PERMISSIONS.READ,
1460
- },
1461
- ];
1462
- visibleTabs = computed(() => {
1463
- // Explicitly track permissions signal for reactivity
1464
- const permissions = this.permissionValidator.permissions();
1465
- if (!permissions.length)
1466
- return [];
1467
- return this.allTabs.filter((tab) => this.permissionValidator.hasPermission(tab.permission));
1468
- }, /* @ts-ignore */
1469
- ...(ngDevMode ? [{ debugName: "visibleTabs" }] : /* istanbul ignore next */ []));
1470
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.5", ngImport: i0, type: LocalizationContainerPage, deps: [], target: i0.ɵɵFactoryTarget.Component });
1471
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.1.5", type: LocalizationContainerPage, isStandalone: true, selector: "lib-localization-container", ngImport: i0, template: `
1472
- <div class="p-2 sm:p-4">
1473
- <div class="mb-4">
1474
- <h1 class="text-xl sm:text-2xl font-bold m-0">{{ 'localization.title' | translate }}</h1>
1475
- <p class="text-muted-color mt-1 text-sm sm:text-base">{{ 'localization.subtitle' | translate }}</p>
1476
- </div>
1477
-
1478
- <div class="scrollbar-hide flex gap-1 mb-4 border-b border-surface overflow-x-auto flex-nowrap">
1479
- @for (tab of visibleTabs(); track tab.id) {
1480
- <a
1481
- [routerLink]="tab.route"
1482
- routerLinkActive="tab-active"
1483
- class="flex items-center gap-2 px-4 py-2 rounded-t-lg cursor-pointer transition-colors text-muted-color no-underline border-b-2 border-transparent -mb-px hover:bg-surface-hover [&.tab-active]:text-primary [&.tab-active]:border-primary [&.tab-active]:bg-surface-ground whitespace-nowrap shrink-0"
1484
- >
1485
- <lib-icon [icon]="tab.icon"></lib-icon>
1486
- <span>{{ tab.labelKey | translate }}</span>
1487
- </a>
1488
- }
1489
- </div>
1490
-
1491
- <router-outlet />
1492
- </div>
1493
- `, isInline: true, styles: [".scrollbar-hide{-ms-overflow-style:none;scrollbar-width:none}.scrollbar-hide::-webkit-scrollbar{display:none}\n"], dependencies: [{ kind: "directive", type: RouterOutlet, selector: "router-outlet", inputs: ["name", "routerOutletData"], outputs: ["activate", "deactivate", "attach", "detach"], exportAs: ["outlet"] }, { kind: "directive", type: RouterLink, selector: "[routerLink]", inputs: ["target", "queryParams", "fragment", "queryParamsHandling", "state", "info", "relativeTo", "preserveFragment", "skipLocationChange", "replaceUrl", "browserUrl", "routerLink"] }, { kind: "directive", type: RouterLinkActive, selector: "[routerLinkActive]", inputs: ["routerLinkActiveOptions", "ariaCurrentWhenActive", "routerLinkActive"], outputs: ["isActiveChange"], exportAs: ["routerLinkActive"] }, { kind: "component", type: IconComponent, selector: "lib-icon", inputs: ["icon", "iconType"] }, { kind: "pipe", type: TranslatePipe, name: "translate" }] });
1494
- }
1495
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.5", ngImport: i0, type: LocalizationContainerPage, decorators: [{
1496
- type: Component,
1497
- args: [{ selector: 'lib-localization-container', imports: [RouterOutlet, RouterLink, RouterLinkActive, TranslatePipe, IconComponent], template: `
1498
- <div class="p-2 sm:p-4">
1499
- <div class="mb-4">
1500
- <h1 class="text-xl sm:text-2xl font-bold m-0">{{ 'localization.title' | translate }}</h1>
1501
- <p class="text-muted-color mt-1 text-sm sm:text-base">{{ 'localization.subtitle' | translate }}</p>
1502
- </div>
1503
-
1504
- <div class="scrollbar-hide flex gap-1 mb-4 border-b border-surface overflow-x-auto flex-nowrap">
1505
- @for (tab of visibleTabs(); track tab.id) {
1506
- <a
1507
- [routerLink]="tab.route"
1508
- routerLinkActive="tab-active"
1509
- class="flex items-center gap-2 px-4 py-2 rounded-t-lg cursor-pointer transition-colors text-muted-color no-underline border-b-2 border-transparent -mb-px hover:bg-surface-hover [&.tab-active]:text-primary [&.tab-active]:border-primary [&.tab-active]:bg-surface-ground whitespace-nowrap shrink-0"
1510
- >
1511
- <lib-icon [icon]="tab.icon"></lib-icon>
1512
- <span>{{ tab.labelKey | translate }}</span>
1513
- </a>
1514
- }
1515
- </div>
1516
-
1517
- <router-outlet />
1518
- </div>
1519
- `, styles: [".scrollbar-hide{-ms-overflow-style:none;scrollbar-width:none}.scrollbar-hide::-webkit-scrollbar{display:none}\n"] }]
1520
- }] });
1521
-
1522
- var localizationContainer_page = /*#__PURE__*/Object.freeze({
1523
- __proto__: null,
1524
- LocalizationContainerPage: LocalizationContainerPage
1525
- });
1526
-
1527
- class TranslationKeyFormPage {
1528
- router = inject(Router);
1529
- route = inject(ActivatedRoute);
1530
- languageService = inject(LanguageApiService);
1531
- translationKeyService = inject(TranslationKeyApiService);
1532
- translationService = inject(TranslationApiService);
1533
- messageService = inject(MessageService);
1534
- translateAdapter = inject(TranslateService, {
1535
- optional: true,
1536
- });
1537
- destroyRef = inject(DestroyRef);
1538
- routeParams = toSignal(this.route.paramMap);
1539
- loadTriggered = false;
1540
- isSaving = signal(false, /* @ts-ignore */
1541
- ...(ngDevMode ? [{ debugName: "isSaving" }] : /* istanbul ignore next */ []));
1542
- isLoadingLanguages = signal(false, /* @ts-ignore */
1543
- ...(ngDevMode ? [{ debugName: "isLoadingLanguages" }] : /* istanbul ignore next */ []));
1544
- existingKey = signal(null, /* @ts-ignore */
1545
- ...(ngDevMode ? [{ debugName: "existingKey" }] : /* istanbul ignore next */ []));
1546
- isEditMode = computed(() => !!this.existingKey(), /* @ts-ignore */
1547
- ...(ngDevMode ? [{ debugName: "isEditMode" }] : /* istanbul ignore next */ []));
1548
- isReadonly = computed(() => this.existingKey()?.isReadonly ?? false, /* @ts-ignore */
1549
- ...(ngDevMode ? [{ debugName: "isReadonly" }] : /* istanbul ignore next */ []));
1550
- languages = signal([], /* @ts-ignore */
1551
- ...(ngDevMode ? [{ debugName: "languages" }] : /* istanbul ignore next */ []));
1552
- modules = signal([], /* @ts-ignore */
1553
- ...(ngDevMode ? [{ debugName: "modules" }] : /* istanbul ignore next */ []));
1554
- translations = signal([], /* @ts-ignore */
1555
- ...(ngDevMode ? [{ debugName: "translations" }] : /* istanbul ignore next */ []));
1556
- translate(key, vars) {
1557
- return this.translateAdapter?.translate(key, vars) ?? key;
1558
- }
1559
- moduleOptions = computed(() => this.modules().map((m) => ({ label: m, value: m })), /* @ts-ignore */
1560
- ...(ngDevMode ? [{ debugName: "moduleOptions" }] : /* istanbul ignore next */ []));
1561
- formModel = signal({
1562
- key: '',
1563
- module: '',
1564
- defaultMessage: '',
1565
- description: '',
1566
- variables: '',
1567
- }, /* @ts-ignore */
1568
- ...(ngDevMode ? [{ debugName: "formModel" }] : /* istanbul ignore next */ []));
1569
- formTree = form(this.formModel, (f) => {
1570
- required(f.key, {
1571
- message: this.translate('localization.validation.key.required'),
1572
- });
1573
- required(f.defaultMessage, {
1574
- message: this.translate('localization.validation.default.message.required'),
1575
- });
1576
- disabled(f.key, { when: () => this.isReadonly() });
1577
- });
1578
- isFormValid = computed(() => {
1579
- const model = this.formModel();
1580
- return (model.key.trim().length > 0 &&
1581
- model.module.trim().length > 0 &&
1582
- model.defaultMessage.trim().length > 0);
1583
- }, /* @ts-ignore */
1584
- ...(ngDevMode ? [{ debugName: "isFormValid" }] : /* istanbul ignore next */ []));
1585
- constructor() {
1586
- effect(() => {
1587
- const params = this.routeParams();
1588
- if (!params || this.loadTriggered)
1589
- return;
1590
- this.loadTriggered = true;
1591
- const id = params.get('id');
1592
- if (id && id !== 'new') {
1593
- this.loadTranslationKey(id);
1594
- }
1595
- });
1596
- }
1597
- ngOnInit() {
1598
- this.isLoadingLanguages.set(true);
1599
- forkJoin({
1600
- languages: from(this.languageService.getAll({
1601
- pagination: { currentPage: 0, pageSize: 100 },
1602
- filter: {},
1603
- sort: { serial: 'ASC', name: 'ASC' },
1604
- })),
1605
- modules: this.translationKeyService.getModules(),
1606
- })
1607
- .pipe(takeUntilDestroyed(this.destroyRef), finalize(() => this.isLoadingLanguages.set(false)))
1608
- .subscribe({
1609
- next: ({ languages, modules }) => {
1610
- if (languages.success && languages.data) {
1611
- const activeLanguages = languages.data.filter((l) => l.isActive);
1612
- this.languages.set(activeLanguages);
1613
- this.initializeTranslations(activeLanguages);
1614
- }
1615
- if (modules.success && modules.data) {
1616
- this.modules.set(modules.data);
1617
- }
1618
- },
1619
- });
1620
- }
1621
- initializeTranslations(languages) {
1622
- const current = this.translations();
1623
- if (current.length === 0) {
1624
- this.translations.set(languages.map((lang) => ({ languageId: lang.id, value: '' })));
1625
- }
1626
- }
1627
- updateModule(value) {
1628
- this.formModel.update((m) => ({ ...m, module: value ?? '' }));
1629
- }
1630
- getTranslationValue(languageId) {
1631
- return (this.translations().find((t) => t.languageId === languageId)?.value ?? '');
1632
- }
1633
- setTranslationValue(languageId, value) {
1634
- this.translations.update((list) => {
1635
- const existing = list.find((t) => t.languageId === languageId);
1636
- if (existing) {
1637
- return list.map((t) => t.languageId === languageId ? { ...t, value } : t);
1638
- }
1639
- return [...list, { languageId, value }];
1640
- });
1641
- }
1642
- loadTranslationKey(id) {
1643
- this.isSaving.set(true);
1644
- from(this.translationKeyService.findById(id))
1645
- .pipe(takeUntilDestroyed(this.destroyRef), finalize(() => this.isSaving.set(false)))
1646
- .subscribe({
1647
- next: (response) => {
1648
- if (response.success && response.data) {
1649
- this.existingKey.set(response.data);
1650
- this.formModel.set(this.toFormModel(response.data));
1651
- this.loadExistingTranslations(id);
1652
- }
1653
- },
1654
- error: () => this.router.navigate(['/localization/keys']),
1655
- });
1656
- }
1657
- loadExistingTranslations(keyId) {
1658
- this.translationService
1659
- .getByKeyId(keyId)
1660
- .pipe(takeUntilDestroyed(this.destroyRef))
1661
- .subscribe({
1662
- next: (response) => {
1663
- if (response.success && response.data) {
1664
- this.translations.update((list) => {
1665
- const updated = [...list];
1666
- response.data.forEach((t) => {
1667
- const existing = updated.find((tr) => tr.languageId === t.languageId);
1668
- if (existing) {
1669
- existing.value = t.value;
1670
- existing.translationId = t.id;
1671
- }
1672
- else {
1673
- updated.push({
1674
- languageId: t.languageId,
1675
- value: t.value,
1676
- translationId: t.id,
1677
- });
1678
- }
1679
- });
1680
- return updated;
1681
- });
1682
- }
1683
- },
1684
- });
1685
- }
1686
- toFormModel(key) {
1687
- return {
1688
- key: key.key,
1689
- module: key.module,
1690
- defaultMessage: key.defaultMessage,
1691
- description: key.description ?? '',
1692
- variables: key.variables?.join(', ') ?? '',
1693
- };
1694
- }
1695
- onSubmit() {
1696
- if (!this.isFormValid())
1697
- return;
1698
- this.isSaving.set(true);
1699
- const model = this.formModel();
1700
- const variables = model.variables
1701
- .split(',')
1702
- .map((v) => v.trim())
1703
- .filter((v) => v.length > 0);
1704
- if (this.isEditMode()) {
1705
- this.updateKey(model, variables);
1706
- }
1707
- else {
1708
- this.createKey(model, variables);
1709
- }
1710
- }
1711
- createKey(model, variables) {
1712
- const createDto = {
1713
- key: model.key,
1714
- module: model.module,
1715
- defaultMessage: model.defaultMessage,
1716
- description: model.description || undefined,
1717
- variables: variables.length > 0 ? variables : undefined,
1718
- };
1719
- from(this.translationKeyService.insert(createDto))
1720
- .pipe(takeUntilDestroyed(this.destroyRef))
1721
- .subscribe({
1722
- next: (response) => {
1723
- if (response.success && response.data) {
1724
- this.saveTranslations(response.data.id, response.messageKey, response.messageVariables);
1725
- }
1726
- else {
1727
- this.isSaving.set(false);
1728
- }
1729
- },
1730
- error: () => this.isSaving.set(false),
1731
- });
1732
- }
1733
- updateKey(model, variables) {
1734
- const updateDto = {
1735
- id: this.existingKey().id,
1736
- key: model.key,
1737
- module: model.module,
1738
- defaultMessage: model.defaultMessage,
1739
- description: model.description || undefined,
1740
- variables: variables.length > 0 ? variables : undefined,
1741
- };
1742
- from(this.translationKeyService.update(updateDto))
1743
- .pipe(takeUntilDestroyed(this.destroyRef))
1744
- .subscribe({
1745
- next: (response) => {
1746
- if (response.success) {
1747
- this.saveTranslations(this.existingKey().id, response.messageKey, response.messageVariables);
1748
- }
1749
- else {
1750
- this.isSaving.set(false);
1751
- }
1752
- },
1753
- error: () => this.isSaving.set(false),
1754
- });
1755
- }
1756
- saveTranslations(keyId, messageKey, messageVariables) {
1757
- const translationsToSave = this.translations()
1758
- .filter((t) => t.value?.trim())
1759
- .map((t) => ({
1760
- id: t.translationId,
1761
- languageId: t.languageId,
1762
- translationKeyId: keyId,
1763
- value: t.value,
1764
- isVerified: false,
1765
- }));
1766
- if (translationsToSave.length === 0) {
1767
- this.onSaveSuccess(messageKey, messageVariables);
1768
- return;
1769
- }
1770
- this.translationService
1771
- .bulkUpdate(translationsToSave)
1772
- .pipe(takeUntilDestroyed(this.destroyRef))
1773
- .subscribe({
1774
- next: () => this.onSaveSuccess(messageKey, messageVariables),
1775
- error: () => this.onSaveSuccess(messageKey, messageVariables),
1776
- });
1777
- }
1778
- onSaveSuccess(messageKey, messageVariables) {
1779
- this.messageService.add({
1780
- severity: 'success',
1781
- summary: this.translate('shared.success'),
1782
- detail: messageKey ? this.translate(messageKey, messageVariables) : '',
1783
- });
1784
- this.isSaving.set(false);
1785
- this.router.navigate(['/localization/keys']);
1786
- }
1787
- onCancel() {
1788
- this.router.navigate(['/localization/keys']);
1789
- }
1790
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.5", ngImport: i0, type: TranslationKeyFormPage, deps: [], target: i0.ɵɵFactoryTarget.Component });
1791
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.1.5", type: TranslationKeyFormPage, isStandalone: true, selector: "lib-translation-key-form-page", providers: [MessageService], ngImport: i0, template: `
1792
- <f-card>
1793
- <ng-template #header>
1794
- <div class="flex justify-between items-center p-4">
1795
- <h2 class="text-xl font-semibold m-0">
1796
- {{
1797
- isEditMode()
1798
- ? ('localization.key.edit' | translate)
1799
- : ('localization.key.new' | translate)
1800
- }}
1801
- </h2>
1802
- </div>
1803
- </ng-template>
1804
-
1805
- <form (ngSubmit)="onSubmit()" class="flex flex-col gap-6">
1806
- <!-- Key Info Section -->
1807
- <div class="p-4 surface-ground rounded-lg">
1808
- <h4 class="font-semibold mt-0 mb-4">
1809
- {{ 'localization.key.about' | translate }}
1810
- </h4>
1811
- <div class="grid grid-cols-1 md:grid-cols-2 gap-4">
1812
- <!-- Key Name -->
1813
- <div class="flex flex-col gap-2">
1814
- <label for="key" class="font-medium"
1815
- >{{ 'localization.key.name' | translate }} *</label
1816
- >
1817
- <input
1818
- fInputText
1819
- id="key"
1820
- [formField]="formTree.key"
1821
- [placeholder]="'localization.placeholder.key.name' | translate"
1822
- />
1823
- </div>
1824
-
1825
- <!-- Module -->
1826
- <div class="flex flex-col gap-2">
1827
- <label for="module" class="font-medium"
1828
- >{{ 'localization.key.module' | translate }} *</label
1829
- >
1830
- <f-select
1831
- id="module"
1832
- [ngModel]="formModel().module"
1833
- (ngModelChange)="updateModule($event)"
1834
- [ngModelOptions]="{ standalone: true }"
1835
- [options]="moduleOptions()"
1836
- optionLabel="label"
1837
- optionValue="value"
1838
- [editable]="true"
1839
- [disabled]="isReadonly()"
1840
- [placeholder]="'localization.key.select.module' | translate"
1841
- class="w-full"
1842
- />
1843
- </div>
1844
-
1845
- @if (isReadonly()) {
1846
- <div class="md:col-span-2 -mt-2">
1847
- <small class="text-muted-color">{{
1848
- 'localization.key.readonly.edit.hint' | translate
1849
- }}</small>
1850
- </div>
1851
- }
1852
-
1853
- <!-- Default Message -->
1854
- <div class="flex flex-col gap-2 md:col-span-2">
1855
- <label for="defaultMessage" class="font-medium"
1856
- >{{ 'localization.key.default.message' | translate }} *</label
1857
- >
1858
- <textarea
1859
- fTextarea
1860
- id="defaultMessage"
1861
- [formField]="formTree.defaultMessage"
1862
- [rows]="2"
1863
- [placeholder]="
1864
- 'localization.placeholder.default.message' | translate
1865
- "
1866
- ></textarea>
1867
- </div>
1868
-
1869
- <!-- Description -->
1870
- <div class="flex flex-col gap-2 md:col-span-2">
1871
- <label for="description" class="font-medium">{{
1872
- 'localization.key.description' | translate
1873
- }}</label>
1874
- <input
1875
- fInputText
1876
- id="description"
1877
- [formField]="formTree.description"
1878
- [placeholder]="
1879
- 'localization.placeholder.key.description' | translate
1880
- "
1881
- />
1882
- </div>
1883
-
1884
- <!-- Variables -->
1885
- <div class="flex flex-col gap-2 md:col-span-2">
1886
- <label for="variables" class="font-medium">{{
1887
- 'localization.key.variables' | translate
1888
- }}</label>
1889
- <input
1890
- fInputText
1891
- id="variables"
1892
- [formField]="formTree.variables"
1893
- [placeholder]="
1894
- 'localization.key.variables.placeholder' | translate
1895
- "
1896
- />
1897
- <small class="text-muted-color">{{
1898
- 'localization.key.variables.hint' | translate
1899
- }}</small>
1900
- </div>
1901
- </div>
1902
- </div>
1903
-
1904
- <!-- Translations Section -->
1905
- <div class="p-4 surface-ground rounded-lg">
1906
- <h4 class="font-semibold mt-0 mb-4">
1907
- {{ 'localization.key.translations.section' | translate }}
1908
- </h4>
1909
- @if (isLoadingLanguages()) {
1910
- <div class="flex justify-center py-4">
1911
- <f-progressspinner strokeWidth="4" />
1912
- </div>
1913
- } @else if (languages().length === 0) {
1914
- <p class="text-muted-color text-center py-4">
1915
- {{ 'localization.key.no.languages' | translate }}
1916
- </p>
1917
- } @else {
1918
- <div class="grid grid-cols-1 md:grid-cols-2 gap-4">
1919
- @for (lang of languages(); track lang.id) {
1920
- <div class="flex flex-col gap-2">
1921
- <div class="flex justify-between items-center">
1922
- <label class="font-medium">
1923
- {{ lang.name }}
1924
- @if (lang.nativeName && lang.nativeName !== lang.name) {
1925
- <span class="text-muted-color"
1926
- >({{ lang.nativeName }})</span
1927
- >
1928
- }
1929
- </label>
1930
- <f-tag
1931
- [value]="lang.code"
1932
- severity="secondary"
1933
- class="uppercase"
1934
- />
1935
- </div>
1936
- <textarea
1937
- fTextarea
1938
- [ngModel]="getTranslationValue(lang.id)"
1939
- (ngModelChange)="setTranslationValue(lang.id, $event)"
1940
- [ngModelOptions]="{ standalone: true }"
1941
- [rows]="2"
1942
- [placeholder]="
1943
- formModel().defaultMessage ||
1944
- ('localization.key.enter.translation' | translate)
1945
- "
1946
- ></textarea>
1947
- </div>
1948
- }
1949
- </div>
1950
- }
1951
- </div>
1952
-
1953
- <!-- Actions -->
1954
- <div class="flex justify-end gap-4 pt-4">
1955
- <button
1956
- fButton
1957
- type="button"
1958
- severity="secondary"
1959
- [outlined]="true"
1960
- (click)="onCancel()"
1961
- >
1962
- <span pButtonLabel>{{ 'shared.cancel' | translate }}</span>
1963
- </button>
1964
- <button
1965
- fButton
1966
- type="submit"
1967
- [disabled]="!isFormValid() || isSaving()"
1968
- >
1969
- @if (isSaving()) {
1970
- <ui-icon pButtonIcon name="spinner" [spin]="true"></ui-icon>
1971
- }
1972
- <span pButtonLabel>{{
1973
- isEditMode()
1974
- ? ('shared.update' | translate)
1975
- : ('shared.create' | translate)
1976
- }}</span>
1977
- </button>
1978
- </div>
1979
- </form>
1980
- </f-card>
1981
- `, isInline: true, dependencies: [{ kind: "component", type: CardComponent, selector: "f-card", inputs: ["header", "subheader", "styleClass", "style"] }, { kind: "directive", type: InputTextDirective, selector: "[fInputText]", inputs: ["invalid", "touched", "variant", "size", "fluid"] }, { kind: "component", type: SelectComponent, selector: "f-select", inputs: ["options", "optionLabel", "optionValue", "placeholder", "disabled", "filter", "filterBy", "filterMatchMode", "filterLocale", "filterPlaceholder", "resetFilterOnHide", "autofocusFilter", "emptyMessage", "emptyFilterMessage", "showClear", "loading", "loadingIcon", "dropdownIcon", "clearIcon", "inputId", "invalid", "touched", "styleClass", "style", "appendTo", "editable", "readonly", "autofocus", "checkmark", "group", "optionGroupLabel", "optionGroupChildren", "virtualScroll", "virtualScrollItemSize", "lazy", "selectedObject"], outputs: ["onFilter", "onChange", "onShow", "onHide", "onBlur", "onFocus", "onClear", "onLazyLoad"] }, { kind: "directive", type: TextareaDirective, selector: "[fTextarea]", inputs: ["invalid", "touched", "variant", "fluid", "autoResize"], outputs: ["onResize"] }, { kind: "component", type: ProgressSpinnerComponent, selector: "f-progressspinner", inputs: ["size", "strokeWidth", "animationDuration", "styleClass", "ariaLabel"] }, { kind: "component", type: TagComponent, selector: "f-tag", inputs: ["value", "severity", "icon", "rounded", "color", "styleClass", "style"] }, { kind: "directive", type: FButtonDirective, selector: "[fButton]", inputs: ["severity", "text", "outlined", "link", "variant", "rounded", "raised", "fluid", "disabled", "loading", "loadingIcon", "size", "label", "icon", "iconPos", "ariaLabel", "plain"] }, { kind: "component", type: UiIconComponent, selector: "ui-icon", inputs: ["name", "size", "spin", "fill", "severity"] }, { kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i1.ɵNgNoValidate, selector: "form:not([ngNoForm]):not([ngNativeValidate])" }, { kind: "directive", type: i1.DefaultValueAccessor, selector: "input:not([type=checkbox]):not([ngNoCva])[formControlName],textarea:not([ngNoCva])[formControlName],input:not([type=checkbox]):not([ngNoCva])[formControl],textarea:not([ngNoCva])[formControl],input:not([type=checkbox]):not([ngNoCva])[ngModel],textarea:not([ngNoCva])[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1.NgControlStatusGroup, selector: "[formGroupName],[formArrayName],[ngModelGroup],[formGroup],[formArray],form:not([ngNoForm]),[ngForm]" }, { kind: "directive", type: i1.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "directive", type: i1.NgForm, selector: "form:not([ngNoForm]):not([formGroup]):not([formArray]),ng-form,[ngForm]", inputs: ["ngFormOptions"], outputs: ["ngSubmit"], exportAs: ["ngForm"] }, { kind: "directive", type: FormField, selector: "[formField]", inputs: ["formField"], exportAs: ["formField"] }, { kind: "pipe", type: TranslatePipe, name: "translate" }] });
1982
- }
1983
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.5", ngImport: i0, type: TranslationKeyFormPage, decorators: [{
1984
- type: Component,
1985
- args: [{
1986
- selector: 'lib-translation-key-form-page',
1987
- imports: [
1988
- CardComponent,
1989
- InputTextDirective,
1990
- SelectComponent,
1991
- TextareaDirective,
1992
- ProgressSpinnerComponent,
1993
- TagComponent,
1994
- FButtonDirective,
1995
- UiIconComponent,
1996
- FormsModule,
1997
- FormField,
1998
- TranslatePipe,
1999
- ],
2000
- providers: [MessageService],
2001
- template: `
2002
- <f-card>
2003
- <ng-template #header>
2004
- <div class="flex justify-between items-center p-4">
2005
- <h2 class="text-xl font-semibold m-0">
2006
- {{
2007
- isEditMode()
2008
- ? ('localization.key.edit' | translate)
2009
- : ('localization.key.new' | translate)
2010
- }}
2011
- </h2>
2012
- </div>
2013
- </ng-template>
2014
-
2015
- <form (ngSubmit)="onSubmit()" class="flex flex-col gap-6">
2016
- <!-- Key Info Section -->
2017
- <div class="p-4 surface-ground rounded-lg">
2018
- <h4 class="font-semibold mt-0 mb-4">
2019
- {{ 'localization.key.about' | translate }}
2020
- </h4>
2021
- <div class="grid grid-cols-1 md:grid-cols-2 gap-4">
2022
- <!-- Key Name -->
2023
- <div class="flex flex-col gap-2">
2024
- <label for="key" class="font-medium"
2025
- >{{ 'localization.key.name' | translate }} *</label
2026
- >
2027
- <input
2028
- fInputText
2029
- id="key"
2030
- [formField]="formTree.key"
2031
- [placeholder]="'localization.placeholder.key.name' | translate"
2032
- />
2033
- </div>
2034
-
2035
- <!-- Module -->
2036
- <div class="flex flex-col gap-2">
2037
- <label for="module" class="font-medium"
2038
- >{{ 'localization.key.module' | translate }} *</label
2039
- >
2040
- <f-select
2041
- id="module"
2042
- [ngModel]="formModel().module"
2043
- (ngModelChange)="updateModule($event)"
2044
- [ngModelOptions]="{ standalone: true }"
2045
- [options]="moduleOptions()"
2046
- optionLabel="label"
2047
- optionValue="value"
2048
- [editable]="true"
2049
- [disabled]="isReadonly()"
2050
- [placeholder]="'localization.key.select.module' | translate"
2051
- class="w-full"
2052
- />
2053
- </div>
2054
-
2055
- @if (isReadonly()) {
2056
- <div class="md:col-span-2 -mt-2">
2057
- <small class="text-muted-color">{{
2058
- 'localization.key.readonly.edit.hint' | translate
2059
- }}</small>
2060
- </div>
2061
- }
2062
-
2063
- <!-- Default Message -->
2064
- <div class="flex flex-col gap-2 md:col-span-2">
2065
- <label for="defaultMessage" class="font-medium"
2066
- >{{ 'localization.key.default.message' | translate }} *</label
2067
- >
2068
- <textarea
2069
- fTextarea
2070
- id="defaultMessage"
2071
- [formField]="formTree.defaultMessage"
2072
- [rows]="2"
2073
- [placeholder]="
2074
- 'localization.placeholder.default.message' | translate
2075
- "
2076
- ></textarea>
2077
- </div>
2078
-
2079
- <!-- Description -->
2080
- <div class="flex flex-col gap-2 md:col-span-2">
2081
- <label for="description" class="font-medium">{{
2082
- 'localization.key.description' | translate
2083
- }}</label>
2084
- <input
2085
- fInputText
2086
- id="description"
2087
- [formField]="formTree.description"
2088
- [placeholder]="
2089
- 'localization.placeholder.key.description' | translate
2090
- "
2091
- />
2092
- </div>
2093
-
2094
- <!-- Variables -->
2095
- <div class="flex flex-col gap-2 md:col-span-2">
2096
- <label for="variables" class="font-medium">{{
2097
- 'localization.key.variables' | translate
2098
- }}</label>
2099
- <input
2100
- fInputText
2101
- id="variables"
2102
- [formField]="formTree.variables"
2103
- [placeholder]="
2104
- 'localization.key.variables.placeholder' | translate
2105
- "
2106
- />
2107
- <small class="text-muted-color">{{
2108
- 'localization.key.variables.hint' | translate
2109
- }}</small>
2110
- </div>
2111
- </div>
2112
- </div>
2113
-
2114
- <!-- Translations Section -->
2115
- <div class="p-4 surface-ground rounded-lg">
2116
- <h4 class="font-semibold mt-0 mb-4">
2117
- {{ 'localization.key.translations.section' | translate }}
2118
- </h4>
2119
- @if (isLoadingLanguages()) {
2120
- <div class="flex justify-center py-4">
2121
- <f-progressspinner strokeWidth="4" />
2122
- </div>
2123
- } @else if (languages().length === 0) {
2124
- <p class="text-muted-color text-center py-4">
2125
- {{ 'localization.key.no.languages' | translate }}
2126
- </p>
2127
- } @else {
2128
- <div class="grid grid-cols-1 md:grid-cols-2 gap-4">
2129
- @for (lang of languages(); track lang.id) {
2130
- <div class="flex flex-col gap-2">
2131
- <div class="flex justify-between items-center">
2132
- <label class="font-medium">
2133
- {{ lang.name }}
2134
- @if (lang.nativeName && lang.nativeName !== lang.name) {
2135
- <span class="text-muted-color"
2136
- >({{ lang.nativeName }})</span
2137
- >
2138
- }
2139
- </label>
2140
- <f-tag
2141
- [value]="lang.code"
2142
- severity="secondary"
2143
- class="uppercase"
2144
- />
2145
- </div>
2146
- <textarea
2147
- fTextarea
2148
- [ngModel]="getTranslationValue(lang.id)"
2149
- (ngModelChange)="setTranslationValue(lang.id, $event)"
2150
- [ngModelOptions]="{ standalone: true }"
2151
- [rows]="2"
2152
- [placeholder]="
2153
- formModel().defaultMessage ||
2154
- ('localization.key.enter.translation' | translate)
2155
- "
2156
- ></textarea>
2157
- </div>
2158
- }
2159
- </div>
2160
- }
2161
- </div>
2162
-
2163
- <!-- Actions -->
2164
- <div class="flex justify-end gap-4 pt-4">
2165
- <button
2166
- fButton
2167
- type="button"
2168
- severity="secondary"
2169
- [outlined]="true"
2170
- (click)="onCancel()"
2171
- >
2172
- <span pButtonLabel>{{ 'shared.cancel' | translate }}</span>
2173
- </button>
2174
- <button
2175
- fButton
2176
- type="submit"
2177
- [disabled]="!isFormValid() || isSaving()"
2178
- >
2179
- @if (isSaving()) {
2180
- <ui-icon pButtonIcon name="spinner" [spin]="true"></ui-icon>
2181
- }
2182
- <span pButtonLabel>{{
2183
- isEditMode()
2184
- ? ('shared.update' | translate)
2185
- : ('shared.create' | translate)
2186
- }}</span>
2187
- </button>
2188
- </div>
2189
- </form>
2190
- </f-card>
2191
- `,
2192
- }]
2193
- }], ctorParameters: () => [] });
2194
-
2195
- var translationKeyForm_page = /*#__PURE__*/Object.freeze({
2196
- __proto__: null,
2197
- TranslationKeyFormPage: TranslationKeyFormPage
588
+ var LanguageSelectorPanelComponent = class LanguageSelectorPanelComponent {
589
+ localizationState = inject(LocalizationStateService);
590
+ translationLoader = inject(TranslationLoaderService);
591
+ destroyRef = inject(DestroyRef);
592
+ languages = this.localizationState.availableLanguages;
593
+ _selectedLanguageCode = signal(this.localizationState.currentLanguageCode(), ...ngDevMode ? [{ debugName: "_selectedLanguageCode" }] : /* istanbul ignore next */ []);
594
+ selectedLanguageCode = this._selectedLanguageCode.asReadonly();
595
+ menuItems = computed(() => this.languages().map((lang) => ({
596
+ label: lang.nativeName || lang.name,
597
+ badge: lang.code.toUpperCase(),
598
+ selected: lang.code === this.selectedLanguageCode(),
599
+ command: () => this.onLanguageSelect(lang.code)
600
+ })), ...ngDevMode ? [{ debugName: "menuItems" }] : /* istanbul ignore next */ []);
601
+ constructor() {
602
+ effect(() => {
603
+ const currentCode = this.localizationState.currentLanguageCode();
604
+ if (currentCode !== this._selectedLanguageCode()) this._selectedLanguageCode.set(currentCode);
605
+ });
606
+ }
607
+ onLanguageSelect(code) {
608
+ const language = this.languages().find((l) => l.code === code);
609
+ if (language) {
610
+ this._selectedLanguageCode.set(code);
611
+ this.localizationState.setCurrentLanguage(language);
612
+ this.translationLoader.loadTranslationsAsync(code, true).pipe(takeUntilDestroyed(this.destroyRef)).subscribe();
613
+ }
614
+ }
615
+ static ɵfac = i0.ɵɵngDeclareFactory({
616
+ minVersion: "12.0.0",
617
+ version: "22.2.1",
618
+ ngImport: i0,
619
+ type: LanguageSelectorPanelComponent,
620
+ deps: [],
621
+ target: i0.ɵɵFactoryTarget.Component
622
+ });
623
+ static ɵcmp = i0.ɵɵngDeclareComponent({
624
+ minVersion: "14.0.0",
625
+ version: "22.2.1",
626
+ type: LanguageSelectorPanelComponent,
627
+ isStandalone: true,
628
+ selector: "lib-language-selector-panel",
629
+ host: {
630
+ styleAttribute: "inset-inline-end: 0;",
631
+ classAttribute: "hidden absolute z-50 top-[24px] p-3 min-w-[180px]"
632
+ },
633
+ ngImport: i0,
634
+ template: `<f-menu [model]="menuItems()" />`,
635
+ isInline: true,
636
+ dependencies: [{
637
+ kind: "component",
638
+ type: MenuComponent,
639
+ selector: "f-menu",
640
+ inputs: [
641
+ "model",
642
+ "popup",
643
+ "appendTo",
644
+ "styleClass",
645
+ "style"
646
+ ],
647
+ outputs: ["onShow", "onHide"]
648
+ }]
649
+ });
650
+ };
651
+ i0.ɵɵngDeclareClassMetadata({
652
+ minVersion: "12.0.0",
653
+ version: "22.2.1",
654
+ ngImport: i0,
655
+ type: LanguageSelectorPanelComponent,
656
+ decorators: [{
657
+ type: Component,
658
+ args: [{
659
+ selector: "lib-language-selector-panel",
660
+ imports: [MenuComponent],
661
+ host: {
662
+ class: "hidden absolute z-50 top-[24px] p-3 min-w-[180px]",
663
+ style: "inset-inline-end: 0;"
664
+ },
665
+ template: `<f-menu [model]="menuItems()" />`
666
+ }]
667
+ }],
668
+ ctorParameters: () => []
2198
669
  });
2199
-
2200
- class TranslationKeyListPage {
2201
- TRANSLATION_KEY_PERMISSIONS = TRANSLATION_KEY_PERMISSIONS;
2202
- router = inject(Router);
2203
- languageService = inject(LanguageApiService);
2204
- translationKeyService = inject(TranslationKeyApiService);
2205
- translationService = inject(TranslationApiService);
2206
- confirmationService = inject(ConfirmationService);
2207
- messageService = inject(MessageService);
2208
- translateService = inject(TranslateService);
2209
- destroyRef = inject(DestroyRef);
2210
- isLoading = signal(false, /* @ts-ignore */
2211
- ...(ngDevMode ? [{ debugName: "isLoading" }] : /* istanbul ignore next */ []));
2212
- languages = signal([], /* @ts-ignore */
2213
- ...(ngDevMode ? [{ debugName: "languages" }] : /* istanbul ignore next */ []));
2214
- modules = signal([], /* @ts-ignore */
2215
- ...(ngDevMode ? [{ debugName: "modules" }] : /* istanbul ignore next */ []));
2216
- translationKeys = signal([], /* @ts-ignore */
2217
- ...(ngDevMode ? [{ debugName: "translationKeys" }] : /* istanbul ignore next */ []));
2218
- translationCounts = signal(new Map(), /* @ts-ignore */
2219
- ...(ngDevMode ? [{ debugName: "translationCounts" }] : /* istanbul ignore next */ []));
2220
- selectedModule = signal(null, /* @ts-ignore */
2221
- ...(ngDevMode ? [{ debugName: "selectedModule" }] : /* istanbul ignore next */ []));
2222
- searchTerm = signal('', /* @ts-ignore */
2223
- ...(ngDevMode ? [{ debugName: "searchTerm" }] : /* istanbul ignore next */ []));
2224
- moduleOptions = computed(() => [
2225
- { label: this.translateService.translate('shared.all'), value: null },
2226
- ...this.modules().map((m) => ({ label: m, value: m })),
2227
- ], /* @ts-ignore */
2228
- ...(ngDevMode ? [{ debugName: "moduleOptions" }] : /* istanbul ignore next */ []));
2229
- filteredKeys = computed(() => {
2230
- let keys = this.translationKeys();
2231
- const module = this.selectedModule();
2232
- const search = this.searchTerm().toLowerCase().trim();
2233
- if (module) {
2234
- keys = keys.filter((k) => k.module === module);
2235
- }
2236
- if (search) {
2237
- keys = keys.filter((k) => k.key.toLowerCase().includes(search) ||
2238
- k.defaultMessage.toLowerCase().includes(search) ||
2239
- k.module.toLowerCase().includes(search));
2240
- }
2241
- return keys;
2242
- }, /* @ts-ignore */
2243
- ...(ngDevMode ? [{ debugName: "filteredKeys" }] : /* istanbul ignore next */ []));
2244
- ngOnInit() {
2245
- this.loadData();
2246
- }
2247
- loadData() {
2248
- this.isLoading.set(true);
2249
- forkJoin({
2250
- languages: from(this.languageService.getAll({
2251
- pagination: { currentPage: 0, pageSize: 100 },
2252
- filter: {},
2253
- sort: { serial: 'ASC', name: 'ASC' },
2254
- })),
2255
- modules: this.translationKeyService.getModules(),
2256
- keys: from(this.translationKeyService.getAll({
2257
- pagination: { currentPage: 0, pageSize: 1000 },
2258
- filter: {},
2259
- sort: { module: 'ASC', key: 'ASC' },
2260
- })),
2261
- })
2262
- .pipe(takeUntilDestroyed(this.destroyRef))
2263
- .subscribe({
2264
- next: ({ languages, modules, keys }) => {
2265
- if (languages.success && languages.data) {
2266
- this.languages.set(languages.data.filter((l) => l.isActive));
2267
- }
2268
- if (modules.success && modules.data) {
2269
- this.modules.set(modules.data);
2270
- }
2271
- if (keys.success && keys.data) {
2272
- this.translationKeys.set(keys.data);
2273
- this.loadTranslationCounts();
2274
- }
2275
- this.isLoading.set(false);
2276
- },
2277
- error: () => {
2278
- this.isLoading.set(false);
2279
- },
2280
- });
2281
- }
2282
- loadTranslationCounts() {
2283
- this.translationService
2284
- .getCounts()
2285
- .pipe(takeUntilDestroyed(this.destroyRef))
2286
- .subscribe({
2287
- next: (response) => {
2288
- if (response.success && response.data) {
2289
- this.translationCounts.set(new Map(Object.entries(response.data)));
2290
- }
2291
- },
2292
- });
2293
- }
2294
- getTranslationCount(keyId) {
2295
- return this.translationCounts().get(keyId) ?? 0;
2296
- }
2297
- onModuleChange(value) {
2298
- this.selectedModule.set(value);
2299
- }
2300
- onCreate() {
2301
- this.router.navigate(['/localization/keys/new']);
2302
- }
2303
- onEdit(key) {
2304
- this.router.navigate(['/localization/keys', key.id]);
2305
- }
2306
- onDelete(key) {
2307
- this.confirmationService.confirm({
2308
- message: this.translateService.translate('shared.confirm.delete'),
2309
- header: this.translateService.translate('localization.key.delete.title'),
2310
- icon: 'exclamation-triangle',
2311
- acceptButtonStyleClass: 'p-button-danger',
2312
- accept: () => {
2313
- from(this.translationKeyService.delete({ id: key.id, type: 'delete' }))
2314
- .pipe(takeUntilDestroyed(this.destroyRef))
2315
- .subscribe({
2316
- next: (response) => {
2317
- if (response.success) {
2318
- this.messageService.add({
2319
- severity: 'success',
2320
- summary: this.translateService.translate('shared.success'),
2321
- detail: response.messageKey
2322
- ? this.translateService.translate(response.messageKey, response.messageVariables)
2323
- : (response.message ?? ''),
2324
- });
2325
- this.loadData();
2326
- }
2327
- },
2328
- });
2329
- },
2330
- });
2331
- }
2332
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.5", ngImport: i0, type: TranslationKeyListPage, deps: [], target: i0.ɵɵFactoryTarget.Component });
2333
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.1.5", type: TranslationKeyListPage, isStandalone: true, selector: "lib-translation-key-list-page", ngImport: i0, template: `
2334
- <div class="card">
2335
- <div
2336
- class="flex flex-col sm:flex-row justify-between items-start sm:items-center gap-3 mb-4"
2337
- >
2338
- <div>
2339
- <h3 class="text-lg sm:text-xl font-semibold m-0">
2340
- {{ 'localization.key.title' | translate }}
2341
- </h3>
2342
- <p class="text-sm text-muted-color mt-1">
2343
- {{ 'localization.key.subtitle' | translate }}
2344
- </p>
2345
- </div>
2346
- <div class="flex gap-2">
2347
- <button
2348
- fButton
2349
- *hasPermission="TRANSLATION_KEY_PERMISSIONS.CREATE"
2350
- (click)="onCreate()"
2351
- >
2352
- <ui-icon pButtonIcon name="plus"></ui-icon>
2353
- <span pButtonLabel>{{ 'localization.key.new' | translate }}</span>
2354
- </button>
2355
- </div>
2356
- </div>
2357
-
2358
- <div class="flex flex-col md:flex-row gap-4 mb-4">
2359
- <div class="flex-1">
2360
- <f-select
2361
- [options]="moduleOptions()"
2362
- [ngModel]="selectedModule()"
2363
- (ngModelChange)="onModuleChange($event)"
2364
- [ngModelOptions]="{ standalone: true }"
2365
- optionLabel="label"
2366
- optionValue="value"
2367
- [placeholder]="'localization.key.filter.module' | translate"
2368
- [showClear]="true"
2369
- class="w-full"
2370
- />
2371
- </div>
2372
- <div class="flex-1">
2373
- <f-iconfield class="w-full">
2374
- <f-inputicon icon="search" />
2375
- <input
2376
- fInputText
2377
- [ngModel]="searchTerm()"
2378
- (ngModelChange)="searchTerm.set($event)"
2379
- [ngModelOptions]="{ standalone: true }"
2380
- [placeholder]="'shared.search' | translate"
2381
- class="w-full"
2382
- />
2383
- </f-iconfield>
2384
- </div>
2385
- </div>
2386
-
2387
- <!-- Table -->
2388
- <f-table
2389
- [value]="filteredKeys()"
2390
- [loading]="isLoading()"
2391
- [paginator]="true"
2392
- [rows]="15"
2393
- [rowsPerPageOptions]="[10, 15, 25, 50]"
2394
- >
2395
- <ng-template #header>
2396
- <tr>
2397
- <th style="width: 15%">
2398
- {{ 'localization.key.module' | translate }}
2399
- </th>
2400
- <th style="width: 25%">
2401
- {{ 'localization.key.name' | translate }}
2402
- </th>
2403
- <th style="width: 35%">
2404
- {{ 'localization.key.default.message' | translate }}
2405
- </th>
2406
- <th style="width: 10%">
2407
- {{ 'localization.key.translations' | translate }}
2408
- </th>
2409
- <th style="width: 15%">{{ 'shared.actions' | translate }}</th>
2410
- </tr>
2411
- </ng-template>
2412
- <ng-template #body let-key>
2413
- @let translationCount = getTranslationCount(key.id);
2414
- @let languageCount = languages().length;
2415
- <tr>
2416
- <td>
2417
- <f-tag [value]="key.module" severity="secondary" />
2418
- </td>
2419
- <td>
2420
- <div class="flex items-center gap-2">
2421
- <span class="font-medium">{{ key.key }}</span>
2422
- @if (key.isReadonly) {
2423
- <f-tag
2424
- [value]="'shared.read.only' | translate"
2425
- severity="warn"
2426
- />
2427
- }
2428
- </div>
2429
- @if (key.description) {
2430
- <small class="text-muted-color">{{ key.description }}</small>
2431
- }
2432
- </td>
2433
- <td>{{ key.defaultMessage }}</td>
2434
- <td class="text-center">
2435
- <span
2436
- class="text-sm"
2437
- [class.text-green-500]="translationCount === languageCount"
2438
- [class.text-orange-500]="
2439
- translationCount < languageCount && translationCount > 0
2440
- "
2441
- [class.text-muted-color]="translationCount === 0"
2442
- >
2443
- {{ translationCount }}/{{ languageCount }}
2444
- </span>
2445
- </td>
2446
- <td>
2447
- <div class="flex gap-1">
2448
- <button
2449
- fButton
2450
- *hasPermission="TRANSLATION_KEY_PERMISSIONS.UPDATE"
2451
- [text]="true"
2452
- severity="secondary"
2453
- size="small"
2454
- [fTooltip]="'shared.edit' | translate"
2455
- (click)="onEdit(key)"
2456
- >
2457
- <ui-icon pButtonIcon name="pencil"></ui-icon>
2458
- </button>
2459
- <button
2460
- fButton
2461
- *hasPermission="TRANSLATION_KEY_PERMISSIONS.DELETE"
2462
- [text]="true"
2463
- severity="danger"
2464
- size="small"
2465
- [disabled]="key.isReadonly"
2466
- [fTooltip]="
2467
- (key.isReadonly
2468
- ? 'localization.key.readonly.delete.forbidden'
2469
- : 'shared.delete'
2470
- ) | translate
2471
- "
2472
- (click)="onDelete(key)"
2473
- >
2474
- <ui-icon pButtonIcon name="trash"></ui-icon>
2475
- </button>
2476
- </div>
2477
- </td>
2478
- </tr>
2479
- </ng-template>
2480
- <ng-template #emptymessage>
2481
- <tr>
2482
- <td colspan="5" class="text-center py-4 text-muted-color">
2483
- {{ 'localization.key.empty' | translate }}
2484
- </td>
2485
- </tr>
2486
- </ng-template>
2487
- </f-table>
2488
- </div>
2489
- `, isInline: true, dependencies: [{ kind: "directive", type: FButtonDirective, selector: "[fButton]", inputs: ["severity", "text", "outlined", "link", "variant", "rounded", "raised", "fluid", "disabled", "loading", "loadingIcon", "size", "label", "icon", "iconPos", "ariaLabel", "plain"] }, { kind: "component", type: UiIconComponent, selector: "ui-icon", inputs: ["name", "size", "spin", "fill", "severity"] }, { kind: "component", type: SelectComponent, selector: "f-select", inputs: ["options", "optionLabel", "optionValue", "placeholder", "disabled", "filter", "filterBy", "filterMatchMode", "filterLocale", "filterPlaceholder", "resetFilterOnHide", "autofocusFilter", "emptyMessage", "emptyFilterMessage", "showClear", "loading", "loadingIcon", "dropdownIcon", "clearIcon", "inputId", "invalid", "touched", "styleClass", "style", "appendTo", "editable", "readonly", "autofocus", "checkmark", "group", "optionGroupLabel", "optionGroupChildren", "virtualScroll", "virtualScrollItemSize", "lazy", "selectedObject"], outputs: ["onFilter", "onChange", "onShow", "onHide", "onBlur", "onFocus", "onClear", "onLazyLoad"] }, { kind: "component", type: IconFieldComponent, selector: "f-iconfield", inputs: ["iconPosition", "styleClass"] }, { kind: "component", type: InputIconComponent, selector: "f-inputicon", inputs: ["icon", "position", "styleClass", "style"] }, { kind: "directive", type: InputTextDirective, selector: "[fInputText]", inputs: ["invalid", "touched", "variant", "size", "fluid"] }, { kind: "component", type: TableComponent, selector: "f-table", inputs: ["value", "lazy", "loading", "rows", "totalRecords", "rowHover", "dataKey", "scrollable", "scrollHeight", "paginator", "first", "rowsPerPageOptions", "showCurrentPageReport", "currentPageReportTemplate", "tableStyle", "styleClass", "globalFilterFields", "filterDelay", "responsiveLayout", "selectionMode", "sortMode", "rowExpandMode", "resizableColumns", "rowStyleClass", "virtualScroll", "virtualScrollItemSize", "sortField", "sortOrder", "multiSortMeta", "filters", "selection", "expandedRowKeys", "contextMenuSelection"], outputs: ["sortFieldChange", "sortOrderChange", "multiSortMetaChange", "filtersChange", "selectionChange", "expandedRowKeysChange", "contextMenuSelectionChange", "onLazyLoad", "onSort", "onFilter", "onRowSelect", "onRowUnselect", "onSelectAllChange", "onRowExpand", "onRowCollapse", "onColResize", "onRowContextMenu"] }, { kind: "component", type: TagComponent, selector: "f-tag", inputs: ["value", "severity", "icon", "rounded", "color", "styleClass", "style"] }, { kind: "directive", type: TooltipDirective, selector: "[fTooltip]", inputs: ["fTooltip", "tooltipPosition", "positionStyle", "tooltipStyleClass", "disabled", "tooltipEvent", "showDelay", "hideDelay", "life", "autoHide", "positionTop", "positionLeft", "escape"] }, { kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i1.DefaultValueAccessor, selector: "input:not([type=checkbox]):not([ngNoCva])[formControlName],textarea:not([ngNoCva])[formControlName],input:not([type=checkbox]):not([ngNoCva])[formControl],textarea:not([ngNoCva])[formControl],input:not([type=checkbox]):not([ngNoCva])[ngModel],textarea:not([ngNoCva])[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "directive", type: HasPermissionDirective, selector: "[hasPermission]", inputs: ["hasPermission"] }, { kind: "pipe", type: TranslatePipe, name: "translate" }] });
670
+ function provideLocalization(config, initialFallbackMessages) {
671
+ const providers = [
672
+ {
673
+ provide: LOCALIZATION_CONFIG,
674
+ useValue: config
675
+ },
676
+ {
677
+ provide: LOCALIZATION_STATE_SERVICE,
678
+ useExisting: LocalizationStateService
679
+ },
680
+ {
681
+ provide: LOCALIZATION_API_SERVICE,
682
+ useExisting: LocalizationApiService
683
+ },
684
+ LocalizationInitializerService,
685
+ provideAppInitializer(() => {
686
+ return inject(LocalizationInitializerService).initialize(initialFallbackMessages);
687
+ })
688
+ ];
689
+ if (config.enableLayoutSelector) providers.push({
690
+ provide: LAYOUT_LANGUAGE_SELECTOR,
691
+ useValue: LanguageSelectorComponent
692
+ }, {
693
+ provide: LAYOUT_LANGUAGE_SELECTOR_PANEL,
694
+ useValue: LanguageSelectorPanelComponent
695
+ });
696
+ return providers;
2490
697
  }
2491
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.5", ngImport: i0, type: TranslationKeyListPage, decorators: [{
2492
- type: Component,
2493
- args: [{
2494
- selector: 'lib-translation-key-list-page',
2495
- imports: [
2496
- FButtonDirective,
2497
- UiIconComponent,
2498
- SelectComponent,
2499
- IconFieldComponent,
2500
- InputIconComponent,
2501
- InputTextDirective,
2502
- TableComponent,
2503
- TagComponent,
2504
- TooltipDirective,
2505
- FormsModule,
2506
- HasPermissionDirective,
2507
- TranslatePipe,
2508
- ],
2509
- template: `
2510
- <div class="card">
2511
- <div
2512
- class="flex flex-col sm:flex-row justify-between items-start sm:items-center gap-3 mb-4"
2513
- >
2514
- <div>
2515
- <h3 class="text-lg sm:text-xl font-semibold m-0">
2516
- {{ 'localization.key.title' | translate }}
2517
- </h3>
2518
- <p class="text-sm text-muted-color mt-1">
2519
- {{ 'localization.key.subtitle' | translate }}
2520
- </p>
2521
- </div>
2522
- <div class="flex gap-2">
2523
- <button
2524
- fButton
2525
- *hasPermission="TRANSLATION_KEY_PERMISSIONS.CREATE"
2526
- (click)="onCreate()"
2527
- >
2528
- <ui-icon pButtonIcon name="plus"></ui-icon>
2529
- <span pButtonLabel>{{ 'localization.key.new' | translate }}</span>
2530
- </button>
2531
- </div>
2532
- </div>
2533
-
2534
- <div class="flex flex-col md:flex-row gap-4 mb-4">
2535
- <div class="flex-1">
2536
- <f-select
2537
- [options]="moduleOptions()"
2538
- [ngModel]="selectedModule()"
2539
- (ngModelChange)="onModuleChange($event)"
2540
- [ngModelOptions]="{ standalone: true }"
2541
- optionLabel="label"
2542
- optionValue="value"
2543
- [placeholder]="'localization.key.filter.module' | translate"
2544
- [showClear]="true"
2545
- class="w-full"
2546
- />
2547
- </div>
2548
- <div class="flex-1">
2549
- <f-iconfield class="w-full">
2550
- <f-inputicon icon="search" />
2551
- <input
2552
- fInputText
2553
- [ngModel]="searchTerm()"
2554
- (ngModelChange)="searchTerm.set($event)"
2555
- [ngModelOptions]="{ standalone: true }"
2556
- [placeholder]="'shared.search' | translate"
2557
- class="w-full"
2558
- />
2559
- </f-iconfield>
2560
- </div>
2561
- </div>
2562
-
2563
- <!-- Table -->
2564
- <f-table
2565
- [value]="filteredKeys()"
2566
- [loading]="isLoading()"
2567
- [paginator]="true"
2568
- [rows]="15"
2569
- [rowsPerPageOptions]="[10, 15, 25, 50]"
2570
- >
2571
- <ng-template #header>
2572
- <tr>
2573
- <th style="width: 15%">
2574
- {{ 'localization.key.module' | translate }}
2575
- </th>
2576
- <th style="width: 25%">
2577
- {{ 'localization.key.name' | translate }}
2578
- </th>
2579
- <th style="width: 35%">
2580
- {{ 'localization.key.default.message' | translate }}
2581
- </th>
2582
- <th style="width: 10%">
2583
- {{ 'localization.key.translations' | translate }}
2584
- </th>
2585
- <th style="width: 15%">{{ 'shared.actions' | translate }}</th>
2586
- </tr>
2587
- </ng-template>
2588
- <ng-template #body let-key>
2589
- @let translationCount = getTranslationCount(key.id);
2590
- @let languageCount = languages().length;
2591
- <tr>
2592
- <td>
2593
- <f-tag [value]="key.module" severity="secondary" />
2594
- </td>
2595
- <td>
2596
- <div class="flex items-center gap-2">
2597
- <span class="font-medium">{{ key.key }}</span>
2598
- @if (key.isReadonly) {
2599
- <f-tag
2600
- [value]="'shared.read.only' | translate"
2601
- severity="warn"
2602
- />
2603
- }
2604
- </div>
2605
- @if (key.description) {
2606
- <small class="text-muted-color">{{ key.description }}</small>
2607
- }
2608
- </td>
2609
- <td>{{ key.defaultMessage }}</td>
2610
- <td class="text-center">
2611
- <span
2612
- class="text-sm"
2613
- [class.text-green-500]="translationCount === languageCount"
2614
- [class.text-orange-500]="
2615
- translationCount < languageCount && translationCount > 0
2616
- "
2617
- [class.text-muted-color]="translationCount === 0"
2618
- >
2619
- {{ translationCount }}/{{ languageCount }}
2620
- </span>
2621
- </td>
2622
- <td>
2623
- <div class="flex gap-1">
2624
- <button
2625
- fButton
2626
- *hasPermission="TRANSLATION_KEY_PERMISSIONS.UPDATE"
2627
- [text]="true"
2628
- severity="secondary"
2629
- size="small"
2630
- [fTooltip]="'shared.edit' | translate"
2631
- (click)="onEdit(key)"
2632
- >
2633
- <ui-icon pButtonIcon name="pencil"></ui-icon>
2634
- </button>
2635
- <button
2636
- fButton
2637
- *hasPermission="TRANSLATION_KEY_PERMISSIONS.DELETE"
2638
- [text]="true"
2639
- severity="danger"
2640
- size="small"
2641
- [disabled]="key.isReadonly"
2642
- [fTooltip]="
2643
- (key.isReadonly
2644
- ? 'localization.key.readonly.delete.forbidden'
2645
- : 'shared.delete'
2646
- ) | translate
2647
- "
2648
- (click)="onDelete(key)"
2649
- >
2650
- <ui-icon pButtonIcon name="trash"></ui-icon>
2651
- </button>
2652
- </div>
2653
- </td>
2654
- </tr>
2655
- </ng-template>
2656
- <ng-template #emptymessage>
2657
- <tr>
2658
- <td colspan="5" class="text-center py-4 text-muted-color">
2659
- {{ 'localization.key.empty' | translate }}
2660
- </td>
2661
- </tr>
2662
- </ng-template>
2663
- </f-table>
2664
- </div>
2665
- `,
2666
- }]
2667
- }] });
2668
-
2669
- var translationKeyList_page = /*#__PURE__*/Object.freeze({
2670
- __proto__: null,
2671
- TranslationKeyListPage: TranslationKeyListPage
2672
- });
2673
-
2674
- /**
2675
- * Resolves which localization tab the current user should land on when
2676
- * navigating to the bare `/localization` route. Mirrors the tab
2677
- * visibility order in LocalizationContainerPage.visibleTabs so the
2678
- * redirect never points at a tab the user can't actually see.
2679
- */
2680
698
  function resolveLocalizationDefaultTab() {
2681
- const permissionValidator = inject(PermissionValidatorService);
2682
- if (permissionValidator.hasPermission(LANGUAGE_PERMISSIONS.READ)) {
2683
- return 'languages';
2684
- }
2685
- if (permissionValidator.hasPermission(TRANSLATION_KEY_PERMISSIONS.READ)) {
2686
- return 'keys';
2687
- }
2688
- return 'languages';
699
+ const permissionValidator = inject(PermissionValidatorService);
700
+ if (permissionValidator.hasPermission(LANGUAGE_PERMISSIONS.READ)) return "languages";
701
+ if (permissionValidator.hasPermission(TRANSLATION_KEY_PERMISSIONS.READ)) return "keys";
702
+ return "languages";
2689
703
  }
2690
- const LOCALIZATION_ROUTES = [
2691
- {
2692
- path: '',
2693
- resolve: { translations: resolveTranslationModule({ modules: ['localization'], fallbackMessages: { ...LOCALIZATION_MESSAGES, ...SHARED_MESSAGES } }) },
2694
- loadComponent: () => Promise.resolve().then(function () { return localizationContainer_page; }).then((m) => m.LocalizationContainerPage),
2695
- children: [
2696
- // Languages Management
2697
- {
2698
- path: 'languages',
2699
- canActivate: [permissionGuard(LANGUAGE_PERMISSIONS.READ)],
2700
- children: [
2701
- {
2702
- path: '',
2703
- loadComponent: () => Promise.resolve().then(function () { return languageList_page; }).then((m) => m.LanguageListPage),
2704
- },
2705
- {
2706
- path: 'new',
2707
- canActivate: [permissionGuard(LANGUAGE_PERMISSIONS.CREATE)],
2708
- loadComponent: () => Promise.resolve().then(function () { return languageForm_page; }).then((m) => m.LanguageFormPage),
2709
- },
2710
- {
2711
- path: ':id',
2712
- canActivate: [permissionGuard(LANGUAGE_PERMISSIONS.UPDATE)],
2713
- loadComponent: () => Promise.resolve().then(function () { return languageForm_page; }).then((m) => m.LanguageFormPage),
2714
- },
2715
- ],
2716
- },
2717
- // Translation Keys Management
2718
- {
2719
- path: 'keys',
2720
- canActivate: [permissionGuard(TRANSLATION_KEY_PERMISSIONS.READ)],
2721
- children: [
2722
- {
2723
- path: '',
2724
- loadComponent: () => Promise.resolve().then(function () { return translationKeyList_page; }).then((m) => m.TranslationKeyListPage),
2725
- },
2726
- {
2727
- path: 'new',
2728
- canActivate: [permissionGuard(TRANSLATION_KEY_PERMISSIONS.CREATE)],
2729
- loadComponent: () => Promise.resolve().then(function () { return translationKeyForm_page; }).then((m) => m.TranslationKeyFormPage),
2730
- },
2731
- {
2732
- path: ':id',
2733
- canActivate: [permissionGuard(TRANSLATION_KEY_PERMISSIONS.UPDATE)],
2734
- loadComponent: () => Promise.resolve().then(function () { return translationKeyForm_page; }).then((m) => m.TranslationKeyFormPage),
2735
- },
2736
- ],
2737
- },
2738
- // Default redirect to the first tab the user has permission to see
2739
- {
2740
- path: '',
2741
- redirectTo: () => resolveLocalizationDefaultTab(),
2742
- pathMatch: 'full',
2743
- },
2744
- ],
2745
- },
2746
- ];
2747
-
2748
- // Interfaces
2749
-
2750
- /**
2751
- * Generated bundle index. Do not edit.
2752
- */
2753
-
2754
- export { LOCALIZATION_CONFIG, LOCALIZATION_MESSAGES, LOCALIZATION_ROUTES, LanguageApiService, LanguageFormPage, LanguageListPage, LanguageSelectorComponent, LocalizationApiService, LocalizationContainerPage, LocalizationInitializerService, LocalizationStateService, STORAGE_KEYS, TranslationApiService, TranslationKeyApiService, TranslationKeyFormPage, TranslationKeyListPage, TranslationLoaderService, getLocalizationConfig, provideLocalization };
2755
- //# sourceMappingURL=flusys-ng-localization.mjs.map
704
+ const LOCALIZATION_ROUTES = [{
705
+ path: "",
706
+ resolve: { translations: resolveTranslationModule({
707
+ modules: ["localization"],
708
+ fallbackMessages: {
709
+ ...LOCALIZATION_MESSAGES,
710
+ ...SHARED_MESSAGES
711
+ }
712
+ }) },
713
+ loadComponent: () => import("./flusys-ng-localization-localization-container.page-CsrYVvth.mjs").then((m) => m.LocalizationContainerPage),
714
+ children: [
715
+ {
716
+ path: "languages",
717
+ canActivate: [permissionGuard(LANGUAGE_PERMISSIONS.READ)],
718
+ children: [
719
+ {
720
+ path: "",
721
+ loadComponent: () => import("./flusys-ng-localization-language-list.page-B8wnYNNd.mjs").then((m) => m.LanguageListPage)
722
+ },
723
+ {
724
+ path: "new",
725
+ canActivate: [permissionGuard(LANGUAGE_PERMISSIONS.CREATE)],
726
+ loadComponent: () => import("./flusys-ng-localization-language-form.page-BdxZ6meb.mjs").then((m) => m.LanguageFormPage)
727
+ },
728
+ {
729
+ path: ":id",
730
+ canActivate: [permissionGuard(LANGUAGE_PERMISSIONS.UPDATE)],
731
+ loadComponent: () => import("./flusys-ng-localization-language-form.page-BdxZ6meb.mjs").then((m) => m.LanguageFormPage)
732
+ }
733
+ ]
734
+ },
735
+ {
736
+ path: "keys",
737
+ canActivate: [permissionGuard(TRANSLATION_KEY_PERMISSIONS.READ)],
738
+ children: [
739
+ {
740
+ path: "",
741
+ loadComponent: () => import("./flusys-ng-localization-translation-key-list.page-DhRI5UvT.mjs").then((m) => m.TranslationKeyListPage)
742
+ },
743
+ {
744
+ path: "new",
745
+ canActivate: [permissionGuard(TRANSLATION_KEY_PERMISSIONS.CREATE)],
746
+ loadComponent: () => import("./flusys-ng-localization-translation-key-form.page-Dgn58Sh7.mjs").then((m) => m.TranslationKeyFormPage)
747
+ },
748
+ {
749
+ path: ":id",
750
+ canActivate: [permissionGuard(TRANSLATION_KEY_PERMISSIONS.UPDATE)],
751
+ loadComponent: () => import("./flusys-ng-localization-translation-key-form.page-Dgn58Sh7.mjs").then((m) => m.TranslationKeyFormPage)
752
+ }
753
+ ]
754
+ },
755
+ {
756
+ path: "",
757
+ redirectTo: () => resolveLocalizationDefaultTab(),
758
+ pathMatch: "full"
759
+ }
760
+ ]
761
+ }];
762
+ export { LOCALIZATION_CONFIG, LOCALIZATION_MESSAGES, LOCALIZATION_ROUTES, LanguageApiService, LanguageSelectorComponent, LocalizationApiService, LocalizationInitializerService, LocalizationStateService, STORAGE_KEYS, TranslationApiService, TranslationKeyApiService, TranslationLoaderService, getLocalizationConfig, provideLocalization };
763
+
764
+ //# sourceMappingURL=flusys-ng-localization.mjs.map