diff --git a/packages/ark/package.json b/packages/ark/package.json index 95aafc8f..4e14134d 100644 --- a/packages/ark/package.json +++ b/packages/ark/package.json @@ -32,6 +32,7 @@ "re-resizable": "^6.9.11", "react": "^18.2.0", "react-currency-input-field": "^3.6.14", + "react-i18next": "^14.0.1", "react-router-dom": "^6.21.3", "react-string-replace": "^1.1.1", "sonner": "^1.3.1", diff --git a/packages/ark/src/components/note/buttons/pin.tsx b/packages/ark/src/components/note/buttons/pin.tsx index 4b8f8848..6125f4e3 100644 --- a/packages/ark/src/components/note/buttons/pin.tsx +++ b/packages/ark/src/components/note/buttons/pin.tsx @@ -1,11 +1,14 @@ import { PinIcon } from "@lume/icons"; import { COL_TYPES } from "@lume/utils"; import * as Tooltip from "@radix-ui/react-tooltip"; +import { useTranslation } from "react-i18next"; import { useColumnContext } from "../../column/provider"; import { useNoteContext } from "../provider"; export function NotePin() { const event = useNoteContext(); + + const { t } = useTranslation(); const { addColumn } = useColumnContext(); return ( @@ -24,12 +27,12 @@ export function NotePin() { className="inline-flex items-center justify-center gap-2 pl-2 pr-3 text-sm font-medium rounded-full h-7 w-max bg-neutral-100 hover:bg-neutral-200 dark:hover:bg-neutral-800 dark:bg-neutral-900" > - Pin + {t("note.buttons.pin")} - Pin note + {t("note.buttons.pinTooltip")} diff --git a/packages/ark/src/components/note/buttons/reply.tsx b/packages/ark/src/components/note/buttons/reply.tsx index 26b1d7e7..1371e4ac 100644 --- a/packages/ark/src/components/note/buttons/reply.tsx +++ b/packages/ark/src/components/note/buttons/reply.tsx @@ -1,5 +1,6 @@ import { ReplyIcon } from "@lume/icons"; import * as Tooltip from "@radix-ui/react-tooltip"; +import { useTranslation } from "react-i18next"; import { useNavigate } from "react-router-dom"; import { useNoteContext } from "../provider"; @@ -7,6 +8,8 @@ export function NoteReply() { const event = useNoteContext(); const navigate = useNavigate(); + const { t } = useTranslation(); + return ( @@ -21,7 +24,7 @@ export function NoteReply() { - View thread + {t("note.menu.viewThread")} diff --git a/packages/ark/src/components/note/buttons/repost.tsx b/packages/ark/src/components/note/buttons/repost.tsx index 8f5218a9..9cc7f07e 100644 --- a/packages/ark/src/components/note/buttons/repost.tsx +++ b/packages/ark/src/components/note/buttons/repost.tsx @@ -5,6 +5,7 @@ import * as Tooltip from "@radix-ui/react-tooltip"; import { useSetAtom } from "jotai"; import { nip19 } from "nostr-tools"; import { useState } from "react"; +import { useTranslation } from "react-i18next"; import { toast } from "sonner"; import { useNoteContext } from "../provider"; @@ -13,6 +14,7 @@ export function NoteRepost() { const setEditorValue = useSetAtom(editorValueAtom); const setIsEditorOpen = useSetAtom(editorAtom); + const [t] = useTranslation(); const [loading, setLoading] = useState(false); const [isRepost, setIsRepost] = useState(false); const [open, setOpen] = useState(false); @@ -81,7 +83,7 @@ export function NoteRepost() { - Repost + {t("note.buttons.repost")} @@ -96,7 +98,7 @@ export function NoteRepost() { className="inline-flex items-center gap-3 px-3 text-sm font-medium rounded-lg h-9 text-black/70 hover:bg-black/10 hover:text-black focus:outline-none dark:text-white/70 dark:hover:bg-white/10 dark:hover:text-white" > - Repost + {t("note.buttons.repost")} @@ -106,7 +108,7 @@ export function NoteRepost() { className="inline-flex items-center gap-3 px-3 text-sm font-medium rounded-lg h-9 text-black/70 hover:bg-black/10 hover:text-black focus:outline-none dark:text-white/70 dark:hover:bg-white/10 dark:hover:text-white" > - Quote + {t("note.buttons.quote")} diff --git a/packages/ark/src/components/note/buttons/zap.tsx b/packages/ark/src/components/note/buttons/zap.tsx index 90073c6e..c27e3505 100644 --- a/packages/ark/src/components/note/buttons/zap.tsx +++ b/packages/ark/src/components/note/buttons/zap.tsx @@ -8,6 +8,7 @@ import * as Tooltip from "@radix-ui/react-tooltip"; import { QRCodeSVG } from "qrcode.react"; import { useState } from "react"; import CurrencyInput from "react-currency-input-field"; +import { useTranslation } from "react-i18next"; import { toast } from "sonner"; import { useProfile } from "../../../hooks/useProfile"; import { useNoteContext } from "../provider"; @@ -23,6 +24,7 @@ export function NoteZap() { const [isLoading, setIsLoading] = useState(false); const [invoice, setInvoice] = useState(null); + const { t } = useTranslation(); const { user } = useProfile(event.pubkey); const createZapRequest = async (instant?: boolean) => { @@ -99,7 +101,7 @@ export function NoteZap() { - Zap + {t("note.zap.tooltip")} @@ -124,7 +126,7 @@ export function NoteZap() { - Zap + {t("note.zap.tooltip")} @@ -145,7 +147,7 @@ export function NoteZap() {
- Send zap to{" "} + {t("note.zap.modalTitle")}{" "} {user?.name || user?.displayName || displayNpub(event.pubkey, 16)} @@ -217,7 +219,7 @@ export function NoteZap() { autoComplete="off" autoCorrect="off" autoCapitalize="off" - placeholder="Enter message (optional)" + placeholder={t("note.zap.messagePlaceholder")} className="w-full resize-none rounded-lg border-transparent bg-neutral-100 px-3 py-3 !outline-none placeholder:text-neutral-600 focus:border-blue-500 focus:ring focus:ring-blue-200 dark:bg-neutral-950 dark:text-neutral-400" />
@@ -227,10 +229,10 @@ export function NoteZap() { className="inline-flex items-center justify-center w-full pb-[2px] font-semibold border-t rounded-lg border-neutral-900 dark:border-neutral-800 h-9 bg-neutral-950 text-neutral-50 dark:bg-neutral-900 hover:bg-neutral-900 dark:hover:bg-neutral-800" > {isCompleted - ? "Zapped" + ? t("note.zap.buttonFinish") : isLoading - ? "Processing..." - : "Zap"} + ? t("note.zap.buttonLoading") + : t("note.zap.zap")}
@@ -241,11 +243,11 @@ export function NoteZap() {
-

Scan to zap

+

+ {t("note.zap.invoiceButton")} +

- You must use Bitcoin wallet which support Lightning -
- such as: Blue Wallet, Bitkit, Phoenix,... + {t("note.zap.invoiceFooter")}
diff --git a/packages/ark/src/components/note/child.tsx b/packages/ark/src/components/note/child.tsx index 10753543..ef839800 100644 --- a/packages/ark/src/components/note/child.tsx +++ b/packages/ark/src/components/note/child.tsx @@ -1,7 +1,7 @@ import { NOSTR_MENTIONS } from "@lume/utils"; import { nanoid } from "nanoid"; -import { nip19 } from "nostr-tools"; import { ReactNode, useMemo } from "react"; +import { useTranslation } from "react-i18next"; import { Link } from "react-router-dom"; import reactStringReplace from "react-string-replace"; import { useEvent } from "../../hooks/useEvent"; @@ -13,6 +13,7 @@ export function NoteChild({ eventId, isRoot, }: { eventId: string; isRoot?: boolean }) { + const { t } = useTranslation(); const { isLoading, isError, data } = useEvent(eventId); const richContent = useMemo(() => { @@ -91,7 +92,7 @@ export function NoteChild({ return (
- Failed to fetch event + {t("note.error")}
); @@ -111,7 +112,7 @@ export function NoteChild({
- {isRoot ? "posted:" : "replied:"} + {isRoot ? t("note.posted") : t("note.replied")}:
diff --git a/packages/ark/src/components/note/mentions/note.tsx b/packages/ark/src/components/note/mentions/note.tsx index 4a6b2172..14e0a354 100644 --- a/packages/ark/src/components/note/mentions/note.tsx +++ b/packages/ark/src/components/note/mentions/note.tsx @@ -1,6 +1,7 @@ import { PinIcon } from "@lume/icons"; import { COL_TYPES, NOSTR_MENTIONS } from "@lume/utils"; import { ReactNode, useMemo } from "react"; +import { useTranslation } from "react-i18next"; import { Link } from "react-router-dom"; import reactStringReplace from "react-string-replace"; import { useEvent } from "../../../hooks/useEvent"; @@ -13,6 +14,7 @@ export function MentionNote({ eventId, openable = true, }: { eventId: string; openable?: boolean }) { + const { t } = useTranslation(); const { addColumn } = useColumnContext(); const { isLoading, isError, data } = useEvent(eventId); @@ -98,7 +100,7 @@ export function MentionNote({ contentEditable={false} className="w-full p-3 my-1 rounded-lg cursor-default bg-neutral-100 dark:bg-neutral-900" > - Failed to fetch event. + {t("note.error")} ); } @@ -127,7 +129,7 @@ export function MentionNote({ to={`/events/${data.id}`} className="text-sm text-blue-500 hover:text-blue-600" > - Show more + {t("note.showMore")} diff --git a/packages/ark/src/components/note/menu.tsx b/packages/ark/src/components/note/menu.tsx index 5f7fd79f..75f2955d 100644 --- a/packages/ark/src/components/note/menu.tsx +++ b/packages/ark/src/components/note/menu.tsx @@ -5,6 +5,7 @@ import { writeText } from "@tauri-apps/plugin-clipboard-manager"; import { nip19 } from "nostr-tools"; import { type EventPointer } from "nostr-tools/lib/types/nip19"; import { useState } from "react"; +import { useTranslation } from "react-i18next"; import { Link, useNavigate } from "react-router-dom"; import { toast } from "sonner"; import { useColumnContext } from "../column/provider"; @@ -13,7 +14,10 @@ import { useNoteContext } from "./provider"; export function NoteMenu() { const event = useNoteContext(); const navigate = useNavigate(); + + const { t } = useTranslation(); const { addColumn } = useColumnContext(); + const [open, setOpen] = useState(false); const copyID = async () => { @@ -67,7 +71,7 @@ export function NoteMenu() { onClick={() => copyLink()} className="inline-flex items-center gap-3 px-3 text-sm font-medium rounded-lg h-9 text-black/70 hover:bg-black/10 hover:text-black focus:outline-none dark:text-white/70 dark:hover:bg-white/10 dark:hover:text-white" > - View thread + {t("note.menu.viewThread")} @@ -76,7 +80,7 @@ export function NoteMenu() { onClick={() => navigate(`/events/${event.id}`)} className="inline-flex items-center gap-3 px-3 text-sm font-medium rounded-lg h-9 text-black/70 hover:bg-black/10 hover:text-black focus:outline-none dark:text-white/70 dark:hover:bg-white/10 dark:hover:text-white" > - Copy shareable link + {t("note.menu.copyLink")} @@ -85,7 +89,7 @@ export function NoteMenu() { onClick={() => copyID()} className="inline-flex items-center gap-3 px-3 text-sm font-medium rounded-lg h-9 text-black/70 hover:bg-black/10 hover:text-black focus:outline-none dark:text-white/70 dark:hover:bg-white/10 dark:hover:text-white" > - Copy note ID + {t("note.menu.copyNoteId")} @@ -94,7 +98,7 @@ export function NoteMenu() { onClick={() => copyNpub()} className="inline-flex items-center gap-3 px-3 text-sm font-medium rounded-lg h-9 text-black/70 hover:bg-black/10 hover:text-black focus:outline-none dark:text-white/70 dark:hover:bg-white/10 dark:hover:text-white" > - Copy author ID + {t("note.menu.copyAuthorId")} @@ -102,7 +106,7 @@ export function NoteMenu() { to={`/users/${event.pubkey}`} className="inline-flex items-center gap-3 px-3 text-sm font-medium rounded-lg h-9 text-black/70 hover:bg-black/10 hover:text-black focus:outline-none dark:text-white/70 dark:hover:bg-white/10 dark:hover:text-white" > - View author + {t("note.menu.viewAuthor")} @@ -117,7 +121,7 @@ export function NoteMenu() { } className="inline-flex items-center gap-3 px-3 text-sm font-medium rounded-lg h-9 text-black/70 hover:bg-black/10 hover:text-black focus:outline-none dark:text-white/70 dark:hover:bg-white/10 dark:hover:text-white" > - Pin author + {t("note.menu.pinAuthor")} @@ -127,7 +131,7 @@ export function NoteMenu() { onClick={() => copyRaw()} className="inline-flex items-center gap-3 px-3 text-sm font-medium rounded-lg h-9 text-black/70 hover:bg-black/10 hover:text-black focus:outline-none dark:text-white/70 dark:hover:bg-white/10 dark:hover:text-white" > - Copy raw event + {t("note.menu.copyRaw")} @@ -136,7 +140,7 @@ export function NoteMenu() { onClick={muteUser} className="inline-flex items-center gap-3 px-3 text-sm font-medium text-red-500 rounded-lg h-9 hover:bg-red-500 hover:text-red-50 focus:outline-none" > - Mute + {t("note.menu.mute")} diff --git a/packages/ark/src/components/note/nip89.tsx b/packages/ark/src/components/note/nip89.tsx index c747b2e9..0399a68b 100644 --- a/packages/ark/src/components/note/nip89.tsx +++ b/packages/ark/src/components/note/nip89.tsx @@ -1,4 +1,5 @@ import { useQuery } from "@tanstack/react-query"; +import { useTranslation } from "react-i18next"; import { useArk } from "../../hooks/useArk"; import { AppHandler } from "./appHandler"; import { useNoteContext } from "./provider"; @@ -7,6 +8,7 @@ export function NIP89({ className }: { className?: string }) { const ark = useArk(); const event = useNoteContext(); + const { t } = useTranslation(); const { isLoading, isError, data } = useQuery({ queryKey: ["app-recommend", event.id], queryFn: () => { @@ -33,7 +35,7 @@ export function NIP89({ className }: { className?: string }) {

- Lume isn't support this event + {t("nip89.unsupported")}

{event.kind} @@ -41,10 +43,10 @@ export function NIP89({ className }: { className?: string }) {

- Open with + {t("nip89.openWith")} - {data.map((item, index) => ( - + {data.map((item) => ( + ))}
diff --git a/packages/ark/src/components/note/primitives/reply.tsx b/packages/ark/src/components/note/primitives/reply.tsx index a1b5e461..0aa447b9 100644 --- a/packages/ark/src/components/note/primitives/reply.tsx +++ b/packages/ark/src/components/note/primitives/reply.tsx @@ -3,6 +3,7 @@ import { NDKEventWithReplies } from "@lume/types"; import { cn } from "@lume/utils"; import * as Collapsible from "@radix-ui/react-collapsible"; import { useState } from "react"; +import { useTranslation } from "react-i18next"; import { Note } from ".."; import { ChildReply } from "./childReply"; @@ -11,6 +12,7 @@ export function Reply({ }: { event: NDKEventWithReplies; }) { + const [t] = useTranslation(); const [open, setOpen] = useState(false); return ( @@ -30,7 +32,9 @@ export function Reply({ className={cn("size-5", open ? "rotate-180 transform" : "")} /> {`${event.replies?.length} ${ - event.replies?.length === 1 ? "reply" : "replies" + event.replies?.length === 1 + ? t("note.reply.single") + : t("note.reply.plural") }`} diff --git a/packages/ark/src/components/note/primitives/repost.tsx b/packages/ark/src/components/note/primitives/repost.tsx index 4d95a018..e2ffb0ee 100644 --- a/packages/ark/src/components/note/primitives/repost.tsx +++ b/packages/ark/src/components/note/primitives/repost.tsx @@ -2,6 +2,7 @@ import { RepostIcon } from "@lume/icons"; import { cn } from "@lume/utils"; import { NDKEvent, NostrEvent } from "@nostr-dev-kit/ndk"; import { useQuery } from "@tanstack/react-query"; +import { useTranslation } from "react-i18next"; import { Note } from ".."; import { useArk } from "../../../hooks/useArk"; import { User } from "../../user"; @@ -12,6 +13,7 @@ export function RepostNote({ }: { event: NDKEvent; className?: string }) { const ark = useArk(); + const { t } = useTranslation(); const { isLoading, isError, @@ -51,7 +53,7 @@ export function RepostNote({
- reposted + {t("note.reposted")}
@@ -59,10 +61,6 @@ export function RepostNote({

Failed to get event

-

- You can consider enable Outbox in Settings for better event - discovery. -

@@ -85,7 +83,7 @@ export function RepostNote({
- reposted + {t("note.reposted")}
diff --git a/packages/ark/src/components/note/thread.tsx b/packages/ark/src/components/note/thread.tsx index 05e0e395..a112ad9b 100644 --- a/packages/ark/src/components/note/thread.tsx +++ b/packages/ark/src/components/note/thread.tsx @@ -1,5 +1,6 @@ import { PinIcon } from "@lume/icons"; import { COL_TYPES, cn } from "@lume/utils"; +import { useTranslation } from "react-i18next"; import { Link } from "react-router-dom"; import { Note } from "."; import { useArk } from "../../hooks/useArk"; @@ -18,6 +19,7 @@ export function NoteThread({ tags: event.tags, }); + const { t } = useTranslation(); const { addColumn } = useColumnContext(); if (!thread) return null; @@ -36,7 +38,7 @@ export function NoteThread({ to={`/events/${thread?.rootEventId || thread?.replyEventId}`} className="self-start text-blue-500 hover:text-blue-600" > - Show thread + {t("note.showThread")}