mirror of
https://github.com/pablorevilla-meshtastic/meshview.git
synced 2026-11-08 06:42:55 +01:00
more language changes
This commit is contained in:
1 parent
7227fa14f4
commit
de602e468d
5 files changed
+120
-7
No files matched your search
@@ -111,7 +111,11 @@
|
||||
"no": "No",
|
||||
"link_copied": "Link Copied!",
|
||||
"legend_traceroute": "Traceroute (with arrows)",
|
||||
"legend_neighbor": "Neighbor"
|
||||
"legend_neighbor": "Neighbor",
|
||||
"layer_nodes": "Nodes",
|
||||
"layer_packets_reach": "Packets Reach",
|
||||
"layer_traffic_heatmap": "Traffic HeatMap",
|
||||
"map_layers": "Map layers"
|
||||
},
|
||||
"stats": {
|
||||
"text_senders_title": "Top text senders · main channel",
|
||||
@@ -585,6 +589,7 @@
|
||||
"Main channel: {channel}": "Main channel: {channel}",
|
||||
"Main navigation": "Main navigation",
|
||||
"Map Report": "Map Report",
|
||||
"Map layers": "Map layers",
|
||||
"Map of highest routers and their altitudes": "Map of highest routers and their altitudes",
|
||||
"Map top 50": "Map top 50",
|
||||
"Mesh Nodes Population Heatmap": "Mesh Nodes Population Heatmap",
|
||||
@@ -677,6 +682,7 @@
|
||||
"Packet totals unavailable": "Packet totals unavailable",
|
||||
"Packet type": "Packet type",
|
||||
"Packet type channel": "Packet type channel",
|
||||
"Packets Reach": "Packets Reach",
|
||||
"Packets sent": "Packets sent",
|
||||
"Packets to review": "Packets to review",
|
||||
"Page {page} / {total}": "Page {page} / {total}",
|
||||
@@ -816,6 +822,7 @@
|
||||
"Traceroute": "Traceroute",
|
||||
"Traceroute edge legend": "Traceroute edge legend",
|
||||
"Traceroute not found.": "Traceroute not found.",
|
||||
"Traffic HeatMap": "Traffic HeatMap",
|
||||
"Traffic unavailable": "Traffic unavailable",
|
||||
"Type": "Type",
|
||||
"UNKNOWN APP": "UNKNOWN APP",
|
||||
|
||||
@@ -114,7 +114,11 @@
|
||||
"no": "No",
|
||||
"link_copied": "¡Enlace copiado!",
|
||||
"legend_traceroute": "Ruta de traceroute (flechas de dirección)",
|
||||
"legend_neighbor": "Vínculo de vecinos"
|
||||
"legend_neighbor": "Vínculo de vecinos",
|
||||
"layer_nodes": "Nodos",
|
||||
"layer_packets_reach": "Alcance de paquetes",
|
||||
"layer_traffic_heatmap": "Mapa de calor del tráfico",
|
||||
"map_layers": "Capas del mapa"
|
||||
},
|
||||
"stats": {
|
||||
"node_id": "ID de Nodo",
|
||||
@@ -593,6 +597,7 @@
|
||||
"Main channel: {channel}": "Canal principal: {channel}",
|
||||
"Main navigation": "Navegación principal",
|
||||
"Map Report": "Informe de mapa",
|
||||
"Map layers": "Capas del mapa",
|
||||
"Map of highest routers and their altitudes": "Mapa de los routers más altos y sus altitudes",
|
||||
"Map top 50": "Ver los 50 primeros en el mapa",
|
||||
"Mesh Nodes Population Heatmap": "Mapa de calor de densidad de nodos mesh",
|
||||
@@ -685,6 +690,7 @@
|
||||
"Packet totals unavailable": "Totales de paquetes no disponibles",
|
||||
"Packet type": "Tipo de paquete",
|
||||
"Packet type channel": "Canal para tipos de paquetes",
|
||||
"Packets Reach": "Alcance de paquetes",
|
||||
"Packets sent": "Paquetes enviados",
|
||||
"Packets to review": "Paquetes que revisar",
|
||||
"Page {page} / {total}": "Página {page} / {total}",
|
||||
@@ -824,6 +830,7 @@
|
||||
"Traceroute": "Trazado de ruta",
|
||||
"Traceroute edge legend": "Leyenda de enlaces de trazado de ruta",
|
||||
"Traceroute not found.": "No se encontró el trazado de ruta.",
|
||||
"Traffic HeatMap": "Mapa de calor del tráfico",
|
||||
"Traffic unavailable": "Tráfico no disponible",
|
||||
"Type": "Tipo",
|
||||
"UNKNOWN APP": "APLICACIÓN DESCONOCIDA",
|
||||
|
||||
@@ -111,7 +111,11 @@
|
||||
"no": "Нет",
|
||||
"link_copied": "Ссылка скопирована!",
|
||||
"legend_traceroute": "Трассировка (со стрелками)",
|
||||
"legend_neighbor": "Сосед"
|
||||
"legend_neighbor": "Сосед",
|
||||
"layer_nodes": "Ноды",
|
||||
"layer_packets_reach": "Достижимость пакетов",
|
||||
"layer_traffic_heatmap": "Тепловая карта трафика",
|
||||
"map_layers": "Слои карты"
|
||||
},
|
||||
"stats": {
|
||||
"text_senders_title": "Топ отправителей текста · основной канал",
|
||||
@@ -585,6 +589,7 @@
|
||||
"Main channel: {channel}": "Основной канал: {channel}",
|
||||
"Main navigation": "Основная навигация",
|
||||
"Map Report": "Отчёт карты",
|
||||
"Map layers": "Слои карты",
|
||||
"Map of highest routers and their altitudes": "Карта самых высоких роутеров и их высот",
|
||||
"Map top 50": "Топ-50 на карте",
|
||||
"Mesh Nodes Population Heatmap": "Тепловая карта плотности mesh-узлов",
|
||||
@@ -677,6 +682,7 @@
|
||||
"Packet totals unavailable": "Итоги пакетов недоступны",
|
||||
"Packet type": "Тип пакета",
|
||||
"Packet type channel": "Канал типов пакетов",
|
||||
"Packets Reach": "Достижимость пакетов",
|
||||
"Packets sent": "Отправлено пакетов",
|
||||
"Packets to review": "Пакетов для проверки",
|
||||
"Page {page} / {total}": "Страница {page} / {total}",
|
||||
@@ -816,6 +822,7 @@
|
||||
"Traceroute": "Трассировка",
|
||||
"Traceroute edge legend": "Легенда связей трассировки",
|
||||
"Traceroute not found.": "Трассировка не найдена.",
|
||||
"Traffic HeatMap": "Тепловая карта трафика",
|
||||
"Traffic unavailable": "Трафик недоступен",
|
||||
"Type": "Тип",
|
||||
"UNKNOWN APP": "НЕИЗВ-НОЕ ПРИЛОЖ",
|
||||
|
||||
@@ -356,6 +356,7 @@ async function loadTranslationsMap() {
|
||||
const res = await fetch(`/api/lang?lang=${lang}§ion=map`);
|
||||
mapTranslations = await res.json();
|
||||
applyTranslationsMap();
|
||||
updateMapLayerTranslations();
|
||||
} catch (err) {
|
||||
console.error("Map translation load failed:", err);
|
||||
}
|
||||
@@ -412,13 +413,33 @@ var trafficHeatLayer = L.heatLayer([], {
|
||||
}
|
||||
});
|
||||
var overlayLayers = {
|
||||
"Nodes": nodeLayer,
|
||||
"Packets Reach": packetSeenLayer,
|
||||
"Traffic HeatMap": trafficHeatLayer
|
||||
[MeshI18n.h("Nodes")]: nodeLayer,
|
||||
[MeshI18n.h("Packets Reach")]: packetSeenLayer,
|
||||
[MeshI18n.h("Traffic HeatMap")]: trafficHeatLayer
|
||||
};
|
||||
L.control.layers(baseLayers, overlayLayers, {
|
||||
var mapLayerControl = L.control.layers(baseLayers, overlayLayers, {
|
||||
collapsed: window.matchMedia("(max-width: 700px)").matches
|
||||
}).addTo(map);
|
||||
const layerToggle = mapLayerControl.getContainer().querySelector('.leaflet-control-layers-toggle');
|
||||
layerToggle.title = MeshI18n.t("Map layers");
|
||||
layerToggle.setAttribute('aria-label', MeshI18n.t("Map layers"));
|
||||
|
||||
function updateMapLayerTranslations() {
|
||||
// The page catalog refreshes independently of the server's startup UI catalog.
|
||||
const layers = [
|
||||
[nodeLayer, mapTranslations.layer_nodes || MeshI18n.t("Nodes")],
|
||||
[packetSeenLayer, mapTranslations.layer_packets_reach || MeshI18n.t("Packets Reach")],
|
||||
[trafficHeatLayer, mapTranslations.layer_traffic_heatmap || MeshI18n.t("Traffic HeatMap")]
|
||||
];
|
||||
layers.forEach(([layer, label]) => {
|
||||
// Removing a layer from the control leaves its visibility on the map intact.
|
||||
mapLayerControl.removeLayer(layer);
|
||||
mapLayerControl.addOverlay(layer, escapeHtml(label));
|
||||
});
|
||||
const title = mapTranslations.map_layers || MeshI18n.t("Map layers");
|
||||
layerToggle.title = title;
|
||||
layerToggle.setAttribute('aria-label', title);
|
||||
}
|
||||
|
||||
// Data structures
|
||||
var nodes = [], markers = {}, markerById = {}, nodeMap = new Map();
|
||||
|
||||
@@ -79,3 +79,74 @@ test('failed static catalog request leaves English copy available', async () =>
|
||||
await api.ready;
|
||||
assert.equal(api.t('Search'),'Search');
|
||||
});
|
||||
|
||||
test('map layer registration translates labels while preserving providers and overlay objects', () => {
|
||||
const path = require('node:path');
|
||||
const template = fs.readFileSync(path.join(__dirname, '../meshview/templates/map-view.html'), 'utf8');
|
||||
const registration = template.slice(template.indexOf('var overlayLayers ='), template.indexOf('// Data structures'));
|
||||
for (const language of ['en', 'es', 'ru']) {
|
||||
const catalog = JSON.parse(fs.readFileSync(path.join(__dirname, `../meshview/lang/${language}.json`))).ui;
|
||||
const {api} = boot({catalog, language});
|
||||
const nodeLayer = {}, packetSeenLayer = {}, trafficHeatLayer = {};
|
||||
const baseLayers = {OpenStreetMap: {}, OpenTopoMap: {}};
|
||||
const toggle = {setAttribute(key, value) {this[key] = value;}};
|
||||
let registered;
|
||||
vm.runInNewContext(registration, {
|
||||
MeshI18n: api, nodeLayer, packetSeenLayer, trafficHeatLayer, baseLayers, map: {},
|
||||
window: {matchMedia: () => ({matches: false})},
|
||||
L: {control: {layers(bases, overlays, options) {
|
||||
registered = {bases, overlays, options};
|
||||
return {addTo: () => ({getContainer: () => ({querySelector: () => toggle})})};
|
||||
}}}
|
||||
});
|
||||
assert.equal(registered.bases, baseLayers);
|
||||
assert.deepEqual(Object.keys(registered.overlays), [catalog.Nodes, catalog['Packets Reach'], catalog['Traffic HeatMap']]);
|
||||
assert.equal(registered.overlays[catalog.Nodes], nodeLayer);
|
||||
assert.equal(registered.overlays[catalog['Packets Reach']], packetSeenLayer);
|
||||
assert.equal(registered.overlays[catalog['Traffic HeatMap']], trafficHeatLayer);
|
||||
assert.equal(toggle.title, catalog['Map layers']);
|
||||
assert.equal(toggle['aria-label'], catalog['Map layers']);
|
||||
}
|
||||
});
|
||||
|
||||
test('fresh map catalog replaces layer labels missing from the startup catalog', async () => {
|
||||
const path = require('node:path');
|
||||
const template = fs.readFileSync(path.join(__dirname, '../meshview/templates/map-view.html'), 'utf8');
|
||||
const loader = template.slice(template.indexOf('let mapTranslations = {};'), template.indexOf('var map = L.map'));
|
||||
const registration = template.slice(template.indexOf('var overlayLayers ='), template.indexOf('// Data structures'));
|
||||
for (const language of ['en', 'es', 'ru']) {
|
||||
const catalog = JSON.parse(fs.readFileSync(path.join(__dirname, `../meshview/lang/${language}.json`)));
|
||||
const {api} = boot({catalog: {Nodes: catalog.ui.Nodes}, language});
|
||||
const nodeLayer = {}, packetSeenLayer = {}, trafficHeatLayer = {};
|
||||
const overlays = new Map();
|
||||
const toggle = {setAttribute(key, value) {this[key] = value;}};
|
||||
const control = {
|
||||
addTo() {return this;},
|
||||
getContainer: () => ({querySelector: () => toggle}),
|
||||
removeLayer(layer) {overlays.delete(layer);},
|
||||
addOverlay(layer, label) {overlays.set(layer, label);}
|
||||
};
|
||||
const window = {matchMedia: () => ({matches: false}), _siteConfigPromise: Promise.resolve({site:{language}})};
|
||||
const context = vm.createContext({
|
||||
MeshI18n: api, nodeLayer, packetSeenLayer, trafficHeatLayer, baseLayers: {}, map: {}, window,
|
||||
document: {querySelectorAll: () => []},
|
||||
fetch: async () => ({json: async () => catalog.map}),
|
||||
console,
|
||||
L: {control: {layers(bases, labels) {
|
||||
for (const [label, layer] of Object.entries(labels)) overlays.set(layer, label);
|
||||
return control;
|
||||
}}}
|
||||
});
|
||||
vm.runInContext(fs.readFileSync(path.join(__dirname, '../meshview/static/html-escape.js'), 'utf8'), context);
|
||||
context.escapeHtml = window.escapeHtml;
|
||||
vm.runInContext(loader + registration, context);
|
||||
assert.equal(overlays.get(packetSeenLayer), 'Packets Reach');
|
||||
assert.equal(overlays.get(trafficHeatLayer), 'Traffic HeatMap');
|
||||
await vm.runInContext('loadTranslationsMap()', context);
|
||||
assert.equal(overlays.size, 3);
|
||||
assert.equal(overlays.get(nodeLayer), catalog.ui.Nodes);
|
||||
assert.equal(overlays.get(packetSeenLayer), catalog.ui['Packets Reach']);
|
||||
assert.equal(overlays.get(trafficHeatLayer), catalog.ui['Traffic HeatMap']);
|
||||
assert.equal(toggle.title, catalog.ui['Map layers']);
|
||||
}
|
||||
});
|
||||
Reference in new issue
Block a user