Translator.java

1
// Copyright © 2004-2006 University of Helsinki, Department of Computer Science
2
// Copyright © 2012 various contributors
3
// This software is released under GNU Lesser General Public License 2.1.
4
// The license text is at http://www.gnu.org/licenses/lgpl-2.1.html
5
6
package fi.helsinki.cs.titokone;
7
8
import java.text.MessageFormat;
9
import java.util.*;
10
import java.util.logging.Logger;
11
12
/**
13
 * This class deals with translating strings. It also remembers which
14
 * language is currently set, but does not know specifically what
15
 * languages are currently available.
16
 */
17
public class Translator {
18
19
    // TODO: get rid of this class, replace it with standard use of resource bundles and keep all translations in properties files
20
21
    /**
22
     * This name identifies the resource files containing translations
23
     * for this software.
24
     */
25
    // XXX: non-final for testing purposes
26
    public static String resourceFamilyName = "fi.helsinki.cs.titokone.translations.Translations";
27
28
    /**
29
     * This field contains the default locale.
30
     */
31
    public static final Locale defaultLocale = Locale.ENGLISH;
32
33
    /**
34
     * This field stores the current locale. It defaults to
35
     * defaultLocale.
36
     */
37
    private static Locale currentLocale = defaultLocale;
38
39
    // XXX: for testing purposes, resourceFamilyName can change after this class is initialized, so we can't save it in a field
40
    private static ResourceBundle defaultTranslations() {
41 2
        return ResourceBundle.getBundle(resourceFamilyName, defaultLocale);
42
    }
43
44
    /**
45
     * This field stores the current ResourceBundle in use.
46
     */
47 1
    private static ResourceBundle translations = defaultTranslations();
48
49
50
    /**
51
     * This function translates a fixed string to the currently used
52
     * language. If the current language has no string corresponding to
53
     * the key, a default translation is used from Translations.class.
54
     * If that class does not have the translation either, the keystring
55
     * itself is returned. The translation therefore fails silently.
56
     *
57
     * @param keyString A string key that identifies the translation in a
58
     *                  Translations*class file.
59
     * @return The translated string, if available, or something otherwise
60
     *         usable.
61
     */
62
    public static String translate(String keyString) {
63
        String result = null;
64
        Logger logger =
65
                Logger.getLogger(Translator.class.getPackage().getName());
66
        try {
67 1
            result = translations.getString(keyString);
68
        } catch (MissingResourceException untranslatedKey) {
69
            logger.fine("Translation for " + keyString + " not found in " +
70
                    "current set."); // No Message used here.
71
            result = null;
72
        } catch (NullPointerException oddity) {
73
            logger.fine("ResourceBundle getString would not just return " +
74
                    "null, it insists on casting. Trasnslating string " +
75
                    "'" + keyString + "' failed.");
76
            result = null;
77
        }
78 1
        if (result == null) { // If there was no luck, try the untranslated.
79
            try {
80 2
                result = defaultTranslations().getString(keyString);
81
            } catch (MissingResourceException totallyUnknownKey) {
82
                logger.fine("Translation for " + keyString + " not found " +
83
                        "in default set either.");
84
                result = null;
85
            } catch (NullPointerException oddity) {
86
                logger.fine("Null pointer exception repeated when trying " +
87
                        "default translations.");
88
                result = null;
89
            }
90
        }
91 1
        if (result == null) // If there was still no luck, go for the keystr.
92
        {
93
            result = keyString;
94
        }
95 1
        return result;
96
    }
97
98
    /**
99
     * This function translates a template string to the currently
100
     * used language and replaces any {i} markers in it with strings
101
     * from the parameters array. If the current language has no
102
     * string corresponding to the key, a default translation is used
103
     * from Translations.class.  If that class does not have the
104
     * translation either, the keystring itself is returned. The
105
     * translation therefore fails silently. The translation string is
106
     * fetched from the current ResourceBundle in use, and the replacement
107
     * is done with the help of java.text.MessageFormat.
108
     *
109
     * @param keyString  A string key that identifies the translation in a
110
     *                   Translations*class file.
111
     * @param parameters A string array containing strings to replace {i}
112
     *                   markers in the string in order - that is, parameters[0] replaces {0},
113
     *                   parameters[1] replaces {1} etc.
114
     * @return The translated string, if available, or something otherwise
115
     *         usable, with {i} markers replaced from the parameters array as
116
     *         far as there are available replacements.
117
     */
118
    public static String translate(String keyString, String[] parameters) {
119
        MessageFormat formatter;
120
        // First, we translate the basic string as if it had no parameters,
121
        // then we replace any {i} fields in it by using MessageFormat's
122
        // format method, with the replacements as its parameter. The
123
        // replacement is done according to locale, although this is
124
        // unlikely to affect anything in our current implementation.
125 2
        formatter = new MessageFormat(translate(keyString));
126 1
        formatter.setLocale(currentLocale);
127 3
        return formatter.format(parameters).toString();
128
    }
129
130
    /**
131
     * This method sets the current locale in use and fetches a
132
     * corresponding ResourceBundle that contains the translations most
133
     * suitable for this locale. This may mean the default English
134
     * translation if there is nothing better available.
135
     *
136
     * @param newLocale The locale to switch to, eg. new Locale("fi",
137
     *                  "FI").
138
     */
139
    public static void setLocale(Locale newLocale) {
140
        Logger logger =
141
                Logger.getLogger(Translator.class.getPackage().getName());
142 1
        if (newLocale == null) {
143 1
            throw new IllegalArgumentException("Trying to set locale to null.");
144
        }
145
        currentLocale = newLocale;
146 1
        translations = ResourceBundle.getBundle(resourceFamilyName, newLocale);
147
        logger.fine("Locale changed, new locale " + newLocale.toString() + ", translations from class " +
148
                translations.getClass().getName() + ".");
149
    }
150
151
    /**
152
     * This method sets the current locale in use and tries to fetch the
153
     * translation from translationPath. Use of setLocale(Locale) is
154
     * recommended, but this method enables the user to have very exact
155
     * control over where the translations are found.
156
     *
157
     * @param newLocale       The locale to switch to, eg. new Locale("fi",
158
     *                        "FI").
159
     * @param newTranslations A class containing the translations for this
160
     *                        locale. If the translations are located in the standard place,
161
     *                        setLocale(Locale) can be used.
162
     */
163
    public static void setLocale(Locale newLocale,
164
                                 ResourceBundle newTranslations) {
165
        Logger logger =
166
                Logger.getLogger(Translator.class.getPackage().getName());
167 1
        if (newLocale == null) {
168 1
            throw new IllegalArgumentException("Trying to set locale to " +
169
                    "null.");
170
        }
171 1
        if (newTranslations == null) {
172 1
            throw new IllegalArgumentException("Trying to set translations " +
173
                    "to null.");
174
        }
175
        currentLocale = newLocale;
176
        translations = newTranslations;
177
        logger.fine("Locale changed with set translations, new locale " +
178
                newLocale.toString() +
179
                ", translations from class " +
180
                translations.getClass().getName() + ".");
181
    }
182
183
    /**
184
     * This method returns the resource bundle in use.
185
     *
186
     * @return The resource bundle set to correspond to the current
187
     *         locale.
188
     */
189
    public static ResourceBundle getResourceBundle() {
190 1
        return translations;
191
    }
192
}

Mutations

41

removed call to java/util/ResourceBundle::getBundle : KILLED -> fi.helsinki.cs.titokone.TranslatorTest.testReplacementTranslation(fi.helsinki.cs.titokone.TranslatorTest)

mutated return of Object value for fi/helsinki/cs/titokone/Translator::defaultTranslations to ( if (x != null) null else throw new RuntimeException ) : KILLED -> fi.helsinki.cs.titokone.TranslatorTest.testReplacementTranslation(fi.helsinki.cs.titokone.TranslatorTest)

47

removed call to fi/helsinki/cs/titokone/Translator::defaultTranslations : TIMED_OUT

67

removed call to java/util/ResourceBundle::getString : KILLED -> fi.helsinki.cs.titokone.TranslatorTest.testBasicTranslate(fi.helsinki.cs.titokone.TranslatorTest)

78

negated conditional : KILLED -> fi.helsinki.cs.titokone.TranslatorTest.testReplacementTranslation(fi.helsinki.cs.titokone.TranslatorTest)

80

removed call to java/util/ResourceBundle::getString : KILLED -> fi.helsinki.cs.titokone.TranslatorTest.testReplacementTranslation(fi.helsinki.cs.titokone.TranslatorTest)

removed call to fi/helsinki/cs/titokone/Translator::defaultTranslations : KILLED -> fi.helsinki.cs.titokone.TranslatorTest.testReplacementTranslation(fi.helsinki.cs.titokone.TranslatorTest)

91

negated conditional : KILLED -> fi.helsinki.cs.titokone.TranslatorTest.testReplacementTranslation(fi.helsinki.cs.titokone.TranslatorTest)

95

mutated return of Object value for fi/helsinki/cs/titokone/Translator::translate to ( if (x != null) null else throw new RuntimeException ) : KILLED -> fi.helsinki.cs.titokone.TranslatorTest.testReplacementTranslation(fi.helsinki.cs.titokone.TranslatorTest)

125

removed call to fi/helsinki/cs/titokone/Translator::translate : KILLED -> fi.helsinki.cs.titokone.TranslatorTest.testReplacementTranslation(fi.helsinki.cs.titokone.TranslatorTest)

removed call to java/text/MessageFormat::<init> : KILLED -> fi.helsinki.cs.titokone.TranslatorTest.testReplacementTranslation(fi.helsinki.cs.titokone.TranslatorTest)

126

removed call to java/text/MessageFormat::setLocale : SURVIVED

127

removed call to java/lang/String::toString : KILLED -> fi.helsinki.cs.titokone.TranslatorTest.testReplacementTranslation(fi.helsinki.cs.titokone.TranslatorTest)

removed call to java/text/MessageFormat::format : KILLED -> fi.helsinki.cs.titokone.TranslatorTest.testReplacementTranslation(fi.helsinki.cs.titokone.TranslatorTest)

mutated return of Object value for fi/helsinki/cs/titokone/Translator::translate to ( if (x != null) null else throw new RuntimeException ) : KILLED -> fi.helsinki.cs.titokone.TranslatorTest.testReplacementTranslation(fi.helsinki.cs.titokone.TranslatorTest)

142

negated conditional : KILLED -> fi.helsinki.cs.titokone.TranslatorTest.testReplacementTranslation(fi.helsinki.cs.titokone.TranslatorTest)

143

removed call to java/lang/IllegalArgumentException::<init> : NO_COVERAGE

146

removed call to java/util/ResourceBundle::getBundle : KILLED -> fi.helsinki.cs.titokone.TranslatorTest.testReplacementTranslation(fi.helsinki.cs.titokone.TranslatorTest)

167

negated conditional : KILLED -> fi.helsinki.cs.titokone.TranslatorTest.testReplacementTranslation(fi.helsinki.cs.titokone.TranslatorTest)

168

removed call to java/lang/IllegalArgumentException::<init> : NO_COVERAGE

171

negated conditional : KILLED -> fi.helsinki.cs.titokone.TranslatorTest.testReplacementTranslation(fi.helsinki.cs.titokone.TranslatorTest)

172

removed call to java/lang/IllegalArgumentException::<init> : NO_COVERAGE

190

mutated return of Object value for fi/helsinki/cs/titokone/Translator::getResourceBundle to ( if (x != null) null else throw new RuntimeException ) : NO_COVERAGE

Active mutators

Tests examined


Report generated by PIT 0.27