mirror of
https://github.com/Alfresco/alfresco-ng2-components.git
synced 2026-09-02 17:53:30 +00:00
[AAE-46242] - Changed the name convention for translation files to ISO standard naming (#11924)
* [AAE-46242] - Changed the name convention for translation files to ISO standard naming * [AAE-46242] - revert en-GB to en.json * [AAE-46242] - fixed crowding config * [AAE-46242] - added fallback fix' * [AAE-46242] - Added defaultLang missing method * [AAE-46242] - Fixing unit test * [AAE-46242] - fixed core unit tests
This commit is contained in:
+1
-1
@@ -6,7 +6,7 @@
|
||||
"files": [
|
||||
{
|
||||
"source": "/**/**/i18n/en.json",
|
||||
"translation": "/%original_path%/%two_letters_code%.%file_extension%",
|
||||
"translation": "/%original_path%/%locale%.%file_extension%",
|
||||
"export_only_approved": "true",
|
||||
"update_option": "update_without_changes"
|
||||
}
|
||||
|
||||
@@ -18,21 +18,21 @@
|
||||
import { LanguageItem } from '../services/language-item.interface';
|
||||
|
||||
export const DEFAULT_LANGUAGE_LIST: LanguageItem[] = [
|
||||
{ key: 'de', label: 'Deutsch' },
|
||||
{ key: 'de-DE', label: 'Deutsch' },
|
||||
{ key: 'en', label: 'English' },
|
||||
{ key: 'es', label: 'Español' },
|
||||
{ key: 'fr', label: 'Français' },
|
||||
{ key: 'it', label: 'Italiano' },
|
||||
{ key: 'ja', label: '日本語' },
|
||||
{ key: 'nb', label: 'Bokmål' },
|
||||
{ key: 'nl', label: 'Nederlands' },
|
||||
{ key: 'es-ES', label: 'Español' },
|
||||
{ key: 'fr-FR', label: 'Français' },
|
||||
{ key: 'it-IT', label: 'Italiano' },
|
||||
{ key: 'ja-JP', label: '日本語' },
|
||||
{ key: 'nb-NO', label: 'Bokmål' },
|
||||
{ key: 'nl-NL', label: 'Nederlands' },
|
||||
{ key: 'pt-BR', label: 'Português (Brasil)' },
|
||||
{ key: 'ru', label: 'Русский' },
|
||||
{ key: 'ru-RU', label: 'Русский' },
|
||||
{ key: 'zh-CN', label: '中文简体' },
|
||||
{ key: 'cs', label: 'Čeština' },
|
||||
{ key: 'da', label: 'Dansk' },
|
||||
{ key: 'fi', label: 'Suomi' },
|
||||
{ key: 'pl', label: 'Polski' },
|
||||
{ key: 'sv', label: 'Svenska' },
|
||||
{ key: 'ar', label: 'العربية', direction: 'rtl' }
|
||||
{ key: 'cs-CZ', label: 'Čeština' },
|
||||
{ key: 'da-DK', label: 'Dansk' },
|
||||
{ key: 'fi-FI', label: 'Suomi' },
|
||||
{ key: 'pl-PL', label: 'Polski' },
|
||||
{ key: 'sv-SE', label: 'Svenska' },
|
||||
{ key: 'ar-SA', label: 'العربية', direction: 'rtl' }
|
||||
];
|
||||
|
||||
@@ -352,7 +352,12 @@ export class UserPreferencesService {
|
||||
if (Array.isArray(customLanguages)) {
|
||||
language = customLanguages.find((customLanguage) => key.includes(customLanguage.key));
|
||||
}
|
||||
language ??= DEFAULT_LANGUAGE_LIST.find((defaultLang) => defaultLang.key === key) ?? defaultLanguage;
|
||||
// Try exact match first, then try base language (e.g., 'ar' matches 'ar-SA')
|
||||
language ??= DEFAULT_LANGUAGE_LIST.find((defaultLang) => defaultLang.key === key);
|
||||
language ??= DEFAULT_LANGUAGE_LIST.find(
|
||||
(defaultLang) => defaultLang.key.startsWith(key + '-') || key.startsWith(defaultLang.key.split('-')[0])
|
||||
);
|
||||
language ??= defaultLanguage;
|
||||
return language;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,29 +16,45 @@
|
||||
*/
|
||||
|
||||
import { addMinutes, isValid } from 'date-fns';
|
||||
import { ar, cs, da, de, enUS, es, fi, fr, it as italian, ja, nb, nl, pl, ptBR, ru, sv, zhCN } from 'date-fns/locale';
|
||||
import { ar, arSA, cs, da, de, enGB, enUS, es, fi, fr, it as italian, ja, nb, nl, pl, pt, ptBR, ru, sv, zhCN } from 'date-fns/locale';
|
||||
import { DateFnsUtils } from './date-fns-utils';
|
||||
|
||||
describe('DateFnsUtils', () => {
|
||||
describe('getLocaleFromString', () => {
|
||||
const localeTestCases = [
|
||||
{ localeCode: 'ar-SA', expectedLocale: arSA, description: 'Arabic (Saudi Arabia)' },
|
||||
{ localeCode: 'ar', expectedLocale: ar, description: 'Arabic' },
|
||||
{ localeCode: 'cs', expectedLocale: cs, description: 'Czech' },
|
||||
{ localeCode: 'da', expectedLocale: da, description: 'Danish' },
|
||||
{ localeCode: 'de', expectedLocale: de, description: 'German' },
|
||||
{ localeCode: 'cs-CZ', expectedLocale: cs, description: 'Czech' },
|
||||
{ localeCode: 'cs', expectedLocale: cs, description: 'Czech (short)' },
|
||||
{ localeCode: 'da-DK', expectedLocale: da, description: 'Danish' },
|
||||
{ localeCode: 'da', expectedLocale: da, description: 'Danish (short)' },
|
||||
{ localeCode: 'de-DE', expectedLocale: de, description: 'German' },
|
||||
{ localeCode: 'de', expectedLocale: de, description: 'German (short)' },
|
||||
{ localeCode: 'en-GB', expectedLocale: enGB, description: 'English (UK)' },
|
||||
{ localeCode: 'en', expectedLocale: enUS, description: 'English (US)' },
|
||||
{ localeCode: 'es', expectedLocale: es, description: 'Spanish' },
|
||||
{ localeCode: 'fi', expectedLocale: fi, description: 'Finnish' },
|
||||
{ localeCode: 'fr', expectedLocale: fr, description: 'French' },
|
||||
{ localeCode: 'it', expectedLocale: italian, description: 'Italian' },
|
||||
{ localeCode: 'ja', expectedLocale: ja, description: 'Japanese' },
|
||||
{ localeCode: 'nb', expectedLocale: nb, description: 'Norwegian Bokmål' },
|
||||
{ localeCode: 'nl', expectedLocale: nl, description: 'Dutch' },
|
||||
{ localeCode: 'pl', expectedLocale: pl, description: 'Polish' },
|
||||
{ localeCode: 'pt', expectedLocale: ptBR, description: 'Portuguese' },
|
||||
{ localeCode: 'es-ES', expectedLocale: es, description: 'Spanish' },
|
||||
{ localeCode: 'es', expectedLocale: es, description: 'Spanish (short)' },
|
||||
{ localeCode: 'fi-FI', expectedLocale: fi, description: 'Finnish' },
|
||||
{ localeCode: 'fi', expectedLocale: fi, description: 'Finnish (short)' },
|
||||
{ localeCode: 'fr-FR', expectedLocale: fr, description: 'French' },
|
||||
{ localeCode: 'fr', expectedLocale: fr, description: 'French (short)' },
|
||||
{ localeCode: 'it-IT', expectedLocale: italian, description: 'Italian' },
|
||||
{ localeCode: 'it', expectedLocale: italian, description: 'Italian (short)' },
|
||||
{ localeCode: 'ja-JP', expectedLocale: ja, description: 'Japanese' },
|
||||
{ localeCode: 'ja', expectedLocale: ja, description: 'Japanese (short)' },
|
||||
{ localeCode: 'nb-NO', expectedLocale: nb, description: 'Norwegian Bokmål' },
|
||||
{ localeCode: 'nb', expectedLocale: nb, description: 'Norwegian Bokmål (short)' },
|
||||
{ localeCode: 'nl-NL', expectedLocale: nl, description: 'Dutch' },
|
||||
{ localeCode: 'nl', expectedLocale: nl, description: 'Dutch (short)' },
|
||||
{ localeCode: 'pl-PL', expectedLocale: pl, description: 'Polish' },
|
||||
{ localeCode: 'pl', expectedLocale: pl, description: 'Polish (short)' },
|
||||
{ localeCode: 'pt-PT', expectedLocale: pt, description: 'Portuguese (Portugal)' },
|
||||
{ localeCode: 'pt-BR', expectedLocale: ptBR, description: 'Portuguese (Brazilian)' },
|
||||
{ localeCode: 'ru', expectedLocale: ru, description: 'Russian' },
|
||||
{ localeCode: 'sv', expectedLocale: sv, description: 'Swedish' },
|
||||
{ localeCode: 'pt', expectedLocale: ptBR, description: 'Portuguese (short)' },
|
||||
{ localeCode: 'ru-RU', expectedLocale: ru, description: 'Russian' },
|
||||
{ localeCode: 'ru', expectedLocale: ru, description: 'Russian (short)' },
|
||||
{ localeCode: 'sv-SE', expectedLocale: sv, description: 'Swedish' },
|
||||
{ localeCode: 'sv', expectedLocale: sv, description: 'Swedish (short)' },
|
||||
{ localeCode: 'zh-CN', expectedLocale: zhCN, description: 'Chinese (Simplified)' }
|
||||
];
|
||||
|
||||
|
||||
@@ -16,7 +16,7 @@
|
||||
*/
|
||||
|
||||
import { format, parse, parseISO, isValid, isBefore, isAfter } from 'date-fns';
|
||||
import { ar, cs, da, de, enUS, es, fi, fr, it, ja, nb, nl, pl, ptBR, ru, sv, zhCN } from 'date-fns/locale';
|
||||
import { ar, arSA, cs, da, de, enGB, enUS, es, fi, fr, it, ja, nb, nl, pl, pt, ptBR, ru, sv, zhCN } from 'date-fns/locale';
|
||||
|
||||
const panDate = (num: number = 1): string => {
|
||||
let text = num.toString();
|
||||
@@ -27,66 +27,46 @@ const panDate = (num: number = 1): string => {
|
||||
};
|
||||
|
||||
export class DateFnsUtils {
|
||||
private static readonly localeMap: Record<string, Locale> = {
|
||||
'ar-SA': arSA,
|
||||
ar: ar,
|
||||
'cs-CZ': cs,
|
||||
cs: cs,
|
||||
'da-DK': da,
|
||||
da: da,
|
||||
'de-DE': de,
|
||||
de: de,
|
||||
'en-GB': enGB,
|
||||
'en-AU': enGB,
|
||||
en: enUS,
|
||||
'es-ES': es,
|
||||
es: es,
|
||||
'fi-FI': fi,
|
||||
fi: fi,
|
||||
'fr-FR': fr,
|
||||
fr: fr,
|
||||
'it-IT': it,
|
||||
it: it,
|
||||
'ja-JP': ja,
|
||||
ja: ja,
|
||||
'nb-NO': nb,
|
||||
nb: nb,
|
||||
'nl-NL': nl,
|
||||
nl: nl,
|
||||
'pl-PL': pl,
|
||||
pl: pl,
|
||||
'pt-PT': pt,
|
||||
'pt-BR': ptBR,
|
||||
pt: ptBR,
|
||||
'ru-RU': ru,
|
||||
ru: ru,
|
||||
'sv-SE': sv,
|
||||
sv: sv,
|
||||
'zh-CN': zhCN
|
||||
};
|
||||
|
||||
static getLocaleFromString(locale: string): Locale {
|
||||
let dateFnsLocale: Locale;
|
||||
switch (locale) {
|
||||
case 'ar':
|
||||
dateFnsLocale = ar;
|
||||
break;
|
||||
case 'cs':
|
||||
dateFnsLocale = cs;
|
||||
break;
|
||||
case 'da':
|
||||
dateFnsLocale = da;
|
||||
break;
|
||||
case 'de':
|
||||
dateFnsLocale = de;
|
||||
break;
|
||||
case 'en':
|
||||
dateFnsLocale = enUS;
|
||||
break;
|
||||
case 'es':
|
||||
dateFnsLocale = es;
|
||||
break;
|
||||
case 'fi':
|
||||
dateFnsLocale = fi;
|
||||
break;
|
||||
case 'fr':
|
||||
dateFnsLocale = fr;
|
||||
break;
|
||||
case 'it':
|
||||
dateFnsLocale = it;
|
||||
break;
|
||||
case 'ja':
|
||||
dateFnsLocale = ja;
|
||||
break;
|
||||
case 'nb':
|
||||
dateFnsLocale = nb;
|
||||
break;
|
||||
case 'nl':
|
||||
dateFnsLocale = nl;
|
||||
break;
|
||||
case 'pl':
|
||||
dateFnsLocale = pl;
|
||||
break;
|
||||
case 'pt':
|
||||
case 'pt-BR':
|
||||
dateFnsLocale = ptBR;
|
||||
break;
|
||||
case 'ru':
|
||||
dateFnsLocale = ru;
|
||||
break;
|
||||
case 'sv':
|
||||
dateFnsLocale = sv;
|
||||
break;
|
||||
case 'zh-CN':
|
||||
dateFnsLocale = zhCN;
|
||||
break;
|
||||
default:
|
||||
dateFnsLocale = enUS;
|
||||
break;
|
||||
}
|
||||
return dateFnsLocale;
|
||||
return this.localeMap[locale] ?? enUS;
|
||||
}
|
||||
|
||||
private static readonly momentToDateFnsMap = {
|
||||
|
||||
@@ -74,7 +74,7 @@ export class DateCellComponent extends DataTableCellComponent implements OnInit
|
||||
super();
|
||||
// Use effect to react to locale signal changes (must be in injection context)
|
||||
effect(() => {
|
||||
this.userLocale = this.userPreferencesService.localeSignal() || 'en';
|
||||
this.userLocale = this.userPreferencesService.localeSignal() || 'en-GB';
|
||||
this.setConfig();
|
||||
});
|
||||
}
|
||||
|
||||
@@ -23,23 +23,23 @@ import { LanguageItem } from '../common/services/language-item.interface';
|
||||
@Injectable()
|
||||
export class LanguageServiceMock implements LanguageServiceInterface {
|
||||
private readonly languages = new BehaviorSubject<LanguageItem[]>([
|
||||
{ key: 'de', label: 'Deutsch' },
|
||||
{ key: 'de-DE', label: 'Deutsch' },
|
||||
{ key: 'en', label: 'English' },
|
||||
{ key: 'es', label: 'Español' },
|
||||
{ key: 'fr', label: 'Français' },
|
||||
{ key: 'it', label: 'Italiano' },
|
||||
{ key: 'ja', label: '日本語' },
|
||||
{ key: 'nb', label: 'Bokmål' },
|
||||
{ key: 'nl', label: 'Nederlands' },
|
||||
{ key: 'es-ES', label: 'Español' },
|
||||
{ key: 'fr-FR', label: 'Français' },
|
||||
{ key: 'it-IT', label: 'Italiano' },
|
||||
{ key: 'ja-JP', label: '日本語' },
|
||||
{ key: 'nb-NO', label: 'Bokmål' },
|
||||
{ key: 'nl-NL', label: 'Nederlands' },
|
||||
{ key: 'pt-BR', label: 'Português (Brasil)' },
|
||||
{ key: 'ru', label: 'Русский' },
|
||||
{ key: 'ru-RU', label: 'Русский' },
|
||||
{ key: 'zh-CN', label: '中文简体' },
|
||||
{ key: 'cs', label: 'Čeština' },
|
||||
{ key: 'da', label: 'Dansk' },
|
||||
{ key: 'fi', label: 'Suomi' },
|
||||
{ key: 'pl', label: 'Polski' },
|
||||
{ key: 'sv', label: 'Svenska' },
|
||||
{ key: 'ar', label: 'العربية', direction: 'rtl' }
|
||||
{ key: 'cs-CZ', label: 'Čeština' },
|
||||
{ key: 'da-DK', label: 'Dansk' },
|
||||
{ key: 'fi-FI', label: 'Suomi' },
|
||||
{ key: 'pl-PL', label: 'Polski' },
|
||||
{ key: 'sv-SE', label: 'Svenska' },
|
||||
{ key: 'ar-SA', label: 'العربية', direction: 'rtl' }
|
||||
]);
|
||||
|
||||
languages$ = this.languages.asObservable();
|
||||
|
||||
@@ -42,7 +42,7 @@ export function provideStoryCore(): (Provider | EnvironmentProviders)[] {
|
||||
provideCoreAuthTesting(),
|
||||
provideAppInitializer(() => {
|
||||
const appConfig = inject(AppConfigService);
|
||||
appConfig.config = { ...appConfig.config, locale: 'en' };
|
||||
appConfig.config = { ...appConfig.config, locale: 'en-GB' };
|
||||
}),
|
||||
provideRouter([], withHashLocation())
|
||||
];
|
||||
|
||||
@@ -21,7 +21,7 @@ import { TranslateLoader } from '@ngx-translate/core';
|
||||
import { Observable, forkJoin, throwError, of } from 'rxjs';
|
||||
import { ComponentTranslationModel } from '../models/component.model';
|
||||
import { ObjectUtils } from '../common/utils/object-utils';
|
||||
import { map, catchError, retry } from 'rxjs/operators';
|
||||
import { map, catchError } from 'rxjs/operators';
|
||||
|
||||
@Injectable({
|
||||
providedIn: 'root'
|
||||
@@ -40,8 +40,33 @@ export class TranslateLoaderService implements TranslateLoader {
|
||||
private queue: string[][] = [];
|
||||
private defaultLang: string = 'en';
|
||||
|
||||
setDefaultLang(value: string) {
|
||||
this.defaultLang = value || 'en';
|
||||
private readonly localeAliases: Record<string, string> = {
|
||||
'en-US': 'en',
|
||||
'en-GB': 'en',
|
||||
'en-AU': 'en',
|
||||
de: 'de-DE',
|
||||
es: 'es-ES',
|
||||
fr: 'fr-FR',
|
||||
it: 'it-IT',
|
||||
ja: 'ja-JP',
|
||||
nb: 'nb-NO',
|
||||
nl: 'nl-NL',
|
||||
ru: 'ru-RU',
|
||||
cs: 'cs-CZ',
|
||||
da: 'da-DK',
|
||||
fi: 'fi-FI',
|
||||
pl: 'pl-PL',
|
||||
sv: 'sv-SE',
|
||||
ar: 'ar-SA',
|
||||
pt: 'pt-BR'
|
||||
};
|
||||
|
||||
setDefaultLang(lang: string): void {
|
||||
this.defaultLang = lang;
|
||||
}
|
||||
|
||||
getDefaultLang(): string {
|
||||
return this.defaultLang;
|
||||
}
|
||||
|
||||
registerProvider(name: string, path: string) {
|
||||
@@ -57,23 +82,49 @@ export class TranslateLoaderService implements TranslateLoader {
|
||||
return this.providers.some((x) => x.name === name);
|
||||
}
|
||||
|
||||
fetchLanguageFile(lang: string, component: ComponentTranslationModel, fallbackUrl?: string): Observable<void> {
|
||||
const translationUrl = fallbackUrl || `${component.path}/${this.prefix}/${lang}${this.suffix}?v=${Date.now()}`;
|
||||
private resolveLocale(lang: string): string {
|
||||
if (this.localeAliases[lang]) {
|
||||
return this.localeAliases[lang];
|
||||
}
|
||||
return lang;
|
||||
}
|
||||
|
||||
private getLocaleFallbacks(lang: string): string[] {
|
||||
const fallbacks: string[] = [lang];
|
||||
const resolved = this.resolveLocale(lang);
|
||||
if (resolved !== lang) {
|
||||
fallbacks.push(resolved);
|
||||
}
|
||||
const baseLang = lang.split('-')[0];
|
||||
if (baseLang !== lang && !fallbacks.includes(baseLang)) {
|
||||
const resolvedBase = this.resolveLocale(baseLang);
|
||||
if (!fallbacks.includes(resolvedBase)) {
|
||||
fallbacks.push(resolvedBase);
|
||||
}
|
||||
}
|
||||
return fallbacks;
|
||||
}
|
||||
|
||||
fetchLanguageFile(lang: string, component: ComponentTranslationModel): Observable<void> {
|
||||
const fallbacks = this.getLocaleFallbacks(lang);
|
||||
return this.tryFetchWithFallbacks(lang, fallbacks, component);
|
||||
}
|
||||
|
||||
private tryFetchWithFallbacks(originalLang: string, fallbacks: string[], component: ComponentTranslationModel): Observable<void> {
|
||||
if (fallbacks.length === 0) {
|
||||
return throwError(() => new Error(`Failed to load translations for ${originalLang}`));
|
||||
}
|
||||
|
||||
const [currentLocale, ...remainingFallbacks] = fallbacks;
|
||||
const translationUrl = `${component.path}/${this.prefix}/${currentLocale}${this.suffix}?v=${Date.now()}`;
|
||||
|
||||
return this.http.get(translationUrl).pipe(
|
||||
map((res: any) => {
|
||||
component.json[lang] = res;
|
||||
component.json[originalLang] = res;
|
||||
}),
|
||||
retry(3),
|
||||
catchError(() => {
|
||||
if (!fallbackUrl && lang.includes('-')) {
|
||||
const [langId] = lang.split('-');
|
||||
|
||||
if (langId && langId !== this.defaultLang) {
|
||||
const url = `${component.path}/${this.prefix}/${langId}${this.suffix}?v=${Date.now()}`;
|
||||
|
||||
return this.fetchLanguageFile(lang, component, url);
|
||||
}
|
||||
if (remainingFallbacks.length > 0) {
|
||||
return this.tryFetchWithFallbacks(originalLang, remainingFallbacks, component);
|
||||
}
|
||||
return throwError(() => new Error(`Failed to load ${translationUrl}`));
|
||||
})
|
||||
|
||||
@@ -17,6 +17,7 @@
|
||||
|
||||
import { fakeAsync, TestBed, tick } from '@angular/core/testing';
|
||||
import { HttpTestingController, provideHttpClientTesting } from '@angular/common/http/testing';
|
||||
import { provideHttpClient } from '@angular/common/http';
|
||||
import { TranslateLoaderService } from './translate-loader.service';
|
||||
|
||||
describe('TranslateLoader', () => {
|
||||
@@ -25,7 +26,7 @@ describe('TranslateLoader', () => {
|
||||
|
||||
beforeEach(() => {
|
||||
TestBed.configureTestingModule({
|
||||
providers: [provideHttpClientTesting(), TranslateLoaderService]
|
||||
providers: [provideHttpClient(), provideHttpClientTesting(), TranslateLoaderService]
|
||||
});
|
||||
customLoader = TestBed.inject(TranslateLoaderService);
|
||||
httpMock = TestBed.inject(HttpTestingController);
|
||||
@@ -60,4 +61,107 @@ describe('TranslateLoader', () => {
|
||||
subscription.unsubscribe();
|
||||
httpMock.verify();
|
||||
}));
|
||||
|
||||
describe('locale fallback', () => {
|
||||
it('should fallback from en-US to en when en-US file does not exist', fakeAsync(() => {
|
||||
let nextInvoked = false;
|
||||
|
||||
const subscription = customLoader.getTranslation('en-US').subscribe({
|
||||
next: () => (nextInvoked = true),
|
||||
error: () => fail('Should not call error handler')
|
||||
});
|
||||
|
||||
const enUsRequest = httpMock.expectOne((request) => request.url.includes('assets/adf-core/i18n/en-US.json'));
|
||||
enUsRequest.flush(null, { status: 404, statusText: 'Not Found' });
|
||||
tick();
|
||||
|
||||
const enRequest = httpMock.expectOne((request) => request.url.includes('assets/adf-core/i18n/en.json'));
|
||||
enRequest.flush({ 'TEST.KEY': 'Test value' });
|
||||
tick();
|
||||
|
||||
expect(nextInvoked).toBeTrue();
|
||||
subscription.unsubscribe();
|
||||
httpMock.verify();
|
||||
}));
|
||||
|
||||
it('should fallback from en-GB to en when en-GB file does not exist', fakeAsync(() => {
|
||||
let nextInvoked = false;
|
||||
|
||||
const subscription = customLoader.getTranslation('en-GB').subscribe({
|
||||
next: () => (nextInvoked = true),
|
||||
error: () => fail('Should not call error handler')
|
||||
});
|
||||
|
||||
const enGbRequest = httpMock.expectOne((request) => request.url.includes('assets/adf-core/i18n/en-GB.json'));
|
||||
enGbRequest.flush(null, { status: 404, statusText: 'Not Found' });
|
||||
tick();
|
||||
|
||||
const enRequest = httpMock.expectOne((request) => request.url.includes('assets/adf-core/i18n/en.json'));
|
||||
enRequest.flush({ 'TEST.KEY': 'Test value' });
|
||||
tick();
|
||||
|
||||
expect(nextInvoked).toBeTrue();
|
||||
subscription.unsubscribe();
|
||||
httpMock.verify();
|
||||
}));
|
||||
|
||||
it('should fallback from short locale (de) to full locale (de-DE)', fakeAsync(() => {
|
||||
let nextInvoked = false;
|
||||
|
||||
const subscription = customLoader.getTranslation('de').subscribe({
|
||||
next: () => (nextInvoked = true),
|
||||
error: () => fail('Should not call error handler')
|
||||
});
|
||||
|
||||
const deRequest = httpMock.expectOne((request) => request.url.includes('assets/adf-core/i18n/de.json'));
|
||||
deRequest.flush(null, { status: 404, statusText: 'Not Found' });
|
||||
tick();
|
||||
|
||||
const deDeRequest = httpMock.expectOne((request) => request.url.includes('assets/adf-core/i18n/de-DE.json'));
|
||||
deDeRequest.flush({ 'TEST.KEY': 'Testwert' });
|
||||
tick();
|
||||
|
||||
expect(nextInvoked).toBeTrue();
|
||||
subscription.unsubscribe();
|
||||
httpMock.verify();
|
||||
}));
|
||||
|
||||
it('should use the file directly when it exists without fallback', fakeAsync(() => {
|
||||
let nextInvoked = false;
|
||||
|
||||
const subscription = customLoader.getTranslation('fr-FR').subscribe({
|
||||
next: () => (nextInvoked = true),
|
||||
error: () => fail('Should not call error handler')
|
||||
});
|
||||
|
||||
const frFrRequest = httpMock.expectOne((request) => request.url.includes('assets/adf-core/i18n/fr-FR.json'));
|
||||
frFrRequest.flush({ 'TEST.KEY': 'Valeur de test' });
|
||||
tick();
|
||||
|
||||
expect(nextInvoked).toBeTrue();
|
||||
subscription.unsubscribe();
|
||||
httpMock.verify();
|
||||
}));
|
||||
|
||||
it('should register translations under the original requested locale', fakeAsync(() => {
|
||||
let translationResult: any;
|
||||
|
||||
const subscription = customLoader.getTranslation('en-US').subscribe({
|
||||
next: (result) => (translationResult = result),
|
||||
error: () => fail('Should not call error handler')
|
||||
});
|
||||
|
||||
const enUsRequest = httpMock.expectOne((request) => request.url.includes('assets/adf-core/i18n/en-US.json'));
|
||||
enUsRequest.flush(null, { status: 404, statusText: 'Not Found' });
|
||||
tick();
|
||||
|
||||
const enRequest = httpMock.expectOne((request) => request.url.includes('assets/adf-core/i18n/en.json'));
|
||||
enRequest.flush({ 'TEST.KEY': 'Test value' });
|
||||
tick();
|
||||
|
||||
expect(translationResult).toEqual({ 'TEST.KEY': 'Test value' });
|
||||
subscription.unsubscribe();
|
||||
httpMock.verify();
|
||||
}));
|
||||
});
|
||||
});
|
||||
|
||||
@@ -29,10 +29,6 @@ describe('TranslationService', () => {
|
||||
// No implementation needed for this test
|
||||
};
|
||||
|
||||
setDefaultLang = (_lang: string): void => {
|
||||
// No implementation needed for this test
|
||||
};
|
||||
|
||||
getTranslation = (lang: string) => {
|
||||
const translations = {
|
||||
en: {
|
||||
|
||||
@@ -62,8 +62,10 @@ export class TranslationService {
|
||||
this.customLoader = this.translate.currentLoader as TranslateLoaderService;
|
||||
|
||||
this.defaultLang = 'en';
|
||||
if (typeof this.customLoader?.setDefaultLang === 'function') {
|
||||
this.customLoader.setDefaultLang(this.defaultLang);
|
||||
}
|
||||
this.translate.setFallbackLang(this.defaultLang);
|
||||
this.customLoader.setDefaultLang(this.defaultLang);
|
||||
|
||||
if (this.providers && this.providers.length > 0) {
|
||||
for (const provider of this.providers) {
|
||||
|
||||
@@ -17,7 +17,7 @@ module.exports = function (config) {
|
||||
served: true,
|
||||
watched: false
|
||||
},
|
||||
{ pattern: 'lib/insights/src/lib/i18n/**/en.json', included: false, served: true, watched: false },
|
||||
{ pattern: 'lib/insights/src/lib/i18n/**/en-GB.json', included: false, served: true, watched: false },
|
||||
{ pattern: 'lib/config/app.config.json', included: false, served: true, watched: false }
|
||||
],
|
||||
|
||||
@@ -25,7 +25,7 @@ module.exports = function (config) {
|
||||
|
||||
proxies: {
|
||||
'/base/assets/': '/base/lib/insights/src/lib/assets/',
|
||||
'/assets/adf-insights/i18n/en.json': '/base/lib/insights/src/lib/i18n/en.json',
|
||||
'/assets/adf-insights/i18n/en-GB.json': '/base/lib/insights/src/lib/i18n/en-GB.json',
|
||||
'/app.config.json': '/base/lib/config/app.config.json'
|
||||
},
|
||||
|
||||
|
||||
@@ -30,8 +30,8 @@ module.exports = function (config) {
|
||||
'/base/assets/': '/base/lib/process-services/assets/',
|
||||
'/assets/adf-core/i18n/en.json': '/base/lib/core/src/lib/i18n/en.json',
|
||||
'/assets/adf-core/i18n/en-GB.json': '/base/lib/core/src/lib/i18n/en.json',
|
||||
'/assets/adf-process-services-cloud/i18n/en.json': '/base/lib/process-services-cloud/lib/i18n/en.json',
|
||||
'/assets/adf-process-services-cloud/i18n/en-GB.json': '/base/lib/process-services-cloud/lib/i18n/en.json',
|
||||
'/assets/adf-process-services-cloud/i18n/en.json': '/base/lib/process-services-cloud/src/lib/i18n/en.json',
|
||||
'/assets/adf-process-services-cloud/i18n/en-GB.json': '/base/lib/process-services-cloud/src/lib/i18n/en.json',
|
||||
'/app.config.json': '/base/lib/config/app.config.json'
|
||||
},
|
||||
plugins: [
|
||||
|
||||
Reference in New Issue
Block a user