diff --git a/.gitignore b/.gitignore index 6509d25e78..205eac9833 100644 --- a/.gitignore +++ b/.gitignore @@ -12,3 +12,5 @@ web/node_modules *.suo *.user /.vs/config +/App_Data/PublishProfiles +*.publishproj diff --git a/docker-compose.yml b/docker-compose.yml index ef235b937f..cf59d1931c 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -19,6 +19,7 @@ services: - './web/server.js:/home/web/server.js' - './web/views:/home/web/views' - './web/webpack.config.js:/home/web/webpack.config.js' + - './web/locales:/home/web/locales' feeder: build: context: . diff --git a/web/app/configs/lang.json b/web/app/configs/lang.json new file mode 100644 index 0000000000..0ddf3d9e70 --- /dev/null +++ b/web/app/configs/lang.json @@ -0,0 +1,128 @@ +{ + "en": { + "wind": "wind", + "solar": "solar", + "hydro": "hydro", + "hydro storage": "hydro storage", + "biomass": "biomass", + "nuclear": "nuclear", + "geothermal": "geothermal", + "gas": "gas", + "coal": "coal", + "oil": "oil", + "unknown": "unknown", + "exportto": "of export to", + "importfrom": "of import from", + "productionimport": "production/import", + "consumption": "consumption", + "electricityfrom": "electricity comes from", + "electricityto": "electricity is exported to", + "electricitystored": "electricity is stored using", + "zoneShortName": { + "AL": "Albania", + "AT": "Austria", + "BA": "Bosnia and Herzegovina", + "BE": "Belgium", + "BG": "Bulgaria", + "BY": "Belarus", + "CH": "Switzerland", + "CZ": "Czechia", + "DE": "Germany", + "DK": "Denmark", + "EE": "Estonia", + "ES": "Spain", + "FI": "Finland", + "FR": "France", + "GB": "Great Britain", + "GB-NIR": "Northern Ireland", + "GR": "Greece", + "HR": "Croatia", + "HU": "Hungary", + "IE": "Ireland", + "IS": "Iceland", + "IT": "Italy", + "LT": "Lithuania", + "LU": "Luxembourg", + "LV": "Latvia", + "MD": "Moldova", + "ME": "Montenegro", + "MK": "Rep. of Macedonia (FYROM)", + "MT": "Malta", + "NL": "Netherlands", + "NO": "Norway", + "PL": "Poland", + "PT": "Portugal", + "RO": "Romania", + "RS": "Serbia", + "RU": "Russia", + "SE": "Sweden", + "SI": "Slovenia", + "SK": "Slovakia", + "TR": "Turkey", + "UA": "Ukraine" + } + }, + "fr": { + "wind": "éolien", + "solar": "solaire", + "hydro": "hydro", + "hydro storage": "stockage hydro", + "biomass": "biomasse", + "nuclear": "nucléaire", + "geothermal": "géothermie", + "gas": "gaz", + "coal": "charbon", + "oil": "fioul", + "unknown": "inconnu", + "exportto": "exportée vers", + "importfrom": " importée depuis", + "productionimport": "production/import", + "consumption": "consommation", + "electricityfrom": "est produite par", + "electricityto": "est exportée vers", + "electricitystored": "est stockée par", + "zoneShortName": { + "AL": "Albanie", + "AT": "Autriche", + "BA": "Bosnie-Herzégovine", + "BE": "Belgique", + "BG": "Bulgarie", + "BY": "Biélorussie", + "CH": "Suisse", + "CZ": "République tchèque", + "DE": "Allemagne", + "DK": "Danemark", + "EE": "Estonie", + "ES": "Espagne", + "FI": "Finlande", + "FR": "France", + "GB": "Grande-Bretagne", + "GB-NIR": "Irlande du Nord", + "GR": "Grèce", + "HR": "Croatie", + "HU": "Hongrie", + "IE": "Irlande", + "IS": "Islande", + "IT": "Italie", + "LT": "Lituanie", + "LU": "Luxembourg", + "LV": "Lettonie", + "MD": "Moldavie", + "ME": "Monténégro", + "MK": "Rép. de Macédoine (FYROM)", + "MT": "Malte", + "NL": "Pays-Bas", + "NO": "Norvège", + "PL": "Pologne", + "PT": "Portugal", + "RO": "Roumanie", + "RS": "Serbie", + "RU": "Russie", + "SE": "Suède", + "SI": "Slovénie", + "SK": "Slovaquie", + "TR": "Turquie", + "UA": "Ukraine" + } + } +} diff --git a/web/app/configs/zones.json b/web/app/configs/zones.json index 7ac5f3572b..eab47d9d45 100644 --- a/web/app/configs/zones.json +++ b/web/app/configs/zones.json @@ -1,163 +1,126 @@ { "AL": { - "country_iso_a2": "AL", - "shortname": "Albania" + "country_iso_a2": "AL" }, "AT": { - "country_iso_a2": "AT", - "shortname": "Austria" + "country_iso_a2": "AT" }, "BA": { - "country_iso_a2": "BA", - "shortname": "Bosnia and Herzegovina" + "country_iso_a2": "BA" }, "BE": { - "country_iso_a2": "BE", - "shortname": "Belgium" + "country_iso_a2": "BE" }, "BG": { - "country_iso_a2": "BG", - "shortname": "Bulgaria" + "country_iso_a2": "BG" }, "BY": { - "country_iso_a2": "BY", - "shortname": "Belarus" + "country_iso_a2": "BY" }, "CH": { - "country_iso_a2": "CH", - "shortname": "Switzerland" + "country_iso_a2": "CH" }, "CZ": { - "country_iso_a2": "CZ", - "shortname": "Czechia" + "country_iso_a2": "CZ" }, "DE": { - "country_iso_a2": "DE", - "shortname": "Germany" + "country_iso_a2": "DE" }, "DK": { - "country_iso_a2": "DK", - "shortname": "Denmark" + "country_iso_a2": "DK" }, "EE": { - "country_iso_a2": "EE", - "shortname": "Estonia" + "country_iso_a2": "EE" }, "ES": { - "country_iso_a2": "ES", - "shortname": "Spain" + "country_iso_a2": "ES" }, "FI": { - "country_iso_a2": "FI", - "shortname": "Finland" + "country_iso_a2": "FI" }, "FR": { - "country_iso_a2": "FR", - "shortname": "France" + "country_iso_a2": "FR" }, "GB": { "country_iso_a2": "GB", - "shortname": "Great Britain", "_comment": "Key should be GB-GBN (see https://en.wikipedia.org/wiki/ISO_3166-2:GB)" }, "GB-NIR": { - "country_iso_a2": "GB", - "shortname": "Northern Ireland" + "country_iso_a2": "GB" }, "GR": { - "country_iso_a2": "GR", - "shortname": "Greece" + "country_iso_a2": "GR" }, "HR": { - "country_iso_a2": "HR", - "shortname": "Croatia" + "country_iso_a2": "HR" }, "HU": { - "country_iso_a2": "HU", - "shortname": "Hungary" + "country_iso_a2": "HU" }, "IE": { - "country_iso_a2": "IE", - "shortname": "Ireland" + "country_iso_a2": "IE" + }, + "IS": { + "country_iso_a2": "IS" }, "IT": { - "country_iso_a2": "IT", - "shortname": "Italy" + "country_iso_a2": "IT" }, "LT": { - "country_iso_a2": "LT", - "shortname": "Lithuania" + "country_iso_a2": "LT" }, "LU": { - "country_iso_a2": "LU", - "shortname": "Luxembourg" + "country_iso_a2": "LU" }, "LV": { - "country_iso_a2": "LV", - "shortname": "Latvia" + "country_iso_a2": "LV" + }, + "MD": { + "country_iso_a2": "MD" + }, + "ME": { + "country_iso_a2": "ME" }, "MK": { - "country_iso_a2": "MK", - "shortname": "Rep. of Macedonia (FYROM)" + "country_iso_a2": "MK" }, "MT": { + "country_iso_a2": "MT" }, "NL": { - "country_iso_a2": "NL", - "shortname": "Netherlands" + "country_iso_a2": "NL" }, "NO": { - "country_iso_a2": "NO", - "shortname": "Norway" + "country_iso_a2": "NO" }, "PL": { - "country_iso_a2": "PL", - "shortname": "Poland" + "country_iso_a2": "PL" }, "PT": { - "country_iso_a2": "PT", - "shortname": "Portugal" + "country_iso_a2": "PT" }, "RO": { - "country_iso_a2": "RO", - "shortname": "Romania" + "country_iso_a2": "RO" }, "RS": { - "country_iso_a2": "RS", - "shortname": "Serbia" + "country_iso_a2": "RS" }, "RU": { - "country_iso_a2": "RU", - "shortname": "Russia" + "country_iso_a2": "RU" }, "SE": { - "country_iso_a2": "SE", - "shortname": "Sweden" + "country_iso_a2": "SE" }, "SI": { - "country_iso_a2": "SI", - "shortname": "Slovenia" + "country_iso_a2": "SI" }, "SK": { - "country_iso_a2": "SK", - "shortname": "Slovakia" + "country_iso_a2": "SK" }, "TR": { - }, - "IS": { - "country_iso_a2": "IS", - "shortname": "Iceland" - }, - "MD": { - "country_iso_a2": "MD", - "shortname": "Moldova" - }, - "ME": { - "country_iso_a2": "ME", - "shortname": "Montenegro" + "country_iso_a2": "TR" }, "UA": { - "country_iso_a2": "UA", - "shortname": "Ukraine" + "country_iso_a2": "UA" } } diff --git a/web/app/countrytable.js b/web/app/countrytable.js index f151f30b0e..e9ddaf8beb 100644 --- a/web/app/countrytable.js +++ b/web/app/countrytable.js @@ -1,4 +1,5 @@ var d3 = require('d3'); +var lang = require('json-loader!./configs/lang.json')[locale]; var moment = require('moment'); // TODO: @@ -55,7 +56,7 @@ function CountryTable(selector, co2Color, modeColor, modeOrder) { return 'translate(0,' + (i * (that.ROW_HEIGHT + that.PADDING_Y)) + ')'; }); gNewRow.append('text') - .text(function(d) { return d.mode }) + .text(function(d) { return lang[d.mode] || d.mode }) .attr('transform', 'translate(0, ' + this.TEXT_ADJUST_Y + ')'); gNewRow.append('rect') .attr('class', 'capacity') @@ -368,7 +369,6 @@ CountryTable.prototype.resize = function() { CountryTable.prototype.data = function(arg) { var that = this; - if (!arg) return this._data; this._data = arg; @@ -393,6 +393,7 @@ CountryTable.prototype.data = function(arg) { isStorage: d.isStorage, capacity: capacity, mode: d.mode, + text: lang[d.mode], gCo2eqPerkWh: footprint, gCo2eqPerH: footprint * 1000.0 * Math.max(production, 0) }; diff --git a/web/app/main.js b/web/app/main.js index cac4619ee1..899985c9a2 100644 --- a/web/app/main.js +++ b/web/app/main.js @@ -15,6 +15,7 @@ var ExchangeConfig = require('./exchangeconfig'); var ExchangeLayer = require('./exchangelayer'); var grib = require('./grib'); var HorizontalColorbar = require('./horizontalcolorbar'); +var lang = require('json-loader!./configs/lang.json')[locale]; var LoadingService = require('./loadingservice'); var Solar = require('./solar'); var Tooltip = require('./tooltip'); @@ -132,7 +133,6 @@ function trackAnalyticsEvent(eventName, paramObj) { } // Set proper locale -var locale = window.navigator.userLanguage || window.navigator.language; moment.locale(locale); // Display embedded warning @@ -218,9 +218,10 @@ var countryHistoryGraph = new LineGraph('.country-history', function(d) { return d.co2intensity != null; }, co2color); -var co2Colorbar = new HorizontalColorbar('.co2-colorbar', co2color) - .markerColor('white') - .render(); // Already render because the size is fixed +if (!isSmallScreen()) + var co2Colorbar = new HorizontalColorbar('.co2-colorbar', co2color) + .markerColor('white') + .render(); var windColorbar = new HorizontalColorbar('.wind-colorbar', windColor) .markerColor('black'); d3.select('.wind-colorbar').style('display', windEnabled ? 'block': 'none'); @@ -294,6 +295,8 @@ d3.entries(zones).forEach(function(d) { var zone = countries[d.key]; d3.entries(d.value).forEach(function(o) { zone[o.key] = o.value; }); zone.maxCapacity = d3.max(d3.values(zone.capacity)); + // Add translation + zone.shortname = lang.zoneShortName[d.key]; }); // Add capacities d3.entries(capacities).forEach(function(d) { @@ -510,7 +513,7 @@ function dataLoaded(err, clientVersion, state, argSolar, argWind) { // Is there a new version? d3.select('#new-version') - .style('top', (clientVersion === bundleHash) ? undefined : 0); + .style('top', (clientVersion === bundleHash || forceRemoteEndpoint) ? undefined : 0); currentMoment = (customDate && moment(customDate) || moment()); d3.select('#current-date').text(currentMoment.format('LL')); @@ -631,7 +634,7 @@ function dataLoaded(err, clientVersion, state, argSolar, argWind) { d3.select(this) .style('opacity', 0.8) .style('cursor', 'pointer') - if (d.co2intensity) + if (d.co2intensity && co2Colorbar) co2Colorbar.currentMarker(d.co2intensity); var tooltip = d3.select('#country-tooltip'); tooltip.style('display', 'inline'); @@ -666,7 +669,7 @@ function dataLoaded(err, clientVersion, state, argSolar, argWind) { d3.select(this) .style('opacity', 1) .style('cursor', 'auto') - if (d.co2intensity) + if (d.co2intensity && co2Colorbar) co2Colorbar.currentMarker(undefined); d3.select('#country-tooltip') .style('display', 'none'); @@ -700,7 +703,7 @@ function dataLoaded(err, clientVersion, state, argSolar, argWind) { d3.select(this) .style('opacity', 0.8) .style('cursor', 'pointer'); - if (d.co2intensity) + if (d.co2intensity && co2Colorbar) co2Colorbar.currentMarker(d.co2intensity); var tooltip = d3.select('#exchange-tooltip'); tooltip.style('display', 'inline'); @@ -727,7 +730,7 @@ function dataLoaded(err, clientVersion, state, argSolar, argWind) { d3.select(this) .style('opacity', 1) .style('cursor', 'auto') - if (d.co2intensity) + if (d.co2intensity && co2Colorbar) co2Colorbar.currentMarker(undefined); d3.select('#exchange-tooltip') .style('display', 'none'); @@ -926,6 +929,7 @@ function redraw() { } if (!isSmallScreen()) { countryMap.render(); + co2Colorbar.render(); exchangeLayer .projection(countryMap.projection()) .render(); diff --git a/web/app/tooltip.js b/web/app/tooltip.js index d290502c13..bd0db7e3d9 100644 --- a/web/app/tooltip.js +++ b/web/app/tooltip.js @@ -1,6 +1,7 @@ exports = module.exports = {}; var d3 = require('d3'); +var lang = require('json-loader!./configs/lang.json')[locale]; // Create power formatting function formatPower(d, numDigits) { @@ -22,10 +23,10 @@ exports.setupCountryTable = function (countryTable, countries, co2Colorbar, co2c var o = d.value < 0 ? countryCode : d.key; var country = countries[countryCode]; var co2intensity = countries[o].co2intensity; - co2Colorbar.currentMarker(co2intensity); + if (co2Colorbar) co2Colorbar.currentMarker(co2intensity); var tooltip = d3.select('#countrypanel-exchange-tooltip'); tooltip.style('display', 'inline'); - tooltip.select('#label').text(isExport ? 'export to' : 'import from'); + tooltip.select('#label').text(isExport ? lang['exportto'] : lang['importfrom']); tooltip.select('#country-code').text(d.key); tooltip.select('.emission-rect') .style('background-color', co2intensity ? co2color(co2intensity) : 'gray'); @@ -38,8 +39,8 @@ exports.setupCountryTable = function (countryTable, countries, co2Colorbar, co2c var totalConsumption = getConsumption(country); var totalPositive = country.totalProduction + country.totalImport; - var domain = isExport ? totalPositive : totalPositive; - var domainName = !isExport ? 'electricity comes from' : 'electricity is exported to'; + var domain = isExport ? totalPositive : totalConsumption; + var domainName = isExport ? lang['electricityfrom'] : lang['electricityto']; var isNull = !isFinite(d.value) || d.value == undefined; var absFlow = Math.abs(d.value); @@ -64,7 +65,7 @@ exports.setupCountryTable = function (countryTable, countries, co2Colorbar, co2c .attr('class', 'country-exchange-source-flag flag-icon flag-icon-' + o.toLowerCase()); }) .onExchangeMouseOut(function (d) { - co2Colorbar.currentMarker(undefined); + if (co2Colorbar) co2Colorbar.currentMarker(undefined); d3.select('#countrypanel-exchange-tooltip') .style('display', 'none'); }) @@ -79,10 +80,10 @@ exports.setupCountryTable = function (countryTable, countries, co2Colorbar, co2c .onProductionMouseOver(function (d, countryCode) { var country = countries[countryCode]; var co2intensity = country.productionCo2Intensities[d.mode]; - co2Colorbar.currentMarker(co2intensity); + if (co2Colorbar) co2Colorbar.currentMarker(co2intensity); var tooltip = d3.select('#countrypanel-production-tooltip'); tooltip.style('display', 'inline'); - tooltip.selectAll('#mode').text(d.mode); + tooltip.selectAll('#mode').text(d.text || d.mode); tooltip.select('.emission-rect') .style('background-color', co2intensity ? co2color(co2intensity) : 'gray'); tooltip.select('.emission-intensity') @@ -98,7 +99,9 @@ exports.setupCountryTable = function (countryTable, countries, co2Colorbar, co2c var value = d.isStorage ? d.storage : d.production; var domain = d.isStorage ? totalPositive : totalPositive; - var domainName = d.isStorage ? ('electricity is stored using ' + d.mode) : ('electricity comes from ' + d.mode); + var domainName = d.isStorage ? + (lang['electricitystored'] + ' ' + (d.text || d.mode)) : + (lang['electricityfrom'] + ' ' + (d.text || d.mode)); var isNull = !isFinite(value) || value == undefined; var productionProportion = !isNull ? Math.round(value / domain * 100) : '?'; @@ -108,7 +111,7 @@ exports.setupCountryTable = function (countryTable, countries, co2Colorbar, co2c (!isNull ? formatPower(value) : '?') + ' ' + ' / ' + (!isNull ? formatPower(domain) : '?')); - tooltip.select('#domain-name').text(domainName); + tooltip.selectAll('#domain-name').text(domainName); tooltip.select('.country-code') .text(countryCode) @@ -125,7 +128,7 @@ exports.setupCountryTable = function (countryTable, countries, co2Colorbar, co2c ')'); }) .onProductionMouseOut(function (d) { - co2Colorbar.currentMarker(undefined); + if (co2Colorbar) co2Colorbar.currentMarker(undefined); d3.select('#countrypanel-production-tooltip') .style('display', 'none'); }); diff --git a/web/locales/en.json b/web/locales/en.json new file mode 100644 index 0000000000..91768d89f3 --- /dev/null +++ b/web/locales/en.json @@ -0,0 +1,70 @@ +{ + "panel-initial-text": { + "youareseeing": "You're currently seeing a ", + "limitedversion": "limited mobile version", + "forthe": "For the", + "fullversion": "full experience", + "visitoncomputer": "visit this page on your computer", + + "thisshowsrealtime": "This shows in real-time", + "whereelectricitycomesfrom": "where your electricity comes from", + "and": "and", + "howmuchco2": "how much CO2", + "wasemitted": "was emitted to produce it", + + "takeinaccount": "We take into account electricity", + "importexport": "imports and exports", + "betweencountries": "between countries", + + "thisproject": "This project is", + "opensource": "Open Source", + "see": "see", + "datasources": "data sources", + + "tip": "Tip: Click on a country to start exploring" + }, + "country-panel": { + "back": "back", + "source": "source", + "fossilfuels": "fossil fuels", + "electricityprice": "Electricity price (day-ahead)", + "electricityproduction": "Electricity production", + "showemissions": "show emissions", + "bysource": "by source", + "emissions": "Emissions", + "showelectricity": "show electricity", + "co2signal": "Our CO2 Signal helps devices and electric vehicles consume electricity at the right time, especially when crossing borders." + }, + "country-history": { + "carbonintensity24h": "Carbon intensity in the last 24 hours" + }, + "footer": { + "likethisvisu": "Like the visualization?", + "loveyourfeedback": "We'd love your feedback", + "foundbugs": "Found bugs or have ideas? Report them", + "here": "here" + }, + "legends": { + "windpotential": "Wind power potential", + "solarpotential": "Solar power potential" + }, + "tooltips": { + "electricityprice": "Electricity price (day-ahead)", + "fossilfuels": "fossil fuels", + "crossborderexport": "Cross-border export", + "carbonintensityexport": "Carbon intensity of export", + "of": "of", + "ofconsumption": "of", + "ofinstalled": "of installed capacity", + "ofelectricity": "of", + "utilizing": "utilizing", + "withcarbonintensity": "with a carbon intensity of" + }, + "misc": { + "carbonintensity": "Carbon intensity", + "maintitle": "Live CO2 emissions of the European electricity consumption", + "lastupdate": "Last update", + "oops": "Oops! We are having trouble reaching the server. We will try again in a few seconds.", + "newversion": "A new version is available! Press here to reload." + } +} diff --git a/web/locales/fr.json b/web/locales/fr.json new file mode 100644 index 0000000000..133f734233 --- /dev/null +++ b/web/locales/fr.json @@ -0,0 +1,71 @@ +{ + "panel-initial-text": { + "youareseeing": "Vous voyez actuellement", + "limitedversion": "une version mobile limitée", + "forthe": "Pour une", + "fullversion": "meilleure experience", + "visitoncomputer": "visitez ce site depuis un ordinateur", + + "thisshowsrealtime": "Cette carte indique", + "whereelectricitycomesfrom": "d'où provient votre électricité", + "and": "et", + "howmuchco2": "quelle quantité de CO2", + "wasemitted": "a été émise pour la produire", + + "takeinaccount": "Elle prend en compte les", + "importexport": "imports et exports", + "betweencountries": "entre pays", + + "thisproject": "Ceci est un projet", + "opensource": "Open Source", + "see": "voir", + "datasources": "source des données", + + "tip": "Cliquez sur un pays pour explorer" + }, + "country-panel": { + "back": "retour", + "source": "sources", + "fossilfuels": "énergies fossiles", + "electricityprice": "Prix de l'électricité (day-ahead)", + "electricityproduction": "Production électrique", + "showemissions": "montrer les émissions", + "bysource": "par source", + "emissions": "Emissions", + "showelectricity": "montrer la production électrique", + "co2signal": "Notre CO2 Signal permet aux appareils et voitures électriques de consommer au bon moment." + }, + "country-history": { + "carbonintensity24h": "Intensité carbone des 24 dernières heures" + }, + "footer": { + "likethisvisu": "Vous aimez cette visualisation?", + "loveyourfeedback": "Envoyez-nous vos commentaires", + "foundbugs": "Un bug ? Une idée ? Cliquez", + "here": "ici" + }, + "legends": { + "windpotential": "Potentiel éolien", + "solarpotential": "Potentiel solaire" + }, + "tooltips": { + "electricityprice": "Prix de l'électricité (day-ahead)", + "fossilfuels": "énergies fossiles", + "crossborderexport": "Export transfrontalier", + "carbonintensityexport": "Intensité carbone de l'export", + "of": "de l'électricité", + "ofconsumption": "de la consomation", + "ofinstalled": "de la capacité installée", + "ofelectricity": "de l'électricité", + "capacity": " ", + "utilizing": "utilisant", + "withcarbonintensity": "avec une intensité carbone de" + }, + "misc": { + "carbonintensity": "Intensité carbone", + "maintitle": "Emissions de CO2 de la production électrique Européenne en temps réel", + "lastupdate": "Dernière mise à jour", + "oops": "Oops! Nous rencontrons un problème de connectivité au serveur. Une nouvelle tentative aura lieu dans quelques instants.", + "newversion": "Une nouvelle version est disponible! Appuyez ici pour la charger." + } +} diff --git a/web/package.json b/web/package.json index 5e9bcdc272..b9390dd80a 100644 --- a/web/package.json +++ b/web/package.json @@ -17,7 +17,8 @@ "mongodb": "^2.2.9", "opbeat": "^3.21.0", "snappy": "^5.0.5", - "topojson": "^2.2.0" + "topojson": "^2.2.0", + "i18n":"^0.8.3" }, "devDependencies": { "nodemon": "^1.10.2", @@ -32,7 +33,7 @@ "build-debug": "BUILD=debug npm run build", "build-release": "BUILD=release npm run build", "clean": "mkdir -p public/dist && rm public/dist/bundle.*.js", - "server-dev": "nodemon server.js", + "server-dev": "nodemon --watch ../shared --watch . server.js", "watch": "BUILD=debug webpack --watch --progress" } } diff --git a/web/public/css/styles.css b/web/public/css/styles.css index 91b4681c02..1084a40893 100644 --- a/web/public/css/styles.css +++ b/web/public/css/styles.css @@ -42,7 +42,7 @@ hr { left: 0; transition: all 0.5s ease; width: 100%; - top: -60px; /* hidden state */ + top: -70px; /* hidden state */ } .flash-message .inner { display: block; diff --git a/web/server.js b/web/server.js index f34181b518..4e8edd7d67 100644 --- a/web/server.js +++ b/web/server.js @@ -20,6 +20,8 @@ var http = require('http'); var Memcached = require('memcached'); var moment = require('moment'); var MongoClient = require('mongodb').MongoClient; +var i18n = require('i18n'); + //var statsd = require('node-statsd'); // TODO: Remove // Custom modules @@ -42,6 +44,24 @@ app.use(function(req, res, next) { var STATIC_PATH = process.env['STATIC_PATH'] || (__dirname + '/public'); app.use(express.static(STATIC_PATH, {etag: true, maxAge: isProduction ? '24h': '0'})); app.set('view engine', 'ejs'); + +// * i18n +i18n.configure({ + // where to store json files - defaults to './locales' relative to modules directory + locales: ['en', 'fr'], + directory: __dirname + '/locales', + defaultLocale: 'en', + queryParameter: 'lang', + objectNotation: true, + updateFiles: false // whether to write new locale information to disk - defaults to true +}); +app.use(i18n.init); +FB_LOCALES = { + 'en': 'en_US', + 'fr': 'fr_FR' +}; + +// * Long-term caching var BUNDLE_HASH = !isProduction ? 'dev' : JSON.parse(fs.readFileSync(STATIC_PATH + '/dist/manifest.json')).hash; @@ -402,8 +422,20 @@ app.get('/', function(req, res) { // Redirect res.redirect(301, 'http://www.electricitymap.org' + req.path); } else { + // Set locale if facebook requests it + if (req.query.fb_locale) { + // Locales are formatted according to + // https://developers.facebook.com/docs/internationalization/#locales + lr = req.query.fb_locale.split('_', 2); + res.setLocale(lr[0]); + } res.render('pages/index', { - 'bundleHash': BUNDLE_HASH, + bundleHash: BUNDLE_HASH, + locale: res.locale, + FBLocale: FB_LOCALES[res.locale], + supportedFBLocales: i18n.getLocales() + .map(function(d) { return FB_LOCALES[d] }) + .filter(function(d) { return d }), useAnalytics: req.get('host').indexOf('electricitymap') != -1 }); } diff --git a/web/views/pages/index.ejs b/web/views/pages/index.ejs index eb80303e9e..4df70ffc2b 100644 --- a/web/views/pages/index.ejs +++ b/web/views/pages/index.ejs @@ -5,10 +5,14 @@ - + + + <% for(var i=0; i + + <% } %> @@ -18,7 +22,7 @@ - Electricity Map | Live CO2 emissions of the European electricity consumption + Electricity Map | <%= __('misc.maintitle') %> @@ -29,6 +33,7 @@ <% if (useAnalytics) { %> @@ -90,27 +95,28 @@ var js, fjs = d.getElementsByTagName(s)[0]; if (d.getElementById(id)) {return;} js = d.createElement(s); js.id = id; - js.src = "//connect.facebook.net/en_US/sdk.js"; + js.src = "//connect.facebook.net/<%= FBLocale %>/sdk.js"; fjs.parentNode.insertBefore(js, fjs); }(document, 'script', 'facebook-jssdk'));
-
Oops! We're having trouble reaching the server. We'll try again in a few seconds.
+
<%= __('misc.oops') %>
-
A new version is available! Press here to reload.
+
<%- __('misc.newversion') %>
+

- Live CO2 emissions of the European electricity consumption + <%= __('misc.maintitle') %>

- Last update: + <%= __('misc.lastupdate') %>:
@@ -121,7 +127,7 @@

- +



- Carbon intensity in the last 24 hours: + <%= __('country-history.carbonintensity24h') %>:

- Our CO2 Signal helps devices and electric vehicles consume electricity at the right time, especially when crossing borders. + <%- __('country-panel.co2signal') %>

- You're currently seeing a limited mobile version. For the full experience, visit this page on your computer. + <%= __('panel-initial-text.youareseeing') %> <%= __('panel-initial-text.limitedversion') %>. <%= __('panel-initial-text.forthe') %> <%= __('panel-initial-text.fullversion') %>, <%= __('panel-initial-text.visitoncomputer') %>.

- This shows in real-time where your electricity comes from and how much CO2 was emitted to produce it. + <%= __('panel-initial-text.thisshowsrealtime') %> <%= __('panel-initial-text.whereelectricitycomesfrom') %> <%= __('panel-initial-text.and') %> <%= __('panel-initial-text.howmuchco2') %> <%= __('panel-initial-text.wasemitted') %>.

- We take into account electricity imports and exports between countries. + <%= __('panel-initial-text.takeinaccount') %> <%= __('panel-initial-text.importexport') %> <%= __('panel-initial-text.betweencountries') %>.

- Tip: Click on a country to start exploring ⟶ + <%= __('panel-initial-text.tip') %> ⟶

- This project is Open Source (see data sources). + <%= __('panel-initial-text.thisproject') %> <%= __('panel-initial-text.opensource') %> (<%= __('panel-initial-text.see') %> <%= __('panel-initial-text.datasources') %>).


- Carbon intensity (gCO2eq/kWh) + <%= __('misc.carbonintensity')%> (gCO2eq/kWh)

- +

- +


- Found bugs or have ideas? Report them here.
- Like the visualization? We'd love your feedback! + <%= __('footer.foundbugs') %> <%= __('footer.here') %>.
+ <%= __('footer.likethisvisu') %> <%= __('footer.loveyourfeedback') %>!

@@ -198,8 +204,8 @@ + data-via="tmrowco" + data-lang="<%= locale %>"> @@ -220,8 +226,8 @@   + data-show-count="false" + data-lang="<%= locale %>">

@@ -230,34 +236,34 @@ Please contact us at hello@tmrow.co for more information.
- Carbon intensity:
+ <%= __('misc.carbonintensity') %>:
: -
gCO2eq/kWh ( % fossil fuels)
+
gCO2eq/kWh ( % <%= __('tooltips.fossilfuels')%>)

- Electricity price (day-ahead): €/MWh + <%= __('tooltips.electricityprice') %>: €/MWh
- Cross-border export:
+ <%= __('tooltips.crossborderexport') %>:
: MW

- Carbon intensity of export:
+ <%= __('tooltips.carbonintensityexport') %>:
gCO2eq/kWh
- of
+ <%= __('tooltips.ofelectricity') %>
()

- utilizing of installed capacity
+ <%= __('tooltips.utilizing') %> <%= __('tooltips.ofinstalled') %>
()

- with a carbon intensity of
+ <%= __('tooltips.withcarbonintensity') %>
gCO2eq/kWh (source: IPCC 2014)
- of
+ <%= __('tooltips.of') %>
()

- with a carbon intensity of
+ <%= __('tooltips.withcarbonintensity') %>
:
gCO2eq/kWh