Newer
Older
francois.grand
committed
import { Message, MessageCode, Observable, Observer } from "jalhyd";
francois.grand
committed
import { StringMap } from "../../stringmap";
import { ApplicationSetupService } from "../app-setup/app-setup.service";
import { HttpService } from "../http/http.service";
francois.grand
committed
/*
language tag : fr-FR
primary subcode : fr
optional subcode : FR
*/
francois.grand
committed
export enum LanguageCode {
francois.grand
committed
FRENCH,
ENGLISH,
}
francois.grand
committed
export class Language {
private _code: LanguageCode;
private _tag: string;
private _label: string;
constructor(c: LanguageCode, t: string, l: string) {
this._code = c;
this._tag = t;
this._label = l;
}
get code(): LanguageCode {
return this._code;
}
get tag(): string {
return this._tag;
}
get label(): string {
return this._label;
}
}
francois.grand
committed
@Injectable()
export class I18nService extends Observable implements Observer {
francois.grand
committed
private _currLang: Language;
private _Messages: StringMap;
francois.grand
committed
private _languages: Language[];
constructor(
private applicationSetupService: ApplicationSetupService,
private httpService: HttpService) {
francois.grand
committed
this._languages = [];
this._languages.push(new Language(LanguageCode.FRENCH, "fr", "Français"));
this._languages.push(new Language(LanguageCode.ENGLISH, "en", "English"));
// add language preferences observer
this.applicationSetupService.addObserver(this);
francois.grand
committed
}
francois.grand
committed
public get languages() {
return this._languages;
}
francois.grand
committed
francois.grand
committed
public get currentLanguage() {
return this._currLang;
}
francois.grand
committed
public get currentMap() {
return this._Messages;
}
francois.grand
committed
private getLanguageFromCode(lc: LanguageCode) {
for (const l of this._languages) {
if (l.code === lc) {
francois.grand
committed
return l;
francois.grand
committed
}
throw new Message(MessageCode.ERROR_LANG_UNSUPPORTED);
francois.grand
committed
}
francois.grand
committed
private getLanguageFromTag(tag: string) {
for (const l of this._languages) {
if (l.tag === tag) {
francois.grand
committed
return l;
francois.grand
committed
}
const e = new Message(MessageCode.ERROR_LANG_UNSUPPORTED);
francois.grand
committed
e.extraVar["locale"] = tag;
throw e;
}
francois.grand
committed
public setLocale(lng: string | LanguageCode) {
let oldLang;
if (this._currLang !== undefined) {
oldLang = this._currLang.code;
}
francois.grand
committed
if (typeof lng === "string") {
francois.grand
committed
this._currLang = this.getLanguageFromTag(t);
francois.grand
committed
this._currLang = this.getLanguageFromCode(lng);
francois.grand
committed
François
committed
if (this._currLang.code !== oldLang) {
this._Messages = undefined;
const prom = this.httpGetMessages();
const is: I18nService = this;
François
committed
prom.then((res) => {
François
committed
is.notifyObservers(undefined);
François
committed
}
const is: I18nService = this;
francois.grand
committed
// fermeture nécessaire pour capturer la valeur de this (undefined sinon)
is._Messages = JSON.parse(s);
francois.grand
committed
francois.grand
committed
switch (this._currLang.code) {
case LanguageCode.FRENCH:
francois.grand
committed
l = "fr";
break;
default:
l = "en";
}
const f: string = "messages." + l + ".json";
return this.httpService.httpGetRequestPromise("locale/" + f).then(
francois.grand
committed
}
private getMessageFromCode(c: MessageCode): string {
if (! this._Messages) {
return `*** Messages not loaded yet ***`;
}
if (this._Messages[MessageCode[c]] === undefined) {
return `*** Message ${MessageCode[c]} non traduit ***`;
}
return this._Messages[MessageCode[c]];
francois.grand
committed
}
private replaceAll(str: string, find: string, replace: string) {
return str.replace(new RegExp(find, "g"), replace);
francois.grand
committed
}
francois.grand
committed
/**
* traduit un message
* @param r message
* @param nDigits nombre de chiffres à utiliser pour l'arrondi dans le cas de données numériques
*/
public localizeMessage(r: Message, nDigits: number = 3): string {
let m: string = this.getMessageFromCode(r.code);
francois.grand
committed
for (const k in r.extraVar) {
if (r.extraVar.hasOwnProperty(k)) {
const v: any = r.extraVar[k];
let s: string;
if (typeof v === "number") {
s = v.toFixed(nDigits);
} else {
s = v;
}
m = this.replaceAll(m, "%" + k + "%", s);
}
francois.grand
committed
}
return m;
}
* Traduit un texte défini dans un fichier de langue (locale/error_message.xx.json par défaut)
* Les ids dans ces fichiers sont soit un enum de JalHyd, soit une chaine libre correspondant au code passé àlocalizeText()
* @param code id du texte
* @param messages Contenu du fichier de langua à utiliser (locale/error_message.xx.json par défaut)
public localizeText(code: string, messages = this._Messages) {
if (messages === undefined) {
francois.grand
committed
return `*** messages not loaded: ${code} ***`;
return `*** message not exists: ${code} ***`;
francois.grand
committed
/**
* analyse un libellé du type ouvrage[n].XX
*/
private parseLabel(lbl: string) {
const re = /([A-Z,a-z]+)\[(\d+)\]\.(.+)/;
return re.exec(lbl);
}
/**
David Dorchies
committed
* Traduit un libellé qui peut être un code
francois.grand
committed
*/
public getExtraResLabel(s: string) {
David Dorchies
committed
const key = "INFO_EXTRARES_LIB_";
const match = this.parseLabel(s);
if (match) {
// Code du type "Ouvrage[n].XXX"
// Les libellés correspondants sont INFO OUVRAGE et INFO_EXTRARES_LIB_OUVRAGE_XXX
return this.localizeText(`INFO_${match[1].toUpperCase()}`) + " n°" + (+match[2] + 1) + ": " +
this.localizeText(`${key}${match[1].toUpperCase()}_${match[3].toUpperCase()}`);
} else {
// Autres codes INFO_EXTRARES_LIB_XXX
return this.localizeText(`${key}${s.toUpperCase()}`);
}
francois.grand
committed
}
francois.grand
committed
/**
* Met en forme un extraResult en fonction du libellé qui l'accompagne
* Les extraResult avec le terme "ENUM_" sont traduit avec le message INFO_EXTRARES_ENUM_[Nom de la variable après ENUM_]
francois.grand
committed
*/
public formatResult(label: string, value: number): string {
const match = label.indexOf("ENUM_");
if (match > -1) {
return this.localizeText(`INFO_EXTRARES_${label.substring(match).toUpperCase()}_${value}`);
David Dorchies
committed
}
const nDigits = this.applicationSetupService.displayDigits;
francois.grand
committed
return value.toFixed(nDigits);
}
David Dorchies
committed
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
// interface Observer
/**
* Should only be triggered once at app startup, when setup service tries loading language
* @param sender should always be ApplicationSetupService
* @param data object {
* action: should always be "languagePreferenceChanged"
* languages: languages codes to try until one works
* }
*/
public update(sender: any, data: any): void {
if (sender instanceof ApplicationSetupService) {
if (data.action === "languagePreferenceChanged") {
let languageEventuallyUsed: string;
for (let i = 0; i < data.languages.length && languageEventuallyUsed === undefined; i++) {
const l = data.languages[i];
if (l !== undefined) {
try {
this.setLocale(l);
languageEventuallyUsed = l;
} catch (e) {
console.error(e.toString());
}
}
}
// confirm to setup service which language was eventually used at app startup (triggers nothing more)
this.applicationSetupService.language = languageEventuallyUsed;
}
}
}