From c009edca6122994bd991a0780cde9b3fec393d8d Mon Sep 17 00:00:00 2001 From: Huang Xin Date: Fri, 17 Jan 2025 19:10:27 +0100 Subject: [PATCH] Fix translation API (#189) * Fix translation API * Add missing translations --- apps/readest-app/.env.local.example | 8 ++++ apps/readest-app/next.config.mjs | 8 ---- .../public/locales/de/translation.json | 5 ++- .../public/locales/el/translation.json | 5 ++- .../public/locales/es/translation.json | 5 ++- .../public/locales/fr/translation.json | 5 ++- .../public/locales/id/translation.json | 5 ++- .../public/locales/it/translation.json | 5 ++- .../public/locales/ja/translation.json | 5 ++- .../public/locales/ko/translation.json | 5 ++- .../public/locales/pt/translation.json | 5 ++- .../public/locales/ru/translation.json | 5 ++- .../public/locales/tr/translation.json | 5 ++- .../public/locales/vi/translation.json | 5 ++- .../public/locales/zh-CN/translation.json | 5 ++- .../public/locales/zh-TW/translation.json | 5 ++- .../components/annotator/DeepLPopup.tsx | 26 +++++------ .../reader/components/settings/DialogMenu.tsx | 7 ++- .../src/app/reader/hooks/useProgressSync.ts | 4 +- apps/readest-app/src/libs/sync.ts | 10 +---- .../src/pages/api/deepl/translate.ts | 44 +++++++++++++++++++ apps/readest-app/src/services/environment.ts | 9 ++++ 22 files changed, 138 insertions(+), 48 deletions(-) create mode 100644 apps/readest-app/.env.local.example create mode 100644 apps/readest-app/src/pages/api/deepl/translate.ts diff --git a/apps/readest-app/.env.local.example b/apps/readest-app/.env.local.example new file mode 100644 index 00000000..d5a1f5c7 --- /dev/null +++ b/apps/readest-app/.env.local.example @@ -0,0 +1,8 @@ +NEXT_PUBLIC_POSTHOG_KEY=YOUR_POSTHOG_KEY +NEXT_PUBLIC_POSTHOG_HOST=YOUR_POSTHOG_HOST + +NEXT_PUBLIC_SUPABASE_URL=YOUR_SUPABASE_URL +NEXT_PUBLIC_SUPABASE_ANON_KEY=YOUR_SUPABASE_ANON_KEY + +DEEPL_PRO_API_KEY=YOUR_DEEPL_PRO_API_KEY +DEEPL_FREE_API_KEY=YOUR_DEEPL_FREE_API_KEY diff --git a/apps/readest-app/next.config.mjs b/apps/readest-app/next.config.mjs index 0ba53b8c..6b63ee9b 100644 --- a/apps/readest-app/next.config.mjs +++ b/apps/readest-app/next.config.mjs @@ -19,14 +19,6 @@ const nextConfig = { // Configure assetPrefix or else the server won't properly resolve your assets. assetPrefix: isProd ? '' : `http://${internalHost}:3000`, reactStrictMode: true, - async rewrites() { - return [ - { - source: '/api/deepl/:path*', - destination: 'https://api-free.deepl.com/v2/:path*', - }, - ]; - }, }; export default nextConfig; diff --git a/apps/readest-app/public/locales/de/translation.json b/apps/readest-app/public/locales/de/translation.json index fcadb328..b1e14245 100644 --- a/apps/readest-app/public/locales/de/translation.json +++ b/apps/readest-app/public/locales/de/translation.json @@ -130,5 +130,8 @@ "TTS not supported for PDF": "TTS wird für PDF nicht unterstützt", "Override Book Font": "Buch-Schriftart überschreiben", "Vertical Margins (px)": "Vertikale Ränder (px)", - "Horizontal Margins (%)": "Horizontale Ränder (%)" + "Horizontal Margins (%)": "Horizontale Ränder (%)", + "Apply to all books": "Auf alle Bücher anwenden", + "Apply to this book": "Auf dieses Buch anwenden", + "Unable to fetch the translation. Try again later.": "Übersetzung konnte nicht abgerufen werden. Versuchen Sie es später erneut." } diff --git a/apps/readest-app/public/locales/el/translation.json b/apps/readest-app/public/locales/el/translation.json index 9730dc4b..d521f218 100644 --- a/apps/readest-app/public/locales/el/translation.json +++ b/apps/readest-app/public/locales/el/translation.json @@ -130,5 +130,8 @@ "TTS not supported for PDF": "Η μετατροπή κειμένου σε ομιλία δεν υποστηρίζεται για αρχεία PDF", "Override Book Font": "Παράκαμψη γραμματοσειράς βιβλίου", "Vertical Margins (px)": "Κάθετα περιθώρια (px)", - "Horizontal Margins (%)": "Οριζόντια περιθώρια (%)" + "Horizontal Margins (%)": "Οριζόντια περιθώρια (%)", + "Apply to all books": "Εφαρμογή σε όλα τα βιβλία", + "Apply to this book": "Εφαρμογή σε αυτό το βιβλίο", + "Unable to fetch the translation. Try again later.": "Αδυναμία λήψης της μετάφρασης. Δοκιμάστε ξανά αργότερα." } diff --git a/apps/readest-app/public/locales/es/translation.json b/apps/readest-app/public/locales/es/translation.json index 7b1de4b6..c240ad6a 100644 --- a/apps/readest-app/public/locales/es/translation.json +++ b/apps/readest-app/public/locales/es/translation.json @@ -130,5 +130,8 @@ "TTS not supported for PDF": "TTS no es compatible con PDF", "Override Book Font": "Sobrescribir fuente del libro", "Vertical Margins (px)": "Márgenes verticales (px)", - "Horizontal Margins (%)": "Márgenes horizontales (%)" + "Horizontal Margins (%)": "Márgenes horizontales (%)", + "Apply to all books": "Aplicar a todos los libros", + "Apply to this book": "Aplicar a este libro", + "Unable to fetch the translation. Try again later.": "No se puede obtener la traducción. Inténtalo de nuevo más tarde." } diff --git a/apps/readest-app/public/locales/fr/translation.json b/apps/readest-app/public/locales/fr/translation.json index edb82cef..cf1f67b4 100644 --- a/apps/readest-app/public/locales/fr/translation.json +++ b/apps/readest-app/public/locales/fr/translation.json @@ -130,5 +130,8 @@ "TTS not supported for PDF": "La synthèse vocale n'est pas prise en charge pour les fichiers PDF", "Override Book Font": "Remplacer la police du livre", "Vertical Margins (px)": "Marges verticales (px)", - "Horizontal Margins (%)": "Marges horizontales (%)" + "Horizontal Margins (%)": "Marges horizontales (%)", + "Apply to all books": "Appliquer à tous les livres", + "Apply to this book": "Appliquer à ce livre", + "Unable to fetch the translation. Try again later.": "Impossible de récupérer la traduction. Réessayez plus tard." } diff --git a/apps/readest-app/public/locales/id/translation.json b/apps/readest-app/public/locales/id/translation.json index 97552426..da1208e4 100644 --- a/apps/readest-app/public/locales/id/translation.json +++ b/apps/readest-app/public/locales/id/translation.json @@ -130,5 +130,8 @@ "TTS not supported for PDF": "TTS tidak didukung untuk PDF", "Override Book Font": "Ganti Font Buku", "Vertical Margins (px)": "Margin Vertikal (px)", - "Horizontal Margins (%)": "Margin Horizontal (%)" + "Horizontal Margins (%)": "Margin Horizontal (%)", + "Apply to all books": "Terapkan ke semua buku", + "Apply to this book": "Terapkan ke buku ini", + "Unable to fetch the translation. Try again later.": "Tidak dapat mengambil terjemahan. Coba lagi nanti." } diff --git a/apps/readest-app/public/locales/it/translation.json b/apps/readest-app/public/locales/it/translation.json index dc7baba1..95ce0bc2 100644 --- a/apps/readest-app/public/locales/it/translation.json +++ b/apps/readest-app/public/locales/it/translation.json @@ -130,5 +130,8 @@ "TTS not supported for PDF": "TTS non supportato per PDF", "Override Book Font": "Sovrascrivi font libro", "Vertical Margins (px)": "Margini verticali (px)", - "Horizontal Margins (%)": "Margini orizzontali (%)" + "Horizontal Margins (%)": "Margini orizzontali (%)", + "Apply to all books": "Applica a tutti i libri", + "Apply to this book": "Applica a questo libro", + "Unable to fetch the translation. Try again later.": "Impossibile recuperare la traduzione. Riprova più tardi." } diff --git a/apps/readest-app/public/locales/ja/translation.json b/apps/readest-app/public/locales/ja/translation.json index 288ee7d2..f5976402 100644 --- a/apps/readest-app/public/locales/ja/translation.json +++ b/apps/readest-app/public/locales/ja/translation.json @@ -130,5 +130,8 @@ "TTS not supported for PDF": "PDFではTTSがサポートされていません", "Override Book Font": "書籍フォントを上書き", "Vertical Margins (px)": "縦マージン(px)", - "Horizontal Margins (%)": "横マージン(%)" + "Horizontal Margins (%)": "横マージン(%)", + "Apply to all books": "すべての書籍に適用", + "Apply to this book": "この書籍に適用", + "Unable to fetch the translation. Try again later.": "翻訳を取得できません。後で再試行してください。" } diff --git a/apps/readest-app/public/locales/ko/translation.json b/apps/readest-app/public/locales/ko/translation.json index 3524ddcb..cc90a38b 100644 --- a/apps/readest-app/public/locales/ko/translation.json +++ b/apps/readest-app/public/locales/ko/translation.json @@ -130,5 +130,8 @@ "TTS not supported for PDF": "PDF에서 TTS가 지원되지 않음", "Override Book Font": "책 글꼴 덮어쓰기", "Vertical Margins (px)": "수직 여백 (px)", - "Horizontal Margins (%)": "수평 여백 (%)" + "Horizontal Margins (%)": "수평 여백 (%)", + "Apply to all books": "모든 책에 적용", + "Apply to this book": "이 책에 적용", + "Unable to fetch the translation. Try again later.": "번역을 가져올 수 없습니다. 나중에 다시 시도하세요." } diff --git a/apps/readest-app/public/locales/pt/translation.json b/apps/readest-app/public/locales/pt/translation.json index 1216151d..a7285d86 100644 --- a/apps/readest-app/public/locales/pt/translation.json +++ b/apps/readest-app/public/locales/pt/translation.json @@ -130,5 +130,8 @@ "TTS not supported for PDF": "TTS não suportado para PDF", "Override Book Font": "Sobrescrever Fonte do Livro", "Vertical Margins (px)": "Margens Verticais (px)", - "Horizontal Margins (%)": "Margens Horizontais (%)" + "Horizontal Margins (%)": "Margens Horizontais (%)", + "Apply to all books": "Aplicar a todos os livros", + "Apply to this book": "Aplicar a este livro", + "Unable to fetch the translation. Try again later.": "Não foi possível buscar a tradução. Tente novamente mais tarde." } diff --git a/apps/readest-app/public/locales/ru/translation.json b/apps/readest-app/public/locales/ru/translation.json index e710c0fc..c8f0438d 100644 --- a/apps/readest-app/public/locales/ru/translation.json +++ b/apps/readest-app/public/locales/ru/translation.json @@ -130,5 +130,8 @@ "TTS not supported for PDF": "TTS не поддерживается для PDF", "Override Book Font": "Переопределить шрифт книги", "Vertical Margins (px)": "Вертикальные поля (px)", - "Horizontal Margins (%)": "Горизонтальные поля (%)" + "Horizontal Margins (%)": "Горизонтальные поля (%)", + "Apply to all books": "Применить ко всем книгам", + "Apply to this book": "Применить к этой книге", + "Unable to fetch the translation. Try again later.": "Не удалось получить перевод. Попробуйте позже." } diff --git a/apps/readest-app/public/locales/tr/translation.json b/apps/readest-app/public/locales/tr/translation.json index 83c97869..bdef43bc 100644 --- a/apps/readest-app/public/locales/tr/translation.json +++ b/apps/readest-app/public/locales/tr/translation.json @@ -130,5 +130,8 @@ "TTS not supported for PDF": "PDF için TTS desteklenmiyor", "Override Book Font": "Kitap Yazı Tipini Geçersiz Kıl", "Vertical Margins (px)": "Dikey Kenar Boşlukları (px)", - "Horizontal Margins (%)": "Yatay Kenar Boşlukları (%)" + "Horizontal Margins (%)": "Yatay Kenar Boşlukları (%)", + "Apply to all books": "Tüm kitaplara uygula", + "Apply to this book": "Bu kitaba uygula", + "Unable to fetch the translation. Try again later.": "Çeviri alınamıyor. Daha sonra tekrar deneyin." } diff --git a/apps/readest-app/public/locales/vi/translation.json b/apps/readest-app/public/locales/vi/translation.json index 404de3ed..6e21fe26 100644 --- a/apps/readest-app/public/locales/vi/translation.json +++ b/apps/readest-app/public/locales/vi/translation.json @@ -130,5 +130,8 @@ "TTS not supported for PDF": "TTS không được hỗ trợ cho PDF", "Override Book Font": "Ghi đè phông chữ sách", "Vertical Margins (px)": "Lề dọc (px)", - "Horizontal Margins (%)": "Lề ngang (%)" + "Horizontal Margins (%)": "Lề ngang (%)", + "Apply to all books": "Áp dụng cho tất cả sách", + "Apply to this book": "Áp dụng cho sách này", + "Unable to fetch the translation. Try again later.": "Không thể tải dịch vụ. Vui lòng thử lại sau." } diff --git a/apps/readest-app/public/locales/zh-CN/translation.json b/apps/readest-app/public/locales/zh-CN/translation.json index 3a45c110..20377b86 100644 --- a/apps/readest-app/public/locales/zh-CN/translation.json +++ b/apps/readest-app/public/locales/zh-CN/translation.json @@ -130,5 +130,8 @@ "TTS not supported for PDF": "PDF 文档暂不支持 TTS", "Override Book Font": "覆盖书籍字体", "Vertical Margins (px)": "垂直边距(px)", - "Horizontal Margins (%)": "水平边距(%)" + "Horizontal Margins (%)": "水平边距(%)", + "Apply to all books": "应用于所有书籍", + "Apply to this book": "应用于此书籍", + "Unable to fetch the translation. Try again later.": "无法获取翻译,请稍后重试。" } diff --git a/apps/readest-app/public/locales/zh-TW/translation.json b/apps/readest-app/public/locales/zh-TW/translation.json index 99cebf51..0d943f78 100644 --- a/apps/readest-app/public/locales/zh-TW/translation.json +++ b/apps/readest-app/public/locales/zh-TW/translation.json @@ -130,5 +130,8 @@ "TTS not supported for PDF": "PDF 文檔暫不支持 TTS", "Override Book Font": "覆蓋書籍字體", "Vertical Margins (px)": "垂直邊距(px)", - "Horizontal Margins (%)": "水平邊距(%)" + "Horizontal Margins (%)": "水平邊距(%)", + "Apply to all books": "應用於所有書籍", + "Apply to this book": "應用於此書籍", + "Unable to fetch the translation. Try again later.": "無法獲取翻譯,請稍後再試。" } diff --git a/apps/readest-app/src/app/reader/components/annotator/DeepLPopup.tsx b/apps/readest-app/src/app/reader/components/annotator/DeepLPopup.tsx index 2281fb56..496de5eb 100644 --- a/apps/readest-app/src/app/reader/components/annotator/DeepLPopup.tsx +++ b/apps/readest-app/src/app/reader/components/annotator/DeepLPopup.tsx @@ -1,9 +1,10 @@ import React, { useEffect, useState } from 'react'; import Popup from '@/components/Popup'; import { Position } from '@/utils/sel'; +import { getAPIBaseUrl } from '@/services/environment'; import { useSettingsStore } from '@/store/settingsStore'; import { useTranslation } from '@/hooks/useTranslation'; -import { isWebAppPlatform } from '@/services/environment'; +import { useAuth } from '@/context/AuthContext'; const LANGUAGES = { AUTO: 'Auto Detect', @@ -23,6 +24,8 @@ const LANGUAGES = { 'ZH-HANT': 'Chinese (Traditional)', }; +const DEEPL_API_ENDPOINT = getAPIBaseUrl() + '/deepl/translate'; + interface DeepLPopupProps { text: string; position: Position; @@ -39,6 +42,7 @@ const DeepLPopup: React.FC = ({ popupHeight, }) => { const _ = useTranslation(); + const { token } = useAuth(); const { settings, setSettings } = useSettingsStore(); const [sourceLang, setSourceLang] = useState('AUTO'); const [targetLang, setTargetLang] = useState(settings.globalReadSettings.translateTargetLang); @@ -63,23 +67,12 @@ const DeepLPopup: React.FC = ({ setError(null); setTranslation(null); - if (!process.env['NEXT_PUBLIC_DEEPL_API_KEY']) { - console.error('DeepL API key not found. Set NEXT_PUBLIC_DEEPL_API_KEY in .env.local'); - } - - const { fetch, url } = isWebAppPlatform() - ? { fetch: window.fetch, url: '/api/deepl/translate' } - : { - fetch: (await import('@tauri-apps/plugin-http')).fetch, - url: 'https://api-free.deepl.com/v2/translate', - }; - try { - const response = await fetch(url, { + const response = await fetch(DEEPL_API_ENDPOINT, { method: 'POST', headers: { 'Content-Type': 'application/json', - Authorization: `DeepL-Auth-Key ${process.env['NEXT_PUBLIC_DEEPL_API_KEY']}`, + Authorization: `Bearer ${token ?? ''}`, }, body: JSON.stringify({ text: [text], @@ -106,14 +99,15 @@ const DeepLPopup: React.FC = ({ setTranslation(translatedText); } catch (err) { console.error(err); - setError('Unable to fetch the translation. Try again later.'); + setError(_('Unable to fetch the translation. Try again later.')); } finally { setLoading(false); } }; fetchTranslation(); - }, [text, sourceLang, targetLang]); + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [text, token, sourceLang, targetLang]); return (
diff --git a/apps/readest-app/src/app/reader/components/settings/DialogMenu.tsx b/apps/readest-app/src/app/reader/components/settings/DialogMenu.tsx index da338e6a..54c3dab7 100644 --- a/apps/readest-app/src/app/reader/components/settings/DialogMenu.tsx +++ b/apps/readest-app/src/app/reader/components/settings/DialogMenu.tsx @@ -29,7 +29,12 @@ const DialogMenu: React.FC = ({ toggleDropdown }) => { {isFontLayoutSettingsGlobal && } -
+
{_('Global Settings')}
diff --git a/apps/readest-app/src/app/reader/hooks/useProgressSync.ts b/apps/readest-app/src/app/reader/hooks/useProgressSync.ts index de966239..4fdadd4c 100644 --- a/apps/readest-app/src/app/reader/hooks/useProgressSync.ts +++ b/apps/readest-app/src/app/reader/hooks/useProgressSync.ts @@ -41,7 +41,9 @@ export const useProgressSync = (bookKey: string) => { if (!configSynced.current) { pullConfig(bookKey); } else { - pushConfig(bookKey, config); + if (config && config.progress && config.progress[0] > 0) { + pushConfig(bookKey, config); + } } }; diff --git a/apps/readest-app/src/libs/sync.ts b/apps/readest-app/src/libs/sync.ts index 330f3830..b348c7d8 100644 --- a/apps/readest-app/src/libs/sync.ts +++ b/apps/readest-app/src/libs/sync.ts @@ -1,14 +1,8 @@ import { supabase } from '@/utils/supabase'; import { Book, BookConfig, BookNote, BookDataRecord } from '@/types/book'; -import { READEST_WEB_BASE_URL } from '@/services/constants'; -import { isWebAppPlatform } from '@/services/environment'; +import { getAPIBaseUrl, isWebAppPlatform } from '@/services/environment'; -// Develop Sync API only in development mode and web platform -// with command `pnpm dev-web` -const SYNC_API_ENDPOINT = - process.env['NODE_ENV'] === 'development' && isWebAppPlatform() - ? '/api/sync' - : `${READEST_WEB_BASE_URL}/api/sync`; +const SYNC_API_ENDPOINT = getAPIBaseUrl() + '/sync'; export type SyncType = 'books' | 'configs' | 'notes'; export type SyncOp = 'push' | 'pull' | 'both'; diff --git a/apps/readest-app/src/pages/api/deepl/translate.ts b/apps/readest-app/src/pages/api/deepl/translate.ts new file mode 100644 index 00000000..d1b1fd91 --- /dev/null +++ b/apps/readest-app/src/pages/api/deepl/translate.ts @@ -0,0 +1,44 @@ +import { NextApiRequest, NextApiResponse } from 'next'; +import { supabase } from '@/utils/supabase'; + +const DEEPL_FREE_API = 'https://api-free.deepl.com/v2/translate'; +const DEEPL_PRO_API = 'https://api.deepl.com/v2/translate'; + +const getUserAndToken = async (authHeader: string | undefined) => { + if (!authHeader) return {}; + + const token = authHeader.replace('Bearer ', ''); + const { + data: { user }, + error, + } = await supabase.auth.getUser(token); + + if (error || !user) return {}; + return { user, token }; +}; + +const handler = async (req: NextApiRequest, res: NextApiResponse) => { + const { user, token } = await getUserAndToken(req.headers['authorization']); + const deeplApiUrl = user && token ? DEEPL_PRO_API : DEEPL_FREE_API; + const deeplAuthKey = + user && token ? process.env['DEEPL_PRO_API_KEY'] : process.env['DEEPL_FREE_API_KEY']; + + try { + console.log('Proxying DeepL request:', deeplApiUrl, deeplAuthKey); + const response = await fetch(deeplApiUrl, { + method: 'POST', + headers: { + Authorization: `DeepL-Auth-Key ${deeplAuthKey}`, + 'Content-Type': 'application/json', + }, + body: req.method === 'POST' ? JSON.stringify(req.body) : undefined, + }); + res.status(response.status); + res.json(await response.json()); + } catch (error) { + console.error('Error proxying DeepL request:', error); + res.status(500).json({ error: 'Internal Server Error' }); + } +}; + +export default handler; diff --git a/apps/readest-app/src/services/environment.ts b/apps/readest-app/src/services/environment.ts index 8d244ed3..93a7e6f5 100644 --- a/apps/readest-app/src/services/environment.ts +++ b/apps/readest-app/src/services/environment.ts @@ -1,4 +1,5 @@ import { AppService } from '@/types/system'; +import { READEST_WEB_BASE_URL } from './constants'; declare global { interface Window { @@ -12,6 +13,14 @@ export const isWebAppPlatform = () => process.env['NEXT_PUBLIC_APP_PLATFORM'] == export const hasUpdater = () => window.__READEST_UPDATER_ACCESS === true; export const hasCli = () => window.__READEST_CLI_ACCESS === true; +// Dev API only in development mode and web platform +// with command `pnpm dev-web` +// for production build or tauri app use the production Web API +export const getAPIBaseUrl = () => + process.env['NODE_ENV'] === 'development' && isWebAppPlatform() + ? '/api' + : `${READEST_WEB_BASE_URL}/api`; + export interface EnvConfigType { getAppService: () => Promise; }