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:
Robert Stoneking committed 2026-09-12 13:53:11 -06:00
1 parent bc9b7e86d7
commit e832239730
20 files changed
+142 -55

No files matched your search

@@ -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 -1
View File
@@ -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 -1
View File
@@ -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 -1
View File
@@ -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 -1
View File
@@ -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 -1
View File
@@ -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 -1
View File
@@ -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 -1
View File
@@ -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 -1
View File
@@ -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 -1
View File
@@ -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 -1
View File
@@ -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 -1
View File
@@ -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 -1
View File
@@ -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 -1
View File
@@ -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 -1
View File
@@ -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 -1
View File
@@ -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 -1
View File
@@ -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 -1
View File
@@ -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}'。"