mirror of
https://github.com/PR0M3TH3AN/VoxVera.git
synced 2026-10-27 09:40:40 +00:00
Bulletin board: add an Event ID column with a per-row copy button
Each flyer row now shows its event ID (shortened, full value in the title attribute) with a Copy button that copies the full 64-char id to the clipboard (flips to "Copied", falls back to selecting the text if the clipboard is blocked). The column is sortable like the others. Copy is handled by a single delegated listener on the tbody, since rows re-render on sort. Localized the new column header (col_event_id) across all 14 languages; Copy/ Copied already existed in the board strings. Updated the column e2e test (now 6 columns) and added a test for the id cell + copy target. Docs updated. Co-Authored-By: Claude Opus 4.8 <[email protected]>
This commit is contained in:
1 parent
82126d52d6
commit
0530dbc905
5 files changed
+102
-20
No files matched your search
@@ -19,7 +19,7 @@ The previous Tor/OnionShare CLI version is preserved on the `legacy-tor` branch.
|
||||
- **US Letter and A4.** Print size defaults to the right paper for the visitor's country (from the browser locale's region — `en-US` → Letter, `en-GB` → A4, no geolocation), with a manual override. Paper size is a local print preference and is never written into the event. See the [spec](docs/nostr-static-client-spec.md#print-paper-size).
|
||||
- **Fits-the-page editing.** Fields that would overflow the printable sheet are rejected as you type, with an inline error under the affected field. See the [field length cheat sheet](docs/flyer-field-limits.md) for how much text fits each field per language and paper size.
|
||||
- **QR codes.** The tear-off QR points at the poster URL (re-open and reprint); the main flyer QR points at the creator's content URL.
|
||||
- **Bulletin board (members-only, trust-filtered).** A small screen-only link below the flyer preview (outside the printed sheet, never printed) opens a spreadsheet-style page of the most recent flyers found on the relays — title, link, posting npub, language, and date — sortable by any column. Viewing the board requires connecting a Nostr identity — via a NIP-07 browser extension, by pasting a private key (`nsec`, decoded in-page and never stored), or by creating a new key (revealed so you can save it). The board then shows only flyers from authors in your **web of trust** (your NIP-02 follow graph), with a "Show all" opt-out. A brand-new key with no follows is seeded from a curated trust anchor so the board is never an unmoderated firehose. Connecting is not required to author or print flyers, which stay fully anonymous. See the [roadmap](docs/roadmap.md) for the phased trust model.
|
||||
- **Bulletin board (members-only, trust-filtered).** A small screen-only link below the flyer preview (outside the printed sheet, never printed) opens a spreadsheet-style page of the most recent flyers found on the relays — title, link, posting npub, language, date, and event ID (with a copy button) — sortable by any column. Viewing the board requires connecting a Nostr identity — via a NIP-07 browser extension, by pasting a private key (`nsec`, decoded in-page and never stored), or by creating a new key (revealed so you can save it). The board then shows only flyers from authors in your **web of trust** (your NIP-02 follow graph), with a "Show all" opt-out. A brand-new key with no follows is seeded from a curated trust anchor so the board is never an unmoderated firehose. Connecting is not required to author or print flyers, which stay fully anonymous. See the [roadmap](docs/roadmap.md) for the phased trust model.
|
||||
- **Self-contained.** No build step and no CDNs — Nostr and QR libraries are vendored under `site/vendor/`.
|
||||
|
||||
## What Is In `main`
|
||||
|
||||
@@ -196,9 +196,10 @@ Field capacity therefore varies by paper size; see
|
||||
`site/board.html` + `site/board.js` are a standalone page that lists the most
|
||||
recent flyers found on the default relays as a sortable, spreadsheet-style
|
||||
table: **title**, **link** (the poster URL, rendered as the word "link"),
|
||||
**posting npub**, **language**, and **posted date**. Any column header sorts
|
||||
(default is date descending); replaceable events are de-duplicated per
|
||||
author+`d` identifier, keeping the latest.
|
||||
**posting npub**, **language**, **posted date**, and the **event ID** (shown
|
||||
shortened, full value in the title attribute, with a per-row copy button). Any
|
||||
column header sorts (default is date descending); replaceable events are
|
||||
de-duplicated per author+`d` identifier, keeping the latest.
|
||||
|
||||
- The entry point is a small screen-only link below the flyer preview
|
||||
(`.board-link`, label `bulletin_board`), outside the printable sheet. It must
|
||||
|
||||
+28
-1
@@ -325,12 +325,13 @@ test.describe("VoxVera static client", () => {
|
||||
await page.locator("#board-connect").click();
|
||||
await expect(page.locator("#board-content")).toBeVisible();
|
||||
const headers = page.locator("#board-head th");
|
||||
await expect(headers).toHaveCount(5);
|
||||
await expect(headers).toHaveCount(6);
|
||||
await expect(headers.nth(0)).toContainText("Title");
|
||||
await expect(headers.nth(1)).toContainText("Link");
|
||||
await expect(headers.nth(2)).toContainText("Posted by");
|
||||
await expect(headers.nth(3)).toContainText("Language");
|
||||
await expect(headers.nth(4)).toContainText("Posted");
|
||||
await expect(headers.nth(5)).toContainText("Event ID");
|
||||
// Default sort is date descending.
|
||||
await expect(headers.nth(4).locator(".sort-indicator")).toContainText("▼");
|
||||
// Clicking the Title header makes it the active ascending sort.
|
||||
@@ -338,6 +339,32 @@ test.describe("VoxVera static client", () => {
|
||||
await expect(headers.nth(0).locator(".sort-indicator")).toContainText("▲");
|
||||
});
|
||||
|
||||
test("the board shows each flyer's event ID with a copy button", async ({ page }) => {
|
||||
const EVENT_ID = "a1b2c3d4e5f6071829303142535465768798a0b1c2d3e4f50617283940a1b2c3";
|
||||
await stubNip07(page);
|
||||
await stubRelays(page, [{
|
||||
id: EVENT_ID, kind: 30078, pubkey: "f".repeat(64), created_at: 5000,
|
||||
tags: [["d", "voxvera:id-test"], ["t", "voxvera"], ["t", "flyer"], ["language", "en"]],
|
||||
content: JSON.stringify({
|
||||
type: "voxvera_flyer", version: 1, folder_name: "id-test", lang: "en",
|
||||
name: "N", title: "ID Test Flyer", subtitle: "s", headline: "h", content: "c",
|
||||
url_message: "m", url: "https://example.com/i", footer_message: "f",
|
||||
tear_off_link: "https://voxvera.org/#naddr1i", qr_target: "flyer_url"
|
||||
})
|
||||
}]);
|
||||
await page.goto("/board.html");
|
||||
await page.locator("#board-connect").click();
|
||||
await expect(page.locator("#board-content")).toBeVisible();
|
||||
// Shortened id is displayed, the full id is in the title, and the copy button
|
||||
// carries the full id as its copy target.
|
||||
const idCell = page.locator(".board-eventid");
|
||||
await expect(idCell).toContainText("a1b2c3d4");
|
||||
await expect(idCell).toHaveAttribute("title", EVENT_ID);
|
||||
const copyBtn = page.locator(".board-copy");
|
||||
await expect(copyBtn).toHaveText("Copy");
|
||||
await expect(copyBtn).toHaveAttribute("data-copy", EVENT_ID);
|
||||
});
|
||||
|
||||
test("bulletin board is gated behind a Nostr connection", async ({ page }) => {
|
||||
// No NIP-07 provider: the gate is shown, the table is hidden, and trying to
|
||||
// connect reports that no extension was found.
|
||||
|
||||
+50
-15
@@ -31,20 +31,20 @@
|
||||
const LOCALES = window.VoxVeraLocales || {};
|
||||
|
||||
const BOARD_UI = {
|
||||
en: { title: "Bulletin Board", subtitle: "Most recent flyers published to the relays.", col_title: "Title", col_link: "Link", col_author: "Posted by", col_language: "Language", col_date: "Posted", link_text: "link", loading: "Loading flyers…", empty: "No flyers found yet.", error: "Could not reach the relays.", back: "← Back to VoxVera", gate_title: "Connect to view the board", gate_body: "The bulletin board is visible to people who connect a Nostr identity. Choose how to connect below.", connect: "Connect", connecting: "Connecting…", no_extension: "No Nostr extension found. Install a NIP-07 browser extension to view the board.", connect_failed: "Connection cancelled or failed. Please try again.", disconnect: "Disconnect", filter_following: "Showing flyers from people you follow.", filter_seeded: "You don't follow anyone yet — showing a curated set from VoxVera's trust network.", filter_all: "Showing all flyers on the relays (unfiltered).", filter_unavailable: "Couldn't load a trust network — showing all flyers.", filter_empty: "No flyers from your trust network yet. Turn on “Show all” to see everything.", show_all_label: "Show all flyers", connect_extension: "Browser extension (NIP-07)", connect_nsec: "Use a private key (nsec)", connect_generate: "Create a new key", key_note: "Your key stays in this browser and is only used to read your follow list — it is never sent anywhere.", invalid_nsec: "That doesn't look like a valid nsec key.", gen_warning: "This is your new secret key. Anyone who has it controls this identity — save it somewhere safe, because VoxVera can't recover it.", copy: "Copy", copied: "Copied", continue_label: "Continue" },
|
||||
es: { title: "Tablón de anuncios", subtitle: "Carteles más recientes publicados en los relés.", col_title: "Título", col_link: "Enlace", col_author: "Publicado por", col_language: "Idioma", col_date: "Publicado", link_text: "enlace", loading: "Cargando carteles…", empty: "Aún no se encontraron carteles.", error: "No se pudo conectar con los relés.", back: "← Volver a VoxVera", gate_title: "Conéctate para ver el tablón", gate_body: "El tablón de anuncios es visible para quienes conectan una identidad Nostr. Elige abajo cómo conectarte.", connect: "Conectar", connecting: "Conectando…", no_extension: "No se encontró ninguna extensión Nostr. Instala una extensión de navegador NIP-07 para ver el tablón.", connect_failed: "Conexión cancelada o fallida. Inténtalo de nuevo.", disconnect: "Desconectar", filter_following: "Mostrando carteles de las personas que sigues.", filter_seeded: "Aún no sigues a nadie: mostramos un conjunto curado de la red de confianza de VoxVera.", filter_all: "Mostrando todos los carteles de los relés (sin filtrar).", filter_unavailable: "No se pudo cargar una red de confianza: mostrando todos los carteles.", filter_empty: "Aún no hay carteles de tu red de confianza. Activa «Mostrar todos» para verlo todo.", show_all_label: "Mostrar todos los carteles", connect_extension: "Extensión del navegador (NIP-07)", connect_nsec: "Usar una clave privada (nsec)", connect_generate: "Crear una clave nueva", key_note: "Tu clave permanece en este navegador y solo se usa para leer tu lista de seguidos; nunca se envía a ningún sitio.", invalid_nsec: "Eso no parece una clave nsec válida.", gen_warning: "Esta es tu nueva clave secreta. Cualquiera que la tenga controla esta identidad: guárdala en un lugar seguro, porque VoxVera no puede recuperarla.", copy: "Copiar", copied: "Copiado", continue_label: "Continuar" },
|
||||
de: { title: "Schwarzes Brett", subtitle: "Neueste auf den Relays veröffentlichte Plakate.", col_title: "Titel", col_link: "Link", col_author: "Veröffentlicht von", col_language: "Sprache", col_date: "Veröffentlicht", link_text: "Link", loading: "Plakate werden geladen…", empty: "Noch keine Plakate gefunden.", error: "Relays konnten nicht erreicht werden.", back: "← Zurück zu VoxVera", gate_title: "Verbinden, um das Brett zu sehen", gate_body: "Das Schwarze Brett ist für Personen sichtbar, die eine Nostr-Identität verbinden. Wähle unten, wie du dich verbinden möchtest.", connect: "Verbinden", connecting: "Verbindung…", no_extension: "Keine Nostr-Erweiterung gefunden. Installiere eine NIP-07-Browsererweiterung, um das Brett zu sehen.", connect_failed: "Verbindung abgebrochen oder fehlgeschlagen. Bitte erneut versuchen.", disconnect: "Trennen", filter_following: "Plakate von Personen, denen du folgst.", filter_seeded: "Du folgst noch niemandem – wir zeigen eine kuratierte Auswahl aus dem Vertrauensnetz von VoxVera.", filter_all: "Alle Plakate auf den Relays (ungefiltert).", filter_unavailable: "Vertrauensnetz konnte nicht geladen werden – alle Plakate werden angezeigt.", filter_empty: "Noch keine Plakate aus deinem Vertrauensnetz. Aktiviere „Alle anzeigen“, um alles zu sehen.", show_all_label: "Alle Plakate anzeigen", connect_extension: "Browser-Erweiterung (NIP-07)", connect_nsec: "Privaten Schlüssel (nsec) verwenden", connect_generate: "Neuen Schlüssel erstellen", key_note: "Dein Schlüssel bleibt in diesem Browser und wird nur zum Lesen deiner Folgeliste verwendet – er wird nirgendwohin gesendet.", invalid_nsec: "Das sieht nicht nach einem gültigen nsec-Schlüssel aus.", gen_warning: "Das ist dein neuer geheimer Schlüssel. Wer ihn hat, kontrolliert diese Identität – bewahre ihn sicher auf, denn VoxVera kann ihn nicht wiederherstellen.", copy: "Kopieren", copied: "Kopiert", continue_label: "Weiter" },
|
||||
fr: { title: "Tableau d'affichage", subtitle: "Affiches les plus récentes publiées sur les relais.", col_title: "Titre", col_link: "Lien", col_author: "Publié par", col_language: "Langue", col_date: "Publié", link_text: "lien", loading: "Chargement des affiches…", empty: "Aucune affiche trouvée pour l'instant.", error: "Impossible de joindre les relais.", back: "← Retour à VoxVera", gate_title: "Connectez-vous pour voir le tableau", gate_body: "Le tableau d'affichage est visible par les personnes qui connectent une identité Nostr. Choisissez ci-dessous comment vous connecter.", connect: "Se connecter", connecting: "Connexion…", no_extension: "Aucune extension Nostr trouvée. Installez une extension de navigateur NIP-07 pour voir le tableau.", connect_failed: "Connexion annulée ou échouée. Veuillez réessayer.", disconnect: "Se déconnecter", filter_following: "Affiches des personnes que vous suivez.", filter_seeded: "Vous ne suivez encore personne – voici une sélection issue du réseau de confiance de VoxVera.", filter_all: "Toutes les affiches sur les relais (non filtrées).", filter_unavailable: "Impossible de charger un réseau de confiance – toutes les affiches sont affichées.", filter_empty: "Aucune affiche de votre réseau de confiance pour l'instant. Activez « Tout afficher » pour tout voir.", show_all_label: "Afficher toutes les affiches", connect_extension: "Extension de navigateur (NIP-07)", connect_nsec: "Utiliser une clé privée (nsec)", connect_generate: "Créer une nouvelle clé", key_note: "Votre clé reste dans ce navigateur et ne sert qu'à lire votre liste d'abonnements ; elle n'est jamais envoyée nulle part.", invalid_nsec: "Cela ne ressemble pas à une clé nsec valide.", gen_warning: "Voici votre nouvelle clé secrète. Quiconque la possède contrôle cette identité — conservez-la en lieu sûr, car VoxVera ne peut pas la récupérer.", copy: "Copier", copied: "Copié", continue_label: "Continuer" },
|
||||
ru: { title: "Доска объявлений", subtitle: "Последние листовки, опубликованные на реле.", col_title: "Заголовок", col_link: "Ссылка", col_author: "Опубликовал", col_language: "Язык", col_date: "Опубликовано", link_text: "ссылка", loading: "Загрузка листовок…", empty: "Листовки пока не найдены.", error: "Не удалось подключиться к реле.", back: "← Назад в VoxVera", gate_title: "Подключитесь, чтобы увидеть доску", gate_body: "Доска объявлений видна тем, кто подключил идентификацию Nostr. Выберите способ подключения ниже.", connect: "Подключить", connecting: "Подключение…", no_extension: "Расширение Nostr не найдено. Установите расширение браузера NIP-07, чтобы увидеть доску.", connect_failed: "Подключение отменено или не удалось. Повторите попытку.", disconnect: "Отключить", filter_following: "Показаны листовки от тех, на кого вы подписаны.", filter_seeded: "Вы пока ни на кого не подписаны — показываем подборку из сети доверия VoxVera.", filter_all: "Показаны все листовки на реле (без фильтра).", filter_unavailable: "Не удалось загрузить сеть доверия — показаны все листовки.", filter_empty: "Пока нет листовок из вашей сети доверия. Включите «Показать все», чтобы увидеть всё.", show_all_label: "Показать все листовки", connect_extension: "Расширение браузера (NIP-07)", connect_nsec: "Использовать приватный ключ (nsec)", connect_generate: "Создать новый ключ", key_note: "Ваш ключ остаётся в этом браузере и используется только для чтения вашего списка подписок — он никуда не отправляется.", invalid_nsec: "Это не похоже на действительный ключ nsec.", gen_warning: "Это ваш новый секретный ключ. Любой, у кого он есть, управляет этой личностью — сохраните его в надёжном месте, потому что VoxVera не сможет его восстановить.", copy: "Копировать", copied: "Скопировано", continue_label: "Продолжить" },
|
||||
he: { title: "לוח מודעות", subtitle: "הכרזות האחרונות שפורסמו לממסרים.", col_title: "כותרת", col_link: "קישור", col_author: "פורסם על ידי", col_language: "שפה", col_date: "פורסם", link_text: "קישור", loading: "טוען כרזות…", empty: "לא נמצאו כרזות עדיין.", error: "לא ניתן להתחבר לממסרים.", back: "← חזרה ל-VoxVera", gate_title: "התחבר כדי לראות את הלוח", gate_body: "לוח המודעות גלוי למי שמחבר זהות Nostr. בחר למטה כיצד להתחבר.", connect: "התחבר", connecting: "מתחבר…", no_extension: "לא נמצא תוסף Nostr. התקן תוסף דפדפן NIP-07 כדי לראות את הלוח.", connect_failed: "ההתחברות בוטלה או נכשלה. נסה שוב.", disconnect: "התנתק", filter_following: "מציג כרזות מאנשים שאתה עוקב אחריהם.", filter_seeded: "אינך עוקב עדיין אחר אף אחד — מוצגת בחירה מרשת האמון של VoxVera.", filter_all: "מציג את כל הכרזות בממסרים (ללא סינון).", filter_unavailable: "לא ניתן לטעון רשת אמון — מוצגות כל הכרזות.", filter_empty: "אין עדיין כרזות מרשת האמון שלך. הפעל «הצג הכול» כדי לראות הכול.", show_all_label: "הצג את כל הכרזות", connect_extension: "תוסף דפדפן (NIP-07)", connect_nsec: "השתמש במפתח פרטי (nsec)", connect_generate: "צור מפתח חדש", key_note: "המפתח שלך נשאר בדפדפן זה ומשמש רק לקריאת רשימת העוקבים שלך — הוא לעולם לא נשלח לשום מקום.", invalid_nsec: "זה לא נראה כמו מפתח nsec תקין.", gen_warning: "זהו המפתח הסודי החדש שלך. כל מי שיש לו אותו שולט בזהות הזו — שמור אותו במקום בטוח, מכיוון ש-VoxVera אינה יכולה לשחזר אותו.", copy: "העתק", copied: "הועתק", continue_label: "המשך" },
|
||||
ar: { title: "لوحة الإعلانات", subtitle: "أحدث الملصقات المنشورة على المرحلات.", col_title: "العنوان", col_link: "رابط", col_author: "نُشر بواسطة", col_language: "اللغة", col_date: "تاريخ النشر", link_text: "رابط", loading: "جارٍ تحميل الملصقات…", empty: "لم يتم العثور على ملصقات بعد.", error: "تعذّر الوصول إلى المرحلات.", back: "← العودة إلى VoxVera", gate_title: "اتصل لعرض اللوحة", gate_body: "لوحة الإعلانات مرئية لمن يربط هوية Nostr. اختر أدناه كيفية الاتصال.", connect: "اتصال", connecting: "جارٍ الاتصال…", no_extension: "لم يتم العثور على إضافة Nostr. ثبّت إضافة متصفح NIP-07 لعرض اللوحة.", connect_failed: "تم إلغاء الاتصال أو فشل. يرجى المحاولة مرة أخرى.", disconnect: "قطع الاتصال", filter_following: "عرض الملصقات من الأشخاص الذين تتابعهم.", filter_seeded: "أنت لا تتابع أحدًا بعد — نعرض مجموعة مختارة من شبكة الثقة في VoxVera.", filter_all: "عرض جميع الملصقات على المرحلات (دون تصفية).", filter_unavailable: "تعذّر تحميل شبكة ثقة — يتم عرض جميع الملصقات.", filter_empty: "لا توجد ملصقات من شبكة الثقة الخاصة بك بعد. فعّل «عرض الكل» لرؤية كل شيء.", show_all_label: "عرض جميع الملصقات", connect_extension: "إضافة المتصفح (NIP-07)", connect_nsec: "استخدام مفتاح خاص (nsec)", connect_generate: "إنشاء مفتاح جديد", key_note: "يبقى مفتاحك في هذا المتصفح ويُستخدم فقط لقراءة قائمة متابَعاتك — ولا يُرسل إلى أي مكان أبدًا.", invalid_nsec: "لا يبدو هذا مفتاح nsec صالحًا.", gen_warning: "هذا مفتاحك السري الجديد. أي شخص يملكه يتحكم في هذه الهوية — احفظه في مكان آمن، لأن VoxVera لا يمكنها استعادته.", copy: "نسخ", copied: "تم النسخ", continue_label: "متابعة" },
|
||||
fa: { title: "تابلوی اعلانات", subtitle: "جدیدترین پوسترهای منتشرشده روی رلهها.", col_title: "عنوان", col_link: "پیوند", col_author: "منتشرشده توسط", col_language: "زبان", col_date: "تاریخ انتشار", link_text: "پیوند", loading: "در حال بارگذاری پوسترها…", empty: "هنوز پوستری یافت نشد.", error: "اتصال به رلهها ممکن نشد.", back: "← بازگشت به VoxVera", gate_title: "برای مشاهده تابلو متصل شوید", gate_body: "تابلوی اعلانات برای کسانی که هویت Nostr را متصل میکنند قابل مشاهده است. در زیر نحوه اتصال را انتخاب کنید.", connect: "اتصال", connecting: "در حال اتصال…", no_extension: "هیچ افزونه Nostr یافت نشد. برای مشاهده تابلو یک افزونه مرورگر NIP-07 نصب کنید.", connect_failed: "اتصال لغو شد یا ناموفق بود. لطفاً دوباره تلاش کنید.", disconnect: "قطع اتصال", filter_following: "نمایش پوسترها از افرادی که دنبال میکنید.", filter_seeded: "هنوز کسی را دنبال نمیکنید — مجموعهای منتخب از شبکه اعتماد VoxVera نمایش داده میشود.", filter_all: "نمایش همه پوسترها روی رلهها (بدون فیلتر).", filter_unavailable: "بارگذاری شبکه اعتماد ممکن نشد — همه پوسترها نمایش داده میشوند.", filter_empty: "هنوز پوستری از شبکه اعتماد شما نیست. «نمایش همه» را روشن کنید تا همه را ببینید.", show_all_label: "نمایش همه پوسترها", connect_extension: "افزونه مرورگر (NIP-07)", connect_nsec: "استفاده از کلید خصوصی (nsec)", connect_generate: "ایجاد کلید جدید", key_note: "کلید شما در همین مرورگر میماند و فقط برای خواندن فهرست دنبالشدههای شما استفاده میشود — هرگز به جایی ارسال نمیشود.", invalid_nsec: "این یک کلید nsec معتبر به نظر نمیرسد.", gen_warning: "این کلید مخفی جدید شماست. هر کسی آن را داشته باشد این هویت را کنترل میکند — آن را جایی امن ذخیره کنید، زیرا VoxVera نمیتواند آن را بازیابی کند.", copy: "کپی", copied: "کپی شد", continue_label: "ادامه" },
|
||||
hi: { title: "बुलेटिन बोर्ड", subtitle: "रिले पर प्रकाशित सबसे हाल के पोस्टर।", col_title: "शीर्षक", col_link: "लिंक", col_author: "द्वारा पोस्ट किया गया", col_language: "भाषा", col_date: "पोस्ट किया गया", link_text: "लिंक", loading: "पोस्टर लोड हो रहे हैं…", empty: "अभी तक कोई पोस्टर नहीं मिला।", error: "रिले तक नहीं पहुँच सके।", back: "← VoxVera पर वापस", gate_title: "बोर्ड देखने के लिए कनेक्ट करें", gate_body: "बुलेटिन बोर्ड उन लोगों को दिखता है जो Nostr पहचान कनेक्ट करते हैं। नीचे चुनें कि कैसे कनेक्ट करना है।", connect: "कनेक्ट करें", connecting: "कनेक्ट हो रहा है…", no_extension: "कोई Nostr एक्सटेंशन नहीं मिला। बोर्ड देखने के लिए NIP-07 ब्राउज़र एक्सटेंशन इंस्टॉल करें।", connect_failed: "कनेक्शन रद्द या विफल। कृपया पुनः प्रयास करें।", disconnect: "डिस्कनेक्ट करें", filter_following: "उन लोगों के पोस्टर दिखा रहे हैं जिन्हें आप फ़ॉलो करते हैं।", filter_seeded: "आप अभी किसी को फ़ॉलो नहीं करते — VoxVera के विश्वास नेटवर्क से चुनिंदा पोस्टर दिखा रहे हैं।", filter_all: "रिले पर सभी पोस्टर दिखा रहे हैं (बिना फ़िल्टर)।", filter_unavailable: "विश्वास नेटवर्क लोड नहीं हो सका — सभी पोस्टर दिखा रहे हैं।", filter_empty: "आपके विश्वास नेटवर्क से अभी कोई पोस्टर नहीं। सब कुछ देखने के लिए “सभी दिखाएँ” चालू करें।", show_all_label: "सभी पोस्टर दिखाएँ", connect_extension: "ब्राउज़र एक्सटेंशन (NIP-07)", connect_nsec: "निजी कुंजी (nsec) का उपयोग करें", connect_generate: "नई कुंजी बनाएँ", key_note: "आपकी कुंजी इसी ब्राउज़र में रहती है और केवल आपकी फ़ॉलो सूची पढ़ने के लिए उपयोग होती है — इसे कहीं नहीं भेजा जाता।", invalid_nsec: "यह एक मान्य nsec कुंजी नहीं लगती।", gen_warning: "यह आपकी नई गुप्त कुंजी है। जिसके पास यह होगी वह इस पहचान को नियंत्रित कर सकता है — इसे सुरक्षित जगह सहेजें, क्योंकि VoxVera इसे पुनर्प्राप्त नहीं कर सकता।", copy: "कॉपी करें", copied: "कॉपी किया गया", continue_label: "जारी रखें" },
|
||||
ja: { title: "掲示板", subtitle: "リレーに公開された最新のちらし。", col_title: "タイトル", col_link: "リンク", col_author: "投稿者", col_language: "言語", col_date: "投稿日", link_text: "リンク", loading: "ちらしを読み込んでいます…", empty: "まだちらしが見つかりません。", error: "リレーに接続できませんでした。", back: "← VoxVera に戻る", gate_title: "掲示板を見るには接続してください", gate_body: "掲示板はNostr IDを接続した人に表示されます。下で接続方法を選んでください。", connect: "接続", connecting: "接続中…", no_extension: "Nostr拡張機能が見つかりません。掲示板を見るにはNIP-07ブラウザ拡張機能をインストールしてください。", connect_failed: "接続がキャンセルされたか失敗しました。もう一度お試しください。", disconnect: "切断", filter_following: "フォローしている人のちらしを表示しています。", filter_seeded: "まだ誰もフォローしていません。VoxVera の信頼ネットワークから厳選したものを表示しています。", filter_all: "リレー上のすべてのちらしを表示しています(フィルターなし)。", filter_unavailable: "信頼ネットワークを読み込めませんでした。すべてのちらしを表示しています。", filter_empty: "あなたの信頼ネットワークのちらしはまだありません。「すべて表示」をオンにするとすべて表示されます。", show_all_label: "すべてのちらしを表示", connect_extension: "ブラウザ拡張機能 (NIP-07)", connect_nsec: "秘密鍵 (nsec) を使う", connect_generate: "新しい鍵を作成", key_note: "鍵はこのブラウザ内に留まり、フォローリストの読み取りにのみ使用されます。どこにも送信されません。", invalid_nsec: "有効な nsec 鍵ではないようです。", gen_warning: "これはあなたの新しい秘密鍵です。これを持つ人は誰でもこの ID を操作できます。VoxVera は復元できないので、安全な場所に保存してください。", copy: "コピー", copied: "コピーしました", continue_label: "続行" },
|
||||
pt: { title: "Quadro de avisos", subtitle: "Panfletos mais recentes publicados nos relés.", col_title: "Título", col_link: "Link", col_author: "Publicado por", col_language: "Idioma", col_date: "Publicado", link_text: "link", loading: "Carregando panfletos…", empty: "Nenhum panfleto encontrado ainda.", error: "Não foi possível acessar os relés.", back: "← Voltar ao VoxVera", gate_title: "Conecte-se para ver o quadro", gate_body: "O quadro de avisos é visível para quem conecta uma identidade Nostr. Escolha abaixo como se conectar.", connect: "Conectar", connecting: "Conectando…", no_extension: "Nenhuma extensão Nostr encontrada. Instale uma extensão de navegador NIP-07 para ver o quadro.", connect_failed: "Conexão cancelada ou falhou. Tente novamente.", disconnect: "Desconectar", filter_following: "Mostrando panfletos de pessoas que você segue.", filter_seeded: "Você ainda não segue ninguém — mostrando uma seleção da rede de confiança da VoxVera.", filter_all: "Mostrando todos os panfletos nos relés (sem filtro).", filter_unavailable: "Não foi possível carregar uma rede de confiança — mostrando todos os panfletos.", filter_empty: "Ainda não há panfletos da sua rede de confiança. Ative “Mostrar todos” para ver tudo.", show_all_label: "Mostrar todos os panfletos", connect_extension: "Extensão do navegador (NIP-07)", connect_nsec: "Usar uma chave privada (nsec)", connect_generate: "Criar uma nova chave", key_note: "Sua chave permanece neste navegador e é usada apenas para ler sua lista de seguidos — nunca é enviada para lugar algum.", invalid_nsec: "Isso não parece uma chave nsec válida.", gen_warning: "Esta é sua nova chave secreta. Qualquer pessoa que a tenha controla esta identidade — guarde-a em local seguro, pois o VoxVera não pode recuperá-la.", copy: "Copiar", copied: "Copiado", continue_label: "Continuar" },
|
||||
sw: { title: "Ubao wa matangazo", subtitle: "Mabango ya hivi karibuni yaliyochapishwa kwenye relay.", col_title: "Kichwa", col_link: "Kiungo", col_author: "Imechapishwa na", col_language: "Lugha", col_date: "Imechapishwa", link_text: "kiungo", loading: "Inapakia mabango…", empty: "Hakuna mabango yaliyopatikana bado.", error: "Imeshindwa kufikia relay.", back: "← Rudi VoxVera", gate_title: "Unganisha ili kuona ubao", gate_body: "Ubao wa matangazo unaonekana kwa wale wanaounganisha kitambulisho cha Nostr. Chagua hapa chini jinsi ya kuunganisha.", connect: "Unganisha", connecting: "Inaunganisha…", no_extension: "Hakuna kiendelezi cha Nostr kilichopatikana. Sakinisha kiendelezi cha kivinjari cha NIP-07 ili kuona ubao.", connect_failed: "Muunganisho umeghairiwa au umeshindwa. Tafadhali jaribu tena.", disconnect: "Tenganisha", filter_following: "Inaonyesha mabango kutoka kwa watu unaowafuata.", filter_seeded: "Bado hufuati mtu yeyote — tunaonyesha mkusanyiko uliochaguliwa kutoka mtandao wa kuaminiana wa VoxVera.", filter_all: "Inaonyesha mabango yote kwenye relay (bila kuchuja).", filter_unavailable: "Imeshindwa kupakia mtandao wa kuaminiana — inaonyesha mabango yote.", filter_empty: "Bado hakuna mabango kutoka mtandao wako wa kuaminiana. Washa “Onyesha yote” kuona yote.", show_all_label: "Onyesha mabango yote", connect_extension: "Kiendelezi cha kivinjari (NIP-07)", connect_nsec: "Tumia ufunguo wa faragha (nsec)", connect_generate: "Tengeneza ufunguo mpya", key_note: "Ufunguo wako unabaki katika kivinjari hiki na hutumika tu kusoma orodha yako ya unaowafuata — haupelekwi popote.", invalid_nsec: "Huu hauonekani kama ufunguo halali wa nsec.", gen_warning: "Huu ndio ufunguo wako mpya wa siri. Yeyote aliye nao anadhibiti utambulisho huu — uhifadhi mahali salama, kwa sababu VoxVera haiwezi kuurejesha.", copy: "Nakili", copied: "Imenakiliwa", continue_label: "Endelea" },
|
||||
tr: { title: "İlan panosu", subtitle: "Rölelere yayımlanan en yeni el ilanları.", col_title: "Başlık", col_link: "Bağlantı", col_author: "Yayımlayan", col_language: "Dil", col_date: "Yayımlandı", link_text: "bağlantı", loading: "El ilanları yükleniyor…", empty: "Henüz el ilanı bulunamadı.", error: "Rölelere ulaşılamadı.", back: "← VoxVera'ya dön", gate_title: "Panoyu görmek için bağlanın", gate_body: "İlan panosu, bir Nostr kimliği bağlayanlara görünür. Aşağıdan nasıl bağlanacağını seç.", connect: "Bağlan", connecting: "Bağlanıyor…", no_extension: "Nostr uzantısı bulunamadı. Panoyu görmek için bir NIP-07 tarayıcı uzantısı kurun.", connect_failed: "Bağlantı iptal edildi veya başarısız oldu. Lütfen tekrar deneyin.", disconnect: "Bağlantıyı kes", filter_following: "Takip ettiğin kişilerin el ilanları gösteriliyor.", filter_seeded: "Henüz kimseyi takip etmiyorsun — VoxVera'nın güven ağından seçilmiş bir küme gösteriliyor.", filter_all: "Rölelerdeki tüm el ilanları gösteriliyor (filtresiz).", filter_unavailable: "Güven ağı yüklenemedi — tüm el ilanları gösteriliyor.", filter_empty: "Güven ağından henüz el ilanı yok. Her şeyi görmek için “Tümünü göster”i aç.", show_all_label: "Tüm el ilanlarını göster", connect_extension: "Tarayıcı uzantısı (NIP-07)", connect_nsec: "Özel anahtar (nsec) kullan", connect_generate: "Yeni anahtar oluştur", key_note: "Anahtarın bu tarayıcıda kalır ve yalnızca takip listeni okumak için kullanılır — hiçbir yere gönderilmez.", invalid_nsec: "Bu geçerli bir nsec anahtarı gibi görünmüyor.", gen_warning: "Bu senin yeni gizli anahtarın. Ona sahip olan herkes bu kimliği kontrol eder — güvenli bir yerde sakla, çünkü VoxVera onu kurtaramaz.", copy: "Kopyala", copied: "Kopyalandı", continue_label: "Devam et" },
|
||||
zh: { title: "公告栏", subtitle: "发布到中继的最新传单。", col_title: "标题", col_link: "链接", col_author: "发布者", col_language: "语言", col_date: "发布时间", link_text: "链接", loading: "正在加载传单…", empty: "尚未找到传单。", error: "无法连接到中继。", back: "← 返回 VoxVera", gate_title: "连接以查看公告栏", gate_body: "公告栏对连接了 Nostr 身份的人可见。请在下方选择连接方式。", connect: "连接", connecting: "正在连接…", no_extension: "未找到 Nostr 扩展。请安装 NIP-07 浏览器扩展以查看公告栏。", connect_failed: "连接已取消或失败。请重试。", disconnect: "断开连接", filter_following: "正在显示你关注的人的传单。", filter_seeded: "你还没有关注任何人——正在显示来自 VoxVera 信任网络的精选内容。", filter_all: "正在显示中继上的所有传单(未过滤)。", filter_unavailable: "无法加载信任网络——正在显示所有传单。", filter_empty: "你的信任网络暂时没有传单。开启“显示全部”以查看所有内容。", show_all_label: "显示所有传单", connect_extension: "浏览器扩展 (NIP-07)", connect_nsec: "使用私钥 (nsec)", connect_generate: "创建新密钥", key_note: "你的密钥保留在此浏览器中,仅用于读取你的关注列表——绝不会发送到任何地方。", invalid_nsec: "这看起来不是有效的 nsec 密钥。", gen_warning: "这是你的新私钥。拥有它的任何人都能控制此身份——请妥善保存,因为 VoxVera 无法恢复它。", copy: "复制", copied: "已复制", continue_label: "继续" }
|
||||
en: { title: "Bulletin Board", subtitle: "Most recent flyers published to the relays.", col_title: "Title", col_link: "Link", col_author: "Posted by", col_language: "Language", col_date: "Posted", col_event_id: "Event ID",link_text: "link", loading: "Loading flyers…", empty: "No flyers found yet.", error: "Could not reach the relays.", back: "← Back to VoxVera", gate_title: "Connect to view the board", gate_body: "The bulletin board is visible to people who connect a Nostr identity. Choose how to connect below.", connect: "Connect", connecting: "Connecting…", no_extension: "No Nostr extension found. Install a NIP-07 browser extension to view the board.", connect_failed: "Connection cancelled or failed. Please try again.", disconnect: "Disconnect", filter_following: "Showing flyers from people you follow.", filter_seeded: "You don't follow anyone yet — showing a curated set from VoxVera's trust network.", filter_all: "Showing all flyers on the relays (unfiltered).", filter_unavailable: "Couldn't load a trust network — showing all flyers.", filter_empty: "No flyers from your trust network yet. Turn on “Show all” to see everything.", show_all_label: "Show all flyers", connect_extension: "Browser extension (NIP-07)", connect_nsec: "Use a private key (nsec)", connect_generate: "Create a new key", key_note: "Your key stays in this browser and is only used to read your follow list — it is never sent anywhere.", invalid_nsec: "That doesn't look like a valid nsec key.", gen_warning: "This is your new secret key. Anyone who has it controls this identity — save it somewhere safe, because VoxVera can't recover it.", copy: "Copy", copied: "Copied", continue_label: "Continue" },
|
||||
es: { title: "Tablón de anuncios", subtitle: "Carteles más recientes publicados en los relés.", col_title: "Título", col_link: "Enlace", col_author: "Publicado por", col_language: "Idioma", col_date: "Publicado", col_event_id: "ID del evento", link_text: "enlace", loading: "Cargando carteles…", empty: "Aún no se encontraron carteles.", error: "No se pudo conectar con los relés.", back: "← Volver a VoxVera", gate_title: "Conéctate para ver el tablón", gate_body: "El tablón de anuncios es visible para quienes conectan una identidad Nostr. Elige abajo cómo conectarte.", connect: "Conectar", connecting: "Conectando…", no_extension: "No se encontró ninguna extensión Nostr. Instala una extensión de navegador NIP-07 para ver el tablón.", connect_failed: "Conexión cancelada o fallida. Inténtalo de nuevo.", disconnect: "Desconectar", filter_following: "Mostrando carteles de las personas que sigues.", filter_seeded: "Aún no sigues a nadie: mostramos un conjunto curado de la red de confianza de VoxVera.", filter_all: "Mostrando todos los carteles de los relés (sin filtrar).", filter_unavailable: "No se pudo cargar una red de confianza: mostrando todos los carteles.", filter_empty: "Aún no hay carteles de tu red de confianza. Activa «Mostrar todos» para verlo todo.", show_all_label: "Mostrar todos los carteles", connect_extension: "Extensión del navegador (NIP-07)", connect_nsec: "Usar una clave privada (nsec)", connect_generate: "Crear una clave nueva", key_note: "Tu clave permanece en este navegador y solo se usa para leer tu lista de seguidos; nunca se envía a ningún sitio.", invalid_nsec: "Eso no parece una clave nsec válida.", gen_warning: "Esta es tu nueva clave secreta. Cualquiera que la tenga controla esta identidad: guárdala en un lugar seguro, porque VoxVera no puede recuperarla.", copy: "Copiar", copied: "Copiado", continue_label: "Continuar" },
|
||||
de: { title: "Schwarzes Brett", subtitle: "Neueste auf den Relays veröffentlichte Plakate.", col_title: "Titel", col_link: "Link", col_author: "Veröffentlicht von", col_language: "Sprache", col_date: "Veröffentlicht", col_event_id: "Event-ID",link_text: "Link", loading: "Plakate werden geladen…", empty: "Noch keine Plakate gefunden.", error: "Relays konnten nicht erreicht werden.", back: "← Zurück zu VoxVera", gate_title: "Verbinden, um das Brett zu sehen", gate_body: "Das Schwarze Brett ist für Personen sichtbar, die eine Nostr-Identität verbinden. Wähle unten, wie du dich verbinden möchtest.", connect: "Verbinden", connecting: "Verbindung…", no_extension: "Keine Nostr-Erweiterung gefunden. Installiere eine NIP-07-Browsererweiterung, um das Brett zu sehen.", connect_failed: "Verbindung abgebrochen oder fehlgeschlagen. Bitte erneut versuchen.", disconnect: "Trennen", filter_following: "Plakate von Personen, denen du folgst.", filter_seeded: "Du folgst noch niemandem – wir zeigen eine kuratierte Auswahl aus dem Vertrauensnetz von VoxVera.", filter_all: "Alle Plakate auf den Relays (ungefiltert).", filter_unavailable: "Vertrauensnetz konnte nicht geladen werden – alle Plakate werden angezeigt.", filter_empty: "Noch keine Plakate aus deinem Vertrauensnetz. Aktiviere „Alle anzeigen“, um alles zu sehen.", show_all_label: "Alle Plakate anzeigen", connect_extension: "Browser-Erweiterung (NIP-07)", connect_nsec: "Privaten Schlüssel (nsec) verwenden", connect_generate: "Neuen Schlüssel erstellen", key_note: "Dein Schlüssel bleibt in diesem Browser und wird nur zum Lesen deiner Folgeliste verwendet – er wird nirgendwohin gesendet.", invalid_nsec: "Das sieht nicht nach einem gültigen nsec-Schlüssel aus.", gen_warning: "Das ist dein neuer geheimer Schlüssel. Wer ihn hat, kontrolliert diese Identität – bewahre ihn sicher auf, denn VoxVera kann ihn nicht wiederherstellen.", copy: "Kopieren", copied: "Kopiert", continue_label: "Weiter" },
|
||||
fr: { title: "Tableau d'affichage", subtitle: "Affiches les plus récentes publiées sur les relais.", col_title: "Titre", col_link: "Lien", col_author: "Publié par", col_language: "Langue", col_date: "Publié", col_event_id: "ID de l'événement",link_text: "lien", loading: "Chargement des affiches…", empty: "Aucune affiche trouvée pour l'instant.", error: "Impossible de joindre les relais.", back: "← Retour à VoxVera", gate_title: "Connectez-vous pour voir le tableau", gate_body: "Le tableau d'affichage est visible par les personnes qui connectent une identité Nostr. Choisissez ci-dessous comment vous connecter.", connect: "Se connecter", connecting: "Connexion…", no_extension: "Aucune extension Nostr trouvée. Installez une extension de navigateur NIP-07 pour voir le tableau.", connect_failed: "Connexion annulée ou échouée. Veuillez réessayer.", disconnect: "Se déconnecter", filter_following: "Affiches des personnes que vous suivez.", filter_seeded: "Vous ne suivez encore personne – voici une sélection issue du réseau de confiance de VoxVera.", filter_all: "Toutes les affiches sur les relais (non filtrées).", filter_unavailable: "Impossible de charger un réseau de confiance – toutes les affiches sont affichées.", filter_empty: "Aucune affiche de votre réseau de confiance pour l'instant. Activez « Tout afficher » pour tout voir.", show_all_label: "Afficher toutes les affiches", connect_extension: "Extension de navigateur (NIP-07)", connect_nsec: "Utiliser une clé privée (nsec)", connect_generate: "Créer une nouvelle clé", key_note: "Votre clé reste dans ce navigateur et ne sert qu'à lire votre liste d'abonnements ; elle n'est jamais envoyée nulle part.", invalid_nsec: "Cela ne ressemble pas à une clé nsec valide.", gen_warning: "Voici votre nouvelle clé secrète. Quiconque la possède contrôle cette identité — conservez-la en lieu sûr, car VoxVera ne peut pas la récupérer.", copy: "Copier", copied: "Copié", continue_label: "Continuer" },
|
||||
ru: { title: "Доска объявлений", subtitle: "Последние листовки, опубликованные на реле.", col_title: "Заголовок", col_link: "Ссылка", col_author: "Опубликовал", col_language: "Язык", col_date: "Опубликовано", col_event_id: "ID события",link_text: "ссылка", loading: "Загрузка листовок…", empty: "Листовки пока не найдены.", error: "Не удалось подключиться к реле.", back: "← Назад в VoxVera", gate_title: "Подключитесь, чтобы увидеть доску", gate_body: "Доска объявлений видна тем, кто подключил идентификацию Nostr. Выберите способ подключения ниже.", connect: "Подключить", connecting: "Подключение…", no_extension: "Расширение Nostr не найдено. Установите расширение браузера NIP-07, чтобы увидеть доску.", connect_failed: "Подключение отменено или не удалось. Повторите попытку.", disconnect: "Отключить", filter_following: "Показаны листовки от тех, на кого вы подписаны.", filter_seeded: "Вы пока ни на кого не подписаны — показываем подборку из сети доверия VoxVera.", filter_all: "Показаны все листовки на реле (без фильтра).", filter_unavailable: "Не удалось загрузить сеть доверия — показаны все листовки.", filter_empty: "Пока нет листовок из вашей сети доверия. Включите «Показать все», чтобы увидеть всё.", show_all_label: "Показать все листовки", connect_extension: "Расширение браузера (NIP-07)", connect_nsec: "Использовать приватный ключ (nsec)", connect_generate: "Создать новый ключ", key_note: "Ваш ключ остаётся в этом браузере и используется только для чтения вашего списка подписок — он никуда не отправляется.", invalid_nsec: "Это не похоже на действительный ключ nsec.", gen_warning: "Это ваш новый секретный ключ. Любой, у кого он есть, управляет этой личностью — сохраните его в надёжном месте, потому что VoxVera не сможет его восстановить.", copy: "Копировать", copied: "Скопировано", continue_label: "Продолжить" },
|
||||
he: { title: "לוח מודעות", subtitle: "הכרזות האחרונות שפורסמו לממסרים.", col_title: "כותרת", col_link: "קישור", col_author: "פורסם על ידי", col_language: "שפה", col_date: "פורסם", col_event_id: "מזהה אירוע",link_text: "קישור", loading: "טוען כרזות…", empty: "לא נמצאו כרזות עדיין.", error: "לא ניתן להתחבר לממסרים.", back: "← חזרה ל-VoxVera", gate_title: "התחבר כדי לראות את הלוח", gate_body: "לוח המודעות גלוי למי שמחבר זהות Nostr. בחר למטה כיצד להתחבר.", connect: "התחבר", connecting: "מתחבר…", no_extension: "לא נמצא תוסף Nostr. התקן תוסף דפדפן NIP-07 כדי לראות את הלוח.", connect_failed: "ההתחברות בוטלה או נכשלה. נסה שוב.", disconnect: "התנתק", filter_following: "מציג כרזות מאנשים שאתה עוקב אחריהם.", filter_seeded: "אינך עוקב עדיין אחר אף אחד — מוצגת בחירה מרשת האמון של VoxVera.", filter_all: "מציג את כל הכרזות בממסרים (ללא סינון).", filter_unavailable: "לא ניתן לטעון רשת אמון — מוצגות כל הכרזות.", filter_empty: "אין עדיין כרזות מרשת האמון שלך. הפעל «הצג הכול» כדי לראות הכול.", show_all_label: "הצג את כל הכרזות", connect_extension: "תוסף דפדפן (NIP-07)", connect_nsec: "השתמש במפתח פרטי (nsec)", connect_generate: "צור מפתח חדש", key_note: "המפתח שלך נשאר בדפדפן זה ומשמש רק לקריאת רשימת העוקבים שלך — הוא לעולם לא נשלח לשום מקום.", invalid_nsec: "זה לא נראה כמו מפתח nsec תקין.", gen_warning: "זהו המפתח הסודי החדש שלך. כל מי שיש לו אותו שולט בזהות הזו — שמור אותו במקום בטוח, מכיוון ש-VoxVera אינה יכולה לשחזר אותו.", copy: "העתק", copied: "הועתק", continue_label: "המשך" },
|
||||
ar: { title: "لوحة الإعلانات", subtitle: "أحدث الملصقات المنشورة على المرحلات.", col_title: "العنوان", col_link: "رابط", col_author: "نُشر بواسطة", col_language: "اللغة", col_date: "تاريخ النشر", col_event_id: "معرّف الحدث",link_text: "رابط", loading: "جارٍ تحميل الملصقات…", empty: "لم يتم العثور على ملصقات بعد.", error: "تعذّر الوصول إلى المرحلات.", back: "← العودة إلى VoxVera", gate_title: "اتصل لعرض اللوحة", gate_body: "لوحة الإعلانات مرئية لمن يربط هوية Nostr. اختر أدناه كيفية الاتصال.", connect: "اتصال", connecting: "جارٍ الاتصال…", no_extension: "لم يتم العثور على إضافة Nostr. ثبّت إضافة متصفح NIP-07 لعرض اللوحة.", connect_failed: "تم إلغاء الاتصال أو فشل. يرجى المحاولة مرة أخرى.", disconnect: "قطع الاتصال", filter_following: "عرض الملصقات من الأشخاص الذين تتابعهم.", filter_seeded: "أنت لا تتابع أحدًا بعد — نعرض مجموعة مختارة من شبكة الثقة في VoxVera.", filter_all: "عرض جميع الملصقات على المرحلات (دون تصفية).", filter_unavailable: "تعذّر تحميل شبكة ثقة — يتم عرض جميع الملصقات.", filter_empty: "لا توجد ملصقات من شبكة الثقة الخاصة بك بعد. فعّل «عرض الكل» لرؤية كل شيء.", show_all_label: "عرض جميع الملصقات", connect_extension: "إضافة المتصفح (NIP-07)", connect_nsec: "استخدام مفتاح خاص (nsec)", connect_generate: "إنشاء مفتاح جديد", key_note: "يبقى مفتاحك في هذا المتصفح ويُستخدم فقط لقراءة قائمة متابَعاتك — ولا يُرسل إلى أي مكان أبدًا.", invalid_nsec: "لا يبدو هذا مفتاح nsec صالحًا.", gen_warning: "هذا مفتاحك السري الجديد. أي شخص يملكه يتحكم في هذه الهوية — احفظه في مكان آمن، لأن VoxVera لا يمكنها استعادته.", copy: "نسخ", copied: "تم النسخ", continue_label: "متابعة" },
|
||||
fa: { title: "تابلوی اعلانات", subtitle: "جدیدترین پوسترهای منتشرشده روی رلهها.", col_title: "عنوان", col_link: "پیوند", col_author: "منتشرشده توسط", col_language: "زبان", col_date: "تاریخ انتشار", col_event_id: "شناسه رویداد",link_text: "پیوند", loading: "در حال بارگذاری پوسترها…", empty: "هنوز پوستری یافت نشد.", error: "اتصال به رلهها ممکن نشد.", back: "← بازگشت به VoxVera", gate_title: "برای مشاهده تابلو متصل شوید", gate_body: "تابلوی اعلانات برای کسانی که هویت Nostr را متصل میکنند قابل مشاهده است. در زیر نحوه اتصال را انتخاب کنید.", connect: "اتصال", connecting: "در حال اتصال…", no_extension: "هیچ افزونه Nostr یافت نشد. برای مشاهده تابلو یک افزونه مرورگر NIP-07 نصب کنید.", connect_failed: "اتصال لغو شد یا ناموفق بود. لطفاً دوباره تلاش کنید.", disconnect: "قطع اتصال", filter_following: "نمایش پوسترها از افرادی که دنبال میکنید.", filter_seeded: "هنوز کسی را دنبال نمیکنید — مجموعهای منتخب از شبکه اعتماد VoxVera نمایش داده میشود.", filter_all: "نمایش همه پوسترها روی رلهها (بدون فیلتر).", filter_unavailable: "بارگذاری شبکه اعتماد ممکن نشد — همه پوسترها نمایش داده میشوند.", filter_empty: "هنوز پوستری از شبکه اعتماد شما نیست. «نمایش همه» را روشن کنید تا همه را ببینید.", show_all_label: "نمایش همه پوسترها", connect_extension: "افزونه مرورگر (NIP-07)", connect_nsec: "استفاده از کلید خصوصی (nsec)", connect_generate: "ایجاد کلید جدید", key_note: "کلید شما در همین مرورگر میماند و فقط برای خواندن فهرست دنبالشدههای شما استفاده میشود — هرگز به جایی ارسال نمیشود.", invalid_nsec: "این یک کلید nsec معتبر به نظر نمیرسد.", gen_warning: "این کلید مخفی جدید شماست. هر کسی آن را داشته باشد این هویت را کنترل میکند — آن را جایی امن ذخیره کنید، زیرا VoxVera نمیتواند آن را بازیابی کند.", copy: "کپی", copied: "کپی شد", continue_label: "ادامه" },
|
||||
hi: { title: "बुलेटिन बोर्ड", subtitle: "रिले पर प्रकाशित सबसे हाल के पोस्टर।", col_title: "शीर्षक", col_link: "लिंक", col_author: "द्वारा पोस्ट किया गया", col_language: "भाषा", col_date: "पोस्ट किया गया", col_event_id: "इवेंट ID",link_text: "लिंक", loading: "पोस्टर लोड हो रहे हैं…", empty: "अभी तक कोई पोस्टर नहीं मिला।", error: "रिले तक नहीं पहुँच सके।", back: "← VoxVera पर वापस", gate_title: "बोर्ड देखने के लिए कनेक्ट करें", gate_body: "बुलेटिन बोर्ड उन लोगों को दिखता है जो Nostr पहचान कनेक्ट करते हैं। नीचे चुनें कि कैसे कनेक्ट करना है।", connect: "कनेक्ट करें", connecting: "कनेक्ट हो रहा है…", no_extension: "कोई Nostr एक्सटेंशन नहीं मिला। बोर्ड देखने के लिए NIP-07 ब्राउज़र एक्सटेंशन इंस्टॉल करें।", connect_failed: "कनेक्शन रद्द या विफल। कृपया पुनः प्रयास करें।", disconnect: "डिस्कनेक्ट करें", filter_following: "उन लोगों के पोस्टर दिखा रहे हैं जिन्हें आप फ़ॉलो करते हैं।", filter_seeded: "आप अभी किसी को फ़ॉलो नहीं करते — VoxVera के विश्वास नेटवर्क से चुनिंदा पोस्टर दिखा रहे हैं।", filter_all: "रिले पर सभी पोस्टर दिखा रहे हैं (बिना फ़िल्टर)।", filter_unavailable: "विश्वास नेटवर्क लोड नहीं हो सका — सभी पोस्टर दिखा रहे हैं।", filter_empty: "आपके विश्वास नेटवर्क से अभी कोई पोस्टर नहीं। सब कुछ देखने के लिए “सभी दिखाएँ” चालू करें।", show_all_label: "सभी पोस्टर दिखाएँ", connect_extension: "ब्राउज़र एक्सटेंशन (NIP-07)", connect_nsec: "निजी कुंजी (nsec) का उपयोग करें", connect_generate: "नई कुंजी बनाएँ", key_note: "आपकी कुंजी इसी ब्राउज़र में रहती है और केवल आपकी फ़ॉलो सूची पढ़ने के लिए उपयोग होती है — इसे कहीं नहीं भेजा जाता।", invalid_nsec: "यह एक मान्य nsec कुंजी नहीं लगती।", gen_warning: "यह आपकी नई गुप्त कुंजी है। जिसके पास यह होगी वह इस पहचान को नियंत्रित कर सकता है — इसे सुरक्षित जगह सहेजें, क्योंकि VoxVera इसे पुनर्प्राप्त नहीं कर सकता।", copy: "कॉपी करें", copied: "कॉपी किया गया", continue_label: "जारी रखें" },
|
||||
ja: { title: "掲示板", subtitle: "リレーに公開された最新のちらし。", col_title: "タイトル", col_link: "リンク", col_author: "投稿者", col_language: "言語", col_date: "投稿日", col_event_id: "イベントID",link_text: "リンク", loading: "ちらしを読み込んでいます…", empty: "まだちらしが見つかりません。", error: "リレーに接続できませんでした。", back: "← VoxVera に戻る", gate_title: "掲示板を見るには接続してください", gate_body: "掲示板はNostr IDを接続した人に表示されます。下で接続方法を選んでください。", connect: "接続", connecting: "接続中…", no_extension: "Nostr拡張機能が見つかりません。掲示板を見るにはNIP-07ブラウザ拡張機能をインストールしてください。", connect_failed: "接続がキャンセルされたか失敗しました。もう一度お試しください。", disconnect: "切断", filter_following: "フォローしている人のちらしを表示しています。", filter_seeded: "まだ誰もフォローしていません。VoxVera の信頼ネットワークから厳選したものを表示しています。", filter_all: "リレー上のすべてのちらしを表示しています(フィルターなし)。", filter_unavailable: "信頼ネットワークを読み込めませんでした。すべてのちらしを表示しています。", filter_empty: "あなたの信頼ネットワークのちらしはまだありません。「すべて表示」をオンにするとすべて表示されます。", show_all_label: "すべてのちらしを表示", connect_extension: "ブラウザ拡張機能 (NIP-07)", connect_nsec: "秘密鍵 (nsec) を使う", connect_generate: "新しい鍵を作成", key_note: "鍵はこのブラウザ内に留まり、フォローリストの読み取りにのみ使用されます。どこにも送信されません。", invalid_nsec: "有効な nsec 鍵ではないようです。", gen_warning: "これはあなたの新しい秘密鍵です。これを持つ人は誰でもこの ID を操作できます。VoxVera は復元できないので、安全な場所に保存してください。", copy: "コピー", copied: "コピーしました", continue_label: "続行" },
|
||||
pt: { title: "Quadro de avisos", subtitle: "Panfletos mais recentes publicados nos relés.", col_title: "Título", col_link: "Link", col_author: "Publicado por", col_language: "Idioma", col_date: "Publicado", col_event_id: "ID do evento", link_text: "link", loading: "Carregando panfletos…", empty: "Nenhum panfleto encontrado ainda.", error: "Não foi possível acessar os relés.", back: "← Voltar ao VoxVera", gate_title: "Conecte-se para ver o quadro", gate_body: "O quadro de avisos é visível para quem conecta uma identidade Nostr. Escolha abaixo como se conectar.", connect: "Conectar", connecting: "Conectando…", no_extension: "Nenhuma extensão Nostr encontrada. Instale uma extensão de navegador NIP-07 para ver o quadro.", connect_failed: "Conexão cancelada ou falhou. Tente novamente.", disconnect: "Desconectar", filter_following: "Mostrando panfletos de pessoas que você segue.", filter_seeded: "Você ainda não segue ninguém — mostrando uma seleção da rede de confiança da VoxVera.", filter_all: "Mostrando todos os panfletos nos relés (sem filtro).", filter_unavailable: "Não foi possível carregar uma rede de confiança — mostrando todos os panfletos.", filter_empty: "Ainda não há panfletos da sua rede de confiança. Ative “Mostrar todos” para ver tudo.", show_all_label: "Mostrar todos os panfletos", connect_extension: "Extensão do navegador (NIP-07)", connect_nsec: "Usar uma chave privada (nsec)", connect_generate: "Criar uma nova chave", key_note: "Sua chave permanece neste navegador e é usada apenas para ler sua lista de seguidos — nunca é enviada para lugar algum.", invalid_nsec: "Isso não parece uma chave nsec válida.", gen_warning: "Esta é sua nova chave secreta. Qualquer pessoa que a tenha controla esta identidade — guarde-a em local seguro, pois o VoxVera não pode recuperá-la.", copy: "Copiar", copied: "Copiado", continue_label: "Continuar" },
|
||||
sw: { title: "Ubao wa matangazo", subtitle: "Mabango ya hivi karibuni yaliyochapishwa kwenye relay.", col_title: "Kichwa", col_link: "Kiungo", col_author: "Imechapishwa na", col_language: "Lugha", col_date: "Imechapishwa", col_event_id: "Kitambulisho cha tukio",link_text: "kiungo", loading: "Inapakia mabango…", empty: "Hakuna mabango yaliyopatikana bado.", error: "Imeshindwa kufikia relay.", back: "← Rudi VoxVera", gate_title: "Unganisha ili kuona ubao", gate_body: "Ubao wa matangazo unaonekana kwa wale wanaounganisha kitambulisho cha Nostr. Chagua hapa chini jinsi ya kuunganisha.", connect: "Unganisha", connecting: "Inaunganisha…", no_extension: "Hakuna kiendelezi cha Nostr kilichopatikana. Sakinisha kiendelezi cha kivinjari cha NIP-07 ili kuona ubao.", connect_failed: "Muunganisho umeghairiwa au umeshindwa. Tafadhali jaribu tena.", disconnect: "Tenganisha", filter_following: "Inaonyesha mabango kutoka kwa watu unaowafuata.", filter_seeded: "Bado hufuati mtu yeyote — tunaonyesha mkusanyiko uliochaguliwa kutoka mtandao wa kuaminiana wa VoxVera.", filter_all: "Inaonyesha mabango yote kwenye relay (bila kuchuja).", filter_unavailable: "Imeshindwa kupakia mtandao wa kuaminiana — inaonyesha mabango yote.", filter_empty: "Bado hakuna mabango kutoka mtandao wako wa kuaminiana. Washa “Onyesha yote” kuona yote.", show_all_label: "Onyesha mabango yote", connect_extension: "Kiendelezi cha kivinjari (NIP-07)", connect_nsec: "Tumia ufunguo wa faragha (nsec)", connect_generate: "Tengeneza ufunguo mpya", key_note: "Ufunguo wako unabaki katika kivinjari hiki na hutumika tu kusoma orodha yako ya unaowafuata — haupelekwi popote.", invalid_nsec: "Huu hauonekani kama ufunguo halali wa nsec.", gen_warning: "Huu ndio ufunguo wako mpya wa siri. Yeyote aliye nao anadhibiti utambulisho huu — uhifadhi mahali salama, kwa sababu VoxVera haiwezi kuurejesha.", copy: "Nakili", copied: "Imenakiliwa", continue_label: "Endelea" },
|
||||
tr: { title: "İlan panosu", subtitle: "Rölelere yayımlanan en yeni el ilanları.", col_title: "Başlık", col_link: "Bağlantı", col_author: "Yayımlayan", col_language: "Dil", col_date: "Yayımlandı", col_event_id: "Olay ID",link_text: "bağlantı", loading: "El ilanları yükleniyor…", empty: "Henüz el ilanı bulunamadı.", error: "Rölelere ulaşılamadı.", back: "← VoxVera'ya dön", gate_title: "Panoyu görmek için bağlanın", gate_body: "İlan panosu, bir Nostr kimliği bağlayanlara görünür. Aşağıdan nasıl bağlanacağını seç.", connect: "Bağlan", connecting: "Bağlanıyor…", no_extension: "Nostr uzantısı bulunamadı. Panoyu görmek için bir NIP-07 tarayıcı uzantısı kurun.", connect_failed: "Bağlantı iptal edildi veya başarısız oldu. Lütfen tekrar deneyin.", disconnect: "Bağlantıyı kes", filter_following: "Takip ettiğin kişilerin el ilanları gösteriliyor.", filter_seeded: "Henüz kimseyi takip etmiyorsun — VoxVera'nın güven ağından seçilmiş bir küme gösteriliyor.", filter_all: "Rölelerdeki tüm el ilanları gösteriliyor (filtresiz).", filter_unavailable: "Güven ağı yüklenemedi — tüm el ilanları gösteriliyor.", filter_empty: "Güven ağından henüz el ilanı yok. Her şeyi görmek için “Tümünü göster”i aç.", show_all_label: "Tüm el ilanlarını göster", connect_extension: "Tarayıcı uzantısı (NIP-07)", connect_nsec: "Özel anahtar (nsec) kullan", connect_generate: "Yeni anahtar oluştur", key_note: "Anahtarın bu tarayıcıda kalır ve yalnızca takip listeni okumak için kullanılır — hiçbir yere gönderilmez.", invalid_nsec: "Bu geçerli bir nsec anahtarı gibi görünmüyor.", gen_warning: "Bu senin yeni gizli anahtarın. Ona sahip olan herkes bu kimliği kontrol eder — güvenli bir yerde sakla, çünkü VoxVera onu kurtaramaz.", copy: "Kopyala", copied: "Kopyalandı", continue_label: "Devam et" },
|
||||
zh: { title: "公告栏", subtitle: "发布到中继的最新传单。", col_title: "标题", col_link: "链接", col_author: "发布者", col_language: "语言", col_date: "发布时间", col_event_id: "事件 ID",link_text: "链接", loading: "正在加载传单…", empty: "尚未找到传单。", error: "无法连接到中继。", back: "← 返回 VoxVera", gate_title: "连接以查看公告栏", gate_body: "公告栏对连接了 Nostr 身份的人可见。请在下方选择连接方式。", connect: "连接", connecting: "正在连接…", no_extension: "未找到 Nostr 扩展。请安装 NIP-07 浏览器扩展以查看公告栏。", connect_failed: "连接已取消或失败。请重试。", disconnect: "断开连接", filter_following: "正在显示你关注的人的传单。", filter_seeded: "你还没有关注任何人——正在显示来自 VoxVera 信任网络的精选内容。", filter_all: "正在显示中继上的所有传单(未过滤)。", filter_unavailable: "无法加载信任网络——正在显示所有传单。", filter_empty: "你的信任网络暂时没有传单。开启“显示全部”以查看所有内容。", show_all_label: "显示所有传单", connect_extension: "浏览器扩展 (NIP-07)", connect_nsec: "使用私钥 (nsec)", connect_generate: "创建新密钥", key_note: "你的密钥保留在此浏览器中,仅用于读取你的关注列表——绝不会发送到任何地方。", invalid_nsec: "这看起来不是有效的 nsec 密钥。", gen_warning: "这是你的新私钥。拥有它的任何人都能控制此身份——请妥善保存,因为 VoxVera 无法恢复它。", copy: "复制", copied: "已复制", continue_label: "继续" }
|
||||
};
|
||||
|
||||
function pickLang() {
|
||||
@@ -177,6 +177,7 @@
|
||||
link: String(payload.tear_off_link || payload.url || "").trim(),
|
||||
npub: npub,
|
||||
pubkey: String(e.pubkey || "").toLowerCase(),
|
||||
id: String(e.id || ""),
|
||||
lang: payload.lang || langFromTags(tags) || "",
|
||||
created_at: e.created_at || 0
|
||||
});
|
||||
@@ -206,9 +207,19 @@
|
||||
cmp: (a, b) => langLabel(a.lang).localeCompare(langLabel(b.lang)) },
|
||||
{ key: "date", label: () => t("col_date"),
|
||||
cell: (r) => `<span class="board-date">${escapeHtml(fmtDate(r.created_at))}</span>`,
|
||||
cmp: (a, b) => (a.created_at || 0) - (b.created_at || 0) }
|
||||
cmp: (a, b) => (a.created_at || 0) - (b.created_at || 0) },
|
||||
{ key: "id", label: () => t("col_event_id"),
|
||||
cell: (r) => r.id
|
||||
? `<span class="board-eventid" title="${escapeHtml(r.id)}">${escapeHtml(shortId(r.id))}</span>`
|
||||
+ `<button type="button" class="board-copy" data-copy="${escapeHtml(r.id)}">${escapeHtml(t("copy"))}</button>`
|
||||
: "—",
|
||||
cmp: (a, b) => (a.id || "").localeCompare(b.id || "") }
|
||||
];
|
||||
|
||||
function shortId(id) {
|
||||
return id && id.length > 16 ? `${id.slice(0, 8)}…${id.slice(-4)}` : (id || "—");
|
||||
}
|
||||
|
||||
function renderHead() {
|
||||
const head = document.getElementById("board-head");
|
||||
head.innerHTML = COLS.map((c) => {
|
||||
@@ -668,6 +679,30 @@
|
||||
else updateFilterControls();
|
||||
});
|
||||
|
||||
// Delegated copy handler for the per-row "copy event ID" buttons (rows are
|
||||
// re-rendered on sort, so a single listener on the tbody is simplest).
|
||||
const rowsBody = document.getElementById("board-rows");
|
||||
if (rowsBody) rowsBody.addEventListener("click", async (event) => {
|
||||
const btn = event.target.closest(".board-copy");
|
||||
if (!btn) return;
|
||||
const value = btn.getAttribute("data-copy") || "";
|
||||
try {
|
||||
await navigator.clipboard.writeText(value);
|
||||
btn.textContent = t("copied");
|
||||
setTimeout(() => { btn.textContent = t("copy"); }, 1200);
|
||||
} catch (_) {
|
||||
// Clipboard blocked (e.g. insecure context): select the id text instead.
|
||||
const id = btn.previousElementSibling;
|
||||
if (id && window.getSelection) {
|
||||
const range = document.createRange();
|
||||
range.selectNodeContents(id);
|
||||
const sel = window.getSelection();
|
||||
sel.removeAllRanges();
|
||||
sel.addRange(range);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
applyLang(currentLang);
|
||||
showGate();
|
||||
|
||||
|
||||
@@ -1128,6 +1128,25 @@ body.mode-viewer #viewer-panel.drawer-open .result-panel {
|
||||
color: var(--muted);
|
||||
}
|
||||
|
||||
.board-table .board-eventid {
|
||||
font-family: "Courier New", monospace;
|
||||
font-size: 12px;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.board-table .board-copy {
|
||||
margin-inline-start: 6px;
|
||||
font: inherit;
|
||||
font-size: 11px;
|
||||
padding: 2px 8px;
|
||||
border: 1px solid var(--line);
|
||||
border-radius: 6px;
|
||||
background: transparent;
|
||||
color: var(--accent);
|
||||
cursor: pointer;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.board-table a {
|
||||
color: var(--accent);
|
||||
}
|
||||
|
||||
Reference in new issue
Block a user