Skip to content

feat: 添加多语言支持(中/英)#45

Merged
sunnylqm merged 4 commits into
mainfrom
feat/i18n
Jul 4, 2026
Merged

feat: 添加多语言支持(中/英)#45
sunnylqm merged 4 commits into
mainfrom
feat/i18n

Conversation

@sunnylqm

@sunnylqm sunnylqm commented Jun 28, 2026

Copy link
Copy Markdown
Collaborator

变更内容

  • 安装 i18next + react-i18next + 语言检测插件
  • 创建 i18n 配置,包含 en 和 zh-CN 两个 locale
  • 替换 36 个组件文件中的硬编码字符串为 t() 调用
  • 添加 Ant Design ConfigProvider 动态 locale 切换
  • 在顶部导航添加语言切换器(桌面端 + 移动端)

待完成

  • user.tsx 的 i18n(文件较大,单独处理中)

Summary by CodeRabbit

  • New Features

    • Added full multilingual/i18n support across the app, including authentication, account/user flows, app management, admin pages, realtime/metrics views, and localized error UI.
    • Added language auto-selection with matching UI framework locale, plus complete English and Chinese translation resources.
  • Bug Fixes

    • Replaced hardcoded text with localized strings throughout headers, modals, tables, tooltips, buttons, and empty states.
    • Improved fallback handling and status/label localization when data is missing or varies by state.

@netlify

netlify Bot commented Jun 28, 2026

Copy link
Copy Markdown

Deploy Preview for pushy ready!

Name Link
🔨 Latest commit 347b29b
🔍 Latest deploy log https://app.netlify.com/projects/pushy/deploys/6a4873ec8412a20008011dd2
😎 Deploy Preview https://deploy-preview-45--pushy.netlify.app
📱 Preview on mobile
Toggle QR Code...

QR Code

Use your smartphone camera to open QR code link.
🤖 Make changes Run an agent on this branch

To edit notification comments on pull requests, go to your Netlify project configuration.

@coderabbitai

coderabbitai Bot commented Jun 28, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

Warning

Review limit reached

@sunnylqm, you've reached your PR review limit, so we couldn't start this review.

Next review available in: 50 minutes

Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available.
You're only billed for reviews past your plan's rate limits ($0.25/file).

How can I continue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews.

How do review limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window.

Please refer docs for additional details.

Review details
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: aee1fdce-a8e2-499a-88b1-03788163c797

📥 Commits

Reviewing files that changed from the base of the PR and between 6dce893 and 347b29b.

📒 Files selected for processing (3)
  • src/components/top-navigation.tsx
  • src/i18n/locales/en.json
  • src/i18n/locales/zh-CN.json
📝 Walkthrough

Walkthrough

Adds i18next, browser language detection, and React bindings, then wires locale selection into the app root and Ant Design. Most UI text across shared components, auth screens, user pages, manage pages, and admin pages now comes from translation keys in en and zh-CN locale files.

Changes

i18n infrastructure and root wiring

Layer / File(s) Summary
i18n setup and root locale wiring
package.json, src/i18n/index.ts, src/i18n/locales/en.json, src/i18n/locales/zh-CN.json, src/index.tsx
Adds i18n packages, initializes i18next with browser detection and React integration, adds full English and Chinese locale resources, and selects the Ant Design locale from the active language at the app root.

Shared shell and layout text

Layer / File(s) Summary
Navigation shell and shared layout text
src/components/top-navigation.tsx, src/components/app-drawer.tsx, src/components/footer.tsx, src/components/error-boundary.tsx
Localizes the top navigation, app drawer, footer copyright, and error boundary actions and messages.

App detail and modal text

Layer / File(s) Summary
App detail, settings, and create modal text
src/components/app-detail-header.tsx, src/components/app-settings-modal.tsx, src/components/create-app-modal.tsx
Localizes app detail fallback text, tabs, settings controls, app settings modal labels and confirmations, and create-app modal fields and validation.

Auth pages

Layer / File(s) Summary
Authentication pages localized
src/pages/login.tsx, src/pages/register.tsx, src/pages/reset-password/..., src/pages/activate.tsx, src/pages/welcome.tsx, src/pages/inactivated.tsx
Localizes login, register, reset-password, activation, welcome, and inactivated flows, including validation, resend messaging, and result content.

User-facing pages

Layer / File(s) Summary
Apps, API tokens, and quota UI localized
src/pages/apps.tsx, src/pages/api-tokens.tsx, src/components/daily-check-quota.tsx
Localizes the apps list, API token management page, and daily quota component, including tables, dialogs, tooltips, status tags, and empty states.

Manage pages

Layer / File(s) Summary
Manage page and subcomponents localized
src/pages/manage/index.tsx, src/pages/manage/components/*
Localizes the manage page shell and its subcomponents, including filters, version/package tables, dependency comparison, publish flows, settings, commit previews, and feature support tables.

Admin and realtime pages

Layer / File(s) Summary
Admin and realtime pages localized
src/pages/audit-logs.tsx, src/pages/admin-metrics.tsx, src/pages/admin-service-status.tsx, src/pages/admin-users.tsx, src/pages/admin-apps.tsx, src/pages/admin-config.tsx, src/pages/realtime-metrics.tsx
Localizes audit logs, admin metrics, service status, users, apps, config, and realtime metrics pages, including filters, charts, tables, drawers, exports, and empty states.

Estimated code review effort: 4 (Complex) | ~60 minutes

Possibly related PRs

  • reactnativecn/pushy-admin#10: Overlaps in src/pages/manage/components/bind-package.tsx, where this PR localizes the same component that the other PR restructures.
  • reactnativecn/pushy-admin#32: Overlaps in src/components/create-app-modal.tsx and src/components/daily-check-quota.tsx, where both changes touch the same UI helpers and rendering paths.
🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 2.08% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly matches the main change: adding Chinese/English multilingual support.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/i18n

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 10

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
src/pages/manage/components/version-table.tsx (1)

153-173: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Finish localizing the delete dialog body.

This function now translates the modal title, but the body still uses versionNames.join(','), which leaves Chinese punctuation in English mode. Rendering the names as a list avoids that locale-specific leak.

Suggested fix
   Modal.confirm({
     title: t('version_table.delete_title'),
-    content: versionNames.join(','),
+    content: (
+      <div className="max-h-48 overflow-y-auto">
+        {versionNames.map((name) => (
+          <div key={name}>{name}</div>
+        ))}
+      </div>
+    ),
     maskClosable: true,
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/pages/manage/components/version-table.tsx` around lines 153 - 173, The
delete dialog body in removeSelectedVersions is still building a locale-specific
string with versionNames.join(','), which leaks Chinese punctuation into
non-Chinese locales. Update the Modal.confirm content to render the selected
version names as a locale-aware list instead of joining with a hardcoded
delimiter, keeping the existing translation flow used by
t('version_table.delete_title') and the versionNames collection logic.
🧹 Nitpick comments (4)
src/pages/register.tsx (1)

129-129: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Use rootRouterPath.login for this link.

This file already uses rootRouterPath for navigation, so hardcoding "/login" creates a second route source that can drift later.

♻️ Proposed fix
-            <Link to="/login">{t('register.has_account')}</Link>
+            <Link to={rootRouterPath.login}>{t('register.has_account')}</Link>
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/pages/register.tsx` at line 129, The register page link is hardcoded to
"/login" instead of using the shared router constant, which can drift from the
app’s routing source of truth. Update the Link in register page to use
rootRouterPath.login, following the existing rootRouterPath usage in this
component, so navigation stays consistent and centralized.
src/pages/inactivated.tsx (1)

49-50: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Reuse rootRouterPath.login for the back button target.

The guard above already navigates with rootRouterPath.login; hardcoding "/user" here makes the destination easier to drift away from the translated “back login” label.

♻️ Proposed fix
-        <Button key="back" href="/user">
+        <Button key="back" href={rootRouterPath.login}>
           {t('inactivated.back_login')}
         </Button>,
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/pages/inactivated.tsx` around lines 49 - 50, The back button in the
inactivated page is hardcoding the login destination instead of reusing the
shared router constant. Update the Button in the inactivated page to use
rootRouterPath.login, matching the existing navigation guard and keeping the
target consistent with the “back login” action.
src/pages/reset-password/components/success.tsx (1)

10-12: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Use the shared login route instead of hardcoding /#/login.

This introduces a second login URL shape in the same flow. Keeping the CTA on the router-managed login path avoids route drift and the forced full-page reload here.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/pages/reset-password/components/success.tsx` around lines 10 - 12, The
reset-password success CTA is hardcoding the login URL, which bypasses the
shared router path and can cause route drift. Update the Button in the Success
component to use the existing login route constant/helper used elsewhere in the
app instead of the literal /#/login, so the reset flow stays aligned with the
router-managed login path.
src/pages/manage/components/publish-feature-table.tsx (1)

20-37: 🎯 Functional Correctness | 🔵 Trivial | ⚡ Quick win

Keep tag state separate from translated copy.

The tag color now depends on text.includes('✓') / text.includes('⚠'). That makes every locale string responsible for preserving those glyphs. Store a semantic status in the row data and translate only the displayed label.

Also applies to: 57-89

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/pages/manage/components/publish-feature-table.tsx` around lines 20 - 37,
The publish feature table is coupling tag color to translated text via the
`text.includes('✓')` / `text.includes('⚠')` logic, so the state should be made
semantic instead of inferred from localized copy. Update the row data used in
`PublishFeatureTable` (and the related entries in the table definition block) to
carry an explicit status field for each tag, and keep `t(...)` only for the
visible label. Then adjust the tag rendering logic to use that status field for
color/variant selection instead of inspecting the translated string.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@src/components/app-drawer.tsx`:
- Around line 136-142: The app count and check-count translations in app-drawer
should keep their count values numeric for i18next pluralization, rather than
passing locale-formatted strings. Update the calls in the app list and the
checkbox label to pass the raw numeric values from apps.length and the active
check count, and handle any locale-specific formatting only in the rendered text
layer. Use the translation keys and the surrounding AppDrawer rendering logic to
locate the affected count labels.

In `@src/components/daily-check-quota.tsx`:
- Around line 37-40: The tier label in daily-check-quota still falls back to
localized data from quotas.title, which can surface Chinese plan names in
English mode. Update the rendering in daily-check-quota (including the other
affected tier display path) to resolve known tiers through t(...) first, then
fall back to a custom server-provided title, and only use user.tier as the last
resort. Use the existing tier lookup points around quota, quotas[user.tier], and
the tier text rendering to keep the fix consistent.

In `@src/components/top-navigation.tsx`:
- Around line 72-89: The `top-navigation` menu items for documentation and about
are using translated labels but fixed `href`s, so the destination does not match
the active locale. Update the `TopNavigation` link setup for the `document` and
`about` entries to choose the locale-specific URL based on the current language
(using the same i18n/lang source already available in this component), or
otherwise ensure each label points to the matching language page.

In `@src/i18n/locales/zh-CN.json`:
- Around line 49-135: The zh-CN locale bundle is missing several keys that the
new UI expects, so align it with the English key set by adding the absent
entries under user, deps_table, and the full publish_feature_table namespace in
zh-CN.json. Use the existing translation patterns in that file to add localized
strings for symbols like user.fetching_addon_quote, user.per_year,
user.upgrade_button, deps_table.cli_required,
deps_table.native_package_with_name, deps_table.ota_version_with_name,
bind_package.publish, and publish_feature_table so the localized screens don’t
fall back to raw keys.

In `@src/index.tsx`:
- Around line 68-70: The Antd locale lookup in App is using i18n.language
directly, which can remain an unresolved detector value and miss the normalized
translation language. Update the locale selection to use the resolved language
from the i18n instance when indexing antdLocaleMap, so the App component picks
the correct Antd locale instead of falling back to zhCN for English. Keep the
fix localized to the App function and the antdLocaleMap lookup.

In `@src/pages/audit-logs.tsx`:
- Line 217: The audit logs page is using a fixed Day.js locale at module load,
so relative timestamps from date.fromNow() can stay in the wrong language even
after useTranslation switches the UI language. Update the audit-logs page logic
around useTranslation and the time rendering to sync Day.js with the active i18n
language, preferably by setting dayjs.locale from the current language inside
the component lifecycle instead of once at import time.
- Around line 123-129: The audit log action filter is using translated text as
the query value, so `selectedAction` can break across locales and shared URLs.
Update `getActionOptions` (and the related select/query handling in the audit
logs page) to store a stable action key derived from `getActionMap` output, such
as `METHOD + normalizedPath`, and use the translated string only for the
displayed `label`. Make sure the same stable key is used wherever the action
value is written to or read from the URL/query param so filtering and selection
remain consistent after language changes.

In `@src/pages/manage/index.tsx`:
- Line 73: The bulk-select aria-label in the Manage page is collapsing to just
filterLabel because the conditional adds nothing, so screen readers miss the
action description. Update the label in the manage page component to use a
descriptive bulk-select text for the checkbox action, and remove the dead
t('common.save') branch from that aria-label expression.

In `@src/pages/realtime-metrics.tsx`:
- Around line 95-100: The tooltip text in formatTooltipItem is assembling
localized output by concatenating translation fragments, which can produce
awkward spacing and word order. Update the realtime-metrics translations and the
code paths using formatTooltipItem/current_dimension to use full interpolated
messages instead of stitching together checks_suffix or similar fragments, so
the locale controls the complete phrase naturally.

In `@src/pages/reset-password/index.tsx`:
- Around line 21-25: The reset-password success route is still using a step
value that is outside the Steps range, so the progress indicator becomes out of
sync on the success screen. Update the reset-password page logic in the
component that renders Steps so the success route is mapped to display index 2
before passing the value into current, while keeping the existing step routes
and content rendering unchanged.

---

Outside diff comments:
In `@src/pages/manage/components/version-table.tsx`:
- Around line 153-173: The delete dialog body in removeSelectedVersions is still
building a locale-specific string with versionNames.join(','), which leaks
Chinese punctuation into non-Chinese locales. Update the Modal.confirm content
to render the selected version names as a locale-aware list instead of joining
with a hardcoded delimiter, keeping the existing translation flow used by
t('version_table.delete_title') and the versionNames collection logic.

---

Nitpick comments:
In `@src/pages/inactivated.tsx`:
- Around line 49-50: The back button in the inactivated page is hardcoding the
login destination instead of reusing the shared router constant. Update the
Button in the inactivated page to use rootRouterPath.login, matching the
existing navigation guard and keeping the target consistent with the “back
login” action.

In `@src/pages/manage/components/publish-feature-table.tsx`:
- Around line 20-37: The publish feature table is coupling tag color to
translated text via the `text.includes('✓')` / `text.includes('⚠')` logic, so
the state should be made semantic instead of inferred from localized copy.
Update the row data used in `PublishFeatureTable` (and the related entries in
the table definition block) to carry an explicit status field for each tag, and
keep `t(...)` only for the visible label. Then adjust the tag rendering logic to
use that status field for color/variant selection instead of inspecting the
translated string.

In `@src/pages/register.tsx`:
- Line 129: The register page link is hardcoded to "/login" instead of using the
shared router constant, which can drift from the app’s routing source of truth.
Update the Link in register page to use rootRouterPath.login, following the
existing rootRouterPath usage in this component, so navigation stays consistent
and centralized.

In `@src/pages/reset-password/components/success.tsx`:
- Around line 10-12: The reset-password success CTA is hardcoding the login URL,
which bypasses the shared router path and can cause route drift. Update the
Button in the Success component to use the existing login route constant/helper
used elsewhere in the app instead of the literal /#/login, so the reset flow
stays aligned with the router-managed login path.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 4bcc56f0-c6ea-4efe-9a44-0ed365d39ffa

📥 Commits

Reviewing files that changed from the base of the PR and between 3b47bb2 and d5bc2b7.

⛔ Files ignored due to path filters (1)
  • bun.lock is excluded by !**/*.lock
📒 Files selected for processing (39)
  • package.json
  • src/components/app-detail-header.tsx
  • src/components/app-drawer.tsx
  • src/components/app-settings-modal.tsx
  • src/components/create-app-modal.tsx
  • src/components/daily-check-quota.tsx
  • src/components/error-boundary.tsx
  • src/components/footer.tsx
  • src/components/top-navigation.tsx
  • src/i18n/index.ts
  • src/i18n/locales/en.json
  • src/i18n/locales/zh-CN.json
  • src/index.tsx
  • src/pages/activate.tsx
  • src/pages/admin-apps.tsx
  • src/pages/admin-config.tsx
  • src/pages/admin-metrics.tsx
  • src/pages/admin-service-status.tsx
  • src/pages/admin-users.tsx
  • src/pages/api-tokens.tsx
  • src/pages/apps.tsx
  • src/pages/audit-logs.tsx
  • src/pages/inactivated.tsx
  • src/pages/login.tsx
  • src/pages/manage/components/bind-package.tsx
  • src/pages/manage/components/commit.tsx
  • src/pages/manage/components/deps-table.tsx
  • src/pages/manage/components/package-list.tsx
  • src/pages/manage/components/publish-feature-table.tsx
  • src/pages/manage/components/setting-modal.tsx
  • src/pages/manage/components/version-table.tsx
  • src/pages/manage/index.tsx
  • src/pages/realtime-metrics.tsx
  • src/pages/register.tsx
  • src/pages/reset-password/components/send-email.tsx
  • src/pages/reset-password/components/set-password.tsx
  • src/pages/reset-password/components/success.tsx
  • src/pages/reset-password/index.tsx
  • src/pages/welcome.tsx

Comment thread src/components/app-drawer.tsx
Comment thread src/components/daily-check-quota.tsx Outdated
Comment thread src/components/top-navigation.tsx
Comment thread src/i18n/locales/zh-CN.json
Comment thread src/index.tsx Outdated
Comment thread src/pages/audit-logs.tsx Outdated
Comment thread src/pages/audit-logs.tsx Outdated
Comment thread src/pages/manage/index.tsx Outdated
Comment thread src/pages/realtime-metrics.tsx Outdated
Comment thread src/pages/reset-password/index.tsx Outdated
sunnylqm added 3 commits July 4, 2026 10:33
- Install i18next, react-i18next, i18next-browser-languagedetector
- Create i18n config with en and zh-CN locales
- Replace hardcoded strings across 36 component/page files
- Add Ant Design ConfigProvider with dynamic locale switching
- Add language switcher in top navigation (desktop + mobile)

Note: user.tsx i18n pending (complex file, separate commit)
- Replace all 63 Chinese strings with t() calls
- Convert module-level constants to functions accepting t
- Add useTranslation to 7 components
- TypeScript and biome checks pass

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick comments (2)
src/pages/audit-logs.tsx (1)

102-129: 🚀 Performance & Scalability | 🔵 Trivial | ⚡ Quick win

Memoize getActionMap(t) to avoid rebuilding it per log/row.

getActionMap(t) recreates a 24-key translated object every call. getActionLabel calls it per rendered row, and buildSearchText calls it once per log inside the filteredAuditLogs useMemo — with up to 1000 logs, that's up to 24k translation lookups recomputed on every search keystroke (debounced) or language change. Consider computing the map once via useMemo(() => getActionMap(t), [t]) and passing it down instead of rebuilding it inside getActionLabel/buildSearchText.

♻️ Suggested approach
-const getActionLabel = (
-  t: TranslateFn,
-  method: string,
-  path: string,
-): string => {
-  const key = getActionKey(method, path);
-  return getActionMap(t)[key] || `${method.toUpperCase()} ${path}`;
-};
+const getActionLabel = (
+  actionMap: Record<string, string>,
+  method: string,
+  path: string,
+): string => {
+  const key = getActionKey(method, path);
+  return actionMap[key] || `${method.toUpperCase()} ${path}`;
+};

Then compute const actionMap = useMemo(() => getActionMap(t), [t]); once in AuditLogs and thread it through buildSearchText, columns, and the Drawer instead of t.

Also applies to: 140-147, 206-221, 295-295

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/pages/audit-logs.tsx` around lines 102 - 129, `getActionMap(t)` is being
rebuilt repeatedly inside `getActionLabel` and `buildSearchText`, causing
unnecessary translation work for every row/log render. Compute the map once in
`AuditLogs` with `useMemo(() => getActionMap(t), [t])`, then pass the memoized
`actionMap` into `buildSearchText`, `getActionLabel`, and the Drawer/column
rendering paths so the existing helpers use the shared map instead of calling
`getActionMap` themselves.
src/pages/user.tsx (1)

419-449: 🎯 Functional Correctness | 🔵 Trivial | ⚡ Quick win

Prefer interpolation over concatenating independently-translated fragments.

Several strings are built by concatenating a raw value with a translated fragment in a fixed order (e.g. `${months} ${t('user.annual_billing')}`, `${t('user.annual_pay')} ${formatMoney(quote.amount)}`). This bakes in an English/Chinese-compatible word order into the code rather than the translation file, so text ordering can't be adjusted per-locale without a code change.

♻️ Suggested approach
-title: isAnnual
-  ? `${months} ${t('user.annual_billing')}`
-  : `${months} ${t('user.price_month')}`,
+title: isAnnual
+  ? t('user.annual_billing_months', { months })
+  : t('user.price_month_months', { months }),

Also applies to: 516-529

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/pages/user.tsx` around lines 419 - 449, The `user.tsx` plan/price labels
are assembling translated text with fixed string concatenation, which prevents
locale-specific word order. Update the affected formatting in the option builder
(including the `title` field here and the similar block around the other
referenced section) to use translation interpolation/placeholders instead of
`${value} ${t(...)}` patterns. Keep the logic in the same `purchase` option
construction path, but move the ordering into the translation keys so each
locale can define its own phrasing.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Nitpick comments:
In `@src/pages/audit-logs.tsx`:
- Around line 102-129: `getActionMap(t)` is being rebuilt repeatedly inside
`getActionLabel` and `buildSearchText`, causing unnecessary translation work for
every row/log render. Compute the map once in `AuditLogs` with `useMemo(() =>
getActionMap(t), [t])`, then pass the memoized `actionMap` into
`buildSearchText`, `getActionLabel`, and the Drawer/column rendering paths so
the existing helpers use the shared map instead of calling `getActionMap`
themselves.

In `@src/pages/user.tsx`:
- Around line 419-449: The `user.tsx` plan/price labels are assembling
translated text with fixed string concatenation, which prevents locale-specific
word order. Update the affected formatting in the option builder (including the
`title` field here and the similar block around the other referenced section) to
use translation interpolation/placeholders instead of `${value} ${t(...)}`
patterns. Keep the logic in the same `purchase` option construction path, but
move the ordering into the translation keys so each locale can define its own
phrasing.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: d12da546-a70c-405c-932e-1fe8d0d20ff7

📥 Commits

Reviewing files that changed from the base of the PR and between d5bc2b7 and 6dce893.

⛔ Files ignored due to path filters (4)
  • bun.lock is excluded by !**/*.lock
  • src/assets/favicon.svg is excluded by !**/*.svg
  • src/assets/logo-h.svg is excluded by !**/*.svg
  • src/assets/logo.svg is excluded by !**/*.svg
📒 Files selected for processing (40)
  • package.json
  • src/components/app-detail-header.tsx
  • src/components/app-drawer.tsx
  • src/components/app-settings-modal.tsx
  • src/components/create-app-modal.tsx
  • src/components/daily-check-quota.tsx
  • src/components/error-boundary.tsx
  • src/components/footer.tsx
  • src/components/top-navigation.tsx
  • src/i18n/index.ts
  • src/i18n/locales/en.json
  • src/i18n/locales/zh-CN.json
  • src/index.tsx
  • src/pages/activate.tsx
  • src/pages/admin-apps.tsx
  • src/pages/admin-config.tsx
  • src/pages/admin-metrics.tsx
  • src/pages/admin-service-status.tsx
  • src/pages/admin-users.tsx
  • src/pages/api-tokens.tsx
  • src/pages/apps.tsx
  • src/pages/audit-logs.tsx
  • src/pages/inactivated.tsx
  • src/pages/login.tsx
  • src/pages/manage/components/bind-package.tsx
  • src/pages/manage/components/commit.tsx
  • src/pages/manage/components/deps-table.tsx
  • src/pages/manage/components/package-list.tsx
  • src/pages/manage/components/publish-feature-table.tsx
  • src/pages/manage/components/setting-modal.tsx
  • src/pages/manage/components/version-table.tsx
  • src/pages/manage/index.tsx
  • src/pages/realtime-metrics.tsx
  • src/pages/register.tsx
  • src/pages/reset-password/components/send-email.tsx
  • src/pages/reset-password/components/set-password.tsx
  • src/pages/reset-password/components/success.tsx
  • src/pages/reset-password/index.tsx
  • src/pages/user.tsx
  • src/pages/welcome.tsx
✅ Files skipped from review due to trivial changes (3)
  • src/pages/welcome.tsx
  • src/components/app-settings-modal.tsx
  • src/i18n/locales/zh-CN.json
🚧 Files skipped from review as they are similar to previous changes (31)
  • src/i18n/index.ts
  • src/pages/login.tsx
  • src/index.tsx
  • src/components/footer.tsx
  • package.json
  • src/pages/inactivated.tsx
  • src/pages/reset-password/components/set-password.tsx
  • src/pages/manage/components/setting-modal.tsx
  • src/pages/apps.tsx
  • src/pages/reset-password/components/send-email.tsx
  • src/pages/register.tsx
  • src/components/app-detail-header.tsx
  • src/components/daily-check-quota.tsx
  • src/components/create-app-modal.tsx
  • src/i18n/locales/en.json
  • src/components/error-boundary.tsx
  • src/pages/manage/components/version-table.tsx
  • src/pages/manage/index.tsx
  • src/pages/admin-service-status.tsx
  • src/pages/admin-apps.tsx
  • src/pages/admin-metrics.tsx
  • src/pages/activate.tsx
  • src/pages/manage/components/deps-table.tsx
  • src/components/app-drawer.tsx
  • src/pages/admin-users.tsx
  • src/pages/manage/components/commit.tsx
  • src/pages/admin-config.tsx
  • src/pages/manage/components/package-list.tsx
  • src/components/top-navigation.tsx
  • src/pages/manage/components/bind-package.tsx
  • src/pages/realtime-metrics.tsx

@sunnylqm sunnylqm merged commit 88fcb3c into main Jul 4, 2026
5 checks passed
@sunnylqm sunnylqm deleted the feat/i18n branch July 4, 2026 03:33
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant