From d41c05feaaf543c473a9db7aa5a3e564cee0e4ed Mon Sep 17 00:00:00 2001 From: Adrien Hopkins Date: Sat, 22 Feb 2025 17:29:28 -0500 Subject: Load locales from text files --- src/main/resources/locales/en.txt | 1 + 1 file changed, 1 insertion(+) create mode 100644 src/main/resources/locales/en.txt (limited to 'src/main/resources/locales/en.txt') diff --git a/src/main/resources/locales/en.txt b/src/main/resources/locales/en.txt new file mode 100644 index 0000000..9d453c9 --- /dev/null +++ b/src/main/resources/locales/en.txt @@ -0,0 +1 @@ +tv.title=7Units [v] \ No newline at end of file -- cgit v1.2.3 From a62fd9e6b4519ffcbd0503c45b159207ce438243 Mon Sep 17 00:00:00 2001 From: Adrien Hopkins Date: Sun, 23 Feb 2025 17:48:55 -0500 Subject: Localize all user-facing strings --- src/main/java/sevenUnitsGUI/TabbedView.java | 114 ++++++++++++++++++++-------- src/main/resources/locales/en.txt | 30 +++++++- 2 files changed, 110 insertions(+), 34 deletions(-) (limited to 'src/main/resources/locales/en.txt') diff --git a/src/main/java/sevenUnitsGUI/TabbedView.java b/src/main/java/sevenUnitsGUI/TabbedView.java index 1da119e..ca9f23c 100644 --- a/src/main/java/sevenUnitsGUI/TabbedView.java +++ b/src/main/java/sevenUnitsGUI/TabbedView.java @@ -24,13 +24,16 @@ import java.awt.event.ItemEvent; import java.awt.event.KeyEvent; import java.util.AbstractSet; import java.util.Collections; +import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; +import java.util.Map; import java.util.NoSuchElementException; import java.util.Objects; import java.util.Optional; import java.util.OptionalInt; import java.util.Set; +import java.util.function.Consumer; import java.util.function.Function; import javax.swing.BorderFactory; @@ -200,6 +203,8 @@ final class TabbedView implements ExpressionConversionView, UnitConversionView { private StandardRoundingType roundingType; private int precision; + private final Map> localizedTextSetters; + /** * Creates the view and makes it visible to the user * @@ -226,9 +231,13 @@ final class TabbedView implements ExpressionConversionView, UnitConversionView { this.masterPane = new JTabbedPane(); this.frame.add(this.masterPane); + this.localizedTextSetters = new HashMap<>(); + // ============ UNIT CONVERSION TAB ============ final JPanel convertUnitPanel = new JPanel(); this.masterPane.addTab("Convert Units", convertUnitPanel); + this.localizedTextSetters.put("tv.convert_units.title", + txt -> this.masterPane.setTitleAt(0, txt)); this.masterPane.setMnemonicAt(0, KeyEvent.VK_U); convertUnitPanel.setLayout(new BorderLayout()); @@ -264,7 +273,9 @@ final class TabbedView implements ExpressionConversionView, UnitConversionView { outputPanel.setLayout(new BorderLayout()); outputPanel.setBorder(new EmptyBorder(3, 6, 6, 6)); - final JLabel valuePrompt = new JLabel("Value to convert: "); + final JLabel valuePrompt = new JLabel(); + this.localizedTextSetters.put("tv.convert_units.value_prompt", + valuePrompt::setText); outputPanel.add(valuePrompt, BorderLayout.LINE_START); this.valueInput = new JTextField(); @@ -272,6 +283,8 @@ final class TabbedView implements ExpressionConversionView, UnitConversionView { // conversion button this.convertUnitButton = new JButton("Convert"); + this.localizedTextSetters.put("tv.convert_units.convert_btn", + this.convertUnitButton::setText); outputPanel.add(this.convertUnitButton, BorderLayout.LINE_END); this.convertUnitButton .addActionListener(e -> this.presenter.convertUnits()); @@ -287,20 +300,26 @@ final class TabbedView implements ExpressionConversionView, UnitConversionView { final JPanel convertExpressionPanel = new JPanel(); this.masterPane.addTab("Convert Unit Expressions", convertExpressionPanel); + this.localizedTextSetters.put("tv.convert_expressions.title", + txt -> this.masterPane.setTitleAt(1, txt)); this.masterPane.setMnemonicAt(1, KeyEvent.VK_E); convertExpressionPanel.setLayout(new GridLayout(4, 1)); // from and to expressions this.fromEntry = new JTextField(); convertExpressionPanel.add(this.fromEntry); - this.fromEntry.setBorder(BorderFactory.createTitledBorder("From")); + this.localizedTextSetters.put("tv.convert_expressions.from", + txt -> this.fromEntry.setBorder(BorderFactory.createTitledBorder(txt))); this.toEntry = new JTextField(); convertExpressionPanel.add(this.toEntry); - this.toEntry.setBorder(BorderFactory.createTitledBorder("To")); + this.localizedTextSetters.put("tv.convert_expressions.to", + txt -> this.toEntry.setBorder(BorderFactory.createTitledBorder(txt))); // button to convert - this.convertExpressionButton = new JButton("Convert"); + this.convertExpressionButton = new JButton(); + this.localizedTextSetters.put("tv.convert_expressions.convert_btn", + this.convertExpressionButton::setText); convertExpressionPanel.add(this.convertExpressionButton); this.convertExpressionButton @@ -310,13 +329,15 @@ final class TabbedView implements ExpressionConversionView, UnitConversionView { // output of conversion this.expressionOutput = new JTextArea(2, 32); convertExpressionPanel.add(this.expressionOutput); - this.expressionOutput - .setBorder(BorderFactory.createTitledBorder("Output")); + this.localizedTextSetters.put("tv.convert_expressions.output", + txt -> this.expressionOutput.setBorder(BorderFactory.createTitledBorder(txt))); this.expressionOutput.setEditable(false); // =========== UNIT VIEWER =========== final JPanel unitLookupPanel = new JPanel(); this.masterPane.addTab("Unit Viewer", unitLookupPanel); + this.localizedTextSetters.put("tv.unit_viewer.title", + txt -> this.masterPane.setTitleAt(2, txt)); this.masterPane.setMnemonicAt(2, KeyEvent.VK_V); unitLookupPanel.setLayout(new GridLayout()); @@ -334,6 +355,8 @@ final class TabbedView implements ExpressionConversionView, UnitConversionView { // ============ PREFIX VIEWER ============= final JPanel prefixLookupPanel = new JPanel(); this.masterPane.addTab("Prefix Viewer", prefixLookupPanel); + this.localizedTextSetters.put("tv.prefix_viewer.title", + txt -> this.masterPane.setTitleAt(3, txt)); this.masterPane.setMnemonicAt(3, KeyEvent.VK_P); prefixLookupPanel.setLayout(new GridLayout(1, 2)); @@ -389,7 +412,8 @@ final class TabbedView implements ExpressionConversionView, UnitConversionView { { final JPanel roundingPanel = new JPanel(); settingsPanel.add(roundingPanel); - roundingPanel.setBorder(new TitledBorder("Rounding Settings")); + this.localizedTextSetters.put("tv.settings.rounding.title", + txt -> roundingPanel.setBorder(new TitledBorder(txt))); roundingPanel.setLayout(new GridBagLayout()); // rounding rule selection @@ -399,13 +423,17 @@ final class TabbedView implements ExpressionConversionView, UnitConversionView { "Presenter loaded non-standard rounding rule")); this.precision = this.getPresenterPrecision().orElse(6); - final JLabel roundingRuleLabel = new JLabel("Rounding Rule:"); + final JLabel roundingRuleLabel = new JLabel(); + this.localizedTextSetters.put("tv.settings.rounding.rule", + roundingRuleLabel::setText); roundingPanel.add(roundingRuleLabel, new GridBagBuilder(0, 0) .setAnchor(GridBagConstraints.LINE_START).build()); // sigDigSlider needs to be first so that the rounding-type buttons can // show and hide it - final JLabel sliderLabel = new JLabel("Precision:"); + final JLabel sliderLabel = new JLabel(); + this.localizedTextSetters.put("tv.settings.rounding.precision", + sliderLabel::setText); sliderLabel.setVisible( this.roundingType != StandardRoundingType.UNCERTAINTY); roundingPanel.add(sliderLabel, new GridBagBuilder(0, 4) @@ -431,8 +459,9 @@ final class TabbedView implements ExpressionConversionView, UnitConversionView { }); // significant digit rounding - final JRadioButton fixedPrecision = new JRadioButton( - "Fixed Precision"); + final JRadioButton fixedPrecision = new JRadioButton(); + this.localizedTextSetters.put("tv.settings.rounding.fixed_sigfig", + fixedPrecision::setText); if (this.roundingType == StandardRoundingType.SIGNIFICANT_DIGITS) { fixedPrecision.setSelected(true); } @@ -447,8 +476,9 @@ final class TabbedView implements ExpressionConversionView, UnitConversionView { .setAnchor(GridBagConstraints.LINE_START).build()); // decimal place rounding - final JRadioButton fixedDecimals = new JRadioButton( - "Fixed Decimal Places"); + final JRadioButton fixedDecimals = new JRadioButton(); + this.localizedTextSetters.put("tv.settings.rounding.fixed_places", + fixedDecimals::setText); if (this.roundingType == StandardRoundingType.DECIMAL_PLACES) { fixedDecimals.setSelected(true); } @@ -463,8 +493,9 @@ final class TabbedView implements ExpressionConversionView, UnitConversionView { .setAnchor(GridBagConstraints.LINE_START).build()); // scientific rounding - final JRadioButton relativePrecision = new JRadioButton( - "Uncertainty-Based Rounding"); + final JRadioButton relativePrecision = new JRadioButton(); + this.localizedTextSetters.put("tv.settings.rounding.uncertainty", + relativePrecision::setText); if (this.roundingType == StandardRoundingType.UNCERTAINTY) { relativePrecision.setSelected(true); } @@ -483,8 +514,8 @@ final class TabbedView implements ExpressionConversionView, UnitConversionView { { final JPanel prefixRepetitionPanel = new JPanel(); settingsPanel.add(prefixRepetitionPanel); - prefixRepetitionPanel - .setBorder(new TitledBorder("Prefix Repetition Settings")); + this.localizedTextSetters.put("tv.settings.repetition.title", + txt -> prefixRepetitionPanel.setBorder(new TitledBorder(txt))); prefixRepetitionPanel.setLayout(new GridBagLayout()); final var prefixRule = this.getPresenterPrefixRule() @@ -494,7 +525,9 @@ final class TabbedView implements ExpressionConversionView, UnitConversionView { // prefix rules final ButtonGroup prefixRuleButtons = new ButtonGroup(); - final JRadioButton noRepetition = new JRadioButton("No Repetition"); + final JRadioButton noRepetition = new JRadioButton(); + this.localizedTextSetters.put("tv.settings.repetition.no", + noRepetition::setText); if (prefixRule == DefaultPrefixRepetitionRule.NO_REPETITION) { noRepetition.setSelected(true); } @@ -507,7 +540,9 @@ final class TabbedView implements ExpressionConversionView, UnitConversionView { prefixRepetitionPanel.add(noRepetition, new GridBagBuilder(0, 0) .setAnchor(GridBagConstraints.LINE_START).build()); - final JRadioButton noRestriction = new JRadioButton("No Restriction"); + final JRadioButton noRestriction = new JRadioButton(); + this.localizedTextSetters.put("tv.settings.repetition.any", + noRestriction::setText); if (prefixRule == DefaultPrefixRepetitionRule.NO_RESTRICTION) { noRestriction.setSelected(true); } @@ -520,8 +555,9 @@ final class TabbedView implements ExpressionConversionView, UnitConversionView { prefixRepetitionPanel.add(noRestriction, new GridBagBuilder(0, 1) .setAnchor(GridBagConstraints.LINE_START).build()); - final JRadioButton customRepetition = new JRadioButton( - "Complex Repetition"); + final JRadioButton customRepetition = new JRadioButton(); + this.localizedTextSetters.put("tv.settings.repetition.complex", + customRepetition::setText); if (prefixRule == DefaultPrefixRepetitionRule.COMPLEX_REPETITION) { customRepetition.setSelected(true); } @@ -539,7 +575,8 @@ final class TabbedView implements ExpressionConversionView, UnitConversionView { { final JPanel searchingPanel = new JPanel(); settingsPanel.add(searchingPanel); - searchingPanel.setBorder(new TitledBorder("Search Settings")); + this.localizedTextSetters.put("tv.settings.search.title", + txt -> searchingPanel.setBorder(new TitledBorder(txt))); searchingPanel.setLayout(new GridBagLayout()); // searching rules @@ -547,8 +584,9 @@ final class TabbedView implements ExpressionConversionView, UnitConversionView { final var searchRule = this.presenter.getSearchRule(); - final JRadioButton noPrefixes = new JRadioButton( - "Never Include Prefixed Units"); + final JRadioButton noPrefixes = new JRadioButton(); + this.localizedTextSetters.put("tv.settings.search.no_prefixes", + noPrefixes::setText); noPrefixes.addActionListener(e -> { this.presenter.setSearchRule(PrefixSearchRule.NO_PREFIXES); this.presenter.updateView(); @@ -558,8 +596,9 @@ final class TabbedView implements ExpressionConversionView, UnitConversionView { searchingPanel.add(noPrefixes, new GridBagBuilder(0, 0) .setAnchor(GridBagConstraints.LINE_START).build()); - final JRadioButton commonPrefixes = new JRadioButton( - "Include Common Prefixes"); + final JRadioButton commonPrefixes = new JRadioButton(); + this.localizedTextSetters.put("tv.settings.search.common_prefixes", + commonPrefixes::setText); commonPrefixes.addActionListener(e -> { this.presenter.setSearchRule(PrefixSearchRule.COMMON_PREFIXES); this.presenter.updateView(); @@ -569,8 +608,9 @@ final class TabbedView implements ExpressionConversionView, UnitConversionView { searchingPanel.add(commonPrefixes, new GridBagBuilder(0, 1) .setAnchor(GridBagConstraints.LINE_START).build()); - final JRadioButton alwaysInclude = new JRadioButton( - "Include All Single Prefixes"); + final JRadioButton alwaysInclude = new JRadioButton(); + this.localizedTextSetters.put("tv.settings.search.all_prefixes", + alwaysInclude::setText); alwaysInclude.addActionListener(e -> { this.presenter .setSearchRule(this.presenter.getUniversalSearchRule()); @@ -599,7 +639,8 @@ final class TabbedView implements ExpressionConversionView, UnitConversionView { settingsPanel.add(miscPanel); miscPanel.setLayout(new GridBagLayout()); - final JCheckBox oneWay = new JCheckBox("Convert One Way Only"); + final JCheckBox oneWay = new JCheckBox(); + this.localizedTextSetters.put("tv.settings.oneway", oneWay::setText); oneWay.setSelected(this.presenter.oneWayConversionEnabled()); oneWay.addItemListener(e -> { this.presenter.setOneWayConversionEnabled( @@ -609,8 +650,9 @@ final class TabbedView implements ExpressionConversionView, UnitConversionView { miscPanel.add(oneWay, new GridBagBuilder(0, 0, 2, 1) .setAnchor(GridBagConstraints.LINE_START).build()); - final JCheckBox showAllVariations = new JCheckBox( - "Show Duplicate Units & Prefixes"); + final JCheckBox showAllVariations = new JCheckBox(); + this.localizedTextSetters.put("tv.settings.show_duplicate", + showAllVariations::setText); showAllVariations.setSelected(this.presenter.duplicatesShown()); showAllVariations.addItemListener(e -> { this.presenter @@ -620,7 +662,9 @@ final class TabbedView implements ExpressionConversionView, UnitConversionView { miscPanel.add(showAllVariations, new GridBagBuilder(0, 1, 2, 1) .setAnchor(GridBagConstraints.LINE_START).build()); - final JLabel localeLabel = new JLabel("Locale:"); + final JLabel localeLabel = new JLabel(); + this.localizedTextSetters.put("tv.settings.locale", + localeLabel::setText); miscPanel.add(localeLabel, new GridBagBuilder(0, 2, 1, 1) .setAnchor(GridBagConstraints.LINE_START).build()); @@ -634,7 +678,9 @@ final class TabbedView implements ExpressionConversionView, UnitConversionView { miscPanel.add(localeSelector, new GridBagBuilder(1, 2, 1, 1) .setAnchor(GridBagConstraints.LINE_END).build()); - final JButton unitFileButton = new JButton("Manage Unit Data Files"); + final JButton unitFileButton = new JButton(); + this.localizedTextSetters.put("tv.settings.unitfiles.button", + unitFileButton::setText); unitFileButton.setEnabled(false); miscPanel.add(unitFileButton, new GridBagBuilder(0, 3, 2, 1) .setAnchor(GridBagConstraints.LINE_START).build()); @@ -849,5 +895,7 @@ final class TabbedView implements ExpressionConversionView, UnitConversionView { public void updateText() { this.frame.setTitle(this.presenter.getLocalizedText("tv.title") .replace("[v]", ProgramInfo.VERSION.toString())); + this.localizedTextSetters.forEach((id, action) -> + action.accept(this.presenter.getLocalizedText(id))); } } diff --git a/src/main/resources/locales/en.txt b/src/main/resources/locales/en.txt index 9d453c9..19ed781 100644 --- a/src/main/resources/locales/en.txt +++ b/src/main/resources/locales/en.txt @@ -1 +1,29 @@ -tv.title=7Units [v] \ No newline at end of file +tv.title=7Units [v] +tv.convert_units.title=Convert Units +tv.convert_units.value_prompt=Value to convert: +tv.convert_units.convert_btn=Convert +tv.convert_expressions.title=Convert Unit Expressions +tv.convert_expressions.from=From +tv.convert_expressions.to=To +tv.convert_expressions.convert_btn=Convert +tv.convert_expressions.output=Output +tv.unit_viewer.title=Unit Viewer +tv.prefix_viewer.title=Prefix Viewer +tv.settings.rounding.title=Rounding Settings +tv.settings.rounding.rule=Rounding Rule: +tv.settings.rounding.precision=Precision: +tv.settings.rounding.fixed_sigfig=Fixed Precision +tv.settings.rounding.fixed_places=Fixed Decimal Places +tv.settings.rounding.uncertainty=Uncertainty-based Rounding +tv.settings.repetition.title=Prefix Repetition Settings +tv.settings.repetition.no=No Repetition +tv.settings.repetition.any=No Restriction +tv.settings.repetition.complex=Complex Repetition +tv.settings.search.title=Search Settings +tv.settings.search.no_prefixes=Never Include Prefixed Units +tv.settings.search.common_prefixes=Include Common Prefixes +tv.settings.search.all_prefixes=Include All Single Prefixes +tv.settings.oneway=Convert One Way Only +tv.settings.show_duplicate=Show Duplicate Units & Prefixes +tv.settings.locale=🌐 Locale: +tv.settings.unitfiles.button=Manage Unit Data Files -- cgit v1.2.3 From 4436b29053a0b757562ecc1d0a78e22902e6e5ae Mon Sep 17 00:00:00 2001 From: Adrien Hopkins Date: Sun, 23 Feb 2025 19:20:30 -0500 Subject: Allow default datafile to be disabled If this option is deselected, the default unit, prefix, dimension and metric exception data will not be loaded, and only custom data and the few units that are not provided by files will be available. The main rationale for this change is so that the data can be localized by custom unit files. --- src/main/java/sevenUnitsGUI/Presenter.java | 93 +++++++++++++++++++++----- src/main/java/sevenUnitsGUI/TabbedView.java | 18 ++++- src/main/resources/locales/en.txt | 1 + src/main/resources/locales/fr.txt | 1 + src/test/java/sevenUnitsGUI/PresenterTest.java | 18 +++++ 5 files changed, 110 insertions(+), 21 deletions(-) (limited to 'src/main/resources/locales/en.txt') diff --git a/src/main/java/sevenUnitsGUI/Presenter.java b/src/main/java/sevenUnitsGUI/Presenter.java index 6467f03..ba600e3 100644 --- a/src/main/java/sevenUnitsGUI/Presenter.java +++ b/src/main/java/sevenUnitsGUI/Presenter.java @@ -347,6 +347,19 @@ public final class Presenter { * unit view in views that show units as a list to choose from. */ private boolean showDuplicates = false; + + /** + * The default unit, prefix, dimension and exception data will only be loaded + * if this variable is true. + */ + private boolean useDefaultDatafiles = true; + + /** Custom unit datafiles that will be loaded by {@link #reloadData} */ + private final Set customUnitFiles = new HashSet<>(); + /** Custom dimension datafiles that will be loaded by {@link #reloadData} */ + private final Set customDimensionFiles = new HashSet<>(); + /** Custom exception datafiles that will be loaded by {@link #reloadData} */ + private final Set customExceptionFiles = new HashSet<>(); /** * Creates a Presenter @@ -357,8 +370,45 @@ public final class Presenter { public Presenter(View view) { this.view = view; this.database = new UnitDatabase(); - addDefaults(this.database); + this.metricExceptions = new HashSet<>(); + + this.locales = this.loadLocales(); + this.userLocale = DEFAULT_LOCALE; + + // set default settings temporarily + if (Files.exists(CONFIG_FILE)) { + this.loadSettings(CONFIG_FILE); + } + this.reloadData(); + + // print out unit counts + System.out.println(this.loadStatMsg()); + } + + /** + * Clears then reloads all unit, prefix, dimension and exception data. + */ + public void reloadData() { + this.database.clear(); + this.metricExceptions.clear(); + addDefaults(this.database); + + if (this.useDefaultDatafiles) { + this.loadDefaultData(); + } + + this.customUnitFiles.forEach( + path -> this.handleLoadErrors(this.database.loadUnitsFile(path))); + this.customDimensionFiles.forEach( + path -> this.handleLoadErrors(this.database.loadDimensionFile(path))); + this.customExceptionFiles.forEach(this::loadExceptionFile); + } + + /** + * Load units, prefixes and dimensions from the default files. + */ + private void loadDefaultData() { // load units and prefixes try (final var units = inputStream(DEFAULT_UNITS_FILEPATH)) { this.handleLoadErrors(this.database.loadUnitsFromStream(units)); @@ -377,7 +427,6 @@ public final class Presenter { // load metric exceptions try { - this.metricExceptions = new HashSet<>(); try (var exceptions = inputStream(DEFAULT_EXCEPTIONS_FILEPATH); var scanner = new Scanner(exceptions)) { while (scanner.hasNextLine()) { @@ -392,17 +441,6 @@ public final class Presenter { throw new AssertionError("Loading of metric_exceptions.txt failed.", e); } - - this.locales = this.loadLocales(); - this.userLocale = DEFAULT_LOCALE; - - // set default settings temporarily - if (Files.exists(CONFIG_FILE)) { - this.loadSettings(CONFIG_FILE); - } - - // print out unit counts - System.out.println(this.loadStatMsg()); } /** @@ -962,6 +1000,10 @@ public final class Presenter { * @since 2021-12-15 */ void loadSettings(Path settingsFile) { + this.customDimensionFiles.clear(); + this.customExceptionFiles.clear(); + this.customUnitFiles.clear(); + for (final Map.Entry setting : this .settingsFromFile(settingsFile)) { final var value = setting.getValue(); @@ -970,15 +1012,13 @@ public final class Presenter { // set manually to avoid the unnecessary saving of the non-manual // methods case "custom_dimension_file": - this.handleLoadErrors( - this.database.loadDimensionFile(pathFromConfig(value))); + this.customDimensionFiles.add(pathFromConfig(value)); break; case "custom_exception_file": - this.loadExceptionFile(pathFromConfig(value)); + this.customExceptionFiles.add(pathFromConfig(value)); break; case "custom_unit_file": - this.handleLoadErrors( - this.database.loadUnitsFile(pathFromConfig(value))); + this.customUnitFiles.add(pathFromConfig(value)); break; case "number_display_rule": this.setDisplayRuleFromString(value); @@ -1204,6 +1244,16 @@ public final class Presenter { return null; } } + + /** + * Sets whether or not the default datafiles will be loaded. + * This method automatically updates the view's units. + */ + public void setUseDefaultDatafiles(boolean useDefaultDatafiles) { + this.useDefaultDatafiles = useDefaultDatafiles; + this.reloadData(); + this.updateView(); + } /** * Sets the user's locale, updating the view. @@ -1304,6 +1354,13 @@ public final class Presenter { ucview.setToUnitNames(toNames); } } + + /** + * @return true iff the default datafiles are being used + */ + public boolean usingDefaultDatafiles() { + return this.useDefaultDatafiles; + } /** * @param message message to add diff --git a/src/main/java/sevenUnitsGUI/TabbedView.java b/src/main/java/sevenUnitsGUI/TabbedView.java index ca9f23c..40ed0a7 100644 --- a/src/main/java/sevenUnitsGUI/TabbedView.java +++ b/src/main/java/sevenUnitsGUI/TabbedView.java @@ -662,10 +662,22 @@ final class TabbedView implements ExpressionConversionView, UnitConversionView { miscPanel.add(showAllVariations, new GridBagBuilder(0, 1, 2, 1) .setAnchor(GridBagConstraints.LINE_START).build()); + final JCheckBox useDefaultFiles = new JCheckBox(); + this.localizedTextSetters.put("tv.settings.use_default_files", + useDefaultFiles::setText); + useDefaultFiles.setSelected(this.presenter.usingDefaultDatafiles()); + useDefaultFiles.addItemListener(e -> { + this.presenter + .setUseDefaultDatafiles(e.getStateChange() == ItemEvent.SELECTED); + this.presenter.saveSettings(); + }); + miscPanel.add(useDefaultFiles, new GridBagBuilder(0, 2, 2, 1) + .setAnchor(GridBagConstraints.LINE_START).build()); + final JLabel localeLabel = new JLabel(); this.localizedTextSetters.put("tv.settings.locale", localeLabel::setText); - miscPanel.add(localeLabel, new GridBagBuilder(0, 2, 1, 1) + miscPanel.add(localeLabel, new GridBagBuilder(0, 3, 1, 1) .setAnchor(GridBagConstraints.LINE_START).build()); this.presenter.getAvailableLocales().stream().sorted() @@ -675,14 +687,14 @@ final class TabbedView implements ExpressionConversionView, UnitConversionView { this.presenter.setUserLocale((String) e.getItem()); this.presenter.saveSettings(); }); - miscPanel.add(localeSelector, new GridBagBuilder(1, 2, 1, 1) + miscPanel.add(localeSelector, new GridBagBuilder(1, 3, 1, 1) .setAnchor(GridBagConstraints.LINE_END).build()); final JButton unitFileButton = new JButton(); this.localizedTextSetters.put("tv.settings.unitfiles.button", unitFileButton::setText); unitFileButton.setEnabled(false); - miscPanel.add(unitFileButton, new GridBagBuilder(0, 3, 2, 1) + miscPanel.add(unitFileButton, new GridBagBuilder(0, 4, 2, 1) .setAnchor(GridBagConstraints.LINE_START).build()); } diff --git a/src/main/resources/locales/en.txt b/src/main/resources/locales/en.txt index 19ed781..5173cf3 100644 --- a/src/main/resources/locales/en.txt +++ b/src/main/resources/locales/en.txt @@ -25,5 +25,6 @@ tv.settings.search.common_prefixes=Include Common Prefixes tv.settings.search.all_prefixes=Include All Single Prefixes tv.settings.oneway=Convert One Way Only tv.settings.show_duplicate=Show Duplicate Units & Prefixes +tv.settings.use_default_files=Use Default Datafiles tv.settings.locale=🌐 Locale: tv.settings.unitfiles.button=Manage Unit Data Files diff --git a/src/main/resources/locales/fr.txt b/src/main/resources/locales/fr.txt index d25e2b0..e8b7138 100644 --- a/src/main/resources/locales/fr.txt +++ b/src/main/resources/locales/fr.txt @@ -25,5 +25,6 @@ tv.settings.search.common_prefixes=Inclure prĂ©fixes frĂ©quents tv.settings.search.all_prefixes=Inclure tous prĂ©fixes seuls tv.settings.oneway=Convertir Seulement en un Direction tv.settings.show_duplicate=Montrer unitĂ©s et prĂ©fixes doubles +tv.settings.use_default_files=Utilise donĂ©es par dĂ©faut tv.settings.locale=🌐 Locale: tv.settings.unitfiles.button=GĂ©rer donĂ©es d’unitĂ©s diff --git a/src/test/java/sevenUnitsGUI/PresenterTest.java b/src/test/java/sevenUnitsGUI/PresenterTest.java index 9e25a08..8b16365 100644 --- a/src/test/java/sevenUnitsGUI/PresenterTest.java +++ b/src/test/java/sevenUnitsGUI/PresenterTest.java @@ -17,6 +17,7 @@ package sevenUnitsGUI; import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertTrue; import static org.junit.jupiter.api.Assumptions.assumeTrue; @@ -154,6 +155,23 @@ public final class PresenterTest { expectedOutput.getValue().toString(false, RoundingMode.HALF_EVEN)); assertEquals(List.of(expectedUC), viewBot.unitConversionList()); } + + /** + * Ensures that the default unitfile can be disabled. + * + * @since v1.0.0 + * @since 2025-02-23 + */ + @Test + void testDisableDefault() { + final var viewBot = new ViewBot(); + final var presenter = new Presenter(viewBot); + assumeTrue(presenter.database.containsUnitName("joule"), + "Attempted to test disabling default on unit not in default file."); + presenter.setUseDefaultDatafiles(false); + assertFalse(presenter.database.containsUnitName("joule"), + "Presenter disabled default datafiles, but still contains the joule."); + } /** * Tests that duplicate units are successfully removed, if that is asked for -- cgit v1.2.3 From 9c358d708ba4988648d7b19ccb842f076ec4c354 Mon Sep 17 00:00:00 2001 From: Adrien Hopkins Date: Sun, 23 Feb 2025 20:23:47 -0500 Subject: Allow internationalization of about.txt This works with custom locales (by placing the text in [config_dir]/about/[name].txt), but if such a file does not exist, it will default to the default locale (en)'s about text. --- src/main/java/sevenUnitsGUI/Presenter.java | 43 +++++++++++++++++++++-------- src/main/java/sevenUnitsGUI/TabbedView.java | 13 +++++---- src/main/resources/about.txt | 25 ----------------- src/main/resources/about/en.txt | 25 +++++++++++++++++ src/main/resources/about/fr.txt | 24 ++++++++++++++++ src/main/resources/locales/en.txt | 1 + src/main/resources/locales/fr.txt | 1 + 7 files changed, 90 insertions(+), 42 deletions(-) delete mode 100644 src/main/resources/about.txt create mode 100644 src/main/resources/about/en.txt create mode 100644 src/main/resources/about/fr.txt (limited to 'src/main/resources/locales/en.txt') diff --git a/src/main/java/sevenUnitsGUI/Presenter.java b/src/main/java/sevenUnitsGUI/Presenter.java index ba600e3..3a039a7 100644 --- a/src/main/java/sevenUnitsGUI/Presenter.java +++ b/src/main/java/sevenUnitsGUI/Presenter.java @@ -780,7 +780,27 @@ public final class Presenter { * @since 2022-02-19 */ public String getAboutText() { - return Presenter.getLinesFromResource("/about.txt").stream() + final Path customFilepath = Presenter.pathFromConfig( + "about/" + this.userLocale + ".txt"); + if (Files.exists(customFilepath)) { + try { + return formatAboutText(Files.lines(customFilepath)); + } catch (IOException e) { + final String filename = String.format("/about/%s.txt", this.userLocale); + return formatAboutText(Presenter.getLinesFromResource(filename).stream()); + } + } else if (LOCAL_LOCALES.contains(this.userLocale)) { + final String filename = String.format("/about/%s.txt", this.userLocale); + return formatAboutText(Presenter.getLinesFromResource(filename).stream()); + } else { + final String filename = String.format("/about/%s.txt", DEFAULT_LOCALE); + return formatAboutText(Presenter.getLinesFromResource(filename).stream()); + } + + } + + private String formatAboutText(Stream rawLines) { + return rawLines .map(Presenter::withoutComments).collect(Collectors.joining("\n")) .replaceAll("\\[VERSION\\]", ProgramInfo.VERSION.toString()) .replaceAll("\\[LOADSTATS\\]", wrapString(this.loadStatMsg(), 72)); @@ -1057,16 +1077,17 @@ public final class Presenter { * @since 2024-08-22 */ private String loadStatMsg() { - return String.format( - "Successfully loaded %d unique units with %d names (%d base units), %d unique prefixes with %d names, %d unit sets, and %d named dimensions.", - this.database.unitMapPrefixless(false).size(), - this.database.unitMapPrefixless(true).size(), - this.database.unitMapPrefixless(false).values().stream() - .filter(IS_FULL_BASE).count(), - this.database.prefixMap(false).size(), - this.database.prefixMap(true).size(), - this.database.unitSetMap().size(), - this.database.dimensionMap().size()); + return this.getLocalizedText("load_stat_msg") + .replace("[u]", Integer.toString( + this.database.unitMapPrefixless(false).size())) + .replace("[un]", Integer.toString( + this.database.unitMapPrefixless(true).size())) + .replace("[b]", Long.toString(this.database.unitMapPrefixless(false) + .values().stream().filter(IS_FULL_BASE).count())) + .replace("[p]", Integer.toString(this.database.prefixMap(false).size())) + .replace("[pn]", Integer.toString(this.database.prefixMap(true).size())) + .replace("[s]", Integer.toString(this.database.unitSetMap().size())) + .replace("[d]", Integer.toString(this.database.dimensionMap().size())); } /** diff --git a/src/main/java/sevenUnitsGUI/TabbedView.java b/src/main/java/sevenUnitsGUI/TabbedView.java index 40ed0a7..9850aac 100644 --- a/src/main/java/sevenUnitsGUI/TabbedView.java +++ b/src/main/java/sevenUnitsGUI/TabbedView.java @@ -198,7 +198,8 @@ final class TabbedView implements ExpressionConversionView, UnitConversionView { /** The text box for prefix data in the prefix viewer */ private final JTextArea prefixTextBox; - // SETTINGS STUFF + // INFO & SETTINGS STUFF + final JTextArea infoTextArea; private final JComboBox localeSelector; private StandardRoundingType roundingType; private int precision; @@ -377,11 +378,10 @@ final class TabbedView implements ExpressionConversionView, UnitConversionView { this.masterPane.addTab("\uD83D\uDEC8", // info (i) character new JScrollPane(infoPanel)); - final JTextArea infoTextArea = new JTextArea(); - infoTextArea.setEditable(false); - infoTextArea.setOpaque(false); - infoPanel.add(infoTextArea); - infoTextArea.setText(this.presenter.getAboutText()); + this.infoTextArea = new JTextArea(); + this.infoTextArea.setEditable(false); + this.infoTextArea.setOpaque(false); + infoPanel.add(this.infoTextArea); // ============ SETTINGS PANEL ============ this.localeSelector = new JComboBox<>(); @@ -907,6 +907,7 @@ final class TabbedView implements ExpressionConversionView, UnitConversionView { public void updateText() { this.frame.setTitle(this.presenter.getLocalizedText("tv.title") .replace("[v]", ProgramInfo.VERSION.toString())); + this.infoTextArea.setText(this.presenter.getAboutText()); this.localizedTextSetters.forEach((id, action) -> action.accept(this.presenter.getLocalizedText(id))); } diff --git a/src/main/resources/about.txt b/src/main/resources/about.txt deleted file mode 100644 index 4c33f8c..0000000 --- a/src/main/resources/about.txt +++ /dev/null @@ -1,25 +0,0 @@ -About 7Units Version [VERSION] - -7Units is a unit converter program with many features, -inspired by GNU Units (https://www.gnu.org/software/units/). -You can use it to simply convert units, but you can also -use it like a calculator, computing and converting expressions -like "10 m/s + (25^2 - 5^2) mi/hr". - -This software was written by Adrien Hopkins -. - -Unit/Prefix/Dimension Statistics: - -[LOADSTATS] - -Copyright Notice: - -Unit Converter Copyright (C) 2018-2024 Adrien Hopkins -This program comes with ABSOLUTELY NO WARRANTY; -for details read the LICENSE file, section 15 - -This is free software, and you are welcome to redistribute -it under certain conditions; for details go to - -or read the LICENSE file. diff --git a/src/main/resources/about/en.txt b/src/main/resources/about/en.txt new file mode 100644 index 0000000..068f922 --- /dev/null +++ b/src/main/resources/about/en.txt @@ -0,0 +1,25 @@ +About 7Units Version [VERSION] + +7Units is a unit converter program with many features, +inspired by GNU Units (https://www.gnu.org/software/units/). +You can use it to simply convert units, but you can also +use it like a calculator, computing and converting expressions +like "10 m/s + (25^2 - 5^2) mi/hr". + +This software was written by Adrien Hopkins +. + +Unit/Prefix/Dimension Statistics: + +[LOADSTATS] + +Copyright Notice: + +Unit Converter Copyright (C) 2018-2025 Adrien Hopkins +This program comes with ABSOLUTELY NO WARRANTY; +for details read the LICENSE file, section 15 + +This is free software, and you are welcome to redistribute +it under certain conditions; for details go to + +or read the LICENSE file. diff --git a/src/main/resources/about/fr.txt b/src/main/resources/about/fr.txt new file mode 100644 index 0000000..d8d82aa --- /dev/null +++ b/src/main/resources/about/fr.txt @@ -0,0 +1,24 @@ +À propos de 7UnitĂ©s version [VERSION] + +7UnitĂ©s est une programme pour convertir les unitĂ©s avec plusieurs fonctions, +inspirĂ© par GNU Units (https://www.gnu.org/software/units/). +Vous pouvez l’utiliser pour convertir des unitĂ©s, mais vous pouvez aussi +l’utiliser comme calculatrice, computer et convertir des expressions +comme "10 m/s + (25^2 - 5^2) mi/hr". + +Ce logiciel est par Adrien Hopkins . + +Statistiques d’unitĂ©s, prĂ©fixes et dimensions: + +[LOADSTATS] + +Copyright Notice: + +Unit Converter Copyright (C) 2018-2025 Adrien Hopkins +This program comes with ABSOLUTELY NO WARRANTY; +for details read the LICENSE file, section 15 + +This is free software, and you are welcome to redistribute +it under certain conditions; for details go to + +or read the LICENSE file. diff --git a/src/main/resources/locales/en.txt b/src/main/resources/locales/en.txt index 5173cf3..666e363 100644 --- a/src/main/resources/locales/en.txt +++ b/src/main/resources/locales/en.txt @@ -28,3 +28,4 @@ tv.settings.show_duplicate=Show Duplicate Units & Prefixes tv.settings.use_default_files=Use Default Datafiles tv.settings.locale=🌐 Locale: tv.settings.unitfiles.button=Manage Unit Data Files +load_stat_msg=Successfully loaded [u] unique units with [un] names ([b] base units), [p] unique prefixes with [pn] names, [s] unit sets, and [d] named dimensions. diff --git a/src/main/resources/locales/fr.txt b/src/main/resources/locales/fr.txt index e8b7138..3fef030 100644 --- a/src/main/resources/locales/fr.txt +++ b/src/main/resources/locales/fr.txt @@ -28,3 +28,4 @@ tv.settings.show_duplicate=Montrer unitĂ©s et prĂ©fixes doubles tv.settings.use_default_files=Utilise donĂ©es par dĂ©faut tv.settings.locale=🌐 Locale: tv.settings.unitfiles.button=GĂ©rer donĂ©es d’unitĂ©s +load_stat_msg=ChargĂ© [u] unitĂ©s uniques avec [un] noms ([b] unitĂ©s bases), [p] prĂ©fixes uniques avec [pn] noms, [s] collections d’unitĂ©s, et [d] dimensions nomĂ©es. -- cgit v1.2.3