From 16e5dcf7b263dada6d8db15caa151c4335b6334b Mon Sep 17 00:00:00 2001 From: Will Miao Date: Fri, 19 Jun 2026 13:37:01 +0800 Subject: [PATCH] feat(i18n): internationalize statistics page strings across all locales --- locales/de.json | 45 +++++++++++++++++++- locales/en.json | 45 +++++++++++++++++++- locales/es.json | 45 +++++++++++++++++++- locales/fr.json | 45 +++++++++++++++++++- locales/he.json | 45 +++++++++++++++++++- locales/ja.json | 45 +++++++++++++++++++- locales/ko.json | 45 +++++++++++++++++++- locales/ru.json | 45 +++++++++++++++++++- locales/zh-CN.json | 45 +++++++++++++++++++- locales/zh-TW.json | 45 +++++++++++++++++++- static/js/statistics.js | 92 ++++++++++++++++++++--------------------- tests/i18n/test_i18n.py | 1 + 12 files changed, 476 insertions(+), 67 deletions(-) diff --git a/locales/de.json b/locales/de.json index c71b6e25..cf596bc8 100644 --- a/locales/de.json +++ b/locales/de.json @@ -1016,6 +1016,18 @@ "storage": "Speicher", "insights": "Erkenntnisse" }, + "metrics": { + "totalModels": "Modelle gesamt", + "totalStorage": "Speicher gesamt", + "totalGenerations": "Generationen gesamt", + "usageRate": "Nutzungsrate", + "loras": "LoRAs", + "checkpoints": "Checkpoints", + "embeddings": "Embeddings", + "uniqueTags": "Einzigartige Tags", + "unusedModels": "Ungenutzte Modelle", + "avgUsesPerModel": "Ø Nutzungen/Modell" + }, "usage": { "mostUsedLoras": "Meistgenutzte LoRAs", "mostUsedCheckpoints": "Meistgenutzte Checkpoints", @@ -1033,13 +1045,42 @@ }, "insights": { "smartInsights": "Intelligente Erkenntnisse", - "recommendations": "Empfehlungen" + "recommendations": "Empfehlungen", + "noInsights": "Keine Erkenntnisse verfügbar" }, "charts": { "collectionOverview": "Sammlungsübersicht", "baseModelDistribution": "Basis-Modell-Verteilung", "usageTrends": "Nutzungstrends (Letzte 30 Tage)", - "usageDistribution": "Nutzungsverteilung" + "usageDistribution": "Nutzungsverteilung", + "date": "Datum", + "usageCount": "Nutzungsanzahl", + "fileSizeBytes": "Dateigröße (Bytes)", + "models": "Modelle", + "loraUsage": "LoRA-Nutzung", + "checkpointUsage": "Checkpoint-Nutzung", + "embeddingUsage": "Embedding-Nutzung" + }, + "modelTypes": { + "lora": "LoRA", + "locon": "LyCORIS", + "dora": "DoRA", + "checkpoint": "Checkpoint", + "diffusion_model": "Diffusionsmodell", + "embedding": "Embeddings" + }, + "placeholders": { + "loading": "Lädt...", + "noModels": "Keine Modelle gefunden", + "errorLoading": "Fehler beim Laden der Daten", + "noStorageData": "Keine Speicherdaten verfügbar", + "rootFolder": "Root", + "chartLibraryMissing": "Diagramm benötigt Chart.js-Bibliothek" + }, + "tooltips": { + "tagCount": "{tag}: {count} Modelle", + "chartUsage": "{name}: {size}, {count} Nutzungen", + "chartPercentage": "{label}: {value} ({pct}%)" } }, "modals": { diff --git a/locales/en.json b/locales/en.json index c79f7ef8..7ca866be 100644 --- a/locales/en.json +++ b/locales/en.json @@ -1016,6 +1016,18 @@ "storage": "Storage", "insights": "Insights" }, + "metrics": { + "totalModels": "Total Models", + "totalStorage": "Total Storage", + "totalGenerations": "Total Generations", + "usageRate": "Usage Rate", + "loras": "LoRAs", + "checkpoints": "Checkpoints", + "embeddings": "Embeddings", + "uniqueTags": "Unique Tags", + "unusedModels": "Unused Models", + "avgUsesPerModel": "Avg. Uses/Model" + }, "usage": { "mostUsedLoras": "Most Used LoRAs", "mostUsedCheckpoints": "Most Used Checkpoints", @@ -1033,13 +1045,42 @@ }, "insights": { "smartInsights": "Smart Insights", - "recommendations": "Recommendations" + "recommendations": "Recommendations", + "noInsights": "No insights available" }, "charts": { "collectionOverview": "Collection Overview", "baseModelDistribution": "Base Model Distribution", "usageTrends": "Usage Trends (Last 30 Days)", - "usageDistribution": "Usage Distribution" + "usageDistribution": "Usage Distribution", + "date": "Date", + "usageCount": "Usage Count", + "fileSizeBytes": "File Size (bytes)", + "models": "Models", + "loraUsage": "LoRA Usage", + "checkpointUsage": "Checkpoint Usage", + "embeddingUsage": "Embedding Usage" + }, + "modelTypes": { + "lora": "LoRA", + "locon": "LyCORIS", + "dora": "DoRA", + "checkpoint": "Checkpoint", + "diffusion_model": "Diffusion Model", + "embedding": "Embeddings" + }, + "placeholders": { + "loading": "Loading...", + "noModels": "No models found", + "errorLoading": "Error loading data", + "noStorageData": "No storage data available", + "rootFolder": "Root", + "chartLibraryMissing": "Chart requires Chart.js library" + }, + "tooltips": { + "tagCount": "{tag}: {count} models", + "chartUsage": "{name}: {size}, {count} uses", + "chartPercentage": "{label}: {value} ({pct}%)" } }, "modals": { diff --git a/locales/es.json b/locales/es.json index 06f91d23..cf2b63a2 100644 --- a/locales/es.json +++ b/locales/es.json @@ -1016,6 +1016,18 @@ "storage": "Almacenamiento", "insights": "Perspectivas" }, + "metrics": { + "totalModels": "Total de modelos", + "totalStorage": "Almacenamiento total", + "totalGenerations": "Generaciones totales", + "usageRate": "Tasa de uso", + "loras": "LoRAs", + "checkpoints": "Puntos de control", + "embeddings": "Embeddings", + "uniqueTags": "Etiquetas únicas", + "unusedModels": "Modelos no usados", + "avgUsesPerModel": "Prom. usos/modelo" + }, "usage": { "mostUsedLoras": "LoRAs más utilizados", "mostUsedCheckpoints": "Checkpoints más utilizados", @@ -1033,13 +1045,42 @@ }, "insights": { "smartInsights": "Perspectivas inteligentes", - "recommendations": "Recomendaciones" + "recommendations": "Recomendaciones", + "noInsights": "No hay información disponible" }, "charts": { "collectionOverview": "Resumen de colección", "baseModelDistribution": "Distribución de modelo base", "usageTrends": "Tendencias de uso (Últimos 30 días)", - "usageDistribution": "Distribución de uso" + "usageDistribution": "Distribución de uso", + "date": "Fecha", + "usageCount": "Conteo de uso", + "fileSizeBytes": "Tamaño del archivo (bytes)", + "models": "Modelos", + "loraUsage": "Uso de LoRA", + "checkpointUsage": "Uso de Checkpoint", + "embeddingUsage": "Uso de Embedding" + }, + "modelTypes": { + "lora": "LoRA", + "locon": "LyCORIS", + "dora": "DoRA", + "checkpoint": "Punto de control", + "diffusion_model": "Modelo de difusión", + "embedding": "Embeddings" + }, + "placeholders": { + "loading": "Cargando...", + "noModels": "No se encontraron modelos", + "errorLoading": "Error al cargar datos", + "noStorageData": "No hay datos de almacenamiento disponibles", + "rootFolder": "Raíz", + "chartLibraryMissing": "El gráfico requiere la librería Chart.js" + }, + "tooltips": { + "tagCount": "{tag}: {count} modelos", + "chartUsage": "{name}: {size}, {count} usos", + "chartPercentage": "{label}: {value} ({pct}%)" } }, "modals": { diff --git a/locales/fr.json b/locales/fr.json index df01f5fe..c8d27e6d 100644 --- a/locales/fr.json +++ b/locales/fr.json @@ -1016,6 +1016,18 @@ "storage": "Stockage", "insights": "Aperçus" }, + "metrics": { + "totalModels": "Total des modèles", + "totalStorage": "Stockage total", + "totalGenerations": "Générations totales", + "usageRate": "Taux d'utilisation", + "loras": "LoRAs", + "checkpoints": "Points de contrôle", + "embeddings": "Embeddings", + "uniqueTags": "Tags uniques", + "unusedModels": "Modèles inutilisés", + "avgUsesPerModel": "Moy. utilisations/modèle" + }, "usage": { "mostUsedLoras": "LoRAs les plus utilisés", "mostUsedCheckpoints": "Checkpoints les plus utilisés", @@ -1033,13 +1045,42 @@ }, "insights": { "smartInsights": "Aperçus intelligents", - "recommendations": "Recommandations" + "recommendations": "Recommandations", + "noInsights": "Aucun aperçu disponible" }, "charts": { "collectionOverview": "Aperçu de la collection", "baseModelDistribution": "Distribution des modèles de base", "usageTrends": "Tendances d'utilisation (30 derniers jours)", - "usageDistribution": "Distribution de l'utilisation" + "usageDistribution": "Distribution de l'utilisation", + "date": "Date", + "usageCount": "Nombre d'utilisations", + "fileSizeBytes": "Taille du fichier (octets)", + "models": "Modèles", + "loraUsage": "Utilisation LoRA", + "checkpointUsage": "Utilisation Checkpoint", + "embeddingUsage": "Utilisation Embedding" + }, + "modelTypes": { + "lora": "LoRA", + "locon": "LyCORIS", + "dora": "DoRA", + "checkpoint": "Point de contrôle", + "diffusion_model": "Modèle de diffusion", + "embedding": "Embeddings" + }, + "placeholders": { + "loading": "Chargement...", + "noModels": "Aucun modèle trouvé", + "errorLoading": "Erreur de chargement des données", + "noStorageData": "Aucune donnée de stockage disponible", + "rootFolder": "Racine", + "chartLibraryMissing": "Le graphique nécessite la bibliothèque Chart.js" + }, + "tooltips": { + "tagCount": "{tag}: {count} modèles", + "chartUsage": "{name}: {size}, {count} utilisations", + "chartPercentage": "{label}: {value} ({pct}%)" } }, "modals": { diff --git a/locales/he.json b/locales/he.json index 0ca63b59..d004a488 100644 --- a/locales/he.json +++ b/locales/he.json @@ -1016,6 +1016,18 @@ "storage": "אחסון", "insights": "תובנות" }, + "metrics": { + "totalModels": "סה\"כ דגמים", + "totalStorage": "סה\"כ אחסון", + "totalGenerations": "סה\"כ יצירות", + "usageRate": "שיעור שימוש", + "loras": "LoRA", + "checkpoints": "נקודות ביקורת", + "embeddings": "הטמעות", + "uniqueTags": "תגיות ייחודיות", + "unusedModels": "דגמים שאינם בשימוש", + "avgUsesPerModel": "ממוצע שימושים/דגם" + }, "usage": { "mostUsedLoras": "LoRAs הנפוצים ביותר", "mostUsedCheckpoints": "Checkpoints הנפוצים ביותר", @@ -1033,13 +1045,42 @@ }, "insights": { "smartInsights": "תובנות חכמות", - "recommendations": "המלצות" + "recommendations": "המלצות", + "noInsights": "אין תובנות זמינות" }, "charts": { "collectionOverview": "סקירת אוסף", "baseModelDistribution": "התפלגות מודלי בסיס", "usageTrends": "מגמות שימוש (30 יום אחרונים)", - "usageDistribution": "התפלגות שימוש" + "usageDistribution": "התפלגות שימוש", + "date": "תאריך", + "usageCount": "מספר שימושים", + "fileSizeBytes": "גודל קובץ (בתים)", + "models": "דגמים", + "loraUsage": "שימוש ב-LoRA", + "checkpointUsage": "שימוש ב-Checkpoint", + "embeddingUsage": "שימוש ב-Embedding" + }, + "modelTypes": { + "lora": "LoRA", + "locon": "LyCORIS", + "dora": "DoRA", + "checkpoint": "נקודת ביקורת", + "diffusion_model": "מודל דיפוזיה", + "embedding": "הטמעות" + }, + "placeholders": { + "loading": "טוען...", + "noModels": "לא נמצאו דגמים", + "errorLoading": "שגיאה בטעינת נתונים", + "noStorageData": "אין נתוני אחסון זמינים", + "rootFolder": "שורש", + "chartLibraryMissing": "הגרף דורש את ספריית Chart.js" + }, + "tooltips": { + "tagCount": "{tag}: {count} דגמים", + "chartUsage": "{name}: {size}, {count} שימושים", + "chartPercentage": "{label}: {value} ({pct}%)" } }, "modals": { diff --git a/locales/ja.json b/locales/ja.json index f3cce870..a43d400b 100644 --- a/locales/ja.json +++ b/locales/ja.json @@ -1016,6 +1016,18 @@ "storage": "ストレージ", "insights": "インサイト" }, + "metrics": { + "totalModels": "モデル総数", + "totalStorage": "ストレージ合計", + "totalGenerations": "生成回数合計", + "usageRate": "使用率", + "loras": "LoRA", + "checkpoints": "Checkpoint", + "embeddings": "Embedding", + "uniqueTags": "ユニークタグ", + "unusedModels": "未使用モデル", + "avgUsesPerModel": "平均使用回数/モデル" + }, "usage": { "mostUsedLoras": "最も使用されているLoRA", "mostUsedCheckpoints": "最も使用されているCheckpoint", @@ -1033,13 +1045,42 @@ }, "insights": { "smartInsights": "スマートインサイト", - "recommendations": "推奨事項" + "recommendations": "推奨事項", + "noInsights": "インサイトはありません" }, "charts": { "collectionOverview": "コレクション概要", "baseModelDistribution": "ベースモデル分布", "usageTrends": "使用傾向(過去30日)", - "usageDistribution": "使用分布" + "usageDistribution": "使用分布", + "date": "日付", + "usageCount": "使用回数", + "fileSizeBytes": "ファイルサイズ(バイト)", + "models": "モデル", + "loraUsage": "LoRA 使用量", + "checkpointUsage": "Checkpoint 使用量", + "embeddingUsage": "Embedding 使用量" + }, + "modelTypes": { + "lora": "LoRA", + "locon": "LyCORIS", + "dora": "DoRA", + "checkpoint": "Checkpoint", + "diffusion_model": "拡散モデル", + "embedding": "Embedding" + }, + "placeholders": { + "loading": "読み込み中...", + "noModels": "モデルが見つかりません", + "errorLoading": "データ読み込みエラー", + "noStorageData": "ストレージデータがありません", + "rootFolder": "ルート", + "chartLibraryMissing": "Chart.js ライブラリが必要です" + }, + "tooltips": { + "tagCount": "{tag}: {count} モデル", + "chartUsage": "{name}: {size}, {count} 回使用", + "chartPercentage": "{label}: {value} ({pct}%)" } }, "modals": { diff --git a/locales/ko.json b/locales/ko.json index efdcc75e..00afc3e3 100644 --- a/locales/ko.json +++ b/locales/ko.json @@ -1016,6 +1016,18 @@ "storage": "저장소", "insights": "인사이트" }, + "metrics": { + "totalModels": "모델 총계", + "totalStorage": "총 저장 공간", + "totalGenerations": "총 생성 횟수", + "usageRate": "사용률", + "loras": "LoRA", + "checkpoints": "Checkpoint", + "embeddings": "Embedding", + "uniqueTags": "고유 태그", + "unusedModels": "미사용 모델", + "avgUsesPerModel": "모델당 평균 사용" + }, "usage": { "mostUsedLoras": "가장 많이 사용된 LoRA", "mostUsedCheckpoints": "가장 많이 사용된 Checkpoint", @@ -1033,13 +1045,42 @@ }, "insights": { "smartInsights": "스마트 인사이트", - "recommendations": "추천" + "recommendations": "추천", + "noInsights": "인사이트 없음" }, "charts": { "collectionOverview": "컬렉션 개요", "baseModelDistribution": "베이스 모델 분포", "usageTrends": "사용량 트렌드 (최근 30일)", - "usageDistribution": "사용량 분포" + "usageDistribution": "사용량 분포", + "date": "날짜", + "usageCount": "사용 횟수", + "fileSizeBytes": "파일 크기(바이트)", + "models": "모델", + "loraUsage": "LoRA 사용량", + "checkpointUsage": "Checkpoint 사용량", + "embeddingUsage": "Embedding 사용량" + }, + "modelTypes": { + "lora": "LoRA", + "locon": "LyCORIS", + "dora": "DoRA", + "checkpoint": "Checkpoint", + "diffusion_model": "확산 모델", + "embedding": "Embedding" + }, + "placeholders": { + "loading": "로딩 중...", + "noModels": "모델을 찾을 수 없음", + "errorLoading": "데이터 로딩 오류", + "noStorageData": "저장 데이터 없음", + "rootFolder": "루트", + "chartLibraryMissing": "Chart.js 라이브러리가 필요합니다" + }, + "tooltips": { + "tagCount": "{tag}: {count}개 모델", + "chartUsage": "{name}: {size}, {count}회 사용", + "chartPercentage": "{label}: {value}({pct}%)" } }, "modals": { diff --git a/locales/ru.json b/locales/ru.json index bd8a188b..f9aebfe6 100644 --- a/locales/ru.json +++ b/locales/ru.json @@ -1016,6 +1016,18 @@ "storage": "Хранение", "insights": "Аналитика" }, + "metrics": { + "totalModels": "Всего моделей", + "totalStorage": "Всего хранилища", + "totalGenerations": "Всего генераций", + "usageRate": "Коэффициент использования", + "loras": "LoRA", + "checkpoints": "Контрольные точки", + "embeddings": "Эмбеддинги", + "uniqueTags": "Уникальные теги", + "unusedModels": "Неиспользуемые модели", + "avgUsesPerModel": "Сред. использований/модель" + }, "usage": { "mostUsedLoras": "Наиболее используемые LoRAs", "mostUsedCheckpoints": "Наиболее используемые Checkpoints", @@ -1033,13 +1045,42 @@ }, "insights": { "smartInsights": "Умная аналитика", - "recommendations": "Рекомендации" + "recommendations": "Рекомендации", + "noInsights": "Нет доступных данных" }, "charts": { "collectionOverview": "Обзор коллекции", "baseModelDistribution": "Распределение базовых моделей", "usageTrends": "Тенденции использования (за последние 30 дней)", - "usageDistribution": "Распределение использования" + "usageDistribution": "Распределение использования", + "date": "Дата", + "usageCount": "Количество использований", + "fileSizeBytes": "Размер файла (байты)", + "models": "Модели", + "loraUsage": "Использование LoRA", + "checkpointUsage": "Использование Checkpoint", + "embeddingUsage": "Использование Embedding" + }, + "modelTypes": { + "lora": "LoRA", + "locon": "LyCORIS", + "dora": "DoRA", + "checkpoint": "Контрольная точка", + "diffusion_model": "Диффузионная модель", + "embedding": "Эмбеддинги" + }, + "placeholders": { + "loading": "Загрузка...", + "noModels": "Модели не найдены", + "errorLoading": "Ошибка загрузки данных", + "noStorageData": "Нет данных о хранилище", + "rootFolder": "Корень", + "chartLibraryMissing": "Для графика требуется библиотека Chart.js" + }, + "tooltips": { + "tagCount": "{tag}: {count} моделей", + "chartUsage": "{name}: {size}, {count} использований", + "chartPercentage": "{label}: {value} ({pct}%)" } }, "modals": { diff --git a/locales/zh-CN.json b/locales/zh-CN.json index eb0353fb..9ef48b66 100644 --- a/locales/zh-CN.json +++ b/locales/zh-CN.json @@ -1016,6 +1016,18 @@ "storage": "存储", "insights": "洞察" }, + "metrics": { + "totalModels": "模型总数", + "totalStorage": "总存储空间", + "totalGenerations": "总生成次数", + "usageRate": "使用率", + "loras": "LoRA", + "checkpoints": "Checkpoint", + "embeddings": "Embedding", + "uniqueTags": "唯一标签", + "unusedModels": "未使用模型", + "avgUsesPerModel": "平均使用次数/模型" + }, "usage": { "mostUsedLoras": "最常用 LoRA", "mostUsedCheckpoints": "最常用 Checkpoint", @@ -1033,13 +1045,42 @@ }, "insights": { "smartInsights": "智能洞察", - "recommendations": "推荐" + "recommendations": "推荐", + "noInsights": "暂无可用洞察" }, "charts": { "collectionOverview": "收藏概览", "baseModelDistribution": "基础模型分布", "usageTrends": "使用趋势(最近30天)", - "usageDistribution": "使用分布" + "usageDistribution": "使用分布", + "date": "日期", + "usageCount": "使用次数", + "fileSizeBytes": "文件大小(字节)", + "models": "模型", + "loraUsage": "LoRA 使用量", + "checkpointUsage": "Checkpoint 使用量", + "embeddingUsage": "Embedding 使用量" + }, + "modelTypes": { + "lora": "LoRA", + "locon": "LyCORIS", + "dora": "DoRA", + "checkpoint": "Checkpoint", + "diffusion_model": "扩散模型", + "embedding": "Embedding" + }, + "placeholders": { + "loading": "加载中...", + "noModels": "未找到模型", + "errorLoading": "数据加载失败", + "noStorageData": "暂无存储数据", + "rootFolder": "根目录", + "chartLibraryMissing": "需要 Chart.js 库来显示图表" + }, + "tooltips": { + "tagCount": "{tag}:{count} 个模型", + "chartUsage": "{name}:{size},{count} 次使用", + "chartPercentage": "{label}:{value}({pct}%)" } }, "modals": { diff --git a/locales/zh-TW.json b/locales/zh-TW.json index 087a7e69..8da83626 100644 --- a/locales/zh-TW.json +++ b/locales/zh-TW.json @@ -1016,6 +1016,18 @@ "storage": "儲存空間", "insights": "洞察" }, + "metrics": { + "totalModels": "模型總數", + "totalStorage": "總儲存空間", + "totalGenerations": "總生成次數", + "usageRate": "使用率", + "loras": "LoRA", + "checkpoints": "Checkpoint", + "embeddings": "Embedding", + "uniqueTags": "唯一標籤", + "unusedModels": "未使用模型", + "avgUsesPerModel": "平均使用次數/模型" + }, "usage": { "mostUsedLoras": "最常用的 LoRA", "mostUsedCheckpoints": "最常用的 Checkpoint", @@ -1033,13 +1045,42 @@ }, "insights": { "smartInsights": "智慧洞察", - "recommendations": "推薦" + "recommendations": "推薦", + "noInsights": "暫無可用洞察" }, "charts": { "collectionOverview": "收藏總覽", "baseModelDistribution": "基礎模型分布", "usageTrends": "使用趨勢(最近 30 天)", - "usageDistribution": "使用分布" + "usageDistribution": "使用分布", + "date": "日期", + "usageCount": "使用次數", + "fileSizeBytes": "檔案大小(位元組)", + "models": "模型", + "loraUsage": "LoRA 使用量", + "checkpointUsage": "Checkpoint 使用量", + "embeddingUsage": "Embedding 使用量" + }, + "modelTypes": { + "lora": "LoRA", + "locon": "LyCORIS", + "dora": "DoRA", + "checkpoint": "Checkpoint", + "diffusion_model": "擴散模型", + "embedding": "Embedding" + }, + "placeholders": { + "loading": "載入中...", + "noModels": "找不到模型", + "errorLoading": "資料載入失敗", + "noStorageData": "暫無儲存資料", + "rootFolder": "根目錄", + "chartLibraryMissing": "需要 Chart.js 函式庫來顯示圖表" + }, + "tooltips": { + "tagCount": "{tag}:{count} 個模型", + "chartUsage": "{name}:{size},{count} 次使用", + "chartPercentage": "{label}:{value}({pct}%)" } }, "modals": { diff --git a/static/js/statistics.js b/static/js/statistics.js index 0cfa2bc0..63ce8a8f 100644 --- a/static/js/statistics.js +++ b/static/js/statistics.js @@ -1,6 +1,8 @@ // Statistics page functionality import { appCore } from './core.js'; import { showToast } from './utils/uiHelpers.js'; +import { translate } from './utils/i18nHelpers.js'; +import { i18n } from './i18n/index.js'; // Chart.js import (assuming it's available globally or via CDN) // If Chart.js isn't available, we'll need to add it to the project @@ -124,43 +126,43 @@ export class StatisticsManager { { icon: 'fas fa-magic', value: this.data.collection.total_models, - label: 'Total Models', + label: translate('statistics.metrics.totalModels'), format: 'number' }, { icon: 'fas fa-database', value: this.data.collection.total_size, - label: 'Total Storage', + label: translate('statistics.metrics.totalStorage'), format: 'size' }, { icon: 'fas fa-play-circle', value: this.data.collection.total_generations, - label: 'Total Generations', + label: translate('statistics.metrics.totalGenerations'), format: 'number' }, { icon: 'fas fa-chart-line', value: this.calculateUsageRate(), - label: 'Usage Rate', + label: translate('statistics.metrics.usageRate'), format: 'percentage' }, { icon: 'fas fa-layer-group', value: this.data.collection.lora_count, - label: 'LoRAs', + label: translate('statistics.metrics.loras'), format: 'number' }, { icon: 'fas fa-check-circle', value: this.data.collection.checkpoint_count, - label: 'Checkpoints', + label: translate('statistics.metrics.checkpoints'), format: 'number' }, { icon: 'fas fa-code', value: this.data.collection.embedding_count, - label: 'Embeddings', + label: translate('statistics.metrics.embeddings'), format: 'number' } ]; @@ -196,11 +198,7 @@ export class StatisticsManager { } formatFileSize(bytes) { - if (bytes === 0) return '0 Bytes'; - const k = 1024; - const sizes = ['Bytes', 'KB', 'MB', 'GB', 'TB']; - const i = Math.floor(Math.log(bytes) / Math.log(k)); - return parseFloat((bytes / Math.pow(k, i)).toFixed(1)) + ' ' + sizes[i]; + return i18n.formatFileSize(bytes); } calculateUsageRate() { @@ -250,7 +248,7 @@ export class StatisticsManager { if (!ctx || !this.data.collection) return; const data = { - labels: ['LoRAs', 'Checkpoints', 'Embeddings'], + labels: [translate('statistics.metrics.loras'), translate('statistics.metrics.checkpoints'), translate('statistics.metrics.embeddings')], datasets: [{ data: [ this.data.collection.lora_count, @@ -300,17 +298,17 @@ export class StatisticsManager { labels: Array.from(allModels), datasets: [ { - label: 'LoRAs', + label: translate('statistics.metrics.loras'), data: Array.from(allModels).map(model => loraData[model] || 0), backgroundColor: 'oklch(68% 0.28 256 / 0.7)' }, { - label: 'Checkpoints', + label: translate('statistics.metrics.checkpoints'), data: Array.from(allModels).map(model => checkpointData[model] || 0), backgroundColor: 'oklch(68% 0.28 200 / 0.7)' }, { - label: 'Embeddings', + label: translate('statistics.metrics.embeddings'), data: Array.from(allModels).map(model => embeddingData[model] || 0), backgroundColor: 'oklch(68% 0.28 120 / 0.7)' } @@ -345,21 +343,21 @@ export class StatisticsManager { labels: timeline.map(item => new Date(item.date).toLocaleDateString()), datasets: [ { - label: 'LoRA Usage', + label: translate('statistics.charts.loraUsage'), data: timeline.map(item => item.lora_usage), borderColor: 'oklch(68% 0.28 256)', backgroundColor: 'oklch(68% 0.28 256 / 0.1)', fill: true }, { - label: 'Checkpoint Usage', + label: translate('statistics.charts.checkpointUsage'), data: timeline.map(item => item.checkpoint_usage), borderColor: 'oklch(68% 0.28 200)', backgroundColor: 'oklch(68% 0.28 200 / 0.1)', fill: true }, { - label: 'Embedding Usage', + label: translate('statistics.charts.embeddingUsage'), data: timeline.map(item => item.embedding_usage), borderColor: 'oklch(68% 0.28 120)', backgroundColor: 'oklch(68% 0.28 120 / 0.1)', @@ -383,14 +381,14 @@ export class StatisticsManager { display: true, title: { display: true, - text: 'Date' + text: translate('statistics.charts.date') } }, y: { display: true, title: { display: true, - text: 'Usage Count' + text: translate('statistics.charts.usageCount') } } } @@ -416,7 +414,7 @@ export class StatisticsManager { const data = { labels: allModels.map(model => model.name), datasets: [{ - label: 'Usage Count', + label: translate('statistics.charts.usageCount'), data: allModels.map(model => model.usage_count), backgroundColor: allModels.map(model => { switch(model.type) { @@ -450,7 +448,7 @@ export class StatisticsManager { if (!ctx || !this.data.collection) return; const data = { - labels: ['LoRAs', 'Checkpoints', 'Embeddings'], + labels: [translate('statistics.metrics.loras'), translate('statistics.metrics.checkpoints'), translate('statistics.metrics.embeddings')], datasets: [{ data: [ this.data.collection.lora_size, @@ -504,7 +502,7 @@ export class StatisticsManager { const data = { datasets: [{ - label: 'Models', + label: translate('statistics.charts.models'), data: allData.map(item => ({ x: item.size, y: item.usage_count, @@ -532,14 +530,14 @@ export class StatisticsManager { x: { title: { display: true, - text: 'File Size (bytes)' + text: translate('statistics.charts.fileSizeBytes') }, type: 'logarithmic' }, y: { title: { display: true, - text: 'Usage Count' + text: translate('statistics.charts.usageCount') } } }, @@ -548,7 +546,7 @@ export class StatisticsManager { callbacks: { label: (context) => { const point = context.raw; - return `${point.name}: ${this.formatFileSize(point.x)}, ${point.y} uses`; + return translate('statistics.tooltips.chartUsage', { name: point.name, size: this.formatFileSize(point.x), count: point.y }); } } } @@ -563,12 +561,12 @@ export class StatisticsManager { const distribution = this.data.collection.model_types_distribution; const typeDisplayNames = { - lora: 'LoRA', - locon: 'LyCORIS', - dora: 'DoRA', - checkpoint: 'Checkpoint', - diffusion_model: 'Diffusion Model', - embedding: 'Embeddings' + lora: translate('statistics.modelTypes.lora'), + locon: translate('statistics.modelTypes.locon'), + dora: translate('statistics.modelTypes.dora'), + checkpoint: translate('statistics.modelTypes.checkpoint'), + diffusion_model: translate('statistics.modelTypes.diffusion_model'), + embedding: translate('statistics.modelTypes.embedding') }; const colorPalette = { @@ -610,7 +608,7 @@ export class StatisticsManager { const total = context.dataset.data.reduce((a, b) => a + b, 0); const value = context.parsed; const pct = ((value / total) * 100).toFixed(1); - return ` ${context.label}: ${value} (${pct}%)`; + return translate('statistics.tooltips.chartPercentage', { label: context.label, value, pct }); } } } @@ -654,7 +652,7 @@ export class StatisticsManager { // Show loading indicator on initial load if (state.offset === 0) { - container.innerHTML = '
Loading...
'; + container.innerHTML = '
' + translate('statistics.placeholders.loading') + '
'; } try { @@ -670,7 +668,7 @@ export class StatisticsManager { } if (items.length === 0 && state.offset === 0) { - container.innerHTML = '
No models found
'; + container.innerHTML = '
' + translate('statistics.placeholders.noModels') + '
'; state.hasMore = false; } else if (items.length < state.limit) { state.hasMore = false; @@ -683,7 +681,7 @@ export class StatisticsManager { onerror="this.src='/loras_static/images/no-preview.png'">
${model.name}
-
${model.base_model} • ${model.folder || 'Root'}
+
${model.base_model} • ${model.folder || translate('statistics.placeholders.rootFolder')}
${model.usage_count}
@@ -695,7 +693,7 @@ export class StatisticsManager { } catch (error) { console.error(`Error loading ${type} list:`, error); if (state.offset === 0) { - container.innerHTML = '
Error loading data
'; + container.innerHTML = '
' + translate('statistics.placeholders.errorLoading') + '
'; } } finally { state.isLoading = false; @@ -718,7 +716,7 @@ export class StatisticsManager { ].sort((a, b) => b.size - a.size).slice(0, 10); if (allModels.length === 0) { - container.innerHTML = '
No storage data available
'; + container.innerHTML = '
' + translate('statistics.placeholders.noStorageData') + '
'; return; } @@ -726,7 +724,7 @@ export class StatisticsManager {
${model.name}
-
${model.type} • ${model.base_model}
+
${translate('statistics.modelTypes.' + model.type.toLowerCase())} • ${model.base_model}
${this.formatFileSize(model.size)}
@@ -744,7 +742,7 @@ export class StatisticsManager { const size = Math.ceil((tagData.count / maxCount) * 5); return ` + title="${translate('statistics.tooltips.tagCount', { tag: tagData.tag, count: tagData.count })}"> ${tagData.tag} `; @@ -758,7 +756,7 @@ export class StatisticsManager { const insights = this.data.insights.insights; if (insights.length === 0) { - container.innerHTML = '
No insights available
'; + container.innerHTML = '
' + translate('statistics.insights.noInsights') + '
'; return; } @@ -782,25 +780,25 @@ export class StatisticsManager { { icon: 'fas fa-percentage', value: this.calculateUsageRate(), - label: 'Usage Rate', + label: translate('statistics.metrics.usageRate'), format: 'percentage' }, { icon: 'fas fa-tags', value: this.data.tags?.total_unique_tags || 0, - label: 'Unique Tags', + label: translate('statistics.metrics.uniqueTags'), format: 'number' }, { icon: 'fas fa-clock', value: this.data.collection.unused_loras + this.data.collection.unused_checkpoints, - label: 'Unused Models', + label: translate('statistics.metrics.unusedModels'), format: 'number' }, { icon: 'fas fa-chart-line', value: this.calculateAverageUsage(), - label: 'Avg. Uses/Model', + label: translate('statistics.metrics.avgUsesPerModel'), format: 'decimal' } ]; @@ -829,7 +827,7 @@ export class StatisticsManager { const chartCanvases = document.querySelectorAll('canvas'); chartCanvases.forEach(canvas => { const container = canvas.parentElement; - container.innerHTML = '
Chart requires Chart.js library
'; + container.innerHTML = '
' + translate('statistics.placeholders.chartLibraryMissing') + '
'; }); } diff --git a/tests/i18n/test_i18n.py b/tests/i18n/test_i18n.py index 898c9626..048b8f1a 100644 --- a/tests/i18n/test_i18n.py +++ b/tests/i18n/test_i18n.py @@ -80,6 +80,7 @@ FALSE_POSITIVES = { "array", "object", "non.existent.key", + "statistics.modelTypes.", } SPECIAL_UI_HELPER_KEYS = {