From c130441700c9a42a1fd96e5b9ddc4eeeed2404b6 Mon Sep 17 00:00:00 2001 From: Sebastian Stenzel Date: Sat, 2 Apr 2022 12:15:09 +0200 Subject: [PATCH 1/8] add setting for user-chosen "language" --- .../java/org/cryptomator/common/settings/Settings.java | 7 +++++++ .../cryptomator/common/settings/SettingsJsonAdapter.java | 2 ++ 2 files changed, 9 insertions(+) diff --git a/src/main/java/org/cryptomator/common/settings/Settings.java b/src/main/java/org/cryptomator/common/settings/Settings.java index 82f2fb794..8cc23bbfc 100644 --- a/src/main/java/org/cryptomator/common/settings/Settings.java +++ b/src/main/java/org/cryptomator/common/settings/Settings.java @@ -44,6 +44,7 @@ public class Settings { public static final String DEFAULT_LICENSE_KEY = ""; public static final boolean DEFAULT_SHOW_MINIMIZE_BUTTON = false; public static final String DEFAULT_DISPLAY_CONFIGURATION = ""; + public static final String DEFAULT_LANGUAGE = null; private final ObservableList directories = FXCollections.observableArrayList(VaultSettings::observables); @@ -66,6 +67,7 @@ public class Settings { private final IntegerProperty windowWidth = new SimpleIntegerProperty(); private final IntegerProperty windowHeight = new SimpleIntegerProperty(); private final ObjectProperty displayConfiguration = new SimpleObjectProperty<>(DEFAULT_DISPLAY_CONFIGURATION); + private final StringProperty language = new SimpleStringProperty(DEFAULT_LANGUAGE); private Consumer saveCmd; @@ -96,6 +98,7 @@ public class Settings { windowWidth.addListener(this::somethingChanged); windowHeight.addListener(this::somethingChanged); displayConfiguration.addListener(this::somethingChanged); + language.addListener(this::somethingChanged); } void setSaveCmd(Consumer saveCmd) { @@ -191,4 +194,8 @@ public class Settings { public ObjectProperty displayConfigurationProperty() { return displayConfiguration; } + + public StringProperty languageProperty() { + return language; + } } diff --git a/src/main/java/org/cryptomator/common/settings/SettingsJsonAdapter.java b/src/main/java/org/cryptomator/common/settings/SettingsJsonAdapter.java index 6d8d880e6..d10066f8f 100644 --- a/src/main/java/org/cryptomator/common/settings/SettingsJsonAdapter.java +++ b/src/main/java/org/cryptomator/common/settings/SettingsJsonAdapter.java @@ -57,6 +57,7 @@ public class SettingsJsonAdapter extends TypeAdapter { out.name("windowWidth").value((value.windowWidthProperty().get())); out.name("windowHeight").value((value.windowHeightProperty().get())); out.name("displayConfiguration").value((value.displayConfigurationProperty().get())); + out.name("language").value((value.languageProperty().get())); out.endObject(); } @@ -97,6 +98,7 @@ public class SettingsJsonAdapter extends TypeAdapter { case "windowWidth" -> settings.windowWidthProperty().set(in.nextInt()); case "windowHeight" -> settings.windowHeightProperty().set(in.nextInt()); case "displayConfiguration" -> settings.displayConfigurationProperty().set(in.nextString()); + case "language" -> settings.languageProperty().set(in.nextString()); default -> { LOG.warn("Unsupported vault setting found in JSON: " + name); From d9aa6ae91a3c7fa81cb618af8d8e260579fdeb8d Mon Sep 17 00:00:00 2001 From: Sebastian Stenzel Date: Sat, 2 Apr 2022 12:16:57 +0200 Subject: [PATCH 2/8] display "language" chooser in general preferences --- .../launcher/SupportedLanguages.java | 12 +++++++ .../GeneralPreferencesController.java | 35 +++++++++++++++++++ .../resources/fxml/preferences_general.fxml | 5 +++ src/main/resources/i18n/strings.properties | 2 ++ 4 files changed, 54 insertions(+) create mode 100644 src/main/java/org/cryptomator/launcher/SupportedLanguages.java diff --git a/src/main/java/org/cryptomator/launcher/SupportedLanguages.java b/src/main/java/org/cryptomator/launcher/SupportedLanguages.java new file mode 100644 index 000000000..7f891ed1f --- /dev/null +++ b/src/main/java/org/cryptomator/launcher/SupportedLanguages.java @@ -0,0 +1,12 @@ +package org.cryptomator.launcher; + +import java.util.List; + +public class SupportedLanguages { + + // these are BCP 47 language codes, not ISO. Note the "-" instead of the "_": + public static List LANGUAGAE_TAGS = List.of("ar", "bn", "bs", "ca", "cs", "de", "el", "es", "fil", "fr", "gl", "he", // + "hi", "hr", "hu", "id", "it", "ja", "ko", "lv", "mk", "nb", "nl", "nn", "no", "pa", "pl", "pt", "pt-BR", "ro", "ru", // + "sk", "sr", "sr-Latn", "sv", "ta", "te", "th", "tr", "uk", "zh", "zh-HK", "zh-TW"); + +} diff --git a/src/main/java/org/cryptomator/ui/preferences/GeneralPreferencesController.java b/src/main/java/org/cryptomator/ui/preferences/GeneralPreferencesController.java index 7430cf207..3ebdad4a4 100644 --- a/src/main/java/org/cryptomator/ui/preferences/GeneralPreferencesController.java +++ b/src/main/java/org/cryptomator/ui/preferences/GeneralPreferencesController.java @@ -1,5 +1,6 @@ package org.cryptomator.ui.preferences; +import com.google.common.base.Strings; import org.cryptomator.common.Environment; import org.cryptomator.common.LicenseHolder; import org.cryptomator.common.settings.Settings; @@ -7,6 +8,7 @@ import org.cryptomator.common.settings.UiTheme; import org.cryptomator.integrations.autostart.AutoStartProvider; import org.cryptomator.integrations.autostart.ToggleAutoStartFailedException; import org.cryptomator.integrations.keychain.KeychainAccessProvider; +import org.cryptomator.launcher.SupportedLanguages; import org.cryptomator.ui.common.FxController; import org.cryptomator.ui.fxapp.FxApplicationWindows; import org.cryptomator.ui.traymenu.TrayMenuComponent; @@ -27,6 +29,7 @@ import javafx.scene.control.Toggle; import javafx.scene.control.ToggleGroup; import javafx.stage.Stage; import javafx.util.StringConverter; +import java.util.Locale; import java.util.Optional; import java.util.ResourceBundle; import java.util.Set; @@ -53,6 +56,7 @@ public class GeneralPreferencesController implements FxController { public CheckBox showMinimizeButtonCheckbox; public CheckBox showTrayIconCheckbox; public CheckBox startHiddenCheckbox; + public ChoiceBox preferredLanguageChoiceBox; public CheckBox debugModeCheckbox; public CheckBox autoStartCheckbox; public ToggleGroup nodeOrientation; @@ -90,6 +94,11 @@ public class GeneralPreferencesController implements FxController { startHiddenCheckbox.selectedProperty().bindBidirectional(settings.startHidden()); + preferredLanguageChoiceBox.getItems().add(null); + preferredLanguageChoiceBox.getItems().addAll(SupportedLanguages.LANGUAGAE_TAGS); + preferredLanguageChoiceBox.valueProperty().bindBidirectional(settings.languageProperty()); + preferredLanguageChoiceBox.setConverter(new LanguageTagConverter(resourceBundle)); + debugModeCheckbox.selectedProperty().bindBidirectional(settings.debugMode()); autoStartProvider.ifPresent(autoStart -> autoStartCheckbox.setSelected(autoStart.isEnabled())); @@ -183,6 +192,32 @@ public class GeneralPreferencesController implements FxController { } + private static class LanguageTagConverter extends StringConverter { + + private final ResourceBundle resourceBundle; + + LanguageTagConverter(ResourceBundle resourceBundle) { + this.resourceBundle = resourceBundle; + } + + @Override + public String toString(String tag) { + if (tag == null) { + return resourceBundle.getString("preferences.general.language.auto"); + } else { + var locale = Locale.forLanguageTag(tag); + var lang = locale.getDisplayLanguage(locale); + var region = locale.getDisplayCountry(locale); + return lang + (Strings.isNullOrEmpty(region) ? "" : " (" + region + ")"); + } + } + + @Override + public String fromString(String displayLanguage) { + throw new UnsupportedOperationException(); + } + } + private class KeychainProviderDisplayNameConverter extends StringConverter { @Override diff --git a/src/main/resources/fxml/preferences_general.fxml b/src/main/resources/fxml/preferences_general.fxml index ea087b9e3..1e3be74c6 100644 --- a/src/main/resources/fxml/preferences_general.fxml +++ b/src/main/resources/fxml/preferences_general.fxml @@ -38,6 +38,11 @@ + + + diff --git a/src/main/resources/i18n/strings.properties b/src/main/resources/i18n/strings.properties index 6648496b9..4c2ab688c 100644 --- a/src/main/resources/i18n/strings.properties +++ b/src/main/resources/i18n/strings.properties @@ -199,6 +199,8 @@ preferences.general.unlockThemes=Unlock dark mode preferences.general.showMinimizeButton=Show minimize button preferences.general.showTrayIcon=Show tray icon (requires restart) preferences.general.startHidden=Hide window when starting Cryptomator +preferences.general.language=Language (requires restart) +preferences.general.language.auto=System Default preferences.general.debugLogging=Enable debug logging preferences.general.debugDirectory=Reveal log files preferences.general.autoStart=Launch Cryptomator on system start From 71d346eddd8aae80ce84ba5c6aa804947087b8c1 Mon Sep 17 00:00:00 2001 From: Sebastian Stenzel Date: Sat, 2 Apr 2022 12:17:10 +0200 Subject: [PATCH 3/8] apply chosen language at application start --- .../org/cryptomator/launcher/Cryptomator.java | 5 +++- .../launcher/SupportedLanguages.java | 29 ++++++++++++++++++- 2 files changed, 32 insertions(+), 2 deletions(-) diff --git a/src/main/java/org/cryptomator/launcher/Cryptomator.java b/src/main/java/org/cryptomator/launcher/Cryptomator.java index a358126d4..90f391ec6 100644 --- a/src/main/java/org/cryptomator/launcher/Cryptomator.java +++ b/src/main/java/org/cryptomator/launcher/Cryptomator.java @@ -34,14 +34,16 @@ public class Cryptomator { private final LoggerConfiguration logConfig; private final DebugMode debugMode; + private final SupportedLanguages supportedLanguages; private final Environment env; private final Lazy ipcMessageHandler; private final ShutdownHook shutdownHook; @Inject - Cryptomator(LoggerConfiguration logConfig, DebugMode debugMode, Environment env, Lazy ipcMessageHandler, ShutdownHook shutdownHook) { + Cryptomator(LoggerConfiguration logConfig, DebugMode debugMode, SupportedLanguages supportedLanguages, Environment env, Lazy ipcMessageHandler, ShutdownHook shutdownHook) { this.logConfig = logConfig; this.debugMode = debugMode; + this.supportedLanguages = supportedLanguages; this.env = env; this.ipcMessageHandler = ipcMessageHandler; this.shutdownHook = shutdownHook; @@ -63,6 +65,7 @@ public class Cryptomator { logConfig.init(); LOG.info("Starting Cryptomator {} on {} {} ({})", env.getAppVersion().orElse("SNAPSHOT"), SystemUtils.OS_NAME, SystemUtils.OS_VERSION, SystemUtils.OS_ARCH); debugMode.initialize(); + supportedLanguages.applyPreferred(); /* * Attempts to create an IPC connection to a running Cryptomator instance and sends it the given args. diff --git a/src/main/java/org/cryptomator/launcher/SupportedLanguages.java b/src/main/java/org/cryptomator/launcher/SupportedLanguages.java index 7f891ed1f..e02e876d6 100644 --- a/src/main/java/org/cryptomator/launcher/SupportedLanguages.java +++ b/src/main/java/org/cryptomator/launcher/SupportedLanguages.java @@ -1,12 +1,39 @@ package org.cryptomator.launcher; -import java.util.List; +import org.cryptomator.common.settings.Settings; +import org.jetbrains.annotations.Nullable; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import javax.inject.Inject; +import javax.inject.Singleton; +import java.util.List; +import java.util.Locale; + +@Singleton public class SupportedLanguages { + private static final Logger LOG = LoggerFactory.getLogger(SupportedLanguages.class); // these are BCP 47 language codes, not ISO. Note the "-" instead of the "_": public static List LANGUAGAE_TAGS = List.of("ar", "bn", "bs", "ca", "cs", "de", "el", "es", "fil", "fr", "gl", "he", // "hi", "hr", "hu", "id", "it", "ja", "ko", "lv", "mk", "nb", "nl", "nn", "no", "pa", "pl", "pt", "pt-BR", "ro", "ru", // "sk", "sr", "sr-Latn", "sv", "ta", "te", "th", "tr", "uk", "zh", "zh-HK", "zh-TW"); + @Nullable + private final String preferredLanguage; + + @Inject + public SupportedLanguages(Settings settings) { + this.preferredLanguage = settings.languageProperty().get(); + } + + public void applyPreferred() { + if (preferredLanguage == null) { + LOG.debug("Using system locale"); + return; + } + var preferredLocale = Locale.forLanguageTag(preferredLanguage); + LOG.debug("Applying preferred locale {}", preferredLocale.getDisplayName(Locale.ENGLISH)); + Locale.setDefault(preferredLocale); + } } From 77d81acb1ed80391d49f8b6c5503e23bd481ccd6 Mon Sep 17 00:00:00 2001 From: Sebastian Stenzel Date: Sat, 2 Apr 2022 15:54:37 +0200 Subject: [PATCH 4/8] added "English" as first option --- src/main/java/org/cryptomator/launcher/SupportedLanguages.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/org/cryptomator/launcher/SupportedLanguages.java b/src/main/java/org/cryptomator/launcher/SupportedLanguages.java index e02e876d6..8057c14b7 100644 --- a/src/main/java/org/cryptomator/launcher/SupportedLanguages.java +++ b/src/main/java/org/cryptomator/launcher/SupportedLanguages.java @@ -15,7 +15,7 @@ public class SupportedLanguages { private static final Logger LOG = LoggerFactory.getLogger(SupportedLanguages.class); // these are BCP 47 language codes, not ISO. Note the "-" instead of the "_": - public static List LANGUAGAE_TAGS = List.of("ar", "bn", "bs", "ca", "cs", "de", "el", "es", "fil", "fr", "gl", "he", // + public static List LANGUAGAE_TAGS = List.of("en", "ar", "bn", "bs", "ca", "cs", "de", "el", "es", "fil", "fr", "gl", "he", // "hi", "hr", "hu", "id", "it", "ja", "ko", "lv", "mk", "nb", "nl", "nn", "no", "pa", "pl", "pt", "pt-BR", "ro", "ru", // "sk", "sr", "sr-Latn", "sv", "ta", "te", "th", "tr", "uk", "zh", "zh-HK", "zh-TW"); From 5dc8fd258283b24146e78dddb676d618fbc3c935 Mon Sep 17 00:00:00 2001 From: Sebastian Stenzel Date: Sun, 3 Apr 2022 12:25:15 +0200 Subject: [PATCH 5/8] made constant final --- .../java/org/cryptomator/launcher/SupportedLanguages.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/main/java/org/cryptomator/launcher/SupportedLanguages.java b/src/main/java/org/cryptomator/launcher/SupportedLanguages.java index 8057c14b7..d473dcdf8 100644 --- a/src/main/java/org/cryptomator/launcher/SupportedLanguages.java +++ b/src/main/java/org/cryptomator/launcher/SupportedLanguages.java @@ -15,9 +15,9 @@ public class SupportedLanguages { private static final Logger LOG = LoggerFactory.getLogger(SupportedLanguages.class); // these are BCP 47 language codes, not ISO. Note the "-" instead of the "_": - public static List LANGUAGAE_TAGS = List.of("en", "ar", "bn", "bs", "ca", "cs", "de", "el", "es", "fil", "fr", "gl", "he", // - "hi", "hr", "hu", "id", "it", "ja", "ko", "lv", "mk", "nb", "nl", "nn", "no", "pa", "pl", "pt", "pt-BR", "ro", "ru", // - "sk", "sr", "sr-Latn", "sv", "ta", "te", "th", "tr", "uk", "zh", "zh-HK", "zh-TW"); + public static final List LANGUAGAE_TAGS = List.of("en", "ar", "bn", "bs", "ca", "cs", "de", "el", "es", "fil", "fr", "gl", "he", // + "hi", "hr", "hu", "id", "it", "ja", "ko", "lv", "mk", "nb", "nl", "nn", "no", "pa", "pl", "pt", "pt-BR", "ro", "ru", "sk", "sr", // + "sr-Latn", "sv", "ta", "te", "th", "tr", "uk", "zh", "zh-HK", "zh-TW"); @Nullable private final String preferredLanguage; From d10c8fcf1783f646b49042643967d564cebe5d64 Mon Sep 17 00:00:00 2001 From: Sebastian Stenzel Date: Mon, 4 Apr 2022 15:13:27 +0200 Subject: [PATCH 6/8] amend settings deserialization test --- .../cryptomator/common/settings/SettingsJsonAdapterTest.java | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/test/java/org/cryptomator/common/settings/SettingsJsonAdapterTest.java b/src/test/java/org/cryptomator/common/settings/SettingsJsonAdapterTest.java index b2ecf4b89..a8bc4551a 100644 --- a/src/test/java/org/cryptomator/common/settings/SettingsJsonAdapterTest.java +++ b/src/test/java/org/cryptomator/common/settings/SettingsJsonAdapterTest.java @@ -29,6 +29,7 @@ public class SettingsJsonAdapterTest { ], "checkForUpdatesEnabled": true, "port": 8080, + "language": "de-DE", "numTrayNotifications": 42, "preferredVolumeImpl": "FUSE" } @@ -39,6 +40,7 @@ public class SettingsJsonAdapterTest { Assertions.assertTrue(settings.checkForUpdates().get()); Assertions.assertEquals(2, settings.getDirectories().size()); Assertions.assertEquals(8080, settings.port().get()); + Assertions.assertEquals("de-DE", settings.languageProperty().get()); Assertions.assertEquals(42, settings.numTrayNotifications().get()); Assertions.assertEquals(WebDavUrlScheme.DAV, settings.preferredGvfsScheme().get()); Assertions.assertEquals(VolumeImpl.FUSE, settings.preferredVolumeImpl().get()); From 508b9f5c6431065f6571abc0ce3496a9f92af035 Mon Sep 17 00:00:00 2001 From: Sebastian Stenzel Date: Mon, 4 Apr 2022 15:57:01 +0200 Subject: [PATCH 7/8] test if bundle for supported locales exist and are not empty --- .../launcher/SupportedLanguagesTest.java | 31 +++++++++++++++++++ 1 file changed, 31 insertions(+) create mode 100644 src/test/java/org/cryptomator/launcher/SupportedLanguagesTest.java diff --git a/src/test/java/org/cryptomator/launcher/SupportedLanguagesTest.java b/src/test/java/org/cryptomator/launcher/SupportedLanguagesTest.java new file mode 100644 index 000000000..5a9f1bc07 --- /dev/null +++ b/src/test/java/org/cryptomator/launcher/SupportedLanguagesTest.java @@ -0,0 +1,31 @@ +package org.cryptomator.launcher; + +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.MethodSource; + +import java.util.Locale; +import java.util.ResourceBundle; +import java.util.stream.Stream; + +public class SupportedLanguagesTest { + + @DisplayName("test if resource bundle is localized") + @ParameterizedTest(name = "{0}") + @MethodSource("languageTags") + public void testResourceBundleExists(String tag) { + var locale = Locale.forLanguageTag(tag); + Assertions.assertNotEquals("und", locale.toLanguageTag(), "Undefined language tag"); + + var bundle = Assertions.assertDoesNotThrow(() -> ResourceBundle.getBundle("/i18n/strings", locale)); + + Assertions.assertEquals(locale, bundle.getLocale()); + Assertions.assertFalse(bundle.keySet().isEmpty()); + } + + public static Stream languageTags() { + return SupportedLanguages.LANGUAGAE_TAGS.stream() // + .filter(tag -> !"en".equals(tag)); // english uses the default bundle + } +} \ No newline at end of file From 7c772e2767b5bab51365f5dbae1e1eac570d27f0 Mon Sep 17 00:00:00 2001 From: Sebastian Stenzel Date: Tue, 5 Apr 2022 07:46:17 +0200 Subject: [PATCH 8/8] removed `user.language=en` and `jdk.gtk.version=2` flags from run configurations [ci skip] --- .idea/runConfigurations/Cryptomator_Linux.xml | 2 +- .idea/runConfigurations/Cryptomator_Linux_Dev.xml | 2 +- .idea/runConfigurations/Cryptomator_Windows.xml | 2 +- .idea/runConfigurations/Cryptomator_Windows_Dev.xml | 2 +- .idea/runConfigurations/Cryptomator_macOS.xml | 2 +- .idea/runConfigurations/Cryptomator_macOS_Dev.xml | 2 +- 6 files changed, 6 insertions(+), 6 deletions(-) diff --git a/.idea/runConfigurations/Cryptomator_Linux.xml b/.idea/runConfigurations/Cryptomator_Linux.xml index 97bcd58df..81c2da004 100644 --- a/.idea/runConfigurations/Cryptomator_Linux.xml +++ b/.idea/runConfigurations/Cryptomator_Linux.xml @@ -2,7 +2,7 @@