feat: also convert quotation marks between Chinese variants (#2578)
This commit is contained in:
@@ -27,6 +27,7 @@ import { getPopupPosition, getPosition, Position, TextSelection } from '@/utils/
|
||||
import { eventDispatcher } from '@/utils/event';
|
||||
import { findTocItemBS } from '@/utils/toc';
|
||||
import { throttle } from '@/utils/throttle';
|
||||
import { runSimpleCC } from '@/utils/simplecc';
|
||||
import { HIGHLIGHT_COLOR_HEX } from '@/services/constants';
|
||||
import AnnotationPopup from './AnnotationPopup';
|
||||
import WiktionaryPopup from './WiktionaryPopup';
|
||||
@@ -485,7 +486,13 @@ const Annotator: React.FC<{ bookKey: string }> = ({ bookKey }) => {
|
||||
const handleSearch = () => {
|
||||
if (!selection || !selection.text) return;
|
||||
handleDismissPopupAndSelection();
|
||||
eventDispatcher.dispatch('search', { term: selection.text });
|
||||
|
||||
let term = selection.text;
|
||||
const convertChineseVariant = viewSettings.convertChineseVariant;
|
||||
if (convertChineseVariant && convertChineseVariant !== 'none') {
|
||||
term = runSimpleCC(term, convertChineseVariant, true);
|
||||
}
|
||||
eventDispatcher.dispatch('search', { term });
|
||||
};
|
||||
|
||||
const handleDictionary = () => {
|
||||
|
||||
@@ -1,40 +1,73 @@
|
||||
import type { Transformer } from './types';
|
||||
|
||||
const punctuationMapHans: Record<string, string> = {
|
||||
const verticalQuotationsMapHans: Record<string, string> = {
|
||||
'“': '﹃',
|
||||
'”': '﹄',
|
||||
'‘': '﹁',
|
||||
'’': '﹂',
|
||||
'「': '﹁',
|
||||
'」': '﹂',
|
||||
'『': '﹃',
|
||||
'』': '﹄',
|
||||
};
|
||||
|
||||
const punctuationMapHant: Record<string, string> = {
|
||||
const verticalQuotationsMapHant: Record<string, string> = {
|
||||
'“': '﹁',
|
||||
'”': '﹂',
|
||||
'‘': '﹃',
|
||||
'’': '﹄',
|
||||
'「': '﹁',
|
||||
'」': '﹂',
|
||||
'『': '﹃',
|
||||
'』': '﹄',
|
||||
};
|
||||
|
||||
const quotationsMapHans2Hant = {
|
||||
'“': '「',
|
||||
'”': '」',
|
||||
'‘': '『',
|
||||
'’': '』',
|
||||
'﹃': '﹁',
|
||||
'﹄': '﹂',
|
||||
'﹁': '﹃',
|
||||
'﹂': '﹄',
|
||||
};
|
||||
|
||||
export const punctuationTransformer: Transformer = {
|
||||
name: 'punctuation',
|
||||
|
||||
transform: async (ctx) => {
|
||||
const shouldTransform = ctx.viewSettings.vertical && ctx.viewSettings.replaceQuotationMarks;
|
||||
if (!shouldTransform) return ctx.content;
|
||||
if (!ctx.viewSettings.replaceQuotationMarks) 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;
|
||||
|
||||
const convertChineseVariant = ctx.viewSettings.convertChineseVariant;
|
||||
const reversePunctuationTransform = ctx.reversePunctuationTransform || false;
|
||||
const shouldConvertChineseVariants = convertChineseVariant && convertChineseVariant !== 'none';
|
||||
if (shouldConvertChineseVariants) {
|
||||
for (const [s, t] of Object.entries(quotationsMapHans2Hant)) {
|
||||
const shouldReverse = reversePunctuationTransform !== convertChineseVariant.includes('2s');
|
||||
const [from, to] = shouldReverse ? [t, s] : [s, t];
|
||||
result = result.replace(new RegExp(from, 'g'), to);
|
||||
}
|
||||
}
|
||||
for (const [original, vertical] of Object.entries(punctuationMap)) {
|
||||
if (ctx.reversePunctuationTransform) {
|
||||
result = result.replace(new RegExp(vertical, 'g'), original);
|
||||
} else {
|
||||
result = result.replace(new RegExp(original, 'g'), vertical);
|
||||
|
||||
const shouldTransformVertical = ctx.viewSettings.vertical;
|
||||
if (shouldTransformVertical) {
|
||||
const traditionalChineseLocales = ['zh-Hant', 'zh-TW', 'zh_TW'];
|
||||
let punctuationMap: Record<string, string> = verticalQuotationsMapHans;
|
||||
if (
|
||||
traditionalChineseLocales.includes(ctx.primaryLanguage || '') ||
|
||||
traditionalChineseLocales.includes(ctx.userLocale)
|
||||
) {
|
||||
punctuationMap = verticalQuotationsMapHant;
|
||||
}
|
||||
for (const [original, vertical] of Object.entries(punctuationMap)) {
|
||||
if (ctx.reversePunctuationTransform) {
|
||||
result = result.replace(new RegExp(vertical, 'g'), original);
|
||||
} else {
|
||||
result = result.replace(new RegExp(original, 'g'), vertical);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import type { Transformer } from './types';
|
||||
import { simplecc, initSimpleCC } from '@/utils/simplecc';
|
||||
import { initSimpleCC, runSimpleCC } from '@/utils/simplecc';
|
||||
|
||||
export const simpleccTransformer: Transformer = {
|
||||
name: 'simplecc',
|
||||
@@ -36,7 +36,7 @@ export const simpleccTransformer: Transformer = {
|
||||
|
||||
for (const textNode of textNodes) {
|
||||
if (textNode.textContent) {
|
||||
textNode.textContent = simplecc(textNode.textContent, convertChineseVariant);
|
||||
textNode.textContent = runSimpleCC(textNode.textContent, convertChineseVariant);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import init, { simplecc } from '@simplecc/simplecc_wasm';
|
||||
import { ConvertChineseVariant } from '@/types/book';
|
||||
|
||||
let initialized = false;
|
||||
|
||||
@@ -9,4 +10,20 @@ const initSimpleCC = async () => {
|
||||
initialized = true;
|
||||
};
|
||||
|
||||
export { simplecc, initSimpleCC };
|
||||
const convertReverseMap: Record<ConvertChineseVariant, ConvertChineseVariant> = {
|
||||
none: 'none',
|
||||
s2t: 't2s',
|
||||
t2s: 's2t',
|
||||
s2tw: 'tw2s',
|
||||
s2hk: 'hk2s',
|
||||
s2twp: 'tw2sp',
|
||||
tw2s: 's2tw',
|
||||
hk2s: 's2hk',
|
||||
tw2sp: 's2twp',
|
||||
};
|
||||
|
||||
const runSimpleCC = (text: string, variant: ConvertChineseVariant, reverse = false): string => {
|
||||
return reverse ? simplecc(text, convertReverseMap[variant]) : simplecc(text, variant);
|
||||
};
|
||||
|
||||
export { initSimpleCC, runSimpleCC };
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { ConvertChineseVariant } from '@/types/book';
|
||||
import { SectionItem, TOCItem, CFI, BookDoc } from '@/libs/document';
|
||||
import { simplecc, initSimpleCC } from '@/utils/simplecc';
|
||||
import { initSimpleCC, runSimpleCC } from '@/utils/simplecc';
|
||||
|
||||
export const findParentPath = (toc: TOCItem[], href: string): TOCItem[] => {
|
||||
for (const item of toc) {
|
||||
@@ -87,7 +87,7 @@ export const updateToc = async (
|
||||
const convertTocLabels = (items: TOCItem[], convertChineseVariant: ConvertChineseVariant) => {
|
||||
items.forEach((item) => {
|
||||
if (item.label) {
|
||||
item.label = simplecc(item.label, convertChineseVariant);
|
||||
item.label = runSimpleCC(item.label, convertChineseVariant);
|
||||
}
|
||||
if (item.subitems) {
|
||||
convertTocLabels(item.subitems, convertChineseVariant);
|
||||
|
||||
Reference in New Issue
Block a user