Explorar el Código

fix(i18n): replace hardcoded Chinese strings in users.ts with translation keys

- Add missing error translation keys to all language files (en, ja, ru, zh-CN, zh-TW):
  * USER_NOT_FOUND
  * USER_CANNOT_MODIFY_SENSITIVE_FIELDS
  * CREATE_USER_FAILED
  * UPDATE_USER_FAILED
  * DELETE_USER_FAILED
  * GET_USER_QUOTA_FAILED

- Create users.json translation files for all languages:
  * Add "neverExpires" key for key expiration display

- Update src/actions/users.ts to use translations:
  * Import getTranslations from next-intl/server
  * Replace all hardcoded Chinese error messages with translation keys
  * Use t("neverExpires") for key expiration display
  * Keep log messages in Chinese (internal use only)

Resolves PR review feedback about hardcoded strings in Server Actions.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <[email protected]>
ding113 hace 3 meses
padre
commit
0860b0a8dc

+ 8 - 1
messages/en/errors.json

@@ -47,5 +47,12 @@
   "RATE_LIMIT_EXCEEDED": "Too many requests, please try again later",
   "RESOURCE_BUSY": "Resource is currently in use",
   "INVALID_STATE": "Operation not allowed in current state",
-  "CONFLICT": "Operation conflict"
+  "CONFLICT": "Operation conflict",
+
+  "USER_NOT_FOUND": "User not found",
+  "USER_CANNOT_MODIFY_SENSITIVE_FIELDS": "Regular users cannot modify quota limits and provider groups",
+  "CREATE_USER_FAILED": "Failed to create user, please try again later",
+  "UPDATE_USER_FAILED": "Failed to update user, please try again later",
+  "DELETE_USER_FAILED": "Failed to delete user, please try again later",
+  "GET_USER_QUOTA_FAILED": "Failed to get user quota information"
 }

+ 5 - 0
messages/en/users.json

@@ -0,0 +1,5 @@
+{
+  "_comment": "User management translations for English",
+
+  "neverExpires": "Never expires"
+}

+ 8 - 1
messages/ja/errors.json

@@ -47,5 +47,12 @@
   "RATE_LIMIT_EXCEEDED": "リクエストが多すぎます。後でもう一度お試しください",
   "RESOURCE_BUSY": "リソースは現在使用中です",
   "INVALID_STATE": "現在の状態では操作が許可されていません",
-  "CONFLICT": "操作の競合"
+  "CONFLICT": "操作の競合",
+
+  "USER_NOT_FOUND": "ユーザーが見つかりません",
+  "USER_CANNOT_MODIFY_SENSITIVE_FIELDS": "一般ユーザーはクォータ制限とプロバイダーグループを変更できません",
+  "CREATE_USER_FAILED": "ユーザーの作成に失敗しました。後でもう一度お試しください",
+  "UPDATE_USER_FAILED": "ユーザーの更新に失敗しました。後でもう一度お試しください",
+  "DELETE_USER_FAILED": "ユーザーの削除に失敗しました。後でもう一度お試しください",
+  "GET_USER_QUOTA_FAILED": "ユーザークォータ情報の取得に失敗しました"
 }

+ 5 - 0
messages/ja/users.json

@@ -0,0 +1,5 @@
+{
+  "_comment": "User management translations for Japanese",
+
+  "neverExpires": "有効期限なし"
+}

+ 8 - 1
messages/ru/errors.json

@@ -47,5 +47,12 @@
   "RATE_LIMIT_EXCEEDED": "Слишком много запросов, попробуйте позже",
   "RESOURCE_BUSY": "Ресурс в настоящее время используется",
   "INVALID_STATE": "Операция не разрешена в текущем состоянии",
-  "CONFLICT": "Конфликт операции"
+  "CONFLICT": "Конфликт операции",
+
+  "USER_NOT_FOUND": "Пользователь не найден",
+  "USER_CANNOT_MODIFY_SENSITIVE_FIELDS": "Обычные пользователи не могут изменять лимиты квоты и группы провайдеров",
+  "CREATE_USER_FAILED": "Не удалось создать пользователя, попробуйте позже",
+  "UPDATE_USER_FAILED": "Не удалось обновить пользователя, попробуйте позже",
+  "DELETE_USER_FAILED": "Не удалось удалить пользователя, попробуйте позже",
+  "GET_USER_QUOTA_FAILED": "Не удалось получить информацию о квоте пользователя"
 }

+ 5 - 0
messages/ru/users.json

@@ -0,0 +1,5 @@
+{
+  "_comment": "User management translations for Russian",
+
+  "neverExpires": "Бессрочно"
+}

+ 8 - 1
messages/zh-CN/errors.json

@@ -47,5 +47,12 @@
   "RATE_LIMIT_EXCEEDED": "请求过于频繁,请稍后重试",
   "RESOURCE_BUSY": "资源正在使用中",
   "INVALID_STATE": "当前状态不允许此操作",
-  "CONFLICT": "操作冲突"
+  "CONFLICT": "操作冲突",
+
+  "USER_NOT_FOUND": "用户不存在",
+  "USER_CANNOT_MODIFY_SENSITIVE_FIELDS": "普通用户不能修改账户限额和供应商分组",
+  "CREATE_USER_FAILED": "创建用户失败,请稍后重试",
+  "UPDATE_USER_FAILED": "更新用户失败,请稍后重试",
+  "DELETE_USER_FAILED": "删除用户失败,请稍后重试",
+  "GET_USER_QUOTA_FAILED": "获取用户限额使用情况失败"
 }

+ 5 - 0
messages/zh-CN/users.json

@@ -0,0 +1,5 @@
+{
+  "_comment": "User management translations for Chinese Simplified",
+
+  "neverExpires": "永不过期"
+}

+ 8 - 1
messages/zh-TW/errors.json

@@ -47,5 +47,12 @@
   "RATE_LIMIT_EXCEEDED": "請求過於頻繁,請稍後重試",
   "RESOURCE_BUSY": "資源正在使用中",
   "INVALID_STATE": "當前狀態不允許此操作",
-  "CONFLICT": "操作衝突"
+  "CONFLICT": "操作衝突",
+
+  "USER_NOT_FOUND": "使用者不存在",
+  "USER_CANNOT_MODIFY_SENSITIVE_FIELDS": "普通使用者不能修改帳戶額度和供應商分組",
+  "CREATE_USER_FAILED": "創建使用者失敗,請稍後重試",
+  "UPDATE_USER_FAILED": "更新使用者失敗,請稍後重試",
+  "DELETE_USER_FAILED": "刪除使用者失敗,請稍後重試",
+  "GET_USER_QUOTA_FAILED": "獲取使用者額度使用情況失敗"
 }

+ 5 - 0
messages/zh-TW/users.json

@@ -0,0 +1,5 @@
+{
+  "_comment": "User management translations for Chinese Traditional",
+
+  "neverExpires": "永不過期"
+}

+ 33 - 13
src/actions/users.ts

@@ -14,6 +14,7 @@ import { getSession } from "@/lib/auth";
 import type { ActionResult } from "./types";
 import { ERROR_CODES } from "@/lib/utils/error-messages";
 import { formatZodError } from "@/lib/utils/zod-i18n";
+import { getTranslations } from "next-intl/server";
 
 // 获取用户数据
 export async function getUsers(): Promise<UserDisplay[]> {
@@ -23,6 +24,9 @@ export async function getUsers(): Promise<UserDisplay[]> {
       return [];
     }
 
+    // Get translations for user-related texts
+    const t = await getTranslations("users");
+
     // 普通用户只能看到自己的数据
     let users;
     if (session.user.role === "user") {
@@ -69,7 +73,7 @@ export async function getUsers(): Promise<UserDisplay[]> {
                 maskedKey: maskKey(key.key),
                 fullKey: canUserManageKey ? key.key : undefined,
                 canCopy: canUserManageKey,
-                expiresAt: key.expiresAt ? key.expiresAt.toISOString().split("T")[0] : "永不过期",
+                expiresAt: key.expiresAt ? key.expiresAt.toISOString().split("T")[0] : t("neverExpires"),
                 status: key.isEnabled ? "enabled" : ("disabled" as const),
                 createdAt: key.createdAt,
                 createdAtFormatted: key.createdAt.toLocaleString("zh-CN", {
@@ -127,12 +131,15 @@ export async function addUser(data: {
   dailyQuota?: number;
 }): Promise<ActionResult> {
   try {
+    // Get translations for error messages
+    const tError = await getTranslations("errors");
+
     // 权限检查:只有管理员可以添加用户
     const session = await getSession();
     if (!session || session.user.role !== "admin") {
       return {
         ok: false,
-        error: "无权限执行此操作",
+        error: tError("PERMISSION_DENIED"),
         errorCode: ERROR_CODES.PERMISSION_DENIED,
       };
     }
@@ -178,7 +185,8 @@ export async function addUser(data: {
     return { ok: true };
   } catch (error) {
     logger.error("添加用户失败:", error);
-    const message = error instanceof Error ? error.message : "添加用户失败,请稍后重试";
+    const tError = await getTranslations("errors");
+    const message = error instanceof Error ? error.message : tError("CREATE_USER_FAILED");
     return {
       ok: false,
       error: message,
@@ -199,11 +207,14 @@ export async function editUser(
   }
 ): Promise<ActionResult> {
   try {
+    // Get translations for error messages
+    const tError = await getTranslations("errors");
+
     const session = await getSession();
     if (!session) {
       return {
         ok: false,
-        error: "未登录",
+        error: tError("UNAUTHORIZED"),
         errorCode: ERROR_CODES.UNAUTHORIZED,
       };
     }
@@ -239,7 +250,7 @@ export async function editUser(
       if (hasSensitiveFields) {
         return {
           ok: false,
-          error: "普通用户不能修改账户限额和供应商分组",
+          error: tError("USER_CANNOT_MODIFY_SENSITIVE_FIELDS"),
           errorCode: ERROR_CODES.PERMISSION_DENIED,
         };
       }
@@ -268,7 +279,7 @@ export async function editUser(
       // 普通用户尝试修改他人信息
       return {
         ok: false,
-        error: "无权限执行此操作",
+        error: tError("PERMISSION_DENIED"),
         errorCode: ERROR_CODES.PERMISSION_DENIED,
       };
     }
@@ -277,7 +288,8 @@ export async function editUser(
     return { ok: true };
   } catch (error) {
     logger.error("更新用户失败:", error);
-    const message = error instanceof Error ? error.message : "更新用户失败,请稍后重试";
+    const tError = await getTranslations("errors");
+    const message = error instanceof Error ? error.message : tError("UPDATE_USER_FAILED");
     return {
       ok: false,
       error: message,
@@ -289,9 +301,12 @@ export async function editUser(
 // 删除用户
 export async function removeUser(userId: number): Promise<ActionResult> {
   try {
+    // Get translations for error messages
+    const tError = await getTranslations("errors");
+
     const session = await getSession();
     if (!session || session.user.role !== "admin") {
-      return { ok: false, error: "无权限执行此操作" };
+      return { ok: false, error: tError("PERMISSION_DENIED") };
     }
 
     await deleteUser(userId);
@@ -299,7 +314,8 @@ export async function removeUser(userId: number): Promise<ActionResult> {
     return { ok: true };
   } catch (error) {
     logger.error("删除用户失败:", error);
-    const message = error instanceof Error ? error.message : "删除用户失败,请稍后重试";
+    const tError = await getTranslations("errors");
+    const message = error instanceof Error ? error.message : tError("DELETE_USER_FAILED");
     return { ok: false, error: message };
   }
 }
@@ -314,19 +330,22 @@ export async function getUserLimitUsage(userId: number): Promise<
   }>
 > {
   try {
+    // Get translations for error messages
+    const tError = await getTranslations("errors");
+
     const session = await getSession();
     if (!session) {
-      return { ok: false, error: "未登录" };
+      return { ok: false, error: tError("UNAUTHORIZED") };
     }
 
     const user = await findUserById(userId);
     if (!user) {
-      return { ok: false, error: "用户不存在" };
+      return { ok: false, error: tError("USER_NOT_FOUND") };
     }
 
     // 权限检查:用户只能查看自己,管理员可以查看所有人
     if (session.user.role !== "admin" && session.user.id !== userId) {
-      return { ok: false, error: "无权限执行此操作" };
+      return { ok: false, error: tError("PERMISSION_DENIED") };
     }
 
     // 动态导入避免循环依赖
@@ -358,7 +377,8 @@ export async function getUserLimitUsage(userId: number): Promise<
     };
   } catch (error) {
     logger.error("获取用户限额使用情况失败:", error);
-    const message = error instanceof Error ? error.message : "获取用户限额使用情况失败";
+    const tError = await getTranslations("errors");
+    const message = error instanceof Error ? error.message : tError("GET_USER_QUOTA_FAILED");
     return { ok: false, error: message };
   }
 }