From 44ff81642b9b625b9a5ba583c08ecd0dc7561531 Mon Sep 17 00:00:00 2001
From: obchain <167975049+obchain@users.noreply.github.com>
Date: Mon, 22 Jun 2026 19:15:57 +0530
Subject: [PATCH] i18n(sync-audit): localize the memory sync-audit panel
(#3706)
---
.../intelligence/SyncAuditPanel.test.tsx | 15 ++++++++++++-
.../intelligence/SyncAuditPanel.tsx | 21 ++++++++++++-------
app/src/lib/i18n/ar.ts | 15 +++++++++++++
app/src/lib/i18n/bn.ts | 15 +++++++++++++
app/src/lib/i18n/de.ts | 15 +++++++++++++
app/src/lib/i18n/en.ts | 15 +++++++++++++
app/src/lib/i18n/es.ts | 15 +++++++++++++
app/src/lib/i18n/fr.ts | 15 +++++++++++++
app/src/lib/i18n/hi.ts | 15 +++++++++++++
app/src/lib/i18n/id.ts | 15 +++++++++++++
app/src/lib/i18n/it.ts | 15 +++++++++++++
app/src/lib/i18n/ko.ts | 15 +++++++++++++
app/src/lib/i18n/pl.ts | 15 +++++++++++++
app/src/lib/i18n/pt.ts | 15 +++++++++++++
app/src/lib/i18n/ru.ts | 15 +++++++++++++
app/src/lib/i18n/zh-CN.ts | 15 +++++++++++++
16 files changed, 237 insertions(+), 9 deletions(-)
diff --git a/app/src/components/intelligence/SyncAuditPanel.test.tsx b/app/src/components/intelligence/SyncAuditPanel.test.tsx
index 1cd37e9d6..988edbc60 100644
--- a/app/src/components/intelligence/SyncAuditPanel.test.tsx
+++ b/app/src/components/intelligence/SyncAuditPanel.test.tsx
@@ -17,7 +17,7 @@ import { render, screen, waitFor, within } from '@testing-library/react';
import { beforeEach, describe, expect, it, vi } from 'vitest';
import type { SyncAuditEntry } from '../../utils/tauriCommands';
-import { SyncAuditPanel } from './SyncAuditPanel';
+import { SyncAuditPanel, timeAgo } from './SyncAuditPanel';
const mockAuditLog = vi.fn();
@@ -166,3 +166,16 @@ describe('', () => {
expect(within(table).getByText('Cost')).toBeInTheDocument();
});
});
+
+describe('timeAgo', () => {
+ // Resolve to the fallback English so the `{n}` interpolation is exercised.
+ const t = (_key: string, fallback?: string) => fallback ?? _key;
+ const isoAgo = (ms: number) => new Date(Date.now() - ms).toISOString();
+
+ it('covers every relative-time bucket and substitutes the {n} placeholder', () => {
+ expect(timeAgo(isoAgo(0), t)).toBe('just now');
+ expect(timeAgo(isoAgo(5 * 60_000), t)).toBe('5m ago');
+ expect(timeAgo(isoAgo(3 * 60 * 60_000), t)).toBe('3h ago');
+ expect(timeAgo(isoAgo(2 * 24 * 60 * 60_000), t)).toBe('2d ago');
+ });
+});
diff --git a/app/src/components/intelligence/SyncAuditPanel.tsx b/app/src/components/intelligence/SyncAuditPanel.tsx
index 852a7b90c..21a998acd 100644
--- a/app/src/components/intelligence/SyncAuditPanel.tsx
+++ b/app/src/components/intelligence/SyncAuditPanel.tsx
@@ -35,15 +35,18 @@ function scopeLabel(scope: string): string {
return scope;
}
-function timeAgo(iso: string): string {
+// `t` is threaded in because this is a module-level helper with no hook scope.
+// The `{n}` placeholder follows the codebase's interpolation convention
+// (t(...).replace('{n}', value)) — `t()` itself does not interpolate params.
+export function timeAgo(iso: string, t: (key: string, fallback?: string) => string): string {
const diff = Date.now() - new Date(iso).getTime();
const mins = Math.floor(diff / 60_000);
- if (mins < 1) return 'just now';
- if (mins < 60) return `${mins}m ago`;
+ if (mins < 1) return t('sync.timeAgo.justNow', 'just now');
+ if (mins < 60) return t('sync.timeAgo.minutes', '{n}m ago').replace('{n}', String(mins));
const hours = Math.floor(mins / 60);
- if (hours < 24) return `${hours}h ago`;
+ if (hours < 24) return t('sync.timeAgo.hours', '{n}h ago').replace('{n}', String(hours));
const days = Math.floor(hours / 24);
- return `${days}d ago`;
+ return t('sync.timeAgo.days', '{n}d ago').replace('{n}', String(days));
}
export function SyncAuditPanel() {
@@ -126,7 +129,7 @@ export function SyncAuditPanel() {
- {timeAgo(e.timestamp)}
+ {timeAgo(e.timestamp, t)}
|
|
{e.success ? (
-
+
✓
) : (
-
+
✗
)}
diff --git a/app/src/lib/i18n/ar.ts b/app/src/lib/i18n/ar.ts
index ce0a8882e..7aa15da41 100644
--- a/app/src/lib/i18n/ar.ts
+++ b/app/src/lib/i18n/ar.ts
@@ -108,6 +108,21 @@ const messages: TranslationMap = {
'common.create': 'إنشاء',
'common.search': 'بحث',
'common.loading': 'جارٍ التحميل…',
+ 'sync.runs': 'عمليات مزامنة',
+ 'sync.totalCost': 'الإجمالي',
+ 'sync.when': 'الوقت',
+ 'sync.source': 'المصدر',
+ 'sync.items': 'العناصر',
+ 'sync.tokens': 'الرموز',
+ 'sync.cost': 'التكلفة',
+ 'sync.duration': 'المدة',
+ 'sync.noAuditEntries': 'لم تُسجَّل أي عمليات مزامنة بعد.',
+ 'sync.timeAgo.justNow': 'الآن',
+ 'sync.timeAgo.minutes': 'قبل {n} د',
+ 'sync.timeAgo.hours': 'قبل {n} س',
+ 'sync.timeAgo.days': 'قبل {n} ي',
+ 'sync.status.success': 'نجاح',
+ 'sync.status.failed': 'فشل',
'common.error': 'خطأ',
'common.success': 'نجاح',
'common.back': 'رجوع',
diff --git a/app/src/lib/i18n/bn.ts b/app/src/lib/i18n/bn.ts
index 23b482783..6a537a5b7 100644
--- a/app/src/lib/i18n/bn.ts
+++ b/app/src/lib/i18n/bn.ts
@@ -109,6 +109,21 @@ const messages: TranslationMap = {
'common.create': 'তৈরি করুন',
'common.search': 'খুঁজুন',
'common.loading': 'লোড হচ্ছে…',
+ 'sync.runs': 'সিঙ্ক রান',
+ 'sync.totalCost': 'মোট',
+ 'sync.when': 'কখন',
+ 'sync.source': 'উৎস',
+ 'sync.items': 'আইটেম',
+ 'sync.tokens': 'টোকেন',
+ 'sync.cost': 'খরচ',
+ 'sync.duration': 'সময়কাল',
+ 'sync.noAuditEntries': 'এখনও কোনো সিঙ্ক রান রেকর্ড করা হয়নি।',
+ 'sync.timeAgo.justNow': 'এইমাত্র',
+ 'sync.timeAgo.minutes': '{n} মি. আগে',
+ 'sync.timeAgo.hours': '{n} ঘ. আগে',
+ 'sync.timeAgo.days': '{n} দি. আগে',
+ 'sync.status.success': 'সফল',
+ 'sync.status.failed': 'ব্যর্থ',
'common.error': 'ত্রুটি',
'common.success': 'সফল',
'common.back': 'পেছনে',
diff --git a/app/src/lib/i18n/de.ts b/app/src/lib/i18n/de.ts
index c222e14bd..4fc0e2422 100644
--- a/app/src/lib/i18n/de.ts
+++ b/app/src/lib/i18n/de.ts
@@ -109,6 +109,21 @@ const messages: TranslationMap = {
'common.create': 'Erstellen',
'common.search': 'Suchen',
'common.loading': 'Laden…',
+ 'sync.runs': 'Synchronisierungen',
+ 'sync.totalCost': 'Gesamt',
+ 'sync.when': 'Wann',
+ 'sync.source': 'Quelle',
+ 'sync.items': 'Elemente',
+ 'sync.tokens': 'Tokens',
+ 'sync.cost': 'Kosten',
+ 'sync.duration': 'Dauer',
+ 'sync.noAuditEntries': 'Noch keine Synchronisierungen aufgezeichnet.',
+ 'sync.timeAgo.justNow': 'gerade eben',
+ 'sync.timeAgo.minutes': 'vor {n} Min.',
+ 'sync.timeAgo.hours': 'vor {n} Std.',
+ 'sync.timeAgo.days': 'vor {n} T.',
+ 'sync.status.success': 'Erfolg',
+ 'sync.status.failed': 'Fehlgeschlagen',
'common.error': 'Fehler',
'common.success': 'Erfolg',
'common.back': 'Zurück',
diff --git a/app/src/lib/i18n/en.ts b/app/src/lib/i18n/en.ts
index 92eda4da6..ca146bcf3 100644
--- a/app/src/lib/i18n/en.ts
+++ b/app/src/lib/i18n/en.ts
@@ -97,6 +97,21 @@ const en: TranslationMap = {
'common.create': 'Create',
'common.search': 'Search',
'common.loading': 'Loading…',
+ 'sync.runs': 'sync runs',
+ 'sync.totalCost': 'total',
+ 'sync.when': 'When',
+ 'sync.source': 'Source',
+ 'sync.items': 'Items',
+ 'sync.tokens': 'Tokens',
+ 'sync.cost': 'Cost',
+ 'sync.duration': 'Duration',
+ 'sync.noAuditEntries': 'No sync runs recorded yet.',
+ 'sync.timeAgo.justNow': 'just now',
+ 'sync.timeAgo.minutes': '{n}m ago',
+ 'sync.timeAgo.hours': '{n}h ago',
+ 'sync.timeAgo.days': '{n}d ago',
+ 'sync.status.success': 'Success',
+ 'sync.status.failed': 'Failed',
'common.error': 'Error',
'common.success': 'Success',
'common.back': 'Back',
diff --git a/app/src/lib/i18n/es.ts b/app/src/lib/i18n/es.ts
index 9187c9039..5ff7800bc 100644
--- a/app/src/lib/i18n/es.ts
+++ b/app/src/lib/i18n/es.ts
@@ -109,6 +109,21 @@ const messages: TranslationMap = {
'common.create': 'Crear',
'common.search': 'Buscar',
'common.loading': 'cargando…',
+ 'sync.runs': 'sincronizaciones',
+ 'sync.totalCost': 'total',
+ 'sync.when': 'Cuándo',
+ 'sync.source': 'Origen',
+ 'sync.items': 'Elementos',
+ 'sync.tokens': 'Tokens',
+ 'sync.cost': 'Costo',
+ 'sync.duration': 'Duración',
+ 'sync.noAuditEntries': 'Aún no hay sincronizaciones registradas.',
+ 'sync.timeAgo.justNow': 'ahora mismo',
+ 'sync.timeAgo.minutes': 'hace {n} min',
+ 'sync.timeAgo.hours': 'hace {n} h',
+ 'sync.timeAgo.days': 'hace {n} d',
+ 'sync.status.success': 'Correcto',
+ 'sync.status.failed': 'Fallido',
'common.error': 'error',
'common.success': 'Éxito',
'common.back': 'Atrás',
diff --git a/app/src/lib/i18n/fr.ts b/app/src/lib/i18n/fr.ts
index aad89ddf9..7810a2d6c 100644
--- a/app/src/lib/i18n/fr.ts
+++ b/app/src/lib/i18n/fr.ts
@@ -110,6 +110,21 @@ const messages: TranslationMap = {
'common.create': 'Créer',
'common.search': 'Rechercher',
'common.loading': 'chargement…',
+ 'sync.runs': 'synchronisations',
+ 'sync.totalCost': 'total',
+ 'sync.when': 'Quand',
+ 'sync.source': 'Source',
+ 'sync.items': 'Éléments',
+ 'sync.tokens': 'Jetons',
+ 'sync.cost': 'Coût',
+ 'sync.duration': 'Durée',
+ 'sync.noAuditEntries': "Aucune synchronisation enregistrée pour l'instant.",
+ 'sync.timeAgo.justNow': "à l'instant",
+ 'sync.timeAgo.minutes': 'il y a {n} min',
+ 'sync.timeAgo.hours': 'il y a {n} h',
+ 'sync.timeAgo.days': 'il y a {n} j',
+ 'sync.status.success': 'Réussi',
+ 'sync.status.failed': 'Échec',
'common.error': 'Erreur',
'common.success': 'Succès',
'common.back': 'Retour',
diff --git a/app/src/lib/i18n/hi.ts b/app/src/lib/i18n/hi.ts
index 7711ee19d..b73db29bb 100644
--- a/app/src/lib/i18n/hi.ts
+++ b/app/src/lib/i18n/hi.ts
@@ -108,6 +108,21 @@ const messages: TranslationMap = {
'common.create': 'बनाएं',
'common.search': 'सर्च करें',
'common.loading': 'लोड हो रहा है…',
+ 'sync.runs': 'सिंक रन',
+ 'sync.totalCost': 'कुल',
+ 'sync.when': 'कब',
+ 'sync.source': 'स्रोत',
+ 'sync.items': 'आइटम',
+ 'sync.tokens': 'टोकन',
+ 'sync.cost': 'लागत',
+ 'sync.duration': 'अवधि',
+ 'sync.noAuditEntries': 'अभी तक कोई सिंक रन दर्ज नहीं हुआ।',
+ 'sync.timeAgo.justNow': 'अभी',
+ 'sync.timeAgo.minutes': '{n} मि. पहले',
+ 'sync.timeAgo.hours': '{n} घं. पहले',
+ 'sync.timeAgo.days': '{n} दि. पहले',
+ 'sync.status.success': 'सफल',
+ 'sync.status.failed': 'विफल',
'common.error': 'एरर',
'common.success': 'सफल',
'common.back': 'वापस',
diff --git a/app/src/lib/i18n/id.ts b/app/src/lib/i18n/id.ts
index 52b0bf6d3..93ed9f276 100644
--- a/app/src/lib/i18n/id.ts
+++ b/app/src/lib/i18n/id.ts
@@ -108,6 +108,21 @@ const messages: TranslationMap = {
'common.create': 'Buat',
'common.search': 'Cari',
'common.loading': 'memuat…',
+ 'sync.runs': 'sinkronisasi',
+ 'sync.totalCost': 'total',
+ 'sync.when': 'Kapan',
+ 'sync.source': 'Sumber',
+ 'sync.items': 'Item',
+ 'sync.tokens': 'Token',
+ 'sync.cost': 'Biaya',
+ 'sync.duration': 'Durasi',
+ 'sync.noAuditEntries': 'Belum ada sinkronisasi yang tercatat.',
+ 'sync.timeAgo.justNow': 'baru saja',
+ 'sync.timeAgo.minutes': '{n} mnt lalu',
+ 'sync.timeAgo.hours': '{n} jam lalu',
+ 'sync.timeAgo.days': '{n} hr lalu',
+ 'sync.status.success': 'Berhasil',
+ 'sync.status.failed': 'Gagal',
'common.error': 'Kesalahan',
'common.success': 'Berhasil',
'common.back': 'Kembali',
diff --git a/app/src/lib/i18n/it.ts b/app/src/lib/i18n/it.ts
index 4342635f5..62cd3050a 100644
--- a/app/src/lib/i18n/it.ts
+++ b/app/src/lib/i18n/it.ts
@@ -110,6 +110,21 @@ const messages: TranslationMap = {
'common.create': 'Crea',
'common.search': 'Cerca',
'common.loading': 'caricamento…',
+ 'sync.runs': 'sincronizzazioni',
+ 'sync.totalCost': 'totale',
+ 'sync.when': 'Quando',
+ 'sync.source': 'Origine',
+ 'sync.items': 'Elementi',
+ 'sync.tokens': 'Token',
+ 'sync.cost': 'Costo',
+ 'sync.duration': 'Durata',
+ 'sync.noAuditEntries': 'Nessuna sincronizzazione registrata finora.',
+ 'sync.timeAgo.justNow': 'proprio ora',
+ 'sync.timeAgo.minutes': '{n} min fa',
+ 'sync.timeAgo.hours': '{n} h fa',
+ 'sync.timeAgo.days': '{n} g fa',
+ 'sync.status.success': 'Riuscito',
+ 'sync.status.failed': 'Non riuscito',
'common.error': 'Errore',
'common.success': 'Successo',
'common.back': 'Indietro',
diff --git a/app/src/lib/i18n/ko.ts b/app/src/lib/i18n/ko.ts
index 363fc29ad..47f0f7ed6 100644
--- a/app/src/lib/i18n/ko.ts
+++ b/app/src/lib/i18n/ko.ts
@@ -109,6 +109,21 @@ const messages: TranslationMap = {
'common.create': '생성',
'common.search': '검색',
'common.loading': '로딩 중…',
+ 'sync.runs': '동기화 실행',
+ 'sync.totalCost': '합계',
+ 'sync.when': '시간',
+ 'sync.source': '소스',
+ 'sync.items': '항목',
+ 'sync.tokens': '토큰',
+ 'sync.cost': '비용',
+ 'sync.duration': '기간',
+ 'sync.noAuditEntries': '아직 기록된 동기화 실행이 없습니다.',
+ 'sync.timeAgo.justNow': '방금',
+ 'sync.timeAgo.minutes': '{n}분 전',
+ 'sync.timeAgo.hours': '{n}시간 전',
+ 'sync.timeAgo.days': '{n}일 전',
+ 'sync.status.success': '성공',
+ 'sync.status.failed': '실패',
'common.error': '오류',
'common.success': '성공',
'common.back': '뒤로',
diff --git a/app/src/lib/i18n/pl.ts b/app/src/lib/i18n/pl.ts
index a67fe5b49..e81bcb04d 100644
--- a/app/src/lib/i18n/pl.ts
+++ b/app/src/lib/i18n/pl.ts
@@ -109,6 +109,21 @@ const messages: TranslationMap = {
'common.create': 'Utwórz',
'common.search': 'Szukaj',
'common.loading': 'ładowanie…',
+ 'sync.runs': 'synchronizacji',
+ 'sync.totalCost': 'razem',
+ 'sync.when': 'Kiedy',
+ 'sync.source': 'Źródło',
+ 'sync.items': 'Elementy',
+ 'sync.tokens': 'Tokeny',
+ 'sync.cost': 'Koszt',
+ 'sync.duration': 'Czas trwania',
+ 'sync.noAuditEntries': 'Brak zarejestrowanych synchronizacji.',
+ 'sync.timeAgo.justNow': 'przed chwilą',
+ 'sync.timeAgo.minutes': '{n} min temu',
+ 'sync.timeAgo.hours': '{n} godz. temu',
+ 'sync.timeAgo.days': '{n} dni temu',
+ 'sync.status.success': 'Sukces',
+ 'sync.status.failed': 'Niepowodzenie',
'common.error': 'Błąd',
'common.success': 'Sukces',
'common.back': 'Wstecz',
diff --git a/app/src/lib/i18n/pt.ts b/app/src/lib/i18n/pt.ts
index dfd381d15..963a1c068 100644
--- a/app/src/lib/i18n/pt.ts
+++ b/app/src/lib/i18n/pt.ts
@@ -110,6 +110,21 @@ const messages: TranslationMap = {
'common.create': 'Criar',
'common.search': 'Pesquisar',
'common.loading': 'carregando…',
+ 'sync.runs': 'sincronizações',
+ 'sync.totalCost': 'total',
+ 'sync.when': 'Quando',
+ 'sync.source': 'Origem',
+ 'sync.items': 'Itens',
+ 'sync.tokens': 'Tokens',
+ 'sync.cost': 'Custo',
+ 'sync.duration': 'Duração',
+ 'sync.noAuditEntries': 'Nenhuma sincronização registrada ainda.',
+ 'sync.timeAgo.justNow': 'agora mesmo',
+ 'sync.timeAgo.minutes': 'há {n} min',
+ 'sync.timeAgo.hours': 'há {n} h',
+ 'sync.timeAgo.days': 'há {n} d',
+ 'sync.status.success': 'Sucesso',
+ 'sync.status.failed': 'Falhou',
'common.error': 'Erro',
'common.success': 'Sucesso',
'common.back': 'Voltar',
diff --git a/app/src/lib/i18n/ru.ts b/app/src/lib/i18n/ru.ts
index 738b10726..071f421c3 100644
--- a/app/src/lib/i18n/ru.ts
+++ b/app/src/lib/i18n/ru.ts
@@ -108,6 +108,21 @@ const messages: TranslationMap = {
'common.create': 'Создать',
'common.search': 'Поиск',
'common.loading': 'загрузка…',
+ 'sync.runs': 'синхронизаций',
+ 'sync.totalCost': 'итого',
+ 'sync.when': 'Когда',
+ 'sync.source': 'Источник',
+ 'sync.items': 'Элементы',
+ 'sync.tokens': 'Токены',
+ 'sync.cost': 'Стоимость',
+ 'sync.duration': 'Длительность',
+ 'sync.noAuditEntries': 'Синхронизаций пока не зафиксировано.',
+ 'sync.timeAgo.justNow': 'только что',
+ 'sync.timeAgo.minutes': '{n} мин назад',
+ 'sync.timeAgo.hours': '{n} ч назад',
+ 'sync.timeAgo.days': '{n} дн назад',
+ 'sync.status.success': 'Успешно',
+ 'sync.status.failed': 'Сбой',
'common.error': 'Ошибка',
'common.success': 'Готово',
'common.back': 'Назад',
diff --git a/app/src/lib/i18n/zh-CN.ts b/app/src/lib/i18n/zh-CN.ts
index 2bb563374..18a1ce40d 100644
--- a/app/src/lib/i18n/zh-CN.ts
+++ b/app/src/lib/i18n/zh-CN.ts
@@ -108,6 +108,21 @@ const messages: TranslationMap = {
'common.create': '创建',
'common.search': '搜索',
'common.loading': '加载中…',
+ 'sync.runs': '次同步',
+ 'sync.totalCost': '合计',
+ 'sync.when': '时间',
+ 'sync.source': '来源',
+ 'sync.items': '条目',
+ 'sync.tokens': '令牌',
+ 'sync.cost': '费用',
+ 'sync.duration': '时长',
+ 'sync.noAuditEntries': '尚未记录任何同步。',
+ 'sync.timeAgo.justNow': '刚刚',
+ 'sync.timeAgo.minutes': '{n}分钟前',
+ 'sync.timeAgo.hours': '{n}小时前',
+ 'sync.timeAgo.days': '{n}天前',
+ 'sync.status.success': '成功',
+ 'sync.status.failed': '失败',
'common.error': '错误',
'common.success': '成功',
'common.back': '返回',
|