add translateHTML function to unify translation logic between current page and remote page translations
This commit is contained in:
parent
a340ea4f75
commit
0f51d07eb1
3 changed files with 88 additions and 102 deletions
|
|
@ -2,7 +2,7 @@
|
|||
import { initializeChunkFiveFont } from "./ui-themes.js";
|
||||
import { getUIText } from "./ui-translations.js";
|
||||
import { extractWebpageContent } from "./content.js";
|
||||
import { translateCurrentPage, translateText, SUPPORTED_LANGUAGES } from "./translation.js";
|
||||
import { translateCurrentPage, translateText, translateHTML, SUPPORTED_LANGUAGES } from "./translation.js";
|
||||
|
||||
// Function to fetch full HTML content from a remote URL using CORS proxy
|
||||
async function fetchRemotePageHTML(url) {
|
||||
|
|
@ -54,93 +54,12 @@ async function fetchRemotePageHTML(url) {
|
|||
}
|
||||
}
|
||||
|
||||
// Function to translate remote HTML content (similar to translateCurrentPage)
|
||||
// Function to translate remote HTML content - reuses translateCurrentPage logic with URL resolution
|
||||
async function translateRemoteHTML(htmlContent, targetLanguage, sourceUrl) {
|
||||
const { preserveSpecialContent, restoreSpecialContent } = await import("./translation.js");
|
||||
const { translateText } = await import("./translation.js");
|
||||
// Use the same translateHTML function but with URL resolution for remote content
|
||||
const translatedHTML = await translateHTML(htmlContent, targetLanguage, sourceUrl);
|
||||
|
||||
// Parse the HTML
|
||||
const parser = new DOMParser();
|
||||
const doc = parser.parseFromString(htmlContent, 'text/html');
|
||||
|
||||
// Extract just the body content for translation (preserve head intact)
|
||||
const bodyHTML = doc.body ? doc.body.innerHTML : doc.documentElement.innerHTML;
|
||||
|
||||
// Use the same content preservation as translateCurrentPage
|
||||
const { preservedText, preservations } = preserveSpecialContent(bodyHTML);
|
||||
|
||||
// Translate the preserved content
|
||||
const translatedPreservedText = await translateText(preservedText, targetLanguage);
|
||||
|
||||
// Restore the special content
|
||||
const translatedContent = restoreSpecialContent(translatedPreservedText, preservations);
|
||||
|
||||
// Replace the body content with translated content
|
||||
if (doc.body) {
|
||||
doc.body.innerHTML = translatedContent;
|
||||
} else {
|
||||
doc.documentElement.innerHTML = translatedContent;
|
||||
}
|
||||
|
||||
// Convert all relative URLs to absolute URLs
|
||||
const baseUrl = new URL('.', sourceUrl).href;
|
||||
|
||||
// Convert relative URLs in img src attributes
|
||||
doc.querySelectorAll('img[src]').forEach(img => {
|
||||
if (!img.src.startsWith('http') && !img.src.startsWith('//') && !img.src.startsWith('data:')) {
|
||||
img.src = new URL(img.getAttribute('src'), sourceUrl).href;
|
||||
}
|
||||
});
|
||||
|
||||
// Convert relative URLs in link href attributes (CSS, etc.)
|
||||
doc.querySelectorAll('link[href]').forEach(link => {
|
||||
if (!link.href.startsWith('http') && !link.href.startsWith('//')) {
|
||||
link.href = new URL(link.getAttribute('href'), sourceUrl).href;
|
||||
}
|
||||
});
|
||||
|
||||
// Convert relative URLs in script src attributes
|
||||
doc.querySelectorAll('script[src]').forEach(script => {
|
||||
if (!script.src.startsWith('http') && !script.src.startsWith('//')) {
|
||||
script.src = new URL(script.getAttribute('src'), sourceUrl).href;
|
||||
}
|
||||
});
|
||||
|
||||
// Convert relative URLs in a href attributes
|
||||
doc.querySelectorAll('a[href]').forEach(link => {
|
||||
if (!link.href.startsWith('http') && !link.href.startsWith('//') && !link.href.startsWith('#') && !link.href.startsWith('mailto:') && !link.href.startsWith('tel:')) {
|
||||
link.href = new URL(link.getAttribute('href'), sourceUrl).href;
|
||||
}
|
||||
});
|
||||
|
||||
// Convert background images in style attributes
|
||||
doc.querySelectorAll('[style*="background"]').forEach(el => {
|
||||
const style = el.getAttribute('style');
|
||||
if (style && style.includes('url(')) {
|
||||
const updatedStyle = style.replace(/url\(['"]?([^'"]+)['"]?\)/g, (match, url) => {
|
||||
if (!url.startsWith('http') && !url.startsWith('//') && !url.startsWith('data:')) {
|
||||
return `url('${new URL(url, sourceUrl).href}')`;
|
||||
}
|
||||
return match;
|
||||
});
|
||||
el.setAttribute('style', updatedStyle);
|
||||
}
|
||||
});
|
||||
|
||||
// Add base tag as fallback for any missed URLs
|
||||
const existingBase = doc.querySelector('base');
|
||||
if (existingBase) {
|
||||
existingBase.remove();
|
||||
}
|
||||
|
||||
const baseTag = doc.createElement('base');
|
||||
baseTag.setAttribute('href', baseUrl);
|
||||
if (doc.head) {
|
||||
doc.head.insertBefore(baseTag, doc.head.firstChild);
|
||||
}
|
||||
|
||||
// Return the complete translated HTML document
|
||||
return doc.documentElement.outerHTML;
|
||||
return translatedHTML;
|
||||
}
|
||||
|
||||
export function openTranslateModal() {
|
||||
|
|
|
|||
|
|
@ -382,34 +382,92 @@ export function extractPageContent() {
|
|||
return documentClone.documentElement.outerHTML;
|
||||
}
|
||||
|
||||
// Translate current page content
|
||||
export async function translateCurrentPage(targetLanguage) {
|
||||
const pageContent = extractPageContent();
|
||||
// Convert relative URLs to absolute URLs based on source URL
|
||||
function convertRelativeToAbsolute(htmlContent, sourceUrl) {
|
||||
if (!sourceUrl) {
|
||||
return htmlContent;
|
||||
}
|
||||
|
||||
if (!pageContent || pageContent.length < 10) {
|
||||
throw new Error("Unable to extract meaningful content from this page.");
|
||||
try {
|
||||
const baseUrl = new URL(sourceUrl);
|
||||
const parser = new DOMParser();
|
||||
const doc = parser.parseFromString(htmlContent, 'text/html');
|
||||
|
||||
// Convert relative URLs in various attributes
|
||||
const elementsWithUrls = [
|
||||
{ selector: 'a[href]', attr: 'href' },
|
||||
{ selector: 'img[src]', attr: 'src' },
|
||||
{ selector: 'link[href]', attr: 'href' },
|
||||
{ selector: 'script[src]', attr: 'src' },
|
||||
{ selector: 'iframe[src]', attr: 'src' },
|
||||
{ selector: 'source[src]', attr: 'src' },
|
||||
{ selector: 'audio[src]', attr: 'src' },
|
||||
{ selector: 'video[src]', attr: 'src' },
|
||||
{ selector: 'embed[src]', attr: 'src' },
|
||||
{ selector: 'object[data]', attr: 'data' },
|
||||
{ selector: 'form[action]', attr: 'action' }
|
||||
];
|
||||
|
||||
elementsWithUrls.forEach(({ selector, attr }) => {
|
||||
const elements = doc.querySelectorAll(selector);
|
||||
elements.forEach(element => {
|
||||
const url = element.getAttribute(attr);
|
||||
if (url && !url.startsWith('http') && !url.startsWith('//') && !url.startsWith('#') && !url.startsWith('mailto:') && !url.startsWith('tel:')) {
|
||||
try {
|
||||
const absoluteUrl = new URL(url, baseUrl).href;
|
||||
element.setAttribute(attr, absoluteUrl);
|
||||
} catch (e) {
|
||||
// If URL conversion fails, keep the original
|
||||
console.warn('Failed to convert relative URL:', url, e);
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
return doc.documentElement.outerHTML;
|
||||
} catch (error) {
|
||||
console.warn('Failed to convert relative URLs:', error);
|
||||
return htmlContent;
|
||||
}
|
||||
}
|
||||
|
||||
// Translate any HTML content with optional URL resolution
|
||||
export async function translateHTML(htmlContent, targetLanguage, sourceUrl = null) {
|
||||
if (!htmlContent || htmlContent.length < 10) {
|
||||
throw new Error("Unable to extract meaningful content from provided HTML.");
|
||||
}
|
||||
|
||||
// Convert relative URLs to absolute if sourceUrl is provided
|
||||
let processedContent = htmlContent;
|
||||
if (sourceUrl) {
|
||||
processedContent = convertRelativeToAbsolute(htmlContent, sourceUrl);
|
||||
console.log('Converted relative URLs using base URL:', sourceUrl);
|
||||
}
|
||||
|
||||
// Limit content length to avoid overwhelming the AI
|
||||
const maxLength = 50000; // Much higher limit for complete page translation
|
||||
const contentToTranslate =
|
||||
pageContent.length > maxLength
|
||||
? `${pageContent.substring(0, maxLength)}...`
|
||||
: pageContent;
|
||||
processedContent.length > maxLength
|
||||
? `${processedContent.substring(0, maxLength)}...`
|
||||
: processedContent;
|
||||
|
||||
// Translate the content
|
||||
const translatedContent = await translateText(contentToTranslate, targetLanguage);
|
||||
|
||||
// Parse the translated content to replace the original content
|
||||
// Parse the content to add base tag for URL resolution
|
||||
const parser = new DOMParser();
|
||||
const doc = parser.parseFromString(pageContent, 'text/html');
|
||||
const doc = parser.parseFromString(translatedContent, 'text/html');
|
||||
|
||||
// Replace the body content with translated content
|
||||
doc.body.innerHTML = translatedContent;
|
||||
|
||||
// Add a base tag to resolve all relative URLs correctly
|
||||
const currentUrl = window.location.href;
|
||||
const baseUrl = new URL('.', currentUrl).href; // Get base URL (without filename)
|
||||
// Add base tag for URL resolution
|
||||
let baseUrl;
|
||||
if (sourceUrl) {
|
||||
// Use the source URL as base
|
||||
baseUrl = new URL('.', sourceUrl).href; // Get base URL (without filename)
|
||||
} else {
|
||||
// Use current page URL as base (like current page translation)
|
||||
const currentUrl = window.location.href;
|
||||
baseUrl = new URL('.', currentUrl).href;
|
||||
}
|
||||
|
||||
// Remove any existing base tag
|
||||
const existingBase = doc.querySelector('base');
|
||||
|
|
@ -426,3 +484,11 @@ export async function translateCurrentPage(targetLanguage) {
|
|||
|
||||
return doc.documentElement.outerHTML;
|
||||
}
|
||||
|
||||
// Translate current page content
|
||||
export async function translateCurrentPage(targetLanguage) {
|
||||
const pageContent = extractPageContent();
|
||||
|
||||
// Use the new translateHTML function for current page translation
|
||||
return await translateHTML(pageContent, targetLanguage);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -67,6 +67,7 @@ export const openTranslateModal = UI.openTranslateModal;
|
|||
// Translation functionality
|
||||
export const translateText = Translation.translateText;
|
||||
export const translateCurrentPage = Translation.translateCurrentPage;
|
||||
export const translateHTML = Translation.translateHTML;
|
||||
|
||||
// -------------------------
|
||||
// Configuration and State
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue