From 78f7267f8b0045b17ebc9afc3f1b42adcc3a6c0a Mon Sep 17 00:00:00 2001 From: Michael Wu Date: Thu, 10 Sep 2026 07:20:44 +0900 Subject: [PATCH] fix(app): localize provider usage labels --- docs/i18n.md | 2 +- packages/app/src/i18n/resources/ar.ts | 6 ++++++ packages/app/src/i18n/resources/en.ts | 6 ++++++ packages/app/src/i18n/resources/es.ts | 6 ++++++ packages/app/src/i18n/resources/fr.ts | 6 ++++++ packages/app/src/i18n/resources/ja.ts | 6 ++++++ packages/app/src/i18n/resources/ko.ts | 6 ++++++ packages/app/src/i18n/resources/pt-BR.ts | 6 ++++++ packages/app/src/i18n/resources/ru.ts | 6 ++++++ packages/app/src/i18n/resources/zh-CN.ts | 6 ++++++ packages/app/src/usage/balance-bar.tsx | 4 ++-- packages/app/src/usage/format.test.ts | 17 ++++++++++++++++- 12 files changed, 73 insertions(+), 4 deletions(-) diff --git a/docs/i18n.md b/docs/i18n.md index 0e132c89a8..e132ece6e7 100644 --- a/docs/i18n.md +++ b/docs/i18n.md @@ -91,4 +91,4 @@ Within a migrated surface, do not leave mixed-language neighboring labels when t - Batch 4Z expanded the supported locale set to the six UN official languages: Arabic, Chinese, English, French, Russian, and Spanish. Arabic, French, Russian, and Spanish now have full client-owned UI resource coverage, with key parity, fallback-ratio, and interpolation-placeholder tests guarding the generated translations. Arabic does not enable RTL layout direction in this batch. - Batch 5A added Brazilian Portuguese (`pt-BR`) resource coverage, language selector labels, i18next registration, and system-locale mapping for `pt-BR` and bare `pt`. Non-Brazilian Portuguese regional locales remain unsupported until a matching resource is added. - Batch 5B added Korean (`ko`) resource coverage, language selector labels, i18next registration, and system-locale mapping for `ko` and Korean regional locales. -- Batch 5C migrated provider-usage chrome across the Usage settings tab and context-meter tooltip: loading/error/empty states, refresh actions, usage/remaining labels, availability statuses, reset/run-out timing, and updated-at text. Provider names, plan labels, usage-window labels, balance/detail labels, source labels, and raw provider errors remain runtime values. +- Batch 5C migrated provider-usage chrome across the Usage settings tab and context-meter tooltip: loading/error/empty states, refresh actions, usage/remaining labels, availability statuses, reset/run-out timing, and updated-at text. Known generic usage labels (`session`, `weekly`, `code_review`, and `credits`) are localized by ID; provider names, plan labels, provider-specific labels, detail labels, source labels, and raw provider errors remain runtime values. diff --git a/packages/app/src/i18n/resources/ar.ts b/packages/app/src/i18n/resources/ar.ts index 8f3fa1a417..17bead2308 100644 --- a/packages/app/src/i18n/resources/ar.ts +++ b/packages/app/src/i18n/resources/ar.ts @@ -1970,6 +1970,12 @@ export const ar: TranslationResources = { used: "تم استخدام {{percentage}}", remaining: "متبقٍ {{amount}}", }, + labels: { + session: "الجلسة", + weekly: "أسبوعي", + codeReview: "مراجعة التعليمات البرمجية", + credits: "الأرصدة", + }, duration: { days: "{{value}} يوم", days_zero: "{{value}} يوم", diff --git a/packages/app/src/i18n/resources/en.ts b/packages/app/src/i18n/resources/en.ts index f2f16e36e4..f4e1bb54ff 100644 --- a/packages/app/src/i18n/resources/en.ts +++ b/packages/app/src/i18n/resources/en.ts @@ -1995,6 +1995,12 @@ export const en = { used: "{{percentage}} used", remaining: "{{amount}} left", }, + labels: { + session: "Session", + weekly: "Weekly", + codeReview: "Code review", + credits: "Credits", + }, duration: { days: "{{value}}d", hours: "{{value}}h", diff --git a/packages/app/src/i18n/resources/es.ts b/packages/app/src/i18n/resources/es.ts index 718499f853..455abc8dc8 100644 --- a/packages/app/src/i18n/resources/es.ts +++ b/packages/app/src/i18n/resources/es.ts @@ -2018,6 +2018,12 @@ export const es: TranslationResources = { used: "Usado: {{percentage}}", remaining: "Quedan {{amount}}", }, + labels: { + session: "Sesión", + weekly: "Semanal", + codeReview: "Revisión de código", + credits: "Créditos", + }, duration: { days: "{{value}} d", hours: "{{value}} h", diff --git a/packages/app/src/i18n/resources/fr.ts b/packages/app/src/i18n/resources/fr.ts index 442c5c940c..d4bb5cd94c 100644 --- a/packages/app/src/i18n/resources/fr.ts +++ b/packages/app/src/i18n/resources/fr.ts @@ -2023,6 +2023,12 @@ export const fr: TranslationResources = { used: "Utilisé : {{percentage}}", remaining: "Il reste {{amount}}", }, + labels: { + session: "Session", + weekly: "Hebdomadaire", + codeReview: "Révision de code", + credits: "Crédits", + }, duration: { days: "{{value}} j", hours: "{{value}} h", diff --git a/packages/app/src/i18n/resources/ja.ts b/packages/app/src/i18n/resources/ja.ts index 6e449075d9..6a5046b3a4 100644 --- a/packages/app/src/i18n/resources/ja.ts +++ b/packages/app/src/i18n/resources/ja.ts @@ -1990,6 +1990,12 @@ export const ja: TranslationResources = { used: "{{percentage}} 使用済み", remaining: "残り {{amount}}", }, + labels: { + session: "セッション", + weekly: "週間", + codeReview: "コードレビュー", + credits: "クレジット", + }, duration: { days: "{{value}}日", hours: "{{value}}時間", diff --git a/packages/app/src/i18n/resources/ko.ts b/packages/app/src/i18n/resources/ko.ts index b03cbf0fc3..8e2d3725b2 100644 --- a/packages/app/src/i18n/resources/ko.ts +++ b/packages/app/src/i18n/resources/ko.ts @@ -1980,6 +1980,12 @@ export const ko: TranslationResources = { used: "{{percentage}} 사용됨", remaining: "{{amount}} 남음", }, + labels: { + session: "세션", + weekly: "주간", + codeReview: "코드 검토", + credits: "크레딧", + }, duration: { days: "{{value}}일", hours: "{{value}}시간", diff --git a/packages/app/src/i18n/resources/pt-BR.ts b/packages/app/src/i18n/resources/pt-BR.ts index bae71a564a..778529b402 100644 --- a/packages/app/src/i18n/resources/pt-BR.ts +++ b/packages/app/src/i18n/resources/pt-BR.ts @@ -2003,6 +2003,12 @@ export const ptBR: TranslationResources = { used: "{{percentage}} usado", remaining: "{{amount}} restantes", }, + labels: { + session: "Sessão", + weekly: "Semanal", + codeReview: "Revisão de código", + credits: "Créditos", + }, duration: { days: "{{value}} d", hours: "{{value}} h", diff --git a/packages/app/src/i18n/resources/ru.ts b/packages/app/src/i18n/resources/ru.ts index 5eac9f746f..1c41ab3106 100644 --- a/packages/app/src/i18n/resources/ru.ts +++ b/packages/app/src/i18n/resources/ru.ts @@ -2003,6 +2003,12 @@ export const ru: TranslationResources = { used: "Использовано: {{percentage}}", remaining: "Осталось: {{amount}}", }, + labels: { + session: "Сессия", + weekly: "Еженедельно", + codeReview: "Проверка кода", + credits: "Кредиты", + }, duration: { days: "{{value}} дн.", hours: "{{value}} ч", diff --git a/packages/app/src/i18n/resources/zh-CN.ts b/packages/app/src/i18n/resources/zh-CN.ts index 3f12ac2005..97200ec74b 100644 --- a/packages/app/src/i18n/resources/zh-CN.ts +++ b/packages/app/src/i18n/resources/zh-CN.ts @@ -1948,6 +1948,12 @@ export const zhCN: TranslationResources = { used: "已使用 {{percentage}}", remaining: "剩余 {{amount}}", }, + labels: { + session: "会话", + weekly: "每周", + codeReview: "代码审查", + credits: "积分", + }, duration: { days: "{{value}}天", hours: "{{value}}小时", diff --git a/packages/app/src/usage/balance-bar.tsx b/packages/app/src/usage/balance-bar.tsx index b8339c1038..2e5302c938 100644 --- a/packages/app/src/usage/balance-bar.tsx +++ b/packages/app/src/usage/balance-bar.tsx @@ -3,7 +3,7 @@ import type { TFunction } from "i18next"; import { useTranslation } from "react-i18next"; import { Text, View, type StyleProp, type ViewStyle } from "react-native"; import { StyleSheet } from "react-native-unistyles"; -import { clampPct, formatAmount, formatResetLabel } from "./format"; +import { clampPct, formatAmount, formatProviderUsageLabel, formatResetLabel } from "./format"; import type { UsageBalance, UsageTone } from "./types"; import { useRelativeTimeTick } from "./use-relative-time-tick"; @@ -66,7 +66,7 @@ export function UsageBalanceBar({ balance }: { balance: UsageBalance }) { - {balance.label} + {formatProviderUsageLabel(balance.id, balance.label)} { expect(formatAmount(1_234, "tokens", "en")).toBe("1.2K"); expect(formatAmount(1_234, "tokens", "ar")).toBe("1.2\u00a0ألف"); }); + + it("localizes known provider-usage labels and preserves provider-specific labels", async () => { + await i18n.changeLanguage("ko"); + + expect(formatProviderUsageLabel("session", "Session")).toBe("세션"); + expect(formatProviderUsageLabel("credits", "Credits")).toBe("크레딧"); + expect(formatProviderUsageLabel("custom_limit", "Custom limit")).toBe("Custom limit"); + }); });