Skip to content
Snippets Groups Projects
Commit 5e2bd1c0 authored by Mathias Chouet's avatar Mathias Chouet Committed by mathias.chouet
Browse files

Reorganize Select classes, update Verificateur GUI

parent ae13eded
No related branches found
No related tags found
1 merge request!82Resolve "Ajout de la fonctionnalité "Respect des critères""
Showing
with 299 additions and 237 deletions
......@@ -270,7 +270,7 @@ case CalculatorType.MacroRugoCompound:
Les listes déroulantes sont toujours associées à des **propriétés** du Nub.
En général les valeurs autorisées sont tirées de l'**enum** correspondant, d'après le tableau `Session.enumFromProperty` de JaLHyd. Pour les autres cas, voir "si la liste n'est pas associée à un enum" ci-dessous.
En général les valeurs autorisées sont tirées de l'**enum** correspondant, d'après le tableau `Session.enumFromProperty` de JaLHyd. Pour les autres cas, voir les paragraphes "si la liste est associée à" ci-dessous.
#### configuration
......@@ -309,7 +309,7 @@ Dans le fichier de configuration du module, ajouter la définition des listes d
Une liste déroulante peut être associée à une **source**, qui détermine quels sont les choix possibles.
Pour ajouter une source, modifier la méthode `parseConfig()` de la classe `SelectField`, dans le fichier `src/app/formulaire/elements/select-field.ts`.
Pour ajouter une source, modifier la méthode `loadEntriesFromSource()` de la classe `SelectField`, dans le fichier `src/app/formulaire/elements/select-field.ts`.
Exemple pour la source "remous_target" associée à la propriété "varCalc", dans le module CourbeRemous :
......
......@@ -77,11 +77,7 @@ describe("ngHyd − check translation of all calculators", () => {
}
// check absence of "*** message not found" in whole DOM
const ps = await browser.getPageSource();
expect(ps).not.toContain("*** message not found");
/* const pos = ps.indexOf("*** message not found");
const bout = ps.substring(pos - 50, pos + 50);
console.log("------------ BOUT ---------------", bout); */
expect(await browser.getPageSource()).not.toContain("*** message not found", "missing translations found");
// empty session
await navBar.clickMenuButton();
......
......@@ -5,8 +5,7 @@
"fields": [
{
"id": "select_target_nub",
"type": "select_reference",
"reference": "nub",
"type": "select_custom",
"source": "solveur_target"
},
{
......@@ -25,8 +24,7 @@
"fields": [
{
"id": "select_searched_param",
"type": "select_reference",
"reference": "parameter",
"type": "select_custom",
"source": "solveur_searched"
},
"Xinit"
......
......@@ -5,8 +5,7 @@
"fields": [
{
"id": "select_target_pass",
"type": "select_reference",
"reference": "nub",
"type": "select_custom",
"source": "verificateur_target"
},
{
......@@ -24,6 +23,7 @@
{
"type": "select",
"id": "select_species_list",
"property": "speciesList",
"source": "verificateur_species",
"multiple": true
}
......@@ -31,7 +31,7 @@
},
{
"type": "options",
"selectIds": [ "select_pab_jet_type" ],
"selectIds": [ "select_pab_jet_type", "select_species_list" ],
"targetPassSelectId": "select_target_pass",
"speciesListSelectId": "select_species_list",
"help": "verification/verificateur.html"
......
......@@ -3,8 +3,8 @@ import { Component, Input, OnInit } from "@angular/core";
import { SelectField } from "../../formulaire/elements/select-field";
import { SelectEntry } from "../../formulaire/elements/select-entry";
import { I18nService } from "../../services/internationalisation.service";
import { SelectFieldReference } from "../../formulaire/elements/select-field-reference";
import { ApplicationSetupService } from "../../services/app-setup.service";
import { SelectFieldCustom } from "../../formulaire/elements/select-field-custom";
@Component({
selector: "select-field-line",
......@@ -115,9 +115,7 @@ export class SelectFieldLineComponent implements OnInit {
// called every time we navigate to the module
ngOnInit(): void {
console.log("> ngOnInit()", this.selectId);
if (this._select instanceof SelectFieldReference) {
console.log(">> updateEntries() !", this.selectId);
if (this._select instanceof SelectFieldCustom) {
this._select.updateEntries();
}
}
......
import { IObservable, ParamDefinition, Nub } from "jalhyd";
import { SelectFieldNub } from "../elements/select-field-nub";
import { SelectFieldParameter } from "../elements/select-field-parameter";
import { SelectFieldCustom } from "../elements/select-field-custom";
import { NgParameter } from "../elements/ngparam";
import { FormulaireFixedVar } from "./form-fixedvar";
import { SelectField } from "../elements/select-field";
......@@ -36,7 +35,7 @@ export class FormulaireSolveur extends FormulaireFixedVar {
sel.addObserver(this);
if (firstNotif) {
// force 1st observation
(sel as SelectFieldNub).notifySelectValueChanged();
(sel as SelectFieldCustom).notifyValueChanged();
}
}
}
......@@ -56,7 +55,7 @@ export class FormulaireSolveur extends FormulaireFixedVar {
sel.addObserver(this);
if (firstNotif) {
// force 1st observation
(sel as SelectFieldNub).notifySelectValueChanged();
(sel as SelectFieldCustom).notifyValueChanged();
}
}
}
......@@ -80,8 +79,8 @@ export class FormulaireSolveur extends FormulaireFixedVar {
this.reset();
}
if (sender instanceof SelectFieldNub) {
if (data.action === "select") {
if (sender instanceof SelectFieldCustom) {
if (sender.id === "select_target_nub" && data.action === "select") {
// update Solveur property: Nub to calculate
try {
// if searchedParam is set to a value that won't be available anymore
......@@ -98,9 +97,7 @@ export class FormulaireSolveur extends FormulaireFixedVar {
}
// refresh parameters selector
this.refreshParameterEntries();
}
} else if (sender instanceof SelectFieldParameter) {
if (data.action === "select") {
} else if (sender.id === "select_searched_param" && data.action === "select") {
// update Solveur property: searched Parameter
try {
const p: ParamDefinition = data.value.value;
......@@ -125,7 +122,7 @@ export class FormulaireSolveur extends FormulaireFixedVar {
* Re-populate searched parameter selector with fresh entries
*/
private refreshParameterEntries() {
const pSel = this.getFormulaireNodeById(this._searchedParamSelectId) as SelectFieldParameter;
const pSel = this.getFormulaireNodeById(this._searchedParamSelectId) as SelectFieldCustom;
if (pSel) {
pSel.updateEntries();
// reflect changes in GUI
......
import { IObservable, Nub } from "jalhyd";
import { IObservable, Nub, Verificateur } from "jalhyd";
import { SelectFieldNub } from "../elements/select-field-nub";
import { SelectFieldCustom } from "../elements/select-field-custom";
import { FormulaireFixedVar } from "./form-fixedvar";
import { SelectField } from '../elements/select-field';
/**
* Formulaire pour les Vérificateurs
......@@ -16,7 +17,7 @@ export class FormulaireVerificateur extends FormulaireFixedVar {
protected parseOptions(json: {}) {
super.parseOptions(json);
this._targetPassSelectId = this.getOption(json, "targetPassSelectI");
this._targetPassSelectId = this.getOption(json, "targetPassSelectId");
this._speciesListSelectId = this.getOption(json, "speciesListSelectId");
}
......@@ -28,21 +29,10 @@ export class FormulaireVerificateur extends FormulaireFixedVar {
sel.addObserver(this);
if (firstNotif) {
// force 1st observation
(sel as SelectFieldNub).notifySelectValueChanged();
(sel as SelectFieldCustom).notifyValueChanged();
}
}
}
if (this._speciesListSelectId) {
const sel = this.getFormulaireNodeById(this._speciesListSelectId);
/* if (sel) {
sel.addObserver(this);
if (firstNotif) {
// force 1st observation
(sel as SelectFieldNub).notifySelectValueChanged();
}
} */
}
}
// interface Observer
......@@ -62,30 +52,30 @@ export class FormulaireVerificateur extends FormulaireFixedVar {
this.reset();
}
if (sender instanceof SelectFieldNub) {
console.log("> update", data, sender.constructor.name);
if (sender instanceof SelectFieldCustom) {
if (data.action === "select") {
// update Verificateur property: Pass to check
this._currentNub.properties.setPropValue("nubToVerify", data.value.value);
let v = undefined;
if (data && data.value && data.value.value) {
v = data.value.value;
}
this._currentNub.properties.setPropValue("nubToVerify", data.value);
// @TODO refresh jet type selector
}
}/* else if (sender instanceof SelectField) {
if (sender.id === "select_target_result") {
// refresh parameters selector
this.refreshParameterEntries();
}
} */
}
}
/**
* Re-populate searched parameter selector with fresh entries
*/
/* private refreshParameterEntries() {
const pSel = this.getFormulaireNodeById(this._speciesListSelectId) as SelectFieldParameter;
public onCalculatorInit() {
// refresh species list selector on each display, because Session might have gained new Espece nubs
console.log(">>>> speciesList avant rafraîchissement :", (this.currentNub as Verificateur).speciesList);
const pSel = this.getFormulaireNodeById(this._speciesListSelectId) as SelectField;
if (pSel) {
pSel.updateEntries();
// reflect changes in GUI
const inputYtarget = this.getFormulaireNodeById("Ytarget") as NgParameter;
inputYtarget.notifyValueModified(this);
/* pSel.clearEntries();
pSel.parseConfig(); */
// (pSel.parent as FieldSet).updateFields()
}
} */
}
}
......@@ -13,8 +13,7 @@ import { SelectField } from "./select-field";
import { NgParameter, ParamRadioConfig } from "./ngparam";
import { StringMap } from "../../stringmap";
import { FieldsetContainer } from "./fieldset-container";
import { SelectFieldNub } from "./select-field-nub";
import { SelectFieldParameter } from "./select-field-parameter";
import { SelectFieldCustom } from "./select-field-custom";
import { FormulaireFixedVar } from "../definition/form-fixedvar";
import { SelectEntry } from './select-entry';
......@@ -84,23 +83,12 @@ export class FieldSet extends FormulaireElement implements Observer {
return res;
}
private parse_select_reference(json: {}): SelectField {
const refType = json["reference"];
private parse_select_custom(json: {}): SelectField {
const source = json["source"];
let res: SelectField;
if (source === undefined || source === "") {
throw new Error(`Fieldset.parse_select_reference(): "source" must not be empty`);
}
switch (refType) {
case "nub": // @TODO upstreamNub / downstreamNub ?
res = new SelectFieldNub(this, source);
break;
case "parameter":
res = new SelectFieldParameter(this, source);
break;
default:
throw new Error(`Fieldset.parse_select_reference(): unknown reference type ${refType}`);
throw new Error(`Fieldset.parse_select_custom(): "source" must not be empty`);
}
const res: SelectField = new SelectFieldCustom(this);
res.parseConfig(json);
res.addObserver(this);
return res;
......@@ -198,8 +186,8 @@ export class FieldSet extends FormulaireElement implements Observer {
this.addField(param);
break;
case "select_reference":
param = this.parse_select_reference(field);
case "select_custom":
param = this.parse_select_custom(field);
this.addField(param);
break;
......@@ -391,7 +379,12 @@ export class FieldSet extends FormulaireElement implements Observer {
const fe = this.parentForm.getFieldById(sId);
if (fe && data.value !== undefined) {
const prop = (fe as SelectField).associatedProperty;
this.setPropValue(prop, data.value.value);
// for multiple select
if (Array.isArray(data.value)) {
this.setPropValue(prop, data.value.map((v: any) => v.value));
} else {
this.setPropValue(prop, data.value.value);
}
}
}
}
......
import { SelectField } from "./select-field";
import { SelectEntry } from "./select-entry";
import { FormulaireNode } from "./formulaire-node";
import { ServiceFactory } from "../../services/service-factory";
import { decodeHtml, arraysAreEqual } from "../../util";
import { arraysAreEqual } from "../../util";
import { Session, Solveur, FishPass, CalculatorType, Verificateur, Nub } from "jalhyd";
import { SelectField } from './select-field';
/**
* A select field that populates itself with references to
* available objects (for ex. Nub or ParamDefinition)
* A select field that populates itself with custom stuff (ex: references to Nubs, Parameters…)
*/
export abstract class SelectFieldReference extends SelectField {
export class SelectFieldCustom extends SelectField {
/** source identifier for populate() method */
protected _source: string;
/**
* Loads UI with the value held by the model
*/
protected initSelectedValue() {
const nub = this.parentForm.currentNub;
console.log("[X] I S V", nub.constructor.name, this.source);
switch (this.source) {
constructor(parent: FormulaireNode, source: string) {
super(parent);
this._source = source;
}
case "solveur_target": // Solveur, module cible (à calculer)
const ntc = (nub as Solveur).nubToCalculate;
if (ntc !== undefined) {
this.setValueFromId(this._entriesBaseId + ntc.uid);
}
break;
case "solveur_searched": // Solveur, paramètre recherché (à faire varier)
const sp = (nub as Solveur).searchedParameter;
if (sp !== undefined) {
this.setValueFromId(this._entriesBaseId + sp.nubUid + "_" + sp.symbol);
}
break;
protected abstract initSelectedValue();
case "verificateur_target": // Vérificateur, passe cible (à vérifier)
const ntv = (nub as Verificateur).nubToVerify;
if (ntv !== undefined) {
this.setValueFromId(this._entriesBaseId + ntv.uid);
}
break;
}
}
/**
* Populates entries with available references
* Populates entries with available options
*/
protected abstract populate();
protected populate() {
const fs = ServiceFactory.instance.formulaireService;
let candidateNubs: any[];
switch (this.source) {
case "solveur_target": // Solveur, module cible (à calculer)
// find all Nubs having at least one link to another Nub's result
candidateNubs =
Session.getInstance().getDownstreamNubs().concat(
Session.getInstance().getUpstreamNubsHavingExtraResults()
).filter(
(element, index, self) => self.findIndex((e) => e.uid === element.uid) === index
);
for (const cn of candidateNubs) {
const nub = fs.getFormulaireFromId(cn.uid);
if (nub) {
const calc = nub.calculatorName;
let label = calc;
// calculated param
if (cn.calculatedParam !== undefined) {
const varName = fs.expandVariableName(cn.calcType, cn.calculatedParam.symbol);
label += ` / ${varName} (${cn.calculatedParam.symbol})`;
}
this.addEntry(new SelectEntry(this._entriesBaseId + cn.uid, cn.uid, decodeHtml(label)));
} else {
// silent fail, this Solveur nub was probably loaded before all the candidate nubs are done loading
}
}
break;
case "solveur_searched": // Solveur, paramètre recherché (à faire varier)
// find all non-calculated, non-linked parameters of all Nubs that
// the current "target" Nub depends on (if any)
const solv = this.parentForm.currentNub as Solveur;
const ntc: Nub = solv.nubToCalculate;
const searchableParams = Solveur.getDependingNubsSearchableParams(
ntc,
solv.targettedResult !== undefined && solv.targettedResult !== ""
);
for (const p of searchableParams) {
if (p.visible) {
const calc = fs.getFormulaireFromId(p.originNub.uid).calculatorName;
const varName = fs.expandVariableName(p.originNub.calcType, p.symbol);
const label = `${p.symbol} - ${varName} (${calc})`;
this.addEntry(new SelectEntry(this._entriesBaseId + p.originNub.uid + "_" + p.symbol, p, decodeHtml(label)));
}
}
break;
case "verificateur_target": // Vérificateur, passe cible (à vérifier)
// find all Nubs of type FishPass
candidateNubs = Session.getInstance().getAllNubs().filter((element) => {
return (
(element instanceof FishPass)
&& element.calcType !== CalculatorType.Par // ParSimulation extends Par @TODO find something better
);
});
for (const cn of candidateNubs) {
const nub = fs.getFormulaireFromId(cn.uid);
if (nub) {
const label = nub.calculatorName + " (" + fs.getLocalisedTitleFromCalculatorType(nub.calculatorType) + ")";
this.addEntry(new SelectEntry(this._entriesBaseId + cn.uid, cn.uid, decodeHtml(label)));
} else {
// silent fail, this Verificateur nub was probably loaded before all the candidate nubs are done loading
}
}
break;
}
}
protected setDefaultValue() {
// default to first available entry if any
if (this._entries.length > 0) {
if (this._multiple) {
this.setValue([ this._entries[0] ]);
} else {
this.setValue(this._entries[0]);
}
} else {
// notify observers that no value is selected anymore
this.notifyValueChanged();
}
}
/**
* Once config is parsed, init original value from model
......@@ -84,17 +188,10 @@ export abstract class SelectFieldReference extends SelectField {
)
);
if (valueChanged) {
this.notifySelectValueChanged();
this.notifyValueChanged();
}
}
public notifySelectValueChanged() {
this.notifyObservers({
"action": "select",
"value": this._selectedEntry
}, this);
}
/**
* Sets value from given ID; if it was not found, sets the
* first available entry as selectedValue
......@@ -116,17 +213,4 @@ export abstract class SelectFieldReference extends SelectField {
}
}
protected setDefaultValue() {
// default to first available entry if any
if (this._entries.length > 0) {
if (this._multiple) {
this.setValue([ this._entries[0] ]);
} else {
this.setValue(this._entries[0]);
}
} else {
// notify observers that no value is selected anymore
this.notifySelectValueChanged();
}
}
}
import { SelectFieldReference } from "./select-field-reference";
import { SelectEntry } from "./select-entry";
import { ServiceFactory } from "../../services/service-factory";
import { decodeHtml } from "../../util";
import { Session, Solveur, FishPass, CalculatorType } from "jalhyd";
/**
* A select field that populates itself with references to Nubs
*/
export class SelectFieldNub extends SelectFieldReference {
protected initSelectedValue() {
const nub = this.parentForm.currentNub;
if (nub instanceof Solveur) {
const ntc = nub.nubToCalculate;
if (ntc !== undefined) {
this.setValueFromId(this._entriesBaseId + ntc.uid);
}
}
}
/**
* Populates entries with available references
*/
protected populate() {
const fs = ServiceFactory.instance.formulaireService;
let candidateNubs: any[];
switch (this._source) {
case "solveur_target": // Solveur, module cible (à calculer)
// find all Nubs having at least one link to another Nub's result
candidateNubs =
Session.getInstance().getDownstreamNubs().concat(
Session.getInstance().getUpstreamNubsHavingExtraResults()
).filter(
(element, index, self) => self.findIndex((e) => e.uid === element.uid) === index
);
for (const cn of candidateNubs) {
const nub = fs.getFormulaireFromId(cn.uid);
if (nub) {
const calc = nub.calculatorName;
let label = calc;
// calculated param
if (cn.calculatedParam !== undefined) {
const varName = fs.expandVariableName(cn.calcType, cn.calculatedParam.symbol);
label += ` / ${varName} (${cn.calculatedParam.symbol})`;
}
this.addEntry(new SelectEntry(this._entriesBaseId + cn.uid, cn.uid, decodeHtml(label)));
} else {
// silent fail, this Solveur nub was probably loaded before all the candidate nubs are done loading
}
}
break;
case "verificateur_target": // Vérificateur, passe cible (à vérifier)
// find all Nubs of type FishPass
candidateNubs = Session.getInstance().getAllNubs().filter((element) => {
return (
(element instanceof FishPass)
&& element.calcType !== CalculatorType.Par // ParSimulation extends Par @TODO find something better
);
});
for (const cn of candidateNubs) {
const nub = fs.getFormulaireFromId(cn.uid);
if (nub) {
const label = nub.calculatorName + " (" + fs.getLocalisedTitleFromCalculatorType(nub.calculatorType) + ")";
this.addEntry(new SelectEntry(this._entriesBaseId + cn.uid, cn.uid, decodeHtml(label)));
} else {
// silent fail, this Verificateur nub was probably loaded before all the candidate nubs are done loading
}
}
break;
}
}
}
import { SelectFieldReference } from "./select-field-reference";
import { SelectEntry } from "./select-entry";
import { decodeHtml } from "../../util";
import { ServiceFactory } from "../../services/service-factory";
import { Nub, Solveur } from "jalhyd";
/**
* A select field that populates itself with references to ParamDefinitions
*/
export class SelectFieldParameter extends SelectFieldReference {
protected initSelectedValue() {
const nub = this.parentForm.currentNub;
if (nub instanceof Solveur) {
const sp = nub.searchedParameter;
if (sp !== undefined) {
this.setValueFromId(this._entriesBaseId + sp.nubUid + "_" + sp.symbol);
}
}
}
/**
* Populates entries with available references
*/
protected populate() {
switch (this._source) {
case "solveur_searched": // Solveur, paramètre recherché (à faire varier)
// find all non-calculated, non-linked parameters of all Nubs that
// the current "target" Nub depends on (if any)
const fs = ServiceFactory.instance.formulaireService;
const solv = this.parentForm.currentNub as Solveur;
const ntc: Nub = solv.nubToCalculate;
const searchableParams = Solveur.getDependingNubsSearchableParams(
ntc,
solv.targettedResult !== undefined && solv.targettedResult !== ""
);
for (const p of searchableParams) {
if (p.visible) {
const calc = fs.getFormulaireFromId(p.originNub.uid).calculatorName;
const varName = fs.expandVariableName(p.originNub.calcType, p.symbol);
const label = `${p.symbol} - ${varName} (${calc})`;
this.addEntry(new SelectEntry(this._entriesBaseId + p.originNub.uid + "_" + p.symbol, p, decodeHtml(label)));
}
}
break;
}
}
}
......@@ -16,6 +16,7 @@ import { FormulaireNode } from "./formulaire-node";
import { FormulaireDefinition } from "../definition/form-definition";
import { ServiceFactory } from "../../services/service-factory";
import { FishSpecies } from 'jalhyd/build/verification/fish_species';
import { sprintf } from 'sprintf-js';
export class SelectField extends Field {
......@@ -35,6 +36,9 @@ export class SelectField extends Field {
/** if true, user can select multiple values */
protected _multiple = false;
/** soruce identifier for populating with available values */
protected source: string;
constructor(parent: FormulaireNode) {
super(parent);
this.clearEntries();
......@@ -75,30 +79,6 @@ export class SelectField extends Field {
}
}
/**
* Reloads available entries, trying to keep the current selected
* value; should not notify observers if value did not change
*/
public updateEntries() {
// store previous selected entry
const pse = this._selectedEntry;
this._selectedEntry = undefined;
// empty
this.clearEntries();
// populate
this.populate();
// keep previously selected entry if possible
if (pse) {
this.setValue(pse);
}
}
/**
* Empties then refills entries list with available entries; does
* nothing by default (to be overloaded)
*/
protected populate() { }
/**
* Triggered at the end of parseConfig()
*/
......@@ -124,6 +104,7 @@ export class SelectField extends Field {
}
public notifyValueChanged() {
console.log("NOT VAL CHA", this.id, this._selectedEntry)
this.notifyObservers({
"action": "select",
"value": this._selectedEntry
......@@ -150,11 +131,20 @@ export class SelectField extends Field {
if (field["multiple"] !== undefined) {
this._multiple = field["multiple"];
}
const source = field["source"];
this.source = field["source"];
this.loadEntriesFromSource();
this.afterParseConfig();
}
/**
* Adds available entries to the selector, depending on the "source" identifier
*/
protected loadEntriesFromSource() {
const nub: Nub = (this.parentForm as FormulaireDefinition).currentNub;
// ad-hoc cases
switch (source) {
switch (this.source) {
// driven by string[], not enum (easier for variable names)
case "remous_target":
this.addEntry(new SelectEntry(this._entriesBaseId + "none", ""));
......@@ -211,13 +201,22 @@ export class SelectField extends Field {
}
break;
// possible values depend on Session
// possible values depend on Session @TODO does not work, because not refreshed when Session gets new Espece nubs
case "verificateur_species":
console.log("[i] loading verif species");
// add UIDs of all Espece type Nubs in the session
const especeNubs = Session.getInstance().getAllNubs().filter((element) => element.calcType === CalculatorType.Espece);
console.log("especeNubs", especeNubs);
for (const en of especeNubs) {
this.addEntry(new SelectEntry(en.uid, en.uid, "Espèce personnalisée : " + en.uid));
this.addEntry(
new SelectEntry(
en.uid,
en.uid,
sprintf(
ServiceFactory.instance.i18nService.localizeText("INFO_VERIFICATEUR_CUSTOM_SPECIES"),
ServiceFactory.instance.formulaireService.getFormulaireFromNubId(en.uid).calculatorName
)
)
);
}
// add all FishSpecies
for (let j = 0; j < Object.keys(FishSpecies).length / 2; j++) {
......@@ -236,7 +235,6 @@ export class SelectField extends Field {
}
}
}
this.afterParseConfig();
}
}
......@@ -532,6 +532,7 @@
"INFO_YAXB_TITRE_COURT": "Linear f.",
"INFO_TRIGO_TITRE": "Trigonometric function",
"INFO_TRIGO_TITRE_COURT": "Trigo. f.",
"INFO_VERIFICATEUR_CUSTOM_SPECIES": "Custom species: %s",
"INFO_VERIFICATEUR_TITRE": "Fish pass verification",
"INFO_VERIFICATEUR_TITRE_COURT": "Verification",
"INFO_ESPECE_TITRE": "Fish species characteristics",
......
......@@ -533,6 +533,7 @@
"INFO_YAXB_TITRE_COURT": "F. affine",
"INFO_TRIGO_TITRE": "Fonction trigonométrique",
"INFO_TRIGO_TITRE_COURT": "F. trigo.",
"INFO_VERIFICATEUR_CUSTOM_SPECIES": "Espèce personnalisée : %s",
"INFO_VERIFICATEUR_TITRE": "Vérification d'une passe",
"INFO_VERIFICATEUR_TITRE_COURT": "Vérification",
"INFO_ESPECE_TITRE": "Caractéristiques d'une espèce",
......
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