File

projects/prestations-ng/src/sdk-dictionary/sdk-dictionary.service.ts

Indexable

[key: string]: string
import { HttpClient } from '@angular/common/http';
import { Injectable } from '@angular/core';
import { BehaviorSubject, combineLatest, Observable, of } from 'rxjs';
import { catchError, map, shareReplay, switchMap, tap } from 'rxjs/operators';

import { GrowlBrokerService } from '../foehn-growl/growl-broker.service';
import { GrowlType } from '../foehn-growl/growl-types';
import { ValidationHandlerService } from '../validation/validation-handler.service';
// eslint-disable-next-line import/no-cycle
import { DEFAULT_DICTIONARY } from './default-dictionary';

export const DICTIONARY_BASE_URL = 'api/dictionary';

export interface PlaceholderType {
    [key: string]: string;
}

export interface DictionaryType {
    [key: string]: string;
}

export interface Language {
    code: string;
    label: string;
}

/**
 * @param value to search in
 * @param placeholders to be replaced
 * @returns string
 */
export const replaceAll = (
    value: string,
    placeholders?: PlaceholderType
): string => {
    if (!placeholders) {
        return value;
    }

    return Object.keys(placeholders).reduce((result, key) => {
        const valueToReplace = placeholders[key];
        return result.replace(new RegExp(`{${key}}`, 'g'), valueToReplace);
    }, value);
};

/**
 * @param dictionary holding all keys
 * @param key from dictionary
 * @param placeholders to be replaced
 * @returns string
 */
function getKeyOrDefault(
    // eslint-disable-next-line @typescript-eslint/no-explicit-any
    dictionary: any,
    key: string,
    placeholders?: PlaceholderType
): string {
    return replaceAll(
        dictionary[key] || DEFAULT_DICTIONARY[key] || key,
        placeholders
    );
}

@Injectable({
    providedIn: 'root'
})
export class SdkDictionaryService {
    private dictionary: DictionaryType;
    private defaultLanguageCode = 'fr';
    private readonly dictionaryObservable: Observable<DictionaryType>;
    private readonly availableLanguagesObservable: Observable<Language[]>;
    private readonly languageCodeSubject = new BehaviorSubject<string>(
        this.getNavigatorLanguageCodeOrDefault()
    );

    constructor(
        private httpClient: HttpClient,
        private growlService: GrowlBrokerService,
        private validationService: ValidationHandlerService
    ) {
        this.availableLanguagesObservable = this.httpClient
            .get<Language[]>(`${DICTIONARY_BASE_URL}/languages`)
            .pipe(
                catchError((e: unknown) => {
                    console.error(e);
                    growlService.addWithType(
                        GrowlType.DANGER,
                        'Impossible de récupérer les langues disponibles'
                    );
                    return of([]);
                }),
                shareReplay(1)
            );

        this.dictionaryObservable = this.getCurrentLanguageCode().pipe(
            switchMap(languageCode => {
                const url =
                    DICTIONARY_BASE_URL +
                    (!!languageCode ? `/${languageCode}` : '');
                return this.httpClient.get<DictionaryType>(url).pipe(
                    catchError((e: unknown) => {
                        console.error(e);
                        growlService.addWithType(
                            GrowlType.DANGER,
                            'Impossible de récupérer les valeurs du dictionnaire'
                        );
                        return of({});
                    }),
                    tap(dictionary => {
                        this.dictionary = dictionary;
                    })
                );
            }),
            shareReplay(1)
        );
    }

    getDictionary(): Observable<DictionaryType> {
        return this.dictionaryObservable;
    }

    getAvailableLanguages(): Observable<Language[]> {
        return this.availableLanguagesObservable;
    }

    getKey(key: string, placeholders?: PlaceholderType): Observable<string> {
        return this.dictionaryObservable.pipe(
            map(dictionary => getKeyOrDefault(dictionary, key, placeholders))
        );
    }

    getKeySync(key: string, placeholders?: PlaceholderType): string {
        if (!this.dictionary) {
            throw new Error('Dictionary not initialized.');
        }

        return getKeyOrDefault(this.dictionary, key, placeholders);
    }

    getPluralMarkerSync(count: number): string {
        if (!count || count === 1) {
            return '';
        }

        return this.getKeySync('plural-marker');
    }

    changeLanguage(code: string): void {
        if (!!code && this.languageCodeSubject.getValue() !== code) {
            this.languageCodeSubject.next(code);
        }
    }

    getCurrentLanguageCode(): Observable<string> {
        return combineLatest([
            this.availableLanguagesObservable,
            this.languageCodeSubject.asObservable()
        ]).pipe(
            switchMap(([availableLanguageCodes, languageCode]) => {
                const matchedLanguage = availableLanguageCodes.find(
                    lc => lc.code === languageCode
                );
                if (!!matchedLanguage && !!matchedLanguage.code) {
                    return of(matchedLanguage.code);
                }
                return of(this.defaultLanguageCode);
            }),
            tap(() => {
                // reset errors as they are translated in the backend
                this.validationService.updateErrors([]);
            }),
            shareReplay(1)
        );
    }

    setDefaultLanguageCode(code: string): void {
        this.defaultLanguageCode = code;
        this.languageCodeSubject.next(this.getNavigatorLanguageCodeOrDefault());
    }

    private getNavigatorLanguageCodeOrDefault(): string {
        return !!navigator && !!navigator.language
            ? navigator.language.split('-')[0]
            : this.defaultLanguageCode;
    }
}

results matching ""

    No results matching ""