i18n: localize all MessageBox dialogs

The previous batch only covered XAML strings. The runtime MessageBox
dialogs (errors, confirmations, info popups) stayed hardcoded in
French, breaking immersion for non-FR users.

Added ~15 keys to Strings.cs for the MessageBox bodies and titles:
- error / launch error / confirm / info / patience / language change /
  release notes (titles)
- launch failed / self-update failed / install failed / uninstall
  failed / uninstall confirm with version+folder+size / no release
  notes / fetch failed / clear cache confirm / deactivate license
  (short and detailed) / language restart (bodies)

Some are parametrized (MsgLaunchFailed(detail), MsgUninstallConfirm
(version, folder, size)) so the localized strings interpolate the
runtime values cleanly across all 5 languages.

Replaced every MessageBox.Show in MainViewModel, SettingsViewModel
and LicenseDetailsDialog.xaml.cs to use these keys. The "Échec : "
prefix in the cache-clear error was dropped — the error message alone
is sufficient with Strings.MsgBoxError as the dialog title.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-05-02 13:56:52 +02:00
parent b9ce591d22
commit 0d4f126e3a
4 changed files with 133 additions and 26 deletions

View File

@@ -12,6 +12,7 @@ using PSLauncher.Core.Configuration;
using PSLauncher.Core.Downloads; using PSLauncher.Core.Downloads;
using PSLauncher.Core.Installations; using PSLauncher.Core.Installations;
using PSLauncher.Core.Licensing; using PSLauncher.Core.Licensing;
using PSLauncher.Core.Localization;
using PSLauncher.Core.Manifests; using PSLauncher.Core.Manifests;
using PSLauncher.Core.Process; using PSLauncher.Core.Process;
using PSLauncher.Core.Updates; using PSLauncher.Core.Updates;
@@ -338,8 +339,8 @@ public sealed partial class MainViewModel : ObservableObject
catch (Exception ex) catch (Exception ex)
{ {
_logger.LogError(ex, "Self-update failed"); _logger.LogError(ex, "Self-update failed");
MessageBox.Show($"Mise à jour du launcher échouée :\n\n{ex.Message}", MessageBox.Show(Strings.MsgSelfUpdateFailed(ex.Message),
"Erreur", MessageBoxButton.OK, MessageBoxImage.Error); Strings.MsgBoxError, MessageBoxButton.OK, MessageBoxImage.Error);
StatusMessage = $"Erreur self-update : {ex.Message}"; StatusMessage = $"Erreur self-update : {ex.Message}";
IsBusy = false; IsBusy = false;
} }
@@ -463,15 +464,15 @@ public sealed partial class MainViewModel : ObservableObject
catch (Exception ex) catch (Exception ex)
{ {
_logger.LogError(ex, "Launch failed"); _logger.LogError(ex, "Launch failed");
MessageBox.Show($"Impossible de lancer la version :\n\n{ex.Message}", MessageBox.Show(Strings.MsgLaunchFailed(ex.Message),
"Erreur de lancement", MessageBoxButton.OK, MessageBoxImage.Error); Strings.MsgBoxLaunchError, MessageBoxButton.OK, MessageBoxImage.Error);
} }
} }
private async Task InstallVersionAsync(VersionRowViewModel row) private async Task InstallVersionAsync(VersionRowViewModel row)
{ {
if (row.Remote is null) return; if (row.Remote is null) return;
if (IsBusy) { MessageBox.Show("Une autre opération est déjà en cours.", "Patience", MessageBoxButton.OK, MessageBoxImage.Information); return; } if (IsBusy) { MessageBox.Show(Strings.MsgBusy, Strings.MsgBoxPatience, MessageBoxButton.OK, MessageBoxImage.Information); return; }
IsBusy = true; IsBusy = true;
_activeRow = row; _activeRow = row;
@@ -555,8 +556,8 @@ public sealed partial class MainViewModel : ObservableObject
row.ProgressDetail = null; row.ProgressDetail = null;
row.ProgressPercent = 0; row.ProgressPercent = 0;
MessageBox.Show( MessageBox.Show(
$"Le téléchargement ou l'installation a échoué :\n\n{ex.Message}", Strings.MsgInstallFailed(ex.Message),
"Erreur", MessageBoxButton.OK, MessageBoxImage.Error); Strings.MsgBoxError, MessageBoxButton.OK, MessageBoxImage.Error);
StatusMessage = $"Erreur : {ex.Message}"; StatusMessage = $"Erreur : {ex.Message}";
_toastService.ShowError($"Échec v{row.Version}", ex.Message); _toastService.ShowError($"Échec v{row.Version}", ex.Message);
} }
@@ -575,13 +576,13 @@ public sealed partial class MainViewModel : ObservableObject
var sizeStr = FormatSize(row.SizeBytes); var sizeStr = FormatSize(row.SizeBytes);
var confirm = MessageBox.Show( var confirm = MessageBox.Show(
$"Supprimer la version v{row.Version} ?\n\nDossier : {row.FolderPath}\nLibérera {sizeStr}.\n\nCette action est irréversible.", Strings.MsgUninstallConfirm(row.Version, row.FolderPath, sizeStr),
"Confirmer la suppression", Strings.MsgBoxConfirm,
MessageBoxButton.YesNo, MessageBoxImage.Warning, MessageBoxResult.No); MessageBoxButton.YesNo, MessageBoxImage.Warning, MessageBoxResult.No);
if (confirm != MessageBoxResult.Yes) return; if (confirm != MessageBoxResult.Yes) return;
if (IsBusy) { MessageBox.Show("Une autre opération est déjà en cours.", "Patience", MessageBoxButton.OK, MessageBoxImage.Information); return; } if (IsBusy) { MessageBox.Show(Strings.MsgBusy, Strings.MsgBoxPatience, MessageBoxButton.OK, MessageBoxImage.Information); return; }
IsBusy = true; IsBusy = true;
row.State = VersionRowState.Uninstalling; row.State = VersionRowState.Uninstalling;
@@ -596,7 +597,7 @@ public sealed partial class MainViewModel : ObservableObject
{ {
_logger.LogError(ex, "Uninstall failed for {Version}", row.Version); _logger.LogError(ex, "Uninstall failed for {Version}", row.Version);
row.State = VersionRowState.InstalledIdle; row.State = VersionRowState.InstalledIdle;
MessageBox.Show($"Suppression échouée : {ex.Message}", "Erreur", MessageBox.Show(Strings.MsgUninstallFailed(ex.Message), Strings.MsgBoxError,
MessageBoxButton.OK, MessageBoxImage.Error); MessageBoxButton.OK, MessageBoxImage.Error);
StatusMessage = $"Erreur : {ex.Message}"; StatusMessage = $"Erreur : {ex.Message}";
} }
@@ -611,8 +612,8 @@ public sealed partial class MainViewModel : ObservableObject
var url = row.Remote?.ReleaseNotesUrl; var url = row.Remote?.ReleaseNotesUrl;
if (string.IsNullOrEmpty(url)) if (string.IsNullOrEmpty(url))
{ {
MessageBox.Show("Aucune release note disponible pour cette version.", MessageBox.Show(Strings.MsgNoReleaseNotes,
"Release notes", MessageBoxButton.OK, MessageBoxImage.Information); Strings.MsgBoxReleaseNotes, MessageBoxButton.OK, MessageBoxImage.Information);
return; return;
} }
string notes; string notes;
@@ -620,8 +621,8 @@ public sealed partial class MainViewModel : ObservableObject
catch (Exception ex) catch (Exception ex)
{ {
_logger.LogWarning(ex, "Failed to fetch release notes"); _logger.LogWarning(ex, "Failed to fetch release notes");
MessageBox.Show($"Impossible de récupérer les release notes :\n{ex.Message}", MessageBox.Show(Strings.MsgReleaseNotesFetchFailed(ex.Message),
"Erreur", MessageBoxButton.OK, MessageBoxImage.Warning); Strings.MsgBoxError, MessageBoxButton.OK, MessageBoxImage.Warning);
return; return;
} }

View File

@@ -9,6 +9,7 @@ using Microsoft.Extensions.Logging;
using PSLauncher.Core.Configuration; using PSLauncher.Core.Configuration;
using PSLauncher.Core.Downloads; using PSLauncher.Core.Downloads;
using PSLauncher.Core.Licensing; using PSLauncher.Core.Licensing;
using PSLauncher.Core.Localization;
using PSLauncher.Models; using PSLauncher.Models;
namespace PSLauncher.App.ViewModels; namespace PSLauncher.App.ViewModels;
@@ -185,8 +186,8 @@ public sealed partial class SettingsViewModel : ObservableObject
private void ClearCache() private void ClearCache()
{ {
var confirm = MessageBox.Show( var confirm = MessageBox.Show(
"Vider le cache de téléchargements ? Les téléchargements en cours/pause seront perdus.", Strings.MsgClearCacheConfirm,
"Confirmer", Strings.MsgBoxConfirm,
MessageBoxButton.YesNo, MessageBoxImage.Warning, MessageBoxResult.No); MessageBoxButton.YesNo, MessageBoxImage.Warning, MessageBoxResult.No);
if (confirm != MessageBoxResult.Yes) return; if (confirm != MessageBoxResult.Yes) return;
@@ -201,7 +202,7 @@ public sealed partial class SettingsViewModel : ObservableObject
catch (Exception ex) catch (Exception ex)
{ {
_logger.LogError(ex, "ClearCache failed"); _logger.LogError(ex, "ClearCache failed");
MessageBox.Show("Échec : " + ex.Message, "Erreur", MessageBoxButton.OK, MessageBoxImage.Error); MessageBox.Show(ex.Message, Strings.MsgBoxError, MessageBoxButton.OK, MessageBoxImage.Error);
} }
} }
@@ -232,8 +233,8 @@ public sealed partial class SettingsViewModel : ObservableObject
// la nouvelle langue. Plutôt que de demander à l'utilisateur de fermer/rouvrir, // la nouvelle langue. Plutôt que de demander à l'utilisateur de fermer/rouvrir,
// on spawne un cmd qui attend la fermeture puis relance. // on spawne un cmd qui attend la fermeture puis relance.
var ok = MessageBox.Show( var ok = MessageBox.Show(
"Le launcher va redémarrer pour appliquer la nouvelle langue.", Strings.MsgLanguageRestart,
"Changement de langue", Strings.MsgBoxLanguageChange,
MessageBoxButton.OKCancel, MessageBoxImage.Information, MessageBoxResult.OK); MessageBoxButton.OKCancel, MessageBoxImage.Information, MessageBoxResult.OK);
if (ok == MessageBoxResult.OK) if (ok == MessageBoxResult.OK)
{ {
@@ -263,8 +264,8 @@ public sealed partial class SettingsViewModel : ObservableObject
private void DeactivateLicense() private void DeactivateLicense()
{ {
var confirm = MessageBox.Show( var confirm = MessageBox.Show(
"Désactiver la license sur cette machine ?\nTu pourras la réactiver avec ta clé.", Strings.MsgDeactivateLicenseConfirm,
"Confirmer", Strings.MsgBoxConfirm,
MessageBoxButton.YesNo, MessageBoxImage.Question, MessageBoxResult.No); MessageBoxButton.YesNo, MessageBoxImage.Question, MessageBoxResult.No);
if (confirm != MessageBoxResult.Yes) return; if (confirm != MessageBoxResult.Yes) return;
_licenseService.Clear(); _licenseService.Clear();

View File

@@ -1,6 +1,7 @@
using System.Windows; using System.Windows;
using System.Windows.Media; using System.Windows.Media;
using PSLauncher.Core.Licensing; using PSLauncher.Core.Licensing;
using PSLauncher.Core.Localization;
using PSLauncher.Models; using PSLauncher.Models;
namespace PSLauncher.App.Views; namespace PSLauncher.App.Views;
@@ -66,10 +67,8 @@ public partial class LicenseDetailsDialog : Window
private void OnDeactivate(object sender, RoutedEventArgs e) private void OnDeactivate(object sender, RoutedEventArgs e)
{ {
var confirm = MessageBox.Show( var confirm = MessageBox.Show(
"Désactiver la license sur cette machine ?\n\n" + Strings.MsgDeactivateLicenseDetailedConfirm,
"Tu pourras la réactiver plus tard avec ta clé. " + Strings.MsgBoxConfirm,
"Les versions déjà installées restent utilisables.",
"Confirmer la désactivation",
MessageBoxButton.YesNo, MessageBoxImage.Question, MessageBoxResult.No); MessageBoxButton.YesNo, MessageBoxImage.Question, MessageBoxResult.No);
if (confirm != MessageBoxResult.Yes) return; if (confirm != MessageBoxResult.Yes) return;
_licenseService.Clear(); _licenseService.Clear();

View File

@@ -152,4 +152,110 @@ public static class Strings
// ==================== UPDATE ==================== // ==================== UPDATE ====================
public static string UpdateAvailableTitle => T("Mise à jour disponible", "Update available", "有可用更新", "มีการอัปเดต", "تحديث متاح"); public static string UpdateAvailableTitle => T("Mise à jour disponible", "Update available", "有可用更新", "มีการอัปเดต", "تحديث متاح");
public static string UpdateDownload => T("⬇ Télécharger", "⬇ Download", "⬇ 下载", "⬇ ดาวน์โหลด", "⬇ تنزيل"); public static string UpdateDownload => T("⬇ Télécharger", "⬇ Download", "⬇ 下载", "⬇ ดาวน์โหลด", "⬇ تنزيل");
// ==================== MESSAGEBOX TITLES ====================
public static string MsgBoxError => T("Erreur", "Error", "错误", "ข้อผิดพลาด", "خطأ");
public static string MsgBoxLaunchError => T("Erreur de lancement", "Launch error", "启动错误", "เปิดใช้งานล้มเหลว", "خطأ في التشغيل");
public static string MsgBoxConfirm => T("Confirmer", "Confirm", "确认", "ยืนยัน", "تأكيد");
public static string MsgBoxInfo => T("Information", "Information", "信息", "ข้อมูล", "معلومة");
public static string MsgBoxPatience => T("Patience", "Please wait", "请稍候", "โปรดรอ", "يرجى الانتظار");
public static string MsgBoxLanguageChange => T("Changement de langue", "Language change", "更改语言", "เปลี่ยนภาษา", "تغيير اللغة");
public static string MsgBoxReleaseNotes => T("Release notes", "Release notes", "版本说明", "บันทึกการเปลี่ยนแปลง", "ملاحظات الإصدار");
// ==================== MESSAGEBOX MESSAGES ====================
public static string MsgBusy => T(
"Une autre opération est déjà en cours.",
"Another operation is already in progress.",
"另一个操作正在进行中。",
"มีการดำเนินการอื่นอยู่แล้ว",
"هناك عملية أخرى قيد التنفيذ بالفعل."
);
public static string MsgLaunchFailed(string detail) => T(
$"Impossible de lancer la version :\n\n{detail}",
$"Could not launch the version:\n\n{detail}",
$"无法启动该版本:\n\n{detail}",
$"ไม่สามารถเปิดเวอร์ชันนี้ได้:\n\n{detail}",
$"تعذر تشغيل النسخة:\n\n{detail}"
);
public static string MsgSelfUpdateFailed(string detail) => T(
$"Mise à jour du launcher échouée :\n\n{detail}",
$"Launcher update failed:\n\n{detail}",
$"启动器更新失败:\n\n{detail}",
$"การอัปเดต Launcher ล้มเหลว:\n\n{detail}",
$"فشل تحديث المُشغِّل:\n\n{detail}"
);
public static string MsgInstallFailed(string detail) => T(
$"Le téléchargement ou l'installation a échoué :\n\n{detail}",
$"Download or installation failed:\n\n{detail}",
$"下载或安装失败:\n\n{detail}",
$"การดาวน์โหลดหรือติดตั้งล้มเหลว:\n\n{detail}",
$"فشل التنزيل أو التثبيت:\n\n{detail}"
);
public static string MsgUninstallFailed(string detail) => T(
$"Suppression échouée : {detail}",
$"Uninstall failed: {detail}",
$"卸载失败:{detail}",
$"การถอนการติดตั้งล้มเหลว: {detail}",
$"فشل الإزالة: {detail}"
);
public static string MsgUninstallConfirm(string version, string folder, string size) => T(
$"Supprimer la version v{version} ?\n\nDossier : {folder}\nLibérera {size}.\n\nCette action est irréversible.",
$"Uninstall version v{version}?\n\nFolder: {folder}\nWill free {size}.\n\nThis action is irreversible.",
$"卸载版本 v{version}\n\n文件夹{folder}\n将释放 {size}。\n\n此操作不可逆。",
$"ถอนการติดตั้งเวอร์ชัน v{version}?\n\nโฟลเดอร์: {folder}\nจะคืนพื้นที่ {size}\n\nการกระทำนี้ไม่สามารถย้อนกลับได้",
$"إلغاء تثبيت النسخة v{version}؟\n\nالمجلد: {folder}\nسيتم تحرير {size}.\n\nهذا الإجراء لا رجعة فيه."
);
public static string MsgNoReleaseNotes => T(
"Aucune release note disponible pour cette version.",
"No release notes available for this version.",
"此版本没有可用的版本说明。",
"ไม่มีบันทึกการเปลี่ยนแปลงสำหรับเวอร์ชันนี้",
"لا توجد ملاحظات إصدار متاحة لهذه النسخة."
);
public static string MsgReleaseNotesFetchFailed(string detail) => T(
$"Impossible de récupérer les release notes :\n{detail}",
$"Could not fetch release notes:\n{detail}",
$"无法获取版本说明:\n{detail}",
$"ไม่สามารถดึงบันทึกการเปลี่ยนแปลงได้:\n{detail}",
$"تعذر جلب ملاحظات الإصدار:\n{detail}"
);
public static string MsgClearCacheConfirm => T(
"Vider le cache de téléchargements ? Les téléchargements en cours/pause seront perdus.",
"Clear the download cache? In-progress / paused downloads will be lost.",
"清空下载缓存?正在进行或暂停的下载将丢失。",
"ล้างแคชดาวน์โหลด? การดาวน์โหลดที่กำลังดำเนินการ/หยุดชั่วคราวจะสูญหาย",
"مسح ذاكرة التخزين المؤقت للتنزيلات؟ ستفقد التنزيلات قيد التقدم أو الموقوفة مؤقتًا."
);
public static string MsgDeactivateLicenseConfirm => T(
"Désactiver la license sur cette machine ?\nTu pourras la réactiver avec ta clé.",
"Deactivate the license on this machine?\nYou can reactivate it later with your key.",
"在此机器上停用许可证?\n您可以稍后用密钥重新激活。",
"ปิดใช้งานใบอนุญาตบนเครื่องนี้?\nคุณสามารถเปิดใช้งานใหม่ในภายหลังด้วยคีย์ของคุณ",
"إلغاء تنشيط الترخيص على هذا الجهاز؟\nيمكنك إعادة تنشيطه لاحقًا بمفتاحك."
);
public static string MsgDeactivateLicenseDetailedConfirm => T(
"Désactiver la license sur cette machine ?\n\nTu pourras la réactiver plus tard avec ta clé. Les versions déjà installées restent utilisables.",
"Deactivate the license on this machine?\n\nYou can reactivate it later with your key. Already-installed versions remain usable.",
"在此机器上停用许可证?\n\n您可以稍后用密钥重新激活。已安装的版本仍可使用。",
"ปิดใช้งานใบอนุญาตบนเครื่องนี้?\n\nคุณสามารถเปิดใช้งานใหม่ในภายหลังด้วยคีย์ของคุณ เวอร์ชันที่ติดตั้งแล้วยังคงใช้งานได้",
"إلغاء تنشيط الترخيص على هذا الجهاز؟\n\nيمكنك إعادة تنشيطه لاحقًا بمفتاحك. تظل النسخ المثبتة قابلة للاستخدام."
);
public static string MsgLanguageRestart => T(
"Le launcher va redémarrer pour appliquer la nouvelle langue.",
"The launcher will restart to apply the new language.",
"启动器将重启以应用新语言。",
"Launcher จะรีสตาร์ทเพื่อใช้ภาษาใหม่",
"سيتم إعادة تشغيل المُشغِّل لتطبيق اللغة الجديدة."
);
} }