Skip to content

Commit

Permalink
Dashboard internationalization (#217)
Browse files Browse the repository at this point in the history
French version included
Fixes #214

Signed-off-by: Laurent Garnier <lg.hc@free.fr>
  • Loading branch information
lolodomo authored and kaikreuzer committed Nov 18, 2017
1 parent 14dc90a commit 3fa30c1
Show file tree
Hide file tree
Showing 10 changed files with 228 additions and 54 deletions.
30 changes: 30 additions & 0 deletions bundles/org.openhab.ui.dashboard/ESH-INF/i18n/dashboard.properties
@@ -0,0 +1,30 @@
index.welcome = Welcome to openHAB 2
index.location-info = If you allow your browser to access your location, openHAB will use it for weather and astro information.
index.your-location = Your location:

setup.subtitle = Setup
setup.welcome = Welcome to openHAB 2 - Initial Setup
setup.intro = openHAB comes with many different add-ons. To allow an easy start, there are four pre-defined packages available that are a good starting point.
setup.check-doc = Check out the online documentation for a <a href="http://docs.openhab.org/configuration/packages.html" target="_blank">detailed description of those packages</a>.
setup.choose-package = Please choose a package:
setup.package-simple-overlay = Simple
setup.package-simple-footer = Purely UI
setup.package-standard-overlay = Standard
setup.package-standard-footer = Recommended setup
setup.package-expert-overlay = Expert
setup.package-expert-footer = Best for 1.x users
setup.package-demo-overlay = Demo
setup.package-demo-footer = Sample setup
setup.skip-package = Skip the package selection...

common.getting-started = Getting started? Please refer to the <a href="http://docs.openhab.org/" target="_blank">online documentation</a>.

entry.no-ui-installed = No user interfaces installed.
entry.install-running = Please stand by while UIs are being installed. This can take several minutes.

warn.exposed = <p><b>WARNING - YOUR HOME IS EXPOSED!</b></p> \
<p>It seems that you have configured your network in a way that you can remotely access your openHAB server. Unfortunately, it is not just you - almost <b>everybody on the Internet can access it!</b></p> \
<p>If this wasn't your plan, please act immediately. Stop the port forwarding of your router or make sure that you have a secure authentication mechanism in place, e.g. by using NGINX as a reverse proxy inbetween.</p> \
<p>If you have read and understood this message and you have taken appropriate actions, please \
<a href="?warn=ihavelearnedmylesson" style="color: #DDDDDD">click here</a> \
to make this message disappear again.</p>
@@ -0,0 +1,29 @@
index.welcome = Bienvenue dans openHAB 2
index.location-info = Si vous autorisez votre navigateur à accéder à votre position, openHAB l'utilisera par exemple pour les données météo locales.
index.your-location = Votre position:
setup.subtitle = Configuration
setup.welcome = Bienvenue dans openHAB 2 - Configuration initiale
setup.intro = openHAB est fourni avec de nombreuses extensions. Pour permettre un démarrage facile, quatre paquets d'extensions pré-définis sont disponibles et constituent un bon point de départ.
setup.check-doc = Consulter la documentation en ligne pour une <a href="http://docs.openhab.org/configuration/packages.html" target="_blank">description détaillée de ces paquets</a>.
setup.choose-package = Merci de choisir un paquet:
setup.package-simple-overlay = Simple
setup.package-simple-footer = Purement UI
setup.package-standard-overlay = Standard
setup.package-standard-footer = Configuration recommandée
setup.package-expert-overlay = Expert
setup.package-expert-footer = Pour les utilisateurs 1.x
setup.package-demo-overlay = Démo
setup.package-demo-footer = Configuration de démo
setup.skip-package = Sauter l'étape de sélection d'un paquet...

common.getting-started = Démarrage ? Merci de vous référer à la <a href="http://docs.openhab.org/" target="_blank">documentation en ligne</a>.

entry.no-ui-installed = Aucune interface utilisateur installée.
entry.install-running = Merci de patienter pendant l'installation. Cela peut prendre plusieurs minutes.
warn.exposed = <p><b>ATTENTION - VOTRE MAISON EST EXPOSÉE!</b></p> \
<p>Il semble que vous ayez configuré votre réseau de manière à pouvoir accéder à distance à votre serveur openHAB. Malheureusement, ce n'est pas seulement vous mais pratiquement <b>tout le monde sur Internet qui peut y accéder !</b></p> \
<p>Si ce n'était pas votre objectif, veuillez agir immédiatement. Arrêtez le renvoi de port de votre routeur ou assurez-vous que vous disposez d'un mécanisme d'authentification sécurisé, par exemple en utilisant NGINX comme reverse proxy entre Internet et votre serveur openHAB.</p> \
<p>Si vous avez lu et compris ce message et que vous avez pris les mesures appropriées, merci de \
<a href="?warn=ihavelearnedmylesson" style="color: #DDDDDD">cliquez ici</a> pour faire disparaître ce message.</p>
@@ -0,0 +1,28 @@
index.welcome = Welkom bij openHAB 2
index.location-info = Als u uw browser toegang geeft tot uw locatie, zal openHAB deze gebruiken voor weer- en astro-informatie.
index.your-location = Je locatie:

setup.subtitle = Setup
setup.welcome = Welkom bij openHAB 2 - Initiële opzet
setup.intro = openHAB komt met verschillende koppelingen. Om een gemakkelijke start mogelijk te maken, zijn er vier vooraf gedefinieerde pakketten beschikbaar die een goed beginpunt zijn.
setup.check-doc = Bekijk de online documentatie voor een <a href="http://docs.openhab.org/configuration/packages.html" target="_blank"> gedetailleerde beschrijving van die pakketten (engels)</a>.
setup.choose-package = Kies een pakket:
setup.package-simple-overlay = Eenvoudig
setup.package-simple-footer = Purely UI
setup.package-standard-overlay = Standaard
setup.package-standard-footer = Aanbevolen
setup.package-expert-overlay = Expert
setup.package-expert-footer = Het beste voor 1.x gebruikers
setup.package-demo-overlay = Demo
setup.package-demo-footer = Voorbeeld opstelling
setup.skip-package = Sla de pakket selectie over...

common.getting-started = Net gestart? Raadpleeg de <a href="http://docs.openhab.org/" target="_blank"> online documentatie (engels)</a>.

entry.no-ui-installed = Geen gebruikersinterfaces geïnstalleerd.
entry.install-running = Even geduld alstublieft terwijl de UI's worden geïnstalleerd. Dit kan enkele minuten duren.
warn.exposed = <p><b>WAARSCHUWING - UW HUIS IS TOEGANKELIJK VIA HET INTERNET!</b></p> \
<p>Het lijkt erop dat u uw netwerk hebt geconfigureerd op een manier die het mogelijk maakt op afstand op uw openHAB-server te komen. Helaas is niet alleen u hebt toegang - bijna <b> iedereen op het internet heeft toegang!</b></p> \
<p>Als dit niet uw plan was, moet u meteen handelen. Stop het doorsturen van uw router naar de openHAB poort of zorg ervoor dat u een beveiligingsmechanisme op zijn plaats hebt, bijvoorbeeld door gebruik te maken van NGINX als een omgekeerde proxy.</p> \
<p>Als u dit bericht hebt gelezen en begrepen en hebt u passende maatregelen genomen, <a href="?warn=ihavelearnedmylesson" style="color: #DDDDDD">Klik hier</a> om dit bericht te laten verdwijnen.</p>
1 change: 1 addition & 0 deletions bundles/org.openhab.ui.dashboard/META-INF/MANIFEST.MF
Expand Up @@ -12,6 +12,7 @@ Import-Package:
javax.servlet.http,
org.apache.commons.io,
org.eclipse.jdt.annotation;resolution:=optional,
org.eclipse.smarthome.core.i18n,
org.eclipse.smarthome.core.net,
org.openhab.core,
org.osgi.framework,
Expand Down
Expand Up @@ -11,6 +11,7 @@
import java.io.IOException;
import java.net.URL;
import java.util.Hashtable;
import java.util.Locale;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.CopyOnWriteArraySet;
Expand All @@ -19,6 +20,8 @@
import javax.servlet.http.HttpServlet;

import org.apache.commons.io.IOUtils;
import org.eclipse.smarthome.core.i18n.LocaleProvider;
import org.eclipse.smarthome.core.i18n.TranslationProvider;
import org.eclipse.smarthome.core.net.HttpServiceUtil;
import org.eclipse.smarthome.core.net.NetworkAddressService;
import org.openhab.ui.dashboard.DashboardTile;
Expand All @@ -40,6 +43,8 @@
* This component registers the dashboard resources.
*
* @author Kai Kreuzer - Initial contribution
* @author Laurent Garnier - internationalization
* @author Hilbrand Bouwkamp - internationalization
*/
@Component(service = DashboardService.class, immediate = true, name = "org.openhab.dashboard")
public class DashboardService {
Expand All @@ -54,6 +59,8 @@ public class DashboardService {
protected HttpService httpService;
protected ConfigurationAdmin configurationAdmin;
protected NetworkAddressService networkAddressService;
protected TranslationProvider i18nProvider;
protected LocaleProvider localeProvider;

protected Set<DashboardTile> tiles = new CopyOnWriteArraySet<>();

Expand Down Expand Up @@ -120,6 +127,24 @@ protected void unsetNetworkAddressService(NetworkAddressService networkAddressSe
this.networkAddressService = null;
}

@Reference
protected void setLocaleProvider(final LocaleProvider localeProvider) {
this.localeProvider = localeProvider;
}

protected void unsetLocaleProvider(final LocaleProvider localeProvider) {
this.localeProvider = null;
}

@Reference
public void setTranslationProvider(TranslationProvider i18nProvider) {
this.i18nProvider = i18nProvider;
}

public void unsetTranslationProvider(TranslationProvider i18nProvider) {
this.i18nProvider = null;
}

@Reference(cardinality = ReferenceCardinality.MULTIPLE, policy = ReferencePolicy.DYNAMIC)
protected void addDashboardTile(DashboardTile tile) {
tiles.add(tile);
Expand Down Expand Up @@ -180,7 +205,7 @@ protected HttpServlet createServlet() {
}

return new DashboardServlet(configurationAdmin, indexTemplate, entryTemplate, warnTemplate, setupTemplate,
tiles);
tiles, this::getLocalizedText);
}

private void addTilesForExternalServices(Map<String, Object> properties) {
Expand All @@ -202,4 +227,27 @@ private void addTilesForExternalServices(Map<String, Object> properties) {
}
}
}

/**
* Returns the localized text for the given key. When the key is 'locale' it returns the locale. If no locale or
* fall-back would be present it returns the key.
*
* @param key key to get locale from
* @param locale known locale
* @return localized text for the key
*/

private String getLocalizedText(String key, Locale locale) {
Locale useLocale = locale == null ? localeProvider.getLocale() : locale;

if ("locale".equals(key)) {
// The return value for "locale" key is an ISO 639-1 language code
// In case there is no translation for the used locale provided with the dashboard, "en" is returned
return bundleContext.getBundle()
.getEntry("ESH-INF/i18n/dashboard_" + useLocale.getLanguage() + ".properties") != null
? useLocale.getLanguage() : "en";
} else {
return i18nProvider.getText(bundleContext.getBundle(), key, key, useLocale);
}
}
}
Expand Up @@ -10,8 +10,15 @@

import java.io.IOException;
import java.util.Dictionary;
import java.util.HashMap;
import java.util.Hashtable;
import java.util.Locale;
import java.util.Map;
import java.util.Set;
import java.util.function.BiFunction;
import java.util.function.Function;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
Expand All @@ -30,12 +37,16 @@
* that are registered as a service.
*
* @author Kai Kreuzer
* @author Laurent Garnier - internationalization
* @author Hilbrand Bouwkamp - internationalization
*
*/
public class DashboardServlet extends HttpServlet {

private static final long serialVersionUID = -5154582000538034381L;

private static final Pattern MESSAGE_KEY_PATTERN = Pattern.compile("\\$\\{([^\\}]+)\\}");

private final Logger logger = LoggerFactory.getLogger(DashboardServlet.class);

private ConfigurationAdmin configurationAdmin;
Expand All @@ -50,14 +61,18 @@ public class DashboardServlet extends HttpServlet {

private Set<DashboardTile> tiles;

private BiFunction<String, Locale, String> localizeFunction;

public DashboardServlet(ConfigurationAdmin configurationAdmin, String indexTemplate, String entryTemplate,
String warnTemplate, String setupTemplate, Set<DashboardTile> tiles) {
String warnTemplate, String setupTemplate, Set<DashboardTile> tiles,
BiFunction<String, Locale, String> localizeFunction) {
this.configurationAdmin = configurationAdmin;
this.indexTemplate = indexTemplate;
this.entryTemplate = entryTemplate;
this.warnTemplate = warnTemplate;
this.setupTemplate = setupTemplate;
this.tiles = tiles;
this.localizeFunction = localizeFunction;
isExposed(null);
}

Expand All @@ -70,31 +85,36 @@ protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws Se
}
}

private void serveDashboard(HttpServletRequest req, HttpServletResponse resp) throws IOException {
String index = indexTemplate.replace("<!--version-->", OpenHAB.getVersion() + " " + OpenHAB.buildString());
private void serveDashboard(HttpServletRequest req, HttpServletResponse resp) throws IOException, ServletException {
StringBuilder entries = new StringBuilder();
for (DashboardTile tile : tiles) {
String entry = entryTemplate.replace("<!--name-->", tile.getName());
Map<String, String> entryMap = new HashMap<>();
entryMap.put("name", tile.getName());
String overlay = tile.getOverlay() == null ? "none" : tile.getOverlay();

entry = entry.replace("<!--url-->", tile.getUrl());
entry = entry.replace("<!--overlay-->", overlay);
entry = entry.replace("<!--icon-->", tile.getImageUrl());
entries.append(entry);
entryMap.put("url", tile.getUrl());
entryMap.put("overlay", overlay);
entryMap.put("icon", tile.getImageUrl());
entries.append(replaceKeysFromMap(entryTemplate, entryMap));
}
resp.setContentType("text/html;charset=UTF-8");
if (tiles.size() == 0) {
if (tiles.isEmpty()) {
if ("minimal".equals(getPackage())) {
entries.append("&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;");
entries.append("No user interfaces installed.");
entries.append("${entry.no-ui-installed}");
} else {
entries.append(
"&nbsp;&nbsp;&nbsp;&nbsp;<div class=\"spinner spinner--steps\"><img src=\"img/spinner.svg\"></div>&nbsp;&nbsp;");
entries.append("Please stand by while UIs are being installed. This can take several minutes.");
entries.append("${entry.install-running}");
}
}
String warn = isExposed(req) ? warnTemplate : "";
resp.getWriter().append(index.replace("<!--entries-->", entries.toString()).replace("<!--warn-->", warn));
Map<String, String> replaceMap = new HashMap<>();
replaceMap.put("version", OpenHAB.getVersion() + " " + OpenHAB.buildString());
replaceMap.put("entries", entries.toString());
replaceMap.put("warn", isExposed(req) ? warnTemplate : "");
// Set the messages in the session
resp.setContentType("text/html;charset=UTF-8");
// We use for UI language the server locale rather than the browser locale that we can get with req.getLocale()
resp.getWriter().append(replaceKeysWithLocaleFunction(replaceKeysFromMap(indexTemplate, replaceMap), null));
resp.getWriter().close();
}

Expand All @@ -103,9 +123,12 @@ private void serveSetup(HttpServletRequest req, HttpServletResponse resp) throws
setPackage(req.getParameter("type"));
resp.sendRedirect(req.getRequestURI());
} else {
Map<String, String> replaceMap = new HashMap<>();
replaceMap.put("version", OpenHAB.getVersion() + " " + OpenHAB.buildString());
resp.setContentType("text/html;charset=UTF-8");
resp.getWriter().append(
setupTemplate.replace("<!--version-->", OpenHAB.getVersion() + " " + OpenHAB.buildString()));
// We use for UI language the server locale rather than the browser locale that we can get with
// req.getLocale()
resp.getWriter().append(replaceKeysWithLocaleFunction(replaceKeysFromMap(setupTemplate, replaceMap), null));
resp.getWriter().close();
}
}
Expand Down Expand Up @@ -180,4 +203,25 @@ private String getPackage() {
}
return null;
}

private String replaceKeysWithLocaleFunction(String template, Locale locale) {
return replaceKeysWithFunction(template, (key) -> localizeFunction.apply(key, locale));
}

private String replaceKeysFromMap(String template, Map<String, String> map) {
return replaceKeysWithFunction(template,
(key) -> Matcher.quoteReplacement(map.getOrDefault(key, "${" + key + '}')));
}

private String replaceKeysWithFunction(String template, Function<String, String> getMessage) {
Matcher m = MESSAGE_KEY_PATTERN.matcher(template);
StringBuffer sb = new StringBuffer();

while (m.find()) {
String key = m.group(1);
m.appendReplacement(sb, getMessage.apply(key));
}
m.appendTail(sb);
return sb.toString();
}
}
8 changes: 4 additions & 4 deletions bundles/org.openhab.ui.dashboard/templates/entry.html
@@ -1,10 +1,10 @@
<div class="link-wrapper col-md-3 col-sm-6 col-xs-6">
<div class="link" onclick="location.href = '<!--url-->'">
<div class="body" style="background-image: url('<!--icon-->')">
<div class="overlay icon-<!--overlay-->"></div>
<div class="link" onclick="location.href = '${url}'">
<div class="body" style="background-image: url('${icon}')">
<div class="overlay icon-${overlay}"></div>
</div>
<div class="footer">
<p><!--name--></p>
<p>${name}</p>
</div>
</div>
</div>
16 changes: 8 additions & 8 deletions bundles/org.openhab.ui.dashboard/templates/index.html
@@ -1,5 +1,5 @@
<!DOCTYPE html>
<html lang="en">
<html lang="${locale}">
<head>

<meta charset="utf-8" />
Expand All @@ -23,29 +23,29 @@
</div>
</header>
<section class="container">
<h1>Welcome to openHAB 2</h1>
<h1>${index.welcome}</h1>
<div class="line">
<div class="decorator"></div>
<hr />
</div>
<!--warn-->
${warn}
<div class="links row">
<!--entries-->
${entries}
</div>
<div class="line">
<div class="geolocation">
<div class="decorator"></div>
<hr />
<p>If you allow your browser to access your location, openHAB will use it for weather and astro information.</p>
<p><span>Your location: </span><span id="geolocation"></span></p>
<p>${index.location-info}</p>
<p><span>${index.your-location} </span><span id="geolocation"></span></p>
</div>
<div class="decorator"></div>
<hr />
</div>
Getting started? Please refer to the <a href="http://docs.openhab.org/">online documentation</a>.
${common.getting-started}
</section>
<footer>
<p>openHAB <!--version--></p>
<p>openHAB ${version}</p>
</footer>
</body>
</html>

0 comments on commit 3fa30c1

Please sign in to comment.