From 82297fa1467584b369ad892dd7f47266070c6c8c Mon Sep 17 00:00:00 2001 From: CJ Pais Date: Wed, 11 Mar 2026 14:43:16 +0800 Subject: [PATCH] Add model loading failure notifications with i18n support (#997) * fix: show toast notification when model fails to load with ORT error The model loading failure (e.g. ORT error) was only shown as a small red dot with truncated text in the footer. Now it also triggers a toast notification, matching how recording errors are already handled. https://claude.ai/code/session_015VfQgoDkUDV6wk63RHrwEs * fix: add missing i18n keys for model load error toast to all locales https://claude.ai/code/session_015VfQgoDkUDV6wk63RHrwEs * fix: simplify model load error toast and add proper translations Remove the unnecessary modelLoadFailed passthrough key that just forwarded a variable. Use the error string directly with a translated fallback. Add proper translations for all 16 locales. https://claude.ai/code/session_015VfQgoDkUDV6wk63RHrwEs * fix: translate recordingFailed string in all locales This was previously left untranslated (English) in all non-English locale files. https://claude.ai/code/session_015VfQgoDkUDV6wk63RHrwEs * cleanup * format --------- Co-authored-by: Claude --- src/App.tsx | 21 +++++++++++++++++++ .../model-selector/ModelSelector.tsx | 7 +------ src/i18n/locales/ar/translation.json | 4 +++- src/i18n/locales/cs/translation.json | 4 +++- src/i18n/locales/de/translation.json | 4 +++- src/i18n/locales/en/translation.json | 4 +++- src/i18n/locales/es/translation.json | 4 +++- src/i18n/locales/fr/translation.json | 4 +++- src/i18n/locales/it/translation.json | 4 +++- src/i18n/locales/ja/translation.json | 4 +++- src/i18n/locales/ko/translation.json | 4 +++- src/i18n/locales/pl/translation.json | 4 +++- src/i18n/locales/pt/translation.json | 4 +++- src/i18n/locales/ru/translation.json | 4 +++- src/i18n/locales/tr/translation.json | 4 +++- src/i18n/locales/uk/translation.json | 4 +++- src/i18n/locales/vi/translation.json | 4 +++- src/i18n/locales/zh-TW/translation.json | 4 +++- src/i18n/locales/zh/translation.json | 4 +++- src/lib/types/events.ts | 6 ++++++ 20 files changed, 79 insertions(+), 23 deletions(-) create mode 100644 src/lib/types/events.ts diff --git a/src/App.tsx b/src/App.tsx index faebb87..b805105 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -7,6 +7,7 @@ import { checkAccessibilityPermission, checkMicrophonePermission, } from "tauri-plugin-macos-permissions-api"; +import { ModelStateEvent } from "./lib/types/events"; import "./App.css"; import AccessibilityPermissions from "./components/AccessibilityPermissions"; import Footer from "./components/footer"; @@ -104,6 +105,26 @@ function App() { }; }, [t]); + // Listen for model loading failures and show a toast + useEffect(() => { + const unlisten = listen("model-state-changed", (event) => { + if (event.payload.event_type === "loading_failed") { + toast.error( + t("errors.modelLoadFailed", { + model: + event.payload.model_name || t("errors.modelLoadFailedUnknown"), + }), + { + description: event.payload.error, + }, + ); + } + }); + return () => { + unlisten.then((fn) => fn()); + }; + }, [t]); + const checkOnboardingStatus = async () => { try { // Check if they have any models available diff --git a/src/components/model-selector/ModelSelector.tsx b/src/components/model-selector/ModelSelector.tsx index 5045c6b..b27dc63 100644 --- a/src/components/model-selector/ModelSelector.tsx +++ b/src/components/model-selector/ModelSelector.tsx @@ -8,12 +8,7 @@ import ModelStatusButton from "./ModelStatusButton"; import ModelDropdown from "./ModelDropdown"; import DownloadProgressDisplay from "./DownloadProgressDisplay"; -interface ModelStateEvent { - event_type: string; - model_id?: string; - model_name?: string; - error?: string; -} +import { ModelStateEvent } from "@/lib/types/events"; type ModelStatus = | "ready" diff --git a/src/i18n/locales/ar/translation.json b/src/i18n/locales/ar/translation.json index 0882d21..2372fe1 100644 --- a/src/i18n/locales/ar/translation.json +++ b/src/i18n/locales/ar/translation.json @@ -541,7 +541,9 @@ }, "errors": { "loadDirectory": "خطأ في تحميل المجلد: {{error}}", - "recordingFailed": "Failed to start recording: {{error}}" + "recordingFailed": "فشل في بدء التسجيل: {{error}}", + "modelLoadFailed": "فشل في تحميل النموذج: {{model}}", + "modelLoadFailedUnknown": "نموذج غير معروف" }, "appLanguage": { "title": "لغة التطبيق", diff --git a/src/i18n/locales/cs/translation.json b/src/i18n/locales/cs/translation.json index acdf41a..9ba2990 100644 --- a/src/i18n/locales/cs/translation.json +++ b/src/i18n/locales/cs/translation.json @@ -541,7 +541,9 @@ }, "errors": { "loadDirectory": "Chyba při načítání adresáře: {{error}}", - "recordingFailed": "Failed to start recording: {{error}}" + "recordingFailed": "Nepodařilo se spustit nahrávání: {{error}}", + "modelLoadFailed": "Nepodařilo se načíst model: {{model}}", + "modelLoadFailedUnknown": "neznámý model" }, "appLanguage": { "title": "Jazyk aplikace", diff --git a/src/i18n/locales/de/translation.json b/src/i18n/locales/de/translation.json index cf349b7..bc77bd3 100644 --- a/src/i18n/locales/de/translation.json +++ b/src/i18n/locales/de/translation.json @@ -541,7 +541,9 @@ }, "errors": { "loadDirectory": "Fehler beim Laden des Verzeichnisses: {{error}}", - "recordingFailed": "Failed to start recording: {{error}}" + "recordingFailed": "Aufnahme konnte nicht gestartet werden: {{error}}", + "modelLoadFailed": "Modell konnte nicht geladen werden: {{model}}", + "modelLoadFailedUnknown": "unbekanntes Modell" }, "appLanguage": { "title": "Anwendungssprache", diff --git a/src/i18n/locales/en/translation.json b/src/i18n/locales/en/translation.json index c934961..275e1b5 100644 --- a/src/i18n/locales/en/translation.json +++ b/src/i18n/locales/en/translation.json @@ -541,7 +541,9 @@ }, "errors": { "loadDirectory": "Error loading directory: {{error}}", - "recordingFailed": "Failed to start recording: {{error}}" + "recordingFailed": "Failed to start recording: {{error}}", + "modelLoadFailed": "Failed to load model: {{model}}", + "modelLoadFailedUnknown": "unknown model" }, "appLanguage": { "title": "Application Language", diff --git a/src/i18n/locales/es/translation.json b/src/i18n/locales/es/translation.json index 1c6a585..cb3a580 100644 --- a/src/i18n/locales/es/translation.json +++ b/src/i18n/locales/es/translation.json @@ -541,7 +541,9 @@ }, "errors": { "loadDirectory": "Error al cargar el directorio: {{error}}", - "recordingFailed": "Failed to start recording: {{error}}" + "recordingFailed": "Error al iniciar la grabación: {{error}}", + "modelLoadFailed": "Error al cargar el modelo: {{model}}", + "modelLoadFailedUnknown": "modelo desconocido" }, "appLanguage": { "title": "Idioma de la aplicación", diff --git a/src/i18n/locales/fr/translation.json b/src/i18n/locales/fr/translation.json index b52f5b6..1de838a 100644 --- a/src/i18n/locales/fr/translation.json +++ b/src/i18n/locales/fr/translation.json @@ -541,7 +541,9 @@ }, "errors": { "loadDirectory": "Erreur lors du chargement du répertoire : {{error}}", - "recordingFailed": "Failed to start recording: {{error}}" + "recordingFailed": "Échec du démarrage de l'enregistrement : {{error}}", + "modelLoadFailed": "Échec du chargement du modèle : {{model}}", + "modelLoadFailedUnknown": "modèle inconnu" }, "appLanguage": { "title": "Langue de l'application", diff --git a/src/i18n/locales/it/translation.json b/src/i18n/locales/it/translation.json index 7b87668..5b00e2b 100644 --- a/src/i18n/locales/it/translation.json +++ b/src/i18n/locales/it/translation.json @@ -541,7 +541,9 @@ }, "errors": { "loadDirectory": "Errore di caricamento cartella: {{error}}", - "recordingFailed": "Failed to start recording: {{error}}" + "recordingFailed": "Impossibile avviare la registrazione: {{error}}", + "modelLoadFailed": "Impossibile caricare il modello: {{model}}", + "modelLoadFailedUnknown": "modello sconosciuto" }, "appLanguage": { "title": "Lingua Applicazione", diff --git a/src/i18n/locales/ja/translation.json b/src/i18n/locales/ja/translation.json index 1bd8149..a73e363 100644 --- a/src/i18n/locales/ja/translation.json +++ b/src/i18n/locales/ja/translation.json @@ -541,7 +541,9 @@ }, "errors": { "loadDirectory": "ディレクトリの読み込みエラー: {{error}}", - "recordingFailed": "Failed to start recording: {{error}}" + "recordingFailed": "録音の開始に失敗しました: {{error}}", + "modelLoadFailed": "モデルの読み込みに失敗しました: {{model}}", + "modelLoadFailedUnknown": "不明なモデル" }, "appLanguage": { "title": "アプリケーション言語", diff --git a/src/i18n/locales/ko/translation.json b/src/i18n/locales/ko/translation.json index 880d895..dbbde1f 100644 --- a/src/i18n/locales/ko/translation.json +++ b/src/i18n/locales/ko/translation.json @@ -541,7 +541,9 @@ }, "errors": { "loadDirectory": "디렉토리 로딩 오류: {{error}}", - "recordingFailed": "Failed to start recording: {{error}}" + "recordingFailed": "녹음을 시작하지 못했습니다: {{error}}", + "modelLoadFailed": "모델을 불러오지 못했습니다: {{model}}", + "modelLoadFailedUnknown": "알 수 없는 모델" }, "appLanguage": { "title": "애플리케이션 언어", diff --git a/src/i18n/locales/pl/translation.json b/src/i18n/locales/pl/translation.json index 5197ae4..9b4adf9 100644 --- a/src/i18n/locales/pl/translation.json +++ b/src/i18n/locales/pl/translation.json @@ -541,7 +541,9 @@ }, "errors": { "loadDirectory": "Błąd wczytywania katalogu: {{error}}", - "recordingFailed": "Failed to start recording: {{error}}" + "recordingFailed": "Nie udało się rozpocząć nagrywania: {{error}}", + "modelLoadFailed": "Nie udało się załadować modelu: {{model}}", + "modelLoadFailedUnknown": "nieznany model" }, "appLanguage": { "title": "Język aplikacji", diff --git a/src/i18n/locales/pt/translation.json b/src/i18n/locales/pt/translation.json index 4d92cf6..7b0c91b 100644 --- a/src/i18n/locales/pt/translation.json +++ b/src/i18n/locales/pt/translation.json @@ -541,7 +541,9 @@ }, "errors": { "loadDirectory": "Erro ao carregar diretório: {{error}}", - "recordingFailed": "Failed to start recording: {{error}}" + "recordingFailed": "Falha ao iniciar a gravação: {{error}}", + "modelLoadFailed": "Falha ao carregar o modelo: {{model}}", + "modelLoadFailedUnknown": "modelo desconhecido" }, "appLanguage": { "title": "Idioma da Aplicação", diff --git a/src/i18n/locales/ru/translation.json b/src/i18n/locales/ru/translation.json index f455852..b60eb3d 100644 --- a/src/i18n/locales/ru/translation.json +++ b/src/i18n/locales/ru/translation.json @@ -541,7 +541,9 @@ }, "errors": { "loadDirectory": "Ошибка загрузки каталога: {{error}}.", - "recordingFailed": "Failed to start recording: {{error}}" + "recordingFailed": "Не удалось начать запись: {{error}}", + "modelLoadFailed": "Не удалось загрузить модель: {{model}}", + "modelLoadFailedUnknown": "неизвестная модель" }, "appLanguage": { "title": "Язык приложения", diff --git a/src/i18n/locales/tr/translation.json b/src/i18n/locales/tr/translation.json index 13ceb15..cd06aa8 100644 --- a/src/i18n/locales/tr/translation.json +++ b/src/i18n/locales/tr/translation.json @@ -541,7 +541,9 @@ }, "errors": { "loadDirectory": "Dizin yüklenirken hata oluştu: {{error}}", - "recordingFailed": "Failed to start recording: {{error}}" + "recordingFailed": "Kayıt başlatılamadı: {{error}}", + "modelLoadFailed": "Model yüklenemedi: {{model}}", + "modelLoadFailedUnknown": "bilinmeyen model" }, "appLanguage": { "title": "Uygulama Dili", diff --git a/src/i18n/locales/uk/translation.json b/src/i18n/locales/uk/translation.json index b16ea43..438d1e1 100644 --- a/src/i18n/locales/uk/translation.json +++ b/src/i18n/locales/uk/translation.json @@ -541,7 +541,9 @@ }, "errors": { "loadDirectory": "Помилка завантаження папки: {{error}}", - "recordingFailed": "Failed to start recording: {{error}}" + "recordingFailed": "Не вдалося розпочати запис: {{error}}", + "modelLoadFailed": "Не вдалося завантажити модель: {{model}}", + "modelLoadFailedUnknown": "невідома модель" }, "appLanguage": { "title": "Мова інтерфейсу", diff --git a/src/i18n/locales/vi/translation.json b/src/i18n/locales/vi/translation.json index 6fd7e8b..6365439 100644 --- a/src/i18n/locales/vi/translation.json +++ b/src/i18n/locales/vi/translation.json @@ -541,7 +541,9 @@ }, "errors": { "loadDirectory": "Lỗi khi tải thư mục: {{error}}", - "recordingFailed": "Failed to start recording: {{error}}" + "recordingFailed": "Không thể bắt đầu ghi âm: {{error}}", + "modelLoadFailed": "Không thể tải mô hình: {{model}}", + "modelLoadFailedUnknown": "mô hình không xác định" }, "appLanguage": { "title": "Ngôn ngữ ứng dụng", diff --git a/src/i18n/locales/zh-TW/translation.json b/src/i18n/locales/zh-TW/translation.json index a40877c..81bd9de 100644 --- a/src/i18n/locales/zh-TW/translation.json +++ b/src/i18n/locales/zh-TW/translation.json @@ -541,7 +541,9 @@ }, "errors": { "loadDirectory": "載入目錄時發生錯誤: {{error}}", - "recordingFailed": "Failed to start recording: {{error}}" + "recordingFailed": "錄音啟動失敗: {{error}}", + "modelLoadFailed": "載入模型失敗: {{model}}", + "modelLoadFailedUnknown": "未知模型" }, "appLanguage": { "title": "應用程式語言", diff --git a/src/i18n/locales/zh/translation.json b/src/i18n/locales/zh/translation.json index d8594f8..fb528a7 100644 --- a/src/i18n/locales/zh/translation.json +++ b/src/i18n/locales/zh/translation.json @@ -541,7 +541,9 @@ }, "errors": { "loadDirectory": "加载目录时出错: {{error}}", - "recordingFailed": "Failed to start recording: {{error}}" + "recordingFailed": "录音启动失败: {{error}}", + "modelLoadFailed": "加载模型失败: {{model}}", + "modelLoadFailedUnknown": "未知模型" }, "appLanguage": { "title": "应用语言", diff --git a/src/lib/types/events.ts b/src/lib/types/events.ts new file mode 100644 index 0000000..a1fb00d --- /dev/null +++ b/src/lib/types/events.ts @@ -0,0 +1,6 @@ +export interface ModelStateEvent { + event_type: string; + model_id?: string; + model_name?: string; + error?: string; +}