diff --git a/Directory.Packages.props b/Directory.Packages.props index 5f434bf17..ea1fc712f 100644 --- a/Directory.Packages.props +++ b/Directory.Packages.props @@ -50,5 +50,7 @@ <PackageVersion Include="System.Net.NameResolution" Version="4.3.0" /> <PackageVersion Include="System.Threading.ThreadPool" Version="4.3.0" /> <PackageVersion Include="XamlNameReferenceGenerator" Version="1.5.1" /> + <PackageVersion Include="Microsoft.CodeAnalysis.Analyzers" Version="3.3.3"/> + <PackageVersion Include="Microsoft.CodeAnalysis.CSharp" Version="4.4.0" /> </ItemGroup> </Project> diff --git a/Ryujinx.Ava/App.axaml.cs b/Ryujinx.Ava/App.axaml.cs index e59f9bd39..e36cbfdd6 100644 --- a/Ryujinx.Ava/App.axaml.cs +++ b/Ryujinx.Ava/App.axaml.cs @@ -59,11 +59,11 @@ namespace Ryujinx.Ava if (ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop) { var result = await ContentDialogHelper.CreateConfirmationDialog( - LocaleManager.Instance["DialogThemeRestartMessage"], - LocaleManager.Instance["DialogThemeRestartSubMessage"], - LocaleManager.Instance["InputDialogYes"], - LocaleManager.Instance["InputDialogNo"], - LocaleManager.Instance["DialogRestartRequiredMessage"]); + LocaleManager.Instance[LocaleKeys.DialogThemeRestartMessage], + LocaleManager.Instance[LocaleKeys.DialogThemeRestartSubMessage], + LocaleManager.Instance[LocaleKeys.InputDialogYes], + LocaleManager.Instance[LocaleKeys.InputDialogNo], + LocaleManager.Instance[LocaleKeys.DialogRestartRequiredMessage]); if (result == UserResult.Yes) { diff --git a/Ryujinx.Ava/AppHost.cs b/Ryujinx.Ava/AppHost.cs index f8bd032cb..0baa94c3b 100644 --- a/Ryujinx.Ava/AppHost.cs +++ b/Ryujinx.Ava/AppHost.cs @@ -432,10 +432,10 @@ namespace Ryujinx.Ava if (userError == UserError.NoFirmware) { UserResult result = await ContentDialogHelper.CreateConfirmationDialog( - LocaleManager.Instance["DialogFirmwareNoFirmwareInstalledMessage"], - string.Format(LocaleManager.Instance["DialogFirmwareInstallEmbeddedMessage"], firmwareVersion.VersionString), - LocaleManager.Instance["InputDialogYes"], - LocaleManager.Instance["InputDialogNo"], + LocaleManager.Instance[LocaleKeys.DialogFirmwareNoFirmwareInstalledMessage], + string.Format(LocaleManager.Instance[LocaleKeys.DialogFirmwareInstallEmbeddedMessage], firmwareVersion.VersionString), + LocaleManager.Instance[LocaleKeys.InputDialogYes], + LocaleManager.Instance[LocaleKeys.InputDialogNo], ""); if (result != UserResult.Yes) @@ -463,11 +463,11 @@ namespace Ryujinx.Ava _parent.RefreshFirmwareStatus(); await ContentDialogHelper.CreateInfoDialog( - string.Format(LocaleManager.Instance["DialogFirmwareInstalledMessage"], firmwareVersion.VersionString), - string.Format(LocaleManager.Instance["DialogFirmwareInstallEmbeddedSuccessMessage"], firmwareVersion.VersionString), - LocaleManager.Instance["InputDialogOk"], + string.Format(LocaleManager.Instance[LocaleKeys.DialogFirmwareInstalledMessage], firmwareVersion.VersionString), + string.Format(LocaleManager.Instance[LocaleKeys.DialogFirmwareInstallEmbeddedSuccessMessage], firmwareVersion.VersionString), + LocaleManager.Instance[LocaleKeys.InputDialogOk], "", - LocaleManager.Instance["RyujinxInfo"]); + LocaleManager.Instance[LocaleKeys.RyujinxInfo]); } } else @@ -869,7 +869,7 @@ namespace Ryujinx.Ava public void UpdateStatus() { // Run a status update only when a frame is to be drawn. This prevents from updating the ui and wasting a render when no frame is queued - string dockedMode = ConfigurationState.Instance.System.EnableDockedMode ? LocaleManager.Instance["Docked"] : LocaleManager.Instance["Handheld"]; + string dockedMode = ConfigurationState.Instance.System.EnableDockedMode ? LocaleManager.Instance[LocaleKeys.Docked] : LocaleManager.Instance[LocaleKeys.Handheld]; float scale = GraphicsConfig.ResScale; if (scale != 1) @@ -879,11 +879,11 @@ namespace Ryujinx.Ava StatusUpdatedEvent?.Invoke(this, new StatusUpdatedEventArgs( Device.EnableDeviceVsync, - LocaleManager.Instance["VolumeShort"] + $": {(int)(Device.GetVolume() * 100)}%", + LocaleManager.Instance[LocaleKeys.VolumeShort] + $": {(int)(Device.GetVolume() * 100)}%", Renderer.IsVulkan ? "Vulkan" : "OpenGL", dockedMode, ConfigurationState.Instance.Graphics.AspectRatio.Value.ToText(), - LocaleManager.Instance["Game"] + $": {Device.Statistics.GetGameFrameRate():00.00} FPS ({Device.Statistics.GetGameFrameTime():00.00} ms)", + LocaleManager.Instance[LocaleKeys.Game] + $": {Device.Statistics.GetGameFrameRate():00.00} FPS ({Device.Statistics.GetGameFrameTime():00.00} ms)", $"FIFO: {Device.Statistics.GetFifoPercent():00.00} %", $"GPU: {_renderer.GetHardwareInfo().GpuVendor}")); } diff --git a/Ryujinx.Ava/Common/ApplicationHelper.cs b/Ryujinx.Ava/Common/ApplicationHelper.cs index 0c562dfe0..8e5bdaec7 100644 --- a/Ryujinx.Ava/Common/ApplicationHelper.cs +++ b/Ryujinx.Ava/Common/ApplicationHelper.cs @@ -81,7 +81,7 @@ namespace Ryujinx.Ava.Common Dispatcher.UIThread.Post(async () => { await ContentDialogHelper.CreateErrorDialog( - string.Format(LocaleManager.Instance["DialogMessageCreateSaveErrorMessage"], result.ToStringWithName())); + string.Format(LocaleManager.Instance[LocaleKeys.DialogMessageCreateSaveErrorMessage], result.ToStringWithName())); }); return false; @@ -100,7 +100,7 @@ namespace Ryujinx.Ava.Common Dispatcher.UIThread.Post(async () => { - await ContentDialogHelper.CreateErrorDialog(string.Format(LocaleManager.Instance["DialogMessageFindSaveErrorMessage"], result.ToStringWithName())); + await ContentDialogHelper.CreateErrorDialog(string.Format(LocaleManager.Instance[LocaleKeys.DialogMessageFindSaveErrorMessage], result.ToStringWithName())); }); return false; @@ -151,7 +151,7 @@ namespace Ryujinx.Ava.Common public static async Task ExtractSection(NcaSectionType ncaSectionType, string titleFilePath, int programIndex = 0) { - OpenFolderDialog folderDialog = new() { Title = LocaleManager.Instance["FolderDialogExtractTitle"] }; + OpenFolderDialog folderDialog = new() { Title = LocaleManager.Instance[LocaleKeys.FolderDialogExtractTitle] }; string destination = await folderDialog.ShowAsync(_owner); @@ -164,11 +164,11 @@ namespace Ryujinx.Ava.Common Dispatcher.UIThread.Post(async () => { UserResult result = await ContentDialogHelper.CreateConfirmationDialog( - string.Format(LocaleManager.Instance["DialogNcaExtractionMessage"], ncaSectionType, Path.GetFileName(titleFilePath)), + string.Format(LocaleManager.Instance[LocaleKeys.DialogNcaExtractionMessage], ncaSectionType, Path.GetFileName(titleFilePath)), "", "", - LocaleManager.Instance["InputDialogCancel"], - LocaleManager.Instance["DialogNcaExtractionTitle"]); + LocaleManager.Instance[LocaleKeys.InputDialogCancel], + LocaleManager.Instance[LocaleKeys.DialogNcaExtractionTitle]); if (result == UserResult.Cancel) { @@ -234,7 +234,7 @@ namespace Ryujinx.Ava.Common "Extraction failure. The main NCA was not present in the selected file"); Dispatcher.UIThread.InvokeAsync(async () => { - await ContentDialogHelper.CreateErrorDialog(LocaleManager.Instance["DialogNcaExtractionMainNcaNotFoundErrorMessage"]); + await ContentDialogHelper.CreateErrorDialog(LocaleManager.Instance[LocaleKeys.DialogNcaExtractionMainNcaNotFoundErrorMessage]); }); return; } @@ -275,7 +275,7 @@ namespace Ryujinx.Ava.Common $"LibHac returned error code: {resultCode.Value.ErrorCode}"); Dispatcher.UIThread.InvokeAsync(async () => { - await ContentDialogHelper.CreateErrorDialog(LocaleManager.Instance["DialogNcaExtractionCheckLogErrorMessage"]); + await ContentDialogHelper.CreateErrorDialog(LocaleManager.Instance[LocaleKeys.DialogNcaExtractionCheckLogErrorMessage]); }); } else if (resultCode.Value.IsSuccess()) @@ -283,11 +283,11 @@ namespace Ryujinx.Ava.Common Dispatcher.UIThread.InvokeAsync(async () => { await ContentDialogHelper.CreateInfoDialog( - LocaleManager.Instance["DialogNcaExtractionSuccessMessage"], + LocaleManager.Instance[LocaleKeys.DialogNcaExtractionSuccessMessage], "", - LocaleManager.Instance["InputDialogOk"], + LocaleManager.Instance[LocaleKeys.InputDialogOk], "", - LocaleManager.Instance["DialogNcaExtractionTitle"]); + LocaleManager.Instance[LocaleKeys.DialogNcaExtractionTitle]); }); } } diff --git a/Ryujinx.Ava/Common/Locale/LocaleExtension.cs b/Ryujinx.Ava/Common/Locale/LocaleExtension.cs index 8fca1a00d..6c54becdc 100644 --- a/Ryujinx.Ava/Common/Locale/LocaleExtension.cs +++ b/Ryujinx.Ava/Common/Locale/LocaleExtension.cs @@ -7,16 +7,16 @@ namespace Ryujinx.Ava.Common.Locale { internal class LocaleExtension : MarkupExtension { - public LocaleExtension(string key) + public LocaleExtension(LocaleKeys key) { Key = key; } - public string Key { get; } + public LocaleKeys Key { get; } public override object ProvideValue(IServiceProvider serviceProvider) { - string keyToUse = Key; + LocaleKeys keyToUse = Key; ReflectionBindingExtension binding = new($"[{keyToUse}]") { diff --git a/Ryujinx.Ava/Common/Locale/LocaleManager.cs b/Ryujinx.Ava/Common/Locale/LocaleManager.cs index acbbf2dff..c2251f851 100644 --- a/Ryujinx.Ava/Common/Locale/LocaleManager.cs +++ b/Ryujinx.Ava/Common/Locale/LocaleManager.cs @@ -2,6 +2,7 @@ using Ryujinx.Common; using Ryujinx.Common.Utilities; using Ryujinx.Ui.Common.Configuration; +using System; using System.Collections.Concurrent; using System.Collections.Generic; using System.Globalization; @@ -13,17 +14,17 @@ namespace Ryujinx.Ava.Common.Locale { private const string DefaultLanguageCode = "en_US"; - private Dictionary<string, string> _localeStrings; - private ConcurrentDictionary<string, object[]> _dynamicValues; + private Dictionary<LocaleKeys, string> _localeStrings; + private ConcurrentDictionary<LocaleKeys, object[]> _dynamicValues; public static LocaleManager Instance { get; } = new LocaleManager(); - public Dictionary<string, string> LocaleStrings { get => _localeStrings; set => _localeStrings = value; } + public Dictionary<LocaleKeys, string> LocaleStrings { get => _localeStrings; set => _localeStrings = value; } public LocaleManager() { - _localeStrings = new Dictionary<string, string>(); - _dynamicValues = new ConcurrentDictionary<string, object[]>(); + _localeStrings = new Dictionary<LocaleKeys, string>(); + _dynamicValues = new ConcurrentDictionary<LocaleKeys, object[]>(); Load(); } @@ -49,7 +50,7 @@ namespace Ryujinx.Ava.Common.Locale } } - public string this[string key] + public string this[LocaleKeys key] { get { @@ -63,7 +64,7 @@ namespace Ryujinx.Ava.Common.Locale return value; } - return key; + return key.ToString(); } set { @@ -73,7 +74,7 @@ namespace Ryujinx.Ava.Common.Locale } } - public void UpdateDynamicValue(string key, params object[] values) + public void UpdateDynamicValue(LocaleKeys key, params object[] values) { _dynamicValues[key] = values; @@ -98,7 +99,10 @@ namespace Ryujinx.Ava.Common.Locale foreach (var item in strings) { - this[item.Key] = item.Value; + if (Enum.TryParse<LocaleKeys>(item.Key, out var key)) + { + this[key] = item.Value; + } } if (Program.PreviewerDetached) diff --git a/Ryujinx.Ava/Input/AvaloniaKeyboardDriver.cs b/Ryujinx.Ava/Input/AvaloniaKeyboardDriver.cs index 31a53c32c..b107898e4 100644 --- a/Ryujinx.Ava/Input/AvaloniaKeyboardDriver.cs +++ b/Ryujinx.Ava/Input/AvaloniaKeyboardDriver.cs @@ -56,7 +56,7 @@ namespace Ryujinx.Ava.Input return null; } - return new AvaloniaKeyboard(this, _keyboardIdentifers[0], LocaleManager.Instance["AllKeyboards"]); + return new AvaloniaKeyboard(this, _keyboardIdentifers[0], LocaleManager.Instance[LocaleKeys.AllKeyboards]); } protected virtual void Dispose(bool disposing) diff --git a/Ryujinx.Ava/Modules/Updater/Updater.cs b/Ryujinx.Ava/Modules/Updater/Updater.cs index d495131f0..7bf147fe4 100644 --- a/Ryujinx.Ava/Modules/Updater/Updater.cs +++ b/Ryujinx.Ava/Modules/Updater/Updater.cs @@ -84,7 +84,7 @@ namespace Ryujinx.Modules Logger.Error?.Print(LogClass.Application, "Failed to convert the current Ryujinx version!"); Dispatcher.UIThread.Post(async () => { - await ContentDialogHelper.CreateWarningDialog(LocaleManager.Instance["DialogUpdaterConvertFailedMessage"], LocaleManager.Instance["DialogUpdaterCancelUpdateMessage"]); + await ContentDialogHelper.CreateWarningDialog(LocaleManager.Instance[LocaleKeys.DialogUpdaterConvertFailedMessage], LocaleManager.Instance[LocaleKeys.DialogUpdaterCancelUpdateMessage]); }); return; @@ -119,7 +119,7 @@ namespace Ryujinx.Modules { Dispatcher.UIThread.Post(async () => { - await ContentDialogHelper.CreateUpdaterInfoDialog(LocaleManager.Instance["DialogUpdaterAlreadyOnLatestVersionMessage"], ""); + await ContentDialogHelper.CreateUpdaterInfoDialog(LocaleManager.Instance[LocaleKeys.DialogUpdaterAlreadyOnLatestVersionMessage], ""); }); } @@ -137,7 +137,7 @@ namespace Ryujinx.Modules { Dispatcher.UIThread.Post(async () => { - await ContentDialogHelper.CreateUpdaterInfoDialog(LocaleManager.Instance["DialogUpdaterAlreadyOnLatestVersionMessage"], ""); + await ContentDialogHelper.CreateUpdaterInfoDialog(LocaleManager.Instance[LocaleKeys.DialogUpdaterAlreadyOnLatestVersionMessage], ""); }); } @@ -150,7 +150,7 @@ namespace Ryujinx.Modules Logger.Error?.Print(LogClass.Application, exception.Message); Dispatcher.UIThread.Post(async () => { - await ContentDialogHelper.CreateErrorDialog(LocaleManager.Instance["DialogUpdaterFailedToGetVersionMessage"]); + await ContentDialogHelper.CreateErrorDialog(LocaleManager.Instance[LocaleKeys.DialogUpdaterFailedToGetVersionMessage]); }); return; @@ -165,7 +165,7 @@ namespace Ryujinx.Modules Logger.Error?.Print(LogClass.Application, "Failed to convert the received Ryujinx version from Github!"); Dispatcher.UIThread.Post(async () => { - await ContentDialogHelper.CreateWarningDialog(LocaleManager.Instance["DialogUpdaterConvertFailedGithubMessage"], LocaleManager.Instance["DialogUpdaterCancelUpdateMessage"]); + await ContentDialogHelper.CreateWarningDialog(LocaleManager.Instance[LocaleKeys.DialogUpdaterConvertFailedGithubMessage], LocaleManager.Instance[LocaleKeys.DialogUpdaterCancelUpdateMessage]); }); return; @@ -177,7 +177,7 @@ namespace Ryujinx.Modules { Dispatcher.UIThread.Post(async () => { - await ContentDialogHelper.CreateUpdaterInfoDialog(LocaleManager.Instance["DialogUpdaterAlreadyOnLatestVersionMessage"], ""); + await ContentDialogHelper.CreateUpdaterInfoDialog(LocaleManager.Instance[LocaleKeys.DialogUpdaterAlreadyOnLatestVersionMessage], ""); }); } @@ -210,8 +210,8 @@ namespace Ryujinx.Modules Dispatcher.UIThread.Post(async () => { // Show a message asking the user if they want to update - var shouldUpdate = await ContentDialogHelper.CreateChoiceDialog(LocaleManager.Instance["RyujinxUpdater"], - LocaleManager.Instance["RyujinxUpdaterMessage"], + var shouldUpdate = await ContentDialogHelper.CreateChoiceDialog(LocaleManager.Instance[LocaleKeys.RyujinxUpdater], + LocaleManager.Instance[LocaleKeys.RyujinxUpdaterMessage], $"{Program.Version} -> {newVersion}"); if (shouldUpdate) @@ -247,8 +247,8 @@ namespace Ryujinx.Modules var taskDialog = new TaskDialog() { - Header = LocaleManager.Instance["RyujinxUpdater"], - SubHeader = LocaleManager.Instance["UpdaterDownloading"], + Header = LocaleManager.Instance[LocaleKeys.RyujinxUpdater], + SubHeader = LocaleManager.Instance[LocaleKeys.UpdaterDownloading], IconSource = new SymbolIconSource { Symbol = Symbol.Download }, Buttons = { }, ShowProgressBar = true @@ -272,9 +272,9 @@ namespace Ryujinx.Modules if (UpdateSuccessful) { - var shouldRestart = await ContentDialogHelper.CreateChoiceDialog(LocaleManager.Instance["RyujinxUpdater"], - LocaleManager.Instance["DialogUpdaterCompleteMessage"], - LocaleManager.Instance["DialogUpdaterRestartMessage"]); + var shouldRestart = await ContentDialogHelper.CreateChoiceDialog(LocaleManager.Instance[LocaleKeys.RyujinxUpdater], + LocaleManager.Instance[LocaleKeys.DialogUpdaterCompleteMessage], + LocaleManager.Instance[LocaleKeys.DialogUpdaterRestartMessage]); if (shouldRestart) { @@ -478,7 +478,7 @@ namespace Ryujinx.Modules private static async void InstallUpdate(TaskDialog taskDialog, string updateFile) { // Extract Update - taskDialog.SubHeader = LocaleManager.Instance["UpdaterExtracting"]; + taskDialog.SubHeader = LocaleManager.Instance[LocaleKeys.UpdaterExtracting]; taskDialog.SetProgressBarState(0, TaskDialogProgressState.Normal); if (OperatingSystem.IsLinux()) @@ -556,7 +556,7 @@ namespace Ryujinx.Modules List<string> allFiles = EnumerateFilesToDelete().ToList(); - taskDialog.SubHeader = LocaleManager.Instance["UpdaterRenaming"]; + taskDialog.SubHeader = LocaleManager.Instance[LocaleKeys.UpdaterRenaming]; taskDialog.SetProgressBarState(0, TaskDialogProgressState.Normal); // Replace old files @@ -577,13 +577,13 @@ namespace Ryujinx.Modules } catch { - Logger.Warning?.Print(LogClass.Application, string.Format(LocaleManager.Instance["UpdaterRenameFailed"], file)); + Logger.Warning?.Print(LogClass.Application, string.Format(LocaleManager.Instance[LocaleKeys.UpdaterRenameFailed], file)); } } Dispatcher.UIThread.Post(() => { - taskDialog.SubHeader = LocaleManager.Instance["UpdaterAddingFiles"]; + taskDialog.SubHeader = LocaleManager.Instance[LocaleKeys.UpdaterAddingFiles]; taskDialog.SetProgressBarState(0, TaskDialogProgressState.Normal); }); @@ -607,8 +607,8 @@ namespace Ryujinx.Modules { if (showWarnings) { - ContentDialogHelper.CreateWarningDialog(LocaleManager.Instance["DialogUpdaterArchNotSupportedMessage"], - LocaleManager.Instance["DialogUpdaterArchNotSupportedSubMessage"]); + ContentDialogHelper.CreateWarningDialog(LocaleManager.Instance[LocaleKeys.DialogUpdaterArchNotSupportedMessage], + LocaleManager.Instance[LocaleKeys.DialogUpdaterArchNotSupportedSubMessage]); } return false; @@ -618,8 +618,8 @@ namespace Ryujinx.Modules { if (showWarnings) { - ContentDialogHelper.CreateWarningDialog(LocaleManager.Instance["DialogUpdaterNoInternetMessage"], - LocaleManager.Instance["DialogUpdaterNoInternetSubMessage"]); + ContentDialogHelper.CreateWarningDialog(LocaleManager.Instance[LocaleKeys.DialogUpdaterNoInternetMessage], + LocaleManager.Instance[LocaleKeys.DialogUpdaterNoInternetSubMessage]); } return false; @@ -629,8 +629,8 @@ namespace Ryujinx.Modules { if (showWarnings) { - ContentDialogHelper.CreateWarningDialog(LocaleManager.Instance["DialogUpdaterDirtyBuildMessage"], - LocaleManager.Instance["DialogUpdaterDirtyBuildSubMessage"]); + ContentDialogHelper.CreateWarningDialog(LocaleManager.Instance[LocaleKeys.DialogUpdaterDirtyBuildMessage], + LocaleManager.Instance[LocaleKeys.DialogUpdaterDirtyBuildSubMessage]); } return false; @@ -642,11 +642,11 @@ namespace Ryujinx.Modules { if (ReleaseInformations.IsFlatHubBuild()) { - ContentDialogHelper.CreateWarningDialog(LocaleManager.Instance["UpdaterDisabledWarningTitle"], LocaleManager.Instance["DialogUpdaterFlatpakNotSupportedMessage"]); + ContentDialogHelper.CreateWarningDialog(LocaleManager.Instance[LocaleKeys.UpdaterDisabledWarningTitle], LocaleManager.Instance[LocaleKeys.DialogUpdaterFlatpakNotSupportedMessage]); } else { - ContentDialogHelper.CreateWarningDialog(LocaleManager.Instance["UpdaterDisabledWarningTitle"], LocaleManager.Instance["DialogUpdaterDirtyBuildSubMessage"]); + ContentDialogHelper.CreateWarningDialog(LocaleManager.Instance[LocaleKeys.UpdaterDisabledWarningTitle], LocaleManager.Instance[LocaleKeys.DialogUpdaterDirtyBuildSubMessage]); } } diff --git a/Ryujinx.Ava/Ryujinx.Ava.csproj b/Ryujinx.Ava/Ryujinx.Ava.csproj index 6da118491..3e3bdc8c7 100644 --- a/Ryujinx.Ava/Ryujinx.Ava.csproj +++ b/Ryujinx.Ava/Ryujinx.Ava.csproj @@ -58,6 +58,7 @@ <ProjectReference Include="..\Ryujinx.Graphics.OpenGL\Ryujinx.Graphics.OpenGL.csproj" /> <ProjectReference Include="..\Ryujinx.Graphics.Gpu\Ryujinx.Graphics.Gpu.csproj" /> <ProjectReference Include="..\Ryujinx.Ui.Common\Ryujinx.Ui.Common.csproj" /> + <ProjectReference Include="..\Ryujinx.Ui.LocaleGenerator\Ryujinx.Ui.LocaleGenerator.csproj" OutputItemType="Analyzer" ReferenceOutputAssembly="false" /> </ItemGroup> <ItemGroup> @@ -158,4 +159,7 @@ <EmbeddedResource Include="Assets\Locales\zh_TW.json" /> <EmbeddedResource Include="Assets\Styles\Styles.xaml" /> </ItemGroup> + <ItemGroup> + <AdditionalFiles Include="Assets\Locales\en_US.json" /> + </ItemGroup> </Project> diff --git a/Ryujinx.Ui.LocaleGenerator/LocaleGenerator.cs b/Ryujinx.Ui.LocaleGenerator/LocaleGenerator.cs new file mode 100644 index 000000000..f154e704c --- /dev/null +++ b/Ryujinx.Ui.LocaleGenerator/LocaleGenerator.cs @@ -0,0 +1,30 @@ +using Microsoft.CodeAnalysis; +using System.Linq; + +namespace Ryujinx.Ui.LocaleGenerator +{ + [Generator] + public class LocaleGenerator : IIncrementalGenerator + { + public void Initialize(IncrementalGeneratorInitializationContext context) + { + var englishLocaleFile = context.AdditionalTextsProvider.Where(static x => x.Path.EndsWith("en_US.json")); + + IncrementalValuesProvider<string> contents = englishLocaleFile.Select((text, cancellationToken) => text.GetText(cancellationToken)!.ToString()); + + context.RegisterSourceOutput(contents, (spc, content) => + { + var lines = content.Split('\n').Where(x => x.Trim().StartsWith("\"")).Select(x => x.Split(':').First().Trim().Replace("\"", "")); + string enumSource = "namespace Ryujinx.Ava.Common.Locale;\n"; + enumSource += "internal enum LocaleKeys\n{\n"; + foreach (var line in lines) + { + enumSource += $" {line},\n"; + } + enumSource += "}\n"; + + spc.AddSource("LocaleKeys", enumSource); + }); + } + } +} diff --git a/Ryujinx.Ui.LocaleGenerator/Ryujinx.Ui.LocaleGenerator.csproj b/Ryujinx.Ui.LocaleGenerator/Ryujinx.Ui.LocaleGenerator.csproj new file mode 100644 index 000000000..0960455d5 --- /dev/null +++ b/Ryujinx.Ui.LocaleGenerator/Ryujinx.Ui.LocaleGenerator.csproj @@ -0,0 +1,20 @@ +<Project Sdk="Microsoft.NET.Sdk"> + + <PropertyGroup> + <TargetFramework>netstandard2.0</TargetFramework> + <Nullable>enable</Nullable> + <EmitCompilerGeneratedFiles>true</EmitCompilerGeneratedFiles> + <CompilerGeneratedFilesOutputPath>Generated</CompilerGeneratedFilesOutputPath> + <IsRoslynComponent>true</IsRoslynComponent> + <LangVersion>latest</LangVersion> + </PropertyGroup> + + <ItemGroup> + <PackageReference Include="Microsoft.CodeAnalysis.Analyzers"> + <PrivateAssets>all</PrivateAssets> + <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets> + </PackageReference> + <PackageReference Include="Microsoft.CodeAnalysis.CSharp"/> + </ItemGroup> + +</Project> diff --git a/Ryujinx.sln b/Ryujinx.sln index 0915ef987..1eaf006c3 100644 --- a/Ryujinx.sln +++ b/Ryujinx.sln @@ -73,12 +73,14 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ryujinx.Ava", "Ryujinx.Ava\ EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ryujinx.Ui.Common", "Ryujinx.Ui.Common\Ryujinx.Ui.Common.csproj", "{BA161CA0-CD65-4E6E-B644-51C8D1E542DC}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Ryujinx.Horizon.Generators", "Ryujinx.Horizon.Generators\Ryujinx.Horizon.Generators.csproj", "{6AE2A5E8-4C5A-48B9-997B-E1455C0355C6}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ryujinx.Horizon.Generators", "Ryujinx.Horizon.Generators\Ryujinx.Horizon.Generators.csproj", "{6AE2A5E8-4C5A-48B9-997B-E1455C0355C6}" EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ryujinx.Graphics.Vulkan", "Ryujinx.Graphics.Vulkan\Ryujinx.Graphics.Vulkan.csproj", "{D4D09B08-D580-4D69-B886-C35D2853F6C8}" EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Spv.Generator", "Spv.Generator\Spv.Generator.csproj", "{2BCB3D7A-38C0-4FE7-8FDA-374C6AD56D0E}" EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ryujinx.Ui.LocaleGenerator", "Ryujinx.Ui.LocaleGenerator\Ryujinx.Ui.LocaleGenerator.csproj", "{77D01AD9-2C98-478E-AE1D-8F7100738FB4}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -225,6 +227,10 @@ Global {2BCB3D7A-38C0-4FE7-8FDA-374C6AD56D0E}.Debug|Any CPU.Build.0 = Debug|Any CPU {2BCB3D7A-38C0-4FE7-8FDA-374C6AD56D0E}.Release|Any CPU.ActiveCfg = Release|Any CPU {2BCB3D7A-38C0-4FE7-8FDA-374C6AD56D0E}.Release|Any CPU.Build.0 = Release|Any CPU + {77D01AD9-2C98-478E-AE1D-8F7100738FB4}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {77D01AD9-2C98-478E-AE1D-8F7100738FB4}.Debug|Any CPU.Build.0 = Debug|Any CPU + {77D01AD9-2C98-478E-AE1D-8F7100738FB4}.Release|Any CPU.ActiveCfg = Release|Any CPU + {77D01AD9-2C98-478E-AE1D-8F7100738FB4}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE