Skip to content

Commit

Permalink
[FIX] translation: correctly translate dynamic content
Browse files Browse the repository at this point in the history
Using backtick to translate will not work with the translation system.
```js
const x = 2;
_lt(`Hello ${x}`);
```
`Hello ${x}` is evaluated *before* the lookup in the table of
translation, so we are looking for "Hello 2" in the table, which is
not correct.

This commit introduces the way to translate such strings in the following
way:
```js
const x = 2;
_lt("Hello %s", x.toString());
```
  • Loading branch information
pro-odoo authored and VincentSchippefilt committed Jan 26, 2021
1 parent fef21b2 commit eef1c5e
Show file tree
Hide file tree
Showing 14 changed files with 186 additions and 106 deletions.
13 changes: 8 additions & 5 deletions src/formulas/compiler.ts
@@ -1,7 +1,7 @@
import { functionRegistry } from "../functions/index";
import { CompiledFormula, Arg } from "../types/index";
import { AST, ASTAsyncFuncall, ASTFuncall, parse } from "./parser";
import { _lt } from "../translation";
import { Arg, CompiledFormula } from "../types/index";
import { AST, ASTAsyncFuncall, ASTFuncall, parse } from "./parser";

const functions = functionRegistry.content;

Expand Down Expand Up @@ -101,9 +101,12 @@ export function compile(
}
if (result.length < minArg || result.length > maxArg) {
throw new Error(
_lt(`
Invalid number of arguments for the ${ast.value.toUpperCase()} function.
Expected ${fn.args.length}, but got ${result.length} instead.`)
_lt(
"Invalid number of arguments for the %s function. Expected %s, but got %s instead.",
ast.value.toUpperCase(),
fn.args.length.toString(),
result.length.toString()
)
);
}
return result;
Expand Down
2 changes: 1 addition & 1 deletion src/formulas/parser.ts
Expand Up @@ -105,7 +105,7 @@ function bindingPower(token: Token): number {
case "OPERATOR":
return OP_PRIORITY[token.value] || 15;
}
throw new Error(_lt("?"));
throw new Error("?");
}

export const cellReference = new RegExp(/\$?[A-Z]+\$?[0-9]+/, "i");
Expand Down
8 changes: 5 additions & 3 deletions src/functions/helpers.ts
@@ -1,11 +1,12 @@
// HELPERS

import { parseNumber, isNumber } from "../helpers/index";
import { isNumber, parseNumber } from "../helpers/index";
import { _lt } from "../translation";

const expectNumberValueError = (value: string) =>
_lt(
`The function [[FUNCTION_NAME]] expects a number value, but '${value}' is a string, and cannot be coerced to a number.`
"The function [[FUNCTION_NAME]] expects a number value, but '%s' is a string, and cannot be coerced to a number.",
value
);

export function toNumber(value: any): number {
Expand Down Expand Up @@ -152,7 +153,8 @@ export function toString(value: any): string {

const expectBooleanValueError = (value: string) =>
_lt(
`The function [[FUNCTION_NAME]] expects a boolean value, but '${value}' is a text, and cannot be coerced to a number.`
"The function [[FUNCTION_NAME]] expects a boolean value, but '%s' is a text, and cannot be coerced to a number.",
value
);

export function toBoolean(value: any): boolean {
Expand Down
20 changes: 10 additions & 10 deletions src/functions/module_database.ts
@@ -1,9 +1,9 @@
import { args } from "./arguments";
import { _lt } from "../translation";
import { FunctionDescription } from "../types";
import { visitMatchingRanges, toString } from "./helpers";
import { COUNT, AVERAGE, COUNTA, MAX, MIN, STDEV, STDEVP, VAR, VARP } from "./module_statistical";
import { args } from "./arguments";
import { toString, visitMatchingRanges } from "./helpers";
import { PRODUCT, SUM } from "./module_math";
import { _lt } from "../translation";
import { AVERAGE, COUNT, COUNTA, MAX, MIN, STDEV, STDEVP, VAR, VARP } from "./module_statistical";

function getMatchingCells(database: any, field: any, criteria: any): any[] {
// Exemple :
Expand Down Expand Up @@ -34,7 +34,9 @@ function getMatchingCells(database: any, field: any, criteria: any): any[] {
if (index < 0 || index > dimRowDB - 1) {
throw new Error(
_lt(
`Function [[FUNCTION_NAME]] parameter 2 value is ${field}. Valid values are between 1 and ${dimRowDB} inclusive.`
"Function [[FUNCTION_NAME]] parameter 2 value is %s. Valid values are between 1 and %s inclusive.",
field,
dimRowDB
)
);
}
Expand All @@ -44,11 +46,9 @@ function getMatchingCells(database: any, field: any, criteria: any): any[] {
if (index === undefined) {
throw new Error(
_lt(
`Function [[FUNCTION_NAME]] parameter 2 value is ${field}. It should be one of: ${[
...indexColNameDB.keys(),
]
.map((v) => "'" + v + "'")
.join(", ")}.`
"Function [[FUNCTION_NAME]] parameter 2 value is %s. It should be one of: %s.",
field,
[...indexColNameDB.keys()].map((v) => "'" + v + "'").join(", ")
)
);
}
Expand Down
27 changes: 17 additions & 10 deletions src/functions/module_date.ts
@@ -1,8 +1,8 @@
import { args } from "./arguments";
import { InternalDate, parseDateTime, toNativeDate } from "../functions/dates";
import { _lt } from "../translation";
import { FunctionDescription } from "../types";
import { toNativeDate, InternalDate, parseDateTime } from "../functions/dates";
import { args } from "./arguments";
import { toNumber, toString, visitAny } from "./helpers";
import { _lt } from "../translation";

const INITIAL_1900_DAY = new Date(1899, 11, 30);

Expand Down Expand Up @@ -62,7 +62,7 @@ export const DATEVALUE: FunctionDescription = {
const _dateString = toString(date_string);
const datetime = parseDateTime(_dateString);
if (datetime === null) {
throw new Error(_lt(`DATEVALUE parameter '${_dateString}' cannot be parsed to date/time.`));
throw new Error(_lt("DATEVALUE parameter '%s' cannot be parsed to date/time.", _dateString));
}
return Math.trunc(datetime.value);
},
Expand Down Expand Up @@ -351,7 +351,8 @@ function weekendToDayNumber(weekend: any): number[] {
default:
throw new Error(
_lt(
`Function [[FUNCTION_NAME]] parameter 3 requires a string composed of 0 or 1. Actual string is '${weekend}'.`
"Function [[FUNCTION_NAME]] parameter 3 requires a string composed of 0 or 1. Actual string is '%s'.",
weekend
)
);
}
Expand All @@ -360,7 +361,8 @@ function weekendToDayNumber(weekend: any): number[] {
}
throw new Error(
_lt(
`Function [[FUNCTION_NAME]] parameter 3 requires a string with 7 characters. Actual string is '${weekend}'.`
"Function [[FUNCTION_NAME]] parameter 3 requires a string with 7 characters. Actual string is '%s'.",
weekend
)
);
}
Expand All @@ -382,7 +384,8 @@ function weekendToDayNumber(weekend: any): number[] {
}
throw new Error(
_lt(
`Function [[FUNCTION_NAME]] parameter 3 requires a string or a number in the range 1-7 or 11-17. Actual number is ${weekend}.`
"Function [[FUNCTION_NAME]] parameter 3 requires a string or a number in the range 1-7 or 11-17. Actual number is %s.",
weekend.toString()
)
);
}
Expand Down Expand Up @@ -531,7 +534,7 @@ export const TIMEVALUE: FunctionDescription = {
const _timeString = toString(time_string);
const datetime = parseDateTime(_timeString);
if (datetime === null) {
throw new Error(_lt(`TIMEVALUE parameter '${_timeString}' cannot be parsed to date/time.`));
throw new Error(_lt("TIMEVALUE parameter '%s' cannot be parsed to date/time.", _timeString));
}
const result = datetime.value - Math.trunc(datetime.value);

Expand Down Expand Up @@ -584,7 +587,9 @@ export const WEEKDAY: FunctionDescription = {
case 3:
return m === 0 ? 6 : m - 1;
}
throw new Error(_lt(`Function WEEKDAY parameter 2 value ${_type} is out of range.`));
throw new Error(
_lt("Function WEEKDAY parameter 2 value %s is out of range.", _type.toString())
);
},
};

Expand Down Expand Up @@ -615,7 +620,9 @@ export const WEEKNUM: FunctionDescription = {
} else if (_type === 21) {
return ISOWEEKNUM.compute(date);
} else {
throw new Error(_lt(`Function WEEKNUM parameter 2 value ${_type} is out of range.`));
throw new Error(
_lt("Function WEEKNUM parameter 2 value %s is out of range.", _type.toString())
);
}

const y = _date.getFullYear();
Expand Down
20 changes: 12 additions & 8 deletions src/functions/module_lookup.ts
@@ -1,12 +1,12 @@
import { args } from "./arguments";
import { _lt } from "../translation";
import { FunctionDescription } from "../types";
import { args } from "./arguments";
import {
toNumber,
toBoolean,
dichotomicPredecessorSearch,
dichotomicSuccessorSearch,
toBoolean,
toNumber,
} from "./helpers";
import { _lt } from "../translation";

/**
* Perform a linear search and return the index of the perfect match.
Expand Down Expand Up @@ -64,13 +64,17 @@ export const LOOKUP: FunctionDescription = {

if (nbCol > 1) {
if (nbCol - 1 < index) {
throw new Error(_lt(`LOOKUP evaluates to an out of range row value ${index + 1}.`));
throw new Error(
_lt("LOOKUP evaluates to an out of range row value %s.", (index + 1).toString())
);
}
return result_range[index][0];
}

if (nbRow - 1 < index) {
throw new Error(_lt(`LOOKUP evaluates to an out of range column value ${index + 1}.`));
throw new Error(
_lt("LOOKUP evaluates to an out of range column value %s.", (index + 1).toString())
);
}
return result_range[0][index];
},
Expand Down Expand Up @@ -114,7 +118,7 @@ export const MATCH: FunctionDescription = {
if (index > -1) {
return index + 1;
} else {
throw new Error(_lt(`Did not find value '${search_key}' in MATCH evaluation.`));
throw new Error(_lt("Did not find value '%s' in MATCH evaluation.", search_key));
}
},
};
Expand Down Expand Up @@ -156,7 +160,7 @@ export const VLOOKUP: FunctionDescription = {
if (lineIndex > -1) {
return range[_index - 1][lineIndex];
} else {
throw new Error(_lt(`Did not find value '${search_key}' in VLOOKUP evaluation.`));
throw new Error(_lt("Did not find value '%s' in VLOOKUP evaluation.", search_key));
}
},
};

0 comments on commit eef1c5e

Please sign in to comment.