From dd9d13b1759ad7c759c1b16d5717c2aa1f5f6e10 Mon Sep 17 00:00:00 2001 From: Elian Doran Date: Tue, 14 Oct 2025 18:42:20 +0300 Subject: [PATCH] refactor(options/i18n): share locale selector with content language selector --- .../src/widgets/ribbon/BasicPropertiesTab.tsx | 67 +++++-------------- .../options/components/LocaleSelector.tsx | 63 +++++++++++++++++ .../src/widgets/type_widgets/options/i18n.tsx | 30 ++------- 3 files changed, 83 insertions(+), 77 deletions(-) create mode 100644 apps/client/src/widgets/type_widgets/options/components/LocaleSelector.tsx diff --git a/apps/client/src/widgets/ribbon/BasicPropertiesTab.tsx b/apps/client/src/widgets/ribbon/BasicPropertiesTab.tsx index b7ef4fa0e..0202f46e4 100644 --- a/apps/client/src/widgets/ribbon/BasicPropertiesTab.tsx +++ b/apps/client/src/widgets/ribbon/BasicPropertiesTab.tsx @@ -5,7 +5,7 @@ import { FormDropdownDivider, FormListBadge, FormListItem } from "../react/FormL import { getAvailableLocales, t } from "../../services/i18n"; import { useNoteLabel, useNoteLabelBoolean, useNoteProperty, useTriliumEvent, useTriliumOption } from "../react/hooks"; import mime_types from "../../services/mime_types"; -import { Locale, NoteType, ToggleInParentResponse } from "@triliumnext/commons"; +import { Locale, LOCALES, NoteType, ToggleInParentResponse } from "@triliumnext/commons"; import server from "../../services/server"; import dialog from "../../services/dialog"; import FormToggle from "../react/FormToggle"; @@ -20,6 +20,7 @@ import { TabContext } from "./ribbon-interface"; import Modal from "../react/Modal"; import { CodeMimeTypesList } from "../type_widgets/options/code_notes"; import { ContentLanguagesList } from "../type_widgets/options/i18n"; +import { LocaleSelector } from "../type_widgets/options/components/LocaleSelector"; export default function BasicPropertiesTab({ note }: TabContext) { return ( @@ -290,68 +291,30 @@ function NoteLanguageSwitch({ note }: { note?: FNote | null }) { id: "", name: t("note_language.not_set") }; - const [ currentNoteLanguage, setCurrentNoteLanguage ] = useNoteLabel(note, "language"); const [ modalShown, setModalShown ] = useState(false); - const locales = useMemo(() => { const enabledLanguages = JSON.parse(languages ?? "[]") as string[]; const filteredLanguages = getAvailableLocales().filter((l) => typeof l !== "object" || enabledLanguages.includes(l.id)); - const leftToRightLanguages = filteredLanguages.filter((l) => !l.rtl); - const rightToLeftLanguages = filteredLanguages.filter((l) => l.rtl); - - let locales: ("---" | Locale)[] = [ - DEFAULT_LOCALE - ]; - - if (leftToRightLanguages.length > 0) { - locales = [ - ...locales, - "---", - ...leftToRightLanguages - ]; - } - - if (rightToLeftLanguages.length > 0) { - locales = [ - ...locales, - "---", - ...rightToLeftLanguages - ]; - } - - // This will separate the list of languages from the "Configure languages" button. - // If there is at least one language. - locales.push("---"); - return locales; + return filteredLanguages; }, [ languages ]); - const currentLocale = useMemo(() => { - return locales.find(locale => typeof locale === "object" && locale.id === currentNoteLanguage) as Locale | undefined; - }, [ currentNoteLanguage ]); - return (
{t("basic_properties.language")}:   - - {locales.map(locale => { - if (typeof locale === "object") { - const checked = locale.id === (currentNoteLanguage ?? ""); - return setCurrentNoteLanguage(locale.id || null)} - >{locale.name} - } else { - return - } - })} + setModalShown(true)} + >{t("note_language.configure-languages")} + )} + > - setModalShown(true)} - >{t("note_language.configure-languages")} - + @@ -364,7 +327,7 @@ function NoteLanguageSwitch({ note }: { note?: FNote | null }) {
- ) + ); } function findTypeTitle(type?: NoteType, mime?: string | null) { diff --git a/apps/client/src/widgets/type_widgets/options/components/LocaleSelector.tsx b/apps/client/src/widgets/type_widgets/options/components/LocaleSelector.tsx new file mode 100644 index 000000000..0161ae6a8 --- /dev/null +++ b/apps/client/src/widgets/type_widgets/options/components/LocaleSelector.tsx @@ -0,0 +1,63 @@ +import { Locale } from "@triliumnext/commons"; +import Dropdown from "../../../react/Dropdown"; +import { FormDropdownDivider, FormListItem } from "../../../react/FormList"; +import { ComponentChildren } from "preact"; +import { useMemo, useState } from "preact/hooks"; + +export function LocaleSelector({ id, locales, currentValue, onChange, defaultLocale, extraChildren }: { + id?: string; + locales: Locale[], + currentValue: string, + onChange: (newLocale: string) => void, + defaultLocale?: Locale, + extraChildren?: ComponentChildren +}) { + const [ activeLocale, setActiveLocale ] = useState(defaultLocale?.id === currentValue ? defaultLocale : locales.find(l => l.id === currentValue)); + console.log("defaultLocale ", defaultLocale, currentValue, activeLocale) + + const processedLocales = useMemo(() => { + const leftToRightLanguages = locales.filter((l) => !l.rtl); + const rightToLeftLanguages = locales.filter((l) => l.rtl); + + let items: ("---" | Locale)[] = []; + if (defaultLocale) items.push(defaultLocale); + + if (leftToRightLanguages.length > 0) { + if (items.length > 0) items.push("---"); + items = [ ...items, ...leftToRightLanguages ]; + } + + if (rightToLeftLanguages.length > 0) { + items = [ + ...items, + "---", + ...rightToLeftLanguages + ]; + } + + if (extraChildren) { + items.push("---"); + } + return items; + }, [ locales ]); + + return ( + + {processedLocales.map(locale => { + if (typeof locale === "object") { + return { + setActiveLocale(locale); + onChange(locale.id); + }} + >{locale.name} + } else { + return + } + })} + {extraChildren} + + ) +} diff --git a/apps/client/src/widgets/type_widgets/options/i18n.tsx b/apps/client/src/widgets/type_widgets/options/i18n.tsx index a1f9e63df..36a0a1100 100644 --- a/apps/client/src/widgets/type_widgets/options/i18n.tsx +++ b/apps/client/src/widgets/type_widgets/options/i18n.tsx @@ -1,20 +1,18 @@ -import { useMemo, useState } from "preact/hooks"; +import { useMemo } from "preact/hooks"; import { getAvailableLocales, t } from "../../../services/i18n"; import FormSelect from "../../react/FormSelect"; import OptionsRow from "./components/OptionsRow"; import OptionsSection from "./components/OptionsSection"; import { useTriliumOption, useTriliumOptionJson } from "../../react/hooks"; import type { Locale } from "@triliumnext/commons"; -import { isElectron, restartDesktopApp } from "../../../services/utils"; -import FormRadioGroup, { FormInlineRadioGroup } from "../../react/FormRadioGroup"; +import { restartDesktopApp } from "../../../services/utils"; +import FormRadioGroup from "../../react/FormRadioGroup"; import FormText from "../../react/FormText"; import RawHtml from "../../react/RawHtml"; import Admonition from "../../react/Admonition"; import Button from "../../react/Button"; import CheckboxList from "./components/CheckboxList"; -import FormDropdownList from "../../react/FormDropdownList"; -import Dropdown from "../../react/Dropdown"; -import { FormListItem } from "../../react/FormList"; +import { LocaleSelector } from "./components/LocaleSelector"; export default function InternationalizationOptions() { return ( @@ -35,7 +33,6 @@ function LocalizationOptions() { return true; }), formattingLocales: [ - { id: "", name: t("i18n.formatting-locale-auto") }, ...allLocales.filter(locale => locale.electronLocale) ] } @@ -51,7 +48,7 @@ function LocalizationOptions() { { - + } @@ -59,23 +56,6 @@ function LocalizationOptions() { ) } -function LocaleSelector({ id, locales, currentValue, onChange }: { id?: string; locales: Locale[], currentValue: string, onChange: (newLocale: string) => void }) { - const [ activeLocale, setActiveLocale ] = useState(locales.find(l => l.id === currentValue)); - return ( - - {locales.map(locale => ( - { - setActiveLocale(locale); - onChange(locale.id); - }} - >{locale.name} - ))} - - ) -} - function DateSettings() { const [ firstDayOfWeek, setFirstDayOfWeek ] = useTriliumOption("firstDayOfWeek"); const [ firstWeekOfYear, setFirstWeekOfYear ] = useTriliumOption("firstWeekOfYear");