mirror of
https://github.com/DrizzleTime/Foxel.git
synced 2026-05-11 09:59:50 +08:00
refactor: optimize backend module
This commit is contained in:
68
web/src/api/audit.ts
Normal file
68
web/src/api/audit.ts
Normal file
@@ -0,0 +1,68 @@
|
||||
import request from './client';
|
||||
|
||||
export interface AuditLogItem {
|
||||
id: number;
|
||||
created_at: string;
|
||||
action: string;
|
||||
description?: string | null;
|
||||
user_id?: number | null;
|
||||
username?: string | null;
|
||||
client_ip?: string | null;
|
||||
method: string;
|
||||
path: string;
|
||||
status_code: number;
|
||||
duration_ms?: number | null;
|
||||
success: boolean;
|
||||
request_params?: Record<string, any> | null;
|
||||
request_body?: Record<string, any> | null;
|
||||
error?: string | null;
|
||||
}
|
||||
|
||||
export interface PaginatedAuditLogs {
|
||||
items: AuditLogItem[];
|
||||
total: number;
|
||||
page: number;
|
||||
page_size: number;
|
||||
pages: number;
|
||||
}
|
||||
|
||||
export interface GetAuditLogsParams {
|
||||
page?: number;
|
||||
page_size?: number;
|
||||
action?: string;
|
||||
success?: boolean;
|
||||
username?: string;
|
||||
path?: string;
|
||||
start_time?: string;
|
||||
end_time?: string;
|
||||
}
|
||||
|
||||
export interface ClearAuditLogsParams {
|
||||
start_time?: string;
|
||||
end_time?: string;
|
||||
}
|
||||
|
||||
export const auditApi = {
|
||||
list: (params: GetAuditLogsParams = {}) => {
|
||||
const query = new URLSearchParams();
|
||||
if (params.page) query.append('page', params.page.toString());
|
||||
if (params.page_size) query.append('page_size', params.page_size.toString());
|
||||
if (params.action) query.append('action', params.action);
|
||||
if (params.success !== undefined && params.success !== null) query.append('success', String(params.success));
|
||||
if (params.username) query.append('username', params.username);
|
||||
if (params.path) query.append('path', params.path);
|
||||
if (params.start_time) query.append('start_time', params.start_time);
|
||||
if (params.end_time) query.append('end_time', params.end_time);
|
||||
const qs = query.toString();
|
||||
return request<PaginatedAuditLogs>(`/audit/logs${qs ? `?${qs}` : ''}`);
|
||||
},
|
||||
clear: (params: ClearAuditLogsParams = {}) => {
|
||||
const query = new URLSearchParams();
|
||||
if (params.start_time) query.append('start_time', params.start_time);
|
||||
if (params.end_time) query.append('end_time', params.end_time);
|
||||
const qs = query.toString();
|
||||
return request<{ deleted_count: number }>(`/audit/logs${qs ? `?${qs}` : ''}`, {
|
||||
method: 'DELETE',
|
||||
});
|
||||
},
|
||||
};
|
||||
@@ -1,54 +0,0 @@
|
||||
import request from './client';
|
||||
|
||||
export interface LogItem {
|
||||
id: number;
|
||||
timestamp: string;
|
||||
level: string;
|
||||
source: string;
|
||||
message: string;
|
||||
details: Record<string, any>;
|
||||
user_id?: number;
|
||||
}
|
||||
|
||||
export interface PaginatedLogs {
|
||||
items: LogItem[];
|
||||
total: number;
|
||||
page: number;
|
||||
page_size: number;
|
||||
pages: number;
|
||||
}
|
||||
|
||||
export interface GetLogsParams {
|
||||
page?: number;
|
||||
page_size?: number;
|
||||
level?: string;
|
||||
source?: string;
|
||||
start_time?: string;
|
||||
end_time?: string;
|
||||
}
|
||||
|
||||
export interface ClearLogsParams {
|
||||
start_time?: string;
|
||||
end_time?: string;
|
||||
}
|
||||
|
||||
export const logsApi = {
|
||||
list: (params: GetLogsParams = {}) => {
|
||||
const query = new URLSearchParams();
|
||||
if (params.page) query.append('page', params.page.toString());
|
||||
if (params.page_size) query.append('page_size', params.page_size.toString());
|
||||
if (params.level) query.append('level', params.level);
|
||||
if (params.source) query.append('source', params.source);
|
||||
if (params.start_time) query.append('start_time', params.start_time);
|
||||
if (params.end_time) query.append('end_time', params.end_time);
|
||||
return request<PaginatedLogs>(`/logs?${query.toString()}`);
|
||||
},
|
||||
clear: (params: ClearLogsParams = {}) => {
|
||||
const query = new URLSearchParams();
|
||||
if (params.start_time) query.append('start_time', params.start_time);
|
||||
if (params.end_time) query.append('end_time', params.end_time);
|
||||
return request<{ deleted_count: number }>(`/logs?${query.toString()}`, {
|
||||
method: 'DELETE',
|
||||
});
|
||||
},
|
||||
};
|
||||
@@ -139,6 +139,6 @@ export const vfsApi = {
|
||||
});
|
||||
if (page !== undefined) params.set('page', String(page));
|
||||
if (page_size !== undefined) params.set('page_size', String(page_size));
|
||||
return request<SearchResponse>(`/search?${params.toString()}`);
|
||||
return request<SearchResponse>(`/fs/search?${params.toString()}`);
|
||||
},
|
||||
};
|
||||
|
||||
@@ -1,14 +1,14 @@
|
||||
import { createContext, useContext, useMemo, useState, useEffect } from 'react';
|
||||
import type { PropsWithChildren } from 'react';
|
||||
import { zh } from './locales/zh';
|
||||
import { en } from './locales/en';
|
||||
import en from './locales/en.json';
|
||||
import zhOverrides from './locales/zh.json';
|
||||
|
||||
type Lang = 'zh' | 'en';
|
||||
type Dict = Record<string, string>;
|
||||
|
||||
const dicts: Record<Lang, Dict> = {
|
||||
zh,
|
||||
en,
|
||||
zh: { ...en, ...zhOverrides },
|
||||
};
|
||||
|
||||
export interface I18nContextValue {
|
||||
|
||||
662
web/src/i18n/locales/en.json
Normal file
662
web/src/i18n/locales/en.json
Normal file
@@ -0,0 +1,662 @@
|
||||
{
|
||||
"All Files": "All Files",
|
||||
"Manage": "Manage",
|
||||
"Follow System": "System",
|
||||
"Automation": "Automation",
|
||||
"My Shares": "My Shares",
|
||||
"Offline Downloads": "Offline Downloads",
|
||||
"Adapters": "Adapters",
|
||||
"Plugins": "App Center",
|
||||
"System Settings": "System Settings",
|
||||
"Backup & Restore": "Backup & Restore",
|
||||
"System Logs": "System Logs",
|
||||
"Audit Logs": "Audit Logs",
|
||||
"Audit Log Details": "Audit Log Details",
|
||||
"Search files / tags / types": "Search files / tags / types",
|
||||
"Log Out": "Log Out",
|
||||
"Admin": "Admin",
|
||||
"Profile": "Profile",
|
||||
"Account Settings": "Account Settings",
|
||||
"Language": "Language",
|
||||
"Chinese": "中文",
|
||||
"English": "English",
|
||||
"Full Name": "Full Name",
|
||||
"Email": "Email",
|
||||
"Change Password": "Change Password",
|
||||
"Old Password": "Old Password",
|
||||
"New Password": "New Password",
|
||||
"Please fill both old and new password": "Please fill both old and new password",
|
||||
"Welcome Back": "Welcome Back",
|
||||
"Sign in to your Foxel account": "Sign in to your Foxel account",
|
||||
"Username / Email": "Username / Email",
|
||||
"Password": "Password",
|
||||
"Sign In": "Sign In",
|
||||
"Please enter username and password": "Please enter username and password",
|
||||
"Login failed": "Login failed",
|
||||
"Your next-generation file manager": "Your next-generation file manager",
|
||||
"Cross-platform sync, access anywhere": "Cross-platform sync, access anywhere",
|
||||
"AI-powered search for quick find": "AI-powered search for quick find",
|
||||
"Flexible sharing and collaboration": "Flexible sharing and collaboration",
|
||||
"Powerful automation to simplify tasks": "Powerful automation to simplify tasks",
|
||||
"Join our community:": "Join our community:",
|
||||
"Refresh": "Refresh",
|
||||
"Copy": "Copy",
|
||||
"Copied link": "Link copied",
|
||||
"Share canceled": "Share canceled",
|
||||
"Cancel failed": "Cancel failed",
|
||||
"Load failed": "Load failed",
|
||||
"Are you sure to cancel share?": "Are you sure to cancel share?",
|
||||
"Clear expired shares": "Clear expired shares",
|
||||
"Confirm clear expired shares?": "Confirm clear expired shares?",
|
||||
"Cleared {count} expired shares": "Cleared {count} expired shares",
|
||||
"Please select time range": "Please select time range",
|
||||
"Share Name": "Share Name",
|
||||
"Share Content": "Share Content",
|
||||
"Created At": "Created At",
|
||||
"Expires At": "Expires At",
|
||||
"Forever": "Forever",
|
||||
"Access": "Access",
|
||||
"Public": "Public",
|
||||
"By Password": "By Password",
|
||||
"Password Required": "Password Required",
|
||||
"Please enter password": "Please enter password",
|
||||
"Confirm": "Confirm",
|
||||
"Unable to load share info": "Unable to load share info",
|
||||
"Share load failed": "Failed to load share",
|
||||
"Wrong password": "Wrong password",
|
||||
"Root": "All Files",
|
||||
"Created on {date}": "Created on {date}",
|
||||
"Expires on {date}": "Expires on {date}",
|
||||
"Download File": "Download File",
|
||||
"Preview not supported for this file type": "Preview not supported for this file type",
|
||||
"Back": "Back",
|
||||
"Download": "Download",
|
||||
"No offline download tasks": "No offline download tasks",
|
||||
"Create Offline Download": "Create Offline Download",
|
||||
"Offline Download Tasks": "Offline Download Tasks",
|
||||
"URL": "URL",
|
||||
"Please input URL": "Please input URL",
|
||||
"Destination Folder": "Destination Folder",
|
||||
"Select destination": "Select destination",
|
||||
"Filename": "Filename",
|
||||
"Please input filename": "Please input filename",
|
||||
"Start Download": "Start Download",
|
||||
"Stage": "Stage",
|
||||
"Progress": "Progress",
|
||||
"Bytes": "Bytes",
|
||||
"Save Path": "Save Path",
|
||||
"Queued": "Queued",
|
||||
"Downloading": "Downloading",
|
||||
"Transferring": "Transferring",
|
||||
"Completed": "Completed",
|
||||
"Pending": "Pending",
|
||||
"Running": "Running",
|
||||
"Success": "Success",
|
||||
"Failed": "Failed",
|
||||
"Failure": "Failure",
|
||||
"Home": "Home",
|
||||
"File Manager": "File Manager",
|
||||
"New Folder": "New Folder",
|
||||
"Upload": "Upload",
|
||||
"Name": "Name",
|
||||
"Size": "Size",
|
||||
"Modified Time": "Modified Time",
|
||||
"Grid": "Grid",
|
||||
"List": "List",
|
||||
"Mount Point": "Mount Point",
|
||||
"Move": "Move",
|
||||
"Move to": "Move to",
|
||||
"Copy to": "Copy to",
|
||||
"Destination path": "Destination path",
|
||||
"Move task queued": "Move task queued",
|
||||
"Move completed": "Move completed",
|
||||
"Copy task queued": "Copy task queued",
|
||||
"Copy completed": "Copy completed",
|
||||
"Please input destination path": "Please input destination path",
|
||||
"Upload File": "Upload File",
|
||||
"Upload Files": "Upload Files",
|
||||
"Upload Folder": "Upload Folder",
|
||||
"Open": "Open",
|
||||
"Open With": "Open With",
|
||||
"Default": "Default",
|
||||
"Processor": "Processor",
|
||||
"Share": "Share",
|
||||
"Rename": "Rename",
|
||||
"Delete": "Delete",
|
||||
"Details": "Details",
|
||||
"Get Direct Link": "Get Direct Link",
|
||||
"User": "User",
|
||||
"Status Code": "Status Code",
|
||||
"Duration (ms)": "Duration (ms)",
|
||||
"Client IP": "Client IP",
|
||||
"Result": "Result",
|
||||
"Request Params": "Request Params",
|
||||
"Request Body": "Request Body",
|
||||
"Total progress": "Total progress",
|
||||
"Upload bytes summary": "{uploaded} / {total}",
|
||||
"Upload task summary": "Tasks: {completed} / {total} completed, {pending} pending, {failures} failed",
|
||||
"Overwrite confirmation required": "Overwrite confirmation required",
|
||||
"Target already exists: {path}": "Target already exists: {path}",
|
||||
"Overwrite": "Overwrite",
|
||||
"Skip": "Skip",
|
||||
"Overwrite All": "Overwrite All",
|
||||
"Skip All": "Skip All",
|
||||
"Directory": "Directory",
|
||||
"Creating directory...": "Creating directory...",
|
||||
"Directory ready": "Directory ready",
|
||||
"Create directory failed": "Create directory failed",
|
||||
"Waiting to create": "Waiting to create",
|
||||
"Waiting for overwrite decision": "Waiting for overwrite decision",
|
||||
"Waiting to upload": "Waiting to upload",
|
||||
"Skipped": "Skipped",
|
||||
"Upload succeeded": "Upload succeeded",
|
||||
"Upload failed": "Upload failed",
|
||||
"No items selected for upload": "No items selected for upload",
|
||||
"No uploadable files or directories found": "No uploadable files or directories found",
|
||||
"Missing file content": "Missing file content",
|
||||
"Directory conflicts with existing file": "A file with the same name already exists at the target location",
|
||||
"Join Community": "Join Community",
|
||||
"Scan to join WeChat group": "Scan to join WeChat group",
|
||||
"If QR expires, add drizzle2001 to join": "If QR expires, add drizzle2001 to join",
|
||||
"Version Info": "Version Info",
|
||||
"Current Version": "Current Version",
|
||||
"Latest Version": "Latest Version",
|
||||
"New version found: {version}": "New version found: {version}",
|
||||
"Please update to the latest for features and fixes": "Please update to the latest for features and fixes",
|
||||
"Open Releases": "Open Releases",
|
||||
"Changelog": "Changelog",
|
||||
"Fetching latest version...": "Fetching latest version...",
|
||||
"Update available": "Update available",
|
||||
"You are on the latest: {version}": "You are on the latest: {version}",
|
||||
"Up to date": "Up to date",
|
||||
"Share {count} items": "Share {count} items",
|
||||
"Share link created": "Share link created",
|
||||
"Create failed": "Create failed",
|
||||
"Copied to clipboard": "Copied to clipboard",
|
||||
"Expiration (days)": "Expiration (days)",
|
||||
"Set 0 or negative for forever": "Set 0 or negative for forever",
|
||||
"Share link created successfully!": "Share link created successfully!",
|
||||
"Share Link": "Share Link",
|
||||
"Share created": "Share created",
|
||||
"Create Share": "Create Share",
|
||||
"Done": "Done",
|
||||
"Create": "Create",
|
||||
"Failed to generate link": "Failed to generate link",
|
||||
"Markdown copied to clipboard": "Markdown copied to clipboard",
|
||||
"Generate a direct link for {name}": "Generate a direct link for {name}",
|
||||
"1 hour": "1 hour",
|
||||
"1 day": "1 day",
|
||||
"7 days": "7 days",
|
||||
"Generating link...": "Generating link...",
|
||||
"Link will appear here": "Link will appear here",
|
||||
"Copy Markdown": "Copy Markdown",
|
||||
"Close": "Close",
|
||||
"Task Queue": "Task Queue",
|
||||
"Last updated at {time}": "Last updated at {time}",
|
||||
"Total Tasks": "Total Tasks",
|
||||
"Running Tasks": "Running Tasks",
|
||||
"Waiting Tasks": "Waiting Tasks",
|
||||
"Failed Tasks": "Failed Tasks",
|
||||
"Active Workers": "Active Workers",
|
||||
"Task Type": "Task Type",
|
||||
"Search by name or ID": "Search by name or ID",
|
||||
"Filter by status": "Filter by status",
|
||||
"Queue Concurrency": "Queue Concurrency",
|
||||
"Settings saved": "Settings saved",
|
||||
"Expand": "Expand",
|
||||
"Adjust worker concurrency immediately": "Adjust worker concurrency immediately",
|
||||
"Auto": "Auto",
|
||||
"Manual": "Manual",
|
||||
"Camera Make": "Camera Make",
|
||||
"Camera Model": "Camera Model",
|
||||
"Capture Time": "Capture Time",
|
||||
"X Resolution": "X Resolution",
|
||||
"Y Resolution": "Y Resolution",
|
||||
"Exposure Time": "Exposure Time",
|
||||
"Aperture": "Aperture",
|
||||
"Focal Length": "Focal Length",
|
||||
"Width": "Width",
|
||||
"Height": "Height",
|
||||
"No common EXIF info": "No common EXIF info",
|
||||
"File Properties": "File Properties",
|
||||
"Loading file info...": "Loading file info...",
|
||||
"Basic Info": "Basic Info",
|
||||
"Type": "Type",
|
||||
"Folder": "Folder",
|
||||
"File": "File",
|
||||
"Path": "Path",
|
||||
"Path copied to clipboard": "Path copied to clipboard",
|
||||
"Copy failed": "Copy failed",
|
||||
"Permissions": "Permissions",
|
||||
"EXIF Info": "EXIF Info",
|
||||
"Index Info": "Index Info",
|
||||
"Indexed Items": "Indexed Items",
|
||||
"Indexed Types": "Indexed Types",
|
||||
"No index data": "No index data",
|
||||
"Indexed Chunks": "Indexed Chunks",
|
||||
"More Indexed Chunks": "More Indexed Chunks",
|
||||
"Chunk ID": "Chunk ID",
|
||||
"Offset Range": "Offset Range",
|
||||
"Vector ID": "Vector ID",
|
||||
"Preview": "Preview",
|
||||
"Showing first {count} entries": "Showing first {count} entries",
|
||||
"Smart Search": "Smart Search",
|
||||
"Name Search": "Name Search",
|
||||
"Search Results": "Search Results",
|
||||
"No files found": "No files found",
|
||||
"Relevance": "Relevance",
|
||||
"Saved successfully": "Saved successfully",
|
||||
"Save failed": "Save failed",
|
||||
"Loading...": "Loading...",
|
||||
"Appearance Settings": "Appearance Settings",
|
||||
"Theme": "Theme",
|
||||
"Theme Mode": "Theme Mode",
|
||||
"Light": "Light",
|
||||
"Dark": "Dark",
|
||||
"Primary Color": "Primary Color",
|
||||
"Border Radius": "Border Radius",
|
||||
"Advanced": "Advanced",
|
||||
"Override AntD Tokens (JSON)": "Override AntD Tokens (JSON)",
|
||||
"e.g. {\"colorText\": \"#222\"}": "e.g. {\"colorText\": \"#222\"}",
|
||||
"Custom CSS": "Custom CSS",
|
||||
"Save": "Save",
|
||||
"App Settings": "App Settings",
|
||||
"Email Settings": "Email Settings",
|
||||
"AI Settings": "AI Settings",
|
||||
"Protocol Mappings": "Protocol Mappings",
|
||||
"S3 Mapping": "S3 Mapping",
|
||||
"S3 Endpoint": "S3 Endpoint",
|
||||
"Bucket Name": "Bucket Name",
|
||||
"Bucket API Path": "Bucket API Path",
|
||||
"Region": "Region",
|
||||
"Base Path": "Base Path",
|
||||
"Access Key": "Access Key",
|
||||
"Secret Key": "Secret Key",
|
||||
"Vision Model": "Vision Model",
|
||||
"Embedding Model": "Embedding Model",
|
||||
"Embedding Dimension": "Embedding Dimension",
|
||||
"Vector Database": "Vector Database",
|
||||
"Vector Database Settings": "Vector Database Settings",
|
||||
"Current Statistics": "Current Statistics",
|
||||
"Collections": "Collections",
|
||||
"Vectors": "Vectors",
|
||||
"Database Size": "Database Size",
|
||||
"Estimated Memory": "Estimated Memory",
|
||||
"No collections": "No collections",
|
||||
"Dimension": "Dimension",
|
||||
"Non-vector collection": "Non-vector collection",
|
||||
"Estimated memory": "Estimated memory",
|
||||
"Indexes": "Indexes",
|
||||
"Unnamed index": "Unnamed index",
|
||||
"Indexed rows": "Indexed rows",
|
||||
"Pending rows": "Pending rows",
|
||||
"Estimated memory is calculated as vectors x dimension x 4 bytes (float32).": "Estimated memory is calculated as vectors x dimension x 4 bytes (float32).",
|
||||
"Database Provider": "Database Provider",
|
||||
"Please select a provider": "Please select a provider",
|
||||
"Coming soon": "Coming soon",
|
||||
"This provider is not available yet": "This provider is not available yet",
|
||||
"Database file path": "Database file path",
|
||||
"Server URI": "Server URI",
|
||||
"Token": "Token",
|
||||
"Server URL": "Server URL",
|
||||
"API Key": "API Key",
|
||||
"Embedded Milvus Lite (local file storage).": "Embedded Milvus Lite (local file storage).",
|
||||
"Remote Milvus instance accessed via URI.": "Remote Milvus instance accessed via URI.",
|
||||
"Qdrant vector database (HTTP API).": "Qdrant vector database (HTTP API).",
|
||||
"Database Type": "Database Type",
|
||||
"Confirm embedding dimension change": "Confirm embedding dimension change",
|
||||
"Changing the embedding dimension will clear the vector database automatically. You will need to rebuild indexes afterwards. Continue?": "Changing the embedding dimension will clear the vector database automatically. You will need to rebuild indexes afterwards. Continue?",
|
||||
"Confirm clear vector database?": "Confirm clear vector database?",
|
||||
"This will delete all collections irreversibly.": "This will delete all collections irreversibly.",
|
||||
"Confirm Clear": "Confirm Clear",
|
||||
"Vector database cleared": "Vector database cleared",
|
||||
"Clear failed": "Clear failed",
|
||||
"Clear Vector DB": "Clear Vector DB",
|
||||
"App Name": "App Name",
|
||||
"Logo URL": "Logo URL",
|
||||
"Favicon URL": "Favicon URL",
|
||||
"App Domain": "App Domain",
|
||||
"File Domain": "File Domain",
|
||||
"Configure Access Key and Secret to enable S3 mapping.": "Configure Access Key and Secret to enable S3 mapping.",
|
||||
"Mount point inside the virtual file system (e.g. / or /workspace).": "Mount point inside the virtual file system (e.g. / or /workspace).",
|
||||
"Please input bucket name": "Please input bucket name",
|
||||
"Please input region": "Please input region",
|
||||
"Please input access key": "Please input access key",
|
||||
"Please input secret key": "Please input secret key",
|
||||
"Save S3 Settings": "Save S3 Settings",
|
||||
"Example CLI command": "Example CLI command",
|
||||
"WebDAV Mapping": "WebDAV Mapping",
|
||||
"WebDAV Endpoint": "WebDAV Endpoint",
|
||||
"Basic (system account password)": "Basic (system account password)",
|
||||
"Root Path": "Root Path",
|
||||
"Client Compatibility": "Client Compatibility",
|
||||
"Supports Finder, Windows network drive, rclone, and other WebDAV clients.": "Supports Finder, Windows network drive, rclone, and other WebDAV clients.",
|
||||
"Toggle the switch to expose the virtual file system via WebDAV.": "Toggle the switch to expose the virtual file system via WebDAV.",
|
||||
"SMTP Settings": "SMTP Settings",
|
||||
"SMTP Host": "SMTP Host",
|
||||
"Please input SMTP host": "Please input SMTP host",
|
||||
"SMTP Port": "SMTP Port",
|
||||
"Please input SMTP port": "Please input SMTP port",
|
||||
"Security": "Security",
|
||||
"None": "None",
|
||||
"SSL": "SSL",
|
||||
"STARTTLS": "STARTTLS",
|
||||
"Timeout (seconds)": "Timeout (seconds)",
|
||||
"Sender": "Sender",
|
||||
"Sender Name": "Sender Name",
|
||||
"Sender Email": "Sender Email",
|
||||
"Please input sender email": "Please input sender email",
|
||||
"Authentication": "Authentication",
|
||||
"SMTP Username": "SMTP Username",
|
||||
"SMTP Password": "SMTP Password",
|
||||
"Test Email": "Test Email",
|
||||
"Current Configuration": "Current Configuration",
|
||||
"Available variables": "Available variables",
|
||||
"Not set": "Not set",
|
||||
"Password Reset Template": "Password Reset Template",
|
||||
"Live Preview": "Live Preview",
|
||||
"Foxel Mail Test": "Foxel Mail Test",
|
||||
"Recipient Address": "Recipient Address",
|
||||
"Please input recipient email": "Please input recipient email",
|
||||
"Test Subject": "Test Subject",
|
||||
"Test User Name": "Test User Name",
|
||||
"Optional": "Optional",
|
||||
"Send Test Email": "Send Test Email",
|
||||
"Please complete all required fields": "Please complete all required fields",
|
||||
"SMTP port must be a positive number": "SMTP port must be a positive number",
|
||||
"Test email queued (task {{taskId}})": "Test email queued (task {{taskId}})",
|
||||
"Test email failed": "Test email failed",
|
||||
"Forgot Password?": "Forgot password?",
|
||||
"Reset Your Password": "Reset Your Password",
|
||||
"Enter the email linked to your account and we will send a reset link.": "Enter the email linked to your account and we will send a reset link.",
|
||||
"If the email exists, a reset link has been sent.": "If the email exists, a reset link has been sent.",
|
||||
"Send Reset Link": "Send Reset Link",
|
||||
"Resend Link": "Resend Link",
|
||||
"Back to login": "Back to login",
|
||||
"Request failed": "Request failed",
|
||||
"Reset link is invalid": "Reset link is invalid",
|
||||
"Reset link is invalid or expired": "Reset link is invalid or expired",
|
||||
"Reset failed": "Reset failed",
|
||||
"Try again": "Try again",
|
||||
"Set a new password": "Set a new password",
|
||||
"Please enter new password": "Please enter new password",
|
||||
"Confirm Password": "Confirm Password",
|
||||
"Please confirm new password": "Please confirm new password",
|
||||
"Update Password": "Update Password",
|
||||
"Passwords do not match": "Passwords do not match",
|
||||
"Password updated, please login again.": "Password updated, please login again.",
|
||||
"Failed to reset password": "Failed to reset password",
|
||||
"Vision API URL": "Vision API URL",
|
||||
"Vision API Key": "Vision API Key",
|
||||
"Embedding API URL": "Embedding API URL",
|
||||
"Embedding API Key": "Embedding API Key",
|
||||
"AI Providers & Models": "AI Providers & Models",
|
||||
"Manage AI providers, synchronize compatible models, and configure default capabilities across the system.": "Manage AI providers, synchronize compatible models, and configure default capabilities across the system.",
|
||||
"Add Provider": "Add Provider",
|
||||
"Edit Provider": "Edit Provider",
|
||||
"Pull Models": "Pull Models",
|
||||
"Manual Add": "Manual Add",
|
||||
"Clear Remote List": "Clear Remote List",
|
||||
"Select models from the list to add them automatically": "Select models from the list to add them automatically",
|
||||
"No remote models": "No remote models",
|
||||
"No remote models found": "No remote models found",
|
||||
"No remote models match search": "No remote models match search",
|
||||
"Search fetched models": "Search fetched models",
|
||||
"Already Added": "Already Added",
|
||||
"Add Selected Models": "Add Selected Models",
|
||||
"Fetch failed": "Fetch failed",
|
||||
"Select models to add": "Select models to add",
|
||||
"Added {count} models": "Added {count} models",
|
||||
"Choose Template": "Choose Template",
|
||||
"Configure Provider": "Configure Provider",
|
||||
"Back to Templates": "Back to Templates",
|
||||
"View Docs": "View Docs",
|
||||
"Custom Provider": "Custom Provider",
|
||||
"Custom Provider Description": "Bring your own endpoint compatible with OpenAI or Gemini formats.",
|
||||
"OpenAI Provider": "OpenAI",
|
||||
"OpenAI Provider Description": "Access GPT-4o, GPT-4.1, GPT-3.5 and more models from OpenAI.",
|
||||
"Azure OpenAI Provider": "Azure OpenAI",
|
||||
"Azure OpenAI Provider Description": "Use OpenAI models deployed on Microsoft Azure.",
|
||||
"Google AI Provider": "Google AI",
|
||||
"Google AI Provider Description": "Gemini series models served via the Google AI platform.",
|
||||
"SiliconFlow Provider": "SiliconFlow",
|
||||
"SiliconFlow Provider Description": "High-performance inference platform with OpenAI-compatible APIs.",
|
||||
"OpenRouter Provider": "OpenRouter",
|
||||
"OpenRouter Provider Description": "Connect to multiple AI providers through a single OpenAI-style endpoint.",
|
||||
"Anthropic Provider": "Anthropic",
|
||||
"Anthropic Provider Description": "Claude 3 family models exposed through the Claude API.",
|
||||
"DeepSeek Provider": "DeepSeek",
|
||||
"DeepSeek Provider Description": "DeepSeek language models via OpenAI-compatible API.",
|
||||
"Grok Provider": "Grok (xAI)",
|
||||
"Grok Provider Description": "Grok models powered by xAI with OpenAI-style routes.",
|
||||
"Ollama Provider": "Ollama",
|
||||
"Ollama Provider Description": "Self-host and run models locally with Ollama's OpenAI bridge.",
|
||||
"Voyage Provider": "Voyage AI",
|
||||
"Voyage Provider Description": "High-quality embeddings and rerankers from Voyage AI.",
|
||||
"Delete provider?": "Delete provider?",
|
||||
"Deleting this provider will also remove all associated models. Continue?": "Deleting this provider will also remove all associated models. Continue?",
|
||||
"Deleted successfully": "Deleted successfully",
|
||||
"Sync Models": "Sync Models",
|
||||
"Sync completed: {created} created, {updated} updated": "Sync completed: {created} created, {updated} updated",
|
||||
"Sync failed": "Sync failed",
|
||||
"Add Model": "Add Model",
|
||||
"Edit Model": "Edit Model",
|
||||
"Delete model?": "Delete model?",
|
||||
"This operation cannot be undone. Continue?": "This operation cannot be undone. Continue?",
|
||||
"No models yet": "No models yet",
|
||||
"Add your first AI provider to get started": "Add your first AI provider to get started",
|
||||
"Default Models Configuration": "Default Models Configuration",
|
||||
"Main Chat Model": "Main Chat Model",
|
||||
"Primary assistant for conversations, reasoning, and tool calls.": "Primary assistant for conversations, reasoning, and tool calls.",
|
||||
"Handles multimodal perception such as image understanding.": "Handles multimodal perception such as image understanding.",
|
||||
"Transforms content into dense vectors for search and retrieval.": "Transforms content into dense vectors for search and retrieval.",
|
||||
"Optimises ranking quality for search candidates.": "Optimises ranking quality for search candidates.",
|
||||
"Covers text-to-speech and speech understanding scenarios.": "Covers text-to-speech and speech understanding scenarios.",
|
||||
"Supports function calling, orchestration, and automation.": "Supports function calling, orchestration, and automation.",
|
||||
"Select a model": "Select a model",
|
||||
"Template": "Template",
|
||||
"Select a template": "Select a template",
|
||||
"Display Name": "Display Name",
|
||||
"Enter name": "Enter name",
|
||||
"Identifier": "Identifier",
|
||||
"Enter identifier": "Enter identifier",
|
||||
"Only lowercase letters, numbers, dash, dot and underscore are allowed": "Only lowercase letters, numbers, dash, dot and underscore are allowed",
|
||||
"API Format": "API Format",
|
||||
"Base URL": "Base URL",
|
||||
"Enter base url": "Enter base URL",
|
||||
"Optional, can also be provided per request": "Optional, can also be provided per request",
|
||||
"Model Identifier": "Model Identifier",
|
||||
"Enter model identifier": "Enter model identifier",
|
||||
"Description": "Description",
|
||||
"Capabilities": "Capabilities",
|
||||
"Context Window": "Context Window",
|
||||
"Embedding Dimensions": "Embedding Dimensions",
|
||||
"Price /1K input tokens": "Price /1K input tokens",
|
||||
"Price /1K output tokens": "Price /1K output tokens",
|
||||
"Missing required config:": "Missing required config:",
|
||||
"Updated successfully": "Updated successfully",
|
||||
"Created successfully": "Created successfully",
|
||||
"Operation failed": "Operation failed",
|
||||
"Deleted": "Deleted",
|
||||
"Delete failed": "Delete failed",
|
||||
"Status updated": "Status updated",
|
||||
"Update failed": "Update failed",
|
||||
"Mount Path": "Mount Path",
|
||||
"Sub Path": "Sub Path",
|
||||
"Sub Path (optional)": "Sub Path (optional)",
|
||||
"Sub directory inside adapter": "Sub directory inside adapter",
|
||||
"Enabled": "Enabled",
|
||||
"Actions": "Actions",
|
||||
"Edit": "Edit",
|
||||
"Confirm delete?": "Confirm delete?",
|
||||
"No config fields": "No config fields",
|
||||
"Please input {label}": "Please input {label}",
|
||||
"Storage Adapters": "Storage Adapters",
|
||||
"Create Adapter": "Create Adapter",
|
||||
"Unique name": "Unique name",
|
||||
"Select adapter type": "Select adapter type",
|
||||
"/ or /drive": "/ or /drive",
|
||||
"Adapter Config": "Adapter Config",
|
||||
"adapter.type.local": "Local Filesystem",
|
||||
"adapter.type.webdav": "WebDAV",
|
||||
"adapter.type.googledrive": "Google Drive",
|
||||
"adapter.type.onedrive": "OneDrive",
|
||||
"adapter.type.s3": "Amazon S3",
|
||||
"adapter.type.ftp": "FTP",
|
||||
"adapter.type.sftp": "SFTP",
|
||||
"adapter.type.telegram": "Telegram",
|
||||
"adapter.type.quark": "Quark Drive",
|
||||
"Automation Tasks": "Automation Tasks",
|
||||
"Create Task": "Create Task",
|
||||
"Edit Task": "Edit Task",
|
||||
"Create Automation Task": "Create Automation Task",
|
||||
"Task Name": "Task Name",
|
||||
"Trigger Event": "Trigger Event",
|
||||
"File Written": "File Written",
|
||||
"File Deleted": "File Deleted",
|
||||
"Matching Rules": "Matching Rules",
|
||||
"Path Prefix (optional)": "Path Prefix (optional)",
|
||||
"Filename Regex (optional)": "Filename Regex (optional)",
|
||||
"Action": "Action",
|
||||
"Current Task Queue": "Current Task Queue",
|
||||
"Params": "Params",
|
||||
"Status": "Status",
|
||||
"Confirm clear logs?": "Confirm clear logs?",
|
||||
"This will delete logs in selected range irreversibly.": "This will delete logs in selected range irreversibly.",
|
||||
"Cleared {count} logs": "Cleared {count} logs",
|
||||
"Time": "Time",
|
||||
"Level": "Level",
|
||||
"Source": "Source",
|
||||
"Message": "Message",
|
||||
"User ID": "User ID",
|
||||
"Search source": "Search source",
|
||||
"Clear": "Clear",
|
||||
"Log Details": "Log Details",
|
||||
"Raw Log": "Raw Log",
|
||||
"Export started, check your downloads.": "Export started, check your downloads.",
|
||||
"Export failed": "Export failed",
|
||||
"Confirm import backup?": "Confirm import backup?",
|
||||
"Are you sure to import from this file?": "Are you sure to import from this file?",
|
||||
"Warning: This will overwrite all data including users (with passwords), settings, storages and tasks. Irreversible!": "Warning: This will overwrite all data including users (with passwords), settings, storages and tasks. Irreversible!",
|
||||
"Confirm Import": "Confirm Import",
|
||||
"Import succeeded! The page will refresh.": "Import succeeded! The page will refresh.",
|
||||
"Import failed": "Import failed",
|
||||
"Export": "Export",
|
||||
"Import": "Import",
|
||||
"Export all data (adapters, users, tasks, shares) into a JSON file.": "Export all data (adapters, users, tasks, shares) into a JSON file.",
|
||||
"Keep your backup file safe.": "Keep your backup file safe.",
|
||||
"Export Backup": "Export Backup",
|
||||
"Restore data from a previously exported JSON file.": "Restore data from a previously exported JSON file.",
|
||||
"Warning: This will clear and overwrite existing data.": "Warning: This will clear and overwrite existing data.",
|
||||
"Choose File and Restore": "Choose File and Restore",
|
||||
"No files yet here": "No files yet here",
|
||||
"This folder is empty": "This folder is empty",
|
||||
"Start uploading files or create folders to organize your content": "Start uploading files or create folders to organize your content",
|
||||
"You can create folders or upload files here": "You can create folders or upload files here",
|
||||
"Please input name": "Please input name",
|
||||
"Confirm delete {name}?": "Confirm delete {name}?",
|
||||
"items": "items",
|
||||
"Downloading folders is not supported": "Downloading folders is not supported",
|
||||
"Download failed": "Download failed",
|
||||
"Please select files or folders to share": "Please select files or folders to share",
|
||||
"Direct links for folders are not supported": "Direct links for folders are not supported",
|
||||
"Processing finished": "Processing finished",
|
||||
"Processing failed": "Processing failed",
|
||||
"Processors": "Processors",
|
||||
"Processor List": "Processor List",
|
||||
"Reload": "Reload",
|
||||
"Run Processor": "Run Processor",
|
||||
"Target Path": "Target Path",
|
||||
"Please select a path": "Please select a path",
|
||||
"Select Directory": "Select Directory",
|
||||
"Overwrite original": "Overwrite original",
|
||||
"Save To": "Save To",
|
||||
"Optional output path": "Optional output path",
|
||||
"Run": "Run",
|
||||
"Select a processor": "Select a processor",
|
||||
"No module path": "No module path",
|
||||
"Source saved": "Source saved",
|
||||
"Processors reloaded": "Processors reloaded",
|
||||
"Unsaved changes": "Unsaved changes",
|
||||
"Switching processor will discard unsaved changes. Continue?": "Switching processor will discard unsaved changes. Continue?",
|
||||
"Task submitted": "Task submitted",
|
||||
"Supported Extensions": "Supported Extensions",
|
||||
"All": "All",
|
||||
"Produces File": "Produces File",
|
||||
"Yes": "Yes",
|
||||
"No": "No",
|
||||
"Please select a processor": "Please select a processor",
|
||||
"Select a path": "Select a path",
|
||||
"Source Editor": "Source Editor",
|
||||
"Module Path": "Module Path",
|
||||
"Directory processing always overwrites original files": "Directory processing always overwrites original files",
|
||||
"No data": "No data",
|
||||
"Select File": "Select File",
|
||||
"Select Path": "Select Path",
|
||||
"Select Folder": "Select Folder",
|
||||
"Select": "Select",
|
||||
"Current": "Current",
|
||||
"Up": "Up",
|
||||
"Select Current Folder": "Select Current Folder",
|
||||
"Please select a file": "Please select a file",
|
||||
"Installed successfully": "Installed successfully",
|
||||
"Plugin": "Plugin",
|
||||
"Open Link": "Open Link",
|
||||
"Link copied": "Link copied",
|
||||
"Copy Link": "Copy Link",
|
||||
"Confirm delete this plugin?": "Confirm delete this plugin?",
|
||||
"Author": "Author",
|
||||
"Website": "Website",
|
||||
"Install App": "Install App",
|
||||
"Search name/author/url/extension": "Search name/author/url/extension",
|
||||
"No plugins": "No plugins",
|
||||
"Install": "Install",
|
||||
"App URL": "App URL",
|
||||
"Please input a valid URL": "Please input a valid URL",
|
||||
"Installed": "Installed",
|
||||
"Discover": "Discover",
|
||||
"Search apps": "Search apps",
|
||||
"Sort by": "Sort by",
|
||||
"Downloads": "Downloads",
|
||||
"Created (newest)": "Created (newest)",
|
||||
"Installed already": "Installed",
|
||||
"No results": "No results",
|
||||
"Initialization succeeded! Logging you in...": "Initialization succeeded! Logging you in...",
|
||||
"Initialization failed, please try later": "Initialization failed, please try later",
|
||||
"Database Setup": "Database Setup",
|
||||
"Choose database driver": "Choose database driver",
|
||||
"Select database and vector database for system data": "Select database and vector database for system data",
|
||||
"Database Driver": "Database Driver",
|
||||
"Vector DB Driver": "Vector DB Driver",
|
||||
"Initialize Mount": "Initialize Mount",
|
||||
"Configure initial storage": "Configure initial storage",
|
||||
"Create the first storage mount for your files": "Create the first storage mount for your files",
|
||||
"Mount Name": "Mount Name",
|
||||
"Local Storage": "Local Storage",
|
||||
"Please input mount name!": "Please input mount name!",
|
||||
"Storage Type": "Storage Type",
|
||||
"Please input mount path!": "Please input mount path!",
|
||||
"Root Directory": "Root Directory",
|
||||
"Please input root directory!": "Please input root directory!",
|
||||
"e.g., data/ or /var/foxel/data": "e.g., data/ or /var/foxel/data",
|
||||
"Optional, used for external links. Leave empty to use the current site.": "Optional, used for external links. Leave empty to use the current site.",
|
||||
"Create Admin": "Create Admin",
|
||||
"Create admin account": "Create admin account",
|
||||
"This is the first account with full permissions": "This is the first account with full permissions",
|
||||
"Username": "Username",
|
||||
"Please input a valid email!": "Please input a valid email!",
|
||||
"Please confirm your password!": "Please confirm your password!",
|
||||
"Passwords do not match!": "Passwords do not match!",
|
||||
"System Initialization": "System Initialization",
|
||||
"Previous": "Previous",
|
||||
"Next": "Next",
|
||||
"Finish Initialization": "Finish Initialization",
|
||||
"Plugin run failed": "Plugin run failed",
|
||||
"Plugin Error": "Plugin Error",
|
||||
"Cannot open file: no available app": "Cannot open file: no available app",
|
||||
"Error": "Error",
|
||||
"App \"{key}\" not found.": "App \"{key}\" not found.",
|
||||
"Open with {app}": "Open with {app}",
|
||||
"Set as default for .{ext}": "Set as default for .{ext}",
|
||||
"Advanced tokens must be valid JSON": "Advanced tokens must be valid JSON"
|
||||
}
|
||||
@@ -1,712 +0,0 @@
|
||||
export const en = {
|
||||
// General
|
||||
'All Files': 'All Files',
|
||||
'Manage': 'Manage',
|
||||
// 'System' defined above for navigation
|
||||
'Follow System': 'System',
|
||||
'Automation': 'Automation',
|
||||
'My Shares': 'My Shares',
|
||||
'Offline Downloads': 'Offline Downloads',
|
||||
'Adapters': 'Adapters',
|
||||
'Plugins': 'App Center',
|
||||
'System Settings': 'System Settings',
|
||||
'Backup & Restore': 'Backup & Restore',
|
||||
'System Logs': 'System Logs',
|
||||
|
||||
// Top header
|
||||
'Search files / tags / types': 'Search files / tags / types',
|
||||
'Log Out': 'Log Out',
|
||||
'Admin': 'Admin',
|
||||
'Profile': 'Profile',
|
||||
'Account Settings': 'Account Settings',
|
||||
'Language': 'Language',
|
||||
'Chinese': '中文',
|
||||
'English': 'English',
|
||||
'Full Name': 'Full Name',
|
||||
'Email': 'Email',
|
||||
'Change Password': 'Change Password',
|
||||
'Old Password': 'Old Password',
|
||||
'New Password': 'New Password',
|
||||
'Please fill both old and new password': 'Please fill both old and new password',
|
||||
|
||||
// Auth / Login
|
||||
'Welcome Back': 'Welcome Back',
|
||||
'Sign in to your Foxel account': 'Sign in to your Foxel account',
|
||||
'Username / Email': 'Username / Email',
|
||||
'Password': 'Password',
|
||||
'Sign In': 'Sign In',
|
||||
'Please enter username and password': 'Please enter username and password',
|
||||
'Login failed': 'Login failed',
|
||||
'Your next-generation file manager': 'Your next-generation file manager',
|
||||
'Cross-platform sync, access anywhere': 'Cross-platform sync, access anywhere',
|
||||
'AI-powered search for quick find': 'AI-powered search for quick find',
|
||||
'Flexible sharing and collaboration': 'Flexible sharing and collaboration',
|
||||
'Powerful automation to simplify tasks': 'Powerful automation to simplify tasks',
|
||||
'Join our community:': 'Join our community:',
|
||||
|
||||
// Share page
|
||||
'Refresh': 'Refresh',
|
||||
'Copy': 'Copy',
|
||||
// 'Cancel' already defined above
|
||||
'Copied link': 'Link copied',
|
||||
'Share canceled': 'Share canceled',
|
||||
'Cancel failed': 'Cancel failed',
|
||||
'Load failed': 'Load failed',
|
||||
'Are you sure to cancel share?': 'Are you sure to cancel share?',
|
||||
'Clear expired shares': 'Clear expired shares',
|
||||
'Confirm clear expired shares?': 'Confirm clear expired shares?',
|
||||
'Cleared {count} expired shares': 'Cleared {count} expired shares',
|
||||
|
||||
'Share Name': 'Share Name',
|
||||
'Share Content': 'Share Content',
|
||||
'Created At': 'Created At',
|
||||
'Expires At': 'Expires At',
|
||||
'Forever': 'Forever',
|
||||
'Access': 'Access',
|
||||
'Public': 'Public',
|
||||
'By Password': 'By Password',
|
||||
|
||||
// Public share page
|
||||
'Password Required': 'Password Required',
|
||||
'Please enter password': 'Please enter password',
|
||||
'Confirm': 'Confirm',
|
||||
'Unable to load share info': 'Unable to load share info',
|
||||
'Share load failed': 'Failed to load share',
|
||||
'Wrong password': 'Wrong password',
|
||||
'Root': 'All Files',
|
||||
'Created on {date}': 'Created on {date}',
|
||||
'Expires on {date}': 'Expires on {date}',
|
||||
'Download File': 'Download File',
|
||||
'Preview not supported for this file type': 'Preview not supported for this file type',
|
||||
'Back': 'Back',
|
||||
'Download': 'Download',
|
||||
|
||||
// Offline download
|
||||
'No offline download tasks': 'No offline download tasks',
|
||||
'Create Offline Download': 'Create Offline Download',
|
||||
'Offline Download Tasks': 'Offline Download Tasks',
|
||||
'URL': 'URL',
|
||||
'Please input URL': 'Please input URL',
|
||||
'Destination Folder': 'Destination Folder',
|
||||
'Select destination': 'Select destination',
|
||||
'Filename': 'Filename',
|
||||
'Please input filename': 'Please input filename',
|
||||
'Start Download': 'Start Download',
|
||||
'Stage': 'Stage',
|
||||
'Progress': 'Progress',
|
||||
'Bytes': 'Bytes',
|
||||
'Save Path': 'Save Path',
|
||||
'Queued': 'Queued',
|
||||
'Downloading': 'Downloading',
|
||||
'Transferring': 'Transferring',
|
||||
'Completed': 'Completed',
|
||||
'Pending': 'Pending',
|
||||
'Running': 'Running',
|
||||
'Success': 'Success',
|
||||
'Failed': 'Failed',
|
||||
// Header/File Explorer
|
||||
'Home': 'Home',
|
||||
'File Manager': 'File Manager',
|
||||
'New Folder': 'New Folder',
|
||||
'Upload': 'Upload',
|
||||
'Name': 'Name',
|
||||
'Size': 'Size',
|
||||
'Modified Time': 'Modified Time',
|
||||
'Grid': 'Grid',
|
||||
'List': 'List',
|
||||
'Mount Point': 'Mount Point',
|
||||
'Move': 'Move',
|
||||
'Move to': 'Move to',
|
||||
'Copy to': 'Copy to',
|
||||
'Destination path': 'Destination path',
|
||||
'Move task queued': 'Move task queued',
|
||||
'Move completed': 'Move completed',
|
||||
'Copy task queued': 'Copy task queued',
|
||||
'Copy completed': 'Copy completed',
|
||||
'Please input destination path': 'Please input destination path',
|
||||
|
||||
// Context menu
|
||||
'Upload File': 'Upload File',
|
||||
'Upload Files': 'Upload Files',
|
||||
'Upload Folder': 'Upload Folder',
|
||||
'Open': 'Open',
|
||||
'Open With': 'Open With',
|
||||
'Default': 'Default',
|
||||
'Processor': 'Processor',
|
||||
'Share': 'Share',
|
||||
'Rename': 'Rename',
|
||||
'Delete': 'Delete',
|
||||
'Details': 'Details',
|
||||
'Get Direct Link': 'Get Direct Link',
|
||||
|
||||
// Upload modal
|
||||
'Total progress': 'Total progress',
|
||||
'Upload bytes summary': '{uploaded} / {total}',
|
||||
'Upload task summary': 'Tasks: {completed} / {total} completed, {pending} pending, {failures} failed',
|
||||
'Overwrite confirmation required': 'Overwrite confirmation required',
|
||||
'Target already exists: {path}': 'Target already exists: {path}',
|
||||
'Overwrite': 'Overwrite',
|
||||
'Skip': 'Skip',
|
||||
'Overwrite All': 'Overwrite All',
|
||||
'Skip All': 'Skip All',
|
||||
'Directory': 'Directory',
|
||||
'Creating directory...': 'Creating directory...',
|
||||
'Directory ready': 'Directory ready',
|
||||
'Create directory failed': 'Create directory failed',
|
||||
'Waiting to create': 'Waiting to create',
|
||||
'Waiting for overwrite decision': 'Waiting for overwrite decision',
|
||||
'Waiting to upload': 'Waiting to upload',
|
||||
'Skipped': 'Skipped',
|
||||
'Upload succeeded': 'Upload succeeded',
|
||||
'Upload failed': 'Upload failed',
|
||||
'No items selected for upload': 'No items selected for upload',
|
||||
'No uploadable files or directories found': 'No uploadable files or directories found',
|
||||
'Missing file content': 'Missing file content',
|
||||
'Directory conflicts with existing file': 'A file with the same name already exists at the target location',
|
||||
|
||||
// Side nav modals
|
||||
'Join Community': 'Join Community',
|
||||
'Scan to join WeChat group': 'Scan to join WeChat group',
|
||||
'If QR expires, add drizzle2001 to join': 'If QR expires, add drizzle2001 to join',
|
||||
'Version Info': 'Version Info',
|
||||
'Current Version': 'Current Version',
|
||||
'Latest Version': 'Latest Version',
|
||||
'New version found: {version}': 'New version found: {version}',
|
||||
'Please update to the latest for features and fixes': 'Please update to the latest for features and fixes',
|
||||
'Open Releases': 'Open Releases',
|
||||
'Changelog': 'Changelog',
|
||||
'Fetching latest version...': 'Fetching latest version...',
|
||||
'Update available': 'Update available',
|
||||
'You are on the latest: {version}': 'You are on the latest: {version}',
|
||||
'Up to date': 'Up to date',
|
||||
|
||||
// Share modal
|
||||
'Share {count} items': 'Share {count} items',
|
||||
'Share link created': 'Share link created',
|
||||
'Create failed': 'Create failed',
|
||||
'Copied to clipboard': 'Copied to clipboard',
|
||||
'Expiration (days)': 'Expiration (days)',
|
||||
'Set 0 or negative for forever': 'Set 0 or negative for forever',
|
||||
'Share link created successfully!': 'Share link created successfully!',
|
||||
'Share Link': 'Share Link',
|
||||
'Share created': 'Share created',
|
||||
'Create Share': 'Create Share',
|
||||
'Done': 'Done',
|
||||
'Create': 'Create',
|
||||
|
||||
// Direct link modal
|
||||
'Failed to generate link': 'Failed to generate link',
|
||||
'Markdown copied to clipboard': 'Markdown copied to clipboard',
|
||||
'Generate a direct link for {name}': 'Generate a direct link for {name}',
|
||||
'1 hour': '1 hour',
|
||||
'1 day': '1 day',
|
||||
'7 days': '7 days',
|
||||
'Generating link...': 'Generating link...',
|
||||
'Link will appear here': 'Link will appear here',
|
||||
'Copy Markdown': 'Copy Markdown',
|
||||
'Close': 'Close',
|
||||
|
||||
// Task queue
|
||||
'Task Queue': 'Task Queue',
|
||||
'Last updated at {time}': 'Last updated at {time}',
|
||||
'Total Tasks': 'Total Tasks',
|
||||
'Running Tasks': 'Running Tasks',
|
||||
'Waiting Tasks': 'Waiting Tasks',
|
||||
'Failed Tasks': 'Failed Tasks',
|
||||
'Active Workers': 'Active Workers',
|
||||
'Task Type': 'Task Type',
|
||||
'Search by name or ID': 'Search by name or ID',
|
||||
'Filter by status': 'Filter by status',
|
||||
'Queue Concurrency': 'Queue Concurrency',
|
||||
'Settings saved': 'Settings saved',
|
||||
'Expand': 'Expand',
|
||||
'Adjust worker concurrency immediately': 'Adjust worker concurrency immediately',
|
||||
'Auto': 'Auto',
|
||||
'Manual': 'Manual',
|
||||
|
||||
// File detail
|
||||
'Camera Make': 'Camera Make',
|
||||
'Camera Model': 'Camera Model',
|
||||
'Capture Time': 'Capture Time',
|
||||
'X Resolution': 'X Resolution',
|
||||
'Y Resolution': 'Y Resolution',
|
||||
'Exposure Time': 'Exposure Time',
|
||||
'Aperture': 'Aperture',
|
||||
'Focal Length': 'Focal Length',
|
||||
'Width': 'Width',
|
||||
'Height': 'Height',
|
||||
'No common EXIF info': 'No common EXIF info',
|
||||
'File Properties': 'File Properties',
|
||||
'Loading file info...': 'Loading file info...',
|
||||
'Basic Info': 'Basic Info',
|
||||
'Type': 'Type',
|
||||
'Folder': 'Folder',
|
||||
'File': 'File',
|
||||
'Path': 'Path',
|
||||
'Path copied to clipboard': 'Path copied to clipboard',
|
||||
'Copy failed': 'Copy failed',
|
||||
'Permissions': 'Permissions',
|
||||
'EXIF Info': 'EXIF Info',
|
||||
'Index Info': 'Index Info',
|
||||
'Indexed Items': 'Indexed Items',
|
||||
'Indexed Types': 'Indexed Types',
|
||||
'No index data': 'No index data',
|
||||
'Indexed Chunks': 'Indexed Chunks',
|
||||
'More Indexed Chunks': 'More Indexed Chunks',
|
||||
'Chunk ID': 'Chunk ID',
|
||||
'Offset Range': 'Offset Range',
|
||||
'Vector ID': 'Vector ID',
|
||||
'Preview': 'Preview',
|
||||
'Showing first {count} entries': 'Showing first {count} entries',
|
||||
|
||||
// Search dialog
|
||||
'Smart Search': 'Smart Search',
|
||||
'Name Search': 'Name Search',
|
||||
'Search Results': 'Search Results',
|
||||
'No files found': 'No files found',
|
||||
'Relevance': 'Relevance',
|
||||
|
||||
// System settings
|
||||
'Saved successfully': 'Saved successfully',
|
||||
'Save failed': 'Save failed',
|
||||
'Loading...': 'Loading...',
|
||||
'Appearance Settings': 'Appearance Settings',
|
||||
'Theme': 'Theme',
|
||||
'Theme Mode': 'Theme Mode',
|
||||
'Light': 'Light',
|
||||
'Dark': 'Dark',
|
||||
// 'Follow System' used for theme mode
|
||||
'Primary Color': 'Primary Color',
|
||||
'Border Radius': 'Border Radius',
|
||||
'Advanced': 'Advanced',
|
||||
'Override AntD Tokens (JSON)': 'Override AntD Tokens (JSON)',
|
||||
'e.g. {"colorText": "#222"}': 'e.g. {"colorText": "#222"}',
|
||||
'Custom CSS': 'Custom CSS',
|
||||
'Save': 'Save',
|
||||
'App Settings': 'App Settings',
|
||||
'Email Settings': 'Email Settings',
|
||||
'AI Settings': 'AI Settings',
|
||||
'Protocol Mappings': 'Protocol Mappings',
|
||||
'S3 Mapping': 'S3 Mapping',
|
||||
'S3 Endpoint': 'S3 Endpoint',
|
||||
'Bucket Name': 'Bucket Name',
|
||||
'Bucket API Path': 'Bucket API Path',
|
||||
'Region': 'Region',
|
||||
'Base Path': 'Base Path',
|
||||
'Access Key': 'Access Key',
|
||||
'Secret Key': 'Secret Key',
|
||||
'Vision Model': 'Vision Model',
|
||||
'Embedding Model': 'Embedding Model',
|
||||
'Embedding Dimension': 'Embedding Dimension',
|
||||
'Vector Database': 'Vector Database',
|
||||
'Vector Database Settings': 'Vector Database Settings',
|
||||
'Current Statistics': 'Current Statistics',
|
||||
'Collections': 'Collections',
|
||||
'Vectors': 'Vectors',
|
||||
'Database Size': 'Database Size',
|
||||
'Estimated Memory': 'Estimated Memory',
|
||||
'No collections': 'No collections',
|
||||
'Dimension': 'Dimension',
|
||||
'Non-vector collection': 'Non-vector collection',
|
||||
'Estimated memory': 'Estimated memory',
|
||||
'Indexes': 'Indexes',
|
||||
'Unnamed index': 'Unnamed index',
|
||||
'Indexed rows': 'Indexed rows',
|
||||
'Pending rows': 'Pending rows',
|
||||
'Estimated memory is calculated as vectors x dimension x 4 bytes (float32).': 'Estimated memory is calculated as vectors x dimension x 4 bytes (float32).',
|
||||
'Database Provider': 'Database Provider',
|
||||
'Please select a provider': 'Please select a provider',
|
||||
'Coming soon': 'Coming soon',
|
||||
'This provider is not available yet': 'This provider is not available yet',
|
||||
'Database file path': 'Database file path',
|
||||
'Server URI': 'Server URI',
|
||||
'Token': 'Token',
|
||||
'Server URL': 'Server URL',
|
||||
'API Key': 'API Key',
|
||||
'Embedded Milvus Lite (local file storage).': 'Embedded Milvus Lite (local file storage).',
|
||||
'Remote Milvus instance accessed via URI.': 'Remote Milvus instance accessed via URI.',
|
||||
'Qdrant vector database (HTTP API).': 'Qdrant vector database (HTTP API).',
|
||||
'Database Type': 'Database Type',
|
||||
'Confirm embedding dimension change': 'Confirm embedding dimension change',
|
||||
'Changing the embedding dimension will clear the vector database automatically. You will need to rebuild indexes afterwards. Continue?': 'Changing the embedding dimension will clear the vector database automatically. You will need to rebuild indexes afterwards. Continue?',
|
||||
'Confirm clear vector database?': 'Confirm clear vector database?',
|
||||
'This will delete all collections irreversibly.': 'This will delete all collections irreversibly.',
|
||||
'Confirm Clear': 'Confirm Clear',
|
||||
// 'Cancel' defined above
|
||||
'Vector database cleared': 'Vector database cleared',
|
||||
'Clear failed': 'Clear failed',
|
||||
'Clear Vector DB': 'Clear Vector DB',
|
||||
'App Name': 'App Name',
|
||||
'Logo URL': 'Logo URL',
|
||||
'Favicon URL': 'Favicon URL',
|
||||
'App Domain': 'App Domain',
|
||||
'File Domain': 'File Domain',
|
||||
'Configure Access Key and Secret to enable S3 mapping.': 'Configure Access Key and Secret to enable S3 mapping.',
|
||||
'Mount point inside the virtual file system (e.g. / or /workspace).': 'Mount point inside the virtual file system (e.g. / or /workspace).',
|
||||
'Please input bucket name': 'Please input bucket name',
|
||||
'Please input region': 'Please input region',
|
||||
'Please input access key': 'Please input access key',
|
||||
'Please input secret key': 'Please input secret key',
|
||||
'Save S3 Settings': 'Save S3 Settings',
|
||||
'Example CLI command': 'Example CLI command',
|
||||
'WebDAV Mapping': 'WebDAV Mapping',
|
||||
'WebDAV Endpoint': 'WebDAV Endpoint',
|
||||
'Basic (system account password)': 'Basic (system account password)',
|
||||
'Root Path': 'Root Path',
|
||||
'Client Compatibility': 'Client Compatibility',
|
||||
'Supports Finder, Windows network drive, rclone, and other WebDAV clients.': 'Supports Finder, Windows network drive, rclone, and other WebDAV clients.',
|
||||
'Toggle the switch to expose the virtual file system via WebDAV.': 'Toggle the switch to expose the virtual file system via WebDAV.',
|
||||
'SMTP Settings': 'SMTP Settings',
|
||||
'SMTP Host': 'SMTP Host',
|
||||
'Please input SMTP host': 'Please input SMTP host',
|
||||
'SMTP Port': 'SMTP Port',
|
||||
'Please input SMTP port': 'Please input SMTP port',
|
||||
'Security': 'Security',
|
||||
'None': 'None',
|
||||
'SSL': 'SSL',
|
||||
'STARTTLS': 'STARTTLS',
|
||||
'Timeout (seconds)': 'Timeout (seconds)',
|
||||
'Sender': 'Sender',
|
||||
'Sender Name': 'Sender Name',
|
||||
'Sender Email': 'Sender Email',
|
||||
'Please input sender email': 'Please input sender email',
|
||||
'Authentication': 'Authentication',
|
||||
'SMTP Username': 'SMTP Username',
|
||||
'SMTP Password': 'SMTP Password',
|
||||
'Test Email': 'Test Email',
|
||||
'Current Configuration': 'Current Configuration',
|
||||
'Available variables': 'Available variables',
|
||||
'Not set': 'Not set',
|
||||
'Password Reset Template': 'Password Reset Template',
|
||||
'Live Preview': 'Live Preview',
|
||||
'Foxel Mail Test': 'Foxel Mail Test',
|
||||
'Recipient Address': 'Recipient Address',
|
||||
'Please input recipient email': 'Please input recipient email',
|
||||
'Test Subject': 'Test Subject',
|
||||
'Test User Name': 'Test User Name',
|
||||
'Optional': 'Optional',
|
||||
'Send Test Email': 'Send Test Email',
|
||||
'Please complete all required fields': 'Please complete all required fields',
|
||||
'SMTP port must be a positive number': 'SMTP port must be a positive number',
|
||||
'Test email queued (task {{taskId}})': 'Test email queued (task {{taskId}})',
|
||||
'Test email failed': 'Test email failed',
|
||||
|
||||
// Auth reset
|
||||
'Forgot Password?': 'Forgot password?',
|
||||
'Reset Your Password': 'Reset Your Password',
|
||||
'Enter the email linked to your account and we will send a reset link.': 'Enter the email linked to your account and we will send a reset link.',
|
||||
'If the email exists, a reset link has been sent.': 'If the email exists, a reset link has been sent.',
|
||||
'Send Reset Link': 'Send Reset Link',
|
||||
'Resend Link': 'Resend Link',
|
||||
'Back to login': 'Back to login',
|
||||
'Request failed': 'Request failed',
|
||||
'Reset link is invalid': 'Reset link is invalid',
|
||||
'Reset link is invalid or expired': 'Reset link is invalid or expired',
|
||||
'Reset failed': 'Reset failed',
|
||||
'Try again': 'Try again',
|
||||
'Set a new password': 'Set a new password',
|
||||
'Please enter new password': 'Please enter new password',
|
||||
'Confirm Password': 'Confirm Password',
|
||||
'Please confirm new password': 'Please confirm new password',
|
||||
'Update Password': 'Update Password',
|
||||
'Passwords do not match': 'Passwords do not match',
|
||||
'Password updated, please login again.': 'Password updated, please login again.',
|
||||
'Failed to reset password': 'Failed to reset password',
|
||||
'Vision API URL': 'Vision API URL',
|
||||
'Vision API Key': 'Vision API Key',
|
||||
'Embedding API URL': 'Embedding API URL',
|
||||
'Embedding API Key': 'Embedding API Key',
|
||||
'AI Providers & Models': 'AI Providers & Models',
|
||||
'Manage AI providers, synchronize compatible models, and configure default capabilities across the system.': 'Manage AI providers, synchronize compatible models, and configure default capabilities across the system.',
|
||||
'Add Provider': 'Add Provider',
|
||||
'Edit Provider': 'Edit Provider',
|
||||
'Pull Models': 'Pull Models',
|
||||
'Manual Add': 'Manual Add',
|
||||
'Clear Remote List': 'Clear Remote List',
|
||||
'Select models from the list to add them automatically': 'Select models from the list to add them automatically',
|
||||
'No remote models': 'No remote models',
|
||||
'No remote models found': 'No remote models found',
|
||||
'No remote models match search': 'No remote models match search',
|
||||
'Search fetched models': 'Search fetched models',
|
||||
'Already Added': 'Already Added',
|
||||
'Add Selected Models': 'Add Selected Models',
|
||||
'Fetch failed': 'Fetch failed',
|
||||
'Select models to add': 'Select models to add',
|
||||
'Added {count} models': 'Added {count} models',
|
||||
'Choose Template': 'Choose Template',
|
||||
'Configure Provider': 'Configure Provider',
|
||||
'Back to Templates': 'Back to Templates',
|
||||
'View Docs': 'View Docs',
|
||||
'Custom Provider': 'Custom Provider',
|
||||
'Custom Provider Description': 'Bring your own endpoint compatible with OpenAI or Gemini formats.',
|
||||
'OpenAI Provider': 'OpenAI',
|
||||
'OpenAI Provider Description': 'Access GPT-4o, GPT-4.1, GPT-3.5 and more models from OpenAI.',
|
||||
'Azure OpenAI Provider': 'Azure OpenAI',
|
||||
'Azure OpenAI Provider Description': 'Use OpenAI models deployed on Microsoft Azure.',
|
||||
'Google AI Provider': 'Google AI',
|
||||
'Google AI Provider Description': 'Gemini series models served via the Google AI platform.',
|
||||
'SiliconFlow Provider': 'SiliconFlow',
|
||||
'SiliconFlow Provider Description': 'High-performance inference platform with OpenAI-compatible APIs.',
|
||||
'OpenRouter Provider': 'OpenRouter',
|
||||
'OpenRouter Provider Description': 'Connect to multiple AI providers through a single OpenAI-style endpoint.',
|
||||
'Anthropic Provider': 'Anthropic',
|
||||
'Anthropic Provider Description': 'Claude 3 family models exposed through the Claude API.',
|
||||
'DeepSeek Provider': 'DeepSeek',
|
||||
'DeepSeek Provider Description': 'DeepSeek language models via OpenAI-compatible API.',
|
||||
'Grok Provider': 'Grok (xAI)',
|
||||
'Grok Provider Description': 'Grok models powered by xAI with OpenAI-style routes.',
|
||||
'Ollama Provider': 'Ollama',
|
||||
'Ollama Provider Description': 'Self-host and run models locally with Ollama\'s OpenAI bridge.',
|
||||
'Voyage Provider': 'Voyage AI',
|
||||
'Voyage Provider Description': 'High-quality embeddings and rerankers from Voyage AI.',
|
||||
'Delete provider?': 'Delete provider?',
|
||||
'Deleting this provider will also remove all associated models. Continue?': 'Deleting this provider will also remove all associated models. Continue?',
|
||||
'Deleted successfully': 'Deleted successfully',
|
||||
'Sync Models': 'Sync Models',
|
||||
'Sync completed: {created} created, {updated} updated': 'Sync completed: {created} created, {updated} updated',
|
||||
'Sync failed': 'Sync failed',
|
||||
'Add Model': 'Add Model',
|
||||
'Edit Model': 'Edit Model',
|
||||
'Delete model?': 'Delete model?',
|
||||
'This operation cannot be undone. Continue?': 'This operation cannot be undone. Continue?',
|
||||
'No models yet': 'No models yet',
|
||||
'Add your first AI provider to get started': 'Add your first AI provider to get started',
|
||||
'Default Models Configuration': 'Default Models Configuration',
|
||||
'Main Chat Model': 'Main Chat Model',
|
||||
'Primary assistant for conversations, reasoning, and tool calls.': 'Primary assistant for conversations, reasoning, and tool calls.',
|
||||
'Handles multimodal perception such as image understanding.': 'Handles multimodal perception such as image understanding.',
|
||||
'Transforms content into dense vectors for search and retrieval.': 'Transforms content into dense vectors for search and retrieval.',
|
||||
'Optimises ranking quality for search candidates.': 'Optimises ranking quality for search candidates.',
|
||||
'Covers text-to-speech and speech understanding scenarios.': 'Covers text-to-speech and speech understanding scenarios.',
|
||||
'Supports function calling, orchestration, and automation.': 'Supports function calling, orchestration, and automation.',
|
||||
'Select a model': 'Select a model',
|
||||
'Template': 'Template',
|
||||
'Select a template': 'Select a template',
|
||||
'Display Name': 'Display Name',
|
||||
'Enter name': 'Enter name',
|
||||
'Identifier': 'Identifier',
|
||||
'Enter identifier': 'Enter identifier',
|
||||
'Only lowercase letters, numbers, dash, dot and underscore are allowed': 'Only lowercase letters, numbers, dash, dot and underscore are allowed',
|
||||
'API Format': 'API Format',
|
||||
'Base URL': 'Base URL',
|
||||
'Enter base url': 'Enter base URL',
|
||||
'Optional, can also be provided per request': 'Optional, can also be provided per request',
|
||||
'Model Identifier': 'Model Identifier',
|
||||
'Enter model identifier': 'Enter model identifier',
|
||||
'Description': 'Description',
|
||||
'Capabilities': 'Capabilities',
|
||||
'Context Window': 'Context Window',
|
||||
'Embedding Dimensions': 'Embedding Dimensions',
|
||||
'Price /1K input tokens': 'Price /1K input tokens',
|
||||
'Price /1K output tokens': 'Price /1K output tokens',
|
||||
|
||||
// Adapters
|
||||
'Missing required config:': 'Missing required config:',
|
||||
'Updated successfully': 'Updated successfully',
|
||||
'Created successfully': 'Created successfully',
|
||||
'Operation failed': 'Operation failed',
|
||||
'Deleted': 'Deleted',
|
||||
'Delete failed': 'Delete failed',
|
||||
'Status updated': 'Status updated',
|
||||
'Update failed': 'Update failed',
|
||||
'Mount Path': 'Mount Path',
|
||||
'Sub Path': 'Sub Path',
|
||||
'Sub Path (optional)': 'Sub Path (optional)',
|
||||
'Sub directory inside adapter': 'Sub directory inside adapter',
|
||||
'Enabled': 'Enabled',
|
||||
'Actions': 'Actions',
|
||||
'Edit': 'Edit',
|
||||
'Confirm delete?': 'Confirm delete?',
|
||||
'No config fields': 'No config fields',
|
||||
'Please input {label}': 'Please input {label}',
|
||||
'Storage Adapters': 'Storage Adapters',
|
||||
'Create Adapter': 'Create Adapter',
|
||||
'Unique name': 'Unique name',
|
||||
'Select adapter type': 'Select adapter type',
|
||||
'/ or /drive': '/ or /drive',
|
||||
'Adapter Config': 'Adapter Config',
|
||||
'adapter.type.local': 'Local Filesystem',
|
||||
'adapter.type.webdav': 'WebDAV',
|
||||
'adapter.type.googledrive': 'Google Drive',
|
||||
'adapter.type.onedrive': 'OneDrive',
|
||||
'adapter.type.s3': 'Amazon S3',
|
||||
'adapter.type.ftp': 'FTP',
|
||||
'adapter.type.sftp': 'SFTP',
|
||||
'adapter.type.telegram': 'Telegram',
|
||||
'adapter.type.quark': 'Quark Drive',
|
||||
|
||||
// Tasks
|
||||
'Automation Tasks': 'Automation Tasks',
|
||||
'Create Task': 'Create Task',
|
||||
'Edit Task': 'Edit Task',
|
||||
'Create Automation Task': 'Create Automation Task',
|
||||
'Task Name': 'Task Name',
|
||||
'Trigger Event': 'Trigger Event',
|
||||
'File Written': 'File Written',
|
||||
'File Deleted': 'File Deleted',
|
||||
'Matching Rules': 'Matching Rules',
|
||||
'Path Prefix (optional)': 'Path Prefix (optional)',
|
||||
'Filename Regex (optional)': 'Filename Regex (optional)',
|
||||
'Action': 'Action',
|
||||
'Current Task Queue': 'Current Task Queue',
|
||||
'Params': 'Params',
|
||||
'Status': 'Status',
|
||||
|
||||
// Logs
|
||||
'Confirm clear logs?': 'Confirm clear logs?',
|
||||
'This will delete logs in selected range irreversibly.': 'This will delete logs in selected range irreversibly.',
|
||||
'Cleared {count} logs': 'Cleared {count} logs',
|
||||
'Time': 'Time',
|
||||
'Level': 'Level',
|
||||
'Source': 'Source',
|
||||
'Message': 'Message',
|
||||
'User ID': 'User ID',
|
||||
'Search source': 'Search source',
|
||||
'Clear': 'Clear',
|
||||
'Log Details': 'Log Details',
|
||||
'Raw Log': 'Raw Log',
|
||||
|
||||
// Backup
|
||||
'Export started, check your downloads.': 'Export started, check your downloads.',
|
||||
'Export failed': 'Export failed',
|
||||
'Confirm import backup?': 'Confirm import backup?',
|
||||
'Are you sure to import from this file?': 'Are you sure to import from this file?',
|
||||
'Warning: This will overwrite all data including users (with passwords), settings, storages and tasks. Irreversible!': 'Warning: This will overwrite all data including users (with passwords), settings, storages and tasks. Irreversible!',
|
||||
'Confirm Import': 'Confirm Import',
|
||||
'Import succeeded! The page will refresh.': 'Import succeeded! The page will refresh.',
|
||||
'Import failed': 'Import failed',
|
||||
'Export': 'Export',
|
||||
'Import': 'Import',
|
||||
'Export all data (adapters, users, tasks, shares) into a JSON file.': 'Export all data (adapters, users, tasks, shares) into a JSON file.',
|
||||
'Keep your backup file safe.': 'Keep your backup file safe.',
|
||||
'Export Backup': 'Export Backup',
|
||||
'Restore data from a previously exported JSON file.': 'Restore data from a previously exported JSON file.',
|
||||
'Warning: This will clear and overwrite existing data.': 'Warning: This will clear and overwrite existing data.',
|
||||
'Choose File and Restore': 'Choose File and Restore',
|
||||
|
||||
// Empty state
|
||||
'No files yet here': 'No files yet here',
|
||||
'This folder is empty': 'This folder is empty',
|
||||
'Start uploading files or create folders to organize your content': 'Start uploading files or create folders to organize your content',
|
||||
'You can create folders or upload files here': 'You can create folders or upload files here',
|
||||
|
||||
// File actions
|
||||
'Please input name': 'Please input name',
|
||||
'Confirm delete {name}?': 'Confirm delete {name}?',
|
||||
'items': 'items',
|
||||
'Downloading folders is not supported': 'Downloading folders is not supported',
|
||||
'Download failed': 'Download failed',
|
||||
'Please select files or folders to share': 'Please select files or folders to share',
|
||||
'Direct links for folders are not supported': 'Direct links for folders are not supported',
|
||||
|
||||
// Processor flow
|
||||
'Processing finished': 'Processing finished',
|
||||
'Processing failed': 'Processing failed',
|
||||
'Processors': 'Processors',
|
||||
'Processor List': 'Processor List',
|
||||
'Reload': 'Reload',
|
||||
'Run Processor': 'Run Processor',
|
||||
'Target Path': 'Target Path',
|
||||
'Please select a path': 'Please select a path',
|
||||
'Select Directory': 'Select Directory',
|
||||
'Overwrite original': 'Overwrite original',
|
||||
'Save To': 'Save To',
|
||||
'Optional output path': 'Optional output path',
|
||||
'Run': 'Run',
|
||||
'Select a processor': 'Select a processor',
|
||||
'No module path': 'No module path',
|
||||
'Source saved': 'Source saved',
|
||||
'Processors reloaded': 'Processors reloaded',
|
||||
'Unsaved changes': 'Unsaved changes',
|
||||
'Switching processor will discard unsaved changes. Continue?': 'Switching processor will discard unsaved changes. Continue?',
|
||||
'Task submitted': 'Task submitted',
|
||||
'Supported Extensions': 'Supported Extensions',
|
||||
'All': 'All',
|
||||
'Produces File': 'Produces File',
|
||||
'Yes': 'Yes',
|
||||
'No': 'No',
|
||||
'Please select a processor': 'Please select a processor',
|
||||
'Select a path': 'Select a path',
|
||||
'Source Editor': 'Source Editor',
|
||||
'Module Path': 'Module Path',
|
||||
'Directory processing always overwrites original files': 'Directory processing always overwrites original files',
|
||||
'No data': 'No data',
|
||||
|
||||
// Path selector
|
||||
'Select File': 'Select File',
|
||||
'Select Path': 'Select Path',
|
||||
'Select Folder': 'Select Folder',
|
||||
'Select': 'Select',
|
||||
'Current': 'Current',
|
||||
'Up': 'Up',
|
||||
'Select Current Folder': 'Select Current Folder',
|
||||
'Please select a file': 'Please select a file',
|
||||
|
||||
// Plugins page
|
||||
'Installed successfully': 'Installed successfully',
|
||||
'Plugin': 'Plugin',
|
||||
'Open Link': 'Open Link',
|
||||
'Link copied': 'Link copied',
|
||||
'Copy Link': 'Copy Link',
|
||||
'Confirm delete this plugin?': 'Confirm delete this plugin?',
|
||||
'Author': 'Author',
|
||||
'Website': 'Website',
|
||||
'Install App': 'Install App',
|
||||
'Search name/author/url/extension': 'Search name/author/url/extension',
|
||||
'No plugins': 'No plugins',
|
||||
'Install': 'Install',
|
||||
'App URL': 'App URL',
|
||||
'Please input a valid URL': 'Please input a valid URL',
|
||||
'Installed': 'Installed',
|
||||
'Discover': 'Discover',
|
||||
'Search apps': 'Search apps',
|
||||
'Sort by': 'Sort by',
|
||||
'Downloads': 'Downloads',
|
||||
'Created (newest)': 'Created (newest)',
|
||||
'Installed already': 'Installed',
|
||||
'No results': 'No results',
|
||||
|
||||
// Setup page
|
||||
'Initialization succeeded! Logging you in...': 'Initialization succeeded! Logging you in...',
|
||||
'Initialization failed, please try later': 'Initialization failed, please try later',
|
||||
'Database Setup': 'Database Setup',
|
||||
'Choose database driver': 'Choose database driver',
|
||||
'Select database and vector database for system data': 'Select database and vector database for system data',
|
||||
'Database Driver': 'Database Driver',
|
||||
'Vector DB Driver': 'Vector DB Driver',
|
||||
'Initialize Mount': 'Initialize Mount',
|
||||
'Configure initial storage': 'Configure initial storage',
|
||||
'Create the first storage mount for your files': 'Create the first storage mount for your files',
|
||||
'Mount Name': 'Mount Name',
|
||||
'Local Storage': 'Local Storage',
|
||||
'Please input mount name!': 'Please input mount name!',
|
||||
'Storage Type': 'Storage Type',
|
||||
'Please input mount path!': 'Please input mount path!',
|
||||
'Root Directory': 'Root Directory',
|
||||
'Please input root directory!': 'Please input root directory!',
|
||||
'e.g., data/ or /var/foxel/data': 'e.g., data/ or /var/foxel/data',
|
||||
'Optional, used for external links. Leave empty to use the current site.': 'Optional, used for external links. Leave empty to use the current site.',
|
||||
'Create Admin': 'Create Admin',
|
||||
'Create admin account': 'Create admin account',
|
||||
'This is the first account with full permissions': 'This is the first account with full permissions',
|
||||
'Username': 'Username',
|
||||
'Please input a valid email!': 'Please input a valid email!',
|
||||
'Please confirm your password!': 'Please confirm your password!',
|
||||
'Passwords do not match!': 'Passwords do not match!',
|
||||
'System Initialization': 'System Initialization',
|
||||
'Previous': 'Previous',
|
||||
'Next': 'Next',
|
||||
'Finish Initialization': 'Finish Initialization',
|
||||
|
||||
// Plugin host
|
||||
'Plugin run failed': 'Plugin run failed',
|
||||
'Plugin Error': 'Plugin Error',
|
||||
'Cannot open file: no available app': 'Cannot open file: no available app',
|
||||
'Error': 'Error',
|
||||
'App "{key}" not found.': 'App "{key}" not found.',
|
||||
'Open with {app}': 'Open with {app}',
|
||||
'Set as default for .{ext}': 'Set as default for .{ext}',
|
||||
'Advanced tokens must be valid JSON': 'Advanced tokens must be valid JSON',
|
||||
} as const;
|
||||
|
||||
export type EnKeys = keyof typeof en;
|
||||
655
web/src/i18n/locales/zh.json
Normal file
655
web/src/i18n/locales/zh.json
Normal file
@@ -0,0 +1,655 @@
|
||||
{
|
||||
"All Files": "全部文件",
|
||||
"Manage": "管理",
|
||||
"System": "系统",
|
||||
"Automation": "自动任务",
|
||||
"My Shares": "我的分享",
|
||||
"Offline Downloads": "离线下载",
|
||||
"No offline download tasks": "暂无离线下载任务",
|
||||
"Create Offline Download": "创建离线下载任务",
|
||||
"Offline Download Tasks": "离线下载任务列表",
|
||||
"URL": "下载地址",
|
||||
"Please input URL": "请输入下载地址",
|
||||
"Destination Folder": "保存目录",
|
||||
"Select destination": "请选择保存目录",
|
||||
"Filename": "文件名",
|
||||
"Please input filename": "请输入文件名",
|
||||
"Start Download": "开始下载",
|
||||
"Stage": "阶段",
|
||||
"Progress": "进度",
|
||||
"Bytes": "已传输",
|
||||
"Save Path": "保存路径",
|
||||
"Queued": "排队中",
|
||||
"Downloading": "下载中",
|
||||
"Transferring": "转存中",
|
||||
"Completed": "已完成",
|
||||
"Pending": "等待",
|
||||
"Running": "进行中",
|
||||
"Success": "成功",
|
||||
"Failed": "失败",
|
||||
"Failure": "失败",
|
||||
"Adapters": "存储挂载",
|
||||
"Plugins": "应用中心",
|
||||
"System Settings": "系统设置",
|
||||
"Backup & Restore": "备份恢复",
|
||||
"System Logs": "系统日志",
|
||||
"Audit Logs": "审计日志",
|
||||
"Audit Log Details": "审计日志详情",
|
||||
"Search files / tags / types": "搜索文件 / 标签 / 类型",
|
||||
"Log Out": "退出登录",
|
||||
"Admin": "管理员",
|
||||
"Profile": "个人资料",
|
||||
"Account Settings": "账户设置",
|
||||
"Language": "语言",
|
||||
"Full Name": "昵称",
|
||||
"Email": "邮箱",
|
||||
"Change Password": "修改密码",
|
||||
"Old Password": "原密码",
|
||||
"New Password": "新密码",
|
||||
"Please fill both old and new password": "请同时填写原密码和新密码",
|
||||
"Welcome Back": "欢迎回来",
|
||||
"Sign in to your Foxel account": "登录到您的 Foxel 账户",
|
||||
"Username / Email": "用户名/邮箱",
|
||||
"Password": "密码",
|
||||
"Sign In": "登录",
|
||||
"Please enter username and password": "请输入用户名与密码",
|
||||
"Login failed": "登录失败",
|
||||
"Forgot Password?": "忘记密码?",
|
||||
"Your next-generation file manager": "您的下一代文件管理系统",
|
||||
"Cross-platform sync, access anywhere": "跨平台同步,随时随地访问",
|
||||
"AI-powered search for quick find": "AI 驱动的智能搜索,快速定位文件",
|
||||
"Flexible sharing and collaboration": "灵活的分享与协作,提升团队效率",
|
||||
"Powerful automation to simplify tasks": "强大的自动化工作流,简化繁琐任务",
|
||||
"Join our community:": "加入我们的社区:",
|
||||
"Reset Your Password": "重置你的密码",
|
||||
"Enter the email linked to your account and we will send a reset link.": "请输入你账户绑定的邮箱,我们会发送重置链接。",
|
||||
"If the email exists, a reset link has been sent.": "如果邮箱存在,我们已发送重置链接。",
|
||||
"Send Reset Link": "发送重置链接",
|
||||
"Resend Link": "重新发送链接",
|
||||
"Back to login": "返回登录",
|
||||
"Request failed": "请求失败",
|
||||
"Reset link is invalid": "重置链接无效",
|
||||
"Reset link is invalid or expired": "重置链接无效或已过期",
|
||||
"Reset failed": "重置失败",
|
||||
"Try again": "重试",
|
||||
"Set a new password": "设置新密码",
|
||||
"Please enter new password": "请输入新密码",
|
||||
"Confirm Password": "确认新密码",
|
||||
"Please confirm new password": "请确认新密码",
|
||||
"Update Password": "更新密码",
|
||||
"Passwords do not match": "两次输入的密码不一致",
|
||||
"Password updated, please login again.": "密码已更新,请重新登录。",
|
||||
"Failed to reset password": "密码重置失败",
|
||||
"Refresh": "刷新",
|
||||
"Copy": "复制",
|
||||
"Cancel": "取消",
|
||||
"Copied link": "链接已复制",
|
||||
"Share canceled": "分享已取消",
|
||||
"Cancel failed": "取消失败",
|
||||
"Load failed": "加载失败",
|
||||
"Are you sure to cancel share?": "确认取消分享?",
|
||||
"Clear expired shares": "清空过期分享",
|
||||
"Confirm clear expired shares?": "确认清空过期分享?",
|
||||
"Cleared {count} expired shares": "已清理 {count} 个过期分享",
|
||||
"Please select time range": "请选择时间范围",
|
||||
"Share Name": "分享名称",
|
||||
"Share Content": "分享内容",
|
||||
"Created At": "创建时间",
|
||||
"Expires At": "过期时间",
|
||||
"Forever": "永久有效",
|
||||
"Access": "访问",
|
||||
"Public": "公开",
|
||||
"By Password": "密码",
|
||||
"Password Required": "需要密码",
|
||||
"Please enter password": "请输入密码",
|
||||
"Confirm": "确认",
|
||||
"Unable to load share info": "无法加载分享信息",
|
||||
"Share load failed": "加载分享失败",
|
||||
"Wrong password": "密码错误",
|
||||
"Root": "全部文件",
|
||||
"Created on {date}": "创建于 {date}",
|
||||
"Expires on {date}": "将于 {date} 过期",
|
||||
"Download File": "下载文件",
|
||||
"Preview not supported for this file type": "暂不支持在线预览此类型文件",
|
||||
"Back": "返回",
|
||||
"Download": "下载",
|
||||
"Home": "主页",
|
||||
"File Manager": "文件管理",
|
||||
"New Folder": "新建目录",
|
||||
"Upload": "上传",
|
||||
"Name": "名称",
|
||||
"Size": "大小",
|
||||
"Modified Time": "修改时间",
|
||||
"Grid": "网格",
|
||||
"List": "列表",
|
||||
"Mount Point": "挂载点",
|
||||
"Move": "移动",
|
||||
"Move to": "移动到",
|
||||
"Copy to": "复制到",
|
||||
"Destination path": "目标路径",
|
||||
"Move task queued": "移动任务已排队",
|
||||
"Move completed": "移动完成",
|
||||
"Copy task queued": "复制任务已排队",
|
||||
"Copy completed": "复制完成",
|
||||
"Please input destination path": "请输入目标路径",
|
||||
"Upload File": "上传文件",
|
||||
"Upload Files": "上传文件",
|
||||
"Upload Folder": "上传文件夹",
|
||||
"Open": "打开",
|
||||
"Open With": "打开方式",
|
||||
"Default": "默认",
|
||||
"Processor": "处理器",
|
||||
"Share": "分享",
|
||||
"Rename": "重命名",
|
||||
"Delete": "删除",
|
||||
"Details": "详情",
|
||||
"Get Direct Link": "获取直链",
|
||||
"User": "用户",
|
||||
"Status Code": "状态码",
|
||||
"Duration (ms)": "耗时 (ms)",
|
||||
"Client IP": "客户端 IP",
|
||||
"Result": "结果",
|
||||
"Request Params": "请求参数",
|
||||
"Request Body": "请求体",
|
||||
"Total progress": "总体进度",
|
||||
"Upload task summary": "任务:已完成 {completed} / {total},待处理 {pending},失败 {failures}",
|
||||
"Overwrite confirmation required": "需要确认是否覆盖",
|
||||
"Target already exists: {path}": "目标已存在:{path}",
|
||||
"Overwrite": "覆盖",
|
||||
"Skip": "跳过",
|
||||
"Overwrite All": "全部覆盖",
|
||||
"Skip All": "全部跳过",
|
||||
"Directory": "目录",
|
||||
"Creating directory...": "正在创建目录...",
|
||||
"Directory ready": "目录已就绪",
|
||||
"Create directory failed": "创建目录失败",
|
||||
"Waiting to create": "等待创建",
|
||||
"Waiting for overwrite decision": "等待覆盖处理",
|
||||
"Waiting to upload": "等待上传",
|
||||
"Skipped": "已跳过",
|
||||
"Upload succeeded": "上传成功",
|
||||
"Upload failed": "上传失败",
|
||||
"No items selected for upload": "未选择任何可上传项",
|
||||
"No uploadable files or directories found": "未找到可上传的文件或目录",
|
||||
"Missing file content": "缺少文件内容",
|
||||
"Directory conflicts with existing file": "目标存在同名文件,无法创建目录",
|
||||
"Join Community": "加入社区",
|
||||
"Scan to join WeChat group": "微信扫码加入交流群",
|
||||
"If QR expires, add drizzle2001 to join": "如二维码失效,请添加 drizzle2001 拉群",
|
||||
"Version Info": "版本信息",
|
||||
"Current Version": "当前版本",
|
||||
"Latest Version": "最新版本",
|
||||
"New version found: {version}": "发现新版本: {version}",
|
||||
"Please update to the latest for features and fixes": "建议尽快更新到最新版本,以获得新功能和安全修复。",
|
||||
"Open Releases": "前往发布页面",
|
||||
"Changelog": "更新日志",
|
||||
"Fetching latest version...": "正在获取最新版本信息...",
|
||||
"Update available": "有更新",
|
||||
"You are on the latest: {version}": "当前为最新版: {version}",
|
||||
"Up to date": "已是最新版",
|
||||
"Share {count} items": "分享 {count} 个项目",
|
||||
"Share link created": "分享链接已创建",
|
||||
"Create failed": "创建失败",
|
||||
"Copied to clipboard": "已复制到剪贴板",
|
||||
"Expiration (days)": "有效期 (天)",
|
||||
"Set 0 or negative for forever": "设置为 0 或负数表示永久有效",
|
||||
"Share link created successfully!": "分享链接已成功创建!",
|
||||
"Share Link": "分享链接",
|
||||
"Share created": "分享创建成功",
|
||||
"Create Share": "创建分享",
|
||||
"Done": "完成",
|
||||
"Create": "创建",
|
||||
"Failed to generate link": "生成链接失败",
|
||||
"Markdown copied to clipboard": "Markdown 格式已复制到剪贴板",
|
||||
"Generate a direct link for {name}": "为 {name} 生成一个直接访问链接。",
|
||||
"1 hour": "1 小时",
|
||||
"1 day": "1 天",
|
||||
"7 days": "7 天",
|
||||
"Generating link...": "正在生成链接...",
|
||||
"Link will appear here": "链接将显示在这里",
|
||||
"Copy Markdown": "复制 Markdown",
|
||||
"Close": "关闭",
|
||||
"Task Queue": "任务队列",
|
||||
"Last updated at {time}": "上次刷新时间 {time}",
|
||||
"Total Tasks": "任务总数",
|
||||
"Waiting Tasks": "等待中的任务",
|
||||
"Failed Tasks": "失败的任务",
|
||||
"Active Workers": "活跃 Worker 数",
|
||||
"Task Type": "任务类型",
|
||||
"Search by name or ID": "按名称或 ID 搜索",
|
||||
"Filter by status": "按状态筛选",
|
||||
"Queue Concurrency": "队列并发数",
|
||||
"Settings saved": "设置已保存",
|
||||
"Expand": "展开",
|
||||
"Adjust worker concurrency immediately": "立即调整任务并发数",
|
||||
"Auto": "自动",
|
||||
"Manual": "手动",
|
||||
"Camera Make": "设备品牌",
|
||||
"Camera Model": "设备型号",
|
||||
"Capture Time": "拍摄时间",
|
||||
"X Resolution": "水平分辨率",
|
||||
"Y Resolution": "垂直分辨率",
|
||||
"Exposure Time": "曝光时间",
|
||||
"Aperture": "光圈值",
|
||||
"Focal Length": "焦距",
|
||||
"Width": "宽度",
|
||||
"Height": "高度",
|
||||
"No common EXIF info": "无常见EXIF信息",
|
||||
"File Properties": "文件属性",
|
||||
"Loading file info...": "加载文件信息...",
|
||||
"Basic Info": "基本信息",
|
||||
"Type": "类型",
|
||||
"Folder": "文件夹",
|
||||
"File": "文件",
|
||||
"Path": "路径",
|
||||
"Path copied to clipboard": "路径已复制到剪贴板",
|
||||
"Copy failed": "复制失败",
|
||||
"Permissions": "权限",
|
||||
"EXIF Info": "EXIF信息",
|
||||
"Index Info": "索引信息",
|
||||
"Indexed Items": "索引条目数",
|
||||
"Indexed Types": "索引类型统计",
|
||||
"No index data": "暂无索引数据",
|
||||
"Indexed Chunks": "索引条目",
|
||||
"More Indexed Chunks": "更多索引条目",
|
||||
"Chunk ID": "分片ID",
|
||||
"Offset Range": "偏移范围",
|
||||
"Vector ID": "向量ID",
|
||||
"Preview": "内容预览",
|
||||
"Showing first {count} entries": "仅展示前 {count} 条",
|
||||
"Smart Search": "智能搜索",
|
||||
"Name Search": "名称搜索",
|
||||
"Search Results": "搜索结果",
|
||||
"No files found": "未找到相关文件",
|
||||
"Relevance": "相关度",
|
||||
"Saved successfully": "保存成功",
|
||||
"Save failed": "保存失败",
|
||||
"Loading...": "加载中...",
|
||||
"Appearance Settings": "外观设置",
|
||||
"Theme": "主题",
|
||||
"Theme Mode": "主题模式",
|
||||
"Light": "亮色",
|
||||
"Dark": "暗色",
|
||||
"Follow System": "跟随系统",
|
||||
"Primary Color": "主色",
|
||||
"Border Radius": "圆角",
|
||||
"Advanced": "高级",
|
||||
"Override AntD Tokens (JSON)": "覆盖 AntD Token(JSON)",
|
||||
"e.g. {\"colorText\": \"#222\"}": "例如:{\"colorText\": \"#222\"}",
|
||||
"Custom CSS": "自定义 CSS",
|
||||
"Save": "保存",
|
||||
"App Settings": "应用设置",
|
||||
"Email Settings": "邮箱设置",
|
||||
"AI Settings": "AI设置",
|
||||
"Protocol Mappings": "映射协议",
|
||||
"S3 Mapping": "S3 映射",
|
||||
"S3 Endpoint": "S3 访问地址",
|
||||
"Bucket Name": "Bucket 名称",
|
||||
"Bucket API Path": "Bucket API 路径",
|
||||
"Region": "区域",
|
||||
"Base Path": "基础路径",
|
||||
"Choose Template": "选择模板",
|
||||
"Configure Provider": "配置提供商",
|
||||
"Back to Templates": "返回选择",
|
||||
"View Docs": "查看文档",
|
||||
"Vision Model": "视觉模型",
|
||||
"Embedding Model": "嵌入模型",
|
||||
"Embedding Dimension": "向量维度",
|
||||
"Vector Database": "向量数据库",
|
||||
"Vector Database Settings": "向量数据库设置",
|
||||
"Current Statistics": "当前统计",
|
||||
"Collections": "集合",
|
||||
"Vectors": "向量",
|
||||
"Database Size": "数据库大小",
|
||||
"Estimated Memory": "估算内存",
|
||||
"No collections": "暂无集合",
|
||||
"Dimension": "维度",
|
||||
"Non-vector collection": "非向量集合",
|
||||
"Estimated memory": "估算内存",
|
||||
"Indexes": "索引",
|
||||
"Unnamed index": "未命名索引",
|
||||
"Indexed rows": "已索引行数",
|
||||
"Pending rows": "待索引行数",
|
||||
"Estimated memory is calculated as vectors x dimension x 4 bytes (float32).": "估算内存 = 向量数量 x 维度 x 4 字节(float32)。",
|
||||
"Database Provider": "数据库提供者",
|
||||
"Please select a provider": "请选择提供者",
|
||||
"Coming soon": "敬请期待",
|
||||
"This provider is not available yet": "该提供者暂不可用",
|
||||
"Database file path": "数据库文件路径",
|
||||
"Server URI": "服务器 URI",
|
||||
"Token": "令牌",
|
||||
"Server URL": "服务器地址",
|
||||
"Embedded Milvus Lite (local file storage).": "嵌入式 Milvus Lite,本地文件存储。",
|
||||
"Remote Milvus instance accessed via URI.": "通过 URI 访问的远程 Milvus 实例。",
|
||||
"Qdrant vector database (HTTP API).": "Qdrant 向量数据库(HTTP API)。",
|
||||
"Database Type": "数据库类型",
|
||||
"Confirm embedding dimension change": "确认修改向量维度",
|
||||
"Changing the embedding dimension will clear the vector database automatically. You will need to rebuild indexes afterwards. Continue?": "修改向量维度会自动清空向量数据库,之后需要重建索引,是否继续?",
|
||||
"Confirm clear vector database?": "确认清空向量数据库?",
|
||||
"This will delete all collections irreversibly.": "此操作将删除所有集合中的所有数据,且不可逆。",
|
||||
"Confirm Clear": "确认清空",
|
||||
"Vector database cleared": "向量数据库已清空",
|
||||
"Clear failed": "清空失败",
|
||||
"Clear Vector DB": "清空向量库",
|
||||
"App Name": "应用名称",
|
||||
"Logo URL": "LOGO地址",
|
||||
"Favicon URL": "Favicon 地址",
|
||||
"App Domain": "应用域名",
|
||||
"File Domain": "文件域名",
|
||||
"Configure Access Key and Secret to enable S3 mapping.": "配置 Access Key 与 Secret 后才能启用 S3 映射。",
|
||||
"Mount point inside the virtual file system (e.g. / or /workspace).": "虚拟文件系统中的挂载路径,例如 / 或 /workspace。",
|
||||
"Please input bucket name": "请输入 Bucket 名",
|
||||
"Please input region": "请输入 Region",
|
||||
"Please input access key": "请输入 Access Key",
|
||||
"Please input secret key": "请输入 Secret Key",
|
||||
"Save S3 Settings": "保存 S3 配置",
|
||||
"Example CLI command": "示例 CLI 命令",
|
||||
"WebDAV Mapping": "WebDAV 映射",
|
||||
"WebDAV Endpoint": "WebDAV 访问地址",
|
||||
"Basic (system account password)": "Basic(系统账号密码)",
|
||||
"Root Path": "根路径",
|
||||
"Client Compatibility": "客户端兼容性",
|
||||
"Supports Finder, Windows network drive, rclone, and other WebDAV clients.": "兼容 Finder、Windows 网络驱动器、rclone 等 WebDAV 客户端。",
|
||||
"Toggle the switch to expose the virtual file system via WebDAV.": "通过开关控制是否对外暴露虚拟文件系统的 WebDAV 协议。",
|
||||
"SMTP Settings": "SMTP 配置",
|
||||
"SMTP Host": "SMTP 服务器",
|
||||
"Please input SMTP host": "请输入 SMTP 服务器",
|
||||
"SMTP Port": "SMTP 端口",
|
||||
"Please input SMTP port": "请输入 SMTP 端口",
|
||||
"Security": "安全协议",
|
||||
"None": "无",
|
||||
"Timeout (seconds)": "超时时间(秒)",
|
||||
"Sender": "发件人",
|
||||
"Sender Name": "发件人名称",
|
||||
"Sender Email": "发件人邮箱",
|
||||
"Please input sender email": "请输入发件人邮箱",
|
||||
"Authentication": "身份认证",
|
||||
"SMTP Username": "SMTP 用户名",
|
||||
"SMTP Password": "SMTP 密码",
|
||||
"Test Email": "测试发信",
|
||||
"Current Configuration": "当前配置摘要",
|
||||
"Available variables": "可用变量",
|
||||
"Not set": "未设置",
|
||||
"Password Reset Template": "密码重置模板",
|
||||
"Live Preview": "实时预览",
|
||||
"Template saved": "模板已保存",
|
||||
"Failed to save template": "模板保存失败",
|
||||
"Failed to load template": "模板加载失败",
|
||||
"Preview failed": "预览失败",
|
||||
"Foxel Mail Test": "Foxel 邮件测试",
|
||||
"Recipient Address": "收件人地址",
|
||||
"Please input recipient email": "请输入收件人邮箱",
|
||||
"Test Subject": "测试邮件标题",
|
||||
"Test User Name": "测试用户名",
|
||||
"Optional": "可选",
|
||||
"Send Test Email": "发送测试邮件",
|
||||
"Please complete all required fields": "请填写所有必填项",
|
||||
"SMTP port must be a positive number": "SMTP 端口必须为正数",
|
||||
"Test email queued (task {{taskId}})": "测试邮件已入队(任务 {{taskId}})",
|
||||
"Test email failed": "测试邮件发送失败",
|
||||
"Vision API URL": "视觉模型 API 地址",
|
||||
"Vision API Key": "视觉模型 API Key",
|
||||
"Embedding API URL": "嵌入模型 API 地址",
|
||||
"Embedding API Key": "嵌入模型 API Key",
|
||||
"AI Providers & Models": "AI 提供商与模型",
|
||||
"Manage AI providers, synchronize compatible models, and configure default capabilities across the system.": "管理所有 AI 提供商,批量同步兼容模型,并配置系统默认能力。",
|
||||
"Add Provider": "添加提供商",
|
||||
"Edit Provider": "编辑提供商",
|
||||
"Pull Models": "拉取模型",
|
||||
"Manual Add": "手动添加",
|
||||
"Clear Remote List": "清空列表",
|
||||
"Select models from the list to add them automatically": "选择模型后可一键添加到系统",
|
||||
"No remote models": "暂无远程模型",
|
||||
"No remote models found": "未获取到远程模型",
|
||||
"No remote models match search": "没有匹配的远程模型",
|
||||
"Search fetched models": "搜索已拉取模型",
|
||||
"Already Added": "已添加",
|
||||
"Add Selected Models": "添加所选模型",
|
||||
"Fetch failed": "拉取失败",
|
||||
"Select models to add": "请选择要添加的模型",
|
||||
"Added {count} models": "已添加 {count} 个模型",
|
||||
"Custom Provider": "自定义提供商",
|
||||
"Custom Provider Description": "自定义兼容 OpenAI 或 Gemini 标准的 API 端点。",
|
||||
"OpenAI Provider Description": "访问 OpenAI 的 GPT-4o、GPT-4.1、GPT-3.5 等模型。",
|
||||
"Azure OpenAI Provider Description": "使用托管在微软 Azure 上的 OpenAI 模型。",
|
||||
"Google AI Provider Description": "Google AI 平台提供的 Gemini 系列模型。",
|
||||
"SiliconFlow Provider": "硅基流动",
|
||||
"SiliconFlow Provider Description": "硅基流动高性能推理平台,兼容 OpenAI 接口。",
|
||||
"OpenRouter Provider Description": "通过一个 OpenAI 风格入口接入多家 AI 提供商。",
|
||||
"Anthropic Provider Description": "通过 Claude API 使用 Claude 3 系列模型。",
|
||||
"DeepSeek Provider Description": "DeepSeek 语言模型,支持 OpenAI 兼容接口。",
|
||||
"Grok Provider Description": "xAI 的 Grok 模型,提供 OpenAI 风格接口。",
|
||||
"Ollama Provider Description": "使用 Ollama 在本地运行并管理大模型。",
|
||||
"Voyage Provider Description": "Voyage AI 提供的高质量嵌入与重排序模型。",
|
||||
"Delete provider?": "确认删除该提供商?",
|
||||
"Deleting this provider will also remove all associated models. Continue?": "删除后将同时移除该提供商下的全部模型,是否继续?",
|
||||
"Deleted successfully": "删除成功",
|
||||
"Sync Models": "同步模型",
|
||||
"Sync completed: {created} created, {updated} updated": "同步完成:新增 {created} 个,更新 {updated} 个",
|
||||
"Sync failed": "同步失败",
|
||||
"Add Model": "添加模型",
|
||||
"Edit Model": "编辑模型",
|
||||
"Delete model?": "确认删除该模型?",
|
||||
"This operation cannot be undone. Continue?": "此操作不可撤销,是否继续?",
|
||||
"No models yet": "暂无模型",
|
||||
"Add your first AI provider to get started": "添加第一个 AI 提供商开始配置",
|
||||
"Default Models Configuration": "默认模型配置",
|
||||
"Main Chat Model": "主对话模型",
|
||||
"Primary assistant for conversations, reasoning, and tool calls.": "用于对话、推理与工具调用的核心模型。",
|
||||
"Handles multimodal perception such as image understanding.": "负责多模态感知与图像理解。",
|
||||
"Transforms content into dense vectors for search and retrieval.": "将内容向量化以驱动搜索与检索。",
|
||||
"Optimises ranking quality for search candidates.": "重新排序候选结果,提升检索相关性。",
|
||||
"Covers text-to-speech and speech understanding scenarios.": "覆盖文本转语音与语音理解场景。",
|
||||
"Supports function calling, orchestration, and automation.": "支持函数调用、编排与自动化。",
|
||||
"Select a model": "选择模型",
|
||||
"Template": "模板",
|
||||
"Select a template": "选择模板",
|
||||
"Display Name": "显示名称",
|
||||
"Enter name": "请输入名称",
|
||||
"Identifier": "标识符",
|
||||
"Enter identifier": "请输入标识符",
|
||||
"Only lowercase letters, numbers, dash, dot and underscore are allowed": "仅允许小写字母、数字、连字符、点和下划线",
|
||||
"API Format": "API 格式",
|
||||
"Base URL": "基础 URL",
|
||||
"Enter base url": "请输入基础 URL",
|
||||
"Optional, can also be provided per request": "可选,也可在请求时提供",
|
||||
"Model Identifier": "模型标识",
|
||||
"Enter model identifier": "请输入模型标识",
|
||||
"Description": "描述",
|
||||
"Capabilities": "能力标签",
|
||||
"Context Window": "上下文窗口",
|
||||
"Embedding Dimensions": "向量维度",
|
||||
"Price /1K input tokens": "价格 /1K 输入 token",
|
||||
"Price /1K output tokens": "价格 /1K 输出 token",
|
||||
"Missing required config:": "缺少必填配置:",
|
||||
"Updated successfully": "更新成功",
|
||||
"Created successfully": "创建成功",
|
||||
"Operation failed": "操作失败",
|
||||
"Deleted": "已删除",
|
||||
"Delete failed": "删除失败",
|
||||
"Status updated": "状态已更新",
|
||||
"Update failed": "更新失败",
|
||||
"Mount Path": "挂载路径",
|
||||
"Sub Path": "子路径",
|
||||
"Sub Path (optional)": "子路径(可选)",
|
||||
"Sub directory inside adapter": "适配器内部子目录",
|
||||
"Enabled": "启用",
|
||||
"Actions": "操作",
|
||||
"Edit": "编辑",
|
||||
"Confirm delete?": "确认删除?",
|
||||
"No config fields": "无配置项",
|
||||
"Please input {label}": "请输入{label}",
|
||||
"Storage Adapters": "存储适配器",
|
||||
"Create Adapter": "新建适配器",
|
||||
"Unique name": "唯一名称",
|
||||
"Select adapter type": "选择适配器类型",
|
||||
"/ or /drive": "/或/drive",
|
||||
"Adapter Config": "适配器配置",
|
||||
"adapter.type.local": "本地文件系统",
|
||||
"adapter.type.quark": "夸克网盘",
|
||||
"Automation Tasks": "自动化任务",
|
||||
"Running Tasks": "运行中的任务",
|
||||
"Create Task": "新建任务",
|
||||
"Edit Task": "编辑任务",
|
||||
"Create Automation Task": "新建自动化任务",
|
||||
"Task Name": "任务名称",
|
||||
"Trigger Event": "触发事件",
|
||||
"File Written": "文件写入",
|
||||
"File Deleted": "文件删除",
|
||||
"Matching Rules": "匹配规则",
|
||||
"Path Prefix (optional)": "路径前缀 (可选)",
|
||||
"Filename Regex (optional)": "文件名正则 (可选)",
|
||||
"Action": "执行动作",
|
||||
"Current Task Queue": "当前任务队列",
|
||||
"Params": "参数",
|
||||
"Status": "状态",
|
||||
"Confirm clear logs?": "确认清理日志?",
|
||||
"This will delete logs in selected range irreversibly.": "该操作将删除选定时间范围内的所有日志,且不可恢复。",
|
||||
"Cleared {count} logs": "成功清理 {count} 条日志",
|
||||
"Time": "时间",
|
||||
"Level": "级别",
|
||||
"Source": "来源",
|
||||
"Message": "消息",
|
||||
"User ID": "用户 ID",
|
||||
"Search source": "搜索来源",
|
||||
"Clear": "清理",
|
||||
"Log Details": "日志详情",
|
||||
"Raw Log": "原始日志",
|
||||
"Export started, check your downloads.": "导出已开始,请检查您的下载。",
|
||||
"Export failed": "导出失败",
|
||||
"Confirm import backup?": "确认导入备份?",
|
||||
"Are you sure to import from this file?": "您确定要从此文件导入数据吗?",
|
||||
"Warning: This will overwrite all data including users (with passwords), settings, storages and tasks. Irreversible!": "警告:此操作将覆盖当前数据库中的所有现有数据,包括用户(含密码)、设置、存储和任务。此操作不可逆!",
|
||||
"Confirm Import": "确认导入",
|
||||
"Import succeeded! The page will refresh.": "导入成功!页面将刷新。",
|
||||
"Import failed": "导入失败",
|
||||
"Export": "导出",
|
||||
"Import": "恢复",
|
||||
"Export all data (adapters, users, tasks, shares) into a JSON file.": "点击按钮将所有数据(包括存储、用户、自动化任务和分享)导出为一个 JSON 文件。",
|
||||
"Keep your backup file safe.": "请妥善保管您的备份文件。",
|
||||
"Export Backup": "导出备份",
|
||||
"Restore data from a previously exported JSON file.": "从之前导出的JSON文件恢复数据。",
|
||||
"Warning: This will clear and overwrite existing data.": "警告:此操作将清除并覆盖现有数据。",
|
||||
"Choose File and Restore": "选择文件并恢复",
|
||||
"No files yet here": "这里还没有任何文件",
|
||||
"This folder is empty": "此目录为空",
|
||||
"Start uploading files or create folders to organize your content": "开始上传文件或创建新目录来组织您的内容",
|
||||
"You can create folders or upload files here": "您可以在此目录中创建新的文件夹或上传文件",
|
||||
"Please input name": "请输入名称",
|
||||
"Confirm delete {name}?": "确认删除 {name} ?",
|
||||
"items": "项",
|
||||
"Downloading folders is not supported": "暂不支持下载目录",
|
||||
"Download failed": "下载失败",
|
||||
"Please select files or folders to share": "请选择要分享的文件或目录",
|
||||
"Direct links for folders are not supported": "不支持获取目录的直链",
|
||||
"Processing finished": "处理完成",
|
||||
"Processing failed": "处理失败",
|
||||
"Processors": "处理器",
|
||||
"Processor List": "处理器列表",
|
||||
"Reload": "重载",
|
||||
"Run Processor": "运行处理器",
|
||||
"Target Path": "目标路径",
|
||||
"Please select a path": "请选择路径",
|
||||
"Select Directory": "选择目录",
|
||||
"Overwrite original": "覆盖原文件",
|
||||
"Save To": "保存到",
|
||||
"Optional output path": "可选输出路径",
|
||||
"Run": "运行",
|
||||
"Select a processor": "选择处理器",
|
||||
"No module path": "未检测到模块路径",
|
||||
"Source saved": "源码已保存",
|
||||
"Processors reloaded": "处理器已重载",
|
||||
"Unsaved changes": "存在未保存的修改",
|
||||
"Switching processor will discard unsaved changes. Continue?": "切换处理器会丢失未保存的修改,确认继续?",
|
||||
"Task submitted": "任务已提交",
|
||||
"Supported Extensions": "支持的扩展名",
|
||||
"All": "全部",
|
||||
"Produces File": "生成文件",
|
||||
"Yes": "是",
|
||||
"No": "否",
|
||||
"Please select a processor": "请选择处理器",
|
||||
"Select a path": "请选择路径",
|
||||
"Source Editor": "源码编辑",
|
||||
"Module Path": "模块路径",
|
||||
"Directory processing always overwrites original files": "选择目录时会强制覆盖原文件",
|
||||
"Directory execution will enqueue one task per file": "目录模式会为每个文件单独创建任务",
|
||||
"Directory scope": "目录范围",
|
||||
"Current level only": "仅当前层级",
|
||||
"Include subdirectories": "包含子目录",
|
||||
"Max depth": "最大层级",
|
||||
"Leave empty to traverse all subdirectories": "留空表示遍历所有子目录",
|
||||
"Depth must be greater or equal to 0": "层级必须大于或等于 0",
|
||||
"Output suffix": "输出后缀",
|
||||
"Suffix will be inserted before the file extension, e.g. demo_processed.mp4": "后缀会插入到文件扩展名前,例如 demo_processed.mp4",
|
||||
"Suffix such as _processed": "例如 _processed 的后缀",
|
||||
"Suffix cannot be empty": "后缀不能为空",
|
||||
"No data": "暂无数据",
|
||||
"Select File": "选择文件",
|
||||
"Select Path": "选择路径",
|
||||
"Select Folder": "选择目录",
|
||||
"Select": "选择",
|
||||
"Current": "当前",
|
||||
"Up": "上一级",
|
||||
"Select Current Folder": "选择当前目录",
|
||||
"Please select a file": "请选择一个文件",
|
||||
"Installed successfully": "安装成功",
|
||||
"Plugin": "插件",
|
||||
"Open Link": "打开链接",
|
||||
"Link copied": "已复制链接",
|
||||
"Copy Link": "复制链接",
|
||||
"Confirm delete this plugin?": "确认删除该插件?",
|
||||
"Author": "作者",
|
||||
"Website": "官网",
|
||||
"Install App": "安装应用",
|
||||
"Search name/author/url/extension": "搜索 名称/作者/链接/扩展名",
|
||||
"No plugins": "暂无插件",
|
||||
"Install": "安装",
|
||||
"App URL": "应用链接",
|
||||
"Please input a valid URL": "请输入合法的 URL",
|
||||
"Installed": "已安装",
|
||||
"Discover": "发现",
|
||||
"Search apps": "搜索应用",
|
||||
"Sort by": "排序",
|
||||
"Downloads": "下载量",
|
||||
"Created (newest)": "创建时间(最新)",
|
||||
"Installed already": "已安装",
|
||||
"No results": "暂无结果",
|
||||
"Initialization succeeded! Logging you in...": "初始化成功!正在为您登录,请不要刷新。",
|
||||
"Initialization failed, please try later": "初始化失败,请稍后重试",
|
||||
"Database Setup": "数据库设置",
|
||||
"Choose database driver": "选择数据库驱动",
|
||||
"Select database and vector database for system data": "选择用于存储系统数据的数据库和向量数据库。",
|
||||
"Database Driver": "数据库驱动",
|
||||
"Vector DB Driver": "向量数据库驱动",
|
||||
"Initialize Mount": "初始化挂载",
|
||||
"Configure initial storage": "配置初始存储",
|
||||
"Create the first storage mount for your files": "为您的文件创建第一个存储挂载点。",
|
||||
"Mount Name": "挂载名称",
|
||||
"Local Storage": "本地存储",
|
||||
"Please input mount name!": "请输入挂载名称!",
|
||||
"Storage Type": "存储类型",
|
||||
"Please input mount path!": "请输入挂载路径!",
|
||||
"Root Directory": "根目录",
|
||||
"Please input root directory!": "请输入根目录!",
|
||||
"e.g., data/ or /var/foxel/data": "例如: data/ 或 /var/foxel/data",
|
||||
"Optional, used for external links. Leave empty to use the current site.": "可选,用于生成外部链接;留空则使用当前站点。",
|
||||
"Create Admin": "创建管理员",
|
||||
"Create admin account": "创建管理员账户",
|
||||
"This is the first account with full permissions": "这是系统的第一个账户,将拥有最高权限。",
|
||||
"Username": "用户名",
|
||||
"Please input a valid email!": "请输入有效的邮箱地址!",
|
||||
"Please confirm your password!": "请确认您的密码!",
|
||||
"Passwords do not match!": "两次输入的密码不一致!",
|
||||
"System Initialization": "系统初始化",
|
||||
"Previous": "上一步",
|
||||
"Next": "下一步",
|
||||
"Finish Initialization": "完成初始化",
|
||||
"Plugin run failed": "插件运行失败",
|
||||
"Plugin Error": "插件错误",
|
||||
"Cannot open file: no available app": "无法打开该文件:没有可用的应用",
|
||||
"Error": "错误",
|
||||
"App \"{key}\" not found.": "应用 \"{key}\" 不存在。",
|
||||
"Open with {app}": "使用 {app} 打开",
|
||||
"Set as default for .{ext}": "设为该类型(.{ext})默认应用",
|
||||
"Advanced tokens must be valid JSON": "高级 Token 需为合法 JSON"
|
||||
}
|
||||
@@ -1,727 +0,0 @@
|
||||
import { en } from './en';
|
||||
|
||||
// Start from English defaults, then override with Chinese translations we have.
|
||||
export const zh = {
|
||||
...en,
|
||||
|
||||
// General
|
||||
'All Files': '全部文件',
|
||||
'Manage': '管理',
|
||||
'System': '系统',
|
||||
'Automation': '自动任务',
|
||||
'My Shares': '我的分享',
|
||||
'Offline Downloads': '离线下载',
|
||||
'No offline download tasks': '暂无离线下载任务',
|
||||
'Create Offline Download': '创建离线下载任务',
|
||||
'Offline Download Tasks': '离线下载任务列表',
|
||||
'URL': '下载地址',
|
||||
'Please input URL': '请输入下载地址',
|
||||
'Destination Folder': '保存目录',
|
||||
'Select destination': '请选择保存目录',
|
||||
'Filename': '文件名',
|
||||
'Please input filename': '请输入文件名',
|
||||
'Start Download': '开始下载',
|
||||
'Stage': '阶段',
|
||||
'Progress': '进度',
|
||||
'Bytes': '已传输',
|
||||
'Save Path': '保存路径',
|
||||
'Queued': '排队中',
|
||||
'Downloading': '下载中',
|
||||
'Transferring': '转存中',
|
||||
'Completed': '已完成',
|
||||
'Pending': '等待',
|
||||
'Running': '进行中',
|
||||
'Success': '成功',
|
||||
'Failed': '失败',
|
||||
'Adapters': '存储挂载',
|
||||
'Plugins': '应用中心',
|
||||
'System Settings': '系统设置',
|
||||
'Backup & Restore': '备份恢复',
|
||||
'System Logs': '系统日志',
|
||||
|
||||
// Top header
|
||||
'Search files / tags / types': '搜索文件 / 标签 / 类型',
|
||||
'Log Out': '退出登录',
|
||||
'Admin': '管理员',
|
||||
'Profile': '个人资料',
|
||||
'Account Settings': '账户设置',
|
||||
'Language': '语言',
|
||||
'Chinese': '中文',
|
||||
'English': 'English',
|
||||
'Full Name': '昵称',
|
||||
'Email': '邮箱',
|
||||
'Change Password': '修改密码',
|
||||
'Old Password': '原密码',
|
||||
'New Password': '新密码',
|
||||
'Please fill both old and new password': '请同时填写原密码和新密码',
|
||||
|
||||
// Auth / Login
|
||||
'Welcome Back': '欢迎回来',
|
||||
'Sign in to your Foxel account': '登录到您的 Foxel 账户',
|
||||
'Username / Email': '用户名/邮箱',
|
||||
'Password': '密码',
|
||||
'Sign In': '登录',
|
||||
'Please enter username and password': '请输入用户名与密码',
|
||||
'Login failed': '登录失败',
|
||||
'Forgot Password?': '忘记密码?',
|
||||
'Your next-generation file manager': '您的下一代文件管理系统',
|
||||
'Cross-platform sync, access anywhere': '跨平台同步,随时随地访问',
|
||||
'AI-powered search for quick find': 'AI 驱动的智能搜索,快速定位文件',
|
||||
'Flexible sharing and collaboration': '灵活的分享与协作,提升团队效率',
|
||||
'Powerful automation to simplify tasks': '强大的自动化工作流,简化繁琐任务',
|
||||
'Join our community:': '加入我们的社区:',
|
||||
'Reset Your Password': '重置你的密码',
|
||||
'Enter the email linked to your account and we will send a reset link.': '请输入你账户绑定的邮箱,我们会发送重置链接。',
|
||||
'If the email exists, a reset link has been sent.': '如果邮箱存在,我们已发送重置链接。',
|
||||
'Send Reset Link': '发送重置链接',
|
||||
'Resend Link': '重新发送链接',
|
||||
'Back to login': '返回登录',
|
||||
'Request failed': '请求失败',
|
||||
'Reset link is invalid': '重置链接无效',
|
||||
'Reset link is invalid or expired': '重置链接无效或已过期',
|
||||
'Reset failed': '重置失败',
|
||||
'Try again': '重试',
|
||||
'Set a new password': '设置新密码',
|
||||
'Please enter new password': '请输入新密码',
|
||||
'Confirm Password': '确认新密码',
|
||||
'Please confirm new password': '请确认新密码',
|
||||
'Update Password': '更新密码',
|
||||
'Passwords do not match': '两次输入的密码不一致',
|
||||
'Password updated, please login again.': '密码已更新,请重新登录。',
|
||||
'Failed to reset password': '密码重置失败',
|
||||
|
||||
// Share page
|
||||
'Refresh': '刷新',
|
||||
'Copy': '复制',
|
||||
'Cancel': '取消',
|
||||
'Copied link': '链接已复制',
|
||||
'Share canceled': '分享已取消',
|
||||
'Cancel failed': '取消失败',
|
||||
'Load failed': '加载失败',
|
||||
'Are you sure to cancel share?': '确认取消分享?',
|
||||
'Clear expired shares': '清空过期分享',
|
||||
'Confirm clear expired shares?': '确认清空过期分享?',
|
||||
'Cleared {count} expired shares': '已清理 {count} 个过期分享',
|
||||
'Share Name': '分享名称',
|
||||
'Share Content': '分享内容',
|
||||
'Created At': '创建时间',
|
||||
'Expires At': '过期时间',
|
||||
'Forever': '永久有效',
|
||||
'Access': '访问',
|
||||
'Public': '公开',
|
||||
'By Password': '密码',
|
||||
|
||||
// Public share page
|
||||
'Password Required': '需要密码',
|
||||
'Please enter password': '请输入密码',
|
||||
'Confirm': '确认',
|
||||
'Unable to load share info': '无法加载分享信息',
|
||||
'Share load failed': '加载分享失败',
|
||||
'Wrong password': '密码错误',
|
||||
'Root': '全部文件',
|
||||
'Created on {date}': '创建于 {date}',
|
||||
'Expires on {date}': '将于 {date} 过期',
|
||||
'Download File': '下载文件',
|
||||
'Preview not supported for this file type': '暂不支持在线预览此类型文件',
|
||||
'Back': '返回',
|
||||
'Download': '下载',
|
||||
|
||||
// Header/File Explorer
|
||||
'Home': '主页',
|
||||
'File Manager': '文件管理',
|
||||
'New Folder': '新建目录',
|
||||
'Upload': '上传',
|
||||
'Name': '名称',
|
||||
'Size': '大小',
|
||||
'Modified Time': '修改时间',
|
||||
'Grid': '网格',
|
||||
'List': '列表',
|
||||
'Mount Point': '挂载点',
|
||||
'Move': '移动',
|
||||
'Move to': '移动到',
|
||||
'Copy to': '复制到',
|
||||
'Destination path': '目标路径',
|
||||
'Move task queued': '移动任务已排队',
|
||||
'Move completed': '移动完成',
|
||||
'Copy task queued': '复制任务已排队',
|
||||
'Copy completed': '复制完成',
|
||||
'Please input destination path': '请输入目标路径',
|
||||
|
||||
// Context menu
|
||||
'Upload File': '上传文件',
|
||||
'Upload Files': '上传文件',
|
||||
'Upload Folder': '上传文件夹',
|
||||
'Open': '打开',
|
||||
'Open With': '打开方式',
|
||||
'Default': '默认',
|
||||
'Processor': '处理器',
|
||||
'Share': '分享',
|
||||
'Rename': '重命名',
|
||||
'Delete': '删除',
|
||||
'Details': '详情',
|
||||
'Get Direct Link': '获取直链',
|
||||
|
||||
// Upload modal
|
||||
'Total progress': '总体进度',
|
||||
'Upload bytes summary': '{uploaded} / {total}',
|
||||
'Upload task summary': '任务:已完成 {completed} / {total},待处理 {pending},失败 {failures}',
|
||||
'Overwrite confirmation required': '需要确认是否覆盖',
|
||||
'Target already exists: {path}': '目标已存在:{path}',
|
||||
'Overwrite': '覆盖',
|
||||
'Skip': '跳过',
|
||||
'Overwrite All': '全部覆盖',
|
||||
'Skip All': '全部跳过',
|
||||
'Directory': '目录',
|
||||
'Creating directory...': '正在创建目录...',
|
||||
'Directory ready': '目录已就绪',
|
||||
'Create directory failed': '创建目录失败',
|
||||
'Waiting to create': '等待创建',
|
||||
'Waiting for overwrite decision': '等待覆盖处理',
|
||||
'Waiting to upload': '等待上传',
|
||||
'Skipped': '已跳过',
|
||||
'Upload succeeded': '上传成功',
|
||||
'Upload failed': '上传失败',
|
||||
'No items selected for upload': '未选择任何可上传项',
|
||||
'No uploadable files or directories found': '未找到可上传的文件或目录',
|
||||
'Missing file content': '缺少文件内容',
|
||||
'Directory conflicts with existing file': '目标存在同名文件,无法创建目录',
|
||||
|
||||
// Side nav modals
|
||||
'Join Community': '加入社区',
|
||||
'Scan to join WeChat group': '微信扫码加入交流群',
|
||||
'If QR expires, add drizzle2001 to join': '如二维码失效,请添加 drizzle2001 拉群',
|
||||
'Version Info': '版本信息',
|
||||
'Current Version': '当前版本',
|
||||
'Latest Version': '最新版本',
|
||||
'New version found: {version}': '发现新版本: {version}',
|
||||
'Please update to the latest for features and fixes': '建议尽快更新到最新版本,以获得新功能和安全修复。',
|
||||
'Open Releases': '前往发布页面',
|
||||
'Changelog': '更新日志',
|
||||
'Fetching latest version...': '正在获取最新版本信息...',
|
||||
'Update available': '有更新',
|
||||
'You are on the latest: {version}': '当前为最新版: {version}',
|
||||
'Up to date': '已是最新版',
|
||||
|
||||
// Share modal
|
||||
'Share {count} items': '分享 {count} 个项目',
|
||||
'Share link created': '分享链接已创建',
|
||||
'Create failed': '创建失败',
|
||||
'Copied to clipboard': '已复制到剪贴板',
|
||||
'Expiration (days)': '有效期 (天)',
|
||||
'Set 0 or negative for forever': '设置为 0 或负数表示永久有效',
|
||||
'Share link created successfully!': '分享链接已成功创建!',
|
||||
'Share Link': '分享链接',
|
||||
'Share created': '分享创建成功',
|
||||
'Create Share': '创建分享',
|
||||
'Done': '完成',
|
||||
'Create': '创建',
|
||||
|
||||
// Direct link modal
|
||||
'Failed to generate link': '生成链接失败',
|
||||
'Markdown copied to clipboard': 'Markdown 格式已复制到剪贴板',
|
||||
'Generate a direct link for {name}': '为 {name} 生成一个直接访问链接。',
|
||||
'1 hour': '1 小时',
|
||||
'1 day': '1 天',
|
||||
'7 days': '7 天',
|
||||
'Generating link...': '正在生成链接...',
|
||||
'Link will appear here': '链接将显示在这里',
|
||||
'Copy Markdown': '复制 Markdown',
|
||||
'Close': '关闭',
|
||||
|
||||
// Task queue
|
||||
'Task Queue': '任务队列',
|
||||
'Last updated at {time}': '上次刷新时间 {time}',
|
||||
'Total Tasks': '任务总数',
|
||||
'Waiting Tasks': '等待中的任务',
|
||||
'Failed Tasks': '失败的任务',
|
||||
'Active Workers': '活跃 Worker 数',
|
||||
'Task Type': '任务类型',
|
||||
'Search by name or ID': '按名称或 ID 搜索',
|
||||
'Filter by status': '按状态筛选',
|
||||
'Queue Concurrency': '队列并发数',
|
||||
'Settings saved': '设置已保存',
|
||||
'Expand': '展开',
|
||||
'Adjust worker concurrency immediately': '立即调整任务并发数',
|
||||
'Auto': '自动',
|
||||
'Manual': '手动',
|
||||
// File detail
|
||||
'Camera Make': '设备品牌',
|
||||
'Camera Model': '设备型号',
|
||||
'Capture Time': '拍摄时间',
|
||||
'X Resolution': '水平分辨率',
|
||||
'Y Resolution': '垂直分辨率',
|
||||
'Exposure Time': '曝光时间',
|
||||
'Aperture': '光圈值',
|
||||
'Focal Length': '焦距',
|
||||
'Width': '宽度',
|
||||
'Height': '高度',
|
||||
'No common EXIF info': '无常见EXIF信息',
|
||||
'File Properties': '文件属性',
|
||||
'Loading file info...': '加载文件信息...',
|
||||
'Basic Info': '基本信息',
|
||||
'Type': '类型',
|
||||
'Folder': '文件夹',
|
||||
'File': '文件',
|
||||
'Path': '路径',
|
||||
'Path copied to clipboard': '路径已复制到剪贴板',
|
||||
'Copy failed': '复制失败',
|
||||
'Permissions': '权限',
|
||||
'EXIF Info': 'EXIF信息',
|
||||
'Index Info': '索引信息',
|
||||
'Indexed Items': '索引条目数',
|
||||
'Indexed Types': '索引类型统计',
|
||||
'No index data': '暂无索引数据',
|
||||
'Indexed Chunks': '索引条目',
|
||||
'More Indexed Chunks': '更多索引条目',
|
||||
'Chunk ID': '分片ID',
|
||||
'Offset Range': '偏移范围',
|
||||
'Vector ID': '向量ID',
|
||||
'Preview': '内容预览',
|
||||
'Showing first {count} entries': '仅展示前 {count} 条',
|
||||
|
||||
// Search dialog
|
||||
'Smart Search': '智能搜索',
|
||||
'Name Search': '名称搜索',
|
||||
'Search Results': '搜索结果',
|
||||
'No files found': '未找到相关文件',
|
||||
'Relevance': '相关度',
|
||||
|
||||
// System settings
|
||||
'Saved successfully': '保存成功',
|
||||
'Save failed': '保存失败',
|
||||
'Loading...': '加载中...',
|
||||
'Appearance Settings': '外观设置',
|
||||
'Theme': '主题',
|
||||
'Theme Mode': '主题模式',
|
||||
'Light': '亮色',
|
||||
'Dark': '暗色',
|
||||
// 'Follow System' used for theme mode
|
||||
'Follow System': '跟随系统',
|
||||
'Primary Color': '主色',
|
||||
'Border Radius': '圆角',
|
||||
'Advanced': '高级',
|
||||
'Override AntD Tokens (JSON)': '覆盖 AntD Token(JSON)',
|
||||
'e.g. {"colorText": "#222"}': '例如:{"colorText": "#222"}',
|
||||
'Custom CSS': '自定义 CSS',
|
||||
'Save': '保存',
|
||||
'App Settings': '应用设置',
|
||||
'Email Settings': '邮箱设置',
|
||||
'AI Settings': 'AI设置',
|
||||
'Protocol Mappings': '映射协议',
|
||||
'S3 Mapping': 'S3 映射',
|
||||
'S3 Endpoint': 'S3 访问地址',
|
||||
'Bucket Name': 'Bucket 名称',
|
||||
'Bucket API Path': 'Bucket API 路径',
|
||||
'Region': '区域',
|
||||
'Base Path': '基础路径',
|
||||
'Access Key': 'Access Key',
|
||||
'Secret Key': 'Secret Key',
|
||||
'Choose Template': '选择模板',
|
||||
'Configure Provider': '配置提供商',
|
||||
'Back to Templates': '返回选择',
|
||||
'View Docs': '查看文档',
|
||||
'Vision Model': '视觉模型',
|
||||
'Embedding Model': '嵌入模型',
|
||||
'Embedding Dimension': '向量维度',
|
||||
'Vector Database': '向量数据库',
|
||||
'Vector Database Settings': '向量数据库设置',
|
||||
'Current Statistics': '当前统计',
|
||||
'Collections': '集合',
|
||||
'Vectors': '向量',
|
||||
'Database Size': '数据库大小',
|
||||
'Estimated Memory': '估算内存',
|
||||
'No collections': '暂无集合',
|
||||
'Dimension': '维度',
|
||||
'Non-vector collection': '非向量集合',
|
||||
'Estimated memory': '估算内存',
|
||||
'Indexes': '索引',
|
||||
'Unnamed index': '未命名索引',
|
||||
'Indexed rows': '已索引行数',
|
||||
'Pending rows': '待索引行数',
|
||||
'Estimated memory is calculated as vectors x dimension x 4 bytes (float32).': '估算内存 = 向量数量 x 维度 x 4 字节(float32)。',
|
||||
'Database Provider': '数据库提供者',
|
||||
'Please select a provider': '请选择提供者',
|
||||
'Coming soon': '敬请期待',
|
||||
'This provider is not available yet': '该提供者暂不可用',
|
||||
'Database file path': '数据库文件路径',
|
||||
'Server URI': '服务器 URI',
|
||||
'Token': '令牌',
|
||||
'Server URL': '服务器地址',
|
||||
'API Key': 'API Key',
|
||||
'Embedded Milvus Lite (local file storage).': '嵌入式 Milvus Lite,本地文件存储。',
|
||||
'Remote Milvus instance accessed via URI.': '通过 URI 访问的远程 Milvus 实例。',
|
||||
'Qdrant vector database (HTTP API).': 'Qdrant 向量数据库(HTTP API)。',
|
||||
'Database Type': '数据库类型',
|
||||
'Confirm embedding dimension change': '确认修改向量维度',
|
||||
'Changing the embedding dimension will clear the vector database automatically. You will need to rebuild indexes afterwards. Continue?': '修改向量维度会自动清空向量数据库,之后需要重建索引,是否继续?',
|
||||
'Confirm clear vector database?': '确认清空向量数据库?',
|
||||
'This will delete all collections irreversibly.': '此操作将删除所有集合中的所有数据,且不可逆。',
|
||||
'Confirm Clear': '确认清空',
|
||||
// 'Cancel' defined above
|
||||
'Vector database cleared': '向量数据库已清空',
|
||||
'Clear failed': '清空失败',
|
||||
'Clear Vector DB': '清空向量库',
|
||||
'App Name': '应用名称',
|
||||
'Logo URL': 'LOGO地址',
|
||||
'Favicon URL': 'Favicon 地址',
|
||||
'App Domain': '应用域名',
|
||||
'File Domain': '文件域名',
|
||||
'Configure Access Key and Secret to enable S3 mapping.': '配置 Access Key 与 Secret 后才能启用 S3 映射。',
|
||||
'Mount point inside the virtual file system (e.g. / or /workspace).': '虚拟文件系统中的挂载路径,例如 / 或 /workspace。',
|
||||
'Please input bucket name': '请输入 Bucket 名',
|
||||
'Please input region': '请输入 Region',
|
||||
'Please input access key': '请输入 Access Key',
|
||||
'Please input secret key': '请输入 Secret Key',
|
||||
'Save S3 Settings': '保存 S3 配置',
|
||||
'Example CLI command': '示例 CLI 命令',
|
||||
'WebDAV Mapping': 'WebDAV 映射',
|
||||
'WebDAV Endpoint': 'WebDAV 访问地址',
|
||||
'Basic (system account password)': 'Basic(系统账号密码)',
|
||||
'Root Path': '根路径',
|
||||
'Client Compatibility': '客户端兼容性',
|
||||
'Supports Finder, Windows network drive, rclone, and other WebDAV clients.': '兼容 Finder、Windows 网络驱动器、rclone 等 WebDAV 客户端。',
|
||||
'Toggle the switch to expose the virtual file system via WebDAV.': '通过开关控制是否对外暴露虚拟文件系统的 WebDAV 协议。',
|
||||
'SMTP Settings': 'SMTP 配置',
|
||||
'SMTP Host': 'SMTP 服务器',
|
||||
'Please input SMTP host': '请输入 SMTP 服务器',
|
||||
'SMTP Port': 'SMTP 端口',
|
||||
'Please input SMTP port': '请输入 SMTP 端口',
|
||||
'Security': '安全协议',
|
||||
'None': '无',
|
||||
'SSL': 'SSL',
|
||||
'STARTTLS': 'STARTTLS',
|
||||
'Timeout (seconds)': '超时时间(秒)',
|
||||
'Sender': '发件人',
|
||||
'Sender Name': '发件人名称',
|
||||
'Sender Email': '发件人邮箱',
|
||||
'Please input sender email': '请输入发件人邮箱',
|
||||
'Authentication': '身份认证',
|
||||
'SMTP Username': 'SMTP 用户名',
|
||||
'SMTP Password': 'SMTP 密码',
|
||||
'Test Email': '测试发信',
|
||||
'Current Configuration': '当前配置摘要',
|
||||
'Available variables': '可用变量',
|
||||
'Not set': '未设置',
|
||||
'Password Reset Template': '密码重置模板',
|
||||
'Live Preview': '实时预览',
|
||||
'Template saved': '模板已保存',
|
||||
'Failed to save template': '模板保存失败',
|
||||
'Failed to load template': '模板加载失败',
|
||||
'Preview failed': '预览失败',
|
||||
'Foxel Mail Test': 'Foxel 邮件测试',
|
||||
'Recipient Address': '收件人地址',
|
||||
'Please input recipient email': '请输入收件人邮箱',
|
||||
'Test Subject': '测试邮件标题',
|
||||
'Test User Name': '测试用户名',
|
||||
'Optional': '可选',
|
||||
'Send Test Email': '发送测试邮件',
|
||||
'Please complete all required fields': '请填写所有必填项',
|
||||
'SMTP port must be a positive number': 'SMTP 端口必须为正数',
|
||||
'Test email queued (task {{taskId}})': '测试邮件已入队(任务 {{taskId}})',
|
||||
'Test email failed': '测试邮件发送失败',
|
||||
'Vision API URL': '视觉模型 API 地址',
|
||||
'Vision API Key': '视觉模型 API Key',
|
||||
'Embedding API URL': '嵌入模型 API 地址',
|
||||
'Embedding API Key': '嵌入模型 API Key',
|
||||
'AI Providers & Models': 'AI 提供商与模型',
|
||||
'Manage AI providers, synchronize compatible models, and configure default capabilities across the system.': '管理所有 AI 提供商,批量同步兼容模型,并配置系统默认能力。',
|
||||
'Add Provider': '添加提供商',
|
||||
'Edit Provider': '编辑提供商',
|
||||
'Pull Models': '拉取模型',
|
||||
'Manual Add': '手动添加',
|
||||
'Clear Remote List': '清空列表',
|
||||
'Select models from the list to add them automatically': '选择模型后可一键添加到系统',
|
||||
'No remote models': '暂无远程模型',
|
||||
'No remote models found': '未获取到远程模型',
|
||||
'No remote models match search': '没有匹配的远程模型',
|
||||
'Search fetched models': '搜索已拉取模型',
|
||||
'Already Added': '已添加',
|
||||
'Add Selected Models': '添加所选模型',
|
||||
'Fetch failed': '拉取失败',
|
||||
'Select models to add': '请选择要添加的模型',
|
||||
'Added {count} models': '已添加 {count} 个模型',
|
||||
'Custom Provider': '自定义提供商',
|
||||
'Custom Provider Description': '自定义兼容 OpenAI 或 Gemini 标准的 API 端点。',
|
||||
'OpenAI Provider': 'OpenAI',
|
||||
'OpenAI Provider Description': '访问 OpenAI 的 GPT-4o、GPT-4.1、GPT-3.5 等模型。',
|
||||
'Azure OpenAI Provider': 'Azure OpenAI',
|
||||
'Azure OpenAI Provider Description': '使用托管在微软 Azure 上的 OpenAI 模型。',
|
||||
'Google AI Provider': 'Google AI',
|
||||
'Google AI Provider Description': 'Google AI 平台提供的 Gemini 系列模型。',
|
||||
'SiliconFlow Provider': '硅基流动',
|
||||
'SiliconFlow Provider Description': '硅基流动高性能推理平台,兼容 OpenAI 接口。',
|
||||
'OpenRouter Provider': 'OpenRouter',
|
||||
'OpenRouter Provider Description': '通过一个 OpenAI 风格入口接入多家 AI 提供商。',
|
||||
'Anthropic Provider': 'Anthropic',
|
||||
'Anthropic Provider Description': '通过 Claude API 使用 Claude 3 系列模型。',
|
||||
'DeepSeek Provider': 'DeepSeek',
|
||||
'DeepSeek Provider Description': 'DeepSeek 语言模型,支持 OpenAI 兼容接口。',
|
||||
'Grok Provider': 'Grok (xAI)',
|
||||
'Grok Provider Description': 'xAI 的 Grok 模型,提供 OpenAI 风格接口。',
|
||||
'Ollama Provider': 'Ollama',
|
||||
'Ollama Provider Description': '使用 Ollama 在本地运行并管理大模型。',
|
||||
'Voyage Provider': 'Voyage AI',
|
||||
'Voyage Provider Description': 'Voyage AI 提供的高质量嵌入与重排序模型。',
|
||||
'Delete provider?': '确认删除该提供商?',
|
||||
'Deleting this provider will also remove all associated models. Continue?': '删除后将同时移除该提供商下的全部模型,是否继续?',
|
||||
'Deleted successfully': '删除成功',
|
||||
'Sync Models': '同步模型',
|
||||
'Sync completed: {created} created, {updated} updated': '同步完成:新增 {created} 个,更新 {updated} 个',
|
||||
'Sync failed': '同步失败',
|
||||
'Add Model': '添加模型',
|
||||
'Edit Model': '编辑模型',
|
||||
'Delete model?': '确认删除该模型?',
|
||||
'This operation cannot be undone. Continue?': '此操作不可撤销,是否继续?',
|
||||
'No models yet': '暂无模型',
|
||||
'Add your first AI provider to get started': '添加第一个 AI 提供商开始配置',
|
||||
'Default Models Configuration': '默认模型配置',
|
||||
'Main Chat Model': '主对话模型',
|
||||
'Primary assistant for conversations, reasoning, and tool calls.': '用于对话、推理与工具调用的核心模型。',
|
||||
'Handles multimodal perception such as image understanding.': '负责多模态感知与图像理解。',
|
||||
'Transforms content into dense vectors for search and retrieval.': '将内容向量化以驱动搜索与检索。',
|
||||
'Optimises ranking quality for search candidates.': '重新排序候选结果,提升检索相关性。',
|
||||
'Covers text-to-speech and speech understanding scenarios.': '覆盖文本转语音与语音理解场景。',
|
||||
'Supports function calling, orchestration, and automation.': '支持函数调用、编排与自动化。',
|
||||
'Select a model': '选择模型',
|
||||
'Template': '模板',
|
||||
'Select a template': '选择模板',
|
||||
'Display Name': '显示名称',
|
||||
'Enter name': '请输入名称',
|
||||
'Identifier': '标识符',
|
||||
'Enter identifier': '请输入标识符',
|
||||
'Only lowercase letters, numbers, dash, dot and underscore are allowed': '仅允许小写字母、数字、连字符、点和下划线',
|
||||
'API Format': 'API 格式',
|
||||
'Base URL': '基础 URL',
|
||||
'Enter base url': '请输入基础 URL',
|
||||
'Optional, can also be provided per request': '可选,也可在请求时提供',
|
||||
'Model Identifier': '模型标识',
|
||||
'Enter model identifier': '请输入模型标识',
|
||||
'Description': '描述',
|
||||
'Capabilities': '能力标签',
|
||||
'Context Window': '上下文窗口',
|
||||
'Embedding Dimensions': '向量维度',
|
||||
'Price /1K input tokens': '价格 /1K 输入 token',
|
||||
'Price /1K output tokens': '价格 /1K 输出 token',
|
||||
|
||||
// Adapters
|
||||
'Missing required config:': '缺少必填配置:',
|
||||
'Updated successfully': '更新成功',
|
||||
'Created successfully': '创建成功',
|
||||
'Operation failed': '操作失败',
|
||||
'Deleted': '已删除',
|
||||
'Delete failed': '删除失败',
|
||||
'Status updated': '状态已更新',
|
||||
'Update failed': '更新失败',
|
||||
'Mount Path': '挂载路径',
|
||||
'Sub Path': '子路径',
|
||||
'Sub Path (optional)': '子路径(可选)',
|
||||
'Sub directory inside adapter': '适配器内部子目录',
|
||||
'Enabled': '启用',
|
||||
'Actions': '操作',
|
||||
'Edit': '编辑',
|
||||
'Confirm delete?': '确认删除?',
|
||||
'No config fields': '无配置项',
|
||||
'Please input {label}': '请输入{label}',
|
||||
'Storage Adapters': '存储适配器',
|
||||
'Create Adapter': '新建适配器',
|
||||
'Unique name': '唯一名称',
|
||||
'Select adapter type': '选择适配器类型',
|
||||
'/ or /drive': '/或/drive',
|
||||
'Adapter Config': '适配器配置',
|
||||
'adapter.type.local': '本地文件系统',
|
||||
'adapter.type.webdav': 'WebDAV',
|
||||
'adapter.type.googledrive': 'Google Drive',
|
||||
'adapter.type.onedrive': 'OneDrive',
|
||||
'adapter.type.s3': 'Amazon S3',
|
||||
'adapter.type.ftp': 'FTP',
|
||||
'adapter.type.sftp': 'SFTP',
|
||||
'adapter.type.telegram': 'Telegram',
|
||||
'adapter.type.quark': '夸克网盘',
|
||||
|
||||
// Tasks
|
||||
'Automation Tasks': '自动化任务',
|
||||
'Running Tasks': '运行中的任务',
|
||||
'Create Task': '新建任务',
|
||||
'Edit Task': '编辑任务',
|
||||
'Create Automation Task': '新建自动化任务',
|
||||
'Task Name': '任务名称',
|
||||
'Trigger Event': '触发事件',
|
||||
'File Written': '文件写入',
|
||||
'File Deleted': '文件删除',
|
||||
'Matching Rules': '匹配规则',
|
||||
'Path Prefix (optional)': '路径前缀 (可选)',
|
||||
'Filename Regex (optional)': '文件名正则 (可选)',
|
||||
'Action': '执行动作',
|
||||
'Current Task Queue': '当前任务队列',
|
||||
'Params': '参数',
|
||||
'Status': '状态',
|
||||
|
||||
// Logs
|
||||
'Confirm clear logs?': '确认清理日志?',
|
||||
'This will delete logs in selected range irreversibly.': '该操作将删除选定时间范围内的所有日志,且不可恢复。',
|
||||
'Cleared {count} logs': '成功清理 {count} 条日志',
|
||||
'Time': '时间',
|
||||
'Level': '级别',
|
||||
'Source': '来源',
|
||||
'Message': '消息',
|
||||
'User ID': '用户 ID',
|
||||
'Search source': '搜索来源',
|
||||
'Clear': '清理',
|
||||
'Log Details': '日志详情',
|
||||
'Raw Log': '原始日志',
|
||||
|
||||
// Backup
|
||||
'Export started, check your downloads.': '导出已开始,请检查您的下载。',
|
||||
'Export failed': '导出失败',
|
||||
'Confirm import backup?': '确认导入备份?',
|
||||
'Are you sure to import from this file?': '您确定要从此文件导入数据吗?',
|
||||
'Warning: This will overwrite all data including users (with passwords), settings, storages and tasks. Irreversible!': '警告:此操作将覆盖当前数据库中的所有现有数据,包括用户(含密码)、设置、存储和任务。此操作不可逆!',
|
||||
'Confirm Import': '确认导入',
|
||||
'Import succeeded! The page will refresh.': '导入成功!页面将刷新。',
|
||||
'Import failed': '导入失败',
|
||||
'Export': '导出',
|
||||
'Import': '恢复',
|
||||
'Export all data (adapters, users, tasks, shares) into a JSON file.': '点击按钮将所有数据(包括存储、用户、自动化任务和分享)导出为一个 JSON 文件。',
|
||||
'Keep your backup file safe.': '请妥善保管您的备份文件。',
|
||||
'Export Backup': '导出备份',
|
||||
'Restore data from a previously exported JSON file.': '从之前导出的JSON文件恢复数据。',
|
||||
'Warning: This will clear and overwrite existing data.': '警告:此操作将清除并覆盖现有数据。',
|
||||
'Choose File and Restore': '选择文件并恢复',
|
||||
|
||||
// Empty state
|
||||
'No files yet here': '这里还没有任何文件',
|
||||
'This folder is empty': '此目录为空',
|
||||
'Start uploading files or create folders to organize your content': '开始上传文件或创建新目录来组织您的内容',
|
||||
'You can create folders or upload files here': '您可以在此目录中创建新的文件夹或上传文件',
|
||||
|
||||
// File actions
|
||||
'Please input name': '请输入名称',
|
||||
'Confirm delete {name}?': '确认删除 {name} ?',
|
||||
'items': '项',
|
||||
'Downloading folders is not supported': '暂不支持下载目录',
|
||||
'Download failed': '下载失败',
|
||||
'Please select files or folders to share': '请选择要分享的文件或目录',
|
||||
'Direct links for folders are not supported': '不支持获取目录的直链',
|
||||
|
||||
// Processor flow
|
||||
'Processing finished': '处理完成',
|
||||
'Processing failed': '处理失败',
|
||||
'Processors': '处理器',
|
||||
'Processor List': '处理器列表',
|
||||
'Reload': '重载',
|
||||
'Run Processor': '运行处理器',
|
||||
'Target Path': '目标路径',
|
||||
'Please select a path': '请选择路径',
|
||||
'Select Directory': '选择目录',
|
||||
'Overwrite original': '覆盖原文件',
|
||||
'Save To': '保存到',
|
||||
'Optional output path': '可选输出路径',
|
||||
'Run': '运行',
|
||||
'Select a processor': '选择处理器',
|
||||
'No module path': '未检测到模块路径',
|
||||
'Source saved': '源码已保存',
|
||||
'Processors reloaded': '处理器已重载',
|
||||
'Unsaved changes': '存在未保存的修改',
|
||||
'Switching processor will discard unsaved changes. Continue?': '切换处理器会丢失未保存的修改,确认继续?',
|
||||
'Task submitted': '任务已提交',
|
||||
'Supported Extensions': '支持的扩展名',
|
||||
'All': '全部',
|
||||
'Produces File': '生成文件',
|
||||
'Yes': '是',
|
||||
'No': '否',
|
||||
'Please select a processor': '请选择处理器',
|
||||
'Select a path': '请选择路径',
|
||||
'Source Editor': '源码编辑',
|
||||
'Module Path': '模块路径',
|
||||
'Directory processing always overwrites original files': '选择目录时会强制覆盖原文件',
|
||||
'Directory execution will enqueue one task per file': '目录模式会为每个文件单独创建任务',
|
||||
'Directory scope': '目录范围',
|
||||
'Current level only': '仅当前层级',
|
||||
'Include subdirectories': '包含子目录',
|
||||
'Max depth': '最大层级',
|
||||
'Leave empty to traverse all subdirectories': '留空表示遍历所有子目录',
|
||||
'Depth must be greater or equal to 0': '层级必须大于或等于 0',
|
||||
'Output suffix': '输出后缀',
|
||||
'Suffix will be inserted before the file extension, e.g. demo_processed.mp4': '后缀会插入到文件扩展名前,例如 demo_processed.mp4',
|
||||
'Suffix such as _processed': '例如 _processed 的后缀',
|
||||
'Suffix cannot be empty': '后缀不能为空',
|
||||
'No data': '暂无数据',
|
||||
|
||||
// Path selector
|
||||
'Select File': '选择文件',
|
||||
'Select Path': '选择路径',
|
||||
'Select Folder': '选择目录',
|
||||
'Select': '选择',
|
||||
'Current': '当前',
|
||||
'Up': '上一级',
|
||||
'Select Current Folder': '选择当前目录',
|
||||
'Please select a file': '请选择一个文件',
|
||||
|
||||
// Plugins page
|
||||
'Installed successfully': '安装成功',
|
||||
'Plugin': '插件',
|
||||
'Open Link': '打开链接',
|
||||
'Link copied': '已复制链接',
|
||||
'Copy Link': '复制链接',
|
||||
'Confirm delete this plugin?': '确认删除该插件?',
|
||||
'Author': '作者',
|
||||
'Website': '官网',
|
||||
'Install App': '安装应用',
|
||||
'Search name/author/url/extension': '搜索 名称/作者/链接/扩展名',
|
||||
'No plugins': '暂无插件',
|
||||
'Install': '安装',
|
||||
'App URL': '应用链接',
|
||||
'Please input a valid URL': '请输入合法的 URL',
|
||||
'Installed': '已安装',
|
||||
'Discover': '发现',
|
||||
'Search apps': '搜索应用',
|
||||
'Sort by': '排序',
|
||||
'Downloads': '下载量',
|
||||
'Created (newest)': '创建时间(最新)',
|
||||
'Installed already': '已安装',
|
||||
'No results': '暂无结果',
|
||||
|
||||
// Setup page
|
||||
'Initialization succeeded! Logging you in...': '初始化成功!正在为您登录,请不要刷新。',
|
||||
'Initialization failed, please try later': '初始化失败,请稍后重试',
|
||||
'Database Setup': '数据库设置',
|
||||
'Choose database driver': '选择数据库驱动',
|
||||
'Select database and vector database for system data': '选择用于存储系统数据的数据库和向量数据库。',
|
||||
'Database Driver': '数据库驱动',
|
||||
'Vector DB Driver': '向量数据库驱动',
|
||||
'Initialize Mount': '初始化挂载',
|
||||
'Configure initial storage': '配置初始存储',
|
||||
'Create the first storage mount for your files': '为您的文件创建第一个存储挂载点。',
|
||||
'Mount Name': '挂载名称',
|
||||
'Local Storage': '本地存储',
|
||||
'Please input mount name!': '请输入挂载名称!',
|
||||
'Storage Type': '存储类型',
|
||||
'Please input mount path!': '请输入挂载路径!',
|
||||
'Root Directory': '根目录',
|
||||
'Please input root directory!': '请输入根目录!',
|
||||
'e.g., data/ or /var/foxel/data': '例如: data/ 或 /var/foxel/data',
|
||||
'Optional, used for external links. Leave empty to use the current site.': '可选,用于生成外部链接;留空则使用当前站点。',
|
||||
'Create Admin': '创建管理员',
|
||||
'Create admin account': '创建管理员账户',
|
||||
'This is the first account with full permissions': '这是系统的第一个账户,将拥有最高权限。',
|
||||
'Username': '用户名',
|
||||
'Please input a valid email!': '请输入有效的邮箱地址!',
|
||||
'Please confirm your password!': '请确认您的密码!',
|
||||
'Passwords do not match!': '两次输入的密码不一致!',
|
||||
'System Initialization': '系统初始化',
|
||||
'Previous': '上一步',
|
||||
'Next': '下一步',
|
||||
'Finish Initialization': '完成初始化',
|
||||
|
||||
// Plugin host
|
||||
'Plugin run failed': '插件运行失败',
|
||||
'Plugin Error': '插件错误',
|
||||
'Cannot open file: no available app': '无法打开该文件:没有可用的应用',
|
||||
'Error': '错误',
|
||||
'App "{key}" not found.': '应用 "{key}" 不存在。',
|
||||
'Open with {app}': '使用 {app} 打开',
|
||||
'Set as default for .{ext}': '设为该类型(.{ext})默认应用',
|
||||
'Advanced tokens must be valid JSON': '高级 Token 需为合法 JSON',
|
||||
} as const;
|
||||
|
||||
export type ZhKeys = keyof typeof zh;
|
||||
@@ -44,7 +44,7 @@ export const navGroups: NavGroup[] = [
|
||||
children: [
|
||||
{ key: 'settings', icon: React.createElement(SettingOutlined), label: 'System Settings' },
|
||||
{ key: 'backup', icon: React.createElement(DatabaseOutlined), label: 'Backup & Restore' },
|
||||
{ key: 'logs', icon: React.createElement(BugOutlined), label: 'System Logs' }
|
||||
{ key: 'audit', icon: React.createElement(BugOutlined), label: 'Audit Logs' }
|
||||
]
|
||||
}
|
||||
];
|
||||
|
||||
298
web/src/pages/AuditLogsPage.tsx
Normal file
298
web/src/pages/AuditLogsPage.tsx
Normal file
@@ -0,0 +1,298 @@
|
||||
import { memo, useState, useEffect, useCallback } from 'react';
|
||||
import { Table, message, Tag, Input, Select, Button, Space, Modal, DatePicker, Descriptions, Divider, Typography } from 'antd';
|
||||
import PageCard from '../components/PageCard';
|
||||
import { auditApi, type AuditLogItem, type PaginatedAuditLogs } from '../api/audit';
|
||||
import { useI18n } from '../i18n';
|
||||
import { format, formatISO } from 'date-fns';
|
||||
|
||||
const { RangePicker } = DatePicker;
|
||||
|
||||
const ACTION_OPTIONS = [
|
||||
'login',
|
||||
'logout',
|
||||
'register',
|
||||
'read',
|
||||
'create',
|
||||
'update',
|
||||
'delete',
|
||||
'reset_password',
|
||||
'share',
|
||||
'download',
|
||||
'upload',
|
||||
'other'
|
||||
];
|
||||
|
||||
const AuditLogsPage = memo(function AuditLogsPage() {
|
||||
const [loading, setLoading] = useState(false);
|
||||
const [data, setData] = useState<PaginatedAuditLogs | null>(null);
|
||||
const [filters, setFilters] = useState<{
|
||||
page: number;
|
||||
page_size: number;
|
||||
action: string;
|
||||
success: '' | boolean;
|
||||
username: string;
|
||||
path: string;
|
||||
start_time: string;
|
||||
end_time: string;
|
||||
}>({
|
||||
page: 1,
|
||||
page_size: 20,
|
||||
action: '',
|
||||
success: '',
|
||||
username: '',
|
||||
path: '',
|
||||
start_time: '',
|
||||
end_time: '',
|
||||
});
|
||||
const [selectedLog, setSelectedLog] = useState<AuditLogItem | null>(null);
|
||||
const { t } = useI18n();
|
||||
|
||||
const buildParams = () => {
|
||||
const params: any = { ...filters };
|
||||
if (!params.action) delete params.action;
|
||||
if (params.success === '') delete params.success;
|
||||
if (!params.username) delete params.username;
|
||||
if (!params.path) delete params.path;
|
||||
if (!params.start_time) delete params.start_time;
|
||||
if (!params.end_time) delete params.end_time;
|
||||
return params;
|
||||
};
|
||||
|
||||
const fetchList = useCallback(async () => {
|
||||
setLoading(true);
|
||||
try {
|
||||
const res = await auditApi.list(buildParams());
|
||||
setData(res);
|
||||
} catch (e: any) {
|
||||
message.error(e.message || t('Load failed'));
|
||||
} finally {
|
||||
setLoading(false);
|
||||
}
|
||||
}, [filters]);
|
||||
|
||||
useEffect(() => {
|
||||
fetchList();
|
||||
}, [fetchList]);
|
||||
|
||||
const handleClearLogs = () => {
|
||||
if (!filters.start_time && !filters.end_time) {
|
||||
message.warning(t('Please select time range'));
|
||||
return;
|
||||
}
|
||||
Modal.confirm({
|
||||
title: t('Confirm clear logs?'),
|
||||
content: t('This will delete logs in selected range irreversibly.'),
|
||||
onOk: async () => {
|
||||
try {
|
||||
const params: any = {};
|
||||
if (filters.start_time) params.start_time = filters.start_time;
|
||||
if (filters.end_time) params.end_time = filters.end_time;
|
||||
const res = await auditApi.clear(params);
|
||||
message.success(t('Cleared {count} logs', { count: String(res.deleted_count) }));
|
||||
fetchList();
|
||||
} catch (e: any) {
|
||||
message.error(e.message || t('Clear failed'));
|
||||
}
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
const columns = [
|
||||
{
|
||||
title: t('Time'),
|
||||
dataIndex: 'created_at',
|
||||
width: 180,
|
||||
render: (ts: string) => format(new Date(ts), 'yyyy-MM-dd HH:mm:ss'),
|
||||
},
|
||||
{
|
||||
title: t('Action'),
|
||||
dataIndex: 'action',
|
||||
width: 140,
|
||||
render: (action: string) => <Tag color="blue">{action}</Tag>,
|
||||
},
|
||||
{
|
||||
title: t('User'),
|
||||
dataIndex: 'username',
|
||||
width: 160,
|
||||
render: (_: any, rec: AuditLogItem) => rec.username || rec.user_id || '-',
|
||||
},
|
||||
{
|
||||
title: t('Path'),
|
||||
dataIndex: 'path',
|
||||
ellipsis: true,
|
||||
render: (path: string, rec: AuditLogItem) => (
|
||||
<Space size={4}>
|
||||
<Tag bordered={false} color="default" style={{ margin: 0, paddingInline: 8 }}>{rec.method}</Tag>
|
||||
<span style={{ maxWidth: 320, display: 'inline-block' }}>{path}</span>
|
||||
</Space>
|
||||
),
|
||||
},
|
||||
{
|
||||
title: t('Status Code'),
|
||||
dataIndex: 'status_code',
|
||||
width: 100,
|
||||
},
|
||||
{
|
||||
title: t('Duration (ms)'),
|
||||
dataIndex: 'duration_ms',
|
||||
width: 120,
|
||||
render: (ms?: number | null) => (ms !== null && ms !== undefined ? ms : '-'),
|
||||
},
|
||||
{
|
||||
title: t('Client IP'),
|
||||
dataIndex: 'client_ip',
|
||||
width: 140,
|
||||
render: (ip?: string | null) => ip || '-',
|
||||
},
|
||||
{
|
||||
title: t('Result'),
|
||||
dataIndex: 'success',
|
||||
width: 100,
|
||||
render: (success: boolean) => (
|
||||
<Tag color={success ? 'green' : 'red'}>
|
||||
{success ? t('Success') : t('Failure')}
|
||||
</Tag>
|
||||
),
|
||||
},
|
||||
{
|
||||
title: t('Actions'),
|
||||
width: 100,
|
||||
render: (_: any, rec: AuditLogItem) => (
|
||||
<Button size="small" onClick={() => setSelectedLog(rec)}>{t('Details')}</Button>
|
||||
),
|
||||
},
|
||||
];
|
||||
|
||||
return (
|
||||
<PageCard
|
||||
title={t('Audit Logs')}
|
||||
extra={
|
||||
<Space align="center" wrap size={[8, 8]}>
|
||||
<RangePicker
|
||||
showTime
|
||||
size="small"
|
||||
onChange={dates => {
|
||||
setFilters(f => ({
|
||||
...f,
|
||||
start_time: dates?.[0] ? formatISO(dates[0].toDate()) : '',
|
||||
end_time: dates?.[1] ? formatISO(dates[1].toDate()) : '',
|
||||
page: 1,
|
||||
}));
|
||||
}}
|
||||
/>
|
||||
<Select
|
||||
style={{ width: 120 }}
|
||||
placeholder={t('Action')}
|
||||
allowClear
|
||||
size="small"
|
||||
value={filters.action || undefined}
|
||||
onChange={action => setFilters(f => ({ ...f, action: action || '', page: 1 }))}
|
||||
options={ACTION_OPTIONS.map(a => ({ value: a, label: a }))}
|
||||
/>
|
||||
<Select
|
||||
style={{ width: 120 }}
|
||||
placeholder={t('Result')}
|
||||
allowClear
|
||||
size="small"
|
||||
value={filters.success === '' ? undefined : filters.success}
|
||||
onChange={value => setFilters(f => ({ ...f, success: (value as boolean) ?? '', page: 1 }))}
|
||||
options={[
|
||||
{ value: true, label: t('Success') },
|
||||
{ value: false, label: t('Failure') },
|
||||
]}
|
||||
/>
|
||||
<Input.Search
|
||||
style={{ width: 120 }}
|
||||
placeholder={t('Username')}
|
||||
size="small"
|
||||
allowClear
|
||||
value={filters.username}
|
||||
onChange={e => setFilters(f => ({ ...f, username: e.target.value }))}
|
||||
onSearch={username => setFilters(f => ({ ...f, username, page: 1 }))}
|
||||
/>
|
||||
<Button onClick={fetchList} loading={loading}>{t('Refresh')}</Button>
|
||||
<Button danger onClick={handleClearLogs}>{t('Clear')}</Button>
|
||||
</Space>
|
||||
}
|
||||
>
|
||||
<Table
|
||||
rowKey="id"
|
||||
dataSource={data?.items}
|
||||
columns={columns}
|
||||
loading={loading}
|
||||
pagination={{
|
||||
current: filters.page,
|
||||
pageSize: filters.page_size,
|
||||
total: data?.total,
|
||||
showSizeChanger: true,
|
||||
onChange: (page, pageSize) => setFilters(f => ({ ...f, page, page_size: pageSize })),
|
||||
}}
|
||||
scroll={{ x: 900 }}
|
||||
/>
|
||||
<Modal
|
||||
title={t('Audit Log Details')}
|
||||
open={!!selectedLog}
|
||||
onCancel={() => setSelectedLog(null)}
|
||||
footer={null}
|
||||
width={900}
|
||||
>
|
||||
{selectedLog && (
|
||||
<Space direction="vertical" size={16} style={{ width: '100%' }}>
|
||||
<Descriptions column={2} bordered size="small">
|
||||
<Descriptions.Item label={t('Time')}>
|
||||
{format(new Date(selectedLog.created_at), 'yyyy-MM-dd HH:mm:ss')}
|
||||
</Descriptions.Item>
|
||||
<Descriptions.Item label={t('Action')}>
|
||||
<Tag color="blue">{selectedLog.action}</Tag>
|
||||
</Descriptions.Item>
|
||||
<Descriptions.Item label={t('User')}>
|
||||
{selectedLog.username || selectedLog.user_id || '-'}
|
||||
</Descriptions.Item>
|
||||
<Descriptions.Item label={t('Client IP')}>
|
||||
{selectedLog.client_ip || '-'}
|
||||
</Descriptions.Item>
|
||||
<Descriptions.Item label={t('Path')} span={2}>
|
||||
<Space size={6}>
|
||||
<Tag bordered={false} color="default" style={{ margin: 0, paddingInline: 8 }}>{selectedLog.method}</Tag>
|
||||
<Typography.Text copyable>{selectedLog.path}</Typography.Text>
|
||||
</Space>
|
||||
</Descriptions.Item>
|
||||
<Descriptions.Item label={t('Status Code')}>
|
||||
{selectedLog.status_code}
|
||||
</Descriptions.Item>
|
||||
<Descriptions.Item label={t('Duration (ms)')}>
|
||||
{selectedLog.duration_ms ?? '-'}
|
||||
</Descriptions.Item>
|
||||
<Descriptions.Item label={t('Result')}>
|
||||
<Tag color={selectedLog.success ? 'green' : 'red'}>
|
||||
{selectedLog.success ? t('Success') : t('Failure')}
|
||||
</Tag>
|
||||
</Descriptions.Item>
|
||||
<Descriptions.Item label={t('Description')}>
|
||||
{selectedLog.description || '-'}
|
||||
</Descriptions.Item>
|
||||
<Descriptions.Item label={t('Error')} span={2}>
|
||||
{selectedLog.error || '-'}
|
||||
</Descriptions.Item>
|
||||
</Descriptions>
|
||||
<Divider style={{ margin: '12px 0 0' }} />
|
||||
<Typography.Title level={5} style={{ margin: 0 }}>
|
||||
{t('Request Params')}
|
||||
</Typography.Title>
|
||||
<pre style={{ maxHeight: 200, overflow: 'auto', background: 'var(--ant-color-fill-tertiary, #f5f5f5)', padding: 12 }}>
|
||||
{selectedLog.request_params ? JSON.stringify(selectedLog.request_params, null, 2) : '-'}
|
||||
</pre>
|
||||
<Typography.Title level={5} style={{ margin: 0 }}>
|
||||
{t('Request Body')}
|
||||
</Typography.Title>
|
||||
<pre style={{ maxHeight: 200, overflow: 'auto', background: 'var(--ant-color-fill-tertiary, #f5f5f5)', padding: 12 }}>
|
||||
{selectedLog.request_body ? JSON.stringify(selectedLog.request_body, null, 2) : '-'}
|
||||
</pre>
|
||||
</Space>
|
||||
)}
|
||||
</Modal>
|
||||
</PageCard>
|
||||
);
|
||||
});
|
||||
|
||||
export default AuditLogsPage;
|
||||
@@ -1,184 +0,0 @@
|
||||
import { memo, useState, useEffect, useCallback } from 'react';
|
||||
import { Table, message, Tag, Input, Select, Button, Space, Modal, DatePicker, Descriptions, Divider, Typography } from 'antd';
|
||||
import PageCard from '../components/PageCard';
|
||||
import { logsApi, type LogItem, type PaginatedLogs } from '../api/logs';
|
||||
import { useI18n } from '../i18n';
|
||||
import { format, formatISO } from 'date-fns';
|
||||
|
||||
const { RangePicker } = DatePicker;
|
||||
|
||||
const LOG_LEVELS = ['API', 'INFO', 'WARNING', 'ERROR'];
|
||||
const LEVEL_COLOR_MAP: Record<string, string> = { API: 'blue', INFO: 'green', WARNING: 'orange', ERROR: 'red' };
|
||||
|
||||
const LogsPage = memo(function LogsPage() {
|
||||
const [loading, setLoading] = useState(false);
|
||||
const [data, setData] = useState<PaginatedLogs | null>(null);
|
||||
const [filters, setFilters] = useState({
|
||||
page: 1,
|
||||
page_size: 20,
|
||||
level: '',
|
||||
source: '',
|
||||
start_time: '',
|
||||
end_time: '',
|
||||
});
|
||||
const [selectedLog, setSelectedLog] = useState<LogItem | null>(null);
|
||||
const { t } = useI18n();
|
||||
|
||||
const fetchList = useCallback(async () => {
|
||||
setLoading(true);
|
||||
try {
|
||||
const params = { ...filters };
|
||||
if (!params.start_time) delete (params as any).start_time;
|
||||
if (!params.end_time) delete (params as any).end_time;
|
||||
|
||||
const res = await logsApi.list(params);
|
||||
setData(res);
|
||||
} catch (e: any) {
|
||||
message.error(e.message || t('Load failed'));
|
||||
} finally {
|
||||
setLoading(false);
|
||||
}
|
||||
}, [filters]);
|
||||
|
||||
useEffect(() => {
|
||||
fetchList();
|
||||
}, [fetchList]);
|
||||
|
||||
const handleClearLogs = () => {
|
||||
Modal.confirm({
|
||||
title: t('Confirm clear logs?'),
|
||||
content: t('This will delete logs in selected range irreversibly.'),
|
||||
onOk: async () => {
|
||||
try {
|
||||
const params = { start_time: filters.start_time, end_time: filters.end_time };
|
||||
if (!params.start_time) delete (params as any).start_time;
|
||||
if (!params.end_time) delete (params as any).end_time;
|
||||
const res = await logsApi.clear(params);
|
||||
message.success(t('Cleared {count} logs', { count: String(res.deleted_count) }));
|
||||
fetchList();
|
||||
} catch (e: any) {
|
||||
message.error(e.message || t('Clear failed'));
|
||||
}
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
const columns = [
|
||||
{
|
||||
title: t('Time'),
|
||||
dataIndex: 'timestamp',
|
||||
width: 180,
|
||||
render: (ts: string) => format(new Date(ts), 'yyyy-MM-dd HH:mm:ss'),
|
||||
},
|
||||
{
|
||||
title: t('Level'),
|
||||
dataIndex: 'level',
|
||||
width: 100,
|
||||
render: (level: string) => {
|
||||
const color = LEVEL_COLOR_MAP[level] || 'default';
|
||||
return <Tag color={color}>{level}</Tag>;
|
||||
},
|
||||
},
|
||||
{ title: t('Source'), dataIndex: 'source', width: 180 },
|
||||
{ title: t('Message'), dataIndex: 'message', ellipsis: true },
|
||||
{
|
||||
title: t('Actions'),
|
||||
width: 100,
|
||||
render: (_: any, rec: LogItem) => (
|
||||
<Button size="small" onClick={() => setSelectedLog(rec)}>{t('Details')}</Button>
|
||||
),
|
||||
},
|
||||
];
|
||||
|
||||
return (
|
||||
<PageCard
|
||||
title={t('System Logs')}
|
||||
extra={
|
||||
<Space align="center">
|
||||
<RangePicker
|
||||
showTime
|
||||
size="small"
|
||||
onChange={dates => {
|
||||
setFilters(f => ({
|
||||
...f,
|
||||
start_time: dates?.[0] ? formatISO(dates[0].toDate()) : '',
|
||||
end_time: dates?.[1] ? formatISO(dates[1].toDate()) : '',
|
||||
page: 1,
|
||||
}));
|
||||
}}
|
||||
/>
|
||||
<Select
|
||||
style={{ width: 120 }}
|
||||
placeholder={t('Level')}
|
||||
allowClear
|
||||
size="small"
|
||||
value={filters.level || undefined}
|
||||
onChange={level => setFilters(f => ({ ...f, level: level || '', page: 1 }))}
|
||||
options={LOG_LEVELS.map(l => ({ value: l, label: l }))}
|
||||
/>
|
||||
<Input.Search
|
||||
style={{ width: 240 }}
|
||||
placeholder={t('Search source')}
|
||||
size="small"
|
||||
onSearch={source => setFilters(f => ({ ...f, source, page: 1 }))}
|
||||
allowClear
|
||||
/>
|
||||
<Button onClick={fetchList} loading={loading}>{t('Refresh')}</Button>
|
||||
<Button danger onClick={handleClearLogs}>{t('Clear')}</Button>
|
||||
</Space>
|
||||
}
|
||||
>
|
||||
<Table
|
||||
rowKey="id"
|
||||
dataSource={data?.items}
|
||||
columns={columns}
|
||||
loading={loading}
|
||||
pagination={{
|
||||
current: filters.page,
|
||||
pageSize: filters.page_size,
|
||||
total: data?.total,
|
||||
showSizeChanger: true,
|
||||
onChange: (page, pageSize) => setFilters(f => ({ ...f, page, page_size: pageSize })),
|
||||
}}
|
||||
/>
|
||||
<Modal
|
||||
title={t('Log Details')}
|
||||
open={!!selectedLog}
|
||||
onCancel={() => setSelectedLog(null)}
|
||||
footer={null}
|
||||
width={800}
|
||||
>
|
||||
{selectedLog && (
|
||||
<Space direction="vertical" size={16} style={{ width: '100%' }}>
|
||||
<Descriptions column={1} bordered size="small">
|
||||
<Descriptions.Item label={t('Time')}>
|
||||
{format(new Date(selectedLog.timestamp), 'yyyy-MM-dd HH:mm:ss')}
|
||||
</Descriptions.Item>
|
||||
<Descriptions.Item label={t('Level')}>
|
||||
<Tag color={LEVEL_COLOR_MAP[selectedLog.level] || 'default'}>{selectedLog.level}</Tag>
|
||||
</Descriptions.Item>
|
||||
<Descriptions.Item label={t('Source')}>
|
||||
{selectedLog.source}
|
||||
</Descriptions.Item>
|
||||
<Descriptions.Item label={t('Message')}>
|
||||
<Typography.Text style={{ whiteSpace: 'pre-wrap' }}>{selectedLog.message}</Typography.Text>
|
||||
</Descriptions.Item>
|
||||
<Descriptions.Item label={t('User ID')}>
|
||||
{selectedLog.user_id ?? '-'}
|
||||
</Descriptions.Item>
|
||||
</Descriptions>
|
||||
<Divider style={{ margin: '12px 0 0' }} />
|
||||
<Typography.Title level={5} style={{ margin: 0 }}>
|
||||
{t('Raw Log')}
|
||||
</Typography.Title>
|
||||
<pre style={{ maxHeight: '60vh', overflow: 'auto', background: 'var(--ant-color-fill-tertiary, #f5f5f5)', padding: 12 }}>
|
||||
{JSON.stringify(selectedLog, null, 2)}
|
||||
</pre>
|
||||
</Space>
|
||||
)}
|
||||
</Modal>
|
||||
</PageCard>
|
||||
);
|
||||
});
|
||||
|
||||
export default LogsPage;
|
||||
@@ -11,7 +11,7 @@ import TaskQueuePage from '../pages/TaskQueuePage.tsx';
|
||||
import ProcessorsPage from '../pages/ProcessorsPage.tsx';
|
||||
import OfflineDownloadPage from '../pages/OfflineDownloadPage.tsx';
|
||||
import SystemSettingsPage from '../pages/SystemSettingsPage/SystemSettingsPage.tsx';
|
||||
import LogsPage from '../pages/LogsPage.tsx';
|
||||
import AuditLogsPage from '../pages/AuditLogsPage.tsx';
|
||||
import BackupPage from '../pages/SystemSettingsPage/BackupPage.tsx';
|
||||
import PluginsPage from '../pages/PluginsPage.tsx';
|
||||
import { AppWindowsProvider, useAppWindows } from '../contexts/AppWindowsContext';
|
||||
@@ -58,7 +58,7 @@ const ShellBody = memo(function ShellBody() {
|
||||
onTabNavigate={(key, options) => navigate(`/settings/${key}`, options)}
|
||||
/>
|
||||
)}
|
||||
{navKey === 'logs' && <LogsPage />}
|
||||
{navKey === 'audit' && <AuditLogsPage />}
|
||||
{navKey === 'backup' && <BackupPage />}
|
||||
</Flex>
|
||||
</div>
|
||||
|
||||
@@ -12,6 +12,7 @@
|
||||
"allowImportingTsExtensions": true,
|
||||
"verbatimModuleSyntax": true,
|
||||
"moduleDetection": "force",
|
||||
"resolveJsonModule": true,
|
||||
"noEmit": true,
|
||||
"jsx": "react-jsx",
|
||||
|
||||
|
||||
Reference in New Issue
Block a user