mirror of
https://github.com/permissionlesstech/bitchat.git
synced 2026-08-29 07:27:16 +00:00
Stop claiming encryption and privacy properties that aren't live (#1595)
* Stop claiming encryption and privacy properties that aren't live
Two of the app's most safety-critical signals could show more security
than actually existed:
1. getEncryptionStatus was sticky-optimistic: any peer whose fingerprint
was ever persisted resolved to noiseSecured/noiseVerified even when
the live Noise session was .none, .handshaking, or .failed. After a
cold launch or a handshake failure the DM header showed a solid lock
and the composer caption claimed "end-to-end encrypted" with no
secure session in existence. The status now reflects only the live
session; a remembered fingerprint decides what an established
session upgrades to (verified vs secured), nothing more. Peers
reachable only over Nostr keep the encrypted caption — gift-wrapped
delivery is end-to-end without a Noise session.
2. Privacy copy told three lies (all 30 locales fixed):
- "ephemeral identity — new peer ID generated regularly": the peer
ID is stable until a panic wipe (rotation is spec-only). Now
"local-only identity — your identity is a keypair created on this
device; panic wipe replaces it instantly."
- "no servers, accounts, or data collection": location channels and
internet delivery go through Nostr relays. Now "no accounts, no
phone numbers, no data collection."
- location channels: "your IP address is hidden by routing all
traffic over tor" — tor is a toggle. The description now says tor
is the default, and the sheet shows the existing red tor-off
warning at the point of joining whenever the toggle is off.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
* Review fixes: gate nostr availability on a stored recipient key
- Codex P2 / review: a mutual favorite with a nil peerNostrPublicKey
still reported .nostrAvailable, so the DM caption could claim
"end-to-end encrypted" with neither a Noise session nor a usable
Nostr key. BitchatPeer.connectionState (and the header fallback in
PrivateConversationModel) now require a stored recipient key —
mirroring NostrTransport's reachability rule. Regression test:
mutual + nil key → .offline; key present → .nostrAvailable.
- Copy: "local-only identity" → "device-local identity" (all 30
locales) — once a fingerprint or QR is shared it isn't "only" local;
device-local says where the keys live without over-claiming.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
---------
Co-authored-by: jack <jackjackbits@users.noreply.github.com>
Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
parent
6ef3945179
commit
4122a4dacd
@ -357,7 +357,10 @@ final class PrivateConversationModel: ObservableObject {
|
||||
}
|
||||
if let noiseKey = Data(hexString: headerPeerID.id),
|
||||
let favoriteStatus = FavoritesPersistenceService.shared.getFavoriteStatus(for: noiseKey),
|
||||
favoriteStatus.isMutual {
|
||||
favoriteStatus.isMutual,
|
||||
// No stored recipient key means no Nostr delivery — and the
|
||||
// "end-to-end encrypted" caption keys off .nostrAvailable.
|
||||
favoriteStatus.peerNostrPublicKey != nil {
|
||||
return .nostrAvailable
|
||||
}
|
||||
if chatViewModel.meshService.isPeerConnected(headerPeerID) || chatViewModel.connectedPeers.contains(headerPeerID) {
|
||||
|
||||
@ -11375,181 +11375,181 @@
|
||||
"ar" : {
|
||||
"stringUnit" : {
|
||||
"state" : "translated",
|
||||
"value" : "يُولد معرف قرين جديد بانتظام"
|
||||
"value" : "هويتك زوج مفاتيح يُنشأ على هذا الجهاز — المسح الطارئ يستبدلها فورًا"
|
||||
}
|
||||
},
|
||||
"bn" : {
|
||||
"stringUnit" : {
|
||||
"state" : "translated",
|
||||
"value" : "নিয়মিত নতুন পিয়ার আইডি তৈরি হয়"
|
||||
"value" : "আপনার পরিচয় এই ডিভাইসে তৈরি একটি কী-জোড়া — প্যানিক ওয়াইপ সঙ্গে সঙ্গে এটি বদলে দেয়"
|
||||
}
|
||||
},
|
||||
"de" : {
|
||||
"stringUnit" : {
|
||||
"state" : "translated",
|
||||
"value" : "neue peer-id wird regelmäßig erzeugt"
|
||||
"value" : "deine identität ist ein schlüsselpaar, das auf diesem gerät erzeugt wird — der panik-löscher ersetzt es sofort"
|
||||
}
|
||||
},
|
||||
"en" : {
|
||||
"stringUnit" : {
|
||||
"state" : "translated",
|
||||
"value" : "new peer ID generated regularly"
|
||||
"value" : "your identity is a keypair created on this device — panic wipe replaces it instantly"
|
||||
}
|
||||
},
|
||||
"es" : {
|
||||
"stringUnit" : {
|
||||
"state" : "translated",
|
||||
"value" : "nuevo ID de peer generado periódicamente"
|
||||
"value" : "tu identidad es un par de claves creado en este dispositivo — el borrado de pánico la reemplaza al instante"
|
||||
}
|
||||
},
|
||||
"fa" : {
|
||||
"stringUnit" : {
|
||||
"state" : "translated",
|
||||
"value" : "شناسهٔ همتای جدید بهطور منظم ساخته میشود"
|
||||
"value" : "هویت شما یک جفت کلید است که روی این دستگاه ساخته میشود — پاکسازی اضطراری آن را فوراً جایگزین میکند"
|
||||
}
|
||||
},
|
||||
"fil" : {
|
||||
"stringUnit" : {
|
||||
"state" : "translated",
|
||||
"value" : "regular na lumilikha ng bagong peer ID"
|
||||
"value" : "ang iyong pagkakakilanlan ay isang key pair na ginawa sa device na ito — agad itong pinapalitan ng panic wipe"
|
||||
}
|
||||
},
|
||||
"fr" : {
|
||||
"stringUnit" : {
|
||||
"state" : "translated",
|
||||
"value" : "nouvel id de pair généré régulièrement"
|
||||
"value" : "votre identité est une paire de clés créée sur cet appareil — l'effacement d'urgence la remplace instantanément"
|
||||
}
|
||||
},
|
||||
"he" : {
|
||||
"stringUnit" : {
|
||||
"state" : "translated",
|
||||
"value" : "מזהה עמית חדש נוצר באופן קבוע"
|
||||
"value" : "הזהות שלך היא צמד מפתחות שנוצר במכשיר הזה — מחיקת חירום מחליפה אותה מיד"
|
||||
}
|
||||
},
|
||||
"hi" : {
|
||||
"stringUnit" : {
|
||||
"state" : "translated",
|
||||
"value" : "नया पीयर आईडी नियमित रूप से बनाया जाता है"
|
||||
"value" : "आपकी पहचान इस डिवाइस पर बनी एक कुंजी-जोड़ी है — पैनिक वाइप इसे तुरंत बदल देता है"
|
||||
}
|
||||
},
|
||||
"id" : {
|
||||
"stringUnit" : {
|
||||
"state" : "translated",
|
||||
"value" : "id peer baru dibuat secara berkala"
|
||||
"value" : "identitas anda adalah pasangan kunci yang dibuat di perangkat ini — panic wipe langsung menggantinya"
|
||||
}
|
||||
},
|
||||
"it" : {
|
||||
"stringUnit" : {
|
||||
"state" : "translated",
|
||||
"value" : "nuovo id peer generato regolarmente"
|
||||
"value" : "la tua identità è una coppia di chiavi creata su questo dispositivo — la cancellazione di emergenza la sostituisce all'istante"
|
||||
}
|
||||
},
|
||||
"ja" : {
|
||||
"stringUnit" : {
|
||||
"state" : "translated",
|
||||
"value" : "新しいpeer idが定期的に生成されます"
|
||||
"value" : "アイデンティティはこの端末上で生成される鍵ペアです — パニックワイプで即座に置き換えられます"
|
||||
}
|
||||
},
|
||||
"ko" : {
|
||||
"stringUnit" : {
|
||||
"state" : "translated",
|
||||
"value" : "새로운 피어 ID가 주기적으로 생성됩니다"
|
||||
"value" : "신원은 이 기기에서 생성되는 키 쌍입니다 — 패닉 와이프로 즉시 교체됩니다"
|
||||
}
|
||||
},
|
||||
"ms" : {
|
||||
"stringUnit" : {
|
||||
"state" : "translated",
|
||||
"value" : "id peer baru dibuat secara berkala"
|
||||
"value" : "identiti anda ialah pasangan kunci yang dicipta pada peranti ini — panic wipe menggantikannya serta-merta"
|
||||
}
|
||||
},
|
||||
"ne" : {
|
||||
"stringUnit" : {
|
||||
"state" : "translated",
|
||||
"value" : "नयाँ peer id नियमित रूपमा सिर्जना हुन्छ"
|
||||
"value" : "तपाईंको परिचय यही यन्त्रमा बनेको कुञ्जी-जोडी हो — प्यानिक वाइपले तुरुन्तै बदल्छ"
|
||||
}
|
||||
},
|
||||
"nl" : {
|
||||
"stringUnit" : {
|
||||
"state" : "translated",
|
||||
"value" : "nieuw peer-ID wordt regelmatig aangemaakt"
|
||||
"value" : "je identiteit is een sleutelpaar dat op dit apparaat wordt gemaakt — de paniekwis vervangt het direct"
|
||||
}
|
||||
},
|
||||
"pl" : {
|
||||
"stringUnit" : {
|
||||
"state" : "translated",
|
||||
"value" : "nowe ID peera generowane regularnie"
|
||||
"value" : "twoja tożsamość to para kluczy utworzona na tym urządzeniu — wymazanie awaryjne natychmiast ją zastępuje"
|
||||
}
|
||||
},
|
||||
"pt" : {
|
||||
"stringUnit" : {
|
||||
"state" : "translated",
|
||||
"value" : "novo ID de par gerado regularmente"
|
||||
"value" : "a tua identidade é um par de chaves criado neste dispositivo — a limpeza de pânico substitui-a de imediato"
|
||||
}
|
||||
},
|
||||
"pt-BR" : {
|
||||
"stringUnit" : {
|
||||
"state" : "translated",
|
||||
"value" : "novo id de peer gerado regularmente"
|
||||
"value" : "sua identidade é um par de chaves criado neste dispositivo — a limpeza de pânico a substitui na hora"
|
||||
}
|
||||
},
|
||||
"ru" : {
|
||||
"stringUnit" : {
|
||||
"state" : "translated",
|
||||
"value" : "новый id пира создаётся регулярно"
|
||||
"value" : "ваша идентичность — пара ключей, созданная на этом устройстве. экстренное стирание мгновенно её заменяет"
|
||||
}
|
||||
},
|
||||
"sv" : {
|
||||
"stringUnit" : {
|
||||
"state" : "translated",
|
||||
"value" : "nytt peer-ID skapas regelbundet"
|
||||
"value" : "din identitet är ett nyckelpar som skapas på den här enheten — panikradering ersätter det direkt"
|
||||
}
|
||||
},
|
||||
"ta" : {
|
||||
"stringUnit" : {
|
||||
"state" : "translated",
|
||||
"value" : "புதிய peer ID வழக்கமாக உருவாக்கப்படுகிறது"
|
||||
"value" : "உங்கள் அடையாளம் இந்தச் சாதனத்தில் உருவாக்கப்படும் விசை-இணை — பேனிக் அழிப்பு உடனே அதை மாற்றும்"
|
||||
}
|
||||
},
|
||||
"th" : {
|
||||
"stringUnit" : {
|
||||
"state" : "translated",
|
||||
"value" : "สร้างรหัสเพียร์ใหม่เป็นระยะ"
|
||||
"value" : "ตัวตนของคุณคือคู่กุญแจที่สร้างบนเครื่องนี้ — panic wipe จะแทนที่ทันที"
|
||||
}
|
||||
},
|
||||
"tr" : {
|
||||
"stringUnit" : {
|
||||
"state" : "translated",
|
||||
"value" : "yeni eş kimliği düzenli olarak oluşturulur"
|
||||
"value" : "kimliğiniz bu cihazda oluşturulan bir anahtar çiftidir — panik silme onu anında değiştirir"
|
||||
}
|
||||
},
|
||||
"uk" : {
|
||||
"stringUnit" : {
|
||||
"state" : "translated",
|
||||
"value" : "новий id піра генерується регулярно"
|
||||
"value" : "ваша ідентичність — пара ключів, створена на цьому пристрої. екстрене стирання миттєво її замінює"
|
||||
}
|
||||
},
|
||||
"ur" : {
|
||||
"stringUnit" : {
|
||||
"state" : "translated",
|
||||
"value" : "نیا peer ID باقاعدگی سے بنایا جاتا ہے"
|
||||
"value" : "آپ کی شناخت اس ڈیوائس پر بنایا گیا کلیدی جوڑا ہے — پینک وائپ اسے فوراً بدل دیتا ہے"
|
||||
}
|
||||
},
|
||||
"vi" : {
|
||||
"stringUnit" : {
|
||||
"state" : "translated",
|
||||
"value" : "tạo ID nút mới định kỳ"
|
||||
"value" : "danh tính của bạn là một cặp khoá được tạo trên thiết bị này — panic wipe thay thế nó ngay lập tức"
|
||||
}
|
||||
},
|
||||
"zh-Hans" : {
|
||||
"stringUnit" : {
|
||||
"state" : "translated",
|
||||
"value" : "定期生成新的 peer id"
|
||||
"value" : "你的身份是在本机生成的密钥对 — 紧急清除会立即更换它"
|
||||
}
|
||||
},
|
||||
"zh-Hant" : {
|
||||
"stringUnit" : {
|
||||
"state" : "translated",
|
||||
"value" : "定期生成新的 peer id"
|
||||
"value" : "你的身分是在本機產生的金鑰對 — 緊急清除會立即更換它"
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -11560,181 +11560,181 @@
|
||||
"ar" : {
|
||||
"stringUnit" : {
|
||||
"state" : "translated",
|
||||
"value" : "هوية مؤقتة"
|
||||
"value" : "هوية محفوظة على الجهاز"
|
||||
}
|
||||
},
|
||||
"bn" : {
|
||||
"stringUnit" : {
|
||||
"state" : "translated",
|
||||
"value" : "অস্থায়ী পরিচয়"
|
||||
"value" : "ডিভাইসে থাকা পরিচয়"
|
||||
}
|
||||
},
|
||||
"de" : {
|
||||
"stringUnit" : {
|
||||
"state" : "translated",
|
||||
"value" : "flüchtige identität"
|
||||
"value" : "gerätelokale identität"
|
||||
}
|
||||
},
|
||||
"en" : {
|
||||
"stringUnit" : {
|
||||
"state" : "translated",
|
||||
"value" : "ephemeral identity"
|
||||
"value" : "device-local identity"
|
||||
}
|
||||
},
|
||||
"es" : {
|
||||
"stringUnit" : {
|
||||
"state" : "translated",
|
||||
"value" : "identidad efímera"
|
||||
"value" : "identidad local del dispositivo"
|
||||
}
|
||||
},
|
||||
"fa" : {
|
||||
"stringUnit" : {
|
||||
"state" : "translated",
|
||||
"value" : "هویت موقت"
|
||||
"value" : "هویت محلی دستگاه"
|
||||
}
|
||||
},
|
||||
"fil" : {
|
||||
"stringUnit" : {
|
||||
"state" : "translated",
|
||||
"value" : "panandaliang pagkakakilanlan"
|
||||
"value" : "pagkakakilanlang nasa device"
|
||||
}
|
||||
},
|
||||
"fr" : {
|
||||
"stringUnit" : {
|
||||
"state" : "translated",
|
||||
"value" : "identité éphémère"
|
||||
"value" : "identité locale à l'appareil"
|
||||
}
|
||||
},
|
||||
"he" : {
|
||||
"stringUnit" : {
|
||||
"state" : "translated",
|
||||
"value" : "זהות זמנית"
|
||||
"value" : "זהות מקומית במכשיר"
|
||||
}
|
||||
},
|
||||
"hi" : {
|
||||
"stringUnit" : {
|
||||
"state" : "translated",
|
||||
"value" : "अस्थायी पहचान"
|
||||
"value" : "डिवाइस-स्थानीय पहचान"
|
||||
}
|
||||
},
|
||||
"id" : {
|
||||
"stringUnit" : {
|
||||
"state" : "translated",
|
||||
"value" : "identitas sementara"
|
||||
"value" : "identitas lokal perangkat"
|
||||
}
|
||||
},
|
||||
"it" : {
|
||||
"stringUnit" : {
|
||||
"state" : "translated",
|
||||
"value" : "identità effimera"
|
||||
"value" : "identità locale al dispositivo"
|
||||
}
|
||||
},
|
||||
"ja" : {
|
||||
"stringUnit" : {
|
||||
"state" : "translated",
|
||||
"value" : "一時的なアイデンティティ"
|
||||
"value" : "端末ローカルのアイデンティティ"
|
||||
}
|
||||
},
|
||||
"ko" : {
|
||||
"stringUnit" : {
|
||||
"state" : "translated",
|
||||
"value" : "임시 신원"
|
||||
"value" : "기기 로컬 신원"
|
||||
}
|
||||
},
|
||||
"ms" : {
|
||||
"stringUnit" : {
|
||||
"state" : "translated",
|
||||
"value" : "identitas sementara"
|
||||
"value" : "identiti setempat peranti"
|
||||
}
|
||||
},
|
||||
"ne" : {
|
||||
"stringUnit" : {
|
||||
"state" : "translated",
|
||||
"value" : "क्षणिक पहिचान"
|
||||
"value" : "यन्त्र-स्थानीय परिचय"
|
||||
}
|
||||
},
|
||||
"nl" : {
|
||||
"stringUnit" : {
|
||||
"state" : "translated",
|
||||
"value" : "tijdelijke identiteit"
|
||||
"value" : "apparaat-lokale identiteit"
|
||||
}
|
||||
},
|
||||
"pl" : {
|
||||
"stringUnit" : {
|
||||
"state" : "translated",
|
||||
"value" : "tymczasowa tożsamość"
|
||||
"value" : "tożsamość lokalna urządzenia"
|
||||
}
|
||||
},
|
||||
"pt" : {
|
||||
"stringUnit" : {
|
||||
"state" : "translated",
|
||||
"value" : "identidade efémera"
|
||||
"value" : "identidade local do dispositivo"
|
||||
}
|
||||
},
|
||||
"pt-BR" : {
|
||||
"stringUnit" : {
|
||||
"state" : "translated",
|
||||
"value" : "identidade efêmera"
|
||||
"value" : "identidade local do dispositivo"
|
||||
}
|
||||
},
|
||||
"ru" : {
|
||||
"stringUnit" : {
|
||||
"state" : "translated",
|
||||
"value" : "эфемерная личность"
|
||||
"value" : "идентичность на устройстве"
|
||||
}
|
||||
},
|
||||
"sv" : {
|
||||
"stringUnit" : {
|
||||
"state" : "translated",
|
||||
"value" : "tillfällig identitet"
|
||||
"value" : "enhetslokal identitet"
|
||||
}
|
||||
},
|
||||
"ta" : {
|
||||
"stringUnit" : {
|
||||
"state" : "translated",
|
||||
"value" : "தற்காலிக அடையாளம்"
|
||||
"value" : "சாதன-உள்ளூர் அடையாளம்"
|
||||
}
|
||||
},
|
||||
"th" : {
|
||||
"stringUnit" : {
|
||||
"state" : "translated",
|
||||
"value" : "ตัวตนชั่วคราว"
|
||||
"value" : "ตัวตนในเครื่อง"
|
||||
}
|
||||
},
|
||||
"tr" : {
|
||||
"stringUnit" : {
|
||||
"state" : "translated",
|
||||
"value" : "geçici kimlik"
|
||||
"value" : "cihaza yerel kimlik"
|
||||
}
|
||||
},
|
||||
"uk" : {
|
||||
"stringUnit" : {
|
||||
"state" : "translated",
|
||||
"value" : "ефемерна ідентичність"
|
||||
"value" : "ідентичність на пристрої"
|
||||
}
|
||||
},
|
||||
"ur" : {
|
||||
"stringUnit" : {
|
||||
"state" : "translated",
|
||||
"value" : "عارضی شناخت"
|
||||
"value" : "ڈیوائس مقامی شناخت"
|
||||
}
|
||||
},
|
||||
"vi" : {
|
||||
"stringUnit" : {
|
||||
"state" : "translated",
|
||||
"value" : "danh tính tạm thời"
|
||||
"value" : "danh tính cục bộ trên thiết bị"
|
||||
}
|
||||
},
|
||||
"zh-Hans" : {
|
||||
"stringUnit" : {
|
||||
"state" : "translated",
|
||||
"value" : "临时身份"
|
||||
"value" : "设备本地身份"
|
||||
}
|
||||
},
|
||||
"zh-Hant" : {
|
||||
"stringUnit" : {
|
||||
"state" : "translated",
|
||||
"value" : "臨時身份"
|
||||
"value" : "裝置本機身分"
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -11745,181 +11745,181 @@
|
||||
"ar" : {
|
||||
"stringUnit" : {
|
||||
"state" : "translated",
|
||||
"value" : "لا خوادم أو حسابات أو جمع بيانات"
|
||||
"value" : "لا حسابات، لا أرقام هواتف، لا جمع بيانات"
|
||||
}
|
||||
},
|
||||
"bn" : {
|
||||
"stringUnit" : {
|
||||
"state" : "translated",
|
||||
"value" : "কোনো সার্ভার, অ্যাকাউন্ট বা ডেটা সংগ্রহ নেই"
|
||||
"value" : "কোনো অ্যাকাউন্ট নেই, ফোন নম্বর নেই, ডেটা সংগ্রহ নেই"
|
||||
}
|
||||
},
|
||||
"de" : {
|
||||
"stringUnit" : {
|
||||
"state" : "translated",
|
||||
"value" : "keine server, konten oder datensammlung"
|
||||
"value" : "keine konten, keine telefonnummern, keine datensammlung"
|
||||
}
|
||||
},
|
||||
"en" : {
|
||||
"stringUnit" : {
|
||||
"state" : "translated",
|
||||
"value" : "no servers, accounts, or data collection"
|
||||
"value" : "no accounts, no phone numbers, no data collection"
|
||||
}
|
||||
},
|
||||
"es" : {
|
||||
"stringUnit" : {
|
||||
"state" : "translated",
|
||||
"value" : "sin servidores, cuentas ni recopilación de datos"
|
||||
"value" : "sin cuentas, sin números de teléfono, sin recopilación de datos"
|
||||
}
|
||||
},
|
||||
"fa" : {
|
||||
"stringUnit" : {
|
||||
"state" : "translated",
|
||||
"value" : "بدون سرور، حساب کاربری یا جمعآوری داده"
|
||||
"value" : "بدون حساب، بدون شماره تلفن، بدون جمعآوری داده"
|
||||
}
|
||||
},
|
||||
"fil" : {
|
||||
"stringUnit" : {
|
||||
"state" : "translated",
|
||||
"value" : "walang mga server, account, o pagkuha ng data"
|
||||
"value" : "walang account, walang numero ng telepono, walang pangongolekta ng data"
|
||||
}
|
||||
},
|
||||
"fr" : {
|
||||
"stringUnit" : {
|
||||
"state" : "translated",
|
||||
"value" : "sans serveurs, comptes ni collecte de données"
|
||||
"value" : "pas de comptes, pas de numéros de téléphone, pas de collecte de données"
|
||||
}
|
||||
},
|
||||
"he" : {
|
||||
"stringUnit" : {
|
||||
"state" : "translated",
|
||||
"value" : "אין שרתים, חשבונות או איסוף נתונים"
|
||||
"value" : "בלי חשבונות, בלי מספרי טלפון, בלי איסוף נתונים"
|
||||
}
|
||||
},
|
||||
"hi" : {
|
||||
"stringUnit" : {
|
||||
"state" : "translated",
|
||||
"value" : "कोई सर्वर, खाते या डेटा संग्रह नहीं"
|
||||
"value" : "कोई खाता नहीं, कोई फ़ोन नंबर नहीं, कोई डेटा संग्रह नहीं"
|
||||
}
|
||||
},
|
||||
"id" : {
|
||||
"stringUnit" : {
|
||||
"state" : "translated",
|
||||
"value" : "tanpa server, akun, atau pengumpulan data"
|
||||
"value" : "tanpa akun, tanpa nomor telepon, tanpa pengumpulan data"
|
||||
}
|
||||
},
|
||||
"it" : {
|
||||
"stringUnit" : {
|
||||
"state" : "translated",
|
||||
"value" : "niente server, account o raccolta dati"
|
||||
"value" : "niente account, niente numeri di telefono, nessuna raccolta dati"
|
||||
}
|
||||
},
|
||||
"ja" : {
|
||||
"stringUnit" : {
|
||||
"state" : "translated",
|
||||
"value" : "サーバーもアカウントもデータ収集もなし"
|
||||
"value" : "アカウントなし・電話番号なし・データ収集なし"
|
||||
}
|
||||
},
|
||||
"ko" : {
|
||||
"stringUnit" : {
|
||||
"state" : "translated",
|
||||
"value" : "서버와 계정이 없으며 데이터를 수집하지 않습니다"
|
||||
"value" : "계정 없음, 전화번호 없음, 데이터 수집 없음"
|
||||
}
|
||||
},
|
||||
"ms" : {
|
||||
"stringUnit" : {
|
||||
"state" : "translated",
|
||||
"value" : "tanpa server, akun, atau pengumpulan data"
|
||||
"value" : "tiada akaun, tiada nombor telefon, tiada pengumpulan data"
|
||||
}
|
||||
},
|
||||
"ne" : {
|
||||
"stringUnit" : {
|
||||
"state" : "translated",
|
||||
"value" : "सर्भर, खाताहरू वा तथ्याङ्क सङ्कलन छैन"
|
||||
"value" : "कुनै खाता छैन, फोन नम्बर छैन, डेटा सङ्कलन छैन"
|
||||
}
|
||||
},
|
||||
"nl" : {
|
||||
"stringUnit" : {
|
||||
"state" : "translated",
|
||||
"value" : "geen servers, accounts of dataverzameling"
|
||||
"value" : "geen accounts, geen telefoonnummers, geen dataverzameling"
|
||||
}
|
||||
},
|
||||
"pl" : {
|
||||
"stringUnit" : {
|
||||
"state" : "translated",
|
||||
"value" : "bez serwerów, kont ani zbierania danych"
|
||||
"value" : "bez kont, bez numerów telefonów, bez zbierania danych"
|
||||
}
|
||||
},
|
||||
"pt" : {
|
||||
"stringUnit" : {
|
||||
"state" : "translated",
|
||||
"value" : "sem servidores, contas ou recolha de dados"
|
||||
"value" : "sem contas, sem números de telefone, sem recolha de dados"
|
||||
}
|
||||
},
|
||||
"pt-BR" : {
|
||||
"stringUnit" : {
|
||||
"state" : "translated",
|
||||
"value" : "sem servidores, contas ou coleta de dados"
|
||||
"value" : "sem contas, sem números de telefone, sem coleta de dados"
|
||||
}
|
||||
},
|
||||
"ru" : {
|
||||
"stringUnit" : {
|
||||
"state" : "translated",
|
||||
"value" : "без серверов, аккаунтов и сбора данных"
|
||||
"value" : "без аккаунтов, без номеров телефона, без сбора данных"
|
||||
}
|
||||
},
|
||||
"sv" : {
|
||||
"stringUnit" : {
|
||||
"state" : "translated",
|
||||
"value" : "inga servrar, konton eller datainsamling"
|
||||
"value" : "inga konton, inga telefonnummer, ingen datainsamling"
|
||||
}
|
||||
},
|
||||
"ta" : {
|
||||
"stringUnit" : {
|
||||
"state" : "translated",
|
||||
"value" : "சர்வர்கள் இல்லை, கணக்குகள் இல்லை, தரவுச் சேகரிப்பு இல்லை"
|
||||
"value" : "கணக்குகள் இல்லை, தொலைபேசி எண்கள் இல்லை, தரவு சேகரிப்பு இல்லை"
|
||||
}
|
||||
},
|
||||
"th" : {
|
||||
"stringUnit" : {
|
||||
"state" : "translated",
|
||||
"value" : "ไม่มีเซิร์ฟเวอร์ บัญชี หรือการเก็บข้อมูล"
|
||||
"value" : "ไม่มีบัญชี ไม่มีเบอร์โทรศัพท์ ไม่มีการเก็บข้อมูล"
|
||||
}
|
||||
},
|
||||
"tr" : {
|
||||
"stringUnit" : {
|
||||
"state" : "translated",
|
||||
"value" : "sunucu, hesap veya veri toplama yok"
|
||||
"value" : "hesap yok, telefon numarası yok, veri toplama yok"
|
||||
}
|
||||
},
|
||||
"uk" : {
|
||||
"stringUnit" : {
|
||||
"state" : "translated",
|
||||
"value" : "жодних серверів, обліковок чи збору даних"
|
||||
"value" : "без облікових записів, без номерів телефону, без збору даних"
|
||||
}
|
||||
},
|
||||
"ur" : {
|
||||
"stringUnit" : {
|
||||
"state" : "translated",
|
||||
"value" : "کوئی سرور، اکاؤنٹس یا ڈیٹا جمع نہیں کیا جاتا"
|
||||
"value" : "نہ اکاؤنٹ، نہ فون نمبر، نہ ڈیٹا اکٹھا کرنا"
|
||||
}
|
||||
},
|
||||
"vi" : {
|
||||
"stringUnit" : {
|
||||
"state" : "translated",
|
||||
"value" : "không máy chủ, không tài khoản, không thu thập dữ liệu"
|
||||
"value" : "không tài khoản, không số điện thoại, không thu thập dữ liệu"
|
||||
}
|
||||
},
|
||||
"zh-Hans" : {
|
||||
"stringUnit" : {
|
||||
"state" : "translated",
|
||||
"value" : "无服务器、无账号、无数据收集"
|
||||
"value" : "无账号、无电话号码、无数据收集"
|
||||
}
|
||||
},
|
||||
"zh-Hant" : {
|
||||
"stringUnit" : {
|
||||
"state" : "translated",
|
||||
"value" : "無伺服器、無帳號、無數據收集"
|
||||
"value" : "無帳號、無電話號碼、無資料收集"
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -52662,181 +52662,181 @@
|
||||
"ar" : {
|
||||
"stringUnit" : {
|
||||
"state" : "translated",
|
||||
"value" : "تحدث مع أشخاص قريبين عبر قنوات geohash. نشارك geohash تقريبي فقط، وليس gps الدقيق. يتم إخفاء عنوان ip لأن كل المرور يمر عبر tor."
|
||||
"value" : "دردش مع من حولك عبر قنوات geohash. الـ geohash المحدد علني وقد يكشف منطقة تقريبية؛ لا تتم مشاركة إحداثيات GPS الدقيقة أبدًا. مع تشغيل tor (الوضع الافتراضي)، ترى المرحّلات عنوان tor بدلاً من عنوان IP الخاص بك."
|
||||
}
|
||||
},
|
||||
"bn" : {
|
||||
"stringUnit" : {
|
||||
"state" : "translated",
|
||||
"value" : "জিওহ্যাশ চ্যানেল দিয়ে কাছাকাছি অঞ্চলের মানুষের সঙ্গে চ্যাট করুন। শুধুই স্থূল জিওহ্যাশ শেয়ার হয়, কখনো সঠিক GPS নয়। আপনার IP টর দিয়ে সব ট্রাফিক রাউট করে লুকানো থাকে।"
|
||||
"value" : "geohash চ্যানেল ব্যবহার করে কাছের মানুষদের সঙ্গে চ্যাট করুন। নির্বাচিত geohash সর্বজনীন এবং আনুমানিক এলাকা প্রকাশ করতে পারে; সঠিক GPS কখনও শেয়ার হয় না। tor চালু থাকলে (ডিফল্ট), রিলেগুলো আপনার IP-এর বদলে tor-এর ঠিকানা দেখে।"
|
||||
}
|
||||
},
|
||||
"de" : {
|
||||
"stringUnit" : {
|
||||
"state" : "translated",
|
||||
"value" : "chatte mit menschen in deiner nähe über geohash-kanäle. geteilt wird nur ein grober geohash, niemals exakte gps-daten. deine ip bleibt verborgen, weil der gesamte verkehr über tor läuft."
|
||||
"value" : "chatte mit leuten in deiner nähe über geohash-kanäle. der gewählte geohash ist öffentlich und kann ein ungefähres gebiet verraten; exaktes GPS wird nie geteilt. mit eingeschaltetem tor (standard) sehen relays tors adresse statt deiner IP-adresse."
|
||||
}
|
||||
},
|
||||
"en" : {
|
||||
"stringUnit" : {
|
||||
"state" : "translated",
|
||||
"value" : "chat with people near you using geohash channels. the selected geohash is public and may reveal an approximate area; exact GPS is never shared. your IP address is hidden by routing all traffic over tor."
|
||||
"value" : "chat with people near you using geohash channels. the selected geohash is public and may reveal an approximate area; exact GPS is never shared. with tor on (the default), relays see tor's address instead of your IP address."
|
||||
}
|
||||
},
|
||||
"es" : {
|
||||
"stringUnit" : {
|
||||
"state" : "translated",
|
||||
"value" : "chatea con personas cercanas usando canales geohash. Solo se comparte un geohash aproximado, nunca GPS exacto. Tu IP se oculta al enrutar todo el tráfico por Tor."
|
||||
"value" : "chatea con gente cercana usando canales geohash. el geohash seleccionado es público y puede revelar una zona aproximada; el GPS exacto nunca se comparte. con tor activado (el valor predeterminado), los relés ven la dirección de tor en lugar de tu IP."
|
||||
}
|
||||
},
|
||||
"fa" : {
|
||||
"stringUnit" : {
|
||||
"state" : "translated",
|
||||
"value" : "با کانالهای ژئوهش با افراد نزدیک خود گفتگو کنید. ژئوهش انتخابشده عمومی است و ممکن است منطقهٔ تقریبی شما را نشان دهد؛ GPS دقیق هرگز به اشتراک گذاشته نمیشود. با عبور همهٔ ترافیک از Tor، آدرس IP شما پنهان میماند."
|
||||
"value" : "با افراد نزدیک خود از طریق کانالهای geohash گفتگو کنید. geohash انتخابی عمومی است و ممکن است منطقهای تقریبی را نشان دهد؛ GPS دقیق هرگز به اشتراک گذاشته نمیشود. با روشن بودن tor (پیشفرض)، رلهها به جای IP شما آدرس tor را میبینند."
|
||||
}
|
||||
},
|
||||
"fil" : {
|
||||
"stringUnit" : {
|
||||
"state" : "translated",
|
||||
"value" : "makipag-chat sa mga taong malapit sa iyo gamit ang mga geohash channel. coarse na geohash lang ang ibinabahagi, hindi ang eksaktong GPS. tinatago ang iyong IP address sa pagreruta ng lahat ng traffic sa tor."
|
||||
"value" : "makipag-chat sa mga taong malapit sa iyo gamit ang mga geohash channel. pampubliko ang napiling geohash at maaaring magbunyag ng tinatayang lugar; hindi kailanman ibinabahagi ang eksaktong GPS. kapag naka-on ang tor (ang default), ang address ng tor ang nakikita ng mga relay sa halip na ang iyong IP."
|
||||
}
|
||||
},
|
||||
"fr" : {
|
||||
"stringUnit" : {
|
||||
"state" : "translated",
|
||||
"value" : "discute avec les personnes proches grâce aux canaux geohash. seul un geohash grossier est partagé, jamais de gps exact. ton ip reste cachée car tout le trafic passe par tor."
|
||||
"value" : "discutez avec les gens autour de vous via les canaux geohash. le geohash choisi est public et peut révéler une zone approximative ; le GPS exact n'est jamais partagé. avec tor activé (par défaut), les relais voient l'adresse de tor au lieu de votre IP."
|
||||
}
|
||||
},
|
||||
"he" : {
|
||||
"stringUnit" : {
|
||||
"state" : "translated",
|
||||
"value" : "שוחח עם אנשים קרובים בערוצי geohash. משתף רק geohash גס, אף פעם לא gps מדויק. כתובת ה-ip מוסתרת כי כל התעבורה עוברת דרך tor."
|
||||
"value" : "שוחחו עם אנשים בסביבה דרך ערוצי geohash. ה-geohash שנבחר הוא ציבורי ועשוי לחשוף אזור משוער; מיקום GPS מדויק לעולם לא משותף. כש-tor פועל (ברירת המחדל), הממסרים רואים את הכתובת של tor במקום ה-IP שלכם."
|
||||
}
|
||||
},
|
||||
"hi" : {
|
||||
"stringUnit" : {
|
||||
"state" : "translated",
|
||||
"value" : "जियोहैश चैनलों से आसपास के लोगों से चैट करें। केवल मोटा जियोहैश साझा होता है, कभी सटीक GPS नहीं। आपका IP सारा ट्रैफ़िक Tor से रूट कर छिपा रहता है।"
|
||||
"value" : "geohash चैनलों के ज़रिए आस-पास के लोगों से चैट करें। चुना गया geohash सार्वजनिक है और अनुमानित क्षेत्र दिखा सकता है; सटीक GPS कभी साझा नहीं होता। tor चालू होने पर (डिफ़ॉल्ट), रिले आपके IP की जगह tor का पता देखते हैं।"
|
||||
}
|
||||
},
|
||||
"id" : {
|
||||
"stringUnit" : {
|
||||
"state" : "translated",
|
||||
"value" : "ngobrol dengan orang terdekat lewat kanal geohash. hanya geohash kasar yang dibagikan, tidak pernah gps tepat. alamat ip-mu tersembunyi karena seluruh trafik lewat tor."
|
||||
"value" : "mengobrol dengan orang di sekitar melalui kanal geohash. geohash yang dipilih bersifat publik dan dapat mengungkap area perkiraan; GPS persis tidak pernah dibagikan. dengan tor aktif (bawaan), relay melihat alamat tor alih-alih IP anda."
|
||||
}
|
||||
},
|
||||
"it" : {
|
||||
"stringUnit" : {
|
||||
"state" : "translated",
|
||||
"value" : "chatta con le persone vicine tramite canali geohash. condividiamo solo geohash approssimativi, mai il gps esatto. il tuo ip resta nascosto perché tutto il traffico passa da tor."
|
||||
"value" : "chatta con chi ti sta vicino usando i canali geohash. il geohash selezionato è pubblico e può rivelare un'area approssimativa; il GPS esatto non viene mai condiviso. con tor attivo (impostazione predefinita), i relay vedono l'indirizzo di tor invece del tuo IP."
|
||||
}
|
||||
},
|
||||
"ja" : {
|
||||
"stringUnit" : {
|
||||
"state" : "translated",
|
||||
"value" : "geohashチャンネルで近くの人と会話。共有されるのはざっくりしたgeohashだけで正確なgpsは含みません。全トラフィックをtor経由にすることであなたのipを隠します。"
|
||||
"value" : "geohash チャンネルで近くの人とチャットできます。選択した geohash は公開され、おおよそのエリアが分かる場合があります。正確な GPS は決して共有されません。tor が有効(デフォルト)なら、リレーにはあなたの IP ではなく tor のアドレスが見えます。"
|
||||
}
|
||||
},
|
||||
"ko" : {
|
||||
"stringUnit" : {
|
||||
"state" : "translated",
|
||||
"value" : "geohash 채널을 사용해 주변 사람들과 대화하세요. 정확한 GPS가 아닌 대략적인 geohash만 공유됩니다. 모든 트래픽을 tor로 라우팅하여 IP 주소를 숨깁니다."
|
||||
"value" : "geohash 채널로 주변 사람들과 대화하세요. 선택한 geohash는 공개되며 대략적인 지역이 드러날 수 있습니다. 정확한 GPS는 절대 공유되지 않습니다. tor가 켜져 있으면(기본값) 릴레이에는 내 IP 대신 tor의 주소가 보입니다."
|
||||
}
|
||||
},
|
||||
"ms" : {
|
||||
"stringUnit" : {
|
||||
"state" : "translated",
|
||||
"value" : "ngobrol dengan orang terdekat lewat kanal geohash. hanya geohash kasar yang dibagikan, tidak pernah gps tepat. alamat ip-mu tersembunyi karena seluruh trafik lewat tor."
|
||||
"value" : "berbual dengan orang berdekatan melalui saluran geohash. geohash yang dipilih adalah umum dan mungkin mendedahkan kawasan anggaran; GPS tepat tidak pernah dikongsi. dengan tor dihidupkan (lalai), relay melihat alamat tor dan bukan IP anda."
|
||||
}
|
||||
},
|
||||
"ne" : {
|
||||
"stringUnit" : {
|
||||
"state" : "translated",
|
||||
"value" : "geohash च्यानलबाट नजिकका मानिससँग कुरा गर। केवल मोटामो geohash साझा हुन्छ, कहिल्यै सहि gps होइन। सबै ट्राफिक tor मार्फत गएका कारण तिम्रो ip लुकेको हुन्छ।"
|
||||
"value" : "geohash च्यानलहरू प्रयोग गरेर नजिकका मानिसहरूसँग कुराकानी गर्नुहोस्। चयन गरिएको geohash सार्वजनिक हुन्छ र अनुमानित क्षेत्र देखाउन सक्छ; सटीक GPS कहिल्यै साझा हुँदैन। tor खुला हुँदा (पूर्वनिर्धारित), रिलेहरूले तपाईंको IP को सट्टा tor को ठेगाना देख्छन्।"
|
||||
}
|
||||
},
|
||||
"nl" : {
|
||||
"stringUnit" : {
|
||||
"state" : "translated",
|
||||
"value" : "chat met mensen bij jou in de buurt via geohash-kanalen. enkel een grove geohash wordt gedeeld, nooit exacte GPS. je IP-adres wordt verborgen doordat al het verkeer via tor wordt gerouteerd."
|
||||
"value" : "chat met mensen in de buurt via geohash-kanalen. de gekozen geohash is openbaar en kan een globaal gebied onthullen; exacte GPS wordt nooit gedeeld. met tor aan (standaard) zien relays het adres van tor in plaats van je IP-adres."
|
||||
}
|
||||
},
|
||||
"pl" : {
|
||||
"stringUnit" : {
|
||||
"state" : "translated",
|
||||
"value" : "rozmawiaj z osobami w pobliżu za pomocą kanałów geohash. udostępniany jest tylko zgrubny geohash, nigdy dokładny GPS. twój adres IP jest ukryty przez kierowanie całego ruchu przez tor."
|
||||
"value" : "rozmawiaj z ludźmi w pobliżu przez kanały geohash. wybrany geohash jest publiczny i może zdradzić przybliżony obszar; dokładny GPS nigdy nie jest udostępniany. przy włączonym tor (domyślnie) przekaźniki widzą adres tora zamiast twojego IP."
|
||||
}
|
||||
},
|
||||
"pt" : {
|
||||
"stringUnit" : {
|
||||
"state" : "translated",
|
||||
"value" : "conversa com pessoas perto de ti usando canais geohash. apenas um geohash de baixa precisão é partilhado, nunca GPS exato. o teu IP fica oculto ao encaminhar todo o tráfego através do Tor."
|
||||
"value" : "conversa com pessoas por perto através de canais geohash. o geohash selecionado é público e pode revelar uma área aproximada; o GPS exato nunca é partilhado. com o tor ligado (predefinição), os relays veem o endereço do tor em vez do teu IP."
|
||||
}
|
||||
},
|
||||
"pt-BR" : {
|
||||
"stringUnit" : {
|
||||
"state" : "translated",
|
||||
"value" : "converse com pessoas próximas usando canais geohash. apenas um geohash grosseiro é compartilhado, nunca gps exato. seu ip fica oculto ao rotear todo o tráfego por tor."
|
||||
"value" : "converse com pessoas por perto usando canais geohash. o geohash selecionado é público e pode revelar uma área aproximada; o GPS exato nunca é compartilhado. com o tor ligado (padrão), os relays veem o endereço do tor em vez do seu IP."
|
||||
}
|
||||
},
|
||||
"ru" : {
|
||||
"stringUnit" : {
|
||||
"state" : "translated",
|
||||
"value" : "общайся с людьми рядом через каналы geohash. делится только грубый geohash, без точного gps. твой ip скрывается за счёт маршрутизации трафика через tor."
|
||||
"value" : "общайтесь с людьми поблизости через geohash-каналы. выбранный geohash публичен и может раскрыть примерный район; точные GPS-координаты никогда не передаются. при включённом tor (по умолчанию) релеи видят адрес tor вместо вашего IP."
|
||||
}
|
||||
},
|
||||
"sv" : {
|
||||
"stringUnit" : {
|
||||
"state" : "translated",
|
||||
"value" : "chatta med folk nära dig via geohash-kanaler. endast en grov geohash delas, aldrig exakt GPS. din IP-adress döljs genom att all trafik går via tor."
|
||||
"value" : "chatta med folk i närheten via geohash-kanaler. vald geohash är offentlig och kan avslöja ett ungefärligt område; exakt GPS delas aldrig. med tor på (standard) ser reläer tors adress istället för din IP."
|
||||
}
|
||||
},
|
||||
"ta" : {
|
||||
"stringUnit" : {
|
||||
"state" : "translated",
|
||||
"value" : "geohash சேனல்கள் மூலம் அருகிலுள்ளவர்களுடன் உரையாடுங்கள். மிகவும் பொது geohash மட்டுமே பகிரப்படும், துல்லியமான GPS அல்ல. உங்கள் IP, அனைத்து போக்குவரத்தையும் tor வழியாக மாற்றுவதன் மூலம் மறைக்கப்படுகிறது."
|
||||
"value" : "geohash சேனல்கள் மூலம் அருகிலுள்ளவர்களுடன் அரட்டையடிக்கவும். தேர்ந்தெடுத்த geohash பொதுவானது, தோராயமான பகுதியை வெளிப்படுத்தலாம்; துல்லியமான GPS ஒருபோதும் பகிரப்படாது. tor இயக்கத்தில் இருந்தால் (இயல்புநிலை), ரிலேக்கள் உங்கள் IP க்குப் பதிலாக tor இன் முகவரியைக் காண்கின்றன."
|
||||
}
|
||||
},
|
||||
"th" : {
|
||||
"stringUnit" : {
|
||||
"state" : "translated",
|
||||
"value" : "แชทกับคนใกล้คุณผ่านช่อง geohash แบ่งปันเพียง geohash แบบหยาบ ไม่ใช่ GPS ที่แม่นยำ ที่อยู่ IP ของคุณถูกซ่อนด้วยการส่งข้อมูลผ่าน tor"
|
||||
"value" : "แชทกับผู้คนใกล้ตัวผ่านช่อง geohash โดย geohash ที่เลือกเป็นแบบสาธารณะและอาจเผยพื้นที่โดยประมาณ พิกัด GPS ที่แน่นอนจะไม่ถูกแชร์ เมื่อเปิด tor (ค่าเริ่มต้น) รีเลย์จะเห็นที่อยู่ของ tor แทน IP ของคุณ"
|
||||
}
|
||||
},
|
||||
"tr" : {
|
||||
"stringUnit" : {
|
||||
"state" : "translated",
|
||||
"value" : "geohash kanallarıyla yakınınızdaki insanlarla sohbet edin. yalnızca kaba bir geohash paylaşılır, tam GPS asla değil. IP adresiniz tüm trafik Tor üzerinden yönlendirilerek gizlenir."
|
||||
"value" : "geohash kanallarıyla yakınınızdaki insanlarla sohbet edin. seçilen geohash herkese açıktır ve yaklaşık bir bölgeyi açığa çıkarabilir; tam GPS asla paylaşılmaz. tor açıkken (varsayılan) röleler IP'niz yerine tor'un adresini görür."
|
||||
}
|
||||
},
|
||||
"uk" : {
|
||||
"stringUnit" : {
|
||||
"state" : "translated",
|
||||
"value" : "спілкуйся з людьми поруч у каналах geohash. передається лише грубий geohash, без точного gps. твій ip приховується, бо весь трафік йде через tor."
|
||||
"value" : "спілкуйтеся з людьми поруч через geohash-канали. вибраний geohash є публічним і може розкрити приблизний район; точні GPS-координати ніколи не передаються. з увімкненим tor (типово) релеї бачать адресу tor замість вашого IP."
|
||||
}
|
||||
},
|
||||
"ur" : {
|
||||
"stringUnit" : {
|
||||
"state" : "translated",
|
||||
"value" : "قریب کے لوگوں سے گفتگو کیلئے geohash چینلز استعمال کریں۔ صرف عمومی geohash شیئر کیا جاتا ہے، کبھی درست GPS نہیں۔ آپ کا IP tor کے ذریعے ساری ٹریفک روٹ کر کے چھپایا جاتا ہے۔"
|
||||
"value" : "geohash چینلز کے ذریعے قریبی لوگوں سے چیٹ کریں۔ منتخب geohash عوامی ہے اور تخمینی علاقہ ظاہر کر سکتا ہے؛ درست GPS کبھی شیئر نہیں ہوتا۔ tor آن ہونے پر (ڈیفالٹ)، ریلے آپ کے IP کی بجائے tor کا پتہ دیکھتے ہیں۔"
|
||||
}
|
||||
},
|
||||
"vi" : {
|
||||
"stringUnit" : {
|
||||
"state" : "translated",
|
||||
"value" : "trò chuyện với người gần bạn bằng các kênh geohash. chỉ chia sẻ geohash thô, không bao giờ là GPS chính xác. địa chỉ IP của bạn được ẩn bằng cách định tuyến toàn bộ lưu lượng qua tor."
|
||||
"value" : "trò chuyện với những người gần bạn qua các kênh geohash. geohash đã chọn là công khai và có thể tiết lộ khu vực gần đúng; GPS chính xác không bao giờ được chia sẻ. khi bật tor (mặc định), các relay thấy địa chỉ của tor thay vì IP của bạn."
|
||||
}
|
||||
},
|
||||
"zh-Hans" : {
|
||||
"stringUnit" : {
|
||||
"state" : "translated",
|
||||
"value" : "使用 geohash 频道与附近的人聊天。只会共享粗略 geohash,从不泄露精确 GPS。所有流量通过 tor 路由来隐藏你的 IP。"
|
||||
"value" : "通过 geohash 频道与附近的人聊天。所选 geohash 是公开的,可能暴露大致区域;精确 GPS 绝不会被共享。开启 tor 时(默认),中继看到的是 tor 的地址而不是你的 IP。"
|
||||
}
|
||||
},
|
||||
"zh-Hant" : {
|
||||
"stringUnit" : {
|
||||
"state" : "translated",
|
||||
"value" : "使用 geohash 頻道與附近的人聊天。只會共享粗略 geohash,從不洩露精確 GPS。所有流量透過 tor 路由來隱藏你的 IP。"
|
||||
"value" : "透過 geohash 頻道與附近的人聊天。所選 geohash 是公開的,可能透露大致區域;精確 GPS 絕不會被分享。開啟 tor 時(預設),中繼看到的是 tor 的位址而不是你的 IP。"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -33,13 +33,22 @@ struct BitchatPeer: Equatable {
|
||||
return .bluetoothConnected
|
||||
} else if isReachable {
|
||||
return .meshReachable
|
||||
} else if favoriteStatus?.isMutual == true {
|
||||
// Mutual favorites can communicate via Nostr when offline
|
||||
} else if favoriteStatus?.isMutual == true, reachableNostrPublicKey != nil {
|
||||
// Mutual favorites can communicate via Nostr when offline — but
|
||||
// only with a stored recipient key. NostrTransport applies the
|
||||
// same rule when computing reachable peers; without a key,
|
||||
// "available" would be a lie, and the DM header's
|
||||
// "end-to-end encrypted" caption keys off this state.
|
||||
return .nostrAvailable
|
||||
} else {
|
||||
return .offline
|
||||
}
|
||||
}
|
||||
|
||||
/// The Nostr key a private envelope would actually be sealed to, if any.
|
||||
var reachableNostrPublicKey: String? {
|
||||
nostrPublicKey ?? favoriteStatus?.peerNostrPublicKey
|
||||
}
|
||||
|
||||
var isFavorite: Bool {
|
||||
favoriteStatus?.isFavorite ?? false
|
||||
|
||||
@ -440,7 +440,13 @@ final class ChatPeerIdentityCoordinator {
|
||||
return cachedStatus
|
||||
}
|
||||
|
||||
let hasEverEstablishedSession = getFingerprint(for: peerID) != nil
|
||||
// The status must reflect the LIVE session, never history. The old
|
||||
// mapping returned secured/verified for any peer whose fingerprint was
|
||||
// ever persisted — so after a cold launch or a handshake FAILURE the
|
||||
// DM header still showed a solid lock and the composer still claimed
|
||||
// "end-to-end encrypted" with no secure session in existence. A
|
||||
// remembered fingerprint changes what an established session upgrades
|
||||
// to (verified vs secured); it must not conjure a lock on its own.
|
||||
let sessionState = context.noiseSessionState(for: peerID)
|
||||
|
||||
let status: EncryptionStatus
|
||||
@ -448,11 +454,11 @@ final class ChatPeerIdentityCoordinator {
|
||||
case .established:
|
||||
status = verifiedEncryptionStatus(for: peerID)
|
||||
case .handshaking, .handshakeQueued:
|
||||
status = hasEverEstablishedSession ? verifiedEncryptionStatus(for: peerID) : .noiseHandshaking
|
||||
status = .noiseHandshaking
|
||||
case .none:
|
||||
status = hasEverEstablishedSession ? verifiedEncryptionStatus(for: peerID) : .noHandshake
|
||||
status = .noHandshake
|
||||
case .failed:
|
||||
status = hasEverEstablishedSession ? verifiedEncryptionStatus(for: peerID) : .none
|
||||
status = .none
|
||||
}
|
||||
|
||||
context.setCachedEncryptionStatus(status, for: peerID)
|
||||
|
||||
@ -615,7 +615,9 @@ private struct ContentPrivateChatSheetView: View {
|
||||
// Geohash DMs use BitChat's private-envelope encryption over Nostr —
|
||||
// always end-to-end encrypted,
|
||||
// even though they carry no Noise session status. Mesh DMs earn the
|
||||
// "encrypted" claim only once the Noise handshake has secured.
|
||||
// "encrypted" claim only once the Noise handshake has secured — or
|
||||
// when the peer is reachable only over Nostr, where delivery is
|
||||
// gift-wrapped end-to-end without a Noise session.
|
||||
let isGeoDM = privateConversationModel.selectedPeerID?.isGeoDM == true
|
||||
let noiseSecured: Bool = {
|
||||
switch privateConversationModel.selectedHeaderState?.encryptionStatus {
|
||||
@ -623,7 +625,8 @@ private struct ContentPrivateChatSheetView: View {
|
||||
default: return false
|
||||
}
|
||||
}()
|
||||
if isGeoDM || noiseSecured {
|
||||
let nostrTransport = privateConversationModel.selectedHeaderState?.availability == .nostrAvailable
|
||||
if isGeoDM || noiseSecured || nostrTransport {
|
||||
return String(localized: "content.private.caption_encrypted", comment: "Caption above the private chat composer once the session is end-to-end encrypted")
|
||||
}
|
||||
return String(localized: "content.private.caption", comment: "Caption above the private chat composer before encryption is established")
|
||||
|
||||
@ -27,6 +27,9 @@ struct LocationChannelsSheet: View {
|
||||
static let grantToFind: LocalizedStringKey = "location_channels.grant_to_find"
|
||||
static let teleport: LocalizedStringKey = "location_channels.action.teleport"
|
||||
static let bookmarked: LocalizedStringKey = "location_channels.bookmarked_section_title"
|
||||
// Same string the settings pane shows under the tor toggle — the
|
||||
// warning belongs wherever the exposure is about to happen.
|
||||
static let torOffWarning = String(localized: "app_info.settings.tor.off_warning", defaultValue: "tor is off: every relay you connect to can see your IP address, including relays carrying your private messages.", comment: "Warning shown under the tor toggle while tor is switched off, stating that relay operators can see the device IP address")
|
||||
|
||||
static let quickJoinTitle = String(localized: "location_channels.quick_join.title", defaultValue: "quick join", comment: "Section header in the location channels sheet for the one-tap suggestion of the region channel derived from the device region")
|
||||
static func quickJoinDescription(_ regionName: String) -> String {
|
||||
@ -126,6 +129,16 @@ struct LocationChannelsSheet: View {
|
||||
.bitchatFont(size: 12)
|
||||
.foregroundColor(palette.secondary)
|
||||
|
||||
// The description's tor claim is only true while tor is on;
|
||||
// when it's off, say what that exposes right where the person
|
||||
// is about to join a channel, not just in settings.
|
||||
if !locationChannelsModel.userTorEnabled {
|
||||
Text(verbatim: Strings.torOffWarning)
|
||||
.bitchatFont(size: 11)
|
||||
.foregroundColor(palette.alertRed)
|
||||
.fixedSize(horizontal: false, vertical: true)
|
||||
}
|
||||
|
||||
Group {
|
||||
switch locationChannelsModel.permissionState {
|
||||
case .notDetermined:
|
||||
|
||||
@ -36,6 +36,30 @@ struct BitchatPeerTests {
|
||||
#expect(peer.statusIcon == "🌙")
|
||||
}
|
||||
|
||||
@Test("Mutual favorite without a stored Nostr key is offline, not nostr-available")
|
||||
func mutualFavoriteWithoutNostrKeyIsOffline() {
|
||||
let peerID = PeerID(str: "0123456789abcdef")
|
||||
let noiseKey = Data((0..<32).map(UInt8.init))
|
||||
var peer = BitchatPeer(peerID: peerID, noisePublicKey: noiseKey, nickname: "A", isConnected: false, isReachable: false)
|
||||
peer.favoriteStatus = FavoriteRelationship(
|
||||
peerNoisePublicKey: noiseKey,
|
||||
peerNostrPublicKey: nil,
|
||||
peerNickname: "A",
|
||||
isFavorite: true,
|
||||
theyFavoritedUs: true,
|
||||
favoritedAt: Date(timeIntervalSince1970: 1),
|
||||
lastUpdated: Date(timeIntervalSince1970: 2)
|
||||
)
|
||||
|
||||
// Nothing to seal a Nostr envelope to: claiming availability here
|
||||
// would relight the DM header's "end-to-end encrypted" caption with
|
||||
// neither a Noise session nor a usable recipient key.
|
||||
#expect(peer.connectionState == .offline)
|
||||
|
||||
peer.nostrPublicKey = "abcdef"
|
||||
#expect(peer.connectionState == .nostrAvailable)
|
||||
}
|
||||
|
||||
@Test("Mutual offline peers show Nostr icon")
|
||||
func mutualFavoriteOfflinePeerShowsNostrIcon() {
|
||||
let peerID = PeerID(str: "0011223344556677")
|
||||
|
||||
@ -360,6 +360,37 @@ struct ChatPeerIdentityCoordinatorContextTests {
|
||||
#expect(context.cachedEncryptionStatuses[peerID] == nil)
|
||||
}
|
||||
|
||||
@Test @MainActor
|
||||
func getEncryptionStatus_reflectsLiveSessionNotHistory() async {
|
||||
let context = MockChatPeerIdentityContext()
|
||||
let coordinator = ChatPeerIdentityCoordinator(context: context)
|
||||
let peerID = PeerID(str: "8877665544332211")
|
||||
|
||||
// A remembered (even verified) fingerprint must not conjure a lock on
|
||||
// its own: the DM header and "end-to-end encrypted" caption follow
|
||||
// this status, and claiming secured with no live session is the false
|
||||
// security signal this mapping used to produce.
|
||||
context.fingerprintsByPeerID[peerID] = "fp"
|
||||
context.verifiedFingerprintSet = ["fp"]
|
||||
|
||||
#expect(coordinator.getEncryptionStatus(for: peerID) == .noHandshake)
|
||||
|
||||
coordinator.invalidateEncryptionCache(for: peerID)
|
||||
context.noiseSessionStates[peerID] = .handshaking
|
||||
#expect(coordinator.getEncryptionStatus(for: peerID) == .noiseHandshaking)
|
||||
|
||||
// A FAILED handshake is a red lock.slash, not yesterday's seal.
|
||||
struct HandshakeError: Error {}
|
||||
coordinator.invalidateEncryptionCache(for: peerID)
|
||||
context.noiseSessionStates[peerID] = .failed(HandshakeError())
|
||||
#expect(coordinator.getEncryptionStatus(for: peerID) == .none)
|
||||
|
||||
// Only a live established session upgrades to the verified seal.
|
||||
coordinator.invalidateEncryptionCache(for: peerID)
|
||||
context.noiseSessionStates[peerID] = .established
|
||||
#expect(coordinator.getEncryptionStatus(for: peerID) == .noiseVerified)
|
||||
}
|
||||
|
||||
@Test @MainActor
|
||||
func resolveNickname_walksMeshIdentityAndAnonFallbacks() async {
|
||||
let context = MockChatPeerIdentityContext()
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user