mirror of
https://github.com/VolmitSoftware/Iris.git
synced 2026-10-17 22:14:56 +00:00
Fixed language reload fallback
- Invalid language updates retain the last valid messages - Malformed startup catalogs use English defaults - Unrelated reloads preserve personal language caches - Deleted locales use English without changing saved choices - Restored locale files resume their translations - Language catalogs include shared editor titles and remove obsolete usage messages
This commit is contained in:
@@ -167,7 +167,16 @@ public final class IrisLanguage {
|
||||
refreshLanguageGuide(override, requestedLocale);
|
||||
}
|
||||
SnapshotCapture capture = captureForReload(override, requestedLocale);
|
||||
boolean initialLoad = dataFolder == null;
|
||||
boolean changedLocale = !requestedLocale.equals(activeLocale);
|
||||
boolean applied = applyReload(resolvedRoot, requestedLocale, capture);
|
||||
if (!applied && initialLoad) {
|
||||
return applyReload(resolvedRoot, requestedLocale,
|
||||
new SnapshotCapture(LocaleHotloadSnapshot.missing(override, requestedLocale), null));
|
||||
}
|
||||
if (applied && changedLocale && selections != null) {
|
||||
requestConfiguredLocale();
|
||||
}
|
||||
if (applied && notifyManualReload) {
|
||||
notifyManualReload(capture.snapshot());
|
||||
}
|
||||
@@ -199,6 +208,12 @@ public final class IrisLanguage {
|
||||
if (!expected.equals(override.getAbsoluteFile())) {
|
||||
return true;
|
||||
}
|
||||
if (rawContent == null && !Files.notExists(expected.toPath(), LinkOption.NOFOLLOW_LINKS)) {
|
||||
IOException failure = new IOException("Language file could not be read: " + expected);
|
||||
IrisLogging.error("Could not reload Iris language " + requestedLocale + "; keeping the last valid messages.");
|
||||
IrisLogging.reportError(failure);
|
||||
return false;
|
||||
}
|
||||
|
||||
LocaleHotloadSnapshot snapshot = rawContent == null
|
||||
? LocaleHotloadSnapshot.missing(expected, requestedLocale)
|
||||
@@ -278,9 +293,6 @@ public final class IrisLanguage {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (current != null) {
|
||||
requestConfiguredLocale();
|
||||
}
|
||||
int warnings = result.validation().warnings().size();
|
||||
IrisLogging.debug("Loaded locale " + requestedLocale + " with " + warnings + " fallback "
|
||||
+ (warnings == 1 ? "entry" : "entries") + ".");
|
||||
@@ -629,11 +641,11 @@ public final class IrisLanguage {
|
||||
}
|
||||
|
||||
static void validateDownload(String locale, String raw) {
|
||||
LocaleOverlay overlay = parseDownloadedOverlay("download:" + locale, locale, raw);
|
||||
LocaleOverlay overlay = parseOverlay("download:" + locale, locale, raw);
|
||||
LocalizationValidator.validate(CATALOG, List.of(overlay)).throwIfInvalid();
|
||||
}
|
||||
|
||||
static LocaleOverlay parseDownloadedOverlay(String source, String locale, String raw) {
|
||||
static LocaleOverlay parseOverlay(String source, String locale, String raw) {
|
||||
LocaleOverlay.Builder builder = LocaleOverlay.builder(source, locale);
|
||||
try {
|
||||
for (Map.Entry<String, MessageValue> entry : TomlLanguageParser.parseValidValues(raw, CATALOG).entrySet()) {
|
||||
@@ -645,16 +657,6 @@ public final class IrisLanguage {
|
||||
return LocalizationValidator.validValues(CATALOG, builder.build());
|
||||
}
|
||||
|
||||
static LocaleOverlay parseOverlay(String source, String locale, String raw) {
|
||||
try {
|
||||
return parseDownloadedOverlay(source, locale, raw);
|
||||
} catch (IllegalArgumentException failure) {
|
||||
IrisLogging.error("Using English for unreadable Iris language file " + source + ".");
|
||||
IrisLogging.reportError(failure);
|
||||
return LocaleOverlay.builder(source, locale).build();
|
||||
}
|
||||
}
|
||||
|
||||
static String englishReference() {
|
||||
Map<String, MessageValue> values = new LinkedHashMap<>();
|
||||
for (MessageKey key : CATALOG.keys()) {
|
||||
@@ -924,10 +926,7 @@ public final class IrisLanguage {
|
||||
}
|
||||
return new SnapshotCapture(snapshot, null);
|
||||
} catch (Exception failure) {
|
||||
IrisLogging.error("Using English for unreadable Iris language file " + file.getPath() + ".");
|
||||
IrisLogging.reportError(failure);
|
||||
return new SnapshotCapture(LocaleHotloadSnapshot.present(
|
||||
file, locale, "", sha256(new byte[0])), null);
|
||||
return new SnapshotCapture(null, failure);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
"language.editor.title" = "{plugin} › {section}"
|
||||
# === Iris language guide ===
|
||||
# Iris - de_DE
|
||||
#
|
||||
@@ -333,7 +334,6 @@ parameters_hover = "Bewege den Mauszeiger über die Parameter, um mehr zu erfahr
|
||||
"error.default_parse_failed" = "Der Standardwert für Parameter {parameter} kann nicht gelesen werden"
|
||||
"error.missing_argument" = "Fehlendes Argument \"{parameter}\" ({type})"
|
||||
"error.execution_failed" = "Befehl {command} konnte nicht ausgeführt werden: {reason}"
|
||||
usage = "Verwendung: {usage}"
|
||||
|
||||
[iris.command]
|
||||
permission_denied = "Dir fehlt die Berechtigung '{permission}'"
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
"language.editor.title" = "{plugin} › {section}"
|
||||
# === Iris language guide ===
|
||||
# Iris - es_ES
|
||||
#
|
||||
@@ -333,7 +334,6 @@ parameters_hover = "Pasa el cursor sobre los parámetros para saber más."
|
||||
"error.default_parse_failed" = "No se puede analizar el valor predeterminado del parámetro {parameter}"
|
||||
"error.missing_argument" = "Falta el argumento \"{parameter}\" ({type})"
|
||||
"error.execution_failed" = "No se pudo ejecutar el comando {command}: {reason}"
|
||||
usage = "Uso: {usage}"
|
||||
|
||||
[iris.command]
|
||||
permission_denied = "No tienes el permiso '{permission}'"
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
"language.editor.title" = "{plugin} › {section}"
|
||||
# === Iris language guide ===
|
||||
# Iris - fi_FI
|
||||
#
|
||||
@@ -338,7 +339,6 @@ parameters_hover = "Vie hiiri parametrien päälle saadaksesi lisätietoja."
|
||||
"error.default_parse_failed" = "Parametrin {parameter} oletusarvoa ei voi jäsentää"
|
||||
"error.missing_argument" = "Puuttuva argumentti \"{parameter}\" ({type})"
|
||||
"error.execution_failed" = "Komennon {command} suoritus epäonnistui: {reason}"
|
||||
usage = "Käyttö: {usage}"
|
||||
|
||||
[iris.command]
|
||||
permission_denied = "Sinulta puuttuu käyttöoikeus '{permission}'"
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
"language.editor.title" = "{plugin} › {section}"
|
||||
# === Iris language guide ===
|
||||
# Iris - fr_FR
|
||||
#
|
||||
@@ -333,7 +334,6 @@ parameters_hover = "Survolez les paramètres pour en savoir plus."
|
||||
"error.default_parse_failed" = "Impossible d'analyser la valeur par défaut du paramètre {parameter}"
|
||||
"error.missing_argument" = "Argument manquant \"{parameter}\" ({type})"
|
||||
"error.execution_failed" = "Échec de l'exécution de la commande {command} : {reason}"
|
||||
usage = "Utilisation: {usage}"
|
||||
|
||||
[iris.command]
|
||||
permission_denied = "Vous n'avez pas la permission '{permission}'"
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
"language.editor.title" = "{plugin} › {section}"
|
||||
# === Iris language guide ===
|
||||
# Iris - he_IL
|
||||
#
|
||||
@@ -338,7 +339,6 @@ parameters_hover = "רחפו מעל הפרמטרים כדי ללמוד עוד."
|
||||
"error.default_parse_failed" = "לא ניתן לנתח את ערך ברירת המחדל של הפרמטר {parameter}"
|
||||
"error.missing_argument" = "חסר ארגומנט \"{parameter}\" ({type})"
|
||||
"error.execution_failed" = "הפעלת הפקודה {command} נכשלה: {reason}"
|
||||
usage = "שימוש: {usage}"
|
||||
|
||||
[iris.command]
|
||||
permission_denied = "אין לך את ההרשאה '{permission}'"
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
"language.editor.title" = "{plugin} › {section}"
|
||||
# === Iris language guide ===
|
||||
# Iris - it_IT
|
||||
#
|
||||
@@ -333,7 +334,6 @@ parameters_hover = "Passa il cursore sui parametri per saperne di più."
|
||||
"error.default_parse_failed" = "Impossibile analizzare il valore predefinito del parametro {parameter}"
|
||||
"error.missing_argument" = "Argomento mancante \"{parameter}\" ({type})"
|
||||
"error.execution_failed" = "Impossibile eseguire il comando {command}: {reason}"
|
||||
usage = "Utilizzo: {usage}"
|
||||
|
||||
[iris.command]
|
||||
permission_denied = "Non disponi del permesso '{permission}'"
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
"language.editor.title" = "{plugin} › {section}"
|
||||
# === Iris language guide ===
|
||||
# Iris - ja-JP
|
||||
#
|
||||
@@ -336,7 +337,6 @@ parameters_hover = "パラメータにカーソルを合わせると詳細が表
|
||||
"error.default_parse_failed" = "パラメーター {parameter} のデフォルト値を解析できません"
|
||||
"error.missing_argument" = "引数 \"{parameter}\" ({type}) が必要です"
|
||||
"error.execution_failed" = "コマンド {command} の実行に失敗しました: {reason}"
|
||||
usage = "使用法: {usage}"
|
||||
|
||||
[iris.command]
|
||||
permission_denied = "権限 '{permission}' がありません"
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
"language.editor.title" = "{plugin} › {section}"
|
||||
# === Iris language guide ===
|
||||
# Iris - ko_KR
|
||||
#
|
||||
@@ -336,7 +337,6 @@ parameters_hover = "매개변수 위에 마우스를 올리면 자세한 내용
|
||||
"error.default_parse_failed" = "매개변수 {parameter}의 기본값을 분석할 수 없습니다"
|
||||
"error.missing_argument" = "인수 \"{parameter}\" ({type})가 필요합니다"
|
||||
"error.execution_failed" = "명령어 {command} 실행에 실패했습니다: {reason}"
|
||||
usage = "사용법: {usage}"
|
||||
|
||||
[iris.command]
|
||||
permission_denied = "'{permission}' 권한이 없습니다"
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
"language.editor.title" = "{plugin} › {section}"
|
||||
# === Iris language guide ===
|
||||
# Iris - lt_LT
|
||||
#
|
||||
@@ -338,7 +339,6 @@ parameters_hover = "Užveskite pelės žymeklį ant parametrų, kad sužinotumė
|
||||
"error.default_parse_failed" = "Nepavyko nuskaityti parametro {parameter} numatytosios reikšmės"
|
||||
"error.missing_argument" = "Trūksta argumento \"{parameter}\" ({type})"
|
||||
"error.execution_failed" = "Nepavyko įvykdyti komandos {command}: {reason}"
|
||||
usage = "Naudojimas: {usage}"
|
||||
|
||||
[iris.command]
|
||||
permission_denied = "Jums trūksta leidimo '{permission}'"
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
"language.editor.title" = "{plugin} › {section}"
|
||||
# === Iris language guide ===
|
||||
# Iris - nl_NL
|
||||
#
|
||||
@@ -333,7 +334,6 @@ parameters_hover = "Beweeg de muis over de parameters voor meer informatie."
|
||||
"error.default_parse_failed" = "Kan de standaardwaarde voor parameter {parameter} niet verwerken"
|
||||
"error.missing_argument" = "Ontbrekend argument \"{parameter}\" ({type})"
|
||||
"error.execution_failed" = "Opdracht {command} kon niet worden uitgevoerd: {reason}"
|
||||
usage = "Gebruik: {usage}"
|
||||
|
||||
[iris.command]
|
||||
permission_denied = "Je hebt niet de vereiste permissie '{permission}'"
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
"language.editor.title" = "{plugin} › {section}"
|
||||
# === Iris language guide ===
|
||||
# Iris - pl_PL
|
||||
#
|
||||
@@ -338,7 +339,6 @@ parameters_hover = "Najedź kursorem na parametry, aby dowiedzieć się więcej.
|
||||
"error.default_parse_failed" = "Nie można odczytać wartości domyślnej parametru {parameter}"
|
||||
"error.missing_argument" = "Brak argumentu \"{parameter}\" ({type})"
|
||||
"error.execution_failed" = "Nie udało się wykonać polecenia {command}: {reason}"
|
||||
usage = "Użycie: {usage}"
|
||||
|
||||
[iris.command]
|
||||
permission_denied = "Nie masz uprawnienia '{permission}'"
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
"language.editor.title" = "{plugin} › {section}"
|
||||
# === Iris language guide ===
|
||||
# Iris - pt_PT
|
||||
#
|
||||
@@ -333,7 +334,6 @@ parameters_hover = "Passe o cursor sobre os parâmetros para saber mais."
|
||||
"error.default_parse_failed" = "Não foi possível interpretar o valor predefinido do parâmetro {parameter}"
|
||||
"error.missing_argument" = "Falta o argumento \"{parameter}\" ({type})"
|
||||
"error.execution_failed" = "Falha ao executar o comando {command}: {reason}"
|
||||
usage = "Utilização: {usage}"
|
||||
|
||||
[iris.command]
|
||||
permission_denied = "Não tem a permissão '{permission}'"
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
"language.editor.title" = "{plugin} › {section}"
|
||||
# === Iris language guide ===
|
||||
# Iris - ru_RU
|
||||
#
|
||||
@@ -338,7 +339,6 @@ parameters_hover = "Наведите курсор на параметры, чт
|
||||
"error.default_parse_failed" = "Невозможно разобрать значение по умолчанию параметра {parameter}"
|
||||
"error.missing_argument" = "Отсутствует аргумент \"{parameter}\" ({type})"
|
||||
"error.execution_failed" = "Не удалось выполнить команду {command}: {reason}"
|
||||
usage = "Использование: {usage}"
|
||||
|
||||
[iris.command]
|
||||
permission_denied = "У вас нет разрешения '{permission}'"
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
"language.editor.title" = "{plugin} › {section}"
|
||||
# === Iris language guide ===
|
||||
# Iris - tr_TR
|
||||
#
|
||||
@@ -338,7 +339,6 @@ parameters_hover = "Daha fazla bilgi için imleci parametrelerin üzerine getiri
|
||||
"error.default_parse_failed" = "{parameter} parametresinin varsayılan değeri ayrıştırılamıyor"
|
||||
"error.missing_argument" = "Eksik bağımsız değişken \"{parameter}\" ({type})"
|
||||
"error.execution_failed" = "{command} komutu çalıştırılamadı: {reason}"
|
||||
usage = "Kullanım: {usage}"
|
||||
|
||||
[iris.command]
|
||||
permission_denied = "'{permission}' iznine sahip değilsiniz"
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
"language.editor.title" = "{plugin} › {section}"
|
||||
# === Iris language guide ===
|
||||
# Iris - vi_VI
|
||||
#
|
||||
@@ -336,7 +337,6 @@ parameters_hover = "Di chuột qua các tham số để tìm hiểu thêm."
|
||||
"error.default_parse_failed" = "Không thể phân tích giá trị mặc định của tham số {parameter}"
|
||||
"error.missing_argument" = "Thiếu đối số \"{parameter}\" ({type})"
|
||||
"error.execution_failed" = "Không thể thực thi lệnh {command}: {reason}"
|
||||
usage = "Cách dùng: {usage}"
|
||||
|
||||
[iris.command]
|
||||
permission_denied = "Bạn không có quyền '{permission}'"
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
"language.editor.title" = "{plugin} › {section}"
|
||||
# === Iris language guide ===
|
||||
# Iris - zh_CN
|
||||
#
|
||||
@@ -336,7 +337,6 @@ parameters_hover = "将鼠标悬停在参数上以了解更多。"
|
||||
"error.default_parse_failed" = "无法解析参数 {parameter} 的默认值"
|
||||
"error.missing_argument" = "缺少参数 \"{parameter}\" ({type})"
|
||||
"error.execution_failed" = "执行命令 {command} 失败: {reason}"
|
||||
usage = "用法: {usage}"
|
||||
|
||||
[iris.command]
|
||||
permission_denied = "你没有权限 '{permission}'。"
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
"language.editor.title" = "{plugin} › {section}"
|
||||
# === Iris language guide ===
|
||||
# Iris - zh_TW
|
||||
#
|
||||
@@ -336,7 +337,6 @@ parameters_hover = "將滑鼠懸停在參數上以瞭解更多。"
|
||||
"error.default_parse_failed" = "無法解析參數 {parameter} 的預設值"
|
||||
"error.missing_argument" = "缺少參數 \"{parameter}\" ({type})"
|
||||
"error.execution_failed" = "執行指令 {command} 失敗: {reason}"
|
||||
usage = "用法: {usage}"
|
||||
|
||||
[iris.command]
|
||||
permission_denied = "你沒有權限 '{permission}'。"
|
||||
|
||||
@@ -5,6 +5,7 @@ import art.arcane.iris.localization.IrisMessages;
|
||||
import art.arcane.iris.testsupport.Await;
|
||||
import art.arcane.volmlib.util.hotload.ConfigHotloadEngine;
|
||||
import art.arcane.volmlib.util.localization.MessageArgument;
|
||||
import art.arcane.volmlib.util.localization.LanguageAudience;
|
||||
import java.time.Duration;
|
||||
import org.junit.After;
|
||||
import org.junit.Before;
|
||||
@@ -17,7 +18,9 @@ import java.io.File;
|
||||
import java.io.PrintStream;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.attribute.FileTime;
|
||||
import java.util.UUID;
|
||||
import java.util.concurrent.CountDownLatch;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
@@ -68,22 +71,23 @@ public class SettingsHotloadWatchTest {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void activeLocaleSnapshotAppliesAndInvalidSnapshotUsesEnglish() {
|
||||
public void activeLocaleSnapshotAppliesAndInvalidSnapshotRetainsValidMessages() {
|
||||
File override = override("en_US");
|
||||
String valid = locale("en_US", "Active {permission}");
|
||||
|
||||
assertTrue(watch.applySnapshot(present(override, valid)));
|
||||
assertEquals("Active " + PERMISSION, permissionMessage());
|
||||
|
||||
assertTrue(watch.applySnapshot(present(override, "{ invalid")));
|
||||
assertEquals("You lack the permission '" + PERMISSION + "'", permissionMessage());
|
||||
assertFalse(watch.applySnapshot(present(override, "{ invalid")));
|
||||
assertEquals("Active " + PERMISSION, permissionMessage());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void activeLocaleDeletionFallsBackToCodeOwnedEnglish() {
|
||||
public void activeLocaleDeletionFallsBackToCodeOwnedEnglish() throws Exception {
|
||||
File override = override("en_US");
|
||||
assertTrue(watch.applySnapshot(present(override, locale("en_US", "Temporary {permission}"))));
|
||||
assertEquals("Temporary " + PERMISSION, permissionMessage());
|
||||
Files.delete(override.toPath());
|
||||
|
||||
assertTrue(watch.applySnapshot(missing(override)));
|
||||
|
||||
@@ -120,13 +124,13 @@ public class SettingsHotloadWatchTest {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void invalidLocaleAllowsItsSettingsSwitchWithEnglishFallback() throws Exception {
|
||||
public void invalidLocaleRejectsItsSettingsSwitchAndRetainsTheCurrentDefault() throws Exception {
|
||||
Files.writeString(override("de_DE").toPath(), "{ invalid", StandardCharsets.UTF_8);
|
||||
|
||||
assertTrue(watch.applySnapshot(present(settingsFile, settings("de_DE"))));
|
||||
assertFalse(watch.applySnapshot(present(settingsFile, settings("de_DE"))));
|
||||
|
||||
assertEquals("de_DE", IrisSettings.get().getGeneral().getLanguage());
|
||||
assertEquals("de_DE", IrisLanguage.activeLocale());
|
||||
assertEquals("en_US", IrisSettings.get().getGeneral().getLanguage());
|
||||
assertEquals("en_US", IrisLanguage.activeLocale());
|
||||
assertEquals("You lack the permission '" + PERMISSION + "'", permissionMessage());
|
||||
}
|
||||
|
||||
@@ -226,7 +230,7 @@ public class SettingsHotloadWatchTest {
|
||||
}
|
||||
|
||||
@Test(timeout = 8_000L)
|
||||
public void oversizedActiveLanguageReportsOnceAndUsesEnglish() throws Exception {
|
||||
public void oversizedActiveLanguageReportsOnceAndRetainsValidMessages() throws Exception {
|
||||
File override = override("en_US");
|
||||
assertTrue(watch.applySnapshot(present(override, locale("en_US", "Active {permission}"))));
|
||||
byte[] oversized = new byte[2 * 1024 * 1024 + 1];
|
||||
@@ -238,11 +242,11 @@ public class SettingsHotloadWatchTest {
|
||||
diagnostic,
|
||||
"Failed to read watched Iris file " + override.getAbsolutePath()
|
||||
));
|
||||
assertEquals("You lack the permission '" + PERMISSION + "'", permissionMessage());
|
||||
assertEquals("Active " + PERMISSION, permissionMessage());
|
||||
}
|
||||
|
||||
@Test(timeout = 8_000L)
|
||||
public void malformedUtf8FailureIsDeduplicatedAndUsesEnglish() throws Exception {
|
||||
public void malformedUtf8FailureIsDeduplicatedAndRetainsValidMessages() throws Exception {
|
||||
File override = override("en_US");
|
||||
assertTrue(watch.applySnapshot(present(override, locale("en_US", "Active {permission}"))));
|
||||
Files.write(override.toPath(), new byte[]{(byte) 0xC3, 0x28});
|
||||
@@ -253,7 +257,74 @@ public class SettingsHotloadWatchTest {
|
||||
diagnostic,
|
||||
"Failed to read watched Iris file " + override.getAbsolutePath()
|
||||
));
|
||||
assertEquals("You lack the permission '" + PERMISSION + "'", permissionMessage());
|
||||
assertEquals("Active " + PERMISSION, permissionMessage());
|
||||
}
|
||||
|
||||
@Test(timeout = 30_000L)
|
||||
public void personalLanguageWatcherRetainsMalformedFilesAndHandlesDeletionAndRestoration() throws Exception {
|
||||
File personal = override("de_DE");
|
||||
Files.writeString(personal.toPath(), locale("de_DE", "Personal {permission}"));
|
||||
UUID player = UUID.randomUUID();
|
||||
IrisLanguage.start();
|
||||
try {
|
||||
IrisLanguage.selections().selectPlayer(player, "de_DE").get(5L, TimeUnit.SECONDS);
|
||||
Path preferences = dataFolder.toPath().resolve("languages/language-preferences.properties");
|
||||
String savedPreferences = Files.readString(preferences);
|
||||
Files.writeString(personal.toPath(), locale("de_DE", "Live {permission}"));
|
||||
awaitPersonalMessage(player, "Live " + PERMISSION);
|
||||
|
||||
Files.writeString(personal.toPath(), "[unterminated");
|
||||
captureErrorsWhilePolling("Could not reload Iris language de_DE", 150L);
|
||||
assertEquals("Live " + PERMISSION, personalMessage(player));
|
||||
|
||||
Files.writeString(override("en_US").toPath(), locale("en_US", "Server {permission}"));
|
||||
awaitPermissionMessage("Server " + PERMISSION);
|
||||
assertEquals("Live " + PERMISSION, personalMessage(player));
|
||||
assertEquals("[unterminated", Files.readString(personal.toPath()));
|
||||
|
||||
String partial = "[iris.command]\nunknown = 'Partial'\n";
|
||||
Files.writeString(personal.toPath(), partial);
|
||||
awaitPersonalUnknown(player, "Partial");
|
||||
assertEquals("You lack the permission '" + PERMISSION + "'", personalMessage(player));
|
||||
|
||||
Files.delete(personal.toPath());
|
||||
awaitPersonalUnknown(player, "Unknown Iris command");
|
||||
assertFalse(personal.exists());
|
||||
assertEquals("de_DE", IrisLanguage.selections().playerLocale(player).orElseThrow());
|
||||
assertEquals(savedPreferences, Files.readString(preferences));
|
||||
assertEquals("en_US", IrisLanguage.activeLocale());
|
||||
assertTrue(IrisLanguage.reload(dataFolder, "en_US"));
|
||||
assertEquals("Unknown Iris command", personalUnknown(player));
|
||||
assertFalse(personal.exists());
|
||||
|
||||
Files.writeString(personal.toPath(), partial);
|
||||
awaitPersonalUnknown(player, "Partial");
|
||||
assertEquals(savedPreferences, Files.readString(preferences));
|
||||
} finally {
|
||||
IrisLanguage.shutdown();
|
||||
}
|
||||
}
|
||||
|
||||
private void awaitPersonalMessage(UUID player, String expected) {
|
||||
assertTrue(Await.reached("the personal permission message '" + expected + "'", Duration.ofSeconds(5L), () -> {
|
||||
watch.checkConfigHotload();
|
||||
return expected.equals(personalMessage(player));
|
||||
}));
|
||||
}
|
||||
|
||||
private void awaitPersonalUnknown(UUID player, String expected) {
|
||||
assertTrue(Await.reached("the personal unknown-command message '" + expected + "'", Duration.ofSeconds(5L), () -> {
|
||||
watch.checkConfigHotload();
|
||||
return expected.equals(personalUnknown(player));
|
||||
}));
|
||||
}
|
||||
|
||||
private String personalMessage(UUID player) {
|
||||
return LanguageAudience.call(player, this::permissionMessage);
|
||||
}
|
||||
|
||||
private String personalUnknown(UUID player) {
|
||||
return LanguageAudience.call(player, () -> IrisLanguage.plain(IrisMessages.COMMAND_UNKNOWN));
|
||||
}
|
||||
|
||||
private void awaitPermissionMessage(String expected) {
|
||||
|
||||
@@ -130,7 +130,7 @@ public class IrisLanguageTest {
|
||||
|
||||
private LocaleOverlay loadSourceOverlay(String locale) throws Exception {
|
||||
Path source = ProjectPaths.moduleFile("src/main/resources/languages").resolve(locale + ".toml");
|
||||
return IrisLanguage.parseDownloadedOverlay(source.toString(), locale, Files.readString(source));
|
||||
return IrisLanguage.parseOverlay(source.toString(), locale, Files.readString(source));
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -139,7 +139,7 @@ public class IrisLanguageTest {
|
||||
"iris.command.unknown" = "Unbekannter Iris-Befehl"
|
||||
"iris.modded.help.entry.command.version" = "Version anzeigen"
|
||||
""";
|
||||
LocaleOverlay downloaded = IrisLanguage.parseDownloadedOverlay("download", "de_DE", raw);
|
||||
LocaleOverlay downloaded = IrisLanguage.parseOverlay("download", "de_DE", raw);
|
||||
assertEquals(Set.of(IrisMessages.COMMAND_UNKNOWN.id()), downloaded.values().keySet());
|
||||
assertTrue(LocalizationValidator.validate(IrisLanguage.catalog(), List.of(downloaded)).errors().isEmpty());
|
||||
LocaleOverlay local = IrisLanguage.parseOverlay("language", "de_DE", raw);
|
||||
@@ -151,7 +151,7 @@ public class IrisLanguageTest {
|
||||
|
||||
@Test
|
||||
public void invalidPlaceholdersAreExcludedWithoutDiscardingValidMessages() {
|
||||
LocaleOverlay downloaded = IrisLanguage.parseDownloadedOverlay("download", "de_DE", """
|
||||
LocaleOverlay downloaded = IrisLanguage.parseOverlay("download", "de_DE", """
|
||||
"iris.command.permission_denied" = "Keine Erlaubnis"
|
||||
"iris.command.unknown" = "Unbekannter Iris-Befehl"
|
||||
""");
|
||||
@@ -238,7 +238,7 @@ public class IrisLanguageTest {
|
||||
public void generatedEnglishLanguageRoundTripsTheEntireCatalog() throws Exception {
|
||||
Path english = dataFolder.toPath().resolve("languages/en_US.toml");
|
||||
assertTrue(Files.isRegularFile(english));
|
||||
LocaleOverlay overlay = IrisLanguage.parseDownloadedOverlay(english.toString(), "en_US", Files.readString(english));
|
||||
LocaleOverlay overlay = IrisLanguage.parseOverlay(english.toString(), "en_US", Files.readString(english));
|
||||
assertEquals(IrisLanguage.catalog().ids(), overlay.values().keySet());
|
||||
for (MessageKey key : IrisLanguage.catalog().keys()) {
|
||||
assertEquals(key.id(), key.englishValue(), overlay.value(key.id()));
|
||||
@@ -475,16 +475,33 @@ public class IrisLanguageTest {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void unreadableTomlUsesEnglishWithoutReplacingTheFile() throws Exception {
|
||||
public void unreadableTomlRetainsTheLastValidMessagesWithoutReplacingTheFile() throws Exception {
|
||||
assertTrue(IrisLanguage.reload(dataFolder, "de_DE"));
|
||||
String previous = IrisLanguage.plain(IrisMessages.COMMAND_UNKNOWN);
|
||||
String raw = "[unterminated";
|
||||
File language = writeLanguage("de_DE", raw);
|
||||
|
||||
assertTrue(IrisLanguage.reload(dataFolder, "de_DE"));
|
||||
assertEquals("Unknown Iris command", IrisLanguage.plain(IrisMessages.COMMAND_UNKNOWN));
|
||||
assertFalse(IrisLanguage.reload(dataFolder, "de_DE"));
|
||||
assertEquals(previous, IrisLanguage.plain(IrisMessages.COMMAND_UNKNOWN));
|
||||
assertEquals(raw, Files.readString(language.toPath()));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void malformedStartupLanguageUsesBuiltInEnglishAndKeepsSelectionsAvailable() throws Exception {
|
||||
IrisLanguage.shutdown();
|
||||
String raw = "[unterminated";
|
||||
File language = writeLanguage("en_US", raw);
|
||||
try {
|
||||
assertTrue(IrisLanguage.reload(dataFolder, "en_US"));
|
||||
IrisLanguage.start();
|
||||
assertNotNull(IrisLanguage.selections());
|
||||
assertEquals("Unknown Iris command", IrisLanguage.plain(IrisMessages.COMMAND_UNKNOWN));
|
||||
assertEquals(raw, Files.readString(language.toPath()));
|
||||
} finally {
|
||||
IrisLanguage.shutdown();
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void englishEditsSurviveReloadAndMissingEnglishIsCreatedForAnotherLocale() throws Exception {
|
||||
String edited = "\"iris.command.unknown\" = \"My Iris command\"\n";
|
||||
|
||||
Reference in New Issue
Block a user