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 <noreply@anthropic.com>
This commit is contained in:
CJ Pais 2026-03-11 14:43:16 +08:00 committed by GitHub
parent d6ed1f9bbc
commit 82297fa146
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
20 changed files with 79 additions and 23 deletions

View file

@ -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<ModelStateEvent>("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

View file

@ -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"

View file

@ -541,7 +541,9 @@
},
"errors": {
"loadDirectory": "خطأ في تحميل المجلد: {{error}}",
"recordingFailed": "Failed to start recording: {{error}}"
"recordingFailed": "فشل في بدء التسجيل: {{error}}",
"modelLoadFailed": "فشل في تحميل النموذج: {{model}}",
"modelLoadFailedUnknown": "نموذج غير معروف"
},
"appLanguage": {
"title": "لغة التطبيق",

View file

@ -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",

View file

@ -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",

View file

@ -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",

View file

@ -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",

View file

@ -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",

View file

@ -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",

View file

@ -541,7 +541,9 @@
},
"errors": {
"loadDirectory": "ディレクトリの読み込みエラー: {{error}}",
"recordingFailed": "Failed to start recording: {{error}}"
"recordingFailed": "録音の開始に失敗しました: {{error}}",
"modelLoadFailed": "モデルの読み込みに失敗しました: {{model}}",
"modelLoadFailedUnknown": "不明なモデル"
},
"appLanguage": {
"title": "アプリケーション言語",

View file

@ -541,7 +541,9 @@
},
"errors": {
"loadDirectory": "디렉토리 로딩 오류: {{error}}",
"recordingFailed": "Failed to start recording: {{error}}"
"recordingFailed": "녹음을 시작하지 못했습니다: {{error}}",
"modelLoadFailed": "모델을 불러오지 못했습니다: {{model}}",
"modelLoadFailedUnknown": "알 수 없는 모델"
},
"appLanguage": {
"title": "애플리케이션 언어",

View file

@ -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",

View file

@ -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",

View file

@ -541,7 +541,9 @@
},
"errors": {
"loadDirectory": "Ошибка загрузки каталога: {{error}}.",
"recordingFailed": "Failed to start recording: {{error}}"
"recordingFailed": "Не удалось начать запись: {{error}}",
"modelLoadFailed": "Не удалось загрузить модель: {{model}}",
"modelLoadFailedUnknown": "неизвестная модель"
},
"appLanguage": {
"title": "Язык приложения",

View file

@ -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",

View file

@ -541,7 +541,9 @@
},
"errors": {
"loadDirectory": "Помилка завантаження папки: {{error}}",
"recordingFailed": "Failed to start recording: {{error}}"
"recordingFailed": "Не вдалося розпочати запис: {{error}}",
"modelLoadFailed": "Не вдалося завантажити модель: {{model}}",
"modelLoadFailedUnknown": "невідома модель"
},
"appLanguage": {
"title": "Мова інтерфейсу",

View file

@ -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",

View file

@ -541,7 +541,9 @@
},
"errors": {
"loadDirectory": "載入目錄時發生錯誤: {{error}}",
"recordingFailed": "Failed to start recording: {{error}}"
"recordingFailed": "錄音啟動失敗: {{error}}",
"modelLoadFailed": "載入模型失敗: {{model}}",
"modelLoadFailedUnknown": "未知模型"
},
"appLanguage": {
"title": "應用程式語言",

View file

@ -541,7 +541,9 @@
},
"errors": {
"loadDirectory": "加载目录时出错: {{error}}",
"recordingFailed": "Failed to start recording: {{error}}"
"recordingFailed": "录音启动失败: {{error}}",
"modelLoadFailed": "加载模型失败: {{model}}",
"modelLoadFailedUnknown": "未知模型"
},
"appLanguage": {
"title": "应用语言",

6
src/lib/types/events.ts Normal file
View file

@ -0,0 +1,6 @@
export interface ModelStateEvent {
event_type: string;
model_id?: string;
model_name?: string;
error?: string;
}