forked from KingLui69/page-translator
-
Notifications
You must be signed in to change notification settings - Fork 4
/
background.js
213 lines (173 loc) · 6.41 KB
/
background.js
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
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
function protocolIsApplicable(tabUrl) {
const APPLICABLE_PROTOCOLS = ['http:', 'https:'];
let url = new URL(tabUrl);
return APPLICABLE_PROTOCOLS.includes(url.protocol);
}
let options = {alwaysShowPageAction: false, automaticallyTranslate: false, translationService: "google", fromLang: "auto", toLang: "auto", contextMenu: false};
let contextMenuItem = false;
async function getPageLanguage(tabId) {
if(!browser.tabs.detectLanguage) {
if(!options.alwaysShowPageAction) {
options.alwaysShowPageAction = true;
browser.storage.local.set({alwaysShowPageAction: true});
}
return "und";
}
return await browser.tabs.detectLanguage(tabId);
}
// string -> boolean
function pageIsInForeignLanguage(pageLanguage) {
// Normalize page language and browser languages
pageLanguage = pageLanguage.toLowerCase();
// If language is unknown, better to show the UI
if (pageLanguage === "und") {
return true;
}
let navigatorLanguages = navigator.languages.map(navigatorLanguage => {
return navigatorLanguage.toLowerCase();
});
// Check if the page's language explicitly matches any of browser's preferred languages
if (navigatorLanguages.includes(pageLanguage)) {
return false;
}
// If you're still here, then check for match of primary language subtags
// If so, assume close enough to native language.
// Get array of the primary languages from the browser, i.e. those without a hyphen
// Ex: `en` but not `en-SV`
let primaryLanguageSubtags = navigatorLanguages.filter(language => {
return language.indexOf('-') === -1;
});
// If no primary language subtag specified in browser, the user has explicitly removed it,
// so assume they want explicit language match instead of partial match.
if (primaryLanguageSubtags.length === 0) {
return true;
}
// Get page's language subtag
let pageLanguageSubtag = pageLanguage.split('-', 1)[0];
// Look for primary language subtag match
if (primaryLanguageSubtags.includes(pageLanguageSubtag)) {
return false;
}
// No match, so page is in foreign language.
return true;
}
let translatedURLs = new Set();
function translationPage(url) {
let parsed = new URL(url);
return parsed.hostname === "ssl.microsofttranslator.com" || parsed.hostname === "translate.google.com";
}
/*
Show the Page Translator page action in the browser address bar, if applicable.
If user always wants the icon, show it.
If page is in foreign language, show it.
If user wants auto translation, invoke it.
*/
async function determinePageAction(tabId, url) {
if (options.alwaysShowPageAction && !options.automaticallyTranslate) {
return true;
}
if(!url) {
let tab = await browser.tabs.get(tabId);
url = tab.url;
}
if (!url || !protocolIsApplicable(url)) {
return false;
}
let pageLanguage = await getPageLanguage(tabId);
let pageLanguageKnown = pageLanguage !== "und";
let pageNeedsTranslating = pageIsInForeignLanguage(pageLanguage);
let isTranslationPage = translationPage(url) || translatedURLs.has(url);
if (pageLanguageKnown && pageNeedsTranslating && options.automaticallyTranslate && !isTranslationPage) {
return "translate";
}
return (pageNeedsTranslating || options.alwaysShowPageAction);
}
async function initializePageAction(tabId, url) {
let action = await determinePageAction(tabId, url);
if (action === "translate") {
translatedURLs.add(url);
doTranslator({id: tabId, url: url});
action = false;
}
if(action === true) {
browser.pageAction.show(tabId);
} else {
browser.pageAction.hide(tabId);
}
if(options.contextMenu && contextMenuItem) {
browser.menus.update(contextMenuItem, {visible: action});
}
}
async function doTranslator(tab) {
let url = tab.url;
let fromLang = options.fromLang;
if(fromLang == "auto2") {
let pageLanguage = await getPageLanguage(tab.id);
if (pageLanguage !== "und") {
fromLang = pageLanguage;
} else {
fromLang = (options.translationService === "microsoft") ? "" : "auto";
}
}
if (options.translationService === "microsoft") {
url = `https://www.translatetheweb.com/?from=${fromLang}&to=${options.toLang}&a=${encodeURIComponent(url)}`;
} else {
url = `https://translate.google.com/translate?sl=${fromLang}&tl=${options.toLang}&u=${encodeURIComponent(url)}`;
}
browser.tabs.update(tab.id,{url: url});
}
browser.tabs.onActivated.addListener((activeInfo) => {
initializePageAction(activeInfo.tabId);
});
try {
browser.tabs.onUpdated.addListener((id, changeInfo, tab) => {
if ((typeof changeInfo.status === "string") && (changeInfo.status === "complete")) {
initializePageAction(tab.id, tab.url);
}
}, {properties: ["status"]});
} catch(err) {
browser.tabs.onUpdated.addListener((id, changeInfo, tab) => {
if ((typeof changeInfo.status === "string") && (changeInfo.status === "complete")) {
initializePageAction(tab.id, tab.url);
}
});
}
browser.pageAction.onClicked.addListener(doTranslator);
let changed = true;
function updateOptions(storedOptions) {
for(o in options) {
if (typeof storedOptions[o] === typeof options[o]) {
changed = changed || options[o] !== storedOptions[o];
options[o] = storedOptions[o];
}
}
if(options.contextMenu && !contextMenuItem) {
contextMenuItem = "translate-page";
browser.menus.create({
id: contextMenuItem,
title: "Translate Page",
command: "_execute_page_action"
});
} else if(contextMenuItem && !options.contextMenu) {
browser.menus.update(contextMenuItem, {visible: false});
}
if(changed) {
browser.tabs.query({}).then((tabs) => {
for (tab of tabs) {
initializePageAction(tab.id, tab.url);
}
});
changed = false;
}
}
browser.storage.local.get().then(updateOptions);
function updateChanged(changes, area) {
var changedItems = Object.keys(changes);
changed = false;
let newOptions = {};
for (var item of changedItems) {
newOptions[item] = changes[item].newValue;
}
updateOptions(newOptions);
}
browser.storage.onChanged.addListener(updateChanged);