fix: harden community joins, approvals and state backups

This commit is contained in:
SkateSpots 2026-10-29 16:29:26 +07:00
parent dcb1a3e674
commit c75ddea8e2
35 changed files with 250 additions and 52 deletions

View file

@ -1535,7 +1535,9 @@ session with **no other memory of this project**. So:
- **Join UX (2026-10-29):** community-specific approval copy names the bot or - **Join UX (2026-10-29):** community-specific approval copy names the bot or
admins, an admin pending-request banner links to the room's request panel, admins, an admin pending-request banner links to the room's request panel,
and waiting users get **Request again** plus the second-device hint (use and waiting users get **Request again** plus the second-device hint (use
**Link device**; the bot cannot add a second MLS key for the same account). **Link device** for a direct handoff, or ask to join again with the new
device; the earlier “already in the group” error was a stale request for the
same key package, not a same-account limitation).
If the bot is missing from the group, the panel warns that an admin must If the bot is missing from the group, the panel warns that an admin must
review requests. review requests.
- **Bot:** `scripts/community-bot.ts`, built to CJS with - **Bot:** `scripts/community-bot.ts`, built to CJS with

View file

@ -23,10 +23,10 @@
Cache-Control = "public, max-age=31536000, immutable" Cache-Control = "public, max-age=31536000, immutable"
# Human-friendly short links. # Human-friendly short links.
# /community and /c -> the app's Community page (HashRouter route). # /community, /c and /cordn -> the app's Community page (HashRouter route).
# /cordn is deliberately the raw Cordn room reference for Staircase/Cordn # The raw room reference is still available via "Copy Cordn link" in Advanced
# clients; normal users should use /c, which stays inside the app's own native # for Staircase/Cordn clients, but no browser short link goes to the currently
# MLS flow (Cordn web currently stalls after NIP-42). # stalled Cordn web client.
# Keep these BEFORE the SPA fallback. # Keep these BEFORE the SPA fallback.
[[redirects]] [[redirects]]
from = "/community" from = "/community"
@ -42,7 +42,7 @@
[[redirects]] [[redirects]]
from = "/cordn" from = "/cordn"
to = "https://cordn.net/chat/cordn1qgdhwumn8ghj7un9d3shjtnnddshgefdwdcx7arn9ehhyecpyqpgz6s9k82wwznal8r39mm0wfe8wsnf6eda3ghjvt8qxvxs3ptfxqq5wd4kzar9wdcx7arn8f3k7mtdw4hxjare8hqt74" to = "/#/community"
status = 302 status = 302
force = true force = true

View file

@ -0,0 +1,42 @@
#!/usr/bin/env bash
# Pull the SkateSpots Community Cordn state off the relay host, keep 30 daily
# snapshots locally, refresh the 0600 working copy, and optionally push the
# snapshot directory to restic when the normal relay backup is configured.
#
# scripts/backup-community-state.sh
# SKATESPOTS_COMMUNITY_HOST=skatespots-cvm scripts/backup-community-state.sh
set -euo pipefail
HOST="${SKATESPOTS_COMMUNITY_HOST:-skatespots-cvm}"
REMOTE_STATE="${SKATESPOTS_COMMUNITY_STATE:-/var/lib/skatespots-community/state.json}"
DEST="${SKATESPOTS_COMMUNITY_BACKUP_DIR:-$HOME/.backups/community}"
CONFIG_DIR="$HOME/.config/skatespots"
mkdir -p "$DEST" "$CONFIG_DIR"
chmod 700 "$DEST" "$CONFIG_DIR"
tmp="$(mktemp)"
trap 'rm -f "$tmp"' EXIT
scp -q "$HOST:$REMOTE_STATE" "$tmp"
# Refuse to rotate a valid backup out for an empty/corrupt download.
node -e 'const fs=require("fs");const s=JSON.parse(fs.readFileSync(process.argv[1],"utf8"));if(!s||typeof s!=="object")process.exit(1)' "$tmp"
stamp="$(date +%F)"
install -m 600 "$tmp" "$DEST/state-$stamp.json"
install -m 600 "$tmp" "$CONFIG_DIR/community-state.json"
echo "saved $DEST/state-$stamp.json"
# Keep 30 daily snapshots.
ls -1t "$DEST"/state-*.json 2>/dev/null | tail -n +31 | xargs -r rm -f
if [[ -f "$CONFIG_DIR/restic.env" ]] && command -v restic >/dev/null 2>&1; then
set -a
# shellcheck disable=SC1091
. "$CONFIG_DIR/restic.env"
set +a
restic backup "$DEST" --tag community-state --host "$(hostname)"
restic forget --tag community-state --keep-daily 30 --prune
else
echo "restic not configured; local snapshots only"
fi

View file

@ -23,6 +23,7 @@
* COMMUNITY_AUTO_APPROVE=0 leave requests for admins instead of adding * COMMUNITY_AUTO_APPROVE=0 leave requests for admins instead of adding
* COMMUNITY_MAX_MEMBERS stop auto-approving at this group size (default 250) * COMMUNITY_MAX_MEMBERS stop auto-approving at this group size (default 250)
* COMMUNITY_MAX_DEVICES_PER_PUBKEY per-account device cap (default 5) * COMMUNITY_MAX_DEVICES_PER_PUBKEY per-account device cap (default 5)
* COMMUNITY_MAX_APPROVALS_PER_HOUR flood brake for auto-approval (default 30)
* COMMUNITY_ONCE=1 one cycle then exit (for a dry run) * COMMUNITY_ONCE=1 one cycle then exit (for a dry run)
*/ */
import { mkdir, readFile, rename, writeFile } from 'node:fs/promises'; import { mkdir, readFile, rename, writeFile } from 'node:fs/promises';
@ -52,6 +53,10 @@ const parsedMaxDevices = Number(process.env.COMMUNITY_MAX_DEVICES_PER_PUBKEY ??
const MAX_DEVICES_PER_PUBKEY = Number.isFinite(parsedMaxDevices) && parsedMaxDevices >= 1 const MAX_DEVICES_PER_PUBKEY = Number.isFinite(parsedMaxDevices) && parsedMaxDevices >= 1
? Math.floor(parsedMaxDevices) ? Math.floor(parsedMaxDevices)
: 5; : 5;
const parsedMaxPerHour = Number(process.env.COMMUNITY_MAX_APPROVALS_PER_HOUR ?? 30);
const MAX_APPROVALS_PER_HOUR = Number.isFinite(parsedMaxPerHour) && parsedMaxPerHour >= 1
? Math.floor(parsedMaxPerHour)
: 30;
const CREATE = process.env.COMMUNITY_CREATE === '1'; const CREATE = process.env.COMMUNITY_CREATE === '1';
const ONCE = process.env.COMMUNITY_ONCE === '1'; const ONCE = process.env.COMMUNITY_ONCE === '1';
@ -139,6 +144,8 @@ let lastJoinRequestAt = 0;
let lastError = ''; let lastError = '';
let lastPending = 0; let lastPending = 0;
let capLogged = false; let capLogged = false;
let hourCapLogged = false;
const approvalTimes: number[] = [];
const deviceCapLogged = new Set<string>(); const deviceCapLogged = new Set<string>();
async function cycle(): Promise<void> { async function cycle(): Promise<void> {
@ -190,6 +197,20 @@ async function cycle(): Promise<void> {
log('member cap cleared; auto-approval resumed'); log('member cap cleared; auto-approval resumed');
} }
const hourAgo = Date.now() - 60 * 60_000;
while (approvalTimes.length > 0 && (approvalTimes[0] ?? 0) < hourAgo) approvalTimes.shift();
if (approvalTimes.length >= MAX_APPROVALS_PER_HOUR) {
if (!hourCapLogged) {
hourCapLogged = true;
log('hourly approval cap reached', approvalTimes.length, '/', MAX_APPROVALS_PER_HOUR, '— leaving requests for admins');
}
return;
}
if (hourCapLogged) {
hourCapLogged = false;
log('hourly approval cap cleared; auto-approval resumed');
}
const handled: Array<{ pk: string; at: number }> = []; const handled: Array<{ pk: string; at: number }> = [];
for (const request of requests.slice(0, 10)) { for (const request of requests.slice(0, 10)) {
if (!/^[0-9a-f]{64}$/i.test(request.pk)) { if (!/^[0-9a-f]{64}$/i.test(request.pk)) {
@ -230,6 +251,7 @@ async function cycle(): Promise<void> {
} }
} }
approved.set(approvalKey, Date.now()); approved.set(approvalKey, Date.now());
approvalTimes.push(Date.now());
handled.push({ pk: request.pk, at: request.at }); handled.push({ pk: request.pk, at: request.at });
log('approved member', short(request.pk)); log('approved member', short(request.pk));
await sleep(1500); // be gentle with the coordinator await sleep(1500); // be gentle with the coordinator
@ -298,6 +320,7 @@ async function main(): Promise<void> {
'auto-approve', AUTO_APPROVE ? 'on' : 'off', 'auto-approve', AUTO_APPROVE ? 'on' : 'off',
'max', MAX_MEMBERS, 'max', MAX_MEMBERS,
'devices/account', MAX_DEVICES_PER_PUBKEY, 'devices/account', MAX_DEVICES_PER_PUBKEY,
'approvals/hour', MAX_APPROVALS_PER_HOUR,
); );
do { do {

View file

@ -0,0 +1,11 @@
#!/usr/bin/env bash
# Install the daily community-state backup timer for the current user.
set -euo pipefail
cd "$(dirname "$0")/.."
UNIT_DIR="$HOME/.config/systemd/user"
mkdir -p "$UNIT_DIR"
cp scripts/systemd/skatespots-community-backup.service "$UNIT_DIR/"
cp scripts/systemd/skatespots-community-backup.timer "$UNIT_DIR/"
systemctl --user daemon-reload
systemctl --user enable --now skatespots-community-backup.timer
systemctl --user list-timers skatespots-community-backup.timer --no-pager

View file

@ -226,6 +226,12 @@ if command -v ssh >/dev/null 2>&1 && grep -qsE "^Host[[:space:]]+${COMMUNITY_HOS
problems+=("community Cordn bot is ${community_state:-unreachable}") problems+=("community Cordn bot is ${community_state:-unreachable}")
signature+="community-bot;" signature+="community-bot;"
fi fi
community_backup="$(systemctl --user is-active skatespots-community-backup.timer 2>/dev/null || echo unknown)"
if [ "$community_backup" = "active" ]; then
p " ${G}✓${N} community state backup timer active"
else
p " ${Y}⚠${N} community state backup timer: ${community_backup}"
fi
else else
p " ${DIM}ssh alias ${COMMUNITY_HOST} not configured — skipped${N}" p " ${DIM}ssh alias ${COMMUNITY_HOST} not configured — skipped${N}"
fi fi

View file

@ -0,0 +1,10 @@
[Unit]
Description=Back up the SkateSpots Community Cordn state
After=network-online.target
Wants=network-online.target
[Service]
Type=oneshot
ExecStart=/home/sovereignsk8/SkateSpots/scripts/backup-community-state.sh
Nice=10
IOSchedulingClass=idle

View file

@ -0,0 +1,10 @@
[Unit]
Description=Daily SkateSpots Community Cordn state backup
[Timer]
OnCalendar=*-*-* 04:17:00
Persistent=true
RandomizedDelaySec=15m
[Install]
WantedBy=timers.target

View file

@ -7,7 +7,6 @@ import {
CheckCheck, CheckCheck,
ChevronDown, ChevronDown,
Copy, Copy,
ExternalLink,
ImagePlus, ImagePlus,
KeyRound, KeyRound,
Loader2, Loader2,
@ -31,6 +30,17 @@ import { Button } from '@/components/ui/button';
import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card'; import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card';
import { Input } from '@/components/ui/input'; import { Input } from '@/components/ui/input';
import { Badge } from '@/components/ui/badge'; import { Badge } from '@/components/ui/badge';
import {
AlertDialog,
AlertDialogAction,
AlertDialogCancel,
AlertDialogContent,
AlertDialogDescription,
AlertDialogFooter,
AlertDialogHeader,
AlertDialogTitle,
AlertDialogTrigger,
} from '@/components/ui/alert-dialog';
import { Avatar, AvatarFallback, AvatarImage } from '@/components/ui/avatar'; import { Avatar, AvatarFallback, AvatarImage } from '@/components/ui/avatar';
import { import {
Dialog, Dialog,
@ -1317,16 +1327,7 @@ function AdvancedPanel({
</Button> </Button>
</div> </div>
<div className="flex flex-wrap items-center gap-2"> <div className="flex flex-wrap items-center gap-2">
<a <span className="w-full text-[10px] text-gray-600">
href={cordnUrl ?? '#'}
target="_blank"
rel="noopener noreferrer"
className="inline-flex items-center gap-1 text-[11px] font-medium text-teal-300 transition-colors hover:text-teal-200"
>
<ExternalLink className="h-3 w-3" />{' '}
{t('socialUi.crewCoordinator.openCordnGroup')}
</a>
<span className="text-[10px] text-gray-600">
{t('cordnUi.cordnWebCaveat')} {t('cordnUi.cordnWebCaveat')}
</span> </span>
<Button <Button
@ -1340,7 +1341,7 @@ function AdvancedPanel({
) : ( ) : (
<Copy className="h-3 w-3" /> <Copy className="h-3 w-3" />
)} )}
{copiedCordn ? t('cordnUi.copied') : t('cordnUi.copyRef')} {copiedCordn ? t('cordnUi.copied') : t('cordnUi.copyCordnLink')}
</Button> </Button>
</div> </div>
</> </>
@ -1486,14 +1487,29 @@ function RequestRow({
return ( return (
<div className="flex items-center justify-between gap-2"> <div className="flex items-center justify-between gap-2">
<PersonLabel pubkey={pubkey} botPubkey={botPubkey} botName={botName} /> <PersonLabel pubkey={pubkey} botPubkey={botPubkey} botName={botName} />
<AlertDialog>
<AlertDialogTrigger asChild>
<Button <Button
size="sm" size="sm"
disabled={disabled} disabled={disabled}
onClick={onApprove}
className="h-7 shrink-0 gap-1 text-xs" className="h-7 shrink-0 gap-1 text-xs"
> >
<UserPlus className="h-3 w-3" /> {t('cordnUi.approve')} <UserPlus className="h-3 w-3" /> {t('cordnUi.approve')}
</Button> </Button>
</AlertDialogTrigger>
<AlertDialogContent>
<AlertDialogHeader>
<AlertDialogTitle>{t('cordnUi.approveTitle')}</AlertDialogTitle>
<AlertDialogDescription>
{t('cordnUi.approveDesc', { name: shortNpub(pubkey) })}
</AlertDialogDescription>
</AlertDialogHeader>
<AlertDialogFooter>
<AlertDialogCancel>{t('common.cancel')}</AlertDialogCancel>
<AlertDialogAction onClick={onApprove}>{t('cordnUi.approve')}</AlertDialogAction>
</AlertDialogFooter>
</AlertDialogContent>
</AlertDialog>
</div> </div>
); );
} }

View file

@ -920,6 +920,7 @@ const batch1 = {
"requestJoin": "Demana d'unir-t'hi", "requestJoin": "Demana d'unir-t'hi",
"requestAgain": "Request again", "requestAgain": "Request again",
"requestHandled": "Request handled", "requestHandled": "Request handled",
"copyCordnLink": "Copy Cordn link",
"cordnWebCaveat": "Cordn web may stall after sign-in; the in-app join is more reliable.", "cordnWebCaveat": "Cordn web may stall after sign-in; the in-app join is more reliable.",
"checkInvite": "Comprova si hi ha invitació", "checkInvite": "Comprova si hi ha invitació",
"requested": "Sol·licitud enviada: un administrador de la colla l'aprovarà.", "requested": "Sol·licitud enviada: un administrador de la colla l'aprovarà.",
@ -930,6 +931,8 @@ const batch1 = {
"requests": "Sol·licituds d'accés", "requests": "Sol·licituds d'accés",
"checkRequests": "Comprova les sol·licituds", "checkRequests": "Comprova les sol·licituds",
"approve": "Afegeix", "approve": "Afegeix",
"approveTitle": "Aprovar aquest dispositiu?",
"approveDesc": "Afegir {{name}} a aquesta sala xifrada? Aquest dispositiu podrà llegir els missatges nous.",
"noRequests": "No hi ha sol·licituds pendents.", "noRequests": "No hi ha sol·licituds pendents.",
"inviteHint": "De moment només hi ets tu en aquest xat: els membres de la colla poden tocar \"Demana d'unir-t'hi\" i després els aproves aquí.", "inviteHint": "De moment només hi ets tu en aquest xat: els membres de la colla poden tocar \"Demana d'unir-t'hi\" i després els aproves aquí.",
"shareInvite": "Convida", "shareInvite": "Convida",
@ -1021,7 +1024,7 @@ const batch1 = {
"pendingRequests": "{{count}} sol·licituds d'entrada en espera", "pendingRequests": "{{count}} sol·licituds d'entrada en espera",
"pendingRequestsDesc": "El bot normalment aprova automàticament. Si s'ha encallat, obre la sala de sota i toca Comprova les sol·licituds.", "pendingRequestsDesc": "El bot normalment aprova automàticament. Si s'ha encallat, obre la sala de sota i toca Comprova les sol·licituds.",
"botMissing": "The community bot isn’t in this room right now, so an admin will review join requests.", "botMissing": "The community bot isn’t in this room right now, so an admin will review join requests.",
"secondDeviceHint": "Already a member on another device? Use “Link device” instead — the bot can’t add a second key to the same account.", "secondDeviceHint": "Ja ets membre en un altre dispositiu? Fes servir «Enllaça aquest dispositiu» per a un traspàs directe, o torna a demanar d’entrar amb aquest dispositiu.",
"create": "Crea la sala de la comunitat", "create": "Crea la sala de la comunitat",
"setupDeviceBody": "Això dóna a aquest dispositiu una clau per a la sala de la comunitat. Un administrador o el bot l'aprova una vegada.", "setupDeviceBody": "Això dóna a aquest dispositiu una clau per a la sala de la comunitat. Un administrador o el bot l'aprova una vegada.",
"onboardingFounder1": "Crea la sala de la comunitat.", "onboardingFounder1": "Crea la sala de la comunitat.",

View file

@ -309,6 +309,7 @@ const drift = {
"requestJoin": "Požádat o připojení", "requestJoin": "Požádat o připojení",
"requestAgain": "Request again", "requestAgain": "Request again",
"requestHandled": "Request handled", "requestHandled": "Request handled",
"copyCordnLink": "Copy Cordn link",
"cordnWebCaveat": "Cordn web may stall after sign-in; the in-app join is more reliable.", "cordnWebCaveat": "Cordn web may stall after sign-in; the in-app join is more reliable.",
"checkInvite": "Zkontrolovat pozvánku", "checkInvite": "Zkontrolovat pozvánku",
"requested": "Žádost odeslána — administrátor crew ji schválí.", "requested": "Žádost odeslána — administrátor crew ji schválí.",
@ -319,6 +320,8 @@ const drift = {
"requests": "Žádosti o připojení", "requests": "Žádosti o připojení",
"checkRequests": "Zkontrolovat žádosti", "checkRequests": "Zkontrolovat žádosti",
"approve": "Přidat", "approve": "Přidat",
"approveTitle": "Schválit toto zařízení?",
"approveDesc": "Přidat {{name}} do této šifrované místnosti? Toto zařízení bude moci číst nové zprávy.",
"noRequests": "Žádné čekající žádosti o připojení.", "noRequests": "Žádné čekající žádosti o připojení.",
"inviteHint": "Zatím jsi v tomto chatu jen ty — členové crew mohou klepnout na „Požádat o připojení“ a ty je zde schválíš.", "inviteHint": "Zatím jsi v tomto chatu jen ty — členové crew mohou klepnout na „Požádat o připojení“ a ty je zde schválíš.",
"memberCount": "{{count}} ve skupině", "memberCount": "{{count}} ve skupině",
@ -407,7 +410,7 @@ const drift = {
"pendingRequests": "{{count}} čekajících žádostí o připojení", "pendingRequests": "{{count}} čekajících žádostí o připojení",
"pendingRequestsDesc": "Bot obvykle schvaluje automaticky. Pokud se zasekl, otevři níže místnost a klepni na Zkontrolovat žádosti.", "pendingRequestsDesc": "Bot obvykle schvaluje automaticky. Pokud se zasekl, otevři níže místnost a klepni na Zkontrolovat žádosti.",
"botMissing": "The community bot isn’t in this room right now, so an admin will review join requests.", "botMissing": "The community bot isn’t in this room right now, so an admin will review join requests.",
"secondDeviceHint": "Already a member on another device? Use “Link device” instead — the bot can’t add a second key to the same account.", "secondDeviceHint": "Už jsi členem na jiném zařízení? Použij „Propojit toto zařízení“ pro přímé předání, nebo požádej o připojení znovu s tímto zařízením.",
"create": "Vytvořit komunitní místnost", "create": "Vytvořit komunitní místnost",
"setupDeviceBody": "Toto zařízení tím získá klíč do komunitní místnosti. Schválí ho jednou administrátor nebo bot.", "setupDeviceBody": "Toto zařízení tím získá klíč do komunitní místnosti. Schválí ho jednou administrátor nebo bot.",
"onboardingFounder1": "Vytvoř komunitní místnost.", "onboardingFounder1": "Vytvoř komunitní místnost.",

View file

@ -311,6 +311,7 @@ const drift = {
requestJoin: "Um Beitritt bitten", requestJoin: "Um Beitritt bitten",
requestAgain: "Request again", requestAgain: "Request again",
requestHandled: "Request handled", requestHandled: "Request handled",
copyCordnLink: "Copy Cordn link",
cordnWebCaveat: "Cordn web may stall after sign-in; the in-app join is more reliable.", cordnWebCaveat: "Cordn web may stall after sign-in; the in-app join is more reliable.",
checkInvite: "Nach Einladung suchen", checkInvite: "Nach Einladung suchen",
requested: "Anfrage gesendet – ein Crew-Admin wird sie genehmigen.", requested: "Anfrage gesendet – ein Crew-Admin wird sie genehmigen.",
@ -321,6 +322,8 @@ const drift = {
requests: "Beitrittsanfragen", requests: "Beitrittsanfragen",
checkRequests: "Anfragen prüfen", checkRequests: "Anfragen prüfen",
approve: "Hinzufügen", approve: "Hinzufügen",
approveTitle: "Dieses Gerät bestätigen?",
approveDesc: "{{name}} zu diesem verschlüsselten Raum hinzufügen? Dieses Gerät kann dann neue Nachrichten lesen.",
noRequests: "Keine offenen Beitrittsanfragen.", noRequests: "Keine offenen Beitrittsanfragen.",
inviteHint: "Bisher bist nur du in diesem Chat – Crew-Mitglieder können auf „Um Beitritt bitten“ tippen, dann genehmigst du sie hier.", inviteHint: "Bisher bist nur du in diesem Chat – Crew-Mitglieder können auf „Um Beitritt bitten“ tippen, dann genehmigst du sie hier.",
memberCount: "{{count}} in der Gruppe", memberCount: "{{count}} in der Gruppe",
@ -409,7 +412,7 @@ const drift = {
pendingRequests: "{{count}} offene Beitrittsanfragen", pendingRequests: "{{count}} offene Beitrittsanfragen",
pendingRequestsDesc: "Der Bot genehmigt normalerweise automatisch. Wenn es hängt, öffne unten den Raum und tippe auf Anfragen prüfen.", pendingRequestsDesc: "Der Bot genehmigt normalerweise automatisch. Wenn es hängt, öffne unten den Raum und tippe auf Anfragen prüfen.",
botMissing: "The community bot isn’t in this room right now, so an admin will review join requests.", botMissing: "The community bot isn’t in this room right now, so an admin will review join requests.",
secondDeviceHint: "Already a member on another device? Use “Link device” instead — the bot can’t add a second key to the same account.", secondDeviceHint: "Schon Mitglied auf einem anderen Gerät? Nutze „Dieses Gerät verknüpfen“ für eine direkte Übergabe oder bitte mit diesem Gerät erneut um Beitritt.",
create: "Community-Raum erstellen", create: "Community-Raum erstellen",
setupDeviceBody: "Damit erhält dieses Gerät einen Schlüssel für den Community-Raum. Ein Admin oder der Bot bestätigt ihn einmal.", setupDeviceBody: "Damit erhält dieses Gerät einen Schlüssel für den Community-Raum. Ein Admin oder der Bot bestätigt ihn einmal.",
onboardingFounder1: "Erstelle den Community-Raum.", onboardingFounder1: "Erstelle den Community-Raum.",

View file

@ -313,6 +313,7 @@ const drift = {
requestJoin: "Ζήτα συμμετοχή", requestJoin: "Ζήτα συμμετοχή",
requestAgain: "Request again", requestAgain: "Request again",
requestHandled: "Request handled", requestHandled: "Request handled",
copyCordnLink: "Copy Cordn link",
cordnWebCaveat: "Cordn web may stall after sign-in; the in-app join is more reliable.", cordnWebCaveat: "Cordn web may stall after sign-in; the in-app join is more reliable.",
checkInvite: "Έλεγχος πρόσκλησης", checkInvite: "Έλεγχος πρόσκλησης",
requested: "Το αίτημα στάλθηκε — ένας διαχειριστής του crew θα το εγκρίνει.", requested: "Το αίτημα στάλθηκε — ένας διαχειριστής του crew θα το εγκρίνει.",
@ -323,6 +324,8 @@ const drift = {
requests: "Αιτήματα συμμετοχής", requests: "Αιτήματα συμμετοχής",
checkRequests: "Έλεγχος αιτημάτων", checkRequests: "Έλεγχος αιτημάτων",
approve: "Προσθήκη", approve: "Προσθήκη",
approveTitle: "Έγκριση αυτής της συσκευής;",
approveDesc: "Προσθήκη του {{name}} σε αυτή την κρυπτογραφημένη αίθουσα; Αυτή η συσκευή θα μπορεί να διαβάζει νέα μηνύματα.",
noRequests: "Δεν υπάρχουν εκκρεμή αιτήματα συμμετοχής.", noRequests: "Δεν υπάρχουν εκκρεμή αιτήματα συμμετοχής.",
inviteHint: "Μόνο εσύ είσαι σε αυτή τη συνομιλία μέχρι στιγμής — τα μέλη του crew μπορούν να πατήσουν «Ζήτα συμμετοχή» και μετά να τα εγκρίνεις εδώ.", inviteHint: "Μόνο εσύ είσαι σε αυτή τη συνομιλία μέχρι στιγμής — τα μέλη του crew μπορούν να πατήσουν «Ζήτα συμμετοχή» και μετά να τα εγκρίνεις εδώ.",
memberCount: "{{count}} στην ομάδα", memberCount: "{{count}} στην ομάδα",
@ -411,7 +414,7 @@ const drift = {
pendingRequests: "{{count}} αιτήματα συμμετοχής σε αναμονή", pendingRequests: "{{count}} αιτήματα συμμετοχής σε αναμονή",
pendingRequestsDesc: "Το bot εγκρίνει συνήθως αυτόματα. Αν έχει κολλήσει, άνοιξε το δωμάτιο παρακάτω και πάτησε Έλεγχος αιτημάτων.", pendingRequestsDesc: "Το bot εγκρίνει συνήθως αυτόματα. Αν έχει κολλήσει, άνοιξε το δωμάτιο παρακάτω και πάτησε Έλεγχος αιτημάτων.",
botMissing: "The community bot isn’t in this room right now, so an admin will review join requests.", botMissing: "The community bot isn’t in this room right now, so an admin will review join requests.",
secondDeviceHint: "Already a member on another device? Use “Link device” instead — the bot can’t add a second key to the same account.", secondDeviceHint: "Είσαι ήδη μέλος σε άλλη συσκευή; Χρησιμοποίησε «Σύνδεση αυτής της συσκευής» για άμεση μεταβίβαση ή ζήτησε ξανά να συμμετάσχεις με αυτή τη συσκευή.",
create: "Δημιουργία δωματίου κοινότητας", create: "Δημιουργία δωματίου κοινότητας",
setupDeviceBody: "Αυτό δίνει σε αυτή τη συσκευή ένα κλειδί για το δωμάτιο της κοινότητας. Ένας διαχειριστής ή το bot το εγκρίνει μία φορά.", setupDeviceBody: "Αυτό δίνει σε αυτή τη συσκευή ένα κλειδί για το δωμάτιο της κοινότητας. Ένας διαχειριστής ή το bot το εγκρίνει μία φορά.",
onboardingFounder1: "Δημιούργησε το δωμάτιο της κοινότητας.", onboardingFounder1: "Δημιούργησε το δωμάτιο της κοινότητας.",

View file

@ -920,6 +920,7 @@ const batch1 = {
requestJoin: "Eskatu sartzea", requestJoin: "Eskatu sartzea",
requestAgain: "Request again", requestAgain: "Request again",
requestHandled: "Request handled", requestHandled: "Request handled",
copyCordnLink: "Copy Cordn link",
cordnWebCaveat: "Cordn web may stall after sign-in; the in-app join is more reliable.", cordnWebCaveat: "Cordn web may stall after sign-in; the in-app join is more reliable.",
checkInvite: "Egiaztatu gonbidapena", checkInvite: "Egiaztatu gonbidapena",
requested: "Eskaera bidali da — taldeko administratzaile batek onartuko du.", requested: "Eskaera bidali da — taldeko administratzaile batek onartuko du.",
@ -930,6 +931,8 @@ const batch1 = {
requests: "Sartzeko eskaerak", requests: "Sartzeko eskaerak",
checkRequests: "Egiaztatu eskaerak", checkRequests: "Egiaztatu eskaerak",
approve: "Gehitu", approve: "Gehitu",
approveTitle: "Gailu hau onartu?",
approveDesc: "{{name}} gela enkriptatu honetara gehitu? Gailu horrek mezu berriak irakurri ahal izango ditu.",
noRequests: "Ez dago sartzeko eskaera zain.", noRequests: "Ez dago sartzeko eskaera zain.",
inviteHint: "Oraindik zu soilik zaude txat honetan — taldeko kideek \"Eskatu sartzea\" sakatu dezakete, eta gero zuk hemen onartuko dituzu.", inviteHint: "Oraindik zu soilik zaude txat honetan — taldeko kideek \"Eskatu sartzea\" sakatu dezakete, eta gero zuk hemen onartuko dituzu.",
shareInvite: "Gonbidatu", shareInvite: "Gonbidatu",
@ -1021,7 +1024,7 @@ const batch1 = {
pendingRequests: "{{count}} sartzeko eskaera zain", pendingRequests: "{{count}} sartzeko eskaera zain",
pendingRequestsDesc: "Botak normalean automatikoki onartzen du. Blokeatuta badago, ireki beheko gela eta sakatu Egiaztatu eskaerak.", pendingRequestsDesc: "Botak normalean automatikoki onartzen du. Blokeatuta badago, ireki beheko gela eta sakatu Egiaztatu eskaerak.",
botMissing: "The community bot isn’t in this room right now, so an admin will review join requests.", botMissing: "The community bot isn’t in this room right now, so an admin will review join requests.",
secondDeviceHint: "Already a member on another device? Use “Link device” instead — the bot can’t add a second key to the same account.", secondDeviceHint: "Beste gailu batean kide zara jada? Erabili «Estekatu gailu hau» zuzeneko transferentzia egiteko, edo eskatu berriro gailu honekin bat egiteko.",
create: "Sortu komunitate gela", create: "Sortu komunitate gela",
setupDeviceBody: "Honek gailu honi komunitate gelarako gako bat ematen dio. Administratzaile batek edo botak behin onartzen du.", setupDeviceBody: "Honek gailu honi komunitate gelarako gako bat ematen dio. Administratzaile batek edo botak behin onartzen du.",
onboardingFounder1: "Sortu komunitate gela.", onboardingFounder1: "Sortu komunitate gela.",

View file

@ -311,6 +311,7 @@ const drift = {
requestJoin: "Demander à rejoindre", requestJoin: "Demander à rejoindre",
requestAgain: "Request again", requestAgain: "Request again",
requestHandled: "Request handled", requestHandled: "Request handled",
copyCordnLink: "Copy Cordn link",
cordnWebCaveat: "Cordn web may stall after sign-in; the in-app join is more reliable.", cordnWebCaveat: "Cordn web may stall after sign-in; the in-app join is more reliable.",
checkInvite: "Vérifier l'invitation", checkInvite: "Vérifier l'invitation",
requested: "Demande envoyée — un admin de la Crew l'approuvera.", requested: "Demande envoyée — un admin de la Crew l'approuvera.",
@ -321,6 +322,8 @@ const drift = {
requests: "Demandes d'adhésion", requests: "Demandes d'adhésion",
checkRequests: "Vérifier les demandes", checkRequests: "Vérifier les demandes",
approve: "Ajouter", approve: "Ajouter",
approveTitle: "Approuver cet appareil ?",
approveDesc: "Ajouter {{name}} à ce salon chiffré ? Cet appareil pourra lire les nouveaux messages.",
noRequests: "Aucune demande d'adhésion en attente.", noRequests: "Aucune demande d'adhésion en attente.",
inviteHint: "Vous êtes seul dans ce chat pour l'instant — les membres de la Crew peuvent appuyer sur « Demander à rejoindre », puis vous les approuvez ici.", inviteHint: "Vous êtes seul dans ce chat pour l'instant — les membres de la Crew peuvent appuyer sur « Demander à rejoindre », puis vous les approuvez ici.",
memberCount: "{{count}} dans le groupe", memberCount: "{{count}} dans le groupe",
@ -409,7 +412,7 @@ const drift = {
pendingRequests: "{{count}} demande(s) d’adhésion en attente", pendingRequests: "{{count}} demande(s) d’adhésion en attente",
pendingRequestsDesc: "Le bot approuve généralement automatiquement. S’il est bloqué, ouvre le salon ci-dessous et appuie sur Vérifier les demandes.", pendingRequestsDesc: "Le bot approuve généralement automatiquement. S’il est bloqué, ouvre le salon ci-dessous et appuie sur Vérifier les demandes.",
botMissing: "The community bot isn’t in this room right now, so an admin will review join requests.", botMissing: "The community bot isn’t in this room right now, so an admin will review join requests.",
secondDeviceHint: "Already a member on another device? Use “Link device” instead — the bot can’t add a second key to the same account.", secondDeviceHint: "Déjà membre sur un autre appareil ? Utilise « Lier cet appareil » pour un transfert direct, ou redemande à rejoindre avec cet appareil.",
create: "Créer le salon de la communauté", create: "Créer le salon de la communauté",
setupDeviceBody: "Cela donne à cet appareil une clé pour le salon de la communauté. Un admin ou le bot l’approuve une fois.", setupDeviceBody: "Cela donne à cet appareil une clé pour le salon de la communauté. Un admin ou le bot l’approuve une fois.",
onboardingFounder1: "Crée le salon de la communauté.", onboardingFounder1: "Crée le salon de la communauté.",

View file

@ -920,6 +920,7 @@ const batch1 = {
requestJoin: "Pedir acceso", requestJoin: "Pedir acceso",
requestAgain: "Request again", requestAgain: "Request again",
requestHandled: "Request handled", requestHandled: "Request handled",
copyCordnLink: "Copy Cordn link",
cordnWebCaveat: "Cordn web may stall after sign-in; the in-app join is more reliable.", cordnWebCaveat: "Cordn web may stall after sign-in; the in-app join is more reliable.",
checkInvite: "Comprobar se hai invitación", checkInvite: "Comprobar se hai invitación",
requested: "Solicitude enviada: un administrador da crew aprobaraa.", requested: "Solicitude enviada: un administrador da crew aprobaraa.",
@ -930,6 +931,8 @@ const batch1 = {
requests: "Solicitudes de acceso", requests: "Solicitudes de acceso",
checkRequests: "Comprobar solicitudes", checkRequests: "Comprobar solicitudes",
approve: "Engadir", approve: "Engadir",
approveTitle: "Aprobar este dispositivo?",
approveDesc: "Engadir {{name}} a esta sala cifrada? Ese dispositivo poderá ler as mensaxes novas.",
noRequests: "Non hai solicitudes de acceso pendentes.", noRequests: "Non hai solicitudes de acceso pendentes.",
inviteHint: "De momento só estás ti neste chat: os membros da crew poden tocar «Pedir acceso» e despois ti apruébalos aquí.", inviteHint: "De momento só estás ti neste chat: os membros da crew poden tocar «Pedir acceso» e despois ti apruébalos aquí.",
shareInvite: "Convidar", shareInvite: "Convidar",
@ -1021,7 +1024,7 @@ const batch1 = {
pendingRequests: "{{count}} solicitudes de entrada en espera", pendingRequests: "{{count}} solicitudes de entrada en espera",
pendingRequestsDesc: "O bot adoita aprobar automaticamente. Se se atascou, abre a sala de abaixo e toca Comprobar solicitudes.", pendingRequestsDesc: "O bot adoita aprobar automaticamente. Se se atascou, abre a sala de abaixo e toca Comprobar solicitudes.",
botMissing: "The community bot isn’t in this room right now, so an admin will review join requests.", botMissing: "The community bot isn’t in this room right now, so an admin will review join requests.",
secondDeviceHint: "Already a member on another device? Use “Link device” instead — the bot can’t add a second key to the same account.", secondDeviceHint: "Xa es membro noutro dispositivo? Usa «Vincular este dispositivo» para un traspaso directo, ou pide entrar de novo con este dispositivo.",
create: "Crear a sala da comunidade", create: "Crear a sala da comunidade",
setupDeviceBody: "Isto dálle a este dispositivo unha chave para a sala da comunidade. Un administrador ou o bot apruxaa unha vez.", setupDeviceBody: "Isto dálle a este dispositivo unha chave para a sala da comunidade. Un administrador ou o bot apruxaa unha vez.",
onboardingFounder1: "Crea a sala da comunidade.", onboardingFounder1: "Crea a sala da comunidade.",

View file

@ -920,6 +920,7 @@ const batch1 = {
"requestJoin": "शामिल होने का अनुरोध करें", "requestJoin": "शामिल होने का अनुरोध करें",
"requestAgain": "Request again", "requestAgain": "Request again",
"requestHandled": "Request handled", "requestHandled": "Request handled",
"copyCordnLink": "Copy Cordn link",
"cordnWebCaveat": "Cordn web may stall after sign-in; the in-app join is more reliable.", "cordnWebCaveat": "Cordn web may stall after sign-in; the in-app join is more reliable.",
"checkInvite": "निमंत्रण जाँचें", "checkInvite": "निमंत्रण जाँचें",
"requested": "अनुरोध भेजा गया — क्रू एडमिन इसे स्वीकृत करेगा।", "requested": "अनुरोध भेजा गया — क्रू एडमिन इसे स्वीकृत करेगा।",
@ -930,6 +931,8 @@ const batch1 = {
"requests": "शामिल होने के अनुरोध", "requests": "शामिल होने के अनुरोध",
"checkRequests": "अनुरोध जाँचें", "checkRequests": "अनुरोध जाँचें",
"approve": "जोड़ें", "approve": "जोड़ें",
"approveTitle": "इस डिवाइस को स्वीकृत करें?",
"approveDesc": "{{name}} को इस एन्क्रिप्टेड रूम में जोड़ें? वह डिवाइस नए संदेश पढ़ सकेगा।",
"noRequests": "कोई लंबित शामिल होने का अनुरोध नहीं।", "noRequests": "कोई लंबित शामिल होने का अनुरोध नहीं।",
"inviteHint": "अभी तक इस चैट में केवल आप हैं — क्रू सदस्य “शामिल होने का अनुरोध करें” टैप कर सकते हैं, फिर आप उन्हें यहाँ स्वीकृत करें।", "inviteHint": "अभी तक इस चैट में केवल आप हैं — क्रू सदस्य “शामिल होने का अनुरोध करें” टैप कर सकते हैं, फिर आप उन्हें यहाँ स्वीकृत करें।",
"shareInvite": "निमंत्रण दें", "shareInvite": "निमंत्रण दें",
@ -1021,7 +1024,7 @@ const batch1 = {
"pendingRequests": "{{count}} शामिल होने के अनुरोध इंतज़ार में", "pendingRequests": "{{count}} शामिल होने के अनुरोध इंतज़ार में",
"pendingRequestsDesc": "बॉट आमतौर पर अपने आप मंज़ूरी देता है। अगर अटका हुआ है, तो नीचे रूम खोलें और “अनुरोध जाँचें” टैप करें।", "pendingRequestsDesc": "बॉट आमतौर पर अपने आप मंज़ूरी देता है। अगर अटका हुआ है, तो नीचे रूम खोलें और “अनुरोध जाँचें” टैप करें।",
"botMissing": "The community bot isn’t in this room right now, so an admin will review join requests.", "botMissing": "The community bot isn’t in this room right now, so an admin will review join requests.",
"secondDeviceHint": "Already a member on another device? Use “Link device” instead — the bot can’t add a second key to the same account.", "secondDeviceHint": "क्या आप पहले से किसी दूसरे डिवाइस पर सदस्य हैं? सीधे हैंडऑफ़ के लिए “इस डिवाइस को लिंक करें” का उपयोग करें, या इस डिवाइस से दोबारा जुड़ने का अनुरोध करें।",
"create": "कम्युनिटी रूम बनाएँ", "create": "कम्युनिटी रूम बनाएँ",
"setupDeviceBody": "इससे इस डिवाइस को कम्युनिटी रूम की एक कुंजी मिलती है। कोई एडमिन या बॉट इसे एक बार मंज़ूर करता है।", "setupDeviceBody": "इससे इस डिवाइस को कम्युनिटी रूम की एक कुंजी मिलती है। कोई एडमिन या बॉट इसे एक बार मंज़ूर करता है।",
"onboardingFounder1": "कम्युनिटी रूम बनाएँ।", "onboardingFounder1": "कम्युनिटी रूम बनाएँ।",

View file

@ -305,6 +305,7 @@ const driftStrings = {
requestJoin: 'Minta gabung', requestJoin: 'Minta gabung',
requestAgain: "Request again", requestAgain: "Request again",
requestHandled: "Request handled", requestHandled: "Request handled",
copyCordnLink: "Copy Cordn link",
cordnWebCaveat: "Cordn web may stall after sign-in; the in-app join is more reliable.", cordnWebCaveat: "Cordn web may stall after sign-in; the in-app join is more reliable.",
checkInvite: 'Periksa undangan', checkInvite: 'Periksa undangan',
requested: 'Permintaan terkirim — admin crew akan menyetujuinya.', requested: 'Permintaan terkirim — admin crew akan menyetujuinya.',
@ -315,6 +316,8 @@ const driftStrings = {
requests: 'Permintaan gabung', requests: 'Permintaan gabung',
checkRequests: 'Periksa permintaan', checkRequests: 'Periksa permintaan',
approve: 'Tambah', approve: 'Tambah',
approveTitle: "Setujui perangkat ini?",
approveDesc: "Tambahkan {{name}} ke ruang terenkripsi ini? Perangkat itu akan dapat membaca pesan baru.",
noRequests: 'Tidak ada permintaan gabung yang tertunda.', noRequests: 'Tidak ada permintaan gabung yang tertunda.',
inviteHint: 'Baru kamu yang ada di chat ini — anggota crew bisa menekan “Minta gabung”, lalu kamu setujui di sini.', inviteHint: 'Baru kamu yang ada di chat ini — anggota crew bisa menekan “Minta gabung”, lalu kamu setujui di sini.',
memberCount: '{{count}} di grup', memberCount: '{{count}} di grup',
@ -403,7 +406,7 @@ const driftStrings = {
pendingRequests: "{{count}} permintaan bergabung menunggu", pendingRequests: "{{count}} permintaan bergabung menunggu",
pendingRequestsDesc: "Bot biasanya menyetujui secara otomatis. Jika macet, buka ruang di bawah dan ketuk Periksa permintaan.", pendingRequestsDesc: "Bot biasanya menyetujui secara otomatis. Jika macet, buka ruang di bawah dan ketuk Periksa permintaan.",
botMissing: "The community bot isn’t in this room right now, so an admin will review join requests.", botMissing: "The community bot isn’t in this room right now, so an admin will review join requests.",
secondDeviceHint: "Already a member on another device? Use “Link device” instead — the bot can’t add a second key to the same account.", secondDeviceHint: "Sudah menjadi anggota di perangkat lain? Gunakan “Tautkan perangkat ini” untuk serah terima langsung, atau minta bergabung lagi dengan perangkat ini.",
create: "Buat ruang komunitas", create: "Buat ruang komunitas",
setupDeviceBody: "Ini memberi perangkat ini kunci untuk ruang komunitas. Admin atau bot menyetujuinya satu kali.", setupDeviceBody: "Ini memberi perangkat ini kunci untuk ruang komunitas. Admin atau bot menyetujuinya satu kali.",
onboardingFounder1: "Buat ruang komunitas.", onboardingFounder1: "Buat ruang komunitas.",

View file

@ -311,6 +311,7 @@ const drift = {
requestJoin: "Chiedi di unirti", requestJoin: "Chiedi di unirti",
requestAgain: "Request again", requestAgain: "Request again",
requestHandled: "Request handled", requestHandled: "Request handled",
copyCordnLink: "Copy Cordn link",
cordnWebCaveat: "Cordn web may stall after sign-in; the in-app join is more reliable.", cordnWebCaveat: "Cordn web may stall after sign-in; the in-app join is more reliable.",
checkInvite: "Controlla l'invito", checkInvite: "Controlla l'invito",
requested: "Richiesta inviata — un admin della crew la approverà.", requested: "Richiesta inviata — un admin della crew la approverà.",
@ -321,6 +322,8 @@ const drift = {
requests: "Richieste di adesione", requests: "Richieste di adesione",
checkRequests: "Controlla le richieste", checkRequests: "Controlla le richieste",
approve: "Aggiungi", approve: "Aggiungi",
approveTitle: "Approva questo dispositivo?",
approveDesc: "Aggiungere {{name}} a questa stanza cifrata? Quel dispositivo potrà leggere i nuovi messaggi.",
noRequests: "Nessuna richiesta di adesione in sospeso.", noRequests: "Nessuna richiesta di adesione in sospeso.",
inviteHint: "Per ora sei solo tu in questa chat — i membri della crew possono toccare “Chiedi di unirti”, poi li approvi qui.", inviteHint: "Per ora sei solo tu in questa chat — i membri della crew possono toccare “Chiedi di unirti”, poi li approvi qui.",
memberCount: "{{count}} nel gruppo", memberCount: "{{count}} nel gruppo",
@ -409,7 +412,7 @@ const drift = {
pendingRequests: "{{count}} richieste di adesione in attesa", pendingRequests: "{{count}} richieste di adesione in attesa",
pendingRequestsDesc: "Il bot di solito approva automaticamente. Se è bloccato, apri la stanza qui sotto e tocca Controlla le richieste.", pendingRequestsDesc: "Il bot di solito approva automaticamente. Se è bloccato, apri la stanza qui sotto e tocca Controlla le richieste.",
botMissing: "The community bot isn’t in this room right now, so an admin will review join requests.", botMissing: "The community bot isn’t in this room right now, so an admin will review join requests.",
secondDeviceHint: "Already a member on another device? Use “Link device” instead — the bot can’t add a second key to the same account.", secondDeviceHint: "Sei già membro su un altro dispositivo? Usa «Collega questo dispositivo» per un passaggio diretto, oppure chiedi di nuovo di entrare con questo dispositivo.",
create: "Crea la stanza della community", create: "Crea la stanza della community",
setupDeviceBody: "Questo assegna a questo dispositivo una chiave per la stanza della community. Un admin o il bot la approva una volta.", setupDeviceBody: "Questo assegna a questo dispositivo una chiave per la stanza della community. Un admin o il bot la approva una volta.",
onboardingFounder1: "Crea la stanza della community.", onboardingFounder1: "Crea la stanza della community.",

View file

@ -321,6 +321,7 @@ const drift: Dict = {
requestJoin: '参加をリクエスト', requestJoin: '参加をリクエスト',
requestAgain: "Request again", requestAgain: "Request again",
requestHandled: "Request handled", requestHandled: "Request handled",
copyCordnLink: "Copy Cordn link",
cordnWebCaveat: "Cordn web may stall after sign-in; the in-app join is more reliable.", cordnWebCaveat: "Cordn web may stall after sign-in; the in-app join is more reliable.",
checkInvite: '招待を確認', checkInvite: '招待を確認',
requested: 'リクエストを送信しました — クルー管理者が承認します。', requested: 'リクエストを送信しました — クルー管理者が承認します。',
@ -331,6 +332,8 @@ const drift: Dict = {
requests: '参加リクエスト', requests: '参加リクエスト',
checkRequests: 'リクエストを確認', checkRequests: 'リクエストを確認',
approve: '追加', approve: '追加',
approveTitle: "このデバイスを承認しますか?",
approveDesc: "{{name}} をこの暗号化ルームに追加しますか?そのデバイスは新しいメッセージを読めるようになります。",
noRequests: '保留中の参加リクエストはありません。', noRequests: '保留中の参加リクエストはありません。',
inviteHint: '今のところこのチャットにはあなただけです — クルーメンバーは「参加をリクエスト」をタップでき、あなたはここで承認します。', inviteHint: '今のところこのチャットにはあなただけです — クルーメンバーは「参加をリクエスト」をタップでき、あなたはここで承認します。',
memberCount: 'グループ内 {{count}}人', memberCount: 'グループ内 {{count}}人',
@ -419,7 +422,7 @@ const drift: Dict = {
pendingRequests: "参加リクエスト {{count}} 件が待機中", pendingRequests: "参加リクエスト {{count}} 件が待機中",
pendingRequestsDesc: "ボットは通常自動で承認します。止まっている場合は、下のルームを開いて「リクエストを確認」をタップしてください。", pendingRequestsDesc: "ボットは通常自動で承認します。止まっている場合は、下のルームを開いて「リクエストを確認」をタップしてください。",
botMissing: "The community bot isn’t in this room right now, so an admin will review join requests.", botMissing: "The community bot isn’t in this room right now, so an admin will review join requests.",
secondDeviceHint: "Already a member on another device? Use “Link device” instead — the bot can’t add a second key to the same account.", secondDeviceHint: "別のデバイスで既にメンバーですか?直接引き継ぐには「このデバイスをリンク」を使うか、このデバイスで改めて参加をリクエストしてください。",
create: "コミュニティルームを作成", create: "コミュニティルームを作成",
setupDeviceBody: "このデバイスにコミュニティルームの鍵を付与します。管理者またはボットが一度だけ承認します。", setupDeviceBody: "このデバイスにコミュニティルームの鍵を付与します。管理者またはボットが一度だけ承認します。",
onboardingFounder1: "コミュニティルームを作成します。", onboardingFounder1: "コミュニティルームを作成します。",

View file

@ -321,6 +321,7 @@ const drift: Dict = {
requestJoin: '참여 요청', requestJoin: '참여 요청',
requestAgain: "Request again", requestAgain: "Request again",
requestHandled: "Request handled", requestHandled: "Request handled",
copyCordnLink: "Copy Cordn link",
cordnWebCaveat: "Cordn web may stall after sign-in; the in-app join is more reliable.", cordnWebCaveat: "Cordn web may stall after sign-in; the in-app join is more reliable.",
checkInvite: '초대 확인', checkInvite: '초대 확인',
requested: '요청을 보냈습니다 — 크루 관리자가 승인합니다.', requested: '요청을 보냈습니다 — 크루 관리자가 승인합니다.',
@ -331,6 +332,8 @@ const drift: Dict = {
requests: '참여 요청', requests: '참여 요청',
checkRequests: '요청 확인', checkRequests: '요청 확인',
approve: '추가', approve: '추가',
approveTitle: "이 기기를 승인할까요?",
approveDesc: "{{name}} 님을 이 암호화된 방에 추가할까요? 해당 기기는 새 메시지를 읽을 수 있게 됩니다.",
noRequests: '대기 중인 참여 요청이 없습니다.', noRequests: '대기 중인 참여 요청이 없습니다.',
inviteHint: '지금까지 이 채팅에는 당신만 있습니다 — 크루 멤버가 “참여 요청”을 누르면 여기서 승인할 수 있습니다.', inviteHint: '지금까지 이 채팅에는 당신만 있습니다 — 크루 멤버가 “참여 요청”을 누르면 여기서 승인할 수 있습니다.',
memberCount: '그룹에 {{count}}명', memberCount: '그룹에 {{count}}명',
@ -419,7 +422,7 @@ const drift: Dict = {
pendingRequests: "참여 요청 {{count}}개 대기 중", pendingRequests: "참여 요청 {{count}}개 대기 중",
pendingRequestsDesc: "봇은 보통 자동으로 승인합니다. 멈춰 있으면 아래 방을 열고 요청 확인을 탭하세요.", pendingRequestsDesc: "봇은 보통 자동으로 승인합니다. 멈춰 있으면 아래 방을 열고 요청 확인을 탭하세요.",
botMissing: "The community bot isn’t in this room right now, so an admin will review join requests.", botMissing: "The community bot isn’t in this room right now, so an admin will review join requests.",
secondDeviceHint: "Already a member on another device? Use “Link device” instead — the bot can’t add a second key to the same account.", secondDeviceHint: "다른 기기에서 이미 멤버인가요? 직접 인계하려면 “이 기기 연결”을 사용하거나, 이 기기로 다시 참여를 요청하세요.",
create: "커뮤니티 방 만들기", create: "커뮤니티 방 만들기",
setupDeviceBody: "이 기기에 커뮤니티 방 열쇠를 부여합니다. 관리자나 봇이 한 번 승인합니다.", setupDeviceBody: "이 기기에 커뮤니티 방 열쇠를 부여합니다. 관리자나 봇이 한 번 승인합니다.",
onboardingFounder1: "커뮤니티 방을 만드세요.", onboardingFounder1: "커뮤니티 방을 만드세요.",

View file

@ -21,7 +21,7 @@ const communityUi = {
pendingRequests: '{{count}} join request(s) waiting', pendingRequests: '{{count}} join request(s) waiting',
pendingRequestsDesc: 'The bot usually approves automatically. If it’s stuck, open the room below and tap Check requests.', pendingRequestsDesc: 'The bot usually approves automatically. If it’s stuck, open the room below and tap Check requests.',
botMissing: 'The community bot isn’t in this room right now, so an admin will review join requests.', botMissing: 'The community bot isn’t in this room right now, so an admin will review join requests.',
secondDeviceHint: 'Already a member on another device? Use “Link device” instead — the bot can’t add a second key to the same account.', secondDeviceHint: 'Already a member on another device? Use “Link device” for a direct handoff, or ask to join again with this device.',
create: 'Create the community room', create: 'Create the community room',
setupDeviceBody: setupDeviceBody:
'This gives this device a key for the community room. An admin or the bot approves it once.', 'This gives this device a key for the community room. An admin or the bot approves it once.',
@ -61,7 +61,7 @@ const communityUi = {
pendingRequests: '{{count}} solicitud(es) de entrada esperando', pendingRequests: '{{count}} solicitud(es) de entrada esperando',
pendingRequestsDesc: 'El bot suele aprobar automáticamente. Si se ha quedado atascado, abre la sala y pulsa Buscar solicitudes.', pendingRequestsDesc: 'El bot suele aprobar automáticamente. Si se ha quedado atascado, abre la sala y pulsa Buscar solicitudes.',
botMissing: 'El bot de la comunidad no está en esta sala ahora mismo; un administrador revisará las solicitudes.', botMissing: 'El bot de la comunidad no está en esta sala ahora mismo; un administrador revisará las solicitudes.',
secondDeviceHint: '¿Ya eres miembro en otro dispositivo? Usa «Vincular dispositivo»; el bot no puede añadir una segunda clave a la misma cuenta.', secondDeviceHint: '¿Ya eres miembro en otro dispositivo? Usa «Vincular dispositivo» para un traspaso directo o pide entrar de nuevo con este dispositivo.',
create: 'Crear la sala de la comunidad', create: 'Crear la sala de la comunidad',
setupDeviceBody: setupDeviceBody:
'Esto da a este dispositivo una clave para la sala de la comunidad. Un administrador o el bot la aprueba una vez.', 'Esto da a este dispositivo una clave para la sala de la comunidad. Un administrador o el bot la aprueba una vez.',

View file

@ -18,6 +18,7 @@ const cordnUi = {
requestJoin: 'Ask to join', requestJoin: 'Ask to join',
requestAgain: 'Request again', requestAgain: 'Request again',
requestHandled: 'Request handled', requestHandled: 'Request handled',
copyCordnLink: 'Copy Cordn link',
cordnWebCaveat: 'Cordn web may stall after sign-in; the in-app join is more reliable.', cordnWebCaveat: 'Cordn web may stall after sign-in; the in-app join is more reliable.',
checkInvite: 'Check for invite', checkInvite: 'Check for invite',
requested: 'Request sent — a crew admin will approve it.', requested: 'Request sent — a crew admin will approve it.',
@ -28,6 +29,8 @@ const cordnUi = {
requests: 'Join requests', requests: 'Join requests',
checkRequests: 'Check requests', checkRequests: 'Check requests',
approve: 'Add', approve: 'Add',
approveTitle: 'Approve this device?',
approveDesc: 'Add {{name}} to this encrypted room? That device will be able to read new messages.',
noRequests: 'No pending join requests.', noRequests: 'No pending join requests.',
inviteHint: 'Only you are in this chat so far — crew members can tap “Ask to join”, then you approve them here.', inviteHint: 'Only you are in this chat so far — crew members can tap “Ask to join”, then you approve them here.',
shareInvite: 'Invite', shareInvite: 'Invite',
@ -123,6 +126,7 @@ const cordnUi = {
requestJoin: 'Pedir entrar', requestJoin: 'Pedir entrar',
requestAgain: 'Volver a pedir', requestAgain: 'Volver a pedir',
requestHandled: 'Solicitud gestionada', requestHandled: 'Solicitud gestionada',
copyCordnLink: 'Copiar enlace de Cordn',
cordnWebCaveat: 'Cordn web puede quedarse colgado tras iniciar sesión; entrar desde la app es más fiable.', cordnWebCaveat: 'Cordn web puede quedarse colgado tras iniciar sesión; entrar desde la app es más fiable.',
checkInvite: 'Buscar invitación', checkInvite: 'Buscar invitación',
requested: 'Solicitud enviada: un administrador la aprobará.', requested: 'Solicitud enviada: un administrador la aprobará.',
@ -133,6 +137,8 @@ const cordnUi = {
requests: 'Solicitudes de entrada', requests: 'Solicitudes de entrada',
checkRequests: 'Ver solicitudes', checkRequests: 'Ver solicitudes',
approve: 'Añadir', approve: 'Añadir',
approveTitle: '¿Aprobar este dispositivo?',
approveDesc: '¿Añadir a {{name}} a esta sala cifrada? Ese dispositivo podrá leer los mensajes nuevos.',
noRequests: 'No hay solicitudes pendientes.', noRequests: 'No hay solicitudes pendientes.',
inviteHint: 'De momento solo estás tú en este chat: los miembros pueden pulsar «Pedir entrar» y tú los apruebas aquí.', inviteHint: 'De momento solo estás tú en este chat: los miembros pueden pulsar «Pedir entrar» y tú los apruebas aquí.',
shareInvite: 'Invitar', shareInvite: 'Invitar',

View file

@ -309,6 +309,7 @@ const drift = {
"requestJoin": "Vragen om deel te nemen", "requestJoin": "Vragen om deel te nemen",
"requestAgain": "Request again", "requestAgain": "Request again",
"requestHandled": "Request handled", "requestHandled": "Request handled",
"copyCordnLink": "Copy Cordn link",
"cordnWebCaveat": "Cordn web may stall after sign-in; the in-app join is more reliable.", "cordnWebCaveat": "Cordn web may stall after sign-in; the in-app join is more reliable.",
"checkInvite": "Op uitnodiging controleren", "checkInvite": "Op uitnodiging controleren",
"requested": "Verzoek verzonden — een crew-admin keurt het goed.", "requested": "Verzoek verzonden — een crew-admin keurt het goed.",
@ -319,6 +320,8 @@ const drift = {
"requests": "Deelnameverzoeken", "requests": "Deelnameverzoeken",
"checkRequests": "Verzoeken controleren", "checkRequests": "Verzoeken controleren",
"approve": "Toevoegen", "approve": "Toevoegen",
"approveTitle": "Dit apparaat goedkeuren?",
"approveDesc": "{{name}} aan deze versleutelde ruimte toevoegen? Dat apparaat kan dan nieuwe berichten lezen.",
"noRequests": "Geen openstaande deelnameverzoeken.", "noRequests": "Geen openstaande deelnameverzoeken.",
"inviteHint": "Alleen jij zit tot nu toe in deze chat — crewleden kunnen op “Vragen om deel te nemen” tikken, daarna keur je hen hier goed.", "inviteHint": "Alleen jij zit tot nu toe in deze chat — crewleden kunnen op “Vragen om deel te nemen” tikken, daarna keur je hen hier goed.",
"memberCount": "{{count}} in de groep", "memberCount": "{{count}} in de groep",
@ -407,7 +410,7 @@ const drift = {
"pendingRequests": "{{count}} openstaande verzoeken om deel te nemen", "pendingRequests": "{{count}} openstaande verzoeken om deel te nemen",
"pendingRequestsDesc": "De bot keurt meestal automatisch goed. Als het vastloopt, open dan hieronder de ruimte en tik op Verzoeken controleren.", "pendingRequestsDesc": "De bot keurt meestal automatisch goed. Als het vastloopt, open dan hieronder de ruimte en tik op Verzoeken controleren.",
"botMissing": "The community bot isn’t in this room right now, so an admin will review join requests.", "botMissing": "The community bot isn’t in this room right now, so an admin will review join requests.",
"secondDeviceHint": "Already a member on another device? Use “Link device” instead — the bot can’t add a second key to the same account.", "secondDeviceHint": "Al lid op een ander apparaat? Gebruik ‘Dit apparaat koppelen’ voor een directe overdracht, of vraag opnieuw om lid te worden met dit apparaat.",
"create": "Communityruimte aanmaken", "create": "Communityruimte aanmaken",
"setupDeviceBody": "Dit geeft dit apparaat een sleutel voor de communityruimte. Een beheerder of de bot keurt die één keer goed.", "setupDeviceBody": "Dit geeft dit apparaat een sleutel voor de communityruimte. Een beheerder of de bot keurt die één keer goed.",
"onboardingFounder1": "Maak de communityruimte aan.", "onboardingFounder1": "Maak de communityruimte aan.",

View file

@ -309,6 +309,7 @@ const drift = {
"requestJoin": "Poproś o dołączenie", "requestJoin": "Poproś o dołączenie",
"requestAgain": "Request again", "requestAgain": "Request again",
"requestHandled": "Request handled", "requestHandled": "Request handled",
"copyCordnLink": "Copy Cordn link",
"cordnWebCaveat": "Cordn web may stall after sign-in; the in-app join is more reliable.", "cordnWebCaveat": "Cordn web may stall after sign-in; the in-app join is more reliable.",
"checkInvite": "Sprawdź zaproszenie", "checkInvite": "Sprawdź zaproszenie",
"requested": "Prośba wysłana — administrator crew ją zatwierdzi.", "requested": "Prośba wysłana — administrator crew ją zatwierdzi.",
@ -319,6 +320,8 @@ const drift = {
"requests": "Prośby o dołączenie", "requests": "Prośby o dołączenie",
"checkRequests": "Sprawdź prośby", "checkRequests": "Sprawdź prośby",
"approve": "Dodaj", "approve": "Dodaj",
"approveTitle": "Zatwierdzić to urządzenie?",
"approveDesc": "Dodać {{name}} do tego zaszyfrowanego pokoju? To urządzenie będzie mogło czytać nowe wiadomości.",
"noRequests": "Brak oczekujących próśb o dołączenie.", "noRequests": "Brak oczekujących próśb o dołączenie.",
"inviteHint": "Na razie tylko ty jesteś w tym czacie — członkowie crew mogą dotknąć „Poproś o dołączenie”, a ty ich tu zatwierdzisz.", "inviteHint": "Na razie tylko ty jesteś w tym czacie — członkowie crew mogą dotknąć „Poproś o dołączenie”, a ty ich tu zatwierdzisz.",
"memberCount": "{{count}} w grupie", "memberCount": "{{count}} w grupie",
@ -407,7 +410,7 @@ const drift = {
"pendingRequests": "{{count}} oczekujących próśb o dołączenie", "pendingRequests": "{{count}} oczekujących próśb o dołączenie",
"pendingRequestsDesc": "Bot zwykle zatwierdza automatycznie. Jeśli utknął, otwórz pokój poniżej i dotknij Sprawdź prośby.", "pendingRequestsDesc": "Bot zwykle zatwierdza automatycznie. Jeśli utknął, otwórz pokój poniżej i dotknij Sprawdź prośby.",
"botMissing": "The community bot isn’t in this room right now, so an admin will review join requests.", "botMissing": "The community bot isn’t in this room right now, so an admin will review join requests.",
"secondDeviceHint": "Already a member on another device? Use “Link device” instead — the bot can’t add a second key to the same account.", "secondDeviceHint": "Masz już konto na innym urządzeniu? Użyj „Połącz to urządzenie”, aby przenieść je bezpośrednio, albo poproś ponownie o dołączenie z tego urządzenia.",
"create": "Utwórz pokój społeczności", "create": "Utwórz pokój społeczności",
"setupDeviceBody": "To daje temu urządzeniu klucz do pokoju społeczności. Administrator lub bot zatwierdza go jednorazowo.", "setupDeviceBody": "To daje temu urządzeniu klucz do pokoju społeczności. Administrator lub bot zatwierdza go jednorazowo.",
"onboardingFounder1": "Utwórz pokój społeczności.", "onboardingFounder1": "Utwórz pokój społeczności.",

View file

@ -311,6 +311,7 @@ const drift = {
requestJoin: "Pedir para entrar", requestJoin: "Pedir para entrar",
requestAgain: "Request again", requestAgain: "Request again",
requestHandled: "Request handled", requestHandled: "Request handled",
copyCordnLink: "Copy Cordn link",
cordnWebCaveat: "Cordn web may stall after sign-in; the in-app join is more reliable.", cordnWebCaveat: "Cordn web may stall after sign-in; the in-app join is more reliable.",
checkInvite: "Verificar convite", checkInvite: "Verificar convite",
requested: "Solicitação enviada — um admin da crew vai aprovar.", requested: "Solicitação enviada — um admin da crew vai aprovar.",
@ -321,6 +322,8 @@ const drift = {
requests: "Pedidos de entrada", requests: "Pedidos de entrada",
checkRequests: "Verificar pedidos", checkRequests: "Verificar pedidos",
approve: "Adicionar", approve: "Adicionar",
approveTitle: "Aprovar este dispositivo?",
approveDesc: "Adicionar {{name}} a esta sala criptografada? Esse dispositivo poderá ler as novas mensagens.",
noRequests: "Nenhum pedido de entrada pendente.", noRequests: "Nenhum pedido de entrada pendente.",
inviteHint: "Por enquanto só você está neste chat — os membros da crew podem tocar em “Pedir para entrar”, e você os aprova aqui.", inviteHint: "Por enquanto só você está neste chat — os membros da crew podem tocar em “Pedir para entrar”, e você os aprova aqui.",
memberCount: "{{count}} no grupo", memberCount: "{{count}} no grupo",
@ -409,7 +412,7 @@ const drift = {
pendingRequests: "{{count}} pedido(s) de entrada aguardando", pendingRequests: "{{count}} pedido(s) de entrada aguardando",
pendingRequestsDesc: "O bot costuma aprovar automaticamente. Se travou, abra a sala abaixo e toque em Verificar pedidos.", pendingRequestsDesc: "O bot costuma aprovar automaticamente. Se travou, abra a sala abaixo e toque em Verificar pedidos.",
botMissing: "The community bot isn’t in this room right now, so an admin will review join requests.", botMissing: "The community bot isn’t in this room right now, so an admin will review join requests.",
secondDeviceHint: "Already a member on another device? Use “Link device” instead — the bot can’t add a second key to the same account.", secondDeviceHint: "Já é membro em outro dispositivo? Use “Vincular este dispositivo” para uma transferência direta, ou peça para entrar de novo com este dispositivo.",
create: "Criar a sala da comunidade", create: "Criar a sala da comunidade",
setupDeviceBody: "Isso dá a este dispositivo uma chave para a sala da comunidade. Um admin ou o bot aprova uma vez.", setupDeviceBody: "Isso dá a este dispositivo uma chave para a sala da comunidade. Um admin ou o bot aprova uma vez.",
onboardingFounder1: "Crie a sala da comunidade.", onboardingFounder1: "Crie a sala da comunidade.",

View file

@ -309,6 +309,7 @@ const drift = {
"requestJoin": "Cere să te alături", "requestJoin": "Cere să te alături",
"requestAgain": "Request again", "requestAgain": "Request again",
"requestHandled": "Request handled", "requestHandled": "Request handled",
"copyCordnLink": "Copy Cordn link",
"cordnWebCaveat": "Cordn web may stall after sign-in; the in-app join is more reliable.", "cordnWebCaveat": "Cordn web may stall after sign-in; the in-app join is more reliable.",
"checkInvite": "Verifică invitația", "checkInvite": "Verifică invitația",
"requested": "Cerere trimisă — un admin de crew o va aproba.", "requested": "Cerere trimisă — un admin de crew o va aproba.",
@ -319,6 +320,8 @@ const drift = {
"requests": "Cereri de alăturare", "requests": "Cereri de alăturare",
"checkRequests": "Verifică cererile", "checkRequests": "Verifică cererile",
"approve": "Adaugă", "approve": "Adaugă",
"approveTitle": "Aprobi acest dispozitiv?",
"approveDesc": "Adaugi {{name}} în această cameră criptată? Dispozitivul respectiv va putea citi mesajele noi.",
"noRequests": "Nicio cerere de alăturare în așteptare.", "noRequests": "Nicio cerere de alăturare în așteptare.",
"inviteHint": "Momentan doar tu ești în acest chat — membrii crew-ului pot apăsa „Cere să te alături”, apoi îi aprobi aici.", "inviteHint": "Momentan doar tu ești în acest chat — membrii crew-ului pot apăsa „Cere să te alături”, apoi îi aprobi aici.",
"memberCount": "{{count}} în grup", "memberCount": "{{count}} în grup",
@ -407,7 +410,7 @@ const drift = {
"pendingRequests": "{{count}} cereri de alăturare în așteptare", "pendingRequests": "{{count}} cereri de alăturare în așteptare",
"pendingRequestsDesc": "Botul aprobă de obicei automat. Dacă s-a blocat, deschide camera de mai jos și apasă Verifică cererile.", "pendingRequestsDesc": "Botul aprobă de obicei automat. Dacă s-a blocat, deschide camera de mai jos și apasă Verifică cererile.",
"botMissing": "The community bot isn’t in this room right now, so an admin will review join requests.", "botMissing": "The community bot isn’t in this room right now, so an admin will review join requests.",
"secondDeviceHint": "Already a member on another device? Use “Link device” instead — the bot can’t add a second key to the same account.", "secondDeviceHint": "Ești deja membru pe alt dispozitiv? Folosește „Conectează acest dispozitiv” pentru un transfer direct sau cere din nou să te alături cu acest dispozitiv.",
"create": "Creează camera comunității", "create": "Creează camera comunității",
"setupDeviceBody": "Aceasta oferă acestui dispozitiv o cheie pentru camera comunității. Un administrator sau botul o aprobă o dată.", "setupDeviceBody": "Aceasta oferă acestui dispozitiv o cheie pentru camera comunității. Un administrator sau botul o aprobă o dată.",
"onboardingFounder1": "Creează camera comunității.", "onboardingFounder1": "Creează camera comunității.",

View file

@ -313,6 +313,7 @@ const drift = {
requestJoin: "Попросить присоединиться", requestJoin: "Попросить присоединиться",
requestAgain: "Request again", requestAgain: "Request again",
requestHandled: "Request handled", requestHandled: "Request handled",
copyCordnLink: "Copy Cordn link",
cordnWebCaveat: "Cordn web may stall after sign-in; the in-app join is more reliable.", cordnWebCaveat: "Cordn web may stall after sign-in; the in-app join is more reliable.",
checkInvite: "Проверить приглашение", checkInvite: "Проверить приглашение",
requested: "Запрос отправлен — администратор Crew одобрит его.", requested: "Запрос отправлен — администратор Crew одобрит его.",
@ -323,6 +324,8 @@ const drift = {
requests: "Запросы на присоединение", requests: "Запросы на присоединение",
checkRequests: "Проверить запросы", checkRequests: "Проверить запросы",
approve: "Добавить", approve: "Добавить",
approveTitle: "Одобрить это устройство?",
approveDesc: "Добавить {{name}} в эту зашифрованную комнату? Это устройство сможет читать новые сообщения.",
noRequests: "Нет ожидающих запросов на присоединение.", noRequests: "Нет ожидающих запросов на присоединение.",
inviteHint: "Пока в этом чате только вы — участники Crew могут нажать «Попросить присоединиться», а вы одобрите их здесь.", inviteHint: "Пока в этом чате только вы — участники Crew могут нажать «Попросить присоединиться», а вы одобрите их здесь.",
memberCount: "{{count}} в группе", memberCount: "{{count}} в группе",
@ -411,7 +414,7 @@ const drift = {
pendingRequests: "{{count}} запросов на вступление в ожидании", pendingRequests: "{{count}} запросов на вступление в ожидании",
pendingRequestsDesc: "Бот обычно одобряет автоматически. Если застряло, открой комнату ниже и нажми «Проверить запросы».", pendingRequestsDesc: "Бот обычно одобряет автоматически. Если застряло, открой комнату ниже и нажми «Проверить запросы».",
botMissing: "The community bot isn’t in this room right now, so an admin will review join requests.", botMissing: "The community bot isn’t in this room right now, so an admin will review join requests.",
secondDeviceHint: "Already a member on another device? Use “Link device” instead — the bot can’t add a second key to the same account.", secondDeviceHint: "Уже участник на другом устройстве? Используй «Привязать это устройство» для прямой передачи или попроси снова присоединиться с этого устройства.",
create: "Создать комнату сообщества", create: "Создать комнату сообщества",
setupDeviceBody: "Это даёт устройству ключ от комнаты сообщества. Администратор или бот подтверждает его один раз.", setupDeviceBody: "Это даёт устройству ключ от комнаты сообщества. Администратор или бот подтверждает его один раз.",
onboardingFounder1: "Создай комнату сообщества.", onboardingFounder1: "Создай комнату сообщества.",

View file

@ -313,6 +313,7 @@ const drift = {
requestJoin: "Be om att gå med", requestJoin: "Be om att gå med",
requestAgain: "Request again", requestAgain: "Request again",
requestHandled: "Request handled", requestHandled: "Request handled",
copyCordnLink: "Copy Cordn link",
cordnWebCaveat: "Cordn web may stall after sign-in; the in-app join is more reliable.", cordnWebCaveat: "Cordn web may stall after sign-in; the in-app join is more reliable.",
checkInvite: "Kontrollera inbjudan", checkInvite: "Kontrollera inbjudan",
requested: "Begäran skickad — en crewadmin godkänner den.", requested: "Begäran skickad — en crewadmin godkänner den.",
@ -323,6 +324,8 @@ const drift = {
requests: "Medlemsbegäranden", requests: "Medlemsbegäranden",
checkRequests: "Kontrollera begäranden", checkRequests: "Kontrollera begäranden",
approve: "Lägg till", approve: "Lägg till",
approveTitle: "Godkänn den här enheten?",
approveDesc: "Lägg till {{name}} i den här krypterade rummet? Enheten kommer att kunna läsa nya meddelanden.",
noRequests: "Inga väntande medlemsbegäranden.", noRequests: "Inga väntande medlemsbegäranden.",
inviteHint: "Bara du är i den här chatten än så länge — crewmedlemmar kan trycka på ”Be om att gå med”, sedan godkänner du dem här.", inviteHint: "Bara du är i den här chatten än så länge — crewmedlemmar kan trycka på ”Be om att gå med”, sedan godkänner du dem här.",
memberCount: "{{count}} i gruppen", memberCount: "{{count}} i gruppen",
@ -411,7 +414,7 @@ const drift = {
pendingRequests: "{{count}} ansökningar om att gå med väntar", pendingRequests: "{{count}} ansökningar om att gå med väntar",
pendingRequestsDesc: "Boten godkänner oftast automatiskt. Om det har fastnat, öppna rummet nedan och tryck på Kontrollera begäranden.", pendingRequestsDesc: "Boten godkänner oftast automatiskt. Om det har fastnat, öppna rummet nedan och tryck på Kontrollera begäranden.",
botMissing: "The community bot isn’t in this room right now, so an admin will review join requests.", botMissing: "The community bot isn’t in this room right now, so an admin will review join requests.",
secondDeviceHint: "Already a member on another device? Use “Link device” instead — the bot can’t add a second key to the same account.", secondDeviceHint: "Redan medlem på en annan enhet? Använd ”Länka den här enheten” för en direkt överlämning, eller be att få gå med igen med den här enheten.",
create: "Skapa communityrummet", create: "Skapa communityrummet",
setupDeviceBody: "Detta ger enheten en nyckel till communityrummet. En admin eller boten godkänner den en gång.", setupDeviceBody: "Detta ger enheten en nyckel till communityrummet. En admin eller boten godkänner den en gång.",
onboardingFounder1: "Skapa communityrummet.", onboardingFounder1: "Skapa communityrummet.",

View file

@ -305,6 +305,7 @@ const driftStrings = {
requestJoin: 'ขอเข้าร่วม', requestJoin: 'ขอเข้าร่วม',
requestAgain: "Request again", requestAgain: "Request again",
requestHandled: "Request handled", requestHandled: "Request handled",
copyCordnLink: "Copy Cordn link",
cordnWebCaveat: "Cordn web may stall after sign-in; the in-app join is more reliable.", cordnWebCaveat: "Cordn web may stall after sign-in; the in-app join is more reliable.",
checkInvite: 'ตรวจสอบคำเชิญ', checkInvite: 'ตรวจสอบคำเชิญ',
requested: 'ส่งคำขอแล้ว — แอดมิน crew จะอนุมัติ', requested: 'ส่งคำขอแล้ว — แอดมิน crew จะอนุมัติ',
@ -315,6 +316,8 @@ const driftStrings = {
requests: 'คำขอเข้าร่วม', requests: 'คำขอเข้าร่วม',
checkRequests: 'ตรวจสอบคำขอ', checkRequests: 'ตรวจสอบคำขอ',
approve: 'เพิ่ม', approve: 'เพิ่ม',
approveTitle: "อนุมัติอุปกรณ์นี้หรือไม่",
approveDesc: "เพิ่ม {{name}} เข้าห้องเข้ารหัสนี้หรือไม่ อุปกรณ์นั้นจะอ่านข้อความใหม่ได้",
noRequests: 'ไม่มีคำขอเข้าร่วมที่ค้างอยู่', noRequests: 'ไม่มีคำขอเข้าร่วมที่ค้างอยู่',
inviteHint: 'ในแชทนี้มีแค่คุณเท่านั้น — สมาชิก crew สามารถแตะ “ขอเข้าร่วม” แล้วคุณอนุมัติที่นี่', inviteHint: 'ในแชทนี้มีแค่คุณเท่านั้น — สมาชิก crew สามารถแตะ “ขอเข้าร่วม” แล้วคุณอนุมัติที่นี่',
memberCount: '{{count}} คนในกลุ่ม', memberCount: '{{count}} คนในกลุ่ม',
@ -403,7 +406,7 @@ const driftStrings = {
pendingRequests: "มีคำขอเข้าร่วม {{count}} รายการรออยู่", pendingRequests: "มีคำขอเข้าร่วม {{count}} รายการรออยู่",
pendingRequestsDesc: "บอทมักอนุมัติโดยอัตโนมัติ หากค้างอยู่ ให้เปิดห้องด้านล่างแล้วแตะ ตรวจสอบคำขอ", pendingRequestsDesc: "บอทมักอนุมัติโดยอัตโนมัติ หากค้างอยู่ ให้เปิดห้องด้านล่างแล้วแตะ ตรวจสอบคำขอ",
botMissing: "The community bot isn’t in this room right now, so an admin will review join requests.", botMissing: "The community bot isn’t in this room right now, so an admin will review join requests.",
secondDeviceHint: "Already a member on another device? Use “Link device” instead — the bot can’t add a second key to the same account.", secondDeviceHint: "เป็นสมาชิกบนอุปกรณ์อื่นอยู่แล้วหรือ? ใช้ “เชื่อมโยงอุปกรณ์นี้” เพื่อโอนย้ายโดยตรง หรือขอเข้าร่วมอีกครั้งด้วยอุปกรณ์นี้",
create: "สร้างห้องชุมชน", create: "สร้างห้องชุมชน",
setupDeviceBody: "สิ่งนี้มอบกุญแจสำหรับห้องชุมชนให้อุปกรณ์นี้ ผู้ดูแลหรือบอทจะอนุมัติหนึ่งครั้ง", setupDeviceBody: "สิ่งนี้มอบกุญแจสำหรับห้องชุมชนให้อุปกรณ์นี้ ผู้ดูแลหรือบอทจะอนุมัติหนึ่งครั้ง",
onboardingFounder1: "สร้างห้องชุมชน", onboardingFounder1: "สร้างห้องชุมชน",

View file

@ -305,6 +305,7 @@ const driftStrings = {
requestJoin: 'Katılmayı iste', requestJoin: 'Katılmayı iste',
requestAgain: "Request again", requestAgain: "Request again",
requestHandled: "Request handled", requestHandled: "Request handled",
copyCordnLink: "Copy Cordn link",
cordnWebCaveat: "Cordn web may stall after sign-in; the in-app join is more reliable.", cordnWebCaveat: "Cordn web may stall after sign-in; the in-app join is more reliable.",
checkInvite: 'Davetiye kontrol et', checkInvite: 'Davetiye kontrol et',
requested: 'İstek gönderildi — bir crew yöneticisi onaylayacak.', requested: 'İstek gönderildi — bir crew yöneticisi onaylayacak.',
@ -315,6 +316,8 @@ const driftStrings = {
requests: 'Katılma istekleri', requests: 'Katılma istekleri',
checkRequests: 'İstekleri kontrol et', checkRequests: 'İstekleri kontrol et',
approve: 'Ekle', approve: 'Ekle',
approveTitle: "Bu cihaz onaylansın mı?",
approveDesc: "{{name}} bu şifreli odaya eklensin mi? Bu cihaz yeni mesajları okuyabilecek.",
noRequests: 'Bekleyen katılma isteği yok.', noRequests: 'Bekleyen katılma isteği yok.',
inviteHint: "Bu sohbette şu ana kadar yalnızca sen varsın — crew üyeleri “Katılmayı iste”ye dokunabilir, sonra burada onaylarsın.", inviteHint: "Bu sohbette şu ana kadar yalnızca sen varsın — crew üyeleri “Katılmayı iste”ye dokunabilir, sonra burada onaylarsın.",
memberCount: 'grupta {{count}} kişi', memberCount: 'grupta {{count}} kişi',
@ -403,7 +406,7 @@ const driftStrings = {
pendingRequests: "{{count}} katılma isteği bekliyor", pendingRequests: "{{count}} katılma isteği bekliyor",
pendingRequestsDesc: "Bot genellikle otomatik onaylar. Takıldıysa aşağıdaki odayı aç ve İstekleri kontrol et’e dokun.", pendingRequestsDesc: "Bot genellikle otomatik onaylar. Takıldıysa aşağıdaki odayı aç ve İstekleri kontrol et’e dokun.",
botMissing: "The community bot isn’t in this room right now, so an admin will review join requests.", botMissing: "The community bot isn’t in this room right now, so an admin will review join requests.",
secondDeviceHint: "Already a member on another device? Use “Link device” instead — the bot can’t add a second key to the same account.", secondDeviceHint: "Başka bir cihazda zaten üye misiniz? Doğrudan devir için “Bu cihazı bağla”yı kullanın veya bu cihazla yeniden katılma isteği gönderin.",
create: "Topluluk odasını oluştur", create: "Topluluk odasını oluştur",
setupDeviceBody: "Bu, bu cihaza topluluk odası için bir anahtar verir. Bir yönetici veya bot bunu bir kez onaylar.", setupDeviceBody: "Bu, bu cihaza topluluk odası için bir anahtar verir. Bir yönetici veya bot bunu bir kez onaylar.",
onboardingFounder1: "Topluluk odasını oluştur.", onboardingFounder1: "Topluluk odasını oluştur.",

View file

@ -313,6 +313,7 @@ const drift = {
requestJoin: "Попросити приєднатися", requestJoin: "Попросити приєднатися",
requestAgain: "Request again", requestAgain: "Request again",
requestHandled: "Request handled", requestHandled: "Request handled",
copyCordnLink: "Copy Cordn link",
cordnWebCaveat: "Cordn web may stall after sign-in; the in-app join is more reliable.", cordnWebCaveat: "Cordn web may stall after sign-in; the in-app join is more reliable.",
checkInvite: "Перевірити запрошення", checkInvite: "Перевірити запрошення",
requested: "Запит надіслано — адміністратор команди його схвалить.", requested: "Запит надіслано — адміністратор команди його схвалить.",
@ -323,6 +324,8 @@ const drift = {
requests: "Запити на приєднання", requests: "Запити на приєднання",
checkRequests: "Перевірити запити", checkRequests: "Перевірити запити",
approve: "Додати", approve: "Додати",
approveTitle: "Схвалити цей пристрій?",
approveDesc: "Додати {{name}} до цієї зашифрованої кімнати? Цей пристрій зможе читати нові повідомлення.",
noRequests: "Немає запитів на приєднання, що очікують.", noRequests: "Немає запитів на приєднання, що очікують.",
inviteHint: "Поки в цьому чаті лише ви — учасники команди можуть натиснути «Попросити приєднатися», а ви схвалите їх тут.", inviteHint: "Поки в цьому чаті лише ви — учасники команди можуть натиснути «Попросити приєднатися», а ви схвалите їх тут.",
memberCount: "{{count}} у групі", memberCount: "{{count}} у групі",
@ -411,7 +414,7 @@ const drift = {
pendingRequests: "{{count}} запитів на приєднання в очікуванні", pendingRequests: "{{count}} запитів на приєднання в очікуванні",
pendingRequestsDesc: "Бот зазвичай схвалює автоматично. Якщо застрягло, відкрий кімнату нижче й натисни «Перевірити запити».", pendingRequestsDesc: "Бот зазвичай схвалює автоматично. Якщо застрягло, відкрий кімнату нижче й натисни «Перевірити запити».",
botMissing: "The community bot isn’t in this room right now, so an admin will review join requests.", botMissing: "The community bot isn’t in this room right now, so an admin will review join requests.",
secondDeviceHint: "Already a member on another device? Use “Link device” instead — the bot can’t add a second key to the same account.", secondDeviceHint: "Уже є учасником на іншому пристрої? Використай «Прив’язати цей пристрій» для прямого перенесення або попроси знову долучитися з цього пристрою.",
create: "Створити кімнату спільноти", create: "Створити кімнату спільноти",
setupDeviceBody: "Це дає цьому пристрою ключ до кімнати спільноти. Адміністратор або бот підтверджує його один раз.", setupDeviceBody: "Це дає цьому пристрою ключ до кімнати спільноти. Адміністратор або бот підтверджує його один раз.",
onboardingFounder1: "Створи кімнату спільноти.", onboardingFounder1: "Створи кімнату спільноти.",

View file

@ -305,6 +305,7 @@ const driftStrings = {
requestJoin: 'Xin tham gia', requestJoin: 'Xin tham gia',
requestAgain: "Request again", requestAgain: "Request again",
requestHandled: "Request handled", requestHandled: "Request handled",
copyCordnLink: "Copy Cordn link",
cordnWebCaveat: "Cordn web may stall after sign-in; the in-app join is more reliable.", cordnWebCaveat: "Cordn web may stall after sign-in; the in-app join is more reliable.",
checkInvite: 'Kiểm tra lời mời', checkInvite: 'Kiểm tra lời mời',
requested: 'Đã gửi yêu cầu — quản trị viên crew sẽ duyệt.', requested: 'Đã gửi yêu cầu — quản trị viên crew sẽ duyệt.',
@ -315,6 +316,8 @@ const driftStrings = {
requests: 'Yêu cầu tham gia', requests: 'Yêu cầu tham gia',
checkRequests: 'Kiểm tra yêu cầu', checkRequests: 'Kiểm tra yêu cầu',
approve: 'Thêm', approve: 'Thêm',
approveTitle: "Phê duyệt thiết bị này?",
approveDesc: "Thêm {{name}} vào phòng được mã hóa này? Thiết bị đó sẽ có thể đọc các tin nhắn mới.",
noRequests: 'Không có yêu cầu tham gia đang chờ.', noRequests: 'Không có yêu cầu tham gia đang chờ.',
inviteHint: 'Mới chỉ có bạn trong chat này — thành viên crew có thể nhấn “Xin tham gia”, rồi bạn duyệt họ ở đây.', inviteHint: 'Mới chỉ có bạn trong chat này — thành viên crew có thể nhấn “Xin tham gia”, rồi bạn duyệt họ ở đây.',
memberCount: '{{count}} trong nhóm', memberCount: '{{count}} trong nhóm',
@ -403,7 +406,7 @@ const driftStrings = {
pendingRequests: "{{count}} yêu cầu tham gia đang chờ", pendingRequests: "{{count}} yêu cầu tham gia đang chờ",
pendingRequestsDesc: "Bot thường phê duyệt tự động. Nếu bị kẹt, mở phòng bên dưới và nhấn Kiểm tra yêu cầu.", pendingRequestsDesc: "Bot thường phê duyệt tự động. Nếu bị kẹt, mở phòng bên dưới và nhấn Kiểm tra yêu cầu.",
botMissing: "The community bot isn’t in this room right now, so an admin will review join requests.", botMissing: "The community bot isn’t in this room right now, so an admin will review join requests.",
secondDeviceHint: "Already a member on another device? Use “Link device” instead — the bot can’t add a second key to the same account.", secondDeviceHint: "Đã là thành viên trên thiết bị khác? Dùng “Liên kết thiết bị này” để chuyển giao trực tiếp, hoặc yêu cầu tham gia lại bằng thiết bị này.",
create: "Tạo phòng cộng đồng", create: "Tạo phòng cộng đồng",
setupDeviceBody: "Việc này cấp cho thiết bị này một khóa cho phòng cộng đồng. Quản trị viên hoặc bot phê duyệt một lần.", setupDeviceBody: "Việc này cấp cho thiết bị này một khóa cho phòng cộng đồng. Quản trị viên hoặc bot phê duyệt một lần.",
onboardingFounder1: "Tạo phòng cộng đồng.", onboardingFounder1: "Tạo phòng cộng đồng.",

View file

@ -321,6 +321,7 @@ const drift: Dict = {
requestJoin: '请求加入', requestJoin: '请求加入',
requestAgain: "Request again", requestAgain: "Request again",
requestHandled: "Request handled", requestHandled: "Request handled",
copyCordnLink: "Copy Cordn link",
cordnWebCaveat: "Cordn web may stall after sign-in; the in-app join is more reliable.", cordnWebCaveat: "Cordn web may stall after sign-in; the in-app join is more reliable.",
checkInvite: '检查邀请', checkInvite: '检查邀请',
requested: '请求已发送——Crew 管理员会批准。', requested: '请求已发送——Crew 管理员会批准。',
@ -331,6 +332,8 @@ const drift: Dict = {
requests: '加入请求', requests: '加入请求',
checkRequests: '检查请求', checkRequests: '检查请求',
approve: '添加', approve: '添加',
approveTitle: "批准此设备?",
approveDesc: "将 {{name}} 添加到此加密房间?该设备将能读取新消息。",
noRequests: '没有待处理的加入请求。', noRequests: '没有待处理的加入请求。',
inviteHint: '目前只有你在这个聊天中——Crew 成员可以点击“请求加入”,然后你在这里批准他们。', inviteHint: '目前只有你在这个聊天中——Crew 成员可以点击“请求加入”,然后你在这里批准他们。',
memberCount: '群组中有 {{count}} 人', memberCount: '群组中有 {{count}} 人',
@ -419,7 +422,7 @@ const drift: Dict = {
pendingRequests: "{{count}} 个加入请求正在等待", pendingRequests: "{{count}} 个加入请求正在等待",
pendingRequestsDesc: "机器人通常会自动批准。如果卡住了,请打开下面的房间并点按“检查请求”。", pendingRequestsDesc: "机器人通常会自动批准。如果卡住了,请打开下面的房间并点按“检查请求”。",
botMissing: "The community bot isn’t in this room right now, so an admin will review join requests.", botMissing: "The community bot isn’t in this room right now, so an admin will review join requests.",
secondDeviceHint: "Already a member on another device? Use “Link device” instead — the bot can’t add a second key to the same account.", secondDeviceHint: "已在其他设备上加入?使用“关联此设备”直接交接,或用此设备再次申请加入。",
create: "创建社区房间", create: "创建社区房间",
setupDeviceBody: "这会为此设备提供社区房间的密钥,管理员或机器人会批准一次。", setupDeviceBody: "这会为此设备提供社区房间的密钥,管理员或机器人会批准一次。",
onboardingFounder1: "创建社区房间。", onboardingFounder1: "创建社区房间。",

View file

@ -321,6 +321,7 @@ const drift: Dict = {
requestJoin: '要求加入', requestJoin: '要求加入',
requestAgain: "Request again", requestAgain: "Request again",
requestHandled: "Request handled", requestHandled: "Request handled",
copyCordnLink: "Copy Cordn link",
cordnWebCaveat: "Cordn web may stall after sign-in; the in-app join is more reliable.", cordnWebCaveat: "Cordn web may stall after sign-in; the in-app join is more reliable.",
checkInvite: '檢查邀請', checkInvite: '檢查邀請',
requested: '要求已送出——Crew 管理員會批准。', requested: '要求已送出——Crew 管理員會批准。',
@ -331,6 +332,8 @@ const drift: Dict = {
requests: '加入要求', requests: '加入要求',
checkRequests: '檢查要求', checkRequests: '檢查要求',
approve: '新增', approve: '新增',
approveTitle: "批准此裝置?",
approveDesc: "將 {{name}} 加入此加密房間?該裝置將能讀取新訊息。",
noRequests: '沒有待處理的加入要求。', noRequests: '沒有待處理的加入要求。',
inviteHint: '目前只有你在這個聊天中——Crew 成員可以點「要求加入」,然後你在這裡批准他們。', inviteHint: '目前只有你在這個聊天中——Crew 成員可以點「要求加入」,然後你在這裡批准他們。',
memberCount: '群組中有 {{count}} 人', memberCount: '群組中有 {{count}} 人',
@ -419,7 +422,7 @@ const drift: Dict = {
pendingRequests: "{{count}} 個加入要求正在等待", pendingRequests: "{{count}} 個加入要求正在等待",
pendingRequestsDesc: "機器人通常會自動核准。如果卡住了,請開啟下面的房間並點按「檢查要求」。", pendingRequestsDesc: "機器人通常會自動核准。如果卡住了,請開啟下面的房間並點按「檢查要求」。",
botMissing: "The community bot isn’t in this room right now, so an admin will review join requests.", botMissing: "The community bot isn’t in this room right now, so an admin will review join requests.",
secondDeviceHint: "Already a member on another device? Use “Link device” instead — the bot can’t add a second key to the same account.", secondDeviceHint: "已在其他裝置上加入?使用「連結此裝置」直接交接,或用此裝置再次申請加入。",
create: "建立社群房間", create: "建立社群房間",
setupDeviceBody: "這會為此裝置提供社群房間的金鑰,管理員或機器人會核准一次。", setupDeviceBody: "這會為此裝置提供社群房間的金鑰,管理員或機器人會核准一次。",
onboardingFounder1: "建立社群房間。", onboardingFounder1: "建立社群房間。",