Skip to content

Commit 2e80ee5

Browse files
author
Federico Fissore
committed
Testing translation for proper formatting. This test will fail when a
translation uses wrong syntax, thus avoiding any future issue similar to #4095
1 parent fd8cb46 commit 2e80ee5

File tree

9 files changed

+225
-114
lines changed

9 files changed

+225
-114
lines changed

app/src/cc/arduino/i18n/Language.java

+56
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
/*
2+
* This file is part of Arduino.
3+
*
4+
* Copyright 2015 Arduino LLC (http://www.arduino.cc/)
5+
*
6+
* Arduino is free software; you can redistribute it and/or modify
7+
* it under the terms of the GNU General Public License as published by
8+
* the Free Software Foundation; either version 2 of the License, or
9+
* (at your option) any later version.
10+
*
11+
* This program is distributed in the hope that it will be useful,
12+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
13+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14+
* GNU General Public License for more details.
15+
*
16+
* You should have received a copy of the GNU General Public License
17+
* along with this program; if not, write to the Free Software
18+
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
19+
*
20+
* As a special exception, you may use this file as part of a free software
21+
* library without restriction. Specifically, if other files instantiate
22+
* templates or use macros or inline functions from this file, or you compile
23+
* this file and link it with other files to produce an executable, this
24+
* file does not by itself cause the resulting executable to be covered by
25+
* the GNU General Public License. This exception does not however
26+
* invalidate any other reasons why the executable file might be covered by
27+
* the GNU General Public License.
28+
*/
29+
30+
package cc.arduino.i18n;
31+
32+
public class Language {
33+
34+
private final String name;
35+
private final String originalName;
36+
private final String isoCode;
37+
38+
public Language(String name, String originalName, String isoCode) {
39+
this.name = name;
40+
this.originalName = originalName;
41+
this.isoCode = isoCode;
42+
}
43+
44+
public String toString() {
45+
if (originalName.length() == 0) {
46+
return name;
47+
}
48+
return originalName + " (" + name + ")";
49+
}
50+
51+
public String getIsoCode() {
52+
return isoCode;
53+
}
54+
55+
}
56+
+117
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,117 @@
1+
/*
2+
* This file is part of Arduino.
3+
*
4+
* Copyright 2015 Arduino LLC (http://www.arduino.cc/)
5+
*
6+
* Arduino is free software; you can redistribute it and/or modify
7+
* it under the terms of the GNU General Public License as published by
8+
* the Free Software Foundation; either version 2 of the License, or
9+
* (at your option) any later version.
10+
*
11+
* This program is distributed in the hope that it will be useful,
12+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
13+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14+
* GNU General Public License for more details.
15+
*
16+
* You should have received a copy of the GNU General Public License
17+
* along with this program; if not, write to the Free Software
18+
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
19+
*
20+
* As a special exception, you may use this file as part of a free software
21+
* library without restriction. Specifically, if other files instantiate
22+
* templates or use macros or inline functions from this file, or you compile
23+
* this file and link it with other files to produce an executable, this
24+
* file does not by itself cause the resulting executable to be covered by
25+
* the GNU General Public License. This exception does not however
26+
* invalidate any other reasons why the executable file might be covered by
27+
* the GNU General Public License.
28+
*/
29+
30+
package cc.arduino.i18n;
31+
32+
import static processing.app.I18n.tr;
33+
34+
public class Languages {
35+
36+
public static final Language[] languages;
37+
38+
// Languages that are not translated at least to 65% are
39+
// kept in the "missingLanguages" array until they have enough
40+
// translated strings.
41+
@SuppressWarnings("unused")
42+
public static final Language[] missingLanguages;
43+
44+
static {
45+
languages = new Language[]{
46+
new Language(tr("System Default"), "", ""),
47+
new Language(tr("Albanian"), "shqip", "sq"),
48+
new Language(tr("Arabic"), "العربية", "ar"),
49+
new Language(tr("Aragonese"), "Aragonés", "an"),
50+
new Language(tr("Basque"), "Euskara", "eu"),
51+
new Language(tr("Belarusian"), "Беларуская мова", "be"),
52+
new Language(tr("Bulgarian"), "български", "bg"),
53+
new Language(tr("Canadian French"), "Canadienne-français", "fr_CA"),
54+
new Language(tr("Catalan"), "Català", "ca"),
55+
new Language(tr("Chinese (China)"), "简体中文", "zh_CN"),
56+
new Language(tr("Chinese (Taiwan)"), "", "zh_TW"),
57+
new Language(tr("Croatian"), "Hrvatski", "hr_HR"),
58+
new Language(tr("Czech (Czech Republic)"), "český (Czech Republic)", "cs_CZ"),
59+
new Language(tr("Dutch"), "Nederlands", "nl"),
60+
new Language(tr("English"), "English", "en"),
61+
new Language(tr("English (United Kingdom)"), "English (United Kingdom)", "en_GB"),
62+
new Language(tr("Estonian"), "Eesti", "et"),
63+
new Language(tr("Estonian (Estonia)"), "Eesti keel", "et_EE"),
64+
new Language(tr("Filipino"), "Pilipino", "fil"),
65+
new Language(tr("Finnish"), "Suomi", "fi"),
66+
new Language(tr("French"), "Français", "fr"),
67+
new Language(tr("Galician"), "Galego", "gl"),
68+
new Language(tr("Galician (Spain)"), "Galego (Spain)", "gl_ES"),
69+
new Language(tr("Georgian"), "საქართველოს", "ka_GE"),
70+
new Language(tr("German"), "Deutsch", "de_DE"),
71+
new Language(tr("Hebrew"), "עברית", "he"),
72+
new Language(tr("Hindi"), "हिंदी", "hi"),
73+
new Language(tr("Indonesian"), "Bahasa Indonesia", "id"),
74+
new Language(tr("Italian"), "Italiano", "it_IT"),
75+
new Language(tr("Japanese"), "日本語", "ja_JP"),
76+
new Language(tr("Korean"), "한국어", "ko_KR"),
77+
new Language(tr("Latvian"), "Latviešu", "lv_LV"),
78+
new Language(tr("Norwegian Bokmål"), "Norsk bokmål", "nb_NO"),
79+
new Language(tr("Persian"), "فارسی", "fa"),
80+
new Language(tr("Polish"), "Język Polski", "pl"),
81+
new Language(tr("Portugese"), "Português", "pt"),
82+
new Language(tr("Portuguese (Brazil)"), "Português (Brazil)", "pt_BR"),
83+
new Language(tr("Portuguese (Portugal)"), "Português (Portugal)", "pt_PT"),
84+
new Language(tr("Romanian"), "Română", "ro"),
85+
new Language(tr("Russian"), "Русский", "ru"),
86+
new Language(tr("Slovak"), "Slovenčina", "sk"),
87+
new Language(tr("Slovenian"), "Slovenščina", "sl_SI"),
88+
new Language(tr("Spanish"), "Español", "es"),
89+
new Language(tr("Swedish"), "Svenska", "sv"),
90+
new Language(tr("Tamil"), "தமிழ்", "ta"),
91+
new Language(tr("Turkish"), "Türk", "tr"),
92+
new Language(tr("Ukrainian"), "Український", "uk"),
93+
new Language(tr("Vietnamese"), "Tiếng Việt", "vi"),
94+
};
95+
96+
missingLanguages = new Language[]{
97+
new Language(tr("Afrikaans"), "Afrikaans", "af"),
98+
new Language(tr("Armenian"), "Հայերեն", "hy"),
99+
new Language(tr("Asturian"), "Asturianu", "ast"),
100+
new Language(tr("Bosnian"), "Bosanski", "bs"),
101+
new Language(tr("Burmese (Myanmar)"), "ဗမာစကား", "my_MM"),
102+
new Language(tr("Chinese (Taiwan) (Big5)"), "", "zh_TW.Big5"),
103+
new Language(tr("Danish (Denmark)"), "Dansk (Denmark)", "da_DK"),
104+
new Language(tr("Dutch (Netherlands)"), "Nederlands", "nl_NL"),
105+
new Language(tr("Greek"), "ελληνικά", "el_GR"),
106+
new Language(tr("Hungarian"), "Magyar", "hu"),
107+
new Language(tr("Lithuaninan"), "Lietuvių Kalba", "lt_LT"),
108+
new Language(tr("Marathi"), "मराठी", "mr"),
109+
new Language(tr("Nepali"), "नेपाली", "ne"),
110+
new Language(tr("Persian (Iran)"), "فارسی (Iran)", "fa_IR"),
111+
new Language(tr("Talossan"), "Talossan", "tzl"),
112+
new Language(tr("Western Frisian"), "Western Frisian", "fy")
113+
};
114+
115+
}
116+
117+
}

app/src/cc/arduino/view/preferences/Preferences.form

+1-1
Original file line numberDiff line numberDiff line change
@@ -244,7 +244,7 @@
244244
</Component>
245245
<Component class="javax.swing.JComboBox" name="comboLanguage">
246246
<AuxValues>
247-
<AuxValue name="JavaCodeGenerator_CreateCodeCustom" type="java.lang.String" value="new JComboBox(languages)"/>
247+
<AuxValue name="JavaCodeGenerator_CreateCodeCustom" type="java.lang.String" value="new JComboBox(Languages.languages)"/>
248248
</AuxValues>
249249
</Component>
250250
<Component class="javax.swing.JLabel" name="requiresRestartLabel">

app/src/cc/arduino/view/preferences/Preferences.java

+4-104
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,8 @@
3030
package cc.arduino.view.preferences;
3131

3232
import cc.arduino.Constants;
33+
import cc.arduino.i18n.Language;
34+
import cc.arduino.i18n.Languages;
3335
import processing.app.Base;
3436
import processing.app.BaseNoGui;
3537
import processing.app.I18n;
@@ -47,41 +49,9 @@
4749

4850
public class Preferences extends javax.swing.JDialog {
4951

50-
private final Language[] languages;
51-
52-
// Languages that are not translated at least to 65% are
53-
// kept in the "missingLanguages" array until they have enough
54-
// translated strings.
55-
@SuppressWarnings("unused")
56-
private final Language[] missingLanguages;
57-
5852
private final WarningItem[] warningItems;
5953
private final Base base;
6054

61-
public static class Language {
62-
63-
private final String name;
64-
private final String originalName;
65-
private final String isoCode;
66-
67-
public Language(String name, String originalName, String isoCode) {
68-
this.name = name;
69-
this.originalName = originalName;
70-
this.isoCode = isoCode;
71-
}
72-
73-
public String toString() {
74-
if (originalName.length() == 0) {
75-
return name;
76-
}
77-
return originalName + " (" + name + ")";
78-
}
79-
80-
public String getIsoCode() {
81-
return isoCode;
82-
}
83-
}
84-
8555
private static class WarningItem {
8656
private final String value;
8757
private final String translation;
@@ -105,76 +75,6 @@ public Preferences(Window parent, Base base) {
10575
super(parent);
10676
this.base = base;
10777

108-
this.languages = new Language[]{
109-
new Language(tr("System Default"), "", ""),
110-
new Language(tr("Albanian"), "shqip", "sq"),
111-
new Language(tr("Arabic"), "العربية", "ar"),
112-
new Language(tr("Aragonese"), "Aragonés", "an"),
113-
new Language(tr("Basque"), "Euskara", "eu"),
114-
new Language(tr("Belarusian"), "Беларуская мова", "be"),
115-
new Language(tr("Bulgarian"), "български", "bg"),
116-
new Language(tr("Canadian French"), "Canadienne-français", "fr_CA"),
117-
new Language(tr("Catalan"), "Català", "ca"),
118-
new Language(tr("Chinese (China)"), "简体中文", "zh_CN"),
119-
new Language(tr("Chinese (Taiwan) (Big5)"), "", "zh_TW.Big5"),
120-
new Language(tr("Chinese (Taiwan)"), "", "zh_TW"),
121-
new Language(tr("Croatian"), "Hrvatski", "hr_HR"),
122-
new Language(tr("Czech (Czech Republic)"), "český (Czech Republic)", "cs_CZ"),
123-
new Language(tr("Dutch"), "Nederlands", "nl"),
124-
new Language(tr("English"), "English", "en"),
125-
new Language(tr("English (United Kingdom)"), "English (United Kingdom)", "en_GB"),
126-
new Language(tr("Estonian"), "Eesti", "et"),
127-
new Language(tr("Estonian (Estonia)"), "Eesti keel", "et_EE"),
128-
new Language(tr("Filipino"), "Pilipino", "fil"),
129-
new Language(tr("Finnish"), "Suomi", "fi"),
130-
new Language(tr("French"), "Français", "fr"),
131-
new Language(tr("Galician"), "Galego", "gl"),
132-
new Language(tr("Galician (Spain)"), "Galego (Spain)", "gl_ES"),
133-
new Language(tr("Georgian"), "საქართველოს", "ka_GE"),
134-
new Language(tr("German"), "Deutsch", "de_DE"),
135-
new Language(tr("Hebrew"), "עברית", "he"),
136-
new Language(tr("Hindi"), "हिंदी", "hi"),
137-
new Language(tr("Indonesian"), "Bahasa Indonesia", "id"),
138-
new Language(tr("Italian"), "Italiano", "it_IT"),
139-
new Language(tr("Japanese"), "日本語", "ja_JP"),
140-
new Language(tr("Korean"), "한국어", "ko_KR"),
141-
new Language(tr("Latvian"), "Latviešu", "lv_LV"),
142-
new Language(tr("Norwegian Bokmål"), "Norsk bokmål", "nb_NO"),
143-
new Language(tr("Persian"), "فارسی", "fa"),
144-
new Language(tr("Polish"), "Język Polski", "pl"),
145-
new Language(tr("Portugese"), "Português", "pt"),
146-
new Language(tr("Portuguese (Brazil)"), "Português (Brazil)", "pt_BR"),
147-
new Language(tr("Portuguese (Portugal)"), "Português (Portugal)", "pt_PT"),
148-
new Language(tr("Romanian"), "Română", "ro"),
149-
new Language(tr("Russian"), "Русский", "ru"),
150-
new Language(tr("Slovak"), "Slovenčina", "sk"),
151-
new Language(tr("Slovenian"), "Slovenščina", "sl_SI"),
152-
new Language(tr("Spanish"), "Español", "es"),
153-
new Language(tr("Swedish"), "Svenska", "sv"),
154-
new Language(tr("Tamil"), "தமிழ்", "ta"),
155-
new Language(tr("Turkish"), "Türk", "tr"),
156-
new Language(tr("Ukrainian"), "Український", "uk"),
157-
new Language(tr("Vietnamese"), "Tiếng Việt", "vi"),
158-
};
159-
160-
this.missingLanguages = new Language[]{
161-
new Language(tr("Afrikaans"), "Afrikaans", "af"),
162-
new Language(tr("Armenian"), "Հայերեն", "hy"),
163-
new Language(tr("Asturian"), "Asturianu", "ast"),
164-
new Language(tr("Bosnian"), "Bosanski", "bs"),
165-
new Language(tr("Burmese (Myanmar)"), "ဗမာစကား", "my_MM"),
166-
new Language(tr("Danish (Denmark)"), "Dansk (Denmark)", "da_DK"),
167-
new Language(tr("Dutch (Netherlands)"), "Nederlands", "nl_NL"),
168-
new Language(tr("Greek"), "ελληνικά", "el_GR"),
169-
new Language(tr("Hungarian"), "Magyar", "hu"),
170-
new Language(tr("Lithuaninan"), "Lietuvių Kalba", "lt_LT"),
171-
new Language(tr("Marathi"), "मराठी", "mr"),
172-
new Language(tr("Nepali"), "नेपाली", "ne"),
173-
new Language(tr("Persian (Iran)"), "فارسی (Iran)", "fa_IR"),
174-
new Language(tr("Talossan"), "Talossan", "tzl"),
175-
new Language(tr("Western Frisian"), "Western Frisian", "fy")
176-
};
177-
17878
this.warningItems = new WarningItem[]{
17979
new WarningItem("none", tr("None")),
18080
new WarningItem("default", tr("Default")),
@@ -207,7 +107,7 @@ private void initComponents() {
207107
sketchbookLocationField = new javax.swing.JTextField();
208108
javax.swing.JButton browseButton = new javax.swing.JButton();
209109
javax.swing.JLabel comboLanguageLabel = new javax.swing.JLabel();
210-
comboLanguage = new JComboBox(languages);
110+
comboLanguage = new JComboBox(Languages.languages);
211111
javax.swing.JLabel requiresRestartLabel = new javax.swing.JLabel();
212112
javax.swing.JLabel fontSizeLabel = new javax.swing.JLabel();
213113
fontSizeField = new javax.swing.JTextField();
@@ -831,7 +731,7 @@ private void showPrerefencesData() {
831731
sketchbookLocationField.setText(PreferencesData.get("sketchbook.path"));
832732

833733
String currentLanguageISOCode = PreferencesData.get("editor.languages.current");
834-
for (Language language : languages) {
734+
for (Language language : Languages.languages) {
835735
if (language.getIsoCode().equals(currentLanguageISOCode)) {
836736
comboLanguage.setSelectedItem(language);
837737
}

app/test/cc/arduino/i18n/I18NTest.java

+35-1
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,10 @@
3333
import processing.app.AbstractWithPreferencesTest;
3434
import processing.app.I18n;
3535

36+
import java.util.Collections;
37+
import java.util.Locale;
3638
import java.util.Map;
39+
import java.util.ResourceBundle;
3740

3841
import static org.junit.Assert.assertEquals;
3942

@@ -50,7 +53,38 @@ public void testMessageFormat() throws Exception {
5053
public void testMessageFormatFromExternalProcess() throws Exception {
5154
Map<String, Object> output = new ExternalProcessOutputParser().parse("===WARNING: Category '{0}' in library {1} is not valid. Setting to '{2}' ||| [ Wire Uncategorized]");
5255

53-
String actual = I18n.format((String) output.get("msg"), (Object[])output.get("args"));
56+
String actual = I18n.format((String) output.get("msg"), (Object[]) output.get("args"));
5457
assertEquals("WARNING: Category '' in library Wire is not valid. Setting to 'Uncategorized'", actual);
5558
}
59+
60+
@Test
61+
public void testAllLocales() {
62+
for (Language language : Languages.languages) {
63+
if (!language.getIsoCode().equals("")) {
64+
Locale locale = toLocale(language);
65+
ResourceBundle bundle = ResourceBundle.getBundle("processing.app.i18n.Resources", locale);
66+
if (locale.equals(bundle.getLocale())) {
67+
Collections.list(bundle.getKeys()).stream().map(bundle::getString).filter(key -> !key.contains("<html")).forEach(key -> {
68+
try {
69+
I18n.format(key);
70+
} catch (IllegalArgumentException e) {
71+
System.out.println(language);
72+
System.out.println(key);
73+
throw e;
74+
}
75+
});
76+
} else {
77+
System.out.println("Missing locale: " + locale);
78+
}
79+
}
80+
}
81+
}
82+
83+
private Locale toLocale(Language language) {
84+
String[] languageParts = language.getIsoCode().split("_");
85+
if (languageParts.length == 2) {
86+
return new Locale(languageParts[0], languageParts[1]);
87+
}
88+
return new Locale(languageParts[0]);
89+
}
5690
}

0 commit comments

Comments
 (0)