[TASK] Add translations

This commit is contained in:
Xaver Maierhofer 2017-01-28 15:33:13 +01:00
parent 5600832305
commit 834f57de64
No known key found for this signature in database
GPG Key ID: 7FDCE23FD2EC9FE8
26 changed files with 385 additions and 105 deletions

View File

@ -3,7 +3,7 @@ module.exports = function exports(grunt) {
grunt.loadTasks('tasks');
grunt.registerTask('default', ['lint', 'copy', 'sass:dist', 'postcss', 'requirejs:default', 'inlinedata', 'cachebreaker', 'inline', 'htmlmin', 'clean:release']);
grunt.registerTask('default', ['lint', 'copy', 'sass:dist', 'postcss', 'requirejs:default', 'inlinedata', 'cachebreaker', 'inline', 'htmlmin', 'json-minify', 'clean:release']);
grunt.registerTask('lint', ['sasslint', 'eslint']);
grunt.registerTask('serve', ['lint', 'copy', 'sass:dev', 'postcss', 'requirejs:dev', 'inlinedata', 'htmlmin', 'browserSync', 'watch']);
grunt.registerTask('serve', ['lint', 'copy', 'sass:dev', 'postcss', 'requirejs:dev', 'inlinedata', 'htmlmin', 'json-minify', 'browserSync', 'watch']);
};

View File

@ -7,6 +7,7 @@
- Updates selected node or list (incl. image stats cache-breaker) - not only overview tables
- Zoom level if you click a node (`nodeZoom`) - Zoom level 22 available, but it is to close for a click
- Formatted Code
- Translation support - https://crowdin.com/project/meshviewer
- Grunt inline for some css and js - less requests
- Icon font with only needed icons
- Upgrade to grunt v1.x (Tested with Node.js 4 LTS,6 LTS,7 Linux,OSX,W**)
@ -154,7 +155,7 @@ This option allows to show node statistics depending on following case-sensitive
- `caption` is shown, if `thumbnail` is not present (no thumbnail in infobox)
To insert current node-id in either `href`, `thumbnail` or `caption`
you can use the case-sensitive template string `{NODE_ID}`, `{NODE_NAME}` and `{TIME}` as cache-breaker.
you can use the case-sensitive template string `{NODE_ID}`, `{NODE_NAME}`, `{LOCALE}` and `{TIME}` as cache-breaker.
Examples for `nodeInfos`:
@ -207,7 +208,7 @@ This option allows to show link statistics depending on the following case-sensi
- `caption` is shown, if `thumbnail` is not present (no thumbnail in infobox)
To insert the source or target node-id in either `href`, `thumbnail` or `caption`
you can use the case-sensitive template strings `{SOURCE}`, `{TARGET}` and `{TIME}` as cache-breaker.
you can use the case-sensitive template strings `{SOURCE}`, `{LOCALE}`, `{TARGET}` and `{TIME}` as cache-breaker.
"linkInfos": [
{ "href": "stats/dashboard/db/links?var-source={SOURCE}&var-target={TARGET}",
@ -233,6 +234,18 @@ Example for `siteNames`:
{ "site": "ffal", "name": "Atlantis" }
]
## supportedLocale (array)
Add supported locale (with matching language file in locales/*.json) and it will be matched against the browser language setting. Fallback is the first language in the array.
Example for `supportedLocale`:
"supportedLocale": [
"en",
"de"
]
## Sponsoring / Supporting
- [BrowserStack](https://www.browserstack.com/) for providing a awesome testing service for hundreds of browsers
- [Travis CI](https://travis-ci.org/) for testing every push and pull request

3
app.js
View File

@ -3,10 +3,11 @@
require.config({
baseUrl: 'lib',
paths: {
'polyglot': '../node_modules/node-polyglot/build/polyglot',
'leaflet': '../node_modules/leaflet/dist/leaflet',
'leaflet.label': '../node_modules/leaflet-label/dist/leaflet.label',
'chroma-js': '../node_modules/chroma-js/chroma.min',
'moment': '../node_modules/moment',
'moment': '../node_modules/moment/moment',
'tablesort': '../node_modules/tablesort/src/tablesort',
'd3': '../node_modules/d3/d3.min',
'virtual-dom': '../node_modules/virtual-dom/dist/virtual-dom',

View File

@ -110,5 +110,9 @@
"site": "ffrgb",
"name": "Regensburg"
}
],
"supportedLocale": [
"en",
"de"
]
}

3
crowdin.yml Normal file
View File

@ -0,0 +1,3 @@
files:
- source: /locale/en.json
translation: /locale/%two_letters_code%.json

View File

@ -3,10 +3,7 @@ define(function () {
return function () {
this.render = function render(d) {
d.innerHTML = '<h2>Über Meshviewer</h2>' +
'<p>Mit Doppelklick und Shift+Doppelklick kann man in der Karte ' +
'auch zoomen.</p>' +
d.innerHTML = _.t('sidebar.aboutInfo') +
'<h3>AGPL 3</h3>' +

View File

@ -81,8 +81,8 @@ define(['chroma-js', 'map', 'sidebar', 'tabs', 'container', 'legend',
var tabs = new Tabs();
var overview = new Container();
var legend = new Legend(config);
var newnodeslist = new SimpleNodelist('new', 'firstseen', router, 'Neue Knoten');
var lostnodeslist = new SimpleNodelist('lost', 'lastseen', router, 'Verschwundene Knoten');
var newnodeslist = new SimpleNodelist('new', 'firstseen', router, _.t('node.new'));
var lostnodeslist = new SimpleNodelist('lost', 'lastseen', router, _.t('node.missing'));
var nodelist = new Nodelist(router);
var linklist = new Linklist(linkScale, router);
var statistics = new Proportions(config, fanout);
@ -106,11 +106,11 @@ define(['chroma-js', 'map', 'sidebar', 'tabs', 'container', 'legend',
header.add(filterGUI);
sidebar.add(tabs);
tabs.add('Aktuelles', overview);
tabs.add('Knoten', nodelist);
tabs.add('Verbindungen', linklist);
tabs.add('Statistiken', statistics);
tabs.add('Über', about);
tabs.add('sidebar.actual', overview);
tabs.add('node.nodes', nodelist);
tabs.add('node.links', linklist);
tabs.add('sidebar.stats', statistics);
tabs.add('sidebar.about', about);
router.addTarget(title);
router.addTarget(infobox);

View File

@ -6,6 +6,7 @@ define(['helper'], function (helper) {
subst['{SOURCE}'] = source;
subst['{TARGET}'] = target;
subst['{TIME}'] = time;
subst['{LOCALE}'] = _.locale();
return helper.showStat(o, subst);
}
@ -37,11 +38,12 @@ define(['helper'], function (helper) {
var attributes = document.createElement('table');
attributes.classList.add('attributes');
helper.attributeEntry(attributes, 'TQ', helper.showTq(d));
helper.attributeEntry(attributes, 'Entfernung', helper.showDistance(d));
helper.attributeEntry(attributes, 'node.tq', helper.showTq(d));
helper.attributeEntry(attributes, 'node.distance', helper.showDistance(d));
var hw1 = unknown ? null : helper.dictGet(d.source.node.nodeinfo, ['hardware', 'model']);
var hw2 = helper.dictGet(d.target.node.nodeinfo, ['hardware', 'model']);
helper.attributeEntry(attributes, 'Hardware', (hw1 !== null ? hw1 : 'unbekannt') + ' ' + (hw2 !== null ? hw2 : 'unbekannt'));
helper.attributeEntry(attributes, 'node.hardware', (hw1 !== null ? hw1 : _.t('unknown')) + ' ' + (hw2 !== null ? hw2 : _.t('unknown')));
el.appendChild(attributes);
if (config.linkInfos) {

View File

@ -3,10 +3,10 @@ define(['helper'], function (helper) {
return function (config, el, router, d) {
var sidebarTitle = document.createElement('h2');
sidebarTitle.textContent = 'Location: ' + d.toString();
sidebarTitle.textContent = _.t('location.location') + ': ' + d.toString();
el.appendChild(sidebarTitle);
helper.getJSON(config.reverseGeocodingApi + '?format=json&lat=' + d.lat + '&lon=' + d.lng + '&zoom=18&addressdetails=0')
helper.getJSON(config.reverseGeocodingApi + '?format=json&lat=' + d.lat + '&lon=' + d.lng + '&zoom=18&addressdetails=0&accept-language=' + _.locale())
.then(function (result) {
if (result.display_name) {
sidebarTitle.textContent = result.display_name;
@ -16,12 +16,12 @@ define(['helper'], function (helper) {
var editLat = document.createElement('input');
editLat.type = 'text';
editLat.value = d.lat.toFixed(9);
el.appendChild(createBox('lat', 'Breitengrad', editLat));
el.appendChild(createBox('lat', _.t('location.latitude'), editLat));
var editLng = document.createElement('input');
editLng.type = 'text';
editLng.value = d.lng.toFixed(9);
el.appendChild(createBox('lng', 'Längengrad', editLng));
el.appendChild(createBox('lng', _.t('location.longitude'), editLng));
var editUci = document.createElement('textarea');
editUci.value =
@ -40,7 +40,7 @@ define(['helper'], function (helper) {
box.appendChild(heading);
var btn = document.createElement('button');
btn.classList.add('ion-ios-copy');
btn.title = 'Kopieren';
btn.title = _.t('location.copy');
btn.onclick = function onclick() {
copy2clip(inputElem.id);
};

View File

@ -1,5 +1,5 @@
define(['chroma-js', 'moment/moment', 'tablesort', 'helper', 'moment/locale/de'],
function (chroma, moment, tablesort, helper) {
define(['chroma-js', 'moment', 'tablesort', 'helper'],
function (chroma, moment, Tablesort, helper) {
'use strict';
function showGeoURI(d) {
@ -19,9 +19,15 @@ define(['chroma-js', 'moment/moment', 'tablesort', 'helper', 'moment/locale/de']
return function (el) {
el.classList.add(d.flags.unseen ? 'unseen' : (d.flags.online ? 'online' : 'offline'));
if (d.flags.online) {
el.textContent = 'online, letzte Nachricht ' + d.lastseen.fromNow() + ' (' + d.lastseen.format('DD.MM.YYYY, H:mm:ss') + ')';
el.textContent = _.t('node.lastOnline', {
time: d.lastseen.fromNow(),
date: d.lastseen.format('DD.MM.YYYY, H:mm:ss')
});
} else {
el.textContent = 'offline, letzte Nachricht ' + d.lastseen.fromNow() + ' (' + d.lastseen.format('DD.MM.YYYY, H:mm:ss') + ')';
el.textContent = _.t('node.lastOffline', {
time: d.lastseen.fromNow(),
date: d.lastseen.format('DD.MM.YYYY, H:mm:ss')
});
}
};
}
@ -72,7 +78,7 @@ define(['chroma-js', 'moment/moment', 'tablesort', 'helper', 'moment/locale/de']
}
return function (el) {
el.appendChild(document.createTextNode(d.statistics.clients > 0 ? d.statistics.clients : 'keine'));
el.appendChild(document.createTextNode(d.statistics.clients > 0 ? d.statistics.clients : _.t('none')));
el.appendChild(document.createElement('br'));
var span = document.createElement('span');
@ -169,14 +175,15 @@ define(['chroma-js', 'moment/moment', 'tablesort', 'helper', 'moment/locale/de']
return undefined;
}
return au.enabled ? 'aktiviert (' + au.branch + ')' : 'deaktiviert';
return au.enabled ? _.t('node.activated', {branch: au.branch}) : _.t('node.deactivated');
}
function showStatImg(o, d) {
var subst = {};
subst['{NODE_ID}'] = d.nodeinfo.node_id ? d.nodeinfo.node_id : 'unknown';
subst['{NODE_NAME}'] = d.nodeinfo.hostname ? d.nodeinfo.hostname.replace(/[^a-z0-9\-]/ig, '_') : 'unknown';
subst['{NODE_ID}'] = d.nodeinfo.node_id ? d.nodeinfo.node_id : _.t('unknown');
subst['{NODE_NAME}'] = d.nodeinfo.hostname ? d.nodeinfo.hostname.replace(/[^a-z0-9\-]/ig, '_') : _.t('unknown');
subst['{TIME}'] = d.lastseen.format('DDMMYYYYHmmss');
subst['{LOCALE}'] = _.locale();
return helper.showStat(o, subst);
}
@ -189,29 +196,29 @@ define(['chroma-js', 'moment/moment', 'tablesort', 'helper', 'moment/locale/de']
var attributes = document.createElement('table');
attributes.classList.add('attributes');
helper.attributeEntry(attributes, 'Status', showStatus(d));
helper.attributeEntry(attributes, 'Gateway', d.flags.gateway ? 'ja' : null);
helper.attributeEntry(attributes, 'Koordinaten', showGeoURI(d));
helper.attributeEntry(attributes, 'node.status', showStatus(d));
helper.attributeEntry(attributes, 'node.gateway', d.flags.gateway ? 'ja' : null);
helper.attributeEntry(attributes, 'node.coordinates', showGeoURI(d));
if (config.nodeInfobox && config.nodeInfobox.contact) {
helper.attributeEntry(attributes, 'Kontakt', helper.dictGet(d.nodeinfo, ['owner', 'contact']));
helper.attributeEntry(attributes, 'node.contact', helper.dictGet(d.nodeinfo, ['owner', 'contact']));
}
helper.attributeEntry(attributes, 'Hardware', helper.dictGet(d.nodeinfo, ['hardware', 'model']));
helper.attributeEntry(attributes, 'Primäre MAC', helper.dictGet(d.nodeinfo, ['network', 'mac']));
helper.attributeEntry(attributes, 'Node ID', helper.dictGet(d.nodeinfo, ['node_id']));
helper.attributeEntry(attributes, 'Firmware', showFirmware(d));
helper.attributeEntry(attributes, 'Site', showSite(d, config));
helper.attributeEntry(attributes, 'Uptime', showUptime(d));
helper.attributeEntry(attributes, 'Teil des Netzes', showFirstseen(d));
helper.attributeEntry(attributes, 'node.hardware', helper.dictGet(d.nodeinfo, ['hardware', 'model']));
helper.attributeEntry(attributes, 'node.primaryMac', helper.dictGet(d.nodeinfo, ['network', 'mac']));
helper.attributeEntry(attributes, 'node.id', helper.dictGet(d.nodeinfo, ['node_id']));
helper.attributeEntry(attributes, 'node.firmware', showFirmware(d));
helper.attributeEntry(attributes, 'node.site', showSite(d, config));
helper.attributeEntry(attributes, 'node.uptime', showUptime(d));
helper.attributeEntry(attributes, 'node.firstSeen', showFirstseen(d));
if (config.nodeInfobox && config.nodeInfobox.hardwareUsage) {
helper.attributeEntry(attributes, 'Systemlast', showLoad(d));
helper.attributeEntry(attributes, 'Arbeitsspeicher', showRAM(d));
helper.attributeEntry(attributes, 'node.systemLoad', showLoad(d));
helper.attributeEntry(attributes, 'node.ram', showRAM(d));
}
helper.attributeEntry(attributes, 'IP Adressen', showIPs(d));
helper.attributeEntry(attributes, 'Gewähltes Gateway', helper.dictGet(d.statistics, ['gateway']));
helper.attributeEntry(attributes, 'Autom. Updates', showAutoupdate(d));
helper.attributeEntry(attributes, 'Clients', showClients(d));
helper.attributeEntry(attributes, 'node.ipAddresses', showIPs(d));
helper.attributeEntry(attributes, 'node.selectedGateway', helper.dictGet(d.statistics, ['gateway']));
helper.attributeEntry(attributes, 'node.update', showAutoupdate(d));
helper.attributeEntry(attributes, 'node.clients', showClients(d));
el.appendChild(attributes);
@ -226,7 +233,7 @@ define(['chroma-js', 'moment/moment', 'tablesort', 'helper', 'moment/locale/de']
if (d.neighbours.length > 0) {
var h3 = document.createElement('h3');
h3.textContent = 'Links (' + d.neighbours.length + ')';
h3.textContent = _.t('node.link', d.neighbours.length) + '(' + d.neighbours.length + ')';
el.appendChild(h3);
var table = document.createElement('table');
@ -238,16 +245,16 @@ define(['chroma-js', 'moment/moment', 'tablesort', 'helper', 'moment/locale/de']
tr.appendChild(th1);
var th2 = document.createElement('th');
th2.textContent = 'Knoten';
th2.textContent = _.t('node.node', d.neighbours.length);
th2.classList.add('sort-default');
tr.appendChild(th2);
var th3 = document.createElement('th');
th3.textContent = 'TQ';
th3.textContent = _.t('node.tq');
tr.appendChild(th3);
var th4 = document.createElement('th');
th4.textContent = 'Entfernung';
th4.textContent = _.t('node.distance');
tr.appendChild(th4);
thead.appendChild(tr);

View File

@ -16,12 +16,11 @@ define(['helper'], function (helper) {
return n.flags.gateway;
}).map(helper.one));
stats.textContent = totalNodes + ' Knoten, ' +
'davon ' + totalOnlineNodes + ' Knoten online ' +
'mit ' + totalClients + ' Client' + ( totalClients === 1 ? ' ' : 's ' ) +
'auf ' + totalGateways + ' Gateway' + ( totalGateways === 1 ? '' : 's' );
stats.textContent = _.t('sidebar.nodes', {total: totalNodes, online: totalOnlineNodes}) + ' ' +
_.t('sidebar.clients', {smart_count: totalClients}) + ' ' +
_.t('sidebar.gateway', {smart_count: totalGateways});
timestamp.textContent = 'Stand: ' + d.timestamp.format('DD.MM.Y HH:mm');
timestamp.textContent = _.t('sidebar.lastUpdate') + ': ' + d.timestamp.format('DD.MM.Y HH:mm');
};
self.render = function render(el) {
@ -31,9 +30,9 @@ define(['helper'], function (helper) {
var p = document.createElement('p');
p.classList.add('legend');
p.innerHTML = '<span class="legend-new"><span class="symbol"></span> Neuer Knoten</span>' +
'<span class="legend-online"><span class="symbol"></span> Knoten ist online</span>' +
'<span class="legend-offline"><span class="symbol"></span> Knoten ist offline</span>';
p.innerHTML = '<span class="legend-new"><span class="symbol"></span> ' + _.t('sidebar.nodeNew') + '</span>' +
'<span class="legend-online"><span class="symbol"></span> ' + _.t('sidebar.nodeOnline') + '</span>' +
'<span class="legend-offline"><span class="symbol"></span> ' + _.t('sidebar.nodeOffline') + '</span>';
el.appendChild(p);
p.appendChild(document.createElement('br'));

View File

@ -6,19 +6,19 @@ define(['sorttable', 'virtual-dom', 'helper'], function (SortTable, V, helper) {
}
var headings = [{
name: 'Knoten',
name: 'node.nodes',
sort: function (a, b) {
return linkName(a).localeCompare(linkName(b));
},
reverse: false
}, {
name: 'TQ',
name: 'node.tq',
sort: function (a, b) {
return a.tq - b.tq;
},
reverse: true
}, {
name: 'Entfernung',
name: 'node.distance',
sort: function (a, b) {
return (a.distance === undefined ? -1 : a.distance) -
(b.distance === undefined ? -1 : b.distance);
@ -42,7 +42,7 @@ define(['sorttable', 'virtual-dom', 'helper'], function (SortTable, V, helper) {
this.render = function render(d) {
var h2 = document.createElement('h2');
h2.textContent = 'Verbindungen';
h2.textContent = _.t('node.links');
d.appendChild(h2);
d.appendChild(table.el);

View File

@ -1,5 +1,5 @@
define(['moment/moment', 'router', 'leaflet', 'gui', 'helper', 'moment/locale/de'],
function (moment, Router, L, GUI, helper) {
define(['polyglot', 'moment', 'router', 'leaflet', 'gui', 'helper'],
function (Polyglot, moment, Router, L, GUI, helper) {
'use strict';
return function (config) {
@ -150,7 +150,35 @@ define(['moment/moment', 'router', 'leaflet', 'gui', 'helper', 'moment/locale/de
};
}
moment.locale('de');
function setTranslation(json) {
_.extend(json);
moment.locale(_.locale(), {
longDateFormat: {
LT: 'HH:mm',
LTS: 'HH:mm:ss',
L: 'DD.MM.YYYY',
LL: 'D. MMMM YYYY',
LLL: 'D. MMMM YYYY HH:mm',
LLLL: 'dddd, D. MMMM YYYY HH:mm'
},
calendar: json.momentjs.calendar,
relativeTime: json.momentjs.relativeTime
});
}
var language = navigator.languages && navigator.languages[0] || navigator.language || navigator.userLanguage;
var locale = config.supportedLocale[0];
config.supportedLocale.some(function (item) {
if (language.indexOf(item) !== -1) {
locale = item;
return true;
}
return false;
});
window._ = new Polyglot({locale: locale, allowMissing: true});
helper.getJSON('locale/' + _.locale() + '.json').then(setTranslation);
var router = new Router();

View File

@ -1,4 +1,6 @@
define(['map/clientlayer', 'map/labelslayer', 'leaflet', 'moment/moment', 'locationmarker', 'rbush', 'helper', 'leaflet.label', 'moment/locale/de'],
define(['map/clientlayer', 'map/labelslayer',
'leaflet', 'moment', 'locationmarker', 'rbush', 'helper',
'leaflet.label'],
function (ClientLayer, LabelsLayer, L, moment, LocationMarker, rbush, helper) {
'use strict';

View File

@ -28,28 +28,28 @@ define(['sorttable', 'virtual-dom', 'helper'], function (SortTable, V, helper) {
var headings = [{
name: ''
}, {
name: 'Knoten',
name: 'node.nodes',
sort: function (a, b) {
return a.nodeinfo.hostname.localeCompare(b.nodeinfo.hostname);
},
reverse: false
}, {
name: 'Uptime',
}, {
name: 'node.uptime',
sort: function (a, b) {
return a.uptime - b.uptime;
},
reverse: true
}, {
name: '#Links',
}, {
name: 'node.links',
sort: function (a, b) {
return a.meshlinks - b.meshlinks;
},
reverse: true
}, {
name: 'Clients',
}, {
name: 'node.clients',
sort: function (a, b) {
return ('clients' in a.statistics ? a.statistics.clients : -1) -
('clients' in b.statistics ? b.statistics.clients : -1);
('clients' in b.statistics ? b.statistics.clients : -1);
},
reverse: true
}];
@ -84,7 +84,7 @@ define(['sorttable', 'virtual-dom', 'helper'], function (SortTable, V, helper) {
this.render = function render(d) {
var h2 = document.createElement('h2');
h2.textContent = 'Alle Knoten';
h2.textContent = _.t('node.all');
d.appendChild(h2);
d.appendChild(table.el);

View File

@ -75,7 +75,7 @@ define(['chroma-js', 'virtual-dom', 'filters/genericnode', 'helper'],
var c1 = Chroma.contrast(scale(v), 'white');
var c2 = Chroma.contrast(scale(v), 'black');
var filter = new Filter(name, d[2], d[0], d[3]);
var filter = new Filter(_.t(name), d[2], d[0], d[3]);
var a = V.h('a', { href: '#', onclick: addFilter(filter) }, d[0]);
@ -111,7 +111,7 @@ define(['chroma-js', 'virtual-dom', 'filters/genericnode', 'helper'],
var fwDict = count(nodes, ['nodeinfo', 'software', 'firmware', 'release']);
var hwDict = count(nodes, ['nodeinfo', 'hardware', 'model']);
var geoDict = count(nodes, ['nodeinfo', 'location'], function (d) {
return d && d.longitude && d.latitude ? 'ja' : 'nein';
return d && d.longitude && d.latitude ? _.t('yes') : _.t('no');
});
var autoDict = count(nodes, ['nodeinfo', 'software', 'autoupdater'], function (d) {
@ -120,7 +120,7 @@ define(['chroma-js', 'virtual-dom', 'filters/genericnode', 'helper'],
} else if (d.enabled) {
return d.branch;
}
return '(deaktiviert)';
return _.t('node.deactivated');
});
var siteDict = count(nodes, ['nodeinfo', 'system', 'site_code'], function (d) {
@ -135,10 +135,10 @@ define(['chroma-js', 'virtual-dom', 'filters/genericnode', 'helper'],
return rt;
});
fillTable('Status', statusTable, statusDict.sort(function (a, b) {
fillTable('node.status', statusTable, statusDict.sort(function (a, b) {
return b[1] - a[1];
}));
fillTable('Firmware', fwTable, fwDict.sort(function (a, b) {
fillTable('node.firmware', fwTable, fwDict.sort(function (a, b) {
if (b[0] < a[0]) {
return -1;
}
@ -147,28 +147,28 @@ define(['chroma-js', 'virtual-dom', 'filters/genericnode', 'helper'],
}
return 0;
}));
fillTable('Hardware', hwTable, hwDict.sort(function (a, b) {
fillTable('node.hardware', hwTable, hwDict.sort(function (a, b) {
return b[1] - a[1];
}));
fillTable('Koordinaten', geoTable, geoDict.sort(function (a, b) {
fillTable('node.visible', geoTable, geoDict.sort(function (a, b) {
return b[1] - a[1];
}));
fillTable('Autom. Updates', autoTable, autoDict.sort(function (a, b) {
fillTable('node.update', autoTable, autoDict.sort(function (a, b) {
return b[1] - a[1];
}));
fillTable('Site', siteTable, siteDict.sort(function (a, b) {
fillTable('node.site', siteTable, siteDict.sort(function (a, b) {
return b[1] - a[1];
}));
};
self.render = function render(el) {
var h2;
self.renderSingle(el, 'Status', statusTable);
self.renderSingle(el, 'Firmwareversionen', fwTable);
self.renderSingle(el, 'Hardwaremodelle', hwTable);
self.renderSingle(el, 'Auf der Karte sichtbar', geoTable);
self.renderSingle(el, 'Autoupdater', autoTable);
self.renderSingle(el, 'Site', siteTable);
self.renderSingle(el, 'node.status', statusTable);
self.renderSingle(el, 'node.firmware', fwTable);
self.renderSingle(el, 'node.hardware', hwTable);
self.renderSingle(el, 'node.visible', geoTable);
self.renderSingle(el, 'node.update', autoTable);
self.renderSingle(el, 'node.site', siteTable);
if (config.globalInfos) {
config.globalInfos.forEach(function (globalInfo) {
@ -183,7 +183,7 @@ define(['chroma-js', 'virtual-dom', 'filters/genericnode', 'helper'],
self.renderSingle = function renderSingle(el, heading, table) {
var h2;
h2 = document.createElement('h2');
h2.textContent = heading;
h2.textContent = _.t(heading);
h2.onclick = function onclick() {
table.classList.toggle('hidden');
};

View File

@ -1,4 +1,4 @@
define(['moment/moment', 'virtual-dom', 'helper', 'moment/locale/de'], function (moment, V, helper) {
define(['moment', 'virtual-dom', 'helper'], function (moment, V, helper) {
'use strict';
return function (nodes, field, router, title) {

View File

@ -34,7 +34,7 @@ define(['virtual-dom'], function (V) {
properties.className += sortReverse ? ' sort-up' : ' sort-down';
}
return V.h('th', properties, d.name);
return V.h('th', properties, _.t(d.name));
});
var links = data.slice(0).sort(headings[sortIndex].sort);

View File

@ -34,7 +34,7 @@ define(function () {
self.add = function add(title, d) {
var li = document.createElement('li');
li.textContent = title;
li.textContent = _.t(title);
li.onclick = switchTab;
li.child = d;
tabs.appendChild(li);

View File

@ -132,9 +132,10 @@ define({
return '';
}
var tr = document.createElement('tr');
var th = document.createElement('th');
th.textContent = label;
th.textContent = _.t(label);
tr.appendChild(th);
var td = document.createElement('td');

83
locale/de.json Normal file
View File

@ -0,0 +1,83 @@
{
"node":{
"all":"Alle Knoten",
"nodes":"Knoten",
"uptime":"Laufzeit",
"links":"Verbindungen",
"clients":"Nutzer",
"distance":"Entfernung",
"tq":"TQ",
"lastOnline":"online, letzte Nachricht %{time} (%{date})",
"lastOffline":"offline, letzte Nachricht %{time} (%{date})",
"activated":"aktiviert (%{branch})",
"deactivated":"deaktiviert",
"status":"Status",
"firmware":"Firmware-Version",
"hardware":"Geräte-Modell",
"visible":"Auf der Karte sichtbar",
"update":"Auto-Update",
"site":"Site",
"gateway":"Gateway",
"coordinates":"Koordinaten",
"contact":"Kontakt",
"primaryMac":"Primäre MAC",
"id":"Knoten ID",
"firstSeen":"Erstmals gesehen",
"systemLoad":"Load average",
"ram":"Speicherauslastung",
"ipAddresses":"IP Adressen",
"selectedGateway":"Gewähltes Gateway",
"link":"Verbindung |||| Verbindungen",
"node":"Knoten",
"new":"Neuer Knoten",
"missing":"Verschwundene Knoten"
},
"location":{
"location":"Standort",
"latitude":"Breitengrad",
"longitude":"Längengrad",
"copy":"Kopieren"
},
"sidebar":{
"nodes":"%{total} Knoten, davon %{online} Knoten online",
"clients":"mit %{smart_count} Nutzer |||| mit %{smart_count} Nutzern",
"gateway":"auf %{smart_count} Gateway |||| auf %{smart_count} Gateways",
"lastUpdate":"Letzte Aktualisierung",
"nodeNew":"Knoten ist neu",
"nodeOnline":"Knoten ist online",
"nodeOffline":"Knoten ist offline",
"aboutInfo":"<h2>Über Meshviewer</h2><p>Mit Doppelklick kann man in die Karte hinein zoomen und Shift+Doppelklick heraus zoomen.</p>",
"actual":"Aktuell",
"stats":"Statistiken",
"about":"Über"
},
"momentjs":{
"calendar":{
"sameDay":"[heute um] LT [Uhr]",
"nextDay":"[morgen um] LT [Uhr]",
"nextWeek":"dddd [um] LT [Uhr]",
"lastDay":"[gestern um] LT [Uhr]",
"lastWeek":"[letzten] dddd [um] LT [Uhr]",
"sameElse":"L"
},
"relativeTime":{
"future":"in %s",
"past":"vor %s",
"s":"ein paar Sekunden",
"m":"einer Minute",
"mm":"%d Minuten",
"h":"einer Stunde",
"hh":"%d Stunden",
"d":"einem Tag",
"dd":"%d Tagen",
"M":"einem Monat",
"MM":"%d Monate",
"y":"einem Jahr",
"yy":"%d Jahre"
}
},
"yes":"ja",
"no":"nein",
"unknown":"unbekannt",
"none":"keine"
}

83
locale/en.json Normal file
View File

@ -0,0 +1,83 @@
{
"node": {
"all": "All nodes",
"nodes": "Nodes",
"uptime": "Uptime",
"links": "Links",
"clients": "Clients",
"distance": "Distance",
"tq": "TQ",
"lastOnline": "online, last message %{time} (%{date})",
"lastOffline": "offline, last message %{time} (%{date})",
"activated": "activated (%{branch})",
"deactivated": "deactivated",
"status": "Status",
"firmware": "Firmware version",
"hardware": "Hardware model",
"visible": "Visible on the map",
"update": "Auto update",
"site": "Site",
"gateway": "Gateway",
"coordinates": "Coordinates",
"contact": "Contact",
"primaryMac": "Primary MAC",
"id": "Node ID",
"firstSeen": "First seen",
"systemLoad": "Load average",
"ram": "Memory usage",
"ipAddresses": "IP addresses",
"selectedGateway": "Selected gateway",
"link": "Link |||| Links",
"node": "Node |||| Nodes",
"new": "New nodes",
"missing": "Disappeared nodes"
},
"location": {
"location": "Location",
"latitude": "Latitude",
"longitude": "Longitude",
"copy": "Copy"
},
"sidebar": {
"nodes": "%{total} nodes, including %{online} nodes online",
"clients": "with %{smart_count} client |||| with %{smart_count} clients",
"gateway": "on %{smart_count} gateway |||| on %{smart_count} gateways",
"lastUpdate": "Last update",
"nodeNew": "Node is new",
"nodeOnline": "Node is online",
"nodeOffline": "Node is offline",
"aboutInfo": "<h2>About Meshviewer</h2><p>You can zoom in with double-click and zoom out with shift+double-click</p>",
"actual": "Actual",
"stats": "Statistics",
"about": "About"
},
"momentjs": {
"calendar": {
"sameDay": "[Today at] LT",
"nextDay": "[Tomorrow at] LT",
"nextWeek": "dddd [at] LT",
"lastDay": "[Yesterday at] LT",
"lastWeek": "[Last] dddd [at] LT",
"sameElse": "L"
},
"relativeTime": {
"future": "in %s",
"past": "%s ago",
"s": "a few seconds",
"m": "a minute",
"mm": "%d minutes",
"h": "an hour",
"hh": "%d hours",
"d": "a day",
"dd": "%d days",
"M": "a month",
"MM": "%d months",
"y": "a year",
"yy": "%d years"
}
},
"yes": "yes",
"no": "no",
"unknown": "unknown",
"none": "none"
}

View File

@ -26,6 +26,7 @@
"grunt-eslint": "^19.0.0",
"grunt-inline": "^0.3.6",
"grunt-inline-data": "git://github.com/xiaokaike/grunt-inline-data.git#2eeb08f",
"grunt-json-minify": "^1.1.0",
"grunt-postcss": "^0.8.0",
"grunt-sass": "^2.0.0",
"grunt-sass-lint": "^0.2.2"
@ -48,6 +49,7 @@
"leaflet": "https://github.com/davojta/Leaflet.git#stable_0_7_7_1_release",
"leaflet-label": "^0.2.1-0",
"moment": "^2.17.1",
"node-polyglot": "airbnb/polyglot.js",
"promise-polyfill": "^6.0.2",
"rbush": "1.4.3",
"requirejs": "^2.3.2",

View File

@ -33,6 +33,12 @@ module.exports = function exports(grunt) {
expand: true,
dest: 'build/',
cwd: 'assets/'
},
locale: {
src: ['locale/*'],
expand: true,
dest: 'build/',
cwd: '.'
}
},
sass: {
@ -123,6 +129,11 @@ module.exports = function exports(grunt) {
}
}
},
'json-minify': {
build: {
files: 'build/locale/*.json'
}
},
cachebreaker: {
default: {
options: {
@ -142,5 +153,6 @@ module.exports = function exports(grunt) {
grunt.loadNpmTasks('grunt-inline');
grunt.loadNpmTasks('grunt-inline-data');
grunt.loadNpmTasks('grunt-contrib-htmlmin');
grunt.loadNpmTasks('grunt-json-minify');
grunt.loadNpmTasks('grunt-cache-breaker');
};

View File

@ -24,12 +24,12 @@ module.exports = function exports(grunt) {
},
watch: {
html: {
files: ['html/index.html', 'config.json'],
tasks: ['copy', 'inlinedata', 'htmlmin']
files: ['html/index.html', 'config.json', 'locale/*.json'],
tasks: ['copy', 'inlinedata', 'htmlmin', 'json-minify']
},
sass: {
files: ['scss/**/*.scss'],
tasks: ['sasslint', 'sass', 'postcss']
tasks: ['sasslint', 'sass', 'postcss']
},
js: {
files: ['app.js', 'lib/**/*.js'],

View File

@ -926,7 +926,7 @@ error@^4.3.0:
string-template "~0.2.0"
xtend "~4.0.0"
es-abstract@^1.7.0:
es-abstract@^1.5.0, es-abstract@^1.7.0:
version "1.7.0"
resolved "https://registry.yarnpkg.com/es-abstract/-/es-abstract-1.7.0.tgz#dfade774e01bfcd97f96180298c449c8623fb94c"
dependencies:
@ -1286,6 +1286,12 @@ flat-cache@^1.2.1:
graceful-fs "^4.1.2"
write "^0.2.1"
for-each@^0.3.2:
version "0.3.2"
resolved "https://registry.yarnpkg.com/for-each/-/for-each-0.3.2.tgz#2c40450b9348e97f281322593ba96704b9abd4d4"
dependencies:
is-function "~1.0.0"
for-in@^0.1.5:
version "0.1.6"
resolved "https://registry.yarnpkg.com/for-in/-/for-in-0.1.6.tgz#c9f96e89bfad18a545af5ec3ed352a1d9e5b4dc8"
@ -1362,7 +1368,7 @@ fstream@^1.0.0, fstream@^1.0.2, fstream@~1.0.10:
mkdirp ">=0.5 0"
rimraf "2"
function-bind@^1.1.0:
function-bind@^1.0.2, function-bind@^1.1.0:
version "1.1.0"
resolved "https://registry.yarnpkg.com/function-bind/-/function-bind-1.1.0.tgz#16176714c801798e4e8f2cf7f7529467bb4a5771"
@ -1583,6 +1589,10 @@ grunt-inline@^0.3.6:
datauri "~0.2.0"
uglify-js "2.4.1"
grunt-json-minify@^1.1.0:
version "1.1.0"
resolved "https://registry.yarnpkg.com/grunt-json-minify/-/grunt-json-minify-1.1.0.tgz#a20b9a3016d1e8fed0c79560c77b94701f64f6fb"
grunt-known-options@~1.1.0:
version "1.1.0"
resolved "https://registry.yarnpkg.com/grunt-known-options/-/grunt-known-options-1.1.0.tgz#a4274eeb32fa765da5a7a3b1712617ce3b144149"
@ -1704,6 +1714,12 @@ has-unicode@^2.0.0:
version "2.0.1"
resolved "https://registry.yarnpkg.com/has-unicode/-/has-unicode-2.0.1.tgz#e0e6fe6a28cf51138855e086d1691e771de2a8b9"
has@^1.0.1:
version "1.0.1"
resolved "https://registry.yarnpkg.com/has/-/has-1.0.1.tgz#8461733f538b0837c9361e39a9ab9e9704dc2f28"
dependencies:
function-bind "^1.0.2"
hawk@~3.1.3:
version "3.1.3"
resolved "https://registry.yarnpkg.com/hawk/-/hawk-3.1.3.tgz#078444bd7c1640b0fe540d2c9b73d59678e8e1c4"
@ -1923,6 +1939,10 @@ is-fullwidth-code-point@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz#a3b30a5c4f199183167aaab93beefae3ddfb654f"
is-function@~1.0.0:
version "1.0.1"
resolved "https://registry.yarnpkg.com/is-function/-/is-function-1.0.1.tgz#12cfb98b65b57dd3d193a3121f5f6e2f437602b5"
is-glob@^2.0.0, is-glob@^2.0.1:
version "2.0.1"
resolved "https://registry.yarnpkg.com/is-glob/-/is-glob-2.0.1.tgz#d096f926a3ded5600f3fdfd91198cb0888c2d863"
@ -2473,6 +2493,15 @@ node-gyp@^3.3.1:
tar "^2.0.0"
which "1"
node-polyglot@airbnb/polyglot.js:
version "2.2.2"
resolved "https://codeload.github.com/airbnb/polyglot.js/tar.gz/eeae4dadf7c0f57bf3266e5370754765ec561037"
dependencies:
for-each "^0.3.2"
has "^1.0.1"
string.prototype.trim "^1.1.2"
warning "^3.0.0"
node-pre-gyp@^0.6.29:
version "0.6.32"
resolved "https://registry.yarnpkg.com/node-pre-gyp/-/node-pre-gyp-0.6.32.tgz#fc452b376e7319b3d255f5f34853ef6fd8fe1fd5"
@ -3314,6 +3343,14 @@ string-width@^2.0.0:
is-fullwidth-code-point "^2.0.0"
strip-ansi "^3.0.0"
string.prototype.trim@^1.1.2:
version "1.1.2"
resolved "https://registry.yarnpkg.com/string.prototype.trim/-/string.prototype.trim-1.1.2.tgz#d04de2c89e137f4d7d206f086b5ed2fae6be8cea"
dependencies:
define-properties "^1.1.2"
es-abstract "^1.5.0"
function-bind "^1.0.2"
string_decoder@~0.10.x:
version "0.10.31"
resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-0.10.31.tgz#62e203bc41766c6c28c9fc84301dab1c5310fa94"
@ -3350,11 +3387,11 @@ strip-indent@^1.0.1:
dependencies:
get-stdin "^4.0.1"
strip-json-comments@1.0.2:
strip-json-comments@1.0.2, strip-json-comments@~1.0.1:
version "1.0.2"
resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-1.0.2.tgz#5a48ab96023dbac1b7b8d0ffabf6f63f1677be9f"
strip-json-comments@~1.0.1, strip-json-comments@~1.0.4:
strip-json-comments@~1.0.4:
version "1.0.4"
resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-1.0.4.tgz#1e15fbcac97d3ee99bf2d73b4c656b082bbafb91"
@ -3593,6 +3630,12 @@ vlq@^0.2.1:
version "0.2.1"
resolved "https://registry.yarnpkg.com/vlq/-/vlq-0.2.1.tgz#14439d711891e682535467f8587c5630e4222a6c"
warning@^3.0.0:
version "3.0.0"
resolved "https://registry.yarnpkg.com/warning/-/warning-3.0.0.tgz#32e5377cb572de4ab04753bdf8821c01ed605b7c"
dependencies:
loose-envify "^1.0.0"
websocket-driver@>=0.5.1:
version "0.6.5"
resolved "https://registry.yarnpkg.com/websocket-driver/-/websocket-driver-0.6.5.tgz#5cb2556ceb85f4373c6d8238aa691c8454e13a36"