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 "./BalanceBox";
|
||||||
export * from "./BetaWarningModal";
|
export * from "./BetaWarningModal";
|
||||||
export * from "./ChooseCurrency";
|
export * from "./ChooseCurrency";
|
||||||
|
export * from "./ChooseLanguage";
|
||||||
export * from "./ContactEditor";
|
export * from "./ContactEditor";
|
||||||
export * from "./ContactForm";
|
export * from "./ContactForm";
|
||||||
export * from "./ContactViewer";
|
export * from "./ContactViewer";
|
||||||
|
|||||||
@@ -32,8 +32,10 @@ const i18n = use(LanguageDetector).init(
|
|||||||
fallbackNS: false,
|
fallbackNS: false,
|
||||||
debug: true,
|
debug: true,
|
||||||
detection: {
|
detection: {
|
||||||
order: ["querystring", "navigator", "htmlTag"],
|
order: ["localStorage", "querystring", "navigator", "htmlTag"],
|
||||||
lookupQuerystring: "lang"
|
lookupQuerystring: "lang",
|
||||||
|
lookupLocalStorage: "i18nextLng",
|
||||||
|
caches: ["localStorage"]
|
||||||
},
|
},
|
||||||
resources: resources
|
resources: resources
|
||||||
// FIXME: this doesn't work when deployed
|
// FIXME: this doesn't work when deployed
|
||||||
|
|||||||
@@ -432,6 +432,16 @@ export default {
|
|||||||
"Request support for more currencies",
|
"Request support for more currencies",
|
||||||
error_unsupported_currency: "Please Select a supported currency."
|
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: {
|
lnurl_auth: {
|
||||||
title: "LNURL Auth",
|
title: "LNURL Auth",
|
||||||
auth: "Auth",
|
auth: "Auth",
|
||||||
|
|||||||
@@ -27,6 +27,7 @@ import {
|
|||||||
EmergencyKit,
|
EmergencyKit,
|
||||||
Encrypt,
|
Encrypt,
|
||||||
Gift,
|
Gift,
|
||||||
|
Language,
|
||||||
ManageFederations,
|
ManageFederations,
|
||||||
Plus,
|
Plus,
|
||||||
Restore,
|
Restore,
|
||||||
@@ -111,6 +112,7 @@ export function Router() {
|
|||||||
<Route path="/channels" component={Channels} />
|
<Route path="/channels" component={Channels} />
|
||||||
<Route path="/connections" component={Connections} />
|
<Route path="/connections" component={Connections} />
|
||||||
<Route path="/currency" component={Currency} />
|
<Route path="/currency" component={Currency} />
|
||||||
|
<Route path="/language" component={Language} />
|
||||||
<Route path="/emergencykit" component={EmergencyKit} />
|
<Route path="/emergencykit" component={EmergencyKit} />
|
||||||
<Route path="/encrypt" component={Encrypt} />
|
<Route path="/encrypt" component={Encrypt} />
|
||||||
<Route path="/gift" component={Gift} />
|
<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"),
|
text: i18n.t("settings.currency.title"),
|
||||||
caption: i18n.t("settings.currency.caption")
|
caption: i18n.t("settings.currency.caption")
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
href: "/settings/language",
|
||||||
|
text: i18n.t("settings.language.title"),
|
||||||
|
caption: i18n.t("settings.language.caption")
|
||||||
|
},
|
||||||
{
|
{
|
||||||
href: "/settings/servers",
|
href: "/settings/servers",
|
||||||
text: i18n.t("settings.servers.title"),
|
text: i18n.t("settings.servers.title"),
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ export * from "./Backup";
|
|||||||
export * from "./Channels";
|
export * from "./Channels";
|
||||||
export * from "./Connections";
|
export * from "./Connections";
|
||||||
export * from "./Currency";
|
export * from "./Currency";
|
||||||
|
export * from "./Language";
|
||||||
export * from "./EmergencyKit";
|
export * from "./EmergencyKit";
|
||||||
export * from "./Encrypt";
|
export * from "./Encrypt";
|
||||||
export * from "./Gift";
|
export * from "./Gift";
|
||||||
|
|||||||
@@ -54,6 +54,7 @@ type MegaStore = [
|
|||||||
price_sync_backoff_multiple?: number;
|
price_sync_backoff_multiple?: number;
|
||||||
price: number;
|
price: number;
|
||||||
fiat: Currency;
|
fiat: Currency;
|
||||||
|
lang?: string;
|
||||||
has_backed_up: boolean;
|
has_backed_up: boolean;
|
||||||
wallet_loading: boolean;
|
wallet_loading: boolean;
|
||||||
setup_error?: Error;
|
setup_error?: Error;
|
||||||
@@ -83,6 +84,7 @@ type MegaStore = [
|
|||||||
checkForSubscription(justPaid?: boolean): Promise<void>;
|
checkForSubscription(justPaid?: boolean): Promise<void>;
|
||||||
fetchPrice(fiat: Currency): Promise<number>;
|
fetchPrice(fiat: Currency): Promise<number>;
|
||||||
saveFiat(fiat: Currency): void;
|
saveFiat(fiat: Currency): void;
|
||||||
|
saveLanguage(lang: string): void;
|
||||||
saveNpub(npub: string): void;
|
saveNpub(npub: string): void;
|
||||||
setPreferredInvoiceType(
|
setPreferredInvoiceType(
|
||||||
type: "unified" | "lightning" | "onchain"
|
type: "unified" | "lightning" | "onchain"
|
||||||
@@ -132,6 +134,7 @@ export const Provider: ParentComponent = (props) => {
|
|||||||
load_stage: "fresh" as LoadStage,
|
load_stage: "fresh" as LoadStage,
|
||||||
settings: undefined as MutinyWalletSettingStrings | undefined,
|
settings: undefined as MutinyWalletSettingStrings | undefined,
|
||||||
safe_mode: searchParams.safe_mode === "true",
|
safe_mode: searchParams.safe_mode === "true",
|
||||||
|
lang: localStorage.getItem("i18nexLng") || undefined,
|
||||||
npub: localStorage.getItem("npub") || undefined,
|
npub: localStorage.getItem("npub") || undefined,
|
||||||
preferredInvoiceType: "unified" as "unified" | "lightning" | "onchain",
|
preferredInvoiceType: "unified" as "unified" | "lightning" | "onchain",
|
||||||
betaWarned: localStorage.getItem("betaWarned") === "true",
|
betaWarned: localStorage.getItem("betaWarned") === "true",
|
||||||
@@ -332,6 +335,10 @@ export const Provider: ParentComponent = (props) => {
|
|||||||
fiat: fiat
|
fiat: fiat
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
saveLanguage(lang: string) {
|
||||||
|
localStorage.setItem("i18nextLng", lang);
|
||||||
|
setState({ lang });
|
||||||
|
},
|
||||||
saveNpub(npub: string) {
|
saveNpub(npub: string) {
|
||||||
localStorage.setItem("npub", npub);
|
localStorage.setItem("npub", npub);
|
||||||
setState({ npub });
|
setState({ npub });
|
||||||
|
|||||||
@@ -16,6 +16,7 @@ export * from "./vibrate";
|
|||||||
export * from "./openLinkProgrammatically";
|
export * from "./openLinkProgrammatically";
|
||||||
export * from "./nostr";
|
export * from "./nostr";
|
||||||
export * from "./currencies";
|
export * from "./currencies";
|
||||||
|
export * from "./languages";
|
||||||
export * from "./bech32";
|
export * from "./bech32";
|
||||||
export * from "./keypad";
|
export * from "./keypad";
|
||||||
export * from "./debounce";
|
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