diff --git a/locales/de.json b/locales/de.json index cf596bc8..d4d2bdd2 100644 --- a/locales/de.json +++ b/locales/de.json @@ -1046,7 +1046,42 @@ "insights": { "smartInsights": "Intelligente Erkenntnisse", "recommendations": "Empfehlungen", - "noInsights": "Keine Erkenntnisse verfügbar" + "noInsights": "Keine Erkenntnisse verfügbar", + "unusedLoras": { + "high": { + "title": "Hohe Anzahl ungenutzter LoRAs", + "description": "{percent}% Ihrer LoRAs ({count}/{total}) wurden noch nie verwendet.", + "suggestion": "Erwägen Sie, ungenutzte Modelle zu organisieren oder zu archivieren, um Speicherplatz freizugeben." + } + }, + "unusedCheckpoints": { + "detected": { + "title": "Ungenutzte Checkpoints erkannt", + "description": "{percent}% Ihrer Checkpoints ({count}/{total}) wurden noch nie verwendet.", + "suggestion": "Überprüfen Sie nicht mehr benötigte Checkpoints und erwägen Sie deren Entfernung." + } + }, + "unusedEmbeddings": { + "high": { + "title": "Hohe Anzahl ungenutzter Embeddings", + "description": "{percent}% Ihrer Embeddings ({count}/{total}) wurden noch nie verwendet.", + "suggestion": "Organisieren oder archivieren Sie ungenutzte Embeddings, um Ihre Sammlung zu optimieren." + } + }, + "collection": { + "large": { + "title": "Große Sammlung erkannt", + "description": "Ihre Modellsammlung verwendet {size} Speicher.", + "suggestion": "Erwägen Sie externe Speicher- oder Cloud-Lösungen für eine bessere Organisation." + } + }, + "activity": { + "active": { + "title": "Aktiver Benutzer", + "description": "Sie haben {count} Generationen abgeschlossen!", + "suggestion": "Entdecken und erstellen Sie weiterhin großartige Inhalte mit Ihren Modellen." + } + } }, "charts": { "collectionOverview": "Sammlungsübersicht", diff --git a/locales/en.json b/locales/en.json index 7ca866be..3162cd73 100644 --- a/locales/en.json +++ b/locales/en.json @@ -1046,7 +1046,42 @@ "insights": { "smartInsights": "Smart Insights", "recommendations": "Recommendations", - "noInsights": "No insights available" + "noInsights": "No insights available", + "unusedLoras": { + "high": { + "title": "High Number of Unused LoRAs", + "description": "{percent}% of your LoRAs ({count}/{total}) have never been used.", + "suggestion": "Consider organizing or archiving unused models to free up storage space." + } + }, + "unusedCheckpoints": { + "detected": { + "title": "Unused Checkpoints Detected", + "description": "{percent}% of your checkpoints ({count}/{total}) have never been used.", + "suggestion": "Review and consider removing checkpoints you no longer need." + } + }, + "unusedEmbeddings": { + "high": { + "title": "High Number of Unused Embeddings", + "description": "{percent}% of your embeddings ({count}/{total}) have never been used.", + "suggestion": "Consider organizing or archiving unused embeddings to optimize your collection." + } + }, + "collection": { + "large": { + "title": "Large Collection Detected", + "description": "Your model collection is using {size} of storage.", + "suggestion": "Consider using external storage or cloud solutions for better organization." + } + }, + "activity": { + "active": { + "title": "Active User", + "description": "You've completed {count} generations so far!", + "suggestion": "Keep exploring and creating amazing content with your models." + } + } }, "charts": { "collectionOverview": "Collection Overview", diff --git a/locales/es.json b/locales/es.json index cf2b63a2..63338661 100644 --- a/locales/es.json +++ b/locales/es.json @@ -1046,7 +1046,42 @@ "insights": { "smartInsights": "Perspectivas inteligentes", "recommendations": "Recomendaciones", - "noInsights": "No hay información disponible" + "noInsights": "No hay información disponible", + "unusedLoras": { + "high": { + "title": "Alta cantidad de LoRAs no utilizadas", + "description": "El {percent}% de tus LoRAs ({count}/{total}) nunca se han utilizado.", + "suggestion": "Considera organizar o archivar modelos no utilizados para liberar espacio." + } + }, + "unusedCheckpoints": { + "detected": { + "title": "Puntos de control no utilizados detectados", + "description": "El {percent}% de tus puntos de control ({count}/{total}) nunca se han utilizado.", + "suggestion": "Revisa y considera eliminar los puntos de control que ya no necesites." + } + }, + "unusedEmbeddings": { + "high": { + "title": "Alta cantidad de Embeddings no utilizados", + "description": "El {percent}% de tus embeddings ({count}/{total}) nunca se han utilizado.", + "suggestion": "Considera organizar o archivar embeddings no utilizados para optimizar tu colección." + } + }, + "collection": { + "large": { + "title": "Colección grande detectada", + "description": "Tu colección de modelos está usando {size} de almacenamiento.", + "suggestion": "Considera usar almacenamiento externo o soluciones en la nube para una mejor organización." + } + }, + "activity": { + "active": { + "title": "Usuario activo", + "description": "¡Has completado {count} generaciones hasta ahora!", + "suggestion": "Sigue explorando y creando contenido increíble con tus modelos." + } + } }, "charts": { "collectionOverview": "Resumen de colección", diff --git a/locales/fr.json b/locales/fr.json index c8d27e6d..fcb7ae0c 100644 --- a/locales/fr.json +++ b/locales/fr.json @@ -1046,7 +1046,42 @@ "insights": { "smartInsights": "Aperçus intelligents", "recommendations": "Recommandations", - "noInsights": "Aucun aperçu disponible" + "noInsights": "Aucun aperçu disponible", + "unusedLoras": { + "high": { + "title": "Nombre élevé de LoRAs inutilisées", + "description": "{percent}% de vos LoRAs ({count}/{total}) n'ont jamais été utilisées.", + "suggestion": "Envisagez d'organiser ou d'archiver les modèles inutilisés pour libérer de l'espace." + } + }, + "unusedCheckpoints": { + "detected": { + "title": "Points de contrôle inutilisés détectés", + "description": "{percent}% de vos points de contrôle ({count}/{total}) n'ont jamais été utilisés.", + "suggestion": "Examinez et envisagez de supprimer les points de contrôle dont vous n'avez plus besoin." + } + }, + "unusedEmbeddings": { + "high": { + "title": "Nombre élevé d'Embeddings inutilisées", + "description": "{percent}% de vos embeddings ({count}/{total}) n'ont jamais été utilisées.", + "suggestion": "Envisagez d'organiser ou d'archiver les embeddings inutilisées pour optimiser votre collection." + } + }, + "collection": { + "large": { + "title": "Grande collection détectée", + "description": "Votre collection de modèles utilise {size} de stockage.", + "suggestion": "Envisagez d'utiliser un stockage externe ou des solutions cloud pour une meilleure organisation." + } + }, + "activity": { + "active": { + "title": "Utilisateur actif", + "description": "Vous avez effectué {count} générations jusqu'à présent !", + "suggestion": "Continuez à explorer et à créer du contenu formidable avec vos modèles." + } + } }, "charts": { "collectionOverview": "Aperçu de la collection", diff --git a/locales/he.json b/locales/he.json index d004a488..3005a3a3 100644 --- a/locales/he.json +++ b/locales/he.json @@ -1046,7 +1046,42 @@ "insights": { "smartInsights": "תובנות חכמות", "recommendations": "המלצות", - "noInsights": "אין תובנות זמינות" + "noInsights": "אין תובנות זמינות", + "unusedLoras": { + "high": { + "title": "כמות גבוהה של LoRAs שאינן בשימוש", + "description": "{percent}% מה-LoRAs שלך ({count}/{total}) מעולם לא נעשה בהם שימוש.", + "suggestion": "שקול לארגן או לאחסן בארכיון מודלים שאינם בשימוש כדי לפנות שטח אחסון." + } + }, + "unusedCheckpoints": { + "detected": { + "title": "התגלו נקודות ביקורת שאינן בשימוש", + "description": "{percent}% מנקודות הביקורת שלך ({count}/{total}) מעולם לא נעשה בהן שימוש.", + "suggestion": "בדוק ושקול להסיר נקודות ביקורת שאינך צריך עוד." + } + }, + "unusedEmbeddings": { + "high": { + "title": "כמות גבוהה של Embeddings שאינם בשימוש", + "description": "{percent}% מה-Embeddings שלך ({count}/{total}) מעולם לא נעשה בהם שימוש.", + "suggestion": "שקול לארגן או לאחסן בארכיון Embeddings שאינם בשימוש כדי לייעל את האוסף." + } + }, + "collection": { + "large": { + "title": "התגלה אוסף גדול", + "description": "אוסף המודלים שלך משתמש ב-{size} של אחסון.", + "suggestion": "שקול להשתמש באחסון חיצוני או בפתרונות ענן לארגון טוב יותר." + } + }, + "activity": { + "active": { + "title": "משתמש פעיל", + "description": "השלמת {count} יצירות עד כה!", + "suggestion": "המשך לחקור וליצור תוכן מדהים עם המודלים שלך." + } + } }, "charts": { "collectionOverview": "סקירת אוסף", diff --git a/locales/ja.json b/locales/ja.json index a43d400b..57eea30c 100644 --- a/locales/ja.json +++ b/locales/ja.json @@ -1046,7 +1046,42 @@ "insights": { "smartInsights": "スマートインサイト", "recommendations": "推奨事項", - "noInsights": "インサイトはありません" + "noInsights": "インサイトはありません", + "unusedLoras": { + "high": { + "title": "未使用のLoRAが多数あります", + "description": "LoRAの{percent}%({count}/{total})が一度も使用されていません。", + "suggestion": "未使用のモデルを整理またはアーカイブしてストレージを解放してください。" + } + }, + "unusedCheckpoints": { + "detected": { + "title": "未使用のCheckpointを検出", + "description": "Checkpointの{percent}%({count}/{total})が一度も使用されていません。", + "suggestion": "不要なCheckpointを確認して削除を検討してください。" + } + }, + "unusedEmbeddings": { + "high": { + "title": "未使用のEmbeddingが多数あります", + "description": "Embeddingの{percent}%({count}/{total})が一度も使用されていません。", + "suggestion": "未使用のEmbeddingを整理またはアーカイブしてコレクションを最適化してください。" + } + }, + "collection": { + "large": { + "title": "大規模コレクションを検出", + "description": "モデルコレクションが{size}のストレージを使用しています。", + "suggestion": "外部ストレージやクラウドソリューションの使用を検討してください。" + } + }, + "activity": { + "active": { + "title": "アクティブユーザー", + "description": "これまでに{count}回の生成を完了しました!", + "suggestion": "モデルを使って素晴らしいコンテンツを作り続けてください。" + } + } }, "charts": { "collectionOverview": "コレクション概要", diff --git a/locales/ko.json b/locales/ko.json index 00afc3e3..368df662 100644 --- a/locales/ko.json +++ b/locales/ko.json @@ -1046,7 +1046,42 @@ "insights": { "smartInsights": "스마트 인사이트", "recommendations": "추천", - "noInsights": "인사이트 없음" + "noInsights": "인사이트 없음", + "unusedLoras": { + "high": { + "title": "사용하지 않은 LoRA가 많음", + "description": "LoRA의 {percent}%({count}/{total})가 한 번도 사용되지 않았습니다.", + "suggestion": "사용하지 않는 모델을 정리하거나 보관하여 저장 공간을 확보하세요." + } + }, + "unusedCheckpoints": { + "detected": { + "title": "사용하지 않은 Checkpoint 감지", + "description": "Checkpoint의 {percent}%({count}/{total})가 한 번도 사용되지 않았습니다.", + "suggestion": "더 이상 필요하지 않은 Checkpoint를 검토하고 제거하세요." + } + }, + "unusedEmbeddings": { + "high": { + "title": "사용하지 않은 Embedding이 많음", + "description": "Embedding의 {percent}%({count}/{total})가 한 번도 사용되지 않았습니다.", + "suggestion": "사용하지 않는 Embedding을 정리하여 컬렉션을 최적화하세요." + } + }, + "collection": { + "large": { + "title": "대규모 컬렉션 감지", + "description": "모델 컬렉션이 {size}의 저장 공간을 사용 중입니다.", + "suggestion": "더 나은 관리를 위해 외부 저장소나 클라우드 솔루션을 고려하세요." + } + }, + "activity": { + "active": { + "title": "활성 사용자", + "description": "지금까지 {count}번의 생성을 완료했습니다!", + "suggestion": "모델로 계속해서 멋진 콘텐츠를 탐색하고 만들어보세요." + } + } }, "charts": { "collectionOverview": "컬렉션 개요", diff --git a/locales/ru.json b/locales/ru.json index f9aebfe6..eae01a07 100644 --- a/locales/ru.json +++ b/locales/ru.json @@ -1046,7 +1046,42 @@ "insights": { "smartInsights": "Умная аналитика", "recommendations": "Рекомендации", - "noInsights": "Нет доступных данных" + "noInsights": "Нет доступных данных", + "unusedLoras": { + "high": { + "title": "Большое количество неиспользуемых LoRA", + "description": "{percent}% ваших LoRA ({count}/{total}) никогда не использовались.", + "suggestion": "Рассмотрите возможность организации или архивирования неиспользуемых моделей для освобождения места." + } + }, + "unusedCheckpoints": { + "detected": { + "title": "Обнаружены неиспользуемые контрольные точки", + "description": "{percent}% ваших контрольных точек ({count}/{total}) никогда не использовались.", + "suggestion": "Проверьте и удалите ненужные контрольные точки." + } + }, + "unusedEmbeddings": { + "high": { + "title": "Большое количество неиспользуемых эмбеддингов", + "description": "{percent}% ваших эмбеддингов ({count}/{total}) никогда не использовались.", + "suggestion": "Организуйте или архивируйте неиспользуемые эмбеддинги для оптимизации коллекции." + } + }, + "collection": { + "large": { + "title": "Обнаружена большая коллекция", + "description": "Ваша коллекция моделей использует {size} хранилища.", + "suggestion": "Рассмотрите внешнее хранилище или облачные решения для лучшей организации." + } + }, + "activity": { + "active": { + "title": "Активный пользователь", + "description": "Вы завершили {count} генераций!", + "suggestion": "Продолжайте исследовать и создавать удивительный контент с вашими моделями." + } + } }, "charts": { "collectionOverview": "Обзор коллекции", diff --git a/locales/zh-CN.json b/locales/zh-CN.json index 9ef48b66..582bd726 100644 --- a/locales/zh-CN.json +++ b/locales/zh-CN.json @@ -1046,7 +1046,42 @@ "insights": { "smartInsights": "智能洞察", "recommendations": "推荐", - "noInsights": "暂无可用洞察" + "noInsights": "暂无可用洞察", + "unusedLoras": { + "high": { + "title": "大量未使用的 LoRA", + "description": "你的 LoRA 中有 {percent}%({count}/{total})从未被使用过。", + "suggestion": "考虑整理或归档未使用的模型以释放存储空间。" + } + }, + "unusedCheckpoints": { + "detected": { + "title": "检测到未使用的 Checkpoint", + "description": "你的 Checkpoint 中有 {percent}%({count}/{total})从未被使用过。", + "suggestion": "审查并考虑删除不再需要的 Checkpoint。" + } + }, + "unusedEmbeddings": { + "high": { + "title": "大量未使用的 Embedding", + "description": "你的 Embedding 中有 {percent}%({count}/{total})从未被使用过。", + "suggestion": "考虑整理或归档未使用的 Embedding 以优化你的收藏。" + } + }, + "collection": { + "large": { + "title": "检测到大型收藏", + "description": "你的模型收藏正在使用 {size} 的存储空间。", + "suggestion": "考虑使用外部存储或云解决方案以获得更好的组织。" + } + }, + "activity": { + "active": { + "title": "活跃用户", + "description": "你已经完成了 {count} 次生成!", + "suggestion": "继续探索并用你的模型创作精彩内容。" + } + } }, "charts": { "collectionOverview": "收藏概览", diff --git a/locales/zh-TW.json b/locales/zh-TW.json index 8da83626..c5fe8eaf 100644 --- a/locales/zh-TW.json +++ b/locales/zh-TW.json @@ -1046,7 +1046,42 @@ "insights": { "smartInsights": "智慧洞察", "recommendations": "推薦", - "noInsights": "暫無可用洞察" + "noInsights": "暫無可用洞察", + "unusedLoras": { + "high": { + "title": "大量未使用的 LoRA", + "description": "你的 LoRA 中有 {percent}%({count}/{total})從未被使用過。", + "suggestion": "考慮整理或封存未使用的模型以釋放儲存空間。" + } + }, + "unusedCheckpoints": { + "detected": { + "title": "檢測到未使用的 Checkpoint", + "description": "你的 Checkpoint 中有 {percent}%({count}/{total})從未被使用過。", + "suggestion": "審查並考慮刪除不再需要的 Checkpoint。" + } + }, + "unusedEmbeddings": { + "high": { + "title": "大量未使用的 Embedding", + "description": "你的 Embedding 中有 {percent}%({count}/{total})從未被使用過。", + "suggestion": "考慮整理或封存未使用的 Embedding 以優化你的收藏。" + } + }, + "collection": { + "large": { + "title": "檢測到大型收藏", + "description": "你的模型收藏正在使用 {size} 的儲存空間。", + "suggestion": "考慮使用外部儲存或雲端解決方案以獲得更好的組織。" + } + }, + "activity": { + "active": { + "title": "活躍用戶", + "description": "你已經完成了 {count} 次生成!", + "suggestion": "繼續探索並用你的模型創作精彩內容。" + } + } }, "charts": { "collectionOverview": "收藏總覽", diff --git a/py/routes/stats_routes.py b/py/routes/stats_routes.py index bd03bd76..22efe863 100644 --- a/py/routes/stats_routes.py +++ b/py/routes/stats_routes.py @@ -477,9 +477,12 @@ class StatsRoutes: if unused_lora_percent > 50: insights.append({ 'type': 'warning', - 'title': 'High Number of Unused LoRAs', - 'description': f'{unused_lora_percent:.1f}% of your LoRAs ({unused_loras}/{total_loras}) have never been used.', - 'suggestion': 'Consider organizing or archiving unused models to free up storage space.' + 'key': 'insights.unusedLoras.high', + 'params': { + 'percent': f'{unused_lora_percent:.1f}', + 'count': str(unused_loras), + 'total': str(total_loras) + } }) if total_checkpoints > 0: @@ -487,9 +490,12 @@ class StatsRoutes: if unused_checkpoint_percent > 30: insights.append({ 'type': 'warning', - 'title': 'Unused Checkpoints Detected', - 'description': f'{unused_checkpoint_percent:.1f}% of your checkpoints ({unused_checkpoints}/{total_checkpoints}) have never been used.', - 'suggestion': 'Review and consider removing checkpoints you no longer need.' + 'key': 'insights.unusedCheckpoints.detected', + 'params': { + 'percent': f'{unused_checkpoint_percent:.1f}', + 'count': str(unused_checkpoints), + 'total': str(total_checkpoints) + } }) if total_embeddings > 0: @@ -497,9 +503,12 @@ class StatsRoutes: if unused_embedding_percent > 50: insights.append({ 'type': 'warning', - 'title': 'High Number of Unused Embeddings', - 'description': f'{unused_embedding_percent:.1f}% of your embeddings ({unused_embeddings}/{total_embeddings}) have never been used.', - 'suggestion': 'Consider organizing or archiving unused embeddings to optimize your collection.' + 'key': 'insights.unusedEmbeddings.high', + 'params': { + 'percent': f'{unused_embedding_percent:.1f}', + 'count': str(unused_embeddings), + 'total': str(total_embeddings) + } }) # Storage insights @@ -510,18 +519,20 @@ class StatsRoutes: if total_size > 100 * 1024 * 1024 * 1024: # 100GB insights.append({ 'type': 'info', - 'title': 'Large Collection Detected', - 'description': f'Your model collection is using {self._format_size(total_size)} of storage.', - 'suggestion': 'Consider using external storage or cloud solutions for better organization.' + 'key': 'insights.collection.large', + 'params': { + 'size': self._format_size(total_size) + } }) # Recent activity insight if usage_data.get('total_executions', 0) > 100: insights.append({ 'type': 'success', - 'title': 'Active User', - 'description': f'You\'ve completed {usage_data["total_executions"]} generations so far!', - 'suggestion': 'Keep exploring and creating amazing content with your models.' + 'key': 'insights.activity.active', + 'params': { + 'count': str(usage_data['total_executions']) + } }) return web.json_response({ diff --git a/static/js/statistics.js b/static/js/statistics.js index 63ce8a8f..1e2efaf8 100644 --- a/static/js/statistics.js +++ b/static/js/statistics.js @@ -191,7 +191,7 @@ export class StatisticsManager { case 'size': return this.formatFileSize(value); case 'percentage': - return `${value.toFixed(1)}%`; + return new Intl.NumberFormat(i18n.getCurrentLocale(), { style: 'percent', maximumFractionDigits: 1 }).format(value / 100); default: return value; } @@ -760,13 +760,26 @@ export class StatisticsManager { return; } - container.innerHTML = insights.map(insight => ` + container.innerHTML = insights.map(insight => { + const params = insight.params || {}; + let title, description, suggestion; + if (insight.key) { + title = translate('statistics.' + insight.key + '.title', params); + description = translate('statistics.' + insight.key + '.description', params); + suggestion = translate('statistics.' + insight.key + '.suggestion', params); + } else { + // Backward compatibility for insights without key/params + title = insight.title || ''; + description = insight.description || ''; + suggestion = insight.suggestion || ''; + } + return `
-
${insight.title}
-
${insight.description}
-
${insight.suggestion}
+
${title}
+
${description}
+
${suggestion}
- `).join(''); + `}).join(''); // Render collection analysis cards this.renderCollectionAnalysis(); diff --git a/tests/i18n/test_i18n.py b/tests/i18n/test_i18n.py index 048b8f1a..1dc002d3 100644 --- a/tests/i18n/test_i18n.py +++ b/tests/i18n/test_i18n.py @@ -81,6 +81,7 @@ FALSE_POSITIVES = { "object", "non.existent.key", "statistics.modelTypes.", + "statistics.", } SPECIAL_UI_HELPER_KEYS = { diff --git a/tests/routes/test_stats_routes.py b/tests/routes/test_stats_routes.py index 08c196fb..d27f2c16 100644 --- a/tests/routes/test_stats_routes.py +++ b/tests/routes/test_stats_routes.py @@ -302,15 +302,15 @@ async def test_get_insights(stats_routes): insights = payload["data"]["insights"] assert len(insights) == 3 - titles = {entry["title"] for entry in insights} - assert "High Number of Unused LoRAs" in titles - assert "Unused Checkpoints Detected" in titles - assert "High Number of Unused Embeddings" in titles + keys = {entry["key"] for entry in insights} + assert "insights.unusedLoras.high" in keys + assert "insights.unusedCheckpoints.detected" in keys + assert "insights.unusedEmbeddings.high" in keys - descriptions = {entry["description"] for entry in insights} - assert any("2/3" in desc for desc in descriptions) - assert any("1/2" in desc for desc in descriptions) - assert any("1/1" in desc for desc in descriptions) + params_list = [entry["params"] for entry in insights] + assert any(p["total"] == "3" for p in params_list) + assert any(p["total"] == "2" for p in params_list) + assert any(p["total"] == "1" for p in params_list) @pytest.mark.asyncio