diff --git a/diylc/diylc-core/src/org/diylc/common/IPlugInPort.java b/diylc/diylc-core/src/org/diylc/common/IPlugInPort.java
index 403fc525..284a2ab9 100644
--- a/diylc/diylc-core/src/org/diylc/common/IPlugInPort.java
+++ b/diylc/diylc-core/src/org/diylc/common/IPlugInPort.java
@@ -90,7 +90,10 @@ public interface IPlugInPort extends ISelectionProcessor, IMouseProcessor, IKeyP
public static final String SNAP_TO_NONE = "None";
public static final String SNAP_TO_GRID = "Grid";
public static final String SNAP_TO_COMPONENTS = "Components";
- public static final String SNAP_TO_DEFAULT = SNAP_TO_GRID;
+ public static final String SNAP_TO_DEFAULT = SNAP_TO_GRID;
+
+ public static final String LANGUAGE = "language";
+ public static final String LANGUAGE_DEFAULT = "English";
public static final int DND_TOGGLE_STICKY = 0x1;
public static final int DND_TOGGLE_SNAP = 0x40000000;
@@ -101,7 +104,7 @@ public interface IPlugInPort extends ISelectionProcessor, IMouseProcessor, IKeyP
public static final int NOBUTTON = 0;
public static final int BUTTON1 = 1;
public static final int BUTTON2 = 2;
- public static final int BUTTON3 = 3;
+ public static final int BUTTON3 = 3;
/**
* Returns size of the canvas that takes project dimensions into account as well as zoom level.
diff --git a/diylc/diylc-core/src/org/diylc/lang/LangUtil.java b/diylc/diylc-core/src/org/diylc/lang/LangUtil.java
new file mode 100644
index 00000000..114cf936
--- /dev/null
+++ b/diylc/diylc-core/src/org/diylc/lang/LangUtil.java
@@ -0,0 +1,156 @@
+/*
+ *
+ * DIY Layout Creator (DIYLC). Copyright (c) 2009-2018 held jointly by the individual authors.
+ *
+ * This file is part of DIYLC.
+ *
+ * DIYLC is free software: you can redistribute it and/or modify it under the terms of the GNU
+ * General Public License as published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * DIYLC is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even
+ * the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
+ * Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along with DIYLC. If not, see
+ * .
+ */
+package org.diylc.lang;
+
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.io.PrintWriter;
+import java.nio.charset.StandardCharsets;
+import java.nio.file.Files;
+import java.nio.file.Paths;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.stream.Stream;
+
+import org.apache.log4j.Logger;
+import org.diylc.appframework.miscutils.ConfigurationManager;
+import org.diylc.common.IPlugInPort;
+import org.diylc.utils.ResourceLoader;
+
+public class LangUtil {
+
+ private static final String LANG_DIR = "lang";
+
+ private static final Logger LOG = Logger.getLogger(LangUtil.class);
+
+ private static Map dict = null;
+ private static List missing;
+
+ public static String translate(String text) {
+ if (text == null || text.length() <= 1)
+ return text;
+
+ try {
+ // don't translate numbers
+ Double.parseDouble(text);
+ return text;
+ } catch (Exception e) {
+ }
+
+ if (dict != null) {
+ if (dict.containsKey(text.toLowerCase())) {
+ return dict.get(text.toLowerCase());
+ } else {
+ if ("true".equalsIgnoreCase(System.getProperty("org.diylc.storeDict"))) {
+ List missing = getMissing();
+ if (!missing.contains(text)) {
+ missing.add(text);
+ Collections.sort(missing);
+ try (PrintWriter out = new PrintWriter("dict.txt")) {
+ for (String m : missing) {
+ out.println(m);
+ }
+ } catch (FileNotFoundException e) {
+ e.printStackTrace();
+ }
+ }
+ }
+ }
+ }
+ return text;
+ }
+
+ public static void configure() {
+ String language = ConfigurationManager.getInstance().readString(IPlugInPort.LANGUAGE, IPlugInPort.LANGUAGE_DEFAULT);
+ if (language != null)
+ load(language);
+ }
+
+ private static void load(String language) {
+ LOG.info("Loading translation for " + language);
+ dict = new HashMap();
+ try {
+ try (Stream stream = Files.lines(
+ Paths.get(LANG_DIR + File.separator + language + ".txt"), StandardCharsets.UTF_8)) {
+ stream.forEach(s -> {
+ if (s.contains("|")) {
+ String[] parts = s.split("\\|");
+ if (parts.length == 2)
+ dict.put(parts[0].toLowerCase().trim(), parts[1].trim());
+ } else {
+ dict.put(s.toLowerCase(), s);
+ }
+ });
+ } catch (IOException e) {
+ LOG.error("Error loading language file", e);
+ }
+ } catch (Exception e) {
+ LOG.error("Error loading language file", e);
+ }
+ }
+
+ private static List getMissing() {
+ if (missing == null) {
+ missing = new ArrayList();
+ try {
+ try (Stream stream = Files.lines(Paths.get("dict.txt"), StandardCharsets.UTF_8)) {
+ stream.forEach(s -> missing.add(s));
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+ } catch (Exception e) {
+
+ }
+ }
+ return missing;
+ }
+
+ public static Map> getAvailableLanguages() {
+ Map> res = new HashMap>();
+ File[] files = ResourceLoader.getFiles(LANG_DIR);
+ if (files == null)
+ return res;
+ try {
+ for (File file : files) {
+ String key = file.getName().replaceFirst("[.][^.]+$", "");
+ Map details = new HashMap();
+ try (Stream stream = Files.lines(
+ Paths.get(file.getAbsolutePath()), StandardCharsets.UTF_8)) {
+ stream.forEach(s -> {
+ if (s.contains("=")) {
+ String[] parts = s.split("=");
+ if (parts.length == 2)
+ details.put(parts[0].toLowerCase().trim(), parts[1].trim());
+ }
+ });
+ } catch (IOException e) {
+ LOG.error("Error loading language file", e);
+ }
+ res.put(key, details);
+ }
+ return res;
+ } catch (Exception e) {
+ LOG.error("Error fetching available languages");
+ return null;
+ }
+ }
+}
diff --git a/diylc/diylc-core/src/org/diylc/presenter/ComponentProcessor.java b/diylc/diylc-core/src/org/diylc/presenter/ComponentProcessor.java
index 4a269309..a13ea7ed 100644
--- a/diylc/diylc-core/src/org/diylc/presenter/ComponentProcessor.java
+++ b/diylc/diylc-core/src/org/diylc/presenter/ComponentProcessor.java
@@ -46,7 +46,8 @@ import org.diylc.core.IPropertyValidator;
import org.diylc.core.annotations.BomPolicy;
import org.diylc.core.annotations.ComponentDescriptor;
import org.diylc.core.annotations.EditableProperty;
-import org.diylc.core.annotations.KeywordPolicy;
+import org.diylc.core.annotations.KeywordPolicy;
+import org.diylc.lang.LangUtil;
/**
* Utility class with component processing methods.
@@ -172,8 +173,16 @@ public class ComponentProcessor {
Method setter = clazz.getMethod("set" + getter.getName().substring(3), getter.getReturnType());
setterName = setter.getName();
} catch (NoSuchMethodException e) {
- LOG.debug("No matching setter found for \"" + getter.getName() + "\". Skipping...");
- }
+ LOG.debug("No matching setter found for " + clazz.getName() + "." + getter.getName() + ". Skipping...");
+ }
+
+ LangUtil.translate(name); // temporary, just to make sure we catch it in the dict
+ if (getter.getReturnType().isEnum()) {
+ for(Object o : getter.getReturnType().getEnumConstants()) {
+ LangUtil.translate(o.toString());
+ }
+ }
+
PropertyWrapper property =
new PropertyWrapper(name, getter.getReturnType(), getter.getName(), setterName,
annotation.defaultable(), validator, annotation.sortOrder());
diff --git a/diylc/diylc-core/src/org/diylc/presenter/Presenter.java b/diylc/diylc-core/src/org/diylc/presenter/Presenter.java
index f0a13e59..d1427841 100644
--- a/diylc/diylc-core/src/org/diylc/presenter/Presenter.java
+++ b/diylc/diylc-core/src/org/diylc/presenter/Presenter.java
@@ -81,6 +81,7 @@ import org.diylc.core.VisibilityPolicy;
import org.diylc.core.annotations.IAutoCreator;
import org.diylc.core.measures.Size;
import org.diylc.core.measures.SizeUnit;
+import org.diylc.lang.LangUtil;
import org.diylc.netlist.Group;
import org.diylc.netlist.Netlist;
import org.diylc.netlist.NetlistAnalyzer;
@@ -102,6 +103,32 @@ import com.thoughtworks.xstream.io.xml.DomDriver;
*/
public class Presenter implements IPlugInPort {
+ private static final String REACHED_BOTTOM =
+ LangUtil.translate("Selected component(s) have reached the bottom of their layer. Do you want to force the selection to the back?");
+ private static final String SEND_SELECTION_TO_BACK = LangUtil.translate("Send Selection to Back");
+ private static final String SAVE_AS_VARIANT = LangUtil.translate("Save as Variant");
+ private static final String VARIANT_EXISTS = LangUtil.translate("A variant with that name already exists. Overwrite?");
+ private static final String ROTATE_CHANGE =
+ LangUtil.translate("Selection contains components that cannot be rotated. Do you want to exclude them?");
+ private static final String MIRRORING_CHANGE =
+ LangUtil.translate("Mirroring operation will change the circuit. Do you want to continue?");
+ private static final String MIRROR_SELECTION = LangUtil.translate("Mirror Selection");
+ private static final String ROTATE_SELECTION = LangUtil.translate("Rotate Selection");
+ private static final String CANNOT_MIRROR =
+ LangUtil.translate("Selection contains components that cannot be mirrored. Do you want to exclude them?");
+ private static final String BRING_SELECTION_TO_FRONT = LangUtil.translate("Bring Selection to Front");
+ private static final String REACHED_TOP =
+ LangUtil.translate("Selected component(s) have reached the top of their layer. Do you want to force the selection to the top?");
+ private static final String WARNING = LangUtil.translate("Warning");
+ private static final String UNSAVED_CHANGES = LangUtil.translate("There are unsaved changes. Would you like to save them?");
+ private static final String ERROR_SLOT = LangUtil.translate("Could not set component type slot. Check log for details.");
+ private static final String ERROR_SAVE = LangUtil.translate("Could not save the project to file. Check the log for details.");
+ private static final String ERROR_CREATE = LangUtil.translate("Could not create component. Check log for details.");
+ private static final String ERROR_EDIT = LangUtil.translate("Error occurred while editing selection. Check the log for details.");
+ private static final String ERROR_NEW = LangUtil.translate("Could not create a new file. Check the log for details.");
+ private static final String ERROR = LangUtil.translate("Error");
+ private static final String APPLY_ERROR = "Could not apply changes. Check the log for details.";
+
private static final Logger LOG = Logger.getLogger(Presenter.class);
public static VersionNumber CURRENT_VERSION = new VersionNumber(4, 0, 0);
@@ -137,7 +164,7 @@ public class Presenter implements IPlugInPort {
public static final int ICON_SIZE = 32;
- private static final int MAX_RECENT_FILES = 20;
+ private static final int MAX_RECENT_FILES = 20;
private Project currentProject;
private Map> componentTypes;
@@ -294,7 +321,7 @@ public class Presenter implements IPlugInPort {
DrawingCache.Instance.clear();
} catch (Exception e) {
LOG.error("Could not create new file", e);
- view.showMessage("Could not create a new file. Check the log for details.", "Error", IView.ERROR_MESSAGE);
+ view.showMessage(ERROR_NEW, ERROR, IView.ERROR_MESSAGE);
}
}
@@ -314,7 +341,7 @@ public class Presenter implements IPlugInPort {
builder.append("
");
}
builder.append("