i18n: full pass + culture-aware dates/sizes + Software Update License wording

- Strings.cs : ~80 new keys covering status messages, progress detail
  (DL/extraction/verify), license summary, license details dialog, onboarding
  statuses, settings field labels, restart/cancel/quit confirmations, toasts,
  resume choice, copyright. Strings.FormatSize and Strings.FormatDate /
  FormatLongDate switch units (o/Ko vs B/KB) and pattern (dd/MM/yyyy vs M/d/yyyy
  vs 2026年) by active language.
- License terminology renamed across UI: "License" → "License de mise à jour"
  / "Software Update License" to clarify it gates UPDATES, not Proserve itself.
  Expired/revoked dialogs now spell out "you can still download versions
  released before this date and launch any installed version".
- "🔒 License insuffisante" → "🔒 License de mise à jour requise pour cette
  version" / "Valid update license needed for this version" so users understand
  it's a per-version eligibility check, not a global block.
- All hardcoded FR strings in views (SettingsDialog labels, LicenseDetails
  fields, Onboarding statuses, dialog titles, copyright, window chrome
  tooltips, "Sortie le", etc.) replaced with x:Static loc bindings.
- All FormatSize duplicates (5 places) and date format strings (8 places)
  delegate to Strings helpers — single source of truth for localization.
- Settings dialog: License section moved to the top before Language. It's
  the most important info and conditions what the user can download.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-05-02 19:14:57 +02:00
parent 962f5a8ce0
commit b4f205cbe5
13 changed files with 634 additions and 135 deletions

View File

@@ -1,3 +1,4 @@
using PSLauncher.Core.Localization;
using PSLauncher.Models;
namespace PSLauncher.App.ViewModels;
@@ -11,17 +12,7 @@ public sealed class InstalledVersionViewModel
public string Display => $"v{Model.Version}";
public string Subtitle =>
$"{FormatSize(Model.SizeBytes)} • Installée le {Model.InstalledAt.ToLocalTime():dd/MM/yyyy}";
private static string FormatSize(long bytes)
{
if (bytes <= 0) return "—";
string[] units = { "o", "Ko", "Mo", "Go", "To" };
double v = bytes;
int u = 0;
while (v >= 1024 && u < units.Length - 1) { v /= 1024; u++; }
return $"{v:0.#} {units[u]}";
}
$"{Strings.FormatSize(Model.SizeBytes)} • {Strings.FormatDate(Model.InstalledAt)}";
public override string ToString() => Display;
}

View File

@@ -52,11 +52,12 @@ public sealed partial class SettingsViewModel : ObservableObject
get
{
var l = _licenseService.GetCached();
if (l is null) return "Aucune license activée — clique pour saisir une clé";
if (l is null) return Strings.LicenseSummaryNone;
if (l.Status == "valid")
return $"{l.OwnerName} • valable jusqu'au {l.DownloadEntitlementUntil:dd/MM/yyyy}";
return Strings.LicenseSummaryValid(l.OwnerName ?? "—",
Strings.FormatDate(l.DownloadEntitlementUntil));
if (l.Status == "expired")
return $"{l.OwnerName} • expirée le {l.DownloadEntitlementUntil:dd/MM/yyyy}";
return Strings.LicenseSummaryExpired(Strings.FormatDate(l.DownloadEntitlementUntil));
return $"{l.OwnerName} • {l.Status}";
}
}
@@ -159,7 +160,7 @@ public sealed partial class SettingsViewModel : ObservableObject
{
var dlg = new Microsoft.Win32.OpenFolderDialog
{
Title = "Choisir le dossier d'installation",
Title = Strings.SettingsInstallRoot,
InitialDirectory = Directory.Exists(InstallRoot) ? InstallRoot : Environment.GetFolderPath(Environment.SpecialFolder.UserProfile),
};
if (dlg.ShowDialog() == true)
@@ -287,12 +288,5 @@ public sealed partial class SettingsViewModel : ObservableObject
catch { CacheSizeDisplay = "—"; }
}
private static string FormatSize(long bytes)
{
if (bytes <= 0) return "0 o";
string[] units = { "o", "Ko", "Mo", "Go", "To" };
double v = bytes; int u = 0;
while (v >= 1024 && u < units.Length - 1) { v /= 1024; u++; }
return $"{v:0.#} {units[u]}";
}
private static string FormatSize(long bytes) => Strings.FormatSize(bytes);
}

View File

@@ -1,7 +1,8 @@
<Window x:Class="PSLauncher.App.Views.LauncherUpdateDialog"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="Mise à jour du launcher"
xmlns:loc="clr-namespace:PSLauncher.Core.Localization;assembly=PSLauncher.Core"
Title="{x:Static loc:Strings.LauncherUpdateTitle}"
Icon="pack://application:,,,/Resources/favicon.ico"
Width="500" Height="320"
ResizeMode="NoResize"
@@ -16,7 +17,7 @@
</Grid.RowDefinitions>
<TextBlock Grid.Row="0"
Text="Mise à jour du launcher disponible"
Text="{x:Static loc:Strings.LauncherUpdateAvailable}"
FontSize="20" FontWeight="SemiBold"
Foreground="{StaticResource Brush.Text.Primary}" />
@@ -33,7 +34,7 @@
<Border Grid.Row="2" Background="{StaticResource Brush.Bg.Card}"
BorderBrush="{StaticResource Brush.Border}" BorderThickness="1"
CornerRadius="6" Padding="16" Margin="0,16,0,0">
<TextBlock Text="Le launcher va se mettre à jour : il téléchargera le nouveau binaire, se fermera, sera remplacé, puis se relancera automatiquement. Cela prend quelques secondes."
<TextBlock Text="{x:Static loc:Strings.LauncherUpdateBody}"
Foreground="{StaticResource Brush.Text.Secondary}"
TextWrapping="Wrap" />
</Border>
@@ -41,11 +42,11 @@
<StackPanel Grid.Row="3" Orientation="Horizontal"
HorizontalAlignment="Right" Margin="0,18,0,0">
<Button Style="{StaticResource SecondaryButton}"
Content="Plus tard" IsCancel="True"
Content="{x:Static loc:Strings.ActionLater}" IsCancel="True"
Margin="0,0,12,0"
Click="OnLater" />
<Button Style="{StaticResource AccentButton}"
Content="⬇ Mettre à jour"
Content="{x:Static loc:Strings.LauncherUpdateNow}"
Padding="32,8"
IsDefault="True"
Click="OnUpdate" />

View File

@@ -1,4 +1,5 @@
using System.Windows;
using PSLauncher.Core.Localization;
using PSLauncher.Models;
namespace PSLauncher.App.Views;
@@ -12,7 +13,7 @@ public partial class LauncherUpdateDialog : Window
InitializeComponent();
VersionText.Text = $"v{currentVersion} → v{info.Version}";
SizeText.Text = info.Download.SizeBytes > 0
? $"Taille : {FormatSize(info.Download.SizeBytes)}"
? Strings.SizeLabel(Strings.FormatSize(info.Download.SizeBytes))
: "";
}
@@ -29,13 +30,4 @@ public partial class LauncherUpdateDialog : Window
DialogResult = false;
Close();
}
private static string FormatSize(long bytes)
{
if (bytes <= 0) return "—";
string[] units = { "o", "Ko", "Mo", "Go" };
double v = bytes; int u = 0;
while (v >= 1024 && u < units.Length - 1) { v /= 1024; u++; }
return $"{v:0.#} {units[u]}";
}
}

View File

@@ -1,6 +1,7 @@
<Window x:Class="PSLauncher.App.Views.LicenseDetailsDialog"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:loc="clr-namespace:PSLauncher.Core.Localization;assembly=PSLauncher.Core"
Title="License"
Icon="pack://application:,,,/Resources/favicon.ico"
Width="500" Height="480"
@@ -45,7 +46,7 @@
<ColumnDefinition Width="160" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<TextBlock Grid.Column="0" Text="Client"
<TextBlock Grid.Column="0" Text="{x:Static loc:Strings.LicenseDetailsClient}"
Foreground="{StaticResource Brush.Text.Secondary}" FontSize="12" />
<TextBlock Grid.Column="1" x:Name="OwnerText"
FontWeight="SemiBold"
@@ -56,7 +57,7 @@
<ColumnDefinition Width="160" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<TextBlock Grid.Column="0" Text="Validité téléchargements"
<TextBlock Grid.Column="0" Text="{x:Static loc:Strings.LicenseDetailsValidity}"
Foreground="{StaticResource Brush.Text.Secondary}" FontSize="12" />
<TextBlock Grid.Column="1" x:Name="UntilText"
FontWeight="SemiBold"
@@ -67,7 +68,7 @@
<ColumnDefinition Width="160" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<TextBlock Grid.Column="0" Text="Activée le"
<TextBlock Grid.Column="0" Text="{x:Static loc:Strings.LicenseDetailsActivatedOn}"
Foreground="{StaticResource Brush.Text.Secondary}" FontSize="12" />
<TextBlock Grid.Column="1" x:Name="IssuedText"
Foreground="{StaticResource Brush.Text.Primary}" />
@@ -77,7 +78,7 @@
<ColumnDefinition Width="160" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<TextBlock Grid.Column="0" Text="ID machine"
<TextBlock Grid.Column="0" Text="{x:Static loc:Strings.LicenseDetailsMachineId}"
Foreground="{StaticResource Brush.Text.Secondary}" FontSize="12"
VerticalAlignment="Center" />
<Grid Grid.Column="1">
@@ -91,7 +92,7 @@
TextTrimming="CharacterEllipsis" VerticalAlignment="Center" />
<Button Grid.Column="1" Style="{StaticResource SecondaryButton}"
Content="📋"
ToolTip="Copier"
ToolTip="{x:Static loc:Strings.CopyTooltip}"
Click="OnCopyMachineId" />
</Grid>
</Grid>
@@ -101,11 +102,11 @@
<!-- Footer actions -->
<StackPanel Grid.Row="3" Orientation="Horizontal" HorizontalAlignment="Right" Margin="0,18,0,0">
<Button Style="{StaticResource SecondaryButton}"
Content="🗑 Désactiver la license"
Content="{x:Static loc:Strings.LicenseDetailsDeactivate}"
Click="OnDeactivate"
Margin="0,0,12,0" />
<Button Style="{StaticResource AccentButton}"
Content="Fermer"
Content="{x:Static loc:Strings.ActionClose}"
IsCancel="True" IsDefault="True"
Padding="32,8"
Click="OnClose" />

View File

@@ -29,11 +29,11 @@ public partial class LicenseDetailsDialog : Window
OwnerText.Text = license.OwnerName ?? "—";
UntilText.Text = license.DownloadEntitlementUntil is { } until
? $"jusqu'au {until.ToLocalTime():dddd dd MMMM yyyy}"
? Strings.FormatLongDate(until)
: "—";
IssuedText.Text = license.IssuedAt is { } issued
? issued.ToLocalTime().ToString("dd/MM/yyyy")
: (license.ServerTime?.ToLocalTime().ToString("dd/MM/yyyy") ?? "—");
? Strings.FormatDate(issued)
: Strings.FormatDate(license.ServerTime);
MachineIdText.Text = licenseService.GetMachineId();
}
@@ -43,19 +43,19 @@ public partial class LicenseDetailsDialog : Window
if (l.Status == "valid")
{
if (until is { } u && (u - DateTime.UtcNow).TotalDays < 30)
return ("⏰", "License valide", $"Expire bientôt — le {u.ToLocalTime():dd/MM/yyyy}",
return ("⏰", Strings.LicenseDetailsValidTitle, Strings.LicenseDetailsExpiringSoon(Strings.FormatDate(u)),
Color.FromRgb(0xF5, 0x9E, 0x0B), Color.FromRgb(0x3A, 0x2A, 0x0E));
return ("✓", "License valide", "Téléchargements de nouvelles versions autorisés",
return ("✓", Strings.LicenseDetailsValidTitle, Strings.LicenseDetailsValidSubtitle,
Color.FromRgb(0x16, 0xA3, 0x4A), Color.FromRgb(0x0E, 0x2A, 0x1B));
}
if (l.Status == "expired")
return ("⚠", "License expirée",
until is { } u ? $"Expirée le {u.ToLocalTime():dd/MM/yyyy}" : "Plus de téléchargements possibles",
return ("⚠", Strings.LicenseDetailsExpiredTitle,
until is { } u ? Strings.LicenseDetailsExpiredSubtitle(Strings.FormatDate(u)) : Strings.LicenseDetailsExpiredFallback,
Color.FromRgb(0xEF, 0x44, 0x44), Color.FromRgb(0x2A, 0x14, 0x14));
if (l.Status == "revoked")
return ("🚫", "License révoquée", "Contacte ASTERION pour plus d'informations",
return ("🚫", Strings.LicenseDetailsRevokedTitle, Strings.LicenseDetailsRevokedSubtitle,
Color.FromRgb(0xEF, 0x44, 0x44), Color.FromRgb(0x2A, 0x14, 0x14));
return ("❓", "License inconnue", l.Message ?? l.Status,
return ("❓", Strings.LicenseDetailsUnknownTitle, l.Message ?? l.Status,
Color.FromRgb(0xEF, 0x44, 0x44), Color.FromRgb(0x2A, 0x14, 0x14));
}

View File

@@ -121,6 +121,14 @@
Content="{Binding InstallButtonLabel}" Padding="22,8" FontSize="13"
Command="{Binding InstallCommand}"
Visibility="{Binding IsRemoteOnly, Converter={StaticResource BoolToVisibility}}" />
<!-- Bouton rouge « Annuler » : visible uniquement quand on a un partial.
Permet d'abandonner le partial et repartir de zéro (avec confirmation). -->
<Button Style="{StaticResource DangerButton}"
Content="{x:Static loc:Strings.ActionCancel}"
Padding="14,8" FontSize="13" Margin="6,0,0,0"
Command="{Binding RestartFromZeroCommand}"
ToolTip="{x:Static loc:Strings.MenuRestartFromZero}"
Visibility="{Binding ShowRestartFromZero, Converter={StaticResource BoolToVisibility}}" />
<StackPanel Orientation="Horizontal"
Visibility="{Binding IsBusy, Converter={StaticResource BoolToVisibility}}">
<ProgressBar Width="140" Height="6"
@@ -149,6 +157,9 @@
<MenuItem Header="{x:Static loc:Strings.MenuOpenFolder}"
Command="{Binding PlacementTarget.Tag.OpenFolderCommand, RelativeSource={RelativeSource AncestorType=ContextMenu}}"
Visibility="{Binding PlacementTarget.Tag.IsInstalled, RelativeSource={RelativeSource AncestorType=ContextMenu}, Converter={StaticResource BoolToVisibility}}" />
<MenuItem Header="{x:Static loc:Strings.MenuRestartFromZero}"
Command="{Binding PlacementTarget.Tag.RestartFromZeroCommand, RelativeSource={RelativeSource AncestorType=ContextMenu}}"
Visibility="{Binding PlacementTarget.Tag.HasResumableDownload, RelativeSource={RelativeSource AncestorType=ContextMenu}, Converter={StaticResource BoolToVisibility}}" />
<Separator Visibility="{Binding PlacementTarget.Tag.IsInstalled, RelativeSource={RelativeSource AncestorType=ContextMenu}, Converter={StaticResource BoolToVisibility}}" />
<MenuItem Header="{x:Static loc:Strings.MenuUninstall}"
Command="{Binding PlacementTarget.Tag.UninstallCommand, RelativeSource={RelativeSource AncestorType=ContextMenu}}"
@@ -289,15 +300,15 @@
Command="{Binding OpenSettingsCommand}"
Margin="0,0,16,0" />
<Button Style="{StaticResource WindowControlButton}"
Content="—" ToolTip="Réduire"
Content="—" ToolTip="{x:Static loc:Strings.Minimize}"
shell:WindowChrome.IsHitTestVisibleInChrome="True"
Click="OnMinimizeClick" />
<Button Style="{StaticResource WindowControlButton}"
Content="☐" ToolTip="Agrandir / Restaurer"
Content="☐" ToolTip="{x:Static loc:Strings.Maximize}"
shell:WindowChrome.IsHitTestVisibleInChrome="True"
Click="OnMaxRestoreClick" />
<Button Style="{StaticResource WindowCloseButton}"
Content="✕" ToolTip="Fermer"
Content="✕" ToolTip="{x:Static loc:Strings.CloseTooltip}"
shell:WindowChrome.IsHitTestVisibleInChrome="True"
Click="OnCloseClick" />
</StackPanel>
@@ -404,7 +415,7 @@
FontSize="13"
Foreground="{StaticResource Brush.Text.Secondary}"
Margin="0,8,0,0">
<Run Text="Sortie le " />
<Run Text="{x:Static loc:Strings.ReleasedOn}" />
<Run Text="{Binding FeaturedVersion.PrimaryDate, Mode=OneWay}" />
<Run Text=" • " />
<Run Text="{Binding FeaturedVersion.SizeDisplay, Mode=OneWay}" />
@@ -419,13 +430,23 @@
Command="{Binding FeaturedVersion.LaunchCommand}"
Visibility="{Binding FeaturedVersion.IsInstalled, Converter={StaticResource BoolToVisibility}}" />
<Button Grid.Row="0" Grid.RowSpan="3" Grid.Column="1"
Style="{StaticResource AccentButton}"
Content="{Binding FeaturedVersion.InstallButtonLabel}"
FontSize="20" Padding="48,16"
VerticalAlignment="Center"
Command="{Binding FeaturedVersion.InstallCommand}"
Visibility="{Binding FeaturedVersion.IsRemoteOnly, Converter={StaticResource BoolToVisibility}}" />
<StackPanel Grid.Row="0" Grid.RowSpan="3" Grid.Column="1"
Orientation="Vertical" VerticalAlignment="Center"
Visibility="{Binding FeaturedVersion.IsRemoteOnly, Converter={StaticResource BoolToVisibility}}">
<Button Style="{StaticResource AccentButton}"
Content="{Binding FeaturedVersion.InstallButtonLabel}"
FontSize="20" Padding="48,16"
Command="{Binding FeaturedVersion.InstallCommand}" />
<!-- Bouton rouge « Annuler » : visible uniquement quand un partial existe.
Permet d'abandonner et repartir de zéro (confirmation requise). -->
<Button Style="{StaticResource DangerButton}"
Content="{x:Static loc:Strings.ActionCancel}"
FontSize="13" Padding="20,8" Margin="0,8,0,0"
HorizontalAlignment="Center"
Command="{Binding FeaturedVersion.RestartFromZeroCommand}"
ToolTip="{x:Static loc:Strings.MenuRestartFromZero}"
Visibility="{Binding FeaturedVersion.ShowRestartFromZero, Converter={StaticResource BoolToVisibility}}" />
</StackPanel>
<StackPanel Grid.Row="0" Grid.RowSpan="3" Grid.Column="1"
Orientation="Vertical" VerticalAlignment="Center" Width="220"
@@ -460,6 +481,9 @@
<MenuItem Header="{x:Static loc:Strings.MenuOpenFolder}"
Command="{Binding PlacementTarget.Tag.OpenFolderCommand, RelativeSource={RelativeSource AncestorType=ContextMenu}}"
Visibility="{Binding PlacementTarget.Tag.IsInstalled, RelativeSource={RelativeSource AncestorType=ContextMenu}, Converter={StaticResource BoolToVisibility}}" />
<MenuItem Header="{x:Static loc:Strings.MenuRestartFromZero}"
Command="{Binding PlacementTarget.Tag.RestartFromZeroCommand, RelativeSource={RelativeSource AncestorType=ContextMenu}}"
Visibility="{Binding PlacementTarget.Tag.HasResumableDownload, RelativeSource={RelativeSource AncestorType=ContextMenu}, Converter={StaticResource BoolToVisibility}}" />
<Separator Visibility="{Binding PlacementTarget.Tag.IsInstalled, RelativeSource={RelativeSource AncestorType=ContextMenu}, Converter={StaticResource BoolToVisibility}}" />
<MenuItem Header="{x:Static loc:Strings.MenuUninstall}"
Command="{Binding PlacementTarget.Tag.UninstallCommand, RelativeSource={RelativeSource AncestorType=ContextMenu}}"
@@ -519,7 +543,7 @@
Background="#A0000000"
CornerRadius="10"
Padding="10,4">
<TextBlock Text="© 2026 ASTERION VR — Tous droits réservés"
<TextBlock Text="{x:Static loc:Strings.Copyright}"
FontSize="11"
Foreground="White" />
</Border>

View File

@@ -1,6 +1,7 @@
using System.Windows;
using System.Windows.Media;
using PSLauncher.Core.Licensing;
using PSLauncher.Core.Localization;
namespace PSLauncher.App.Views;
@@ -24,12 +25,12 @@ public partial class OnboardingDialog : Window
var key = KeyBox.Text?.Trim().ToUpperInvariant() ?? string.Empty;
if (string.IsNullOrEmpty(key) || key.Contains("XXXX"))
{
ShowStatus("Saisis une clé valide au format PRSRV-XXXX-XXXX-XXXX-XXXX.", isError: true);
ShowStatus(Strings.OnboardingInvalidKey, isError: true);
return;
}
ActivateButton.IsEnabled = false;
ShowStatus("Validation en cours…", isError: false);
ShowStatus(Strings.OnboardingValidating, isError: false);
try
{
@@ -38,7 +39,8 @@ public partial class OnboardingDialog : Window
{
case "valid":
_licenseService.SaveCached(key, resp);
ShowStatus($"License activée ({resp.OwnerName}). Téléchargements autorisés jusqu'au {resp.DownloadEntitlementUntil:dd/MM/yyyy}.", isError: false);
ShowStatus(Strings.OnboardingActivated(resp.OwnerName ?? "—",
Strings.FormatDate(resp.DownloadEntitlementUntil)), isError: false);
LicenseActivated = true;
DialogResult = true;
Close();
@@ -46,27 +48,27 @@ public partial class OnboardingDialog : Window
case "expired":
_licenseService.SaveCached(key, resp); // on cache quand même pour permettre de lancer les versions installées
ShowStatus($"License expirée le {resp.DownloadEntitlementUntil:dd/MM/yyyy}. Tu peux toujours utiliser les versions déjà installées, mais plus en télécharger de nouvelles.", isError: true);
ShowStatus(Strings.OnboardingExpired(Strings.FormatDate(resp.DownloadEntitlementUntil)), isError: true);
LicenseActivated = true;
DialogResult = true;
Close();
return;
case "revoked":
ShowStatus("Cette license a été révoquée. Contacte ASTERION.", isError: true);
ShowStatus(Strings.OnboardingRevoked, isError: true);
break;
case "machine_limit_exceeded":
ShowStatus(resp.Message ?? "Cette license a atteint son nombre maximum de machines.", isError: true);
ShowStatus(resp.Message ?? Strings.OnboardingMachineLimit, isError: true);
break;
case "invalid":
default:
ShowStatus(resp.Message ?? "Clé de license inconnue.", isError: true);
ShowStatus(resp.Message ?? Strings.OnboardingInvalid, isError: true);
break;
}
}
catch (Exception ex)
{
ShowStatus($"Erreur de communication avec le serveur :\n{ex.Message}", isError: true);
ShowStatus(Strings.OnboardingServerError(ex.Message), isError: true);
}
finally
{

View File

@@ -1,7 +1,8 @@
<Window x:Class="PSLauncher.App.Views.ReleaseNotesViewerDialog"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="Notes de version"
xmlns:loc="clr-namespace:PSLauncher.Core.Localization;assembly=PSLauncher.Core"
Title="{x:Static loc:Strings.MsgBoxReleaseNotes}"
Icon="pack://application:,,,/Resources/favicon.ico"
Width="640" Height="540"
MinWidth="480" MinHeight="400"
@@ -33,7 +34,7 @@
<Button Grid.Row="2"
Style="{StaticResource SecondaryButton}"
Content="Fermer"
Content="{x:Static loc:Strings.ActionClose}"
IsCancel="True"
IsDefault="True"
HorizontalAlignment="Right"

View File

@@ -30,30 +30,9 @@
<ScrollViewer Grid.Row="1" VerticalScrollBarVisibility="Auto" Padding="32,0,32,16">
<StackPanel>
<!-- Langue de l'interface -->
<Border Background="{StaticResource Brush.Bg.Card}"
BorderBrush="{StaticResource Brush.Border}" BorderThickness="1"
CornerRadius="6" Padding="20" Margin="0,0,0,16">
<StackPanel>
<TextBlock Text="{x:Static loc:Strings.SettingsLanguage}"
FontSize="11" FontWeight="Bold"
Foreground="{StaticResource Brush.Text.Secondary}"
Margin="0,0,0,8" />
<ComboBox ItemsSource="{Binding AvailableLanguages}"
DisplayMemberPath="Name"
SelectedValuePath="Code"
SelectedValue="{Binding Language, Mode=TwoWay}"
Background="#1A1A20"
Foreground="{StaticResource Brush.Text.Primary}"
BorderBrush="{StaticResource Brush.Border}"
BorderThickness="1" Padding="8" />
<TextBlock Text="{x:Static loc:Strings.SettingsLanguageHint}"
FontSize="11" Foreground="{StaticResource Brush.Text.Secondary}"
FontStyle="Italic" Margin="0,6,0,0" TextWrapping="Wrap" />
</StackPanel>
</Border>
<!-- License (en tête, bordure colorée selon état) -->
<!-- License de mise à jour (en tête : c'est l'info la plus importante,
elle conditionne ce que l'utilisateur peut télécharger.
Bordure colorée selon l'état : vert/jaune/rouge). -->
<Border BorderThickness="2"
CornerRadius="6" Padding="20" Margin="0,0,0,16">
<Border.Style>
@@ -124,7 +103,7 @@
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<StackPanel Grid.Column="0">
<TextBlock Text="Identifiant machine (anonyme)"
<TextBlock Text="{x:Static loc:Strings.SettingsMachineId}"
FontSize="11"
Foreground="{StaticResource Brush.Text.Secondary}" />
<TextBlock Text="{Binding MachineId}"
@@ -134,12 +113,35 @@
</StackPanel>
<Button Grid.Column="1"
Style="{StaticResource SecondaryButton}"
Content="📋 Copier"
Content="{x:Static loc:Strings.SettingsCopyMachineId}"
Command="{Binding CopyMachineIdCommand}" />
</Grid>
</Grid>
</Border>
<!-- Langue de l'interface -->
<Border Background="{StaticResource Brush.Bg.Card}"
BorderBrush="{StaticResource Brush.Border}" BorderThickness="1"
CornerRadius="6" Padding="20" Margin="0,0,0,16">
<StackPanel>
<TextBlock Text="{x:Static loc:Strings.SettingsLanguage}"
FontSize="11" FontWeight="Bold"
Foreground="{StaticResource Brush.Text.Secondary}"
Margin="0,0,0,8" />
<ComboBox ItemsSource="{Binding AvailableLanguages}"
DisplayMemberPath="Name"
SelectedValuePath="Code"
SelectedValue="{Binding Language, Mode=TwoWay}"
Background="#1A1A20"
Foreground="{StaticResource Brush.Text.Primary}"
BorderBrush="{StaticResource Brush.Border}"
BorderThickness="1" Padding="8" />
<TextBlock Text="{x:Static loc:Strings.SettingsLanguageHint}"
FontSize="11" Foreground="{StaticResource Brush.Text.Secondary}"
FontStyle="Italic" Margin="0,6,0,0" TextWrapping="Wrap" />
</StackPanel>
</Border>
<!-- Serveur -->
<Border Background="{StaticResource Brush.Bg.Card}"
BorderBrush="{StaticResource Brush.Border}" BorderThickness="1"
@@ -149,7 +151,7 @@
FontSize="11" FontWeight="Bold"
Foreground="{StaticResource Brush.Text.Secondary}"
Margin="0,0,0,8" />
<TextBlock Text="URL de l'API"
<TextBlock Text="{x:Static loc:Strings.SettingsApiUrl}"
Foreground="{StaticResource Brush.Text.Secondary}"
FontSize="12" Margin="0,0,0,4" />
<Grid>
@@ -184,7 +186,7 @@
FontSize="11" FontWeight="Bold"
Foreground="{StaticResource Brush.Text.Secondary}"
Margin="0,0,0,8" />
<TextBlock Text="Dossier où sont installées les versions"
<TextBlock Text="{x:Static loc:Strings.SettingsInstallRoot}"
Foreground="{StaticResource Brush.Text.Secondary}"
FontSize="12" Margin="0,0,0,4" />
<Grid>
@@ -229,7 +231,8 @@
TextTrimming="CharacterEllipsis" />
<TextBlock FontSize="12"
Foreground="{StaticResource Brush.Text.Secondary}">
<Run Text="Taille actuelle : " />
<Run Text="{x:Static loc:Strings.SettingsCacheSize}" />
<Run Text=" " />
<Run Text="{Binding CacheSizeDisplay}" FontWeight="SemiBold" />
</TextBlock>
</StackPanel>
@@ -256,10 +259,11 @@
Margin="0,0,0,8" />
<TextBlock FontSize="13"
Foreground="{StaticResource Brush.Text.Primary}">
<Run Text="Version : " />
<Run Text="{x:Static loc:Strings.SettingsLauncherVersion}" />
<Run Text=" " />
<Run Text="{Binding LauncherVersion, Mode=OneWay}" FontWeight="SemiBold" />
</TextBlock>
<TextBlock Text="© 2026 ASTERION VR — Tous droits réservés"
<TextBlock Text="{x:Static loc:Strings.Copyright}"
FontSize="12"
Foreground="{StaticResource Brush.Text.Secondary}"
Margin="0,4,0,0" />
@@ -273,14 +277,14 @@
FontFamily="Consolas" FontSize="11"
Foreground="{StaticResource Brush.Text.Primary}"
TextTrimming="CharacterEllipsis" />
<TextBlock Text="Logs rotation quotidienne, 10 jours conservés"
<TextBlock Text="{x:Static loc:Strings.SettingsCacheRotation}"
FontSize="12"
Foreground="{StaticResource Brush.Text.Secondary}"
Margin="0,2,0,0" />
</StackPanel>
<Button Grid.Column="1" Margin="8,0,0,0"
Style="{StaticResource SecondaryButton}"
Content="📁 Ouvrir"
Content="{x:Static loc:Strings.OpenButton}"
Command="{Binding OpenLogsFolderCommand}" />
</Grid>
</StackPanel>

View File

@@ -1,7 +1,8 @@
<Window x:Class="PSLauncher.App.Views.UpdateAvailableDialog"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="Mise à jour disponible"
xmlns:loc="clr-namespace:PSLauncher.Core.Localization;assembly=PSLauncher.Core"
Title="{x:Static loc:Strings.UpdateAvailableTitle}"
Icon="pack://application:,,,/Resources/favicon.ico"
Width="640" Height="540"
MinWidth="480" MinHeight="400"
@@ -38,12 +39,12 @@
<StackPanel Grid.Row="3" Orientation="Horizontal" HorizontalAlignment="Right" Margin="0,18,0,0">
<Button Style="{StaticResource SecondaryButton}"
Content="Plus tard"
Content="{x:Static loc:Strings.ActionLater}"
IsCancel="True"
Margin="0,0,12,0"
Click="OnLater" />
<Button Style="{StaticResource PrimaryButton}"
Content="⬇ Télécharger"
Content="{x:Static loc:Strings.UpdateDownload}"
Padding="32,10"
Click="OnDownload" />
</StackPanel>

View File

@@ -1,4 +1,5 @@
using System.Windows;
using PSLauncher.Core.Localization;
using PSLauncher.Models;
namespace PSLauncher.App.Views;
@@ -9,10 +10,14 @@ public partial class UpdateAvailableDialog : Window
{
InitializeComponent();
var sizeText = version.Download.SizeBytes > 0
? Strings.FormatSize(version.Download.SizeBytes)
: Strings.SizeUnknown;
DataContext = new
{
Title = $"PROSERVE v{version.Version} disponible",
Subtitle = $"Date de release : {version.ReleasedAt.ToLocalTime():dd MMMM yyyy} • {FormatSize(version.Download.SizeBytes)}",
Title = Strings.UpdateAvailableHeading(version.Version),
Subtitle = Strings.UpdateAvailableSubtitle(Strings.FormatLongDate(version.ReleasedAt), sizeText),
ReleaseNotesDocument = MarkdownTheming.BuildThemedDocument(releaseNotesMarkdown)
};
}
@@ -32,14 +37,4 @@ public partial class UpdateAvailableDialog : Window
DialogResult = false;
Close();
}
private static string FormatSize(long bytes)
{
if (bytes <= 0) return "taille inconnue";
string[] units = { "o", "Ko", "Mo", "Go", "To" };
double v = bytes;
int u = 0;
while (v >= 1024 && u < units.Length - 1) { v /= 1024; u++; }
return $"{v:0.#} {units[u]}";
}
}