tools/installer: move language to external file

This moves all language used in the installer to an embedded JSON file
to allow for customisation per game.
This commit is contained in:
lahm86 2025-03-25 11:38:46 +00:00
parent e16fcda94b
commit 365cff79c1
23 changed files with 170 additions and 97 deletions

View file

@ -6,7 +6,7 @@
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:models="clr-namespace:TRX_InstallerLib.Models"
d:DataContext="{d:DesignInstance Type=models:FinishStep}"
mc:Ignorable="d"
mc:Ignorable="d"
d:DesignHeight="450" d:DesignWidth="800">
<UserControl.Resources>
@ -14,19 +14,17 @@
</UserControl.Resources>
<StackPanel Orientation="Vertical">
<TextBlock TextWrapping="Wrap" VerticalAlignment="Center" Margin="0,0,0,12" Style="{StaticResource heading}">
Step 4: Done
</TextBlock>
<TextBlock
Style="{StaticResource heading}"
Text="{Binding ViewText[step_finish_heading]}"/>
<TextBlock VerticalAlignment="Center" Margin="0,0,0,12" TextWrapping="Wrap">
Installation complete. To configure more advanced features, you can edit the JSON files in the cfg/ directory with a text editor.
</TextBlock>
<TextBlock
VerticalAlignment="Center"
Margin="0,0,0,24"
TextWrapping="Wrap"
Text="{Binding ViewText[step_finish_content]}"/>
<TextBlock VerticalAlignment="Center" Margin="0,0,0,12">
Happy raiding :)
</TextBlock>
<CheckBox IsChecked="{Binding FinishSettings.OpenGameDirectory}" Content="Open game directory after closing this window" Margin="0,0,0,12" />
<CheckBox IsChecked="{Binding FinishSettings.LaunchGame}" Content="Launch the game after closing this window" Margin="0,0,0,12" />
<CheckBox IsChecked="{Binding FinishSettings.OpenGameDirectory}" Content="{Binding ViewText[step_finish_open_directory]}" Margin="0,0,0,12" />
<CheckBox IsChecked="{Binding FinishSettings.LaunchGame}" Content="{Binding ViewText[step_finish_open_game]}" Margin="0,0,0,12" />
</StackPanel>
</UserControl>

View file

@ -7,7 +7,7 @@
xmlns:models="clr-namespace:TRX_InstallerLib.Models"
xmlns:utils="clr-namespace:TRX_InstallerLib.Utils"
d:DataContext="{d:DesignInstance Type=models:InstallSettingsStep}"
mc:Ignorable="d"
mc:Ignorable="d"
d:DesignHeight="450" d:DesignWidth="800">
<UserControl.Resources>
@ -20,9 +20,9 @@
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<TextBlock Grid.Row="0" TextWrapping="Wrap" VerticalAlignment="Center" Margin="0,0,0,12" Style="{StaticResource heading}">
Step 2: Installation options
</TextBlock>
<TextBlock
Style="{StaticResource heading}"
Text="{Binding ViewText[step_settings_heading]}"/>
<ScrollViewer Grid.Row="1" ScrollViewer.VerticalScrollBarVisibility="Auto">
<ScrollViewer.Template>
@ -82,15 +82,10 @@
<StackPanel Orientation="Vertical">
<CheckBox VerticalAlignment="Center" Margin="0,0,0,12" IsChecked="{Binding InstallSettings.DownloadMusic}" IsEnabled="{Binding InstallSettings.IsDownloadingMusicNeeded}">
<TextBlock TextWrapping="Wrap">
Download music tracks
<Run Foreground="ForestGreen" Text="{Binding InstallSettings.IsDownloadingMusicNeeded, Converter={utils:ConditionalMarkupConverter TrueValue='', FalseValue='(already found)'}, Mode=OneWay}" />
<Run Text="{Binding ViewText[step_settings_music_heading]}"/>
<Run Foreground="ForestGreen" Text="{Binding InstallSettings.IsDownloadingMusicNeeded, Converter={utils:ConditionalViewTextConverter TrueValue='', FalseValue='label_already_found'}, Mode=OneWay}" />
<LineBreak />
<Run Style="{StaticResource small}">
This option lets you download compatible music files for the game
automatically (60 MB). The legality of these files is disputable;
the most legal way to import the music to PC is to obtain them from
your own source - TR2 supports FLAC, OOG, MP3 and WAV files.
</Run>
<Run Style="{StaticResource small}" Text="{Binding ViewText[step_settings_music_content]}"/>
</TextBlock>
</CheckBox>
@ -100,33 +95,31 @@
<utils:ComparisonConverter x:Key="ComparisonConverter" />
</StackPanel.Resources>
<TextBlock TextWrapping="Wrap" Margin="0,0,0,6">
Download Unfinished Business expansion pack
<Run Foreground="ForestGreen" Text="{Binding InstallSettings.IsDownloadingExpansionNeeded, Converter={utils:ConditionalMarkupConverter TrueValue='', FalseValue='(already found)'}, Mode=OneWay}" />
<Run Text="{Binding ViewText[step_settings_expansion_heading]}"/>
<Run Foreground="ForestGreen" Text="{Binding InstallSettings.IsDownloadingExpansionNeeded, Converter={utils:ConditionalViewTextConverter TrueValue='', FalseValue='label_already_found'}, Mode=OneWay}" />
<LineBreak />
<Run Style="{StaticResource small}">
The Unfinished Business expansion pack was made freeware. However, the Steam and GOG versions do not ship it. This option lets you download the expansion files automatically (6 MB).
</Run>
<Run Style="{StaticResource small}" Text="{Binding ViewText[step_settings_expansion_content]}"/>
</TextBlock>
<RadioButton IsEnabled="{Binding InstallSettings.DownloadExpansionPack}" Style="{StaticResource small}" Margin="0,0,0,6" Content="Fan-made edition (includes music triggers)"
<RadioButton IsEnabled="{Binding InstallSettings.DownloadExpansionPack}" Style="{StaticResource small}" Margin="0,0,0,6" Content="{Binding ViewText[step_settings_expansion_music]}"
IsChecked="{Binding Path=InstallSettings.ExpansionPackType, Converter={StaticResource ComparisonConverter}, ConverterParameter={x:Static models:ExpansionPackType.Music}}"/>
<RadioButton IsEnabled="{Binding InstallSettings.DownloadExpansionPack}" Style="{StaticResource small}" Margin="0,0,0,6" Content="Original edition (does not include music triggers)"
<RadioButton IsEnabled="{Binding InstallSettings.DownloadExpansionPack}" Style="{StaticResource small}" Margin="0,0,0,6" Content="{Binding ViewText[step_settings_expansion_vanilla]}"
IsChecked="{Binding Path=InstallSettings.ExpansionPackType, Converter={StaticResource ComparisonConverter}, ConverterParameter={x:Static models:ExpansionPackType.Vanilla}}"/>
</StackPanel>
</CheckBox>
<CheckBox VerticalAlignment="Center" Margin="0,0,0,12" IsChecked="{Binding InstallSettings.ImportSaves}" IsEnabled="{Binding InstallSettings.InstallSource.IsImportingSavesSupported}">
<TextBlock TextWrapping="Wrap">
Import saves
<Run Text="{Binding ViewText[step_settings_saves_header]}"/>
<LineBreak />
<Run Style="{StaticResource small}">
Imports existing savegame files. Only TombATI and TR1X savegame format is supported at this time.
</Run>
<Run Style="{StaticResource small}" Text="{Binding ViewText[step_settings_saves_content]}"/>
</TextBlock>
</CheckBox>
<CheckBox VerticalAlignment="Center" Margin="0,0,0,12" IsChecked="{Binding InstallSettings.CreateDesktopShortcut}">
Create desktop shortcut
</CheckBox>
<CheckBox
VerticalAlignment="Center"
Margin="0,0,0,12"
IsChecked="{Binding InstallSettings.CreateDesktopShortcut}"
Content="{Binding ViewText[step_settings_shortcut_heading]}"/>
<Separator />
@ -136,9 +129,9 @@
<ColumnDefinition Width="*" />
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<Label Grid.Column="0" VerticalAlignment="Center" Margin="0,0,12,0" Padding="0" Content="Destination folder:" />
<Label Grid.Column="0" VerticalAlignment="Center" Margin="0,0,12,0" Padding="0" Content="{Binding ViewText[label_destination_folder]}" />
<TextBlock Grid.Column="1" VerticalAlignment="Center" Text="{Binding InstallSettings.TargetDirectory}" TextTrimming="CharacterEllipsis" />
<Button Grid.Column="2" VerticalAlignment="Center" Margin="12,0,0,0" Command="{Binding ChooseLocationCommand}" Content="Change..." />
<Button Grid.Column="2" VerticalAlignment="Center" Margin="12,0,0,0" Command="{Binding ChooseLocationCommand}" Content="{Binding ViewText[command_change]}" />
</Grid>
</StackPanel>
</ScrollViewer>

View file

@ -50,8 +50,8 @@
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<TextBlock Grid.Column="0" Padding="0" Margin="0,-3,12,3" VerticalAlignment="Top" Style="{StaticResource subHeading}" Text="{Binding InstallSource.SourceName}" Height="16" />
<TextBlock Grid.Column="1" Padding="0" Margin="0,-3,0,3" HorizontalAlignment="Right" VerticalAlignment="Top" Style="{StaticResource subHeadingFound}" Visibility="{Binding IsAvailable, Converter={StaticResource BoolToVisibleConverter}}" Text="Found" />
<TextBlock Grid.Column="1" Padding="0" Margin="0,-3,0,3" HorizontalAlignment="Right" VerticalAlignment="Top" Style="{StaticResource subHeadingNotFound}" Visibility="{Binding IsAvailable, Converter={StaticResource BoolToHiddenConverter}}" Text="Not found" />
<TextBlock Grid.Column="1" Padding="0" Margin="0,-3,0,3" HorizontalAlignment="Right" VerticalAlignment="Top" Style="{StaticResource subHeadingFound}" Visibility="{Binding IsAvailable, Converter={StaticResource BoolToVisibleConverter}}" Text="{Binding ViewText[label_found]}" />
<TextBlock Grid.Column="1" Padding="0" Margin="0,-3,0,3" HorizontalAlignment="Right" VerticalAlignment="Top" Style="{StaticResource subHeadingNotFound}" Visibility="{Binding IsAvailable, Converter={StaticResource BoolToHiddenConverter}}" Text="{Binding ViewText[label_not_found]}" />
</Grid>
<Grid Grid.Row="1" Grid.Column="1">
@ -59,9 +59,12 @@
<ColumnDefinition Width="*" />
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<TextBlock Grid.Column="0" VerticalAlignment="Center" Text="{Binding SourceDirectory, TargetNullValue='(no folder selected)'}" TextTrimming="CharacterEllipsis" />
<TextBlock Grid.Column="0" VerticalAlignment="Center" Text="{Binding ViewText[label_folder_not_selected]}" Visibility="{Binding IsSourceDirectoryDefined, Converter={StaticResource BoolToHiddenConverter}}" />
<TextBlock Grid.Column="0" VerticalAlignment="Center" Text="{Binding SourceDirectory}" TextTrimming="CharacterEllipsis" />
<TextBlock Grid.Column="1" VerticalAlignment="Center" Margin="6,0,0,0">
<Hyperlink Command="{Binding ChooseLocationCommand}" >(change)</Hyperlink>
<Hyperlink Command="{Binding ChooseLocationCommand}">
<Run Text="{Binding ViewText[command_change_link]}"/>
</Hyperlink>
</TextBlock>
</Grid>
</Grid>

View file

@ -24,9 +24,9 @@
</Grid.RowDefinitions>
<StackPanel Grid.Row="0" Orientation="Vertical">
<TextBlock TextWrapping="Wrap" VerticalAlignment="Center" Margin="0,0,0,12" Style="{StaticResource heading}">
Step 3: Installing
</TextBlock>
<TextBlock
Style="{StaticResource heading}"
Text="{Binding ViewText[step_install_heading]}"/>
<TextBlock VerticalAlignment="Center" Margin="0,0,0,12" Text="{Binding Description}" />

View file

@ -7,8 +7,8 @@
xmlns:controls="clr-namespace:TRX_InstallerLib.Controls"
xmlns:models="clr-namespace:TRX_InstallerLib.Models"
xmlns:utils="clr-namespace:TRX_InstallerLib.Utils"
d:DataContext="{d:DesignInstance Type=models:MainWindowViewModel}"
mc:Ignorable="d"
d:DataContext="{d:DesignInstance Type=models:SourceStep}"
mc:Ignorable="d"
d:DesignHeight="450" d:DesignWidth="800">
<UserControl.Resources>
@ -18,7 +18,7 @@
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>
</UserControl.Resources>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto" MinHeight="30" />
@ -29,17 +29,16 @@
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<TextBlock Grid.Row="0" Grid.Column="0" TextWrapping="Wrap" VerticalAlignment="Center" Margin="0,0,0,12" Style="{StaticResource heading}">
Step 1: Choose installation source
</TextBlock>
<TextBlock
Style="{StaticResource heading}"
Text="{Binding ViewText[step_source_heading]}"/>
<TextBlock Grid.Row="1" Grid.Column="0" TextWrapping="Wrap" VerticalAlignment="Center" Margin="0,0,0,10">
TR1X requires original game files to run.
<LineBreak />
Please choose the source location where to install the data files from.
<LineBreak />
If you're upgrading an existing installation, please choose TR1X.
</TextBlock>
<TextBlock
Grid.Row="1"
TextWrapping="Wrap"
VerticalAlignment="Center"
Margin="0,0,0,10"
Text="{Binding ViewText[step_source_content]}"/>
<ListView
BorderThickness="0"

View file

@ -90,17 +90,17 @@
<Button
Command="{Binding GoToPreviousStepCommand}"
Visibility="{Binding IsFinalStep, Converter={StaticResource BoolToHiddenConverter}}"
Content="_Back" />
Content="{Binding ViewText[command_back]}" />
<Button
Grid.Column="1"
Command="{Binding GoToNextStepCommand}"
Visibility="{Binding IsFinalStep, Converter={StaticResource BoolToHiddenConverter}}"
Content="_Next" />
Content="{Binding ViewText[command_next]}" />
<Button
Grid.Column="2"
Command="{Binding CloseWindowCommand}"
CommandParameter="{Binding RelativeSource={RelativeSource AncestorType=Window}}"
Content="{Binding IsFinalStep, Converter={utils:ConditionalMarkupConverter TrueValue='_Close', FalseValue='_Cancel'}}" />
Content="{Binding IsFinalStep, Converter={utils:ConditionalViewTextConverter TrueValue='command_close', FalseValue='command_cancel'}}" />
</Grid>
</Grid>
</Grid>

View file

@ -52,7 +52,7 @@ public class InstallExecutor
CreateDesktopShortcut(_settings.TargetDirectory);
}
progress.Report(new InstallProgress { Description = "Finished", Finished = true });
progress.Report(new InstallProgress { Description = Language.Instance.Controls!["progress_finished"], Finished = true });
}
protected async Task CopyOriginalGameFiles(string sourceDirectory, string targetDirectory, IProgress<InstallProgress> progress)
@ -72,11 +72,11 @@ public class InstallExecutor
{
CurrentValue = 0,
MaximumValue = 1,
Description = "Opening embedded ZIP",
Description = Language.Instance.Controls!["progress_opening_zip"],
});
using var stream = AssemblyUtils.GetResourceStream("Resources.release.zip", false)
?? throw new ApplicationException($"Could not open embedded ZIP.");
?? throw new ApplicationException(Language.Instance.Controls!["progress_zip_failure"]);
await InstallUtils.ExtractZip(stream, targetDirectory, progress, overwrite: true);
}

View file

@ -26,7 +26,7 @@ public static class InstallUtils
{
try
{
progress.Report(new InstallProgress { Description = "Scanning directory" });
progress.Report(new InstallProgress { Description = Language.Instance.Controls!["progress_scanning"] });
var files = Directory.GetFiles(sourceDirectory, "*", SearchOption.AllDirectories);
var currentProgress = 0;
var maximumProgress = files.Length;
@ -45,7 +45,7 @@ public static class InstallUtils
{
CurrentValue = currentProgress,
MaximumValue = maximumProgress,
Description = $"Copying {relPath}",
Description = string.Format(Language.Instance.Controls!["progress_copying"], relPath),
});
Directory.CreateDirectory(Path.GetDirectoryName(targetPath)!);
await Task.Run(() => File.Copy(sourcePath, targetPath, true));
@ -56,7 +56,7 @@ public static class InstallUtils
{
CurrentValue = currentProgress,
MaximumValue = maximumProgress,
Description = $"Copying {relPath} - skipped",
Description = string.Format(Language.Instance.Controls!["progress_skipped"], relPath),
});
}
@ -65,7 +65,7 @@ public static class InstallUtils
}
catch (Exception e)
{
throw new ApplicationException($"Could not extract ZIP:\n{e.Message}");
throw new ApplicationException(e.Message);
}
}
@ -79,14 +79,14 @@ public static class InstallUtils
public static async Task<byte[]> DownloadFile(string url, IProgress<InstallProgress> progress)
{
HttpProgressClient wc = new();
progress.Report(new InstallProgress { Description = $"Initializing download of {url}" });
progress.Report(new InstallProgress { Description = string.Format(Language.Instance.Controls!["progress_init_download"], url) });
wc.DownloadProgressChanged += (totalBytesToReceive, bytesReceived) =>
{
progress.Report(new InstallProgress
{
CurrentValue = (int)bytesReceived,
MaximumValue = (int)totalBytesToReceive,
Description = $"Downloading {url}",
Description = string.Format(Language.Instance.Controls!["progress_downloading"], url),
});
};
return await wc.DownloadDataTaskAsync(new Uri(url));
@ -116,7 +116,7 @@ public static class InstallUtils
using var zip = new ZipArchive(stream);
progress.Report(new InstallProgress
{
Description = "Scanning ZIP",
Description = Language.Instance.Controls!["progress_scanning_zip"],
});
var currentProgress = 0;
var maximumProgress = zip.Entries.Count;
@ -140,7 +140,7 @@ public static class InstallUtils
{
CurrentValue = currentProgress,
MaximumValue = maximumProgress,
Description = $"Extracting {entry.FullName}",
Description = string.Format(Language.Instance.Controls!["progress_extracting"], entry.FullName),
});
Directory.CreateDirectory(Path.GetDirectoryName(targetPath)!);
@ -152,7 +152,7 @@ public static class InstallUtils
{
CurrentValue = currentProgress,
MaximumValue = maximumProgress,
Description = $"Extracting {entry.FullName} - skipped",
Description = string.Format(Language.Instance.Controls!["progress_extracting_skipped"], entry.FullName),
});
}
@ -161,7 +161,7 @@ public static class InstallUtils
}
catch (Exception e)
{
throw new ApplicationException($"Could not extract ZIP:\n{e.Message}");
throw new ApplicationException(e.Message);
}
}

View file

@ -2,7 +2,7 @@ using TRX_InstallerLib.Utils;
namespace TRX_InstallerLib.Models;
public class FinishStep : BaseNotifyPropertyChanged, IStep
public class FinishStep : BaseLanguageViewModel, IStep
{
public FinishStep(FinishSettings finishSettings)
{

View file

@ -3,7 +3,7 @@ using TRX_InstallerLib.Utils;
namespace TRX_InstallerLib.Models;
public class InstallSettingsStep : BaseNotifyPropertyChanged, IStep
public class InstallSettingsStep : BaseLanguageViewModel, IStep
{
public InstallSettingsStep(InstallSettings installSettings)
{
@ -24,7 +24,7 @@ public class InstallSettingsStep : BaseNotifyPropertyChanged, IStep
public ICommand ChooseLocationCommand
{
get => _chooseLocationCommand ??= new RelayCommand(ChooseLocation);
}
}
private void ChooseLocation()
{

View file

@ -4,7 +4,7 @@ using TRX_InstallerLib.Utils;
namespace TRX_InstallerLib.Models;
public class InstallSourceViewModel : BaseNotifyPropertyChanged
public class InstallSourceViewModel : BaseLanguageViewModel
{
public InstallSourceViewModel(IInstallSource source)
{
@ -38,6 +38,11 @@ public class InstallSourceViewModel : BaseNotifyPropertyChanged
}
}
public bool IsSourceDirectoryDefined
{
get => SourceDirectory != null;
}
public string? SourceDirectory
{
get => _sourceDirectory;
@ -48,6 +53,7 @@ public class InstallSourceViewModel : BaseNotifyPropertyChanged
_sourceDirectory = value;
NotifyPropertyChanged();
NotifyPropertyChanged(nameof(IsAvailable));
NotifyPropertyChanged(nameof(IsSourceDirectoryDefined));
}
}
}

View file

@ -4,7 +4,7 @@ using TRX_InstallerLib.Utils;
namespace TRX_InstallerLib.Models;
public class InstallStep : BaseNotifyPropertyChanged, IStep
public class InstallStep : BaseLanguageViewModel, IStep
{
public InstallStep(InstallSettings installSettings)
{

View file

@ -13,7 +13,7 @@ public class Language
public static Language Instance { get; private set; }
public Dictionary<string, string>? Controls { get; set; }
static Language()
{
CultureInfo defaultCulture = CultureInfo.GetCultureInfo(_defaultCulture);

View file

@ -4,7 +4,7 @@ using TRX_InstallerLib.Utils;
namespace TRX_InstallerLib.Models;
public class SourceStep : BaseNotifyPropertyChanged, IStep
public class SourceStep : BaseLanguageViewModel, IStep
{
public SourceStep(IEnumerable<IInstallSource> installSources)
{

View file

@ -1,5 +1,56 @@
{
"Controls": {
"command_back": "_Back",
"command_next": "_Next",
"command_close": "_Close",
"command_cancel": "_Cancel",
"command_change": "C_hange...",
"command_change_link": "(change)",
"label_found": "Found",
"label_not_found": "Not found",
"label_already_found": "(already found)",
"label_folder_not_selected": "(no folder selected)",
"label_destination_folder": "Destination folder:",
"label_select_folder": "Choose directory",
"step_source_heading": "Step 1: Choose installation source",
"step_source_content": "Placeholder",
"step_settings_heading": "Step 2: Installation options",
"step_settings_music_heading": "Download music tracks",
"step_settings_music_content": "Placeholder",
"step_settings_expansion_heading": "Placeholder",
"step_settings_expansion_content": "Placeholder",
"step_settings_expansion_music": "Placeholder",
"step_settings_expansion_vanilla": "Placeholder",
"step_settings_saves_header": "Import saves",
"step_settings_saves_content": "Placeholder",
"step_settings_shortcut_heading": "Create desktop shortcut",
"step_install_heading": "Step 3: Installing",
"step_finish_heading": "Step 4: Done",
"step_finish_content": "Installation complete. To configure more advanced features, you can edit the JSON files in the cfg/ directory with a text editor.\n\nHappy raiding :)",
"step_finish_open_directory": "Open game directory after closing this window",
"step_finish_open_game": "Launch the game after closing this window",
"progress_scanning": "Scanning directory",
"progress_scanning_source": "Scanning the source directory",
"progress_preparing_extract": "Preparing to extract the ISO",
"progress_copying": "Copying {0}",
"progress_skipped": "Copying {0} - skipped",
"progress_init_download": "Initializing download of {0}",
"progress_downloading": "Downloading {0}",
"progress_opening_zip": "Opening embedded ZIP",
"progress_zip_failure": "Could not open embedded ZIP.",
"progress_scanning_zip": "Scanning ZIP",
"progress_extracting": "Extracting {0}",
"progress_extracting_skipped": "Extracting {0} - skipped",
"progress_converting_bin": "Converting BIN to ISO",
"progress_converting_bin_failure": "Could not convert BIN to ISO: {0}",
"progress_converting_iso_failure": "Could not open converted ISO: {0}",
"progress_track_write_failure": "Could not write to track file {0}: {1}",
"progress_track_seek_failure": "Could not seek to track location: {0}",
"progress_bin_failure": "Could not open BIN {0}: {1}",
"progress_cue_failure": "Could not read CUE {0}: {1}",
"progress_cue_empty": "Could not parse {0}: no tracks were found",
"progress_finished": "Finished",
"shortcut_signature_failure": "Invalid LNK signature",
"shortcut_target_failure": "Unable to determine link target path"
}
}

View file

@ -8,6 +8,9 @@
<Style TargetType="{x:Type Button}" BasedOn="{StaticResource ButtonStyle}" />
<Style x:Key="heading" TargetType="TextBlock">
<Setter Property="FontSize" Value="20" />
<Setter Property="TextWrapping" Value="Wrap" />
<Setter Property="VerticalAlignment" Value="Center" />
<Setter Property="Margin" Value="0,0,0,12" />
</Style>
<Style x:Key="subHeading" TargetType="TextBlock">
<Setter Property="FontSize" Value="15" />

View file

@ -9,9 +9,11 @@
</PropertyGroup>
<ItemGroup>
<None Remove="Resources\const.json" />
<None Remove="Resources\Lang\en.json" />
</ItemGroup>
<ItemGroup>
<EmbeddedResource Include="Resources\const.json" />
<EmbeddedResource Include="Resources\Lang\en.json" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="Newtonsoft.Json" Version="13.0.3" />

View file

@ -4,12 +4,12 @@ using System.Windows.Markup;
namespace TRX_InstallerLib.Utils;
public sealed class ConditionalMarkupConverter : MarkupExtension, IValueConverter
public class ConditionalMarkupConverter : MarkupExtension, IValueConverter
{
public object FalseValue { get; set; } = new();
public object TrueValue { get; set; } = new();
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
public virtual object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
return value is true ? TrueValue : FalseValue;
}

View file

@ -0,0 +1,13 @@
using System.Globalization;
using TRX_InstallerLib.Models;
namespace TRX_InstallerLib.Utils;
public class ConditionalViewTextConverter : ConditionalMarkupConverter
{
public override object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
string result = base.Convert(value, targetType, parameter, culture).ToString()!;
return result.Length == 0 ? string.Empty : Language.Instance.Controls![result];
}
}

View file

@ -1,5 +1,6 @@
using System.IO;
using System.Text.RegularExpressions;
using TRX_InstallerLib.Models;
namespace TRX_InstallerLib.Utils;
@ -19,7 +20,7 @@ public class CueFile
MatchCollection fileMatches = _fileGroupRegex.Matches(cueFileContent);
if (fileMatches.Count == 0)
{
throw new ApplicationException($"Could not parse {cueFilePath}: no tracks were found");
throw new ApplicationException(string.Format(Language.Instance.Controls!["progress_cue_empty"], cueFilePath));
}
foreach (Match fileMatch in fileMatches.Cast<Match>())
@ -29,7 +30,7 @@ public class CueFile
if (matches.Count == 0)
{
throw new ApplicationException($"Could not parse {cueFilePath}: no tracks were found");
throw new ApplicationException(string.Format(Language.Instance.Controls!["progress_cue_empty"], cueFilePath));
}
CueTrack? track = null;

View file

@ -1,4 +1,5 @@
using System.IO;
using TRX_InstallerLib.Models;
namespace TRX_InstallerLib.Utils;
@ -78,7 +79,7 @@ public class CueTrack
{
MaximumValue = (int)TotalBytes,
CurrentValue = (int)convertedBytes,
Description = "Converting BIN to ISO"
Description = Language.Instance.Controls!["progress_converting_bin"],
});
}
@ -87,14 +88,14 @@ public class CueTrack
}
catch (Exception e)
{
throw new ApplicationException(string.Format(" Could not write to track file {0}: {1}", targetPath, e.Message));
throw new ApplicationException(string.Format(Language.Instance.Controls!["progress_track_write_failure"], targetPath, e.Message));
}
progress.Report(new InstallProgress
{
MaximumValue = (int)TotalBytes,
CurrentValue = (int)TotalBytes,
Description = "Converting BIN to ISO",
Description = Language.Instance.Controls!["progress_converting_bin"],
});
}
@ -169,7 +170,7 @@ public class CueTrack
}
catch (Exception e)
{
throw new ApplicationException($"Could not open BIN {BinFilePath}: {e.Message}");
throw new ApplicationException(string.Format(Language.Instance.Controls!["progress_bin_failure"], BinFilePath, e.Message));
}
try
{
@ -177,7 +178,7 @@ public class CueTrack
}
catch (Exception e)
{
throw new ApplicationException(string.Format("Could not seek to track location: {0}", e.Message));
throw new ApplicationException(string.Format(Language.Instance.Controls!["progress_track_seek_failure"], e.Message));
}
return fileStream;
}

View file

@ -1,3 +1,5 @@
using TRX_InstallerLib.Models;
namespace TRX_InstallerLib.Utils;
public class FileBrowser
@ -6,7 +8,7 @@ public class FileBrowser
{
using var dlg = new FolderBrowserDialog()
{
Description = "Choose directory",
Description = Language.Instance.Controls!["label_select_folder"],
SelectedPath = initialDirectory,
ShowNewFolderButton = true,
};

View file

@ -2,6 +2,7 @@ using System.IO;
using System.Text;
using System.Text.RegularExpressions;
using TRX_InstallerLib.Models;
namespace TRX_InstallerLib.Utils;
@ -146,7 +147,7 @@ public static class ShortcutUtils
var headerSize = br.ReadUInt32();
if (headerSize != 0x4C)
{
throw new ApplicationException("Invalid LNK signature");
throw new ApplicationException(Language.Instance.Controls!["shortcut_signature_failure"]);
}
br.ReadBytes(0x10); // skip LinkCLSID
@ -230,6 +231,6 @@ public static class ShortcutUtils
return Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments), relativePath);
}
throw new ApplicationException("Unable to determine link target path");
throw new ApplicationException(Language.Instance.Controls!["shortcut_target_failure"]);
}
}