Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Dashboard internationalization #217

Merged
merged 6 commits into from Nov 18, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
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>