From 3c37ec570efc1ba57d20045799e4a149cd104579 Mon Sep 17 00:00:00 2001 From: esmaeelE Date: Sat, 1 Aug 2026 10:34:30 -0400 Subject: [PATCH] translate to Persian --- frontend/src/i18n/index.tsx | 23 +- frontend/src/i18n/translations/fa.json | 353 +++++++++++++++++++++++++ 2 files changed, 373 insertions(+), 3 deletions(-) create mode 100644 frontend/src/i18n/translations/fa.json diff --git a/frontend/src/i18n/index.tsx b/frontend/src/i18n/index.tsx index b4525d9..e54e426 100644 --- a/frontend/src/i18n/index.tsx +++ b/frontend/src/i18n/index.tsx @@ -1,11 +1,15 @@ 'use client'; -import { createContext, useContext, useState, useCallback, type ReactNode } from 'react'; +import { createContext, useContext, useState, useCallback, useEffect, type ReactNode } from 'react'; import en from './translations/en.json'; import zhCN from './translations/zh-CN.json'; import fr from './translations/fr.json'; +import fa from './translations/fa.json'; -export type Locale = 'en' | 'zh-CN' | 'fr'; +export type Locale = 'en' | 'zh-CN' | 'fr' | 'fa'; + +/** Locales that read right-to-left. Used to set and lang. */ +const RTL_LOCALES: ReadonlySet = new Set(['fa']); /** * Registry of available locales for the UI language toggle. @@ -29,9 +33,15 @@ export const LOCALES: ReadonlyArray<{ code: Locale; label: string }> = [ { code: 'en', label: 'English' }, { code: 'zh-CN', label: '中文 (简体)' }, { code: 'fr', label: 'Français' }, + { code: 'fa', label: 'فارسی' }, ]; -const translations: Record>> = { en, 'zh-CN': zhCN, fr }; +const translations: Record>> = { + en, + 'zh-CN': zhCN, + fr, + fa, +}; function isLocale(value: unknown): value is Locale { return typeof value === 'string' && LOCALES.some((entry) => entry.code === value); @@ -98,6 +108,13 @@ export function I18nProvider({ children }: { children: ReactNode }) { } }, []); + // Keep / in sync with the active locale. + useEffect(() => { + if (typeof document === 'undefined') return; + document.documentElement.dir = RTL_LOCALES.has(locale) ? 'rtl' : 'ltr'; + document.documentElement.lang = locale; + }, [locale]); + const t = useCallback( (key: string): string => { const dict = translations[locale] ?? translations.en; diff --git a/frontend/src/i18n/translations/fa.json b/frontend/src/i18n/translations/fa.json new file mode 100644 index 0000000..3c9513d --- /dev/null +++ b/frontend/src/i18n/translations/fa.json @@ -0,0 +1,353 @@ +{ + "brand": { + "title": "S H A D O W B R O K E R", + "subtitle": "رهگیری تهدیدات جهانی", + "systemMetrics": "OPTIC VIS:113 SRC:180 DENS:1.42 0.8ms" + }, + "nav": { + "layers": "لایه‌ها", + "intel": "اطلاعات", + "markets": "بازارها", + "dataLayers": "لایه‌های داده", + "prioritizingMapFeeds": "اولویت‌بندی فیدهای نقشه", + "restoreUi": "بازیابی رابط کاربری" + }, + "controls": { + "updates": "به‌روزرسانی‌ها", + "checking": "در حال بررسی...", + "upToDate": "به‌روز", + "checkFailed": "بررسی ناموفق بود", + "node": "گره", + "terminal": "ترمینال", + "coordinates": "مختصات", + "location": "موقعیت", + "style": "سبک", + "solar": "خورشیدی", + "hoverMap": "ماوس را روی نقشه نگه دارید...", + "na": "ناموجود" + }, + "update": { + "downloadInstaller": "دانلود نصب‌کننده", + "installUpdate": "نصب به‌روزرسانی", + "autoUpdate": "به‌روزرسانی خودکار", + "viewRelease": "مشاهده نسخه", + "manualDownload": "دانلود دستی", + "cancel": "لغو", + "tryAgain": "تلاش مجدد", + "downloadingUpdate": "در حال دانلود به‌روزرسانی...", + "restarting": "در حال راه‌اندازی مجدد...", + "updateFailed": "به‌روزرسانی ناموفق بود", + "dockerUpdate": "به‌روزرسانی داکر", + "dockerUpdateDetail": "کانتینرهای داکر باید با دریافت ایمیج‌های جدید به‌روزرسانی شوند.\n این دستور را روی سیستم میزبان خود اجرا کنید:" + }, + "node": { + "activateNode": "فعال‌سازی گره", + "activatingNode": "در حال فعال‌سازی گره...", + "nodeActivated": "گره فعال شد", + "stipulations": "شرایط", + "yes": "بله", + "no": "خیر", + "agree": "موافقم", + "disagree": "مخالفم", + "turnOff": "خاموش کردن", + "keepOn": "روشن نگه‌داشتن", + "turningOff": "در حال خاموش شدن...", + "activating": "در حال فعال‌سازی...", + "nodeOnline": "گره آنلاین", + "generatingIdentity": "در حال ایجاد هویت...", + "identityReady": "هویت آماده است", + "preparingTransport": "در حال آماده‌سازی انتقال از طریق Onion...", + "findingPeers": "در حال یافتن همتاهای اولیه...", + "peersReady": "همتاهای اولیه آماده‌اند", + "syncingChain": "در حال همگام‌سازی زنجیره...", + "soloNodeReady": "گره مستقل آماده است", + "synced": "همگام‌سازی شد", + "events": "رویدادها", + "peers": "همتاها", + "close": "بستن", + "activatePrompt": "آیا می‌خواهید یک گره را در این نصب فعال کنید؟", + "activateDetail": "این کار گره مشارکت‌کنندهٔ محلی شما را روشن می‌کند و Infonet را فقط از طریق همتاهای Onion/RNS در دسترسِ Wormhole همگام‌سازی می‌کند. راه‌اندازی اولیه از طریق شبکهٔ آشکار (Clearnet) به‌طور پیش‌فرض غیرفعال است.", + "keepSyncing": "تا زمانی که بک‌اند در حال اجراست، گرهٔ شما به همگام‌سازی ادامه می‌دهد — می‌توانید این تب مرورگر را ببندید. برای اجرای یک گره بدون رابط گرافیکی (headless)، از این استفاده کنید:", + "termsTitle": "با ادامه دادن، شما موافقت می‌کنید که:", + "term1": "این نصب می‌تواند یک نسخهٔ محلی از زنجیرهٔ عمومی Infonet را نگه دارد.", + "term2": "نصب‌های تازه از یک seed شبکهٔ آشکار (Clearnet) برای Infonet استفاده نمی‌کنند.", + "term3": "همگام‌سازی گرهٔ مشارکت‌کننده نیازمند یک همتای Onion/RNS از طریق Wormhole است.", + "term4": "بک‌اند شما ممکن است در پس‌زمینه با همتاهای اولیهٔ خصوصیِ پیکربندی‌شده همگام‌سازی شود.", + "term5": "Wormhole ترافیک Infonet، gateها، Dead Drop و پیام‌های مستقیم (DM) را در مسیر مبهم‌سازی‌شده نگه می‌دارد.", + "syncTakingLong": "همگام‌سازی بیش از حد انتظار طول کشیده است. گرهٔ شما فعال است و همگام‌سازی در پس‌زمینه ادامه می‌یابد." + }, + "terminal": { + "infonetTerminal": "ترمینال Infonet", + "privateLaneReady": "مسیر خصوصی آماده است", + "privateLaneStarting": "مسیر خصوصی در حال راه‌اندازی است", + "privateLaneOffline": "مسیر خصوصی آفلاین است", + "enterTerminal": "وارد ترمینال مبتنی بر Wormhole شوید و با فضای مشترکِ مبهم‌سازی‌شدهٔ Infonet همگام شوید؟", + "terminalDetail": "این ترمینال از طریق Wormhole برای gateهای مبهم‌سازی‌شده، صندوق ورودی و ارتباطات آزمایشی اجرا می‌شود.", + "identityReady": "هویت مبهم‌سازی‌شدهٔ شما از قبل ایجاد شده است. ورود در حال حاضر، مسیر مبهم‌سازی‌شده را از مسیر همگام‌سازیِ عمومیِ گره جدا نگه می‌دارد.", + "identityNotReady": "این کار Wormhole را روشن کرده و مسیر مبهم‌سازی‌شده را باز می‌کند. اگر از قبل هویت Wormhole دارید، از همان استفاده می‌شود. در غیر این صورت، یک‌بار یک هویت جدید ایجاد شده و سپس مورد استفاده قرار می‌گیرد.", + "beforeYouEnter": "پیش از ورود:", + "termTerminal1": "این ترمینال برای گیت‌های Wormhole (مسیر خصوصیِ انتقال) و Dead Drop / پیام مستقیم (مسیر خصوصیِ قوی‌تر) است.", + "termTerminal2": "گرهٔ مشارکت‌کنندهٔ شما می‌تواند بدون تغییر این مسیر هویتیِ مبهم‌سازی‌شده، به‌طور جداگانه فعال بماند.", + "termTerminal3": "Mesh همچنان محیط عمومی است. Wormhole فضای مشترکِ مبهم‌سازی‌شده است.", + "wormholeCleanup": "پاک‌سازی Wormhole:", + "cleanupDetail": "بستن ترمینال Infonet به‌طور خودکار Wormhole را خاموش می‌کند. اگر مرورگر را به‌زور ببندید یا فرآیند خاموش‌سازی ناموفق باشد، ممکن است Wormhole در پس‌زمینه به اجرا ادامه دهد. این دستور را اجرا کنید:", + "cleanupFromRoot": "از ریشهٔ پروژه، تا از توقف کامل آن مطمئن شوید.", + "enterWormhole": "ورود به Wormhole", + "activateWormhole": "فعال‌سازی Wormhole", + "entering": "در حال ورود...", + "goToMesh": "رفتن به Mesh" + }, + "status": { + "off": "خاموش", + "solo": "مستقل", + "connected": "متصل", + "syncing": "در حال همگام‌سازی", + "forkStop": "توقف انشعاب", + "syncIssue": "مشکل در همگام‌سازی", + "active": "فعال", + "participant": "مشارکت‌کننده", + "nodeOff": "گره • خاموش", + "bootstrapWarning": "گره • هشدار راه‌اندازی اولیه" + }, + "backend": { + "offline": "بک‌اند آفلاین است — امکان اتصال به سرور بک‌اند وجود ندارد. بررسی کنید که کانتینر بک‌اند در حال اجراست و BACKEND_URL صحیح است." + }, + "settings": { + "title": "تنظیمات", + "close": "بستن", + "general": "عمومی", + "feeds": "خوراک‌ها", + "shodan": "Shodan", + "sar": "SAR", + "infonet": "Infonet", + "about": "درباره" + }, + "legend": { + "title": "راهنما", + "close": "بستن" + }, + "onboarding": { + "welcome": "به ShadowBroker خوش آمدید", + "getStarted": "شروع کنید" + }, + "news": { + "title": "اطلاعات خبری", + "noResults": "نتیجه‌ای یافت نشد", + "searchPlaceholder": "جستجوی اخبار..." + }, + "filters": { + "title": "فیلترهای داده", + "clear": "پاک کردن", + "all": "همه" + }, + "map": { + "findLocate": "جستجو / مکان‌یابی", + "searchPlaceholder": "جستجوی مختصات، مکان یا کال‌ساین...", + "measure": "اندازه‌گیری", + "clearMeasure": "پاک کردن اندازه‌گیری" + }, + "layers": { + "aircraft": "هواپیما", + "commercialFlights": "پروازهای تجاری", + "privateAircraft": "هواپیماهای خصوصی", + "privateJets": "جت‌های خصوصی", + "militaryFlights": "پروازهای نظامی", + "trackedAircraft": "هواپیماهای پیگیری شده", + "gpsJamming": "اختلال GPS", + "maritime": "دریایی", + "militaryVessels": "شناورهای نظامی", + "cargoShips": "کشتی‌های باری", + "civilianShips": "شناورهای غیرنظامی", + "passengerShips": "کشتی‌های مسافربری", + "trackedYachts": "قایق‌های تفریحی پیگیری شده", + "fishingActivity": "فعالیت ماهیگیری", + "space": "فضا", + "satellites": "ماهواره‌ها", + "gibsImagery": "تصاویر GIBS", + "highresSatellite": "ماهواره با وضوح بالا", + "sentinelHub": "Sentinel Hub", + "viirsNightlights": "نورهای شبانه VIIRS", + "hazards": "مخاطرات", + "earthquakes": "زمین‌لرزه‌ها", + "fires": "آتش‌سوزی‌ها", + "ukraineAlerts": "هشدارهای اوکراین", + "weatherAlerts": "هشدارهای هواشناسی", + "volcanoes": "آتشفشان‌ها", + "airQuality": "کیفیت هوا", + "infrastructure": "زیرساخت", + "cctv": "دوربین‌های مداربسته", + "datacenters": "مراکز داده", + "internetOutages": "قطعی اینترنت", + "powerPlants": "نیروگاه‌ها", + "militaryBases": "پایگاه‌های نظامی", + "trains": "قطارها", + "sigint": "SIGINT", + "kiwisdr": "KiwiSDR", + "pskReporter": "PSK Reporter", + "satnogs": "SatNOGS", + "tinygs": "TinyGS", + "scanners": "اسکنرها", + "meshtastic": "Meshtastic", + "aprs": "APRS", + "overlays": "لایه‌های اضافی", + "ukraineFrontline": "خط مقدم اوکراین", + "globalIncidents": "حوادث جهانی", + "dayNight": "روز/شب", + "correlations": "همبستگی‌ها", + "contradictions": "تناقض‌ها", + "uapSightings": "مشاهدات UAP", + "biosurveillance": "پایش زیستی", + "wastewater": "فاضلاب", + "crowdThreat": "تهدیدات جمعیتی", + "shodanOverlay": "لایه Shodan", + "aiIntel": "اطلاعات هوش مصنوعی", + "sar": "SAR", + "roadCorridorTrends": "روند حمل‌ونقل بار جاده‌ای", + "roadCorridorSource": "Copernicus S-2 · روندها به‌صورت زنده نیستند", + "submarineCables": "کابل‌های زیردریایی", + "malwareC2": "کنترل‌وفرمان بدافزار (C2)", + "scmSuppliers": "تأمین‌کنندگان زنجیره تأمین", + "cyberThreats": "تهدیدات سایبری", + "telegramOsint": "OSINT تلگرام", + "strategicRisk": "ریسک راهبردی", + "derivedOsint": "OSINT استنتاجی (ریسک راهبردی)", + "derivedOsintSource": "آزمایشی · به‌طور پیش‌فرض غیرفعال" + }, + "gtLean": { + "title": "فعال‌سازی OSINT استنتاجی روی یک گره با منابع محدود؟", + "message": "Shadowbroker محدودیت 1 هستهٔ پردازشی مجازی (vCPU) را روی این گره تشخیص داد. روشن کردن لایهٔ نقشهٔ ریسک راهبردی برای نمایش بی‌خطر است، اما فعال‌سازی موتور بک‌اند (GT_ANALYTICS_ENABLED) ممکن است دریافت داده از تلگرام، GDELT و سایر منابع OSINT را کند کند. روی گره‌های ناوگان، از هشدارهای دیده‌بان (watchdog) OpenClaw بدون فعال‌سازی موتور کامل استفاده کنید.", + "confirm": "با این حال لایه را روشن کن", + "cancel": "لغو" + }, + "gtHud": { + "title": "تحلیل GT", + "dragHint": "برای جابه‌جایی بکشید", + "collapse": "جمع کردن پنل", + "expand": "باز کردن پنل" + }, + "gtAlerts": { + "title": "مهم‌ترین هشدارها", + "counts": "{plotted} روی نقشه · {tracked} تحت پیگیری (حداکثر {max})", + "empty": "هنوز منطقه‌ای برای نمایش وجود ندارد — نیاز به اطلاعات دارای برچسب مکانی (تلگرام/GDELT/اخبار) است.", + "ignition": "اشتعال", + "line": "ریسک {risk} · تعارض {conflict}", + "hint": "500 = حداکثر مناطق تحت پیگیری، نه رویدادهای مجزا. برای پرواز به آنجا کلیک کنید." + }, + "gtRisk": { + "popupTitle": "ریسک راهبردی", + "region": "منطقه", + "composite": "ریسک پسین", + "financial": "مالی", + "unrest": "ناآرامی", + "conflict": "تعارض", + "contagion": "سرایت", + "costlySignals": "نشانه‌های پرهزینه", + "loadingSignals": "در حال بارگذاری تطابق‌های فید…", + "noSignals": "هیچ متنِ نشانهٔ پرهزینه‌ای در تلگرام/GDELT/اخبار اخیر برای این منطقه یافت نشد. امتیازها می‌توانند از پیشینه‌های حوزه یا سرایتِ مناطق مجاور افزایش یابند.", + "unknownSource": "منبع نامشخص" + }, + "gtBacktest": { + "title": "بک‌تست GT", + "layerOff": "خاموش — نقشهٔ حرارتیِ ریسک راهبردی را در لایه‌های داده فعال کنید.", + "disabled": "تحلیل GT غیرفعال است (GT_ANALYTICS_ENABLED=true را تنظیم کنید).", + "loading": "در حال اجرای اعتبارسنجی تاریخی…", + "refresh": "به‌روزرسانی بک‌تست", + "accuracy": "دقت", + "confidence": "کران پایین ۹۵٪ ویلسون", + "cases": "{count} موردِ برچسب‌گذاری‌شده", + "threshold": "هشدار ≥ {value}", + "target": "هدف {value}", + "pass": "قبول", + "fail": "رد", + "collecting": "در حال جمع‌آوری", + "meetsTarget": "به هدف اطمینان رسیده است", + "belowTarget": "پایین‌تر از هدف اطمینان", + "misclassified": "{count} مورد اشتباه طبقه‌بندی‌شده", + "tabBenchmark": "معیار", + "tabOperational": "عملیاتی", + "benchmarkNote": "مجموعهٔ برچسب‌گذاری‌شدهٔ ثابت — آزمون رگرسیون، نه پیش‌بینیِ زنده.", + "operationalLoading": "در حال بارگذاری روند عملیاتیِ متحرک…", + "operationalEmpty": "هنوز عکس فوریِ هفتگی‌ای وجود ندارد — ثبت (freeze) دوشنبه‌ها ساعت 00:05 UTC یا از طریق OpenClaw اجرا می‌شود.", + "operationalWeeks": "{stored} هفته ذخیره‌شده · {scorable} قابل امتیازدهی", + "operationalLabeled": "{labeled} برچسب‌گذاری‌شده · {pending} در انتظار", + "operationalTrend": "دقت هفته به هفته", + "operationalImproving": "بهبود نسبت به هفتهٔ قابل‌امتیازِ قبلی", + "operationalFlat": "ثابت یا کاهش نسبت به هفتهٔ قابل‌امتیازِ قبلی", + "operationalMinLabels": "برای امتیازدهی به ≥{count} برچسب در هفته نیاز است", + "microTitle": "تحلیل خرد ۳ روزه", + "microWindow": "میانگین متحرک {days} روزه · Δ اشتعال ≥ {delta}", + "microIgnitions": "{count} اشتعال", + "microAlerted3d": "{count} بالاتر از آستانه در میانگین 3 روزه", + "microEmpty": "در حال جمع‌آوری قرائت‌های روزانه — با دریافت داده GT به‌روزرسانی می‌شود.", + "microRegionLine": "{region}: لحظه‌ای {spot} · 3 روزه {avg} · Δ {delta}", + "microIgnitionBadge": "اشتعال" + }, + "roadCorridor": { + "analyzeHere": "تحلیل در این‌جا", + "analyzing": "در حال تحلیل…", + "hintTrends": "روند حجم تردد کامیون‌ها با استفاده از تصاویر ماهواره‌ای در بزرگراه‌های اصلی (بازدید مجدد تقریباً هر ۵ روز)", + "missingDeps": "برای تحلیل، افزونه‌های road-corridor بک‌اند را نصب کنید", + "missingCreds": "اطلاعات ورود Sentinel را در تنظیمات ← تصاویر اضافه کنید", + "panMapFirst": "برای انتخاب یک منطقه، نقشه را جابه‌جا کنید", + "analyzeFailed": "تحلیل ناموفق بود" + }, + "recon": { + "title": "جعبه‌ابزار شناسایی (Recon)", + "proxyNote": "پراکسیِ سمت سرور · فقط برای اپراتور محلی" + }, + "scm": { + "title": "زنجیرهٔ تأمین", + "allClear": "هیچ ریسک بالایی در گره‌های سطح 1/2 تحت پایش وجود ندارد.", + "layerOff": "خاموش — برای پایش کارخانه‌ها، زنجیرهٔ تأمین را در لایه‌های داده فعال کنید." + }, + "shodan": { + "title": "اتصال‌دهندهٔ Shodan", + "searchPlaceholder": "جستجوی دستگاه‌ها...", + "apiKeyRequired": "کلید API لازم است", + "results": "نتایج" + }, + "ai": { + "title": "پنل اطلاعات هوش مصنوعی", + "connected": "متصل", + "disconnected": "قطع شده" + }, + "meshChat": { + "title": "چت Meshtastic", + "infonet": "Infonet", + "meshtastic": "Meshtastic", + "deadDrop": "Dead Drop", + "sendMessage": "ارسال پیام", + "placeholder": "پیامی بنویسید..." + }, + "watchlist": { + "title": "فهرست پایش", + "empty": "موردی برای پایش وجود ندارد", + "clear": "پاک کردن" + }, + "timeline": { + "title": "جدول زمانیِ رویدادها", + "noEvents": "رویدادی وجود ندارد" + }, + "sar": { + "title": "تشخیص تغییرات سطح زمین با SAR", + "modeA": "حالت کاتالوگ", + "modeB": "حالت ناهنجاری", + "aoiEditor": "ویرایشگر منطقهٔ موردعلاقه (AOI)", + "addAoi": "افزودن AOI", + "groundDeformation": "تغییر شکل زمین", + "waterChange": "تغییر آب", + "vegetation": "اختلال پوشش گیاهی", + "damage": "ارزیابی خسارت", + "coherence": "تغییرات همدوسی" + }, + "telegram": { + "disclaimer": "هشدار: محتوای زیر از کانال‌های عمومیِ تلگرام بارگذاری شده است. Shadowbroker آن را میزبانی، تأیید یا پشتیبانی نمی‌کند. مشاهدهٔ آن کاملاً بر عهدهٔ خود شماست و هیچ ارتباطی با Shadowbroker ندارد.", + "loadMedia": "مشاهده رسانه (تلگرام)", + "openOriginal": "باز کردن در تلگرام ←", + "embedTitle": "جاسازی پست تلگرام", + "postsAtLocation": "{count} پست در این موقعیت — برای مشاهده بیشتر پیمایش کنید", + "translatedFrom": "ترجمه‌شده از {lang}", + "showOriginal": "نمایش نسخه اصلی ({lang})", + "showTranslation": "نمایش ترجمه" + } +}