feat(cjk): add an option to replace quotation marks in vertical layout for CJK languages (#2511)

This commit is contained in:
Huang Xin
2025-11-22 21:09:20 +08:00
committed by GitHub
parent 40673f9cb8
commit cd614e3845
38 changed files with 219 additions and 65 deletions
@@ -672,5 +672,8 @@
"Current email": "البريد الإلكتروني الحالي",
"Update Email": "تحديث البريد الإلكتروني",
"All": "الكل",
"Unable to open book": "غير قادر على فتح الكتاب"
"Unable to open book": "غير قادر على فتح الكتاب",
"Punctuation": "علامات الترقيم",
"Replace Quotation Marks": "استبدال علامات الاقتباس",
"Enabled only in vertical layout.": "مفعل فقط في التخطيط الرأسي."
}
@@ -656,5 +656,8 @@
"Current email": "বর্তমান ইমেল",
"Update Email": "ইমেল আপডেট করুন",
"All": "সব",
"Unable to open book": "বই খুলতে অক্ষম"
"Unable to open book": "বই খুলতে অক্ষম",
"Punctuation": "বিরামচিহ্ন",
"Replace Quotation Marks": "উদ্ধৃতি চিহ্ন প্রতিস্থাপন করুন",
"Enabled only in vertical layout.": "শুধুমাত্র উল্লম্ব বিন্যাসে সক্রিয়।"
}
@@ -652,5 +652,8 @@
"Current email": "མིག་སྔའི་ཡིག་འཕྲིན།",
"Update Email": "ཡིག་འཕྲིན་གསར་བསྒྱུར།",
"All": "ཆེན་མོ།",
"Unable to open book": "དེབ་ཀྱི་སྤྱོད་བྱས་མ་ཐུབ།"
"Unable to open book": "དེབ་ཀྱི་སྤྱོད་བྱས་མ་ཐུབ།",
"Punctuation": "ཚིག་གྲུབ།",
"Replace Quotation Marks": "འདྲེན་འབྲེལ་མཐུད་ཐིག་བརྗེ་བ།",
"Enabled only in vertical layout.": "གཡས་སྒྲིག་ནང་མིང་བརྗེ་བ།"
}
@@ -656,5 +656,8 @@
"Current email": "Aktuelle E-Mail",
"Update Email": "E-Mail aktualisieren",
"All": "Alle",
"Unable to open book": "Buch kann nicht geöffnet werden"
"Unable to open book": "Buch kann nicht geöffnet werden",
"Punctuation": "Interpunktion",
"Replace Quotation Marks": "Anführungszeichen ersetzen",
"Enabled only in vertical layout.": "Nur im vertikalen Layout aktiviert."
}
@@ -656,5 +656,8 @@
"Current email": "Τρέχον email",
"Update Email": "Ενημέρωση email",
"All": "Όλα",
"Unable to open book": "Αδύνατη η άνοιγμα του βιβλίου"
"Unable to open book": "Αδύνατη η άνοιγμα του βιβλίου",
"Punctuation": "Στίξη",
"Replace Quotation Marks": "Αντικατάσταση εισαγωγικών",
"Enabled only in vertical layout.": "Ενεργοποιημένο μόνο σε κάθετη διάταξη."
}
@@ -660,5 +660,8 @@
"Current email": "Correo electrónico actual",
"Update Email": "Actualizar correo electrónico",
"All": "Todos",
"Unable to open book": "No se puede abrir el libro"
"Unable to open book": "No se puede abrir el libro",
"Punctuation": "Signos de puntuación",
"Replace Quotation Marks": "Reemplazar comillas",
"Enabled only in vertical layout.": "Habilitado solo en diseño vertical."
}
@@ -656,5 +656,8 @@
"Current email": "ایمیل فعلی",
"Update Email": "به‌روزرسانی ایمیل",
"All": "همه",
"Unable to open book": "قادر به باز کردن کتاب نیست"
"Unable to open book": "قادر به باز کردن کتاب نیست",
"Punctuation": "علامت نگارشی",
"Replace Quotation Marks": "جایگزینی علامت نقل قول",
"Enabled only in vertical layout.": "فقط در طرح عمودی فعال است."
}
@@ -660,5 +660,8 @@
"Current email": "E-mail actuel",
"Update Email": "Mettre à jour le-mail",
"All": "Tous",
"Unable to open book": "Impossible d'ouvrir le livre"
"Unable to open book": "Impossible d'ouvrir le livre",
"Punctuation": "Ponctuation",
"Replace Quotation Marks": "Remplacer les guillemets",
"Enabled only in vertical layout.": "Activé uniquement en disposition verticale."
}
@@ -656,5 +656,8 @@
"Current email": "वर्तमान ईमेल",
"Update Email": "ईमेल अपडेट करें",
"All": "सभी",
"Unable to open book": "पुस्तक खोलने में असमर्थ"
"Unable to open book": "पुस्तक खोलने में असमर्थ",
"Punctuation": "विराम चिह्न",
"Replace Quotation Marks": "उद्धरण चिह्न बदलें",
"Enabled only in vertical layout.": "केवल ऊर्ध्वाधर लेआउट में सक्षम।"
}
@@ -652,5 +652,8 @@
"Current email": "Email saat ini",
"Update Email": "Perbarui email",
"All": "Semua",
"Unable to open book": "Tidak dapat membuka buku"
"Unable to open book": "Tidak dapat membuka buku",
"Punctuation": "Tanda Baca",
"Replace Quotation Marks": "Ganti Tanda Kutip",
"Enabled only in vertical layout.": "Hanya diaktifkan dalam tata letak vertikal."
}
@@ -660,5 +660,8 @@
"Current email": "Email attuale",
"Update Email": "Aggiorna email",
"All": "Tutti",
"Unable to open book": "Impossibile aprire il libro"
"Unable to open book": "Impossibile aprire il libro",
"Punctuation": "Segni di punteggiatura",
"Replace Quotation Marks": "Sostituisci le virgolette",
"Enabled only in vertical layout.": "Abilitato solo in layout verticale."
}
@@ -652,5 +652,8 @@
"Current email": "現在のメールアドレス",
"Update Email": "メールを更新",
"All": "すべて",
"Unable to open book": "本を開くことができません"
"Unable to open book": "本を開くことができません",
"Punctuation": "句読点",
"Replace Quotation Marks": "引用符を置き換える",
"Enabled only in vertical layout.": "縦書きレイアウトでのみ有効です。"
}
@@ -652,5 +652,8 @@
"Current email": "현재 이메일",
"Update Email": "이메일 업데이트",
"All": "전체",
"Unable to open book": "책을 열 수 없습니다"
"Unable to open book": "책을 열 수 없습니다",
"Punctuation": "구두점",
"Replace Quotation Marks": "인용 부호 바꾸기",
"Enabled only in vertical layout.": "세로 레이아웃에서만 활성화됩니다."
}
@@ -656,5 +656,8 @@
"Current email": "Huidig e-mailadres",
"Update Email": "E-mail bijwerken",
"All": "Alles",
"Unable to open book": "Kan boek niet openen"
"Unable to open book": "Kan boek niet openen",
"Punctuation": "Interpunctie",
"Replace Quotation Marks": "Aanhalingstekens vervangen",
"Enabled only in vertical layout.": "Alleen ingeschakeld in verticale lay-out."
}
@@ -664,5 +664,8 @@
"Current email": "Obecny e-mail",
"Update Email": "Zaktualizuj e-mail",
"All": "Wszystkie",
"Unable to open book": "Nie można otworzyć książki"
"Unable to open book": "Nie można otworzyć książki",
"Punctuation": "Interpunkcja",
"Replace Quotation Marks": "Zamień cudzysłowy",
"Enabled only in vertical layout.": "Włączone tylko w układzie pionowym."
}
@@ -660,5 +660,8 @@
"Current email": "E-mail atual",
"Update Email": "Atualizar e-mail",
"All": "Todos",
"Unable to open book": "Não foi possível abrir o livro"
"Unable to open book": "Não foi possível abrir o livro",
"Punctuation": "Pontuação",
"Replace Quotation Marks": "Substituir Aspas",
"Enabled only in vertical layout.": "Habilitado apenas no layout vertical."
}
@@ -664,5 +664,8 @@
"Current email": "Текущий адрес электронной почты",
"Update Email": "Обновить электронную почту",
"All": "Все",
"Unable to open book": "Не удалось открыть книгу"
"Unable to open book": "Не удалось открыть книгу",
"Punctuation": "Пунктуация",
"Replace Quotation Marks": "Заменить кавычки",
"Enabled only in vertical layout.": "Включено только в вертикальной раскладке."
}
@@ -656,5 +656,8 @@
"Current email": "වත්මන් විද්‍යුත් තැපැල්",
"Update Email": "විද්‍යුත් තැපැල් යාවත්කාලීන කරන්න",
"All": "සියල්ල",
"Unable to open book": "පොත විවෘත කිරීමට නොහැක"
"Unable to open book": "පොත විවෘත කිරීමට නොහැක",
"Punctuation": "ලකුණු",
"Replace Quotation Marks": "උද්ධෘත ලකුණු ප්‍රතිස්ථාපනය කරන්න",
"Enabled only in vertical layout.": "සිරස් ආකෘතියේ පමණක් සක්‍රීයයි."
}
@@ -656,5 +656,8 @@
"Current email": "Nuvarande e-post",
"Update Email": "Uppdatera e-post",
"All": "Alla",
"Unable to open book": "Kunde inte öppna bok"
"Unable to open book": "Kunde inte öppna bok",
"Punctuation": "Interpunktion",
"Replace Quotation Marks": "Ersätt citattecken",
"Enabled only in vertical layout.": "Aktiverad endast i vertikal layout."
}
@@ -656,5 +656,8 @@
"Current email": "தற்போதைய மின்னஞ்சல்",
"Update Email": "மின்னஞ்சல் புதுப்பிக்கவும்",
"All": "அனைத்து",
"Unable to open book": "புத்தகத்தைத் திறக்க முடியவில்லை"
"Unable to open book": "புத்தகத்தைத் திறக்க முடியவில்லை",
"Punctuation": "விருத்தி",
"Replace Quotation Marks": "உதயம்சுட்டிகளை மாற்றவும்",
"Enabled only in vertical layout.": "செங்குத்து அமைப்பில் மட்டுமே இயலுமைப்படுத்தப்பட்டது."
}
@@ -652,5 +652,8 @@
"Current email": "อีเมลปัจจุบัน",
"Update Email": "อัปเดตอีเมล",
"All": "ทั้งหมด",
"Unable to open book": "ไม่สามารถเปิดหนังสือได้"
"Unable to open book": "ไม่สามารถเปิดหนังสือได้",
"Punctuation": "เครื่องหมายวรรคตอน",
"Replace Quotation Marks": "แทนที่เครื่องหมายคำพูด",
"Enabled only in vertical layout.": "เปิดใช้งานเฉพาะในเค้าโครงแนวตั้งเท่านั้น"
}
@@ -656,5 +656,8 @@
"Current email": "Mevcut e-posta",
"Update Email": "E-postayı güncelle",
"All": "Tümü",
"Unable to open book": "Kitap açılamıyor"
"Unable to open book": "Kitap açılamıyor",
"Punctuation": "Noktalama İşaretleri",
"Replace Quotation Marks": "Tırnak İşaretlerini Değiştir",
"Enabled only in vertical layout.": "Yalnızca dikey düzenle etkinleştirildi."
}
@@ -664,5 +664,8 @@
"Current email": "Поточна електронна пошта",
"Update Email": "Оновити електронну пошту",
"All": "Всі",
"Unable to open book": "Не вдалося відкрити книгу"
"Unable to open book": "Не вдалося відкрити книгу",
"Punctuation": "Пунктуація",
"Replace Quotation Marks": "Замінити лапки",
"Enabled only in vertical layout.": "Активовано лише у вертикальному макеті."
}
@@ -652,5 +652,8 @@
"Current email": "Email hiện tại",
"Update Email": "Cập nhật email",
"All": "Tất cả",
"Unable to open book": "Không thể mở sách"
"Unable to open book": "Không thể mở sách",
"Punctuation": "Dấu câu",
"Replace Quotation Marks": "Thay thế dấu ngoặc kép",
"Enabled only in vertical layout.": "Chỉ bật trong bố cục dọc."
}
@@ -652,5 +652,8 @@
"Current email": "当前邮箱",
"Update Email": "更新邮箱",
"All": "全部",
"Unable to open book": "无法打开书籍"
"Unable to open book": "无法打开书籍",
"Punctuation": "标点符号",
"Replace Quotation Marks": "替换引号",
"Enabled only in vertical layout.": "仅在竖排布局中启用"
}
@@ -652,5 +652,8 @@
"Current email": "當前電子郵件",
"Update Email": "更新電子郵件",
"All": "全部",
"Unable to open book": "無法打開書籍"
"Unable to open book": "無法打開書籍",
"Punctuation": "標點符號",
"Replace Quotation Marks": "替換引號",
"Enabled only in vertical layout.": "僅在直排佈局中啟用"
}
@@ -54,6 +54,7 @@ export async function POST(request: NextRequest) {
ui_mode: embedded ? 'embedded' : 'hosted',
customer: customerId,
mode: planType === 'subscription' ? 'subscription' : 'payment',
allow_promotion_codes: true,
line_items: [
{
price: priceId,
@@ -46,7 +46,6 @@ import {
} from '../utils/iframeEventHandlers';
import { getMaxInlineSize } from '@/utils/config';
import { getDirFromUILanguage } from '@/utils/rtl';
import { isCJKLang } from '@/utils/lang';
import { isTauriAppPlatform } from '@/services/environment';
import { TransformContext } from '@/services/transformers/types';
import { transformContent } from '@/services/transformService';
@@ -56,6 +55,8 @@ import { useBookCoverAutoSave } from '../hooks/useAutoSaveBookCover';
import { manageSyntaxHighlighting } from '@/utils/highlightjs';
import { getViewInsets } from '@/utils/insets';
import { removeTabIndex } from '@/utils/a11y';
import { isCJKLang } from '@/utils/lang';
import { getLocale } from '@/utils/misc';
import Spinner from '@/components/Spinner';
import KOSyncConflictResolver from './KOSyncResolver';
@@ -137,6 +138,7 @@ const FoliateViewer: React.FC<{
width,
height,
primaryLanguage: bookData.book?.primaryLanguage,
userLocale: getLocale(),
content: data,
transformers: [
'style',
@@ -2,10 +2,11 @@ import { useEffect, useRef, useState } from 'react';
import { useEnv } from '@/context/EnvContext';
import { useReaderStore } from '@/store/readerStore';
import { useBookDataStore } from '@/store/bookDataStore';
import { getOSPlatform } from '@/utils/misc';
import { getLocale, getOSPlatform } from '@/utils/misc';
import { eventDispatcher } from '@/utils/event';
import { getTextFromRange, TextSelection } from '@/utils/sel';
import { transformContent } from '@/services/transformService';
import { TransformContext } from '@/services/transformers/types';
export const useTextSelector = (
bookKey: string,
@@ -31,9 +32,10 @@ export const useTextSelector = (
return sel && sel.toString().trim().length > 0 && sel.rangeCount > 0;
};
const transformCtx = {
const transformCtx: TransformContext = {
bookKey,
viewSettings: getViewSettings(bookKey)!,
userLocale: getLocale(),
content: '',
transformers: ['punctuation'],
reversePunctuationTransform: true,
@@ -155,7 +155,7 @@ const PlansComparison: React.FC<PlansComparisonProps> = ({
plan={plan}
comingSoon={false}
isUserPlan={plan.plan === userPlan}
upgradable={index > userPlanIndex}
upgradable={index > 0 && (index > userPlanIndex || userPlan === 'purchase')}
index={index}
currentPlanIndex={currentPlanIndex}
onSubscribe={onSubscribe}
@@ -11,7 +11,6 @@ import { getStyles } from '@/utils/style';
import { getMaxInlineSize } from '@/utils/config';
import { saveSysSettings, saveViewSettings } from '@/helpers/settings';
import { SettingsPanelPanelProp } from './SettingsDialog';
import { RELOAD_BEFORE_SAVED_TIMEOUT_MS } from '@/services/constants';
import NumberInput from './NumberInput';
const ControlPanel: React.FC<SettingsPanelPanelProp> = ({ bookKey, onRegisterReset }) => {
@@ -143,10 +142,9 @@ const ControlPanel: React.FC<SettingsPanelPanelProp> = ({ bookKey, onRegisterRes
useEffect(() => {
if (viewSettings.allowScript === allowScript) return;
saveViewSettings(envConfig, bookKey, 'allowScript', allowScript, true, false);
setTimeout(() => {
saveViewSettings(envConfig, bookKey, 'allowScript', allowScript, true, false).then(() => {
recreateViewer(envConfig, bookKey);
}, RELOAD_BEFORE_SAVED_TIMEOUT_MS);
});
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [allowScript]);
@@ -8,12 +8,9 @@ import { useSettingsStore } from '@/store/settingsStore';
import { saveViewSettings } from '@/helpers/settings';
import { getTranslators } from '@/services/translators';
import { useResetViewSettings } from '@/hooks/useResetSettings';
import {
RELOAD_BEFORE_SAVED_TIMEOUT_MS,
TRANSLATED_LANGS,
TRANSLATOR_LANGS,
} from '@/services/constants';
import { TRANSLATED_LANGS, TRANSLATOR_LANGS } from '@/services/constants';
import { SettingsPanelPanelProp } from './SettingsDialog';
import { isCJKEnv } from '@/utils/misc';
import Select from '@/components/Select';
const LangPanel: React.FC<SettingsPanelPanelProp> = ({ bookKey, onRegisterReset }) => {
@@ -30,6 +27,9 @@ const LangPanel: React.FC<SettingsPanelPanelProp> = ({ bookKey, onRegisterReset
const [translateTargetLang, setTranslateTargetLang] = useState(viewSettings.translateTargetLang);
const [showTranslateSource, setShowTranslateSource] = useState(viewSettings.showTranslateSource);
const [ttsReadAloudText, setTtsReadAloudText] = useState(viewSettings.ttsReadAloudText);
const [replaceQuotationMarks, setReplaceQuotationMarks] = useState(
viewSettings.replaceQuotationMarks,
);
const resetToDefaults = useResetViewSettings();
@@ -41,6 +41,7 @@ const LangPanel: React.FC<SettingsPanelPanelProp> = ({ bookKey, onRegisterReset
translateTargetLang: setTranslateTargetLang,
showTranslateSource: setShowTranslateSource,
ttsReadAloudText: setTtsReadAloudText,
replaceQuotationMarks: setReplaceQuotationMarks,
});
};
@@ -143,17 +144,33 @@ const LangPanel: React.FC<SettingsPanelPanelProp> = ({ bookKey, onRegisterReset
useEffect(() => {
if (translationEnabled === viewSettings.translationEnabled) return;
saveViewSettings(envConfig, bookKey, 'translationEnabled', translationEnabled, true, false);
if (!showTranslateSource && translationEnabled) {
setTimeout(() => recreateViewer(envConfig, bookKey), RELOAD_BEFORE_SAVED_TIMEOUT_MS);
}
saveViewSettings(
envConfig,
bookKey,
'translationEnabled',
translationEnabled,
true,
false,
).then(() => {
if (!showTranslateSource && translationEnabled) {
recreateViewer(envConfig, bookKey);
}
});
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [translationEnabled]);
useEffect(() => {
if (showTranslateSource === viewSettings.showTranslateSource) return;
saveViewSettings(envConfig, bookKey, 'showTranslateSource', showTranslateSource, false, false);
setTimeout(() => recreateViewer(envConfig, bookKey), RELOAD_BEFORE_SAVED_TIMEOUT_MS);
saveViewSettings(
envConfig,
bookKey,
'showTranslateSource',
showTranslateSource,
false,
false,
).then(() => {
recreateViewer(envConfig, bookKey);
});
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [showTranslateSource]);
@@ -163,6 +180,21 @@ const LangPanel: React.FC<SettingsPanelPanelProp> = ({ bookKey, onRegisterReset
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [ttsReadAloudText]);
useEffect(() => {
if (replaceQuotationMarks === viewSettings.replaceQuotationMarks) return;
saveViewSettings(
envConfig,
bookKey,
'replaceQuotationMarks',
replaceQuotationMarks,
true,
false,
).then(() => {
recreateViewer(envConfig, bookKey);
});
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [replaceQuotationMarks]);
return (
<div className={clsx('my-4 w-full space-y-6')}>
<div className='w-full'>
@@ -234,6 +266,28 @@ const LangPanel: React.FC<SettingsPanelPanelProp> = ({ bookKey, onRegisterReset
</div>
</div>
</div>
{isCJKEnv() && (
<div className='w-full'>
<h2 className='mb-2 font-medium'>{_('Punctuation')}</h2>
<div className='card border-base-200 bg-base-100 border shadow'>
<div className='divide-base-200'>
<div className='config-item !h-16'>
<div className='flex flex-col gap-1'>
<span className=''>{_('Replace Quotation Marks')}</span>
<span className='text-xs'>{_('Enabled only in vertical layout.')}</span>
</div>
<input
type='checkbox'
className='toggle'
checked={replaceQuotationMarks}
onChange={() => setReplaceQuotationMarks(!replaceQuotationMarks)}
/>
</div>
</div>
</div>
</div>
)}
</div>
);
};
@@ -16,7 +16,7 @@ import { getMaxInlineSize } from '@/utils/config';
import { lockScreenOrientation } from '@/utils/bridge';
import { saveViewSettings } from '@/helpers/settings';
import { getBookDirFromWritingMode, getBookLangCode } from '@/utils/book';
import { MIGHT_BE_RTL_LANGS, RELOAD_BEFORE_SAVED_TIMEOUT_MS } from '@/services/constants';
import { MIGHT_BE_RTL_LANGS } from '@/services/constants';
import { SettingsPanelPanelProp } from './SettingsDialog';
import Select from '@/components/Select';
import NumberInput from './NumberInput';
@@ -273,18 +273,19 @@ const LayoutPanel: React.FC<SettingsPanelPanelProp> = ({ bookKey, onRegisterRese
} else {
viewSettings.vertical = false;
}
saveViewSettings(envConfig, bookKey, 'writingMode', writingMode, true);
if (view) {
view.renderer.setStyles?.(getStyles(viewSettings));
view.book.dir = getBookDirFromWritingMode(writingMode);
}
if (
prevWritingMode !== writingMode &&
(['horizontal-rl', 'vertical-rl'].includes(writingMode) ||
['horizontal-rl', 'vertical-rl'].includes(prevWritingMode))
) {
setTimeout(() => recreateViewer(envConfig, bookKey), RELOAD_BEFORE_SAVED_TIMEOUT_MS);
}
saveViewSettings(envConfig, bookKey, 'writingMode', writingMode, true).then(() => {
if (view) {
view.renderer.setStyles?.(getStyles(viewSettings));
view.book.dir = getBookDirFromWritingMode(writingMode);
}
if (
prevWritingMode !== writingMode &&
(['horizontal-rl', 'vertical-rl'].includes(writingMode) ||
['horizontal-rl', 'vertical-rl'].includes(prevWritingMode))
) {
recreateViewer(envConfig, bookKey);
}
});
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [writingMode]);
+5 -3
View File
@@ -18,7 +18,6 @@ export const saveViewSettings = async <K extends keyof ViewSettings>(
const { getView, getViewSettings, setViewSettings } = useReaderStore.getState();
const { getConfig, saveConfig } = useBookDataStore.getState();
const viewSettings = getViewSettings(bookKey);
const config = getConfig(bookKey);
if (bookKey && viewSettings && viewSettings[key] !== value) {
viewSettings[key] = value;
if (applyStyles) {
@@ -33,9 +32,12 @@ export const saveViewSettings = async <K extends keyof ViewSettings>(
await saveSettings(envConfig, settings);
}
if (bookKey && config && viewSettings) {
if (bookKey && viewSettings) {
setViewSettings(bookKey, viewSettings);
await saveConfig(envConfig, bookKey, config, settings);
const config = getConfig(bookKey);
if (config) {
await saveConfig(envConfig, bookKey, config, settings);
}
}
};
+1 -2
View File
@@ -148,6 +148,7 @@ export const DEFAULT_BOOK_LAYOUT: BookLayout = {
writingMode: 'auto',
vertical: false,
rtl: false,
replaceQuotationMarks: true,
scrollingOverlap: 0,
allowScript: false,
};
@@ -607,8 +608,6 @@ export const SYNC_NOTES_INTERVAL_SEC = 5;
export const SYNC_BOOKS_INTERVAL_SEC = 5;
export const CHECK_UPDATE_INTERVAL_SEC = 24 * 60 * 60;
export const RELOAD_BEFORE_SAVED_TIMEOUT_MS = 300;
export const MAX_ZOOM_LEVEL = 500;
export const MIN_ZOOM_LEVEL = 50;
export const ZOOM_STEP = 10;
@@ -1,20 +1,35 @@
import type { Transformer } from './types';
const punctuationMap: Record<string, string> = {
const punctuationMapHans: Record<string, string> = {
'“': '﹃',
'”': '﹄',
'': '﹁',
'': '﹂',
};
const punctuationMapHant: Record<string, string> = {
'“': '﹁',
'”': '﹂',
'': '﹃',
'': '﹄',
};
export const punctuationTransformer: Transformer = {
name: 'punctuation',
transform: async (ctx) => {
const shouldTransform = ctx.viewSettings.vertical === true;
const shouldTransform = ctx.viewSettings.vertical && ctx.viewSettings.replaceQuotationMarks;
if (!shouldTransform) return ctx.content;
let result = ctx.content;
const traditionalChineseLocales = ['zh-Hant', 'zh-TW', 'zh_TW'];
let punctuationMap: Record<string, string> = punctuationMapHans;
if (
traditionalChineseLocales.includes(ctx.primaryLanguage || '') ||
traditionalChineseLocales.includes(ctx.userLocale)
) {
punctuationMap = punctuationMapHant;
}
for (const [original, vertical] of Object.entries(punctuationMap)) {
if (ctx.reversePunctuationTransform) {
result = result.replace(new RegExp(vertical, 'g'), original);
@@ -3,6 +3,7 @@ import { ViewSettings } from '@/types/book';
export type TransformContext = {
bookKey: string;
viewSettings: ViewSettings;
userLocale: string;
primaryLanguage?: string;
width?: number;
height?: number;
+1
View File
@@ -112,6 +112,7 @@ export interface BookLayout {
writingMode: WritingMode;
vertical: boolean;
rtl: boolean;
replaceQuotationMarks: boolean;
scrollingOverlap: number;
allowScript: boolean;
}