add override locale selector for dates and currencies (#648)

* feat: add override locale selector

* fix: dateExample computed property to ensure Vue updates with locale changes in LanguageSelector.vue
This commit is contained in:
Tonya
2025-05-04 17:11:28 +00:00
committed by GitHub
parent 16bcffac45
commit 89aaa8c595
5 changed files with 59 additions and 11 deletions

View File

@@ -1,31 +1,51 @@
<script setup lang="ts"> <script setup lang="ts">
import { computed } from "vue"; import { computed } from "vue";
import * as Locales from "date-fns/locale";
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select"; import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select";
import { Label } from "@/components/ui/label"; import { Label } from "@/components/ui/label";
import { fmtDate } from "~~/composables/use-formatters"; import { fmtDate } from "~~/composables/use-formatters";
import { useViewPreferences } from "~~/composables/use-preferences"; import { useViewPreferences } from "~~/composables/use-preferences";
const preferences = useViewPreferences();
const locales = Object.values(Locales).map(l => {
return {
code: l.code,
name: new Intl.DisplayNames([preferences.value.language ?? "en-US"], { type: "language" }).of(l.code) ?? l.code,
localName: new Intl.DisplayNames([l.code], { type: "language" }).of(l.code) ?? l.code,
};
});
defineProps({ defineProps({
includeText: { expanded: {
type: Boolean, type: Boolean,
default: true, default: true,
}, },
}); });
const preferences = useViewPreferences();
function setLanguage(lang: string) { function setLanguage(lang: string) {
preferences.value.language = lang; preferences.value.language = lang;
} }
function setOverrideLocale(locale: string | undefined) {
if (locale === undefined || locale === preferences.value.overrideFormatLocale) {
preferences.value.overrideFormatLocale = undefined;
} else {
preferences.value.overrideFormatLocale = locale;
}
}
const dateExample = computed(() => { const dateExample = computed(() => {
// hack to force vue to update
// eslint-disable-next-line @typescript-eslint/no-unused-vars
const locale = preferences.value.overrideFormatLocale;
return fmtDate(new Date(Date.now() - 15 * 60000), "relative"); return fmtDate(new Date(Date.now() - 15 * 60000), "relative");
}); });
</script> </script>
<template> <template>
<div class="w-full" :class="{ 'p-5 pt-0': includeText }"> <div class="w-full" :class="{ 'p-5 pt-0': expanded }">
<Label v-if="includeText" for="language"> {{ $t("profile.language") }} </Label> <Label v-if="expanded" for="language"> {{ $t("profile.language") }} </Label>
<Select <Select
id="language" id="language"
v-model="$i18n.locale" v-model="$i18n.locale"
@@ -44,8 +64,27 @@
</SelectItem> </SelectItem>
</SelectContent> </SelectContent>
</Select> </Select>
<p v-if="includeText" class="m-2 text-sm"> <template v-if="expanded">
{{ $t("profile.example") }}: {{ $t("global.created") }} {{ dateExample }} <Label for="overrideLocale"> {{ $t("profile.override_locale") }} </Label>
</p> <Select
id="overrideLocale"
:model-value="preferences.overrideFormatLocale"
@update:model-value="
val => {
setOverrideLocale(val?.toString());
}
"
>
<SelectTrigger>
<SelectValue :placeholder="$t('profile.no_override')" />
</SelectTrigger>
<SelectContent>
<SelectItem v-for="locale in locales" :key="locale.code" :value="locale.code">
{{ locale.name }} - ({{ locale.localName }})
</SelectItem>
</SelectContent>
</Select>
<p class="m-2 text-sm">{{ $t("profile.example") }}: {{ $t("global.created") }} {{ dateExample }}</p>
</template>
</div> </div>
</template> </template>

View File

@@ -29,6 +29,11 @@ export type DateTimeType = "date" | "time" | "datetime";
export function getLocaleCode() { export function getLocaleCode() {
const { $i18nGlobal } = useNuxtApp(); const { $i18nGlobal } = useNuxtApp();
const preferences = useViewPreferences();
// TODO: make reactive
if (preferences.value.overrideFormatLocale) {
return preferences.value.overrideFormatLocale;
}
return ($i18nGlobal?.locale?.value as string) ?? "en-US"; return ($i18nGlobal?.locale?.value as string) ?? "en-US";
} }

View File

@@ -14,6 +14,7 @@ export type LocationViewPreferences = {
tableHeaders?: TableHeaderType[]; tableHeaders?: TableHeaderType[];
displayHeaderDecor: boolean; displayHeaderDecor: boolean;
language?: string; language?: string;
overrideFormatLocale?: string;
}; };
/** /**
@@ -32,6 +33,7 @@ export function useViewPreferences(): Ref<LocationViewPreferences> {
itemsPerTablePage: 10, itemsPerTablePage: 10,
displayHeaderDecor: true, displayHeaderDecor: true,
language: null, language: null,
overrideFormatLocale: null,
}, },
{ mergeDefaults: true } { mergeDefaults: true }
); );

View File

@@ -279,9 +279,9 @@
"zh-CN": "Chinese (Simplified)", "zh-CN": "Chinese (Simplified)",
"zh-HK": "Chinese (Hong Kong)", "zh-HK": "Chinese (Hong Kong)",
"zh-MO": "Chinese (Macau)", "zh-MO": "Chinese (Macau)",
"zh-TW": "Chinese (Traditional)" "zh-TW": "Chinese (Traditional)",
"sq-AL": "Albanian"
}, },
"languages.sq-AL": "Albanian",
"locations": { "locations": {
"child_locations": "Child Locations", "child_locations": "Child Locations",
"collapse_tree": "Collapse Tree", "collapse_tree": "Collapse Tree",
@@ -351,6 +351,8 @@
"group_settings_sub": "Shared Group Settings. You may need to refresh your browser for some settings to apply.", "group_settings_sub": "Shared Group Settings. You may need to refresh your browser for some settings to apply.",
"inactive": "Inactive", "inactive": "Inactive",
"language": "Language", "language": "Language",
"override_locale": "Override Date and Currency Language",
"no_override": "No override",
"new_password": "New Password", "new_password": "New Password",
"no_notifiers": "No notifiers configured", "no_notifiers": "No notifiers configured",
"notifier_modal": "{ type, select, true {Edit} false {Create} other {Other}} Notifier", "notifier_modal": "{ type, select, true {Edit} false {Create} other {Other}} Notifier",

View File

@@ -202,7 +202,7 @@
<TooltipContent>{{ $t("global.read_docs") }}</TooltipContent> <TooltipContent>{{ $t("global.read_docs") }}</TooltipContent>
</Tooltip> </Tooltip>
<LanguageSelector class="z-10 text-primary" :include-text="false" /> <LanguageSelector class="z-10 text-primary" :expanded="false" />
</div> </div>
</TooltipProvider> </TooltipProvider>
</header> </header>