fix(app): localize provider usage labels

This commit is contained in:
Michael Wu
2026-10-03 05:07:05 +09:00
parent b57a03c005
commit 78f7267f8b
12 changed files with 73 additions and 4 deletions
+1 -1
View File
@@ -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.
+6
View File
@@ -1970,6 +1970,12 @@ export const ar: TranslationResources = {
used: "تم استخدام {{percentage}}",
remaining: "متبقٍ {{amount}}",
},
labels: {
session: "الجلسة",
weekly: "أسبوعي",
codeReview: "مراجعة التعليمات البرمجية",
credits: "الأرصدة",
},
duration: {
days: "{{value}} يوم",
days_zero: "{{value}} يوم",
+6
View File
@@ -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",
+6
View File
@@ -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",
+6
View File
@@ -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",
+6
View File
@@ -1990,6 +1990,12 @@ export const ja: TranslationResources = {
used: "{{percentage}} 使用済み",
remaining: "残り {{amount}}",
},
labels: {
session: "セッション",
weekly: "週間",
codeReview: "コードレビュー",
credits: "クレジット",
},
duration: {
days: "{{value}}日",
hours: "{{value}}時間",
+6
View File
@@ -1980,6 +1980,12 @@ export const ko: TranslationResources = {
used: "{{percentage}} 사용됨",
remaining: "{{amount}} 남음",
},
labels: {
session: "세션",
weekly: "주간",
codeReview: "코드 검토",
credits: "크레딧",
},
duration: {
days: "{{value}}일",
hours: "{{value}}시간",
+6
View File
@@ -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",
+6
View File
@@ -2003,6 +2003,12 @@ export const ru: TranslationResources = {
used: "Использовано: {{percentage}}",
remaining: "Осталось: {{amount}}",
},
labels: {
session: "Сессия",
weekly: "Еженедельно",
codeReview: "Проверка кода",
credits: "Кредиты",
},
duration: {
days: "{{value}} дн.",
hours: "{{value}} ч",
+6
View File
@@ -1948,6 +1948,12 @@ export const zhCN: TranslationResources = {
used: "已使用 {{percentage}}",
remaining: "剩余 {{amount}}",
},
labels: {
session: "会话",
weekly: "每周",
codeReview: "代码审查",
credits: "积分",
},
duration: {
days: "{{value}}天",
hours: "{{value}}小时",
+2 -2
View File
@@ -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 }) {
<View style={styles.container}>
<View style={styles.labelRow}>
<Text style={styles.label} numberOfLines={1}>
{balance.label}
{formatProviderUsageLabel(balance.id, balance.label)}
</Text>
<Text
style={styles.value}
+16 -1
View File
@@ -1,6 +1,13 @@
import { afterEach, beforeEach, describe, expect, it } from "vitest";
import { i18n } from "@/i18n/i18next";
import { formatAgo, formatAmount, formatPct, formatResetLabel, formatRunsOutLabel } from "./format";
import {
formatAgo,
formatAmount,
formatPct,
formatProviderUsageLabel,
formatResetLabel,
formatRunsOutLabel,
} from "./format";
const NOW = Date.parse("2026-07-19T00:00:00.000Z");
@@ -67,4 +74,12 @@ describe("formatAmount", () => {
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");
});
});