[FIX] web: assign numbering system to luxon

Before this commit, the numbering system used by luxon was the default
one set by the browser (in most cases: 'latn'). This caused issues when
translating luxon dates with moment dates, as moment supports some
locales differently (e.g.: ar_001 uses the 'arab' numbering system
while Intl uses 'latn').

To have proper consistency, this commit assigns (as much as possible)
the same numbering systems as luxon for each supported locale.

Warning: since the numbering system is now set according to the user
'lang' parameter, it's now unsafe to use `DateTime.fromFormat(...)` with
static date strings, as they might not be parsable with the current
numbering system.

closes odoo/odoo#84833

X-original-commit: 8bd8b299d57ea930b0549446900bd51d03800041
Signed-off-by: Géry Debongnie <ged@odoo.com>
Signed-off-by: Julien Mougenot (jum) <jum@odoo.com>
This commit is contained in:
Julien Mougenot
2022-02-19 22:17:32 +00:00
committed by Julien Mougenot
parent 07290bfe8d
commit 915191cb18
3 changed files with 154 additions and 55 deletions
@@ -1,11 +1,26 @@
/** @odoo-module **/
import { session } from "@web/session";
import { browser } from "../browser/browser";
import { registry } from "../registry";
import { strftimeToLuxonFormat } from "./dates";
import { localization } from "./localization";
import { translatedTerms, _t } from "./translation";
import { session } from "@web/session";
const { Settings } = luxon;
/** @type {[RegExp, string][]} */
const NUMBERING_SYSTEMS = [
[/^ar-(sa|001)$/i, "arab"],
[/^bn/i, "beng"],
[/^bo/i, "tibt"],
// [/^fa/i, "Farsi (Persian)"], // No numberingSystem found in Intl
// [/^(hi|mr|ne)/i, "Hindi"], // No numberingSystem found in Intl
// [/^my/i, "Burmese"], // No numberingSystem found in Intl
[/^pa-in/i, "guru"],
[/^ta/i, "tamldec"],
[/.*/i, "latn"],
];
export const localizationService = {
dependencies: ["user"],
@@ -38,13 +53,18 @@ export const localizationService = {
Object.setPrototypeOf(translatedTerms, terms);
env._t = _t;
// Setup lang inside luxon. The locale codes received from the server contain "_", whereas
// the Intl codes use "-" (Unicode BCP 47). There's only one exception, which is locale
// "sr@latin", for which we manually fallback to the "sr-Latn-RS" locale.
if (lang === "sr@latin") {
luxon.Settings.defaultLocale = "sr-Latn-RS";
} else if (lang) {
luxon.Settings.defaultLocale = lang.replace(/_/g, "-");
if (lang) {
// Setup lang inside luxon. The locale codes received from the server contain "_",
// whereas the Intl codes use "-" (Unicode BCP 47). There's only one exception, which
// is locale "sr@latin", for which we manually fallback to the "sr-Latn-RS" locale.
const locale = lang === "sr@latin" ? "sr-Latn-RS" : lang.replace(/_/g, "-");
Settings.defaultLocale = locale;
for (const [re, numberingSystem] of NUMBERING_SYSTEMS) {
if (re.test(locale)) {
Settings.defaultNumberingSystem = numberingSystem;
break;
}
}
}
const dateFormat = strftimeToLuxonFormat(userLocalization.date_format);
@@ -167,17 +167,17 @@ export class CustomFilterItem extends Component {
condition.value = 0;
break;
}
case "date": {
case "date":
case "datetime": {
condition.value = [DateTime.local()];
if (operator.symbol === "between") {
condition.value.push(DateTime.local());
}
break;
}
case "datetime": {
condition.value = [DateTime.fromFormat("00:00:00", "hh:mm:ss")];
if (operator.symbol === "between") {
condition.value.push(DateTime.fromFormat("23:59:59", "hh:mm:ss"));
if (genericType === "datetime") {
condition.value[0].set({ hour: 0, minute: 0, second: 0 });
if (operator.symbol === "between") {
condition.value[1].set({ hour: 23, minute: 59, second: 59 });
}
}
break;
}
@@ -1,22 +1,58 @@
/** @odoo-module **/
import { browser } from "@web/core/browser/browser";
import { translatedTerms, _lt } from "@web/core/l10n/translation";
import { localizationService } from "@web/core/l10n/localization_service";
import { registry } from "@web/core/registry";
import { patch, unpatch } from "@web/core/utils/patch";
import { session } from "@web/session";
import { registerCleanup } from "@web/../tests/helpers/cleanup";
import { makeTestEnv } from "@web/../tests/helpers/mock_env";
import { makeFakeLocalizationService } from "@web/../tests/helpers/mock_services";
import { getFixture, mount, patchWithCleanup } from "@web/../tests/helpers/utils";
import { registerCleanup } from "@web/../tests/helpers/cleanup";
import { browser } from "@web/core/browser/browser";
import { localizationService } from "@web/core/l10n/localization_service";
import { translatedTerms, _lt } from "@web/core/l10n/translation";
import { registry } from "@web/core/registry";
import { patch, unpatch } from "@web/core/utils/patch";
import { session } from "@web/session";
const { Component, xml } = owl;
const { DateTime, Settings } = luxon;
const terms = { Hello: "Bonjour" };
const serviceRegistry = registry.category("services");
class TestComponent extends Component {}
/**
* Patches the 'lang' of the user session and context.
*
* @param {string} lang
* @returns {Promise<void>}
*/
const patchLang = async (lang) => {
const { defaultLocale, defaultNumberingSystem } = Settings;
registerCleanup(() => {
Settings.defaultLocale = defaultLocale;
Settings.defaultNumberingSystem = defaultNumberingSystem;
});
patchWithCleanup(session.user_context, { lang });
patchWithCleanup(browser, {
fetch: async () => ({
ok: true,
json: async () => ({
modules: {},
lang_parameters: {
direction: "ltr",
date_format: "%d/%m/%Y",
time_format: "%H:%M:%S",
grouping: "[3,0]",
decimal_point: ",",
thousands_sep: ".",
week_start: 1,
},
}),
}),
});
serviceRegistry.add("localization", localizationService);
await makeTestEnv();
};
QUnit.module("Translations");
QUnit.test("can translate a text node", async (assert) => {
@@ -61,37 +97,80 @@ QUnit.test("_t is in env", async (assert) => {
});
QUnit.test("luxon is configured in the correct lang", async (assert) => {
const defaultLocale = luxon.Settings.defaultLocale;
registerCleanup(() => {
luxon.Settings.defaultLocale = defaultLocale;
});
patchWithCleanup(session, {
user_context: { ...session.user_context, lang: "fr_BE" },
});
patchWithCleanup(browser, {
fetch() {
return {
ok: true,
json() {
return {
modules: {},
lang_parameters: {
direction: "ltr",
date_format: "%d/%m/%Y",
time_format: "%H:%M:%S",
grouping: "[3,0]",
decimal_point: ",",
thousands_sep: ".",
week_start: 1,
},
};
},
};
},
});
serviceRegistry.add("localization", localizationService);
await makeTestEnv();
assert.strictEqual(luxon.DateTime.utc(2021, 12, 10).toFormat("MMMM"), "décembre");
await patchLang("fr_BE");
assert.strictEqual(DateTime.utc(2021, 12, 10).toFormat("MMMM"), "décembre");
});
QUnit.module("Numbering system");
QUnit.test("arabic has the correct numbering system (generic)", async (assert) => {
await patchLang("ar_001");
assert.strictEqual(
DateTime.utc(2021, 12, 10).toFormat("dd MMM, yyyy hh:mm:ss"),
"١٠ ديسمبر, ٢٠٢١ ١٢:٠٠:٠٠"
);
});
QUnit.test("arabic has the correct numbering system (Algeria)", async (assert) => {
await patchLang("ar_DZ");
assert.strictEqual(
DateTime.utc(2021, 12, 10).toFormat("dd MMM, yyyy hh:mm:ss"),
"10 ديسمبر, 2021 12:00:00"
);
});
QUnit.test("arabic has the correct numbering system (Lybia)", async (assert) => {
await patchLang("ar_LY");
assert.strictEqual(
DateTime.utc(2021, 12, 10).toFormat("dd MMM, yyyy hh:mm:ss"),
"10 ديسمبر, 2021 12:00:00"
);
});
QUnit.test("arabic has the correct numbering system (Morocco)", async (assert) => {
await patchLang("ar_MA");
assert.strictEqual(
DateTime.utc(2021, 12, 10).toFormat("dd MMM, yyyy hh:mm:ss"),
"10 دجنبر, 2021 12:00:00"
);
});
QUnit.test("arabic has the correct numbering system (Saudi Arabia)", async (assert) => {
await patchLang("ar_SA");
assert.strictEqual(
DateTime.utc(2021, 12, 10).toFormat("dd MMM, yyyy hh:mm:ss"),
"١٠ جمادى الأولى, ٢٠٢١ ١٢:٠٠:٠٠"
);
});
QUnit.test("arabic has the correct numbering system (Tunisia)", async (assert) => {
await patchLang("ar_TN");
assert.strictEqual(
DateTime.utc(2021, 12, 10).toFormat("dd MMM, yyyy hh:mm:ss"),
"10 ديسمبر, 2021 12:00:00"
);
});
QUnit.test("bengalese has the correct numbering system", async (assert) => {
await patchLang("bn");
assert.strictEqual(
DateTime.utc(2021, 12, 10).toFormat("dd MMM, yyyy hh:mm:ss"),
"১০ ডিসেম্বর, ২০২১ ১২:০০:০০"
);
});
QUnit.test("punjabi (gurmukhi) has the correct numbering system", async (assert) => {
await patchLang("pa_in");
assert.strictEqual(
DateTime.utc(2021, 12, 10).toFormat("dd MMM, yyyy hh:mm:ss"),
"੧੦ M12, ੨੦੨੧ ੧੨:੦੦:੦੦"
);
});
QUnit.test("tamil has the correct numbering system", async (assert) => {
await patchLang("ta");
assert.strictEqual(
DateTime.utc(2021, 12, 10).toFormat("dd MMM, yyyy hh:mm:ss"),
"௧௦ டிச., ௨௦௨௧ ௧௨:௦௦:௦௦"
);
});