diff --git a/src/app/components/generic-calculator/calculator.component.ts b/src/app/components/generic-calculator/calculator.component.ts
index dd4e31e8a8416ea6234b73a3f66ebe422123bb78..92462013f634e7ec6f954d77d07203e3369794f6 100644
--- a/src/app/components/generic-calculator/calculator.component.ts
+++ b/src/app/components/generic-calculator/calculator.component.ts
@@ -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;
diff --git a/src/app/formulaire/definition/form-definition.ts b/src/app/formulaire/definition/form-definition.ts
index 140741bebf22d2d630cff6942ff6947b4ba42524..e0c346cf8ad5a72a4a242cb85cc4a5cf8bd5b0e9 100644
--- a/src/app/formulaire/definition/form-definition.ts
+++ b/src/app/formulaire/definition/form-definition.ts
@@ -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();
         }
     }
 
diff --git a/src/app/formulaire/elements/fieldset-container.ts b/src/app/formulaire/elements/fieldset-container.ts
index 1c31212daed733bd6a8a84e360f6b4a9ed09b5c0..96f719fcf0dd192ee0f7af1bf613bb9b69309b48 100644
--- a/src/app/formulaire/elements/fieldset-container.ts
+++ b/src/app/formulaire/elements/fieldset-container.ts
@@ -1,15 +1,12 @@
 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);
-        }
-    }
 }
diff --git a/src/app/formulaire/elements/fieldset.ts b/src/app/formulaire/elements/fieldset.ts
index f9063cb45285821b85b4a7fedcd8eb35095560b1..dce1e17d48ec85a85578f333ccbf908aaca69413 100644
--- a/src/app/formulaire/elements/fieldset.ts
+++ b/src/app/formulaire/elements/fieldset.ts
@@ -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
      */
diff --git a/src/app/formulaire/elements/formulaire-element.ts b/src/app/formulaire/elements/formulaire-element.ts
index 95c14c76d51a5ad2004d2468b5557b649ae66407..79a4899ea702d5c7f964ca0b5124d242bf4290c9 100644
--- a/src/app/formulaire/elements/formulaire-element.ts
+++ b/src/app/formulaire/elements/formulaire-element.ts
@@ -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();
         }
     }
 
diff --git a/src/app/formulaire/elements/select-field.ts b/src/app/formulaire/elements/select-field.ts
index 9be231395892bab31c6003d5ac62774a64acedbb..4f7196cf6b8b8ab4d78cca767a28c99e36913ea5 100644
--- a/src/app/formulaire/elements/select-field.ts
+++ b/src/app/formulaire/elements/select-field.ts
@@ -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]}`);
             }
         }
     }
diff --git a/src/app/services/formulaire.service.ts b/src/app/services/formulaire.service.ts
index d4cdb6ef29af0b04ac60d3991cf1a3ea151bde0e..78c74c8a7e8545eccb75eb90242fb160a7782f0d 100644
--- a/src/app/services/formulaire.service.ts
+++ b/src/app/services/formulaire.service.ts
@@ -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`);
     }
 
     /**
@@ -163,7 +96,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`);
     }
 
     /**
@@ -174,7 +107,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
         }
@@ -207,7 +140,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
         }
@@ -282,8 +215,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 {
@@ -530,11 +463,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
@@ -632,8 +560,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
diff --git a/src/app/services/internationalisation.service.ts b/src/app/services/internationalisation.service.ts
index 14df039699075e1d427e1cd84958c0f914f7fcd4..26d2cecce0c2e8490055f851a0df7d184916838e 100644
--- a/src/app/services/internationalisation.service.ts
+++ b/src/app/services/internationalisation.service.ts
@@ -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"