l10n updates
This commit is contained in:
parent
6a189e5a41
commit
8f9bdc832f
BIN
pix-src/icon/icon-large-close.xcf.gz
Normal file
BIN
pix-src/icon/icon-large-close.xcf.gz
Normal file
Binary file not shown.
92
src/net/sf/openrocket/l10n/ClassBasedTranslator.java
Normal file
92
src/net/sf/openrocket/l10n/ClassBasedTranslator.java
Normal file
@ -0,0 +1,92 @@
|
|||||||
|
package net.sf.openrocket.l10n;
|
||||||
|
|
||||||
|
import java.util.MissingResourceException;
|
||||||
|
|
||||||
|
import net.sf.openrocket.logging.TraceException;
|
||||||
|
import net.sf.openrocket.util.BugException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A translator that prepends a pre-defined class name in front of a translation key
|
||||||
|
* and retrieves the translator for that key, and only if that is missing reverts to
|
||||||
|
* the base key name. The base class name can either be provided to the constructor
|
||||||
|
* or retrieved from the stack.
|
||||||
|
*
|
||||||
|
* @author Sampo Niskanen <sampo.niskanen@iki.fi>
|
||||||
|
*/
|
||||||
|
public class ClassBasedTranslator implements Translator {
|
||||||
|
|
||||||
|
|
||||||
|
private final Translator translator;
|
||||||
|
private final String className;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Construct a translator using a specified class name.
|
||||||
|
*
|
||||||
|
* @param translator the translator from which to obtain the translations.
|
||||||
|
* @param className the base class name to prepend.
|
||||||
|
*/
|
||||||
|
public ClassBasedTranslator(Translator translator, String className) {
|
||||||
|
this.translator = translator;
|
||||||
|
this.className = className;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Construct a translator by obtaining the base class name from the stack.
|
||||||
|
*
|
||||||
|
* @param translator the translator from which to obtain the translations.
|
||||||
|
* @param levels the number of levels to move upwards in the stack from the point where this method is called.
|
||||||
|
*/
|
||||||
|
public ClassBasedTranslator(Translator translator, int levels) {
|
||||||
|
this(translator, getStackClass(levels));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String get(String key) {
|
||||||
|
String classKey = className + "." + key;
|
||||||
|
|
||||||
|
try {
|
||||||
|
return translator.get(classKey);
|
||||||
|
} catch (MissingResourceException e) {
|
||||||
|
// Ignore
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
return translator.get(key);
|
||||||
|
} catch (MissingResourceException e) {
|
||||||
|
MissingResourceException mre = new MissingResourceException(
|
||||||
|
"Neither key '" + classKey + "' nor '" + key + "' could be found", e.getClassName(), key);
|
||||||
|
mre.initCause(e);
|
||||||
|
throw mre;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
private static String getStackClass(int levels) {
|
||||||
|
TraceException trace = new TraceException();
|
||||||
|
StackTraceElement stack[] = trace.getStackTrace();
|
||||||
|
final int index = levels + 2;
|
||||||
|
if (stack.length <= index) {
|
||||||
|
throw new BugException("Stack trace is too short, length=" + stack.length + ", expected=" + index, trace);
|
||||||
|
}
|
||||||
|
|
||||||
|
StackTraceElement element = stack[index];
|
||||||
|
String cn = element.getClassName();
|
||||||
|
int pos = cn.lastIndexOf('.');
|
||||||
|
if (pos >= 0) {
|
||||||
|
cn = cn.substring(pos + 1);
|
||||||
|
}
|
||||||
|
return cn;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
// For unit testing purposes
|
||||||
|
String getClassName() {
|
||||||
|
return className;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,58 @@
|
|||||||
|
package net.sf.openrocket.l10n;
|
||||||
|
|
||||||
|
import java.util.HashSet;
|
||||||
|
import java.util.Locale;
|
||||||
|
import java.util.MissingResourceException;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
import net.sf.openrocket.gui.main.ExceptionHandler;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A translator that suppresses MissingResourceExceptions and handles them gracefully.
|
||||||
|
* For every missing key this class calls the exception handler exactly once, and
|
||||||
|
* returns the key itself as the translation.
|
||||||
|
*
|
||||||
|
* @author Sampo Niskanen <sampo.niskanen@iki.fi>
|
||||||
|
*/
|
||||||
|
public class ExceptionSuppressingTranslator implements Translator {
|
||||||
|
|
||||||
|
private static final Set<String> errors = new HashSet<String>();
|
||||||
|
// For unit testing:
|
||||||
|
static int failures = 0;
|
||||||
|
|
||||||
|
private final Translator translator;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sole constructor.
|
||||||
|
*
|
||||||
|
* @param translator the translator to use
|
||||||
|
*/
|
||||||
|
public ExceptionSuppressingTranslator(Translator translator) {
|
||||||
|
this.translator = translator;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String get(String key) {
|
||||||
|
try {
|
||||||
|
return translator.get(key);
|
||||||
|
} catch (MissingResourceException e) {
|
||||||
|
handleError(key, e);
|
||||||
|
}
|
||||||
|
|
||||||
|
return key;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
private static synchronized void handleError(String key, MissingResourceException e) {
|
||||||
|
if (errors.add(key)) {
|
||||||
|
failures++;
|
||||||
|
ExceptionHandler.handleErrorCondition("Can not find translation for '" + key + "' locale=" + Locale.getDefault(), e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -1,26 +1,16 @@
|
|||||||
package net.sf.openrocket.l10n;
|
package net.sf.openrocket.l10n;
|
||||||
|
|
||||||
import java.util.Locale;
|
import java.util.Locale;
|
||||||
import java.util.MissingResourceException;
|
|
||||||
import java.util.ResourceBundle;
|
import java.util.ResourceBundle;
|
||||||
|
|
||||||
import net.sf.openrocket.logging.LogHelper;
|
|
||||||
import net.sf.openrocket.startup.Application;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A translator that obtains translated strings from a resource bundle.
|
* A translator that obtains translated strings from a resource bundle.
|
||||||
* <p>
|
|
||||||
* If a message is not found in any resource bundle, an error is logged and the key itself
|
|
||||||
* is returned.
|
|
||||||
*
|
*
|
||||||
* @author Sampo Niskanen <sampo.niskanen@iki.fi>
|
* @author Sampo Niskanen <sampo.niskanen@iki.fi>
|
||||||
*/
|
*/
|
||||||
public class ResourceBundleTranslator implements Translator {
|
public class ResourceBundleTranslator implements Translator {
|
||||||
private static final LogHelper log = Application.getLogger();
|
|
||||||
|
|
||||||
private final ResourceBundle bundle;
|
private final ResourceBundle bundle;
|
||||||
private final String baseName;
|
|
||||||
private final Locale locale;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create a ResourceBundleTranslator using the default Locale.
|
* Create a ResourceBundleTranslator using the default Locale.
|
||||||
@ -39,8 +29,6 @@ public class ResourceBundleTranslator implements Translator {
|
|||||||
*/
|
*/
|
||||||
public ResourceBundleTranslator(String baseName, Locale locale) {
|
public ResourceBundleTranslator(String baseName, Locale locale) {
|
||||||
this.bundle = ResourceBundle.getBundle(baseName, locale);
|
this.bundle = ResourceBundle.getBundle(baseName, locale);
|
||||||
this.baseName = baseName;
|
|
||||||
this.locale = locale;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -49,12 +37,7 @@ public class ResourceBundleTranslator implements Translator {
|
|||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
public synchronized String get(String key) {
|
public synchronized String get(String key) {
|
||||||
try {
|
return bundle.getString(key);
|
||||||
return bundle.getString(key);
|
|
||||||
} catch (MissingResourceException e) {
|
|
||||||
log.error("String not found for key '" + key + "' in bundle '" + baseName + "' with locale " + locale, e);
|
|
||||||
}
|
|
||||||
return key;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,7 @@
|
|||||||
package net.sf.openrocket.l10n;
|
package net.sf.openrocket.l10n;
|
||||||
|
|
||||||
|
import java.util.MissingResourceException;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* An interface for obtaining translations from logical keys.
|
* An interface for obtaining translations from logical keys.
|
||||||
* <p>
|
* <p>
|
||||||
@ -15,6 +17,7 @@ public interface Translator {
|
|||||||
*
|
*
|
||||||
* @param key the logical string key.
|
* @param key the logical string key.
|
||||||
* @return the translated string.
|
* @return the translated string.
|
||||||
|
* @throws MissingResourceException if the translation corresponding to the key is not found.
|
||||||
* @throws NullPointerException if key is null.
|
* @throws NullPointerException if key is null.
|
||||||
*/
|
*/
|
||||||
public String get(String key);
|
public String get(String key);
|
||||||
|
@ -1,7 +1,9 @@
|
|||||||
package net.sf.openrocket.startup;
|
package net.sf.openrocket.startup;
|
||||||
|
|
||||||
import net.sf.openrocket.database.ThrustCurveMotorSetDatabase;
|
import net.sf.openrocket.database.ThrustCurveMotorSetDatabase;
|
||||||
|
import net.sf.openrocket.l10n.ClassBasedTranslator;
|
||||||
import net.sf.openrocket.l10n.DebugTranslator;
|
import net.sf.openrocket.l10n.DebugTranslator;
|
||||||
|
import net.sf.openrocket.l10n.ExceptionSuppressingTranslator;
|
||||||
import net.sf.openrocket.l10n.Translator;
|
import net.sf.openrocket.l10n.Translator;
|
||||||
import net.sf.openrocket.logging.LogHelper;
|
import net.sf.openrocket.logging.LogHelper;
|
||||||
import net.sf.openrocket.logging.LogLevel;
|
import net.sf.openrocket.logging.LogLevel;
|
||||||
@ -18,7 +20,7 @@ public final class Application {
|
|||||||
private static LogHelper logger;
|
private static LogHelper logger;
|
||||||
private static LogLevelBufferLogger logBuffer;
|
private static LogLevelBufferLogger logBuffer;
|
||||||
|
|
||||||
private static Translator translator = new DebugTranslator();
|
private static Translator baseTranslator = new DebugTranslator();
|
||||||
|
|
||||||
private static ThrustCurveMotorSetDatabase motorSetDatabase;
|
private static ThrustCurveMotorSetDatabase motorSetDatabase;
|
||||||
|
|
||||||
@ -87,15 +89,18 @@ public final class Application {
|
|||||||
* @return a translator.
|
* @return a translator.
|
||||||
*/
|
*/
|
||||||
public static Translator getTranslator() {
|
public static Translator getTranslator() {
|
||||||
return translator;
|
Translator t = baseTranslator;
|
||||||
|
t = new ClassBasedTranslator(t, 1);
|
||||||
|
t = new ExceptionSuppressingTranslator(t);
|
||||||
|
return t;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Set the translator used in obtaining translated strings.
|
* Set the translator used in obtaining translated strings.
|
||||||
* @param translator the translator to set.
|
* @param translator the translator to set.
|
||||||
*/
|
*/
|
||||||
public static void setTranslator(Translator translator) {
|
public static void setBaseTranslator(Translator translator) {
|
||||||
Application.translator = translator;
|
Application.baseTranslator = translator;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -134,6 +134,7 @@ public class Startup {
|
|||||||
} else {
|
} else {
|
||||||
l = new Locale(split[0], split[1], split[2]);
|
l = new Locale(split[0], split[1], split[2]);
|
||||||
}
|
}
|
||||||
|
log.info("Setting custom locale " + l);
|
||||||
Locale.setDefault(l);
|
Locale.setDefault(l);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -147,7 +148,7 @@ public class Startup {
|
|||||||
log.info("Set up translation for locale " + Locale.getDefault() +
|
log.info("Set up translation for locale " + Locale.getDefault() +
|
||||||
", debug.currentFile=" + t.get("debug.currentFile"));
|
", debug.currentFile=" + t.get("debug.currentFile"));
|
||||||
|
|
||||||
Application.setTranslator(t);
|
Application.setBaseTranslator(t);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
79
test/net/sf/openrocket/l10n/TestClassBasedTranslator.java
Normal file
79
test/net/sf/openrocket/l10n/TestClassBasedTranslator.java
Normal file
@ -0,0 +1,79 @@
|
|||||||
|
package net.sf.openrocket.l10n;
|
||||||
|
|
||||||
|
import static org.junit.Assert.*;
|
||||||
|
|
||||||
|
import java.util.MissingResourceException;
|
||||||
|
|
||||||
|
import org.jmock.Expectations;
|
||||||
|
import org.jmock.Mockery;
|
||||||
|
import org.jmock.auto.Mock;
|
||||||
|
import org.jmock.integration.junit4.JMock;
|
||||||
|
import org.jmock.integration.junit4.JUnit4Mockery;
|
||||||
|
import org.junit.Test;
|
||||||
|
import org.junit.runner.RunWith;
|
||||||
|
|
||||||
|
|
||||||
|
@RunWith(JMock.class)
|
||||||
|
public class TestClassBasedTranslator {
|
||||||
|
Mockery context = new JUnit4Mockery();
|
||||||
|
|
||||||
|
@Mock
|
||||||
|
Translator translator;
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testClassName() {
|
||||||
|
ClassBasedTranslator cbt = new ClassBasedTranslator(null, 0);
|
||||||
|
assertEquals("TestClassBasedTranslator", cbt.getClassName());
|
||||||
|
|
||||||
|
cbt = new ClassBasedTranslator(null, "foobar");
|
||||||
|
assertEquals("foobar", cbt.getClassName());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testGetWithClassName() {
|
||||||
|
ClassBasedTranslator cbt = new ClassBasedTranslator(translator, 0);
|
||||||
|
|
||||||
|
// @formatter:off
|
||||||
|
context.checking(new Expectations() {{
|
||||||
|
oneOf(translator).get("TestClassBasedTranslator.fake.key"); will(returnValue("foobar"));
|
||||||
|
}});
|
||||||
|
// @formatter:on
|
||||||
|
|
||||||
|
assertEquals("foobar", cbt.get("fake.key"));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testGetWithoutClassName() {
|
||||||
|
ClassBasedTranslator cbt = new ClassBasedTranslator(translator, 0);
|
||||||
|
|
||||||
|
// @formatter:off
|
||||||
|
context.checking(new Expectations() {{
|
||||||
|
oneOf(translator).get("TestClassBasedTranslator.fake.key"); will(throwException(new MissingResourceException("a", "b", "c")));
|
||||||
|
oneOf(translator).get("fake.key"); will(returnValue("barbaz"));
|
||||||
|
}});
|
||||||
|
// @formatter:on
|
||||||
|
|
||||||
|
assertEquals("barbaz", cbt.get("fake.key"));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testMissing() {
|
||||||
|
ClassBasedTranslator cbt = new ClassBasedTranslator(translator, 0);
|
||||||
|
|
||||||
|
// @formatter:off
|
||||||
|
context.checking(new Expectations() {{
|
||||||
|
oneOf(translator).get("TestClassBasedTranslator.fake.key"); will(throwException(new MissingResourceException("a", "b", "c")));
|
||||||
|
oneOf(translator).get("fake.key"); will(throwException(new MissingResourceException("a", "b", "c")));
|
||||||
|
}});
|
||||||
|
// @formatter:on
|
||||||
|
|
||||||
|
try {
|
||||||
|
fail("Returned: " + cbt.get("fake.key"));
|
||||||
|
} catch (MissingResourceException e) {
|
||||||
|
assertEquals("Neither key 'TestClassBasedTranslator.fake.key' nor 'fake.key' could be found", e.getMessage());
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,64 @@
|
|||||||
|
package net.sf.openrocket.l10n;
|
||||||
|
|
||||||
|
import static org.junit.Assert.assertEquals;
|
||||||
|
|
||||||
|
import java.util.MissingResourceException;
|
||||||
|
|
||||||
|
import org.jmock.Expectations;
|
||||||
|
import org.jmock.Mockery;
|
||||||
|
import org.jmock.auto.Mock;
|
||||||
|
import org.jmock.integration.junit4.JMock;
|
||||||
|
import org.jmock.integration.junit4.JUnit4Mockery;
|
||||||
|
import org.junit.Test;
|
||||||
|
import org.junit.runner.RunWith;
|
||||||
|
|
||||||
|
@RunWith(JMock.class)
|
||||||
|
public class TestExceptionSuppressingTranslator {
|
||||||
|
Mockery context = new JUnit4Mockery();
|
||||||
|
|
||||||
|
@Mock
|
||||||
|
Translator translator;
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testSuccessful() {
|
||||||
|
ExceptionSuppressingTranslator est = new ExceptionSuppressingTranslator(translator);
|
||||||
|
|
||||||
|
// @formatter:off
|
||||||
|
context.checking(new Expectations() {{
|
||||||
|
oneOf(translator).get("fake.key"); will(returnValue("foobar"));
|
||||||
|
}});
|
||||||
|
// @formatter:on
|
||||||
|
|
||||||
|
assertEquals("foobar", est.get("fake.key"));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testFailure() {
|
||||||
|
ExceptionSuppressingTranslator est = new ExceptionSuppressingTranslator(translator);
|
||||||
|
|
||||||
|
assertEquals("Prerequisite failed", 0, ExceptionSuppressingTranslator.failures);
|
||||||
|
|
||||||
|
// @formatter:off
|
||||||
|
context.checking(new Expectations() {{
|
||||||
|
oneOf(translator).get("fake.key"); will(throwException(new MissingResourceException("a", "b", "c")));
|
||||||
|
oneOf(translator).get("fake.key"); will(throwException(new MissingResourceException("a", "b", "c")));
|
||||||
|
oneOf(translator).get("fake.key2"); will(throwException(new MissingResourceException("a", "b", "c")));
|
||||||
|
}});
|
||||||
|
// @formatter:on
|
||||||
|
|
||||||
|
// Test first failure
|
||||||
|
assertEquals("fake.key", est.get("fake.key"));
|
||||||
|
assertEquals(1, ExceptionSuppressingTranslator.failures);
|
||||||
|
|
||||||
|
// Test second failure
|
||||||
|
assertEquals("fake.key", est.get("fake.key"));
|
||||||
|
assertEquals(1, ExceptionSuppressingTranslator.failures);
|
||||||
|
|
||||||
|
// Test failure with other key
|
||||||
|
assertEquals("fake.key2", est.get("fake.key"));
|
||||||
|
assertEquals(2, ExceptionSuppressingTranslator.failures);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
Binary file not shown.
Before Width: | Height: | Size: 2.8 KiB After Width: | Height: | Size: 2.8 KiB |
Loading…
x
Reference in New Issue
Block a user