Lokalisierungs-Editor: liest/schreibt messages_<lang>.properties in blight-lang
TextBundleIO von JSON auf Java-Properties-Format umgestellt (messages_*.properties). Reihenfolge der Keys bleibt beim Laden erhalten. EditorApp zeigt jetzt auf blight-lang/src/main/resources/lang/ statt blight-assets/localization/. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -1,13 +1,9 @@
|
|||||||
package de.blight.common.model;
|
package de.blight.common.model;
|
||||||
|
|
||||||
import com.google.gson.Gson;
|
|
||||||
import com.google.gson.GsonBuilder;
|
|
||||||
import com.google.gson.reflect.TypeToken;
|
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.*;
|
||||||
import java.lang.reflect.Type;
|
|
||||||
import java.nio.charset.StandardCharsets;
|
import java.nio.charset.StandardCharsets;
|
||||||
import java.nio.file.Files;
|
import java.nio.file.Files;
|
||||||
import java.nio.file.Path;
|
import java.nio.file.Path;
|
||||||
@@ -15,31 +11,57 @@ import java.util.*;
|
|||||||
import java.util.stream.Stream;
|
import java.util.stream.Stream;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Lädt und speichert {@link TextBundle}-Instanzen als JSON.
|
* Lädt und speichert {@link TextBundle}-Instanzen als Java-Properties-Dateien.
|
||||||
* Dateiformat: {@code <lang>.json} im localization/-Verzeichnis.
|
* Dateiformat: {@code messages_<lang>.properties} im lang/-Verzeichnis.
|
||||||
*/
|
*/
|
||||||
public final class TextBundleIO {
|
public final class TextBundleIO {
|
||||||
|
|
||||||
private static final Logger log = LoggerFactory.getLogger(TextBundleIO.class);
|
private static final Logger log = LoggerFactory.getLogger(TextBundleIO.class);
|
||||||
private static final String EXTENSION = ".json";
|
private static final String PREFIX = "messages_";
|
||||||
private static final Gson GSON = new GsonBuilder().setPrettyPrinting().create();
|
private static final String EXTENSION = ".properties";
|
||||||
private static final Type MAP_TYPE = new TypeToken<LinkedHashMap<String, String>>(){}.getType();
|
|
||||||
|
|
||||||
private TextBundleIO() {}
|
private TextBundleIO() {}
|
||||||
|
|
||||||
public static void save(TextBundle bundle, Path dir) throws IOException {
|
public static void save(TextBundle bundle, Path dir) throws IOException {
|
||||||
Files.createDirectories(dir);
|
Files.createDirectories(dir);
|
||||||
Files.writeString(dir.resolve(bundle.getLanguage() + EXTENSION),
|
Path file = dir.resolve(PREFIX + bundle.getLanguage() + EXTENSION);
|
||||||
GSON.toJson(bundle.getEntries()), StandardCharsets.UTF_8);
|
Properties props = new Properties();
|
||||||
log.debug("[TextBundleIO] Gespeichert: {}", bundle.getLanguage());
|
props.putAll(bundle.getEntries());
|
||||||
|
try (BufferedWriter w = Files.newBufferedWriter(file, StandardCharsets.UTF_8)) {
|
||||||
|
// Manuell schreiben um Kommentar-Header und Reihenfolge zu erhalten
|
||||||
|
for (Map.Entry<String, String> e : bundle.getEntries().entrySet()) {
|
||||||
|
w.write(escapeKey(e.getKey()) + "=" + escapeValue(e.getValue()));
|
||||||
|
w.newLine();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
log.debug("[TextBundleIO] Gespeichert: {}", file);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static TextBundle load(Path file) throws IOException {
|
public static TextBundle load(Path file) throws IOException {
|
||||||
String lang = file.getFileName().toString().replace(EXTENSION, "");
|
String name = file.getFileName().toString();
|
||||||
Map<String, String> map = GSON.fromJson(
|
String lang = name.replace(PREFIX, "").replace(EXTENSION, "");
|
||||||
Files.readString(file, StandardCharsets.UTF_8), MAP_TYPE);
|
Properties props = new Properties();
|
||||||
|
try (BufferedReader r = Files.newBufferedReader(file, StandardCharsets.UTF_8)) {
|
||||||
|
props.load(r);
|
||||||
|
}
|
||||||
|
// Properties erhält keine garantierte Reihenfolge – beim Laden Reihenfolge aus Datei rekonstruieren
|
||||||
|
LinkedHashMap<String, String> ordered = new LinkedHashMap<>();
|
||||||
|
try (BufferedReader r = Files.newBufferedReader(file, StandardCharsets.UTF_8)) {
|
||||||
|
String line;
|
||||||
|
while ((line = r.readLine()) != null) {
|
||||||
|
line = line.trim();
|
||||||
|
if (line.isEmpty() || line.startsWith("#") || line.startsWith("!")) continue;
|
||||||
|
int eq = line.indexOf('=');
|
||||||
|
int colon = line.indexOf(':');
|
||||||
|
int sep = (eq >= 0 && (colon < 0 || eq <= colon)) ? eq : colon;
|
||||||
|
if (sep < 0) continue;
|
||||||
|
String key = line.substring(0, sep).trim();
|
||||||
|
String val = props.getProperty(key, "");
|
||||||
|
ordered.put(key, val);
|
||||||
|
}
|
||||||
|
}
|
||||||
TextBundle bundle = new TextBundle(lang);
|
TextBundle bundle = new TextBundle(lang);
|
||||||
if (map != null) bundle.setEntries(new LinkedHashMap<>(map));
|
bundle.setEntries(ordered);
|
||||||
return bundle;
|
return bundle;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -47,7 +69,10 @@ public final class TextBundleIO {
|
|||||||
List<TextBundle> result = new ArrayList<>();
|
List<TextBundle> result = new ArrayList<>();
|
||||||
if (!Files.isDirectory(dir)) return result;
|
if (!Files.isDirectory(dir)) return result;
|
||||||
try (Stream<Path> walk = Files.list(dir)) {
|
try (Stream<Path> walk = Files.list(dir)) {
|
||||||
walk.filter(p -> p.toString().endsWith(EXTENSION))
|
walk.filter(p -> {
|
||||||
|
String n = p.getFileName().toString();
|
||||||
|
return n.startsWith(PREFIX) && n.endsWith(EXTENSION);
|
||||||
|
})
|
||||||
.sorted()
|
.sorted()
|
||||||
.forEach(p -> {
|
.forEach(p -> {
|
||||||
try { result.add(load(p)); }
|
try { result.add(load(p)); }
|
||||||
@@ -63,8 +88,11 @@ public final class TextBundleIO {
|
|||||||
List<String> langs = new ArrayList<>();
|
List<String> langs = new ArrayList<>();
|
||||||
if (!Files.isDirectory(dir)) return langs;
|
if (!Files.isDirectory(dir)) return langs;
|
||||||
try (Stream<Path> walk = Files.list(dir)) {
|
try (Stream<Path> walk = Files.list(dir)) {
|
||||||
walk.filter(p -> p.toString().endsWith(EXTENSION))
|
walk.filter(p -> {
|
||||||
.map(p -> p.getFileName().toString().replace(EXTENSION, ""))
|
String n = p.getFileName().toString();
|
||||||
|
return n.startsWith(PREFIX) && n.endsWith(EXTENSION);
|
||||||
|
})
|
||||||
|
.map(p -> p.getFileName().toString().replace(PREFIX, "").replace(EXTENSION, ""))
|
||||||
.sorted()
|
.sorted()
|
||||||
.forEach(langs::add);
|
.forEach(langs::add);
|
||||||
} catch (IOException ignored) {}
|
} catch (IOException ignored) {}
|
||||||
@@ -72,6 +100,16 @@ public final class TextBundleIO {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public static void delete(String language, Path dir) throws IOException {
|
public static void delete(String language, Path dir) throws IOException {
|
||||||
Files.deleteIfExists(dir.resolve(language + EXTENSION));
|
Files.deleteIfExists(dir.resolve(PREFIX + language + EXTENSION));
|
||||||
|
}
|
||||||
|
|
||||||
|
// ── Hilfsmethoden ─────────────────────────────────────────────────────────
|
||||||
|
|
||||||
|
private static String escapeKey(String key) {
|
||||||
|
return key.replace(" ", "\\ ");
|
||||||
|
}
|
||||||
|
|
||||||
|
private static String escapeValue(String val) {
|
||||||
|
return val.replace("\\", "\\\\").replace("\n", "\\n").replace("\r", "\\r");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -10249,7 +10249,8 @@ public class EditorApp extends Application {
|
|||||||
label.setStyle("-fx-font-weight: bold; -fx-font-size: 13;");
|
label.setStyle("-fx-font-weight: bold; -fx-font-size: 13;");
|
||||||
tb.getItems().addAll(backBtn, new Separator(Orientation.VERTICAL), label);
|
tb.getItems().addAll(backBtn, new Separator(Orientation.VERTICAL), label);
|
||||||
topBar.getChildren().set(1, tb);
|
topBar.getChildren().set(1, tb);
|
||||||
root.setCenter(new de.blight.editor.ui.LocalizationEditorView(ASSET_ROOT.resolve("localization")));
|
root.setCenter(new de.blight.editor.ui.LocalizationEditorView(
|
||||||
|
de.blight.editor.ProjectRoot.resolve("blight-lang", "src", "main", "resources", "lang")));
|
||||||
root.setRight(null);
|
root.setRight(null);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user