Skip to content

Commit 7ad5edf

Browse files
release: VisuStruct 1.2.2 with 21 UI languages (Swift sync)
Align desktop UI locales with VisuStruct-swift via sync_java_messages_from_swift.py and new Messages_* bundles. Split Settings: Languages menu, dedicated block-text preset dialog, and Simulation dialog to avoid duplicate language selection. Co-authored-by: Cursor <cursoragent@cursor.com>
1 parent 125dfd8 commit 7ad5edf

38 files changed

Lines changed: 6235 additions & 152 deletions

README.md

Lines changed: 12 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010

1111
**VisuStruct** is a **structure-chart editor** for **Nassi–Shneiderman** diagrams (desktop, Java / Swing).
1212

13-
Under **Settings → Languages** the **user interface** can be set to **English**, **German**, or **European Portuguese (Portugal)** (`uilanguage`: `en`, `de`, `pt_PT` in `visustruct.properties`). A few auxiliary dialogs may still show English-only labels.
13+
Under **Settings → Languages** the **user interface** can be set to any of **21 languages** aligned with **VisuStruct-swift** (English, German, Spanish, French, Italian, Polish, Turkish, Korean, Japanese, Greek, Ukrainian, Arabic, Russian, Hebrew, Dutch, Swedish, Danish, Norwegian Bokmål, Chinese Simplified/Traditional, European Portuguese). Stored as `uilanguage` in `visustruct.properties` (e.g. `zh_Hans`, `pt_PT`). UI strings are synced from the Swift app where keys overlap: `python3 tools/sync_java_messages_from_swift.py` (map: `VisuStruct-swift/tools/java_swift_i18n_map.json`). Desktop-only labels may still fall back to English.
1414

1515
*Lineage:* Based on the open-source project [kekru/struktogrammeditor](https://github.com/kekru/struktogrammeditor) (Kevin Krummenauer, MIT). Copyright and terms: [LICENSE](LICENSE).
1616

@@ -22,6 +22,13 @@ git clone https://github.com/code4teaching/VisuStruct.git
2222

2323
---
2424

25+
## Changes in 1.2.2 (summary)
26+
27+
- **Languages:** **21 UI languages** (VisuStruct-swift); `tools/sync_java_messages_from_swift.py`.
28+
- **CI:** GitHub Actions build passes again (headless-safe drag-and-drop init for tests).
29+
30+
Full notes: [`release-notes/v1.2.2.md`](release-notes/v1.2.2.md).
31+
2532
## Changes in 1.2.1 (summary)
2633

2734
- **Simulation:** **true/false** branch selection by case name; **reset (Stop)** for new input on rerun; highlight mode and play speed in Settings; sidebar layout (input always visible, current block shows instruction text).
@@ -98,7 +105,7 @@ Full notes: [`release-notes/v1.0.7.md`](release-notes/v1.0.7.md).
98105
## Changes in 1.0.6 (summary)
99106

100107
- **UI & theme:** cleaner menus; **light / dark** FlatLaf **without restart**; improved dark-mode readability.
101-
- **Languages:** **Settings → Languages** lists **English**, **German**, **Portuguese**; stored in **`visustruct.properties`** (`uilanguage`). If unset, default follows **JVM locale**.
108+
- **Languages:** **Settings → Languages** lists **21 languages** (same set as VisuStruct-swift, except the Java-keyword pseudo-locale); stored in **`visustruct.properties`** (`uilanguage`). If unset, default follows **JVM locale**. Regenerate bundles: `python3 tools/sync_java_messages_from_swift.py`.
102109
- **I18n:** menu, core confirmation dialogs, `JFileChooser` labels, context menu, palette (PNG, about, trash, etc.); **English** is no longer overridden by the OS default locale.
103110
- **Structure-chart labels:** preset **“Java (default)”** (and localized equivalents); **preview** in the settings dialog; **palette** shows Java keywords on buttons when the Java preset is active.
104111
- **Tabs:** new diagram tab uses a localized **“Untitled”**-style title instead of a hard-coded English string.
@@ -141,7 +148,7 @@ This release aligns **branding and technical identity** with **VisuStruct**. **`
141148
- **Java 17** target (build and run with JDK 17+).
142149
- **FlatLaf** light/dark themes; **JDOM2**; no legacy AppleJavaExtensions.
143150
- **Motif** look-and-feel removed; **Metal** and FlatLaf available.
144-
- Current fat JAR name follows **`pom.xml`** **`version`** (e.g. **`visustruct-1.2.1.jar`**).
151+
- Current fat JAR name follows **`pom.xml`** **`version`** (e.g. **`visustruct-1.2.2.jar`**).
145152

146153
---
147154

@@ -216,13 +223,13 @@ chmod +x mvnw
216223
Output (version from `pom.xml`):
217224

218225
```text
219-
target/visustruct-1.2.1.jar
226+
target/visustruct-1.2.2.jar
220227
```
221228

222229
## Run
223230

224231
```bash
225-
java -jar target/visustruct-1.2.1.jar
232+
java -jar target/visustruct-1.2.2.jar
226233
```
227234

228235
Double-click may work if `.jar` is associated with Java.

packaging/macos/build-lightweight-app.sh

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
set -euo pipefail
55
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
66
ROOT="$(cd "$SCRIPT_DIR/../.." && pwd)"
7-
VER="${PROJECT_VERSION:-1.2.1}"
7+
VER="${PROJECT_VERSION:-1.2.2}"
88
JAR_NAME="visustruct-${VER}.jar"
99
OUT="${ROOT}/dist/macos-app-light"
1010
APP="${OUT}/VisuStruct.app"

packaging/macos/build-macos-app.sh

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
# Voraussetzung: JDK 17+ mit jpackage (im PATH oder JAVA_HOME).
55
set -euo pipefail
66
ROOT="$(cd "$(dirname "$0")/../.." && pwd)"
7-
VER="${PROJECT_VERSION:-1.2.1}"
7+
VER="${PROJECT_VERSION:-1.2.2}"
88
JAR="visustruct-${VER}.jar"
99
INPUT="${ROOT}/target"
1010
OUT="${ROOT}/dist/macos-app"

pom.xml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
<modelVersion>4.0.0</modelVersion>
55
<groupId>de.visustruct</groupId>
66
<artifactId>visustruct</artifactId>
7-
<version>1.2.1</version>
7+
<version>1.2.2</version>
88
<name>VisuStruct</name>
99

1010
<properties>

release-notes/v1.2.2.md

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
# VisuStruct 1.2.2
2+
3+
Patch release **after** **1.2.1**.
4+
5+
## Languages
6+
7+
- **21 UI languages** (aligned with VisuStruct-swift): Settings → Languages and the settings dialog; `uilanguage` in `visustruct.properties`.
8+
- Bundles synced from the Swift app where keys overlap: `python3 tools/sync_java_messages_from_swift.py`.
9+
10+
## Build / CI
11+
12+
- **GitHub Actions:** unit tests no longer fail with `java.awt.HeadlessException` on CI runners without a display. Drag-and-drop is initialized only when not in headless mode (`GraphicsEnvironment.isHeadless()`).
13+
14+
## Build
15+
16+
- Maven: **`de.visustruct:visustruct:1.2.2`**
17+
- Fat JAR: **`visustruct-1.2.2.jar`** (`./mvnw package``target/`)

src/main/java/de/visustruct/control/Controlling.java

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,7 @@
5555
import de.visustruct.view.AuswahlPanel;
5656
import de.visustruct.view.CodeErzeuger;
5757
import de.visustruct.view.EinstellungsDialog;
58+
import de.visustruct.view.SimulationEinstellungenDialog;
5859
import de.visustruct.view.FontChooser;
5960
import de.visustruct.view.GUI;
6061
import de.visustruct.view.ZoomEinstellungen;
@@ -418,6 +419,10 @@ public void actionPerformed(ActionEvent e) {
418419
new EinstellungsDialog(gui, true);
419420
break;
420421

422+
case simulationEinstellen:
423+
new SimulationEinstellungenDialog(gui, true);
424+
break;
425+
421426
case schriftartAendern:
422427
new FontChooser(this,true);
423428
break;
@@ -483,6 +488,11 @@ public void actionPerformed(ActionEvent e) {
483488
}
484489

485490

491+
/** UI-Sprache wechseln (Menü oder Einstellungsdialog). */
492+
public void applyUiLanguageChange(String tag) {
493+
changeUiLanguageIfNeeded(tag);
494+
}
495+
486496
private void changeUiLanguageIfNeeded(String tag) {
487497
String next = GlobalSettings.normalizeUiLanguageTag(tag);
488498
if (next.equals(GlobalSettings.getUiLanguageTag())) {

src/main/java/de/visustruct/control/GlobalSettings.java

Lines changed: 170 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -59,9 +59,35 @@ public class GlobalSettings implements Konstanten{
5959
*/
6060
private static int lookAndFeelAktuell = 4;
6161

62-
/** UI-Sprache: {@code en}, {@code de} oder {@code pt_PT} (Portugal; Menüleiste, I18n). */
62+
/** UI-Sprache (Menüleiste, I18n); siehe {@link #UI_LANGUAGE_OPTIONS}. */
6363
private static String uiLanguageTag = "en";
6464

65+
/** Tag und Menü-Label-Key für „Einstellungen → Sprachen“ (wie VisuStruct-swift). */
66+
public record UiLanguageOption(String tag, String menuLabelKey) {}
67+
68+
public static final List<UiLanguageOption> UI_LANGUAGE_OPTIONS = List.of(
69+
new UiLanguageOption("en", "menu.settings.language.en"),
70+
new UiLanguageOption("de", "menu.settings.language.de"),
71+
new UiLanguageOption("es", "menu.settings.language.es"),
72+
new UiLanguageOption("fr", "menu.settings.language.fr"),
73+
new UiLanguageOption("it", "menu.settings.language.it"),
74+
new UiLanguageOption("pl", "menu.settings.language.pl"),
75+
new UiLanguageOption("tr", "menu.settings.language.tr"),
76+
new UiLanguageOption("ko", "menu.settings.language.ko"),
77+
new UiLanguageOption("ja", "menu.settings.language.ja"),
78+
new UiLanguageOption("el", "menu.settings.language.el"),
79+
new UiLanguageOption("uk", "menu.settings.language.uk"),
80+
new UiLanguageOption("ar", "menu.settings.language.ar"),
81+
new UiLanguageOption("ru", "menu.settings.language.ru"),
82+
new UiLanguageOption("he", "menu.settings.language.he"),
83+
new UiLanguageOption("nl", "menu.settings.language.nl"),
84+
new UiLanguageOption("sv", "menu.settings.language.sv"),
85+
new UiLanguageOption("da", "menu.settings.language.da"),
86+
new UiLanguageOption("nb", "menu.settings.language.nb"),
87+
new UiLanguageOption("zh_Hans", "menu.settings.language.zh_Hans"),
88+
new UiLanguageOption("zh_Hant", "menu.settings.language.zh_Hant"),
89+
new UiLanguageOption("pt_PT", "menu.settings.language.pt"));
90+
6591
/** {@code true}, wenn {@code uilanguage} in der geladenen Properties-Datei stand (sonst JVM-Locale als Vorgabe). */
6692
private static boolean uiLanguageFromPropertiesFile = false;
6793

@@ -398,6 +424,79 @@ private static void applyDefaultUiLanguageFromRuntimeLocale() {
398424
setUiLanguageTag("pt_PT");
399425
return;
400426
}
427+
if ("es".equals(lang)) {
428+
setUiLanguageTag("es");
429+
return;
430+
}
431+
if ("fr".equals(lang)) {
432+
setUiLanguageTag("fr");
433+
return;
434+
}
435+
if ("it".equals(lang)) {
436+
setUiLanguageTag("it");
437+
return;
438+
}
439+
if ("nl".equals(lang)) {
440+
setUiLanguageTag("nl");
441+
return;
442+
}
443+
if ("pl".equals(lang)) {
444+
setUiLanguageTag("pl");
445+
return;
446+
}
447+
if ("tr".equals(lang)) {
448+
setUiLanguageTag("tr");
449+
return;
450+
}
451+
if ("ru".equals(lang)) {
452+
setUiLanguageTag("ru");
453+
return;
454+
}
455+
if ("ko".equals(lang)) {
456+
setUiLanguageTag("ko");
457+
return;
458+
}
459+
if ("ja".equals(lang)) {
460+
setUiLanguageTag("ja");
461+
return;
462+
}
463+
if ("el".equals(lang)) {
464+
setUiLanguageTag("el");
465+
return;
466+
}
467+
if ("uk".equals(lang)) {
468+
setUiLanguageTag("uk");
469+
return;
470+
}
471+
if ("ar".equals(lang)) {
472+
setUiLanguageTag("ar");
473+
return;
474+
}
475+
if ("he".equals(lang)) {
476+
setUiLanguageTag("he");
477+
return;
478+
}
479+
if ("sv".equals(lang)) {
480+
setUiLanguageTag("sv");
481+
return;
482+
}
483+
if ("da".equals(lang)) {
484+
setUiLanguageTag("da");
485+
return;
486+
}
487+
if ("nb".equals(lang)) {
488+
setUiLanguageTag("nb");
489+
return;
490+
}
491+
if ("zh".equals(lang)) {
492+
String country = def.getCountry();
493+
if ("TW".equalsIgnoreCase(country) || "HK".equalsIgnoreCase(country) || "MO".equalsIgnoreCase(country)) {
494+
setUiLanguageTag("zh_Hant");
495+
} else {
496+
setUiLanguageTag("zh_Hans");
497+
}
498+
return;
499+
}
401500
setUiLanguageTag("en");
402501
}
403502

@@ -650,34 +749,64 @@ public static void setLookAndFeelAktuell(int themeIndex) {
650749
GlobalSettings.lookAndFeelAktuell = themeIndex;
651750
}
652751

653-
/** Locale für die UI-Texte: Englisch, Deutsch oder Português (Portugal, {@code pt-PT}). */
752+
/** Locale für UI-Texte. */
654753
public static Locale getUiLocale() {
655-
if ("de".equals(uiLanguageTag)) {
656-
return Locale.GERMAN;
657-
}
658-
if ("pt_PT".equals(uiLanguageTag)) {
659-
return Locale.forLanguageTag("pt-PT");
660-
}
661-
return Locale.ENGLISH;
754+
return switch (uiLanguageTag) {
755+
case "de" -> Locale.GERMAN;
756+
case "pt_PT" -> Locale.forLanguageTag("pt-PT");
757+
case "es" -> Locale.forLanguageTag("es");
758+
case "fr" -> Locale.FRENCH;
759+
case "it" -> Locale.ITALIAN;
760+
case "nl" -> Locale.forLanguageTag("nl");
761+
case "pl" -> Locale.forLanguageTag("pl");
762+
case "tr" -> Locale.forLanguageTag("tr");
763+
case "ru" -> Locale.forLanguageTag("ru");
764+
case "ko" -> Locale.KOREAN;
765+
case "ja" -> Locale.JAPANESE;
766+
case "el" -> Locale.forLanguageTag("el");
767+
case "uk" -> Locale.forLanguageTag("uk");
768+
case "ar" -> Locale.forLanguageTag("ar");
769+
case "he" -> Locale.forLanguageTag("he");
770+
case "sv" -> Locale.forLanguageTag("sv");
771+
case "da" -> Locale.forLanguageTag("da");
772+
case "nb" -> Locale.forLanguageTag("nb");
773+
case "zh_Hans" -> Locale.forLanguageTag("zh-Hans");
774+
case "zh_Hant" -> Locale.forLanguageTag("zh-Hant");
775+
default -> Locale.ENGLISH;
776+
};
662777
}
663778

664779
public static String getUiLanguageTag() {
665780
return uiLanguageTag;
666781
}
667782

668-
/** Kanonische Tags: {@code en}, {@code de}, {@code pt_PT}. */
783+
/** Kanonischer Tag aus {@link #UI_LANGUAGE_OPTIONS}; unbekannt → {@code en}. */
669784
public static String normalizeUiLanguageTag(String tag) {
670785
if (tag == null || tag.isBlank()) {
671786
return "en";
672787
}
673788
String t = tag.trim();
674-
if (t.equalsIgnoreCase("de")) {
675-
return "de";
789+
for (UiLanguageOption opt : UI_LANGUAGE_OPTIONS) {
790+
if (opt.tag().equalsIgnoreCase(t)) {
791+
return opt.tag();
792+
}
676793
}
677794
String hy = t.replace('_', '-').toLowerCase(Locale.ROOT);
678-
if ("pt-pt".equals(hy)) {
795+
for (UiLanguageOption opt : UI_LANGUAGE_OPTIONS) {
796+
String optHy = opt.tag().replace('_', '-').toLowerCase(Locale.ROOT);
797+
if (hy.equals(optHy) || hy.startsWith(optHy + "-")) {
798+
return opt.tag();
799+
}
800+
}
801+
if ("pt".equalsIgnoreCase(t) || "pt-pt".equals(hy)) {
679802
return "pt_PT";
680803
}
804+
if ("zh-cn".equals(hy) || "zh-hans".equals(hy)) {
805+
return "zh_Hans";
806+
}
807+
if ("zh-tw".equals(hy) || "zh-hant".equals(hy) || "zh-hk".equals(hy)) {
808+
return "zh_Hant";
809+
}
681810
return "en";
682811
}
683812

@@ -696,6 +825,34 @@ public static boolean isUiPortuguesePortugal() {
696825
return "pt_PT".equals(uiLanguageTag);
697826
}
698827

828+
public static boolean isUiSpanish() {
829+
return "es".equals(uiLanguageTag);
830+
}
831+
832+
public static boolean isUiFrench() {
833+
return "fr".equals(uiLanguageTag);
834+
}
835+
836+
public static boolean isUiItalian() {
837+
return "it".equals(uiLanguageTag);
838+
}
839+
840+
public static boolean isUiDutch() {
841+
return "nl".equals(uiLanguageTag);
842+
}
843+
844+
public static boolean isUiPolish() {
845+
return "pl".equals(uiLanguageTag);
846+
}
847+
848+
public static boolean isUiTurkish() {
849+
return "tr".equals(uiLanguageTag);
850+
}
851+
852+
public static boolean isUiRussian() {
853+
return "ru".equals(uiLanguageTag);
854+
}
855+
699856
public static double getSimulationPlayDelaySec() {
700857
return simulationPlayDelaySec;
701858
}

src/main/java/de/visustruct/i18n/I18n.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@
1414
* <p>
1515
* Ohne spezielles {@link ResourceBundle.Control} würde {@link ResourceBundle#getBundle(String, Locale)}
1616
* bei fehlendem sprachspezifischen Bundle auf die <b>System-Locale</b> zurückfallen und die falsche Sprache laden.
17-
* Unterstützt {@code en}, {@code de} und {@code pt_PT} ({@code Messages_pt_PT.properties}).
17+
* Unterstützt alle in {@link GlobalSettings#UI_LANGUAGE_OPTIONS} (21 Sprachen, wie VisuStruct-swift).
1818
*/
1919
public final class I18n {
2020

src/main/java/de/visustruct/other/XActionCommands.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,8 @@ public enum XActionCommands {
1313

1414
/** Dialog „Beschriftung (Struktogramm)“ / Textvorlagen für neue Blöcke. */
1515
elementBeschriftungEinstellen,
16+
/** Dialog Simulations-Einstellungen (Geschwindigkeit, Markierung). */
17+
simulationEinstellen,
1618

1719
info,
1820

0 commit comments

Comments
 (0)