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>
116 lines
4.7 KiB
Java
116 lines
4.7 KiB
Java
package de.blight.common.model;
|
||
|
||
import org.slf4j.Logger;
|
||
import org.slf4j.LoggerFactory;
|
||
|
||
import java.io.*;
|
||
import java.nio.charset.StandardCharsets;
|
||
import java.nio.file.Files;
|
||
import java.nio.file.Path;
|
||
import java.util.*;
|
||
import java.util.stream.Stream;
|
||
|
||
/**
|
||
* Lädt und speichert {@link TextBundle}-Instanzen als Java-Properties-Dateien.
|
||
* Dateiformat: {@code messages_<lang>.properties} im lang/-Verzeichnis.
|
||
*/
|
||
public final class TextBundleIO {
|
||
|
||
private static final Logger log = LoggerFactory.getLogger(TextBundleIO.class);
|
||
private static final String PREFIX = "messages_";
|
||
private static final String EXTENSION = ".properties";
|
||
|
||
private TextBundleIO() {}
|
||
|
||
public static void save(TextBundle bundle, Path dir) throws IOException {
|
||
Files.createDirectories(dir);
|
||
Path file = dir.resolve(PREFIX + bundle.getLanguage() + EXTENSION);
|
||
Properties props = new Properties();
|
||
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 {
|
||
String name = file.getFileName().toString();
|
||
String lang = name.replace(PREFIX, "").replace(EXTENSION, "");
|
||
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);
|
||
bundle.setEntries(ordered);
|
||
return bundle;
|
||
}
|
||
|
||
public static List<TextBundle> loadAll(Path dir) {
|
||
List<TextBundle> result = new ArrayList<>();
|
||
if (!Files.isDirectory(dir)) return result;
|
||
try (Stream<Path> walk = Files.list(dir)) {
|
||
walk.filter(p -> {
|
||
String n = p.getFileName().toString();
|
||
return n.startsWith(PREFIX) && n.endsWith(EXTENSION);
|
||
})
|
||
.sorted()
|
||
.forEach(p -> {
|
||
try { result.add(load(p)); }
|
||
catch (IOException e) { log.warn("[TextBundleIO] Fehler: {}", e.getMessage()); }
|
||
});
|
||
} catch (IOException e) {
|
||
log.warn("[TextBundleIO] Scan-Fehler: {}", e.getMessage());
|
||
}
|
||
return result;
|
||
}
|
||
|
||
public static List<String> availableLanguages(Path dir) {
|
||
List<String> langs = new ArrayList<>();
|
||
if (!Files.isDirectory(dir)) return langs;
|
||
try (Stream<Path> walk = Files.list(dir)) {
|
||
walk.filter(p -> {
|
||
String n = p.getFileName().toString();
|
||
return n.startsWith(PREFIX) && n.endsWith(EXTENSION);
|
||
})
|
||
.map(p -> p.getFileName().toString().replace(PREFIX, "").replace(EXTENSION, ""))
|
||
.sorted()
|
||
.forEach(langs::add);
|
||
} catch (IOException ignored) {}
|
||
return langs;
|
||
}
|
||
|
||
public static void delete(String language, Path dir) throws IOException {
|
||
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");
|
||
}
|
||
}
|