translator: also translate table of contents (#1273)

This commit is contained in:
Huang Xin
2025-05-29 16:08:01 +08:00
committed by GitHub
parent b37a804192
commit 5820191c26
5 changed files with 27 additions and 13 deletions
@@ -120,6 +120,7 @@ const HeaderBar: React.FC<HeaderBarProps> = ({
<SidebarToggler bookKey={bookKey} />
</div>
<BookmarkToggler bookKey={bookKey} />
<TranslationToggler bookKey={bookKey} />
</div>
<div className='header-title z-15 bg-base-100 pointer-events-none absolute inset-0 hidden items-center justify-center sm:flex'>
@@ -129,7 +130,6 @@ const HeaderBar: React.FC<HeaderBarProps> = ({
</div>
<div className='bg-base-100 z-20 ml-auto flex h-full items-center space-x-4'>
<TranslationToggler bookKey={bookKey} />
<SettingsToggler />
<NotebookToggler bookKey={bookKey} />
<Dropdown
@@ -34,7 +34,9 @@ const TranslationToggler = ({ bookKey }: { bookKey: string }) => {
<RiTranslateAi className={translationEnabled ? 'text-blue-500' : 'text-base-content'} />
}
disabled={
!bookData || isSameLang(bookData.book?.primaryLanguage, viewSettings.translateTargetLang!)
!bookData ||
bookData.book?.format === 'PDF' ||
isSameLang(bookData.book?.primaryLanguage, viewSettings.translateTargetLang!)
}
onClick={() => setTranslationEnabled(!translationEnabled)}
tooltip={translationEnabled ? _('Disable Translation') : _('Enable Translation')}
@@ -8,6 +8,7 @@ import { findParentPath } from '@/utils/toc';
import { getContentMd5 } from '@/utils/misc';
import { eventDispatcher } from '@/utils/event';
import { BookProgress } from '@/types/book';
import { useTextTranslation } from '../../hooks/useTextTranslation';
const createExpanderIcon = (isExpanded: boolean) => {
return (
@@ -129,6 +130,8 @@ const TOCView: React.FC<{
const [expandedItems, setExpandedItems] = useState<string[]>([]);
const viewRef = useRef<HTMLUListElement | null>(null);
useTextTranslation(bookKey, viewRef.current);
const expandParents = (toc: TOCItem[], href: string) => {
const parentPath = findParentPath(toc, href).map((item) => item.href);
setExpandedItems(parentPath.filter(Boolean) as string[]);
@@ -7,7 +7,7 @@ import { walkTextNodes } from '@/utils/walk';
import { localeToLang } from '@/utils/lang';
import { getLocale } from '@/utils/misc';
export function useTextTranslation(bookKey: string, view: FoliateView | null) {
export function useTextTranslation(bookKey: string, view: FoliateView | HTMLElement | null) {
const { getViewSettings } = useReaderStore();
const viewSettings = getViewSettings(bookKey)!;
@@ -136,6 +136,9 @@ export function useTextTranslation(bookKey: string, view: FoliateView | null) {
blockWrapper.appendChild(inner);
wrapper.appendChild(blockWrapper);
if (el.querySelector('.translation-target')) {
return;
}
el.appendChild(wrapper);
translatedElements.current.push(el);
} catch (err) {
@@ -176,7 +179,7 @@ export function useTextTranslation(bookKey: string, view: FoliateView | null) {
useEffect(() => {
if (!view) return;
const onLoad = () => {
const observeTextNodes = () => {
const observer = createTranslationObserver();
observerRef.current = observer;
const nodes = walkTextNodes(view);
@@ -184,18 +187,18 @@ export function useTextTranslation(bookKey: string, view: FoliateView | null) {
nodes.forEach((el) => observer.observe(el));
};
view.addEventListener('load', onLoad);
return () => {
view.removeEventListener('load', onLoad);
observerRef.current?.disconnect();
};
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [view]);
useEffect(() => {
if ('renderer' in view) {
view.addEventListener('load', observeTextNodes);
} else {
observeTextNodes();
}
return () => {
if ('renderer' in view) {
view.removeEventListener('load', observeTextNodes);
}
observerRef.current?.disconnect();
translatedElements.current = [];
};
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [view]);
}
+6
View File
@@ -60,6 +60,12 @@ export const updateToc = (bookDoc: BookDoc, items: TOCItem[], sections: SectionI
return map;
}, {});
updateTocData(bookDoc, items, sectionsMap);
items.sort((a, b) => {
if (a.location && b.location) {
return a.location.current - b.location.current;
}
return 0;
});
};
const updateTocData = (