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