Start localization

Trac 30310
This commit is contained in:
Arlo Breault 2019-08-14 13:45:15 -04:00
parent f94ef87c46
commit 4e5a50f2b5
9 changed files with 115 additions and 26 deletions

1
.gitignore vendored
View file

@ -19,5 +19,6 @@ proxy/webext/popup.js
proxy/webext/embed.html proxy/webext/embed.html
proxy/webext/embed.css proxy/webext/embed.css
proxy/webext/assets/ proxy/webext/assets/
proxy/webext/_locales/
ignore/ ignore/
npm-debug.log npm-debug.log

View file

@ -4,6 +4,20 @@
UI UI
*/ */
class Messages {
constructor(json) {
this.json = json;
}
getMessage(m, ...rest) {
let message = this.json[m].message;
return message.replace(/\$(\d+)/g, (...args) => {
return rest[Number(args[1]) - 1];
});
}
}
let messages = null;
class BadgeUI extends UI { class BadgeUI extends UI {
constructor() { constructor() {
@ -16,7 +30,7 @@ class BadgeUI extends UI {
missingFeature(missing) { missingFeature(missing) {
this.popup.setEnabled(false); this.popup.setEnabled(false);
this.popup.setActive(false); this.popup.setActive(false);
this.popup.setStatusText("Snowflake is off"); this.popup.setStatusText(messages.getMessage('popupStatusOff'));
this.popup.setStatusDesc(missing, true); this.popup.setStatusDesc(missing, true);
this.popup.hideButton(); this.popup.hideButton();
} }
@ -24,20 +38,23 @@ class BadgeUI extends UI {
turnOn() { turnOn() {
const clients = this.active ? 1 : 0; const clients = this.active ? 1 : 0;
this.popup.setChecked(true); this.popup.setChecked(true);
this.popup.setToggleText('Turn Off'); this.popup.setToggleText(messages.getMessage('popupTurnOff'));
this.popup.setStatusText(`${clients} client${(clients !== 1) ? 's' : ''} connected.`); if (clients > 0) {
this.popup.setStatusText(messages.getMessage('popupStatusOn', String(clients)));
} else {
this.popup.setStatusText(messages.getMessage('popupStatusReady'));
}
// FIXME: Share stats from webext // FIXME: Share stats from webext
const total = 0; this.popup.setStatusDesc('');
this.popup.setStatusDesc(`Your snowflake has helped ${total} user${(total !== 1) ? 's' : ''} circumvent censorship in the last 24 hours.`);
this.popup.setEnabled(true); this.popup.setEnabled(true);
this.popup.setActive(this.active); this.popup.setActive(this.active);
} }
turnOff() { turnOff() {
this.popup.setChecked(false); this.popup.setChecked(false);
this.popup.setToggleText('Turn On'); this.popup.setToggleText(messages.getMessage('popupTurnOn'));
this.popup.setStatusText("Snowflake is off"); this.popup.setStatusText(messages.getMessage('popupStatusOff'));
this.popup.setStatusDesc(""); this.popup.setStatusDesc('');
this.popup.setEnabled(false); this.popup.setEnabled(false);
this.popup.setActive(false); this.popup.setActive(false);
} }
@ -108,12 +125,12 @@ var debug, snowflake, config, broker, ui, log, dbg, init, update, silenceNotific
ui = new BadgeUI(); ui = new BadgeUI();
if (!Util.hasWebRTC()) { if (!Util.hasWebRTC()) {
ui.missingFeature("WebRTC feature is not detected."); ui.missingFeature(messages.getMessage('popupWebRTCOff'));
return; return;
} }
if (!Util.hasCookies()) { if (!Util.hasCookies()) {
ui.missingFeature("Cookies are not enabled."); ui.missingFeature(messages.getMessage('badgeCookiesOff'));
return; return;
} }
@ -153,6 +170,20 @@ var debug, snowflake, config, broker, ui, log, dbg, init, update, silenceNotific
return null; return null;
}; };
window.onload = init; window.onload = function() {
const lang = 'en_US';
fetch(`./_locales/${lang}/messages.json`)
.then((res) => {
if (!res.ok) { return; }
return res.json();
})
.then((json) => {
messages = new Messages(json);
Popup.fill(document.body, (m) => {
return messages.getMessage(m);
});
init();
});
}
}()); }());

View file

@ -32,7 +32,8 @@ var SHARED_FILES = [
'embed.html', 'embed.html',
'embed.css', 'embed.css',
'popup.js', 'popup.js',
'assets' 'assets',
'_locales',
]; ];
var concatJS = function(outDir, init, outFile) { var concatJS = function(outDir, init, outFile) {
@ -67,7 +68,7 @@ task('test', 'snowflake unit tests', function() {
}); });
task('build', 'build the snowflake proxy', function() { task('build', 'build the snowflake proxy', function() {
execSync('rm -r build'); execSync('rm -rf build');
execSync('cp -r ' + STATIC + '/ build/'); execSync('cp -r ' + STATIC + '/ build/');
concatJS('build', 'badge', 'embed.js'); concatJS('build', 'badge', 'embed.js');
console.log('Snowflake prepared.'); console.log('Snowflake prepared.');
@ -87,7 +88,7 @@ task('node', 'build the node binary', function() {
}); });
task('clean', 'remove all built files', function() { task('clean', 'remove all built files', function() {
execSync('rm -r build test spec/support'); execSync('rm -rf build test spec/support');
}); });
var cmd = process.argv[2]; var cmd = process.argv[2];

View file

@ -0,0 +1,32 @@
{
"appDesc": {
"message": "Snowflake is a WebRTC pluggable transport for Tor."
},
"popupTurnOn": {
"message": "Turn On"
},
"popupTurnOff": {
"message": "Turn Off"
},
"popupLearnMore": {
"message": "Learn more"
},
"popupStatusOff": {
"message": "Snowflake is off"
},
"popupStatusOn": {
"message": "Number of users currently connected: $1"
},
"popupStatusReady": {
"message": "Your Snowflake is ready to help users circumvent censorship!"
},
"popupWebRTCOff": {
"message": "WebRTC feature is not detected."
},
"popupDescOn": {
"message": "Number of users your Snowflake has helped circumvent censorship in the last 24 hours: $1"
},
"badgeCookiesOff": {
"message": "Cookies are not enabled."
}
}

View file

@ -11,18 +11,18 @@
<body> <body>
<div id="active"> <div id="active">
<div id="statusimg"></div> <div id="statusimg"></div>
<p id="statustext">Snowflake is off</p> <p id="statustext">__MSG_popupStatusOff__</p>
<p id="statusdesc"></p> <p id="statusdesc"></p>
</div> </div>
<div class="b button"> <div class="b button">
<label id="toggle" for="enabled">Turn On</label> <label id="toggle" for="enabled">__MSG_popupTurnOn__</label>
<label class="switch"> <label class="switch">
<input id="enabled" type="checkbox" /> <input id="enabled" type="checkbox" />
<span class="slider round"></span> <span class="slider round"></span>
</label> </label>
</div> </div>
<div class="b learn"> <div class="b learn">
<a target="_blank" href="https://snowflake.torproject.org/">Learn more</a> <a target="_blank" href="https://snowflake.torproject.org/">__MSG_popupLearnMore__</a>
</div> </div>
</body> </body>
</html> </html>

View file

@ -86,7 +86,7 @@
<p>Which looks like this:</p> <p>Which looks like this:</p>
<iframe src="embed.html" width="320px" height="200px" frameborder="0" scrolling="no"></iframe> <iframe src="embed.html" width="320px" height="240px" frameborder="0" scrolling="no"></iframe>
</div> </div>
</body> </body>

View file

@ -38,4 +38,16 @@ class Popup {
setToggleText(txt) { setToggleText(txt) {
document.getElementById('toggle').innerText = txt; document.getElementById('toggle').innerText = txt;
} }
static fill(n, func) {
switch(n.nodeType) {
case 3: { // Node.TEXT_NODE
const m = /^__MSG_([^_]*)__$/.exec(n.nodeValue);
if (m) { n.nodeValue = func(m[1]); }
break;
}
case 1: // Node.ELEMENT_NODE
n.childNodes.forEach(c => Popup.fill(c, func));
break;
}
}
} }

View file

@ -1,5 +1,12 @@
/* global chrome, Popup */ /* global chrome, Popup */
// Fill i18n in HTML
window.onload = () => {
Popup.fill(document.body, (m) => {
return chrome.i18n.getMessage(m);
});
};
const port = chrome.runtime.connect({ const port = chrome.runtime.connect({
name: "popup" name: "popup"
}); });
@ -11,8 +18,8 @@ port.onMessage.addListener((m) => {
if (missingFeature) { if (missingFeature) {
popup.setEnabled(false); popup.setEnabled(false);
popup.setActive(false); popup.setActive(false);
popup.setStatusText("Snowflake is off"); popup.setStatusText(chrome.i18n.getMessage('popupStatusOff'));
popup.setStatusDesc("WebRTC feature is not detected.", true); popup.setStatusDesc(chrome.i18n.getMessage('popupWebRTCOff'), true);
popup.hideButton(); popup.hideButton();
return; return;
} }
@ -21,13 +28,17 @@ port.onMessage.addListener((m) => {
if (enabled) { if (enabled) {
popup.setChecked(true); popup.setChecked(true);
popup.setToggleText('Turn Off'); popup.setToggleText(chrome.i18n.getMessage('popupTurnOff'));
popup.setStatusText(`${clients} client${(clients !== 1) ? 's' : ''} connected.`); if (clients > 0) {
popup.setStatusDesc(`Your snowflake has helped ${total} user${(total !== 1) ? 's' : ''} circumvent censorship in the last 24 hours.`); popup.setStatusText(chrome.i18n.getMessage('popupStatusOn', String(clients)));
} else {
popup.setStatusText(chrome.i18n.getMessage('popupStatusReady'));
}
popup.setStatusDesc((total > 0) ? chrome.i18n.getMessage('popupDescOn', String(total)) : '');
} else { } else {
popup.setChecked(false); popup.setChecked(false);
popup.setToggleText('Turn On'); popup.setToggleText(chrome.i18n.getMessage('popupTurnOn'));
popup.setStatusText("Snowflake is off"); popup.setStatusText(chrome.i18n.getMessage('popupStatusOff'));
popup.setStatusDesc(""); popup.setStatusDesc("");
} }
popup.setEnabled(enabled); popup.setEnabled(enabled);

View file

@ -2,7 +2,8 @@
"manifest_version": 2, "manifest_version": 2,
"name": "Snowflake", "name": "Snowflake",
"version": "0.0.9", "version": "0.0.9",
"description": "Snowflake is a WebRTC pluggable transport for Tor.", "description": "__MSG_appDesc__",
"default_locale": "en_US",
"background": { "background": {
"scripts": ["snowflake.js"], "scripts": ["snowflake.js"],
"persistent": true "persistent": true