diff --git a/apps/readest-app/AGENTS.md b/apps/readest-app/AGENTS.md index 7ec1ed68..72df3adc 100644 --- a/apps/readest-app/AGENTS.md +++ b/apps/readest-app/AGENTS.md @@ -75,6 +75,10 @@ Platform-specific code lives in `src-tauri/src/{macos,windows,android,ios}/`. Cu - No lookbehind regex in build output — verified by `check:lookbehind-regex`. - Run `pnpm build-check` (builds both targets + runs all checks) before submitting. +### i18n + +See [docs/i18n.md](docs/i18n.md) for the key-as-content translation approach, `stubTranslation` usage in non-React modules, and extraction workflow. + ### Safe Area Insets See [docs/safe-area-insets.md](docs/safe-area-insets.md) for rules on handling top/bottom insets for UI elements near screen edges. diff --git a/apps/readest-app/docs/i18n.md b/apps/readest-app/docs/i18n.md new file mode 100644 index 00000000..6e88d918 --- /dev/null +++ b/apps/readest-app/docs/i18n.md @@ -0,0 +1,48 @@ +## i18n Guide + +Readest uses a **key-as-content** approach — English strings are the translation keys. The English locale (`en/translation.json`) is empty because keys serve as content. Other locales contain actual translations. + +### In React Components + +```tsx +import { useTranslation } from '@/hooks/useTranslation'; + +const _ = useTranslation(); +_('Progress synced'); +``` + +### In Non-React Modules + +Two-step process: + +**1. Declaration** — Use `stubTranslation` to mark strings for scanner extraction (returns key as-is, does NOT translate): + +```ts +import { stubTranslation as _ } from '@/utils/misc'; + +// These calls only register keys for extraction +_('Reveal in Finder'); +_('Reveal in Explorer'); +``` + +**2. Usage** — In the React component that consumes the value, apply the real `_()` from `useTranslation`: + +```tsx +const _ = useTranslation(); +const label = _(getRevealLabel()); // translates at runtime +``` + +### Extraction & Translation + +```bash +pnpm i18n:extract # Scans codebase, adds new keys with __STRING_NOT_TRANSLATED__ +``` + +- Translation files: `public/locales//translation.json` +- Only `_('KEY')` and `_('KEY', options)` patterns are recognized by i18next-scanner + +### Rules + +- `stubTranslation` is for extraction only — always apply `_()` from `useTranslation` in the component for runtime translation. +- Fallback: when no translation exists, the English key itself is displayed. +- Error messages: register keys with `stubTranslation` in utility modules (e.g. `src/services/errors.ts`), return the English key from helpers, wrap with `_()` in the component. diff --git a/apps/readest-app/public/locales/ar/translation.json b/apps/readest-app/public/locales/ar/translation.json index a771caa1..46a3ac3c 100644 --- a/apps/readest-app/public/locales/ar/translation.json +++ b/apps/readest-app/public/locales/ar/translation.json @@ -1117,5 +1117,6 @@ "Decrease font size": "تصغير حجم الخط", "Increase font size": "تكبير حجم الخط", "Focus": "تركيز", - "Theme color": "لون السمة" + "Theme color": "لون السمة", + "Import failed": "فشل الاستيراد" } diff --git a/apps/readest-app/public/locales/bn/translation.json b/apps/readest-app/public/locales/bn/translation.json index e77286db..cf03b4a6 100644 --- a/apps/readest-app/public/locales/bn/translation.json +++ b/apps/readest-app/public/locales/bn/translation.json @@ -1069,5 +1069,6 @@ "Decrease font size": "ফন্টের আকার কমান", "Increase font size": "ফন্টের আকার বাড়ান", "Focus": "ফোকাস", - "Theme color": "থিম রঙ" + "Theme color": "থিম রঙ", + "Import failed": "আমদানি ব্যর্থ" } diff --git a/apps/readest-app/public/locales/bo/translation.json b/apps/readest-app/public/locales/bo/translation.json index b2141e24..caf2d887 100644 --- a/apps/readest-app/public/locales/bo/translation.json +++ b/apps/readest-app/public/locales/bo/translation.json @@ -1057,5 +1057,6 @@ "Decrease font size": "ཡིག་གཟུགས་ཆུང་དུ་གཏོང་བ", "Increase font size": "ཡིག་གཟུགས་ཆེ་རུ་གཏོང་བ", "Focus": "དམིགས་གཏད", - "Theme color": "བརྗོད་དོན་ཚོན་མདོག" + "Theme color": "བརྗོད་དོན་ཚོན་མདོག", + "Import failed": "ནང་འདྲེན་མི་ཐུབ།" } diff --git a/apps/readest-app/public/locales/de/translation.json b/apps/readest-app/public/locales/de/translation.json index ecef94af..3dda93be 100644 --- a/apps/readest-app/public/locales/de/translation.json +++ b/apps/readest-app/public/locales/de/translation.json @@ -1069,5 +1069,6 @@ "Decrease font size": "Schriftgröße verkleinern", "Increase font size": "Schriftgröße vergrößern", "Focus": "Fokus", - "Theme color": "Themenfarbe" + "Theme color": "Themenfarbe", + "Import failed": "Import fehlgeschlagen" } diff --git a/apps/readest-app/public/locales/el/translation.json b/apps/readest-app/public/locales/el/translation.json index 26d5d33e..8da4d004 100644 --- a/apps/readest-app/public/locales/el/translation.json +++ b/apps/readest-app/public/locales/el/translation.json @@ -1069,5 +1069,6 @@ "Decrease font size": "Μείωση μεγέθους γραμματοσειράς", "Increase font size": "Αύξηση μεγέθους γραμματοσειράς", "Focus": "Εστίαση", - "Theme color": "Χρώμα θέματος" + "Theme color": "Χρώμα θέματος", + "Import failed": "Η εισαγωγή απέτυχε" } diff --git a/apps/readest-app/public/locales/es/translation.json b/apps/readest-app/public/locales/es/translation.json index b7f5d2cf..cdb1032c 100644 --- a/apps/readest-app/public/locales/es/translation.json +++ b/apps/readest-app/public/locales/es/translation.json @@ -1081,5 +1081,6 @@ "Decrease font size": "Reducir tamaño de fuente", "Increase font size": "Aumentar tamaño de fuente", "Focus": "Enfoque", - "Theme color": "Color del tema" + "Theme color": "Color del tema", + "Import failed": "Error de importación" } diff --git a/apps/readest-app/public/locales/fa/translation.json b/apps/readest-app/public/locales/fa/translation.json index 7865e154..ad781140 100644 --- a/apps/readest-app/public/locales/fa/translation.json +++ b/apps/readest-app/public/locales/fa/translation.json @@ -1069,5 +1069,6 @@ "Decrease font size": "کاهش اندازه قلم", "Increase font size": "افزایش اندازه قلم", "Focus": "تمرکز", - "Theme color": "رنگ پوسته" + "Theme color": "رنگ پوسته", + "Import failed": "وارد کردن ناموفق بود" } diff --git a/apps/readest-app/public/locales/fr/translation.json b/apps/readest-app/public/locales/fr/translation.json index 8dd2848f..b2b44fe7 100644 --- a/apps/readest-app/public/locales/fr/translation.json +++ b/apps/readest-app/public/locales/fr/translation.json @@ -1081,5 +1081,6 @@ "Decrease font size": "Réduire la taille de police", "Increase font size": "Augmenter la taille de police", "Focus": "Focus", - "Theme color": "Couleur du thème" + "Theme color": "Couleur du thème", + "Import failed": "Échec de l'importation" } diff --git a/apps/readest-app/public/locales/he/translation.json b/apps/readest-app/public/locales/he/translation.json index e65ce632..b31e2b08 100644 --- a/apps/readest-app/public/locales/he/translation.json +++ b/apps/readest-app/public/locales/he/translation.json @@ -1081,5 +1081,6 @@ "Decrease font size": "הקטן גודל גופן", "Increase font size": "הגדל גודל גופן", "Focus": "מיקוד", - "Theme color": "צבע ערכת נושא" + "Theme color": "צבע ערכת נושא", + "Import failed": "הייבוא נכשל" } diff --git a/apps/readest-app/public/locales/hi/translation.json b/apps/readest-app/public/locales/hi/translation.json index d762da57..e5c4cc21 100644 --- a/apps/readest-app/public/locales/hi/translation.json +++ b/apps/readest-app/public/locales/hi/translation.json @@ -1069,5 +1069,6 @@ "Decrease font size": "फ़ॉन्ट आकार घटाएँ", "Increase font size": "फ़ॉन्ट आकार बढ़ाएँ", "Focus": "फ़ोकस", - "Theme color": "थीम रंग" + "Theme color": "थीम रंग", + "Import failed": "आयात विफल" } diff --git a/apps/readest-app/public/locales/id/translation.json b/apps/readest-app/public/locales/id/translation.json index 10e03f3c..88c6b66f 100644 --- a/apps/readest-app/public/locales/id/translation.json +++ b/apps/readest-app/public/locales/id/translation.json @@ -1057,5 +1057,6 @@ "Decrease font size": "Perkecil ukuran font", "Increase font size": "Perbesar ukuran font", "Focus": "Fokus", - "Theme color": "Warna tema" + "Theme color": "Warna tema", + "Import failed": "Impor gagal" } diff --git a/apps/readest-app/public/locales/it/translation.json b/apps/readest-app/public/locales/it/translation.json index afd87a22..8fe83772 100644 --- a/apps/readest-app/public/locales/it/translation.json +++ b/apps/readest-app/public/locales/it/translation.json @@ -1081,5 +1081,6 @@ "Decrease font size": "Riduci dimensione carattere", "Increase font size": "Aumenta dimensione carattere", "Focus": "Messa a fuoco", - "Theme color": "Colore del tema" + "Theme color": "Colore del tema", + "Import failed": "Importazione fallita" } diff --git a/apps/readest-app/public/locales/ja/translation.json b/apps/readest-app/public/locales/ja/translation.json index 3b33bea0..2ad460c5 100644 --- a/apps/readest-app/public/locales/ja/translation.json +++ b/apps/readest-app/public/locales/ja/translation.json @@ -1057,5 +1057,6 @@ "Decrease font size": "文字サイズを縮小", "Increase font size": "文字サイズを拡大", "Focus": "フォーカス", - "Theme color": "テーマカラー" + "Theme color": "テーマカラー", + "Import failed": "インポートに失敗しました" } diff --git a/apps/readest-app/public/locales/ko/translation.json b/apps/readest-app/public/locales/ko/translation.json index ff41d935..d4823f8b 100644 --- a/apps/readest-app/public/locales/ko/translation.json +++ b/apps/readest-app/public/locales/ko/translation.json @@ -1057,5 +1057,6 @@ "Decrease font size": "글꼴 크기 줄이기", "Increase font size": "글꼴 크기 늘리기", "Focus": "초점", - "Theme color": "테마 색상" + "Theme color": "테마 색상", + "Import failed": "가져오기 실패" } diff --git a/apps/readest-app/public/locales/ms/translation.json b/apps/readest-app/public/locales/ms/translation.json index d124a6e4..e454cd56 100644 --- a/apps/readest-app/public/locales/ms/translation.json +++ b/apps/readest-app/public/locales/ms/translation.json @@ -1057,5 +1057,6 @@ "Decrease font size": "Kecilkan saiz fon", "Increase font size": "Besarkan saiz fon", "Focus": "Fokus", - "Theme color": "Warna tema" + "Theme color": "Warna tema", + "Import failed": "Import gagal" } diff --git a/apps/readest-app/public/locales/nl/translation.json b/apps/readest-app/public/locales/nl/translation.json index 1bee1aa7..f88699d7 100644 --- a/apps/readest-app/public/locales/nl/translation.json +++ b/apps/readest-app/public/locales/nl/translation.json @@ -1069,5 +1069,6 @@ "Decrease font size": "Lettergrootte verkleinen", "Increase font size": "Lettergrootte vergroten", "Focus": "Focus", - "Theme color": "Themakleur" + "Theme color": "Themakleur", + "Import failed": "Import mislukt" } diff --git a/apps/readest-app/public/locales/pl/translation.json b/apps/readest-app/public/locales/pl/translation.json index 27c89c71..1fb4a212 100644 --- a/apps/readest-app/public/locales/pl/translation.json +++ b/apps/readest-app/public/locales/pl/translation.json @@ -1093,5 +1093,6 @@ "Decrease font size": "Zmniejsz rozmiar czcionki", "Increase font size": "Zwiększ rozmiar czcionki", "Focus": "Fokus", - "Theme color": "Kolor motywu" + "Theme color": "Kolor motywu", + "Import failed": "Import nie powiódł się" } diff --git a/apps/readest-app/public/locales/pt/translation.json b/apps/readest-app/public/locales/pt/translation.json index 2db71aa8..bb3a004c 100644 --- a/apps/readest-app/public/locales/pt/translation.json +++ b/apps/readest-app/public/locales/pt/translation.json @@ -1081,5 +1081,6 @@ "Decrease font size": "Diminuir tamanho da fonte", "Increase font size": "Aumentar tamanho da fonte", "Focus": "Foco", - "Theme color": "Cor do tema" + "Theme color": "Cor do tema", + "Import failed": "Falha na importação" } diff --git a/apps/readest-app/public/locales/ru/translation.json b/apps/readest-app/public/locales/ru/translation.json index 78ae39b3..359a5524 100644 --- a/apps/readest-app/public/locales/ru/translation.json +++ b/apps/readest-app/public/locales/ru/translation.json @@ -1093,5 +1093,6 @@ "Decrease font size": "Уменьшить размер шрифта", "Increase font size": "Увеличить размер шрифта", "Focus": "Фокус", - "Theme color": "Цвет темы" + "Theme color": "Цвет темы", + "Import failed": "Ошибка импорта" } diff --git a/apps/readest-app/public/locales/si/translation.json b/apps/readest-app/public/locales/si/translation.json index 65ec4412..3354210c 100644 --- a/apps/readest-app/public/locales/si/translation.json +++ b/apps/readest-app/public/locales/si/translation.json @@ -1069,5 +1069,6 @@ "Decrease font size": "අකුරු ප්‍රමාණය අඩු කරන්න", "Increase font size": "අකුරු ප්‍රමාණය වැඩි කරන්න", "Focus": "අවධානය", - "Theme color": "තේමා වර්ණය" + "Theme color": "තේමා වර්ණය", + "Import failed": "ආයාත කිරීම අසාර්ථකයි" } diff --git a/apps/readest-app/public/locales/sl/translation.json b/apps/readest-app/public/locales/sl/translation.json index 65d50353..e008e6d3 100644 --- a/apps/readest-app/public/locales/sl/translation.json +++ b/apps/readest-app/public/locales/sl/translation.json @@ -1093,5 +1093,6 @@ "Decrease font size": "Pomanjšaj pisavo", "Increase font size": "Povečaj pisavo", "Focus": "Fokus", - "Theme color": "Barva teme" + "Theme color": "Barva teme", + "Import failed": "Uvoz ni uspel" } diff --git a/apps/readest-app/public/locales/sv/translation.json b/apps/readest-app/public/locales/sv/translation.json index d0b5d70c..ba6b9bf8 100644 --- a/apps/readest-app/public/locales/sv/translation.json +++ b/apps/readest-app/public/locales/sv/translation.json @@ -1069,5 +1069,6 @@ "Decrease font size": "Minska teckenstorlek", "Increase font size": "Öka teckenstorlek", "Focus": "Fokus", - "Theme color": "Temafärg" + "Theme color": "Temafärg", + "Import failed": "Importen misslyckades" } diff --git a/apps/readest-app/public/locales/ta/translation.json b/apps/readest-app/public/locales/ta/translation.json index bbff1762..8e369db0 100644 --- a/apps/readest-app/public/locales/ta/translation.json +++ b/apps/readest-app/public/locales/ta/translation.json @@ -1069,5 +1069,6 @@ "Decrease font size": "எழுத்துரு அளவைக் குறை", "Increase font size": "எழுத்துரு அளவை அதிகரி", "Focus": "குவியம்", - "Theme color": "தீம் நிறம்" + "Theme color": "தீம் நிறம்", + "Import failed": "இறக்குமதி தோல்வி" } diff --git a/apps/readest-app/public/locales/th/translation.json b/apps/readest-app/public/locales/th/translation.json index f01dc969..8c5b9b4a 100644 --- a/apps/readest-app/public/locales/th/translation.json +++ b/apps/readest-app/public/locales/th/translation.json @@ -1057,5 +1057,6 @@ "Decrease font size": "ลดขนาดตัวอักษร", "Increase font size": "เพิ่มขนาดตัวอักษร", "Focus": "โฟกัส", - "Theme color": "สีธีม" + "Theme color": "สีธีม", + "Import failed": "นำเข้าล้มเหลว" } diff --git a/apps/readest-app/public/locales/tr/translation.json b/apps/readest-app/public/locales/tr/translation.json index 0236def7..6c4f0fb6 100644 --- a/apps/readest-app/public/locales/tr/translation.json +++ b/apps/readest-app/public/locales/tr/translation.json @@ -1069,5 +1069,6 @@ "Decrease font size": "Yazı tipi boyutunu küçült", "Increase font size": "Yazı tipi boyutunu büyüt", "Focus": "Odak", - "Theme color": "Tema rengi" + "Theme color": "Tema rengi", + "Import failed": "İçe aktarma başarısız" } diff --git a/apps/readest-app/public/locales/uk/translation.json b/apps/readest-app/public/locales/uk/translation.json index a170c1e9..5089b906 100644 --- a/apps/readest-app/public/locales/uk/translation.json +++ b/apps/readest-app/public/locales/uk/translation.json @@ -1093,5 +1093,6 @@ "Decrease font size": "Зменшити розмір шрифту", "Increase font size": "Збільшити розмір шрифту", "Focus": "Фокус", - "Theme color": "Колір теми" + "Theme color": "Колір теми", + "Import failed": "Помилка імпорту" } diff --git a/apps/readest-app/public/locales/vi/translation.json b/apps/readest-app/public/locales/vi/translation.json index 4066959f..f9cded9c 100644 --- a/apps/readest-app/public/locales/vi/translation.json +++ b/apps/readest-app/public/locales/vi/translation.json @@ -1057,5 +1057,6 @@ "Decrease font size": "Giảm cỡ chữ", "Increase font size": "Tăng cỡ chữ", "Focus": "Tiêu điểm", - "Theme color": "Màu chủ đề" + "Theme color": "Màu chủ đề", + "Import failed": "Nhập thất bại" } diff --git a/apps/readest-app/public/locales/zh-CN/translation.json b/apps/readest-app/public/locales/zh-CN/translation.json index f486900b..293c1d3c 100644 --- a/apps/readest-app/public/locales/zh-CN/translation.json +++ b/apps/readest-app/public/locales/zh-CN/translation.json @@ -1057,5 +1057,6 @@ "Decrease font size": "缩小字号", "Increase font size": "放大字号", "Focus": "焦点", - "Theme color": "主题颜色" + "Theme color": "主题颜色", + "Import failed": "导入失败" } diff --git a/apps/readest-app/public/locales/zh-TW/translation.json b/apps/readest-app/public/locales/zh-TW/translation.json index 72650fcb..e1e930c6 100644 --- a/apps/readest-app/public/locales/zh-TW/translation.json +++ b/apps/readest-app/public/locales/zh-TW/translation.json @@ -1057,5 +1057,6 @@ "Decrease font size": "縮小字型", "Increase font size": "放大字型", "Focus": "焦點", - "Theme color": "主題顏色" + "Theme color": "主題顏色", + "Import failed": "匯入失敗" } diff --git a/apps/readest-app/src/app/library/page.tsx b/apps/readest-app/src/app/library/page.tsx index 45a2154e..a03fd671 100644 --- a/apps/readest-app/src/app/library/page.tsx +++ b/apps/readest-app/src/app/library/page.tsx @@ -12,6 +12,7 @@ import { Book } from '@/types/book'; import { AppService, DeleteAction } from '@/types/system'; import { navigateToLibrary, navigateToReader } from '@/utils/nav'; import { formatAuthors, formatTitle, getPrimaryLanguage, listFormater } from '@/utils/book'; +import { getImportErrorMessage } from '@/services/errors'; import { eventDispatcher } from '@/utils/event'; import { ProgressPayload } from '@/utils/transfer'; import { throttle } from '@/utils/throttle'; @@ -495,14 +496,6 @@ const LibraryPageContent = ({ searchParams }: { searchParams: ReadonlyURLSearchP const { library } = useLibraryStore.getState(); const failedImports: Array<{ filename: string; errorMessage: string }> = []; const successfulImports: string[] = []; - const errorMap: [string, string][] = [ - ['No chapters detected', _('No chapters detected')], - ['Failed to parse EPUB', _('Failed to parse the EPUB file')], - ['Unsupported format', _('This book format is not supported')], - ['Failed to open file', _('Failed to open the book file')], - ['Invalid or empty book file', _('The book file is empty')], - ['Unsupported or corrupted book file', _('The book file is corrupted')], - ]; const processFile = async (selectedFile: SelectedFile): Promise => { const file = selectedFile.file || selectedFile.path; @@ -530,10 +523,7 @@ const LibraryPageContent = ({ searchParams }: { searchParams: ReadonlyURLSearchP } catch (error) { const filename = typeof file === 'string' ? file : file.name; const baseFilename = getFilename(filename); - const errorMessage = - error instanceof Error - ? errorMap.find(([str]) => error.message.includes(str))?.[1] || error.message - : ''; + const errorMessage = error instanceof Error ? _(getImportErrorMessage(error.message)) : ''; failedImports.push({ filename: baseFilename, errorMessage }); console.error('Failed to import book:', filename, error); return null; diff --git a/apps/readest-app/src/app/opds/components/PublicationView.tsx b/apps/readest-app/src/app/opds/components/PublicationView.tsx index 1c7478f0..f9e3ed35 100644 --- a/apps/readest-app/src/app/opds/components/PublicationView.tsx +++ b/apps/readest-app/src/app/opds/components/PublicationView.tsx @@ -9,6 +9,7 @@ import { OPDSLink, OPDSPublication, REL, SYMBOL } from '@/types/opds'; import { useTranslation } from '@/hooks/useTranslation'; import { getFileExtFromMimeType } from '@/libs/document'; import { formatDate, formatLanguage } from '@/utils/book'; +import { getImportErrorMessage, ImportError } from '@/services/errors'; import { eventDispatcher } from '@/utils/event'; import { navigateToReader } from '@/utils/nav'; import { CachedImage } from '@/components/CachedImage'; @@ -96,10 +97,19 @@ export function PublicationView({ eventDispatcher.dispatch('toast', { type: 'success', message: _('Download completed') }); } catch (error) { console.error('Download failed:', error); - eventDispatcher.dispatch('toast', { - type: 'error', - message: _('Download failed') + `:\n${href}`, - }); + if (error instanceof ImportError) { + const friendlyMsg = _(getImportErrorMessage(error.message)); + eventDispatcher.dispatch('toast', { + type: 'error', + message: _('Import failed') + `:\n${friendlyMsg}`, + timeout: 5000, + }); + } else { + eventDispatcher.dispatch('toast', { + type: 'error', + message: _('Download failed') + `:\n${href}`, + }); + } } finally { setDownloading(false); setProgress(null); diff --git a/apps/readest-app/src/app/opds/page.tsx b/apps/readest-app/src/app/opds/page.tsx index 57c29a87..141845c0 100644 --- a/apps/readest-app/src/app/opds/page.tsx +++ b/apps/readest-app/src/app/opds/page.tsx @@ -36,6 +36,7 @@ import { needsProxy, probeFilename, } from './utils/opdsReq'; +import { ImportError } from '@/services/errors'; import { READEST_OPDS_USER_AGENT } from '@/services/constants'; import { FeedView } from './components/FeedView'; import { PublicationView } from './components/PublicationView'; @@ -463,15 +464,20 @@ export default function BrowserPage() { } const { library, setLibrary } = useLibraryStore.getState(); - const book = await appService.importBook(dstFilePath, library); - if (user && book && !book.uploadedAt && settings.autoUpload) { - setTimeout(() => { - transferManager.queueUpload(book); - }, 3000); + try { + const book = await appService.importBook(dstFilePath, library); + if (user && book && !book.uploadedAt && settings.autoUpload) { + setTimeout(() => { + transferManager.queueUpload(book); + }, 3000); + } + setLibrary(library); + appService.saveLibraryBooks(library); + return book; + } catch (importError) { + console.error('Import error:', importError); + throw new ImportError(importError); } - setLibrary(library); - appService.saveLibraryBooks(library); - return book; } } catch (e) { console.error('Download error:', e); diff --git a/apps/readest-app/src/services/appService.ts b/apps/readest-app/src/services/appService.ts index cc031e49..066e753b 100644 --- a/apps/readest-app/src/services/appService.ts +++ b/apps/readest-app/src/services/appService.ts @@ -80,7 +80,7 @@ import { import { ClosableFile } from '@/utils/file'; import { ProgressHandler } from '@/utils/transfer'; import { TxtToEpubConverter } from '@/utils/txt'; -import { BOOK_FILE_NOT_FOUND_ERROR } from './errors'; +import { BookFileNotFoundError } from './errors'; import { CustomTextureInfo } from '@/styles/textures'; import { CustomFont, CustomFontInfo } from '@/styles/fonts'; import { parseFontInfo } from '@/utils/font'; @@ -395,7 +395,7 @@ export abstract class BaseAppService implements AppService { loadedBook.metadata.title = getBaseFilename(filename); } } catch (error) { - throw new Error(`Failed to open the book: ${(error as Error).message || error}`); + throw new Error(`Failed to open the book file: ${(error as Error).message || error}`); } const hash = await partialMD5(fileobj); @@ -769,10 +769,10 @@ export abstract class BaseAppService implements AppService { if (bookFile) { file = await this.fs.openFile(`${bookDir}/${bookFile.path}`, 'Books'); } else { - throw new Error(BOOK_FILE_NOT_FOUND_ERROR); + throw new BookFileNotFoundError(); } } else { - throw new Error(BOOK_FILE_NOT_FOUND_ERROR); + throw new BookFileNotFoundError(); } } return { book, file }; diff --git a/apps/readest-app/src/services/errors.ts b/apps/readest-app/src/services/errors.ts index 5f23ae25..784c32ac 100644 --- a/apps/readest-app/src/services/errors.ts +++ b/apps/readest-app/src/services/errors.ts @@ -1 +1,30 @@ -export const BOOK_FILE_NOT_FOUND_ERROR = 'Book file not found'; +import { stubTranslation as _ } from '@/utils/misc'; + +export class BookFileNotFoundError extends Error { + constructor() { + super('Book file not found'); + this.name = 'BookFileNotFoundError'; + } +} + +export class ImportError extends Error { + constructor(cause: unknown) { + const msg = cause instanceof Error ? cause.message : String(cause); + super(msg); + this.name = 'ImportError'; + } +} + +const IMPORT_ERROR_MAP: [string, string][] = [ + ['No chapters detected', _('No chapters detected')], + ['Failed to parse EPUB', _('Failed to parse the EPUB file')], + ['Unsupported format', _('This book format is not supported')], + ['Failed to open file', _('Failed to open the book file')], + ['Invalid or empty book file', _('The book file is empty')], + ['Unsupported or corrupted book file', _('The book file is corrupted')], +]; + +export const getImportErrorMessage = (errorMsg: string): string => { + const match = IMPORT_ERROR_MAP.find(([str]) => errorMsg.includes(str)); + return match ? match[1] : errorMsg; +};