feat(api-tokens): add PAT management UI, CSRF handling, and auth-loading fixes

Add an API tokens management route to create, list, and revoke PATs, showing
the plaintext token once on creation with scope and server-id selection.

Mirror the nz-csrf cookie into the X-CSRF-Token header on unsafe fetcher
methods (POST/PUT/PATCH/DELETE) for the server-side double-submit check, and
self-heal expired sessions via refresh-token without a recursive fetch loop.

Gate protected routes behind resolved auth state to avoid pre-auth SWR fetches,
and fix the login loading/race so stale probes cannot clobber the session.

Add i18n keys for the new screens across all locales.

Co-authored-by: cloudcode <cloudcode@users.noreply.github.com>
This commit is contained in:
naiba
2026-05-30 15:56:53 +00:00
co-authored by cloudcode
parent f47d4a8772
commit f07c557029
37 changed files with 2041 additions and 69 deletions
+26 -1
View File
@@ -180,5 +180,30 @@
"EmptyNote": "Du hast noch keine Notizen.",
"BackToHome": "Zurück zur Startseite",
"OnAlert": "Server mit Warnung",
"EmptyText": "Text ist leer"
"EmptyText": "Text ist leer",
"EnableMCP": "Enable MCP endpoint (off by default; review API token scopes and server allow-list first)",
"ApiTokens": "API Tokens",
"CreateApiToken": "Create API token",
"CreateApiTokenDescription": "Tokens authenticate MCP and external clients on your behalf. They cannot exceed your own permissions.",
"ConfirmDeleteApiToken": "Revoke API token '{{name}}'? This cannot be undone.",
"ApiTokenRevoked": "API token revoked.",
"ApiTokenCreated": "API token created",
"ApiTokenRevealOnce": "Copy this token now. It will never be shown again.",
"ApiTokenStoreSafely": "Treat this token like a password. Anyone with it can act on your behalf within its scopes.",
"ApiTokenServers": "Servers",
"ApiTokenAllServers": "all permitted",
"ApiTokenNever": "never",
"ApiTokenExpiresAt": "Expires",
"ApiTokenLastUsed": "Last used",
"ApiTokenServerIDs": "Restrict to server IDs (optional)",
"ApiTokenServerIDsPlaceholder": "comma-separated, e.g. 1,2,3",
"ApiTokenExpiresInDays": "Expires in (days, 0 = never)",
"ApiTokenScopeRequired": "At least one scope is required.",
"ApiTokenServersInvalid": "Server IDs must be positive integers.",
"ApiTokenExpiryInvalid": "Days must be between 0 and 3650.",
"NameRequired": "Name is required.",
"Revoke": "Revoke",
"Copy": "Copy",
"Copied": "Copied to clipboard",
"Scopes": "Scopes"
}
+27 -1
View File
@@ -155,6 +155,7 @@
"UseDirectConnectingIP": "Use direct connection IP",
"IPChangeNotification": "IP Change Notification",
"FullIPNotification": "Show Full IP Address in Notification Messages",
"EnableMCP": "Enable MCP endpoint (off by default; review API token scopes and server allow-list first)",
"EditService": "Edit Service",
"CreateService": "Create Service",
"EditTask": "Edit Task",
@@ -288,5 +289,30 @@
"StatusFailed": "Failed",
"StatusTimeout": "Timeout",
"StatusCancelled": "Cancelled"
}
},
"ApiTokens": "API Tokens",
"CreateApiToken": "Create API token",
"CreateApiTokenDescription": "Tokens authenticate MCP and external clients on your behalf. They cannot exceed your own permissions.",
"ConfirmDeleteApiToken": "Revoke API token '{{name}}'? This cannot be undone.",
"ApiTokenRevoked": "API token revoked.",
"ApiTokenCreated": "API token created",
"ApiTokenRevealOnce": "Copy this token now. It will never be shown again.",
"ApiTokenStoreSafely": "Treat this token like a password. Anyone with it can act on your behalf within its scopes.",
"ApiTokenServers": "Servers",
"ApiTokenAllServers": "all permitted",
"ApiTokenNever": "never",
"ApiTokenExpiresAt": "Expires",
"ApiTokenLastUsed": "Last used",
"ApiTokenServerIDs": "Restrict to server IDs (optional)",
"ApiTokenServerIDsPlaceholder": "comma-separated, e.g. 1,2,3",
"ApiTokenExpiresInDays": "Expires in (days, 0 = never)",
"ApiTokenScopeRequired": "At least one scope is required.",
"ApiTokenServersInvalid": "Server IDs must be positive integers.",
"ApiTokenExpiryInvalid": "Days must be between 0 and 3650.",
"NameRequired": "Name is required.",
"Revoke": "Revoke",
"Copy": "Copy",
"Copied": "Copied to clipboard",
"Done": "Done",
"Scopes": "Scopes"
}
+25 -1
View File
@@ -254,5 +254,29 @@
"CopiedToClipboard": "Copiar al portapapeles",
"ClipboardWriteFailed": "Error al escribir en el portapapeles",
"PastedFromClipboard": "Pegado del portapapeles",
"ClipboardReadFailed": "Falló la lectura del portapapeles"
"ClipboardReadFailed": "Falló la lectura del portapapeles",
"EnableMCP": "Enable MCP endpoint (off by default; review API token scopes and server allow-list first)",
"ApiTokens": "API Tokens",
"CreateApiToken": "Create API token",
"CreateApiTokenDescription": "Tokens authenticate MCP and external clients on your behalf. They cannot exceed your own permissions.",
"ConfirmDeleteApiToken": "Revoke API token '{{name}}'? This cannot be undone.",
"ApiTokenRevoked": "API token revoked.",
"ApiTokenCreated": "API token created",
"ApiTokenRevealOnce": "Copy this token now. It will never be shown again.",
"ApiTokenStoreSafely": "Treat this token like a password. Anyone with it can act on your behalf within its scopes.",
"ApiTokenServers": "Servers",
"ApiTokenAllServers": "all permitted",
"ApiTokenNever": "never",
"ApiTokenExpiresAt": "Expires",
"ApiTokenLastUsed": "Last used",
"ApiTokenServerIDs": "Restrict to server IDs (optional)",
"ApiTokenServerIDsPlaceholder": "comma-separated, e.g. 1,2,3",
"ApiTokenExpiresInDays": "Expires in (days, 0 = never)",
"ApiTokenScopeRequired": "At least one scope is required.",
"ApiTokenServersInvalid": "Server IDs must be positive integers.",
"ApiTokenExpiryInvalid": "Days must be between 0 and 3650.",
"NameRequired": "Name is required.",
"Revoke": "Revoke",
"Copied": "Copied to clipboard",
"Scopes": "Scopes"
}
+26 -1
View File
@@ -92,5 +92,30 @@
"UserInvalid": "Lutilisateur est invalide",
"BlockByUser": "Bloqué par un administrateur",
"OnlineUser": "Utilisateur en ligne",
"UserId": "ID utilisateur"
"UserId": "ID utilisateur",
"EnableMCP": "Enable MCP endpoint (off by default; review API token scopes and server allow-list first)",
"ApiTokens": "API Tokens",
"CreateApiToken": "Create API token",
"CreateApiTokenDescription": "Tokens authenticate MCP and external clients on your behalf. They cannot exceed your own permissions.",
"ConfirmDeleteApiToken": "Revoke API token '{{name}}'? This cannot be undone.",
"ApiTokenRevoked": "API token revoked.",
"ApiTokenCreated": "API token created",
"ApiTokenRevealOnce": "Copy this token now. It will never be shown again.",
"ApiTokenStoreSafely": "Treat this token like a password. Anyone with it can act on your behalf within its scopes.",
"ApiTokenServers": "Servers",
"ApiTokenAllServers": "all permitted",
"ApiTokenNever": "never",
"ApiTokenExpiresAt": "Expires",
"ApiTokenLastUsed": "Last used",
"ApiTokenServerIDs": "Restrict to server IDs (optional)",
"ApiTokenServerIDsPlaceholder": "comma-separated, e.g. 1,2,3",
"ApiTokenExpiresInDays": "Expires in (days, 0 = never)",
"ApiTokenScopeRequired": "At least one scope is required.",
"ApiTokenServersInvalid": "Server IDs must be positive integers.",
"ApiTokenExpiryInvalid": "Days must be between 0 and 3650.",
"NameRequired": "Name is required.",
"Revoke": "Revoke",
"Copy": "Copy",
"Copied": "Copied to clipboard",
"Scopes": "Scopes"
}
+27 -1
View File
@@ -21,5 +21,31 @@
"NoRowsAreSelected": "Non hai filas seleccionadas",
"ThisOperationIsUnrecoverable": "A operación non se pode desfacer!",
"TaskTriggeredSuccessfully": "A tarefa desencadeouse correctamente"
}
},
"EnableMCP": "Enable MCP endpoint (off by default; review API token scopes and server allow-list first)",
"ApiTokens": "API Tokens",
"CreateApiToken": "Create API token",
"CreateApiTokenDescription": "Tokens authenticate MCP and external clients on your behalf. They cannot exceed your own permissions.",
"ConfirmDeleteApiToken": "Revoke API token '{{name}}'? This cannot be undone.",
"ApiTokenRevoked": "API token revoked.",
"ApiTokenCreated": "API token created",
"ApiTokenRevealOnce": "Copy this token now. It will never be shown again.",
"ApiTokenStoreSafely": "Treat this token like a password. Anyone with it can act on your behalf within its scopes.",
"ApiTokenServers": "Servers",
"ApiTokenAllServers": "all permitted",
"ApiTokenNever": "never",
"ApiTokenExpiresAt": "Expires",
"ApiTokenLastUsed": "Last used",
"ApiTokenServerIDs": "Restrict to server IDs (optional)",
"ApiTokenServerIDsPlaceholder": "comma-separated, e.g. 1,2,3",
"ApiTokenExpiresInDays": "Expires in (days, 0 = never)",
"ApiTokenScopeRequired": "At least one scope is required.",
"ApiTokenServersInvalid": "Server IDs must be positive integers.",
"ApiTokenExpiryInvalid": "Days must be between 0 and 3650.",
"NameRequired": "Name is required.",
"Revoke": "Revoke",
"Copy": "Copy",
"Copied": "Copied to clipboard",
"Done": "Done",
"Scopes": "Scopes"
}
+25 -1
View File
@@ -255,5 +255,29 @@
"ClipboardWriteFailed": "Gagal menulis ke papan klip",
"PastedFromClipboard": "Ditempel dari papan klip",
"ClipboardReadFailed": "Gagal membaca papan klip",
"FormatMetricUnits": "Format Satuan Metrik"
"FormatMetricUnits": "Format Satuan Metrik",
"EnableMCP": "Enable MCP endpoint (off by default; review API token scopes and server allow-list first)",
"ApiTokens": "API Tokens",
"CreateApiToken": "Create API token",
"CreateApiTokenDescription": "Tokens authenticate MCP and external clients on your behalf. They cannot exceed your own permissions.",
"ConfirmDeleteApiToken": "Revoke API token '{{name}}'? This cannot be undone.",
"ApiTokenRevoked": "API token revoked.",
"ApiTokenCreated": "API token created",
"ApiTokenRevealOnce": "Copy this token now. It will never be shown again.",
"ApiTokenStoreSafely": "Treat this token like a password. Anyone with it can act on your behalf within its scopes.",
"ApiTokenServers": "Servers",
"ApiTokenAllServers": "all permitted",
"ApiTokenNever": "never",
"ApiTokenExpiresAt": "Expires",
"ApiTokenLastUsed": "Last used",
"ApiTokenServerIDs": "Restrict to server IDs (optional)",
"ApiTokenServerIDsPlaceholder": "comma-separated, e.g. 1,2,3",
"ApiTokenExpiresInDays": "Expires in (days, 0 = never)",
"ApiTokenScopeRequired": "At least one scope is required.",
"ApiTokenServersInvalid": "Server IDs must be positive integers.",
"ApiTokenExpiryInvalid": "Days must be between 0 and 3650.",
"NameRequired": "Name is required.",
"Revoke": "Revoke",
"Copied": "Copied to clipboard",
"Scopes": "Scopes"
}
+26 -1
View File
@@ -175,5 +175,30 @@
"OnlineUser": "Utente online",
"BlockIdentifier": "Identificatore del blocco",
"UserId": "ID utente",
"ConnectedAt": "Connesso alle"
"ConnectedAt": "Connesso alle",
"EnableMCP": "Enable MCP endpoint (off by default; review API token scopes and server allow-list first)",
"ApiTokens": "API Tokens",
"CreateApiToken": "Create API token",
"CreateApiTokenDescription": "Tokens authenticate MCP and external clients on your behalf. They cannot exceed your own permissions.",
"ConfirmDeleteApiToken": "Revoke API token '{{name}}'? This cannot be undone.",
"ApiTokenRevoked": "API token revoked.",
"ApiTokenCreated": "API token created",
"ApiTokenRevealOnce": "Copy this token now. It will never be shown again.",
"ApiTokenStoreSafely": "Treat this token like a password. Anyone with it can act on your behalf within its scopes.",
"ApiTokenServers": "Servers",
"ApiTokenAllServers": "all permitted",
"ApiTokenNever": "never",
"ApiTokenExpiresAt": "Expires",
"ApiTokenLastUsed": "Last used",
"ApiTokenServerIDs": "Restrict to server IDs (optional)",
"ApiTokenServerIDsPlaceholder": "comma-separated, e.g. 1,2,3",
"ApiTokenExpiresInDays": "Expires in (days, 0 = never)",
"ApiTokenScopeRequired": "At least one scope is required.",
"ApiTokenServersInvalid": "Server IDs must be positive integers.",
"ApiTokenExpiryInvalid": "Days must be between 0 and 3650.",
"NameRequired": "Name is required.",
"Revoke": "Revoke",
"Copy": "Copy",
"Copied": "Copied to clipboard",
"Scopes": "Scopes"
}
+28 -1
View File
@@ -1 +1,28 @@
{}
{
"EnableMCP": "Enable MCP endpoint (off by default; review API token scopes and server allow-list first)",
"ApiTokens": "API Tokens",
"CreateApiToken": "Create API token",
"CreateApiTokenDescription": "Tokens authenticate MCP and external clients on your behalf. They cannot exceed your own permissions.",
"ConfirmDeleteApiToken": "Revoke API token '{{name}}'? This cannot be undone.",
"ApiTokenRevoked": "API token revoked.",
"ApiTokenCreated": "API token created",
"ApiTokenRevealOnce": "Copy this token now. It will never be shown again.",
"ApiTokenStoreSafely": "Treat this token like a password. Anyone with it can act on your behalf within its scopes.",
"ApiTokenServers": "Servers",
"ApiTokenAllServers": "all permitted",
"ApiTokenNever": "never",
"ApiTokenExpiresAt": "Expires",
"ApiTokenLastUsed": "Last used",
"ApiTokenServerIDs": "Restrict to server IDs (optional)",
"ApiTokenServerIDsPlaceholder": "comma-separated, e.g. 1,2,3",
"ApiTokenExpiresInDays": "Expires in (days, 0 = never)",
"ApiTokenScopeRequired": "At least one scope is required.",
"ApiTokenServersInvalid": "Server IDs must be positive integers.",
"ApiTokenExpiryInvalid": "Days must be between 0 and 3650.",
"NameRequired": "Name is required.",
"Revoke": "Revoke",
"Copy": "Copy",
"Copied": "Copied to clipboard",
"Done": "Done",
"Scopes": "Scopes"
}
+26 -1
View File
@@ -185,5 +185,30 @@
"IPChangeNotification": "Уведомление об изменении IP",
"CreateAlertRule": "Создать правила оповещений",
"UserId": "ID пользователя",
"NewUser": "Новый пользователь"
"NewUser": "Новый пользователь",
"EnableMCP": "Enable MCP endpoint (off by default; review API token scopes and server allow-list first)",
"ApiTokens": "API Tokens",
"CreateApiToken": "Create API token",
"CreateApiTokenDescription": "Tokens authenticate MCP and external clients on your behalf. They cannot exceed your own permissions.",
"ConfirmDeleteApiToken": "Revoke API token '{{name}}'? This cannot be undone.",
"ApiTokenRevoked": "API token revoked.",
"ApiTokenCreated": "API token created",
"ApiTokenRevealOnce": "Copy this token now. It will never be shown again.",
"ApiTokenStoreSafely": "Treat this token like a password. Anyone with it can act on your behalf within its scopes.",
"ApiTokenServers": "Servers",
"ApiTokenAllServers": "all permitted",
"ApiTokenNever": "never",
"ApiTokenExpiresAt": "Expires",
"ApiTokenLastUsed": "Last used",
"ApiTokenServerIDs": "Restrict to server IDs (optional)",
"ApiTokenServerIDsPlaceholder": "comma-separated, e.g. 1,2,3",
"ApiTokenExpiresInDays": "Expires in (days, 0 = never)",
"ApiTokenScopeRequired": "At least one scope is required.",
"ApiTokenServersInvalid": "Server IDs must be positive integers.",
"ApiTokenExpiryInvalid": "Days must be between 0 and 3650.",
"NameRequired": "Name is required.",
"Revoke": "Revoke",
"Copy": "Copy",
"Copied": "Copied to clipboard",
"Scopes": "Scopes"
}
+26 -1
View File
@@ -180,5 +180,30 @@
"ConfirmBlock": "தொகுதி உறுதிப்படுத்தவும்",
"RejectPassword": "கடவுச்சொல் உள்நுழைவை நிராகரிக்கவும்",
"EmptyText": "உரை காலியாக உள்ளது",
"EmptyNote": "உங்களிடம் எந்த குறிப்பும் இல்லை."
"EmptyNote": "உங்களிடம் எந்த குறிப்பும் இல்லை.",
"EnableMCP": "Enable MCP endpoint (off by default; review API token scopes and server allow-list first)",
"ApiTokens": "API Tokens",
"CreateApiToken": "Create API token",
"CreateApiTokenDescription": "Tokens authenticate MCP and external clients on your behalf. They cannot exceed your own permissions.",
"ConfirmDeleteApiToken": "Revoke API token '{{name}}'? This cannot be undone.",
"ApiTokenRevoked": "API token revoked.",
"ApiTokenCreated": "API token created",
"ApiTokenRevealOnce": "Copy this token now. It will never be shown again.",
"ApiTokenStoreSafely": "Treat this token like a password. Anyone with it can act on your behalf within its scopes.",
"ApiTokenServers": "Servers",
"ApiTokenAllServers": "all permitted",
"ApiTokenNever": "never",
"ApiTokenExpiresAt": "Expires",
"ApiTokenLastUsed": "Last used",
"ApiTokenServerIDs": "Restrict to server IDs (optional)",
"ApiTokenServerIDsPlaceholder": "comma-separated, e.g. 1,2,3",
"ApiTokenExpiresInDays": "Expires in (days, 0 = never)",
"ApiTokenScopeRequired": "At least one scope is required.",
"ApiTokenServersInvalid": "Server IDs must be positive integers.",
"ApiTokenExpiryInvalid": "Days must be between 0 and 3650.",
"NameRequired": "Name is required.",
"Revoke": "Revoke",
"Copy": "Copy",
"Copied": "Copied to clipboard",
"Scopes": "Scopes"
}
+26 -1
View File
@@ -127,5 +127,30 @@
"RequestType": "Тип запиту",
"RequestBody": "Тіло запиту",
"FileManager": "Псевдо Менеджер файлів",
"Downloading": "Завантаження"
"Downloading": "Завантаження",
"EnableMCP": "Enable MCP endpoint (off by default; review API token scopes and server allow-list first)",
"ApiTokens": "API Tokens",
"CreateApiToken": "Create API token",
"CreateApiTokenDescription": "Tokens authenticate MCP and external clients on your behalf. They cannot exceed your own permissions.",
"ConfirmDeleteApiToken": "Revoke API token '{{name}}'? This cannot be undone.",
"ApiTokenRevoked": "API token revoked.",
"ApiTokenCreated": "API token created",
"ApiTokenRevealOnce": "Copy this token now. It will never be shown again.",
"ApiTokenStoreSafely": "Treat this token like a password. Anyone with it can act on your behalf within its scopes.",
"ApiTokenServers": "Servers",
"ApiTokenAllServers": "all permitted",
"ApiTokenNever": "never",
"ApiTokenExpiresAt": "Expires",
"ApiTokenLastUsed": "Last used",
"ApiTokenServerIDs": "Restrict to server IDs (optional)",
"ApiTokenServerIDsPlaceholder": "comma-separated, e.g. 1,2,3",
"ApiTokenExpiresInDays": "Expires in (days, 0 = never)",
"ApiTokenScopeRequired": "At least one scope is required.",
"ApiTokenServersInvalid": "Server IDs must be positive integers.",
"ApiTokenExpiryInvalid": "Days must be between 0 and 3650.",
"NameRequired": "Name is required.",
"Revoke": "Revoke",
"Copy": "Copy",
"Copied": "Copied to clipboard",
"Scopes": "Scopes"
}
+27 -1
View File
@@ -162,6 +162,7 @@
"UseDirectConnectingIP": "使用直连 IP",
"IPChangeNotification": "IP变更通知",
"FullIPNotification": "在通知消息中显示完整的 IP 地址",
"EnableMCP": "启用 MCP 接入(默认关闭;启用前请确认已审阅 API 令牌 scope 与服务器白名单)",
"LoginFailed": "登录失败",
"BruteForceAttackingToken": "暴力攻击令牌",
"BruteForceAttackingAgentSecret": "暴力攻击代理秘密",
@@ -288,5 +289,30 @@
"StatusFailed": "已失败",
"StatusTimeout": "已超时",
"StatusCancelled": "已取消"
}
},
"ApiTokens": "API 令牌",
"CreateApiToken": "创建 API 令牌",
"CreateApiTokenDescription": "令牌用于 MCP 和外部客户端以你的身份调用 API,权限不能超过你本人。",
"ConfirmDeleteApiToken": "确定吊销 API 令牌 '{{name}}'?此操作不可撤销。",
"ApiTokenRevoked": "API 令牌已吊销。",
"ApiTokenCreated": "API 令牌已创建",
"ApiTokenRevealOnce": "请立即复制此令牌,离开后将无法再次查看。",
"ApiTokenStoreSafely": "请像密码一样妥善保管。任何持有者都可在 scope 内以你的身份操作。",
"ApiTokenServers": "服务器",
"ApiTokenAllServers": "全部可访问",
"ApiTokenNever": "永不",
"ApiTokenExpiresAt": "过期时间",
"ApiTokenLastUsed": "最近使用",
"ApiTokenServerIDs": "限定服务器 ID(可选)",
"ApiTokenServerIDsPlaceholder": "逗号分隔,例如 1,2,3",
"ApiTokenExpiresInDays": "有效期(天,0 = 永不过期)",
"ApiTokenScopeRequired": "至少选择一个 scope。",
"ApiTokenServersInvalid": "服务器 ID 必须为正整数。",
"ApiTokenExpiryInvalid": "天数必须在 0 到 3650 之间。",
"NameRequired": "名称必填。",
"Revoke": "吊销",
"Copy": "复制",
"Copied": "已复制到剪贴板",
"Done": "完成",
"Scopes": "权限"
}
+26 -1
View File
@@ -219,5 +219,30 @@
"StatusFailed": "已失敗",
"StatusTimeout": "已逾時",
"StatusCancelled": "已取消"
}
},
"EnableMCP": "啟用 MCP 端點(預設關閉;啟用前請先檢查 API token 範圍與伺服器允許清單)",
"ApiTokens": "API Token",
"CreateApiToken": "建立 API token",
"CreateApiTokenDescription": "Token 會代表你對 MCP 與外部用戶端進行驗證,其權限不會超過你本身的權限。",
"ConfirmDeleteApiToken": "撤銷 API token「{{name}}」?此操作無法復原。",
"ApiTokenRevoked": "API token 已撤銷。",
"ApiTokenCreated": "API token 已建立",
"ApiTokenRevealOnce": "請立即複製此 token,之後將不再顯示。",
"ApiTokenStoreSafely": "請將此 token 視為密碼。任何持有者都能在其範圍內代表你操作。",
"ApiTokenServers": "伺服器",
"ApiTokenAllServers": "全部允許",
"ApiTokenNever": "永不",
"ApiTokenExpiresAt": "到期時間",
"ApiTokenLastUsed": "最後使用",
"ApiTokenServerIDs": "限制伺服器 ID(選填)",
"ApiTokenServerIDsPlaceholder": "以逗號分隔,例如 1,2,3",
"ApiTokenExpiresInDays": "有效天數(0 = 永不過期)",
"ApiTokenScopeRequired": "至少需要一個範圍。",
"ApiTokenServersInvalid": "伺服器 ID 必須為正整數。",
"ApiTokenExpiryInvalid": "天數必須介於 0 到 3650 之間。",
"NameRequired": "名稱為必填。",
"Revoke": "撤銷",
"Copy": "複製",
"Copied": "已複製到剪貼簿",
"Scopes": "範圍"
}