Skip to content
Snippets Groups Projects
Commit 481c2c6d authored by mathias.chouet's avatar mathias.chouet
Browse files

Work on #223 − enhance translation system

parent e9c25833
No related branches found
No related tags found
No related merge requests found
......@@ -433,8 +433,6 @@ export class GenericCalculatorComponent implements OnInit, DoCheck, AfterViewChe
this.setForm(this.formulaireService.getFormulaireFromId(uid));
this.resultsComponent.formulaire = this._formulaire;
this._calculatorNameComponent.model = this._formulaire;
// reload localisation in all cases
this.formulaireService.loadUpdateFormulaireLocalisation(this._formulaire);
// call Form init hook
this._formulaire.onCalculatorInit();
break;
......
......@@ -52,12 +52,6 @@ export abstract class FormulaireDefinition extends FormulaireNode implements Obs
/** fichier de configuration */
private _jsonConfig: {};
/** clé-valeurs du fichier de localisation spécifique à ce module */
private _specificLocalisation: StringMap;
/** ISO 639-1 language code of the current language (to avoid unnecessary localisation reload) */
private _currentLanguage: string;
/** copy of options.resultsHelp read by FormDefinition.parseOptions() */
public helpLinks: { [key: string]: string };
......@@ -79,14 +73,6 @@ export abstract class FormulaireDefinition extends FormulaireNode implements Obs
return this._calculateDisabled;
}
public get specificLocalisation() {
return this._specificLocalisation;
}
public get currentLanguage() {
return this._currentLanguage;
}
public get calculatorType(): CalculatorType {
const props = this._currentNub === undefined ? this.defaultProperties : (this._currentNub.properties as Props).props;
return props["calcType"];
......@@ -424,11 +410,9 @@ export abstract class FormulaireDefinition extends FormulaireNode implements Obs
}
}
public updateLocalisation(localisation: StringMap, lang: string) {
this._specificLocalisation = localisation;
this._currentLanguage = lang;
public updateLocalisation(lang: string) {
for (const fe of this.topFormElements) {
fe.updateLocalisation(localisation);
fe.updateLocalisation();
}
}
......
import { FormulaireElement } from "./formulaire-element";
import { FieldSet } from "./fieldset";
import { FieldsetTemplate } from "./fieldset-template";
import { StringMap } from "../../stringmap";
import { FormulaireNode } from "./formulaire-node";
import { Nub } from "jalhyd";
export class FieldsetContainer extends FormulaireElement {
private _templates: FieldsetTemplate[];
private _localisation: StringMap;
public title: string;
constructor(parent: FormulaireNode) {
......@@ -109,11 +106,4 @@ export class FieldsetContainer extends FormulaireElement {
}
}
}
public updateLocalisation(loc: StringMap = this._localisation) {
this._localisation = loc;
if (loc !== undefined) {
super.updateLocalisation(loc);
}
}
}
......@@ -11,7 +11,6 @@ import { FormulaireElement } from "./formulaire-element";
import { Field } from "./field";
import { SelectField } from "./select-field";
import { NgParameter, ParamRadioConfig } from "./ngparam";
import { StringMap } from "../../stringmap";
import { FieldsetContainer } from "./fieldset-container";
import { SelectFieldCustom } from "./select-field-custom";
import { FormulaireFixedVar } from "../definition/form-fixedvar";
......@@ -22,9 +21,6 @@ export class FieldSet extends FormulaireElement implements Observer {
/** Nub associé */
private _nub: Nub;
/** dictionnaire de traduction */
private _localisation: StringMap;
/** fichier de configuration */
private _jsonConfig: {};
......@@ -203,18 +199,6 @@ export class FieldSet extends FormulaireElement implements Observer {
this.clearKids();
}
public updateLocalisation(loc?: StringMap) {
if (! loc) {
loc = this._localisation;
} else {
this._localisation = loc;
}
if (loc) {
super.updateLocalisation(loc);
}
}
/**
* Reloads the model values and properties, and reloads localisation strings
*/
......
......@@ -71,13 +71,13 @@ export abstract class FormulaireElement extends FormulaireNode {
* @param loc calculator-specific localised messages map
* @param key Element label key
*/
public updateLocalisation(loc: StringMap, key?: string) {
public updateLocalisation(key?: string) {
if (!key) {
key = this._confId;
}
this._label = this.intlService.localizeText(key, loc);
this._label = this.intlService.localizeText(key);
for (const f of this.getKids()) {
f.updateLocalisation(loc);
f.updateLocalisation();
}
}
......
......@@ -110,13 +110,13 @@ export class SelectField extends Field {
}, this);
}
public updateLocalisation(loc: StringMap) {
super.updateLocalisation(loc);
public updateLocalisation() {
super.updateLocalisation();
for (const e of this._entries) {
// some Select fields already have a translated label at this time; translate others
if (e.label === undefined) {
const aId = e.id.split("_");
e.label = ServiceFactory.i18nService.localizeText(`${aId[1].toUpperCase()}_${aId[2]}`, loc);
e.label = ServiceFactory.i18nService.localizeText(`${aId[1].toUpperCase()}_${aId[2]}`);
}
}
}
......
......@@ -26,7 +26,6 @@ import { FormulaireDefinition } from "../formulaire/definition/form-definition";
import { FormulaireElement } from "../formulaire/elements/formulaire-element";
import { InputField } from "../formulaire/elements/input-field";
import { SelectField } from "../formulaire/elements/select-field";
import { StringMap } from "../stringmap";
import { FormulaireSectionParametree } from "../formulaire/definition/form-section-parametree";
import { FormulaireCourbeRemous } from "../formulaire/definition/form-courbe-remous";
import { FormulaireParallelStructure } from "../formulaire/definition/form-parallel-structures";
......@@ -52,8 +51,10 @@ export class FormulaireService extends Observable {
private _currentFormId: string = null;
/** to avoid loading language files multiple times */
private _languageCache = {};
public static getConfigPathPrefix(ct: CalculatorType): string {
const ctName = CalculatorType[ct].toLowerCase();
return "app/calculators/" + ctName + "/";
}
constructor(
private i18nService: I18nService,
......@@ -66,84 +67,16 @@ export class FormulaireService extends Observable {
this._formulaires = [];
}
private get _intlService(): I18nService {
return this.i18nService;
}
private get _httpService(): HttpService {
return this.httpService;
}
public get formulaires(): FormulaireDefinition[] {
return this._formulaires;
}
public get languageCache() {
return this._languageCache;
}
/**
* Loads the localisation file dedicated to calculator type ct; tries the current
* language then the fallback language; uses cache if available
*/
public loadLocalisation(calc: CalculatorType): Promise<any> {
const lang = this._intlService.currentLanguage;
return this.loadLocalisationForLang(calc, lang).then((localisation) => {
return localisation as StringMap;
}).catch((e) => {
console.error(e);
// try default lang (the one in the config file) ?
const fallbackLang = this.appSetupService.fallbackLanguage;
if (lang !== fallbackLang) {
console.error(`trying fallback language: ${fallbackLang}`);
return this.loadLocalisationForLang(calc, fallbackLang);
}
});
}
/**
* Loads the localisation file dedicated to calculator type ct for language lang;
* keeps it in cache for subsequent calls ()
*/
private loadLocalisationForLang(calc: CalculatorType, lang: string): Promise<any> {
const ct = String(calc);
// already in cache ?
if (Object.keys(this._languageCache).includes(ct) && Object.keys(this._languageCache[calc]).includes(lang)) {
return new Promise((resolve) => {
resolve(this._languageCache[ct][lang]);
});
} else {
const f: string = this.getConfigPathPrefix(calc) + lang + ".json";
return this._httpService.httpGetRequestPromise(f).then((localisation) => {
this._languageCache[ct] = this._languageCache[ct] || {};
this._languageCache[ct][lang] = localisation;
return localisation as StringMap;
}).catch((e) => {
throw new Error(`LOCALISATION_FILE_NOT_FOUND "${f}"`);
});
}
}
/**
* Loads localisation file corresponding to current language then updates all form strings,
* only if form language was not already set to current language
*/
public loadUpdateFormulaireLocalisation(f: FormulaireDefinition): Promise<FormulaireDefinition> {
const requiredLang = this._intlService.currentLanguage;
if (requiredLang !== f.currentLanguage) {
return this.loadLocalisation(f.calculatorType).then(localisation => {
f.updateLocalisation(localisation, requiredLang);
return f;
});
}
}
/**
* Retourne le titre complet du type de module de calcul, dans la langue en cours
*/
public getLocalisedTitleFromCalculatorType(type: CalculatorType) {
const sCalculator: string = CalculatorType[type].toUpperCase();
return this._intlService.localizeText(`INFO_${sCalculator}_TITRE`);
return this.intlService.localizeText(`INFO_${sCalculator}_TITRE`);
}
/**
......@@ -155,7 +88,7 @@ export class FormulaireService extends Observable {
type = CalculatorType[type];
}
const sCalculator: string = type.toUpperCase();
return this._intlService.localizeText(`INFO_${sCalculator}_TITRE_COURT`);
return this.intlService.localizeText(`INFO_${sCalculator}_TITRE_COURT`);
}
/**
......@@ -166,7 +99,7 @@ export class FormulaireService extends Observable {
public expandVariableName(calcType: CalculatorType, symbol: string): string {
let s = "";
// language cache…
let langCache = this.languageCache;
let langCache = this.i18nService.languageCache;
if (langCache && langCache[calcType]) {
langCache = langCache[calcType]; // …for target Nub type
}
......@@ -199,7 +132,7 @@ export class FormulaireService extends Observable {
*/
public expandVariableNameAndUnit(calcType: CalculatorType, symbol: string, forceUnit?: string): string {
let s = this.expandVariableName(calcType, symbol);
let langCache = this.languageCache; // language cache…
let langCache = this.i18nService.languageCache; // language cache…
if (langCache && langCache[calcType]) {
langCache = langCache[calcType]; // …for target Nub type
}
......@@ -274,8 +207,8 @@ export class FormulaireService extends Observable {
}
public loadConfig(ct: CalculatorType): Promise<any> {
const f: string = this.getConfigPathPrefix(ct) + "config.json";
return this._httpService.httpGetRequestPromise(f);
const f: string = FormulaireService.getConfigPathPrefix(ct) + "config.json";
return this.httpService.httpGetRequestPromise(f);
}
private newFormulaire(ct: CalculatorType): FormulaireDefinition {
......@@ -522,11 +455,6 @@ export class FormulaireService extends Observable {
}
}
public getConfigPathPrefix(ct: CalculatorType): string {
const ctName = CalculatorType[ct].toLowerCase();
return "app/calculators/" + ctName + "/";
}
/**
* Supprime le formulaire ciblé, et demande à JaLHyd d'effacer son Nub de la Session
* @param uid formulaire à supprimer
......@@ -624,8 +552,6 @@ export class FormulaireService extends Observable {
if (nn.meta && nn.meta.title) {
title = nn.meta.title;
}
// pre-fill language cache (for LinkedValues labels for ex.)
await this.loadLocalisation(nn.nub.calcType);
await this.createFormulaire(nn.nub.calcType, nn.nub, title); // await guarantees loading order
}
// apply settings
......
......@@ -7,6 +7,7 @@ import { ApplicationSetupService } from "./app-setup.service";
import { HttpService } from "./http.service";
import { fv, decodeHtml } from "../util";
import { ServiceFactory } from "./service-factory";
import { FormulaireService } from "./formulaire.service";
@Injectable()
export class I18nService extends Observable implements Observer {
......@@ -23,6 +24,9 @@ export class I18nService extends Observable implements Observer {
/** localized messages in fallback language (the one in the config file) */
private _fallbackMessages: StringMap;
/** to avoid loading language files multiple times */
private _languageCache = {};
constructor(
private applicationSetupService: ApplicationSetupService,
private httpService: HttpService
......@@ -52,6 +56,10 @@ export class I18nService extends Observable implements Observer {
return this._Messages;
}
public get languageCache() {
return this._languageCache;
}
/**
* Defines the current language code from its ISO 639-1 code (2 characters) or locale code
* (ex: "fr", "en", "fr_FR", "en-US")
......@@ -69,12 +77,64 @@ export class I18nService extends Observable implements Observer {
if (this._currentLanguage !== code) {
this._currentLanguage = code;
this._Messages = undefined;
// reload all messages
// reload all messages: global lang files, plus lang files for all calculators !
const that = this;
this.httpGetMessages(code).then((res: any) => {
that._Messages = res;
// propagate language change to all application
that.notifyObservers(undefined);
console.log("> promise.all !");
const promisesList: Promise<any>[] = [];
for (const ct in CalculatorType) {
const calcType = Number(ct);
if (!isNaN(calcType)) {
promisesList.push(this.loadLocalisation(calcType).catch((err) => { /* silent fail */ }));
}
}
Promise.all(promisesList).then(() => {
console.log(">> get global messages !");
this.httpGetMessages(code).then((res: any) => {
that._Messages = res;
// propagate language change to all application
that.notifyObservers(undefined);
});
});
}
}
/**
* Loads the localisation file dedicated to calculator type ct; tries the current
* language then the fallback language; uses cache if available
*/
public loadLocalisation(calc: CalculatorType): Promise<any> {
const lang = this.currentLanguage;
return this.loadLocalisationForLang(calc, lang).then((localisation) => {
return localisation as StringMap;
}).catch((e) => {
// try default lang (the one in the config file) ?
const fallbackLang = this.applicationSetupService.fallbackLanguage;
if (lang !== fallbackLang) {
console.error(`localisation for ${CalculatorType[calc]} not found, trying fallback language: ${fallbackLang}`);
return this.loadLocalisationForLang(calc, fallbackLang);
}
});
}
/**
* Loads the localisation file dedicated to calculator type ct for language lang;
* keeps it in cache for subsequent calls ()
*/
private loadLocalisationForLang(calc: CalculatorType, lang: string): Promise<any> {
const ct = String(calc);
// already in cache ?
if (Object.keys(this._languageCache).includes(ct) && Object.keys(this._languageCache[calc]).includes(lang)) {
return new Promise((resolve) => {
resolve(this._languageCache[ct][lang]);
});
} else {
const f: string = FormulaireService.getConfigPathPrefix(calc) + lang + ".json";
return this.httpService.httpGetRequestPromise(f).then((localisation) => {
this._languageCache[ct] = this._languageCache[ct] || {};
this._languageCache[ct][lang] = localisation;
return localisation as StringMap;
}).catch((e) => {
throw new Error(`LOCALISATION_FILE_NOT_FOUND "${f}"`);
});
}
}
......@@ -285,7 +345,8 @@ export class I18nService extends Observable implements Observer {
// interface Observer
/**
* Should only be triggered once at app startup, when setup service tries loading language
* Should only be triggered once at app startup, when setup service tries loading language,
* then everytime language is changed through Preferences screen
* @param sender should always be ApplicationSetupService
* @param data object {
* action: should always be "languagePreferenceChanged"
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment