translator: post-process translated text for punctuation spacing (#1245)

This commit is contained in:
Huang Xin
2025-05-27 20:32:25 +08:00
committed by GitHub
parent 5c0670031a
commit 6b033f7509
6 changed files with 87 additions and 7 deletions
@@ -57,18 +57,48 @@ export function useTextTranslation(bookKey: string, view: FoliateView | null) {
const createTranslationObserver = () => {
return new IntersectionObserver(
(entries) => {
let beforeIntersectedElement: HTMLElement | null = null;
let lastIntersectedElement: HTMLElement | null = null;
for (const entry of entries) {
if (!entry.isIntersecting) continue;
translateElement(entry.target as HTMLElement);
if (!entry.isIntersecting) {
if (!lastIntersectedElement) {
beforeIntersectedElement = entry.target as HTMLElement;
}
continue;
}
const currentElement = entry.target as HTMLElement;
translateElement(currentElement);
lastIntersectedElement = currentElement;
}
if (beforeIntersectedElement) {
translateElement(beforeIntersectedElement);
}
if (lastIntersectedElement) {
preTranslateNextElements(lastIntersectedElement, 2);
}
},
{
rootMargin: '2560px',
threshold: 0.01,
rootMargin: '1280px',
threshold: 0,
},
);
};
const preTranslateNextElements = (currentElement: HTMLElement, count: number) => {
if (!allTextNodes.current || count <= 0) return;
const currentIndex = allTextNodes.current.indexOf(currentElement);
if (currentIndex === -1) {
return;
}
const nextElements = allTextNodes.current.slice(currentIndex + 1, currentIndex + 1 + count);
nextElements.forEach((element, index) => {
setTimeout(() => {
translateElement(element);
}, index * 500);
});
};
const recreateTranslationObserver = () => {
const observer = createTranslationObserver();
observerRef.current?.disconnect();
+3 -2
View File
@@ -1,12 +1,13 @@
import { useState, useCallback, useEffect } from 'react';
import { useAuth } from '@/context/AuthContext';
import { getTranslator, getTranslators } from '@/services/translators';
import { getFromCache, storeInCache, UseTranslatorOptions } from '@/services/translators';
import { getFromCache, storeInCache, polish, UseTranslatorOptions } from '@/services/translators';
export function useTranslator({
provider = 'deepl',
sourceLang = 'AUTO',
targetLang = 'EN',
enablePolishing = true,
}: UseTranslatorOptions = {}) {
const { token } = useAuth();
const [loading, setLoading] = useState(false);
@@ -120,7 +121,7 @@ export function useTranslator({
);
setLoading(false);
return results;
return enablePolishing ? polish(results, targetLanguage) : results;
} catch (err) {
console.error('Translation error:', err);
setLoading(false);
@@ -1,3 +1,4 @@
export * from './types';
export * from './cache';
export * from './polish';
export * from './providers';
@@ -0,0 +1,47 @@
export type Polisher = (text: string) => string;
const languagePolishers: Record<string, Polisher> = {
// Chinese - fix punctuation spacing
zh: (text: string) =>
text
.replace(/--/g, '⸺')
.replace(/\s+([。、!?])/g, '$1')
.replace(/([。、!?])\s+/g, '$1'),
// Spanish - fix punctuation spacing
es: (text: string) =>
text.replace(/\?([A-ZÁÉÍÓÚÑÜ])/g, '? $1').replace(/\!([A-ZÁÉÍÓÚÑÜ])/g, '! $1'),
// French - fix punctuation spacing
fr: (text: string) => text.replace(/\s+([!?:;])/g, ' $1').replace(/([!?:;])\s+/g, '$1 '),
// Japanese - fix punctuation spacing
ja: (text: string) => text.replace(/\s+([。、!?])/g, '$1').replace(/([。、!?])\s+/g, '$1'),
};
export const basicPolish: Polisher = (text: string) => {
return text
.replace(/\s+/g, ' ') // Multiple spaces to single space
.replace(/\s+([.,!?;:])/g, '$1') // Remove space before punctuation
.trim();
};
export function getPolisher(targetLang: string): Polisher {
const langCode = targetLang.split('-')[0]!.toLowerCase();
const languagePolisher = languagePolishers[langCode];
if (languagePolisher) {
return (text: string) => {
const basicPolished = basicPolish(text);
const polished = languagePolisher(basicPolished);
return polished;
};
}
return basicPolish;
}
export function polish(texts: string[], targetLang: string): string[] {
const polisher = getPolisher(targetLang);
return texts.map(polisher);
}
@@ -20,4 +20,5 @@ export interface UseTranslatorOptions {
provider?: TranslatorName;
sourceLang?: string;
targetLang?: string;
enablePolishing?: boolean;
}
+1 -1
View File
@@ -359,7 +359,7 @@ export const getFootnoteStyles = () => `
const getTranslationStyles = () => `
.translation-block-wrapper {
display: inline-block !important;
display: block !important;
margin: 0.5em 0 !important;
}
.translation-target.hidden {