From 8eefba2bee0710d61a02ed7ed7ee8aac615a321b Mon Sep 17 00:00:00 2001 From: Huang Xin Date: Wed, 2 Jul 2025 13:27:05 +0800 Subject: [PATCH] feat: add an option to show/hide original text in translation, also closes #1492 (#1511) --- .../public/locales/ar/translation.json | 3 +- .../public/locales/de/translation.json | 3 +- .../public/locales/el/translation.json | 3 +- .../public/locales/es/translation.json | 3 +- .../public/locales/fr/translation.json | 3 +- .../public/locales/hi/translation.json | 3 +- .../public/locales/id/translation.json | 3 +- .../public/locales/it/translation.json | 3 +- .../public/locales/ja/translation.json | 3 +- .../public/locales/ko/translation.json | 3 +- .../public/locales/nl/translation.json | 3 +- .../public/locales/pl/translation.json | 3 +- .../public/locales/pt/translation.json | 3 +- .../public/locales/ru/translation.json | 3 +- .../public/locales/tr/translation.json | 3 +- .../public/locales/uk/translation.json | 3 +- .../public/locales/vi/translation.json | 3 +- .../public/locales/zh-CN/translation.json | 5 +- .../public/locales/zh-TW/translation.json | 5 +- .../app/reader/components/FoliateViewer.tsx | 3 +- .../app/reader/components/ReaderContent.tsx | 7 +- .../reader/components/annotator/Annotator.tsx | 2 +- .../components/settings/ControlPanel.tsx | 6 +- .../reader/components/settings/LangPanel.tsx | 23 ++++++ .../components/settings/LayoutPanel.tsx | 7 +- .../app/reader/components/sidebar/TOCItem.tsx | 12 +-- .../app/reader/components/sidebar/TOCView.tsx | 2 +- .../app/reader/hooks/useTextTranslation.ts | 74 +++++++++++++++++-- apps/readest-app/src/services/constants.ts | 3 +- apps/readest-app/src/types/book.ts | 1 + apps/readest-app/src/utils/reload.ts | 7 ++ apps/readest-app/src/utils/style.ts | 34 +++++++-- apps/readest-app/src/utils/walk.ts | 12 ++- packages/foliate-js | 2 +- 34 files changed, 203 insertions(+), 53 deletions(-) create mode 100644 apps/readest-app/src/utils/reload.ts diff --git a/apps/readest-app/public/locales/ar/translation.json b/apps/readest-app/public/locales/ar/translation.json index 038a7ede..712a598a 100644 --- a/apps/readest-app/public/locales/ar/translation.json +++ b/apps/readest-app/public/locales/ar/translation.json @@ -418,5 +418,6 @@ "Parallel Read": "القراءة المتزامنة", "Disable": "تعطيل", "Enable": "تمكين", - "Upgrade to Readest Premium": "ترقية إلى Readest Premium" + "Upgrade to Readest Premium": "ترقية إلى Readest Premium", + "Show Source Text": "عرض النص المصدر" } diff --git a/apps/readest-app/public/locales/de/translation.json b/apps/readest-app/public/locales/de/translation.json index e0879813..f8f4a027 100644 --- a/apps/readest-app/public/locales/de/translation.json +++ b/apps/readest-app/public/locales/de/translation.json @@ -402,5 +402,6 @@ "Parallel Read": "Paralleles Lesen", "Disable": "Deaktivieren", "Enable": "Aktivieren", - "Upgrade to Readest Premium": "Upgrade auf Readest Premium" + "Upgrade to Readest Premium": "Upgrade auf Readest Premium", + "Show Source Text": "Quelltext anzeigen" } diff --git a/apps/readest-app/public/locales/el/translation.json b/apps/readest-app/public/locales/el/translation.json index b955407b..a9434a43 100644 --- a/apps/readest-app/public/locales/el/translation.json +++ b/apps/readest-app/public/locales/el/translation.json @@ -402,5 +402,6 @@ "Cloud Sync Storage": "Χώρος συγχρονισμού cloud", "Disable": "Απενεργοποίηση", "Enable": "Ενεργοποίηση", - "Upgrade to Readest Premium": "Αναβάθμιση στο Readest Premium" + "Upgrade to Readest Premium": "Αναβάθμιση στο Readest Premium", + "Show Source Text": "Εμφάνιση αρχικού κειμένου" } diff --git a/apps/readest-app/public/locales/es/translation.json b/apps/readest-app/public/locales/es/translation.json index 29f03cfe..e3affea1 100644 --- a/apps/readest-app/public/locales/es/translation.json +++ b/apps/readest-app/public/locales/es/translation.json @@ -406,5 +406,6 @@ "Cloud Sync Storage": "Almacenamiento en la nube", "Disable": "Desactivar", "Enable": "Activar", - "Upgrade to Readest Premium": "Actualizar a Readest Premium" + "Upgrade to Readest Premium": "Actualizar a Readest Premium", + "Show Source Text": "Mostrar texto fuente" } diff --git a/apps/readest-app/public/locales/fr/translation.json b/apps/readest-app/public/locales/fr/translation.json index 6ad4596a..783a0d2b 100644 --- a/apps/readest-app/public/locales/fr/translation.json +++ b/apps/readest-app/public/locales/fr/translation.json @@ -406,5 +406,6 @@ "Cloud Sync Storage": "Stockage de synchronisation cloud", "Disable": "Désactiver", "Enable": "Activer", - "Upgrade to Readest Premium": "Passer à Readest Premium" + "Upgrade to Readest Premium": "Passer à Readest Premium", + "Show Source Text": "Afficher le texte source" } diff --git a/apps/readest-app/public/locales/hi/translation.json b/apps/readest-app/public/locales/hi/translation.json index fa27f10f..c1ed7766 100644 --- a/apps/readest-app/public/locales/hi/translation.json +++ b/apps/readest-app/public/locales/hi/translation.json @@ -402,5 +402,6 @@ "Cloud Sync Storage": "क्लाउड सिंक स्टोरेज", "Disable": "अक्षम करें", "Enable": "सक्षम करें", - "Upgrade to Readest Premium": "Readest प्रीमियम में अपग्रेड करें" + "Upgrade to Readest Premium": "Readest प्रीमियम में अपग्रेड करें", + "Show Source Text": "मूल पाठ दिखाएं" } diff --git a/apps/readest-app/public/locales/id/translation.json b/apps/readest-app/public/locales/id/translation.json index 9b4e50dd..3bf2ad7b 100644 --- a/apps/readest-app/public/locales/id/translation.json +++ b/apps/readest-app/public/locales/id/translation.json @@ -398,5 +398,6 @@ "Cloud Sync Storage": "Ruang cloud", "Disable": "Nonaktifkan", "Enable": "Aktifkan", - "Upgrade to Readest Premium": "Tingkatkan ke Readest Premium" + "Upgrade to Readest Premium": "Tingkatkan ke Readest Premium", + "Show Source Text": "Perlihatkan Teks Sumber" } diff --git a/apps/readest-app/public/locales/it/translation.json b/apps/readest-app/public/locales/it/translation.json index e766f2b1..795d5bfe 100644 --- a/apps/readest-app/public/locales/it/translation.json +++ b/apps/readest-app/public/locales/it/translation.json @@ -406,5 +406,6 @@ "Cloud Sync Storage": "Spazio cloud", "Disable": "Disabilita", "Enable": "Abilita", - "Upgrade to Readest Premium": "Aggiorna a Readest Premium" + "Upgrade to Readest Premium": "Aggiorna a Readest Premium", + "Show Source Text": "Mostra Testo Originale" } diff --git a/apps/readest-app/public/locales/ja/translation.json b/apps/readest-app/public/locales/ja/translation.json index dda9aac5..7d547450 100644 --- a/apps/readest-app/public/locales/ja/translation.json +++ b/apps/readest-app/public/locales/ja/translation.json @@ -398,5 +398,6 @@ "Cloud Sync Storage": "クラウド同期容量", "Disable": "無効にする", "Enable": "有効にする", - "Upgrade to Readest Premium": "Readest Premiumにアップグレード" + "Upgrade to Readest Premium": "Readest Premiumにアップグレード", + "Show Source Text": "原文を表示" } diff --git a/apps/readest-app/public/locales/ko/translation.json b/apps/readest-app/public/locales/ko/translation.json index b79d8b53..05fc54a0 100644 --- a/apps/readest-app/public/locales/ko/translation.json +++ b/apps/readest-app/public/locales/ko/translation.json @@ -398,5 +398,6 @@ "Cloud Sync Storage": "클라우드 저장 공간", "Disable": "비활성화", "Enable": "활성화", - "Upgrade to Readest Premium": "Readest Premium으로 업그레이드" + "Upgrade to Readest Premium": "Readest Premium으로 업그레이드", + "Show Source Text": "원본 텍스트 표시" } diff --git a/apps/readest-app/public/locales/nl/translation.json b/apps/readest-app/public/locales/nl/translation.json index ad77248e..7b2bfbdc 100644 --- a/apps/readest-app/public/locales/nl/translation.json +++ b/apps/readest-app/public/locales/nl/translation.json @@ -402,5 +402,6 @@ "Cloud Sync Storage": "Cloud Sync Opslag", "Disable": "Uitschakelen", "Enable": "Inschakelen", - "Upgrade to Readest Premium": "Upgrade naar Readest Premium" + "Upgrade to Readest Premium": "Upgrade naar Readest Premium", + "Show Source Text": "Toon brontekst" } diff --git a/apps/readest-app/public/locales/pl/translation.json b/apps/readest-app/public/locales/pl/translation.json index d65e9c88..6c4c07e7 100644 --- a/apps/readest-app/public/locales/pl/translation.json +++ b/apps/readest-app/public/locales/pl/translation.json @@ -410,5 +410,6 @@ "Cloud Sync Storage": "Miejsce w chmurze", "Disable": "Wyłącz", "Enable": "Włącz", - "Upgrade to Readest Premium": "Uaktualnij do Readest Premium" + "Upgrade to Readest Premium": "Uaktualnij do Readest Premium", + "Show Source Text": "Pokaż tekst źródłowy" } diff --git a/apps/readest-app/public/locales/pt/translation.json b/apps/readest-app/public/locales/pt/translation.json index 44531a7a..93c31d0d 100644 --- a/apps/readest-app/public/locales/pt/translation.json +++ b/apps/readest-app/public/locales/pt/translation.json @@ -406,5 +406,6 @@ "Cloud Sync Storage": "Armazenamento na Nuvem", "Disable": "Desativar", "Enable": "Ativar", - "Upgrade to Readest Premium": "Atualizar para Readest Premium" + "Upgrade to Readest Premium": "Atualizar para Readest Premium", + "Show Source Text": "Mostrar Texto Fonte" } diff --git a/apps/readest-app/public/locales/ru/translation.json b/apps/readest-app/public/locales/ru/translation.json index 7e81a794..33dc2bb2 100644 --- a/apps/readest-app/public/locales/ru/translation.json +++ b/apps/readest-app/public/locales/ru/translation.json @@ -410,5 +410,6 @@ "Cloud Sync Storage": "Облачное хранилище", "Disable": "Отключить", "Enable": "Включить", - "Upgrade to Readest Premium": "Обновить до Readest Premium" + "Upgrade to Readest Premium": "Обновить до Readest Premium", + "Show Source Text": "Показать исходный текст" } diff --git a/apps/readest-app/public/locales/tr/translation.json b/apps/readest-app/public/locales/tr/translation.json index 9f02e9c5..13edd3af 100644 --- a/apps/readest-app/public/locales/tr/translation.json +++ b/apps/readest-app/public/locales/tr/translation.json @@ -402,5 +402,6 @@ "Cloud Sync Storage": "Bulut Senkronizasyon Depolama", "Disable": "Devre Dışı Bırak", "Enable": "Etkinleştir", - "Upgrade to Readest Premium": "Readest Premium’a Yükselt" + "Upgrade to Readest Premium": "Readest Premium’a Yükselt", + "Show Source Text": "Kaynak Metni Göster" } diff --git a/apps/readest-app/public/locales/uk/translation.json b/apps/readest-app/public/locales/uk/translation.json index ee7fbe85..a656b6d7 100644 --- a/apps/readest-app/public/locales/uk/translation.json +++ b/apps/readest-app/public/locales/uk/translation.json @@ -410,5 +410,6 @@ "Cloud Sync Storage": "Облачне хранилище", "Disable": "Вимкнути", "Enable": "Увімкнути", - "Upgrade to Readest Premium": "Оновити до Readest Premium" + "Upgrade to Readest Premium": "Оновити до Readest Premium", + "Show Source Text": "Показати вихідний текст" } diff --git a/apps/readest-app/public/locales/vi/translation.json b/apps/readest-app/public/locales/vi/translation.json index 1b606f20..702c00d1 100644 --- a/apps/readest-app/public/locales/vi/translation.json +++ b/apps/readest-app/public/locales/vi/translation.json @@ -398,5 +398,6 @@ "Cloud Sync Storage": "Lưu trữ đồng bộ đám mây", "Disable": "Tắt", "Enable": "Bật", - "Upgrade to Readest Premium": "Đăng ký Readest Premium" + "Upgrade to Readest Premium": "Đăng ký Readest Premium", + "Show Source Text": "Hiển thị văn bản gốc" } diff --git a/apps/readest-app/public/locales/zh-CN/translation.json b/apps/readest-app/public/locales/zh-CN/translation.json index 1763afe5..bfdd62ae 100644 --- a/apps/readest-app/public/locales/zh-CN/translation.json +++ b/apps/readest-app/public/locales/zh-CN/translation.json @@ -381,7 +381,7 @@ "Need help? Contact our support team at support@readest.com": "需要帮助?请联系支持团队:support@readest.com", "Free Plan": "免费套餐", "month": "月", - "Unlimited Text-to-Speech Hours": "无限文本转语音时长", + "Unlimited Text-to-Speech Hours": "AI 朗读不限时长", "DeepL Free Access (10K characters/day)": "DeepL 免费额度(每日1万字符)", "AI Translations (per day)": "AI 翻译(每日)", "Plus Plan": "Plus 套餐", @@ -398,5 +398,6 @@ "Cloud Sync Storage": "云同步空间", "Disable": "禁用", "Enable": "启用", - "Upgrade to Readest Premium": "升级到 Readest Premium" + "Upgrade to Readest Premium": "升级到 Readest Premium", + "Show Source Text": "显示原文" } diff --git a/apps/readest-app/public/locales/zh-TW/translation.json b/apps/readest-app/public/locales/zh-TW/translation.json index 4c37fe61..5001dd9c 100644 --- a/apps/readest-app/public/locales/zh-TW/translation.json +++ b/apps/readest-app/public/locales/zh-TW/translation.json @@ -381,7 +381,7 @@ "Need help? Contact our support team at support@readest.com": "需要協助?請聯絡支援團隊:support@readest.com", "Free Plan": "免費方案", "month": "月", - "Unlimited Text-to-Speech Hours": "無限文字轉語音時數", + "Unlimited Text-to-Speech Hours": "AI 朗讀不限時長", "DeepL Free Access (10K characters/day)": "DeepL 免費使用(每日1萬字)", "AI Translations (per day)": "AI 翻譯(每日)", "Plus Plan": "Plus 方案", @@ -398,5 +398,6 @@ "Cloud Sync Storage": "雲同步空間", "Disable": "禁用", "Enable": "啟用", - "Upgrade to Readest Premium": "升級到 Readest Premium" + "Upgrade to Readest Premium": "升級到 Readest Premium", + "Show Source Text": "顯示原文" } diff --git a/apps/readest-app/src/app/reader/components/FoliateViewer.tsx b/apps/readest-app/src/app/reader/components/FoliateViewer.tsx index 6bc41c8e..e855fc63 100644 --- a/apps/readest-app/src/app/reader/components/FoliateViewer.tsx +++ b/apps/readest-app/src/app/reader/components/FoliateViewer.tsx @@ -13,7 +13,7 @@ import { usePagination } from '../hooks/usePagination'; import { useFoliateEvents } from '../hooks/useFoliateEvents'; import { useProgressSync } from '../hooks/useProgressSync'; import { useProgressAutoSave } from '../hooks/useProgressAutoSave'; -import { getStyles, transformStylesheet } from '@/utils/style'; +import { applyTranslationStyles, getStyles, transformStylesheet } from '@/utils/style'; import { mountAdditionalFonts } from '@/utils/font'; import { getBookDirFromLanguage, getBookDirFromWritingMode } from '@/utils/book'; import { useUICSS } from '@/hooks/useUICSS'; @@ -242,6 +242,7 @@ const FoliateViewer: React.FC<{ const height = viewHeight - insets.top - insets.bottom; book.transformTarget?.addEventListener('data', getDocTransformHandler({ width, height })); view.renderer.setStyles?.(getStyles(viewSettings)); + applyTranslationStyles(viewSettings); const animated = viewSettings.animated!; const maxColumnCount = viewSettings.maxColumnCount!; diff --git a/apps/readest-app/src/app/reader/components/ReaderContent.tsx b/apps/readest-app/src/app/reader/components/ReaderContent.tsx index 37b949c2..57e503cb 100644 --- a/apps/readest-app/src/app/reader/components/ReaderContent.tsx +++ b/apps/readest-app/src/app/reader/components/ReaderContent.tsx @@ -16,6 +16,7 @@ import { parseOpenWithFiles } from '@/helpers/openWith'; import { tauriHandleClose, tauriHandleOnCloseWindow } from '@/utils/window'; import { isTauriAppPlatform } from '@/services/environment'; import { uniqueId } from '@/utils/misc'; +import { throttle } from '@/utils/throttle'; import { eventDispatcher } from '@/utils/event'; import { navigateToLibrary } from '@/utils/nav'; import { BOOK_IDS_SEPARATOR } from '@/services/constants'; @@ -84,9 +85,11 @@ const ReaderContent: React.FC<{ ids?: string; settings: SystemSettings }> = ({ i if (isTauriAppPlatform()) tauriHandleOnCloseWindow(handleCloseBooks); window.addEventListener('beforeunload', handleCloseBooks); + eventDispatcher.on('beforereload', handleCloseBooks); eventDispatcher.on('quit-app', handleCloseBooks); return () => { window.removeEventListener('beforeunload', handleCloseBooks); + eventDispatcher.off('beforereload', handleCloseBooks); eventDispatcher.off('quit-app', handleCloseBooks); }; // eslint-disable-next-line react-hooks/exhaustive-deps @@ -121,11 +124,11 @@ const ReaderContent: React.FC<{ ids?: string; settings: SystemSettings }> = ({ i navigateToLibrary(router); }; - const handleCloseBooks = async () => { + const handleCloseBooks = throttle(async () => { const settings = useSettingsStore.getState().settings; await Promise.all(bookKeys.map((key) => saveConfigAndCloseBook(key))); await saveSettings(envConfig, settings); - }; + }, 500); const handleCloseBooksToLibrary = () => { handleCloseBooks(); diff --git a/apps/readest-app/src/app/reader/components/annotator/Annotator.tsx b/apps/readest-app/src/app/reader/components/annotator/Annotator.tsx index de22a553..8a001f69 100644 --- a/apps/readest-app/src/app/reader/components/annotator/Annotator.tsx +++ b/apps/readest-app/src/app/reader/components/annotator/Annotator.tsx @@ -249,7 +249,7 @@ const Annotator: React.FC<{ bookKey: string }> = ({ bookKey }) => { try { Promise.all(annotations.map((annotation) => view?.addAnnotation(annotation))); } catch (e) { - console.error(e); + console.warn(e); } // eslint-disable-next-line react-hooks/exhaustive-deps }, [progress]); diff --git a/apps/readest-app/src/app/reader/components/settings/ControlPanel.tsx b/apps/readest-app/src/app/reader/components/settings/ControlPanel.tsx index c85185e2..44883525 100644 --- a/apps/readest-app/src/app/reader/components/settings/ControlPanel.tsx +++ b/apps/readest-app/src/app/reader/components/settings/ControlPanel.tsx @@ -4,12 +4,12 @@ import { useReaderStore } from '@/store/readerStore'; import { useDeviceControlStore } from '@/store/deviceStore'; import { useTranslation } from '@/hooks/useTranslation'; import { useBookDataStore } from '@/store/bookDataStore'; +import { useResetViewSettings } from '../../hooks/useResetSettings'; import { getStyles } from '@/utils/style'; +import { saveAndReload } from '@/utils/reload'; import { getMaxInlineSize } from '@/utils/config'; import { saveViewSettings } from '../../utils/viewSettingsHelper'; -import { RELOAD_BEFREE_SAVED_TIMEOUT_MS } from '@/services/constants'; import { SettingsPanelPanelProp } from './SettingsDialog'; -import { useResetViewSettings } from '../../hooks/useResetSettings'; import NumberInput from './NumberInput'; const ControlPanel: React.FC = ({ bookKey, onRegisterReset }) => { @@ -108,7 +108,7 @@ const ControlPanel: React.FC = ({ bookKey, onRegisterRes useEffect(() => { if (viewSettings.allowScript === allowScript) return; saveViewSettings(envConfig, bookKey, 'allowScript', allowScript, true, false); - setTimeout(() => window.location.reload(), RELOAD_BEFREE_SAVED_TIMEOUT_MS); + saveAndReload(); // eslint-disable-next-line react-hooks/exhaustive-deps }, [allowScript]); diff --git a/apps/readest-app/src/app/reader/components/settings/LangPanel.tsx b/apps/readest-app/src/app/reader/components/settings/LangPanel.tsx index 51878a55..8844f00c 100644 --- a/apps/readest-app/src/app/reader/components/settings/LangPanel.tsx +++ b/apps/readest-app/src/app/reader/components/settings/LangPanel.tsx @@ -10,6 +10,7 @@ import { getTranslators } from '@/services/translators'; import { TRANSLATED_LANGS } from '@/services/constants'; import { SettingsPanelPanelProp } from './SettingsDialog'; import { useResetViewSettings } from '../../hooks/useResetSettings'; +import { saveAndReload } from '@/utils/reload'; import { initDayjs } from '@/utils/time'; import Select from '@/components/Select'; @@ -24,6 +25,7 @@ const LangPanel: React.FC = ({ bookKey, onRegisterReset const [translationEnabled, setTranslationEnabled] = useState(viewSettings.translationEnabled!); const [translationProvider, setTranslationProvider] = useState(viewSettings.translationProvider!); const [translateTargetLang, setTranslateTargetLang] = useState(viewSettings.translateTargetLang!); + const [showTranslateSource, setShowTranslateSource] = useState(viewSettings.showTranslateSource!); const resetToDefaults = useResetViewSettings(); @@ -127,9 +129,19 @@ const LangPanel: React.FC = ({ bookKey, onRegisterReset saveViewSettings(envConfig, bookKey, 'translationEnabled', translationEnabled, true, false); viewSettings.translationEnabled = translationEnabled; setViewSettings(bookKey, { ...viewSettings }); + if (!showTranslateSource && !translationEnabled) { + saveAndReload(); + } // eslint-disable-next-line react-hooks/exhaustive-deps }, [translationEnabled]); + useEffect(() => { + if (showTranslateSource === viewSettings.showTranslateSource) return; + saveViewSettings(envConfig, bookKey, 'showTranslateSource', showTranslateSource, false, false); + saveAndReload(); + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [showTranslateSource]); + return (
@@ -162,6 +174,17 @@ const LangPanel: React.FC = ({ bookKey, onRegisterReset />
+
+ {_('Show Source Text')} + setShowTranslateSource(!showTranslateSource)} + /> +
+
{_('Translation Service')}