From 4ba33abd9104d715c6e3f53c1d2b23398cc196d6 Mon Sep 17 00:00:00 2001
From: Kai Reinhard <K.Reinhard@micromata.de>
Date: Sun, 09 Dec 2018 22:20:07 +0000
Subject: [PATCH] web...

---
 borgbutler-server/src/main/java/de/micromata/borgbutler/server/ServerConfiguration.java |    2 
 borgbutler-server/src/main/resources/BorgButlerClientMessagesBundle.properties          |   88 +++++++++++
 borgbutler-server/src/main/java/de/micromata/borgbutler/server/rest/I18nRest.java       |    7 
 borgbutler-core/src/main/java/de/micromata/borgbutler/I18n.java                         |  109 +++++++++++++
 borgbutler-server/src/main/resources/BorgButlerClientMessagesBundle_de.properties       |   88 +++++++++++
 borgbutler-webapp/src/components/views/Start.jsx                                        |    8 
 borgbutler-server/src/main/java/de/micromata/borgbutler/server/I18nClientMessages.java  |   55 ++++++
 borgbutler-core/src/main/resources/BorgButlerCoreMessagesBundle.properties              |    1 
 borgbutler-core/src/main/java/de/micromata/borgbutler/CoreI18n.java                     |   84 ++++++++++
 9 files changed, 436 insertions(+), 6 deletions(-)

diff --git a/borgbutler-core/src/main/java/de/micromata/borgbutler/CoreI18n.java b/borgbutler-core/src/main/java/de/micromata/borgbutler/CoreI18n.java
new file mode 100644
index 0000000..4c1d7d4
--- /dev/null
+++ b/borgbutler-core/src/main/java/de/micromata/borgbutler/CoreI18n.java
@@ -0,0 +1,84 @@
+package de.micromata.borgbutler;
+
+import org.apache.commons.lang3.StringUtils;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.util.*;
+
+/**
+ * For internationalization.
+ */
+public class CoreI18n extends I18n {
+    private static Logger log = LoggerFactory.getLogger(CoreI18n.class);
+    private static Locale[] DEFAULT_LOCALES = {Locale.ROOT, Locale.GERMAN};
+
+    private static CoreI18n defaultInstance = new CoreI18n();
+    public static final String BUNDLE_NAME = "BorgButlerCoreMessagesBundle";
+    private static List<I18n> allResourceBundles = new ArrayList<>();
+
+    public static CoreI18n getDefault() {
+        return defaultInstance;
+    }
+
+    /**
+     * Use this if only one locale is used (in a none multi user system).
+     * At default the default message bundle "MessagesBundle" of the class path with the system's default locale is used.
+     *
+     * @param instance
+     */
+    public static void setDefault(CoreI18n instance) {
+        defaultInstance = instance;
+    }
+
+    /**
+     * Use this if only one locale is used (in a none multi user system).
+     * Uses bundle "MessagesBundle" of the class path with the given locale.
+     *
+     * @param locale
+     * @return new default instance for chaining.
+     */
+    public static CoreI18n setDefault(Locale locale) {
+        defaultInstance = new CoreI18n(locale);
+        return defaultInstance;
+    }
+
+    public static Set<String> getAllTranslations(String key) {
+        Set<String> set = new HashSet<>();
+        for (I18n i18n : getAllResourceBundles()) {
+            String translation = i18n.getMessage(key);
+            if (StringUtils.isNotBlank(translation))
+                set.add(translation);
+        }
+        return set;
+    }
+
+    private static List<I18n> getAllResourceBundles() {
+        if (!allResourceBundles.isEmpty()) {
+            return allResourceBundles;
+        }
+        synchronized (allResourceBundles) {
+            for (Locale locale : DEFAULT_LOCALES) {
+                allResourceBundles.add(new CoreI18n(locale));
+            }
+        }
+        return allResourceBundles;
+    }
+
+    /**
+     * Uses the default message bundle "MessagesBundle" of class path with systems default locale.
+     */
+    public CoreI18n() {
+        super(BUNDLE_NAME);
+    }
+
+    public CoreI18n(Locale locale) {
+        super(BUNDLE_NAME, locale);
+    }
+
+    @Override
+    protected I18n create(Locale locale) {
+        return new CoreI18n(locale);
+    }
+
+}
diff --git a/borgbutler-core/src/main/java/de/micromata/borgbutler/I18n.java b/borgbutler-core/src/main/java/de/micromata/borgbutler/I18n.java
new file mode 100644
index 0000000..7d797cf
--- /dev/null
+++ b/borgbutler-core/src/main/java/de/micromata/borgbutler/I18n.java
@@ -0,0 +1,109 @@
+package de.micromata.borgbutler;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.text.MessageFormat;
+import java.util.*;
+
+/**
+ * For internationalization.
+ */
+public class I18n {
+    private Logger log = LoggerFactory.getLogger(I18n.class);
+    private ResourceBundle resourceBundle;
+    private Map<Locale, I18n> i18nMap;
+
+    public I18n get(Locale locale) {
+        if (i18nMap == null) {
+            i18nMap = new HashMap<>();
+        }
+        I18n i18n = i18nMap.get(locale);
+        if (i18n == null) {
+            i18n = create(locale);
+            i18nMap.put(locale, i18n);
+        }
+        return i18n;
+    }
+
+    protected I18n create(Locale locale) {
+        return new I18n(this.resourceBundle.getBaseBundleName(), locale);
+    }
+
+    /**
+     * Uses the default message bundle "MessagesBundle" of class path with systems default locale.
+     */
+    public I18n(String bundleName) {
+        this.resourceBundle = I18n.getBundle(bundleName);
+    }
+
+    public I18n(String bundleName, Locale locale) {
+        this.resourceBundle = I18n.getBundle(bundleName, locale);
+    }
+
+    /**
+     * Throws an error if messageId not found.
+     *
+     * @param messageId
+     * @return localized message.
+     */
+    public String getMessage(String messageId) {
+        return resourceBundle.getString(messageId);
+    }
+
+    /**
+     * @param messageId
+     * @return true, if the messageId is found in the bundle, otherwise false.
+     */
+    public boolean containsMessage(String messageId) {
+        return resourceBundle.containsKey(messageId);
+    }
+
+    /**
+     * @param messageId
+     * @param params    Message parameter to replace in message.
+     * @return localized message.
+     * @see MessageFormat#format(String, Object...)
+     */
+    public String formatMessage(String messageId, Object... params) {
+        if (params == null || params.length == 0) {
+            return getMessage(messageId);
+        }
+        try {
+            return MessageFormat.format(resourceBundle.getString(messageId), params);
+        } catch (MissingResourceException ex) {
+            log.error("Missing resource '" + messageId + "': " + ex.getMessage(), ex);
+            return messageId;
+        }
+    }
+
+    public ResourceBundle getResourceBundle() {
+        return resourceBundle;
+    }
+
+    /**
+     *
+     * @param bundleName
+     * @param locale
+     * @return The root bundle if the given locale's language is "en" or language not found, otherwise the desired bundle for the given locale.
+     */
+    public static ResourceBundle getBundle(String bundleName, Locale locale) {
+        if ("en".equals(locale.getLanguage())) {
+            return ResourceBundle.getBundle(bundleName, Locale.ROOT);
+        }
+        return ResourceBundle.getBundle(bundleName, locale);
+        //return ResourceBundle.getBundle(bundleName, locale,
+        //        ResourceBundle.Control.getNoFallbackControl(ResourceBundle.Control.FORMAT_PROPERTIES));
+    }
+
+    /**
+     * Simply calls {@link ResourceBundle#getBundle(String)}.
+     * @param bundleName
+     * @return The bundle for {@link Locale#getDefault()} or root bundle if not found..
+     */
+    public static ResourceBundle getBundle(String bundleName) {
+        return ResourceBundle.getBundle(bundleName);
+        //return ResourceBundle.getBundle(bundleName, locale,
+        //        ResourceBundle.Control.getNoFallbackControl(ResourceBundle.Control.FORMAT_PROPERTIES));
+    }
+}
diff --git a/borgbutler-core/src/main/resources/BorgButlerCoreMessagesBundle.properties b/borgbutler-core/src/main/resources/BorgButlerCoreMessagesBundle.properties
new file mode 100644
index 0000000..0716330
--- /dev/null
+++ b/borgbutler-core/src/main/resources/BorgButlerCoreMessagesBundle.properties
@@ -0,0 +1 @@
+# Prepared for i18n.
\ No newline at end of file
diff --git a/borgbutler-server/src/main/java/de/micromata/borgbutler/server/I18nClientMessages.java b/borgbutler-server/src/main/java/de/micromata/borgbutler/server/I18nClientMessages.java
new file mode 100644
index 0000000..60692bd
--- /dev/null
+++ b/borgbutler-server/src/main/java/de/micromata/borgbutler/server/I18nClientMessages.java
@@ -0,0 +1,55 @@
+package de.micromata.borgbutler.server;
+
+import de.micromata.borgbutler.CoreI18n;
+import de.micromata.borgbutler.I18n;
+
+import java.text.MessageFormat;
+import java.util.*;
+
+/**
+ * For internationalization.
+ */
+public class I18nClientMessages {
+    private static final String BUNDLE_NAME = "BorgButlerClientMessagesBundle";
+    private static String[] params = {"{0}", "{1}", "{2}", "{3}", "{4}", "{5}", "{6}", "{7}", "{8}", "{9}"};
+    private static final I18nClientMessages instance = new I18nClientMessages();
+
+    public static I18nClientMessages getInstance() {
+        return instance;
+    }
+
+    private I18nClientMessages() {
+    }
+
+    /**
+     *
+     * @param locale
+     * @param keysOnly If true, only the keys will be returned. Default is false.
+     * @return
+     */
+    public Map<String, String> getAllMessages(Locale locale, boolean keysOnly) {
+        Map<String, String> map = new HashMap<>();
+        //addAllMessages(map, new AppI18n(locale).getResourceBundle(), keysOnly);
+        addAllMessages(map, new CoreI18n(locale).getResourceBundle(), keysOnly);
+        ResourceBundle bundle = I18n.getBundle(BUNDLE_NAME, locale);
+        addAllMessages(map, bundle, keysOnly);
+        return new TreeMap<>(map); // Sorted by keys.
+    }
+
+    private Map<String, String> addAllMessages(Map<String, String> map, ResourceBundle bundle, boolean keysOnly) {
+        for (String key : bundle.keySet()) {
+            String value = bundle.getString(key);
+            map.put(key, keysOnly ? "" : prepareForReactClient(value));
+        }
+        return map;
+    }
+
+    /**
+     * @param str
+     * @return
+     * @see MessageFormat#format(Object)
+     */
+    static String prepareForReactClient(String str) {
+        return MessageFormat.format(str, (Object[])params);
+    }
+}
diff --git a/borgbutler-server/src/main/java/de/micromata/borgbutler/server/ServerConfiguration.java b/borgbutler-server/src/main/java/de/micromata/borgbutler/server/ServerConfiguration.java
index 3d2023b..53c01d6 100644
--- a/borgbutler-server/src/main/java/de/micromata/borgbutler/server/ServerConfiguration.java
+++ b/borgbutler-server/src/main/java/de/micromata/borgbutler/server/ServerConfiguration.java
@@ -8,7 +8,7 @@
 
 public class ServerConfiguration {
     private static Logger log = LoggerFactory.getLogger(ServerConfiguration.class);
-    private final static String[] SUPPORTED_LANGUAGES = {"en"};
+    private final static String[] SUPPORTED_LANGUAGES = {"en", "de"};
     private static String applicationHome;
 
     private int port;
diff --git a/borgbutler-server/src/main/java/de/micromata/borgbutler/server/rest/I18nRest.java b/borgbutler-server/src/main/java/de/micromata/borgbutler/server/rest/I18nRest.java
index f71cb84..7e060d6 100644
--- a/borgbutler-server/src/main/java/de/micromata/borgbutler/server/rest/I18nRest.java
+++ b/borgbutler-server/src/main/java/de/micromata/borgbutler/server/rest/I18nRest.java
@@ -1,5 +1,7 @@
 package de.micromata.borgbutler.server.rest;
 
+import de.micromata.borgbutler.json.JsonUtils;
+import de.micromata.borgbutler.server.I18nClientMessages;
 import org.apache.commons.lang3.StringUtils;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
@@ -12,6 +14,7 @@
 import javax.ws.rs.core.Context;
 import javax.ws.rs.core.MediaType;
 import java.util.Locale;
+import java.util.Map;
 
 @Path("/i18n")
 public class I18nRest {
@@ -36,6 +39,8 @@
         } else {
             localeObject = RestUtils.getUserLocale(requestContext);
         }
-        return ""; // i18n not yet supported.
+        Map<String, String> translations = I18nClientMessages.getInstance().getAllMessages(localeObject, keysOnly);
+        String json = JsonUtils.toJson(translations, prettyPrinter);
+        return json;
     }
 }
diff --git a/borgbutler-server/src/main/resources/BorgButlerClientMessagesBundle.properties b/borgbutler-server/src/main/resources/BorgButlerClientMessagesBundle.properties
new file mode 100644
index 0000000..8ce4d6b
--- /dev/null
+++ b/borgbutler-server/src/main/resources/BorgButlerClientMessagesBundle.properties
@@ -0,0 +1,88 @@
+common.browse=Browse
+common.browse.hint=Browse local filesystem
+common.cancel=Cancel
+common.comment=Comment
+common.common=Common
+common.description=Description
+common.droparea.title=Select a file or drop one here.
+common.droparea.hint=Drop or open your files here, such as serial definition files (Excel).
+common.filename=File name
+common.fileSize=File size
+common.information=Information
+common.lastModified=Last modified
+common.limitsResultSize=Limits result size.
+common.loading=Loading...
+common.new=New
+common.none=none
+common.required=Required
+common.path=Path
+common.run=Run
+common.save=Save
+common.unique=Unique
+common.update=Update
+common.alert.cantLoadData=Can't load data
+common.alert.genericRestAPIFailure=Something went wrong with the server call. Is the server available?
+common.alert.tryAgain=Try again
+
+configuration=Configuration
+configuration.account=Account
+configuration.addDirectory=Add directory
+configuration.addDirectory.hint=Add new template directory.
+configuration.application.dateFormat=Date format
+configuration.application.dateFormat.option.auto=Auto
+configuration.application.language=Application language
+configuration.application.language.option.default=Default (browser)
+configuration.cancel.hint=Discard changes and go to Start page.
+configuration.expertSettings=Expert settings
+configuration.forExpertsOnly=For experts only
+configuration.reloadDictionary.hint=Force reloading all translations for this app.
+configuration.removeItem=Remove item
+configuration.resetAllSettings=Reset all settings
+configuration.resetAllSettings.question=Are you sure you want to reset all settings? All settings will be resetted on server. Template files will not be deleted.
+configuration.resetAllSettings.hint=Reset factory settings.
+configuration.save.hint=Save changes and go to Start page.
+configuration.server=Server
+configuration.showTestData=Show test data
+configuration.templatesDirectory=Directory
+configuration.webDevelopmentMode=Web development
+configuration.webDevelopmentMode.hint=Activate this checkbox only for web development of BorgButler (with yarn or npm). The CrossOriginFilter will be set. A restart of BorgButler is required.
+
+language.english=English
+language.german=German
+
+logviewer=Log viewer
+logviewer.filter=Filter
+logviewer.filter.level.hint=Minimum displayed log level.
+logviewer.filter.location.hint=Show location of message in source code.
+logviewer.filter.location.option.short=short
+logviewer.filter.location.option.normal=long
+logviewer.filter.stacktraces=Stacktraces
+logviewer.filter.stacktraces.showHide.hint=Show/hide stack traces.
+logviewer.level=Level
+logviewer.location=Location in source code
+logviewer.message=Message
+logviewer.timestamp=Timestamp
+
+startscreen.welcome.title=Welcome to BorgButler
+startscreen.welcome.text=Congratulations. With BorgButler you have started a smart assistant for your work with borg backups.
+startscreen.welcome.enjoy=Enjoy your work!
+startscreen.welcome.documentation=Go to Documentation
+
+update=Update
+update.installerUrl=Installer url
+update.newVersion=New version
+update.newVersionAvailable=The new version {0} is available.
+update.newVersion.simplyClickButton=You can start the update process by simply clicking the update button.
+update.noUpdateAvailable=No update available.
+update.update.button.hint=Download and execute update.
+update.description.line1=The update button works only if your browser is running on the same computer as your BorgBackup server.
+update.description.line2=If the installer doesn't start after clicking the update button, please proceed manually by downloading the installer from the given url below.
+
+validation.requiredField=Value is required. Please insert a value.
+validation.numberExpected=Value must be a number.
+validation.numberMustBeLower=The number can't be higher than {0}.
+validation.numberMustBeHigher=The number can't be lower than {0}.
+
+version=Version
+version.buildDate=Build date
+version.updateAvailable=Update available!
diff --git a/borgbutler-server/src/main/resources/BorgButlerClientMessagesBundle_de.properties b/borgbutler-server/src/main/resources/BorgButlerClientMessagesBundle_de.properties
new file mode 100644
index 0000000..ac02000
--- /dev/null
+++ b/borgbutler-server/src/main/resources/BorgButlerClientMessagesBundle_de.properties
@@ -0,0 +1,88 @@
+common.browse=Browse
+common.browse.hint=Öffne lokales Dateisystem
+common.cancel=Abbrechen
+common.comment=Kommentar
+common.common=Allgemein
+common.description=Beschreibung
+common.droparea.title=Datei hierhin ziehen oder auswählen.
+common.droparea.hint=Ziehen Sie Ihre Dateien hierhin, wie beispielsweise Seriendefinitionen(Excel).
+common.filename=Dateiname
+common.fileSize=Dateigröße
+common.information=Information
+common.lastModified=Letzte Änderung
+common.limitsResultSize=Ergebnisliste begrenzen.
+common.loading=Lädt gerade...
+common.new=Neu
+common.none=keine
+common.required=Erforderlich
+common.path=Pfad
+common.run=Ausführen
+common.save=Speichern
+common.unique=Eindeuting
+common.update=Aktualisieren
+common.alert.cantLoadData=Fehler beim Laden von Daten
+common.alert.genericRestAPIFailure=Ein allgemeiner Fehler ist beim Serveraufruf aufgetreten. Ist der Server erreichbar?
+common.alert.tryAgain=Erneut probieren
+
+configuration=Konfiguration
+configuration.account=Zugang
+configuration.addDirectory=Verzeichnis hinzufügen
+configuration.addDirectory.hint=Ein neues Template-Verzeichnis hinzufügen.
+configuration.application.dateFormat=Datumsformat
+configuration.application.dateFormat.option.auto=Automatisch
+configuration.application.language=Anwendungssprache
+configuration.application.language.option.default=Standard (Browser)
+configuration.cancel.hint=Änderungen verwerfen und zur Startseite.
+configuration.expertSettings=Experteneinstellungen
+configuration.forExpertsOnly=Nur für Experten
+configuration.reloadDictionary.hint=Erzwingt das Neuladen aller Übersetzungstexte dieser Anwendung.
+configuration.removeItem=Eintrag entfernen
+configuration.resetAllSettings=Alles zurücksetzen
+configuration.resetAllSettings.question=Sind Sie sich wirklich sicher, dass Sie alle Einstellungen zurücksetzen wollen? Alle Einstellungen werden auf dem Server zurückgesetzt. Templatedateien werden nicht gelöscht.
+configuration.resetAllSettings.hint=Alle Einstellungen zurücksetzen.
+configuration.save.hint=Änderungen speichern und zur Startseite.
+configuration.server=Server
+configuration.showTestData=Testdaten anzeigen
+configuration.templatesDirectory=Verzeichnis
+configuration.webDevelopmentMode=Webentwicklung
+configuration.webDevelopmentMode.hint=Aktivieren Sie diese Funktion nur, wenn Sie die Webentwicklung für BorgButler durchführen (yarn oder npm). Der CrossOriginFilter wird hierüber aktiviert. Ein Neustart von BorgButler ist erforderlich.
+
+language.english=Englisch
+language.german=Deutsch
+
+logviewer=Protokolldateien
+logviewer.filter=Filter
+logviewer.filter.level.hint=Zeige nur Meldungen ab diesem Level.
+logviewer.filter.location.hint=Zeige den Ort der Meldung im Quellcode.
+logviewer.filter.location.option.short=kurz
+logviewer.filter.location.option.normal=lang
+logviewer.filter.stacktraces=Stacktraces
+logviewer.filter.stacktraces.showHide.hint=Zeige/verstecke Stacktraces.
+logviewer.level=Level
+logviewer.location=Stelle im Quellcode
+logviewer.message=Meldung
+logviewer.timestamp=Zeitstempel
+
+startscreen.welcome.title=Willkommen zu BorgButler
+startscreen.welcome.text=Herzlichen Glückwunsch. Mit BorgButler haben Sie einen smarten Assistenten für Ihre tägliche Arbeit mit Borg-Backups gestartet.
+startscreen.welcome.enjoy=Viel Spaß!
+startscreen.welcome.documentation=Gehe zur Dokumentation
+
+update=Aktualisierung
+update.installerUrl=Webadresse Installation
+update.newVersion=Neue Version
+update.newVersionAvailable=Die neue Version {0} ist verfügbar.
+update.newVersion.simplyClickButton=Sie können die Aktualisierung auf Knopfdruck automatisch starten.
+update.noUpdateAvailable=Keine Aktualisierung verfügbar.
+update.update.button.hint=Aktualisierung herunterladen und ausführen.
+update.description.line1=Der Aktualisierungsknopf funktioniert nur, wenn Ihr Browser auf dem gleichen Computer läuft wie der BorgButler-Server.
+update.description.line2=Wenn das Installationsprogramm nicht auf Knopfdruck startet, fahren Sie bitte mit dem manuellen Download von der unten angegebenen Webadresse fort.
+
+validation.requiredField=Der Wert ist erforderlich. Bitte geben Sie einen Wert ein.
+validation.numberExpected=Der Wert muss eine Zahl sein.
+validation.numberMustBeLower=Die Zahl muss kleiner als {0} sein.
+validation.numberMustBeHigher=Die Zahl muss größer als {0} sein.
+
+version=Version
+version.buildDate=Erzeugt am
+version.updateAvailable=Aktualisierung verfügbar!
diff --git a/borgbutler-webapp/src/components/views/Start.jsx b/borgbutler-webapp/src/components/views/Start.jsx
index c058232..ef337ee 100644
--- a/borgbutler-webapp/src/components/views/Start.jsx
+++ b/borgbutler-webapp/src/components/views/Start.jsx
@@ -7,11 +7,11 @@
         return (
             <React.Fragment>
                 <PageHeader>
-                    Welcome to BorgButler
+                    <I18n name={'startscreen.welcome.title'}/>
                 </PageHeader>
-                <div className="welcome-intro">BorgButler is the frontend for BorgBackup.</div>
-                <div className="welcome-enjoy">Enjoy your work with BorgButler.</div>
-                <div className="welcome-documentation-link"><a className={'btn btn-link btn-outline-primary'} href={'https://github.com/micromata/borgbackup-butler'} target="_blank" rel="noopener noreferrer">Documentation</a></div>
+                <div className="welcome-intro"><I18n name={'startscreen.welcome.text'}/></div>
+                <div className="welcome-enjoy"><I18n name={'startscreen.welcome.enjoy'}/></div>
+                <div className="welcome-documentation-link"><a className={'btn btn-link btn-outline-primary'} href={'https://github.com/micromata/borgbackup-butler'} target="_blank" rel="noopener noreferrer"><I18n name={'startscreen.welcome.documentation'}/></a></div>
             </React.Fragment>
         );
     }

--
Gitblit v1.10.0