borgbutler-core/src/main/java/de/micromata/borgbutler/CoreI18n.java
New file @@ -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); } } borgbutler-core/src/main/java/de/micromata/borgbutler/I18n.java
New file @@ -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)); } } borgbutler-core/src/main/resources/BorgButlerCoreMessagesBundle.properties
New file @@ -0,0 +1 @@ # Prepared for i18n. borgbutler-server/src/main/java/de/micromata/borgbutler/server/I18nClientMessages.java
New file @@ -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); } } 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; 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; } } borgbutler-server/src/main/resources/BorgButlerClientMessagesBundle.properties
New file @@ -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! borgbutler-server/src/main/resources/BorgButlerClientMessagesBundle_de.properties
New file @@ -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! 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> ); }