v1.0.0 🎉 — Production release. i18n Espagnol + Allemand (launcher + emails).
Première release MAJEURE après ~30 itérations 0.x. Le système est complet et stable pour la production : distribution PROSERVE multi-channels, install launcher cross-fleet, monitoring santé VR, sécurité (signatures Ed25519, HMAC URLs présignées, settings lock per-license, RFC1918 filter cache LAN). i18n nouvelles langues (Espagnol + Allemand) : - Strings.cs : signature T() étendue avec 2 params optionnels (es?/de?) en plus des 5 obligatoires. Fallback automatique sur l'anglais quand non traduit → infrastructure prête immédiatement, traduction progressive sur les ~317 strings du launcher selon les besoins terrain. - ~30 strings critiques traduites maintenant (top bar, body, status badges, actions principales Launch/Install/Cancel/Yes/No, MessageBox titles). - Available[] inclut es/Español + de/Deutsch ; auto-détection Windows pour es-* / de-* funcionne au premier launch. - Init() whitelist mise à jour. Emails release announce localisés par license : - Migration 005 : ajout `language VARCHAR(8) NULL` sur licenses (whitelist fr/en/es/de/zh/th/ar, NULL = fallback English). - getEmailStrings() PHP étendu : dictionnaire 7 langues × 14 strings. Traduction complète es + de (gérable : 28 nouvelles strings, vs 634 pour traduire le launcher en intégralité). - notify_release boucle par license : récupère language + locale fallback 'en' + render avec la bonne locale → chaque destinataire reçoit son email dans la langue de SA license, indépendamment des autres clients. - Subject localisé aussi (« PROSERVE v1.5.4 ya está disponible » / « PROSERVE v1.5.4 ist jetzt verfügbar »). - Release notes body reste TOUJOURS en anglais (évite d'avoir à maintenir N traductions du Markdown des notes). - Direction RTL préservée pour l'arabe (dir="rtl" sur <html> + <table>). Admin UI : dropdown langue dans la modal Contacts d'une license + whitelist backend (set_contact_emails accepte fr/en/es/de/zh/th/ar). Bumps : 0.29.10 → 1.0.0 — milestone V1, premier release majeur en prod. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -18,9 +18,9 @@
|
||||
<Product>PROSERVE Launcher</Product>
|
||||
<Copyright>© 2026 ASTERION VR — All rights reserved</Copyright>
|
||||
<RootNamespace>PSLauncher.App</RootNamespace>
|
||||
<Version>0.29.10</Version>
|
||||
<AssemblyVersion>0.29.10.0</AssemblyVersion>
|
||||
<FileVersion>0.29.10.0</FileVersion>
|
||||
<Version>1.0.0</Version>
|
||||
<AssemblyVersion>1.0.0.0</AssemblyVersion>
|
||||
<FileVersion>1.0.0.0</FileVersion>
|
||||
|
||||
<!-- Single-file self-contained publish profile (used by `dotnet publish`) -->
|
||||
<PublishSingleFile>true</PublishSingleFile>
|
||||
|
||||
@@ -8,7 +8,11 @@ namespace PSLauncher.Core.Localization;
|
||||
/// session. Tout changement de langue nécessite un redémarrage du launcher
|
||||
/// (les bindings XAML x:Static ne refresh pas dynamiquement).
|
||||
///
|
||||
/// Langues supportées : fr (par défaut), en, zh (Simplified), th, ar.
|
||||
/// Langues supportées :
|
||||
/// • fr (par défaut), en, zh (Simplified), th, ar : traduction complète historique
|
||||
/// • es, de : ajoutées en v0.29.11, traduction progressive — les strings non encore
|
||||
/// traduites tombent automatiquement sur l'anglais via T() (cf. fallback dans
|
||||
/// <see cref="T(string,string,string,string,string,string?,string?)"/>).
|
||||
/// Pour Arabic, FlowDirection doit aussi être basculé en RTL côté Window.
|
||||
/// </summary>
|
||||
public static class Strings
|
||||
@@ -27,6 +31,8 @@ public static class Strings
|
||||
("auto", "Automatique (langue du système)"),
|
||||
("fr", "Français"),
|
||||
("en", "English"),
|
||||
("es", "Español"),
|
||||
("de", "Deutsch"),
|
||||
("zh", "中文"),
|
||||
("th", "ไทย"),
|
||||
("ar", "العربية"),
|
||||
@@ -39,7 +45,7 @@ public static class Strings
|
||||
/// </summary>
|
||||
public static void Init(string? configValue)
|
||||
{
|
||||
var supported = new[] { "fr", "en", "zh", "th", "ar" };
|
||||
var supported = new[] { "fr", "en", "es", "de", "zh", "th", "ar" };
|
||||
var v = configValue?.Trim().ToLowerInvariant() ?? "auto";
|
||||
|
||||
if (v == "auto" || string.IsNullOrEmpty(v) || Array.IndexOf(supported, v) < 0)
|
||||
@@ -62,9 +68,21 @@ public static class Strings
|
||||
catch { /* culture inconnue, on garde le défaut */ }
|
||||
}
|
||||
|
||||
private static string T(string fr, string en, string zh, string th, string ar) => _lang switch
|
||||
/// <summary>
|
||||
/// Helper de localisation. Les 5 premiers paramètres (fr/en/zh/th/ar) sont
|
||||
/// requis = chaque string DOIT exister dans ces langues (traduction historique
|
||||
/// complète). Les 2 derniers (es, de) sont OPTIONNELS — si non fournis, on
|
||||
/// retombe sur l'anglais. Stratégie progressive : on traduit en es/de
|
||||
/// uniquement les strings les plus visibles (top bar, actions principales,
|
||||
/// états licenses, errors common) ; le reste reste en anglais pour les
|
||||
/// utilisateurs es/de tant qu'on n'a pas traduit. Mieux qu'un crash ou
|
||||
/// une chaîne vide, et l'anglais est globalement lisible par ces locuteurs.
|
||||
/// </summary>
|
||||
private static string T(string fr, string en, string zh, string th, string ar, string? es = null, string? de = null) => _lang switch
|
||||
{
|
||||
"en" => en,
|
||||
"es" => es ?? en, // fallback English si pas encore traduit
|
||||
"de" => de ?? en, // idem
|
||||
"zh" => zh,
|
||||
"th" => th,
|
||||
"ar" => ar,
|
||||
@@ -72,34 +90,36 @@ public static class Strings
|
||||
};
|
||||
|
||||
// ==================== TOP BAR ====================
|
||||
public static string TopBarCheckUpdates => T("🔄 Vérifier les MAJ", "🔄 Check for updates", "🔄 检查更新", "🔄 ตรวจสอบการอัปเดต", "🔄 التحقق من التحديثات");
|
||||
public static string TopBarSettings => T("⚙ Paramètres", "⚙ Settings", "⚙ 设置", "⚙ ตั้งค่า", "⚙ الإعدادات");
|
||||
public static string TopBarCheckUpdates => T("🔄 Vérifier les MAJ", "🔄 Check for updates", "🔄 检查更新", "🔄 ตรวจสอบการอัปเดต", "🔄 التحقق من التحديثات", "🔄 Buscar actualizaciones", "🔄 Updates suchen");
|
||||
public static string TopBarSettings => T("⚙ Paramètres", "⚙ Settings", "⚙ 设置", "⚙ ตั้งค่า", "⚙ الإعدادات", "⚙ Ajustes", "⚙ Einstellungen");
|
||||
|
||||
// ==================== BODY ====================
|
||||
public static string BodyVersions => T("Versions", "Versions", "版本", "เวอร์ชัน", "النسخ");
|
||||
public static string BodyVersions => T("Versions", "Versions", "版本", "เวอร์ชัน", "النسخ", "Versiones", "Versionen");
|
||||
public static string BodyVersionsHint => T(
|
||||
"Lance, installe ou supprime une version. Les versions installées et celles disponibles sur le serveur cohabitent.",
|
||||
"Launch, install or remove a version. Installed and available versions can coexist.",
|
||||
"启动、安装或删除版本。已安装的版本与服务器上可用的版本可以共存。",
|
||||
"เปิด ติดตั้ง หรือลบเวอร์ชัน เวอร์ชันที่ติดตั้งและที่มีในเซิร์ฟเวอร์อยู่ร่วมกันได้",
|
||||
"تشغيل أو تثبيت أو إزالة نسخة. النسخ المثبتة والمتاحة على الخادم يمكن أن تتعايش."
|
||||
"تشغيل أو تثبيت أو إزالة نسخة. النسخ المثبتة والمتاحة على الخادم يمكن أن تتعايش.",
|
||||
"Inicia, instala o elimina una versión. Las versiones instaladas y las disponibles en el servidor pueden coexistir.",
|
||||
"Starte, installiere oder entferne eine Version. Installierte und verfügbare Versionen können koexistieren."
|
||||
);
|
||||
|
||||
public static string FeaturedCurrent => T("VERSION COURANTE", "CURRENT VERSION", "当前版本", "เวอร์ชันปัจจุบัน", "النسخة الحالية");
|
||||
public static string SectionOther => T("AUTRES VERSIONS", "OTHER VERSIONS", "其他版本", "เวอร์ชันอื่นๆ", "نسخ أخرى");
|
||||
public static string ReleasedOn => T("Sortie le ", "Released ", "发布于 ", "เผยแพร่เมื่อ ", "صدر في ");
|
||||
public static string FeaturedCurrent => T("VERSION COURANTE", "CURRENT VERSION", "当前版本", "เวอร์ชันปัจจุบัน", "النسخة الحالية", "VERSIÓN ACTUAL", "AKTUELLE VERSION");
|
||||
public static string SectionOther => T("AUTRES VERSIONS", "OTHER VERSIONS", "其他版本", "เวอร์ชันอื่นๆ", "نسخ أخرى", "OTRAS VERSIONES", "ANDERE VERSIONEN");
|
||||
public static string ReleasedOn => T("Sortie le ", "Released ", "发布于 ", "เผยแพร่เมื่อ ", "صدر في ", "Publicado el ", "Veröffentlicht am ");
|
||||
|
||||
// ==================== STATUS BADGES ====================
|
||||
public static string StatusInstalled => T("● Installée", "● Installed", "● 已安装", "● ติดตั้งแล้ว", "● مثبتة");
|
||||
public static string StatusAvailable => T("○ Disponible", "○ Available", "○ 可用", "○ พร้อมใช้งาน", "○ متاحة");
|
||||
public static string StatusDownloading => T("⬇ Téléchargement…", "⬇ Downloading…", "⬇ 下载中…", "⬇ กำลังดาวน์โหลด…", "⬇ جارٍ التنزيل…");
|
||||
public static string StatusInstalling => T("📦 Installation…", "📦 Installing…", "📦 安装中…", "📦 กำลังติดตั้ง…", "📦 جارٍ التثبيت…");
|
||||
public static string StatusUninstalling => T("🗑 Suppression…", "🗑 Uninstalling…", "🗑 卸载中…", "🗑 กำลังถอนการติดตั้ง…", "🗑 جارٍ الإزالة…");
|
||||
public static string StatusVerifying => T("🔍 Vérification…", "🔍 Verifying…", "🔍 校验中…", "🔍 กำลังตรวจสอบ…", "🔍 جارٍ التحقق…");
|
||||
public static string StatusInstalled => T("● Installée", "● Installed", "● 已安装", "● ติดตั้งแล้ว", "● مثبتة", "● Instalada", "● Installiert");
|
||||
public static string StatusAvailable => T("○ Disponible", "○ Available", "○ 可用", "○ พร้อมใช้งาน", "○ متاحة", "○ Disponible", "○ Verfügbar");
|
||||
public static string StatusDownloading => T("⬇ Téléchargement…", "⬇ Downloading…", "⬇ 下载中…", "⬇ กำลังดาวน์โหลด…", "⬇ جارٍ التنزيل…", "⬇ Descargando…", "⬇ Wird heruntergeladen…");
|
||||
public static string StatusInstalling => T("📦 Installation…", "📦 Installing…", "📦 安装中…", "📦 กำลังติดตั้ง…", "📦 جارٍ التثبيت…", "📦 Instalando…", "📦 Wird installiert…");
|
||||
public static string StatusUninstalling => T("🗑 Suppression…", "🗑 Uninstalling…", "🗑 卸载中…", "🗑 กำลังถอนการติดตั้ง…", "🗑 جارٍ الإزالة…", "🗑 Eliminando…", "🗑 Wird entfernt…");
|
||||
public static string StatusVerifying => T("🔍 Vérification…", "🔍 Verifying…", "🔍 校验中…", "🔍 กำลังตรวจสอบ…", "🔍 جارٍ التحقق…", "🔍 Verificando…", "🔍 Wird überprüft…");
|
||||
|
||||
// ==================== BETA BADGE ====================
|
||||
/// <summary>Texte affiché dans la pill orange à côté de la version. Court et localisé.</summary>
|
||||
public static string BetaBadgeLabel => T("BÊTA", "BETA", "测试版", "เบต้า", "تجريبي");
|
||||
public static string BetaBadgeLabel => T("BÊTA", "BETA", "测试版", "เบต้า", "تجريبي", "BETA", "BETA");
|
||||
|
||||
/// <summary>Tooltip par défaut quand la version est tag BÊTA mais sans note des testeurs.</summary>
|
||||
public static string BetaBadgeTooltipDefault => T(
|
||||
@@ -120,17 +140,17 @@ public static class Strings
|
||||
);
|
||||
|
||||
// ==================== ACTIONS ====================
|
||||
public static string ActionLaunch => T("▶ Lancer", "▶ Launch", "▶ 启动", "▶ เปิด", "▶ تشغيل");
|
||||
public static string ActionLaunchBig => T("▶ LANCER", "▶ LAUNCH", "▶ 启动", "▶ เปิด", "▶ تشغيل");
|
||||
public static string ActionInstall => T("⬇ Installer", "⬇ Install", "⬇ 安装", "⬇ ติดตั้ง", "⬇ تثبيت");
|
||||
public static string ActionInstallBig => T("⬇ INSTALLER", "⬇ INSTALL", "⬇ 安装", "⬇ ติดตั้ง", "⬇ تثبيت");
|
||||
public static string ActionCancel => T("Annuler", "Cancel", "取消", "ยกเลิก", "إلغاء");
|
||||
public static string ActionOk => T("OK", "OK", "确定", "ตกลง", "موافق");
|
||||
public static string ActionYes => T("Oui", "Yes", "是", "ใช่", "نعم");
|
||||
public static string ActionNo => T("Non", "No", "否", "ไม่", "لا");
|
||||
public static string ActionLater => T("Plus tard", "Later", "稍后", "ภายหลัง", "لاحقاً");
|
||||
public static string ActionSave => T("Enregistrer", "Save", "保存", "บันทึก", "حفظ");
|
||||
public static string ActionClose => T("Fermer", "Close", "关闭", "ปิด", "إغلاق");
|
||||
public static string ActionLaunch => T("▶ Lancer", "▶ Launch", "▶ 启动", "▶ เปิด", "▶ تشغيل", "▶ Iniciar", "▶ Starten");
|
||||
public static string ActionLaunchBig => T("▶ LANCER", "▶ LAUNCH", "▶ 启动", "▶ เปิด", "▶ تشغيل", "▶ INICIAR", "▶ STARTEN");
|
||||
public static string ActionInstall => T("⬇ Installer", "⬇ Install", "⬇ 安装", "⬇ ติดตั้ง", "⬇ تثبيت", "⬇ Instalar", "⬇ Installieren");
|
||||
public static string ActionInstallBig => T("⬇ INSTALLER", "⬇ INSTALL", "⬇ 安装", "⬇ ติดตั้ง", "⬇ تثبيت", "⬇ INSTALAR", "⬇ INSTALLIEREN");
|
||||
public static string ActionCancel => T("Annuler", "Cancel", "取消", "ยกเลิก", "إلغاء", "Cancelar", "Abbrechen");
|
||||
public static string ActionOk => T("OK", "OK", "确定", "ตกลง", "موافق", "OK", "OK");
|
||||
public static string ActionYes => T("Oui", "Yes", "是", "ใช่", "نعم", "Sí", "Ja");
|
||||
public static string ActionNo => T("Non", "No", "否", "ไม่", "لا", "No", "Nein");
|
||||
public static string ActionLater => T("Plus tard", "Later", "稍后", "ภายหลัง", "لاحقاً", "Más tarde", "Später");
|
||||
public static string ActionSave => T("Enregistrer", "Save", "保存", "บันทึก", "حفظ", "Guardar", "Speichern");
|
||||
public static string ActionClose => T("Fermer", "Close", "关闭", "ปิด", "إغلاق", "Cerrar", "Schließen");
|
||||
// Affiché sur le bouton install d'une version dont le minLicenseDate dépasse l'entitlement de la license.
|
||||
// Concrètement : ta license expire le 2024-12-31 mais cette version a été sortie le 2025-03-15 → tu ne peux pas la télécharger.
|
||||
// Tu peux toujours installer/lancer une version antérieure couverte par ta période de license.
|
||||
@@ -270,13 +290,13 @@ public static class Strings
|
||||
public static string LauncherUpdateNow => T("⬇ Mettre à jour", "⬇ Update now", "⬇ 立即更新", "⬇ อัปเดตเดี๋ยวนี้", "⬇ تحديث الآن");
|
||||
|
||||
// ==================== 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", "版本说明", "บันทึกการเปลี่ยนแปลง", "ملاحظات الإصدار");
|
||||
public static string MsgBoxError => T("Erreur", "Error", "错误", "ข้อผิดพลาด", "خطأ", "Error", "Fehler");
|
||||
public static string MsgBoxLaunchError => T("Erreur de lancement", "Launch error", "启动错误", "เปิดใช้งานล้มเหลว", "خطأ في التشغيل", "Error al iniciar", "Startfehler");
|
||||
public static string MsgBoxConfirm => T("Confirmer", "Confirm", "确认", "ยืนยัน", "تأكيد", "Confirmar", "Bestätigen");
|
||||
public static string MsgBoxInfo => T("Information", "Information", "信息", "ข้อมูล", "معلومة", "Información", "Information");
|
||||
public static string MsgBoxPatience => T("Patience", "Please wait", "请稍候", "โปรดรอ", "يرجى الانتظار", "Por favor espera", "Bitte warten");
|
||||
public static string MsgBoxLanguageChange => T("Changement de langue", "Language change", "更改语言", "เปลี่ยนภาษา", "تغيير اللغة", "Cambio de idioma", "Sprachänderung");
|
||||
public static string MsgBoxReleaseNotes => T("Release notes", "Release notes", "版本说明", "บันทึกการเปลี่ยนแปลง", "ملاحظات الإصدار", "Notas de versión", "Versionshinweise");
|
||||
|
||||
// ==================== MESSAGEBOX MESSAGES ====================
|
||||
public static string MsgBusy => T(
|
||||
@@ -860,7 +880,7 @@ public static class Strings
|
||||
);
|
||||
|
||||
// ---- Mode auto (auto-launch + auto-relaunch après exit) ----
|
||||
public static string ActionAuto => T("AUTO", "AUTO", "自动", "อัตโนมัติ", "تلقائي");
|
||||
public static string ActionAuto => T("AUTO", "AUTO", "自动", "อัตโนมัติ", "تلقائي", "AUTO", "AUTO");
|
||||
public static string AutoModeTooltipActive => T(
|
||||
"Cette version est désignée pour le lancement automatique. Clique pour désactiver.",
|
||||
"This version is set for auto-launch. Click to disable.",
|
||||
|
||||
Reference in New Issue
Block a user