LoParkour/src/main/java/dev/loki/loparkour/config/locale/LocaleLoader.java
loki5512344 b6712391fd
fix(lint,command,scheduler): lpk alias, ReturnCount/IllegalCatch triage, scheduler facade
- command: replace /lp alias with /lpk (LuckPerms conflict, p.144);
  parkour alias kept; docs/changelog/javadoc updated
- lint p.169: restructure last 69 void early-returns into if-blocks and
  helpers; ReturnCount is now an error gate (0 violations)
- lint p.170: Throwable->RuntimeException on 3 boundaries; narrow 5
  catches to concrete types; document 14 intentional boundary catches
- scheduler p.21: add runTimerAsync to the facade (Bukkit + Folia
  backends); MetricsCacheStore and duels go through the facade, no
  BukkitRunnable/Bukkit.getScheduler outside scheduler/ (archunit
  freeze-store emptied); drop the unverified Folia badge from README
- tracker: mark p.26-29/32-33 done (fixed in fea15e6, verified)
2026-10-10 14:52:10 +02:00

119 lines
4.3 KiB
Java

package dev.loki.loparkour.config.locale;
import dev.loki.loparkour.LoParkour;
import org.bukkit.configuration.ConfigurationSection;
import org.bukkit.configuration.file.FileConfiguration;
import org.bukkit.configuration.file.YamlConfiguration;
import org.bukkit.plugin.Plugin;
import org.jetbrains.annotations.NotNull;
import java.io.File;
import java.io.IOException;
import java.io.InputStreamReader;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Stream;
/**
* Loads locale files from disk with validation and auto-fix.
*/
public final class LocaleLoader {
private LocaleLoader() {
}
/**
* Load all locale files from the locales/ folder.
* Returns a map of locale name → FileConfiguration.
*/
@NotNull
public static Map<String, FileConfiguration> loadAll() {
final Plugin plugin = LoParkour.getPlugin();
try {
final FileConfiguration embedded = YamlConfiguration.loadConfiguration(
new InputStreamReader(plugin.getResource("locales/en.yml"), StandardCharsets.UTF_8));
return loadFromDisk(embedded);
} catch (Exception ex) {
// Boundary: locale loading must never break enable; disk files may be corrupt.
plugin.getLogger().severe("Error while loading locale files: " + ex.getMessage());
return Collections.emptyMap();
}
}
@NotNull
private static Map<String, FileConfiguration> loadFromDisk(@NotNull FileConfiguration embedded) throws Exception {
final Map<String, FileConfiguration> result = new HashMap<>();
final File folder = LoParkour.getInFolder("locales");
ensureFolder(folder);
try (Stream<Path> stream = Files.list(folder.toPath())) {
stream.forEach(path -> {
final File file = path.toFile();
if (!file.getName().endsWith(".yml")) {
return;
}
final String locale = file.getName().split("\\.")[0];
LoParkour.log("Found locale " + locale);
final FileConfiguration config = YamlConfiguration.loadConfiguration(file);
validateAndFix(embedded, config, file);
result.put(locale, config);
});
}
return result;
}
private static void ensureFolder(@NotNull File folder) {
if (!folder.exists()) {
folder.mkdirs();
}
final String[] files = folder.list();
if (files == null || files.length == 0) {
final Plugin plugin = LoParkour.getPlugin();
plugin.saveResource("locales/en.yml", false);
plugin.saveResource("locales/ru.yml", false);
}
}
/**
* Validate a locale file against the reference (en.yml) and auto-fix missing keys.
*/
private static void validateAndFix(@NotNull FileConfiguration reference,
@NotNull FileConfiguration locale,
@NotNull File file) {
final List<String> refNodes = getChildren(reference);
final List<String> locNodes = getChildren(locale);
boolean modified = false;
for (final String node : refNodes) {
if (!locNodes.contains(node)) {
LoParkour.log("Fixing missing config node %s in %s".formatted(node, file.getName()));
locale.set(node, reference.get(node));
modified = true;
}
}
if (modified) {
try {
locale.save(file);
} catch (IOException ex) {
LoParkour.getPlugin().getLogger().severe(
"Error saving fixed locale %s — delete and restart — %s".formatted(file.getName(), ex.getMessage()));
}
}
LoParkour.log("Validated locale " + file.getName());
}
@NotNull
private static List<String> getChildren(@NotNull FileConfiguration config) {
final ConfigurationSection section = config.getConfigurationSection("");
return section != null ? new ArrayList<>(section.getKeys(true)) : Collections.emptyList();
}
}