diff --git a/global.d.ts b/global.d.ts
new file mode 100644
index 0000000..1cbcc65
--- /dev/null
+++ b/global.d.ts
@@ -0,0 +1,4 @@
+// Use type safe message keys with `next-intl`
+// eslint-disable-next-line @typescript-eslint/consistent-type-imports
+type Messages = typeof import("./messages/en.json");
+type IntlMessages = Messages;
diff --git a/messages/en.json b/messages/en.json
new file mode 100644
index 0000000..c85959b
--- /dev/null
+++ b/messages/en.json
@@ -0,0 +1,719 @@
+{
+ "admin-config": {},
+ "admin-config-[classify]": {},
+ "admin-config-code-input-dialog": {
+ "edit": "Edit",
+ "preview": "Preview"
+ },
+ "admin-config-config-field": {
+ "save": "Save"
+ },
+ "admin-config-config-list": {},
+ "admin-config-cron-input": {
+ "after": "After",
+ "any_value": "Any Value",
+ "day": "Day",
+ "hide": "Hide",
+ "hour": "Hour",
+ "minute": "Minute",
+ "month": "Month",
+ "more": "More",
+ "next": "Next",
+ "possible_values": "Possible Values",
+ "step": "Step",
+ "value_in_range": "Value in Range",
+ "value_separator": "Value Separator",
+ "week": "Week"
+ },
+ "admin-config-layout": {
+ "general": "General",
+ "general_config_etc": "general config, etc.",
+ "including_log_config": "Including log config",
+ "log_config": "Log Config",
+ "node_related_config": "Node Related Config",
+ "payment_records": "Payment Records",
+ "recharge_code": "Recharge Code",
+ "system_config": "System Config",
+ "website_system_config": "Website System Config",
+ "withdrawal_records": "Withdrawal Records"
+ },
+ "admin-config-payment": {},
+ "admin-config-payment-info": {},
+ "admin-config-payment-table": {
+ "info": "Info",
+ "received_amount": "Received Amount",
+ "recharge_amount": "Recharge Amount",
+ "reset": "Reset",
+ "status": "Status",
+ "user": "User"
+ },
+ "admin-config-recharge-code": {
+ "balance": "Balance",
+ "batch_generate_export_recharge_code": "Batch generate and export recharge code",
+ "manage_recharge_code": "Manage Recharge Code",
+ "personal_center": "Personal Center",
+ "recharge_code": "Recharge Code",
+ "recharge_code_in": "Recharge code in",
+ "recharge_use": "Recharge Use"
+ },
+ "admin-config-recharge-code-table": {
+ "add": "Add",
+ "amount": "Amount",
+ "cancel": "Cancel",
+ "confirm_delete_recharge_code": "Are you sure you want to delete this recharge code?",
+ "continue": "Continue",
+ "create_recharge_code": "Create Recharge Code",
+ "export_after_creation": "Export after creation",
+ "pieces": "pieces",
+ "quantity": "Quantity",
+ "recharge_code": "Recharge Code",
+ "reset": "Reset",
+ "save": "Save",
+ "total": "Total",
+ "usage_status": "Usage Status",
+ "used": "Used",
+ "used_by": "Used By",
+ "user": "User",
+ "yuan": "yuan"
+ },
+ "admin-config-traffic-price-config": {
+ "price": "Price",
+ "unit": "Unit"
+ },
+ "admin-config-withdraw": {},
+ "admin-config-withdrawal-table": {
+ "address": "Address",
+ "operation_successful": "Operation Successful",
+ "reset": "Reset",
+ "status": "Status",
+ "user": "User",
+ "user_wallet_balance_updated": "User wallet earnings balance has been automatically updated",
+ "withdrawal_amount": "Withdrawal Amount",
+ "withdrawal_status_updated": "Withdrawal status has been updated"
+ },
+ "admin-log": {
+ "logs": "Logs"
+ },
+ "admin-log-log": {
+ "copied_to_clipboard": "Copied to clipboard"
+ },
+ "admin-log-log-delete": {
+ "cancel": "Cancel",
+ "confirm_delete_all_logs": "Are you sure you want to delete all logs?",
+ "continue": "Continue"
+ },
+ "admin-log-log-glance": {},
+ "admin-log-log-search-keyword": {
+ "advanced_search": "Advanced Search",
+ "array_index_is": "Array index is",
+ "array_index_with": "Array index with",
+ "default_fuzzy_search": "Default fuzzy search",
+ "field": "Field",
+ "for_example": "For example",
+ "format": "Format",
+ "of": "of",
+ "query": "Query",
+ "search": "Search",
+ "separated": "separated",
+ "support": "Support",
+ "syntax": "Syntax",
+ "with": "with",
+ "wrapped": "wrapped"
+ },
+ "admin-log-log-toolbar": {
+ "add_log_display_field_config": "Add log display field configuration",
+ "new_logs_available": "New logs available",
+ "reset": "Reset"
+ },
+ "admin-log-logs": {},
+ "admin-users": {
+ "users": "Users"
+ },
+ "admin-users-user-role-setting": {
+ "modify_role": "Modify Role",
+ "relogin_required_after_modification": "Relogin required after modification"
+ },
+ "admin-users-user-status": {
+ "activate_user": "Activate this user",
+ "ban_user": "Ban this user"
+ },
+ "admin-users-user-table": {
+ "email": "Email",
+ "filter_name": "Filter Name",
+ "info": "Info",
+ "operation": "Operation",
+ "reset": "Reset",
+ "role": "Role",
+ "status": "Status"
+ },
+ "agent": {},
+ "agent-[agentId]-config-base": {
+ "basic_info": "Basic Info",
+ "server": "Server"
+ },
+ "agent-[agentId]-config-other": {
+ "other_settings": "Other Settings",
+ "server": "Server"
+ },
+ "agent-[agentId]-forward": {
+ "forward": "Forward",
+ "server": "Server"
+ },
+ "agent-[agentId]-install": {
+ "install": "Install",
+ "server": "Server"
+ },
+ "agent-[agentId]-layout": {
+ "server": "Server"
+ },
+ "agent-[agentId]-loading": {},
+ "agent-[agentId]-log": {
+ "logs": "Logs",
+ "server": "Server"
+ },
+ "agent-[agentId]-status": {
+ "address": "Address",
+ "bandwidth": "Bandwidth",
+ "info": "Info",
+ "last_response": "Last Response",
+ "memory": "Memory",
+ "node_version": "Node Version",
+ "price": "Price",
+ "server": "Server",
+ "status": "Status",
+ "system": "System",
+ "traffic": "Traffic",
+ "unknown": "Unknown"
+ },
+ "agent-agent-command": {
+ "enter_command": "Enter command",
+ "execute": "Execute",
+ "execute_command": "Execute Command",
+ "execution_result": "Execution Result",
+ "failed": "Failed",
+ "success": "Success",
+ "type": "Type"
+ },
+ "agent-agent-config": {
+ "server_basic_info_settings": "Server Basic Info Settings"
+ },
+ "agent-agent-delete": {
+ "all_forwarding_stopped": "All forwarding will be stopped.",
+ "all_related_data_deleted": "And all related data will be deleted.",
+ "cancel": "Cancel",
+ "confirm_delete_server": "Are you sure you want to delete this server?",
+ "continue": "Continue",
+ "operation_irreversible": "This action cannot be undone.",
+ "server_deleted_from_system": "This server will be deleted from the system."
+ },
+ "agent-agent-form": {
+ "description": "Description",
+ "name": "Name",
+ "save": "Save",
+ "server_description": "Server Description",
+ "server_name": "Server Name",
+ "share": "Share",
+ "share_with_other_users": "Share with other users",
+ "used_to_distinguish_servers": "Used to distinguish different servers"
+ },
+ "agent-agent-install": {
+ "after_installation": "After installation",
+ "click": "click",
+ "copy_command_below_to_install": "Copy the command below to install",
+ "copy_command_below_to_uninstall": "Copy the command below to uninstall",
+ "execute_in_installation_directory": "Please execute in the installation directory",
+ "if_installation_command_leaked": "If the installation command is leaked",
+ "install": "Install",
+ "server_status_will_change": "the server status will change",
+ "successful_log_message": "You will see a successful log message",
+ "uninstall": "Uninstall",
+ "uninstall_deletes_all_configs_and_files": "Uninstalling will delete all configurations and files under the installation directory",
+ "update": "Update",
+ "version": "Version",
+ "view_installation_progress_on_logs_page": "You can view the installation progress on the logs page"
+ },
+ "agent-agent-list": {},
+ "agent-agent-list-aside": {
+ "add_server": "Add Server",
+ "offline_servers": "Offline Servers",
+ "online_servers": "Online Servers",
+ "search_server": "Search Server",
+ "unknown_servers": "Unknown Servers",
+ "add_tips": "Click save to view the sidebar server list.Add Relay Server, Save to receive an installation command, Copy and execute it on the server"
+ },
+ "agent-agent-menu": {
+ "basic_info": "Basic Info",
+ "config": "Config",
+ "forward": "Forward",
+ "install": "Install",
+ "log_config_etc": "Log config, etc.",
+ "logs": "Logs",
+ "other_settings": "Other Settings",
+ "port": "Port",
+ "price": "Price",
+ "remarks_etc": "Remarks, etc.",
+ "server_name": "Server Name",
+ "status": "Status"
+ },
+ "agent-agent-price": {},
+ "agent-agent-resizable-layout": {},
+ "agent-bandwidth-usage": {
+ "download": "Download",
+ "upload": "Upload"
+ },
+ "agent-cpu-usage": {},
+ "agent-mem-usage": {},
+ "agent-traffic-usage": {
+ "download": "Download",
+ "upload": "Upload"
+ },
+ "auth-error": {
+ "access_denied": "Access Denied",
+ "an_error_occurred": "Whoops! Something went wrong.",
+ "back_to_signin": "Back to Sign In",
+ "check_details_provided_are_correct": "Check that the details you provided are correct.",
+ "check_information_provided_is_correct": "Please check that the information you provided is correct.",
+ "check_server_logs_for_more_info": "Check the server logs for more information.",
+ "email_could_not_be_sent": "The email could not be sent.",
+ "it_may_have_been_used_or_expired": "It may have been used already or it may have expired.",
+ "not_authorized_to_signin": "You are not authorized to sign in.",
+ "please_signin_to_access_this_page": "Please sign in to access this page.",
+ "server_error": "Server Error",
+ "server_misconfigured": "There's a problem with the server configuration.",
+ "signin_link_no_longer_valid": "The sign-in link is no longer valid.",
+ "oauth_account_not_linked": "To confirm your identity, please sign in with the same account you originally used.",
+ "try_signing_in_with_different_account": "Try signing in with a different account.",
+ "unable_to_signin": "Unable to Sign In",
+ "user_disabled": "User Disabled",
+ "your_account_has_been_disabled": "Your account has been disabled."
+ },
+ "auth-layout": {},
+ "auth-new": {
+ "create_password": "Create Password",
+ "create_password_for_account": "Create a password for your account",
+ "skip_password_tips": "Skip password setup and sign in directly",
+ "create_password_tips": "Create a password for your account, so you can sign in with your email address and password."
+ },
+ "auth-signin": {
+ "and": "and",
+ "by_continuing_you_agree_to_our": "By continuing, you agree to our",
+ "continue": "Continue",
+ "create_an_account": "Create an Account",
+ "enter_email_signin_or_register": "Enter your email to sign in or create an account.",
+ "or_use": "Or use",
+ "password_signin": "Sign in with Password",
+ "privacy_policy": "Privacy Policy",
+ "registration_closed": "Registration is currently closed.",
+ "signin": "Sign In",
+ "terms_of_service": "Terms of Service",
+ "your_email_address": "Your Email Address"
+ },
+ "auth-signin-credential": {
+ "and": "and",
+ "back_to_signup": "Back to Sign Up",
+ "by_continuing_you_agree_to_our": "By continuing, you agree to our",
+ "enter_valid_email_address": "Please enter a valid email address.",
+ "enter_your_password": "Please enter your password.",
+ "password_signin": "Sign in with Password",
+ "privacy_policy": "Privacy Policy",
+ "signin": "Sign In",
+ "terms_of_service": "Terms of Service",
+ "your_email_address": "Your Email Address",
+ "your_password": "Your Password"
+ },
+ "auth-signout": {
+ "confirm": "Confirm",
+ "confirm_sign_out": "Sure you wanna sign out?"
+ },
+ "auth-verify": {},
+ "dashboard": {
+ "announcement": "Announcement",
+ "balance": "Balance",
+ "earnings": "Earnings",
+ "no_announcement": "No announcement",
+ "recent_days_traffic_usage": "Recent 7 days of traffic usage",
+ "system_running_normally": "System is running normally",
+ "system_status": "System Status",
+ "traffic_usage": "Traffic Usage",
+ "yesterday_consumption": "Yesterday's Consumption",
+ "yesterday_earnings": "Yesterday's Earnings"
+ },
+ "dashboard-system-status": {
+ "download": "Download",
+ "memory": "Memory",
+ "network": "Network",
+ "upload": "Upload"
+ },
+ "dashboard-traffic-usage": {
+ "used_traffic": "Used Traffic"
+ },
+ "forward": {
+ "forward": "Forward"
+ },
+ "forward-forward-delete": {
+ "all_related_data_deleted": "and all related data will be deleted.",
+ "cancel": "Cancel",
+ "confirm_delete_forward_config": "Are you sure you want to delete this forwarding configuration?",
+ "continue": "Continue",
+ "delete_entire_network_on_networking_page": "you should go to the networking page to delete the entire network",
+ "forwarding_stopped": "Forwarding will be stopped",
+ "if_forward_created_through_networking": "If this forwarding was created through networking",
+ "operation_irreversible": "This action cannot be undone."
+ },
+ "forward-forward-modify-remark": {
+ "cancel": "Cancel",
+ "enter_remark_here": "Enter your remark here",
+ "save": "Save",
+ "update_forward_remark": "Update Forward Remark"
+ },
+ "forward-forward-new": {
+ "can_be": "Can be",
+ "enter_forwarding_info": "Please fill in the forwarding information",
+ "forwarding_method": "Forwarding Method",
+ "forwarding_target": "Forwarding Target",
+ "limit_range": "Limit Range",
+ "more_config": "More Config",
+ "new_forward": "New Forward",
+ "no_available_servers": "No available servers",
+ "or_domain_name": "or domain name",
+ "relay_server": "Relay Server",
+ "remark": "Remark",
+ "reset": "Reset",
+ "save": "Save",
+ "select_forwarding_method": "Please select a forwarding method",
+ "select_relay_server": "Please select a relay server",
+ "server": "Server",
+ "server_port": "Server Port",
+ "target_port": "Target Port"
+ },
+ "forward-forward-new-gost": {
+ "channel": "Channel",
+ "enable_after_selection": "Enable after selection",
+ "multiplexing": "Multiplexing",
+ "protocol": "Protocol",
+ "relay_data_channel": "Relay Data Channel",
+ "relay_data_protocol": "Relay Data Protocol"
+ },
+ "forward-forward-reset-traffic": {
+ "cancel": "Cancel",
+ "confirm_reset_forward_traffic": "Are you sure you want to reset the traffic for this forwarding configuration?",
+ "continue": "Continue",
+ "operation_irreversible": "This action cannot be undone.",
+ "used_traffic_reset": "Used traffic will be reset"
+ },
+ "forward-forward-table": {
+ "add": "Add",
+ "delete": "Delete",
+ "modify_remark": "Modify Remark",
+ "remark": "Remark",
+ "reset": "Reset",
+ "reset_traffic": "Reset Traffic",
+ "server": "Server",
+ "status": "Status",
+ "target": "Target",
+ "used_traffic": "Used Traffic",
+ "user": "User"
+ },
+ "global_theme-provider": {},
+ "global_monthly-traffic-usage": {
+ "january": "January",
+ "february": "February",
+ "march": "March",
+ "april": "April",
+ "may": "May",
+ "june": "June",
+ "july": "July",
+ "august": "August",
+ "september": "September",
+ "october": "October",
+ "november": "November",
+ "december": "December",
+ "upload": "Upload",
+ "download": "Download"
+ },
+ "global_user-column": {},
+ "global_channel-selector": {
+ "select-a-channel": "Select a channel",
+ "search": "Search",
+ "no-data": "No data"
+ },
+ "global_search-empty-state": {},
+ "global_update-password-dialog": {
+ "change-password": "Change Password",
+ "set-password": "Set Password",
+ "update-password": "Update Password"
+ },
+ "global_resizable-layout": {},
+ "global_traffic": {},
+ "global_code-input": {
+ "format": "Format"
+ },
+ "global_update-password-form": {
+ "password-must-be-at-least": "Password must be at least",
+ "characters-long": "characters long",
+ "and-contain-at-least": "and contain at least",
+ "letters-and": "letters and",
+ "numbers": "numbers",
+ "passwords-do-not-match": "Passwords do not match",
+ "please-enter-your-old-password": "Please enter your old password",
+ "old-password": "Old Password",
+ "password": "Password",
+ "confirm-password": "Confirm Password",
+ "save": "Save"
+ },
+ "global_locale-switcher": {
+ "label": "Change language",
+ "locale": "{locale, select, zh {🇨🇳 简体中文} en {🇺🇸 English} other {Unknown}}"
+ },
+ "global_faceted-filter": {},
+ "global_logo": {},
+ "global_id": {
+ "created-at": "Created at"
+ },
+ "global_menu": {
+ "personal-center": "Personal Center",
+ "balance": "Balance",
+ "logout": "Logout",
+ "dashboard": "Dashboard",
+ "forward": "Forward",
+ "agent": "Agent",
+ "network": "Network",
+ "ticket": "Ticket",
+ "manage": "Manage",
+ "users": "Users",
+ "statistics": "Statistics",
+ "system": "System",
+ "log": "Log",
+ "config": "Config"
+ },
+ "global_table-faceted-filter": {},
+ "global_loading": {},
+ "global_table": {
+ "no-data": "No data"
+ },
+ "global_sidebar-nav": {},
+ "global_welcome": {},
+ "global_table-view-options": {},
+ "index": {
+ "Dashboard": "Dashboard",
+ "Sign In": "Sign In",
+ "Welcome": "Welcome"
+ },
+ "network": {
+ "networking": "Nope"
+ },
+ "network-[networkId]": {
+ "networking": "Nope"
+ },
+ "network-agent-edge": {
+ "random": "Random"
+ },
+ "network-agent-edge-forward-settings": {
+ "channel": "Channel",
+ "default_random": "Default Random",
+ "enter_port_to_forward": "Enter the port you need to forward",
+ "forwarding_method": "Forwarding Method",
+ "forwarding_method_required": "Forwarding method is required",
+ "forwarding_port": "Forwarding Port",
+ "forwarding_port_required": "Forwarding port is required",
+ "listening_port": "Listening Port",
+ "listening_port_greater_than": "Listening port must be greater than",
+ "listening_port_less_than": "Listening port must be less than",
+ "port_range": "Port Range",
+ "relay_settings": "Relay Settings",
+ "target_port_greater_than": "Target port must be greater than",
+ "target_port_less_than": "Target port must be less than"
+ },
+ "network-agent-edge-test": {
+ "avg_delay": "Avg Delay",
+ "diagnose_relayed_network_connection": "Diagnose relayed network connection",
+ "execution_result": "Execution Result",
+ "failed": "Failed",
+ "max_delay": "Max Delay",
+ "min_delay": "Min Delay",
+ "network_diagnostics": "Network Diagnostics",
+ "packet_loss_rate": "Packet Loss Rate",
+ "start": "Start",
+ "test": "Test",
+ "test_method": "Test Method"
+ },
+ "network-agent-edge-toolbar": {},
+ "network-agent-node": {},
+ "network-external-node": {},
+ "network-external-node-new": {
+ "address": "Address",
+ "address_or_domain_name": "address or domain name",
+ "create": "Create",
+ "create_external_node": "Create External Node",
+ "enter_external_node": "Enter external node",
+ "enter_external_node_name": "Enter external node name",
+ "enter_node_address": "Enter node address",
+ "enter_node_name": "Enter node name",
+ "enter_valid_address": "Please enter a valid address.",
+ "name": "Name",
+ "support": "Support",
+ "used_to_identify_node": "Used to identify this node"
+ },
+ "network-network-command": {
+ "add_external_node": "Add External Node",
+ "added": "Added",
+ "operation": "Operation",
+ "search_add_server": "Search and add server"
+ },
+ "network-network-delete": {
+ "all_forwarding_stopped": "All forwarding will be stopped.",
+ "all_related_data_deleted": "And all related data will be deleted.",
+ "cancel": "Cancel",
+ "confirm_delete_network_config": "Are you sure you want to delete this network configuration?",
+ "continue": "Continue",
+ "network_config_deleted_from_system": "This network configuration will be deleted from the system.",
+ "operation_irreversible": "This action cannot be undone."
+ },
+ "network-network-edit": {
+ "apply_to_network": "Apply to Network",
+ "cancel": "Cancel",
+ "network_name": "Network Name",
+ "please_operate_with_caution": "Please proceed with caution.",
+ "save": "Save",
+ "save_as_new_network_config": "Save as New Network Config",
+ "this_will_overwrite_current_network_config": "This will overwrite the current network configuration.",
+ "update_network_config": "Update Network Config"
+ },
+ "network-network-flow": {
+ "new_network": "New Network"
+ },
+ "network-network-selector": {},
+ "network-network-table": {
+ "copy_entrance_address": "Copy Entrance Address",
+ "create_new_network": "Create New Network",
+ "creator": "Creator",
+ "filter_name": "Filter Name",
+ "name": "Name",
+ "node_info": "Node Info",
+ "nodes": "Nodes",
+ "reset": "Reset",
+ "traffic": "Traffic"
+ },
+ "ticket": {
+ "tickets": "Tickets"
+ },
+ "ticket-[ticketId]": {
+ "created_at": "Created At",
+ "ticket": "Ticket"
+ },
+ "ticket-markdown-input": {
+ "edit": "Edit",
+ "preview": "Preview"
+ },
+ "ticket-new": {
+ "create_ticket": "Create Ticket",
+ "ticket": "Ticket"
+ },
+ "ticket-new-ticket-form": {
+ "briefly_describe_problem": "Please briefly describe your problem",
+ "characters": "characters",
+ "content": "Content",
+ "format": "format",
+ "max_length": "Max length",
+ "save": "Save",
+ "support": "Support",
+ "title": "Title"
+ },
+ "ticket-ticket-close": {
+ "cancel": "Cancel",
+ "close": "Close",
+ "confirm_close_ticket": "Are you sure you want to close this ticket?",
+ "continue": "Continue"
+ },
+ "ticket-ticket-reply": {
+ "reply": "Reply"
+ },
+ "ticket-ticket-status-badge": {},
+ "ticket-ticket-table": {
+ "create_new_ticket": "Create New Ticket",
+ "creator": "Creator",
+ "reset": "Reset",
+ "status": "Status",
+ "title": "Title"
+ },
+ "user-[userId]": {
+ "profile": "Profile",
+ "relogin_required_to_take_effect": "Requires re-login to take effect",
+ "set_name_avatar": "Set your name and avatar"
+ },
+ "user-[userId]-account": {
+ "account": "Account",
+ "email": "Email",
+ "password": "Password",
+ "password_not_set": "Password not set",
+ "view_update_account_info": "View and update your account information"
+ },
+ "user-[userId]-balance": {
+ "available_balance": "Available Balance",
+ "earnings_amount": "Earnings Amount"
+ },
+ "user-[userId]-balance-log": {
+ "balance": "Balance",
+ "balance_history": "Balance History",
+ "change_amount": "Change Amount",
+ "other_info": "Other Info",
+ "time": "Time"
+ },
+ "user-[userId]-layout": {
+ "account": "Account",
+ "balance": "Balance",
+ "manage_account_settings_email_preferences": "Manage your account settings and set email preferences",
+ "personal_center": "Personal Center",
+ "profile": "Profile",
+ "user_center": "User Center"
+ },
+ "user-[userId]-profile-form": {
+ "avatar": "Avatar",
+ "avatar_link": "Avatar Link",
+ "name": "Name",
+ "name_displayed_in_profile": "Name displayed in your profile",
+ "save": "Save"
+ },
+ "user-[userId]-recharge-balance": {
+ "balance_recharge": "Balance Recharge",
+ "enter_recharge_code": "Enter recharge code",
+ "recharge": "Recharge",
+ "recharge_code": "Recharge Code"
+ },
+ "user-[userId]-recharge-depay": {
+ "after_clicking_confirm": "After clicking confirm",
+ "and": "and",
+ "confirm": "Confirm",
+ "enter_crypto_wallet_select_payment_method": "you will enter the crypto wallet to select a payment method",
+ "invalid_amount": "Invalid amount",
+ "minimum_recharge_amount": "Minimum recharge amount",
+ "notes": "Notes",
+ "recharge": "Recharge",
+ "recharge_amount": "Recharge Amount",
+ "recharge_failed": "Recharge Failed",
+ "recharge_handling_fee": "Recharge requires a handling fee of",
+ "recharge_successful": "Recharge Successful",
+ "support": "Support",
+ "supported_networks": "Supported networks"
+ },
+ "user-[userId]-update-balance": {
+ "add_or_subtract_balance": "Add or subtract balance for the user",
+ "amount": "Amount",
+ "reduce_balance": "Reduce Balance",
+ "remark": "Remark",
+ "save": "Save",
+ "update_balance": "Update Balance"
+ },
+ "user-[userId]-withdrawal-balance": {
+ "amount": "Amount",
+ "current_earnings_balance": "Current Earnings Balance",
+ "deduct_handling_fee": "A handling fee will be deducted",
+ "minimum_withdrawal_amount": "Minimum withdrawal amount is",
+ "please_provide": "Please provide",
+ "receiving_address": "Receiving Address",
+ "request_withdrawal": "Request Withdrawal",
+ "request_withdrawal_to_specified_address": "Request withdrawal to the specified address",
+ "subject_to_actual_received_amount": "Subject to the actual received amount",
+ "withdrawal": "Withdrawal"
+ }
+}
diff --git a/messages/zh.json b/messages/zh.json
new file mode 100644
index 0000000..9c65efb
--- /dev/null
+++ b/messages/zh.json
@@ -0,0 +1,719 @@
+{
+ "admin-config": {},
+ "admin-config-[classify]": {},
+ "admin-config-code-input-dialog": {
+ "edit": "编辑",
+ "preview": "预览"
+ },
+ "admin-config-config-field": {
+ "save": "保存"
+ },
+ "admin-config-config-list": {},
+ "admin-config-cron-input": {
+ "after": "之后",
+ "any_value": "任何值",
+ "day": "日",
+ "hide": "隐藏",
+ "hour": "时",
+ "minute": "分",
+ "month": "月",
+ "more": "更多",
+ "next": "下次",
+ "possible_values": "可取的值",
+ "step": "步长",
+ "value_in_range": "范围内的值",
+ "value_separator": "取值分隔符",
+ "week": "周"
+ },
+ "admin-config-layout": {
+ "general": "通用",
+ "general_config_etc": "通用配置等",
+ "including_log_config": "包括日志配置",
+ "log_config": "日志配置",
+ "node_related_config": "节点相关配置",
+ "payment_records": "支付记录",
+ "recharge_code": "充值码",
+ "system_config": "系统配置",
+ "website_system_config": "网站系统配置",
+ "withdrawal_records": "提现记录"
+ },
+ "admin-config-payment-info": {},
+ "admin-config-payment": {},
+ "admin-config-payment-table": {
+ "info": "信息",
+ "received_amount": "到账金额",
+ "recharge_amount": "充值金额",
+ "reset": "重置",
+ "status": "状态",
+ "user": "用户"
+ },
+ "admin-config-recharge-code": {
+ "balance": "余额",
+ "batch_generate_export_recharge_code": "可以批量生成导出充值码",
+ "manage_recharge_code": "管理充值码",
+ "personal_center": "个人中心",
+ "recharge_code": "充值码",
+ "recharge_code_in": "充值码在",
+ "recharge_use": "充值使用"
+ },
+ "admin-config-recharge-code-table": {
+ "add": "添加",
+ "amount": "金额",
+ "cancel": "取消",
+ "confirm_delete_recharge_code": "你确认要删除这条充值码吗",
+ "continue": "继续",
+ "create_recharge_code": "创建充值码",
+ "export_after_creation": "创建后导出",
+ "pieces": "个",
+ "quantity": "数量",
+ "recharge_code": "充值码",
+ "reset": "重置",
+ "save": "保存",
+ "total": "共",
+ "usage_status": "使用状态",
+ "used": "已使用",
+ "used_by": "使用用户",
+ "user": "用户",
+ "yuan": "元"
+ },
+ "admin-config-traffic-price-config": {
+ "price": "价格",
+ "unit": "单位"
+ },
+ "admin-config-withdraw": {},
+ "admin-config-withdrawal-table": {
+ "address": "地址",
+ "operation_successful": "操作成功",
+ "reset": "重置",
+ "status": "状态",
+ "user": "用户",
+ "user_wallet_balance_updated": "已自动更新用户钱包收益余额",
+ "withdrawal_amount": "提现金额",
+ "withdrawal_status_updated": "提现状态已更新"
+ },
+ "admin-log": {
+ "logs": "日志"
+ },
+ "admin-log-log": {
+ "copied_to_clipboard": "已复制到剪贴板"
+ },
+ "admin-log-log-delete": {
+ "cancel": "取消",
+ "confirm_delete_all_logs": "你确认要删除所有日志吗",
+ "continue": "继续"
+ },
+ "admin-log-log-glance": {},
+ "admin-log-log-search-keyword": {
+ "advanced_search": "高级搜索",
+ "array_index_is": "数组下标为",
+ "array_index_with": "数组下标以",
+ "default_fuzzy_search": "默认模糊查询",
+ "field": "字段",
+ "for_example": "如",
+ "format": "格式",
+ "of": "的",
+ "query": "查询",
+ "search": "搜索",
+ "separated": "分割",
+ "support": "支持",
+ "syntax": "语法",
+ "with": "以",
+ "wrapped": "包裹"
+ },
+ "admin-log-log-toolbar": {
+ "add_log_display_field_config": "增加日志展示字段的配置",
+ "new_logs_available": "有新日志",
+ "reset": "重置"
+ },
+ "admin-log-logs": {},
+ "admin-users": {
+ "users": "用户"
+ },
+ "admin-users-user-role-setting": {
+ "modify_role": "修改角色",
+ "relogin_required_after_modification": "修改之后需重新登录"
+ },
+ "admin-users-user-status": {
+ "activate_user": "激活此用户",
+ "ban_user": "封禁此用户"
+ },
+ "admin-users-user-table": {
+ "email": "邮箱",
+ "filter_name": "过滤名称",
+ "info": "信息",
+ "operation": "操作",
+ "reset": "重置",
+ "role": "角色",
+ "status": "状态"
+ },
+ "agent": {},
+ "agent-[agentId]-config-base": {
+ "basic_info": "基础信息",
+ "server": "服务器"
+ },
+ "agent-[agentId]-config-other": {
+ "other_settings": "其它设置",
+ "server": "服务器"
+ },
+ "agent-[agentId]-forward": {
+ "forward": "转发",
+ "server": "服务器"
+ },
+ "agent-[agentId]-install": {
+ "install": "安装",
+ "server": "服务器"
+ },
+ "agent-[agentId]-layout": {
+ "server": "服务器"
+ },
+ "agent-[agentId]-loading": {},
+ "agent-[agentId]-log": {
+ "logs": "日志",
+ "server": "服务器"
+ },
+ "agent-[agentId]-status": {
+ "address": "地址",
+ "bandwidth": "带宽",
+ "info": "信息",
+ "last_response": "上次响应",
+ "memory": "内存",
+ "node_version": "节点版本",
+ "price": "价格",
+ "server": "服务器",
+ "status": "状态",
+ "system": "系统",
+ "traffic": "流量",
+ "unknown": "未知"
+ },
+ "agent-agent-command": {
+ "enter_command": "请输入命令",
+ "execute": "执行",
+ "execute_command": "执行命令",
+ "execution_result": "执行结果",
+ "failed": "失败",
+ "success": "成功",
+ "type": "类型"
+ },
+ "agent-agent-config": {
+ "server_basic_info_settings": "服务器基本信息设置"
+ },
+ "agent-agent-delete": {
+ "all_forwarding_stopped": "会停止所有的转发",
+ "all_related_data_deleted": "并且删除所有相关的数据",
+ "cancel": "取消",
+ "confirm_delete_server": "你确认要删除这台服务器吗",
+ "continue": "继续",
+ "operation_irreversible": "这个操作不可逆转",
+ "server_deleted_from_system": "将会从系统上将这台服务器删除"
+ },
+ "agent-agent-form": {
+ "description": "描述",
+ "name": "名称",
+ "save": "保存",
+ "server_description": "服务器描述",
+ "server_name": "服务器名称",
+ "share": "共享",
+ "share_with_other_users": "是否共享给其他用户",
+ "used_to_distinguish_servers": "用于区分不同服务器"
+ },
+ "agent-agent-install": {
+ "after_installation": "安装完成后",
+ "click": "可以点击",
+ "copy_command_below_to_install": "复制下方命令进行安装",
+ "copy_command_below_to_uninstall": "复制下方命令进行卸载",
+ "execute_in_installation_directory": "请在安装目录下执行",
+ "if_installation_command_leaked": "如果安装命令泄露",
+ "install": "安装",
+ "server_status_will_change": "服务器状态将会修改",
+ "successful_log_message": "成功将会看到一条",
+ "uninstall": "卸载",
+ "uninstall_deletes_all_configs_and_files": "卸载后会删除所有配置和安装目录下的文件",
+ "update": "更新",
+ "version": "版本",
+ "view_installation_progress_on_logs_page": "可以通过日志页面查看安装情况"
+ },
+ "agent-agent-list": {},
+ "agent-agent-list-aside": {
+ "add_server": "添加服务器",
+ "offline_servers": "掉线服务器",
+ "online_servers": "在线服务器",
+ "search_server": "搜索服务器",
+ "unknown_servers": "未知服务器",
+ "add_tips": "点击保存后查看侧边服务器栏。添加中转服务器,保存后会给你一个安装命令,复制到服务器上执行即可"
+ },
+ "agent-agent-menu": {
+ "basic_info": "基础信息",
+ "config": "配置",
+ "forward": "转发",
+ "install": "安装",
+ "log_config_etc": "日志等配置",
+ "logs": "日志",
+ "other_settings": "其它设置",
+ "port": "端口",
+ "price": "价格",
+ "remarks_etc": "备注等",
+ "server_name": "服务器名称",
+ "status": "状态"
+ },
+ "agent-agent-price": {},
+ "agent-agent-resizable-layout": {},
+ "agent-bandwidth-usage": {
+ "download": "下载",
+ "upload": "上传"
+ },
+ "agent-cpu-usage": {},
+ "agent-mem-usage": {},
+ "agent-traffic-usage": {
+ "download": "下载",
+ "upload": "上传"
+ },
+ "auth-error": {
+ "access_denied": "拒绝访问",
+ "an_error_occurred": "发生错误",
+ "back_to_signin": "返回登录",
+ "check_details_provided_are_correct": "检查您提供的详细信息是否正确",
+ "check_information_provided_is_correct": "请检查您提供的信息是否正确",
+ "check_server_logs_for_more_info": "检查服务器日志以获取更多信息",
+ "email_could_not_be_sent": "电子邮件无法发送",
+ "it_may_have_been_used_or_expired": "它可能已经被使用过或者可能已经过期",
+ "not_authorized_to_signin": "您没有登录权限",
+ "please_signin_to_access_this_page": "请登录才能访问此页面",
+ "server_error": "服务器错误",
+ "server_misconfigured": "服务器配置有问题",
+ "signin_link_no_longer_valid": "登录链接不再有效",
+ "oauth_account_not_linked": "要确认您的身份,请使用您最初使用的同一帐户登录",
+ "try_signing_in_with_different_account": "尝试使用其他帐户登录",
+ "unable_to_signin": "无法登录",
+ "user_disabled": "用户已禁用",
+ "your_account_has_been_disabled": "您的帐户已被禁用"
+ },
+ "auth-layout": {},
+ "auth-new": {
+ "create_password": "创建密码",
+ "signin_directly": "直接登录",
+ "skip_password_tips": "不设置密码,直接登录",
+ "create_password_tips": "为您的账户创建一个密码,以便您可以使用电子邮件地址和密码登录"
+ },
+ "auth-signin": {
+ "and": "和",
+ "by_continuing_you_agree_to_our": "继续即表示您同意我们的",
+ "continue": "继续",
+ "create_an_account": "创建一个账号",
+ "enter_email_signin_or_register": "输入你的邮箱来登录或者注册一个账号",
+ "or_use": "或者使用",
+ "password_signin": "密码登录",
+ "privacy_policy": "隐私政策",
+ "registration_closed": "已关闭注册",
+ "signin": "登录",
+ "terms_of_service": "服务条款",
+ "your_email_address": "你的邮箱地址"
+ },
+ "auth-signin-credential": {
+ "and": "和",
+ "back_to_signup": "返回注册",
+ "by_continuing_you_agree_to_our": "继续即表示您同意我们的",
+ "enter_valid_email_address": "请输入正确的邮箱地址",
+ "enter_your_password": "请输入你的密码",
+ "password_signin": "密码登录",
+ "privacy_policy": "隐私政策",
+ "signin": "登录",
+ "terms_of_service": "服务条款",
+ "your_email_address": "你的邮箱地址",
+ "your_password": "你的密码"
+ },
+ "auth-signout": {
+ "confirm": "确认",
+ "confirm_sign_out": "确认退出登录"
+ },
+ "auth-verify": {},
+ "dashboard": {
+ "announcement": "公告",
+ "balance": "余额",
+ "earnings": "收益",
+ "no_announcement": "暂无公告",
+ "recent_days_traffic_usage": "最近7天的流量使用情况",
+ "system_running_normally": "系统运行正常",
+ "system_status": "系统状态",
+ "traffic_usage": "流量使用",
+ "yesterday_consumption": "昨日消费",
+ "yesterday_earnings": "昨日收益"
+ },
+ "dashboard-system-status": {
+ "download": "下载",
+ "memory": "内存",
+ "network": "网络",
+ "upload": "上传"
+ },
+ "dashboard-traffic-usage": {
+ "used_traffic": "使用流量"
+ },
+ "forward": {
+ "forward": "Forward"
+ },
+ "forward-forward-delete": {
+ "all_related_data_deleted": "并且删除所有相关的数据",
+ "cancel": "取消",
+ "confirm_delete_forward_config": "你确认要删除这个转发配置吗",
+ "continue": "继续",
+ "delete_entire_network_on_networking_page": "你应该去组网页面删除整个组网",
+ "forwarding_stopped": "将会停止转发",
+ "if_forward_created_through_networking": "如果这个转发是通过组网创建的",
+ "operation_irreversible": "这个操作不可逆转"
+ },
+ "forward-forward-modify-remark": {
+ "cancel": "取消",
+ "enter_remark_here": "在这里输入你的备注",
+ "save": "保存",
+ "update_forward_remark": "更新转发备注"
+ },
+ "forward-forward-new": {
+ "can_be": "可以是",
+ "enter_forwarding_info": "请填写转发信息",
+ "forwarding_method": "转发方式",
+ "forwarding_target": "转发目标",
+ "limit_range": "限制范围",
+ "more_config": "更多配置",
+ "new_forward": "新增转发",
+ "no_available_servers": "暂无可用服务器",
+ "or_domain_name": "或者域名",
+ "relay_server": "中转服务器",
+ "remark": "备注",
+ "reset": "重置",
+ "save": "保存",
+ "select_forwarding_method": "请选择一种转发方式进行转发",
+ "select_relay_server": "请选择中转服务器",
+ "server": "服务器",
+ "server_port": "服务器端口",
+ "target_port": "目标端口"
+ },
+ "forward-forward-new-gost": {
+ "channel": "通道",
+ "enable_after_selection": "选择后开启",
+ "multiplexing": "多路复用",
+ "protocol": "协议",
+ "relay_data_channel": "中转数据通道",
+ "relay_data_protocol": "中转数据协议"
+ },
+ "forward-forward-reset-traffic": {
+ "cancel": "取消",
+ "confirm_reset_forward_traffic": "你确认要重置这个转发配置的流量吗",
+ "continue": "继续",
+ "operation_irreversible": "这个操作不可逆转",
+ "used_traffic_reset": "将会重置已使用的流量"
+ },
+ "forward-forward-table": {
+ "add": "添加",
+ "delete": "删除",
+ "modify_remark": "修改备注",
+ "remark": "备注",
+ "reset": "重置",
+ "reset_traffic": "重置流量",
+ "server": "服务器",
+ "status": "状态",
+ "target": "目标",
+ "used_traffic": "已用流量",
+ "user": "用户"
+ },
+ "index": {
+ "Dashboard": "控制台",
+ "Sign In": "登录",
+ "Welcome": "欢迎"
+ },
+ "global_theme-provider": {},
+ "global_monthly-traffic-usage": {
+ "january": "一月",
+ "february": "二月",
+ "march": "三月",
+ "april": "四月",
+ "may": "五月",
+ "june": "六月",
+ "july": "七月",
+ "august": "八月",
+ "september": "九月",
+ "october": "十月",
+ "november": "十一月",
+ "december": "十二月",
+ "upload": "上传",
+ "download": "下载"
+ },
+ "global_user-column": {},
+ "global_channel-selector": {
+ "select-a-channel": "选择一个通道",
+ "search": "搜索",
+ "no-data": "无数据"
+ },
+ "global_search-empty-state": {},
+ "global_update-password-dialog": {
+ "change-password": "更改密码",
+ "set-password": "设置密码",
+ "update-password": "更新密码"
+ },
+ "global_resizable-layout": {},
+ "global_traffic": {},
+ "global_code-input": {
+ "format": "格式化"
+ },
+ "global_update-password-form": {
+ "password-must-be-at-least": "密码必须至少为",
+ "characters-long": "个字符",
+ "and-contain-at-least": "并且至少包含",
+ "letters-and": "个字母和",
+ "numbers": "个数字",
+ "passwords-do-not-match": "密码不匹配",
+ "please-enter-your-old-password": "请输入原密码",
+ "old-password": "原密码",
+ "password": "密码",
+ "confirm-password": "确认密码",
+ "save": "保存"
+ },
+ "global_locale-switcher": {
+ "label": "选择语言",
+ "locale": "{locale, select, zh {🇨🇳 简体中文} en {🇺🇸 English} other {Unknown}}"
+ },
+ "global_faceted-filter": {},
+ "global_logo": {},
+ "global_id": {
+ "created-at": "创建于"
+ },
+ "global_menu": {
+ "personal-center": "个人中心",
+ "balance": "余额",
+ "logout": "退出",
+ "dashboard": "控制台",
+ "forward": "转发",
+ "agent": "服务器",
+ "network": "组网",
+ "ticket": "工单",
+ "manage": "管理",
+ "users": "用户",
+ "statistics": "统计",
+ "system": "系统",
+ "log": "日志",
+ "config": "配置"
+ },
+ "global_table-faceted-filter": {},
+ "global_loading": {},
+ "global_table": {
+ "no-data": "无数据"
+ },
+ "global_sidebar-nav": {},
+ "global_welcome": {},
+ "global_table-view-options": {},
+ "network": {
+ "networking": "组网"
+ },
+ "network-[networkId]": {
+ "networking": "组网"
+ },
+ "network-agent-edge": {
+ "random": "随机"
+ },
+ "network-agent-edge-forward-settings": {
+ "channel": "通道",
+ "default_random": "默认随机",
+ "enter_port_to_forward": "填写你需要转发的端口",
+ "forwarding_method": "转发方式",
+ "forwarding_method_required": "必须选择转发方式",
+ "forwarding_port": "转发端口",
+ "forwarding_port_required": "必须填写转发端口",
+ "listening_port": "监听端口",
+ "listening_port_greater_than": "监听端口必须大于",
+ "listening_port_less_than": "监听端口必须小于",
+ "port_range": "端口范围",
+ "relay_settings": "中转设置",
+ "target_port_greater_than": "目标端口必须大于",
+ "target_port_less_than": "目标端口必须小于"
+ },
+ "network-agent-edge-test": {
+ "avg_delay": "平均延迟",
+ "diagnose_relayed_network_connection": "诊断中转的网络连接",
+ "execution_result": "执行结果",
+ "failed": "失败",
+ "max_delay": "最大延迟",
+ "min_delay": "最小延迟",
+ "network_diagnostics": "网络诊断",
+ "packet_loss_rate": "丢包率",
+ "start": "开始",
+ "test": "测试",
+ "test_method": "测试方式"
+ },
+ "network-agent-edge-toolbar": {},
+ "network-agent-node": {},
+ "network-external-node": {},
+ "network-external-node-new": {
+ "address": "地址",
+ "address_or_domain_name": "地址或域名",
+ "create": "创建",
+ "create_external_node": "创建外部节点",
+ "enter_external_node": "请输入外部节点的",
+ "enter_external_node_name": "请输入外部节点的名称",
+ "enter_node_address": "请输入节点地址",
+ "enter_node_name": "请输入节点名称",
+ "enter_valid_address": "请输入正确的地址",
+ "name": "名称",
+ "support": "支持",
+ "used_to_identify_node": "用于标识该节点"
+ },
+ "network-network-command": {
+ "add_external_node": "添加外部节点",
+ "added": "已添加",
+ "operation": "操作",
+ "search_add_server": "搜索添加服务器"
+ },
+ "network-network-delete": {
+ "all_forwarding_stopped": "会停止所有的转发",
+ "all_related_data_deleted": "并且删除所有相关的数据",
+ "cancel": "取消",
+ "confirm_delete_network_config": "你确认要删除这条网络配置吗",
+ "continue": "继续",
+ "network_config_deleted_from_system": "将会从系统上将这条网络配置删除",
+ "operation_irreversible": "这个操作不可逆转"
+ },
+ "network-network-edit": {
+ "apply_to_network": "是否应用至网络",
+ "cancel": "取消",
+ "network_name": "组网名称",
+ "please_operate_with_caution": "请谨慎操作",
+ "save": "保存",
+ "save_as_new_network_config": "保存为新的组网配置",
+ "this_will_overwrite_current_network_config": "选择此选项将会覆盖当前网络配置",
+ "update_network_config": "更新组网配置"
+ },
+ "network-network-flow": {
+ "new_network": "新的组网"
+ },
+ "network-network-selector": {},
+ "network-network-table": {
+ "copy_entrance_address": "复制入口地址",
+ "create_new_network": "创建新的组网",
+ "creator": "创建者",
+ "filter_name": "过滤名称",
+ "name": "名称",
+ "node_info": "节点信息",
+ "nodes": "个节点",
+ "reset": "重置",
+ "traffic": "流量"
+ },
+ "ticket": {
+ "tickets": "工单"
+ },
+ "ticket-[ticketId]": {
+ "created_at": "创建时间",
+ "ticket": "工单"
+ },
+ "ticket-markdown-input": {
+ "edit": "编辑",
+ "preview": "预览"
+ },
+ "ticket-new": {
+ "create_ticket": "创建工单",
+ "ticket": "工单"
+ },
+ "ticket-new-ticket-form": {
+ "briefly_describe_problem": "请简要描述您的问题",
+ "characters": "字符",
+ "content": "内容",
+ "format": "格式",
+ "max_length": "最大长度",
+ "save": "保存",
+ "support": "支持",
+ "title": "标题"
+ },
+ "ticket-ticket-close": {
+ "cancel": "取消",
+ "close": "关闭",
+ "confirm_close_ticket": "你确认要关闭这个工单吗",
+ "continue": "继续"
+ },
+ "ticket-ticket-reply": {
+ "reply": "回复"
+ },
+ "ticket-ticket-status-badge": {},
+ "ticket-ticket-table": {
+ "create_new_ticket": "创建新的工单",
+ "creator": "创建者",
+ "reset": "重置",
+ "status": "状态",
+ "title": "标题"
+ },
+ "user-[userId]": {
+ "profile": "个人资料",
+ "relogin_required_to_take_effect": "需要重新登录后生效",
+ "set_name_avatar": "设置你的名称和头像"
+ },
+ "user-[userId]-account": {
+ "account": "账号",
+ "email": "邮箱",
+ "password": "密码",
+ "password_not_set": "未设置密码",
+ "view_update_account_info": "查看和更新你的账号信息"
+ },
+ "user-[userId]-balance": {
+ "available_balance": "可消费余额",
+ "earnings_amount": "收益金额"
+ },
+ "user-[userId]-balance-log": {
+ "balance": "余额",
+ "balance_history": "余额历史",
+ "change_amount": "变动金额",
+ "other_info": "其它信息",
+ "time": "时间"
+ },
+ "user-[userId]-layout": {
+ "account": "账户",
+ "balance": "余额",
+ "manage_account_settings_email_preferences": "管理您的帐户设置并设置电子邮件首选项",
+ "personal_center": "个人中心",
+ "profile": "个人资料",
+ "user_center": "用户中心"
+ },
+ "user-[userId]-profile-form": {
+ "avatar": "头像",
+ "avatar_link": "头像链接",
+ "name": "名称",
+ "name_displayed_in_profile": "显示在您的个人资料中的名称",
+ "save": "保存"
+ },
+ "user-[userId]-recharge-balance": {
+ "balance_recharge": "余额充值",
+ "enter_recharge_code": "请输入充值码",
+ "recharge": "充值",
+ "recharge_code": "充值码"
+ },
+ "user-[userId]-recharge-depay": {
+ "after_clicking_confirm": "点击确认后",
+ "and": "和",
+ "confirm": "确认",
+ "enter_crypto_wallet_select_payment_method": "将会进入加密钱包选择支付方式",
+ "invalid_amount": "无效的金额",
+ "minimum_recharge_amount": "充值金额最低",
+ "notes": "注意事项",
+ "recharge": "充值",
+ "recharge_amount": "充值金额",
+ "recharge_failed": "充值失败",
+ "recharge_handling_fee": "充值需要收取",
+ "recharge_successful": "充值成功",
+ "support": "支持",
+ "supported_networks": "支持的网络"
+ },
+ "user-[userId]-update-balance": {
+ "add_or_subtract_balance": "为用户添加或减少余额",
+ "amount": "金额",
+ "reduce_balance": "减少余额",
+ "remark": "备注",
+ "save": "保存",
+ "update_balance": "更新余额"
+ },
+ "user-[userId]-withdrawal-balance": {
+ "amount": "金额",
+ "current_earnings_balance": "当前收益余额",
+ "deduct_handling_fee": "需要扣除部分手续费",
+ "minimum_withdrawal_amount": "最低提现金额为",
+ "please_provide": "请提供",
+ "receiving_address": "收款地址",
+ "request_withdrawal": "申请提现",
+ "request_withdrawal_to_specified_address": "申请提现到指定的地址",
+ "subject_to_actual_received_amount": "以实际到账金额为准",
+ "withdrawal": "提现"
+ }
+}
diff --git a/next.config.js b/next.config.js
index b273362..c519344 100644
--- a/next.config.js
+++ b/next.config.js
@@ -2,8 +2,12 @@
* Run `build` or `dev` with `SKIP_ENV_VALIDATION` to skip env validation. This is especially useful
* for Docker builds.
*/
+import createNextIntlPlugin from "next-intl/plugin";
+
await import("./src/env.js");
+const withNextIntl = createNextIntlPlugin();
+
/** @type {import("next").NextConfig} */
const config = {
experimental: {
@@ -30,4 +34,4 @@ const config = {
},
};
-export default config;
+export default withNextIntl(config);
diff --git a/package-lock.json b/package-lock.json
index c3225ba..58af33c 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -57,6 +57,7 @@
"lucide-react": "^0.316.0",
"next": "^14.1.0",
"next-auth": "^4.24.5",
+ "next-intl": "^3.9.5",
"next-themes": "^0.2.1",
"nodemailer": "^6.9.9",
"pino": "^8.17.2",
@@ -3371,6 +3372,92 @@
"resolved": "https://registry.npmjs.org/@floating-ui/utils/-/utils-0.1.6.tgz",
"integrity": "sha512-OfX7E2oUDYxtBvsuS4e/jSn4Q9Qb6DzgeYtsAdkPZ47znpoNsMgZw0+tVijiv3uGNR6dgNlty6r9rzIzHjtd/A=="
},
+ "node_modules/@formatjs/ecma402-abstract": {
+ "version": "1.18.2",
+ "resolved": "https://registry.npmjs.org/@formatjs/ecma402-abstract/-/ecma402-abstract-1.18.2.tgz",
+ "integrity": "sha512-+QoPW4csYALsQIl8GbN14igZzDbuwzcpWrku9nyMXlaqAlwRBgl5V+p0vWMGFqHOw37czNXaP/lEk4wbLgcmtA==",
+ "dependencies": {
+ "@formatjs/intl-localematcher": "0.5.4",
+ "tslib": "^2.4.0"
+ }
+ },
+ "node_modules/@formatjs/ecma402-abstract/node_modules/@formatjs/intl-localematcher": {
+ "version": "0.5.4",
+ "resolved": "https://registry.npmjs.org/@formatjs/intl-localematcher/-/intl-localematcher-0.5.4.tgz",
+ "integrity": "sha512-zTwEpWOzZ2CiKcB93BLngUX59hQkuZjT2+SAQEscSm52peDW/getsawMcWF1rGRpMCX6D7nSJA3CzJ8gn13N/g==",
+ "dependencies": {
+ "tslib": "^2.4.0"
+ }
+ },
+ "node_modules/@formatjs/fast-memoize": {
+ "version": "1.2.1",
+ "resolved": "https://registry.npmjs.org/@formatjs/fast-memoize/-/fast-memoize-1.2.1.tgz",
+ "integrity": "sha512-Rg0e76nomkz3vF9IPlKeV+Qynok0r7YZjL6syLz4/urSg0IbjPZCB/iYUMNsYA643gh4mgrX3T7KEIFIxJBQeg==",
+ "dependencies": {
+ "tslib": "^2.1.0"
+ }
+ },
+ "node_modules/@formatjs/icu-messageformat-parser": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/@formatjs/icu-messageformat-parser/-/icu-messageformat-parser-2.1.0.tgz",
+ "integrity": "sha512-Qxv/lmCN6hKpBSss2uQ8IROVnta2r9jd3ymUEIjm2UyIkUCHVcbUVRGL/KS/wv7876edvsPe+hjHVJ4z8YuVaw==",
+ "dependencies": {
+ "@formatjs/ecma402-abstract": "1.11.4",
+ "@formatjs/icu-skeleton-parser": "1.3.6",
+ "tslib": "^2.1.0"
+ }
+ },
+ "node_modules/@formatjs/icu-messageformat-parser/node_modules/@formatjs/ecma402-abstract": {
+ "version": "1.11.4",
+ "resolved": "https://registry.npmjs.org/@formatjs/ecma402-abstract/-/ecma402-abstract-1.11.4.tgz",
+ "integrity": "sha512-EBikYFp2JCdIfGEb5G9dyCkTGDmC57KSHhRQOC3aYxoPWVZvfWCDjZwkGYHN7Lis/fmuWl906bnNTJifDQ3sXw==",
+ "dependencies": {
+ "@formatjs/intl-localematcher": "0.2.25",
+ "tslib": "^2.1.0"
+ }
+ },
+ "node_modules/@formatjs/icu-messageformat-parser/node_modules/@formatjs/intl-localematcher": {
+ "version": "0.2.25",
+ "resolved": "https://registry.npmjs.org/@formatjs/intl-localematcher/-/intl-localematcher-0.2.25.tgz",
+ "integrity": "sha512-YmLcX70BxoSopLFdLr1Ds99NdlTI2oWoLbaUW2M406lxOIPzE1KQhRz2fPUkq34xVZQaihCoU29h0KK7An3bhA==",
+ "dependencies": {
+ "tslib": "^2.1.0"
+ }
+ },
+ "node_modules/@formatjs/icu-skeleton-parser": {
+ "version": "1.3.6",
+ "resolved": "https://registry.npmjs.org/@formatjs/icu-skeleton-parser/-/icu-skeleton-parser-1.3.6.tgz",
+ "integrity": "sha512-I96mOxvml/YLrwU2Txnd4klA7V8fRhb6JG/4hm3VMNmeJo1F03IpV2L3wWt7EweqNLES59SZ4d6hVOPCSf80Bg==",
+ "dependencies": {
+ "@formatjs/ecma402-abstract": "1.11.4",
+ "tslib": "^2.1.0"
+ }
+ },
+ "node_modules/@formatjs/icu-skeleton-parser/node_modules/@formatjs/ecma402-abstract": {
+ "version": "1.11.4",
+ "resolved": "https://registry.npmjs.org/@formatjs/ecma402-abstract/-/ecma402-abstract-1.11.4.tgz",
+ "integrity": "sha512-EBikYFp2JCdIfGEb5G9dyCkTGDmC57KSHhRQOC3aYxoPWVZvfWCDjZwkGYHN7Lis/fmuWl906bnNTJifDQ3sXw==",
+ "dependencies": {
+ "@formatjs/intl-localematcher": "0.2.25",
+ "tslib": "^2.1.0"
+ }
+ },
+ "node_modules/@formatjs/icu-skeleton-parser/node_modules/@formatjs/intl-localematcher": {
+ "version": "0.2.25",
+ "resolved": "https://registry.npmjs.org/@formatjs/intl-localematcher/-/intl-localematcher-0.2.25.tgz",
+ "integrity": "sha512-YmLcX70BxoSopLFdLr1Ds99NdlTI2oWoLbaUW2M406lxOIPzE1KQhRz2fPUkq34xVZQaihCoU29h0KK7An3bhA==",
+ "dependencies": {
+ "tslib": "^2.1.0"
+ }
+ },
+ "node_modules/@formatjs/intl-localematcher": {
+ "version": "0.2.32",
+ "resolved": "https://registry.npmjs.org/@formatjs/intl-localematcher/-/intl-localematcher-0.2.32.tgz",
+ "integrity": "sha512-k/MEBstff4sttohyEpXxCmC3MqbUn9VvHGlZ8fauLzkbwXmVrEeyzS+4uhrvAk9DWU9/7otYWxyDox4nT/KVLQ==",
+ "dependencies": {
+ "tslib": "^2.4.0"
+ }
+ },
"node_modules/@hookform/resolvers": {
"version": "3.3.4",
"resolved": "https://registry.npmjs.org/@hookform/resolvers/-/resolvers-3.3.4.tgz",
@@ -11251,6 +11338,34 @@
"node": ">=12"
}
},
+ "node_modules/intl-messageformat": {
+ "version": "9.13.0",
+ "resolved": "https://registry.npmjs.org/intl-messageformat/-/intl-messageformat-9.13.0.tgz",
+ "integrity": "sha512-7sGC7QnSQGa5LZP7bXLDhVDtQOeKGeBFGHF2Y8LVBwYZoQZCgWeKoPGTa5GMG8g/TzDgeXuYJQis7Ggiw2xTOw==",
+ "dependencies": {
+ "@formatjs/ecma402-abstract": "1.11.4",
+ "@formatjs/fast-memoize": "1.2.1",
+ "@formatjs/icu-messageformat-parser": "2.1.0",
+ "tslib": "^2.1.0"
+ }
+ },
+ "node_modules/intl-messageformat/node_modules/@formatjs/ecma402-abstract": {
+ "version": "1.11.4",
+ "resolved": "https://registry.npmjs.org/@formatjs/ecma402-abstract/-/ecma402-abstract-1.11.4.tgz",
+ "integrity": "sha512-EBikYFp2JCdIfGEb5G9dyCkTGDmC57KSHhRQOC3aYxoPWVZvfWCDjZwkGYHN7Lis/fmuWl906bnNTJifDQ3sXw==",
+ "dependencies": {
+ "@formatjs/intl-localematcher": "0.2.25",
+ "tslib": "^2.1.0"
+ }
+ },
+ "node_modules/intl-messageformat/node_modules/@formatjs/intl-localematcher": {
+ "version": "0.2.25",
+ "resolved": "https://registry.npmjs.org/@formatjs/intl-localematcher/-/intl-localematcher-0.2.25.tgz",
+ "integrity": "sha512-YmLcX70BxoSopLFdLr1Ds99NdlTI2oWoLbaUW2M406lxOIPzE1KQhRz2fPUkq34xVZQaihCoU29h0KK7An3bhA==",
+ "dependencies": {
+ "tslib": "^2.1.0"
+ }
+ },
"node_modules/invariant": {
"version": "2.2.4",
"resolved": "https://registry.npmjs.org/invariant/-/invariant-2.2.4.tgz",
@@ -14127,6 +14242,14 @@
"integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==",
"dev": true
},
+ "node_modules/negotiator": {
+ "version": "0.6.3",
+ "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz",
+ "integrity": "sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==",
+ "engines": {
+ "node": ">= 0.6"
+ }
+ },
"node_modules/next": {
"version": "14.1.0",
"resolved": "https://registry.npmjs.org/next/-/next-14.1.0.tgz",
@@ -14199,6 +14322,26 @@
}
}
},
+ "node_modules/next-intl": {
+ "version": "3.9.5",
+ "resolved": "https://registry.npmjs.org/next-intl/-/next-intl-3.9.5.tgz",
+ "integrity": "sha512-tsp4N433WgTAbbyZdMlcsLGHFM88wv2a7ZpF/od8X9+qAlO1TrajZrNrGBpIg6nA9EGZyMbQPzZD7XZrqYIv7g==",
+ "funding": [
+ {
+ "type": "individual",
+ "url": "https://github.com/sponsors/amannn"
+ }
+ ],
+ "dependencies": {
+ "@formatjs/intl-localematcher": "^0.2.32",
+ "negotiator": "^0.6.3",
+ "use-intl": "^3.9.5"
+ },
+ "peerDependencies": {
+ "next": "^10.0.0 || ^11.0.0 || ^12.0.0 || ^13.0.0 || ^14.0.0",
+ "react": "^16.8.0 || ^17.0.0 || ^18.0.0"
+ }
+ },
"node_modules/next-themes": {
"version": "0.2.1",
"resolved": "https://registry.npmjs.org/next-themes/-/next-themes-0.2.1.tgz",
@@ -17643,6 +17786,18 @@
}
}
},
+ "node_modules/use-intl": {
+ "version": "3.9.5",
+ "resolved": "https://registry.npmjs.org/use-intl/-/use-intl-3.9.5.tgz",
+ "integrity": "sha512-1g+f/pKEeXqOXrd+QBvwnIN5kzM56PHsorbVWzNvlnGk2fo/eRwuuT/S0jTuzKLRW4uNybpHvRs6U06rP31iKw==",
+ "dependencies": {
+ "@formatjs/ecma402-abstract": "^1.11.4",
+ "intl-messageformat": "^9.3.18"
+ },
+ "peerDependencies": {
+ "react": "^16.8.0 || ^17.0.0 || ^18.0.0"
+ }
+ },
"node_modules/use-sidecar": {
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/use-sidecar/-/use-sidecar-1.1.2.tgz",
diff --git a/package.json b/package.json
index ad5a920..995257d 100644
--- a/package.json
+++ b/package.json
@@ -71,6 +71,7 @@
"lucide-react": "^0.316.0",
"next": "^14.1.0",
"next-auth": "^4.24.5",
+ "next-intl": "^3.9.5",
"next-themes": "^0.2.1",
"nodemailer": "^6.9.9",
"pino": "^8.17.2",
diff --git a/src/app/(manage)/admin/config/payment/page.tsx b/src/app/(manage)/admin/config/payment/page.tsx
deleted file mode 100644
index 1e05cfa..0000000
--- a/src/app/(manage)/admin/config/payment/page.tsx
+++ /dev/null
@@ -1,5 +0,0 @@
-import PaymentTable from "~/app/(manage)/admin/config/_components/payment-table";
-
-export default function PaymentPage() {
- return ;
-}
diff --git a/src/app/(manage)/admin/config/withdraw/page.tsx b/src/app/(manage)/admin/config/withdraw/page.tsx
deleted file mode 100644
index a02775d..0000000
--- a/src/app/(manage)/admin/config/withdraw/page.tsx
+++ /dev/null
@@ -1,5 +0,0 @@
-import WithdrawalTable from "~/app/(manage)/admin/config/_components/withdrawal-table";
-
-export default function WithdrawalPage() {
- return ;
-}
diff --git a/src/app/(manage)/forward/page.tsx b/src/app/(manage)/forward/page.tsx
deleted file mode 100644
index 0db7afd..0000000
--- a/src/app/(manage)/forward/page.tsx
+++ /dev/null
@@ -1,14 +0,0 @@
-import ForwardTable from "~/app/(manage)/forward/_components/forward-table";
-
-export const metadata = {
- title: "转发 - vortex",
-};
-
-export default function Forward() {
- return (
-
-
转发
-
-
- );
-}
diff --git a/src/app/(manage)/network/page.tsx b/src/app/(manage)/network/page.tsx
deleted file mode 100644
index e4384ce..0000000
--- a/src/app/(manage)/network/page.tsx
+++ /dev/null
@@ -1,21 +0,0 @@
-import "reactflow/dist/style.css";
-import NetworkTable from "~/app/(manage)/network/_components/network-table";
-
-export const metadata = {
- title: "组网 - vortex",
-};
-
-export default async function NetworksPage({
- searchParams: { keyword },
-}: {
- searchParams: { keyword: string };
-}) {
- return (
-
- );
-}
diff --git a/src/app/(manage)/ticket/page.tsx b/src/app/(manage)/ticket/page.tsx
deleted file mode 100644
index d0b689d..0000000
--- a/src/app/(manage)/ticket/page.tsx
+++ /dev/null
@@ -1,20 +0,0 @@
-import TicketTable from "~/app/(manage)/ticket/_components/ticket-table";
-
-export const metadata = {
- title: "工单 - vortex",
-};
-
-export default function TicketsPage({
- searchParams: { keyword },
-}: {
- searchParams: { keyword: string };
-}) {
- return (
-
- );
-}
diff --git a/src/app/(manage)/admin/config/[classify]/page.tsx b/src/app/[local]/(manage)/admin/config/[classify]/page.tsx
similarity index 93%
rename from src/app/(manage)/admin/config/[classify]/page.tsx
rename to src/app/[local]/(manage)/admin/config/[classify]/page.tsx
index 238ad29..fec046b 100644
--- a/src/app/(manage)/admin/config/[classify]/page.tsx
+++ b/src/app/[local]/(manage)/admin/config/[classify]/page.tsx
@@ -1,5 +1,5 @@
import { api } from "~/trpc/server";
-import ConfigList from "~/app/(manage)/admin/config/_components/config-list";
+import ConfigList from "~/app/[local]/(manage)/admin/config/_components/config-list";
import { GLOBAL_CONFIG_SCHEMA_MAP } from "~/lib/constants/config";
import { type CONFIG_KEY } from "~/lib/types";
diff --git a/src/app/(manage)/admin/config/_components/code-input-dialog.tsx b/src/app/[local]/(manage)/admin/config/_components/code-input-dialog.tsx
similarity index 97%
rename from src/app/(manage)/admin/config/_components/code-input-dialog.tsx
rename to src/app/[local]/(manage)/admin/config/_components/code-input-dialog.tsx
index 15b9e9c..45eedaa 100644
--- a/src/app/(manage)/admin/config/_components/code-input-dialog.tsx
+++ b/src/app/[local]/(manage)/admin/config/_components/code-input-dialog.tsx
@@ -5,7 +5,7 @@ import {
DialogTitle,
DialogTrigger,
} from "~/lib/ui/dialog";
-import CodeInput from "~/app/_components/code-input";
+import CodeInput from "~/app/[local]/_components/code-input";
import { useState } from "react";
import { Tabs, TabsList, TabsTrigger } from "~/lib/ui/tabs";
import Markdown from "react-markdown";
diff --git a/src/app/(manage)/admin/config/_components/config-field.tsx b/src/app/[local]/(manage)/admin/config/_components/config-field.tsx
similarity index 97%
rename from src/app/(manage)/admin/config/_components/config-field.tsx
rename to src/app/[local]/(manage)/admin/config/_components/config-field.tsx
index 0094f87..d88db6c 100644
--- a/src/app/(manage)/admin/config/_components/config-field.tsx
+++ b/src/app/[local]/(manage)/admin/config/_components/config-field.tsx
@@ -18,7 +18,7 @@ import {
} from "~/lib/ui/select";
import { Switch } from "~/lib/ui/switch";
import { CONFIG_DEFAULT_VALUE_MAP } from "~/lib/constants/config";
-import CodeInputDialog from "~/app/(manage)/admin/config/_components/code-input-dialog";
+import CodeInputDialog from "~/app/[local]/(manage)/admin/config/_components/code-input-dialog";
import { isTrue } from "~/lib/utils";
import { useTrack } from "~/lib/hooks/use-track";
diff --git a/src/app/(manage)/admin/config/_components/config-list.tsx b/src/app/[local]/(manage)/admin/config/_components/config-list.tsx
similarity index 91%
rename from src/app/(manage)/admin/config/_components/config-list.tsx
rename to src/app/[local]/(manage)/admin/config/_components/config-list.tsx
index 85b6a2e..5f294c6 100644
--- a/src/app/(manage)/admin/config/_components/config-list.tsx
+++ b/src/app/[local]/(manage)/admin/config/_components/config-list.tsx
@@ -1,5 +1,5 @@
import { type Config } from "@prisma/client";
-import ConfigField from "~/app/(manage)/admin/config/_components/config-field";
+import ConfigField from "~/app/[local]/(manage)/admin/config/_components/config-field";
import { type CONFIG_KEY, type ConfigSchema } from "~/lib/types";
export default function ConfigList({
diff --git a/src/app/(manage)/admin/config/_components/cron-input.tsx b/src/app/[local]/(manage)/admin/config/_components/cron-input.tsx
similarity index 100%
rename from src/app/(manage)/admin/config/_components/cron-input.tsx
rename to src/app/[local]/(manage)/admin/config/_components/cron-input.tsx
diff --git a/src/app/(manage)/admin/config/_components/payment-info.tsx b/src/app/[local]/(manage)/admin/config/_components/payment-info.tsx
similarity index 98%
rename from src/app/(manage)/admin/config/_components/payment-info.tsx
rename to src/app/[local]/(manage)/admin/config/_components/payment-info.tsx
index e108064..84c6970 100644
--- a/src/app/(manage)/admin/config/_components/payment-info.tsx
+++ b/src/app/[local]/(manage)/admin/config/_components/payment-info.tsx
@@ -2,7 +2,7 @@ import Blockchains from "@depay/web3-blockchains";
import * as React from "react";
import { Label } from "~/lib/ui/label";
import Link from "next/link";
-import ID from "~/app/_components/id";
+import ID from "~/app/[local]/_components/id";
import Image from "next/image";
import { Badge } from "~/lib/ui/badge";
import { formatDate } from "~/lib/utils";
diff --git a/src/app/(manage)/admin/config/_components/payment-table.tsx b/src/app/[local]/(manage)/admin/config/_components/payment-table.tsx
similarity index 95%
rename from src/app/(manage)/admin/config/_components/payment-table.tsx
rename to src/app/[local]/(manage)/admin/config/_components/payment-table.tsx
index c8222ed..4d6da1f 100644
--- a/src/app/(manage)/admin/config/_components/payment-table.tsx
+++ b/src/app/[local]/(manage)/admin/config/_components/payment-table.tsx
@@ -11,15 +11,15 @@ import {
} from "@tanstack/react-table";
import { api } from "~/trpc/react";
import { Input } from "~/lib/ui/input";
-import Table from "~/app/_components/table";
+import Table from "~/app/[local]/_components/table";
import { type PaymentGetAllOutput } from "~/lib/types/trpc";
import { Button } from "~/lib/ui/button";
import { MoreHorizontalIcon, XIcon } from "lucide-react";
-import { DataTableViewOptions } from "~/app/_components/table-view-options";
-import ID from "~/app/_components/id";
-import UserColumn from "~/app/_components/user-column";
+import { DataTableViewOptions } from "~/app/[local]/_components/table-view-options";
+import ID from "~/app/[local]/_components/id";
+import UserColumn from "~/app/[local]/_components/user-column";
import { MoneyInput } from "~/lib/ui/money-input";
-import { TableFacetedFilter } from "~/app/_components/table-faceted-filter";
+import { TableFacetedFilter } from "~/app/[local]/_components/table-faceted-filter";
import { PaymentStatusOptions } from "~/lib/constants";
import { type $Enums } from ".prisma/client";
import { cn, formatDate } from "~/lib/utils";
@@ -37,7 +37,7 @@ import {
AccordionItem,
AccordionTrigger,
} from "~/lib/ui/accordion";
-import PaymentInfo from "~/app/(manage)/admin/config/_components/payment-info";
+import PaymentInfo from "~/app/[local]/(manage)/admin/config/_components/payment-info";
type PaymentStatus = $Enums.PaymentStatus;
diff --git a/src/app/(manage)/admin/config/_components/recharge-code-table.tsx b/src/app/[local]/(manage)/admin/config/_components/recharge-code-table.tsx
similarity index 97%
rename from src/app/(manage)/admin/config/_components/recharge-code-table.tsx
rename to src/app/[local]/(manage)/admin/config/_components/recharge-code-table.tsx
index b6d8e4c..c20aa66 100644
--- a/src/app/(manage)/admin/config/_components/recharge-code-table.tsx
+++ b/src/app/[local]/(manage)/admin/config/_components/recharge-code-table.tsx
@@ -9,13 +9,13 @@ import {
} from "@tanstack/react-table";
import { api } from "~/trpc/react";
import { Input } from "~/lib/ui/input";
-import Table from "~/app/_components/table";
+import Table from "~/app/[local]/_components/table";
import { type RechargeCodeGetAllOutput } from "~/lib/types/trpc";
import { Button } from "~/lib/ui/button";
import { TicketCheckIcon, TicketIcon, Trash2Icon, XIcon } from "lucide-react";
-import { DataTableViewOptions } from "~/app/_components/table-view-options";
-import ID from "~/app/_components/id";
-import UserColumn from "~/app/_components/user-column";
+import { DataTableViewOptions } from "~/app/[local]/_components/table-view-options";
+import ID from "~/app/[local]/_components/id";
+import UserColumn from "~/app/[local]/_components/user-column";
import {
AlertDialog,
AlertDialogAction,
diff --git a/src/app/(manage)/admin/config/_components/traffic-price-config.tsx b/src/app/[local]/(manage)/admin/config/_components/traffic-price-config.tsx
similarity index 100%
rename from src/app/(manage)/admin/config/_components/traffic-price-config.tsx
rename to src/app/[local]/(manage)/admin/config/_components/traffic-price-config.tsx
diff --git a/src/app/(manage)/admin/config/_components/withdrawal-table.tsx b/src/app/[local]/(manage)/admin/config/_components/withdrawal-table.tsx
similarity index 94%
rename from src/app/(manage)/admin/config/_components/withdrawal-table.tsx
rename to src/app/[local]/(manage)/admin/config/_components/withdrawal-table.tsx
index 809c70a..6cc6154 100644
--- a/src/app/(manage)/admin/config/_components/withdrawal-table.tsx
+++ b/src/app/[local]/(manage)/admin/config/_components/withdrawal-table.tsx
@@ -11,15 +11,15 @@ import {
} from "@tanstack/react-table";
import { api } from "~/trpc/react";
import { Input } from "~/lib/ui/input";
-import Table from "~/app/_components/table";
+import Table from "~/app/[local]/_components/table";
import { type WithdrawalGetAllOutput } from "~/lib/types/trpc";
import { Button } from "~/lib/ui/button";
import { CheckSquareIcon, XIcon } from "lucide-react";
-import { DataTableViewOptions } from "~/app/_components/table-view-options";
-import ID from "~/app/_components/id";
-import UserColumn from "~/app/_components/user-column";
+import { DataTableViewOptions } from "~/app/[local]/_components/table-view-options";
+import ID from "~/app/[local]/_components/id";
+import UserColumn from "~/app/[local]/_components/user-column";
import { MoneyInput } from "~/lib/ui/money-input";
-import { TableFacetedFilter } from "~/app/_components/table-faceted-filter";
+import { TableFacetedFilter } from "~/app/[local]/_components/table-faceted-filter";
import { WithdrawalStatusOptions } from "~/lib/constants";
import { Badge } from "~/lib/ui/badge";
import { toast } from "~/lib/ui/use-toast";
diff --git a/src/app/(manage)/admin/config/layout.tsx b/src/app/[local]/(manage)/admin/config/layout.tsx
similarity index 95%
rename from src/app/(manage)/admin/config/layout.tsx
rename to src/app/[local]/(manage)/admin/config/layout.tsx
index 5e448a8..2491dba 100644
--- a/src/app/(manage)/admin/config/layout.tsx
+++ b/src/app/[local]/(manage)/admin/config/layout.tsx
@@ -1,6 +1,6 @@
import { Separator } from "~/lib/ui/separator";
import { type ReactNode } from "react";
-import { SidebarNav } from "~/app/_components/sidebar-nav";
+import { SidebarNav } from "~/app/[local]/_components/sidebar-nav";
import type { Metadata } from "next";
export const metadata: Metadata = {
diff --git a/src/app/(manage)/admin/config/page.tsx b/src/app/[local]/(manage)/admin/config/page.tsx
similarity index 76%
rename from src/app/(manage)/admin/config/page.tsx
rename to src/app/[local]/(manage)/admin/config/page.tsx
index 8359e28..6b36275 100644
--- a/src/app/(manage)/admin/config/page.tsx
+++ b/src/app/[local]/(manage)/admin/config/page.tsx
@@ -1,5 +1,5 @@
import { api } from "~/trpc/server";
-import ConfigList from "~/app/(manage)/admin/config/_components/config-list";
+import ConfigList from "~/app/[local]/(manage)/admin/config/_components/config-list";
import { GLOBAL_CONFIG_SCHEMA_MAP } from "~/lib/constants/config";
export default async function Config() {
diff --git a/src/app/[local]/(manage)/admin/config/payment/page.tsx b/src/app/[local]/(manage)/admin/config/payment/page.tsx
new file mode 100644
index 0000000..5319477
--- /dev/null
+++ b/src/app/[local]/(manage)/admin/config/payment/page.tsx
@@ -0,0 +1,5 @@
+import PaymentTable from "~/app/[local]/(manage)/admin/config/_components/payment-table";
+
+export default function PaymentPage() {
+ return ;
+}
diff --git a/src/app/(manage)/admin/config/recharge-code/page.tsx b/src/app/[local]/(manage)/admin/config/recharge-code/page.tsx
similarity index 90%
rename from src/app/(manage)/admin/config/recharge-code/page.tsx
rename to src/app/[local]/(manage)/admin/config/recharge-code/page.tsx
index e3e33dc..fe7efc2 100644
--- a/src/app/(manage)/admin/config/recharge-code/page.tsx
+++ b/src/app/[local]/(manage)/admin/config/recharge-code/page.tsx
@@ -1,4 +1,4 @@
-import RechargeCodeTable from "~/app/(manage)/admin/config/_components/recharge-code-table";
+import RechargeCodeTable from "~/app/[local]/(manage)/admin/config/_components/recharge-code-table";
import { Separator } from "~/lib/ui/separator";
import Link from "next/link";
import { MoveRightIcon } from "lucide-react";
diff --git a/src/app/[local]/(manage)/admin/config/withdraw/page.tsx b/src/app/[local]/(manage)/admin/config/withdraw/page.tsx
new file mode 100644
index 0000000..0e3d5c6
--- /dev/null
+++ b/src/app/[local]/(manage)/admin/config/withdraw/page.tsx
@@ -0,0 +1,5 @@
+import WithdrawalTable from "~/app/[local]/(manage)/admin/config/_components/withdrawal-table";
+
+export default function WithdrawalPage() {
+ return ;
+}
diff --git a/src/app/(manage)/admin/log/_components/log-delete.tsx b/src/app/[local]/(manage)/admin/log/_components/log-delete.tsx
similarity index 95%
rename from src/app/(manage)/admin/log/_components/log-delete.tsx
rename to src/app/[local]/(manage)/admin/log/_components/log-delete.tsx
index 59283c9..0f59852 100644
--- a/src/app/(manage)/admin/log/_components/log-delete.tsx
+++ b/src/app/[local]/(manage)/admin/log/_components/log-delete.tsx
@@ -14,7 +14,7 @@ import { Button } from "~/lib/ui/button";
import { Trash2Icon } from "lucide-react";
import React from "react";
import { toast } from "~/lib/ui/use-toast";
-import { useLogStore } from "~/app/(manage)/admin/log/store/log-store";
+import { useLogStore } from "~/app/[local]/(manage)/admin/log/store/log-store";
import { useTrack } from "~/lib/hooks/use-track";
export default function LogDelete() {
diff --git a/src/app/(manage)/admin/log/_components/log-glance.tsx b/src/app/[local]/(manage)/admin/log/_components/log-glance.tsx
similarity index 98%
rename from src/app/(manage)/admin/log/_components/log-glance.tsx
rename to src/app/[local]/(manage)/admin/log/_components/log-glance.tsx
index 639d0f3..2e7d2a5 100644
--- a/src/app/(manage)/admin/log/_components/log-glance.tsx
+++ b/src/app/[local]/(manage)/admin/log/_components/log-glance.tsx
@@ -7,7 +7,7 @@ import {
import { BarChartHorizontalIcon } from "lucide-react";
import React from "react";
import { cn } from "~/lib/utils";
-import { useLogStore } from "~/app/(manage)/admin/log/store/log-store";
+import { useLogStore } from "~/app/[local]/(manage)/admin/log/store/log-store";
import { api } from "~/trpc/react";
export default function LogGlance() {
diff --git a/src/app/(manage)/admin/log/_components/log-search-keyword.tsx b/src/app/[local]/(manage)/admin/log/_components/log-search-keyword.tsx
similarity index 96%
rename from src/app/(manage)/admin/log/_components/log-search-keyword.tsx
rename to src/app/[local]/(manage)/admin/log/_components/log-search-keyword.tsx
index a776d32..f6792ac 100644
--- a/src/app/(manage)/admin/log/_components/log-search-keyword.tsx
+++ b/src/app/[local]/(manage)/admin/log/_components/log-search-keyword.tsx
@@ -1,4 +1,4 @@
-import { useLogStore } from "~/app/(manage)/admin/log/store/log-store";
+import { useLogStore } from "~/app/[local]/(manage)/admin/log/store/log-store";
import { Popover, PopoverContent, PopoverTrigger } from "~/lib/ui/popover";
import { Input } from "~/lib/ui/input";
import React, { useEffect } from "react";
diff --git a/src/app/(manage)/admin/log/_components/log-toolbar.tsx b/src/app/[local]/(manage)/admin/log/_components/log-toolbar.tsx
similarity index 90%
rename from src/app/(manage)/admin/log/_components/log-toolbar.tsx
rename to src/app/[local]/(manage)/admin/log/_components/log-toolbar.tsx
index 308b036..9762763 100644
--- a/src/app/(manage)/admin/log/_components/log-toolbar.tsx
+++ b/src/app/[local]/(manage)/admin/log/_components/log-toolbar.tsx
@@ -6,10 +6,10 @@ import { Popover, PopoverContent, PopoverTrigger } from "~/lib/ui/popover";
import { Label } from "~/lib/ui/label";
import React from "react";
import { LEVELS } from "~/lib/constants/log-level";
-import { useLogStore } from "~/app/(manage)/admin/log/store/log-store";
-import LogDelete from "~/app/(manage)/admin/log/_components/log-delete";
-import { FacetedFilter } from "~/app/_components/faceted-filter";
-import LogSearchKeyword from "~/app/(manage)/admin/log/_components/log-search-keyword";
+import { useLogStore } from "~/app/[local]/(manage)/admin/log/store/log-store";
+import LogDelete from "~/app/[local]/(manage)/admin/log/_components/log-delete";
+import { FacetedFilter } from "~/app/[local]/_components/faceted-filter";
+import LogSearchKeyword from "~/app/[local]/(manage)/admin/log/_components/log-search-keyword";
export default function LogToolbar() {
const { params, setParams, resetParams, isFiltering } = useLogStore();
diff --git a/src/app/(manage)/admin/log/_components/log.tsx b/src/app/[local]/(manage)/admin/log/_components/log.tsx
similarity index 100%
rename from src/app/(manage)/admin/log/_components/log.tsx
rename to src/app/[local]/(manage)/admin/log/_components/log.tsx
diff --git a/src/app/(manage)/admin/log/_components/logs.tsx b/src/app/[local]/(manage)/admin/log/_components/logs.tsx
similarity index 88%
rename from src/app/(manage)/admin/log/_components/logs.tsx
rename to src/app/[local]/(manage)/admin/log/_components/logs.tsx
index b133e07..54413e3 100644
--- a/src/app/(manage)/admin/log/_components/logs.tsx
+++ b/src/app/[local]/(manage)/admin/log/_components/logs.tsx
@@ -3,12 +3,12 @@ import React, { useEffect } from "react";
import { ScrollArea } from "~/lib/ui/scroll-area";
import { Accordion } from "~/lib/ui/accordion";
import { api } from "~/trpc/react";
-import { useLogStore } from "~/app/(manage)/admin/log/store/log-store";
-import LogGlance from "~/app/(manage)/admin/log/_components/log-glance";
-import Log from "~/app/(manage)/admin/log/_components/log";
-import LogToolbar from "~/app/(manage)/admin/log/_components/log-toolbar";
+import { useLogStore } from "~/app/[local]/(manage)/admin/log/store/log-store";
+import LogGlance from "~/app/[local]/(manage)/admin/log/_components/log-glance";
+import Log from "~/app/[local]/(manage)/admin/log/_components/log";
+import LogToolbar from "~/app/[local]/(manage)/admin/log/_components/log-toolbar";
import type { LogsOutput } from "~/lib/types/trpc";
-import SearchEmptyState from "~/app/_components/search-empty-state";
+import SearchEmptyState from "~/app/[local]/_components/search-empty-state";
export function Logs({ agentId }: { agentId?: string }) {
const { convertParams, setParams, params } = useLogStore();
diff --git a/src/app/(manage)/admin/log/page.tsx b/src/app/[local]/(manage)/admin/log/page.tsx
similarity index 74%
rename from src/app/(manage)/admin/log/page.tsx
rename to src/app/[local]/(manage)/admin/log/page.tsx
index 6fe30b9..a02a00c 100644
--- a/src/app/(manage)/admin/log/page.tsx
+++ b/src/app/[local]/(manage)/admin/log/page.tsx
@@ -1,4 +1,4 @@
-import { Logs } from "~/app/(manage)/admin/log/_components/logs";
+import { Logs } from "~/app/[local]/(manage)/admin/log/_components/logs";
export const metadata = {
title: "日志 - vortex",
diff --git a/src/app/(manage)/admin/log/store/log-store.ts b/src/app/[local]/(manage)/admin/log/store/log-store.ts
similarity index 100%
rename from src/app/(manage)/admin/log/store/log-store.ts
rename to src/app/[local]/(manage)/admin/log/store/log-store.ts
diff --git a/src/app/(manage)/admin/users/_components/user-role-setting.tsx b/src/app/[local]/(manage)/admin/users/_components/user-role-setting.tsx
similarity index 100%
rename from src/app/(manage)/admin/users/_components/user-role-setting.tsx
rename to src/app/[local]/(manage)/admin/users/_components/user-role-setting.tsx
diff --git a/src/app/(manage)/admin/users/_components/user-status.tsx b/src/app/[local]/(manage)/admin/users/_components/user-status.tsx
similarity index 100%
rename from src/app/(manage)/admin/users/_components/user-status.tsx
rename to src/app/[local]/(manage)/admin/users/_components/user-status.tsx
diff --git a/src/app/(manage)/admin/users/_components/user-table.tsx b/src/app/[local]/(manage)/admin/users/_components/user-table.tsx
similarity index 90%
rename from src/app/(manage)/admin/users/_components/user-table.tsx
rename to src/app/[local]/(manage)/admin/users/_components/user-table.tsx
index fab8c87..8328ddf 100644
--- a/src/app/(manage)/admin/users/_components/user-table.tsx
+++ b/src/app/[local]/(manage)/admin/users/_components/user-table.tsx
@@ -7,12 +7,12 @@ import {
} from "@tanstack/react-table";
import { Input } from "~/lib/ui/input";
import { type ChangeEvent, useMemo, useState } from "react";
-import Table from "~/app/_components/table";
+import Table from "~/app/[local]/_components/table";
import { api } from "~/trpc/react";
import { type UserGetAllOutput } from "~/lib/types/trpc";
-import ID from "~/app/_components/id";
-import UserColumn from "~/app/_components/user-column";
-import UserStatusSwitch from "~/app/(manage)/admin/users/_components/user-status";
+import ID from "~/app/[local]/_components/id";
+import UserColumn from "~/app/[local]/_components/user-column";
+import UserStatusSwitch from "~/app/[local]/(manage)/admin/users/_components/user-status";
import { TooltipProvider } from "~/lib/ui/tooltip";
import { Badge } from "~/lib/ui/badge";
import {
@@ -22,8 +22,8 @@ import {
} from "~/lib/ui/dropdown-menu";
import { Button } from "~/lib/ui/button";
import { MoreHorizontalIcon, XIcon } from "lucide-react";
-import UserRoleSettings from "~/app/(manage)/admin/users/_components/user-role-setting";
-import { DataTableViewOptions } from "~/app/_components/table-view-options";
+import UserRoleSettings from "~/app/[local]/(manage)/admin/users/_components/user-role-setting";
+import { DataTableViewOptions } from "~/app/[local]/_components/table-view-options";
export default function UserTable() {
const [keyword, setKeyword] = useState("");
diff --git a/src/app/(manage)/admin/users/page.tsx b/src/app/[local]/(manage)/admin/users/page.tsx
similarity index 73%
rename from src/app/(manage)/admin/users/page.tsx
rename to src/app/[local]/(manage)/admin/users/page.tsx
index 2a667ca..5fda043 100644
--- a/src/app/(manage)/admin/users/page.tsx
+++ b/src/app/[local]/(manage)/admin/users/page.tsx
@@ -1,4 +1,4 @@
-import UserTable from "~/app/(manage)/admin/users/_components/user-table";
+import UserTable from "~/app/[local]/(manage)/admin/users/_components/user-table";
export const metadata = {
title: "用户 - vortex",
diff --git a/src/app/(manage)/agent/[agentId]/config/base/page.tsx b/src/app/[local]/(manage)/agent/[agentId]/config/base/page.tsx
similarity index 82%
rename from src/app/(manage)/agent/[agentId]/config/base/page.tsx
rename to src/app/[local]/(manage)/agent/[agentId]/config/base/page.tsx
index f1196b9..9ba019a 100644
--- a/src/app/(manage)/agent/[agentId]/config/base/page.tsx
+++ b/src/app/[local]/(manage)/agent/[agentId]/config/base/page.tsx
@@ -1,4 +1,4 @@
-import { AgentForm } from "~/app/(manage)/agent/_components/agent-form";
+import { AgentForm } from "~/app/[local]/(manage)/agent/_components/agent-form";
import { api } from "~/trpc/server";
export const metadata = {
diff --git a/src/app/(manage)/agent/[agentId]/config/other/page.tsx b/src/app/[local]/(manage)/agent/[agentId]/config/other/page.tsx
similarity index 86%
rename from src/app/(manage)/agent/[agentId]/config/other/page.tsx
rename to src/app/[local]/(manage)/agent/[agentId]/config/other/page.tsx
index 7e98a0e..1fa77d4 100644
--- a/src/app/(manage)/agent/[agentId]/config/other/page.tsx
+++ b/src/app/[local]/(manage)/agent/[agentId]/config/other/page.tsx
@@ -1,4 +1,4 @@
-import ConfigList from "~/app/(manage)/admin/config/_components/config-list";
+import ConfigList from "~/app/[local]/(manage)/admin/config/_components/config-list";
import { AGENT_CONFIG_SCHEMA_MAP } from "~/lib/constants/config";
import { api } from "~/trpc/server";
diff --git a/src/app/(manage)/agent/[agentId]/forward/page.tsx b/src/app/[local]/(manage)/agent/[agentId]/forward/page.tsx
similarity index 76%
rename from src/app/(manage)/agent/[agentId]/forward/page.tsx
rename to src/app/[local]/(manage)/agent/[agentId]/forward/page.tsx
index cb21c66..5327010 100644
--- a/src/app/(manage)/agent/[agentId]/forward/page.tsx
+++ b/src/app/[local]/(manage)/agent/[agentId]/forward/page.tsx
@@ -1,4 +1,4 @@
-import ForwardTable from "~/app/(manage)/forward/_components/forward-table";
+import ForwardTable from "~/app/[local]/(manage)/forward/_components/forward-table";
export const metadata = {
title: "服务器 - 转发 - vortex",
diff --git a/src/app/(manage)/agent/[agentId]/install/page.tsx b/src/app/[local]/(manage)/agent/[agentId]/install/page.tsx
similarity index 73%
rename from src/app/(manage)/agent/[agentId]/install/page.tsx
rename to src/app/[local]/(manage)/agent/[agentId]/install/page.tsx
index 3400a66..72aabda 100644
--- a/src/app/(manage)/agent/[agentId]/install/page.tsx
+++ b/src/app/[local]/(manage)/agent/[agentId]/install/page.tsx
@@ -1,4 +1,4 @@
-import AgentInstall from "~/app/(manage)/agent/_components/agent-install";
+import AgentInstall from "~/app/[local]/(manage)/agent/_components/agent-install";
export const metadata = {
title: "服务器 - 安装 - vortex",
diff --git a/src/app/(manage)/agent/[agentId]/layout.tsx b/src/app/[local]/(manage)/agent/[agentId]/layout.tsx
similarity index 85%
rename from src/app/(manage)/agent/[agentId]/layout.tsx
rename to src/app/[local]/(manage)/agent/[agentId]/layout.tsx
index 0d69e04..ca9d8bd 100644
--- a/src/app/(manage)/agent/[agentId]/layout.tsx
+++ b/src/app/[local]/(manage)/agent/[agentId]/layout.tsx
@@ -1,7 +1,7 @@
import { api } from "~/trpc/server";
-import AgentResizableLayout from "~/app/(manage)/agent/_components/agent-resizable-layout";
+import AgentResizableLayout from "~/app/[local]/(manage)/agent/_components/agent-resizable-layout";
import { type ReactNode } from "react";
-import AgentMenu from "~/app/(manage)/agent/_components/agent-menu";
+import AgentMenu from "~/app/[local]/(manage)/agent/_components/agent-menu";
export const metadata = {
title: "服务器 - vortex",
diff --git a/src/app/(manage)/agent/[agentId]/loading.tsx b/src/app/[local]/(manage)/agent/[agentId]/loading.tsx
similarity index 100%
rename from src/app/(manage)/agent/[agentId]/loading.tsx
rename to src/app/[local]/(manage)/agent/[agentId]/loading.tsx
diff --git a/src/app/(manage)/agent/[agentId]/log/page.tsx b/src/app/[local]/(manage)/agent/[agentId]/log/page.tsx
similarity index 77%
rename from src/app/(manage)/agent/[agentId]/log/page.tsx
rename to src/app/[local]/(manage)/agent/[agentId]/log/page.tsx
index 5942ddf..4714d35 100644
--- a/src/app/(manage)/agent/[agentId]/log/page.tsx
+++ b/src/app/[local]/(manage)/agent/[agentId]/log/page.tsx
@@ -1,4 +1,4 @@
-import { Logs } from "~/app/(manage)/admin/log/_components/logs";
+import { Logs } from "~/app/[local]/(manage)/admin/log/_components/logs";
export const metadata = {
title: "服务器 - 日志 - vortex",
diff --git a/src/app/(manage)/agent/[agentId]/status/page.tsx b/src/app/[local]/(manage)/agent/[agentId]/status/page.tsx
similarity index 94%
rename from src/app/(manage)/agent/[agentId]/status/page.tsx
rename to src/app/[local]/(manage)/agent/[agentId]/status/page.tsx
index 14f001b..08e9208 100644
--- a/src/app/(manage)/agent/[agentId]/status/page.tsx
+++ b/src/app/[local]/(manage)/agent/[agentId]/status/page.tsx
@@ -1,22 +1,22 @@
import { getPlatformIcon } from "~/lib/icons";
import CpuUsage, {
type CpuStat,
-} from "~/app/(manage)/agent/_components/cpu-usage";
+} from "~/app/[local]/(manage)/agent/_components/cpu-usage";
import MemUsage, {
type MemStat,
-} from "~/app/(manage)/agent/_components/mem-usage";
+} from "~/app/[local]/(manage)/agent/_components/mem-usage";
import BandwidthUsage, {
type BandwidthStat,
-} from "~/app/(manage)/agent/_components/bandwidth-usage";
+} from "~/app/[local]/(manage)/agent/_components/bandwidth-usage";
import TrafficUsage, {
type TrafficStat,
-} from "~/app/(manage)/agent/_components/traffic-usage";
+} from "~/app/[local]/(manage)/agent/_components/traffic-usage";
import { convertBytes, convertBytesToBestUnit, formatDate } from "~/lib/utils";
import "/node_modules/flag-icons/css/flag-icons.min.css";
-import ID from "~/app/_components/id";
+import ID from "~/app/[local]/_components/id";
import { MoveDownIcon, MoveUpIcon } from "lucide-react";
import { api } from "~/trpc/server";
-import AgentPrice from "~/app/(manage)/agent/_components/agent-price";
+import AgentPrice from "~/app/[local]/(manage)/agent/_components/agent-price";
export const metadata = {
title: "服务器 - 状态 - vortex",
diff --git a/src/app/(manage)/agent/_components/agent-command.tsx b/src/app/[local]/(manage)/agent/_components/agent-command.tsx
similarity index 100%
rename from src/app/(manage)/agent/_components/agent-command.tsx
rename to src/app/[local]/(manage)/agent/_components/agent-command.tsx
diff --git a/src/app/(manage)/agent/_components/agent-config.tsx b/src/app/[local]/(manage)/agent/_components/agent-config.tsx
similarity index 84%
rename from src/app/(manage)/agent/_components/agent-config.tsx
rename to src/app/[local]/(manage)/agent/_components/agent-config.tsx
index 7cb456b..8f8f9ab 100644
--- a/src/app/(manage)/agent/_components/agent-config.tsx
+++ b/src/app/[local]/(manage)/agent/_components/agent-config.tsx
@@ -1,4 +1,4 @@
-import ConfigList from "~/app/(manage)/admin/config/_components/config-list";
+import ConfigList from "~/app/[local]/(manage)/admin/config/_components/config-list";
import { AGENT_CONFIG_SCHEMA_MAP } from "~/lib/constants/config";
import { api } from "~/trpc/server";
diff --git a/src/app/(manage)/agent/_components/agent-delete.tsx b/src/app/[local]/(manage)/agent/_components/agent-delete.tsx
similarity index 100%
rename from src/app/(manage)/agent/_components/agent-delete.tsx
rename to src/app/[local]/(manage)/agent/_components/agent-delete.tsx
diff --git a/src/app/(manage)/agent/_components/agent-form.tsx b/src/app/[local]/(manage)/agent/_components/agent-form.tsx
similarity index 100%
rename from src/app/(manage)/agent/_components/agent-form.tsx
rename to src/app/[local]/(manage)/agent/_components/agent-form.tsx
diff --git a/src/app/(manage)/agent/_components/agent-install.tsx b/src/app/[local]/(manage)/agent/_components/agent-install.tsx
similarity index 100%
rename from src/app/(manage)/agent/_components/agent-install.tsx
rename to src/app/[local]/(manage)/agent/_components/agent-install.tsx
diff --git a/src/app/(manage)/agent/_components/agent-list-aside.tsx b/src/app/[local]/(manage)/agent/_components/agent-list-aside.tsx
similarity index 78%
rename from src/app/(manage)/agent/_components/agent-list-aside.tsx
rename to src/app/[local]/(manage)/agent/_components/agent-list-aside.tsx
index 0d2f21c..bff04b7 100644
--- a/src/app/(manage)/agent/_components/agent-list-aside.tsx
+++ b/src/app/[local]/(manage)/agent/_components/agent-list-aside.tsx
@@ -1,7 +1,7 @@
"use client";
import { ScrollArea } from "~/lib/ui/scroll-area";
-import AgentList from "~/app/(manage)/agent/_components/agent-list";
-import { AgentForm } from "~/app/(manage)/agent/_components/agent-form";
+import AgentList from "~/app/[local]/(manage)/agent/_components/agent-list";
+import { AgentForm } from "~/app/[local]/(manage)/agent/_components/agent-form";
import { Button } from "~/lib/ui/button";
import { PlusIcon } from "lucide-react";
import { type AgentGetAllOutput } from "~/lib/types/trpc";
@@ -9,7 +9,7 @@ import { api } from "~/trpc/react";
import { cn } from "~/lib/utils";
import { hasPermission } from "~/lib/constants/permission";
import { useSession } from "next-auth/react";
-import { CSSProperties, useMemo, useState } from "react";
+import { type CSSProperties, useMemo, useState } from "react";
import {
Dialog,
DialogContent,
@@ -19,6 +19,7 @@ import {
DialogTrigger,
} from "~/lib/ui/dialog";
import { Input } from "~/lib/ui/input";
+import { useTranslations } from "use-intl";
export default function AgentListAside({
agentId,
@@ -31,6 +32,7 @@ export default function AgentListAside({
className?: string;
style?: CSSProperties;
}) {
+ const t = useTranslations("agent-agent-list-aside");
const [keyword, setKeyword] = useState("");
agents =
api.agent.getAll.useQuery(undefined, {
@@ -52,23 +54,23 @@ export default function AgentListAside({
setKeyword(e.target.value)}
/>
@@ -79,15 +81,13 @@ export default function AgentListAside({
- 添加服务器
+ {t("add_server")}
- 添加服务器
-
- 点击保存后查看侧边服务器栏。添加中转服务器,保存后会给你一个安装命令,复制到服务器上执行即可。
-
+ {t("add_server")}
+ {t("add_tips")}
diff --git a/src/app/(manage)/agent/_components/agent-list.tsx b/src/app/[local]/(manage)/agent/_components/agent-list.tsx
similarity index 100%
rename from src/app/(manage)/agent/_components/agent-list.tsx
rename to src/app/[local]/(manage)/agent/_components/agent-list.tsx
diff --git a/src/app/(manage)/agent/_components/agent-menu.tsx b/src/app/[local]/(manage)/agent/_components/agent-menu.tsx
similarity index 96%
rename from src/app/(manage)/agent/_components/agent-menu.tsx
rename to src/app/[local]/(manage)/agent/_components/agent-menu.tsx
index ec4d207..b513585 100644
--- a/src/app/(manage)/agent/_components/agent-menu.tsx
+++ b/src/app/[local]/(manage)/agent/_components/agent-menu.tsx
@@ -12,8 +12,8 @@ import { usePathname } from "next/navigation";
import * as React from "react";
import { cn } from "~/lib/utils";
import { useSession } from "next-auth/react";
-import AgentCommand from "~/app/(manage)/agent/_components/agent-command";
-import AgentDelete from "~/app/(manage)/agent/_components/agent-delete";
+import AgentCommand from "~/app/[local]/(manage)/agent/_components/agent-command";
+import AgentDelete from "~/app/[local]/(manage)/agent/_components/agent-delete";
import { type Agent } from ".prisma/client";
import { Role } from "@prisma/client";
diff --git a/src/app/(manage)/agent/_components/agent-price.tsx b/src/app/[local]/(manage)/agent/_components/agent-price.tsx
similarity index 100%
rename from src/app/(manage)/agent/_components/agent-price.tsx
rename to src/app/[local]/(manage)/agent/_components/agent-price.tsx
diff --git a/src/app/(manage)/agent/_components/agent-resizable-layout.tsx b/src/app/[local]/(manage)/agent/_components/agent-resizable-layout.tsx
similarity index 93%
rename from src/app/(manage)/agent/_components/agent-resizable-layout.tsx
rename to src/app/[local]/(manage)/agent/_components/agent-resizable-layout.tsx
index 4f70122..e76d751 100644
--- a/src/app/(manage)/agent/_components/agent-resizable-layout.tsx
+++ b/src/app/[local]/(manage)/agent/_components/agent-resizable-layout.tsx
@@ -4,7 +4,7 @@ import {
ResizablePanel,
ResizablePanelGroup,
} from "~/lib/ui/resizable";
-import AgentListAside from "~/app/(manage)/agent/_components/agent-list-aside";
+import AgentListAside from "~/app/[local]/(manage)/agent/_components/agent-list-aside";
import { type AgentGetAllOutput } from "~/lib/types/trpc";
import { type ReactNode, useRef } from "react";
import { useResizeObserver } from "~/lib/hooks/use-resize-observer";
diff --git a/src/app/(manage)/agent/_components/bandwidth-usage.tsx b/src/app/[local]/(manage)/agent/_components/bandwidth-usage.tsx
similarity index 100%
rename from src/app/(manage)/agent/_components/bandwidth-usage.tsx
rename to src/app/[local]/(manage)/agent/_components/bandwidth-usage.tsx
diff --git a/src/app/(manage)/agent/_components/cpu-usage.tsx b/src/app/[local]/(manage)/agent/_components/cpu-usage.tsx
similarity index 100%
rename from src/app/(manage)/agent/_components/cpu-usage.tsx
rename to src/app/[local]/(manage)/agent/_components/cpu-usage.tsx
diff --git a/src/app/(manage)/agent/_components/mem-usage.tsx b/src/app/[local]/(manage)/agent/_components/mem-usage.tsx
similarity index 100%
rename from src/app/(manage)/agent/_components/mem-usage.tsx
rename to src/app/[local]/(manage)/agent/_components/mem-usage.tsx
diff --git a/src/app/(manage)/agent/_components/traffic-usage.tsx b/src/app/[local]/(manage)/agent/_components/traffic-usage.tsx
similarity index 100%
rename from src/app/(manage)/agent/_components/traffic-usage.tsx
rename to src/app/[local]/(manage)/agent/_components/traffic-usage.tsx
diff --git a/src/app/(manage)/agent/page.tsx b/src/app/[local]/(manage)/agent/page.tsx
similarity index 77%
rename from src/app/(manage)/agent/page.tsx
rename to src/app/[local]/(manage)/agent/page.tsx
index bd3df06..ab8fc21 100644
--- a/src/app/(manage)/agent/page.tsx
+++ b/src/app/[local]/(manage)/agent/page.tsx
@@ -1,7 +1,20 @@
-import AgentResizableLayout from "~/app/(manage)/agent/_components/agent-resizable-layout";
+import AgentResizableLayout from "~/app/[local]/(manage)/agent/_components/agent-resizable-layout";
import { api } from "~/trpc/server";
import { redirect, RedirectType } from "next/navigation";
import { AgentStatus } from "@prisma/client";
+import { getTranslations } from "next-intl/server";
+
+export async function generateMetadata({
+ params: { locale },
+}: {
+ params: { locale: string };
+}) {
+ const t = await getTranslations({ locale, namespace: "global_menu" });
+
+ return {
+ title: `${t("agent")} - vortex`,
+ };
+}
export default async function AgentPage() {
const agents = await api.agent.getAll.query(undefined);
diff --git a/src/app/(manage)/dashboard/_components/system-status.tsx b/src/app/[local]/(manage)/dashboard/_components/system-status.tsx
similarity index 92%
rename from src/app/(manage)/dashboard/_components/system-status.tsx
rename to src/app/[local]/(manage)/dashboard/_components/system-status.tsx
index 7604ed1..1146e29 100644
--- a/src/app/(manage)/dashboard/_components/system-status.tsx
+++ b/src/app/[local]/(manage)/dashboard/_components/system-status.tsx
@@ -7,8 +7,10 @@ import { Line, LineChart, ResponsiveContainer, Tooltip } from "recharts";
import { useEffect, useState } from "react";
import { MoveDownIcon, MoveUpIcon } from "lucide-react";
import type { TooltipProps } from "recharts/types/component/Tooltip";
+import { useTranslations } from "use-intl";
export default function SystemStatus() {
+ const t = useTranslations("dashboard-system-status");
const [networks, setNetworks] = useState<
{
upload: number;
@@ -57,8 +59,8 @@ export default function SystemStatus() {
- 下载
- 上传
+ {t("download")}
+ {t("upload")}
{`${download} ${downloadUnit}`}
@@ -84,7 +86,7 @@ export default function SystemStatus() {
-
内存
+
{t("memory")}
{data?.mem.toFixed(2)}%
@@ -93,7 +95,7 @@ export default function SystemStatus() {
-
网络
+
{t("network")}
diff --git a/src/app/(manage)/dashboard/_components/traffic-usage.tsx b/src/app/[local]/(manage)/dashboard/_components/traffic-usage.tsx
similarity index 91%
rename from src/app/(manage)/dashboard/_components/traffic-usage.tsx
rename to src/app/[local]/(manage)/dashboard/_components/traffic-usage.tsx
index e698522..f1c9b10 100644
--- a/src/app/(manage)/dashboard/_components/traffic-usage.tsx
+++ b/src/app/[local]/(manage)/dashboard/_components/traffic-usage.tsx
@@ -5,8 +5,10 @@ import { convertBytesToBestUnit } from "~/lib/utils";
import { api } from "~/trpc/react";
import dayjs from "dayjs";
import { useMemo } from "react";
+import { useTranslations } from "use-intl";
export default function UserTrafficUsage() {
+ const t = useTranslations("dashboard-traffic-usage");
const startDate = dayjs().subtract(7, "day").startOf("day").toDate();
const endDate = dayjs().endOf("day").toDate();
@@ -36,7 +38,7 @@ export default function UserTrafficUsage() {
{`${payload[0]?.payload.date}`}
- 使用流量
+ {t("used_traffic")}
{`${traffic} ${trafficUnit}`}
diff --git a/src/app/(manage)/dashboard/page.tsx b/src/app/[local]/(manage)/dashboard/page.tsx
similarity index 83%
rename from src/app/(manage)/dashboard/page.tsx
rename to src/app/[local]/(manage)/dashboard/page.tsx
index 4a49550..1c32af8 100644
--- a/src/app/(manage)/dashboard/page.tsx
+++ b/src/app/[local]/(manage)/dashboard/page.tsx
@@ -9,14 +9,16 @@ import {
CardTitle,
} from "~/lib/ui/card";
import Link from "next/link";
-import UserTrafficUsage from "~/app/(manage)/dashboard/_components/traffic-usage";
-import SystemStatus from "~/app/(manage)/dashboard/_components/system-status";
+import UserTrafficUsage from "~/app/[local]/(manage)/dashboard/_components/traffic-usage";
+import SystemStatus from "~/app/[local]/(manage)/dashboard/_components/system-status";
import { api } from "~/trpc/server";
import { MoneyInput } from "~/lib/ui/money-input";
import { MoreHorizontalIcon } from "lucide-react";
import Markdown from "react-markdown";
import { Dialog, DialogContent, DialogTrigger } from "~/lib/ui/dialog";
import { type RouterOutputs } from "~/trpc/shared";
+import { getTranslations } from "next-intl/server";
+import { type NamespaceKeys } from "use-intl/core";
export const metadata = {
title: "Dashboard - vortex",
@@ -27,6 +29,7 @@ export default async function Dashboard() {
if (!session) {
return null;
}
+ const t = await getTranslations("dashboard");
const wallet = await api.user.getWallet.query({ id: session.user.id });
const yesterdayBalanceChange = await api.user.getYesterdayBalanceChange.query(
{ id: session.user.id },
@@ -55,8 +58,10 @@ export default async function Dashboard() {
- 流量使用
- 最近7天的流量使用情况
+ {t("traffic_usage")}
+
+ {t("recent_days_traffic_usage")}
+
@@ -71,8 +76,8 @@ export default async function Dashboard() {
- 系统状态
- 系统运行正常
+ {t("system_status")}
+ {t("system_running_normally")}
- 公告
+ {t("announcement")}
{announcement && (
)}
@@ -107,7 +113,7 @@ export default async function Dashboard() {
{announcement}
) : (
- 暂无公告
+ {t("no_announcement")}
)}
@@ -121,17 +127,21 @@ function UserBalance({
userId,
wallet,
yesterdayBalanceChange,
+ t,
}: {
userId: string;
wallet: RouterOutputs["user"]["getWallet"];
yesterdayBalanceChange: RouterOutputs["user"]["getYesterdayBalanceChange"];
+ t: Awaited<
+ ReturnType
>>
+ >;
}) {
return (
<>
- 余额
+ {t("balance")}
@@ -144,7 +154,9 @@ function UserBalance({
className="bg-gradient-to-r from-blue-500 to-green-500 bg-clip-text text-5xl text-transparent"
/>
- 昨日消费
+
+ {t("yesterday_consumption")}
+
{yesterdayBalanceChange?.CONSUMPTION?.toNumber() ?? 0}
@@ -153,7 +165,7 @@ function UserBalance({
- 收益
+ {t("earnings")}
-
昨日收益
+
+ {t("yesterday_earnings")}
+
{yesterdayBalanceChange?.INCOME?.toNumber() ?? 0}
diff --git a/src/app/(manage)/forward/_components/forward-delete.tsx b/src/app/[local]/(manage)/forward/_components/forward-delete.tsx
similarity index 100%
rename from src/app/(manage)/forward/_components/forward-delete.tsx
rename to src/app/[local]/(manage)/forward/_components/forward-delete.tsx
diff --git a/src/app/(manage)/forward/_components/forward-modify-remark.tsx b/src/app/[local]/(manage)/forward/_components/forward-modify-remark.tsx
similarity index 100%
rename from src/app/(manage)/forward/_components/forward-modify-remark.tsx
rename to src/app/[local]/(manage)/forward/_components/forward-modify-remark.tsx
diff --git a/src/app/(manage)/forward/_components/forward-new-form-schema.ts b/src/app/[local]/(manage)/forward/_components/forward-new-form-schema.ts
similarity index 100%
rename from src/app/(manage)/forward/_components/forward-new-form-schema.ts
rename to src/app/[local]/(manage)/forward/_components/forward-new-form-schema.ts
diff --git a/src/app/(manage)/forward/_components/forward-new-gost.tsx b/src/app/[local]/(manage)/forward/_components/forward-new-gost.tsx
similarity index 93%
rename from src/app/(manage)/forward/_components/forward-new-gost.tsx
rename to src/app/[local]/(manage)/forward/_components/forward-new-gost.tsx
index f069921..1c6e1ba 100644
--- a/src/app/(manage)/forward/_components/forward-new-gost.tsx
+++ b/src/app/[local]/(manage)/forward/_components/forward-new-gost.tsx
@@ -13,9 +13,9 @@ import {
SelectTrigger,
SelectValue,
} from "~/lib/ui/select";
-import { type ForwardForm } from "~/app/(manage)/forward/_components/forward-new-form-schema";
+import { type ForwardForm } from "~/app/[local]/(manage)/forward/_components/forward-new-form-schema";
import { GostChannelOptions, GostProtocolOptions } from "~/lib/constants";
-import { WithDescSelector } from "~/app/_components/with-desc-selector";
+import { WithDescSelector } from "~/app/[local]/_components/with-desc-selector";
export default function ForwardNewGost({ form }: { form: ForwardForm }) {
return (
diff --git a/src/app/(manage)/forward/_components/forward-new.tsx b/src/app/[local]/(manage)/forward/_components/forward-new.tsx
similarity index 99%
rename from src/app/(manage)/forward/_components/forward-new.tsx
rename to src/app/[local]/(manage)/forward/_components/forward-new.tsx
index 6f085d3..c7ba6a1 100644
--- a/src/app/(manage)/forward/_components/forward-new.tsx
+++ b/src/app/[local]/(manage)/forward/_components/forward-new.tsx
@@ -29,7 +29,7 @@ import { cn } from "~/lib/utils";
import {
forwardFormSchema,
type ForwardFormValues,
-} from "~/app/(manage)/forward/_components/forward-new-form-schema";
+} from "~/app/[local]/(manage)/forward/_components/forward-new-form-schema";
import {
Dialog,
DialogContent,
diff --git a/src/app/(manage)/forward/_components/forward-reset-traffic.tsx b/src/app/[local]/(manage)/forward/_components/forward-reset-traffic.tsx
similarity index 100%
rename from src/app/(manage)/forward/_components/forward-reset-traffic.tsx
rename to src/app/[local]/(manage)/forward/_components/forward-reset-traffic.tsx
diff --git a/src/app/(manage)/forward/_components/forward-table.tsx b/src/app/[local]/(manage)/forward/_components/forward-table.tsx
similarity index 92%
rename from src/app/(manage)/forward/_components/forward-table.tsx
rename to src/app/[local]/(manage)/forward/_components/forward-table.tsx
index dc7cf0d..6b06e38 100644
--- a/src/app/(manage)/forward/_components/forward-table.tsx
+++ b/src/app/[local]/(manage)/forward/_components/forward-table.tsx
@@ -10,8 +10,8 @@ import {
} from "@tanstack/react-table";
import { api } from "~/trpc/react";
import { Input } from "~/lib/ui/input";
-import Table from "~/app/_components/table";
-import { TableFacetedFilter } from "~/app/_components/table-faceted-filter";
+import Table from "~/app/[local]/_components/table";
+import { TableFacetedFilter } from "~/app/[local]/_components/table-faceted-filter";
import { ForwardStatusOptions } from "~/lib/constants";
import { type ForwardGetAllOutput } from "~/lib/types/trpc";
import { type AgentInfo } from "~/lib/types/agent";
@@ -21,7 +21,7 @@ import { Tooltip, TooltipContent, TooltipTrigger } from "~/lib/ui/tooltip";
import { $Enums, type Agent, ForwardTargetType } from ".prisma/client";
import { Button } from "~/lib/ui/button";
import { MoreHorizontalIcon, XIcon } from "lucide-react";
-import { DataTableViewOptions } from "~/app/_components/table-view-options";
+import { DataTableViewOptions } from "~/app/[local]/_components/table-view-options";
import {
DropdownMenu,
DropdownMenuContent,
@@ -29,13 +29,13 @@ import {
DropdownMenuSeparator,
DropdownMenuTrigger,
} from "~/lib/ui/dropdown-menu";
-import ForwardDelete from "~/app/(manage)/forward/_components/forward-delete";
-import ForwardModifyRemark from "~/app/(manage)/forward/_components/forward-modify-remark";
-import ForwardResetTraffic from "~/app/(manage)/forward/_components/forward-reset-traffic";
-import ForwardNew from "~/app/(manage)/forward/_components/forward-new";
-import ID from "~/app/_components/id";
-import UserColumn from "~/app/_components/user-column";
-import Traffic from "~/app/_components/traffic";
+import ForwardDelete from "~/app/[local]/(manage)/forward/_components/forward-delete";
+import ForwardModifyRemark from "~/app/[local]/(manage)/forward/_components/forward-modify-remark";
+import ForwardResetTraffic from "~/app/[local]/(manage)/forward/_components/forward-reset-traffic";
+import ForwardNew from "~/app/[local]/(manage)/forward/_components/forward-new";
+import ID from "~/app/[local]/_components/id";
+import UserColumn from "~/app/[local]/_components/user-column";
+import Traffic from "~/app/[local]/_components/traffic";
import { useSession } from "next-auth/react";
import { Role } from "@prisma/client";
import ForwardStatus = $Enums.ForwardStatus;
diff --git a/src/app/[local]/(manage)/forward/page.tsx b/src/app/[local]/(manage)/forward/page.tsx
new file mode 100644
index 0000000..9f4cb96
--- /dev/null
+++ b/src/app/[local]/(manage)/forward/page.tsx
@@ -0,0 +1,24 @@
+import ForwardTable from "~/app/[local]/(manage)/forward/_components/forward-table";
+import { getTranslations } from "next-intl/server";
+
+export async function generateMetadata({
+ params: { locale },
+}: {
+ params: { locale: string };
+}) {
+ const t = await getTranslations({ locale, namespace: "global_menu" });
+
+ return {
+ title: `${t("forward")} - vortex`,
+ };
+}
+
+export default async function Forward() {
+ const t = await getTranslations("global_menu");
+ return (
+
+
{t("forward")}
+
+
+ );
+}
diff --git a/src/app/(manage)/layout.tsx b/src/app/[local]/(manage)/layout.tsx
similarity index 92%
rename from src/app/(manage)/layout.tsx
rename to src/app/[local]/(manage)/layout.tsx
index 3e48e62..88a4a10 100644
--- a/src/app/(manage)/layout.tsx
+++ b/src/app/[local]/(manage)/layout.tsx
@@ -1,5 +1,5 @@
import { type ReactNode } from "react";
-import ResizableLayout from "~/app/_components/resizable-layout";
+import ResizableLayout from "~/app/[local]/_components/resizable-layout";
import { cookies } from "next/headers";
import { getServerAuthSession } from "~/server/auth";
import { redirect } from "next/navigation";
diff --git a/src/app/(manage)/loading.tsx b/src/app/[local]/(manage)/loading.tsx
similarity index 100%
rename from src/app/(manage)/loading.tsx
rename to src/app/[local]/(manage)/loading.tsx
diff --git a/src/app/(manage)/network/[networkId]/page.tsx b/src/app/[local]/(manage)/network/[networkId]/page.tsx
similarity index 84%
rename from src/app/(manage)/network/[networkId]/page.tsx
rename to src/app/[local]/(manage)/network/[networkId]/page.tsx
index 6b56085..3c21597 100644
--- a/src/app/(manage)/network/[networkId]/page.tsx
+++ b/src/app/[local]/(manage)/network/[networkId]/page.tsx
@@ -1,5 +1,5 @@
import { api } from "~/trpc/server";
-import NetworkFlow from "~/app/(manage)/network/_components/network-flow";
+import NetworkFlow from "~/app/[local]/(manage)/network/_components/network-flow";
export const metadata = {
title: "组网 - vortex",
diff --git a/src/app/(manage)/network/_components/agent-edge-forward-settings.tsx b/src/app/[local]/(manage)/network/_components/agent-edge-forward-settings.tsx
similarity index 97%
rename from src/app/(manage)/network/_components/agent-edge-forward-settings.tsx
rename to src/app/[local]/(manage)/network/_components/agent-edge-forward-settings.tsx
index 465d029..a719796 100644
--- a/src/app/(manage)/network/_components/agent-edge-forward-settings.tsx
+++ b/src/app/[local]/(manage)/network/_components/agent-edge-forward-settings.tsx
@@ -11,7 +11,7 @@ import {
import { ForwardMethod } from ".prisma/client";
import { useStore } from "zustand";
import { useContext, useMemo, useState } from "react";
-import { NetworkContext } from "~/app/(manage)/network/store/network-store";
+import { NetworkContext } from "~/app/[local]/(manage)/network/store/network-store";
import { type NetworkAgentEdge } from "~/lib/types/agent";
import { Input } from "~/lib/ui/input";
import { z } from "zod";
@@ -27,7 +27,7 @@ import {
import { useForm } from "react-hook-form";
import { zodResolver } from "@hookform/resolvers/zod";
import { cn } from "~/lib/utils";
-import { WithDescSelector } from "~/app/_components/with-desc-selector";
+import { WithDescSelector } from "~/app/[local]/_components/with-desc-selector";
import { ForwardMethodOptions, GostChannelOptions } from "~/lib/constants";
import { api } from "~/trpc/react";
diff --git a/src/app/(manage)/network/_components/agent-edge-test.tsx b/src/app/[local]/(manage)/network/_components/agent-edge-test.tsx
similarity index 98%
rename from src/app/(manage)/network/_components/agent-edge-test.tsx
rename to src/app/[local]/(manage)/network/_components/agent-edge-test.tsx
index 64fdaff..6eb6794 100644
--- a/src/app/(manage)/network/_components/agent-edge-test.tsx
+++ b/src/app/[local]/(manage)/network/_components/agent-edge-test.tsx
@@ -12,7 +12,7 @@ import {
import { useContext, useMemo, useState } from "react";
import { api } from "~/trpc/react";
import { useStore } from "zustand";
-import { NetworkContext } from "~/app/(manage)/network/store/network-store";
+import { NetworkContext } from "~/app/[local]/(manage)/network/store/network-store";
import { cn, isBase64, isJson } from "~/lib/utils";
import { type NetworkExternalNode } from "~/lib/types/agent";
import { useTrack } from "~/lib/hooks/use-track";
diff --git a/src/app/(manage)/network/_components/agent-edge-toolbar.tsx b/src/app/[local]/(manage)/network/_components/agent-edge-toolbar.tsx
similarity index 63%
rename from src/app/(manage)/network/_components/agent-edge-toolbar.tsx
rename to src/app/[local]/(manage)/network/_components/agent-edge-toolbar.tsx
index affd9d2..03c814a 100644
--- a/src/app/(manage)/network/_components/agent-edge-toolbar.tsx
+++ b/src/app/[local]/(manage)/network/_components/agent-edge-toolbar.tsx
@@ -1,7 +1,7 @@
"use client";
import { type NetworkAgentEdge } from "~/lib/types/agent";
-import AgentEdgeTest from "~/app/(manage)/network/_components/agent-edge-test";
-import AgentEdgeForwardSettings from "~/app/(manage)/network/_components/agent-edge-forward-settings";
+import AgentEdgeTest from "~/app/[local]/(manage)/network/_components/agent-edge-test";
+import AgentEdgeForwardSettings from "~/app/[local]/(manage)/network/_components/agent-edge-forward-settings";
export default function AgentEdgeToolbar({
id,
diff --git a/src/app/(manage)/network/_components/agent-edge.tsx b/src/app/[local]/(manage)/network/_components/agent-edge.tsx
similarity index 94%
rename from src/app/(manage)/network/_components/agent-edge.tsx
rename to src/app/[local]/(manage)/network/_components/agent-edge.tsx
index ccc4d1b..6f3b070 100644
--- a/src/app/(manage)/network/_components/agent-edge.tsx
+++ b/src/app/[local]/(manage)/network/_components/agent-edge.tsx
@@ -4,12 +4,12 @@ import {
type EdgeProps,
getBezierPath,
} from "reactflow";
-import AgentEdgeToolbar from "~/app/(manage)/network/_components/agent-edge-toolbar";
+import AgentEdgeToolbar from "~/app/[local]/(manage)/network/_components/agent-edge-toolbar";
import { type NetworkAgentEdge } from "~/lib/types/agent";
import { MoveRightIcon, XIcon } from "lucide-react";
import { useStore } from "zustand";
import { useContext, useMemo } from "react";
-import { NetworkContext } from "~/app/(manage)/network/store/network-store";
+import { NetworkContext } from "~/app/[local]/(manage)/network/store/network-store";
export default function AgentEdge({
id,
diff --git a/src/app/(manage)/network/_components/agent-node.tsx b/src/app/[local]/(manage)/network/_components/agent-node.tsx
similarity index 96%
rename from src/app/(manage)/network/_components/agent-node.tsx
rename to src/app/[local]/(manage)/network/_components/agent-node.tsx
index fad1199..abcb3a2 100644
--- a/src/app/(manage)/network/_components/agent-node.tsx
+++ b/src/app/[local]/(manage)/network/_components/agent-node.tsx
@@ -12,7 +12,7 @@ import { ServerIcon } from "~/lib/icons";
import { type NetworkAgentNode } from "~/lib/types/agent";
import { useStore } from "zustand";
import { useContext } from "react";
-import { NetworkContext } from "~/app/(manage)/network/store/network-store";
+import { NetworkContext } from "~/app/[local]/(manage)/network/store/network-store";
export default function AgentNode(props: NodeProps
) {
const { data } = props;
diff --git a/src/app/(manage)/network/_components/external-node-new.tsx b/src/app/[local]/(manage)/network/_components/external-node-new.tsx
similarity index 97%
rename from src/app/(manage)/network/_components/external-node-new.tsx
rename to src/app/[local]/(manage)/network/_components/external-node-new.tsx
index 65b2e40..c8b684d 100644
--- a/src/app/(manage)/network/_components/external-node-new.tsx
+++ b/src/app/[local]/(manage)/network/_components/external-node-new.tsx
@@ -22,7 +22,7 @@ import {
import { Input } from "~/lib/ui/input";
import React, { useContext } from "react";
import { useStore } from "zustand";
-import { NetworkContext } from "~/app/(manage)/network/store/network-store";
+import { NetworkContext } from "~/app/[local]/(manage)/network/store/network-store";
import { getNewNodePosition, isValidHost, uuid } from "~/lib/utils";
import { useStoreApi } from "reactflow";
diff --git a/src/app/(manage)/network/_components/external-node.tsx b/src/app/[local]/(manage)/network/_components/external-node.tsx
similarity index 100%
rename from src/app/(manage)/network/_components/external-node.tsx
rename to src/app/[local]/(manage)/network/_components/external-node.tsx
diff --git a/src/app/(manage)/network/_components/network-command.tsx b/src/app/[local]/(manage)/network/_components/network-command.tsx
similarity index 98%
rename from src/app/(manage)/network/_components/network-command.tsx
rename to src/app/[local]/(manage)/network/_components/network-command.tsx
index 4e1eaf7..9a9ae69 100644
--- a/src/app/(manage)/network/_components/network-command.tsx
+++ b/src/app/[local]/(manage)/network/_components/network-command.tsx
@@ -16,7 +16,7 @@ import { useStore } from "zustand";
import {
type AgentProps,
NetworkContext,
-} from "~/app/(manage)/network/store/network-store";
+} from "~/app/[local]/(manage)/network/store/network-store";
import { AgentStatus } from "@prisma/client";
import { getNewNodePosition } from "~/lib/utils";
import { useStoreApi } from "reactflow";
diff --git a/src/app/(manage)/network/_components/network-delete.tsx b/src/app/[local]/(manage)/network/_components/network-delete.tsx
similarity index 100%
rename from src/app/(manage)/network/_components/network-delete.tsx
rename to src/app/[local]/(manage)/network/_components/network-delete.tsx
diff --git a/src/app/(manage)/network/_components/network-edit.tsx b/src/app/[local]/(manage)/network/_components/network-edit.tsx
similarity index 97%
rename from src/app/(manage)/network/_components/network-edit.tsx
rename to src/app/[local]/(manage)/network/_components/network-edit.tsx
index cabb3ec..ca73819 100644
--- a/src/app/(manage)/network/_components/network-edit.tsx
+++ b/src/app/[local]/(manage)/network/_components/network-edit.tsx
@@ -12,7 +12,7 @@ import { useContext, useEffect, useState } from "react";
import { api } from "~/trpc/react";
import { Button } from "~/lib/ui/button";
import { Input } from "~/lib/ui/input";
-import { NetworkContext } from "~/app/(manage)/network/store/network-store";
+import { NetworkContext } from "~/app/[local]/(manage)/network/store/network-store";
import { useStore } from "zustand";
import { useRouter } from "next/navigation";
import { Switch } from "~/lib/ui/switch";
diff --git a/src/app/(manage)/network/_components/network-flow.tsx b/src/app/[local]/(manage)/network/_components/network-flow.tsx
similarity index 81%
rename from src/app/(manage)/network/_components/network-flow.tsx
rename to src/app/[local]/(manage)/network/_components/network-flow.tsx
index 5c01dbf..beb41b3 100644
--- a/src/app/(manage)/network/_components/network-flow.tsx
+++ b/src/app/[local]/(manage)/network/_components/network-flow.tsx
@@ -1,7 +1,7 @@
"use client";
import ReactFlow, { Background, Controls, Panel } from "reactflow";
import { useEffect, useRef } from "react";
-import AgentNode from "~/app/(manage)/network/_components/agent-node";
+import AgentNode from "~/app/[local]/(manage)/network/_components/agent-node";
import {
type AgentGetAllOutput,
type NetworkGetOneOutput,
@@ -9,13 +9,13 @@ import {
import {
createNetworkStore,
NetworkContext,
-} from "~/app/(manage)/network/store/network-store";
-import AgentEdge from "~/app/(manage)/network/_components/agent-edge";
-import ExternalNode from "~/app/(manage)/network/_components/external-node";
-import { NetworkCommand } from "~/app/(manage)/network/_components/network-command";
-import NetworkEdit from "~/app/(manage)/network/_components/network-edit";
+} from "~/app/[local]/(manage)/network/store/network-store";
+import AgentEdge from "~/app/[local]/(manage)/network/_components/agent-edge";
+import ExternalNode from "~/app/[local]/(manage)/network/_components/external-node";
+import { NetworkCommand } from "~/app/[local]/(manage)/network/_components/network-command";
+import NetworkEdit from "~/app/[local]/(manage)/network/_components/network-edit";
import "reactflow/dist/style.css";
-import ExternalNodeNew from "~/app/(manage)/network/_components/external-node-new";
+import ExternalNodeNew from "~/app/[local]/(manage)/network/_components/external-node-new";
interface NetworkFlowProps {
agents: AgentGetAllOutput;
diff --git a/src/app/(manage)/network/_components/network-selector.tsx b/src/app/[local]/(manage)/network/_components/network-selector.tsx
similarity index 96%
rename from src/app/(manage)/network/_components/network-selector.tsx
rename to src/app/[local]/(manage)/network/_components/network-selector.tsx
index 780b4d9..bd4727f 100644
--- a/src/app/(manage)/network/_components/network-selector.tsx
+++ b/src/app/[local]/(manage)/network/_components/network-selector.tsx
@@ -16,7 +16,7 @@ import {
import { Popover, PopoverContent, PopoverTrigger } from "~/lib/ui/popover";
import { CheckIcon, ChevronsUpDownIcon } from "lucide-react";
import { useStore } from "zustand";
-import { NetworkContext } from "~/app/(manage)/network/store/network-store";
+import { NetworkContext } from "~/app/[local]/(manage)/network/store/network-store";
import Link from "next/link";
import { type NetworkGetOneOutput } from "~/lib/types/trpc";
diff --git a/src/app/(manage)/network/_components/network-table.tsx b/src/app/[local]/(manage)/network/_components/network-table.tsx
similarity index 95%
rename from src/app/(manage)/network/_components/network-table.tsx
rename to src/app/[local]/(manage)/network/_components/network-table.tsx
index 2d8eb50..1c51f4c 100644
--- a/src/app/(manage)/network/_components/network-table.tsx
+++ b/src/app/[local]/(manage)/network/_components/network-table.tsx
@@ -7,19 +7,19 @@ import {
} from "@tanstack/react-table";
import { Input } from "~/lib/ui/input";
import { type ChangeEvent, useMemo, useState } from "react";
-import Table from "~/app/_components/table";
+import Table from "~/app/[local]/_components/table";
import { api } from "~/trpc/react";
import { type NetworkGetAllOutput } from "~/lib/types/trpc";
-import ID from "~/app/_components/id";
-import UserColumn from "~/app/_components/user-column";
-import NetworkDelete from "~/app/(manage)/network/_components/network-delete";
+import ID from "~/app/[local]/_components/id";
+import UserColumn from "~/app/[local]/_components/user-column";
+import NetworkDelete from "~/app/[local]/(manage)/network/_components/network-delete";
import { Button } from "~/lib/ui/button";
import { ArrowRightIcon, CopyIcon, XIcon } from "lucide-react";
import Link from "next/link";
import { copyToClipboard } from "~/lib/utils";
import { type AgentInfo, type NetworkFlow } from "~/lib/types/agent";
import { Tooltip, TooltipContent, TooltipTrigger } from "~/lib/ui/tooltip";
-import Traffic from "~/app/_components/traffic";
+import Traffic from "~/app/[local]/_components/traffic";
import { ForwardTrafficDimensions } from "~/lib/constants";
export default function NetworkTable({ keyword: k }: { keyword: string }) {
diff --git a/src/app/[local]/(manage)/network/page.tsx b/src/app/[local]/(manage)/network/page.tsx
new file mode 100644
index 0000000..d67dbc3
--- /dev/null
+++ b/src/app/[local]/(manage)/network/page.tsx
@@ -0,0 +1,30 @@
+import "reactflow/dist/style.css";
+import NetworkTable from "~/app/[local]/(manage)/network/_components/network-table";
+import { getTranslations } from "next-intl/server";
+
+export async function generateMetadata({
+ params: { locale },
+}: {
+ params: { locale: string };
+}) {
+ const t = await getTranslations({ locale, namespace: "global_menu" });
+
+ return {
+ title: `${t("network")} - vortex`,
+ };
+}
+
+export default async function NetworksPage({
+ searchParams: { keyword },
+}: {
+ searchParams: { keyword: string };
+}) {
+ return (
+
+ );
+}
diff --git a/src/app/(manage)/network/store/network-store.ts b/src/app/[local]/(manage)/network/store/network-store.ts
similarity index 100%
rename from src/app/(manage)/network/store/network-store.ts
rename to src/app/[local]/(manage)/network/store/network-store.ts
diff --git a/src/app/(manage)/ticket/[ticketId]/page.tsx b/src/app/[local]/(manage)/ticket/[ticketId]/page.tsx
similarity index 88%
rename from src/app/(manage)/ticket/[ticketId]/page.tsx
rename to src/app/[local]/(manage)/ticket/[ticketId]/page.tsx
index 78e1b7c..b14b3bd 100644
--- a/src/app/(manage)/ticket/[ticketId]/page.tsx
+++ b/src/app/[local]/(manage)/ticket/[ticketId]/page.tsx
@@ -1,8 +1,8 @@
import { api } from "~/trpc/server";
import Markdown from "react-markdown";
-import UserColumn from "~/app/_components/user-column";
-import TicketReply from "~/app/(manage)/ticket/_components/ticket-reply";
-import TicketStatusBadge from "~/app/(manage)/ticket/_components/ticket-status-badge";
+import UserColumn from "~/app/[local]/_components/user-column";
+import TicketReply from "~/app/[local]/(manage)/ticket/_components/ticket-reply";
+import TicketStatusBadge from "~/app/[local]/(manage)/ticket/_components/ticket-status-badge";
import dayjs from "dayjs";
export const metadata = {
diff --git a/src/app/(manage)/ticket/_components/markdown-input.tsx b/src/app/[local]/(manage)/ticket/_components/markdown-input.tsx
similarity index 94%
rename from src/app/(manage)/ticket/_components/markdown-input.tsx
rename to src/app/[local]/(manage)/ticket/_components/markdown-input.tsx
index 238e968..d35ffc5 100644
--- a/src/app/(manage)/ticket/_components/markdown-input.tsx
+++ b/src/app/[local]/(manage)/ticket/_components/markdown-input.tsx
@@ -1,6 +1,6 @@
import { Tabs, TabsContent, TabsList, TabsTrigger } from "~/lib/ui/tabs";
import Markdown from "react-markdown";
-import CodeInput from "~/app/_components/code-input";
+import CodeInput from "~/app/[local]/_components/code-input";
interface MarkdownInputProps {
value: string;
diff --git a/src/app/(manage)/ticket/_components/new-ticket-form.tsx b/src/app/[local]/(manage)/ticket/_components/new-ticket-form.tsx
similarity index 96%
rename from src/app/(manage)/ticket/_components/new-ticket-form.tsx
rename to src/app/[local]/(manage)/ticket/_components/new-ticket-form.tsx
index 3f3ddae..b885c5d 100644
--- a/src/app/(manage)/ticket/_components/new-ticket-form.tsx
+++ b/src/app/[local]/(manage)/ticket/_components/new-ticket-form.tsx
@@ -9,7 +9,7 @@ import {
FormMessage,
} from "~/lib/ui/form";
import { Input } from "~/lib/ui/input";
-import MarkdownInput from "~/app/(manage)/ticket/_components/markdown-input";
+import MarkdownInput from "~/app/[local]/(manage)/ticket/_components/markdown-input";
import { Button } from "~/lib/ui/button";
import { useTrack } from "~/lib/hooks/use-track";
import { api } from "~/trpc/react";
diff --git a/src/app/(manage)/ticket/_components/ticket-close.tsx b/src/app/[local]/(manage)/ticket/_components/ticket-close.tsx
similarity index 100%
rename from src/app/(manage)/ticket/_components/ticket-close.tsx
rename to src/app/[local]/(manage)/ticket/_components/ticket-close.tsx
diff --git a/src/app/(manage)/ticket/_components/ticket-reply.tsx b/src/app/[local]/(manage)/ticket/_components/ticket-reply.tsx
similarity index 93%
rename from src/app/(manage)/ticket/_components/ticket-reply.tsx
rename to src/app/[local]/(manage)/ticket/_components/ticket-reply.tsx
index 9e6b48f..7fad6a9 100644
--- a/src/app/(manage)/ticket/_components/ticket-reply.tsx
+++ b/src/app/[local]/(manage)/ticket/_components/ticket-reply.tsx
@@ -1,5 +1,5 @@
"use client";
-import MarkdownInput from "~/app/(manage)/ticket/_components/markdown-input";
+import MarkdownInput from "~/app/[local]/(manage)/ticket/_components/markdown-input";
import { Button } from "~/lib/ui/button";
import { useState } from "react";
import { api } from "~/trpc/react";
diff --git a/src/app/(manage)/ticket/_components/ticket-status-badge.tsx b/src/app/[local]/(manage)/ticket/_components/ticket-status-badge.tsx
similarity index 100%
rename from src/app/(manage)/ticket/_components/ticket-status-badge.tsx
rename to src/app/[local]/(manage)/ticket/_components/ticket-status-badge.tsx
diff --git a/src/app/(manage)/ticket/_components/ticket-table.tsx b/src/app/[local]/(manage)/ticket/_components/ticket-table.tsx
similarity index 91%
rename from src/app/(manage)/ticket/_components/ticket-table.tsx
rename to src/app/[local]/(manage)/ticket/_components/ticket-table.tsx
index 4c48f4f..0bbb715 100644
--- a/src/app/(manage)/ticket/_components/ticket-table.tsx
+++ b/src/app/[local]/(manage)/ticket/_components/ticket-table.tsx
@@ -8,16 +8,16 @@ import {
import { Input } from "~/lib/ui/input";
import * as React from "react";
import { type ChangeEvent, useMemo, useState } from "react";
-import Table from "~/app/_components/table";
+import Table from "~/app/[local]/_components/table";
import { api } from "~/trpc/react";
import { type TicketGetAllOutput } from "~/lib/types/trpc";
-import ID from "~/app/_components/id";
-import UserColumn from "~/app/_components/user-column";
+import ID from "~/app/[local]/_components/id";
+import UserColumn from "~/app/[local]/_components/user-column";
import { Button } from "~/lib/ui/button";
import { XIcon } from "lucide-react";
import Link from "next/link";
-import TicketStatusBadge from "~/app/(manage)/ticket/_components/ticket-status-badge";
-import TicketClose from "~/app/(manage)/ticket/_components/ticket-close";
+import TicketStatusBadge from "~/app/[local]/(manage)/ticket/_components/ticket-status-badge";
+import TicketClose from "~/app/[local]/(manage)/ticket/_components/ticket-close";
export default function TicketTable({ keyword: k }: { keyword: string }) {
const [keyword, setKeyword] = useState(k);
diff --git a/src/app/(manage)/ticket/new/page.tsx b/src/app/[local]/(manage)/ticket/new/page.tsx
similarity index 72%
rename from src/app/(manage)/ticket/new/page.tsx
rename to src/app/[local]/(manage)/ticket/new/page.tsx
index d6da472..3557b8a 100644
--- a/src/app/(manage)/ticket/new/page.tsx
+++ b/src/app/[local]/(manage)/ticket/new/page.tsx
@@ -1,4 +1,4 @@
-import NewTicketForm from "~/app/(manage)/ticket/_components/new-ticket-form";
+import NewTicketForm from "~/app/[local]/(manage)/ticket/_components/new-ticket-form";
export const metadata = {
title: "工单 - vortex",
diff --git a/src/app/[local]/(manage)/ticket/page.tsx b/src/app/[local]/(manage)/ticket/page.tsx
new file mode 100644
index 0000000..217faef
--- /dev/null
+++ b/src/app/[local]/(manage)/ticket/page.tsx
@@ -0,0 +1,30 @@
+import TicketTable from "~/app/[local]/(manage)/ticket/_components/ticket-table";
+import { getTranslations } from "next-intl/server";
+
+export async function generateMetadata({
+ params: { locale },
+}: {
+ params: { locale: string };
+}) {
+ const t = await getTranslations({ locale, namespace: "global_menu" });
+
+ return {
+ title: `${t("ticket")} - vortex`,
+ };
+}
+
+export default async function TicketsPage({
+ searchParams: { keyword },
+}: {
+ searchParams: { keyword: string };
+}) {
+ const t = await getTranslations("global_menu");
+ return (
+
+ );
+}
diff --git a/src/app/(manage)/user/[userId]/_components/balance-log.tsx b/src/app/[local]/(manage)/user/[userId]/_components/balance-log.tsx
similarity index 98%
rename from src/app/(manage)/user/[userId]/_components/balance-log.tsx
rename to src/app/[local]/(manage)/user/[userId]/_components/balance-log.tsx
index 01a09f6..badce1c 100644
--- a/src/app/(manage)/user/[userId]/_components/balance-log.tsx
+++ b/src/app/[local]/(manage)/user/[userId]/_components/balance-log.tsx
@@ -4,7 +4,7 @@ import { ScrollArea } from "~/lib/ui/scroll-area";
import { api } from "~/trpc/react";
import React from "react";
import { formatDate } from "~/lib/utils";
-import SearchEmptyState from "~/app/_components/search-empty-state";
+import SearchEmptyState from "~/app/[local]/_components/search-empty-state";
import { Tooltip, TooltipContent, TooltipTrigger } from "~/lib/ui/tooltip";
import type { RouterOutputs } from "~/trpc/shared";
import { MoneyInput } from "~/lib/ui/money-input";
diff --git a/src/app/(manage)/user/[userId]/_components/balance.tsx b/src/app/[local]/(manage)/user/[userId]/_components/balance.tsx
similarity index 81%
rename from src/app/(manage)/user/[userId]/_components/balance.tsx
rename to src/app/[local]/(manage)/user/[userId]/_components/balance.tsx
index 46a5a14..215e6ae 100644
--- a/src/app/(manage)/user/[userId]/_components/balance.tsx
+++ b/src/app/[local]/(manage)/user/[userId]/_components/balance.tsx
@@ -1,12 +1,12 @@
"use client";
import { Card, CardContent, CardFooter, CardHeader } from "~/lib/ui/card";
-import UpdateBalance from "~/app/(manage)/user/[userId]/_components/update-balance";
+import UpdateBalance from "~/app/[local]/(manage)/user/[userId]/_components/update-balance";
import { useSession } from "next-auth/react";
import { hasPermission } from "~/lib/constants/permission";
-import RechargeBalance from "~/app/(manage)/user/[userId]/_components/recharge-balance";
+import RechargeBalance from "~/app/[local]/(manage)/user/[userId]/_components/recharge-balance";
import { MoneyInput } from "~/lib/ui/money-input";
import { BalanceType } from "@prisma/client";
-import WithdrawalBalance from "~/app/(manage)/user/[userId]/_components/withdrawal-balance";
+import WithdrawalBalance from "~/app/[local]/(manage)/user/[userId]/_components/withdrawal-balance";
interface BalanceProps {
wallet: {
diff --git a/src/app/(manage)/user/[userId]/_components/profile-form.tsx b/src/app/[local]/(manage)/user/[userId]/_components/profile-form.tsx
similarity index 100%
rename from src/app/(manage)/user/[userId]/_components/profile-form.tsx
rename to src/app/[local]/(manage)/user/[userId]/_components/profile-form.tsx
diff --git a/src/app/(manage)/user/[userId]/_components/recharge-balance.tsx b/src/app/[local]/(manage)/user/[userId]/_components/recharge-balance.tsx
similarity index 100%
rename from src/app/(manage)/user/[userId]/_components/recharge-balance.tsx
rename to src/app/[local]/(manage)/user/[userId]/_components/recharge-balance.tsx
diff --git a/src/app/(manage)/user/[userId]/_components/recharge-depay.tsx b/src/app/[local]/(manage)/user/[userId]/_components/recharge-depay.tsx
similarity index 100%
rename from src/app/(manage)/user/[userId]/_components/recharge-depay.tsx
rename to src/app/[local]/(manage)/user/[userId]/_components/recharge-depay.tsx
diff --git a/src/app/(manage)/user/[userId]/_components/update-balance.tsx b/src/app/[local]/(manage)/user/[userId]/_components/update-balance.tsx
similarity index 100%
rename from src/app/(manage)/user/[userId]/_components/update-balance.tsx
rename to src/app/[local]/(manage)/user/[userId]/_components/update-balance.tsx
diff --git a/src/app/(manage)/user/[userId]/_components/withdrawal-balance.tsx b/src/app/[local]/(manage)/user/[userId]/_components/withdrawal-balance.tsx
similarity index 100%
rename from src/app/(manage)/user/[userId]/_components/withdrawal-balance.tsx
rename to src/app/[local]/(manage)/user/[userId]/_components/withdrawal-balance.tsx
diff --git a/src/app/(manage)/user/[userId]/account/page.tsx b/src/app/[local]/(manage)/user/[userId]/account/page.tsx
similarity index 88%
rename from src/app/(manage)/user/[userId]/account/page.tsx
rename to src/app/[local]/(manage)/user/[userId]/account/page.tsx
index b69d957..3bf0369 100644
--- a/src/app/(manage)/user/[userId]/account/page.tsx
+++ b/src/app/[local]/(manage)/user/[userId]/account/page.tsx
@@ -1,7 +1,7 @@
-import { Separator } from "~/lib/ui/separator";
+import { Separator } from "../../../../../../lib/ui/separator";
import { api } from "~/trpc/server";
import { Label } from "~/lib/ui/label";
-import UpdatePasswordDialog from "~/app/_components/update-password-dialog";
+import UpdatePasswordDialog from "~/app/[local]/_components/update-password-dialog";
export default async function UserAccountPage({
params: { userId },
diff --git a/src/app/(manage)/user/[userId]/balance/page.tsx b/src/app/[local]/(manage)/user/[userId]/balance/page.tsx
similarity index 91%
rename from src/app/(manage)/user/[userId]/balance/page.tsx
rename to src/app/[local]/(manage)/user/[userId]/balance/page.tsx
index 1e07e05..a3e05a0 100644
--- a/src/app/(manage)/user/[userId]/balance/page.tsx
+++ b/src/app/[local]/(manage)/user/[userId]/balance/page.tsx
@@ -1,7 +1,7 @@
import { Separator } from "~/lib/ui/separator";
import { api } from "~/trpc/server";
-import Balance from "~/app/(manage)/user/[userId]/_components/balance";
-import BalanceLog from "~/app/(manage)/user/[userId]/_components/balance-log";
+import Balance from "~/app/[local]/(manage)/user/[userId]/_components/balance";
+import BalanceLog from "~/app/[local]/(manage)/user/[userId]/_components/balance-log";
import { Tabs, TabsContent, TabsList, TabsTrigger } from "~/lib/ui/tabs";
import { getServerAuthSession } from "~/server/auth";
import { BalanceType, Role } from "@prisma/client";
diff --git a/src/app/(manage)/user/[userId]/layout.tsx b/src/app/[local]/(manage)/user/[userId]/layout.tsx
similarity index 94%
rename from src/app/(manage)/user/[userId]/layout.tsx
rename to src/app/[local]/(manage)/user/[userId]/layout.tsx
index 3e3abeb..2c90c63 100644
--- a/src/app/(manage)/user/[userId]/layout.tsx
+++ b/src/app/[local]/(manage)/user/[userId]/layout.tsx
@@ -1,6 +1,6 @@
import { Separator } from "~/lib/ui/separator";
import { type ReactNode } from "react";
-import { SidebarNav } from "~/app/_components/sidebar-nav";
+import { SidebarNav } from "~/app/[local]/_components/sidebar-nav";
import type { Metadata } from "next";
export const metadata: Metadata = {
diff --git a/src/app/(manage)/user/[userId]/page.tsx b/src/app/[local]/(manage)/user/[userId]/page.tsx
similarity index 87%
rename from src/app/(manage)/user/[userId]/page.tsx
rename to src/app/[local]/(manage)/user/[userId]/page.tsx
index 33b9b3d..ccded3b 100644
--- a/src/app/(manage)/user/[userId]/page.tsx
+++ b/src/app/[local]/(manage)/user/[userId]/page.tsx
@@ -1,5 +1,5 @@
import { Separator } from "~/lib/ui/separator";
-import ProfileForm from "~/app/(manage)/user/[userId]/_components/profile-form";
+import ProfileForm from "~/app/[local]/(manage)/user/[userId]/_components/profile-form";
import { api } from "~/trpc/server";
export default async function SettingProfilePage({
diff --git a/src/app/_components/code-input.tsx b/src/app/[local]/_components/code-input.tsx
similarity index 97%
rename from src/app/_components/code-input.tsx
rename to src/app/[local]/_components/code-input.tsx
index e584e28..d6e680f 100644
--- a/src/app/_components/code-input.tsx
+++ b/src/app/[local]/_components/code-input.tsx
@@ -12,6 +12,7 @@ import { linter, lintGutter } from "@codemirror/lint";
import { ClipboardTypeIcon } from "lucide-react";
import { cn } from "~/lib/utils";
import { Tooltip, TooltipContent, TooltipTrigger } from "~/lib/ui/tooltip";
+import { useTranslations } from "use-intl";
interface CodeInputProps {
className?: string;
@@ -36,7 +37,7 @@ const CodeInput: React.FC = ({
}) => {
const [code, setCode] = useState("");
const [extensions, setExtensions] = useState([]);
-
+ const t = useTranslations("global_code-input");
useEffect(() => {
if (language === "json") {
try {
@@ -156,7 +157,7 @@ const CodeInput: React.FC = ({
/>
- 格式化
+ {t("format")}
diff --git a/src/app/_components/faceted-filter.tsx b/src/app/[local]/_components/faceted-filter.tsx
similarity index 100%
rename from src/app/_components/faceted-filter.tsx
rename to src/app/[local]/_components/faceted-filter.tsx
diff --git a/src/app/_components/id.tsx b/src/app/[local]/_components/id.tsx
similarity index 89%
rename from src/app/_components/id.tsx
rename to src/app/[local]/_components/id.tsx
index 246ee27..c6af473 100644
--- a/src/app/_components/id.tsx
+++ b/src/app/[local]/_components/id.tsx
@@ -4,6 +4,7 @@ import { CopyIcon } from "lucide-react";
import { cn, copyToClipboard } from "~/lib/utils";
import { Tooltip, TooltipContent, TooltipTrigger } from "~/lib/ui/tooltip";
import dayjs from "dayjs";
+import { useTranslations } from "use-intl";
interface IDProps {
id: string;
@@ -13,6 +14,7 @@ interface IDProps {
export default function ID({ id, createdAt, copy = true }: IDProps) {
if (!id) return null;
+ const t = useTranslations("global_id");
const shortId = id.length > 10 ? id.slice(0, 4) + " ... " + id.slice(-6) : id;
return (
@@ -35,7 +37,7 @@ export default function ID({ id, createdAt, copy = true }: IDProps) {
{createdAt && (
- 创建于 {dayjs(createdAt).locale("zh-cn").fromNow()}
+ {t("created-at")} {dayjs(createdAt).locale("zh-cn").fromNow()}
)}
diff --git a/src/app/_components/loading.tsx b/src/app/[local]/_components/loading.tsx
similarity index 100%
rename from src/app/_components/loading.tsx
rename to src/app/[local]/_components/loading.tsx
diff --git a/src/app/[local]/_components/locale-switcher.tsx b/src/app/[local]/_components/locale-switcher.tsx
new file mode 100644
index 0000000..0328179
--- /dev/null
+++ b/src/app/[local]/_components/locale-switcher.tsx
@@ -0,0 +1,52 @@
+"use client";
+import { useLocale, useTranslations } from "use-intl";
+import { locales } from "~/lib/constants";
+import { usePathname, useRouter } from "~/navigation";
+import { useTransition } from "react";
+import {
+ DropdownMenu,
+ DropdownMenuCheckboxItem,
+ DropdownMenuContent,
+ DropdownMenuTrigger,
+} from "~/lib/ui/dropdown-menu";
+import { Button } from "~/lib/ui/button";
+import { LanguagesIcon } from "lucide-react";
+
+export default function LocaleSwitcher() {
+ const t = useTranslations("global_locale-switcher");
+ const locale = useLocale();
+
+ const router = useRouter();
+ const [isPending, startTransition] = useTransition();
+ const pathname = usePathname();
+
+ function handleLocaleChange(nextLocale: string) {
+ startTransition(() => {
+ router.replace(pathname, { locale: nextLocale });
+ });
+ }
+
+ return (
+
+
+
+
+ Toggle local
+
+
+
+ {locales.map((option) => (
+ handleLocaleChange(option)}
+ disabled={isPending}
+ className="cursor-pointer"
+ >
+ {t("locale", { locale: option })}
+
+ ))}
+
+
+ );
+}
diff --git a/src/app/_components/logo.tsx b/src/app/[local]/_components/logo.tsx
similarity index 100%
rename from src/app/_components/logo.tsx
rename to src/app/[local]/_components/logo.tsx
diff --git a/src/app/_components/menu.tsx b/src/app/[local]/_components/menu.tsx
similarity index 69%
rename from src/app/_components/menu.tsx
rename to src/app/[local]/_components/menu.tsx
index f68a7fc..2c46e23 100644
--- a/src/app/_components/menu.tsx
+++ b/src/app/[local]/_components/menu.tsx
@@ -4,11 +4,11 @@ import Link from "next/link";
import { Button, buttonVariants } from "~/lib/ui/button";
import { cn, comparePath } from "~/lib/utils";
import { usePathname } from "next/navigation";
-import { ThemeChange } from "~/app/_components/theme-provider";
-import Logo from "~/app/_components/logo";
+import { ThemeChange } from "~/app/[local]/_components/theme-provider";
+import Logo from "~/app/[local]/_components/logo";
import { type Menu } from "~/lib/types";
import { Tooltip, TooltipContent, TooltipTrigger } from "~/lib/ui/tooltip";
-import { type CSSProperties, Fragment, type ReactNode } from "react";
+import { type CSSProperties, Fragment, type ReactNode, useMemo } from "react";
import { Separator } from "~/lib/ui/separator";
import {
DropdownMenu,
@@ -20,6 +20,8 @@ import {
import { Avatar, AvatarFallback, AvatarImage } from "~/lib/ui/avatar";
import { useSession } from "next-auth/react";
import { env } from "~/env";
+import LocaleSwitcher from "~/app/[local]/_components/locale-switcher";
+import { type MessageKeys, useLocale, useTranslations } from "use-intl";
interface MenuProps {
menus: Menu[];
@@ -28,24 +30,44 @@ interface MenuProps {
style?: CSSProperties;
}
+type MenuMessagesType = MessageKeys
;
+
export default function Menu({
menus,
isCollapsed = false,
className,
style,
}: MenuProps) {
+ const t = useTranslations("global_menu");
+ const local = useLocale();
const pathName = usePathname();
- const menusWithActive = menus.map((menu) => {
- return {
- ...menu,
- active: menu.href ? false : comparePath(pathName, menu.href!),
- };
- });
+ const menusWithActive = useMemo(() => {
+ const removedLocalPathName = pathName.replace(`/${local}`, "");
+ return menus.map((menu) => {
+ return {
+ ...menu,
+ active: menu.href
+ ? comparePath(removedLocalPathName, menu.href)
+ : false,
+ children: menu.children?.map((child) => {
+ return {
+ ...child,
+ active: child.href
+ ? comparePath(removedLocalPathName, child.href)
+ : false,
+ };
+ }),
+ };
+ });
+ }, [local, menus, pathName]);
- const renderCollapsedMenu = (menu: Menu, index: number): ReactNode => {
+ const renderCollapsedMenu = (
+ menu: Menu & { active: boolean },
+ index: number,
+ ): ReactNode => {
if (menu.href) {
- const active = comparePath(pathName, menu.href);
+ const active = menu.active;
return (
@@ -63,11 +85,13 @@ export default function Menu({
)}
>
{menu.icon && }
- {menu.title}
+
+ {t(menu.title as MenuMessagesType)}
+
- {menu.title}
+ {t(menu.title as MenuMessagesType)}
);
@@ -76,15 +100,21 @@ export default function Menu({
{menu.children?.map((child, index) => {
- return renderCollapsedMenu(child, index);
+ return renderCollapsedMenu(
+ child as Menu & { active: boolean },
+ index,
+ );
})}
);
};
- const renderMenu = (menu: Menu, index: number): ReactNode => {
+ const renderMenu = (
+ menu: Menu & { active: boolean },
+ index: number,
+ ): ReactNode => {
if (menu.href) {
- const active = comparePath(pathName, menu.href);
+ const active = menu.active;
return (
{menu.icon && }
- {menu.title}
+ {t(menu.title as MenuMessagesType)}
);
}
return (
- {menu.title}
+
+ {t(menu.title as MenuMessagesType)}
+
{menu.children?.map((child, index) => {
- return renderMenu(child, index);
+ return renderMenu(child as Menu & { active: boolean }, index);
})}
);
@@ -139,15 +171,26 @@ export default function Menu({
-
-
+
+
+
+
+
{!isCollapsed &&
}
);
}
-function UserProfile({ isCollapsed = false }: { isCollapsed?: boolean }) {
+function UserProfile({
+ isCollapsed = false,
+ t,
+}: {
+ isCollapsed?: boolean;
+ t: ReturnType
;
+}) {
const { data: session } = useSession();
return (
@@ -183,7 +226,7 @@ function UserProfile({ isCollapsed = false }: { isCollapsed?: boolean }) {
className="flex w-full items-center"
>
- 个人中心
+ {t("personal-center")}
@@ -192,14 +235,14 @@ function UserProfile({ isCollapsed = false }: { isCollapsed?: boolean }) {
className="flex w-full items-center"
>
- 余额
+ {t("balance")}
- 退出
+ {t("logout")}
@@ -209,7 +252,7 @@ function UserProfile({ isCollapsed = false }: { isCollapsed?: boolean }) {
function Version() {
return (
-
+
{
column?: Column
;
diff --git a/src/app/_components/table-view-options.tsx b/src/app/[local]/_components/table-view-options.tsx
similarity index 100%
rename from src/app/_components/table-view-options.tsx
rename to src/app/[local]/_components/table-view-options.tsx
diff --git a/src/app/_components/table.tsx b/src/app/[local]/_components/table.tsx
similarity index 95%
rename from src/app/_components/table.tsx
rename to src/app/[local]/_components/table.tsx
index 4e6ea3d..9afec94 100644
--- a/src/app/_components/table.tsx
+++ b/src/app/[local]/_components/table.tsx
@@ -10,6 +10,7 @@ import {
import { flexRender, type Table as ReactTable } from "@tanstack/react-table";
import { DataTablePagination } from "~/lib/ui/pagination";
import { LoaderIcon } from "lucide-react";
+import { useTranslations } from "use-intl";
export default function Table({
table,
@@ -18,6 +19,7 @@ export default function Table({
table: ReactTable;
isLoading: boolean;
}) {
+ const t = useTranslations("global_table");
return (
<>
@@ -74,7 +76,7 @@ export default function Table
({
colSpan={table.getAllColumns().length}
className="h-24 text-center"
>
- 无数据
+ {t("no-data")}
)}
diff --git a/src/app/_components/theme-provider.tsx b/src/app/[local]/_components/theme-provider.tsx
similarity index 100%
rename from src/app/_components/theme-provider.tsx
rename to src/app/[local]/_components/theme-provider.tsx
diff --git a/src/app/_components/traffic-usage-month.tsx b/src/app/[local]/_components/traffic-usage-month.tsx
similarity index 100%
rename from src/app/_components/traffic-usage-month.tsx
rename to src/app/[local]/_components/traffic-usage-month.tsx
diff --git a/src/app/_components/traffic.tsx b/src/app/[local]/_components/traffic.tsx
similarity index 95%
rename from src/app/_components/traffic.tsx
rename to src/app/[local]/_components/traffic.tsx
index c7c1086..8720f9a 100644
--- a/src/app/_components/traffic.tsx
+++ b/src/app/[local]/_components/traffic.tsx
@@ -5,7 +5,7 @@ import {
HoverCardTrigger,
} from "~/lib/ui/hover-card";
import { MoveDownIcon, MoveUpIcon } from "lucide-react";
-import TrafficUsageMonth from "~/app/_components/traffic-usage-month";
+import TrafficUsageMonth from "~/app/[local]/_components/traffic-usage-month";
import { type ForwardTrafficDimensions } from "~/lib/types";
interface TrafficProps {
diff --git a/src/app/_components/update-password-dialog.tsx b/src/app/[local]/_components/update-password-dialog.tsx
similarity index 88%
rename from src/app/_components/update-password-dialog.tsx
rename to src/app/[local]/_components/update-password-dialog.tsx
index ee8454b..2c188d1 100644
--- a/src/app/_components/update-password-dialog.tsx
+++ b/src/app/[local]/_components/update-password-dialog.tsx
@@ -5,7 +5,7 @@ import {
DialogTitle,
DialogTrigger,
} from "~/lib/ui/dialog";
-import UpdatePasswordForm from "~/app/_components/update-password-form";
+import UpdatePasswordForm from "~/app/[local]/_components/update-password-form";
import { Button } from "~/lib/ui/button";
export default function UpdatePasswordDialog({
diff --git a/src/app/_components/update-password-form.tsx b/src/app/[local]/_components/update-password-form.tsx
similarity index 100%
rename from src/app/_components/update-password-form.tsx
rename to src/app/[local]/_components/update-password-form.tsx
diff --git a/src/app/_components/user-column.tsx b/src/app/[local]/_components/user-column.tsx
similarity index 100%
rename from src/app/_components/user-column.tsx
rename to src/app/[local]/_components/user-column.tsx
diff --git a/src/app/_components/welcome.tsx b/src/app/[local]/_components/welcome.tsx
similarity index 97%
rename from src/app/_components/welcome.tsx
rename to src/app/[local]/_components/welcome.tsx
index 8ceb83f..52a718e 100644
--- a/src/app/_components/welcome.tsx
+++ b/src/app/[local]/_components/welcome.tsx
@@ -1,10 +1,16 @@
import { cn } from "~/lib/utils";
import Image from "next/image";
-export default function Welcome({ className }: { className?: string }) {
+export default function Welcome({
+ className,
+ welcome,
+}: {
+ className?: string;
+ welcome: string;
+}) {
return (
-
Welcome
+
{welcome}
= {
default: {
- heading: "发生错误",
+ heading: t("an_error_occurred"),
},
configuration: {
- heading: "服务器错误",
+ heading: t("server_error"),
message: (
-
服务器配置有问题。
-
检查服务器日志以获取更多信息。
+
{t("server_misconfigured")}
+
{t("check_server_logs_for_more_info")}
),
},
accessdenied: {
- heading: "拒绝访问",
+ heading: t("access_denied"),
message: (
-
您没有登录权限。
+
{t("not_authorized_to_signin")}
),
},
verification: {
- heading: "无法登录",
+ heading: t("unable_to_signin"),
message: (
-
登录链接不再有效。
-
它可能已经被使用过或者可能已经过期。
+
{t("signin_link_no_longer_valid")}
+
{t("it_may_have_been_used_or_expired")}
),
},
signin: {
- heading: "无法登录",
+ heading: t("unable_to_signin"),
message: (
-
检查您提供的详细信息是否正确。
+
{t("check_details_provided_are_correct")}
),
},
oauthsignin: {
- heading: "无法登录",
+ heading: t("unable_to_signin"),
message: (
-
尝试使用其他帐户登录。
+
{t("try_signing_in_with_different_account")}
),
},
oauthcallback: {
- heading: "无法登录",
+ heading: t("unable_to_signin"),
message: (
-
尝试使用其他帐户登录。
+
{t("try_signing_in_with_different_account")}
),
},
oauthcreateaccount: {
- heading: "无法登录",
+ heading: t("unable_to_signin"),
message: (
-
尝试使用其他帐户登录。
+
{t("try_signing_in_with_different_account")}
),
},
emailcreateaccount: {
- heading: "无法登录",
+ heading: t("unable_to_signin"),
message: (
-
尝试使用其他帐户登录。
+
{t("try_signing_in_with_different_account")}
),
},
callback: {
- heading: "无法登录",
+ heading: t("unable_to_signin"),
message: (
-
尝试使用其他帐户登录。
+
{t("try_signing_in_with_different_account")}
),
},
oauthaccountnotlinked: {
- heading: "无法登录",
+ heading: t("unable_to_signin"),
message: (
-
要确认您的身份,请使用您最初使用的同一帐户登录。
+
{t("oauth_account_not_linked")}
),
},
emailsignin: {
- heading: "无法登录",
+ heading: t("unable_to_signin"),
message: (
-
电子邮件无法发送。
+
{t("email_could_not_be_sent")}
),
},
credentialssignin: {
- heading: "无法登录",
+ heading: t("unable_to_signin"),
message: (
-
请检查您提供的信息是否正确。
+
{t("check_information_provided_is_correct")}
),
},
sessionrequired: {
- heading: "拒绝访问",
+ heading: t("access_denied"),
message: (
-
请登录才能访问此页面。
+
{t("please_signin_to_access_this_page")}
),
},
userbanned: {
- heading: "用户已禁用",
+ heading: t("user_disabled"),
message: (
-
您的帐户已被禁用。
+
{t("your_account_has_been_disabled")}
),
},
@@ -157,7 +159,7 @@ export default function AuthErrorPage({
{heading}
{message}
- 返回登录
+ {t("back_to_signin")}
diff --git a/src/app/auth/layout.tsx b/src/app/[local]/auth/layout.tsx
similarity index 72%
rename from src/app/auth/layout.tsx
rename to src/app/[local]/auth/layout.tsx
index b199e60..5fb8e6a 100644
--- a/src/app/auth/layout.tsx
+++ b/src/app/[local]/auth/layout.tsx
@@ -1,5 +1,7 @@
import React, { type ReactNode } from "react";
-import Logo from "~/app/_components/logo";
+import Logo from "~/app/[local]/_components/logo";
+import LocaleSwitcher from "~/app/[local]/_components/locale-switcher";
+import { ThemeChange } from "~/app/[local]/_components/theme-provider";
export default function SigninLayout({ children }: { children: ReactNode }) {
return (
@@ -21,7 +23,13 @@ export default function SigninLayout({ children }: { children: ReactNode }) {
- {children}
+
>
);
diff --git a/src/app/auth/new/page.tsx b/src/app/[local]/auth/new/page.tsx
similarity index 68%
rename from src/app/auth/new/page.tsx
rename to src/app/[local]/auth/new/page.tsx
index febb588..e9c2505 100644
--- a/src/app/auth/new/page.tsx
+++ b/src/app/[local]/auth/new/page.tsx
@@ -1,8 +1,9 @@
import { redirect, RedirectType } from "next/navigation";
import React from "react";
-import UpdatePasswordForm from "~/app/_components/update-password-form";
+import UpdatePasswordForm from "~/app/[local]/_components/update-password-form";
import { getServerAuthSession } from "~/server/auth";
import Link from "next/link";
+import { getTranslations } from "next-intl/server";
export default async function NewUserPage() {
const session = await getServerAuthSession();
@@ -15,20 +16,24 @@ export default async function NewUserPage() {
redirect("/", RedirectType.replace);
}
+ const t = await getTranslations("auth-new");
+
return (
-
创建密码
+
+ {t("create_password")}
+
- 为您的账户创建一个密码,以便您可以使用电子邮件地址和密码登录。
+ {t("create_password_tips")}
- 不设置密码,直接登录
+ {t("skip_password_tips")}
);
diff --git a/src/app/auth/signin/credential/page.tsx b/src/app/[local]/auth/signin/credential/page.tsx
similarity index 88%
rename from src/app/auth/signin/credential/page.tsx
rename to src/app/[local]/auth/signin/credential/page.tsx
index 1c864be..183d65e 100644
--- a/src/app/auth/signin/credential/page.tsx
+++ b/src/app/[local]/auth/signin/credential/page.tsx
@@ -15,6 +15,7 @@ import {
FormMessage,
} from "~/lib/ui/form";
import { signIn } from "next-auth/react";
+import { useTranslations } from "use-intl";
const credentialSignInFormSchema = z.object({
username: z.string().email("请输入正确的邮箱地址"),
@@ -26,6 +27,7 @@ export default function SigninCredentialPage({
}: {
searchParams: { callbackUrl: string };
}) {
+ const t = useTranslations("auth-signin-credential");
const form = useForm>({
resolver: zodResolver(credentialSignInFormSchema),
});
@@ -48,11 +50,13 @@ export default function SigninCredentialPage({
}
className="absolute right-4 top-4 md:right-8 md:top-8"
>
- 返回注册
+ {t("back_to_signup")}
-
密码登录
+
+ {t("password_signin")}
+
- 继续即表示您同意我们的{" "}
+ {t("by_continuing_you_agree_to_our")}{" "}
- 服务条款
+ {t("terms_of_service")}
{" "}
- 和{" "}
+ {t("and")}{" "}
- 隐私政策
+ {t("privacy_policy")}
diff --git a/src/app/auth/signin/page.tsx b/src/app/[local]/auth/signin/page.tsx
similarity index 89%
rename from src/app/auth/signin/page.tsx
rename to src/app/[local]/auth/signin/page.tsx
index 421244d..0f2aded 100644
--- a/src/app/auth/signin/page.tsx
+++ b/src/app/[local]/auth/signin/page.tsx
@@ -12,6 +12,7 @@ import { redirect } from "next/navigation";
import { RedirectType } from "next/dist/client/components/redirect";
import { api } from "~/trpc/server";
import Image from "next/image";
+import { getTranslations } from "next-intl/server";
export default async function SigninPage({
searchParams: { error, callbackUrl },
@@ -21,6 +22,7 @@ export default async function SigninPage({
if (error) {
redirect(`/auth/error?error=${error}`, RedirectType.replace);
}
+ const t = await getTranslations("auth-signin");
const { ENABLE_REGISTER } = await api.publicSystem.getConfig.query({
key: "ENABLE_REGISTER",
});
@@ -36,7 +38,7 @@ export default async function SigninPage({
}
className="absolute right-4 top-4 md:right-8 md:top-8"
>
- 密码登录
+ {t("password_signin")}
);
@@ -53,7 +55,7 @@ export default async function SigninPage({
height={200}
/>
- 已关闭注册!
+ {t("registration_closed")}!
>
@@ -86,7 +88,7 @@ export default async function SigninPage({
- 继续
+ {t("continue")}
@@ -124,7 +126,7 @@ export default async function SigninPage({
{provider.id === "google" && (
)}
- {provider.name} 登录
+ {provider.name} {t("signin")}
);
@@ -137,10 +139,10 @@ export default async function SigninPage({
- 创建一个账号
+ {t("create_an_account")}
- 输入你的邮箱来登录或者注册一个账号
+ {t("enter_email_signin_or_register")}
@@ -151,26 +153,26 @@ export default async function SigninPage({
- 或者使用
+ {t("or_use")}
- 继续即表示您同意我们的{" "}
+ {t("by_continuing_you_agree_to_our")}{" "}
- 服务条款
+ {t("terms_of_service")}
{" "}
- 和{" "}
+ {t("and")}{" "}
- 隐私政策
+ {t("privacy_policy")}
diff --git a/src/app/auth/signout/page.tsx b/src/app/[local]/auth/signout/page.tsx
similarity index 79%
rename from src/app/auth/signout/page.tsx
rename to src/app/[local]/auth/signout/page.tsx
index 052c6f4..22c36f1 100644
--- a/src/app/auth/signout/page.tsx
+++ b/src/app/[local]/auth/signout/page.tsx
@@ -1,19 +1,21 @@
"use client";
import { Button } from "~/lib/ui/button";
import { signOut } from "next-auth/react";
+import { useTranslations } from "use-intl";
export default function SignOutPage() {
+ const t = useTranslations("auth-signout");
return (
- 确认退出登录?
+ {t("confirm_sign_out")}
signOut({ callbackUrl: "/" })}
data-umami-event="signout-button"
>
- 确认
+ {t("confirm")}
diff --git a/src/app/auth/verify/page.tsx b/src/app/[local]/auth/verify/page.tsx
similarity index 100%
rename from src/app/auth/verify/page.tsx
rename to src/app/[local]/auth/verify/page.tsx
diff --git a/src/app/layout.tsx b/src/app/[local]/layout.tsx
similarity index 59%
rename from src/app/layout.tsx
rename to src/app/[local]/layout.tsx
index 23a2b64..1c5f638 100644
--- a/src/app/layout.tsx
+++ b/src/app/[local]/layout.tsx
@@ -4,10 +4,13 @@ import { Inter } from "next/font/google";
import { cookies } from "next/headers";
import { TRPCReactProvider } from "~/trpc/react";
-import { ThemeProvider } from "~/app/_components/theme-provider";
+import { ThemeProvider } from "~/app/[local]/_components/theme-provider";
import { Toaster } from "~/lib/ui/toaster";
import Script from "next/script";
import { env } from "~/env";
+import { type ReactNode } from "react";
+import { NextIntlClientProvider } from "next-intl";
+import { getMessages, unstable_setRequestLocale } from "next-intl/server";
const inter = Inter({
subsets: ["latin"],
@@ -20,13 +23,18 @@ export const metadata = {
icons: [{ rel: "icon", url: "/favicon.ico" }],
};
-export default function RootLayout({
+export default async function RootLayout({
children,
+ params: { locale },
}: {
- children: React.ReactNode;
+ children: ReactNode;
+ params: { locale: string };
}) {
+ unstable_setRequestLocale(locale);
+ const messages = await getMessages({ locale });
+
return (
-
+
-
- {children}
-
-
+
+
+ {children}
+
+
+
{env.NEXT_PUBLIC_UMAMI && env.NEXT_PUBLIC_UMAMI_ID && (
diff --git a/src/app/[local]/page.tsx b/src/app/[local]/page.tsx
new file mode 100644
index 0000000..f5c7bc2
--- /dev/null
+++ b/src/app/[local]/page.tsx
@@ -0,0 +1,32 @@
+import { getServerAuthSession } from "~/server/auth";
+import Logo from "~/app/[local]/_components/logo";
+import Welcome from "~/app/[local]/_components/welcome";
+import Link from "next/link";
+import LocaleSwitcher from "~/app/[local]/_components/locale-switcher";
+import { getTranslations } from "next-intl/server";
+import { ThemeChange } from "~/app/[local]/_components/theme-provider";
+
+export default async function Home() {
+ const session = await getServerAuthSession();
+ const t = await getTranslations("index");
+
+ return (
+
+
+
+
+
+
+ {session ? (
+
+ {t("Dashboard")}
+
+ ) : (
+
+ {t("Sign In")}
+
+ )}
+
+
+ );
+}
diff --git a/src/app/page.tsx b/src/app/page.tsx
index 3c279f9..77d15e3 100644
--- a/src/app/page.tsx
+++ b/src/app/page.tsx
@@ -1,26 +1,5 @@
-import { getServerAuthSession } from "~/server/auth";
-import Logo from "~/app/_components/logo";
-import Welcome from "~/app/_components/welcome";
-import Link from "next/link";
+import { redirect } from "next/navigation";
-export default async function Home() {
- const session = await getServerAuthSession();
-
- return (
-
-
-
-
- {session ? (
-
- Dashboard
-
- ) : (
-
- Sign In
-
- )}
-
-
- );
+export default function RootPage() {
+ redirect("/en");
}
diff --git a/src/i18n.ts b/src/i18n.ts
new file mode 100644
index 0000000..b7f89e5
--- /dev/null
+++ b/src/i18n.ts
@@ -0,0 +1,14 @@
+import { notFound } from "next/navigation";
+import { getRequestConfig } from "next-intl/server";
+
+// Can be imported from a shared config
+const locales = ["en", "zh"];
+
+export default getRequestConfig(async ({ locale }) => {
+ // Validate that the incoming `locale` parameter is valid
+ if (!locales.includes(locale)) notFound();
+
+ return {
+ messages: (await import(`../messages/${locale}.json`)).default,
+ };
+});
diff --git a/src/lib/constants/custom-component-config.ts b/src/lib/constants/custom-component-config.ts
index 3e7520f..4ed5920 100644
--- a/src/lib/constants/custom-component-config.ts
+++ b/src/lib/constants/custom-component-config.ts
@@ -1,10 +1,13 @@
"use client";
import type { ConfigSchema } from "~/lib/types";
import dynamic from "next/dynamic";
-import { TrafficPriceSkeleton } from "~/app/(manage)/admin/config/_components/traffic-price-config";
+import { TrafficPriceSkeleton } from "~/app/[local]/(manage)/admin/config/_components/traffic-price-config";
const TrafficPriceConfig = dynamic(
- () => import("~/app/(manage)/admin/config/_components/traffic-price-config"),
+ () =>
+ import(
+ "~/app/[local]/(manage)/admin/config/_components/traffic-price-config"
+ ),
{
ssr: false,
loading: TrafficPriceSkeleton,
diff --git a/src/lib/constants/index.ts b/src/lib/constants/index.ts
index 51b9a6e..12743f5 100644
--- a/src/lib/constants/index.ts
+++ b/src/lib/constants/index.ts
@@ -5,6 +5,8 @@ import {
XSquareIcon,
} from "lucide-react";
+export const locales = ["en", "zh"] as const;
+
export const Regexps = {
ipv4: /^([0-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])(\.([0-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])){3}$/,
ipv6: /(^(?:(?:[0-9A-Fa-f]{1,4}:){7}[0-9A-Fa-f]{1,4}|(([0-9A-Fa-f]{1,4}:){6}:[0-9A-Fa-f]{1,4})|(([0-9A-Fa-f]{1,4}:){5}:([0-9A-Fa-f]{1,4}:)?[0-9A-Fa-f]{1,4})|(([0-9A-Fa-f]{1,4}:){4}:([0-9A-Fa-f]{1,4}:){0,2}[0-9A-Fa-f]{1,4})|(([0-9A-Fa-f]{1,4}:){3}:([0-9A-Fa-f]{1,4}:){0,3}[0-9A-Fa-f]{1,4})|(([0-9A-Fa-f]{1,4}:){2}:([0-9A-Fa-f]{1,4}:){0,4}[0-9A-Fa-f]{1,4})|(([0-9A-Fa-f]{1,4}:){6}((\b((25[0-5])|(1\d{2})|(2[0-4]\d)|(\d{1,2}))\b)\.){3}(\b((25[0-5])|(1\d{2})|(2[0-4]\d)|(\d{1,2}))\b))|(([0-9A-Fa-f]{1,4}:){0,5}:((\b((25[0-5])|(1\d{2})|(2[0-4]\d)|(\d{1,2}))\b)\.){3}(\b((25[0-5])|(1\d{2})|(2[0-4]\d)|(\d{1,2}))\b))|(::([0-9A-Fa-f]{1,4}:){0,5}((\b((25[0-5])|(1\d{2})|(2[0-4]\d)|(\d{1,2}))\b)\.){3}(\b((25[0-5])|(1\d{2})|(2[0-4]\d)|(\d{1,2}))\b))|([0-9A-Fa-f]{1,4}::([0-9A-Fa-f]{1,4}:){0,5}[0-9A-Fa-f]{1,4})|(::([0-9A-Fa-f]{1,4}:){0,6}[0-9A-Fa-f]{1,4})|(([0-9A-Fa-f]{1,4}:){1,7}:))$)|(^\[(?:(?:[0-9A-Fa-f]{1,4}:){7}[0-9A-Fa-f]{1,4}|(([0-9A-Fa-f]{1,4}:){6}:[0-9A-Fa-f]{1,4})|(([0-9A-Fa-f]{1,4}:){5}:([0-9A-Fa-f]{1,4}:)?[0-9A-Fa-f]{1,4})|(([0-9A-Fa-f]{1,4}:){4}:([0-9A-Fa-f]{1,4}:){0,2}[0-9A-Fa-f]{1,4})|(([0-9A-Fa-f]{1,4}:){3}:([0-9A-Fa-f]{1,4}:){0,3}[0-9A-Fa-f]{1,4})|(([0-9A-Fa-f]{1,4}:){2}:([0-9A-Fa-f]{1,4}:){0,4}[0-9A-Fa-f]{1,4})|(([0-9A-Fa-f]{1,4}:){6}((\b((25[0-5])|(1\d{2})|(2[0-4]\d)|(\d{1,2}))\b)\.){3}(\b((25[0-5])|(1\d{2})|(2[0-4]\d)|(\d{1,2}))\b))|(([0-9A-Fa-f]{1,4}:){0,5}:((\b((25[0-5])|(1\d{2})|(2[0-4]\d)|(\d{1,2}))\b)\.){3}(\b((25[0-5])|(1\d{2})|(2[0-4]\d)|(\d{1,2}))\b))|(::([0-9A-Fa-f]{1,4}:){0,5}((\b((25[0-5])|(1\d{2})|(2[0-4]\d)|(\d{1,2}))\b)\.){3}(\b((25[0-5])|(1\d{2})|(2[0-4]\d)|(\d{1,2}))\b))|([0-9A-Fa-f]{1,4}::([0-9A-Fa-f]{1,4}:){0,5}[0-9A-Fa-f]{1,4})|(::([0-9A-Fa-f]{1,4}:){0,6}[0-9A-Fa-f]{1,4})|(([0-9A-Fa-f]{1,4}:){1,7}:))]$)/i,
diff --git a/src/lib/constants/menu.ts b/src/lib/constants/menu.ts
index 9efb983..b373acd 100644
--- a/src/lib/constants/menu.ts
+++ b/src/lib/constants/menu.ts
@@ -17,55 +17,55 @@ import { env } from "~/env";
const menus: Menu[] = [
{
- title: "控制台",
+ title: "dashboard",
href: "/dashboard",
icon: LayoutDashboardIcon,
},
{
- title: "转发",
+ title: "forward",
href: "/forward",
icon: OrbitIcon,
},
{
- title: "服务器",
+ title: "agent",
href: "/agent",
icon: ServerIcon,
},
{
- title: "组网",
+ title: "network",
href: "/network",
icon: NetworkIcon,
},
{
- title: "工单",
+ title: "ticket",
href: "/ticket",
icon: TicketIcon,
},
{
- title: "管理",
+ title: "manage",
children: [
{
- title: "用户",
+ title: "users",
href: "/admin/users",
icon: UserIcon,
},
{
- title: "统计",
+ title: "statistics",
href: env.NEXT_PUBLIC_UMAMI_URL ?? "/not-found",
icon: LineChartIcon,
},
],
},
{
- title: "系统",
+ title: "system",
children: [
{
- title: "日志",
+ title: "log",
href: "/admin/log",
icon: LibraryBigIcon,
},
{
- title: "配置",
+ title: "config",
href: "/admin/config",
icon: Settings2Icon,
},
diff --git a/src/lib/messages/en.json b/src/lib/messages/en.json
new file mode 100644
index 0000000..04a23fd
--- /dev/null
+++ b/src/lib/messages/en.json
@@ -0,0 +1,9 @@
+{
+ "locale-switcher": {
+ "label": "Change language",
+ "locale": "{locale, select, zh {🇨🇳 Chinese} en {🇺🇸 English} other {Unknown}}"
+ },
+ "index": {
+ "Welcome": "Welcome"
+ }
+}
diff --git a/src/lib/messages/zh.json b/src/lib/messages/zh.json
new file mode 100644
index 0000000..dff5f37
--- /dev/null
+++ b/src/lib/messages/zh.json
@@ -0,0 +1,9 @@
+{
+ "locale-switcher": {
+ "label": "选择语言",
+ "locale": "{locale, select, zh {🇨🇳 中文} en {🇺🇸 English} other {Unknown}}"
+ },
+ "index": {
+ "Welcome": "欢迎"
+ }
+}
diff --git a/src/middleware.ts b/src/middleware.ts
new file mode 100644
index 0000000..85850b2
--- /dev/null
+++ b/src/middleware.ts
@@ -0,0 +1,14 @@
+import createMiddleware from "next-intl/middleware";
+
+export default createMiddleware({
+ // A list of all locales that are supported
+ locales: ["en", "zh"],
+
+ // Used when no locale matches
+ defaultLocale: "en",
+});
+
+export const config = {
+ // Match only internationalized pathnames
+ matcher: ["/", "/((?!api|_next|_vercel|.*\\..*).*)", "/(zh|en)/:path*"],
+};
diff --git a/src/navigation.ts b/src/navigation.ts
new file mode 100644
index 0000000..94cbebb
--- /dev/null
+++ b/src/navigation.ts
@@ -0,0 +1,7 @@
+import { createSharedPathnamesNavigation } from "next-intl/navigation";
+import { locales } from "~/lib/constants";
+
+export const localePrefix = "as-needed";
+
+export const { Link, redirect, usePathname, useRouter } =
+ createSharedPathnamesNavigation({ locales, localePrefix });
diff --git a/src/server/logger.ts b/src/server/logger.ts
index 1c0531d..b7bece0 100644
--- a/src/server/logger.ts
+++ b/src/server/logger.ts
@@ -3,19 +3,13 @@ import pino, { type Logger } from "pino";
import "pino-abstract-transport";
const targets = [];
-targets.push({
- target: path.join(process.cwd(), "./src/lib/pino-prisma.mjs"),
- options: {},
- level: "trace",
-});
+
if (process.env.NODE_ENV === "production") {
- // targets.push({
- // target: 'pino/file',
- // options: {
- // destination: path.join(process.cwd(), './app.log'),
- // },
- // level: 'trace',
- // })
+ targets.push({
+ target: path.join(process.cwd(), "./src/lib/pino-prisma.mjs"),
+ options: {},
+ level: "trace",
+ });
} else {
targets.push({
target: "pino-pretty",