-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathcontent.js
More file actions
181 lines (163 loc) · 6.25 KB
/
content.js
File metadata and controls
181 lines (163 loc) · 6.25 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
// content.js
chrome.runtime.onMessage.addListener((request, sender, sendResponse) => {
if (request.action === 'translatePage') {
const { targetLang, aiProvider } = request;
console.log(`Translating page to ${targetLang} using ${aiProvider}`);
// Find all text nodes in the body
const textNodes = findTextNodes(document.body);
// Collect valid text nodes (skip filtered elements)
const validNodes = [];
const validTexts = [];
textNodes.forEach(node => {
const originalText = node.nodeValue.trim();
if (originalText && !isElementScriptOrStyle(node.parentElement)) {
validNodes.push(node);
validTexts.push(originalText);
}
});
if (validTexts.length === 0) {
sendResponse({ status: 'No text to translate.' });
return true;
}
// Split into batches of 100 to avoid backend limit
const batchSize = 100;
const promises = [];
for (let i = 0; i < validTexts.length; i += batchSize) {
const batchTexts = validTexts.slice(i, i + batchSize);
const batchNodes = validNodes.slice(i, i + batchSize);
promises.push(
translateBatchWithCache(batchTexts, targetLang, aiProvider).then(translations => {
// Apply translations for this batch
translations.forEach((translation, idx) => {
const nodeIndex = i + idx;
if (validNodes[nodeIndex]) {
validNodes[nodeIndex].nodeValue = translation;
}
});
})
);
}
// Wait for all batches
Promise.all(promises).then(() => {
console.log('All batches translated.');
});
sendResponse({ status: 'Batch translation initiated.' });
return true;
}
});
function findTextNodes(element) {
const walker = document.createTreeWalker(
element,
NodeFilter.SHOW_TEXT,
null,
false
);
const textNodes = [];
let node;
while (node = walker.nextNode()) {
textNodes.push(node);
}
return textNodes;
}
function isElementScriptOrStyle(element) {
const skipTags = ['SCRIPT', 'STYLE', 'CODE', 'PRE', 'NAV', 'FOOTER', 'HEADER', 'ASIDE', 'INPUT', 'TEXTAREA', 'BUTTON'];
return skipTags.includes(element.tagName) || element.closest('code, pre, nav, footer, header, aside, input, textarea, button');
}
// Fungsi hash sederhana untuk cache key
function simpleHash(str) {
let hash = 0;
for (let i = 0; i < str.length; i++) {
const char = str.charCodeAt(i);
hash = ((hash << 5) - hash) + char;
hash = hash & hash; // Convert to 32bit integer
}
return hash.toString();
}
// Fungsi untuk translate dengan cache (single)
async function translateWithCache(text, targetLang, aiProvider) {
const pageUrl = window.location.href;
const cacheKey = `${pageUrl}-${simpleHash(text)}-${targetLang}`;
// Cek cache
const cached = await chrome.storage.local.get([cacheKey]);
if (cached[cacheKey]) {
return cached[cacheKey];
}
// Jika tidak ada cache, translate
return new Promise((resolve) => {
chrome.runtime.sendMessage(
{
action: 'getTranslation',
text: text,
targetLang: targetLang,
aiProvider: aiProvider
},
(response) => {
if (chrome.runtime.lastError) {
console.error(chrome.runtime.lastError.message);
resolve(text); // Fallback to original
} else if (response && response.translation) {
// Simpan ke cache
chrome.storage.local.set({ [cacheKey]: response.translation });
resolve(response.translation);
} else {
resolve(text);
}
}
);
});
}
// Fungsi untuk batch translate dengan cache
async function translateBatchWithCache(texts, targetLang, aiProvider) {
const pageUrl = window.location.href;
const cacheKeys = texts.map(text => `${pageUrl}-${simpleHash(text)}-${targetLang}`);
// Cek cache untuk semua
const cachedData = await chrome.storage.local.get(cacheKeys);
const results = [];
const uncachedTexts = [];
const uncachedIndices = [];
texts.forEach((text, index) => {
const cacheKey = cacheKeys[index];
if (cachedData[cacheKey]) {
results[index] = cachedData[cacheKey];
} else {
uncachedTexts.push(text);
uncachedIndices.push(index);
results[index] = null; // Placeholder
}
});
// Jika ada yang belum cache, translate batch
if (uncachedTexts.length > 0) {
return new Promise((resolve) => {
chrome.runtime.sendMessage(
{
action: 'getBatchTranslation',
texts: uncachedTexts,
targetLang: targetLang,
aiProvider: aiProvider
},
(response) => {
if (chrome.runtime.lastError) {
console.error(chrome.runtime.lastError.message);
// Fallback to original
uncachedIndices.forEach(i => results[i] = texts[i]);
} else if (response && response.translations) {
// Simpan ke cache dan set results
const cacheToSet = {};
response.translations.forEach((translation, idx) => {
const originalIndex = uncachedIndices[idx];
results[originalIndex] = translation;
cacheToSet[cacheKeys[originalIndex]] = translation;
});
chrome.storage.local.set(cacheToSet);
} else {
// Fallback
uncachedIndices.forEach(i => results[i] = texts[i]);
}
resolve(results);
}
);
});
} else {
return results;
}
}