feat: own sync server

This commit is contained in:
zhom committed 2026-10-13 12:59:44 +04:00
1 parent d6c21de169
commit 8f64745b1a
42 files changed
+1965 -136

No files matched your search

+244
View File
@@ -0,0 +1,244 @@
"use client";
import { invoke } from "@tauri-apps/api/core";
import { useCallback, useEffect, useState } from "react";
import { useTranslation } from "react-i18next";
import { LuServer } from "react-icons/lu";
import { LoadingButton } from "@/components/loading-button";
import { Button } from "@/components/ui/button";
import { SoftFields } from "@/components/ui/field-variant";
import { Input } from "@/components/ui/input";
import { Label } from "@/components/ui/label";
import {
FeatureIcon,
SettingsPanel,
StatusLight,
} from "@/components/ui/settings-panel";
import { translateBackendError } from "@/lib/backend-errors";
import { showErrorToast, showSuccessToast } from "@/lib/toast-utils";
import type { SyncServerCheck } from "@/types";
type Probe =
| { state: "unknown" | "testing" | "unreachable" | "no-accounts" }
| { state: "ready" }
| { state: "mismatch"; address: string };
/** A quick check before saving. The backend compares the exact form. */
function sameAddress(a: string, b: string): boolean {
const trim = (value: string) =>
value.trim().replace(/\/+$/, "").toLowerCase();
return trim(a) === trim(b);
}
/**
* A signed-in paid account's own donut-sync server. The app signs in to it
* with a token the Donut API binds to its address, so the plan's limit and
* the team's rules apply there as on Donut cloud.
*/
export function AccountSyncServer({
paid,
teamSeat,
}: {
paid: boolean;
teamSeat: boolean;
}) {
const { t } = useTranslation();
const [current, setCurrent] = useState<string | null>(null);
const [serverUrl, setServerUrl] = useState("");
const [probe, setProbe] = useState<Probe>({ state: "unknown" });
const [isSaving, setIsSaving] = useState(false);
useEffect(() => {
void invoke<string | null>("get_account_sync_server")
.then((server) => {
setCurrent(server);
setServerUrl(server ?? "");
})
.catch((error: unknown) => {
console.error("Failed to load the account sync server:", error);
});
}, []);
const restartSync = useCallback(async () => {
try {
await invoke("restart_sync_service");
} catch (e) {
console.error("Failed to restart sync service:", e);
}
}, []);
const handleTest = useCallback(async () => {
setProbe({ state: "testing" });
try {
const result = await invoke<SyncServerCheck>(
"check_sync_server_connection",
{ serverUrl },
);
if (
!result.server_reachable ||
result.storage_ready === false ||
result.storage_reachable === false
) {
setProbe({ state: "unreachable" });
} else if (!result.account_url) {
setProbe({ state: "no-accounts" });
} else if (!sameAddress(result.account_url, serverUrl)) {
setProbe({ state: "mismatch", address: result.account_url });
} else {
setProbe({ state: "ready" });
}
} catch {
setProbe({ state: "unreachable" });
}
}, [serverUrl]);
const save = useCallback(
async (next: string | null) => {
setIsSaving(true);
try {
const saved = await invoke<string | null>("set_account_sync_server", {
serverUrl: next,
});
setCurrent(saved);
setServerUrl(saved ?? "");
setProbe({ state: saved ? "ready" : "unknown" });
await restartSync();
showSuccessToast(
saved
? t("account.ownServer.saved")
: t("account.ownServer.backToCloud"),
);
} catch (error) {
showErrorToast(translateBackendError(t as never, error));
} finally {
setIsSaving(false);
}
},
[restartSync, t],
);
const probeTone = {
unknown: "muted",
testing: "muted",
ready: "success",
unreachable: "destructive",
"no-accounts": "destructive",
mismatch: "destructive",
} as const;
const probeLabel =
probe.state === "mismatch"
? t("backendErrors.syncServerAddressMismatch", { address: probe.address })
: {
unknown: t("account.selfHosted.statusUnknown"),
testing: t("appFeedback.checking"),
ready: t("account.ownServer.accountsOn"),
unreachable: t("backendErrors.syncServerUnreachable"),
"no-accounts": t("backendErrors.syncServerNoAccounts"),
}[probe.state];
return (
<SettingsPanel className="flex flex-col gap-4">
<div className="flex items-start gap-3">
<FeatureIcon active={current !== null}>
<LuServer />
</FeatureIcon>
<div className="min-w-0 flex-1">
<p className="text-sm font-medium">{t("account.ownServer.title")}</p>
<p className="mt-0.5 text-xs leading-relaxed text-muted-foreground">
{t("account.ownServer.description")}
</p>
<p className="mt-1.5 text-xs font-medium break-all">
{current
? t("account.ownServer.current", { server: current })
: t("account.ownServer.currentCloud")}
</p>
</div>
</div>
{paid ? (
<>
<SoftFields>
<div className="space-y-1.5">
<Label
htmlFor="account-sync-server-url"
className="text-xs font-medium text-muted-foreground"
>
{t("sync.serverUrl")}
</Label>
<Input
id="account-sync-server-url"
type="url"
placeholder={t("sync.serverUrlPlaceholder")}
value={serverUrl}
onChange={(e) => {
setServerUrl(e.target.value);
setProbe({ state: "unknown" });
}}
autoComplete="off"
spellCheck={false}
/>
</div>
</SoftFields>
<ul className="flex flex-col gap-1 text-xs text-muted-foreground">
<li>{t("account.ownServer.cloudOnly")}</li>
{teamSeat && <li>{t("account.ownServer.teamNote")}</li>}
</ul>
<div className="flex flex-wrap items-center gap-2">
<span
role="status"
className="mr-auto inline-flex min-w-0 items-center gap-2 text-xs text-muted-foreground"
>
<StatusLight
tone={probeTone[probe.state]}
live={probe.state === "testing" || probe.state === "ready"}
/>
<span className="min-w-0 font-medium text-foreground">
{probeLabel}
</span>
</span>
{current && (
<Button
size="sm"
variant="subtle"
disabled={isSaving}
onClick={() => void save(null)}
className="h-8 rounded-lg text-xs"
>
{t("account.ownServer.useDonutCloud")}
</Button>
)}
<LoadingButton
size="sm"
variant="soft"
isLoading={probe.state === "testing"}
disabled={!serverUrl.trim() || isSaving}
onClick={() => void handleTest()}
className="h-8 rounded-lg text-xs"
>
{t("account.selfHosted.testConnection")}
</LoadingButton>
<LoadingButton
size="sm"
isLoading={isSaving}
disabled={
!serverUrl.trim() ||
probe.state === "testing" ||
(current !== null && sameAddress(current, serverUrl))
}
onClick={() => void save(serverUrl)}
className="h-8 rounded-lg text-xs"
>
{t("common.buttons.save")}
</LoadingButton>
</div>
</>
) : (
<p className="text-xs text-muted-foreground">
{t("backendErrors.accountSyncPlanRequired")}
</p>
)}
</SettingsPanel>
);
}
+14
View File
@@ -76,6 +76,7 @@ export function SyncConfigDialog({
} = useCloudAuth();
const [activeTab, setActiveTab] = useState<string>("cloud");
const [accountServer, setAccountServer] = useState<string | null>(null);
const [, setLiveProxyUsage] = useState<ProxyUsage | null>(null);
const [connectionStatus, setConnectionStatus] = useState<
@@ -146,6 +147,11 @@ export function SyncConfigDialog({
if (isOpen) {
setConnectionStatus("unknown");
void loadSettings();
void invoke<string | null>("get_account_sync_server")
.then(setAccountServer)
.catch(() => {
setAccountServer(null);
});
void invoke<ProxyUsage | null>("cloud_get_proxy_usage")
.then(setLiveProxyUsage)
.catch(() => {
@@ -309,6 +315,14 @@ export function SyncConfigDialog({
{user.planPeriod ? ` (${user.planPeriod})` : ""}
</span>
</div>
<div className="flex justify-between gap-4">
<span className="text-muted-foreground">
{t("sync.cloud.server")}
</span>
<span className="truncate">
{accountServer ?? t("account.ownServer.donutCloud")}
</span>
</div>
<div className="flex justify-between">
<span className="text-muted-foreground">
{t("sync.cloud.profiles")}
+26 -4
View File
@@ -825,7 +825,8 @@
"logout": "Abmelden",
"logoutConfirm": "Möchten Sie sich wirklich abmelden? Die Cloud-Synchronisierung wird beendet.",
"loginSuccess": "Erfolgreich angemeldet!",
"logoutSuccess": "Erfolgreich abgemeldet."
"logoutSuccess": "Erfolgreich abgemeldet.",
"server": "Server"
},
"team": {
"title": "Team",
@@ -2664,7 +2665,16 @@
"snapshotNotFound": "Diesen Wiederherstellungspunkt gibt es nicht mehr.",
"restorePointsUnsupported": "Der Sync-Server ist zu alt für Wiederherstellungspunkte. Aktualisiere ihn.",
"restorePointFailed": "Der Wiederherstellungspunkt ist fehlgeschlagen: {{detail}}",
"restorePointsNeedSync": "Wiederherstellungspunkte brauchen eingeschaltete Synchronisierung für dieses Profil."
"restorePointsNeedSync": "Wiederherstellungspunkte brauchen eingeschaltete Synchronisierung für dieses Profil.",
"accountSyncSignInRequired": "Melden Sie sich bei Ihrem Donut-Konto an, um einen eigenen Sync-Server zu verwenden.",
"accountSyncPlanRequired": "Ein eigener Sync-Server erfordert einen kostenpflichtigen Plan.",
"syncServerUrlInvalid": "Geben Sie die vollständige Serveradresse ein, zum Beispiel https://sync.example.com.",
"syncServerUnreachable": "Keine Verbindung zum Sync-Server möglich.",
"syncServerStorageUnreachable": "Der Sync-Server antwortet, aber dieser Computer kann seinen Speicher unter {{endpoint}} nicht erreichen.",
"syncServerNoAccounts": "Dieser Server akzeptiert keine Donut-Konten. Setzen Sie SYNC_PUBLIC_URL auf dem Server.",
"syncServerAddressMismatch": "Die Adresse des Servers ist {{address}}. Geben Sie diese Adresse ein.",
"remoteNeedsDonutSync": "Remote-Sitzungen und Cookie Bot erhalten Profile aus der Donut-Cloud-Synchronisierung. Dieser Computer synchronisiert mit Ihrem eigenen Server.",
"accountSyncUnavailable": "Donut kann sich noch nicht bei Ihrem eigenen Server anmelden. Versuchen Sie es später erneut."
},
"rail": {
"profiles": "Profile",
@@ -2760,7 +2770,6 @@
"selfHosted": {
"title": "Selbst gehosteter Sync-Server",
"description": "Verbinden Sie Donut mit Ihrem eigenen donut-sync-Server, um Profile, Proxys, Gruppen und Erweiterungen ohne die gehostete Cloud zu synchronisieren.",
"disabledWhileLoggedIn": "Die selbst gehostete Synchronisierung ist nicht verfügbar, solange Sie bei Ihrem Donut-Konto angemeldet sind. Melden Sie sich ab, um einen eigenen Server zu verwenden.",
"connectionStatus": "Verbindung:",
"statusUnknown": "Nicht getestet",
"testConnection": "Verbindung testen",
@@ -2769,7 +2778,20 @@
"deviceOrdinal": "{{ordinal}} von {{count}}",
"automationPrimaryOnly": "Die Browser-Automatisierung läuft nur auf Ihrem Hauptgerät (Gerät 1). Melden Sie sich dort ab, um sie hier zu nutzen.",
"automationActiveHere": "Die Browser-Automatisierung ist auf diesem Gerät aktiv.",
"viewTeamUsage": "Team-Nutzung anzeigen"
"viewTeamUsage": "Team-Nutzung anzeigen",
"ownServer": {
"title": "Eigener Sync-Server",
"description": "Synchronisieren Sie dieses Konto mit einem donut-sync-Server, den Sie selbst betreiben, statt mit der Donut-Cloud. Das Profillimit Ihres Plans sowie die Rollen und der Gruppenzugriff Ihres Teams bleiben gleich.",
"current": "Synchronisiert mit {{server}}",
"currentCloud": "Synchronisiert mit der Donut-Cloud",
"donutCloud": "Donut-Cloud",
"useDonutCloud": "Donut-Cloud verwenden",
"accountsOn": "Dieser Server akzeptiert Donut-Konten.",
"cloudOnly": "Remote-Sitzungen und Cookie Bot benötigen die Synchronisierung mit der Donut-Cloud.",
"teamNote": "Alle Mitglieder Ihres Teams müssen denselben Server verwenden.",
"saved": "Sync-Server gespeichert.",
"backToCloud": "Synchronisiert wieder mit der Donut-Cloud."
}
},
"shortcutsPage": {
"title": "Tastenkürzel",
+26 -4
View File
@@ -825,7 +825,8 @@
"logout": "Log Out",
"logoutConfirm": "Are you sure you want to log out? Cloud sync will stop.",
"loginSuccess": "Successfully logged in!",
"logoutSuccess": "Successfully logged out."
"logoutSuccess": "Successfully logged out.",
"server": "Server"
},
"team": {
"title": "Team",
@@ -2664,7 +2665,16 @@
"snapshotNotFound": "This restore point no longer exists.",
"restorePointsUnsupported": "The sync server is too old for restore points. Update it.",
"restorePointFailed": "The restore point failed: {{detail}}",
"restorePointsNeedSync": "Restore points need sync on for this profile."
"restorePointsNeedSync": "Restore points need sync on for this profile.",
"accountSyncSignInRequired": "Sign in to your Donut account to use your own sync server.",
"accountSyncPlanRequired": "Your own sync server needs a paid plan.",
"syncServerUrlInvalid": "Enter the full server address, for example https://sync.example.com.",
"syncServerUnreachable": "Could not connect to the sync server.",
"syncServerStorageUnreachable": "The sync server answers, but this computer cannot connect to its storage at {{endpoint}}.",
"syncServerNoAccounts": "This server does not accept Donut accounts. Set SYNC_PUBLIC_URL on the server.",
"syncServerAddressMismatch": "The server's address is {{address}}. Enter that address.",
"remoteNeedsDonutSync": "Remote sessions and Cookie Bot get profiles from Donut cloud sync. This computer syncs with your own server.",
"accountSyncUnavailable": "Donut cannot sign in to your own server yet. Try again later."
},
"rail": {
"profiles": "Profiles",
@@ -2760,7 +2770,6 @@
"selfHosted": {
"title": "Self-hosted sync server",
"description": "Point Donut at your own donut-sync server to sync profiles, proxies, groups, and extensions without using the hosted cloud.",
"disabledWhileLoggedIn": "Self-hosted sync is unavailable while you're signed into your Donut account. Sign out to use a custom server.",
"connectionStatus": "Connection:",
"statusUnknown": "Untested",
"testConnection": "Test connection",
@@ -2769,7 +2778,20 @@
"deviceOrdinal": "{{ordinal}} of {{count}}",
"automationPrimaryOnly": "Browser automation runs only on your primary device (Device 1). Sign out there to use it here.",
"automationActiveHere": "Browser automation is active on this device.",
"viewTeamUsage": "View team usage"
"viewTeamUsage": "View team usage",
"ownServer": {
"title": "Your own sync server",
"description": "Sync this account with a donut-sync server that you run, not with Donut cloud. Your plan's profile limit and your team's roles and group access stay the same.",
"current": "Syncing with {{server}}",
"currentCloud": "Syncing with Donut cloud",
"donutCloud": "Donut cloud",
"useDonutCloud": "Use Donut cloud",
"accountsOn": "This server accepts Donut accounts.",
"cloudOnly": "Remote sessions and Cookie Bot need Donut cloud sync.",
"teamNote": "All members of your team must use the same server.",
"saved": "Sync server saved.",
"backToCloud": "Syncing with Donut cloud again."
}
},
"shortcutsPage": {
"title": "Keyboard shortcuts",
+26 -4
View File
@@ -830,7 +830,8 @@
"logout": "Cerrar Sesión",
"logoutConfirm": "¿Estás seguro de que deseas cerrar sesión? La sincronización en la nube se detendrá.",
"loginSuccess": "¡Sesión iniciada exitosamente!",
"logoutSuccess": "Sesión cerrada exitosamente."
"logoutSuccess": "Sesión cerrada exitosamente.",
"server": "Servidor"
},
"team": {
"title": "Equipo",
@@ -2684,7 +2685,16 @@
"snapshotNotFound": "Este punto de restauración ya no existe.",
"restorePointsUnsupported": "El servidor de sincronización es demasiado antiguo para los puntos de restauración. Actualízalo.",
"restorePointFailed": "El punto de restauración falló: {{detail}}",
"restorePointsNeedSync": "Los puntos de restauración necesitan la sincronización activada en este perfil."
"restorePointsNeedSync": "Los puntos de restauración necesitan la sincronización activada en este perfil.",
"accountSyncSignInRequired": "Inicia sesión en tu cuenta de Donut para usar tu propio servidor de sincronización.",
"accountSyncPlanRequired": "Tu propio servidor de sincronización requiere un plan de pago.",
"syncServerUrlInvalid": "Introduce la dirección completa del servidor, por ejemplo https://sync.example.com.",
"syncServerUnreachable": "No se pudo conectar con el servidor de sincronización.",
"syncServerStorageUnreachable": "El servidor de sincronización responde, pero este equipo no puede conectarse a su almacenamiento en {{endpoint}}.",
"syncServerNoAccounts": "Este servidor no acepta cuentas de Donut. Define SYNC_PUBLIC_URL en el servidor.",
"syncServerAddressMismatch": "La dirección del servidor es {{address}}. Introduce esa dirección.",
"remoteNeedsDonutSync": "Las sesiones remotas y Cookie Bot obtienen los perfiles de la sincronización con Donut cloud. Este equipo se sincroniza con tu propio servidor.",
"accountSyncUnavailable": "Donut todavía no puede iniciar sesión en tu propio servidor. Inténtalo más tarde."
},
"rail": {
"profiles": "Perfiles",
@@ -2780,7 +2790,6 @@
"selfHosted": {
"title": "Servidor de sincronización autoalojado",
"description": "Conecta Donut a tu propio servidor donut-sync para sincronizar perfiles, proxies, grupos y extensiones sin usar la nube alojada.",
"disabledWhileLoggedIn": "La sincronización autoalojada no está disponible mientras estás conectado a tu cuenta de Donut. Cierra sesión para usar un servidor personalizado.",
"connectionStatus": "Conexión:",
"statusUnknown": "Sin probar",
"testConnection": "Probar conexión",
@@ -2789,7 +2798,20 @@
"deviceOrdinal": "{{ordinal}} de {{count}}",
"automationPrimaryOnly": "La automatización del navegador solo funciona en tu dispositivo principal (Dispositivo 1). Cierra sesión allí para usarla aquí.",
"automationActiveHere": "La automatización del navegador está activa en este dispositivo.",
"viewTeamUsage": "Ver el uso del equipo"
"viewTeamUsage": "Ver el uso del equipo",
"ownServer": {
"title": "Tu propio servidor de sincronización",
"description": "Sincroniza esta cuenta con un servidor donut-sync que gestionas tú, no con Donut cloud. El límite de perfiles de tu plan y los roles y el acceso por grupos de tu equipo no cambian.",
"current": "Sincronizando con {{server}}",
"currentCloud": "Sincronizando con Donut cloud",
"donutCloud": "Donut cloud",
"useDonutCloud": "Usar Donut cloud",
"accountsOn": "Este servidor acepta cuentas de Donut.",
"cloudOnly": "Las sesiones remotas y Cookie Bot necesitan la sincronización con Donut cloud.",
"teamNote": "Todos los miembros de tu equipo deben usar el mismo servidor.",
"saved": "Servidor de sincronización guardado.",
"backToCloud": "Sincronizando de nuevo con Donut cloud."
}
},
"shortcutsPage": {
"title": "Atajos de teclado",
+26 -4
View File
@@ -830,7 +830,8 @@
"logout": "Se Déconnecter",
"logoutConfirm": "Êtes-vous sûr de vouloir vous déconnecter ? La synchronisation cloud sera arrêtée.",
"loginSuccess": "Connexion réussie !",
"logoutSuccess": "Déconnexion réussie."
"logoutSuccess": "Déconnexion réussie.",
"server": "Serveur"
},
"team": {
"title": "Équipe",
@@ -2684,7 +2685,16 @@
"snapshotNotFound": "Ce point de restauration n’existe plus.",
"restorePointsUnsupported": "Le serveur de synchronisation est trop ancien pour les points de restauration. Mettez-le à jour.",
"restorePointFailed": "Le point de restauration a échoué : {{detail}}",
"restorePointsNeedSync": "Les points de restauration nécessitent la synchronisation activée pour ce profil."
"restorePointsNeedSync": "Les points de restauration nécessitent la synchronisation activée pour ce profil.",
"accountSyncSignInRequired": "Connectez-vous à votre compte Donut pour utiliser votre propre serveur de synchronisation.",
"accountSyncPlanRequired": "Votre propre serveur de synchronisation nécessite une offre payante.",
"syncServerUrlInvalid": "Saisissez l'adresse complète du serveur, par exemple https://sync.example.com.",
"syncServerUnreachable": "Impossible de se connecter au serveur de synchronisation.",
"syncServerStorageUnreachable": "Le serveur de synchronisation répond, mais cet ordinateur ne peut pas joindre son stockage à {{endpoint}}.",
"syncServerNoAccounts": "Ce serveur n'accepte pas les comptes Donut. Définissez SYNC_PUBLIC_URL sur le serveur.",
"syncServerAddressMismatch": "L'adresse du serveur est {{address}}. Saisissez cette adresse.",
"remoteNeedsDonutSync": "Les sessions à distance et Cookie Bot obtiennent les profils depuis la synchronisation Donut cloud. Cet ordinateur se synchronise avec votre propre serveur.",
"accountSyncUnavailable": "Donut ne peut pas encore se connecter à votre propre serveur. Réessayez plus tard."
},
"rail": {
"profiles": "Profils",
@@ -2780,7 +2790,6 @@
"selfHosted": {
"title": "Serveur de synchronisation auto-hébergé",
"description": "Connectez Donut à votre propre serveur donut-sync pour synchroniser profils, proxys, groupes et extensions sans utiliser le cloud hébergé.",
"disabledWhileLoggedIn": "La synchronisation auto-hébergée n'est pas disponible lorsque vous êtes connecté à votre compte Donut. Déconnectez-vous pour utiliser un serveur personnalisé.",
"connectionStatus": "Connexion :",
"statusUnknown": "Non testé",
"testConnection": "Tester la connexion",
@@ -2789,7 +2798,20 @@
"deviceOrdinal": "{{ordinal}} sur {{count}}",
"automationPrimaryOnly": "L'automatisation du navigateur ne fonctionne que sur votre appareil principal (Appareil 1). Déconnectez-vous là-bas pour l'utiliser ici.",
"automationActiveHere": "L'automatisation du navigateur est active sur cet appareil.",
"viewTeamUsage": "Voir l'utilisation de l'équipe"
"viewTeamUsage": "Voir l'utilisation de l'équipe",
"ownServer": {
"title": "Votre propre serveur de synchronisation",
"description": "Synchronisez ce compte avec un serveur donut-sync que vous gérez, et non avec Donut cloud. La limite de profils de votre offre ainsi que les rôles et l'accès par groupe de votre équipe restent les mêmes.",
"current": "Synchronisation avec {{server}}",
"currentCloud": "Synchronisation avec Donut cloud",
"donutCloud": "Donut cloud",
"useDonutCloud": "Utiliser Donut cloud",
"accountsOn": "Ce serveur accepte les comptes Donut.",
"cloudOnly": "Les sessions à distance et Cookie Bot nécessitent la synchronisation avec Donut cloud.",
"teamNote": "Tous les membres de votre équipe doivent utiliser le même serveur.",
"saved": "Serveur de synchronisation enregistré.",
"backToCloud": "Synchronisation de nouveau avec Donut cloud."
}
},
"shortcutsPage": {
"title": "Raccourcis clavier",
+26 -4
View File
@@ -825,7 +825,8 @@
"logout": "ログアウト",
"logoutConfirm": "ログアウトしてもよろしいですか?クラウド同期が停止します。",
"loginSuccess": "ログインに成功しました!",
"logoutSuccess": "ログアウトしました。"
"logoutSuccess": "ログアウトしました。",
"server": "サーバー"
},
"team": {
"title": "チーム",
@@ -2664,7 +2665,16 @@
"snapshotNotFound": "この復元ポイントはもう存在しません。",
"restorePointsUnsupported": "同期サーバーが古いため、復元ポイントを使えません。更新してください。",
"restorePointFailed": "復元ポイントの処理に失敗しました: {{detail}}",
"restorePointsNeedSync": "復元ポイントには、このプロファイルの同期をオンにする必要があります。"
"restorePointsNeedSync": "復元ポイントには、このプロファイルの同期をオンにする必要があります。",
"accountSyncSignInRequired": "独自の同期サーバーを使用するには、Donut アカウントにサインインしてください。",
"accountSyncPlanRequired": "独自の同期サーバーには有料プランが必要です。",
"syncServerUrlInvalid": "サーバーの完全なアドレスを入力してください(例: https://sync.example.com)。",
"syncServerUnreachable": "同期サーバーに接続できませんでした。",
"syncServerStorageUnreachable": "同期サーバーは応答していますが、このコンピューターは {{endpoint}} のストレージに接続できません。",
"syncServerNoAccounts": "このサーバーは Donut アカウントを受け付けません。サーバーで SYNC_PUBLIC_URL を設定してください。",
"syncServerAddressMismatch": "サーバーのアドレスは {{address}} です。そのアドレスを入力してください。",
"remoteNeedsDonutSync": "リモートセッションと Cookie Bot は Donut クラウド同期からプロファイルを取得します。このコンピューターは独自のサーバーと同期しています。",
"accountSyncUnavailable": "Donut はまだ独自のサーバーにサインインできません。後でもう一度お試しください。"
},
"rail": {
"profiles": "プロファイル",
@@ -2760,7 +2770,6 @@
"selfHosted": {
"title": "セルフホスト同期サーバー",
"description": "Donut を独自の donut-sync サーバーに接続して、ホスト型クラウドを使わずにプロファイル、プロキシ、グループ、拡張機能を同期します。",
"disabledWhileLoggedIn": "Donut アカウントにサインインしている間はセルフホスト同期を利用できません。カスタムサーバーを使うにはサインアウトしてください。",
"connectionStatus": "接続:",
"statusUnknown": "未テスト",
"testConnection": "接続をテスト",
@@ -2769,7 +2778,20 @@
"deviceOrdinal": "{{count}} 台中 {{ordinal}} 台目",
"automationPrimaryOnly": "ブラウザの自動化はプライマリデバイス(デバイス1)でのみ実行できます。ここで使用するには、そのデバイスでサインアウトしてください。",
"automationActiveHere": "ブラウザの自動化はこのデバイスで有効です。",
"viewTeamUsage": "チームの使用状況を表示"
"viewTeamUsage": "チームの使用状況を表示",
"ownServer": {
"title": "独自の同期サーバー",
"description": "このアカウントを Donut クラウドではなく、自分で運用する donut-sync サーバーと同期します。プランのプロファイル上限と、チームのロールおよびグループアクセスは変わりません。",
"current": "{{server}} と同期中",
"currentCloud": "Donut クラウドと同期中",
"donutCloud": "Donut クラウド",
"useDonutCloud": "Donut クラウドを使用",
"accountsOn": "このサーバーは Donut アカウントを受け付けます。",
"cloudOnly": "リモートセッションと Cookie Bot には Donut クラウド同期が必要です。",
"teamNote": "チームの全メンバーが同じサーバーを使用する必要があります。",
"saved": "同期サーバーを保存しました。",
"backToCloud": "再び Donut クラウドと同期しています。"
}
},
"shortcutsPage": {
"title": "キーボードショートカット",
+26 -4
View File
@@ -825,7 +825,8 @@
"logout": "로그아웃",
"logoutConfirm": "정말 로그아웃하시겠습니까? 클라우드 동기화가 중지됩니다.",
"loginSuccess": "로그인 성공!",
"logoutSuccess": "로그아웃되었습니다."
"logoutSuccess": "로그아웃되었습니다.",
"server": "서버"
},
"team": {
"title": "팀",
@@ -2664,7 +2665,16 @@
"snapshotNotFound": "이 복원 지점은 더 이상 없습니다.",
"restorePointsUnsupported": "동기화 서버가 너무 오래되어 복원 지점을 쓸 수 없습니다. 업데이트하세요.",
"restorePointFailed": "복원 지점 작업이 실패했습니다: {{detail}}",
"restorePointsNeedSync": "복원 지점을 쓰려면 이 프로필의 동기화를 켜야 합니다."
"restorePointsNeedSync": "복원 지점을 쓰려면 이 프로필의 동기화를 켜야 합니다.",
"accountSyncSignInRequired": "자체 동기화 서버를 사용하려면 Donut 계정에 로그인하세요.",
"accountSyncPlanRequired": "자체 동기화 서버에는 유료 플랜이 필요합니다.",
"syncServerUrlInvalid": "서버의 전체 주소를 입력하세요. 예: https://sync.example.com",
"syncServerUnreachable": "동기화 서버에 연결할 수 없습니다.",
"syncServerStorageUnreachable": "동기화 서버는 응답하지만 이 컴퓨터가 {{endpoint}}의 저장소에 연결할 수 없습니다.",
"syncServerNoAccounts": "이 서버는 Donut 계정을 허용하지 않습니다. 서버에서 SYNC_PUBLIC_URL을 설정하세요.",
"syncServerAddressMismatch": "서버 주소는 {{address}}입니다. 그 주소를 입력하세요.",
"remoteNeedsDonutSync": "원격 세션과 Cookie Bot은 Donut 클라우드 동기화에서 프로필을 가져옵니다. 이 컴퓨터는 자체 서버와 동기화합니다.",
"accountSyncUnavailable": "Donut이 아직 자체 서버에 로그인할 수 없습니다. 나중에 다시 시도하세요."
},
"rail": {
"profiles": "프로필",
@@ -2760,7 +2770,6 @@
"selfHosted": {
"title": "자체 호스팅 동기화 서버",
"description": "호스팅 클라우드를 사용하지 않고 프로필, 프록시, 그룹 및 확장 프로그램을 동기화하려면 Donut을 자체 donut-sync 서버로 연결하세요.",
"disabledWhileLoggedIn": "Donut 계정에 로그인되어 있는 동안에는 자체 호스팅 동기화를 사용할 수 없습니다. 사용자 지정 서버를 사용하려면 로그아웃하세요.",
"connectionStatus": "연결:",
"statusUnknown": "테스트 안 됨",
"testConnection": "연결 테스트",
@@ -2769,7 +2778,20 @@
"deviceOrdinal": "{{count}}대 중 {{ordinal}}번째",
"automationPrimaryOnly": "브라우저 자동화는 기본 기기(기기 1)에서만 실행됩니다. 여기서 사용하려면 해당 기기에서 로그아웃하세요.",
"automationActiveHere": "이 기기에서 브라우저 자동화가 활성화되어 있습니다.",
"viewTeamUsage": "팀 사용량 보기"
"viewTeamUsage": "팀 사용량 보기",
"ownServer": {
"title": "자체 동기화 서버",
"description": "이 계정을 Donut 클라우드가 아니라 직접 운영하는 donut-sync 서버와 동기화합니다. 플랜의 프로필 한도와 팀의 역할 및 그룹 접근 권한은 그대로 유지됩니다.",
"current": "{{server}}와(과) 동기화 중",
"currentCloud": "Donut 클라우드와 동기화 중",
"donutCloud": "Donut 클라우드",
"useDonutCloud": "Donut 클라우드 사용",
"accountsOn": "이 서버는 Donut 계정을 허용합니다.",
"cloudOnly": "원격 세션과 Cookie Bot에는 Donut 클라우드 동기화가 필요합니다.",
"teamNote": "팀의 모든 구성원이 같은 서버를 사용해야 합니다.",
"saved": "동기화 서버를 저장했습니다.",
"backToCloud": "다시 Donut 클라우드와 동기화합니다."
}
},
"shortcutsPage": {
"title": "키보드 단축키",
+26 -4
View File
@@ -830,7 +830,8 @@
"logout": "Sair",
"logoutConfirm": "Tem certeza de que deseja sair? A sincronização na nuvem será interrompida.",
"loginSuccess": "Login realizado com sucesso!",
"logoutSuccess": "Logout realizado com sucesso."
"logoutSuccess": "Logout realizado com sucesso.",
"server": "Servidor"
},
"team": {
"title": "Equipe",
@@ -2684,7 +2685,16 @@
"snapshotNotFound": "Este ponto de restauração não existe mais.",
"restorePointsUnsupported": "O servidor de sincronização é antigo demais para pontos de restauração. Atualize-o.",
"restorePointFailed": "O ponto de restauração falhou: {{detail}}",
"restorePointsNeedSync": "Pontos de restauração precisam da sincronização ativada neste perfil."
"restorePointsNeedSync": "Pontos de restauração precisam da sincronização ativada neste perfil.",
"accountSyncSignInRequired": "Entre na sua conta Donut para usar o seu próprio servidor de sincronização.",
"accountSyncPlanRequired": "O seu próprio servidor de sincronização requer um plano pago.",
"syncServerUrlInvalid": "Digite o endereço completo do servidor, por exemplo https://sync.example.com.",
"syncServerUnreachable": "Não foi possível conectar ao servidor de sincronização.",
"syncServerStorageUnreachable": "O servidor de sincronização responde, mas este computador não consegue conectar ao armazenamento em {{endpoint}}.",
"syncServerNoAccounts": "Este servidor não aceita contas Donut. Defina SYNC_PUBLIC_URL no servidor.",
"syncServerAddressMismatch": "O endereço do servidor é {{address}}. Digite esse endereço.",
"remoteNeedsDonutSync": "Sessões remotas e o Cookie Bot obtêm os perfis da sincronização com a Donut cloud. Este computador sincroniza com o seu próprio servidor.",
"accountSyncUnavailable": "O Donut ainda não consegue entrar no seu próprio servidor. Tente novamente mais tarde."
},
"rail": {
"profiles": "Perfis",
@@ -2780,7 +2790,6 @@
"selfHosted": {
"title": "Servidor de sincronização auto-hospedado",
"description": "Conecte o Donut ao seu próprio servidor donut-sync para sincronizar perfis, proxies, grupos e extensões sem usar a nuvem hospedada.",
"disabledWhileLoggedIn": "A sincronização auto-hospedada não está disponível enquanto você está conectado à sua conta Donut. Saia para usar um servidor personalizado.",
"connectionStatus": "Conexão:",
"statusUnknown": "Não testado",
"testConnection": "Testar conexão",
@@ -2789,7 +2798,20 @@
"deviceOrdinal": "{{ordinal}} de {{count}}",
"automationPrimaryOnly": "A automação do navegador funciona apenas no seu dispositivo principal (Dispositivo 1). Saia da conta nele para usá-la aqui.",
"automationActiveHere": "A automação do navegador está ativa neste dispositivo.",
"viewTeamUsage": "Ver o uso da equipe"
"viewTeamUsage": "Ver o uso da equipe",
"ownServer": {
"title": "O seu próprio servidor de sincronização",
"description": "Sincronize esta conta com um servidor donut-sync que você opera, e não com a Donut cloud. O limite de perfis do seu plano e as funções e o acesso por grupo da sua equipe continuam iguais.",
"current": "Sincronizando com {{server}}",
"currentCloud": "Sincronizando com a Donut cloud",
"donutCloud": "Donut cloud",
"useDonutCloud": "Usar a Donut cloud",
"accountsOn": "Este servidor aceita contas Donut.",
"cloudOnly": "Sessões remotas e o Cookie Bot precisam da sincronização com a Donut cloud.",
"teamNote": "Todos os membros da sua equipe devem usar o mesmo servidor.",
"saved": "Servidor de sincronização salvo.",
"backToCloud": "Sincronizando com a Donut cloud novamente."
}
},
"shortcutsPage": {
"title": "Atalhos de teclado",
+26 -4
View File
@@ -835,7 +835,8 @@
"logout": "Выйти",
"logoutConfirm": "Вы уверены, что хотите выйти? Облачная синхронизация будет остановлена.",
"loginSuccess": "Вход выполнен успешно!",
"logoutSuccess": "Выход выполнен успешно."
"logoutSuccess": "Выход выполнен успешно.",
"server": "Сервер"
},
"team": {
"title": "Команда",
@@ -2704,7 +2705,16 @@
"snapshotNotFound": "Этой точки восстановления больше нет.",
"restorePointsUnsupported": "Сервер синхронизации слишком старый для точек восстановления. Обновите его.",
"restorePointFailed": "Ошибка точки восстановления: {{detail}}",
"restorePointsNeedSync": "Для точек восстановления нужна включённая синхронизация этого профиля."
"restorePointsNeedSync": "Для точек восстановления нужна включённая синхронизация этого профиля.",
"accountSyncSignInRequired": "Войдите в аккаунт Donut, чтобы использовать свой сервер синхронизации.",
"accountSyncPlanRequired": "Для своего сервера синхронизации нужен платный тариф.",
"syncServerUrlInvalid": "Введите полный адрес сервера, например https://sync.example.com.",
"syncServerUnreachable": "Не удалось подключиться к серверу синхронизации.",
"syncServerStorageUnreachable": "Сервер синхронизации отвечает, но этот компьютер не может подключиться к его хранилищу по адресу {{endpoint}}.",
"syncServerNoAccounts": "Этот сервер не принимает аккаунты Donut. Задайте SYNC_PUBLIC_URL на сервере.",
"syncServerAddressMismatch": "Адрес сервера — {{address}}. Введите этот адрес.",
"remoteNeedsDonutSync": "Удалённые сеансы и Cookie Bot получают профили из синхронизации Donut cloud. Этот компьютер синхронизируется с вашим сервером.",
"accountSyncUnavailable": "Donut пока не может войти на ваш сервер. Попробуйте позже."
},
"rail": {
"profiles": "Профили",
@@ -2800,7 +2810,6 @@
"selfHosted": {
"title": "Свой сервер синхронизации",
"description": "Подключите Donut к собственному серверу donut-sync, чтобы синхронизировать профили, прокси, группы и расширения без использования облака.",
"disabledWhileLoggedIn": "Свой сервер синхронизации недоступен, пока вы вошли в аккаунт Donut. Выйдите из аккаунта, чтобы использовать собственный сервер.",
"connectionStatus": "Соединение:",
"statusUnknown": "Не проверено",
"testConnection": "Проверить соединение",
@@ -2809,7 +2818,20 @@
"deviceOrdinal": "{{ordinal}} из {{count}}",
"automationPrimaryOnly": "Автоматизация браузера работает только на вашем основном устройстве (Устройство 1). Выйдите из аккаунта на нём, чтобы использовать её здесь.",
"automationActiveHere": "Автоматизация браузера активна на этом устройстве.",
"viewTeamUsage": "Посмотреть использование командой"
"viewTeamUsage": "Посмотреть использование командой",
"ownServer": {
"title": "Свой сервер синхронизации для аккаунта",
"description": "Синхронизируйте этот аккаунт со своим сервером donut-sync, а не с Donut cloud. Лимит профилей вашего тарифа, роли и доступ по группам в команде остаются прежними.",
"current": "Синхронизация с {{server}}",
"currentCloud": "Синхронизация с Donut cloud",
"donutCloud": "Donut cloud",
"useDonutCloud": "Использовать Donut cloud",
"accountsOn": "Этот сервер принимает аккаунты Donut.",
"cloudOnly": "Для удалённых сеансов и Cookie Bot нужна синхронизация с Donut cloud.",
"teamNote": "Все участники команды должны использовать один и тот же сервер.",
"saved": "Сервер синхронизации сохранён.",
"backToCloud": "Снова синхронизация с Donut cloud."
}
},
"shortcutsPage": {
"title": "Сочетания клавиш",
+26 -4
View File
@@ -825,7 +825,8 @@
"logout": "Çıkış Yap",
"logoutConfirm": "Çıkış yapmak istediğinizden emin misiniz? Bulut eşitlemesi duracak.",
"loginSuccess": "Başarıyla giriş yapıldı!",
"logoutSuccess": "Başarıyla çıkış yapıldı."
"logoutSuccess": "Başarıyla çıkış yapıldı.",
"server": "Sunucu"
},
"team": {
"title": "Ekip",
@@ -2664,7 +2665,16 @@
"snapshotNotFound": "Bu geri yükleme noktası artık yok.",
"restorePointsUnsupported": "Eşitleme sunucusu geri yükleme noktaları için çok eski. Güncelle.",
"restorePointFailed": "Geri yükleme noktası başarısız oldu: {{detail}}",
"restorePointsNeedSync": "Geri yükleme noktaları için bu profilde eşitleme açık olmalı."
"restorePointsNeedSync": "Geri yükleme noktaları için bu profilde eşitleme açık olmalı.",
"accountSyncSignInRequired": "Kendi eşitleme sunucunuzu kullanmak için Donut hesabınızla oturum açın.",
"accountSyncPlanRequired": "Kendi eşitleme sunucunuz için ücretli bir plan gerekir.",
"syncServerUrlInvalid": "Sunucunun tam adresini girin, örneğin https://sync.example.com.",
"syncServerUnreachable": "Eşitleme sunucusuna bağlanılamadı.",
"syncServerStorageUnreachable": "Eşitleme sunucusu yanıt veriyor, ancak bu bilgisayar {{endpoint}} adresindeki depolamaya bağlanamıyor.",
"syncServerNoAccounts": "Bu sunucu Donut hesaplarını kabul etmiyor. Sunucuda SYNC_PUBLIC_URL değerini ayarlayın.",
"syncServerAddressMismatch": "Sunucunun adresi {{address}}. Bu adresi girin.",
"remoteNeedsDonutSync": "Uzak oturumlar ve Cookie Bot profilleri Donut cloud eşitlemesinden alır. Bu bilgisayar kendi sunucunuzla eşitleniyor.",
"accountSyncUnavailable": "Donut henüz kendi sunucunuzda oturum açamıyor. Daha sonra tekrar deneyin."
},
"rail": {
"profiles": "Profiller",
@@ -2760,7 +2770,6 @@
"selfHosted": {
"title": "Kendi eşitleme sunucunuz",
"description": "Barındırılan bulutu kullanmadan profilleri, proxy'leri, grupları ve uzantıları eşitlemek için Donut'u kendi donut-sync sunucunuza yönlendirin.",
"disabledWhileLoggedIn": "Donut hesabınızda oturum açıkken kendi sunucunuzla eşitleme kullanılamaz. Özel bir sunucu kullanmak için çıkış yapın.",
"connectionStatus": "Bağlantı:",
"statusUnknown": "Test edilmedi",
"testConnection": "Bağlantıyı test et",
@@ -2769,7 +2778,20 @@
"deviceOrdinal": "{{ordinal}} / {{count}}",
"automationPrimaryOnly": "Tarayıcı otomasyonu yalnızca birincil cihazınızda (Cihaz 1) çalışır. Burada kullanmak için oradaki oturumu kapatın.",
"automationActiveHere": "Tarayıcı otomasyonu bu cihazda etkin.",
"viewTeamUsage": "Ekip kullanımını görüntüle"
"viewTeamUsage": "Ekip kullanımını görüntüle",
"ownServer": {
"title": "Kendi eşitleme sunucunuz (hesapla)",
"description": "Bu hesabı Donut cloud yerine kendi çalıştırdığınız bir donut-sync sunucusuyla eşitleyin. Planınızın profil sınırı ve ekibinizin rolleri ile grup erişimi aynı kalır.",
"current": "{{server}} ile eşitleniyor",
"currentCloud": "Donut cloud ile eşitleniyor",
"donutCloud": "Donut cloud",
"useDonutCloud": "Donut cloud'u kullan",
"accountsOn": "Bu sunucu Donut hesaplarını kabul ediyor.",
"cloudOnly": "Uzak oturumlar ve Cookie Bot, Donut cloud eşitlemesi gerektirir.",
"teamNote": "Ekibinizin tüm üyeleri aynı sunucuyu kullanmalıdır.",
"saved": "Eşitleme sunucusu kaydedildi.",
"backToCloud": "Yeniden Donut cloud ile eşitleniyor."
}
},
"shortcutsPage": {
"title": "Klavye kısayolları",
+26 -4
View File
@@ -825,7 +825,8 @@
"logout": "Đăng xuất",
"logoutConfirm": "Bạn có chắc muốn đăng xuất? Đồng bộ đám mây sẽ dừng lại.",
"loginSuccess": "Đăng nhập thành công!",
"logoutSuccess": "Đăng xuất thành công."
"logoutSuccess": "Đăng xuất thành công.",
"server": "Máy chủ"
},
"team": {
"title": "Nhóm",
@@ -2664,7 +2665,16 @@
"snapshotNotFound": "Điểm khôi phục này không còn nữa.",
"restorePointsUnsupported": "Máy chủ đồng bộ quá cũ để dùng điểm khôi phục. Hãy cập nhật.",
"restorePointFailed": "Điểm khôi phục thất bại: {{detail}}",
"restorePointsNeedSync": "Điểm khôi phục cần bật đồng bộ cho hồ sơ này."
"restorePointsNeedSync": "Điểm khôi phục cần bật đồng bộ cho hồ sơ này.",
"accountSyncSignInRequired": "Hãy đăng nhập tài khoản Donut để dùng máy chủ đồng bộ riêng.",
"accountSyncPlanRequired": "Máy chủ đồng bộ riêng cần gói trả phí.",
"syncServerUrlInvalid": "Nhập địa chỉ đầy đủ của máy chủ, ví dụ https://sync.example.com.",
"syncServerUnreachable": "Không kết nối được tới máy chủ đồng bộ.",
"syncServerStorageUnreachable": "Máy chủ đồng bộ có phản hồi, nhưng máy tính này không kết nối được tới bộ lưu trữ tại {{endpoint}}.",
"syncServerNoAccounts": "Máy chủ này không chấp nhận tài khoản Donut. Hãy đặt SYNC_PUBLIC_URL trên máy chủ.",
"syncServerAddressMismatch": "Địa chỉ của máy chủ là {{address}}. Hãy nhập địa chỉ đó.",
"remoteNeedsDonutSync": "Phiên từ xa và Cookie Bot lấy hồ sơ từ đồng bộ Donut cloud. Máy tính này đồng bộ với máy chủ riêng của bạn.",
"accountSyncUnavailable": "Donut chưa thể đăng nhập vào máy chủ riêng của bạn. Hãy thử lại sau."
},
"rail": {
"profiles": "Profile",
@@ -2760,7 +2770,6 @@
"selfHosted": {
"title": "Máy chủ đồng bộ tự lưu trữ",
"description": "Trỏ Donut đến máy chủ donut-sync của riêng bạn để đồng bộ profile, proxy, nhóm và tiện ích mà không cần sử dụng đám mây.",
"disabledWhileLoggedIn": "Đồng bộ tự lưu trữ không khả dụng khi bạn đã đăng nhập vào tài khoản Donut. Đăng xuất để sử dụng máy chủ tùy chỉnh.",
"connectionStatus": "Kết nối:",
"statusUnknown": "Chưa kiểm tra",
"testConnection": "Kiểm tra kết nối",
@@ -2769,7 +2778,20 @@
"deviceOrdinal": "{{ordinal}} trên {{count}}",
"automationPrimaryOnly": "Tự động hóa trình duyệt chỉ chạy trên thiết bị chính của bạn (Thiết bị 1). Đăng xuất ở đó để sử dụng tại đây.",
"automationActiveHere": "Tự động hóa trình duyệt đang hoạt động trên thiết bị này.",
"viewTeamUsage": "Xem mức dùng của nhóm"
"viewTeamUsage": "Xem mức dùng của nhóm",
"ownServer": {
"title": "Máy chủ đồng bộ riêng của bạn",
"description": "Đồng bộ tài khoản này với máy chủ donut-sync do bạn vận hành, thay vì Donut cloud. Giới hạn hồ sơ của gói và vai trò, quyền truy cập nhóm của đội bạn vẫn giữ nguyên.",
"current": "Đang đồng bộ với {{server}}",
"currentCloud": "Đang đồng bộ với Donut cloud",
"donutCloud": "Donut cloud",
"useDonutCloud": "Dùng Donut cloud",
"accountsOn": "Máy chủ này chấp nhận tài khoản Donut.",
"cloudOnly": "Phiên từ xa và Cookie Bot cần đồng bộ với Donut cloud.",
"teamNote": "Tất cả thành viên trong đội phải dùng cùng một máy chủ.",
"saved": "Đã lưu máy chủ đồng bộ.",
"backToCloud": "Đang đồng bộ lại với Donut cloud."
}
},
"shortcutsPage": {
"title": "Phím tắt",
+26 -4
View File
@@ -825,7 +825,8 @@
"logout": "退出登录",
"logoutConfirm": "您确定要退出登录吗?云同步将会停止。",
"loginSuccess": "登录成功!",
"logoutSuccess": "已成功退出登录。"
"logoutSuccess": "已成功退出登录。",
"server": "服务器"
},
"team": {
"title": "团队",
@@ -2664,7 +2665,16 @@
"snapshotNotFound": "此还原点已不存在。",
"restorePointsUnsupported": "同步服务器版本太旧,无法使用还原点。请更新它。",
"restorePointFailed": "还原点操作失败:{{detail}}",
"restorePointsNeedSync": "使用还原点需要为此配置文件开启同步。"
"restorePointsNeedSync": "使用还原点需要为此配置文件开启同步。",
"accountSyncSignInRequired": "请登录 Donut 账户以使用你自己的同步服务器。",
"accountSyncPlanRequired": "使用你自己的同步服务器需要付费套餐。",
"syncServerUrlInvalid": "请输入服务器的完整地址,例如 https://sync.example.com。",
"syncServerUnreachable": "无法连接到同步服务器。",
"syncServerStorageUnreachable": "同步服务器有响应,但此计算机无法连接到其位于 {{endpoint}} 的存储。",
"syncServerNoAccounts": "此服务器不接受 Donut 账户。请在服务器上设置 SYNC_PUBLIC_URL。",
"syncServerAddressMismatch": "服务器的地址是 {{address}}。请输入该地址。",
"remoteNeedsDonutSync": "远程会话和 Cookie Bot 从 Donut 云同步获取配置文件。此计算机正在与你自己的服务器同步。",
"accountSyncUnavailable": "Donut 暂时无法登录你自己的服务器。请稍后重试。"
},
"rail": {
"profiles": "配置文件",
@@ -2760,7 +2770,6 @@
"selfHosted": {
"title": "自托管同步服务器",
"description": "将 Donut 连接到您自己的 donut-sync 服务器,无需使用托管云即可同步配置文件、代理、组和扩展程序。",
"disabledWhileLoggedIn": "登录 Donut 账户时无法使用自托管同步。请先退出登录以使用自定义服务器。",
"connectionStatus": "连接:",
"statusUnknown": "未测试",
"testConnection": "测试连接",
@@ -2769,7 +2778,20 @@
"deviceOrdinal": "第 {{ordinal}} 台,共 {{count}} 台",
"automationPrimaryOnly": "浏览器自动化仅在您的主设备(设备 1)上运行。请在该设备上退出登录,才能在此设备上使用。",
"automationActiveHere": "浏览器自动化已在此设备上启用。",
"viewTeamUsage": "查看团队用量"
"viewTeamUsage": "查看团队用量",
"ownServer": {
"title": "你自己的同步服务器",
"description": "将此账户与你自己运行的 donut-sync 服务器同步,而不是与 Donut 云同步。你的套餐配置文件上限以及团队的角色和分组访问权限保持不变。",
"current": "正在与 {{server}} 同步",
"currentCloud": "正在与 Donut 云同步",
"donutCloud": "Donut 云",
"useDonutCloud": "使用 Donut 云",
"accountsOn": "此服务器接受 Donut 账户。",
"cloudOnly": "远程会话和 Cookie Bot 需要 Donut 云同步。",
"teamNote": "团队的所有成员必须使用同一台服务器。",
"saved": "同步服务器已保存。",
"backToCloud": "已重新与 Donut 云同步。"
}
},
"shortcutsPage": {
"title": "键盘快捷键",
+31
View File
@@ -56,6 +56,15 @@ export type BackendErrorCode =
| "COOKIE_IMPORT_REMOTE_SESSION"
| "COOKIE_IMPORT_NO_COOKIES"
| "SELF_HOSTED_REQUIRES_LOGOUT"
| "ACCOUNT_SYNC_SIGN_IN_REQUIRED"
| "ACCOUNT_SYNC_PLAN_REQUIRED"
| "ACCOUNT_SYNC_UNAVAILABLE"
| "SYNC_SERVER_URL_INVALID"
| "SYNC_SERVER_UNREACHABLE"
| "SYNC_SERVER_STORAGE_UNREACHABLE"
| "SYNC_SERVER_NO_ACCOUNTS"
| "SYNC_SERVER_ADDRESS_MISMATCH"
| "REMOTE_NEEDS_DONUT_SYNC"
| "PROXY_NOT_FOUND"
| "GROUP_NOT_FOUND"
| "GROUP_ALREADY_EXISTS"
@@ -487,6 +496,28 @@ export function translateBackendError(t: TFunction, err: unknown): string {
return t("backendErrors.cookieImportNoCookies");
case "SELF_HOSTED_REQUIRES_LOGOUT":
return t("backendErrors.selfHostedRequiresLogout");
case "ACCOUNT_SYNC_SIGN_IN_REQUIRED":
return t("backendErrors.accountSyncSignInRequired");
case "ACCOUNT_SYNC_PLAN_REQUIRED":
return t("backendErrors.accountSyncPlanRequired");
case "ACCOUNT_SYNC_UNAVAILABLE":
return t("backendErrors.accountSyncUnavailable");
case "SYNC_SERVER_URL_INVALID":
return t("backendErrors.syncServerUrlInvalid");
case "SYNC_SERVER_UNREACHABLE":
return t("backendErrors.syncServerUnreachable");
case "SYNC_SERVER_STORAGE_UNREACHABLE":
return t("backendErrors.syncServerStorageUnreachable", {
endpoint: parsed.params?.endpoint ?? "",
});
case "SYNC_SERVER_NO_ACCOUNTS":
return t("backendErrors.syncServerNoAccounts");
case "SYNC_SERVER_ADDRESS_MISMATCH":
return t("backendErrors.syncServerAddressMismatch", {
address: parsed.params?.address ?? "",
});
case "REMOTE_NEEDS_DONUT_SYNC":
return t("backendErrors.remoteNeedsDonutSync");
case "PROXY_NOT_FOUND":
return t("backendErrors.proxyNotFound");
case "GROUP_NOT_FOUND":
+2
View File
@@ -109,6 +109,8 @@ export interface SyncServerCheck {
storage_endpoint: string | null;
storage_reachable: boolean | null;
storage_error: string | null;
/** The address Donut accounts sign in to the server with, when allowed. */
account_url?: string | null;
}
/**