A resource type additionally holds information about the filename representation.
7 | *
8 | * @author Jacob van Mourik
9 | */
10 | public enum ResourceType {
11 | JSON(".json"),
12 | ES6(".js"),
13 | Properties(".properties");
14 |
15 | private final String extension;
16 |
17 | /**
18 | * Gets the file extension of the resource type.
19 | *
20 | * @return the file extension.
21 | */
22 | public String getExtension() {
23 | return extension;
24 | }
25 |
26 | private ResourceType(String extension) {
27 | this.extension = extension;
28 | }
29 | }
--------------------------------------------------------------------------------
/src/main/java/com/jvms/i18neditor/editor/menu/ExpandTranslationsMenuItem.java:
--------------------------------------------------------------------------------
1 | package com.jvms.i18neditor.editor.menu;
2 |
3 | import javax.swing.JMenuItem;
4 |
5 | import com.jvms.i18neditor.editor.TranslationTree;
6 | import com.jvms.i18neditor.util.MessageBundle;
7 |
8 | /**
9 | * This class represents a menu item for expanding all keys in of the translation tree.
10 | *
11 | * @author Jacob van Mourik
12 | */
13 | public class ExpandTranslationsMenuItem extends JMenuItem {
14 | private final static long serialVersionUID = 7316102121075733726L;
15 |
16 | public ExpandTranslationsMenuItem(TranslationTree tree) {
17 | super(MessageBundle.get("menu.view.expand.title"));
18 | addActionListener(e -> tree.expandAll());
19 | }
20 | }
--------------------------------------------------------------------------------
/src/main/java/com/jvms/i18neditor/editor/menu/CollapseTranslationsMenuItem.java:
--------------------------------------------------------------------------------
1 | package com.jvms.i18neditor.editor.menu;
2 |
3 | import javax.swing.JMenuItem;
4 |
5 | import com.jvms.i18neditor.editor.TranslationTree;
6 | import com.jvms.i18neditor.util.MessageBundle;
7 |
8 | /**
9 | * This class represents a menu item for collapsing all keys of the translation tree.
10 | *
11 | * @author Jacob van Mourik
12 | */
13 | public class CollapseTranslationsMenuItem extends JMenuItem {
14 | private final static long serialVersionUID = 7885728865417192564L;
15 |
16 | public CollapseTranslationsMenuItem(TranslationTree tree) {
17 | super(MessageBundle.get("menu.view.collapse.title"));
18 | addActionListener(e -> tree.collapseAll());
19 | }
20 | }
--------------------------------------------------------------------------------
/src/main/java/com/jvms/i18neditor/swing/text/DeleteAction.java:
--------------------------------------------------------------------------------
1 | package com.jvms.i18neditor.swing.text;
2 |
3 | import java.awt.event.ActionEvent;
4 |
5 | import javax.swing.text.JTextComponent;
6 | import javax.swing.text.TextAction;
7 |
8 | /**
9 | * An action implementation useful for deleting text.
10 | *
11 | * @author Jacob van Mourik
12 | */
13 | public class DeleteAction extends TextAction {
14 | private final static long serialVersionUID = -7933405670677160997L;
15 |
16 | public DeleteAction(String name) {
17 | super(name);
18 | }
19 |
20 | @Override
21 | public void actionPerformed(ActionEvent e) {
22 | JTextComponent component = getFocusedComponent();
23 | component.replaceSelection("");
24 | }
25 | }
26 |
--------------------------------------------------------------------------------
/src/main/java/com/jvms/i18neditor/swing/text/SelectAllAction.java:
--------------------------------------------------------------------------------
1 | package com.jvms.i18neditor.swing.text;
2 |
3 | import java.awt.event.ActionEvent;
4 |
5 | import javax.swing.text.JTextComponent;
6 | import javax.swing.text.TextAction;
7 |
8 | /**
9 | * An action implementation useful for selecting all text.
10 | *
11 | * @author Jacob van Mourik
12 | */
13 | public class SelectAllAction extends TextAction {
14 | private final static long serialVersionUID = -4913270947629733919L;
15 |
16 | public SelectAllAction(String name) {
17 | super(name);
18 | }
19 |
20 | @Override
21 | public void actionPerformed(ActionEvent e) {
22 | JTextComponent component = getFocusedComponent();
23 | component.selectAll();
24 | }
25 | }
26 |
--------------------------------------------------------------------------------
/src/main/java/com/jvms/i18neditor/swing/RedoAction.java:
--------------------------------------------------------------------------------
1 | package com.jvms.i18neditor.swing;
2 |
3 | import java.awt.event.ActionEvent;
4 |
5 | import javax.swing.AbstractAction;
6 | import javax.swing.undo.UndoManager;
7 |
8 | /**
9 | * An action implementation useful for redoing an edit.
10 | *
11 | * @author Jacob van Mourik
12 | */
13 | public class RedoAction extends AbstractAction {
14 | private final static long serialVersionUID = -3051499148079684354L;
15 | private final UndoManager undoManager;
16 |
17 | public RedoAction(UndoManager undoManager) {
18 | super();
19 | this.undoManager = undoManager;
20 | }
21 |
22 | @Override
23 | public void actionPerformed(ActionEvent e) {
24 | if (undoManager.canRedo()) {
25 | undoManager.redo();
26 | }
27 | }
28 | }
29 |
--------------------------------------------------------------------------------
/src/main/java/com/jvms/i18neditor/swing/UndoAction.java:
--------------------------------------------------------------------------------
1 | package com.jvms.i18neditor.swing;
2 |
3 | import java.awt.event.ActionEvent;
4 |
5 | import javax.swing.AbstractAction;
6 | import javax.swing.undo.UndoManager;
7 |
8 | /**
9 | * An action implementation useful for undoing an edit.
10 | *
11 | * @author Jacob van Mourik
12 | */
13 | public class UndoAction extends AbstractAction {
14 | private final static long serialVersionUID = -3051499148079684354L;
15 | private final UndoManager undoManager;
16 |
17 | public UndoAction(UndoManager undoManager) {
18 | super();
19 | this.undoManager = undoManager;
20 | }
21 |
22 | @Override
23 | public void actionPerformed(ActionEvent e) {
24 | if (undoManager.canUndo()) {
25 | undoManager.undo();
26 | }
27 | }
28 | }
29 |
--------------------------------------------------------------------------------
/.settings/org.eclipse.jdt.core.prefs:
--------------------------------------------------------------------------------
1 | eclipse.preferences.version=1
2 | org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled
3 | org.eclipse.jdt.core.compiler.codegen.methodParameters=do not generate
4 | org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.8
5 | org.eclipse.jdt.core.compiler.codegen.unusedLocal=preserve
6 | org.eclipse.jdt.core.compiler.compliance=1.8
7 | org.eclipse.jdt.core.compiler.debug.lineNumber=generate
8 | org.eclipse.jdt.core.compiler.debug.localVariable=generate
9 | org.eclipse.jdt.core.compiler.debug.sourceFile=generate
10 | org.eclipse.jdt.core.compiler.problem.assertIdentifier=error
11 | org.eclipse.jdt.core.compiler.problem.enumIdentifier=error
12 | org.eclipse.jdt.core.compiler.problem.forbiddenReference=warning
13 | org.eclipse.jdt.core.compiler.source=1.8
14 |
--------------------------------------------------------------------------------
/src/main/java/com/jvms/i18neditor/io/ChecksumException.java:
--------------------------------------------------------------------------------
1 | package com.jvms.i18neditor.io;
2 |
3 | import java.io.IOException;
4 |
5 | public class ChecksumException extends IOException {
6 | private final static long serialVersionUID = -5164866588227844439L;
7 |
8 | /**
9 | * Constructs an {@code ChecksumException} with {@code null}
10 | * as its error detail message.
11 | */
12 | public ChecksumException() {
13 | super();
14 | }
15 |
16 | /**
17 | * Constructs an {@code ChecksumException} with the specified detail message.
18 | *
19 | * @param message
20 | * The detail message (which is saved for later retrieval
21 | * by the {@link #getMessage()} method)
22 | */
23 | public ChecksumException(String message) {
24 | super(message);
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/src/main/java/com/jvms/i18neditor/swing/JHelpLabel.java:
--------------------------------------------------------------------------------
1 | package com.jvms.i18neditor.swing;
2 |
3 | import java.awt.Dimension;
4 | import java.awt.Font;
5 |
6 | import javax.swing.JLabel;
7 | import javax.swing.UIManager;
8 |
9 | /**
10 | * This class extends a default {@link javax.swing.JLabel} with a custom look and feel
11 | * for help messages.
12 | *
13 | * @author Jacob van Mourik
14 | */
15 | public class JHelpLabel extends JLabel {
16 | private final static long serialVersionUID = -6879887592161450052L;
17 |
18 | /**
19 | * Constructs a {@link JHelpLabel}.
20 | */
21 | public JHelpLabel(String text) {
22 | super(text);
23 |
24 | Dimension size = getSize();
25 | size.height -= 10;
26 | setSize(size);
27 | setFont(getFont().deriveFont(Font.PLAIN, getFont().getSize()-1));
28 | setForeground(UIManager.getColor("Label.disabledForeground"));
29 | }
30 | }
31 |
--------------------------------------------------------------------------------
/src/main/java/com/jvms/i18neditor/editor/TranslationKeyField.java:
--------------------------------------------------------------------------------
1 | package com.jvms.i18neditor.editor;
2 |
3 | import com.jvms.i18neditor.swing.JTextField;
4 |
5 | /**
6 | * This class represents a text field for displaying, finding and adding a translation key.
7 | *
8 | * @author Jacob van Mourik
9 | */
10 | public class TranslationKeyField extends JTextField {
11 | private final static long serialVersionUID = -3951187528785224704L;
12 |
13 | public TranslationKeyField() {
14 | super();
15 | setupUI();
16 | }
17 |
18 | public void clear() {
19 | setValue(null);
20 | }
21 |
22 | public String getValue() {
23 | return getText().trim();
24 | }
25 |
26 | public void setValue(String value) {
27 | setText(value);
28 | undoManager.discardAllEdits();
29 | }
30 |
31 | private void setupUI() {
32 | setEditable(false);
33 | setCaret(new TranslationKeyCaret());
34 | }
35 | }
36 |
--------------------------------------------------------------------------------
/src/main/java/com/jvms/i18neditor/util/Images.java:
--------------------------------------------------------------------------------
1 | package com.jvms.i18neditor.util;
2 |
3 | import java.net.URL;
4 |
5 | import javax.swing.ImageIcon;
6 |
7 | /**
8 | * This class provides utility functions for loading images.
9 | *
10 | * @author Jacob van Mourik
11 | */
12 | public final class Images {
13 |
14 | /**
15 | * Loads an image icon from the current classpath.
16 | *
17 | * @param path the path of the image to load
18 | * @return the image icon
19 | */
20 | public static ImageIcon loadFromClasspath(String path) {
21 | return new ImageIcon(getClasspathURL(path));
22 | }
23 |
24 | /**
25 | * Gets the URL of an image from the current classpath.
26 | *
27 | * @param path the path of the image to load
28 | * @return the image icon
29 | */
30 | public static URL getClasspathURL(String path) {
31 | return Images.class.getClassLoader().getResource(path);
32 | }
33 | }
34 |
--------------------------------------------------------------------------------
/src/main/java/com/jvms/i18neditor/editor/menu/RemoveTranslationMenuItem.java:
--------------------------------------------------------------------------------
1 | package com.jvms.i18neditor.editor.menu;
2 |
3 | import java.awt.event.KeyEvent;
4 |
5 | import javax.swing.JMenuItem;
6 | import javax.swing.KeyStroke;
7 |
8 | import com.jvms.i18neditor.editor.Editor;
9 | import com.jvms.i18neditor.util.MessageBundle;
10 |
11 | /**
12 | * This class represents a menu item for removing a translation key.
13 | *
14 | * @author Jacob van Mourik
15 | */
16 | public class RemoveTranslationMenuItem extends JMenuItem {
17 | private final static long serialVersionUID = 5207946396515235714L;
18 |
19 | public RemoveTranslationMenuItem(Editor editor, boolean enabled) {
20 | super(MessageBundle.get("menu.edit.delete.title"));
21 | setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_DELETE, 0));
22 | addActionListener(e -> editor.removeSelectedTranslation());
23 | setEnabled(enabled);
24 | }
25 | }
--------------------------------------------------------------------------------
/src/main/java/com/jvms/i18neditor/editor/menu/AddLocaleMenuItem.java:
--------------------------------------------------------------------------------
1 | package com.jvms.i18neditor.editor.menu;
2 |
3 | import java.awt.Toolkit;
4 | import java.awt.event.KeyEvent;
5 |
6 | import javax.swing.JMenuItem;
7 | import javax.swing.KeyStroke;
8 |
9 | import com.jvms.i18neditor.editor.Editor;
10 | import com.jvms.i18neditor.util.MessageBundle;
11 |
12 | /**
13 | * This class represents a menu item for adding a new locale.
14 | *
15 | * @author Jacob van Mourik
16 | */
17 | public class AddLocaleMenuItem extends JMenuItem {
18 | private final static long serialVersionUID = -5108677891532028898L;
19 |
20 | public AddLocaleMenuItem(Editor editor, boolean enabled) {
21 | super(MessageBundle.get("menu.edit.add.locale.title"));
22 | setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_L, Toolkit.getDefaultToolkit().getMenuShortcutKeyMask()));
23 | addActionListener(e -> editor.showAddLocaleDialog());
24 | setEnabled(enabled);
25 | }
26 | }
--------------------------------------------------------------------------------
/src/main/java/com/jvms/i18neditor/editor/menu/RenameTranslationMenuItem.java:
--------------------------------------------------------------------------------
1 | package com.jvms.i18neditor.editor.menu;
2 |
3 | import java.awt.Toolkit;
4 | import java.awt.event.KeyEvent;
5 |
6 | import javax.swing.JMenuItem;
7 | import javax.swing.KeyStroke;
8 |
9 | import com.jvms.i18neditor.editor.Editor;
10 | import com.jvms.i18neditor.util.MessageBundle;
11 |
12 | /**
13 | * This class represents a menu item for renaming a translation key.
14 | *
15 | * @author Jacob van Mourik
16 | */
17 | public class RenameTranslationMenuItem extends JMenuItem {
18 | private final static long serialVersionUID = 907122077814626286L;
19 |
20 | public RenameTranslationMenuItem(Editor editor, boolean enabled) {
21 | super(MessageBundle.get("menu.edit.rename.title"));
22 | setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_R, Toolkit.getDefaultToolkit().getMenuShortcutKeyMask()));
23 | addActionListener(e -> editor.renameSelectedTranslation());
24 | setEnabled(enabled);
25 | }
26 | }
--------------------------------------------------------------------------------
/src/main/java/com/jvms/i18neditor/editor/menu/FindTranslationMenuItem.java:
--------------------------------------------------------------------------------
1 | package com.jvms.i18neditor.editor.menu;
2 |
3 | import java.awt.Toolkit;
4 | import java.awt.event.KeyEvent;
5 |
6 | import javax.swing.JMenuItem;
7 | import javax.swing.KeyStroke;
8 |
9 | import com.jvms.i18neditor.editor.Editor;
10 | import com.jvms.i18neditor.util.MessageBundle;
11 |
12 | /**
13 | * This class represents a menu item for searching a translation key.
14 | *
15 | * @author Jacob van Mourik
16 | */
17 | public class FindTranslationMenuItem extends JMenuItem {
18 | private final static long serialVersionUID = -1298283182450978961L;
19 |
20 | public FindTranslationMenuItem(Editor editor, boolean enabled) {
21 | super(MessageBundle.get("menu.edit.find.translation.title"));
22 | setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_F, Toolkit.getDefaultToolkit().getMenuShortcutKeyMask()));
23 | addActionListener(e -> editor.showFindTranslationDialog());
24 | setEnabled(enabled);
25 | }
26 | }
--------------------------------------------------------------------------------
/src/main/java/com/jvms/i18neditor/editor/menu/DuplicateTranslationMenuItem.java:
--------------------------------------------------------------------------------
1 | package com.jvms.i18neditor.editor.menu;
2 |
3 | import java.awt.Toolkit;
4 | import java.awt.event.KeyEvent;
5 |
6 | import javax.swing.JMenuItem;
7 | import javax.swing.KeyStroke;
8 |
9 | import com.jvms.i18neditor.editor.Editor;
10 | import com.jvms.i18neditor.util.MessageBundle;
11 |
12 | /**
13 | * This class represents a menu item for duplicating a translation key.
14 | *
15 | * @author Jacob van Mourik
16 | */
17 | public class DuplicateTranslationMenuItem extends JMenuItem {
18 | private final static long serialVersionUID = 5207946396515235714L;
19 |
20 | public DuplicateTranslationMenuItem(Editor editor, boolean enabled) {
21 | super(MessageBundle.get("menu.edit.duplicate.title"));
22 | setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_D, Toolkit.getDefaultToolkit().getMenuShortcutKeyMask()));
23 | addActionListener(e -> editor.duplicateSelectedTranslation());
24 | setEnabled(enabled);
25 | }
26 | }
--------------------------------------------------------------------------------
/src/main/java/com/jvms/i18neditor/editor/TranslationTreeMenu.java:
--------------------------------------------------------------------------------
1 | package com.jvms.i18neditor.editor;
2 |
3 | import javax.swing.JPopupMenu;
4 |
5 | import com.jvms.i18neditor.editor.menu.AddTranslationMenuItem;
6 | import com.jvms.i18neditor.editor.menu.CollapseTranslationsMenuItem;
7 | import com.jvms.i18neditor.editor.menu.ExpandTranslationsMenuItem;
8 | import com.jvms.i18neditor.editor.menu.FindTranslationMenuItem;
9 |
10 | /**
11 | * This class represents a right click menu for the translation tree.
12 | *
13 | * @author Jacob van Mourik
14 | */
15 | public class TranslationTreeMenu extends JPopupMenu {
16 | private final static long serialVersionUID = -8450484152294368841L;
17 |
18 | public TranslationTreeMenu(Editor editor, TranslationTree tree) {
19 | super();
20 | add(new AddTranslationMenuItem(editor, tree, true));
21 | add(new FindTranslationMenuItem(editor, true));
22 | addSeparator();
23 | add(new ExpandTranslationsMenuItem(tree));
24 | add(new CollapseTranslationsMenuItem(tree));
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/src/main/java/com/jvms/i18neditor/swing/JHtmlPane.java:
--------------------------------------------------------------------------------
1 | package com.jvms.i18neditor.swing;
2 |
3 | import java.awt.Component;
4 | import java.awt.Desktop;
5 |
6 | import javax.swing.JEditorPane;
7 | import javax.swing.event.HyperlinkEvent;
8 |
9 | /**
10 | * This class extends a default {@link JEditorPane} with default settings for HTML content.
11 | *
12 | * @author Jacob van Mourik
13 | */
14 | public class JHtmlPane extends JEditorPane {
15 | private final static long serialVersionUID = 2873290055720408299L;
16 |
17 | public JHtmlPane(Component parent, String content) {
18 | super("text/html", content);
19 | setEditable(false);
20 | setBackground(parent.getBackground());
21 | addHyperlinkListener(e -> {
22 | if (e.getEventType().equals(HyperlinkEvent.EventType.ACTIVATED)) {
23 | try {
24 | Desktop.getDesktop().browse(e.getURL().toURI());
25 | } catch (Exception e1) {
26 | //
27 | }
28 | }
29 | });
30 | }
31 | }
32 |
--------------------------------------------------------------------------------
/src/main/java/com/jvms/i18neditor/swing/event/RequestInitialFocusListener.java:
--------------------------------------------------------------------------------
1 | package com.jvms.i18neditor.swing.event;
2 |
3 | import javax.swing.*;
4 | import javax.swing.event.*;
5 |
6 | /**
7 | * This class implements a {@link AncestorListener} to request initial focus on a component.
8 | *
9 | *
When the component is added to an active ancestor the component will request focus immediately.
10 | * When the component is added to a non active ancestor, the focus request will be made once the ancestor is active.
11 | *
12 | * @author Jacob van Mourik
13 | */
14 | public class RequestInitialFocusListener implements AncestorListener {
15 |
16 | @Override
17 | public void ancestorAdded(AncestorEvent e) {
18 | JComponent component = e.getComponent();
19 | component.requestFocusInWindow();
20 | component.removeAncestorListener(this);
21 | }
22 |
23 | @Override
24 | public void ancestorMoved(AncestorEvent e) {
25 | //
26 | }
27 |
28 | @Override
29 | public void ancestorRemoved(AncestorEvent e) {
30 | //
31 | }
32 | }
33 |
--------------------------------------------------------------------------------
/src/main/java/com/jvms/i18neditor/editor/menu/CopyTranslationKeyToClipboardMenuItem.java:
--------------------------------------------------------------------------------
1 | package com.jvms.i18neditor.editor.menu;
2 |
3 | import java.awt.Toolkit;
4 | import java.awt.event.KeyEvent;
5 |
6 | import javax.swing.JMenuItem;
7 | import javax.swing.KeyStroke;
8 |
9 | import com.jvms.i18neditor.editor.Editor;
10 | import com.jvms.i18neditor.util.MessageBundle;
11 |
12 | /**
13 | * This class represents a menu item for copying a translations key to the system clipboard.
14 | *
15 | * @author Fabian Terstegen
16 | *
17 | */
18 | public class CopyTranslationKeyToClipboardMenuItem extends JMenuItem {
19 | private static final long serialVersionUID = 6032182493888769724L;
20 |
21 | public CopyTranslationKeyToClipboardMenuItem(Editor editor, boolean enabled) {
22 | super(MessageBundle.get("menu.edit.copy.key.title"));
23 | setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_C, Toolkit.getDefaultToolkit().getMenuShortcutKeyMask()));
24 | addActionListener(e -> editor.copySelectedTranslationKey());
25 | setEnabled(enabled);
26 | }
27 |
28 | }
29 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | The MIT License (MIT)
2 |
3 | Copyright (c) 2015-2017 Jacob van Mourik
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
23 |
--------------------------------------------------------------------------------
/src/main/java/com/jvms/i18neditor/util/Locales.java:
--------------------------------------------------------------------------------
1 | package com.jvms.i18neditor.util;
2 |
3 | import java.util.Locale;
4 | import java.util.regex.Matcher;
5 | import java.util.regex.Pattern;
6 |
7 | import com.google.common.base.Strings;
8 |
9 | /**
10 | * This class provides utility functions for locales.
11 | *
12 | * @author Jacob van Mourik
13 | */
14 | public final class Locales {
15 | public final static String LOCALE_REGEX = "([^_-]*)(?:[_-]([^_-]*)(?:[_-]([^_-]*))?)?";
16 | public final static Pattern LOCALE_PATTERN = Pattern.compile(LOCALE_REGEX);
17 |
18 | public static Locale parseLocale(String localeString) {
19 | if (Strings.isNullOrEmpty(localeString)) {
20 | return null;
21 | }
22 | Matcher matcher = LOCALE_PATTERN.matcher(localeString);
23 | if (matcher.matches()) {
24 | String language = matcher.group(1);
25 | language = (language == null) ? "" : language;
26 | String country = matcher.group(2);
27 | country = (country == null) ? "" : country;
28 | String variant = matcher.group(3);
29 | variant = (variant == null) ? "" : variant;
30 | return new Locale(language, country, variant);
31 | }
32 | return null;
33 | }
34 | }
35 |
--------------------------------------------------------------------------------
/src/main/java/com/jvms/i18neditor/swing/text/JTextComponentMenuListener.java:
--------------------------------------------------------------------------------
1 | package com.jvms.i18neditor.swing.text;
2 |
3 | import java.awt.event.MouseAdapter;
4 | import java.awt.event.MouseEvent;
5 |
6 | import javax.swing.text.JTextComponent;
7 | import javax.swing.undo.UndoManager;
8 |
9 | /**
10 | * Mouse listener for showing a {@link JTextComponentMenu}.
11 | *
12 | * @author Jacob van Mourik
13 | */
14 | public class JTextComponentMenuListener extends MouseAdapter {
15 | private final JTextComponent parent;
16 | private final JTextComponentMenu menu;
17 |
18 | public JTextComponentMenuListener(JTextComponent parent, UndoManager undoManager) {
19 | super();
20 | this.parent = parent;
21 | this.menu = new JTextComponentMenu(parent, undoManager);
22 | }
23 |
24 | @Override
25 | public void mouseReleased(MouseEvent e) {
26 | showPopupMenu(e);
27 | }
28 |
29 | @Override
30 | public void mousePressed(MouseEvent e) {
31 | showPopupMenu(e);
32 | }
33 |
34 | private void showPopupMenu(MouseEvent e) {
35 | if (!e.isPopupTrigger() || !parent.isEditable()) {
36 | return;
37 | }
38 | parent.requestFocusInWindow();
39 | menu.show(parent, e.getX(), e.getY());
40 | }
41 | }
42 |
--------------------------------------------------------------------------------
/src/main/java/com/jvms/i18neditor/util/Colors.java:
--------------------------------------------------------------------------------
1 | package com.jvms.i18neditor.util;
2 |
3 | import java.awt.Color;
4 |
5 | /**
6 | * This class provides utility functions for colors.
7 | *
8 | * @author Jacob van Mourik
9 | */
10 | public final class Colors {
11 |
12 | /**
13 | * Multiplies the RGB values of a color with the given factor.
14 | *
15 | * @param c the original color
16 | * @param factor the factor to scale with
17 | * @return the scaled color
18 | */
19 | public static Color scale(Color c, float factor) {
20 | return new Color(
21 | roundColorValue(c.getRed()*factor),
22 | roundColorValue(c.getGreen()*factor),
23 | roundColorValue(c.getBlue()*factor),
24 | c.getAlpha());
25 | }
26 |
27 | /**
28 | * Gets the hex representation of the RGB values of the given color.
29 | *
30 | * @param color the color to get the hex value from
31 | * @return the hex representation of the color
32 | */
33 | public static String hexValue(Color color) {
34 | return String.format("#%02x%02x%02x", color.getRed(), color.getGreen(), color.getBlue());
35 | }
36 |
37 | private static int roundColorValue(float value) {
38 | return Math.round(Math.max(0, Math.min(255, value)));
39 | }
40 | }
41 |
--------------------------------------------------------------------------------
/src/main/java/com/jvms/i18neditor/editor/TranslationTreeNodeMenu.java:
--------------------------------------------------------------------------------
1 | package com.jvms.i18neditor.editor;
2 |
3 | import javax.swing.JPopupMenu;
4 |
5 | import com.jvms.i18neditor.editor.menu.AddTranslationMenuItem;
6 | import com.jvms.i18neditor.editor.menu.CopyTranslationKeyToClipboardMenuItem;
7 | import com.jvms.i18neditor.editor.menu.DuplicateTranslationMenuItem;
8 | import com.jvms.i18neditor.editor.menu.RemoveTranslationMenuItem;
9 | import com.jvms.i18neditor.editor.menu.RenameTranslationMenuItem;
10 |
11 | /**
12 | * This class represents a right click menu for a single node of the translation tree.
13 | *
14 | * @author Jacob van Mourik
15 | */
16 | public class TranslationTreeNodeMenu extends JPopupMenu {
17 | private final static long serialVersionUID = -8450484152294368841L;
18 |
19 | public TranslationTreeNodeMenu(Editor editor, TranslationTreeNode node) {
20 | super();
21 | add(new AddTranslationMenuItem(editor, node, true));
22 | if (!node.isRoot()) {
23 | addSeparator();
24 | add(new RenameTranslationMenuItem(editor, true));
25 | add(new DuplicateTranslationMenuItem(editor, true));
26 | add(new RemoveTranslationMenuItem(editor, true));
27 | add(new CopyTranslationKeyToClipboardMenuItem(editor, true));
28 | }
29 | }
30 | }
31 |
--------------------------------------------------------------------------------
/.classpath:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
--------------------------------------------------------------------------------
/src/main/java/com/jvms/i18neditor/editor/menu/AddTranslationMenuItem.java:
--------------------------------------------------------------------------------
1 | package com.jvms.i18neditor.editor.menu;
2 |
3 | import java.awt.Toolkit;
4 | import java.awt.event.ActionListener;
5 | import java.awt.event.KeyEvent;
6 |
7 | import javax.swing.JMenuItem;
8 | import javax.swing.KeyStroke;
9 |
10 | import com.jvms.i18neditor.editor.Editor;
11 | import com.jvms.i18neditor.editor.TranslationTree;
12 | import com.jvms.i18neditor.editor.TranslationTreeNode;
13 | import com.jvms.i18neditor.util.MessageBundle;
14 |
15 | /**
16 | * This class represents a menu item for adding a new translation.
17 | *
18 | * @author Jacob van Mourik
19 | */
20 | public class AddTranslationMenuItem extends JMenuItem {
21 | private final static long serialVersionUID = -2673278052970076105L;
22 |
23 | public AddTranslationMenuItem(Editor editor, TranslationTreeNode node, boolean enabled) {
24 | this(editor, enabled, e -> editor.showAddTranslationDialog(node));
25 | }
26 |
27 | public AddTranslationMenuItem(Editor editor, TranslationTree tree, boolean enabled) {
28 | this(editor, enabled, e -> editor.showAddTranslationDialog(tree.getSelectionNode()));
29 | }
30 |
31 | private AddTranslationMenuItem(Editor editor, boolean enabled, ActionListener action) {
32 | super(MessageBundle.get("menu.edit.add.translation.title"));
33 | setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_T, Toolkit.getDefaultToolkit().getMenuShortcutKeyMask()));
34 | addActionListener(action);
35 | setEnabled(enabled);
36 | }
37 | }
--------------------------------------------------------------------------------
/src/main/java/com/jvms/i18neditor/editor/TranslationTreeToggleIcon.java:
--------------------------------------------------------------------------------
1 | package com.jvms.i18neditor.editor;
2 |
3 | import java.awt.Component;
4 | import java.awt.Graphics;
5 |
6 | import javax.swing.Icon;
7 | import javax.swing.UIManager;
8 |
9 | import com.jvms.i18neditor.util.Colors;
10 |
11 | /**
12 | * This class represents a toggle icon for a translation tree cell.
13 | *
14 | * @author Jacob van Mourik
15 | */
16 | public class TranslationTreeToggleIcon implements Icon {
17 | private final static int SIZE = 10;
18 | private final ToggleIconType type;
19 |
20 | public enum ToggleIconType {
21 | Collapsed, Expanded
22 | }
23 |
24 | public TranslationTreeToggleIcon(ToggleIconType type) {
25 | this.type = type;
26 | }
27 |
28 | @Override
29 | public void paintIcon(Component c, Graphics g, int x, int y) {
30 | g.setColor(UIManager.getColor("Tree.background"));
31 | g.fillRect(x, y, SIZE, SIZE);
32 | g.setColor(Colors.scale(UIManager.getColor("Panel.background"), .8f));
33 | g.drawRect(x, y, SIZE, SIZE);
34 | g.setColor(UIManager.getColor("Tree.foreground"));
35 | g.drawLine(x + 2, y + SIZE/2, x + SIZE - 2, y + SIZE/2);
36 | if (type == ToggleIconType.Collapsed) {
37 | g.drawLine(x + SIZE/2, y + 2, x + SIZE/2, y + SIZE - 2);
38 | }
39 | }
40 |
41 | @Override
42 | public int getIconWidth() {
43 | return SIZE;
44 | }
45 |
46 | @Override
47 | public int getIconHeight() {
48 | return SIZE;
49 | }
50 | }
51 |
--------------------------------------------------------------------------------
/src/main/java/com/jvms/i18neditor/editor/TranslationTreeStatusIcon.java:
--------------------------------------------------------------------------------
1 | package com.jvms.i18neditor.editor;
2 |
3 | import java.awt.Color;
4 | import java.awt.Component;
5 | import java.awt.Graphics;
6 | import java.awt.Graphics2D;
7 | import java.awt.RenderingHints;
8 |
9 | import javax.swing.Icon;
10 |
11 | /**
12 | * This class represents a status icon for a translation tree cell.
13 | *
14 | * @author Jacob van Mourik
15 | */
16 | public class TranslationTreeStatusIcon implements Icon {
17 | private final static int SIZE = 7;
18 | private final StatusIconType type;
19 |
20 | public enum StatusIconType {
21 | Warning(new Color(220,160,0));
22 |
23 | private Color color;
24 |
25 | public Color getColor() {
26 | return color;
27 | }
28 |
29 | private StatusIconType(Color color) {
30 | this.color = color;
31 | }
32 | }
33 |
34 | public TranslationTreeStatusIcon(StatusIconType type) {
35 | this.type = type;
36 | }
37 |
38 | @Override
39 | public void paintIcon(Component c, Graphics g, int x, int y) {
40 | Graphics2D g2 = (Graphics2D) g.create();
41 | g2.setRenderingHints(new RenderingHints(
42 | RenderingHints.KEY_ANTIALIASING,
43 | RenderingHints.VALUE_ANTIALIAS_ON));
44 | g2.setColor(type.getColor());
45 | g2.fillOval(x, y, SIZE, SIZE);
46 | g2.dispose();
47 | }
48 |
49 | @Override
50 | public int getIconWidth() {
51 | return SIZE;
52 | }
53 |
54 | @Override
55 | public int getIconHeight() {
56 | return SIZE;
57 | }
58 | }
--------------------------------------------------------------------------------
/src/main/java/com/jvms/i18neditor/editor/TranslationKeyCaret.java:
--------------------------------------------------------------------------------
1 | package com.jvms.i18neditor.editor;
2 |
3 | import java.awt.event.MouseEvent;
4 |
5 | import javax.swing.SwingUtilities;
6 |
7 | import com.jvms.i18neditor.swing.JTextField;
8 | import com.jvms.i18neditor.swing.text.BlinkCaret;
9 |
10 | public class TranslationKeyCaret extends BlinkCaret {
11 | private final static long serialVersionUID = -4481421558690248419L;
12 |
13 | @Override
14 | public void mouseClicked(MouseEvent e) {
15 | if (!e.isConsumed() && SwingUtilities.isLeftMouseButton(e) && e.getClickCount() == 2) {
16 | handleDoubleClick(e);
17 | return;
18 | }
19 | super.mouseClicked(e);
20 | }
21 |
22 | @Override
23 | public void mousePressed(MouseEvent e) {
24 | if (!e.isConsumed() && SwingUtilities.isLeftMouseButton(e) && e.getClickCount() == 2) {
25 | handleDoubleClick(e);
26 | return;
27 | }
28 | super.mousePressed(e);
29 | }
30 |
31 | private void handleDoubleClick(MouseEvent e) {
32 | JTextField field = (JTextField)e.getComponent();
33 |
34 | int caretPos = field.getCaretPosition();
35 | int start = caretPos;
36 | int end = caretPos;
37 | String text = field.getText();
38 |
39 | while (start > 0) {
40 | if (text.charAt(start-1) == '.') {
41 | break;
42 | }
43 | start--;
44 | }
45 | while (end < text.length()) {
46 | if (text.charAt(end) == '.') {
47 | break;
48 | }
49 | end++;
50 | }
51 |
52 | field.setSelectionStart(start);
53 | field.setSelectionEnd(end);
54 | }
55 | }
56 |
--------------------------------------------------------------------------------
/src/main/java/com/jvms/i18neditor/util/MessageBundle.java:
--------------------------------------------------------------------------------
1 | package com.jvms.i18neditor.util;
2 |
3 | import java.text.MessageFormat;
4 | import java.util.Locale;
5 | import java.util.ResourceBundle;
6 |
7 | /**
8 | * This class provides utility functions for retrieving translations from a resource bundle.
9 | * By default it loads translations from {@value #RESOURCES_PATH}.
10 | *
11 | *
The locale used is the current value of the default locale for this instance of the Java Virtual Machine.
12 | *
13 | * @author Jacob van Mourik
14 | */
15 | public final class MessageBundle {
16 | private final static String RESOURCES_PATH = "bundles/messages";
17 | private static ResourceBundle RESOURCES;
18 |
19 | /**
20 | * Sets the preferred locale to use.
21 | * When calling this function resources will be reloaded from disk.
22 | *
23 | * @param locale the preferred locale to use
24 | */
25 | public static void setLocale(Locale locale) {
26 | RESOURCES = ResourceBundle.getBundle(RESOURCES_PATH, locale);
27 | }
28 |
29 | /**
30 | * Gets a value from this bundle for the given {@code key}. Any second arguments will
31 | * be used to format the value.
32 | *
33 | * @param key the bundle key
34 | * @param args objects used to format the value.
35 | * @return the formatted value for the given key.
36 | */
37 | public static String get(String key, Object... args) {
38 | String value = RESOURCES.getString(key);
39 | return MessageFormat.format(value, args);
40 | }
41 |
42 | /**
43 | * Gets a mnemonic value from this bundle.
44 | *
45 | * @param key the bundle key.
46 | * @return the mnemonic value for the given key.
47 | */
48 | public static Character getMnemonic(String key) {
49 | String value = RESOURCES.getString(key);
50 | return value.charAt(0);
51 | }
52 |
53 | /**
54 | * Loads the resources.
55 | */
56 | public static void loadResources() {
57 | RESOURCES = ResourceBundle.getBundle(RESOURCES_PATH);
58 | }
59 | }
--------------------------------------------------------------------------------
/src/main/java/com/jvms/i18neditor/swing/JScrollablePanel.java:
--------------------------------------------------------------------------------
1 | package com.jvms.i18neditor.swing;
2 |
3 | import java.awt.Dimension;
4 | import java.awt.Rectangle;
5 |
6 | import javax.swing.JPanel;
7 | import javax.swing.Scrollable;
8 |
9 | /**
10 | * This class extends a default {@link JPanel} with {@link Scrollable} capabilities.
11 | *
12 | * @author Jacob van Mourik
13 | */
14 | public class JScrollablePanel extends JPanel implements Scrollable {
15 | private final static long serialVersionUID = -7947570506111556197L;
16 | private final boolean scrollableTracksViewportWidth;
17 | private final boolean scrollableTracksViewportHeight;
18 |
19 | /**
20 | * Constructs a {@link JScrollablePanel}.
21 | *
22 | * @param scrollableTracksViewportWidth whether to force the width of this panel to match the width of the viewport.
23 | * @param scrollableTracksViewportHeight whether to force the height of this panel to match the height of the viewport.
24 | */
25 | public JScrollablePanel(boolean scrollableTracksViewportWidth, boolean scrollableTracksViewportHeight) {
26 | super();
27 | this.scrollableTracksViewportWidth = scrollableTracksViewportWidth;
28 | this.scrollableTracksViewportHeight = scrollableTracksViewportHeight;
29 | }
30 |
31 | @Override
32 | public Dimension getPreferredScrollableViewportSize() {
33 | return getPreferredSize();
34 | }
35 |
36 | @Override
37 | public int getScrollableUnitIncrement(Rectangle visibleRect, int orientation, int direction) {
38 | return 10;
39 | }
40 |
41 | @Override
42 | public int getScrollableBlockIncrement(Rectangle visibleRect, int orientation, int direction) {
43 | return 100;
44 | }
45 |
46 | @Override
47 | public boolean getScrollableTracksViewportWidth() {
48 | return scrollableTracksViewportWidth;
49 | }
50 |
51 | @Override
52 | public boolean getScrollableTracksViewportHeight() {
53 | return scrollableTracksViewportHeight;
54 | }
55 | }
--------------------------------------------------------------------------------
/src/main/java/com/jvms/i18neditor/swing/JTextArea.java:
--------------------------------------------------------------------------------
1 | package com.jvms.i18neditor.swing;
2 |
3 | import java.awt.Toolkit;
4 | import java.awt.event.KeyEvent;
5 |
6 | import javax.swing.BorderFactory;
7 | import javax.swing.KeyStroke;
8 | import javax.swing.UIManager;
9 | import javax.swing.border.Border;
10 | import javax.swing.undo.UndoManager;
11 |
12 | import com.jvms.i18neditor.swing.text.JTextComponentMenuListener;
13 | import com.jvms.i18neditor.util.Colors;
14 |
15 | /**
16 | * This class extends a default {@link javax.swing.JTextArea} with a {@link UndoManager},
17 | * a right click menu and a custom look and feel.
18 | *
19 | * @author Jacob van Mourik
20 | */
21 | public class JTextArea extends javax.swing.JTextArea {
22 | private final static long serialVersionUID = -5043046809426384893L;
23 | protected final UndoManager undoManager = new UndoManager();
24 |
25 | @Override
26 | public void setEnabled(boolean enabled) {
27 | super.setEnabled(enabled);
28 | setOpaque(enabled);
29 | }
30 |
31 | /**
32 | * Constructs a {@link JTextArea}.
33 | */
34 | public JTextArea() {
35 | super();
36 |
37 | Border border = BorderFactory.createLineBorder(Colors.scale(UIManager.getColor("Panel.background"), .8f));
38 | setBorder(BorderFactory.createCompoundBorder(border, BorderFactory.createEmptyBorder(5,8,5,8)));
39 | getDocument().addUndoableEditListener(e -> undoManager.addEdit(e.getEdit()));
40 |
41 | setAlignmentX(LEFT_ALIGNMENT);
42 | setLineWrap(true);
43 | setWrapStyleWord(true);
44 |
45 | // Add undo support
46 | getActionMap().put("undo", new UndoAction(undoManager));
47 | getInputMap().put(KeyStroke.getKeyStroke(KeyEvent.VK_Z, Toolkit.getDefaultToolkit().getMenuShortcutKeyMask()), "undo");
48 |
49 | // Add redo support
50 | getActionMap().put("redo", new RedoAction(undoManager));
51 | getInputMap().put(KeyStroke.getKeyStroke(KeyEvent.VK_Y, Toolkit.getDefaultToolkit().getMenuShortcutKeyMask()), "redo");
52 |
53 | // Add popup menu support
54 | addMouseListener(new JTextComponentMenuListener(this, undoManager));
55 | }
56 | }
57 |
--------------------------------------------------------------------------------
/src/main/java/com/jvms/i18neditor/Main.java:
--------------------------------------------------------------------------------
1 | package com.jvms.i18neditor;
2 |
3 | import java.awt.Font;
4 |
5 | import javax.swing.SwingUtilities;
6 | import javax.swing.UIDefaults;
7 | import javax.swing.UIManager;
8 |
9 | import org.apache.commons.lang3.SystemUtils;
10 | import org.slf4j.Logger;
11 | import org.slf4j.LoggerFactory;
12 |
13 | import com.google.common.collect.Sets;
14 | import com.jvms.i18neditor.editor.Editor;
15 |
16 | /**
17 | *
18 | * @author Jacob van Mourik
19 | */
20 | public class Main {
21 | private final static Logger log = LoggerFactory.getLogger(Main.class);
22 |
23 | public static void main(String[] args) {
24 | SwingUtilities.invokeLater(() -> {
25 | // Enable global menu on MAC OS
26 | if (SystemUtils.IS_OS_MAC) {
27 | System.setProperty("apple.laf.useScreenMenuBar", "true");
28 | }
29 | try {
30 | UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
31 | // For windows use menu font for entire UI
32 | if (SystemUtils.IS_OS_WINDOWS) {
33 | setUIFont(UIManager.getFont("Menu.font"));
34 | }
35 | } catch (Exception e) {
36 | log.warn("Unable to use native look and feel");
37 | }
38 | new Editor().launch();
39 | });
40 | }
41 |
42 | private static void setUIFont(Font font) {
43 | UIDefaults defaults = UIManager.getDefaults();
44 | Sets.newHashSet(
45 | "List.font",
46 | "TableHeader.font",
47 | "Panel.font",
48 | "TextArea.font",
49 | "ToggleButton.font",
50 | "ComboBox.font",
51 | "ScrollPane.font",
52 | "Spinner.font",
53 | "Slider.font",
54 | "EditorPane.font",
55 | "OptionPane.font",
56 | "ToolBar.font",
57 | "Tree.font",
58 | "TitledBorder.font",
59 | "Table.font",
60 | "Label.font",
61 | "TextField.font",
62 | "TextPane.font",
63 | "CheckBox.font",
64 | "ProgressBar.font",
65 | "FormattedTextField.font",
66 | "ColorChooser.font",
67 | "PasswordField.font",
68 | "Viewport.font",
69 | "TabbedPane.font",
70 | "RadioButton.font",
71 | "ToolTip.font",
72 | "Button.font"
73 | ).forEach(key -> defaults.put(key, font));
74 | }
75 | }
--------------------------------------------------------------------------------
/src/main/java/com/jvms/i18neditor/swing/JTextField.java:
--------------------------------------------------------------------------------
1 | package com.jvms.i18neditor.swing;
2 |
3 | import java.awt.Toolkit;
4 | import java.awt.event.KeyEvent;
5 |
6 | import javax.swing.BorderFactory;
7 | import javax.swing.KeyStroke;
8 | import javax.swing.UIManager;
9 | import javax.swing.border.Border;
10 | import javax.swing.undo.UndoManager;
11 |
12 | import com.jvms.i18neditor.swing.text.JTextComponentMenuListener;
13 | import com.jvms.i18neditor.util.Colors;
14 |
15 | /**
16 | * This class extends a default {@link javax.swing.JTextField} with a {@link UndoManager},
17 | * a right click menu and a custom look and feel.
18 | *
19 | * @author Jacob van Mourik
20 | */
21 | public class JTextField extends javax.swing.JTextField {
22 | private final static long serialVersionUID = 5296894112638304738L;
23 | protected final UndoManager undoManager = new UndoManager();
24 |
25 | /**
26 | * Constructs a {@link JTextField}.
27 | */
28 | public JTextField() {
29 | this(null);
30 | }
31 |
32 | @Override
33 | public void setEnabled(boolean enabled) {
34 | super.setEnabled(enabled);
35 | setOpaque(enabled);
36 | }
37 |
38 | /**
39 | * Constructs a {@link JTextField} with an initial text.
40 | */
41 | public JTextField(String text) {
42 | super(text, 25);
43 |
44 | Border border = BorderFactory.createLineBorder(Colors.scale(UIManager.getColor("Panel.background"), .8f));
45 | setBorder(BorderFactory.createCompoundBorder(border, BorderFactory.createEmptyBorder(5,8,5,8)));
46 | getDocument().addUndoableEditListener(e -> undoManager.addEdit(e.getEdit()));
47 |
48 | // Add undo support
49 | getActionMap().put("undo", new UndoAction(undoManager));
50 | getInputMap().put(KeyStroke.getKeyStroke(KeyEvent.VK_Z, Toolkit.getDefaultToolkit().getMenuShortcutKeyMask()), "undo");
51 |
52 | // Add redo support
53 | getActionMap().put("redo", new RedoAction(undoManager));
54 | getInputMap().put(KeyStroke.getKeyStroke(KeyEvent.VK_Y, Toolkit.getDefaultToolkit().getMenuShortcutKeyMask()), "redo");
55 |
56 | // Add popup menu support
57 | addMouseListener(new JTextComponentMenuListener(this, undoManager));
58 | }
59 | }
60 |
--------------------------------------------------------------------------------
/.inno.iss:
--------------------------------------------------------------------------------
1 | #define AppName "i18n-editor"
2 | #define AppVersion "2.0.0-beta.1"
3 | #define AppPublisher "JvMs Software"
4 | #define AppURL "https://github.com/jcbvm/i18n-editor"
5 | #define AppExeName "i18n-editor.exe"
6 |
7 | [Setup]
8 | AppId={{16A49296-8A8D-4BDA-A743-5F1BF02953D5}
9 | AppName={#AppName}
10 | AppVersion={#AppVersion}
11 | AppPublisher={#AppPublisher}
12 | AppPublisherURL={#AppURL}
13 | AppSupportURL={#AppURL}
14 | AppUpdatesURL={#AppURL}
15 | DefaultDirName={pf}\{#AppPublisher}\{#AppName}
16 | DisableProgramGroupPage=auto
17 | DisableDirPage=auto
18 | AlwaysShowDirOnReadyPage=yes
19 | LicenseFile=LICENSE
20 | OutputBaseFilename={#AppName}-{#AppVersion}-setup
21 | OutputDir=target\{#AppName}-{#AppVersion}
22 | SetupIconFile=src\main\resources\images\icon.ico
23 | UninstallDisplayIcon={uninstallexe}
24 | Compression=lzma
25 | SolidCompression=yes
26 | ArchitecturesAllowed=x86 x64 ia64
27 | ArchitecturesInstallIn64BitMode=x64 ia64
28 |
29 | [Languages]
30 | Name: "english"; MessagesFile: "compiler:Default.isl"
31 | Name: "dutch"; MessagesFile: "compiler:Languages\Dutch.isl"
32 | Name: "brazilianportuguese"; MessagesFile: "compiler:Languages\BrazilianPortuguese.isl"
33 |
34 | [Tasks]
35 | Name: "desktopicon"; Description: "{cm:CreateDesktopIcon}"; GroupDescription: "{cm:AdditionalIcons}"; Flags: unchecked
36 | Name: "quicklaunchicon"; Description: "{cm:CreateQuickLaunchIcon}"; GroupDescription: "{cm:AdditionalIcons}"; Flags: unchecked; OnlyBelowVersion: 0,6.1
37 |
38 | [Files]
39 | Source: "target\{#AppName}-{#AppVersion}\{#AppExeName}"; DestDir: "{app}"; Flags: ignoreversion
40 | ; NOTE: Don't use "Flags: ignoreversion" on any shared system files
41 |
42 | [Icons]
43 | Name: "{commonprograms}\{#AppPublisher}\{#AppName}"; Filename: "{app}\{#AppExeName}"
44 | Name: "{commondesktop}\{#AppPublisher}\{#AppName}"; Filename: "{app}\{#AppExeName}"; Tasks: desktopicon
45 | Name: "{userappdata}\Microsoft\Internet Explorer\Quick Launch\{#AppPublisher}\{#AppName}"; Filename: "{app}\{#AppExeName}"; Tasks: quicklaunchicon
46 |
47 | [Run]
48 | Filename: "{app}\{#AppExeName}"; Description: "{cm:LaunchProgram,{#StringChange(AppName, '&', '&&')}}"; Flags: nowait postinstall skipifsilent
49 |
--------------------------------------------------------------------------------
/src/main/java/com/jvms/i18neditor/editor/ResourceField.java:
--------------------------------------------------------------------------------
1 | package com.jvms.i18neditor.editor;
2 |
3 | import java.awt.KeyboardFocusManager;
4 | import java.awt.Rectangle;
5 | import java.awt.event.FocusAdapter;
6 | import java.awt.event.FocusEvent;
7 | import java.util.Locale;
8 |
9 | import javax.swing.JComponent;
10 |
11 | import com.jvms.i18neditor.Resource;
12 | import com.jvms.i18neditor.swing.JTextArea;
13 |
14 | /**
15 | * This class represents a text area to edit the value of a translation.
16 | *
17 | * @author Jacob van Mourik
18 | */
19 | public class ResourceField extends JTextArea implements Comparable {
20 | private final static long serialVersionUID = 2034814490878477055L;
21 | private final Resource resource;
22 |
23 | public ResourceField(Resource resource) {
24 | super();
25 | this.resource = resource;
26 | setupUI();
27 | }
28 |
29 | public String getValue() {
30 | return getText().trim();
31 | }
32 |
33 | public void setValue(String key) {
34 | setText(resource.getTranslation(key));
35 | undoManager.discardAllEdits();
36 | }
37 |
38 | public Resource getResource() {
39 | return resource;
40 | }
41 |
42 | @Override
43 | public int compareTo(ResourceField o) {
44 | Locale a = getResource().getLocale();
45 | Locale b = o.getResource().getLocale();
46 | if (a == null) {
47 | return -1;
48 | }
49 | if (b == null) {
50 | return 1;
51 | }
52 | return a.getDisplayName().compareTo(b.getDisplayName());
53 | }
54 |
55 | private void setupUI() {
56 | // Add focus traversal support
57 | setFocusTraversalKeys(KeyboardFocusManager.FORWARD_TRAVERSAL_KEYS, null);
58 | setFocusTraversalKeys(KeyboardFocusManager.BACKWARD_TRAVERSAL_KEYS, null);
59 | addFocusListener(new ResourceFieldFocusListener());
60 | }
61 |
62 | private class ResourceFieldFocusListener extends FocusAdapter {
63 | @Override
64 | public void focusGained(FocusEvent e) {
65 | JComponent parent = (JComponent)getParent();
66 | Rectangle bounds = new Rectangle(getBounds());
67 | bounds.y -= 35; // add fixed space at the top
68 | bounds.height += 70; // add fixed space at the bottom
69 | parent.scrollRectToVisible(bounds);
70 | }
71 | }
72 | }
73 |
--------------------------------------------------------------------------------
/src/main/java/com/jvms/i18neditor/editor/TranslationTreeCellRenderer.java:
--------------------------------------------------------------------------------
1 | package com.jvms.i18neditor.editor;
2 |
3 | import java.awt.Color;
4 | import java.awt.Component;
5 | import java.awt.event.MouseListener;
6 |
7 | import javax.swing.ImageIcon;
8 | import javax.swing.JLabel;
9 | import javax.swing.JTree;
10 | import javax.swing.UIManager;
11 | import javax.swing.tree.DefaultTreeCellRenderer;
12 |
13 | import com.jvms.i18neditor.editor.TranslationTreeStatusIcon.StatusIconType;
14 | import com.jvms.i18neditor.util.Images;
15 |
16 | /**
17 | * This class represents a default cell renderer for the translation tree.
18 | *
19 | * @author Jacob van Mourik
20 | */
21 | public class TranslationTreeCellRenderer extends DefaultTreeCellRenderer {
22 | private final static long serialVersionUID = 3511394180407171920L;
23 | private final static ImageIcon ROOT_ICON = Images.loadFromClasspath("images/icon-folder.png");
24 | private final Color selectionBackground;
25 |
26 | public TranslationTreeCellRenderer() {
27 | super();
28 | Color bg = UIManager.getColor("Panel.background");
29 | selectionBackground = new Color(bg.getRed(), bg.getGreen(), bg.getBlue());
30 | setLeafIcon(null);
31 | setClosedIcon(null);
32 | setOpenIcon(null);
33 | for (MouseListener l : getMouseListeners()) {
34 | removeMouseListener(l);
35 | }
36 | }
37 |
38 | public Color getSelectionBackground() {
39 | return selectionBackground;
40 | }
41 |
42 | @Override
43 | public Component getTreeCellRendererComponent(JTree tree, Object value, boolean selected, boolean expanded,
44 | boolean leaf, int row, boolean hasFocus) {
45 | TranslationTreeNode node = (TranslationTreeNode) value;
46 | TranslationTreeModel model = (TranslationTreeModel) tree.getModel();
47 | JLabel l = (JLabel) super.getTreeCellRendererComponent(tree, value, selected, expanded, leaf, row, hasFocus);
48 | l.setOpaque(true);
49 | l.setForeground(tree.getForeground());
50 | l.setBackground(tree.getBackground());
51 | if (!node.isRoot() && (node.hasError() || model.hasErrorChildNode(node))) {
52 | l.setIcon(new TranslationTreeStatusIcon(StatusIconType.Warning));
53 | }
54 | if (node.isRoot()) {
55 | l.setIcon(ROOT_ICON);
56 | }
57 | if (selected) {
58 | l.setBackground(selectionBackground);
59 | }
60 | return l;
61 | }
62 | }
63 |
--------------------------------------------------------------------------------
/src/main/java/com/jvms/i18neditor/editor/TranslationTreeUI.java:
--------------------------------------------------------------------------------
1 | package com.jvms.i18neditor.editor;
2 |
3 | import java.awt.Graphics;
4 | import java.awt.Insets;
5 | import java.awt.Rectangle;
6 |
7 | import javax.swing.Icon;
8 | import javax.swing.JComponent;
9 | import javax.swing.JTree;
10 | import javax.swing.plaf.basic.BasicTreeUI;
11 | import javax.swing.tree.TreePath;
12 |
13 | import com.jvms.i18neditor.editor.TranslationTreeToggleIcon.ToggleIconType;
14 |
15 | /**
16 | * This class represents a default UI for the translation tree.
17 | *
18 | * @author Jacob van Mourik
19 | */
20 | public class TranslationTreeUI extends BasicTreeUI {
21 | private TranslationTreeToggleIcon expandedIcon = new TranslationTreeToggleIcon(ToggleIconType.Expanded);
22 | private TranslationTreeToggleIcon collapsedIcon = new TranslationTreeToggleIcon(ToggleIconType.Collapsed);
23 |
24 | @Override
25 | protected void toggleExpandState(TreePath path) {
26 | // do nothing
27 | }
28 |
29 | @Override
30 | protected void paintVerticalLine(Graphics g, JComponent c, int y, int left, int right) {}
31 |
32 | @Override
33 | protected void paintHorizontalLine(Graphics g, JComponent c, int y, int left, int right) {}
34 |
35 | @Override
36 | protected void paintVerticalPartOfLeg(Graphics g, Rectangle clipBounds, Insets insets, TreePath path) {}
37 |
38 | @Override
39 | protected void paintHorizontalPartOfLeg(Graphics g, Rectangle clipBounds, Insets insets, Rectangle bounds,
40 | TreePath path, int row, boolean expanded, boolean hasBeenExpanded, boolean leaf) {}
41 |
42 | @Override
43 | public Rectangle getPathBounds(JTree tree, TreePath path) {
44 | if (tree != null && treeState != null) {
45 | return getPathBounds(path, tree.getInsets(), new Rectangle());
46 | }
47 | return null;
48 | }
49 |
50 | @Override
51 | public Icon getCollapsedIcon() {
52 | return collapsedIcon;
53 | }
54 |
55 | @Override
56 | public Icon getExpandedIcon() {
57 | return expandedIcon;
58 | }
59 |
60 | private Rectangle getPathBounds(TreePath path, Insets insets, Rectangle bounds) {
61 | bounds = treeState.getBounds(path, bounds);
62 | if (bounds != null) {
63 | bounds.x = 0;
64 | bounds.y += insets.top;
65 | bounds.width = tree.getWidth();
66 | }
67 | return bounds;
68 | }
69 | }
70 |
--------------------------------------------------------------------------------
/src/main/java/com/jvms/i18neditor/editor/TranslationTreeNode.java:
--------------------------------------------------------------------------------
1 | package com.jvms.i18neditor.editor;
2 |
3 | import java.util.Arrays;
4 | import java.util.Collections;
5 | import java.util.List;
6 | import java.util.Optional;
7 | import java.util.stream.Collectors;
8 |
9 | import javax.swing.tree.DefaultMutableTreeNode;
10 | import javax.swing.tree.TreeNode;
11 |
12 | import com.jvms.i18neditor.util.ResourceKeys;
13 |
14 | /**
15 | * This class represents a single node of the translation tree.
16 | *
17 | * @author Jacob van Mourik
18 | */
19 | public class TranslationTreeNode extends DefaultMutableTreeNode {
20 | private final static long serialVersionUID = -7372403592538358822L;
21 | private String name;
22 | private boolean error;
23 |
24 | public TranslationTreeNode(String name, List keys) {
25 | super();
26 | this.name = name;
27 | ResourceKeys.uniqueRootKeys(keys).forEach(rootKey -> {
28 | List subKeys = ResourceKeys.extractChildKeys(keys, rootKey);
29 | add(new TranslationTreeNode(rootKey, subKeys));
30 | });
31 | }
32 |
33 | public String getName() {
34 | return name;
35 | }
36 |
37 | public void setName(String name) {
38 | this.name = name;
39 | }
40 |
41 | public void setError(boolean error) {
42 | this.error = error;
43 | }
44 |
45 | public boolean hasError() {
46 | return isEditable() && error;
47 | }
48 |
49 | public boolean isEditable() {
50 | return !isRoot();
51 | }
52 |
53 | public String getKey() {
54 | List path = Arrays.asList(getPath());
55 | List parts = path.stream()
56 | .filter(p -> !((TranslationTreeNode)p).isRoot())
57 | .map(p -> p.toString())
58 | .collect(Collectors.toList());
59 | return ResourceKeys.create(parts);
60 | }
61 |
62 | @SuppressWarnings("unchecked")
63 | public List getChildren() {
64 | return Collections.list(children());
65 | }
66 |
67 | public TranslationTreeNode getChild(String name) {
68 | Optional child = getChildren().stream()
69 | .filter(i -> i.getName().equals(name))
70 | .findFirst();
71 | return child.isPresent() ? child.get() : null;
72 | }
73 |
74 | public TranslationTreeNode cloneWithChildren() {
75 | return cloneWithChildren(this);
76 | }
77 |
78 | @Override
79 | public String toString() {
80 | return name;
81 | }
82 |
83 | private TranslationTreeNode cloneWithChildren(TranslationTreeNode parent) {
84 | TranslationTreeNode newParent = (TranslationTreeNode) parent.clone();
85 | for (TranslationTreeNode n : parent.getChildren()) {
86 | newParent.add(cloneWithChildren(n));
87 | }
88 | return newParent;
89 | }
90 | }
--------------------------------------------------------------------------------
/src/main/java/com/jvms/i18neditor/editor/EditorProject.java:
--------------------------------------------------------------------------------
1 | package com.jvms.i18neditor.editor;
2 |
3 | import java.nio.file.Path;
4 | import java.util.List;
5 |
6 | import com.google.common.collect.ImmutableList;
7 | import com.google.common.collect.Lists;
8 | import com.jvms.i18neditor.FileStructure;
9 | import com.jvms.i18neditor.Resource;
10 | import com.jvms.i18neditor.ResourceType;
11 |
12 | /**
13 | * This class represents an editor project.
14 | *
15 | * @author Jacob van Mourik
16 | */
17 | public class EditorProject {
18 | private Path path;
19 | private String resourceFileDefinition;
20 | private ResourceType resourceType;
21 | private List resources;
22 | private boolean minifyResources;
23 | private boolean flattenJSON;
24 | private FileStructure resourceFileStructure;
25 |
26 | public EditorProject(Path path) {
27 | this.path = path;
28 | this.resources = Lists.newLinkedList();
29 | }
30 |
31 | public Path getPath() {
32 | return path;
33 | }
34 |
35 | public void setPath(Path path) {
36 | this.path = path;
37 | }
38 |
39 | public ResourceType getResourceType() {
40 | return resourceType;
41 | }
42 |
43 | public void setResourceType(ResourceType resourceType) {
44 | this.resourceType = resourceType;
45 | }
46 |
47 | public List getResources() {
48 | return ImmutableList.copyOf(resources);
49 | }
50 |
51 | public void setResources(List resources) {
52 | this.resources = resources;
53 | }
54 |
55 | public void addResource(Resource resource) {
56 | resources.add(resource);
57 | }
58 |
59 | public boolean hasResources() {
60 | return !resources.isEmpty();
61 | }
62 |
63 | public String getResourceFileDefinition() {
64 | return resourceFileDefinition;
65 | }
66 |
67 | public void setResourceFileDefinition(String resourceFileDefinition) {
68 | this.resourceFileDefinition = resourceFileDefinition;
69 | }
70 |
71 | public boolean isMinifyResources() {
72 | return minifyResources;
73 | }
74 |
75 | public void setMinifyResources(boolean minifyResources) {
76 | this.minifyResources = minifyResources;
77 | }
78 |
79 | public boolean isFlattenJSON() {
80 | return flattenJSON;
81 | }
82 |
83 | public void setFlattenJSON(boolean flattenJSON) {
84 | this.flattenJSON = flattenJSON;
85 | }
86 |
87 | public boolean supportsResourceParentValues() {
88 | return resourceType == ResourceType.Properties;
89 | }
90 |
91 | public FileStructure getResourceFileStructure() {
92 | return resourceFileStructure;
93 | }
94 |
95 | public void setResourceFileStructure(FileStructure resourceFileStructure) {
96 | this.resourceFileStructure = resourceFileStructure;
97 | }
98 | }
99 |
--------------------------------------------------------------------------------
/src/main/java/com/jvms/i18neditor/util/GithubRepoUtil.java:
--------------------------------------------------------------------------------
1 | package com.jvms.i18neditor.util;
2 |
3 | import java.io.IOException;
4 | import java.io.InputStreamReader;
5 | import java.net.HttpURLConnection;
6 | import java.net.URL;
7 | import java.util.concurrent.ExecutorService;
8 | import java.util.concurrent.Executors;
9 | import java.util.concurrent.Future;
10 |
11 | import org.slf4j.Logger;
12 | import org.slf4j.LoggerFactory;
13 |
14 | import com.google.common.base.Charsets;
15 | import com.google.common.util.concurrent.ThreadFactoryBuilder;
16 | import com.google.gson.Gson;
17 | import com.google.gson.annotations.SerializedName;
18 |
19 | /**
20 | * This class provides utility functions for retrieving Github Repository data.
21 | *
22 | * @author Jacob van Mourik
23 | */
24 | public final class GithubRepoUtil {
25 | private final static Logger log = LoggerFactory.getLogger(GithubRepoUtil.class);
26 | private final static Gson gson = new Gson();
27 | private final static ExecutorService executor;
28 |
29 | static {
30 | executor = Executors.newCachedThreadPool(new ThreadFactoryBuilder()
31 | .setNameFormat("github-repo-util-pool-%d")
32 | .build());
33 | }
34 |
35 | /**
36 | * Gets the full Github Repository URL.
37 | *
38 | * @param username the Github Repository user name.
39 | * @param project the Github Repository project name.
40 | * @return the full Github Repository URL.
41 | */
42 | public static String getURL(String username, String project) {
43 | return "https://github.com/" + username + "/" + project;
44 | }
45 |
46 | /**
47 | * Gets the latest release data of a Github Repository.
48 | *
49 | * @param username the Github Repository user name.
50 | * @param project the Github Repository project name.
51 | * @return the latest Github Repository release data.
52 | */
53 | public static Future getLatestRelease(String username, String project) {
54 | return executor.submit(() -> {
55 | HttpURLConnection connection = null;
56 | URL url = new URL("https://api.github.com/repos/" + username + "/" + project + "/releases/latest");
57 | try {
58 | connection = (HttpURLConnection)url.openConnection();
59 | try (InputStreamReader reader = new InputStreamReader(connection.getInputStream(), Charsets.UTF_8)) {
60 | return gson.fromJson(reader, GithubRepoReleaseData.class);
61 | }
62 | } catch (IOException e) {
63 | log.error("Unable to retrieve latest github release data", e);
64 | return null;
65 | } finally {
66 | if (connection != null) {
67 | connection.disconnect();
68 | }
69 | }
70 | });
71 | }
72 |
73 | /**
74 | * This class represents Github Repository release data.
75 | */
76 | public final class GithubRepoReleaseData {
77 | @SerializedName("tag_name")
78 | private String tagName;
79 | @SerializedName("html_url")
80 | private String htmlUrl;
81 |
82 | public String getTagName() {
83 | return tagName;
84 | }
85 |
86 | public String getHtmlUrl() {
87 | return htmlUrl;
88 | }
89 | }
90 | }
91 |
--------------------------------------------------------------------------------
/src/main/java/com/jvms/i18neditor/swing/text/JTextComponentMenu.java:
--------------------------------------------------------------------------------
1 | package com.jvms.i18neditor.swing.text;
2 |
3 | import java.awt.Component;
4 | import java.awt.Toolkit;
5 | import java.awt.event.KeyEvent;
6 |
7 | import javax.swing.Action;
8 | import javax.swing.JPopupMenu;
9 | import javax.swing.KeyStroke;
10 | import javax.swing.text.DefaultEditorKit;
11 | import javax.swing.text.JTextComponent;
12 | import javax.swing.undo.UndoManager;
13 |
14 | import com.google.common.base.Strings;
15 | import com.jvms.i18neditor.swing.UndoAction;
16 | import com.jvms.i18neditor.util.MessageBundle;
17 |
18 | /**
19 | * A popup menu implementation useful as a edit menu for a {@link JTextComponent}.
20 | *
21 | * @author Jacob van Mourik
22 | */
23 | public class JTextComponentMenu extends JPopupMenu {
24 | private final static long serialVersionUID = 5967213965940023534L;
25 | private JTextComponent parent;
26 | private UndoManager undoManager;
27 | private Action cutAction;
28 | private Action copyAction;
29 | private Action deleteAction;
30 | private Action undoAction;
31 |
32 | public JTextComponentMenu(JTextComponent parent, UndoManager undoManager) {
33 | super();
34 |
35 | this.parent = parent;
36 | this.undoManager = undoManager;
37 | int keyMask =Toolkit.getDefaultToolkit().getMenuShortcutKeyMask();
38 |
39 | undoAction = new UndoAction(undoManager);
40 | undoAction.putValue(Action.NAME, MessageBundle.get("swing.action.undo"));
41 | undoAction.putValue(Action.ACCELERATOR_KEY, KeyStroke.getKeyStroke(KeyEvent.VK_Z, keyMask));
42 | add(undoAction);
43 |
44 | addSeparator();
45 |
46 | cutAction = new DefaultEditorKit.CutAction();
47 | cutAction.putValue(Action.NAME, MessageBundle.get("swing.action.cut"));
48 | cutAction.putValue(Action.ACCELERATOR_KEY, KeyStroke.getKeyStroke(KeyEvent.VK_X, keyMask));
49 | add(cutAction);
50 |
51 | copyAction = new DefaultEditorKit.CopyAction();
52 | copyAction.putValue(Action.NAME, MessageBundle.get("swing.action.copy"));
53 | copyAction.putValue(Action.ACCELERATOR_KEY, KeyStroke.getKeyStroke(KeyEvent.VK_C, keyMask));
54 | add(copyAction);
55 |
56 | Action pasteAction = new DefaultEditorKit.PasteAction();
57 | pasteAction.putValue(Action.NAME, MessageBundle.get("swing.action.paste"));
58 | pasteAction.putValue(Action.ACCELERATOR_KEY, KeyStroke.getKeyStroke(KeyEvent.VK_V, keyMask));
59 | add(pasteAction);
60 |
61 | deleteAction = new DeleteAction(MessageBundle.get("swing.action.delete"));
62 | add(deleteAction);
63 |
64 | addSeparator();
65 |
66 | Action selectAllAction = new SelectAllAction(MessageBundle.get("swing.action.selectall"));
67 | selectAllAction.putValue(Action.ACCELERATOR_KEY, KeyStroke.getKeyStroke(KeyEvent.VK_A, keyMask));
68 | add(selectAllAction);
69 | }
70 |
71 | @Override
72 | public void show(Component invoker, int x, int y) {
73 | super.show(invoker, x, y);
74 | boolean hasSelection = !Strings.isNullOrEmpty(parent.getSelectedText());
75 | undoAction.setEnabled(undoManager.canUndo());
76 | cutAction.setEnabled(hasSelection);
77 | copyAction.setEnabled(hasSelection);
78 | deleteAction.setEnabled(hasSelection);
79 | }
80 | }
81 |
--------------------------------------------------------------------------------
/src/main/java/com/jvms/i18neditor/editor/AbstractSettingsPane.java:
--------------------------------------------------------------------------------
1 | package com.jvms.i18neditor.editor;
2 |
3 | import java.awt.GridBagConstraints;
4 | import java.awt.GridBagLayout;
5 | import java.awt.Insets;
6 | import java.util.List;
7 | import java.util.Locale;
8 | import java.util.stream.Collectors;
9 |
10 | import javax.swing.BorderFactory;
11 | import javax.swing.JPanel;
12 |
13 | import com.google.common.collect.Lists;
14 | import com.jvms.i18neditor.FileStructure;
15 | import com.jvms.i18neditor.util.MessageBundle;
16 |
17 | /**
18 | * This class represents an abstract base class for all setting panes.
19 | *
20 | * @author Jacob van Mourik
21 | */
22 | public abstract class AbstractSettingsPane extends JPanel {
23 | private final static long serialVersionUID = -8953194193840198893L;
24 | private GridBagConstraints vGridBagConstraints;
25 |
26 | protected final List fileStructureComboBoxItems = Lists.newArrayList(FileStructure.values()).stream()
27 | .map(val -> new ComboBoxFileStructure(val, MessageBundle.get("settings.filestructure." + val.name().toLowerCase())))
28 | .sorted()
29 | .collect(Collectors.toList());
30 |
31 | protected final List localeComboBoxItems = Editor.SUPPORTED_LANGUAGES.stream()
32 | .map(val -> new ComboBoxLocale(val))
33 | .sorted()
34 | .collect(Collectors.toList());
35 |
36 | protected AbstractSettingsPane() {
37 | super();
38 | vGridBagConstraints = new GridBagConstraints();
39 | vGridBagConstraints.insets = new Insets(4,4,4,4);
40 | vGridBagConstraints.fill = java.awt.GridBagConstraints.HORIZONTAL;
41 | vGridBagConstraints.anchor = java.awt.GridBagConstraints.WEST;
42 | vGridBagConstraints.weightx = 1;
43 | }
44 |
45 | protected GridBagConstraints createVerticalGridBagConstraints() {
46 | vGridBagConstraints.gridy = (vGridBagConstraints.gridy + 1) % Integer.MAX_VALUE;
47 | return vGridBagConstraints;
48 | }
49 |
50 | protected JPanel createFieldset(String title) {
51 | JPanel fieldset = new JPanel(new GridBagLayout());
52 | fieldset.setBorder(BorderFactory.createCompoundBorder(
53 | BorderFactory.createTitledBorder(null, title),
54 | BorderFactory.createEmptyBorder(5,5,5,5)));
55 | return fieldset;
56 | }
57 |
58 | protected class ComboBoxFileStructure implements Comparable {
59 | private FileStructure structure;
60 | private String label;
61 |
62 | public ComboBoxFileStructure(FileStructure structure, String label) {
63 | this.structure = structure;
64 | this.label = label;
65 | }
66 |
67 | public FileStructure getStructure() {
68 | return structure;
69 | }
70 |
71 | public String toString() {
72 | return label;
73 | }
74 |
75 | @Override
76 | public int compareTo(ComboBoxFileStructure o) {
77 | return toString().compareTo(o.toString());
78 | }
79 | }
80 |
81 | protected class ComboBoxLocale implements Comparable {
82 | private Locale locale;
83 |
84 | public ComboBoxLocale(Locale locale) {
85 | this.locale = locale;
86 | }
87 |
88 | public Locale getLocale() {
89 | return locale;
90 | }
91 |
92 | public String toString() {
93 | return locale.getDisplayName();
94 | }
95 |
96 | @Override
97 | public int compareTo(ComboBoxLocale o) {
98 | return toString().compareTo(o.toString());
99 | }
100 | }
101 | }
102 |
--------------------------------------------------------------------------------
/src/main/java/com/jvms/i18neditor/editor/TranslationTreeModel.java:
--------------------------------------------------------------------------------
1 | package com.jvms.i18neditor.editor;
2 |
3 | import java.util.Enumeration;
4 | import java.util.List;
5 |
6 | import javax.swing.tree.DefaultTreeModel;
7 |
8 | import com.google.common.collect.Lists;
9 | import com.jvms.i18neditor.util.MessageBundle;
10 | import com.jvms.i18neditor.util.ResourceKeys;
11 |
12 | /**
13 | * This class represents a model for the translation tree.
14 | *
15 | * @author Jacob van Mourik
16 | */
17 | public class TranslationTreeModel extends DefaultTreeModel {
18 | private final static long serialVersionUID = 3261808274177599488L;
19 |
20 | public TranslationTreeModel() {
21 | super(new TranslationTreeNode(MessageBundle.get("tree.root.name"), Lists.newArrayList()));
22 | }
23 |
24 | public TranslationTreeModel(List keys) {
25 | super(new TranslationTreeNode(MessageBundle.get("tree.root.name"), keys));
26 | }
27 |
28 | public Enumeration getEnumeration() {
29 | return getEnumeration((TranslationTreeNode) getRoot());
30 | }
31 |
32 | @SuppressWarnings("unchecked")
33 | public Enumeration getEnumeration(TranslationTreeNode node) {
34 | return node.depthFirstEnumeration();
35 | }
36 |
37 | public TranslationTreeNode getNodeByKey(String key) {
38 | Enumeration e = getEnumeration();
39 | while (e.hasMoreElements()) {
40 | TranslationTreeNode n = e.nextElement();
41 | if (n.getKey().equals(key)) {
42 | return n;
43 | }
44 | }
45 | return null;
46 | }
47 |
48 | public boolean hasErrorChildNode(TranslationTreeNode node) {
49 | Enumeration e = getEnumeration(node);
50 | while (e.hasMoreElements()) {
51 | TranslationTreeNode n = e.nextElement();
52 | if (n.hasError()) {
53 | return true;
54 | }
55 | }
56 | return false;
57 | }
58 |
59 | public TranslationTreeNode getClosestParentNodeByKey(String key) {
60 | TranslationTreeNode node = null;
61 | int count = ResourceKeys.size(key);
62 | while (node == null && count > 0) {
63 | key = ResourceKeys.withoutLastPart(key);
64 | node = getNodeByKey(key);
65 | count--;
66 | }
67 | if (node != null) {
68 | return node;
69 | } else {
70 | return (TranslationTreeNode) getRoot();
71 | }
72 | }
73 |
74 | public void insertNodeInto(TranslationTreeNode newChild, TranslationTreeNode parent) {
75 | insertNodeInto(newChild, parent, getNewChildIndex(newChild, parent));
76 | }
77 |
78 | public void insertDescendantsInto(TranslationTreeNode source, TranslationTreeNode target) {
79 | source.getChildren().forEach(child -> {
80 | TranslationTreeNode existing = target.getChild(child.getName());
81 | if (existing != null) {
82 | if (existing.isLeaf()) {
83 | removeNodeFromParent(existing);
84 | insertNodeInto(child, target);
85 | } else {
86 | insertDescendantsInto(child, existing);
87 | }
88 | } else {
89 | insertNodeInto(child, target);
90 | }
91 | });
92 | }
93 |
94 | public void nodeWithParentsChanged(TranslationTreeNode node) {
95 | while (node != null) {
96 | nodeChanged(node);
97 | node = (TranslationTreeNode) node.getParent();
98 | }
99 | }
100 |
101 | private int getNewChildIndex(TranslationTreeNode newChild, TranslationTreeNode parent) {
102 | int result = 0;
103 | for (TranslationTreeNode n : parent.getChildren()) {
104 | if (n.getName().compareTo(newChild.getName()) < 0) {
105 | result++;
106 | }
107 | }
108 | return result;
109 | }
110 | }
111 |
--------------------------------------------------------------------------------
/src/main/java/com/jvms/i18neditor/swing/util/Dialogs.java:
--------------------------------------------------------------------------------
1 | package com.jvms.i18neditor.swing.util;
2 |
3 | import java.awt.Component;
4 | import java.awt.Font;
5 | import java.awt.GridBagLayout;
6 | import java.awt.GridLayout;
7 |
8 | import javax.swing.BorderFactory;
9 | import javax.swing.JLabel;
10 | import javax.swing.JOptionPane;
11 | import javax.swing.JPanel;
12 | import javax.swing.text.Caret;
13 |
14 | import com.google.common.base.Strings;
15 | import com.jvms.i18neditor.swing.JHelpLabel;
16 | import com.jvms.i18neditor.swing.JHtmlPane;
17 | import com.jvms.i18neditor.swing.JTextField;
18 | import com.jvms.i18neditor.swing.event.RequestInitialFocusListener;
19 | import com.jvms.i18neditor.swing.text.BlinkCaret;
20 |
21 | /**
22 | * This class provides utility functions for dialogs using {@link JOptionPane}.
23 | *
24 | * @author Jacob van Mourik
25 | */
26 | public final class Dialogs {
27 |
28 | public static void showErrorDialog(Component parent, String title, String message) {
29 | JOptionPane.showMessageDialog(parent, message, title, JOptionPane.ERROR_MESSAGE);
30 | }
31 |
32 | public static void showWarningDialog(Component parent, String title, String message) {
33 | JOptionPane.showMessageDialog(parent, message, title, JOptionPane.WARNING_MESSAGE);
34 | }
35 |
36 | public static void showInfoDialog(Component parent, String title, String message) {
37 | JOptionPane.showMessageDialog(parent, message, title, JOptionPane.INFORMATION_MESSAGE);
38 | }
39 |
40 | public static void showMessageDialog(Component parent, String title, String message) {
41 | JOptionPane.showMessageDialog(parent, message, title, JOptionPane.PLAIN_MESSAGE);
42 | }
43 |
44 | public static void showComponentDialog(Component parent, String title, Component component) {
45 | JOptionPane.showMessageDialog(parent, component, title, JOptionPane.PLAIN_MESSAGE);
46 | }
47 |
48 | public static void showHtmlDialog(Component parent, String title, String body) {
49 | Font font = parent.getFont();
50 | JHtmlPane pane = new JHtmlPane(parent, "" + body + "");
52 | pane.setBorder(BorderFactory.createEmptyBorder(15,15,15,15));
53 | showComponentDialog(parent, title, pane);
54 | }
55 |
56 | public static boolean showConfirmDialog(Component parent, String title, String message, int type) {
57 | return JOptionPane.showConfirmDialog(parent, message, title, JOptionPane.YES_NO_OPTION, type) == JOptionPane.YES_OPTION;
58 | }
59 |
60 | public static String showInputDialog(Component parent, String title, String label, String help, int type) {
61 | return showInputDialog(parent, title, label, help, type, null, new BlinkCaret());
62 | }
63 |
64 | public static String showInputDialog(Component parent, String title, String label, String help, int type, String initialText, Caret caret) {
65 | JPanel content = new JPanel(new GridLayout(0, 1));
66 |
67 | if (!Strings.isNullOrEmpty(label)) {
68 | content.add(new JLabel(label));
69 | }
70 |
71 | JTextField field = new JTextField(initialText);
72 | field.addAncestorListener(new RequestInitialFocusListener());
73 | field.setCaret(caret);
74 | if (initialText != null) {
75 | field.setCaretPosition(initialText.length());
76 | }
77 | content.add(field);
78 |
79 | if (!Strings.isNullOrEmpty(help)) {
80 | content.add(new JHelpLabel(help));
81 | }
82 |
83 | JPanel container = new JPanel(new GridBagLayout());
84 | container.add(content);
85 |
86 | int result = JOptionPane.showConfirmDialog(parent, container, title, JOptionPane.OK_CANCEL_OPTION, type);
87 | return result == JOptionPane.OK_OPTION ? field.getText() : null;
88 | }
89 | }
--------------------------------------------------------------------------------
/src/main/java/com/jvms/i18neditor/editor/EditorProjectSettingsPane.java:
--------------------------------------------------------------------------------
1 | package com.jvms.i18neditor.editor;
2 |
3 | import java.awt.GridBagLayout;
4 | import java.awt.GridLayout;
5 | import java.awt.event.KeyAdapter;
6 | import java.awt.event.KeyEvent;
7 |
8 | import javax.swing.JCheckBox;
9 | import javax.swing.JComboBox;
10 | import javax.swing.JLabel;
11 | import javax.swing.JPanel;
12 |
13 | import com.jvms.i18neditor.ResourceType;
14 | import com.jvms.i18neditor.swing.JHelpLabel;
15 | import com.jvms.i18neditor.swing.JTextField;
16 | import com.jvms.i18neditor.util.MessageBundle;
17 |
18 | /**
19 | * This class represents the project settings pane.
20 | *
21 | * @author Jacob van Mourik
22 | */
23 | public class EditorProjectSettingsPane extends AbstractSettingsPane {
24 | private final static long serialVersionUID = 5665963334924596315L;
25 | private Editor editor;
26 |
27 | public EditorProjectSettingsPane(Editor editor) {
28 | super();
29 | this.editor = editor;
30 | this.setupUI();
31 | }
32 |
33 | private void setupUI() {
34 | EditorProject project = editor.getProject();
35 |
36 | // General settings
37 | JPanel fieldset1 = createFieldset(MessageBundle.get("settings.fieldset.general"));
38 |
39 | ComboBoxFileStructure currentFileStructureItem = null;
40 | for (ComboBoxFileStructure item : fileStructureComboBoxItems) {
41 | if (item.getStructure().equals(project.getResourceFileStructure())) {
42 | currentFileStructureItem = item;
43 | break;
44 | }
45 | }
46 | JPanel fileStructurePanel = new JPanel(new GridLayout(0, 1));
47 | JLabel fileStructureLabel = new JLabel(MessageBundle.get("settings.filestructure.title"));
48 | JComboBox fileStructureField = new JComboBox(fileStructureComboBoxItems.toArray());
49 | fileStructureField.setSelectedItem(currentFileStructureItem);
50 | fileStructureField.addActionListener(e -> {
51 | project.setResourceFileStructure(((ComboBoxFileStructure)fileStructureField.getSelectedItem()).getStructure());
52 | });
53 | fileStructurePanel.add(fileStructureLabel);
54 | fileStructurePanel.add(fileStructureField);
55 | fieldset1.add(fileStructurePanel, createVerticalGridBagConstraints());
56 |
57 | JPanel resourceDefinitionPanel = new JPanel(new GridLayout(0, 1));
58 | JLabel resourceDefinitionLabel = new JLabel(MessageBundle.get("settings.resourcedef.title"));
59 | JHelpLabel resourceDefinitionHelpLabel = new JHelpLabel(MessageBundle.get("settings.resourcedef.help"));
60 | JTextField resourceDefinitionField = new JTextField(project.getResourceFileDefinition());
61 | resourceDefinitionField.addKeyListener(new KeyAdapter() {
62 | @Override
63 | public void keyReleased(KeyEvent e) {
64 | String value = resourceDefinitionField.getText().trim();
65 | project.setResourceFileDefinition(value.isEmpty() ? EditorSettings.DEFAULT_RESOURCE_FILE_DEFINITION : value);
66 | }
67 | });
68 | resourceDefinitionPanel.add(resourceDefinitionLabel);
69 | resourceDefinitionPanel.add(resourceDefinitionField);
70 | fieldset1.add(resourceDefinitionPanel, createVerticalGridBagConstraints());
71 | fieldset1.add(resourceDefinitionHelpLabel, createVerticalGridBagConstraints());
72 |
73 | ResourceType type = project.getResourceType();
74 | if (type == ResourceType.JSON || type == ResourceType.ES6) {
75 | JCheckBox minifyBox = new JCheckBox(MessageBundle.get("settings.minify.title"));
76 | minifyBox.setSelected(project.isMinifyResources());
77 | minifyBox.addChangeListener(e -> project.setMinifyResources(minifyBox.isSelected()));
78 | fieldset1.add(minifyBox, createVerticalGridBagConstraints());
79 |
80 | JCheckBox flattenJSONBox = new JCheckBox(MessageBundle.get("settings.flattenjson.title"));
81 | flattenJSONBox.setSelected(project.isFlattenJSON());
82 | flattenJSONBox.addChangeListener(e -> project.setFlattenJSON(flattenJSONBox.isSelected()));
83 | fieldset1.add(flattenJSONBox, createVerticalGridBagConstraints());
84 | }
85 |
86 | setLayout(new GridBagLayout());
87 | add(fieldset1, createVerticalGridBagConstraints());
88 | }
89 | }
90 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | THIS PROJECT IS NO LONGER MAINTAINED
2 |
3 | # i18n-editor [](https://travis-ci.org/jcbvm/i18n-editor)
4 |
5 | This application lets you manage multiple translation files at once.
6 | The editor supports translation files with the following format:
7 | - `JSON`
8 | - `ES6` (JSON wrapped in a javascript ES6 module)
9 | - `Properties` (java properties files, for example to be used for a ResourceBundle).
10 |
11 | ###
12 |
13 |
14 |
15 | ## Features
16 |
17 | - Editing multiple translation files at once.
18 | - Creating new translations/locales or editing existing ones.
19 | - Renaming, duplicating, creating or deleting individual translations.
20 | - Detecting missing translations.
21 | - Supports multiple project layouts.
22 | - Supports custom file naming.
23 | - Supports file minification.
24 | - Supports both nested and flat JSON/ES6 structure.
25 |
26 | ## Requirements
27 |
28 | The application requires java 8 to be installed on your system.
29 | http://www.oracle.com/technetwork/java/javase/downloads/jre8-downloads-2133155.html
30 |
31 | ## Download
32 |
33 | You can download the latest release by going to [this page](https://github.com/jcbvm/i18n-editor/releases/latest) and downloading the `.zip` file. If you're on Windows you can install the application by running the `.exe` file. If you're on Mac you can use the application by running the `.app` file. If you're on Linux you can use the application by running the `.jar` file.
34 |
35 | ## Usage
36 |
37 | #### Getting started
38 | To start, open the application and go to `File > New Project` to create a new project. After choosing the desired file format for your translations, select the root folder where you want to store your translation files. After selecting the root folder you'll be asked to add your first locale. From here you can add more locales by going to `Edit > Add Locale...` or start adding translations either via `Edit > Add Translation...`, via the right click menu in the left side panel or via the key field at the bottom of the left side panel. Each time you start the editor it will open the last project you was working on. You can always import an existing project by going to `File > Import Project...` and selecting the root folder of your existing project or by simply dragging your project folder into the application.
39 |
40 | #### Project layout
41 | The translations files can be stored in two different ways. Either using flat or folder structure.
42 | This setting can be changed in the settings menu, the default setting is flat structure.
43 |
44 | ##### Flat structure
45 | When using the flat structure the translation files are all in the same folder and follow the name pattern `translations_en_US`, `translations_nl_NL` etc. (the name and place of the locale within the name can be changed in the settings menu). When you create a project with a flat structure there will also a file be created called `translations`, this is a file you can use as default/fallback translation file (if you don't need a default/fallback file, you can simply remove it from your project folder).
46 |
47 | ##### Folder structure
48 | When using the folder structure folders will be named after the locales and the files within these folders will be of the form `translations` (the name can be changed in the settings menu).
49 |
50 | #### Changing settings
51 | You can access the settings of the editor by going to `Settings > Preferences...`. Here you can change the project file structure, the filename of the translation files you want to use (by default they are named `translations`), select whether you want to minify and or flatten the translations on save and change interface related properties. Some of this settings can also be applied on each project individually via `Settings > Project Preferences...`.
52 |
53 | ## Help translating
54 |
55 | Do you want this editor to be in your native language? You are free to create a pull request or issue with a new translation file of your desired language. Take a look at `src/main/resources/bundles` on how to create a translation file.
56 |
57 | ## License
58 |
59 | This project is released under the MIT license.
60 |
--------------------------------------------------------------------------------
/pom.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | 4.0.0
4 | com.jvms
5 | i18n-editor
6 | 2.0.0-beta.1
7 | jar
8 |
9 |
10 | org.apache.commons
11 | commons-lang3
12 | 3.5
13 |
14 |
15 | com.google.code.gson
16 | gson
17 | 2.8.0
18 |
19 |
20 | com.google.guava
21 | guava
22 | 21.0
23 |
24 |
25 | org.slf4j
26 | slf4j-simple
27 | 1.7.22
28 |
29 |
30 | junit
31 | junit
32 | 4.12
33 | test
34 |
35 |
36 |
37 |
38 |
39 | maven-compiler-plugin
40 | 3.3
41 |
42 | 1.8
43 | 1.8
44 |
45 |
46 |
47 | org.apache.maven.plugins
48 | maven-assembly-plugin
49 | 2.6
50 |
51 |
52 | package
53 |
54 | single
55 |
56 |
57 |
58 |
59 | com.jvms.i18neditor.Main
60 |
61 |
62 |
63 | jar-with-dependencies
64 |
65 |
66 |
67 |
68 |
69 |
70 | sh.tak.appbundler
71 | appbundle-maven-plugin
72 | 1.2.0
73 |
74 |
75 | package
76 |
77 | bundle
78 |
79 |
80 | com.jvms.i18neditor.Main
81 | images/icons.icns
82 |
83 |
84 |
85 |
86 |
87 | com.akathist.maven.plugins.launch4j
88 | launch4j-maven-plugin
89 | 1.7.7
90 |
91 |
92 | package
93 |
94 | launch4j
95 |
96 |
97 | gui
98 | ${project.build.directory}/${project.artifactId}-${project.version}/${project.artifactId}.exe
99 | ${project.build.directory}/${project.artifactId}-${project.version}-jar-with-dependencies.jar
100 | i18n Editor
101 |
102 | com.jvms.i18neditor.Main
103 |
104 |
105 | 1.8.0
106 |
107 | ${project.build.outputDirectory}/images/icon.ico
108 |
109 |
110 |
111 |
112 |
113 | org.apache.maven.plugins
114 | maven-antrun-plugin
115 | 1.8
116 |
117 |
118 | install
119 |
120 | run
121 |
122 |
123 |
124 |
126 |
127 |
128 |
129 |
130 |
131 |
132 |
133 |
--------------------------------------------------------------------------------
/src/main/java/com/jvms/i18neditor/editor/EditorSettings.java:
--------------------------------------------------------------------------------
1 | package com.jvms.i18neditor.editor;
2 |
3 | import java.util.List;
4 | import java.util.Locale;
5 |
6 | import com.jvms.i18neditor.FileStructure;
7 |
8 | /**
9 | * This class represents the editor settings.
10 | *
11 | * @author Jacob van Mourik
12 | */
13 | public class EditorSettings {
14 | public final static String DEFAULT_RESOURCE_FILE_DEFINITION = "translations{_LOCALE}";
15 |
16 | private int windowPositionX;
17 | private int windowPositionY;
18 | private int windowDeviderPosition;
19 | private int windowWidth;
20 | private int windowHeight;
21 | private boolean minifyResources;
22 | private boolean flattenJSON;
23 | private List history;
24 | private List lastExpandedNodes;
25 | private String lastSelectedNode;
26 | private boolean checkVersionOnStartup;
27 | private int defaultInputHeight;
28 | private boolean keyFieldEnabled;
29 | private boolean doubleClickTreeToggling;
30 | private String resourceFileDifinition;
31 | private Locale editorLanguage;
32 | private FileStructure resourceFileStructure;
33 |
34 | public int getWindowPositionX() {
35 | return windowPositionX;
36 | }
37 |
38 | public void setWindowPositionX(int windowPositionX) {
39 | this.windowPositionX = windowPositionX;
40 | }
41 |
42 | public int getWindowPositionY() {
43 | return windowPositionY;
44 | }
45 |
46 | public void setWindowPositionY(int windowPositionY) {
47 | this.windowPositionY = windowPositionY;
48 | }
49 |
50 | public int getWindowDeviderPosition() {
51 | return windowDeviderPosition;
52 | }
53 |
54 | public void setWindowDeviderPosition(int deviderPosition) {
55 | this.windowDeviderPosition = deviderPosition;
56 | }
57 |
58 | public int getWindowWidth() {
59 | return windowWidth;
60 | }
61 |
62 | public void setWindowWidth(int width) {
63 | this.windowWidth = width;
64 | }
65 |
66 | public int getWindowHeight() {
67 | return windowHeight;
68 | }
69 |
70 | public void setWindowHeight(int height) {
71 | this.windowHeight = height;
72 | }
73 |
74 | public List getHistory() {
75 | return history;
76 | }
77 |
78 | public void setHistory(List history) {
79 | this.history = history;
80 | }
81 |
82 | public List getLastExpandedNodes() {
83 | return lastExpandedNodes;
84 | }
85 |
86 | public void setLastExpandedNodes(List lastExpandedNodes) {
87 | this.lastExpandedNodes = lastExpandedNodes;
88 | }
89 |
90 | public String getLastSelectedNode() {
91 | return lastSelectedNode;
92 | }
93 |
94 | public void setLastSelectedNode(String lastSelectedNode) {
95 | this.lastSelectedNode = lastSelectedNode;
96 | }
97 |
98 | public String getResourceFileDefinition() {
99 | return resourceFileDifinition;
100 | }
101 |
102 | public void setResourceFileDefinition(String resourceFileDifinition) {
103 | this.resourceFileDifinition = resourceFileDifinition;
104 | }
105 |
106 | public boolean isMinifyResources() {
107 | return minifyResources;
108 | }
109 |
110 | public void setMinifyResources(boolean minifyResources) {
111 | this.minifyResources = minifyResources;
112 | }
113 |
114 | public boolean isCheckVersionOnStartup() {
115 | return checkVersionOnStartup;
116 | }
117 |
118 | public void setCheckVersionOnStartup(boolean checkVersionOnStartup) {
119 | this.checkVersionOnStartup = checkVersionOnStartup;
120 | }
121 |
122 | public int getDefaultInputHeight() {
123 | return defaultInputHeight;
124 | }
125 |
126 | public void setDefaultInputHeight(int rows) {
127 | this.defaultInputHeight = rows;
128 | }
129 |
130 | public boolean isKeyFieldEnabled() {
131 | return keyFieldEnabled;
132 | }
133 |
134 | public void setKeyFieldEnabled(boolean keyFieldEnabled) {
135 | this.keyFieldEnabled = keyFieldEnabled;
136 | }
137 |
138 | public boolean isDoubleClickTreeToggling() {
139 | return doubleClickTreeToggling;
140 | }
141 |
142 | public void setDoubleClickTreeToggling(boolean doubleClickTreeToggling) {
143 | this.doubleClickTreeToggling = doubleClickTreeToggling;
144 | }
145 |
146 | public boolean isFlattenJSON() {
147 | return flattenJSON;
148 | }
149 |
150 | public void setFlattenJSON(boolean flattenJSON) {
151 | this.flattenJSON = flattenJSON;
152 | }
153 |
154 | public Locale getEditorLanguage() {
155 | return editorLanguage;
156 | }
157 |
158 | public void setEditorLanguage(Locale editorLanguage) {
159 | this.editorLanguage = editorLanguage;
160 | }
161 |
162 | public FileStructure getResourceFileStructure() {
163 | return resourceFileStructure;
164 | }
165 |
166 | public void setResourceFileStructure(FileStructure resourceFileStructure) {
167 | this.resourceFileStructure = resourceFileStructure;
168 | }
169 | }
170 |
--------------------------------------------------------------------------------
/src/test/java/com/jvms/i18neditor/util/ResourceKeysTest.java:
--------------------------------------------------------------------------------
1 | package com.jvms.i18neditor.util;
2 |
3 | import static org.junit.Assert.*;
4 |
5 | import java.util.Arrays;
6 | import java.util.List;
7 |
8 | import org.junit.Test;
9 |
10 | import com.google.common.collect.Lists;
11 |
12 | /**
13 | *
14 | * @author Jacob
15 | */
16 | public class ResourceKeysTest {
17 |
18 | @Test
19 | public void createTest() {
20 | assertEquals("a", ResourceKeys.create("a"));
21 | assertEquals("a.b", ResourceKeys.create("a","b"));
22 | assertEquals("a.b.c", ResourceKeys.create("a","b.c"));
23 | assertEquals("a.b.c", ResourceKeys.create(Arrays.asList("a","b","c")));
24 | assertEquals("a.b.c", ResourceKeys.create(new String[]{"a","b","c"}));
25 | }
26 |
27 | @Test
28 | public void sizeTest() {
29 | assertEquals(1, ResourceKeys.size(""));
30 | assertEquals(1, ResourceKeys.size("a"));
31 | assertEquals(2, ResourceKeys.size("a.b"));
32 | assertEquals(3, ResourceKeys.size("a.b.c"));
33 | }
34 |
35 | @Test
36 | public void partsTest() {
37 | assertArrayEquals(new String[]{""}, ResourceKeys.parts(""));
38 | assertArrayEquals(new String[]{"a"}, ResourceKeys.parts("a"));
39 | assertArrayEquals(new String[]{"a","b"}, ResourceKeys.parts("a.b"));
40 | assertArrayEquals(new String[]{"a","b","c"}, ResourceKeys.parts("a.b.c"));
41 | }
42 |
43 | @Test
44 | public void subPartsTest() {
45 | assertArrayEquals(new String[]{""}, ResourceKeys.subParts("",0));
46 | assertArrayEquals(new String[]{}, ResourceKeys.subParts("",1));
47 | assertArrayEquals(new String[]{"a"}, ResourceKeys.subParts("a",0));
48 | assertArrayEquals(new String[]{}, ResourceKeys.subParts("a",1));
49 | assertArrayEquals(new String[]{"a","b"}, ResourceKeys.subParts("a.b",0));
50 | assertArrayEquals(new String[]{"b"}, ResourceKeys.subParts("a.b",1));
51 | assertArrayEquals(new String[]{}, ResourceKeys.subParts("a.b",2));
52 | assertArrayEquals(new String[]{"a","b","c"}, ResourceKeys.subParts("a.b.c",0));
53 | assertArrayEquals(new String[]{"b","c"}, ResourceKeys.subParts("a.b.c",1));
54 | assertArrayEquals(new String[]{"c"}, ResourceKeys.subParts("a.b.c",2));
55 | assertArrayEquals(new String[]{}, ResourceKeys.subParts("a.b.c",3));
56 | }
57 |
58 | @Test
59 | public void firstPartTest() {
60 | assertEquals("", ResourceKeys.firstPart(""));
61 | assertEquals("a", ResourceKeys.firstPart("a"));
62 | assertEquals("a", ResourceKeys.firstPart("a.b"));
63 | assertEquals("a", ResourceKeys.firstPart("a.b.c"));
64 | }
65 |
66 | @Test
67 | public void lastPartTest() {
68 | assertEquals("", ResourceKeys.lastPart(""));
69 | assertEquals("a", ResourceKeys.lastPart("a"));
70 | assertEquals("b", ResourceKeys.lastPart("a.b"));
71 | assertEquals("c", ResourceKeys.lastPart("a.b.c"));
72 | }
73 |
74 | @Test
75 | public void withoutFirstPartTest() {
76 | assertEquals("", ResourceKeys.withoutFirstPart(""));
77 | assertEquals("", ResourceKeys.withoutFirstPart("a"));
78 | assertEquals("b", ResourceKeys.withoutFirstPart("a.b"));
79 | assertEquals("b.c", ResourceKeys.withoutFirstPart("a.b.c"));
80 | }
81 |
82 | @Test
83 | public void withoutLastPartTest() {
84 | assertEquals("", ResourceKeys.withoutLastPart(""));
85 | assertEquals("", ResourceKeys.withoutLastPart("a"));
86 | assertEquals("a", ResourceKeys.withoutLastPart("a.b"));
87 | assertEquals("a.b", ResourceKeys.withoutLastPart("a.b.c"));
88 | }
89 |
90 | @Test
91 | public void isChildKeyOfTest() {
92 | assertFalse(ResourceKeys.isChildKeyOf("", ""));
93 | assertFalse(ResourceKeys.isChildKeyOf("a", "a"));
94 | assertTrue(ResourceKeys.isChildKeyOf("a.b.c", "a"));
95 | assertTrue(ResourceKeys.isChildKeyOf("a.b.c", "a.b"));
96 | }
97 |
98 | @Test
99 | public void childKeyTest() {
100 | assertEquals("", ResourceKeys.childKey("", ""));
101 | assertEquals("", ResourceKeys.childKey("a", "a"));
102 | assertEquals("", ResourceKeys.childKey("a", "a.b.c"));
103 | assertEquals("d", ResourceKeys.childKey("a.b.c.d", "a.b.c"));
104 | assertEquals("d.e.f", ResourceKeys.childKey("a.b.c.d.e.f", "a.b.c"));
105 | assertEquals("", ResourceKeys.childKey("b.c.d", "a.b.c"));
106 | }
107 |
108 | @Test
109 | public void uniqueRootKeysTest() {
110 | List keys = Lists.newArrayList("a.b.c", "a.b", "b.c.d", "b.c", "c.d.e", "c.d");
111 | List expected = Lists.newArrayList("a", "b", "c");
112 | assertEquals(expected, ResourceKeys.uniqueRootKeys(keys));
113 | }
114 |
115 | @Test
116 | public void extractChildKeysTest() {
117 | List keys = Lists.newArrayList("a.b.c", "b.c.d", "a.b.c.d.e", "b.c", "b.c.d.e");
118 | List expected = Lists.newArrayList("d", "d.e");
119 | assertEquals(expected, ResourceKeys.extractChildKeys(keys, "b.c"));
120 | }
121 |
122 | @Test
123 | public void isValidTest() {
124 | assertTrue(ResourceKeys.isValid("a"));
125 | assertTrue(ResourceKeys.isValid("a.b"));
126 | assertTrue(ResourceKeys.isValid("a.b.c"));
127 | assertFalse(ResourceKeys.isValid("."));
128 | assertFalse(ResourceKeys.isValid(".a"));
129 | assertFalse(ResourceKeys.isValid(".a.b"));
130 | assertFalse(ResourceKeys.isValid("a."));
131 | assertFalse(ResourceKeys.isValid("a.b."));
132 | assertFalse(ResourceKeys.isValid(" "));
133 | assertFalse(ResourceKeys.isValid("a.\tb"));
134 | assertFalse(ResourceKeys.isValid("a.\nb"));
135 | assertFalse(ResourceKeys.isValid("a .b"));
136 | assertFalse(ResourceKeys.isValid("a. b"));
137 | }
138 | }
139 |
--------------------------------------------------------------------------------
/src/main/resources/bundles/messages.properties:
--------------------------------------------------------------------------------
1 | core.intro.text=Drop an existing project here or go to File\uFE65New Project to start
2 | dialogs.about.title=About {0}
3 | dialogs.error.title=Error
4 | dialogs.locale.add.error.create=An error occurred while creating the new locale.
5 | dialogs.locale.add.error.invalid=The locale you entered is invalid or does already exist.
6 | dialogs.locale.add.text=Enter locale (i.e. en_US)\:
7 | dialogs.locale.add.title=Add Locale
8 | dialogs.preferences.editor.title=Preferences
9 | dialogs.preferences.project.title=Project Preferences
10 | dialogs.project.import.title=Import Project
11 | dialogs.project.new.conflict.text=There are existing translations found at this location, do you want to import them instead?
12 | dialogs.project.new.conflict.title=Conflict
13 | dialogs.project.new.title=New Project
14 | dialogs.save.checksum.text=The following translation file has been modified by another program\: "{0}"
Do you want to overwrite the file?
15 | dialogs.save.checksum.title=Save Translation
16 | dialogs.save.text=You have unsaved changes, do you want to save them?
17 | dialogs.save.title=Save Translations
18 | dialogs.translation.add.help=HINT\: use dots (.) to create a nested key (i.e. a.b)
19 | dialogs.translation.add.text=Enter translation key\:
20 | dialogs.translation.add.title=Add Translation
21 | dialogs.translation.conflict.text.merge=There already exists a translation with this key, do you want to merge it?
22 | dialogs.translation.conflict.text.replace=There already exists a translation with this key, do you want to replace it?
23 | dialogs.translation.conflict.title=Conflict
24 | dialogs.translation.duplicate.text=Enter new translation key\:
25 | dialogs.translation.duplicate.title=Duplicate Translation
26 | dialogs.translation.find.error=No translation found.
27 | dialogs.translation.find.text=Enter translation key\:
28 | dialogs.translation.find.title=Find Translation
29 | dialogs.translation.key.error=The translation key you entered is invalid.
30 | dialogs.translation.overwrite.text=Performing this action will erase the value for translation\: "{0}"
Do you want to continue?
31 | dialogs.translation.overwrite.title=Confirm
32 | dialogs.translation.rename.text=Enter new translation key\:
33 | dialogs.translation.rename.title=Rename Translation
34 | dialogs.version.link=Click here to download
35 | dialogs.version.new=A new version is available\:
36 | dialogs.version.title=Available Updates
37 | dialogs.version.uptodate=You are using the latest version.
38 | menu.edit.add.locale.title=Add Locale...
39 | menu.edit.add.translation.title=Add Translation...
40 | menu.edit.copy.key.title=Copy Translation Key
41 | menu.edit.delete.title=Delete Translation
42 | menu.edit.duplicate.title=Duplicate Translation...
43 | menu.edit.find.translation.title=Find Translation...
44 | menu.edit.rename.title=Rename Translation...
45 | menu.edit.title=Edit
46 | menu.edit.vk=E
47 | menu.file.exit.title=Exit
48 | menu.file.exit.vk=E
49 | menu.file.folder.title=Open Containing Folder
50 | menu.file.project.import.title=Import Project...
51 | menu.file.project.import.vk=I
52 | menu.file.project.new.es6.title=ES6 Format...
53 | menu.file.project.new.json.title=JSON Format...
54 | menu.file.project.new.properties.title=Properties Format...
55 | menu.file.project.new.title=New Project
56 | menu.file.project.new.vk=N
57 | menu.file.recent.clear.title=Clear List
58 | menu.file.recent.title=Open Recent
59 | menu.file.recent.vk=C
60 | menu.file.reload.title=Reload from Disk
61 | menu.file.reload.vk=R
62 | menu.file.save.title=Save
63 | menu.file.save.vk=S
64 | menu.file.title=File
65 | menu.file.vk=F
66 | menu.help.about.title=About {0}
67 | menu.help.home.title={0} Website
68 | menu.help.title=Help
69 | menu.help.version.title=Check for Updates...
70 | menu.help.vk=H
71 | menu.settings.preferences.editor.title=Preferences...
72 | menu.settings.preferences.project.title=Project Preferences...
73 | menu.settings.title=Settings
74 | menu.settings.vk=S
75 | menu.view.collapse.title=Collapse All Translation Keys
76 | menu.view.expand.title=Expand All Translation Keys
77 | menu.view.title=View
78 | menu.view.vk=V
79 | resources.create.error=An error occurred while creating translation files.
80 | resources.import.empty=No translation files found in\: "{0}"
81 | resources.import.error.multiple=An error occurred while opening translation files.
82 | resources.import.error.single=An error occurred while opening the translation file\: "{0}"
83 | resources.locale.default=Default
84 | resources.write.error.single=An error occurred while writing the translation file\: "{0}"
85 | settings.checkversion.title=Check for new version on startup
86 | settings.fieldset.editing=Editing
87 | settings.fieldset.general=General
88 | settings.fieldset.newprojects=New Projects
89 | settings.filestructure.flat=Flat structure
90 | settings.filestructure.nested=Folder structure
91 | settings.filestructure.title=Project layout
92 | settings.flattenjson.title=Flatten translation keys on save
93 | settings.inputheight.title=Default height of input fields
94 | settings.keyfield.title=Show translation key field
95 | settings.language.title=Interface language (restart required)
96 | settings.minify.title=Minify JSON/ES6 translation files on save
97 | settings.resource.jsones6=(JSON/ES6)
98 | settings.resourcedef.help=You can use '{' '}' tags to specify the locale part within the filename. The text LOCALE within this tags will be replaced by the actual locale. Example\: translations'{'_LOCALE'}' will become translations_en_US.
99 | settings.resourcedef.title=Translations filename
100 | settings.treetogglemode.title=Expand and collapse translation keys using double click
101 | swing.action.copy=Copy
102 | swing.action.cut=Cut
103 | swing.action.delete=Delete
104 | swing.action.paste=Paste
105 | swing.action.selectall=Select All
106 | swing.action.undo=Undo
107 | tree.root.name=Translations
108 |
--------------------------------------------------------------------------------
/src/main/resources/bundles/messages_pt_BR.properties:
--------------------------------------------------------------------------------
1 | core.intro.text=Solte um projeto existente aqui ou v\u00E1 para Arquivo\uFE65Novo Projeto para iniciar
2 | dialogs.about.title=Sobre {0}
3 | dialogs.error.title=Erro
4 | dialogs.locale.add.error.create=Ocorreu um erro ao criar o nova localidade.
5 | dialogs.locale.add.error.invalid=A localidade digitada \u00E9 inv\u00E1lida ou n\u00E3o existe.
6 | dialogs.locale.add.text=Informe uma localidade (Ex. pt_BR)\:
7 | dialogs.locale.add.title=Incluir localidade
8 | dialogs.preferences.editor.title=Prefer\u00EAncias
9 | dialogs.preferences.project.title=Prefer\u00EAncias do Projeto
10 | dialogs.project.import.title=Projeto de Importa\u00E7\u00E3o
11 | dialogs.project.new.conflict.text=Existem tradu\u00E7\u00F5es existentes neste local, voc\u00EA deseja import\u00E1-las?
12 | dialogs.project.new.conflict.title=Conflito
13 | dialogs.project.new.title=Novo Projeto
14 | dialogs.save.text=Voc\u00EA tem modifica\u00E7\u00F5es n\u00E3o salvas, deseja salv\u00E1-las?
15 | dialogs.save.title=Salvar Tradu\u00E7\u00F5es
16 | dialogs.translation.add.help=HINT\: use dots (.) to create a nested key (i.e. a.b)
17 | dialogs.translation.add.text=Informe a chave da tradu\u00E7\u00E3o\:
18 | dialogs.translation.add.title=Incluir Tradu\u00E7\u00E3o
19 | dialogs.translation.conflict.text.merge=J\u00E1 existe uma tradu\u00E7\u00E3o com esta chave, deseja mescl\u00E1-la?
20 | dialogs.translation.conflict.text.replace=J\u00E1 existe uma tradu\u00E7\u00E3o com esta chave, deseja substitu\u00ED-la?
21 | dialogs.translation.conflict.title=Conflito na Tradu\u00E7\u00E3o
22 | dialogs.translation.duplicate.text=Informe uma nova chave\:
23 | dialogs.translation.duplicate.title=Duplicar Tradu\u00E7\u00E3o
24 | dialogs.translation.find.error=Nenhumma tradu\u00E7\u00E3o encontrada.
25 | dialogs.translation.find.text=Informe uma chave de tradu\u00E7\u00E3o\:
26 | dialogs.translation.find.title=Localizar Tradu\u00E7\u00E3o
27 | dialogs.translation.key.error=A chave de tradu\u00E7\u00E3o inofrmada \u00E9 inv\u00E1lida.
28 | dialogs.translation.overwrite.text=Performing this action will erase the value for translation\: "{0}"
Do you want to continue?
29 | dialogs.translation.overwrite.title=Confirm
30 | dialogs.translation.rename.text=Informe a nova chave\:
31 | dialogs.translation.rename.title=Renomear Tradu\u00E7\u00E3o
32 | dialogs.version.link=Clique para baixar
33 | dialogs.version.new=Uma nova vers\u00E3o est\u00E1 dispon\u00EDvel\:
34 | dialogs.version.title=Atualiza\u00E7\u00F5es Dispon\u00EDveis
35 | dialogs.version.uptodate=Voc\u00EA est\u00E1 atualizado com a vers\u00E3o mais recente.
36 | menu.edit.add.locale.title=Incluir Localiza\u00E7\u00E3o...
37 | menu.edit.add.translation.title=Incluir Tradu\u00E7\u00E3o...
38 | menu.edit.copy.key.title=Copy Translation Key
39 | menu.edit.delete.title=Excluir Tradu\u00E7\u00E3o
40 | menu.edit.duplicate.title=Duplicar Tradu\u00E7\u00E3o...
41 | menu.edit.find.translation.title=Localizar Tradu\u00E7\u00E3o...
42 | menu.edit.rename.title=Renomear Tradu\u00E7\u00E3o...
43 | menu.edit.title=Editar
44 | menu.edit.vk=E
45 | menu.file.exit.title=Sair
46 | menu.file.exit.vk=S
47 | menu.file.folder.title=Abrir Pasta de Conte\u00FAdo
48 | menu.file.project.import.title=Projeto de Importa\u00E7\u00E3o...
49 | menu.file.project.import.vk=I
50 | menu.file.project.new.es6.title=Formato ES6...
51 | menu.file.project.new.json.title=Formato JSON...
52 | menu.file.project.new.properties.title=Formato Properties...
53 | menu.file.project.new.title=Novo Projeto
54 | menu.file.project.new.vk=N
55 | menu.file.recent.clear.title=Limpar Lista
56 | menu.file.recent.title=Abrir Recente
57 | menu.file.recent.vk=E
58 | menu.file.reload.title=Recarregar do Disco
59 | menu.file.reload.vk=R
60 | menu.file.save.title=Salvar
61 | menu.file.save.vk=L
62 | menu.file.title=Arquivo
63 | menu.file.vk=A
64 | menu.help.about.title=Sobre {0}
65 | menu.help.title=Ajuda
66 | menu.help.version.title=Verificar a nova vers\u00E3o...
67 | menu.help.vk=J
68 | menu.settings.preferences.editor.title=Prefer\u00EAncias...
69 | menu.settings.preferences.project.title=Prefer\u00EAncias do Projeto...
70 | menu.settings.title=Configura\u00E7\u00F5es
71 | menu.settings.vk=C
72 | menu.view.collapse.title=Recolher todas as tradu\u00E7\u00F5es
73 | menu.view.expand.title=Expandir todas as tradu\u00E7\u00F5es
74 | menu.view.title=Ver
75 | menu.view.vk=V
76 | resources.create.error=Um erro ocorreu ao criar arquivos de tradu\u00E7\u00F5es.
77 | resources.import.empty=Nenhum arquivo de tradu\u00E7\u00E3o encontrado em\: "{0}"
78 | resources.import.error.multiple=Um erro ocorreu enquanto os arquivos de tradu\u00E7\u00F5es eram carregados.
79 | resources.import.error.single=Um erro ocorreu enquanto o arquivo de tradu\u00E7\u00E3o era carregado\: "{0}"
80 | resources.locale.default=padr\u00E3o
81 | resources.write.error.single=Um erro ocorreu enquanto o arquivo de tradu\u00E7\u00E3o era salvo\: "{0}"
82 | settings.checkversion.title=Verificar a nova vers\u00E3o na inicializa\u00E7\u00E3o
83 | settings.fieldset.editing=Edi\u00E7\u00E3o
84 | settings.fieldset.general=Geral
85 | settings.fieldset.newprojects=Novos Projetos
86 | settings.filestructure.flat=Estrutura plana
87 | settings.filestructure.nested=Estrutura de pastas
88 | settings.filestructure.title=Layout do projeto
89 | settings.flattenjson.title=Achatar as chaves de tradu\u00E7\u00E3o ao salvar
90 | settings.inputheight.title=Altura padr\u00E3o dos campos de entrada
91 | settings.keyfield.title=Mostrar campo da chave de tradu\u00E7\u00E3o
92 | settings.language.title=Interface de linguagem (\u00E9 necess\u00E1rio reiniciar)
93 | settings.minify.title=Minificar tradu\u00E7\u00F5es ao salvar
94 | settings.resource.jsones6=(JSON/ES6)
95 | settings.resourcedef.title=Nome do arquivo de tradu\u00E7\u00E3es
96 | settings.treetogglemode.title=Expandir e contrair tradu\u00E7\u00F5es usando duplo clique
97 | swing.action.copy=Copiar
98 | swing.action.cut=Cortar
99 | swing.action.delete=Apagar
100 | swing.action.paste=Colar
101 | swing.action.selectall=Selecionar Tudo
102 | swing.action.undo=Desfazer
103 | tree.root.name=Tradu\u00E7\u00F5es
104 |
--------------------------------------------------------------------------------
/src/main/resources/bundles/messages_nl.properties:
--------------------------------------------------------------------------------
1 | core.intro.text=Sleep een bestaand project hierheen of ga naar Bestand\uFE65Nieuw Project om te starten
2 | dialogs.about.title=Over {0}
3 | dialogs.error.title=Fout
4 | dialogs.locale.add.error.create=Er is iets fout gegaan bij het toevoegen van de nieuwe taal.
5 | dialogs.locale.add.error.invalid=De opgegeven taal is niet geldig of bestaat al.
6 | dialogs.locale.add.text=Taal (bijv. nl_NL)\:
7 | dialogs.locale.add.title=Taal Toevoegen
8 | dialogs.preferences.editor.title=Voorkeuren
9 | dialogs.preferences.project.title=Projectvoorkeuren
10 | dialogs.project.import.title=Importeer Project
11 | dialogs.project.new.conflict.text=Er zijn bestaande vertaalbestanden gevonden op deze locatie, wilt u deze importeren?
12 | dialogs.project.new.conflict.title=Conflict
13 | dialogs.project.new.title=Nieuw Project
14 | dialogs.save.checksum.text=Het volgende vertaalbestand is gewijzigd door een ander programma\: "{0}"
Wilt u het bestand overschrijven?
15 | dialogs.save.checksum.title=Vertaling Opslaan
16 | dialogs.save.text=U heeft nog onopgeslagen wijzigingen, wilt u deze opslaan?
17 | dialogs.save.title=Vertalingen Opslaan
18 | dialogs.translation.add.help=TIP\: gebruik punten (.) om een geneste sleutel te cree\u00EBren (bijv. a.b)
19 | dialogs.translation.add.text=Vertaalsleutel\:
20 | dialogs.translation.add.title=Vertaling Toevoegen
21 | dialogs.translation.conflict.text.merge=Er bestaat al een vertaling met deze vertaalsleutel, wilt u deze samenvoegen?
22 | dialogs.translation.conflict.text.replace=Er bestaat al een vertaling met deze vertaalsleutel, wilt u deze vervangen?
23 | dialogs.translation.conflict.title=Conflict
24 | dialogs.translation.duplicate.text=Nieuwe vertaalsleutel\:
25 | dialogs.translation.duplicate.title=Vertaling Dupliceren
26 | dialogs.translation.find.error=Geen vertaling gevonden.
27 | dialogs.translation.find.text=Vertaalsleutel\:
28 | dialogs.translation.find.title=Vertaling Zoeken
29 | dialogs.translation.key.error=De opgegeven vertaalsleutel is niet geldig.
30 | dialogs.translation.overwrite.text=Deze actie zal de volgende vertaling verwijderen\: "{0}"
Wilt u doorgaan?
31 | dialogs.translation.overwrite.title=Bevestigen
32 | dialogs.translation.rename.text=Nieuwe vertaalsleutel\:
33 | dialogs.translation.rename.title=Vertaling Hernoemen
34 | dialogs.version.link=Klik hier om te downloaden
35 | dialogs.version.new=Een nieuwe versie is beschikbaar\:
36 | dialogs.version.title=Beschikbare Updates
37 | dialogs.version.uptodate=U gebruikt de nieuwste versie.
38 | menu.edit.add.locale.title=Taal Toevoegen...
39 | menu.edit.add.translation.title=Vertaling Toevoegen...
40 | menu.edit.copy.key.title=Vertaalsleutel Kopi\u00EBren
41 | menu.edit.delete.title=Vertaling Verwijderen
42 | menu.edit.duplicate.title=Vertaling Dupliceren...
43 | menu.edit.find.translation.title=Vertaling Zoeken...
44 | menu.edit.rename.title=Vertaling Hernoemen...
45 | menu.edit.title=Bewerken
46 | menu.edit.vk=W
47 | menu.file.exit.title=Sluiten
48 | menu.file.exit.vk=S
49 | menu.file.folder.title=In Map Weergeven
50 | menu.file.project.import.title=Importeer Project...
51 | menu.file.project.import.vk=I
52 | menu.file.project.new.es6.title=ES6 Formaat...
53 | menu.file.project.new.json.title=JSON Formaat...
54 | menu.file.project.new.properties.title=Properties Formaat...
55 | menu.file.project.new.title=Nieuw Project
56 | menu.file.project.new.vk=N
57 | menu.file.recent.clear.title=Lijst Wissen
58 | menu.file.recent.title=Recent Geopend
59 | menu.file.recent.vk=R
60 | menu.file.reload.title=Herladen vanaf Schijf
61 | menu.file.reload.vk=H
62 | menu.file.save.title=Opslaan
63 | menu.file.save.vk=P
64 | menu.file.title=Bestand
65 | menu.file.vk=B
66 | menu.help.about.title=Over {0}
67 | menu.help.home.title={0} Website
68 | menu.help.title=Help
69 | menu.help.version.title=Controleer nieuwe Versie...
70 | menu.help.vk=H
71 | menu.settings.preferences.editor.title=Voorkeuren...
72 | menu.settings.preferences.project.title=Projectvoorkeuren...
73 | menu.settings.title=Instellingen
74 | menu.settings.vk=I
75 | menu.view.collapse.title=Alle Vertaalsleutels Invouwen
76 | menu.view.expand.title=Alle Vertaalsleutels Uitvouwen
77 | menu.view.title=Beeld
78 | menu.view.vk=L
79 | resources.create.error=Er is iets fout gegaan bij het aanmaken van de vertaalbestanden.
80 | resources.import.empty=Geen vertaalbestanden gevonden in\: "{0}"
81 | resources.import.error.multiple=Er is iets fout gegaan bij het openen van de vertaalbetanden.
82 | resources.import.error.single=Er is iets fout gegaan bij het openen van het vertaalbestand\: "{0}"
83 | resources.locale.default=Standaard
84 | resources.write.error.single=Er is iets fout gegaan bij het opslaan van het vertaalbestand\: "{0}"
85 | settings.checkversion.title=Controleer op nieuwe versie bij opstarten
86 | settings.fieldset.editing=Weergave
87 | settings.fieldset.general=Algemeen
88 | settings.fieldset.newprojects=Nieuwe Projecten
89 | settings.filestructure.flat=Platte bestandsstructuur
90 | settings.filestructure.nested=Mappenstructuur
91 | settings.filestructure.title=Project lay-out
92 | settings.flattenjson.title=Sla vertaalsleutels in platte structuur op
93 | settings.inputheight.title=Standaardhoogte van invoervelden
94 | settings.keyfield.title=Toon veld met vertaalsleutel
95 | settings.language.title=Interfacetaal (herstart vereist)
96 | settings.minify.title=Sla vertaalbestanden gecomprimeerd op
97 | settings.resource.jsones6=(JSON/ES6)
98 | settings.resourcedef.help=U kunt met '{' '}' tags het gedeelte van de taal aangeven binnen de bestandsnaam. De tekst LOCALE binnen deze tags zal vervangen worden door de werkelijke taal. Voorbeeld\: translations'{'_LOCALE'}' wordt translations_nl_NL.
99 | settings.resourcedef.title=Bestandsnaam vertalingen
100 | settings.treetogglemode.title=Vertaalsleutels in- en uitvouwen met dubbelklik
101 | swing.action.copy=Kopi\u00EBren
102 | swing.action.cut=Knippen
103 | swing.action.delete=Verwijderen
104 | swing.action.paste=Plakken
105 | swing.action.selectall=Alles Selecteren
106 | swing.action.undo=Ongedaan Maken
107 | tree.root.name=Vertalingen
108 |
--------------------------------------------------------------------------------
/src/main/resources/bundles/messages_es_ES.properties:
--------------------------------------------------------------------------------
1 | core.intro.text=Arrastre aqu\u00ED un proyecto existente o vaya a Archivo->Nuevo proyecto para empezar
2 | dialogs.about.title=Sobre {0}
3 | dialogs.error.title=Error
4 | dialogs.locale.add.error.create=Ocurri\u00F3 alg\u00FAn error al crear la nueva locale
5 | dialogs.locale.add.error.invalid=La locale que ha introducido es inv\u00E1lida o ya existe.
6 | dialogs.locale.add.text=Introduzca una locale (p. ej. es_ES)\:
7 | dialogs.locale.add.title=A\u00F1adir locale
8 | dialogs.preferences.editor.title=Preferencias
9 | dialogs.preferences.project.title=Preferencias del proyecto
10 | dialogs.project.import.title=Importar Proyecto
11 | dialogs.project.new.conflict.text=Se han encontrado traducciones en esta ubicaci\u00F3n, \u00BFdesea importarlas?
12 | dialogs.project.new.conflict.title=Conflicto
13 | dialogs.project.new.title=Nuevo Proyecto
14 | dialogs.save.checksum.text=Esta traducci\u00F3n ha sido modificada por otro programa\: "{0}"
\u00BFDesea sobreescribir el fichero?
15 | dialogs.save.checksum.title=Guardar traducci\u00F3n
16 | dialogs.save.text=Hay cambios sin guardar, \u00BFdesea guardar los cambios?
17 | dialogs.save.title=Guardar traducciones
18 | dialogs.translation.add.help=SUGERENCIA\: utilice puntos (.) para crear claves anidadas (i.e.a.b)
19 | dialogs.translation.add.text=Introducir clave de traducci\u00F3n\:
20 | dialogs.translation.add.title=A\u00F1adir Traducci\u00F3n
21 | dialogs.translation.conflict.text.merge=Ya existe una traducci\u00F3n con esta clave, \u00BFdesea mezclarla?
22 | dialogs.translation.conflict.text.replace=Ya existe una traducci\u00F3n con esta clave, \u00BFdesea reemplazarla?
23 | dialogs.translation.conflict.title=Conflicto
24 | dialogs.translation.duplicate.text=Introducir nueva clave de traducci\u00F3n\:
25 | dialogs.translation.duplicate.title=Duplicar Traducci\u00F3n
26 | dialogs.translation.find.error=No se ha encontrado traducci\u00F3n.
27 | dialogs.translation.find.text=Introducir clave de traducci\u00F3n\:
28 | dialogs.translation.find.title=Buscar Traducci\u00F3n
29 | dialogs.translation.key.error=La clave de traducci\u00F3n que ha introducido es incorrecta.
30 | dialogs.translation.overwrite.text=Esta acci\u00F3n borrar\u00E1 el valor de la traducci\u00F3n\: "{0}"
\u00BFDesea continuar?
31 | dialogs.translation.overwrite.title=Confirmar
32 | dialogs.translation.rename.text=Introducir nueva clave de traducci\u00F3n\:
33 | dialogs.translation.rename.title=Renombrar Traducci\u00F3n
34 | dialogs.version.link=Click para descargar
35 | dialogs.version.new=Hay una nueva versi\u00F3n disponible\:
36 | dialogs.version.title=Actualizaciones disponibles
37 | dialogs.version.uptodate=Est\u00E1 utilizando la \u00FAltima versi\u00F3n.
38 | menu.edit.add.locale.title=A\u00F1adir Locale...
39 | menu.edit.add.translation.title=A\u00F1adir Traducci\u00F3n...
40 | menu.edit.copy.key.title=Copiar clave de Traducci\u00F3n
41 | menu.edit.delete.title=Borrar Traducci\u00F3n
42 | menu.edit.duplicate.title=Duplicar Traducci\u00F3n...
43 | menu.edit.find.translation.title=Buscar Traducci\u00F3n...
44 | menu.edit.rename.title=Renombrar Traducci\u00F3n...
45 | menu.edit.title=Editar
46 | menu.edit.vk=E
47 | menu.file.exit.title=Salir
48 | menu.file.exit.vk=S
49 | menu.file.folder.title=Abrir desde directorio
50 | menu.file.project.import.title=Importar Proyecto
51 | menu.file.project.import.vk=I
52 | menu.file.project.new.es6.title=Formato ES6...
53 | menu.file.project.new.json.title=Formato JSON...
54 | menu.file.project.new.properties.title=Formato Properties...
55 | menu.file.project.new.title=Nuevo Proyecto
56 | menu.file.project.new.vk=N
57 | menu.file.recent.clear.title=Limpiar Lista
58 | menu.file.recent.title=Abrir Reciente
59 | menu.file.recent.vk=A
60 | menu.file.reload.title=Recargar desde Disco
61 | menu.file.reload.vk=R
62 | menu.file.save.title=Guardar
63 | menu.file.save.vk=G
64 | menu.file.title=Archivo
65 | menu.file.vk=A
66 | menu.help.about.title=Sobre {0}
67 | menu.help.home.title=Web {0}
68 | menu.help.title=Ayuda
69 | menu.help.version.title=Comprobar actualizaciones...
70 | menu.help.vk=y
71 | menu.settings.preferences.editor.title=Preferencias...
72 | menu.settings.preferences.project.title=Preferencias del proyecto...
73 | menu.settings.title=Configuraci\u00F3n
74 | menu.settings.vk=C
75 | menu.view.collapse.title=Agrupar todas las claves de traducci\u00F3n
76 | menu.view.expand.title=Expandir todas las claves de traducci\u00F3n
77 | menu.view.title=Ver
78 | menu.view.vk=V
79 | resources.create.error=Ocurri\u00F3 un error al crear los ficheros de traducci\u00F3n.
80 | resources.import.empty=No se han encontrado ficheros de traducci\u00F3n en\: "{0}"
81 | resources.import.error.multiple=Ocurri\u00F3 un error al abrir los ficheros de traducci\u00F3n.
82 | resources.import.error.single=Ocurri\u00F3 un error al abrir el fichero de traducci\u00F3n\: "{0}"
83 | resources.locale.default=Por defecto
84 | resources.write.error.single=Ocurri\u00F3 un error al escribir el fichero de traducci\u00F3n\: "{0}"
85 | settings.checkversion.title=Comprobar actualizaciones en el inicio
86 | settings.fieldset.editing=Edici\u00F3n
87 | settings.fieldset.general=General
88 | settings.fieldset.newprojects=Nuevos proyectos
89 | settings.filestructure.flat=Estructura plana
90 | settings.filestructure.nested=Estructura de arbol
91 | settings.filestructure.title=Estructura del proyecto
92 | settings.flattenjson.title=Aplanar las claves de traducci\u00F3n al guardar
93 | settings.inputheight.title=Altura por defecto de los campos de entrada
94 | settings.keyfield.title=Mostrar el campo clave de traducci\u00F3n
95 | settings.language.title=Idioma de la interfaz (requiere reiniciar)
96 | settings.minify.title=Minimizar los ficheros JSON/ES6 al guardar
97 | settings.resource.jsones6=(JSON/ES6)
98 | settings.resourcedef.help=Puede utilizar los tags '{' '}' para especificar la locale como parte del nombre del fichero. El texto LOCALE ser\u00E1 reemplazado por la locale en curso. Ejemplo\: translations'{'_LOCALE'}' se converir\u00EDa en translations_es_ES.
99 | settings.resourcedef.title=Nombre del fichero de traducciones
100 | settings.treetogglemode.title=Expandir y contraer las claves de traducci\u00F3n utilizando doble click
101 | swing.action.copy=Copiar
102 | swing.action.cut=Cortar
103 | swing.action.delete=Borrar
104 | swing.action.paste=Pegar
105 | swing.action.selectall=Seleccionar todo
106 | swing.action.undo=Deshacer
107 | tree.root.name=Traducciones
108 |
--------------------------------------------------------------------------------
/src/main/java/com/jvms/i18neditor/util/ResourceKeys.java:
--------------------------------------------------------------------------------
1 | package com.jvms.i18neditor.util;
2 |
3 | import java.util.Arrays;
4 | import java.util.List;
5 | import java.util.stream.Collectors;
6 |
7 | import com.google.common.collect.Lists;
8 | import com.jvms.i18neditor.Resource;
9 |
10 | /**
11 | * This class provides translation key utility functions for a {@link Resource}.
12 | *
13 | *
A translation key is a {@code String} consisting of one or more parts separated by a dot.
14 | * A key starting or ending with a dot or a key containing white spaces is considered to be invalid.
15 | *
16 | * @author Jacob van Mourik
17 | */
18 | public final class ResourceKeys {
19 |
20 | /**
21 | * See {@link #create(List)}.
22 | */
23 | public static String create(String... parts) {
24 | return create(Arrays.asList(parts));
25 | }
26 |
27 | /**
28 | * Creates a key by joining the given parts.
29 | *
30 | * @param parts the parts of the key.
31 | * @return the created key.
32 | */
33 | public static String create(List parts) {
34 | return parts.stream().filter(p -> p != null && !p.isEmpty()).collect(Collectors.joining("."));
35 | }
36 |
37 | /**
38 | * Checks whether the given key is a valid key.
39 | * A key starting or ending with a dot or a key containing white spaces is considered to be invalid.
40 | *
41 | * @param key the key to validate.
42 | * @return whether the key is valid or not.
43 | */
44 | public static boolean isValid(String key) {
45 | return !key.isEmpty() && !key.startsWith(".") && !key.endsWith(".") && key.matches("[^\\s]+");
46 | }
47 |
48 | /**
49 | * Returns the size of a key.
50 | * The size is the number of parts the key consists of.
51 | *
52 | * @param key the key.
53 | * @return the number of parts the key consists of.
54 | */
55 | public static int size(String key) {
56 | return parts(key).length;
57 | }
58 |
59 | /**
60 | * Returns the parts of a key.
61 | *
62 | * @param key the key.
63 | * @return the parts of the key.
64 | */
65 | public static String[] parts(String key) {
66 | return key.split("\\.");
67 | }
68 |
69 | /**
70 | * Returns the last parts of a key with a given offset.
71 | * The offset must lie between zero and the total number of parts, inclusive.
72 | *
73 | * @param key the key.
74 | * @param offset the number of parts to skip from the beginning.
75 | * @return the last sub parts.
76 | */
77 | public static String[] subParts(String key, int offset) {
78 | String[] parts = parts(key);
79 | return Arrays.copyOfRange(parts, offset, parts.length);
80 | }
81 |
82 | /**
83 | * Returns the first part of a key.
84 | *
85 | * @param key the key.
86 | * @return the first part.
87 | */
88 | public static String firstPart(String key) {
89 | return parts(key)[0];
90 | }
91 |
92 | /**
93 | * Returns the last part of a key.
94 | *
95 | * @param key the key.
96 | * @return the last part.
97 | */
98 | public static String lastPart(String key) {
99 | String[] parts = parts(key);
100 | return parts[parts.length-1];
101 | }
102 |
103 | /**
104 | * Creates a new key consisting of all but the first part.
105 | * If the key has only one part, an empty key will be returned.
106 | *
107 | * @param key the key.
108 | * @return the key without the first part.
109 | */
110 | public static String withoutFirstPart(String key) {
111 | if (size(key) == 1) return "";
112 | return key.replaceAll("^[^.]+\\.", "");
113 | }
114 |
115 | /**
116 | * Creates a new key consisting of all but the last part.
117 | * If the key has only one part, an empty key will be returned.
118 | *
119 | * @param key the key.
120 | * @return the key without the last part.
121 | */
122 | public static String withoutLastPart(String key) {
123 | if (size(key) == 1) return "";
124 | return key.replaceAll("\\.[^.]+$", "");
125 | }
126 |
127 | /**
128 | * Retrieve the part of the given key which is a child part of the given parent key.
129 | * A key is a child of another key if it has the same parts at the beginning as the other key.
130 | * This function will only return the child parts, so without the beginning parent parts.
131 | *
132 | *
If the resulting key is the same as the given key, the key is considered not to be a child
133 | * of the given parent key, so an empty key will be returned.
{@code type} the type of the resource, either {@code JSON} or {@code ES6}.
21 | *
{@code path} the path to the resource file on disk.
22 | *
{@code locale} the locale of the resource.
23 | *
{@code translations} a sorted map containing the translations of the resource by key value pair.
24 | *
25 | *
26 | *
Objects can listen to a resource by adding a {@link ResourceListener} which
27 | * will be called when any change is made to the {@code translations}.
28 | *
29 | * @author Jacob van Mourik
30 | */
31 | public class Resource {
32 | private final Path path;
33 | private final Locale locale;
34 | private final ResourceType type;
35 | private final List listeners = Lists.newLinkedList();
36 | private SortedMap translations = Maps.newTreeMap();
37 | private String checksum;
38 |
39 | /**
40 | * See {@link #Resource(ResourceType, Path, Locale)}.
41 | */
42 | public Resource(ResourceType type, Path path) {
43 | this(type, path, null);
44 | }
45 |
46 | /**
47 | * Creates a new instance of a resource.
48 | *
49 | * @param type the type of the resource.
50 | * @param path the path to the file on disk.
51 | * @param locale the locale of the translations.
52 | */
53 | public Resource(ResourceType type, Path path, Locale locale) {
54 | this.path = path;
55 | this.locale = locale;
56 | this.type = type;
57 | }
58 |
59 | /**
60 | * Gets the type of the resource.
61 | *
62 | * @return the type.
63 | */
64 | public ResourceType getType() {
65 | return type;
66 | }
67 |
68 | /**
69 | * Gets the path to the resource file on disk.
70 | *
71 | * @return the path.
72 | */
73 | public Path getPath() {
74 | return path;
75 | }
76 |
77 | /**
78 | * Gets the locale of the translations of the resource.
79 | *
80 | * @return the locale of the resource, may be {@code null}.
81 | */
82 | public Locale getLocale() {
83 | return locale;
84 | }
85 |
86 | /**
87 | * Gets a map of the translations of the resource.
88 | *
89 | *
The returned map is an immutable sorted map. Modifications to the translations should be done via
90 | * {@link #storeTranslation(String, String)}, {@link #removeTranslation(String)} or
91 | * {@link #renameTranslation(String, String)}.
92 | *
93 | * @return the translations of the resource.
94 | */
95 | public SortedMap getTranslations() {
96 | return ImmutableSortedMap.copyOf(translations);
97 | }
98 |
99 | /**
100 | * Sets the translations of the resource.
101 | *
102 | * @param translations the translations
103 | */
104 | public void setTranslations(SortedMap translations) {
105 | this.translations = translations;
106 | }
107 |
108 | /**
109 | * Checks whether the resource has a translation with the given key.
110 | *
111 | * @param key the key of the translation to look for.
112 | * @return whether a translation with the given key exists.
113 | */
114 | public boolean hasTranslation(String key) {
115 | return !Strings.isNullOrEmpty(translations.get(key));
116 | }
117 |
118 | /**
119 | * Gets a translation from the resource's translations.
120 | *
121 | * @param key the key of the translation to get.
122 | * @return value of the translation or {@code null} if there is no translation for the given key.
123 | */
124 | public String getTranslation(String key) {
125 | return translations.get(key);
126 | }
127 |
128 | /**
129 | * Stores a translation to the resource's translations.
130 | *
131 | *
132 | *
If the given key does not exists yet and is not empty, a new translation will be added to the map.
133 | *
If the given key already exists, the existing value will be overwritten with the given value.
134 | *
If the given key is a parent key of any existing keys, the existing child keys will be removed (when parent values are not supported).
135 | *
If the given key is a child key of any existing keys, the existing parent keys will be removed (when parent values are not supported).
136 | *
137 | *
138 | * @param key the key of the translation to add.
139 | * @param value the value of the translation to add corresponding the given key.
140 | */
141 | public void storeTranslation(String key, String value) {
142 | checkKey(key);
143 | String existing = translations.get(key);
144 | if (value == null || existing != null && existing.equals(value)) {
145 | return;
146 | }
147 | if (!supportsParentValues()) {
148 | removeParents(key);
149 | removeChildren(key);
150 | }
151 | translations.put(key, value);
152 | notifyListeners();
153 | }
154 |
155 | /**
156 | * Removes a translation from the resource's translations.
157 | * Any child keys of the given key will also be removed.
158 | *
159 | * @param key the key of the translation to remove.
160 | */
161 | public void removeTranslation(String key) {
162 | removeChildren(key);
163 | translations.remove(key);
164 | notifyListeners();
165 | }
166 |
167 | /**
168 | * Renames a translation key in the resource's translations.
169 | * Any existing child keys of the translation key will also be renamed.
170 | *
171 | * @param key the old key of the translation to rename.
172 | * @param newKey the new key.
173 | */
174 | public void renameTranslation(String key, String newKey) {
175 | checkKey(newKey);
176 | duplicateTranslation(key, newKey, false);
177 | notifyListeners();
178 | }
179 |
180 | /**
181 | * Duplicates a translation key, and any child keys, in the resource's translations.
182 | *
183 | * @param key the key of the translation to duplicate.
184 | * @param newKey the new key.
185 | */
186 | public void duplicateTranslation(String key, String newKey) {
187 | checkKey(newKey);
188 | duplicateTranslation(key, newKey, true);
189 | notifyListeners();
190 | }
191 |
192 | /**
193 | * Adds a listener to the resource. The listener will be called whenever there is made
194 | * a change to the translations of the resource.
195 | *
196 | * @param listener the listener to add.
197 | */
198 | public void addListener(ResourceListener listener) {
199 | listeners.add(listener);
200 | }
201 |
202 | /**
203 | * Removes a listener from the resource previously added by {@link #addListener(ResourceListener)}.
204 | *
205 | * @param listener the listener to remove.
206 | */
207 | public void removeListener(ResourceListener listener) {
208 | listeners.remove(listener);
209 | }
210 |
211 | /**
212 | * Gets the checksum of the resource's file.
213 | * This method only returns the checksum set via {@link #setChecksum(checksum)}.
214 | *
215 | * @return the checksum.
216 | */
217 | public String getChecksum() {
218 | return checksum;
219 | }
220 |
221 | /**
222 | * Returns whether the resource has support for parent values.
223 | *
224 | *
For example if we have a value set for the key a.b we
225 | * might also set a value for a.
This implementation adds the ability to load and store properties from a given file path
28 | * and adds support to retrieve and store {@code Integer} and {@code List} values.
29 | *
30 | *
This implementation also adds extended functionality like {@link #containsKeys(String...)}.
31 | *
32 | * @author Jacob van Mourik
33 | */
34 | public class ExtendedProperties extends Properties {
35 | private final static long serialVersionUID = 6042931434040718478L;
36 | private final static Logger log = LoggerFactory.getLogger(ExtendedProperties.class);
37 | private final String listSeparator;
38 |
39 | /**
40 | * Creates an empty property list with no default values and "," as list separator.
41 | */
42 | public ExtendedProperties() {
43 | this(null, ",");
44 | }
45 |
46 | /**
47 | * Creates an empty property list with no default values and the specified list separator.
48 | *
49 | * @param separator the separator used for storing list values.
50 | */
51 | public ExtendedProperties(String separator) {
52 | this(null, separator);
53 | }
54 |
55 | /**
56 | * Creates an empty property list with the specified defaults and "," as list separator.
57 | *
58 | * @param defaults the defaults.
59 | */
60 | public ExtendedProperties(Properties defaults) {
61 | this(defaults, ",");
62 | }
63 |
64 | /**
65 | * Creates an empty property list with the specified defaults and list separator.
66 | *
67 | * @param defaults the defaults.
68 | * @param separator the separator used for storing list values.
69 | */
70 | public ExtendedProperties(Properties defaults, String separator) {
71 | super(defaults);
72 | this.listSeparator = separator;
73 | }
74 |
75 | /**
76 | * Reads a property list from the given file path.
77 | *
78 | *
Any {@code IOException} will be ignored.
79 | *
80 | * @param path the path to the property file.
81 | */
82 | public void load(Path path) {
83 | try (InputStream in = Files.newInputStream(path)) {
84 | load(in);
85 | } catch (IOException e) {
86 | log.error("Unable to load properties from " + path, e);
87 | }
88 | }
89 |
90 | /**
91 | * Writes the property list to the given file path.
92 | *
93 | *
Any {@code IOException} will be ignored.
94 | *
95 | * @param path the path to the property file.
96 | */
97 | public void store(Path path) {
98 | try (OutputStream out = new OutputStreamWrapper(Files.newOutputStream(path))) {
99 | store(out, null);
100 | } catch (IOException e) {
101 | log.error("Unable to store properties to " + path, e);
102 | }
103 | }
104 |
105 | /**
106 | * Sets a value in the property list. The list of values will be converted
107 | * to a single string separated by {@value #listSeparator}.
108 | *
109 | * @param key the key to be placed in this property list.
110 | * @param values the value corresponding to {@code key}.
111 | */
112 | public void setProperty(String key, List values) {
113 | setProperty(key, values.stream().collect(Collectors.joining(listSeparator)));
114 | }
115 |
116 | /**
117 | * Sets a value in the property list. The {@code Integer} value will be
118 | * stored as a {@code String} value.
119 | *
120 | * @param key the key to be placed in this property list.
121 | * @param value the value corresponding to {@code key}.
122 | */
123 | public void setProperty(String key, Integer value) {
124 | setProperty(key, value == null ? null : value.toString());
125 | }
126 |
127 | /**
128 | * Sets a value in the property list. The {@code boolean} value will be
129 | * stored as a {@code String} value; "1" for {@code true}, "0" for {@code false}.
130 | *
131 | * @param key the key to be placed in this property list.
132 | * @param value the value corresponding to {@code key}.
133 | */
134 | public void setProperty(String key, Boolean value) {
135 | setProperty(key, value == null ? null : (value ? 1 : 0));
136 | }
137 |
138 | /**
139 | * Sets a value in the property list. The {@code Enum} value will be
140 | * stored as a {@code String} value.
141 | *
142 | * @param key the key to be placed in this property list.
143 | * @param value the value corresponding to {@code key}.
144 | */
145 | public void setProperty(String key, Enum> value) {
146 | setProperty(key, value.toString());
147 | }
148 |
149 | /**
150 | * Sets a value in the property list. The {@code Locale} value will be
151 | * stored as a {@code String} value.
152 | *
153 | * @param key the key to be placed in this property list.
154 | * @param value the value corresponding to {@code key}.
155 | */
156 | public void setProperty(String key, Locale locale) {
157 | setProperty(key, locale.toString());
158 | }
159 |
160 | /**
161 | * Gets a value from the property list as a {@code List}. This method should be used
162 | * to retrieve a value previously stored by {@link #setProperty(String, List)}.
163 | *
164 | * @param key the property key.
165 | * @return the value in this property list with the specified key value or an empty list
166 | * if no such key exists.
167 | */
168 | public List getListProperty(String key) {
169 | String value = getProperty(key);
170 | return value == null ? Lists.newLinkedList() : Lists.newLinkedList(Arrays.asList(value.split(listSeparator)));
171 | }
172 |
173 | /**
174 | * Gets a value from the property list as an {@code Integer}. This method should be used
175 | * to retrieve a value previously stored by {@link #setProperty(String, Integer)}.
176 | *
177 | * @param key the property key.
178 | * @return the value in this property list with the specified key value or {@code null}
179 | * if no such key exists or the value is not a valid {@code Integer}.
180 | */
181 | public Integer getIntegerProperty(String key) {
182 | String value = getProperty(key);
183 | if (!Strings.isNullOrEmpty(value)) {
184 | try {
185 | return Integer.parseInt(value);
186 | } catch (Exception e) {
187 | log.warn("Unable to parse integer property value " + value);
188 | }
189 | }
190 | return null;
191 | }
192 |
193 | /**
194 | * See {@link #getIntegerProperty(String)}. This method returns {@code defaultValue} when
195 | * there is no value in the property list with the specified {@code key} or when
196 | * the value is not a valid {@code Integer}.
197 | *
198 | * @param key the property key.
199 | * @param defaultValue the default value to return when there is no value for the specified key
200 | * @return the value in this property list with the specified key value or {@code defaultValue}
201 | * if no such key exists or the value is not a valid {@code Integer}.
202 | */
203 | public Integer getIntegerProperty(String key, Integer defaultValue) {
204 | Integer value = getIntegerProperty(key);
205 | return value != null ? value : defaultValue;
206 | }
207 |
208 | /**
209 | * Gets a value from the property list as an {@code Boolean}. This method should be used
210 | * to retrieve a value previously stored by {@link #setProperty(String, Boolean)}.
211 | *
212 | * @param key the property key.
213 | * @return the value in this property list with the specified key value or {@code null}
214 | * if no such key exists.
215 | */
216 | public Boolean getBooleanProperty(String key) {
217 | Integer value = getIntegerProperty(key);
218 | return value != null ? (value != 0) : null;
219 | }
220 |
221 | /**
222 | * See {@link #getBooleanProperty(String)}. This method returns {@code defaultValue} when
223 | * there is no value in the property list with the specified {@code key}.
224 | *
225 | * @param key the property key.
226 | * @param defaultValue the default value to return when there is no value for the specified key
227 | * @return the value in this property list with the specified key value or {@code defaultValue}
228 | * if no such key exists.
229 | */
230 | public Boolean getBooleanProperty(String key, boolean defaultValue) {
231 | Boolean value = getBooleanProperty(key);
232 | return value != null ? value : defaultValue;
233 | }
234 |
235 | /**
236 | * Gets a value from the property list as an {@code Enum}. This method should be used
237 | * to retrieve a value previously stored by {@link #setProperty(String, Enum)}.
238 | *
239 | * @param key the property key.
240 | * @param defaultValue the default value to return when there is no value for the specified key
241 | * @return the value in this property list with the specified key value or {@code null}
242 | * if no such key exists or the value is not a valid enum value.
243 | */
244 | public > T getEnumProperty(String key, Class enumType) {
245 | String value = getProperty(key);
246 | if (!Strings.isNullOrEmpty(value)) {
247 | try {
248 | return T.valueOf(enumType, value);
249 | } catch (Exception e) {
250 | log.warn("Unable to parse enum property value " + value);
251 | }
252 | }
253 | return null;
254 | }
255 |
256 | /**
257 | * See {@link #getEnumProperty(String, Class)}. This method returns {@code defaultValue} when
258 | * there is no value in the property list with the specified {@code key} or when
259 | * the value is not a valid enum value.
260 | *
261 | * @param key the property key.
262 | * @param defaultValue the default value to return when there is no value for the specified key
263 | * @return the value in this property list with the specified key value or {@code defaultValue}
264 | * if no such key exists or the value is not a valid enum value.
265 | */
266 | public > T getEnumProperty(String key, Class enumType, T defaultValue) {
267 | T value = getEnumProperty(key, enumType);
268 | return value != null ? value : defaultValue;
269 | }
270 |
271 | /**
272 | * Gets a value from the property list as an {@code Locale}. This method should be used
273 | * to retrieve a value previously stored by {@link #setProperty(String, Locale)}.
274 | *
275 | * @param key the property key.
276 | * @return the value in this property list with the specified key value or {@code null}
277 | * if no such key exists.
278 | */
279 | public Locale getLocaleProperty(String key) {
280 | String value = getProperty(key);
281 | return Locales.parseLocale(value);
282 | }
283 |
284 | /**
285 | * See {@link #getLocaleProperty(Locale)}. This method returns {@code defaultValue} when
286 | * there is no value in the property list with the specified {@code key}.
287 | *
288 | * @param key the property key.
289 | * @param defaultValue the default value to return when there is no value for the specified key
290 | * @return the value in this property list with the specified key value or {@code defaultValue}
291 | * if no such key exists.
292 | */
293 | public Locale getLocaleProperty(String key, Locale defaultValue) {
294 | Locale value = getLocaleProperty(key);
295 | return value != null ? value : defaultValue;
296 | }
297 |
298 | /**
299 | * This function does the same as {@link #containsKey(Object)}, only for multiple keys.
300 | *
301 | * @param keys possible keys.
302 | * @return {@code true} if and only if the specified objects are keys in this hashtable,
303 | * as determined by the equals method; false otherwise.
304 | */
305 | public boolean containsKeys(String... keys) {
306 | return Arrays.asList(keys).stream().allMatch(k -> containsKey(k));
307 | }
308 |
309 | @Override
310 | public synchronized Enumeration