- Receive some sats to get started
+ {i18n.t("receive_some_sats_to_get_started")}
= (props) => {
+ const i18n = useI18n();
const navigate = useNavigate();
const [isOpen, setIsOpen] = createSignal(props.initialOpen);
const [state, _actions] = useMegaStore();
@@ -236,7 +238,7 @@ export const AmountEditable: ParentComponent<{
if (network === "bitcoin") {
return "Your first lightning receive needs to be 50,000 sats or greater. A setup fee will be deducted from the requested amount.";
} else {
- return "Your first lightning receive needs to be 10,000 sats or greater. A setup fee will be deducted from the requested amount.";
+ return i18n.t("amount_editable_first_payment_10k_or_greater");
}
}
@@ -399,7 +401,7 @@ export const AmountEditable: ParentComponent<{
Set amount
+
{i18n.t("set_amount")}
}
>
@@ -526,7 +528,7 @@ export const AmountEditable: ParentComponent<{
class="w-full flex-none"
onClick={handleSubmit}
>
- Set Amount
+ {i18n.t("set_amount")}
diff --git a/src/components/App.tsx b/src/components/App.tsx
index f649034..97ae5f8 100644
--- a/src/components/App.tsx
+++ b/src/components/App.tsx
@@ -12,9 +12,11 @@ import { BetaWarningModal } from "~/components/BetaWarningModal";
import settings from "~/assets/icons/settings.svg";
import pixelLogo from "~/assets/mutiny-pixel-logo.png";
import { PendingNwc } from "./PendingNwc";
+import { useI18n } from "~/i18n/context";
export default function App() {
const [state, _actions] = useMegaStore();
+ const i18n = useI18n();
return (
@@ -68,7 +70,7 @@ export default function App() {
href="/activity"
class="text-m-red active:text-m-red/80 font-semibold no-underline self-center"
>
- View All
+ {i18n.t("view_all")}
@@ -76,7 +78,7 @@ export default function App() {
Bugs? Feedback?{" "}
- Create an issue
+ {i18n.t("create_an_issue")}
- Mutiny is a self-custodial wallet. To initiate a lightning
- payment we must open a lightning channel, which requires a
- minimum amount and a setup fee.
+ {i18n.t("more_info_modal_p1")}
- Future payments, both send and recieve, will only incur normal
- network fees and a nominal service fee unless your channel runs
- out of inbound capacity.
+ {i18n.t("more_info_modal_p2")}
- Learn more about liquidity
+ {i18n.t("learn_more_about_liquidity")}
diff --git a/src/components/layout/index.tsx b/src/components/layout/index.tsx
index 981c323..9d22f23 100644
--- a/src/components/layout/index.tsx
+++ b/src/components/layout/index.tsx
@@ -30,7 +30,7 @@ export const SmallHeader: ParentComponent<{ class?: string }> = (props) => {
};
export const Card: ParentComponent<{
- title?: string;
+ title?: string | null;
titleElement?: JSX.Element;
}> = (props) => {
return (
diff --git a/src/i18n/config.ts b/src/i18n/config.ts
new file mode 100644
index 0000000..9221378
--- /dev/null
+++ b/src/i18n/config.ts
@@ -0,0 +1,26 @@
+import i18next from 'i18next';
+import HttpApi from 'i18next-http-backend';
+import LanguageDetector from 'i18next-browser-languagedetector';
+const i18n = i18next
+ .use(HttpApi)
+ .use(LanguageDetector)
+ .init({
+ fallbackLng: 'en',
+ preload: ['en'],
+ load: 'languageOnly',
+ ns: ['translations'],
+ defaultNS: 'translations',
+ fallbackNS: false,
+ debug: true,
+ detection: {
+ order: ['querystring', 'navigator', 'htmlTag'],
+ lookupQuerystring: 'lang',
+ },
+ backend: {
+ loadPath: 'src/i18n/{{lng}}/{{ns}}.json',
+ }
+ }, (err, t) => {
+ // Do we actually wanna log something in case of an unsupported language?
+ if (err) return console.error(err)
+ });
+export default i18n;
diff --git a/src/i18n/context.ts b/src/i18n/context.ts
new file mode 100644
index 0000000..f992da2
--- /dev/null
+++ b/src/i18n/context.ts
@@ -0,0 +1,12 @@
+import { createContext, useContext } from 'solid-js';
+import { i18n } from "i18next";
+
+export const I18nContext = createContext();
+
+export function useI18n() {
+ const context = useContext(I18nContext);
+
+ if (!context) throw new ReferenceError('I18nContext');
+
+ return context;
+}
diff --git a/src/i18n/en/translations.json b/src/i18n/en/translations.json
new file mode 100644
index 0000000..56284d7
--- /dev/null
+++ b/src/i18n/en/translations.json
@@ -0,0 +1,19 @@
+{
+ "create_an_issue": "Create an issue",
+ "view_all": "View all",
+ "receive_some_sats_to_get_started": "Receive some sats to get started",
+ "send_bitcoin": "Send Bitcoin",
+ "view_transaction": "View Transaction",
+ "amount_editable_first_payment_10k_or_greater": "Your first lightning receive needs to be 10,000 sats or greater. A setup fee will be deducted from the requested amount.",
+ "why?": "Why?",
+ "more_info_modal_p1": "Mutiny is a self-custodial wallet. To initiate a lightning payment we must open a lightning channel, which requires a minimum amount and a setup fee.",
+ "more_info_modal_p2": "Future payments, both send and recieve, will only incur normal network fees and a nominal service fee unless your channel runs out of inbound capacity.",
+ "learn_more_about_liquidity": "Learn more about liquidity",
+ "set_amount": "Set amount",
+ "whats_with_the_fees": "What's with the fees?",
+ "private_tags": "Private tags",
+ "receive_add_the_sender": "Add the sender for your records",
+ "continue": "Continue",
+ "receive_bitcoin": "Receive Bitcoin",
+ "keep_mutiny_open": "Keep Mutiny open to complete the payment."
+}
diff --git a/src/i18n/pt/translations.json b/src/i18n/pt/translations.json
new file mode 100644
index 0000000..6133f1a
--- /dev/null
+++ b/src/i18n/pt/translations.json
@@ -0,0 +1,18 @@
+{
+ "create_an_issue": "Crie uma issue",
+ "view_all": "Ver todas",
+ "receive_some_sats_to_get_started": "Receba alguns satoshis para começar",
+ "send_bitcoin": "Enviar Bitcoin",
+ "view_transaction": "Ver transação",
+ "amount_editable_first_payment_10k_or_greater": "Seu primeiro recebimento na lightning precisa ser de pelo menos 10.000 sats. Uma taxa de configuração será deduzida da quantidade requisitada.",
+ "why?": "Por que?",
+ "more_info_modal_p1": "Mutiny é uma carteira de auto custódia. Para iniciar um pagamento na lightning, nós precisamos abrir um canal que requer uma quantidade mínima e uma taxa de configuração.",
+ "more_info_modal_p2": "Transaçoēs futuras, como envios e recebimentos, terão somente taxas normais da rede e uma taxa de serviço nominal a não ser que seu canal fique sem capacidade de entrada.",
+ "learn_more_about_liquidity": "Aprenda mais sobre liquidez",
+ "set_amount": "Definir quantidade",
+ "whats_with_the_fees": "O que há com as taxas?",
+ "private_tags": "Tags privadas",
+ "receive_add_the_sender": "Marque quem o enviou para registro próprio",
+ "continue": "Continuar",
+ "receive_bitcoin": "Receber Bitcoin"
+}
diff --git a/src/root.tsx b/src/root.tsx
index 81a1aa9..568abf3 100644
--- a/src/root.tsx
+++ b/src/root.tsx
@@ -16,6 +16,7 @@ import "./root.css";
import { Provider as MegaStoreProvider } from "~/state/megaStore";
import { Toaster } from "~/components/Toaster";
import ErrorDisplay from "./components/ErrorDisplay";
+import { I18nProvider } from "./components/I18nProvider";
export default function Root() {
return (
@@ -76,12 +77,14 @@ export default function Root() {
}>
-
-
-
-
-
-
+
+
+
+
+
+
+
+
diff --git a/src/routes/Receive.tsx b/src/routes/Receive.tsx
index 7f61b42..3288b84 100644
--- a/src/routes/Receive.tsx
+++ b/src/routes/Receive.tsx
@@ -44,6 +44,7 @@ import { InfoBox } from "~/components/InfoBox";
import { FeesModal } from "~/components/MoreInfoModal";
import { IntegratedQr } from "~/components/IntegratedQR";
import side2side from "~/assets/icons/side-to-side.svg";
+import { useI18n } from "~/i18n/context";
type OnChainTx = {
transaction: {
@@ -143,6 +144,7 @@ function FeeExplanation(props: { fee: bigint }) {
export default function Receive() {
const [state, _actions] = useMegaStore();
const navigate = useNavigate();
+ const i18n = useI18n();
const [amount, setAmount] = createSignal("");
const [receiveState, setReceiveState] = createSignal("edit");
@@ -331,7 +333,7 @@ export default function Receive() {
)
}
>
- Receive Bitcoin
+ {i18n.t("receive_bitcoin")}
@@ -344,11 +346,11 @@ export default function Receive() {
exitRoute={amount() ? "/receive" : "/"}
/>
-
+
@@ -359,7 +361,7 @@ export default function Receive() {
intent="green"
onClick={onSubmit}
>
- Continue
+ {i18n.t("continue")}
@@ -371,7 +373,7 @@ export default function Receive() {
kind={flavor()}
/>
- Keep Mutiny open to receive the payment.
+ {i18n.t("keep_mutiny_open")}