From bd30726ee3844db4c9d2508de2199d692bf4dd54 Mon Sep 17 00:00:00 2001 From: Nicolas Pennec Date: Mon, 1 Jun 2026 17:48:41 +0200 Subject: [PATCH 1/7] [i18n] Make the active formatting locale reactive Expose a reactive localeCode (a valid Intl/moment code such as 'en' or 'zh-tw') updated by setLocale, so consumers re-localize when the language changes. --- src/lib/lang.js | 17 +++++++++++------ tests/unit/lib/lang.spec.js | 14 +++++++++++++- 2 files changed, 24 insertions(+), 7 deletions(-) diff --git a/src/lib/lang.js b/src/lib/lang.js index 28d6df33fb..66ab59b6c6 100644 --- a/src/lib/lang.js +++ b/src/lib/lang.js @@ -1,11 +1,16 @@ import moment from 'moment-timezone' +import { ref } from 'vue' + import i18n from '@/lib/i18n' const LOCALE_MAP = { - zh_Hans_CN: { language: 'zh', localeCode: 'zh-cn' }, - zh_Hant_TW: { language: 'zh_tw', localeCode: 'zh-tw' } + zh_Hans_CN: { language: 'zh', code: 'zh-cn' }, + zh_Hant_TW: { language: 'zh_tw', code: 'zh-tw' } } +// Reactive, Intl-safe formatting code for the active language ('en', 'zh-tw', …). +export const localeCode = ref('en') + export default { /** * Set the locale for the application (vue-i18n + moment.js) @@ -13,14 +18,14 @@ export default { */ setLocale(locale) { const fallback = locale?.substring(0, 2) || 'en' - const { language, localeCode } = LOCALE_MAP[locale] || { + const { language, code } = LOCALE_MAP[locale] || { language: fallback, - localeCode: fallback + code: fallback } - moment.locale(localeCode) + moment.locale(code) i18n.global.locale = language - i18n.global.locale_code = localeCode + localeCode.value = code } } diff --git a/tests/unit/lib/lang.spec.js b/tests/unit/lib/lang.spec.js index 728f15a9a4..420c818b31 100644 --- a/tests/unit/lib/lang.spec.js +++ b/tests/unit/lib/lang.spec.js @@ -1,5 +1,5 @@ import moment from 'moment-timezone' -import lang from '@/lib/lang' +import lang, { localeCode } from '@/lib/lang' import timezone from '@/lib/timezone' import i18n from '@/lib/i18n' @@ -27,6 +27,18 @@ describe('lang', () => { expect(moment.locale()).toEqual('fr') expect(i18n.global.locale).toEqual('fr') }) + + test('localeCode tracks the active language with an Intl-safe code', () => { + lang.setLocale('en_US') + expect(localeCode.value).toEqual('en') + + // Mapped locale keeps its region (Traditional Chinese). + lang.setLocale('zh_Hant_TW') + expect(localeCode.value).toEqual('zh-tw') + + lang.setLocale('fr_FR') + expect(localeCode.value).toEqual('fr') + }) }) describe('timezone', () => { From 4dc784b437068eff93ec974dc794c132e011fb9d Mon Sep 17 00:00:00 2001 From: Nicolas Pennec Date: Mon, 1 Jun 2026 17:49:12 +0200 Subject: [PATCH 2/7] [calendar] Localize the user calendar from the active language Drive FullCalendar's locale from the reactive localeCode and refresh it on language change, instead of being hardcoded to English. --- src/components/widgets/UserCalendar.vue | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/src/components/widgets/UserCalendar.vue b/src/components/widgets/UserCalendar.vue index e8169ec3e5..aa8ee5bf73 100644 --- a/src/components/widgets/UserCalendar.vue +++ b/src/components/widgets/UserCalendar.vue @@ -72,6 +72,8 @@ import allLocales from '@fullcalendar/core/locales-all' import dayGridPlugin from '@fullcalendar/daygrid' import multiMonthPlugin from '@fullcalendar/multimonth' +import { localeCode } from '@/lib/lang' + import EntityThumbnail from '@/components/widgets/EntityThumbnail.vue' import ProductionName from '@/components/widgets/ProductionName.vue' import Spinner from '@/components/widgets/Spinner.vue' @@ -113,7 +115,7 @@ const calendarOptions = ref({ initialView: 'dayGridMonth', firstDay: 1, locales: allLocales, - locale: 'en' + locale: localeCode.value }) const resetEvents = () => { @@ -231,6 +233,11 @@ watch( resetEvents() } ) + +watch(localeCode, code => { + calendarOptions.value.locale = code + calendarRef.value?.getApi().setOption('locale', code) +})