diff --git a/frontend/src/metabase/app.js b/frontend/src/metabase/app.js index 261fbe97c6ae37aea657c3c0aab44f0f407b1dd3..c9b29521e2c04b35b841065db6bb4266ff251817 100644 --- a/frontend/src/metabase/app.js +++ b/frontend/src/metabase/app.js @@ -11,6 +11,9 @@ import "number-to-locale-string"; // Should be imported before any other metabase import import "ee-overrides"; // eslint-disable-line import/no-duplicates +// day.js plugins should be enabled before setting the locale +import "metabase/lib/dayjs"; + // If enabled this monkeypatches `t` and `jt` to return blacked out // strings/elements to assist in finding untranslated strings. import "metabase/lib/i18n-debug"; diff --git a/frontend/src/metabase/lib/dayjs.ts b/frontend/src/metabase/lib/dayjs.ts new file mode 100644 index 0000000000000000000000000000000000000000..00cf862e2724de1ab2194ece664fed77e178122c --- /dev/null +++ b/frontend/src/metabase/lib/dayjs.ts @@ -0,0 +1,6 @@ +import dayjs from "dayjs"; +import localeDataPlugin from "dayjs/plugin/localeData"; +import updateLocalePlugin from "dayjs/plugin/updateLocale"; + +dayjs.extend(localeDataPlugin); +dayjs.extend(updateLocalePlugin); diff --git a/frontend/src/metabase/lib/i18n.js b/frontend/src/metabase/lib/i18n.js index 35256d3b70aabaed39a5931e322dc0df3ac2f6e5..9946b411fcc8e11d6f87c46c19ae594d3751afe4 100644 --- a/frontend/src/metabase/lib/i18n.js +++ b/frontend/src/metabase/lib/i18n.js @@ -1,5 +1,6 @@ import { addLocale, useLocale } from "ttag"; import moment from "moment-timezone"; +import dayjs from "dayjs"; import MetabaseSettings from "metabase/lib/settings"; import { DAY_OF_WEEK_OPTIONS } from "metabase/lib/date-time"; @@ -26,26 +27,20 @@ export async function loadLocalization(locale) { setLocalization(translationsObject); } -// Tell Moment.js to use the value of the start-of-week Setting for its current locale +// Tell moment.js to use the value of the start-of-week Setting for its current locale +// Moment.js dow range Sunday (0) - Saturday (6) export function updateMomentStartOfWeek() { - const startOfWeekDayName = MetabaseSettings.get("start-of-week"); - if (!startOfWeekDayName) { - return; + const startOfWeekDay = getStartOfWeekDay(); + if (startOfWeekDay != null) { + moment.updateLocale(moment.locale(), { week: { dow: startOfWeekDay } }); } +} - const startOfWeekDayNumber = DAY_OF_WEEK_OPTIONS.findIndex( - ({ id }) => id === startOfWeekDayName, - ); - if (startOfWeekDayNumber === -1) { - return; +export function updateDayjsStartOfWeek() { + const startOfWeekDay = getStartOfWeekDay(); + if (startOfWeekDay != null) { + dayjs.updateLocale(dayjs.locale(), { week: { dow: startOfWeekDay } }); } - - moment.updateLocale(moment.locale(), { - week: { - // Moment.js dow range Sunday (0) - Saturday (6) - dow: startOfWeekDayNumber, - }, - }); } // if the start of week Setting is updated, update the moment start of week @@ -62,35 +57,66 @@ function setLanguage(translationsObject) { } function setLocalization(translationsObject) { - const locale = translationsObject.headers.language; - + const language = translationsObject.headers.language; setLanguage(translationsObject); - - updateMomentLocale(locale); - updateMomentStartOfWeek(locale); + updateMomentLocale(language); + updateDayjsLocale(language); + updateMomentStartOfWeek(); + updateDayjsStartOfWeek(); } -function updateMomentLocale(locale) { - const momentLocale = mapToMomentLocale(locale); +function updateMomentLocale(language) { + const locale = getLocale(language); + try { - if (momentLocale !== "en") { - require("moment/locale/" + momentLocale); + if (locale !== "en") { + require(`moment/locale/${locale}.js`); } - moment.locale(momentLocale); + moment.locale(locale); } catch (e) { - console.warn(`Could not set moment locale to ${momentLocale}`); + console.warn(`Could not set moment.js locale to ${locale}`); moment.locale("en"); } } -function mapToMomentLocale(locale = "") { - switch (locale) { +function updateDayjsLocale(language) { + const locale = getLocale(language); + + try { + if (locale !== "en") { + require(`dayjs/locale/${locale}.js`); + } + dayjs.locale(locale); + } catch (e) { + console.warn(`Could not set day.js locale to ${locale}`); + dayjs.locale("en"); + } +} + +function getLocale(language = "") { + switch (language) { case "zh": case "zh-Hans": return "zh-cn"; default: - return locale.toLowerCase(); + return language.toLowerCase(); + } +} + +function getStartOfWeekDay() { + const startOfWeekDayName = MetabaseSettings.get("start-of-week"); + if (!startOfWeekDayName) { + return; } + + const startOfWeekDayNumber = DAY_OF_WEEK_OPTIONS.findIndex( + ({ id }) => id === startOfWeekDayName, + ); + if (startOfWeekDayNumber === -1) { + return; + } + + return startOfWeekDayNumber; } // we delete msgid property since it's redundant, but have to add it back in to diff --git a/frontend/src/metabase/ui/components/theme/DatesProvider/DatesProvider.tsx b/frontend/src/metabase/ui/components/theme/DatesProvider/DatesProvider.tsx new file mode 100644 index 0000000000000000000000000000000000000000..04b93ab2312373b93243e877404993c573b0c701 --- /dev/null +++ b/frontend/src/metabase/ui/components/theme/DatesProvider/DatesProvider.tsx @@ -0,0 +1,19 @@ +import type { ReactNode } from "react"; +import dayjs from "dayjs"; +import { DatesProvider as MantineDatesProvider } from "@mantine/dates"; +import type { DatesProviderSettings, DayOfWeek } from "@mantine/dates"; + +interface DatesProviderProps { + children?: ReactNode; +} + +export function DatesProvider({ children }: DatesProviderProps) { + const settings: DatesProviderSettings = { + locale: dayjs.locale(), + firstDayOfWeek: dayjs.localeData().firstDayOfWeek() as DayOfWeek, + }; + + return ( + <MantineDatesProvider settings={settings}>{children}</MantineDatesProvider> + ); +} diff --git a/frontend/src/metabase/ui/components/theme/DatesProvider/index.ts b/frontend/src/metabase/ui/components/theme/DatesProvider/index.ts new file mode 100644 index 0000000000000000000000000000000000000000..12f87bccc691795072b664e9a40923f51d8b4607 --- /dev/null +++ b/frontend/src/metabase/ui/components/theme/DatesProvider/index.ts @@ -0,0 +1 @@ +export * from "./DatesProvider"; diff --git a/frontend/src/metabase/ui/components/theme/ThemeProvider/ThemeProvider.tsx b/frontend/src/metabase/ui/components/theme/ThemeProvider/ThemeProvider.tsx index b76d55b6f259dc3670b99d0072de6a9b842597ed..8908e451f7de040377f474701a49155bf0fe9f05 100644 --- a/frontend/src/metabase/ui/components/theme/ThemeProvider/ThemeProvider.tsx +++ b/frontend/src/metabase/ui/components/theme/ThemeProvider/ThemeProvider.tsx @@ -3,6 +3,7 @@ import { MantineProvider } from "@mantine/core"; import { withEmotionCache } from "@emotion/react"; import type { EmotionCache } from "@emotion/react"; import { getThemeOverrides } from "../../../theme"; +import { DatesProvider } from "../DatesProvider"; interface ThemeProviderProps { children: ReactNode; @@ -14,7 +15,7 @@ export const ThemeProvider = withEmotionCache( return ( <MantineProvider theme={theme} emotionCache={cache}> - {children} + <DatesProvider>{children}</DatesProvider> </MantineProvider> ); }, diff --git a/frontend/test/jest-setup.js b/frontend/test/jest-setup.js index 97acb8023cea8e2ceaf044f4347eebcee7af1e42..d84cc236b7427c206ba8d43e3c9d36f359ab40ca 100644 --- a/frontend/test/jest-setup.js +++ b/frontend/test/jest-setup.js @@ -3,6 +3,7 @@ import "cross-fetch/polyfill"; import "raf/polyfill"; import "jest-localstorage-mock"; import "jest-canvas-mock"; +import "metabase/lib/dayjs"; import "__support__/mocks"; // NOTE: this is needed because sometimes asynchronous code tries to access diff --git a/package.json b/package.json index 4fd560e0f5e014f25c92dc18bdaef7339dd9bd1f..5fa8f24cf11a73f5eac65afb33236ab577f90692 100644 --- a/package.json +++ b/package.json @@ -49,6 +49,7 @@ "d3-array": "^3.1.1", "d3-scale": "^3.3.0", "d3-shape": "^3.1.0", + "dayjs": "^1.10.4", "dc": "2.1.9", "diff": "^3.2.0", "formik": "^2.2.9", @@ -244,8 +245,8 @@ "jest-environment-jsdom": "^29.5.0", "jest-localstorage-mock": "^2.4.22", "jest-watch-typeahead": "^2.2.1", - "jsonwebtoken": "^9.0.0", "json-to-pretty-yaml": "^1.2.2", + "jsonwebtoken": "^9.0.0", "knex": "^2.4.2", "lint-staged": "^13.1.2", "markdown-link-check": "^3.10.2",