From 104831891e5f065b2cc71e0042df9e4476339d98 Mon Sep 17 00:00:00 2001 From: Leonardo Lima Date: Mon, 22 Jan 2024 12:45:20 +0000 Subject: [PATCH] feat: add language selection on settings page --- src/components/ChooseLanguage.tsx | 99 +++++++++++++++++++++++++++++++ src/components/index.ts | 1 + src/i18n/config.ts | 6 +- src/i18n/en/translations.ts | 10 ++++ src/router.tsx | 2 + src/routes/settings/Language.tsx | 35 +++++++++++ src/routes/settings/Root.tsx | 5 ++ src/routes/settings/index.ts | 1 + src/state/megaStore.tsx | 7 +++ src/utils/index.ts | 1 + src/utils/languages.ts | 20 +++++++ 11 files changed, 185 insertions(+), 2 deletions(-) create mode 100644 src/components/ChooseLanguage.tsx create mode 100644 src/routes/settings/Language.tsx create mode 100644 src/utils/languages.ts diff --git a/src/components/ChooseLanguage.tsx b/src/components/ChooseLanguage.tsx new file mode 100644 index 00000000..302f0de8 --- /dev/null +++ b/src/components/ChooseLanguage.tsx @@ -0,0 +1,99 @@ +import { createForm } from "@modular-forms/solid"; +import { useNavigate } from "@solidjs/router"; +import { createSignal, For, Show } from "solid-js"; + +import { Button, ExternalLink, InfoBox, NiceP, VStack } from "~/components"; +import { useI18n } from "~/i18n/context"; +import { useMegaStore } from "~/state/megaStore"; +import { eify, EN_OPTION, Language, LANGUAGE_OPTIONS, timeout } from "~/utils"; + +type ChooseLanguageForm = { + selectedLanguage: string; +}; + +const COMBINED_OPTIONS: Language[] = [EN_OPTION, ...LANGUAGE_OPTIONS]; + +export function ChooseLanguage() { + const i18n = useI18n(); + const [error, setError] = createSignal(); + const [state, actions] = useMegaStore(); + const [loading, setLoading] = createSignal(false); + const navigate = useNavigate(); + + function findLanguageByValue(value: string) { + return ( + COMBINED_OPTIONS.find((language) => language.value === value) ?? + EN_OPTION + ); + } + + const [_chooseLanguageForm, { Form, Field }] = + createForm({ + initialValues: { + selectedLanguage: findLanguageByValue(state.lang ?? "").value, + }, + validate: (values) => { + const errors: Record = {}; + if (values.selectedLanguage === undefined) { + errors.selectedLanguage = i18n.t( + "settings.language.error_unsupported_language" + ); + } + return errors; + } + }); + + const handleFormSubmit = async (f: ChooseLanguageForm) => { + setLoading(true); + try { + actions.saveLanguage(findLanguageByValue(f.selectedLanguage).label); + + await i18n.changeLanguage( + findLanguageByValue(f.selectedLanguage).label + ); + + await timeout(1000); + navigate("/"); + } catch (e) { + console.error(e); + setError(eify(e)); + setLoading(false); + } + }; + + return ( + +
+ {i18n.t("settings.language.caption")} + + {i18n.t("settings.language.request_language_support_link")} + +
+ + + {(field, props) => ( + + )} + + + {error()?.message} + +
+ + + + + ); +} diff --git a/src/components/index.ts b/src/components/index.ts index 241728ba..ec104643 100644 --- a/src/components/index.ts +++ b/src/components/index.ts @@ -9,6 +9,7 @@ export * from "./AmountEditable"; export * from "./BalanceBox"; export * from "./BetaWarningModal"; export * from "./ChooseCurrency"; +export * from "./ChooseLanguage"; export * from "./ContactEditor"; export * from "./ContactForm"; export * from "./ContactViewer"; diff --git a/src/i18n/config.ts b/src/i18n/config.ts index 41859469..1897837f 100644 --- a/src/i18n/config.ts +++ b/src/i18n/config.ts @@ -32,8 +32,10 @@ const i18n = use(LanguageDetector).init( fallbackNS: false, debug: true, detection: { - order: ["querystring", "navigator", "htmlTag"], - lookupQuerystring: "lang" + order: ["localStorage", "querystring", "navigator", "htmlTag"], + lookupQuerystring: "lang", + lookupLocalStorage: "i18nextLng", + caches: ["localStorage"] }, resources: resources // FIXME: this doesn't work when deployed diff --git a/src/i18n/en/translations.ts b/src/i18n/en/translations.ts index 61786379..bfb08640 100644 --- a/src/i18n/en/translations.ts +++ b/src/i18n/en/translations.ts @@ -425,6 +425,16 @@ export default { "Request support for more currencies", error_unsupported_currency: "Please Select a supported currency." }, + language: { + title: "Language", + caption: "Choose your preferred language", + select_language: "Select Language", + select_language_label: "Language", + select_language_caption: + "Choosing a new currency will change the wallet language, ignoring current browser language", + request_language_support_link: "Request support for more languages", + error_unsupported_language: "Please Select a supported language." + }, lnurl_auth: { title: "LNURL Auth", auth: "Auth", diff --git a/src/router.tsx b/src/router.tsx index 8eea78a7..6f4d2ebc 100644 --- a/src/router.tsx +++ b/src/router.tsx @@ -26,6 +26,7 @@ import { EmergencyKit, Encrypt, Gift, + Language, ManageFederations, Plus, Restore, @@ -109,6 +110,7 @@ export function Router() { + diff --git a/src/routes/settings/Language.tsx b/src/routes/settings/Language.tsx new file mode 100644 index 00000000..136ff736 --- /dev/null +++ b/src/routes/settings/Language.tsx @@ -0,0 +1,35 @@ +import { + BackLink, + Card, + ChooseLanguage, + DefaultMain, + LargeHeader, + MutinyWalletGuard, + NavBar, + SafeArea +} from "~/components"; +import { useI18n } from "~/i18n/context"; + +export function Language() { + const i18n = useI18n(); + return ( + + + + + + {i18n.t("settings.language.title")} + + + + +
+ + + + + ); +} diff --git a/src/routes/settings/Root.tsx b/src/routes/settings/Root.tsx index ee141774..d7e5e74c 100644 --- a/src/routes/settings/Root.tsx +++ b/src/routes/settings/Root.tsx @@ -118,6 +118,11 @@ export function Settings() { text: i18n.t("settings.currency.title"), caption: i18n.t("settings.currency.caption") }, + { + href: "/settings/language", + text: i18n.t("settings.language.title"), + caption: i18n.t("settings.language.caption") + }, { href: "/settings/servers", text: i18n.t("settings.servers.title"), diff --git a/src/routes/settings/index.ts b/src/routes/settings/index.ts index 0cf20685..3de23b8b 100644 --- a/src/routes/settings/index.ts +++ b/src/routes/settings/index.ts @@ -4,6 +4,7 @@ export * from "./Backup"; export * from "./Channels"; export * from "./Connections"; export * from "./Currency"; +export * from "./Language"; export * from "./EmergencyKit"; export * from "./Encrypt"; export * from "./Gift"; diff --git a/src/state/megaStore.tsx b/src/state/megaStore.tsx index f4f11e7a..a058320b 100644 --- a/src/state/megaStore.tsx +++ b/src/state/megaStore.tsx @@ -54,6 +54,7 @@ type MegaStore = [ price_sync_backoff_multiple?: number; price: number; fiat: Currency; + lang?: string; has_backed_up: boolean; wallet_loading: boolean; setup_error?: Error; @@ -83,6 +84,7 @@ type MegaStore = [ checkForSubscription(justPaid?: boolean): Promise; fetchPrice(fiat: Currency): Promise; saveFiat(fiat: Currency): void; + saveLanguage(lang: string): void; saveNpub(npub: string): void; setPreferredInvoiceType( type: "unified" | "lightning" | "onchain" @@ -132,6 +134,7 @@ export const Provider: ParentComponent = (props) => { load_stage: "fresh" as LoadStage, settings: undefined as MutinyWalletSettingStrings | undefined, safe_mode: searchParams.safe_mode === "true", + lang: localStorage.getItem("lang") || undefined, npub: localStorage.getItem("npub") || undefined, preferredInvoiceType: "unified" as "unified" | "lightning" | "onchain", betaWarned: localStorage.getItem("betaWarned") === "true", @@ -332,6 +335,10 @@ export const Provider: ParentComponent = (props) => { fiat: fiat }); }, + saveLanguage(lang: string) { + localStorage.setItem("i18nextLng", lang); + setState({ lang }); + }, saveNpub(npub: string) { localStorage.setItem("npub", npub); setState({ npub }); diff --git a/src/utils/index.ts b/src/utils/index.ts index 754f0922..cf118bb3 100644 --- a/src/utils/index.ts +++ b/src/utils/index.ts @@ -16,6 +16,7 @@ export * from "./vibrate"; export * from "./openLinkProgrammatically"; export * from "./nostr"; export * from "./currencies"; +export * from "./languages"; export * from "./bech32"; export * from "./keypad"; export * from "./debounce"; diff --git a/src/utils/languages.ts b/src/utils/languages.ts new file mode 100644 index 00000000..8a08c852 --- /dev/null +++ b/src/utils/languages.ts @@ -0,0 +1,20 @@ +export interface Language { + value: string; + label: string; +} + +export const EN_OPTION: Language = { + value: "English", + label: "en" +}; + +export const LANGUAGE_OPTIONS: Language[] = [ + { + value: "Português", + label: "pt" + }, + { + value: "Korean", + label: "ko" + } +];