feat: add translation to relay screen

This commit is contained in:
reya
2024-01-29 09:30:33 +07:00
parent 25ae4f2201
commit b97676dd3e
8 changed files with 78 additions and 137 deletions

View File

@@ -18,10 +18,13 @@ import { TutorialModal } from "@lume/ui/src/tutorial/modal";
import { COL_TYPES } from "@lume/utils"; import { COL_TYPES } from "@lume/utils";
import * as Tooltip from "@radix-ui/react-tooltip"; import * as Tooltip from "@radix-ui/react-tooltip";
import { useState } from "react"; import { useState } from "react";
import { useTranslation } from "react-i18next";
import { VList } from "virtua"; import { VList } from "virtua";
export function HomeScreen() { export function HomeScreen() {
const { t } = useTranslation();
const { columns, vlistRef, addColumn } = useColumnContext(); const { columns, vlistRef, addColumn } = useColumnContext();
const [selectedIndex, setSelectedIndex] = useState(-1); const [selectedIndex, setSelectedIndex] = useState(-1);
const renderItem = (column: IColumn) => { const renderItem = (column: IColumn) => {
@@ -124,7 +127,7 @@ export function HomeScreen() {
</Tooltip.Trigger> </Tooltip.Trigger>
<Tooltip.Portal> <Tooltip.Portal>
<Tooltip.Content className="inline-flex h-7 select-none text-neutral-50 dark:text-neutral-950 items-center justify-center rounded-md bg-neutral-950 dark:bg-neutral-50 px-3.5 text-sm will-change-[transform,opacity] data-[state=delayed-open]:data-[side=bottom]:animate-slideUpAndFade data-[state=delayed-open]:data-[side=left]:animate-slideRightAndFade data-[state=delayed-open]:data-[side=right]:animate-slideLeftAndFade data-[state=delayed-open]:data-[side=top]:animate-slideDownAndFade"> <Tooltip.Content className="inline-flex h-7 select-none text-neutral-50 dark:text-neutral-950 items-center justify-center rounded-md bg-neutral-950 dark:bg-neutral-50 px-3.5 text-sm will-change-[transform,opacity] data-[state=delayed-open]:data-[side=bottom]:animate-slideUpAndFade data-[state=delayed-open]:data-[side=left]:animate-slideRightAndFade data-[state=delayed-open]:data-[side=right]:animate-slideLeftAndFade data-[state=delayed-open]:data-[side=top]:animate-slideDownAndFade">
Move Left {t("global.moveLeft")}
<Tooltip.Arrow className="fill-neutral-950 dark:fill-neutral-50" /> <Tooltip.Arrow className="fill-neutral-950 dark:fill-neutral-50" />
</Tooltip.Content> </Tooltip.Content>
</Tooltip.Portal> </Tooltip.Portal>
@@ -151,7 +154,7 @@ export function HomeScreen() {
</Tooltip.Trigger> </Tooltip.Trigger>
<Tooltip.Portal> <Tooltip.Portal>
<Tooltip.Content className="inline-flex h-7 select-none text-neutral-50 dark:text-neutral-950 items-center justify-center rounded-md bg-neutral-950 dark:bg-neutral-50 px-3.5 text-sm will-change-[transform,opacity] data-[state=delayed-open]:data-[side=bottom]:animate-slideUpAndFade data-[state=delayed-open]:data-[side=left]:animate-slideRightAndFade data-[state=delayed-open]:data-[side=right]:animate-slideLeftAndFade data-[state=delayed-open]:data-[side=top]:animate-slideDownAndFade"> <Tooltip.Content className="inline-flex h-7 select-none text-neutral-50 dark:text-neutral-950 items-center justify-center rounded-md bg-neutral-950 dark:bg-neutral-50 px-3.5 text-sm will-change-[transform,opacity] data-[state=delayed-open]:data-[side=bottom]:animate-slideUpAndFade data-[state=delayed-open]:data-[side=left]:animate-slideRightAndFade data-[state=delayed-open]:data-[side=right]:animate-slideLeftAndFade data-[state=delayed-open]:data-[side=top]:animate-slideDownAndFade">
Move Right {t("global.moveRight")}
<Tooltip.Arrow className="fill-neutral-950 dark:fill-neutral-50" /> <Tooltip.Arrow className="fill-neutral-950 dark:fill-neutral-50" />
</Tooltip.Content> </Tooltip.Content>
</Tooltip.Portal> </Tooltip.Portal>
@@ -174,7 +177,7 @@ export function HomeScreen() {
</Tooltip.Trigger> </Tooltip.Trigger>
<Tooltip.Portal> <Tooltip.Portal>
<Tooltip.Content className="inline-flex h-7 select-none text-neutral-50 dark:text-neutral-950 items-center justify-center rounded-md bg-neutral-950 dark:bg-neutral-50 px-3.5 text-sm will-change-[transform,opacity] data-[state=delayed-open]:data-[side=bottom]:animate-slideUpAndFade data-[state=delayed-open]:data-[side=left]:animate-slideRightAndFade data-[state=delayed-open]:data-[side=right]:animate-slideLeftAndFade data-[state=delayed-open]:data-[side=top]:animate-slideDownAndFade"> <Tooltip.Content className="inline-flex h-7 select-none text-neutral-50 dark:text-neutral-950 items-center justify-center rounded-md bg-neutral-950 dark:bg-neutral-50 px-3.5 text-sm will-change-[transform,opacity] data-[state=delayed-open]:data-[side=bottom]:animate-slideUpAndFade data-[state=delayed-open]:data-[side=left]:animate-slideRightAndFade data-[state=delayed-open]:data-[side=right]:animate-slideLeftAndFade data-[state=delayed-open]:data-[side=top]:animate-slideDownAndFade">
New Column {t("global.newColum")}
<Tooltip.Arrow className="fill-neutral-950 dark:fill-neutral-50" /> <Tooltip.Arrow className="fill-neutral-950 dark:fill-neutral-50" />
</Tooltip.Content> </Tooltip.Content>
</Tooltip.Portal> </Tooltip.Portal>

View File

@@ -4,10 +4,13 @@ import { FETCH_LIMIT } from "@lume/utils";
import { NDKEvent, NDKKind } from "@nostr-dev-kit/ndk"; import { NDKEvent, NDKKind } from "@nostr-dev-kit/ndk";
import { useInfiniteQuery } from "@tanstack/react-query"; import { useInfiniteQuery } from "@tanstack/react-query";
import { useCallback, useMemo } from "react"; import { useCallback, useMemo } from "react";
import { useTranslation } from "react-i18next";
import { VList } from "virtua"; import { VList } from "virtua";
export function RelayEventList({ relayUrl }: { relayUrl: string }) { export function RelayEventList({ relayUrl }: { relayUrl: string }) {
const ark = useArk(); const ark = useArk();
const { t } = useTranslation();
const { status, data, hasNextPage, isFetchingNextPage, fetchNextPage } = const { status, data, hasNextPage, isFetchingNextPage, fetchNextPage } =
useInfiniteQuery({ useInfiniteQuery({
queryKey: ["relay-events", relayUrl], queryKey: ["relay-events", relayUrl],
@@ -37,14 +40,10 @@ export function RelayEventList({ relayUrl }: { relayUrl: string }) {
if (!lastEvent) return; if (!lastEvent) return;
return lastEvent.created_at - 1; return lastEvent.created_at - 1;
}, },
select: (data) => data?.pages.flatMap((page) => page),
refetchOnWindowFocus: false, refetchOnWindowFocus: false,
}); });
const allEvents = useMemo(
() => (data ? data.pages.flatMap((page) => page) : []),
[data],
);
const renderItem = useCallback( const renderItem = useCallback(
(event: NDKEvent) => { (event: NDKEvent) => {
switch (event.kind) { switch (event.kind) {
@@ -64,7 +63,7 @@ export function RelayEventList({ relayUrl }: { relayUrl: string }) {
{status === "pending" ? ( {status === "pending" ? (
<NoteSkeleton /> <NoteSkeleton />
) : ( ) : (
allEvents.map((item) => renderItem(item)) data.map((item) => renderItem(item))
)} )}
<div className="flex h-16 items-center justify-center px-3 pb-3"> <div className="flex h-16 items-center justify-center px-3 pb-3">
{hasNextPage ? ( {hasNextPage ? (
@@ -79,7 +78,7 @@ export function RelayEventList({ relayUrl }: { relayUrl: string }) {
) : ( ) : (
<> <>
<ArrowRightCircleIcon className="h-5 w-5" /> <ArrowRightCircleIcon className="h-5 w-5" />
Load more {t("global.loading")}
</> </>
)} )}
</button> </button>

View File

@@ -1,17 +1,20 @@
import { User, useRelaylist } from "@lume/ark"; import { User, useRelaylist } from "@lume/ark";
import { PlusIcon, SearchIcon } from "@lume/icons"; import { PlusIcon, SearchIcon } from "@lume/icons";
import { normalizeRelayUrl } from "nostr-fetch"; import { normalizeRelayUrl } from "nostr-fetch";
import { useTranslation } from "react-i18next";
import { Link } from "react-router-dom"; import { Link } from "react-router-dom";
export function RelayItem({ url, users }: { url: string; users?: string[] }) { export function RelayItem({ url, users }: { url: string; users?: string[] }) {
const domain = new URL(url).hostname; const domain = new URL(url).hostname;
const { t } = useTranslation();
const { connectRelay } = useRelaylist(); const { connectRelay } = useRelaylist();
return ( return (
<div className="flex h-14 w-full items-center justify-between border-b border-neutral-100 px-5 dark:border-neutral-950"> <div className="flex h-14 w-full items-center justify-between border-b border-neutral-100 px-5 dark:border-neutral-950">
<div className="inline-flex items-center gap-2"> <div className="inline-flex items-center gap-2">
<span className="text-sm font-semibold text-neutral-500 dark:text-neutral-400"> <span className="text-sm font-semibold text-neutral-500 dark:text-neutral-400">
Relay:{" "} {t("global.relay")}:{" "}
</span> </span>
<span className="max-w-[200px] truncate text-sm font-medium text-neutral-900 dark:text-neutral-100"> <span className="max-w-[200px] truncate text-sm font-medium text-neutral-900 dark:text-neutral-100">
{url} {url}
@@ -39,7 +42,7 @@ export function RelayItem({ url, users }: { url: string; users?: string[] }) {
className="inline-flex h-8 items-center justify-center gap-2 rounded-lg bg-neutral-100 px-2 text-sm font-medium hover:bg-neutral-200 dark:bg-neutral-900 dark:hover:bg-neutral-800" className="inline-flex h-8 items-center justify-center gap-2 rounded-lg bg-neutral-100 px-2 text-sm font-medium hover:bg-neutral-200 dark:bg-neutral-900 dark:hover:bg-neutral-800"
> >
<SearchIcon className="size-4" /> <SearchIcon className="size-4" />
Inspect {t("global.inspect")}
</Link> </Link>
<button <button
type="button" type="button"

View File

@@ -1,91 +0,0 @@
import { useArk, useRelaylist } from "@lume/ark";
import { LoaderIcon, PlusIcon, ShareIcon } from "@lume/icons";
import { User } from "@lume/ui";
import { useQuery } from "@tanstack/react-query";
import { useNavigate } from "react-router-dom";
import { VList } from "virtua";
export function RelayList() {
const ark = useArk();
const { connectRelay } = useRelaylist();
const { status, data } = useQuery({
queryKey: ["relays"],
queryFn: async () => {
return await ark.getAllRelaysFromContacts();
},
refetchOnWindowFocus: false,
refetchOnMount: false,
refetchOnReconnect: false,
staleTime: Infinity,
});
const navigate = useNavigate();
const inspectRelay = (relayUrl: string) => {
const url = new URL(relayUrl);
navigate(`/relays/${url.hostname}`);
};
return (
<div className="col-span-2 bg-white">
{status === "pending" ? (
<div className="flex h-full w-full items-center justify-center pb-10">
<div className="inline-flex flex-col items-center justify-center gap-2">
<LoaderIcon className="h-5 w-5 animate-spin text-neutral-900 dark:text-neutral-100" />
<p>Loading relay...</p>
</div>
</div>
) : (
<VList className="h-full">
<div className="inline-flex h-16 w-full items-center border-b border-neutral-100 px-3 dark:border-neutral-900">
<h3 className="font-semibold">Relay discovery</h3>
</div>
{[...data].map(([key, value]) => (
<div
key={key}
className="flex h-14 w-full items-center justify-between border-b border-neutral-100 px-3 dark:border-neutral-900"
>
<div className="inline-flex items-center gap-2 divide-x divide-neutral-100 dark:divide-neutral-900">
<div className="inline-flex items-center gap-2">
<button
type="button"
onClick={() => inspectRelay(key)}
className="inline-flex h-6 items-center justify-center gap-1 rounded bg-neutral-200 px-1.5 text-sm font-medium text-neutral-900 hover:bg-neutral-300 dark:bg-neutral-800 dark:text-neutral-100 dark:hover:bg-neutral-700"
>
<ShareIcon className="h-3 w-3" />
Inspect
</button>
<button
type="button"
onClick={() => connectRelay.mutate(key)}
className="inline-flex h-6 w-6 items-center justify-center rounded text-neutral-900 hover:bg-neutral-200 dark:text-neutral-100 dark:hover:bg-neutral-800"
>
<PlusIcon className="h-3 w-3" />
</button>
</div>
<div className="inline-flex items-center gap-2 pl-3">
<span className="text-sm font-semibold text-neutral-500 dark:text-neutral-400">
Relay:{" "}
</span>
<span className="max-w-[200px] truncate text-sm font-medium text-neutral-900 dark:text-neutral-100">
{key}
</span>
</div>
</div>
<div className="isolate flex -space-x-2">
{value.slice(0, 4).map((item) => (
<User key={item} pubkey={item} variant="stacked" />
))}
{value.length > 4 ? (
<div className="inline-flex h-8 w-8 items-center justify-center rounded-full bg-neutral-200 text-neutral-900 ring-1 ring-neutral-300 dark:bg-neutral-800 dark:text-neutral-100 dark:ring-neutral-700">
<span className="text-xs font-medium">+{value.length}</span>
</div>
) : null}
</div>
</div>
))}
</VList>
)}
</div>
);
}

View File

@@ -3,11 +3,13 @@ import { CancelIcon, LoaderIcon, RefreshIcon } from "@lume/icons";
import { cn } from "@lume/utils"; import { cn } from "@lume/utils";
import { NDKKind, NDKSubscriptionCacheUsage } from "@nostr-dev-kit/ndk"; import { NDKKind, NDKSubscriptionCacheUsage } from "@nostr-dev-kit/ndk";
import { useQuery } from "@tanstack/react-query"; import { useQuery } from "@tanstack/react-query";
import { useTranslation } from "react-i18next";
import { RelayForm } from "./relayForm"; import { RelayForm } from "./relayForm";
export function RelaySidebar({ className }: { className?: string }) { export function RelaySidebar({ className }: { className?: string }) {
const ark = useArk(); const ark = useArk();
const { t } = useTranslation();
const { removeRelay } = useRelaylist(); const { removeRelay } = useRelaylist();
const { status, data, isRefetching, refetch } = useQuery({ const { status, data, isRefetching, refetch } = useQuery({
queryKey: ["relay-personal"], queryKey: ["relay-personal"],
@@ -40,7 +42,7 @@ export function RelaySidebar({ className }: { className?: string }) {
)} )}
> >
<div className="inline-flex items-center justify-between w-full h-14 px-3 border-b border-black/10 dark:border-white/10"> <div className="inline-flex items-center justify-between w-full h-14 px-3 border-b border-black/10 dark:border-white/10">
<h3 className="font-semibold">Connected relays</h3> <h3 className="font-semibold">{t("relays.sidebar.title")}</h3>
<button <button
type="button" type="button"
onClick={() => refetch()} onClick={() => refetch()}
@@ -58,7 +60,7 @@ export function RelaySidebar({ className }: { className?: string }) {
</div> </div>
) : !data.length ? ( ) : !data.length ? (
<div className="flex items-center justify-center w-full h-20 rounded-lg bg-black/10 dark:bg-white/10"> <div className="flex items-center justify-center w-full h-20 rounded-lg bg-black/10 dark:bg-white/10">
<p className="text-sm font-medium">Empty.</p> <p className="text-sm font-medium">{t("relays.sidebar.empty")}</p>
</div> </div>
) : ( ) : (
data.map((item) => ( data.map((item) => (

View File

@@ -1,8 +1,11 @@
import { cn } from "@lume/utils"; import { cn } from "@lume/utils";
import { useTranslation } from "react-i18next";
import { NavLink, Outlet } from "react-router-dom"; import { NavLink, Outlet } from "react-router-dom";
import { RelaySidebar } from "./components/sidebar"; import { RelaySidebar } from "./components/sidebar";
export function RelaysScreen() { export function RelaysScreen() {
const { t } = useTranslation();
return ( return (
<div className="grid h-full w-full lg:grid-cols-4 xl:grid-cols-5 rounded-xl shadow-[rgba(50,_50,_105,_0.15)_0px_2px_5px_0px,_rgba(0,_0,_0,_0.05)_0px_1px_1px_0px] dark:shadow-none dark:ring-1 dark:ring-white/10"> <div className="grid h-full w-full lg:grid-cols-4 xl:grid-cols-5 rounded-xl shadow-[rgba(50,_50,_105,_0.15)_0px_2px_5px_0px,_rgba(0,_0,_0,_0.05)_0px_1px_1px_0px] dark:shadow-none dark:ring-1 dark:ring-white/10">
<RelaySidebar className="col-span-1" /> <RelaySidebar className="col-span-1" />
@@ -20,7 +23,7 @@ export function RelaysScreen() {
) )
} }
> >
Global {t("relays.global")}
</NavLink> </NavLink>
<NavLink <NavLink
to={"/relays/follows/"} to={"/relays/follows/"}
@@ -33,7 +36,7 @@ export function RelaysScreen() {
) )
} }
> >
Follows {t("relays.follows")}
</NavLink> </NavLink>
</div> </div>
<div className="flex flex-col flex-1 min-h-0 overflow-y-auto"> <div className="flex flex-col flex-1 min-h-0 overflow-y-auto">

View File

@@ -1,15 +1,16 @@
import { ArrowLeftIcon, LoaderIcon } from "@lume/icons"; import { LoaderIcon } from "@lume/icons";
import { NIP11 } from "@lume/types"; import { NIP11 } from "@lume/types";
import { User } from "@lume/ui"; import { User } from "@lume/ui";
import { Suspense } from "react"; import { Suspense } from "react";
import { Await, useLoaderData, useNavigate, useParams } from "react-router-dom"; import { useTranslation } from "react-i18next";
import { Await, useLoaderData, useParams } from "react-router-dom";
import { RelayEventList } from "./components/relayEventList"; import { RelayEventList } from "./components/relayEventList";
export function RelayUrlScreen() { export function RelayUrlScreen() {
const { t } = useTranslation();
const { url } = useParams(); const { url } = useParams();
const data: { relay?: { [key: string]: string } } = useLoaderData(); const data: { relay?: { [key: string]: string } } = useLoaderData();
const navigate = useNavigate();
const getSoftwareName = (url: string) => { const getSoftwareName = (url: string) => {
const filename = url.substring(url.lastIndexOf("/") + 1); const filename = url.substring(url.lastIndexOf("/") + 1);
@@ -32,7 +33,7 @@ export function RelayUrlScreen() {
fallback={ fallback={
<div className="flex items-center gap-2 text-sm font-medium text-neutral-900 dark:text-neutral-100"> <div className="flex items-center gap-2 text-sm font-medium text-neutral-900 dark:text-neutral-100">
<LoaderIcon className="h-4 w-4 animate-spin" /> <LoaderIcon className="h-4 w-4 animate-spin" />
Loading... {t("global.loading")}
</div> </div>
} }
> >
@@ -40,7 +41,7 @@ export function RelayUrlScreen() {
resolve={data.relay} resolve={data.relay}
errorElement={ errorElement={
<div className="text-sm font-medium"> <div className="text-sm font-medium">
<p>Could not load relay information 😬</p> <p>{t("relays.relayView.empty")}</p>
</div> </div>
} }
> >
@@ -55,7 +56,7 @@ export function RelayUrlScreen() {
{resolvedRelay.pubkey ? ( {resolvedRelay.pubkey ? (
<div className="flex flex-col gap-1"> <div className="flex flex-col gap-1">
<h5 className="text-sm font-semibold text-neutral-600 dark:text-neutral-400"> <h5 className="text-sm font-semibold text-neutral-600 dark:text-neutral-400">
Owner: {t("relays.relayView.owner")}:
</h5> </h5>
<div className="w-full rounded-lg bg-neutral-100 px-2 py-2 dark:bg-neutral-900"> <div className="w-full rounded-lg bg-neutral-100 px-2 py-2 dark:bg-neutral-900">
<User pubkey={resolvedRelay.pubkey} variant="simple" /> <User pubkey={resolvedRelay.pubkey} variant="simple" />
@@ -65,7 +66,7 @@ export function RelayUrlScreen() {
{resolvedRelay.contact ? ( {resolvedRelay.contact ? (
<div> <div>
<h5 className="text-sm font-semibold text-neutral-600 dark:text-neutral-400"> <h5 className="text-sm font-semibold text-neutral-600 dark:text-neutral-400">
Contact: {t("relays.relayView.contact")}:
</h5> </h5>
<a <a
href={`mailto:${resolvedRelay.contact}`} href={`mailto:${resolvedRelay.contact}`}
@@ -79,7 +80,7 @@ export function RelayUrlScreen() {
) : null} ) : null}
<div> <div>
<h5 className="text-sm font-semibold text-neutral-600 dark:text-neutral-400"> <h5 className="text-sm font-semibold text-neutral-600 dark:text-neutral-400">
Software: {t("relays.relayView.software")}:
</h5> </h5>
<a <a
href={resolvedRelay.software} href={resolvedRelay.software}
@@ -94,7 +95,7 @@ export function RelayUrlScreen() {
</div> </div>
<div> <div>
<h5 className="text-sm font-semibold text-neutral-600 dark:text-neutral-400"> <h5 className="text-sm font-semibold text-neutral-600 dark:text-neutral-400">
Supported NIPs: {t("relays.relayView.nips")}:
</h5> </h5>
<div className="mt-2 grid grid-cols-7 gap-2"> <div className="mt-2 grid grid-cols-7 gap-2">
{resolvedRelay.supported_nips.map((item) => ( {resolvedRelay.supported_nips.map((item) => (
@@ -113,14 +114,13 @@ export function RelayUrlScreen() {
{resolvedRelay.limitation ? ( {resolvedRelay.limitation ? (
<div> <div>
<h5 className="text-sm font-semibold text-neutral-600 dark:text-neutral-400"> <h5 className="text-sm font-semibold text-neutral-600 dark:text-neutral-400">
Limitation {t("relays.relayView.limit")}
</h5> </h5>
<div className="flex flex-col gap-2 divide-y divide-white/5"> <div className="flex flex-col gap-2 divide-y divide-white/5">
{Object.keys(resolvedRelay.limitation).map( {Object.keys(resolvedRelay.limitation).map((key) => {
(key, index) => {
return ( return (
<div <div
key={key + index} key={key}
className="flex items-baseline justify-between pt-2" className="flex items-baseline justify-between pt-2"
> >
<p className="text-sm font-medium text-neutral-900 dark:text-neutral-100"> <p className="text-sm font-medium text-neutral-900 dark:text-neutral-100">
@@ -131,8 +131,7 @@ export function RelayUrlScreen() {
</p> </p>
</div> </div>
); );
}, })}
)}
</div> </div>
</div> </div>
) : null} ) : null}
@@ -144,10 +143,10 @@ export function RelayUrlScreen() {
rel="noreferrer" rel="noreferrer"
className="inline-flex h-10 w-full items-center justify-center rounded-lg bg-blue-500 text-sm font-medium hover:bg-blue-600" className="inline-flex h-10 w-full items-center justify-center rounded-lg bg-blue-500 text-sm font-medium hover:bg-blue-600"
> >
Open payment website {t("relays.relayView.payment")}
</a> </a>
<span className="text-center text-xs text-neutral-600 dark:text-neutral-400"> <span className="text-center text-xs text-neutral-600 dark:text-neutral-400">
You need to make a payment to connect this relay {t("relays.relayView.paymentNote")}
</span> </span>
</div> </div>
) : null} ) : null}

View File

@@ -1,7 +1,12 @@
{ {
"global": { "global": {
"relay": "Relay",
"continue": "Continue", "continue": "Continue",
"loading": "Loading" "loading": "Loading",
"moveLeft": "Move Left",
"moveRight": "Move Right",
"newColumn": "New Column",
"inspect": "Inspect"
}, },
"welcome": { "welcome": {
"title": "Lume is a magnificent client for Nostr to meet, explore\nand freely share your thoughts with everyone.", "title": "Lume is a magnificent client for Nostr to meet, explore\nand freely share your thoughts with everyone.",
@@ -66,5 +71,23 @@
"subtitle": "Translate text to your preferred language, powered by Nostr Wine." "subtitle": "Translate text to your preferred language, powered by Nostr Wine."
}, },
"footer": "There are many more settings you can configure from the 'Settings' Screen. Be sure to visit it later." "footer": "There are many more settings you can configure from the 'Settings' Screen. Be sure to visit it later."
},
"relays": {
"global": "Global",
"follows": "Follows",
"sidebar": {
"title": "Connected relays",
"empty": "Empty."
},
"relayView": {
"empty": "Could not load relay information 😬",
"owner": "Owner",
"contact": "Contact",
"software": "Software",
"nips": "Supported NIPs",
"limit": "Limitation",
"payment": "Open payment website",
"paymentNote": "You need to make a payment to connect this relay"
}
} }
} }