mirror of
https://github.com/aljazceru/mutiny-web.git
synced 2025-12-20 15:54:22 +01:00
feat: add language selection on settings page
This commit is contained in:
committed by
benthecarman
parent
8e04901e18
commit
075c066fad
95
src/components/ChooseLanguage.tsx
Normal file
95
src/components/ChooseLanguage.tsx
Normal file
@@ -0,0 +1,95 @@
|
||||
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<Error>();
|
||||
const [state, actions] = useMegaStore();
|
||||
const [loading, setLoading] = createSignal(false);
|
||||
const navigate = useNavigate();
|
||||
|
||||
const [_chooseLanguageForm, { Form, Field }] =
|
||||
createForm<ChooseLanguageForm>({
|
||||
initialValues: {
|
||||
selectedLanguage: state.lang ?? ""
|
||||
},
|
||||
validate: (values) => {
|
||||
const errors: Record<string, string> = {};
|
||||
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(f.selectedLanguage);
|
||||
|
||||
await i18n.changeLanguage(f.selectedLanguage);
|
||||
|
||||
await timeout(1000);
|
||||
navigate("/");
|
||||
} catch (e) {
|
||||
console.error(e);
|
||||
setError(eify(e));
|
||||
setLoading(false);
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<VStack>
|
||||
<Form onSubmit={handleFormSubmit} class="flex flex-col gap-4">
|
||||
<NiceP>{i18n.t("settings.language.caption")}</NiceP>
|
||||
<ExternalLink href="https://github.com/MutinyWallet/mutiny-web/issues/new">
|
||||
{i18n.t("settings.language.request_language_support_link")}
|
||||
</ExternalLink>
|
||||
<div />
|
||||
<VStack>
|
||||
<Field name="selectedLanguage">
|
||||
{(field, props) => (
|
||||
<select
|
||||
{...props}
|
||||
value={field.value}
|
||||
class="w-full rounded-lg bg-m-grey-750 py-2 pl-4 pr-12 text-base font-normal text-white"
|
||||
>
|
||||
<For each={COMBINED_OPTIONS}>
|
||||
{({ value, shortName }) => (
|
||||
<option
|
||||
selected={field.value === shortName}
|
||||
value={shortName}
|
||||
>
|
||||
{value}
|
||||
</option>
|
||||
)}
|
||||
</For>
|
||||
</select>
|
||||
)}
|
||||
</Field>
|
||||
<Show when={error()}>
|
||||
<InfoBox accent="red">{error()?.message}</InfoBox>
|
||||
</Show>
|
||||
<div />
|
||||
<Button intent="blue" loading={loading()}>
|
||||
{i18n.t("settings.language.select_language")}
|
||||
</Button>
|
||||
</VStack>
|
||||
</Form>
|
||||
</VStack>
|
||||
);
|
||||
}
|
||||
@@ -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";
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -432,6 +432,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",
|
||||
|
||||
@@ -27,6 +27,7 @@ import {
|
||||
EmergencyKit,
|
||||
Encrypt,
|
||||
Gift,
|
||||
Language,
|
||||
ManageFederations,
|
||||
Plus,
|
||||
Restore,
|
||||
@@ -111,6 +112,7 @@ export function Router() {
|
||||
<Route path="/channels" component={Channels} />
|
||||
<Route path="/connections" component={Connections} />
|
||||
<Route path="/currency" component={Currency} />
|
||||
<Route path="/language" component={Language} />
|
||||
<Route path="/emergencykit" component={EmergencyKit} />
|
||||
<Route path="/encrypt" component={Encrypt} />
|
||||
<Route path="/gift" component={Gift} />
|
||||
|
||||
35
src/routes/settings/Language.tsx
Normal file
35
src/routes/settings/Language.tsx
Normal file
@@ -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 (
|
||||
<MutinyWalletGuard>
|
||||
<SafeArea>
|
||||
<DefaultMain>
|
||||
<BackLink
|
||||
href="/settings"
|
||||
title={i18n.t("settings.header")}
|
||||
/>
|
||||
<LargeHeader>
|
||||
{i18n.t("settings.language.title")}
|
||||
</LargeHeader>
|
||||
<Card title={i18n.t("settings.language.select_language")}>
|
||||
<ChooseLanguage />
|
||||
</Card>
|
||||
<div class="h-full" />
|
||||
</DefaultMain>
|
||||
<NavBar activeTab="settings" />
|
||||
</SafeArea>
|
||||
</MutinyWalletGuard>
|
||||
);
|
||||
}
|
||||
@@ -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"),
|
||||
|
||||
@@ -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";
|
||||
|
||||
@@ -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<void>;
|
||||
fetchPrice(fiat: Currency): Promise<number>;
|
||||
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("i18nexLng") || 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 });
|
||||
|
||||
@@ -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";
|
||||
|
||||
20
src/utils/languages.ts
Normal file
20
src/utils/languages.ts
Normal file
@@ -0,0 +1,20 @@
|
||||
export interface Language {
|
||||
value: string;
|
||||
shortName: string;
|
||||
}
|
||||
|
||||
export const EN_OPTION: Language = {
|
||||
value: "English",
|
||||
shortName: "en"
|
||||
};
|
||||
|
||||
export const LANGUAGE_OPTIONS: Language[] = [
|
||||
{
|
||||
value: "Português",
|
||||
shortName: "pt"
|
||||
},
|
||||
{
|
||||
value: "Korean",
|
||||
shortName: "ko"
|
||||
}
|
||||
];
|
||||
Reference in New Issue
Block a user