feat: relay info page
Some checks failed
continuous-integration/drone/push Build is failing

This commit is contained in:
kieran 2024-09-11 13:50:45 +01:00
parent 9660c07633
commit 6fafae67aa
Signed by: Kieran
GPG Key ID: DE71CEB3925BE941
14 changed files with 747 additions and 188 deletions

View File

@ -33,10 +33,11 @@ interface CollapsedSectionProps {
title: ReactNode;
children: ReactNode;
className?: string;
startClosed?: boolean;
}
export const CollapsedSection = ({ title, children, className }: CollapsedSectionProps) => {
const [collapsed, setCollapsed] = useState(true);
export const CollapsedSection = ({ title, children, className, startClosed }: CollapsedSectionProps) => {
const [collapsed, setCollapsed] = useState(startClosed ?? true);
const icon = (
<div className={classNames("collapse-icon", { flip: !collapsed })}>
<Icon name="arrowFront" />

View File

@ -1,6 +1,4 @@
import { RelaySettings } from "@snort/system";
import classNames from "classnames";
import { FormattedMessage } from "react-intl";
import { Link } from "react-router-dom";
import useRelayState from "@/Feed/RelayState";
import useLogin from "@/Hooks/useLogin";
@ -8,65 +6,30 @@ import RelayUptime from "@/Pages/settings/relays/uptime";
import { getRelayName } from "@/Utils";
import Icon from "../Icons/Icon";
import RelayPermissions from "./permissions";
import RelayStatusLabel from "./status-label";
export interface RelayProps {
addr: string;
}
export default function Relay(props: RelayProps) {
const { state } = useLogin(s => ({ v: s.state.version, state: s.state }));
const connection = useRelayState(props.addr);
const settings = state.relays?.find(a => a.url === props.addr)?.settings;
if (!connection || !settings) return;
async function configure(o: RelaySettings) {
await state.updateRelay(props.addr, o);
}
const { state } = useLogin(s => ({ v: s.state.version, state: s.state }));
if (!connection) return;
const name = connection.info?.name ?? getRelayName(props.addr);
return (
<tr>
<td className="text-ellipsis" title={props.addr}>
{name.length > 20 ? <>{name.slice(0, 20)}...</> : name}
<Link to={`/settings/relays/${encodeURIComponent(props.addr)}`}>
{name.length > 20 ? <>{name.slice(0, 20)}...</> : name}
</Link>
</td>
<td>
<div className="flex gap-1 items-center">
<div
className={classNames("rounded-full w-4 h-4", {
"bg-success": connection.isOpen,
"bg-error": !connection.isOpen,
})}></div>
{connection.isOpen ? (
<FormattedMessage defaultMessage="Connected" />
) : (
<FormattedMessage defaultMessage="Offline" />
)}
</div>
<RelayStatusLabel conn={connection} />
</td>
<td>
<div className="flex gap-2 cursor-pointer select-none justify-center">
<div
className={settings.read ? "" : "text-gray"}
onClick={() =>
configure({
read: !settings.read,
write: settings.write,
})
}>
<FormattedMessage defaultMessage="Read" />
</div>
<div
className={settings.write ? "" : "text-gray"}
onClick={() =>
configure({
read: settings.read,
write: !settings.write,
})
}>
<FormattedMessage defaultMessage="Write" />
</div>
</div>
<RelayPermissions conn={connection} />
</td>
<td className="text-center">
<RelayUptime url={props.addr} />

View File

@ -1,9 +0,0 @@
.favicon {
width: 21px;
height: 21px;
max-width: unset;
}
.relay-active {
color: var(--highlight);
}

View File

@ -1,12 +1,10 @@
import "./RelaysMetadata.css";
import { FullRelaySettings } from "@snort/system";
import { useState } from "react";
import Nostrich from "@/assets/img/nostrich.webp";
import Icon from "@/Components/Icons/Icon";
export const RelayFavicon = ({ url }: { url: string }) => {
export const RelayFavicon = ({ url, size }: { url: string; size?: number }) => {
const cleanUrl = url
.replace(/^wss:\/\//, "https://")
.replace(/^ws:\/\//, "http://")
@ -14,10 +12,12 @@ export const RelayFavicon = ({ url }: { url: string }) => {
const [faviconUrl, setFaviconUrl] = useState(`${cleanUrl}/favicon.ico`);
return (
<img
className="circle favicon"
className="rounded-full object-cover"
src={faviconUrl}
onError={() => setFaviconUrl(Nostrich)}
alt={`favicon for ${url}`}
width={size ?? 20}
height={size ?? 20}
/>
);
};

View File

@ -0,0 +1,17 @@
import { RelayInfo } from "@snort/system";
import classNames from "classnames";
import { FormattedMessage } from "react-intl";
export default function RelayPaymentLabel({ info }: { info: RelayInfo }) {
const isPaid = info?.limitation?.payment_required ?? false;
return (
<div
className={classNames("rounded-full px-2 py-1 font-medium", {
"bg-[var(--pro)] text-black": isPaid,
"bg-[var(--free)]": !isPaid,
})}>
{isPaid && <FormattedMessage defaultMessage="Paid" />}
{!isPaid && <FormattedMessage defaultMessage="Free" />}
</div>
);
}

View File

@ -0,0 +1,33 @@
import { ConnectionType } from "@snort/system/dist/connection-pool";
import { FormattedMessage } from "react-intl";
import useLogin from "@/Hooks/useLogin";
export default function RelayPermissions({ conn }: { conn: ConnectionType }) {
const { state } = useLogin(s => ({ v: s.state.version, state: s.state }));
return (
<div className="flex gap-2 cursor-pointer select-none">
<div
className={conn.settings.read ? "" : "text-gray"}
onClick={async () =>
await state.updateRelay(conn.address, {
read: !conn.settings.read,
write: conn.settings.write,
})
}>
<FormattedMessage defaultMessage="Read" />
</div>
<div
className={conn.settings.write ? "" : "text-gray"}
onClick={async () =>
await state.updateRelay(conn.address, {
read: conn.settings.read,
write: !conn.settings.write,
})
}>
<FormattedMessage defaultMessage="Write" />
</div>
</div>
);
}

View File

@ -0,0 +1,9 @@
import { Link } from "react-router-dom";
export default function RelaySoftware({ software }: { software: string }) {
if (software.includes("git")) {
const u = new URL(software);
return <Link to={software}>{u.pathname.split("/").at(-1)?.replace(".git", "")}</Link>;
}
return software;
}

View File

@ -0,0 +1,16 @@
import { ConnectionType } from "@snort/system/dist/connection-pool";
import classNames from "classnames";
import { FormattedMessage } from "react-intl";
export default function RelayStatusLabel({ conn }: { conn: ConnectionType }) {
return (
<div className="flex gap-1 items-center">
<div
className={classNames("rounded-full w-4 h-4", {
"bg-success": conn.isOpen,
"bg-error": !conn.isOpen,
})}></div>
{conn.isOpen ? <FormattedMessage defaultMessage="Connected" /> : <FormattedMessage defaultMessage="Offline" />}
</div>
);
}

View File

@ -0,0 +1,149 @@
import { FormattedMessage } from "react-intl";
export default function NipDescription({ nip }: { nip: number }) {
switch (nip) {
case 1:
return <FormattedMessage defaultMessage="Basic protocol flow description" />;
case 2:
return <FormattedMessage defaultMessage="Follow List" />;
case 3:
return <FormattedMessage defaultMessage="OpenTimestamps Attestations for Events" />;
case 4:
return <FormattedMessage defaultMessage="Encrypted Direct Message" />;
case 5:
return <FormattedMessage defaultMessage="Mapping Nostr keys to DNS-based internet identifiers" />;
case 6:
return <FormattedMessage defaultMessage="Basic key derivation from mnemonic seed phrase" />;
case 7:
return <FormattedMessage defaultMessage="window.nostr capability for web browsers" />;
case 8:
return <FormattedMessage defaultMessage="Handling Mentions" />;
case 9:
return <FormattedMessage defaultMessage="Event Deletion Request" />;
case 10:
return <FormattedMessage defaultMessage="Conventions for clients' use of e and p tags in text events" />;
case 11:
return <FormattedMessage defaultMessage="Relay Information Document" />;
case 13:
return <FormattedMessage defaultMessage="Proof of Work" />;
case 14:
return <FormattedMessage defaultMessage="Subject tag in text events" />;
case 15:
return <FormattedMessage defaultMessage="Nostr Marketplace (for resilient marketplaces)" />;
case 17:
return <FormattedMessage defaultMessage="Private Direct Messages" />;
case 18:
return <FormattedMessage defaultMessage="Reposts" />;
case 19:
return <FormattedMessage defaultMessage="bech32-encoded entities" />;
case 21:
return <FormattedMessage defaultMessage="nostr: URI scheme" />;
case 23:
return <FormattedMessage defaultMessage="Long-form Content" />;
case 24:
return <FormattedMessage defaultMessage="Extra metadata fields and tags" />;
case 25:
return <FormattedMessage defaultMessage="Reactions" />;
case 26:
return <FormattedMessage defaultMessage="Delegated Event Signing" />;
case 27:
return <FormattedMessage defaultMessage="Text Note References" />;
case 28:
return <FormattedMessage defaultMessage="Public Chat" />;
case 29:
return <FormattedMessage defaultMessage="Relay-based Groups" />;
case 30:
return <FormattedMessage defaultMessage="Custom Emoji" />;
case 31:
return <FormattedMessage defaultMessage="Dealing with Unknown Events" />;
case 32:
return <FormattedMessage defaultMessage="Labeling" />;
case 34:
return <FormattedMessage defaultMessage="git stuff" />;
case 35:
return <FormattedMessage defaultMessage="Torrents" />;
case 36:
return <FormattedMessage defaultMessage="Sensitive Content" />;
case 38:
return <FormattedMessage defaultMessage="User Statuses" />;
case 39:
return <FormattedMessage defaultMessage="External Identities in Profiles" />;
case 40:
return <FormattedMessage defaultMessage="Expiration Timestamp" />;
case 42:
return <FormattedMessage defaultMessage="Authentication of clients to relays" />;
case 44:
return <FormattedMessage defaultMessage="Versioned Encryption" />;
case 45:
return <FormattedMessage defaultMessage="Counting results" />;
case 46:
return <FormattedMessage defaultMessage="Nostr Connect" />;
case 47:
return <FormattedMessage defaultMessage="Wallet Connect" />;
case 48:
return <FormattedMessage defaultMessage="Proxy Tags" />;
case 49:
return <FormattedMessage defaultMessage="Private Key Encryption" />;
case 50:
return <FormattedMessage defaultMessage="Search Capability" />;
case 51:
return <FormattedMessage defaultMessage="Lists" />;
case 52:
return <FormattedMessage defaultMessage="Calendar Events" />;
case 53:
return <FormattedMessage defaultMessage="Live Activities" />;
case 54:
return <FormattedMessage defaultMessage="Wiki" />;
case 55:
return <FormattedMessage defaultMessage="Android Signer Application" />;
case 56:
return <FormattedMessage defaultMessage="Reporting" />;
case 57:
return <FormattedMessage defaultMessage="Lightning Zaps" />;
case 58:
return <FormattedMessage defaultMessage="Badges" />;
case 59:
return <FormattedMessage defaultMessage="Gift Wrap" />;
case 64:
return <FormattedMessage defaultMessage="Chess (PGN)" />;
case 65:
return <FormattedMessage defaultMessage="Relay List Metadata" />;
case 70:
return <FormattedMessage defaultMessage="Protected Events" />;
case 71:
return <FormattedMessage defaultMessage="Video Events" />;
case 72:
return <FormattedMessage defaultMessage="Moderated Communities" />;
case 73:
return <FormattedMessage defaultMessage="External Content IDs" />;
case 75:
return <FormattedMessage defaultMessage="Zap Goals" />;
case 78:
return <FormattedMessage defaultMessage="Application-specific data" />;
case 84:
return <FormattedMessage defaultMessage="Highlights" />;
case 89:
return <FormattedMessage defaultMessage="Recommended Application Handlers" />;
case 90:
return <FormattedMessage defaultMessage="Data Vending Machines" />;
case 92:
return <FormattedMessage defaultMessage="Media Attachments" />;
case 94:
return <FormattedMessage defaultMessage="File Metadata" />;
case 96:
return <FormattedMessage defaultMessage="HTTP File Storage Integration" />;
case 98:
return <FormattedMessage defaultMessage="HTTP Auth" />;
case 99:
return <FormattedMessage defaultMessage="Classified Listings" />;
default:
return (
<FormattedMessage
defaultMessage="Unknown NIP-{x}"
values={{
x: nip.toString().padStart(2, "0"),
}}
/>
);
}
}

View File

@ -1,126 +1,212 @@
import { Connection } from "@snort/system";
import { FormattedMessage } from "react-intl";
import { useNavigate, useParams } from "react-router-dom";
import { RelayInfo as RI } from "@snort/system";
import { useEffect, useState, useSyncExternalStore } from "react";
import { FormattedMessage, FormattedNumber } from "react-intl";
import { Link, useParams } from "react-router-dom";
import AsyncButton from "@/Components/Button/AsyncButton";
import ProfilePreview from "@/Components/User/ProfilePreview";
import { RelayMetrics } from "@/Cache";
import { CollapsedSection } from "@/Components/Collapsed";
import NipDescription from "@/Components/nip";
import RelayPaymentLabel from "@/Components/Relay/paid";
import RelayPermissions from "@/Components/Relay/permissions";
import { RelayFavicon } from "@/Components/Relay/RelaysMetadata";
import RelaySoftware from "@/Components/Relay/software";
import RelayStatusLabel from "@/Components/Relay/status-label";
import ProfileImage from "@/Components/User/ProfileImage";
import useRelayState from "@/Feed/RelayState";
import useEventPublisher from "@/Hooks/useEventPublisher";
import useLogin from "@/Hooks/useLogin";
import { parseId, unwrap } from "@/Utils";
import { getRelayName, parseId } from "@/Utils";
import messages from "./messages";
import RelayUptime from "./relays/uptime";
const RelayInfo = () => {
const params = useParams();
const navigate = useNavigate();
const login = useLogin();
const { system } = useEventPublisher();
const [info, setInfo] = useState<RI>();
const conn = [...system.pool].find(([, a]) => a.id === params.id)?.[1];
const conn = useRelayState(params.id ?? "");
async function loadRelayInfo() {
const u = new URL(params.id ?? "");
const rsp = await fetch(`${u.protocol === "wss:" ? "https:" : "http:"}//${u.host}`, {
headers: {
accept: "application/nostr+json",
},
});
if (rsp.ok) {
const data = await rsp.json();
for (const [k, v] of Object.entries(data)) {
if (v === "unset" || v === "" || v === "~") {
data[k] = undefined;
}
}
setInfo(data);
}
}
useEffect(() => {
loadRelayInfo().catch(console.error);
}, []);
const stats = useSyncExternalStore(
c => RelayMetrics.hook(c, "*"),
() => RelayMetrics.snapshot(),
).find(a => a.addr === params.id);
const stats = useRelayState(conn?.address ?? "");
return (
<>
<h3 className="pointer" onClick={() => navigate("/settings/relays")}>
<FormattedMessage {...messages.Relays} />
</h3>
<div>
<h3>{stats?.info?.name}</h3>
<p>{stats?.info?.description}</p>
{stats?.info?.pubkey && (
<>
<h4>
<FormattedMessage {...messages.Owner} />
</h4>
<ProfilePreview pubkey={parseId(stats.info.pubkey)} />
</>
)}
{stats?.info?.software && (
<div className="flex">
<h4 className="grow">
<FormattedMessage {...messages.Software} />
</h4>
<div className="flex flex-col">
{stats.info.software.startsWith("http") ? (
<a href={stats.info.software} target="_blank" rel="noreferrer">
{stats.info.software}
</a>
) : (
<>{stats.info.software}</>
)}
<small>
{!stats.info.version?.startsWith("v") && "v"}
{stats.info.version}
</small>
<div className="flex flex-col gap-4">
<div className="flex justify-between">
<div className="flex gap-4 items-center">
<RelayFavicon url={params.id ?? ""} size={80} />
<div className="flex flex-col gap-2">
<div className="flex items-center gap-2">
<div className="text-2xl font-bold">{info?.name ?? getRelayName(params.id ?? "")}</div>
{info && <RelayPaymentLabel info={info} />}
</div>
<div className="text-gray-light">{params.id}</div>
</div>
</div>
)}
{stats?.info?.contact && (
<div className="flex">
<h4 className="grow">
<FormattedMessage {...messages.Contact} />
</h4>
<a
href={`${stats.info.contact.startsWith("mailto:") ? "" : "mailto:"}${stats.info.contact}`}
target="_blank"
rel="noreferrer">
{stats.info.contact}
</a>
</div>
)}
{stats?.info?.supported_nips && (
<>
<h4>
<FormattedMessage {...messages.Supports} />
</h4>
<div className="grow">
{stats.info?.supported_nips?.map(a => (
<a key={a} target="_blank" rel="noreferrer" href={`https://nips.be/${a}`} className="pill">
NIP-{a.toString().padStart(2, "0")}
</a>
))}
</div>
</>
)}
{conn instanceof Connection && (
<>
<h4>
<FormattedMessage defaultMessage="Active Subscriptions" />
</h4>
<div className="grow">
{conn.ActiveRequests.map(a => (
<span className="pill" key={a}>
{a}
</span>
))}
</div>
</>
)}
{conn instanceof Connection && (
<>
<h4>
<FormattedMessage defaultMessage="Pending Subscriptions" />
</h4>
<div className="grow">
{conn.PendingRequests.map(a => (
<span className="pill" key={a.obj[1]}>
{a.obj[1]}
</span>
))}
</div>
</>
)}
<div className="flex mt10 justify-end">
<AsyncButton
onClick={async () => {
await login.state.removeRelay(unwrap(conn).address, true);
navigate("/settings/relays");
}}>
<FormattedMessage {...messages.Remove} />
</AsyncButton>
</div>
{info && (
<div className="grid grid-cols-3 gap-4">
<div className="flex flex-col gap-2">
<div className="uppercase text-secondary font-bold text-sm">
<FormattedMessage defaultMessage="Admin" />
</div>
<div>{info?.pubkey && <ProfileImage pubkey={parseId(info.pubkey)} size={30} />}</div>
</div>
<div className="flex flex-col gap-2">
<div className="uppercase text-secondary font-bold text-sm">
<FormattedMessage defaultMessage="Contact" />
</div>
<div>
{info?.contact && (
<a
href={`${info.contact.startsWith("mailto:") ? "" : "mailto:"}${info.contact}`}
target="_blank"
rel="noreferrer">
{info.contact.replace("mailto:", "")}
</a>
)}
</div>
</div>
<div className="flex flex-col gap-2">
<div className="uppercase text-secondary font-bold text-sm">
<FormattedMessage defaultMessage="Software" />
</div>
<div>{info?.software && <RelaySoftware software={info.software} />}</div>
</div>
{conn && (
<>
<div className="flex flex-col gap-2">
<div className="uppercase text-secondary font-bold text-sm">
<FormattedMessage defaultMessage="Status" />
</div>
<div>
<RelayStatusLabel conn={conn} />
</div>
</div>
<div className="flex flex-col gap-2">
<div className="uppercase text-secondary font-bold text-sm">
<FormattedMessage defaultMessage="Permissions" />
</div>
<div>
<RelayPermissions conn={conn} />
</div>
</div>
<div className="flex flex-col gap-2">
<div className="uppercase text-secondary font-bold text-sm">
<FormattedMessage defaultMessage="Uptime" />
</div>
<div>
<RelayUptime url={conn.address} />
</div>
</div>
</>
)}
</div>
)}
<hr className="border-border-color" />
{stats && (
<CollapsedSection
title={
<div className="text-xl font-semibold">
<FormattedMessage defaultMessage="Relay Stats" />
</div>
}
startClosed={false}>
<ul className="list-disc">
<li>
<span className="text-gray-light">
<FormattedMessage defaultMessage="Total Events:" />
</span>
&nbsp;
<FormattedNumber value={stats.events} />
</li>
<li>
<span className="text-gray-light">
<FormattedMessage defaultMessage="Connection Success:" />
</span>
&nbsp;
<FormattedNumber value={stats.connects} />
</li>
<li>
<span className="text-gray-light">
<FormattedMessage defaultMessage="Connection Failed:" />
</span>
&nbsp;
<FormattedNumber value={stats.disconnects} />
</li>
<li>
<span className="text-gray-light">
<FormattedMessage defaultMessage="Average Latency:" />
</span>
&nbsp;
<FormattedMessage
defaultMessage="{n} ms"
values={{
n: (
<FormattedNumber
maximumFractionDigits={0}
value={stats.latency.reduce((acc, v) => acc + v, 0) / stats.latency.length}
/>
),
}}
/>
</li>
<li>
<span className="text-gray-light">
<FormattedMessage defaultMessage="Last Seen:" />
</span>
&nbsp;
{new Date(stats.lastSeen).toLocaleString()}
</li>
</ul>
</CollapsedSection>
)}
<hr className="border-border-color" />
{info?.supported_nips && (
<CollapsedSection
title={
<div className="text-xl font-semibold">
<FormattedMessage defaultMessage="Supported NIPs" />
</div>
}
startClosed={false}>
<ul className="list-disc">
{info.supported_nips.map(n => (
<li key={n}>
<Link
target="_blank"
to={`https://github.com/nostr-protocol/nips/blob/master/${n.toString().padStart(2, "0")}.md`}>
<NipDescription nip={n} />
</Link>
</li>
))}
</ul>
</CollapsedSection>
)}
</div>
</>
);

View File

@ -3,6 +3,7 @@ import { OutboxModel } from "@snort/system";
import { SnortContext } from "@snort/system-react";
import { useContext, useMemo, useSyncExternalStore } from "react";
import { FormattedMessage } from "react-intl";
import { Link } from "react-router-dom";
import { RelayMetrics } from "@/Cache";
import AsyncButton from "@/Components/Button/AsyncButton";
@ -89,9 +90,11 @@ export function DiscoverRelays() {
.slice(0, 20)
.map(a => (
<tr key={a.relay}>
<td className="flex gap-2 items-center">
<RelayFavicon url={a.relay} />
{getRelayName(a.relay)}
<td>
<Link to={`/settings/relays/${encodeURIComponent(a.relay)}`} className="flex gap-2 items-center">
<RelayFavicon url={a.relay} />
{getRelayName(a.relay)}
</Link>
</td>
<td className="text-center">
<RelayUptime url={a.relay} />
@ -135,9 +138,11 @@ export function DiscoverRelays() {
<tbody>
{reliableRelays.slice(0, 40).map(a => (
<tr key={a.addr}>
<td className="flex gap-2 items-center" title={a.addr}>
<RelayFavicon url={a.addr} />
{getRelayName(a.addr)}
<td title={a.addr}>
<Link to={`/settings/relays/${encodeURIComponent(a.addr)}`} className="flex gap-2 items-center">
<RelayFavicon url={a.addr} />
{getRelayName(a.addr)}
</Link>
</td>
<td className="text-center">
<UptimeLabel avgPing={a.avgLatency} />

View File

@ -54,6 +54,7 @@
--primary-bg: #ff3f15;
--cashu-gradient: linear-gradient(90deg, #40b039, #adff2a);
--pro: #ffdd65;
--free: #1a5aff;
}
::-webkit-scrollbar {
@ -121,6 +122,10 @@ code {
font-family: source-code-pro, Menlo, Monaco, Consolas, "Courier New", monospace;
}
ul {
padding-inline-start: 2rem;
}
a {
color: inherit;
line-height: 1.3em;
@ -227,7 +232,8 @@ a.ext {
small {
color: var(--font-secondary-color);
font-size: 14px;
line-height: 22px; /* 157.143% */
line-height: 22px;
/* 157.143% */
}
.button {
@ -898,6 +904,7 @@ svg.repeat {
border: 1px solid var(--border-color);
box-shadow: rgba(0, 0, 0, 0.08) 0 1px 1px;
}
.light button.primary {
background: var(--primary-bg);
color: #fff;
@ -909,6 +916,7 @@ svg.repeat {
.light button.secondary:hover {
box-shadow: rgba(0, 0, 0, 0.2) 0 1px 3px !important;
}
.light button.primary:hover {
box-shadow: 0px 0px 10px var(--primary-bg) !important;
}
@ -923,7 +931,8 @@ svg.repeat {
}
.hide-scrollbar::-webkit-scrollbar {
display: none; /* Safari and Chrome */
display: none;
/* Safari and Chrome */
}
.pb-safe-area {

View File

@ -8,6 +8,9 @@
"+QM0PJ": {
"defaultMessage": "Sync all events for your profile into local cache"
},
"+QMdsy": {
"defaultMessage": "Relay Stats"
},
"+UjDmN": {
"defaultMessage": "Logged in with write access"
},
@ -44,6 +47,9 @@
"/JE/X+": {
"defaultMessage": "Account Support"
},
"/T7HId": {
"defaultMessage": "HTTP File Storage Integration"
},
"/Xf4UW": {
"defaultMessage": "Send anonymous usage metrics"
},
@ -77,6 +83,9 @@
"0jOEtS": {
"defaultMessage": "Invalid LNURL"
},
"0kOBMu": {
"defaultMessage": "Handling Mentions"
},
"0mch2Y": {
"defaultMessage": "name has disallowed characters"
},
@ -93,6 +102,9 @@
"defaultMessage": "Go",
"description": "Button text after entering username in quick signup"
},
"1/BFEj": {
"defaultMessage": "git stuff"
},
"1Mo59U": {
"defaultMessage": "Are you sure you want to remove this note from bookmarks?"
},
@ -126,6 +138,15 @@
"25WwxF": {
"defaultMessage": "Don't have an account?"
},
"28oKbu": {
"defaultMessage": "Moderated Communities"
},
"29sHFE": {
"defaultMessage": "Wallet Connect"
},
"2BBGxX": {
"defaultMessage": "Subject tag in text events"
},
"2IFGap": {
"defaultMessage": "Donate"
},
@ -147,12 +168,18 @@
"2oCF7O": {
"defaultMessage": "Followed by friends of friends"
},
"2raFAu": {
"defaultMessage": "Application-specific data"
},
"2ukA4d": {
"defaultMessage": "{n} hours"
},
"39AHJm": {
"defaultMessage": "Sign Up"
},
"3GWu6/": {
"defaultMessage": "User Statuses"
},
"3KNMbJ": {
"defaultMessage": "Articles"
},
@ -177,9 +204,18 @@
"3yk8fB": {
"defaultMessage": "Wallet"
},
"40VR6s": {
"defaultMessage": "Nostr Connect"
},
"41BSaT": {
"defaultMessage": "Total Events:"
},
"450Fty": {
"defaultMessage": "None"
},
"47E53q": {
"defaultMessage": "Wiki"
},
"47FYwb": {
"defaultMessage": "Cancel"
},
@ -195,6 +231,9 @@
"4OB335": {
"defaultMessage": "Dislike"
},
"4P/kKm": {
"defaultMessage": "Private Key Encryption"
},
"4Vmpt4": {
"defaultMessage": "Nostr Plebs is one of the first NIP-05 providers in the space and offers a good collection of domains at reasonable prices"
},
@ -207,6 +246,9 @@
"4rYCjn": {
"defaultMessage": "Note to Self"
},
"4wgYpI": {
"defaultMessage": "Recommended Application Handlers"
},
"5BVs2e": {
"defaultMessage": "zap"
},
@ -219,6 +261,9 @@
"5oTnfy": {
"defaultMessage": "Buy Handle"
},
"5qEWCr": {
"defaultMessage": "File Metadata"
},
"5u6iEc": {
"defaultMessage": "Transfer to Pubkey"
},
@ -234,6 +279,9 @@
"62nsdy": {
"defaultMessage": "Retry"
},
"634VVz": {
"defaultMessage": "Connection Failed:"
},
"6559gb": {
"defaultMessage": "New follow list length {length}"
},
@ -255,6 +303,9 @@
"6mr8WU": {
"defaultMessage": "Followed by"
},
"6pdxsi": {
"defaultMessage": "Extra metadata fields and tags"
},
"6uMqL1": {
"defaultMessage": "Unpaid"
},
@ -267,6 +318,12 @@
"712i26": {
"defaultMessage": "Proxy uses HODL invoices to forward the payment, which hides the pubkey of your node"
},
"77nkEO": {
"defaultMessage": "Relay Information Document"
},
"7LFU8U": {
"defaultMessage": "Search Capability"
},
"7UOvbT": {
"defaultMessage": "Offline"
},
@ -282,6 +339,9 @@
"89q5wc": {
"defaultMessage": "Confirm Reposts"
},
"8BDFvJ": {
"defaultMessage": "Conventions for clients' use of e and p tags in text events"
},
"8ED/4u": {
"defaultMessage": "Reply To"
},
@ -300,6 +360,9 @@
"8g2vyB": {
"defaultMessage": "name too long"
},
"8jmwT8": {
"defaultMessage": "bech32-encoded entities"
},
"8v1NN+": {
"defaultMessage": "Pairing phrase"
},
@ -379,6 +442,9 @@
"BGCM48": {
"defaultMessage": "Write access to Snort relay, with 1 year of event retention"
},
"BQW4gi": {
"defaultMessage": "Relay-based Groups"
},
"BWpuKl": {
"defaultMessage": "Update"
},
@ -389,6 +455,9 @@
"defaultMessage": "Relay",
"description": "Relay name (URL)"
},
"Bo+O//": {
"defaultMessage": "HTTP Auth"
},
"C1LjMx": {
"defaultMessage": "Lightning Donation"
},
@ -431,6 +500,9 @@
"CzHZoc": {
"defaultMessage": "Social Graph"
},
"D++Njw": {
"defaultMessage": "Text Note References"
},
"D+KzKd": {
"defaultMessage": "Automatically zap every note when loaded"
},
@ -500,9 +572,15 @@
"F3l7xL": {
"defaultMessage": "Add Account"
},
"F4eJ/3": {
"defaultMessage": "Classified Listings"
},
"FDguSC": {
"defaultMessage": "{n} Zaps"
},
"FHvSk3": {
"defaultMessage": "Authentication of clients to relays"
},
"FMfjrl": {
"defaultMessage": "Show status messages on profile pages"
},
@ -539,6 +617,9 @@
"GFOoEE": {
"defaultMessage": "Salt"
},
"GIqktu": {
"defaultMessage": "Supported NIPs"
},
"GL8aXW": {
"defaultMessage": "Bookmarks ({n})"
},
@ -564,6 +645,9 @@
"defaultMessage": "Hex Key..",
"description": "Hexidecimal 'key' input for improxy"
},
"H/oroO": {
"defaultMessage": "Dealing with Unknown Events"
},
"H0JBH6": {
"defaultMessage": "Log Out"
},
@ -594,6 +678,9 @@
"HqRNN8": {
"defaultMessage": "Support"
},
"HzSFeV": {
"defaultMessage": "Expiration Timestamp"
},
"I1AoOu": {
"defaultMessage": "Last post {time}"
},
@ -615,6 +702,9 @@
"IWz1ta": {
"defaultMessage": "Auto Translate"
},
"IcHcWj": {
"defaultMessage": "Last Seen:"
},
"Ig9/a1": {
"defaultMessage": "Sent {n} sats to {name}"
},
@ -678,27 +768,45 @@
"JymXbw": {
"defaultMessage": "Private Key"
},
"K1wl1/": {
"defaultMessage": "Average Latency:"
},
"K3r6DQ": {
"defaultMessage": "Delete"
},
"K7AkdL": {
"defaultMessage": "Show"
},
"K9zklU": {
"defaultMessage": "External Content IDs"
},
"KAhAcM": {
"defaultMessage": "Enter LNDHub config"
},
"KGmQjH": {
"defaultMessage": "Highlights"
},
"KQvWvD": {
"defaultMessage": "Deleted"
},
"KT9nox": {
"defaultMessage": "Protected Events"
},
"KahimY": {
"defaultMessage": "Unknown event kind: {kind}"
},
"KipVeG": {
"defaultMessage": "Mapping Nostr keys to DNS-based internet identifiers"
},
"KtsyO0": {
"defaultMessage": "Enter Pin"
},
"LBAnc7": {
"defaultMessage": "View as user?"
},
"LEmxc8": {
"defaultMessage": "Zap Goals"
},
"LKw/ue": {
"defaultMessage": "Check out the code {link}"
},
@ -720,6 +828,9 @@
"Lu5/Bj": {
"defaultMessage": "Open on Zapstr"
},
"LuDBLj": {
"defaultMessage": "Torrents"
},
"Lw+I+J": {
"defaultMessage": "{n,plural,=0{{name} zapped} other{{name} & {n} others zapped}}"
},
@ -748,6 +859,9 @@
"MiMipu": {
"defaultMessage": "Set as primary Nostr address (nip05)"
},
"MkQ4FX": {
"defaultMessage": "Proxy Tags"
},
"Ml7+RS": {
"defaultMessage": "Send this link to your friends and share the magic of the nostr."
},
@ -757,6 +871,9 @@
"MuVeKe": {
"defaultMessage": "Buy nostr address"
},
"Muhna4": {
"defaultMessage": "Counting results"
},
"MzRYWH": {
"defaultMessage": "Buying {item}"
},
@ -781,6 +898,9 @@
"NndBJE": {
"defaultMessage": "New users page"
},
"Nr9Yyx": {
"defaultMessage": "Reposts"
},
"NxzeNU": {
"defaultMessage": "Dead"
},
@ -790,6 +910,12 @@
"OEW7yJ": {
"defaultMessage": "Zaps"
},
"OIqnZN": {
"defaultMessage": "OpenTimestamps Attestations for Events"
},
"OJHKIL": {
"defaultMessage": "Gift Wrap"
},
"OKhRC6": {
"defaultMessage": "Share"
},
@ -808,6 +934,9 @@
"OoZgbB": {
"defaultMessage": "Failed to update, please try again"
},
"OuProE": {
"defaultMessage": "Long-form Content"
},
"OxPdQ0": {
"defaultMessage": "Scanning {date}"
},
@ -850,6 +979,9 @@
"Qxv0B2": {
"defaultMessage": "You currently have {number} sats in your zap pool."
},
"Qy6/Ft": {
"defaultMessage": "Private Direct Messages"
},
"R/6nsx": {
"defaultMessage": "Subscription"
},
@ -871,6 +1003,9 @@
"RhDAoS": {
"defaultMessage": "Are you sure you want to delete {id}"
},
"RmxSZo": {
"defaultMessage": "Data Vending Machines"
},
"RoOyAh": {
"defaultMessage": "Relays"
},
@ -937,6 +1072,9 @@
"TdtZQ5": {
"defaultMessage": "Crypto"
},
"TgDKhI": {
"defaultMessage": "Calendar Events"
},
"TpgeGw": {
"defaultMessage": "Hex Salt..",
"description": "Hexidecimal 'salt' input for imgproxy"
@ -953,9 +1091,6 @@
"U1aPPi": {
"defaultMessage": "Stop listening"
},
"UDYlxu": {
"defaultMessage": "Pending Subscriptions"
},
"UJTWqI": {
"defaultMessage": "Remove from my relays"
},
@ -989,6 +1124,9 @@
"UxgyeY": {
"defaultMessage": "Your referral code is {code}"
},
"V20Og0": {
"defaultMessage": "Labeling"
},
"VOjC1i": {
"defaultMessage": "Pick which upload service you want to upload attachments to"
},
@ -1064,6 +1202,9 @@
"YR2I9M": {
"defaultMessage": "No keys, no {app}, There is no way to reset it if you don't back up. It only takes a minute."
},
"YU7ZYp": {
"defaultMessage": "Public Chat"
},
"YXA3AH": {
"defaultMessage": "Enable reactions"
},
@ -1073,6 +1214,9 @@
"Z4BMCZ": {
"defaultMessage": "Enter pairing phrase"
},
"Z7kkeJ": {
"defaultMessage": "Delegated Event Signing"
},
"ZKORll": {
"defaultMessage": "Activate Now"
},
@ -1185,6 +1329,9 @@
"cuV2gK": {
"defaultMessage": "name is registered"
},
"cw1Ftc": {
"defaultMessage": "Live Activities"
},
"cyR7Kh": {
"defaultMessage": "Back"
},
@ -1242,6 +1389,9 @@
"eXT2QQ": {
"defaultMessage": "Group Chat"
},
"eZtOxB": {
"defaultMessage": "window.nostr capability for web browsers"
},
"egib+2": {
"defaultMessage": "{n,plural,=1{& {n} other} other{& {n} others}}"
},
@ -1317,9 +1467,18 @@
"gjBiyj": {
"defaultMessage": "Loading..."
},
"gkMmvC": {
"defaultMessage": "Android Signer Application"
},
"gl1NeW": {
"defaultMessage": "Lists"
},
"grQ+mI": {
"defaultMessage": "Proof of Work"
},
"gtNjNP": {
"defaultMessage": "Basic protocol flow description"
},
"h7jvCs": {
"defaultMessage": "{site} is more fun together!"
},
@ -1374,6 +1533,9 @@
"iGT1eE": {
"defaultMessage": "Prevent fake accounts from imitating you"
},
"iHN12u": {
"defaultMessage": "Admin"
},
"iICVoL": {
"defaultMessage": "{x} follows ({y} duplicates)"
},
@ -1455,6 +1617,9 @@
"l3H1EK": {
"defaultMessage": "Invite your friends"
},
"l3nTjd": {
"defaultMessage": "Basic key derivation from mnemonic seed phrase"
},
"lCILNz": {
"defaultMessage": "Buy Now"
},
@ -1551,9 +1716,6 @@
"p4N05H": {
"defaultMessage": "Upload"
},
"p85Uwy": {
"defaultMessage": "Active Subscriptions"
},
"p9Ps2l": {
"defaultMessage": "{x}/{y} have relays ({percent})"
},
@ -1566,9 +1728,15 @@
"pRess9": {
"defaultMessage": "ZapPool"
},
"plOM0t": {
"defaultMessage": "Custom Emoji"
},
"puLNUJ": {
"defaultMessage": "Pin"
},
"pyjJ5f": {
"defaultMessage": "Nostr Marketplace (for resilient marketplaces)"
},
"pzTOmv": {
"defaultMessage": "Followers"
},
@ -1626,15 +1794,27 @@
"r5srDR": {
"defaultMessage": "Enter wallet password"
},
"rAQG0X": {
"defaultMessage": "Relay List Metadata"
},
"rMgF34": {
"defaultMessage": "Back up now"
},
"rRRXtB": {
"defaultMessage": "Lightning Zaps"
},
"rT14Ow": {
"defaultMessage": "Add Relays"
},
"reFEEC": {
"defaultMessage": "Reporting"
},
"rfuMjE": {
"defaultMessage": "(Default)"
},
"rkM7l8": {
"defaultMessage": "Encrypted Direct Message"
},
"rmdsT4": {
"defaultMessage": "{n} days"
},
@ -1656,12 +1836,30 @@
"saInmO": {
"defaultMessage": "The relay name shown is not the same as the full URL entered."
},
"saorw+": {
"defaultMessage": "Event Deletion Request"
},
"sfL/O+": {
"defaultMessage": "Muted notes will not be shown"
},
"t79a6U": {
"defaultMessage": "Connection Success:"
},
"tO1oq9": {
"defaultMessage": "Video Events"
},
"tOdNiY": {
"defaultMessage": "Dark"
},
"tRGdV1": {
"defaultMessage": "Versioned Encryption"
},
"tU0ADf": {
"defaultMessage": "Unknown NIP-{x}"
},
"tf1lIh": {
"defaultMessage": "Free"
},
"th5lxp": {
"defaultMessage": "Send note to a subset of your write relays"
},
@ -1692,6 +1890,9 @@
"uCk8r+": {
"defaultMessage": "Already have an account?"
},
"uD7Els": {
"defaultMessage": "External Identities in Profiles"
},
"uSV4Ti": {
"defaultMessage": "Reposts need to be manually confirmed"
},
@ -1749,6 +1950,9 @@
"wSZR47": {
"defaultMessage": "Submit"
},
"wc9st7": {
"defaultMessage": "Media Attachments"
},
"whSrs+": {
"defaultMessage": "Nostr Public Chat"
},
@ -1782,6 +1986,9 @@
"xIoGG9": {
"defaultMessage": "Go to"
},
"xPCyu+": {
"defaultMessage": "nostr: URI scheme"
},
"xSoIUU": {
"defaultMessage": "Worker Relay"
},
@ -1836,6 +2043,9 @@
"zcaOTs": {
"defaultMessage": "Zap amount in sats"
},
"zi9MdS": {
"defaultMessage": "Chess (PGN)"
},
"zm6qS1": {
"defaultMessage": "{n} mins to read"
},

View File

@ -2,6 +2,7 @@
"+D82kt": "Are you sure you want to repost: {id}",
"+PzQ9Y": "Payout Now",
"+QM0PJ": "Sync all events for your profile into local cache",
"+QMdsy": "Relay Stats",
"+UjDmN": "Logged in with write access",
"+Vxixo": "Secret Group Chat",
"+aZY2h": "Zap Type",
@ -14,6 +15,7 @@
"/B8zwF": "Your space the way you want it 😌",
"/GCoTA": "Clear",
"/JE/X+": "Account Support",
"/T7HId": "HTTP File Storage Integration",
"/Xf4UW": "Send anonymous usage metrics",
"/clOBU": "Weekly",
"/d6vEc": "Make your profile easier to find and share",
@ -25,11 +27,13 @@
"0BUTMv": "Search...",
"0MndVW": "Generic LNDHub wallet (BTCPayServer / Alby / LNBits)",
"0jOEtS": "Invalid LNURL",
"0kOBMu": "Handling Mentions",
"0mch2Y": "name has disallowed characters",
"0siT4z": "Politics",
"0uoY11": "Show Status",
"0yO7wF": "{n} secs",
"0zASjL": "Go",
"1/BFEj": "git stuff",
"1Mo59U": "Are you sure you want to remove this note from bookmarks?",
"1R43+L": "Enter Nostr Wallet Connect config",
"1UWegE": "Be sure to back up your keys!",
@ -41,6 +45,9 @@
"2/2yg+": "Add",
"25V4l1": "Banner",
"25WwxF": "Don't have an account?",
"28oKbu": "Moderated Communities",
"29sHFE": "Wallet Connect",
"2BBGxX": "Subject tag in text events",
"2IFGap": "Donate",
"2LbrkB": "Enter password",
"2O2sfp": "Finish",
@ -48,8 +55,10 @@
"2k0Cv+": "Dislikes ({n})",
"2mcwT8": "New Note",
"2oCF7O": "Followed by friends of friends",
"2raFAu": "Application-specific data",
"2ukA4d": "{n} hours",
"39AHJm": "Sign Up",
"3GWu6/": "User Statuses",
"3KNMbJ": "Articles",
"3QwfJR": "~{amount}",
"3cc4Ct": "Light",
@ -58,25 +67,32 @@
"3t3kok": "{n,plural,=1{{n} new note} other{{n} new notes}}",
"3tVy+Z": "{n} Followers",
"3yk8fB": "Wallet",
"40VR6s": "Nostr Connect",
"41BSaT": "Total Events:",
"450Fty": "None",
"47E53q": "Wiki",
"47FYwb": "Cancel",
"4IPzdn": "Primary Developers",
"4L2vUY": "Your new NIP-05 handle is:",
"4MjsHk": "Life",
"4OB335": "Dislike",
"4P/kKm": "Private Key Encryption",
"4Vmpt4": "Nostr Plebs is one of the first NIP-05 providers in the space and offers a good collection of domains at reasonable prices",
"4Z3t5i": "Use imgproxy to compress images",
"4emo2p": "Missing Relays",
"4rYCjn": "Note to Self",
"4wgYpI": "Recommended Application Handlers",
"5BVs2e": "zap",
"5CB6zB": "Zap Splits",
"5PRWs7": "Notifications API Enabled",
"5oTnfy": "Buy Handle",
"5qEWCr": "File Metadata",
"5u6iEc": "Transfer to Pubkey",
"5vMmmR": "Usernames are not unique on Nostr. The nostr address is your unique human-readable address that is unique to you upon registration.",
"5ykRmX": "Send zap",
"6/hB3S": "Watch Replay",
"62nsdy": "Retry",
"634VVz": "Connection Failed:",
"6559gb": "New follow list length {length}",
"65BmHb": "Failed to proxy image from {host}, click here to load directly",
"6OSOXl": "Reason: <i>{reason}</i>",
@ -84,21 +100,26 @@
"6ewQqw": "Likes ({n})",
"6k7xfM": "Trending notes",
"6mr8WU": "Followed by",
"6pdxsi": "Extra metadata fields and tags",
"6uMqL1": "Unpaid",
"6xap9L": "Good",
"7+Domh": "Notes",
"712i26": "Proxy uses HODL invoices to forward the payment, which hides the pubkey of your node",
"77nkEO": "Relay Information Document",
"7LFU8U": "Search Capability",
"7UOvbT": "Offline",
"7YkSA2": "Community Leader",
"7hp70g": "NIP-05",
"8/vBbP": "Reposts ({n})",
"89q5wc": "Confirm Reposts",
"8BDFvJ": "Conventions for clients' use of e and p tags in text events",
"8ED/4u": "Reply To",
"8HJxXG": "Sign up",
"8QDesP": "Zap {n} sats",
"8Rkoyb": "Recipient",
"8Y6bZQ": "Invalid zap split: {input}",
"8g2vyB": "name too long",
"8jmwT8": "bech32-encoded entities",
"8v1NN+": "Pairing phrase",
"9+Ddtu": "Next",
"9HU8vw": "Reply",
@ -125,9 +146,11 @@
"B6H7eJ": "nsec, npub, nip-05, hex",
"B7wvUM": "You can add a single or multiple relays, one per line.",
"BGCM48": "Write access to Snort relay, with 1 year of event retention",
"BQW4gi": "Relay-based Groups",
"BWpuKl": "Update",
"BjNwZW": "Nostr address (nip05)",
"Blxcdx": "Relay",
"Bo+O//": "HTTP Auth",
"C1LjMx": "Lightning Donation",
"C7642/": "Quote Repost",
"C81/uG": "Logout",
@ -142,6 +165,7 @@
"CsCUYo": "{n} sats",
"Cu/K85": "Translated from {lang}",
"CzHZoc": "Social Graph",
"D++Njw": "Text Note References",
"D+KzKd": "Automatically zap every note when loaded",
"D3idYv": "Settings",
"DBiVK1": "Cache",
@ -165,7 +189,9 @@
"EjFyoR": "On-chain Donation Address",
"EnCOBJ": "Buy",
"F3l7xL": "Add Account",
"F4eJ/3": "Classified Listings",
"FDguSC": "{n} Zaps",
"FHvSk3": "Authentication of clients to relays",
"FMfjrl": "Show status messages on profile pages",
"FSYL8G": "Trending Users",
"FcNSft": "Redirect issues HTTP redirect to the supplied lightning address",
@ -178,6 +204,7 @@
"G1BGCg": "Select Wallet",
"G3A56c": "Subscribed to Push",
"GFOoEE": "Salt",
"GIqktu": "Supported NIPs",
"GL8aXW": "Bookmarks ({n})",
"GSye7T": "Lightning Address",
"GUlSVG": "Claim your included Snort nostr address",
@ -186,6 +213,7 @@
"GspYR7": "{n} Dislike",
"Gxcr08": "Broadcast Event",
"H+vHiz": "Hex Key..",
"H/oroO": "Dealing with Unknown Events",
"H0JBH6": "Log Out",
"H0OG3T": "Leader Info",
"H6/kLh": "Order Paid!",
@ -196,6 +224,7 @@
"HbefNb": "Open Wallet",
"HhcAVH": "You don't follow this person, click here to load media from <i>{link}</i>, or update <a><i>your preferences</i></a> to always load media from everybody.",
"HqRNN8": "Support",
"HzSFeV": "Expiration Timestamp",
"I1AoOu": "Last post {time}",
"IEwZvs": "Are you sure you want to unpin this note?",
"IIOul1": "Account Data",
@ -203,6 +232,7 @@
"IOu4Xh": "You must be a {tier} subscriber to access {app} deck",
"IVbtTS": "Zap all {n} sats",
"IWz1ta": "Auto Translate",
"IcHcWj": "Last Seen:",
"Ig9/a1": "Sent {n} sats to {name}",
"IgsWFG": "Not followed by anyone you follow",
"IoQq+a": "Click here to load anyway",
@ -224,13 +254,19 @@
"JkLHGw": "Website",
"JmcxzF": "Relays are servers you connect to for sending and receiving events. Aim for 4-8 relays.",
"JymXbw": "Private Key",
"K1wl1/": "Average Latency:",
"K3r6DQ": "Delete",
"K7AkdL": "Show",
"K9zklU": "External Content IDs",
"KAhAcM": "Enter LNDHub config",
"KGmQjH": "Highlights",
"KQvWvD": "Deleted",
"KT9nox": "Protected Events",
"KahimY": "Unknown event kind: {kind}",
"KipVeG": "Mapping Nostr keys to DNS-based internet identifiers",
"KtsyO0": "Enter Pin",
"LBAnc7": "View as user?",
"LEmxc8": "Zap Goals",
"LKw/ue": "Check out the code {link}",
"LR1XjT": "Pin too short",
"LXxsbk": "Anonymous",
@ -238,6 +274,7 @@
"LhLvRx": "Name must be between 8 and 15 characters",
"LmdPXO": "Cannot verify Nostr Address",
"Lu5/Bj": "Open on Zapstr",
"LuDBLj": "Torrents",
"Lw+I+J": "{n,plural,=0{{name} zapped} other{{name} & {n} others zapped}}",
"LwYmVi": "Zaps on this note will be split to the following users.",
"M3Oirc": "Debug Menus",
@ -247,9 +284,11 @@
"MP54GY": "Wallet password",
"MWTx65": "Default Page",
"MiMipu": "Set as primary Nostr address (nip05)",
"MkQ4FX": "Proxy Tags",
"Ml7+RS": "Send this link to your friends and share the magic of the nostr.",
"Mrpkot": "Pay for subscription",
"MuVeKe": "Buy nostr address",
"Muhna4": "Counting results",
"MzRYWH": "Buying {item}",
"Mzizei": "Iris.to account",
"N2IrpM": "Confirm",
@ -258,15 +297,19 @@
"NdOYJJ": "Hmm nothing here.. Checkout {newUsersPage} to follow some recommended nostrich's!",
"NepkXH": "Can't vote with {amount} sats, please set a different default zap amount",
"NndBJE": "New users page",
"Nr9Yyx": "Reposts",
"NxzeNU": "Dead",
"O3Jz4E": "Use your invite code to earn sats!",
"OEW7yJ": "Zaps",
"OIqnZN": "OpenTimestamps Attestations for Events",
"OJHKIL": "Gift Wrap",
"OKhRC6": "Share",
"OLEm6z": "Unknown login error",
"OQSOJF": "Get a free nostr address",
"OQXnew": "You subscription is still active, you can't renew yet",
"ORGv1Q": "Created",
"OoZgbB": "Failed to update, please try again",
"OuProE": "Long-form Content",
"OxPdQ0": "Scanning {date}",
"P2o+ZZ": "Invalid Nostr Address",
"P61BTu": "Copy Event JSON",
@ -281,6 +324,7 @@
"QJfhKt": "The private key is like a password, but it cannot be reset. Guard it carefully and never show it to anyone. Once someone has your private key, they will have access to your account forever.",
"QWhotP": "Zap Pool only works if you use one of the supported wallet connections (WebLN, LNC, LNDHub or Nostr Wallet Connect)",
"Qxv0B2": "You currently have {number} sats in your zap pool.",
"Qy6/Ft": "Private Direct Messages",
"R/6nsx": "Subscription",
"R81upa": "People you follow",
"RDha9y": "Service Worker Not Running",
@ -288,6 +332,7 @@
"RahCRH": "Expired",
"RfhLwC": "By: {author}",
"RhDAoS": "Are you sure you want to delete {id}",
"RmxSZo": "Data Vending Machines",
"RoOyAh": "Relays",
"RrCui3": "Summary",
"Rs4kCE": "Bookmark",
@ -310,12 +355,12 @@
"TaeBqw": "Sign in with Nostr Extension",
"TdTXXf": "Learn more",
"TdtZQ5": "Crypto",
"TgDKhI": "Calendar Events",
"TpgeGw": "Hex Salt..",
"Tpy00S": "People",
"TvKqBp": "liked",
"TwyMau": "Account",
"U1aPPi": "Stop listening",
"UDYlxu": "Pending Subscriptions",
"UJTWqI": "Remove from my relays",
"ULXFfP": "Receive",
"UNjfWJ": "Check all event signatures received from relays",
@ -327,6 +372,7 @@
"Ups2/p": "Your application is pending",
"UrKTqQ": "You have an active iris.to account",
"UxgyeY": "Your referral code is {code}",
"V20Og0": "Labeling",
"VOjC1i": "Pick which upload service you want to upload attachments to",
"VR5eHw": "Public key (npub/nprofile)",
"VcwrfF": "Yes please",
@ -352,9 +398,11 @@
"YDMrKK": "Users",
"YDURw6": "Service URL",
"YR2I9M": "No keys, no {app}, There is no way to reset it if you don't back up. It only takes a minute.",
"YU7ZYp": "Public Chat",
"YXA3AH": "Enable reactions",
"Yf3DwC": "Connect a wallet to send instant payments",
"Z4BMCZ": "Enter pairing phrase",
"Z7kkeJ": "Delegated Event Signing",
"ZKORll": "Activate Now",
"ZLmyG9": "Contributors",
"ZS+jRE": "Send zap splits to",
@ -392,6 +440,7 @@
"cg1VJ2": "Connect Wallet",
"cuP16y": "Multi account support",
"cuV2gK": "name is registered",
"cw1Ftc": "Live Activities",
"cyR7Kh": "Back",
"d+6YsV": "Lists to mute:",
"d2ebEu": "Not Subscribed to Push",
@ -411,6 +460,7 @@
"eJj8HD": "Get Verified",
"eSzf2G": "A single zap of {nIn} sats will allocate {nOut} sats to the zap pool.",
"eXT2QQ": "Group Chat",
"eZtOxB": "window.nostr capability for web browsers",
"egib+2": "{n,plural,=1{& {n} other} other{& {n} others}}",
"ejEGdx": "Home",
"eoV49s": "Poor",
@ -436,7 +486,10 @@
"gczcC5": "Subscribe",
"geppt8": "{count} ({count2} in memory)",
"gjBiyj": "Loading...",
"gkMmvC": "Android Signer Application",
"gl1NeW": "Lists",
"grQ+mI": "Proof of Work",
"gtNjNP": "Basic protocol flow description",
"h7jvCs": "{site} is more fun together!",
"h8XMJL": "Badges",
"hF6IN2": "Prune Follow List",
@ -455,6 +508,7 @@
"iCqGww": "Reactions ({n})",
"iEoXYx": "DeepL translations",
"iGT1eE": "Prevent fake accounts from imitating you",
"iHN12u": "Admin",
"iICVoL": "{x} follows ({y} duplicates)",
"iNWbVV": "Handle",
"iXPL0Z": "Can't login with private key on an insecure connection, please use a Nostr key manager extension instead",
@ -482,6 +536,7 @@
"kuPHYE": "{n,plural,=0{{name} liked} other{{name} & {n} others liked}}",
"l+ikU1": "Everything in {plan}",
"l3H1EK": "Invite your friends",
"l3nTjd": "Basic key derivation from mnemonic seed phrase",
"lCILNz": "Buy Now",
"lD3+8a": "Pay",
"lEnclp": "My events: {n}",
@ -514,12 +569,13 @@
"odFwjL": "Follows only",
"ojzbwv": "Hey, it looks like you dont have a Nostr Address yet, you should get one! Check out {link}",
"p4N05H": "Upload",
"p85Uwy": "Active Subscriptions",
"p9Ps2l": "{x}/{y} have relays ({percent})",
"pEEBFk": "Reliable Relays",
"pI+77w": "Downloadable backups from Snort relay",
"pRess9": "ZapPool",
"plOM0t": "Custom Emoji",
"puLNUJ": "Pin",
"pyjJ5f": "Nostr Marketplace (for resilient marketplaces)",
"pzTOmv": "Followers",
"qD9EUF": "Email <> DM bridge for your Snort nostr address",
"qDwvZ4": "Unknown error",
@ -539,9 +595,13 @@
"qz9fty": "Incorrect pin",
"r3C4x/": "Software",
"r5srDR": "Enter wallet password",
"rAQG0X": "Relay List Metadata",
"rMgF34": "Back up now",
"rRRXtB": "Lightning Zaps",
"rT14Ow": "Add Relays",
"reFEEC": "Reporting",
"rfuMjE": "(Default)",
"rkM7l8": "Encrypted Direct Message",
"rmdsT4": "{n} days",
"rn52n9": "Public Chat Channels",
"rx1i0i": "Short link",
@ -549,8 +609,14 @@
"sUNhQE": "user",
"sZQzjQ": "Failed to parse zap split: {input}",
"saInmO": "The relay name shown is not the same as the full URL entered.",
"saorw+": "Event Deletion Request",
"sfL/O+": "Muted notes will not be shown",
"t79a6U": "Connection Success:",
"tO1oq9": "Video Events",
"tOdNiY": "Dark",
"tRGdV1": "Versioned Encryption",
"tU0ADf": "Unknown NIP-{x}",
"tf1lIh": "Free",
"th5lxp": "Send note to a subset of your write relays",
"thnRpU": "Getting NIP-05 verified can help:",
"tj6kdX": "{sign} {amount} sats",
@ -561,6 +627,7 @@
"u81G9+": "Uptime",
"u9NoC1": "Name must be less than {limit} characters",
"uCk8r+": "Already have an account?",
"uD7Els": "External Identities in Profiles",
"uSV4Ti": "Reposts need to be manually confirmed",
"uc0din": "Send sats splits to",
"ufvXH1": "Found {n} events",
@ -580,6 +647,7 @@
"w6qrwX": "NSFW",
"wEQDC6": "Edit",
"wSZR47": "Submit",
"wc9st7": "Media Attachments",
"whSrs+": "Nostr Public Chat",
"wih7iJ": "name is blocked",
"wofVHy": "Moderation",
@ -591,6 +659,7 @@
"xEjBS7": "For you",
"xIcAOU": "Votes by {type}",
"xIoGG9": "Go to",
"xPCyu+": "nostr: URI scheme",
"xSoIUU": "Worker Relay",
"xaj9Ba": "Provider",
"xbVgIm": "Automatically load media",
@ -609,6 +678,7 @@
"zINlao": "Owner",
"zQvVDJ": "All",
"zcaOTs": "Zap amount in sats",
"zi9MdS": "Chess (PGN)",
"zm6qS1": "{n} mins to read",
"zonsdq": "Failed to load LNURL service",
"zvCDao": "Automatically show latest notes",