Explorar el Código

Localized (almost?) everything

Krzysztof Krysiński hace 2 años
padre
commit
f3b5719768
Se han modificado 36 ficheros con 267 adiciones y 143 borrados
  1. 4 1
      src/PixiEditor/App.xaml.cs
  2. 98 1
      src/PixiEditor/Data/Localization/Languages/en.json
  3. 1 1
      src/PixiEditor/Helpers/Converters/BlendModeToStringConverter.cs
  4. 20 19
      src/PixiEditor/Helpers/Extensions/BlendModeEx.cs
  5. 1 1
      src/PixiEditor/Models/Commands/CommandController.cs
  6. 1 1
      src/PixiEditor/Models/Controllers/ClipboardController.cs
  7. 3 2
      src/PixiEditor/Models/Dialogs/OptionDialog.cs
  8. 1 1
      src/PixiEditor/Models/ExternalServices/LospecPaletteFetcher.cs
  9. 7 7
      src/PixiEditor/ViewModels/SettingsWindowViewModel.cs
  10. 2 1
      src/PixiEditor/ViewModels/SubViewModels/Document/DocumentViewModel.cs
  11. 2 1
      src/PixiEditor/ViewModels/SubViewModels/Main/FileViewModel.cs
  12. 3 2
      src/PixiEditor/ViewModels/SubViewModels/Main/LayersViewModel.cs
  13. 2 2
      src/PixiEditor/ViewModels/ViewModelMain.cs
  14. 2 1
      src/PixiEditor/Views/Dialogs/ConfirmationPopup.xaml
  15. 3 2
      src/PixiEditor/Views/Dialogs/ConfirmationPopup.xaml.cs
  16. 2 2
      src/PixiEditor/Views/Dialogs/ExportFilePopup.xaml
  17. 2 1
      src/PixiEditor/Views/Dialogs/ExportFilePopup.xaml.cs
  18. 1 1
      src/PixiEditor/Views/Dialogs/ImportShortcutTemplatePopup.xaml
  19. 7 6
      src/PixiEditor/Views/Dialogs/ImportShortcutTemplatePopup.xaml.cs
  20. 1 1
      src/PixiEditor/Views/Dialogs/NewFilePopup.xaml
  21. 2 1
      src/PixiEditor/Views/Dialogs/NoticePopup.xaml
  22. 2 2
      src/PixiEditor/Views/Dialogs/PalettesBrowser.xaml
  23. 6 6
      src/PixiEditor/Views/Dialogs/PalettesBrowser.xaml.cs
  24. 1 1
      src/PixiEditor/Views/Dialogs/ResizeCanvasPopup.xaml
  25. 10 9
      src/PixiEditor/Views/UserControls/AnchorPointPicker.xaml
  26. 17 17
      src/PixiEditor/Views/UserControls/BlendModeComboBox.cs
  27. 11 11
      src/PixiEditor/Views/UserControls/Layers/FolderControl.xaml
  28. 15 14
      src/PixiEditor/Views/UserControls/Layers/LayerControl.xaml
  29. 7 6
      src/PixiEditor/Views/UserControls/Layers/LayersManager.xaml
  30. 4 3
      src/PixiEditor/Views/UserControls/Palettes/ColorReplacer.xaml
  31. 2 1
      src/PixiEditor/Views/UserControls/Palettes/CompactPaletteViewer.xaml
  32. 3 2
      src/PixiEditor/Views/UserControls/Palettes/PaletteColorAdder.xaml
  33. 6 4
      src/PixiEditor/Views/UserControls/Palettes/PaletteItem.xaml
  34. 7 4
      src/PixiEditor/Views/UserControls/Palettes/PaletteViewer.xaml
  35. 3 1
      src/PixiEditor/Views/UserControls/PrependTextBlock.xaml
  36. 8 7
      src/PixiEditor/Views/UserControls/ShortcutBox.cs

+ 4 - 1
src/PixiEditor/App.xaml.cs

@@ -1,6 +1,7 @@
 using System.IO;
 using System.Text.RegularExpressions;
 using System.Windows;
+using PixiEditor.Localization;
 using PixiEditor.Models.Controllers;
 using PixiEditor.Models.DataHolders;
 using PixiEditor.Models.Dialogs;
@@ -104,7 +105,9 @@ internal partial class App : Application
 
         if (vm.DocumentManagerSubViewModel.Documents.Any(x => !x.AllChangesSaved))
         {
-            ConfirmationType confirmation = ConfirmationDialog.Show($"{e.ReasonSessionEnding} with unsaved data. Are you sure?", $"{e.ReasonSessionEnding}");
+            ConfirmationType confirmation = ConfirmationDialog.Show(
+                new LocalizedString("SESSION_UNSAVED_DATA", e.ReasonSessionEnding),
+                $"{e.ReasonSessionEnding}");
             e.Cancel = confirmation != ConfirmationType.Yes;
         }
     }

+ 98 - 1
src/PixiEditor/Data/Localization/Languages/en.json

@@ -10,6 +10,11 @@
   "EDIT": "Edit",
   "EXIT": "Exit",
   "RECENT_EMPTY_TEXT": "So much empty space",
+
+  "YES": "Yes",
+  "NO": "No",
+  "CANCEL": "Cancel",
+  "UNNAMED": "Unnamed",
   
   "LANGUAGE": "Language",
   "GENERAL": "General",
@@ -210,6 +215,7 @@
   "NOTHING_FOUND": "Nothing found",
   "EXPORT": "Export",
   "EXPORT_IMAGE": "Export image",
+  "EXPORT_SIZE_HINT": "If you want to share the image, try {0}% for the best clarity",
   "IMPORT": "Import",
   "SHORTCUT_TEMPLATES": "Shortcut templates",
   "RESET_ALL": "Reset all",
@@ -224,9 +230,11 @@
   "DELETE_SELECTED_PIXELS": "Delete selected pixels",
 
   "NEW_FOLDER": "New folder",
+  "CREATE": "Create",
   "CREATE_NEW_FOLDER": "Create new folder",
   "NEW_LAYER": "New layer",
   "CREATE_NEW_LAYER": "Create new layer",
+  "BASE_LAYER_NAME": "Base layer",
   "NEW_IMAGE": "New image",
   "CREATE_NEW_IMAGE": "Create new image",
 
@@ -242,6 +250,7 @@
   "CREATE_MASK": "Create mask",
   "DELETE_MASK": "Delete mask",
   "TOGGLE_MASK": "Toggle mask",
+  "ENABLE_MASK": "Enable mask",
   "APPLY_MASK": "Apply mask",
   "TOGGLE_VISIBILITY": "Toggle visibility",
   
@@ -256,6 +265,9 @@
   "MERGE_WITH_ABOVE_DESCRIPTIVE": "Merge selected layer with the one above it",
   "MERGE_WITH_BELOW": "Merge selected layer with below",
   "MERGE_WITH_BELOW_DESCRIPTIVE": "Merge selected layer with the one below it",
+
+  "SELECTED_AREA_EMPTY": "Selected area is empty",
+  "NOTHING_TO_COPY": "Nothing to copy",
   
   "ADD_REFERENCE_LAYER": "Add reference layer",
   "DELETE_REFERENCE_LAYER": "Delete reference layer",
@@ -263,8 +275,10 @@
   "TOGGLE_REFERENCE_LAYER_POS": "Toggle reference layer position",
   "TOGGLE_REFERENCE_LAYER_POS_DESCRIPTIVE": "Toggle reference layer between topmost or most below",
   "RESET_REFERENCE_LAYER_POS": "Reset reference layer position",
+  "REFERENCE_LAYER_PATH": "Reference layer path",
 
   "CLIP_CANVAS": "Clip Canvas",
+  "CLIP_LAYER_BELOW": "Clip with layer below",
   "FLIP": "Flip",
   "FLIP_IMG_VERTICALLY": "Flip Image Vertically",
   "FLIP_IMG_HORIZONTALLY": "Flip Image Horizontally",
@@ -330,6 +344,7 @@
   "PALETTE_EXISTS": "Palette already exists",
   "REPLACE_PALETTE_CONSENT": "Replace current palette with selected one?",
   "REPLACE_PALETTE": "Replace current palette",
+  "UNNAMED_PALETTE": "Unnamed Palette",
 
   "SELECT_COLOR_1": "Select color 1",
   "SELECT_COLOR_2": "Select color 2",
@@ -425,6 +440,25 @@
   "LAYERS_TITLE": "Layers",
   "NAVIGATION_TITLE": "Navigation",
 
+  "NORMAL_BLEND_MODE": "Normal",
+  "DARKEN_BLEND_MODE": "Darken",
+  "MULTIPLY_BLEND_MODE": "Multiply",
+  "COLOR_BURN_BLEND_MODE": "Color burn",
+  "LIGHTEN_BLEND_MODE": "Lighten",
+  "SCREEN_BLEND_MODE": "Screen",
+  "COLOR_DODGE_BLEND_MODE": "Color dodge",
+  "LINEAR_DOGE_BLEND_MODE": "Linear dodge (Add)",
+  "OVERLAY_BLEND_MODE": "Overlay",
+  "SOFT_LIGHT_BLEND_MODE": "Soft light",
+  "HARD_LIGHT_BLEND_MODE": "Hard light",
+  "DIFFERENCE_BLEND_MODE": "Difference",
+  "EXCLUSION_BLEND_MODE": "Exclusion",
+  "HUE_BLEND_MODE": "Hue",
+  "SATURATION_BLEND_MODE": "Saturation",
+  "LUMINOSITY_BLEND_MODE": "Luminosity",
+  "COLOR_BLEND_MODE": "Color",
+  "NOT_SUPPORTED_BLEND_MODE": "Not supported",
+
   "RESTART": "Restart",
 
   "SORT_BY": "Sort by",
@@ -442,23 +476,86 @@
   "ASCENDING": "Ascending",
   "DESCENDING": "Descending",
 
+  "NAME_IS_TOO_LONG": "The name is too long",
+  "STOP_IT_TEXT1": "That's enough. Tidy up your file names.",
+  "STOP_IT_TEXT2": "Can you stop copying these names please?\", \"No, really, stop it.\", \"Don't you have anything better to do?",
+
+  "REPLACER_TOOLTIP": "Right click on palette color and choose 'Replace' or drop it here.",
+  "CLICK_TO_CHOOSE_COLOR": "Click to choose the color",
+  "REPLACE_COLOR": "Replace color",
+  "PALETTE_COLOR_TOOLTIP": "Click to select as main color. Drag and drop onto another palette color to swap them.",
+  "ADD_FROM_SWATCHES": "Add from swatches",
+  "ADD_COLOR_TO_PALETTE": "Add color to palette",
+
+  "USE_IN_CURRENT_IMAGE": "Use in current image",
+  "ADD_TO_FAVORITES": "Add to favorites",
+
+  "BROWSE_PALETTES": "Browse palettes",
+  "LOAD_PALETTE": "Load palette",
+  "SAVE_PALETTE": "Save palette",
+
   "FAVORITES": "Favorites",
   "ADD_FROM_CURRENT_PALETTE": "Add from current palette",
   "OPEN_PALETTES_DIR_TOOLTIP": "Open palettes directory in explorer",
   "BROWSE_ON_LOSPEC_TOOLTIP": "Browse palettes on Lospec",
   "IMPORT_FROM_FILE_TOOLTIP": "Import from file",
 
+  "TOP_LEFT": "Top left",
+  "TOP_CENTER": "Top center",
+  "TOP_RIGHT": "Top right",
+  "MIDDLE_LEFT": "Middle left",
+  "MIDDLE_CENTER": "Middle center",
+  "MIDDLE_RIGHT": "Middle right",
+  "BOTTOM_LEFT": "Bottom left",
+  "BOTTOM_CENTER": "Bottom center",
+  "BOTTOM_RIGHT": "Bottom right",
+
+  "CLIP_TO_BELOW": "Clip to layer below",
+  "MOVE_UPWARDS": "Move upwards",
+  "MOVE_DOWNWARDS": "Move downwards",
+  "MERGE_SELECTED": "Merge selected",
+  "LOCK_TRANSPARENCY": "Lock transparency",
+
   "COULD_NOT_LOAD_PALETTE": "Couldn't fetch palettes",
   "NO_PALETTES_FOUND": "No palettes found.",
   "LOSPEC_LINK_TEXT": "I heard you can find some here: lospec.com/palette-list",
   "PALETTE_BROWSER": "Palette Browser",
-
+  "DELETE_PALETTE_CONFIRMATION": "Are you sure you want to delete this palette? This cannot be undone.",
+
+  "SHORTCUTS_IMPORTED": "Shortcuts from {0} were imported successfully.",
+  "SHORTCUT_PROVIDER_DETECTED": "We've detected, that you have {0} installed. Do you want to import shortcuts from it?",
+  "IMPORT_FROM_INSTALLATION": "Import from installation",
+  "IMPORT_INSTALLATION_OPTION1": "Import from installation",
+  "IMPORT_INSTALLATION_OPTION2": "Use defaults",
+  "IMPORT_FROM_TEMPLATE": "Import from template",
+  "SHORTCUTS_IMPORTED_SUCCESS": "Shortcuts were imported successfully.",
+  "WARNING_RESET_SHORTCUTS_DEFAULT": "Are you sure you want to reset all shortcuts to their default value?",
+
+  "SUCCESS": "Success",
   "ERROR": "Error",
+  "WARNING": "Warning",
   "INTERNAL_ERROR": "Internal error",
+  "ERROR_IMPORTING_IMAGE": "An error occured while importing the image.",
   "ERROR_SAVE_LOCATION": "Couldn't save the file to the specified location",
   "ERROR_WHILE_SAVING": "An internal error occured while saving. Please try again.",
   "UNKNOWN_ERROR_SAVING": "An error occured while saving.",
   "FAILED_ASSOCIATE_LOSPEC": "Failed to associate Lospec Palette protocol.",
+  "SHORTCUTS_CORRUPTED_TITLE": "Corrupted shortcuts file",
+  "SHORTCUTS_CORRUPTED": "Shortcuts file was corrupted, resetting to default.",
+  "FAILED_DOWNLOAD_PALETTE": "Failed to download palette",
+  "FILE_INCORRECT_FORMAT": "The file was not in a correct format",
+  "INVALID_FILE": "Invalid file",
+  "SHORTCUTS_FILE_INCORRECT_FORMAT": "Shortcuts file was not in a correct format",
+  "UNSUPPORTED_FILE_FORMAT": "This file format is unsupported",
+  "ALREADY_ASSIGNED": "Already assigned",
+  "REPLACE": "Replace",
+  "SWAP": "Swap",
+  "SHORTCUT_ALREADY_ASSIGNED_SWAP": "This shortcut is already assigned to '{0}'\nDo you want to replace the existing shortcut or swap the two?",
+  "SHORTCUT_ALREADY_ASSIGNED_OVERWRITE": "This shortcut is already assigned to '{oldCommand.DisplayName}'\nDo you want to replace the existing shortcut?",
+
+  "UNSAVED_CHANGES": "Unsaved changes",
+  "DOCUMENT_MODIFIED_SAVE": "The document has been modified. Do you want to save changes?",
+  "SESSION_UNSAVED_DATA": "{0} with unsaved data. Are you sure?",
 
   "PROJECT_MAINTAINERS": "Project Maintainers",
   "OTHER_AWESOME_CONTRIBUTORS": "And other awesome contributors",

+ 1 - 1
src/PixiEditor/Helpers/Converters/BlendModeToStringConverter.cs

@@ -9,7 +9,7 @@ internal class BlendModeToStringConverter : SingleInstanceConverter<BlendModeToS
     {
         if (value is not BlendMode mode)
             return "<null>";
-        return mode.EnglishName();
+        return mode.LocalizedName();
     }
 
     public override object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)

+ 20 - 19
src/PixiEditor/Helpers/Extensions/BlendModeEx.cs

@@ -1,30 +1,31 @@
 using PixiEditor.ChangeableDocument.Enums;
+using PixiEditor.Localization;
 
 namespace PixiEditor.Helpers.Extensions;
 internal static class BlendModeEx
 {
-    public static string EnglishName(this BlendMode mode)
+    public static string LocalizedName(this BlendMode mode)
     {
         return mode switch
         {
-            BlendMode.Normal => "Normal",
-            BlendMode.Darken => "Darken",
-            BlendMode.Multiply => "Multiply",
-            BlendMode.ColorBurn => "Color Burn",
-            BlendMode.Lighten => "Lighten",
-            BlendMode.Screen => "Screen",
-            BlendMode.ColorDodge => "Color Dodge",
-            BlendMode.LinearDodge => "Linear Dodge (Add)",
-            BlendMode.Overlay => "Overlay",
-            BlendMode.SoftLight => "Soft Light",
-            BlendMode.HardLight => "Hard Light",
-            BlendMode.Difference => "Difference",
-            BlendMode.Exclusion => "Exclusion",
-            BlendMode.Hue => "Hue",
-            BlendMode.Saturation => "Saturation",
-            BlendMode.Luminosity => "Luminosity",
-            BlendMode.Color => "Color",
-            _ => "<no name>",
+            BlendMode.Normal => new LocalizedString("NORMAL_BLEND_MODE"),
+            BlendMode.Darken => new LocalizedString("DARKEN_BLEND_MODE"),
+            BlendMode.Multiply => new LocalizedString("MULTIPLY_BLEND_MODE"),
+            BlendMode.ColorBurn => new LocalizedString("COLOR_BURN_BLEND_MODE"),
+            BlendMode.Lighten => new LocalizedString("LIGHTEN_BLEND_MODE"),
+            BlendMode.Screen => new LocalizedString("SCREEN_BLEND_MODE"),
+            BlendMode.ColorDodge => new LocalizedString("COLOR_DODGE_BLEND_MODE"),
+            BlendMode.LinearDodge => new LocalizedString("LINEAR_DODGE_BLEND_MODE"),
+            BlendMode.Overlay => new LocalizedString("OVERLAY_BLEND_MODE"),
+            BlendMode.SoftLight => new LocalizedString("SOFT_LIGHT_BLEND_MODE"),
+            BlendMode.HardLight => new LocalizedString("HARD_LIGHT_BLEND_MODE"),
+            BlendMode.Difference => new LocalizedString("DIFFERENCE_BLEND_MODE"),
+            BlendMode.Exclusion => new LocalizedString("EXCLUSION_BLEND_MODE"),
+            BlendMode.Hue => new LocalizedString("HUE_BLEND_MODE"),
+            BlendMode.Saturation => new LocalizedString("SATURATION_BLEND_MODE"),
+            BlendMode.Luminosity => new LocalizedString("LUMINOSITY_BLEND_MODE"),
+            BlendMode.Color => new LocalizedString("COLOR_BLEND_MODE"),
+            _ => "NOT_SUPPORTED_BLEND_MODE"
         };
     }
 }

+ 1 - 1
src/PixiEditor/Models/Commands/CommandController.cs

@@ -112,7 +112,7 @@ internal class CommandController
             File.Move(shortcutFile.Path, $"{shortcutFile.Path}.corrupted", true);
             shortcutFile = new ShortcutFile(ShortcutsPath, this);
             template = shortcutFile.LoadTemplate();
-            NoticeDialog.Show("Shortcuts file was corrupted, resetting to default.", "Corrupted shortcuts file");
+            NoticeDialog.Show("SHORTCUTS_CORRUPTED", "SHORTCUTS_CORRUPTED_TITLE");
         }
         var compiledCommandList = new CommandNameList();
         List<(string internalName, string displayName)> commandGroupsData = FindCommandGroups(compiledCommandList.Groups);

+ 1 - 1
src/PixiEditor/Models/Controllers/ClipboardController.cs

@@ -38,7 +38,7 @@ internal static class ClipboardController
             return;
         if (surface.IsT1)
         {
-            NoticeDialog.Show("Selected area is empty", "Nothing to copy");
+            NoticeDialog.Show("SELECTED_AREA_EMPTY", "NOTHING_TO_COPY");
             return;
         }
         var (actuallySurface, _) = surface.AsT2;

+ 3 - 2
src/PixiEditor/Models/Dialogs/OptionDialog.cs

@@ -1,11 +1,12 @@
-using PixiEditor.Models.Enums;
+using PixiEditor.Localization;
+using PixiEditor.Models.Enums;
 using PixiEditor.Views.Dialogs;
 
 namespace PixiEditor.Models.Dialogs;
 
 internal static class OptionDialog
 {
-    public static OptionResult Show(string message, string title, string option1Text, string option2Text)
+    public static OptionResult Show(LocalizedString message, LocalizedString title, LocalizedString option1Text, LocalizedString option2Text)
     {
         ConfirmationPopup popup = new ConfirmationPopup
         {

+ 1 - 1
src/PixiEditor/Models/ExternalServices/LospecPaletteFetcher.cs

@@ -33,7 +33,7 @@ internal static class LospecPaletteFetcher
         }
         catch (HttpRequestException)
         {
-            NoticeDialog.Show("Failed to download palette.", "Error");
+            NoticeDialog.Show("FAILED_DOWNLOAD_PALETTE", "ERROR");
             return null;
         }
 

+ 7 - 7
src/PixiEditor/ViewModels/SettingsWindowViewModel.cs

@@ -68,10 +68,10 @@ internal class SettingsWindowViewModel : ViewModelBase
     [Command.Internal("PixiEditor.Shortcuts.Reset")]
     public static void ResetCommand()
     {
-        var dialog = new OptionsDialog<string>("Are you sure?", "Are you sure you want to reset all shortcuts to their default value?")
+        var dialog = new OptionsDialog<string>("ARE_YOU_SURE", "WARNING_RESET_SHORTCUTS_DEFAULT")
         {
-            { "Yes", x => CommandController.Current.ResetShortcuts() },
-            "Cancel"
+            { new LocalizedString("YES"), x => CommandController.Current.ResetShortcuts() },
+            new LocalizedString("CANCEL")
         }.ShowDialog();
     }
 
@@ -110,14 +110,14 @@ internal class SettingsWindowViewModel : ViewModelBase
             }
             catch (Exception e)
             {
-                NoticeDialog.Show("Shortcuts file was not in a valid format", "Invalid file");
+                NoticeDialog.Show("SHORTCUTS_FILE_INCORRECT_FORMAT", "INVALID_FILE");
                 return;
             }
             
             CommandController.Current.ResetShortcuts();
             CommandController.Current.Import(shortcuts, false);
             File.Copy(dialog.FileName, CommandController.ShortcutsPath, true);
-            NoticeDialog.Show("Shortcuts were imported successfully", "Success");
+            NoticeDialog.Show("SHORTCUTS_IMPORTED_SUCCESS", "SUCCESS");
         }
         // Sometimes, focus was brought back to the last edited shortcut
         Keyboard.ClearFocus();
@@ -130,7 +130,7 @@ internal class SettingsWindowViewModel : ViewModelBase
             shortcuts = ShortcutFile.LoadTemplate(dialog.FileName)?.Shortcuts.ToList();
             if (shortcuts is null)
             {
-                NoticeDialog.Show("Shortcuts file was not in a valid format", "Invalid file");
+                NoticeDialog.Show("SHORTCUTS_FILE_INCORRECT_FORMAT", "INVALID_FILE");
                 return false;
             }
         }
@@ -140,7 +140,7 @@ internal class SettingsWindowViewModel : ViewModelBase
                 x.CustomShortcutExtensions.Contains(Path.GetExtension(dialog.FileName), StringComparer.OrdinalIgnoreCase));
             if (provider is null)
             {
-                NoticeDialog.Show("This file format is unsupported.", "Invalid file");
+                NoticeDialog.Show("UNSUPPORTED_FILE_FORMAT", "INVALID_FILE");
                 return false;
             }
 

+ 2 - 1
src/PixiEditor/ViewModels/SubViewModels/Document/DocumentViewModel.cs

@@ -16,6 +16,7 @@ using PixiEditor.DrawingApi.Core.Surface;
 using PixiEditor.DrawingApi.Core.Surface.ImageData;
 using PixiEditor.DrawingApi.Core.Surface.Vector;
 using PixiEditor.Helpers;
+using PixiEditor.Localization;
 using PixiEditor.Models.Controllers;
 using PixiEditor.Models.DataHolders;
 using PixiEditor.Models.DocumentModels;
@@ -61,7 +62,7 @@ internal partial class DocumentViewModel : NotifyableObject
     
     public string FileName
     {
-        get => fullFilePath is null ? "Unnamed" : Path.GetFileName(fullFilePath);
+        get => fullFilePath is null ? new LocalizedString("UNNAMED") : Path.GetFileName(fullFilePath);
     }
 
     private Guid? lastChangeOnSave = null;

+ 2 - 1
src/PixiEditor/ViewModels/SubViewModels/Main/FileViewModel.cs

@@ -11,6 +11,7 @@ using PixiEditor.ChangeableDocument.Enums;
 using PixiEditor.DrawingApi.Core.Numerics;
 using PixiEditor.Exceptions;
 using PixiEditor.Helpers;
+using PixiEditor.Localization;
 using PixiEditor.Models.Commands.Attributes.Commands;
 using PixiEditor.Models.Controllers;
 using PixiEditor.Models.DataHolders;
@@ -235,7 +236,7 @@ internal class FileViewModel : SubViewModel<ViewModelMain>
             NewDocument(b => b
                 .WithSize(newFile.Width, newFile.Height)
                 .WithLayer(l => l
-                    .WithName("Base Layer")
+                    .WithName(new LocalizedString("BASE_LAYER_NAME"))
                     .WithSurface(new Surface(new VecI(newFile.Width, newFile.Height)))));
         }
     }

+ 3 - 2
src/PixiEditor/ViewModels/SubViewModels/Main/LayersViewModel.cs

@@ -6,6 +6,7 @@ using System.Windows.Media.Imaging;
 using Microsoft.Win32;
 using PixiEditor.ChangeableDocument.Enums;
 using PixiEditor.DrawingApi.Core.Numerics;
+using PixiEditor.Localization;
 using PixiEditor.Models.Commands.Attributes.Commands;
 using PixiEditor.Models.Controllers;
 using PixiEditor.Models.Dialogs;
@@ -359,7 +360,7 @@ internal class LayersViewModel : SubViewModel<ViewModelMain>
         }
         catch (Exception e)
         {
-            NoticeDialog.Show("Error while importing the image", "Error");
+            NoticeDialog.Show("ERROR_IMPORTING_IMAGE", "ERROR");
             return;
         }
 
@@ -378,7 +379,7 @@ internal class LayersViewModel : SubViewModel<ViewModelMain>
         var imagesFilter = new FileTypeDialogDataSet(FileTypeDialogDataSet.SetKind.Images).GetFormattedTypes();
         OpenFileDialog dialog = new OpenFileDialog
         {
-            Title = "Reference layer path",
+            Title = new LocalizedString("REFERENCE_LAYER_PATH"),
             CheckPathExists = true,
             Filter = imagesFilter
         };

+ 2 - 2
src/PixiEditor/ViewModels/ViewModelMain.cs

@@ -211,8 +211,8 @@ internal class ViewModelMain : ViewModelBase
 
     public bool DisposeDocumentWithSaveConfirmation(DocumentViewModel document)
     {
-        const string ConfirmationDialogTitle = "Unsaved changes";
-        const string ConfirmationDialogMessage = "The document has been modified. Do you want to save changes?";
+        const string ConfirmationDialogTitle = "UNSAVED_CHANGES";
+        const string ConfirmationDialogMessage = "DOCUMENT_MODIFIED_SAVE";
 
         ConfirmationType result = ConfirmationType.No;
         if (!document.AllChangesSaved)

+ 2 - 1
src/PixiEditor/Views/Dialogs/ConfirmationPopup.xaml

@@ -8,6 +8,7 @@
         xmlns:behaviours="clr-namespace:PixiEditor.Helpers.Behaviours" 
         xmlns:i="http://schemas.microsoft.com/xaml/behaviors"
         xmlns:dial="clr-namespace:PixiEditor.Views.Dialogs"
+        xmlns:views="clr-namespace:PixiEditor.Views"
         mc:Ignorable="d" d:Title="Unsaved changes"
         Name="popup" WindowStartupLocation="CenterScreen" 
         Height="180" Width="400" MinHeight="180" MinWidth="400"
@@ -42,7 +43,7 @@
                     <system:Boolean>False</system:Boolean>
                 </Button.CommandParameter>
             </Button>
-            <Button Margin="10,0,10,0" Width="70" Style="{StaticResource DarkRoundButton}" Content="Cancel"
+            <Button Margin="10,0,10,0" Width="70" Style="{StaticResource DarkRoundButton}" views:Translator.Key="CANCEL"
                     Command="{Binding DataContext.CancelCommand, ElementName=popup}" />
         </StackPanel>
 

+ 3 - 2
src/PixiEditor/Views/Dialogs/ConfirmationPopup.xaml.cs

@@ -1,5 +1,6 @@
 using System.Windows;
 using PixiEditor.Helpers;
+using PixiEditor.Localization;
 
 namespace PixiEditor.Views.Dialogs;
 
@@ -16,7 +17,7 @@ internal partial class ConfirmationPopup : Window
         DependencyProperty.Register(nameof(Body), typeof(string), typeof(ConfirmationPopup), new PropertyMetadata(""));
 
     public static readonly DependencyProperty FirstOptionTextProperty = DependencyProperty.Register(
-        nameof(FirstOptionText), typeof(string), typeof(ConfirmationPopup), new PropertyMetadata("Yes"));
+        nameof(FirstOptionText), typeof(string), typeof(ConfirmationPopup), new PropertyMetadata(new LocalizedString("YES").Value));
 
     public string FirstOptionText
     {
@@ -25,7 +26,7 @@ internal partial class ConfirmationPopup : Window
     }
 
     public static readonly DependencyProperty SecondOptionTextProperty = DependencyProperty.Register(
-        nameof(SecondOptionText), typeof(string), typeof(ConfirmationPopup), new PropertyMetadata("No"));
+        nameof(SecondOptionText), typeof(string), typeof(ConfirmationPopup), new PropertyMetadata(new LocalizedString("NO").Value));
 
     public string SecondOptionText
     {

+ 2 - 2
src/PixiEditor/Views/Dialogs/ExportFilePopup.xaml

@@ -29,10 +29,10 @@
 
 
         <dial:DialogTitleBar DockPanel.Dock="Top"
-            TitleText="Export image" CloseCommand="{x:Static SystemCommands.CloseWindowCommand}"/>
+            TitleText="EXPORT_IMAGE" CloseCommand="{x:Static SystemCommands.CloseWindowCommand}"/>
 
         <Button DockPanel.Dock="Bottom" Width="70" HorizontalAlignment="Center" IsDefault="True"
-                    Margin="15" Style="{StaticResource DarkRoundButton}" Content="Export" Command="{Binding OkCommand}"
+                    Margin="15" Style="{StaticResource DarkRoundButton}" local:Translator.Key="EXPORT" Command="{Binding OkCommand}"
                     CommandParameter="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type Window}}}" />
 
         <Grid HorizontalAlignment="Center" Margin="0,30,0,0" Background="{StaticResource MainColor}"

+ 2 - 1
src/PixiEditor/Views/Dialogs/ExportFilePopup.xaml.cs

@@ -1,5 +1,6 @@
 using System.Windows;
 using System.Windows.Input;
+using PixiEditor.Localization;
 using PixiEditor.Models.Enums;
 using PixiEditor.ViewModels;
 
@@ -18,7 +19,7 @@ internal partial class ExportFilePopup : Window
 
     private int imageWidth;
     private int imageHeight;
-    public string SizeHint => $"If you want to share the image, try {GetBestPercentage()}% for the best clarity";
+    public string SizeHint => new LocalizedString("EXPORT_SIZE_HINT", GetBestPercentage());
 
     private void CommandBinding_CanExecute(object sender, CanExecuteRoutedEventArgs e)
     {

+ 1 - 1
src/PixiEditor/Views/Dialogs/ImportShortcutTemplatePopup.xaml

@@ -31,7 +31,7 @@
             <RowDefinition Height="*"/>
         </Grid.RowDefinitions>
         <diag:DialogTitleBar DockPanel.Dock="Top"
-                             TitleText="Import from template" CloseCommand="{x:Static SystemCommands.CloseWindowCommand}"/>
+                             TitleText="IMPORT_FROM_TEMPLATE" CloseCommand="{x:Static SystemCommands.CloseWindowCommand}"/>
         <ItemsControl Grid.Row="1" ItemsSource="{Binding Templates, ElementName=window}"
                       Margin="10,10,10,5">
             <ItemsControl.ItemsPanel>

+ 7 - 6
src/PixiEditor/Views/Dialogs/ImportShortcutTemplatePopup.xaml.cs

@@ -1,5 +1,6 @@
 using System.Windows;
 using System.Windows.Input;
+using PixiEditor.Localization;
 using PixiEditor.Models.Commands;
 using PixiEditor.Models.Commands.Attributes.Commands;
 using PixiEditor.Models.Commands.Templates;
@@ -52,14 +53,14 @@ internal partial class ImportShortcutTemplatePopup : Window
         }
         catch
         {
-            NoticeDialog.Show($"The file was not in a correct format", "Error");
+            NoticeDialog.Show($"FILE_INCORRECT_FORMAT", "ERROR");
             return;
         }
 
         Success(provider);
     }
 
-    private static void Success(ShortcutProvider provider) => NoticeDialog.Show($"Shortcuts from {provider.Name} were imported successfully", "Success");
+    private static void Success(ShortcutProvider provider) => NoticeDialog.Show(new LocalizedString("SHORTCUTS_IMPORTED", provider.Name), "SUCCESS");
 
     private void CommandBinding_CanExecute(object sender, CanExecuteRoutedEventArgs e)
     {
@@ -92,10 +93,10 @@ internal partial class ImportShortcutTemplatePopup : Window
         if (provider.ProvidesFromInstallation && provider.HasInstallationPresent)
         {
             var result = OptionDialog.Show(
-                $"We've detected, that you have {provider.Name} installed. Do you want to import shortcuts from it?",
-                "Import from installation",
-                "Import from installation",
-                "Use defaults");
+                new LocalizedString("SHORTCUT_PROVIDER_DETECTED", provider.Name),
+                "IMPORT_FROM_INSTALLATION",
+                "IMPORT_INSTALLATION_OPTION1",
+                "IMPORT_INSTALLATION_OPTION2");
 
             if (result == OptionResult.Option1)
             {

+ 1 - 1
src/PixiEditor/Views/Dialogs/NewFilePopup.xaml

@@ -39,7 +39,7 @@
             TitleText="CREATE_NEW_IMAGE" CloseCommand="{x:Static SystemCommands.CloseWindowCommand}" />
 
         <Button DockPanel.Dock="Bottom" Width="70" Margin="0,15,0,15" HorizontalAlignment="Center"
-                IsDefault="True" Content="Create" x:Name="createButton"
+                IsDefault="True" local:Translator.Key="CREATE" x:Name="createButton"
                 Style="{StaticResource DarkRoundButton}" 
                 Command="{Binding OkCommand}"
                 CommandParameter="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type Window}}}" />

+ 2 - 1
src/PixiEditor/Views/Dialogs/NoticePopup.xaml

@@ -8,6 +8,7 @@
         xmlns:behaviours="clr-namespace:PixiEditor.Helpers.Behaviours" 
         xmlns:i="http://schemas.microsoft.com/xaml/behaviors"
         xmlns:dial="clr-namespace:PixiEditor.Views.Dialogs"
+        xmlns:views="clr-namespace:PixiEditor.Views"
         mc:Ignorable="d" WindowStyle="None"
         d:Title="Notice" Height="180" Width="400" MinHeight="180" MinWidth="400"
         WindowStartupLocation="CenterScreen"
@@ -27,7 +28,7 @@
             TitleText="{Binding ElementName=popup, Path=Title}" CloseCommand="{Binding DataContext.CancelCommand, ElementName=popup}" />
 
         <StackPanel DockPanel.Dock="Bottom" Orientation="Horizontal" HorizontalAlignment="Center" Margin="0,0,0,15">
-            <Button Width="70" IsDefault="True" Click="OkButton_Close" Style="{StaticResource DarkRoundButton}" Content="Close"/>
+            <Button Width="70" IsDefault="True" Click="OkButton_Close" Style="{StaticResource DarkRoundButton}" views:Translator.Key="CLOSE"/>
         </StackPanel>
 
         <TextBlock 

+ 2 - 2
src/PixiEditor/Views/Dialogs/PalettesBrowser.xaml

@@ -63,10 +63,10 @@
                                                 <RotateTransform Angle="180" CenterX="11.5" CenterY="11.5"/>
                                             </Setter.Value>
                                         </Setter>
-                                        <Setter Property="ToolTip" Value="Ascending"/>
+                                        <Setter Property="views:Translator.TooltipKey" Value="ASCENDING"/>
                                     </DataTrigger>
                                     <DataTrigger Binding="{Binding IsChecked, ElementName=toggleBtn}" Value="false">
-                                        <Setter Property="ToolTip" Value="Descending"/>
+                                        <Setter Property="views:Translator.TooltipKey" Value="DESCENDING"/>
                                     </DataTrigger>
                                 </Style.Triggers>
                             </Style>

+ 6 - 6
src/PixiEditor/Views/Dialogs/PalettesBrowser.xaml.cs

@@ -24,10 +24,10 @@ internal partial class PalettesBrowser : Window
 {
     private const int ItemsPerLoad = 10;
 
-    private readonly string[] stopItTexts = new[]
+    private readonly LocalizedString[] stopItTexts = new[]
     {
-        "That's enough. Tidy up your file names.",
-        "Can you stop copying these names please?", "No, really, stop it.", "Don't you have anything better to do?"
+        new LocalizedString("STOP_IT_TEXT1"),
+        new LocalizedString("STOP_IT_TEXT2")
     };
 
     public PaletteList PaletteList
@@ -280,7 +280,7 @@ internal partial class PalettesBrowser : Window
         string filePath = Path.Join(LocalPalettesFetcher.PathToPalettesFolder, palette.FileName);
         if (File.Exists(filePath))
         {
-            if (ConfirmationDialog.Show("Are you sure you want to delete this palette? This cannot be undone.", "Warning!") == ConfirmationType.Yes)
+            if (ConfirmationDialog.Show("DELETE_PALETTE_CONFIRMATION", "WARNING") == ConfirmationType.Yes)
             {
                 _ = LocalPalettesFetcher.DeletePalette(palette.FileName);
                 RemoveFavouritePalette(palette);
@@ -487,7 +487,7 @@ internal partial class PalettesBrowser : Window
         if (CurrentEditingPalette?.Count == 0)
             return;
 
-        string finalFileName = LocalPalettesFetcher.GetNonExistingName("Unnamed Palette.pal", true);
+        string finalFileName = LocalPalettesFetcher.GetNonExistingName($"{new LocalizedString("UNNAMED_PALETTE").Value}.pal", true);
         await LocalPalettesFetcher.SavePalette(finalFileName, CurrentEditingPalette.ToArray());
     }
 
@@ -506,7 +506,7 @@ internal partial class PalettesBrowser : Window
 
         if (newPath.Length > 250)
         {
-            NoticeDialog.Show(stopItTexts[Random.Shared.Next(stopItTexts.Length - 1)], "The name is too long.");
+            NoticeDialog.Show(stopItTexts[Random.Shared.Next(stopItTexts.Length - 1)], "NAME_IS_TOO_LONG");
             return;
         }
 

+ 1 - 1
src/PixiEditor/Views/Dialogs/ResizeCanvasPopup.xaml

@@ -36,7 +36,7 @@
             TitleText="RESIZE_CANVAS" CloseCommand="{x:Static SystemCommands.CloseWindowCommand}" />
 
         <Button DockPanel.Dock="Bottom" Width="70" HorizontalAlignment="Center" Margin="15"
-                Style="{StaticResource DarkRoundButton}" Content="Resize" Click="Button_Click" IsDefault="True" />
+                Style="{StaticResource DarkRoundButton}" local:Translator.Key="RESIZE" Click="Button_Click" IsDefault="True" />
 
         <Border HorizontalAlignment="Center" Margin="0,30,0,0" Background="{StaticResource MainColor}"
                  VerticalAlignment="Top" Grid.Row="1" Width="250" Height="290" CornerRadius="10">

+ 10 - 9
src/PixiEditor/Views/UserControls/AnchorPointPicker.xaml

@@ -4,6 +4,7 @@
              xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
              xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
              xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
+             xmlns:views="clr-namespace:PixiEditor.Views"
              mc:Ignorable="d"
              d:DesignHeight="78" d:DesignWidth="78">
     <Grid Name="container">
@@ -18,28 +19,28 @@
             <ColumnDefinition Width="26" />
         </Grid.ColumnDefinitions>
         <ToggleButton IsChecked="True" Checked="ToggleButton_Checked" Click="ToggleButton_Click" Margin="0.25"
-                      Style="{DynamicResource AnchorPointToggleButtonStyle}" ToolTip="Top left" Grid.Row="0"
+                      Style="{DynamicResource AnchorPointToggleButtonStyle}" views:Translator.TooltipKey="TOP_LEFT" Grid.Row="0"
                       Grid.Column="0" BorderBrush="Black">
             <ToggleButton.Background>
                 <ImageBrush ImageSource="../../Images/AnchorDot.png" />
             </ToggleButton.Background>
         </ToggleButton>
         <ToggleButton Checked="ToggleButton_Checked" Click="ToggleButton_Click" Margin="0.25"
-                      Style="{DynamicResource AnchorPointToggleButtonStyle}" Grid.Row="0" ToolTip="Top center"
+                      Style="{DynamicResource AnchorPointToggleButtonStyle}" Grid.Row="0" views:Translator.TooltipKey="TOP_CENTER"
                       Grid.Column="1" BorderBrush="Black">
             <ToggleButton.Background>
                 <ImageBrush ImageSource="../../Images/AnchorDot.png" />
             </ToggleButton.Background>
         </ToggleButton>
         <ToggleButton Checked="ToggleButton_Checked" Click="ToggleButton_Click" Margin="0.25"
-                      Style="{DynamicResource AnchorPointToggleButtonStyle}" ToolTip="Top right" Grid.Row="0"
+                      Style="{DynamicResource AnchorPointToggleButtonStyle}" views:Translator.TooltipKey="TOP_RIGHT" Grid.Row="0"
                       Grid.Column="2" BorderBrush="Black">
             <ToggleButton.Background>
                 <ImageBrush ImageSource="../../Images/AnchorDot.png" />
             </ToggleButton.Background>
         </ToggleButton>
         <ToggleButton Checked="ToggleButton_Checked" Click="ToggleButton_Click" Margin="0.25"
-                      Style="{DynamicResource AnchorPointToggleButtonStyle}" Grid.Row="1" ToolTip="Middle left"
+                      Style="{DynamicResource AnchorPointToggleButtonStyle}" Grid.Row="1" views:Translator.TooltipKey="MIDDLE_LEFT"
                       Grid.Column="0" BorderBrush="Black">
             <ToggleButton.Background>
                 <ImageBrush ImageSource="../../Images/AnchorDot.png" />
@@ -47,35 +48,35 @@
         </ToggleButton>
         <ToggleButton Checked="ToggleButton_Checked" Click="ToggleButton_Click" Margin="0.25"
                       Style="{DynamicResource AnchorPointToggleButtonStyle}" Grid.Row="1" Grid.Column="1"
-                      ToolTip="Middle center" BorderBrush="Black">
+                      views:Translator.TooltipKey="MIDDLE_CENTER" BorderBrush="Black">
             <ToggleButton.Background>
                 <ImageBrush ImageSource="../../Images/AnchorDot.png" />
             </ToggleButton.Background>
         </ToggleButton>
         <ToggleButton Checked="ToggleButton_Checked" Click="ToggleButton_Click" Margin="0.25"
                       Style="{DynamicResource AnchorPointToggleButtonStyle}" Grid.Row="1" Grid.Column="2"
-                      ToolTip="Middle right" BorderBrush="Black">
+                      views:Translator.TooltipKey="MIDDLE_RIGHT" BorderBrush="Black">
             <ToggleButton.Background>
                 <ImageBrush ImageSource="../../Images/AnchorDot.png" />
             </ToggleButton.Background>
         </ToggleButton>
         <ToggleButton Checked="ToggleButton_Checked" Click="ToggleButton_Click" Margin="0.25"
                       Style="{DynamicResource AnchorPointToggleButtonStyle}" Grid.Row="2" Grid.Column="0"
-                      ToolTip="Bottom left" BorderBrush="Black">
+                      views:Translator.TooltipKey="BOTTOM_LEFT" BorderBrush="Black">
             <ToggleButton.Background>
                 <ImageBrush ImageSource="../../Images/AnchorDot.png" />
             </ToggleButton.Background>
         </ToggleButton>
         <ToggleButton Checked="ToggleButton_Checked" Click="ToggleButton_Click" Margin="0.25"
                       Style="{DynamicResource AnchorPointToggleButtonStyle}" Grid.Row="2" Grid.Column="1"
-                      ToolTip="Bottom center" BorderBrush="Black">
+                      views:Translator.TooltipKey="BOTTOM_CENTER" BorderBrush="Black">
             <ToggleButton.Background>
                 <ImageBrush ImageSource="../../Images/AnchorDot.png" />
             </ToggleButton.Background>
         </ToggleButton>
         <ToggleButton Checked="ToggleButton_Checked" Click="ToggleButton_Click" Margin="0.25"
                       Style="{DynamicResource AnchorPointToggleButtonStyle}" Grid.Row="2" Grid.Column="2"
-                      ToolTip="Bottom right" BorderBrush="Black">
+                      views:Translator.TooltipKey="BOTTOM_RIGHT" BorderBrush="Black">
             <ToggleButton.Background>
                 <ImageBrush ImageSource="../../Images/AnchorDot.png" />
             </ToggleButton.Background>

+ 17 - 17
src/PixiEditor/Views/UserControls/BlendModeComboBox.cs

@@ -57,28 +57,28 @@ internal class BlendModeComboBox : ComboBox
     {
         var items = new List<UIElement>() 
         {
-            new ComboBoxItem() { Content = BlendMode.Normal.EnglishName(), Tag = BlendMode.Normal },
+            new ComboBoxItem() { Content = BlendMode.Normal.LocalizedName(), Tag = BlendMode.Normal },
             new Separator(),
-            new ComboBoxItem() { Content = BlendMode.Darken.EnglishName(), Tag = BlendMode.Darken },
-            new ComboBoxItem() { Content = BlendMode.Multiply.EnglishName(), Tag = BlendMode.Multiply },
-            new ComboBoxItem() { Content = BlendMode.ColorBurn.EnglishName(), Tag = BlendMode.ColorBurn },
+            new ComboBoxItem() { Content = BlendMode.Darken.LocalizedName(), Tag = BlendMode.Darken },
+            new ComboBoxItem() { Content = BlendMode.Multiply.LocalizedName(), Tag = BlendMode.Multiply },
+            new ComboBoxItem() { Content = BlendMode.ColorBurn.LocalizedName(), Tag = BlendMode.ColorBurn },
             new Separator(),
-            new ComboBoxItem() { Content = BlendMode.Lighten.EnglishName(), Tag = BlendMode.Lighten },
-            new ComboBoxItem() { Content = BlendMode.Screen.EnglishName(), Tag = BlendMode.Screen },
-            new ComboBoxItem() { Content = BlendMode.ColorDodge.EnglishName(), Tag = BlendMode.ColorDodge },
-            new ComboBoxItem() { Content = BlendMode.LinearDodge.EnglishName(), Tag = BlendMode.LinearDodge },
+            new ComboBoxItem() { Content = BlendMode.Lighten.LocalizedName(), Tag = BlendMode.Lighten },
+            new ComboBoxItem() { Content = BlendMode.Screen.LocalizedName(), Tag = BlendMode.Screen },
+            new ComboBoxItem() { Content = BlendMode.ColorDodge.LocalizedName(), Tag = BlendMode.ColorDodge },
+            new ComboBoxItem() { Content = BlendMode.LinearDodge.LocalizedName(), Tag = BlendMode.LinearDodge },
             new Separator(),
-            new ComboBoxItem() { Content = BlendMode.Overlay.EnglishName(), Tag = BlendMode.Overlay },
-            new ComboBoxItem() { Content = BlendMode.SoftLight.EnglishName(), Tag = BlendMode.SoftLight },
-            new ComboBoxItem() { Content = BlendMode.HardLight.EnglishName(), Tag = BlendMode.HardLight },
+            new ComboBoxItem() { Content = BlendMode.Overlay.LocalizedName(), Tag = BlendMode.Overlay },
+            new ComboBoxItem() { Content = BlendMode.SoftLight.LocalizedName(), Tag = BlendMode.SoftLight },
+            new ComboBoxItem() { Content = BlendMode.HardLight.LocalizedName(), Tag = BlendMode.HardLight },
             new Separator(),
-            new ComboBoxItem() { Content = BlendMode.Difference.EnglishName(), Tag = BlendMode.Difference },
-            new ComboBoxItem() { Content = BlendMode.Exclusion.EnglishName(), Tag = BlendMode.Exclusion },
+            new ComboBoxItem() { Content = BlendMode.Difference.LocalizedName(), Tag = BlendMode.Difference },
+            new ComboBoxItem() { Content = BlendMode.Exclusion.LocalizedName(), Tag = BlendMode.Exclusion },
             new Separator(),
-            new ComboBoxItem() { Content = BlendMode.Hue.EnglishName(), Tag = BlendMode.Hue },
-            new ComboBoxItem() { Content = BlendMode.Saturation.EnglishName(), Tag = BlendMode.Saturation },
-            new ComboBoxItem() { Content = BlendMode.Luminosity.EnglishName(), Tag = BlendMode.Luminosity },
-            new ComboBoxItem() { Content = BlendMode.Color.EnglishName(), Tag = BlendMode.Color }
+            new ComboBoxItem() { Content = BlendMode.Hue.LocalizedName(), Tag = BlendMode.Hue },
+            new ComboBoxItem() { Content = BlendMode.Saturation.LocalizedName(), Tag = BlendMode.Saturation },
+            new ComboBoxItem() { Content = BlendMode.Luminosity.LocalizedName(), Tag = BlendMode.Luminosity },
+            new ComboBoxItem() { Content = BlendMode.Color.LocalizedName(), Tag = BlendMode.Color }
         };
         foreach (var item in items)
         {

+ 11 - 11
src/PixiEditor/Views/UserControls/Layers/FolderControl.xaml

@@ -122,27 +122,27 @@
         </Grid>
         <Border.ContextMenu>
             <ContextMenu>
-                <MenuItem Header="Delete" Command="{cmds:Command PixiEditor.Layer.DeleteAllSelected}"/>
-                <MenuItem Header="Rename" Click="RenameMenuItem_Click"/>
+                <MenuItem userControls:Translator.Key="DELETE" Command="{cmds:Command PixiEditor.Layer.DeleteAllSelected}"/>
+                <MenuItem userControls:Translator.Key="RENAME" Click="RenameMenuItem_Click"/>
                 <MenuItem 
                     IsCheckable="True" 
                     IsChecked="{Binding PlacementTarget.Tag.Folder.ClipToMemberBelowEnabledBindable, RelativeSource={RelativeSource AncestorType=ContextMenu}}" 
-                    Header="Clip to layer below"/>
+                    userControls:Translator.Key="CLIP_TO_BELOW"/>
                 <Separator/>
-                <MenuItem Header="Move upwards" Command="{cmds:Command PixiEditor.Layer.MoveSelectedMemberUpwards}"/>
-                <MenuItem Header="Move downwards" Command="{cmds:Command PixiEditor.Layer.MoveSelectedMemberDownwards}"/>
+                <MenuItem userControls:Translator.Key="MOVE_UPWARDS" Command="{cmds:Command PixiEditor.Layer.MoveSelectedMemberUpwards}"/>
+                <MenuItem userControls:Translator.Key="MOVE_DOWNWARDS" Command="{cmds:Command PixiEditor.Layer.MoveSelectedMemberDownwards}"/>
                 <Separator/>
-                <MenuItem Header="Create mask" Command="{cmds:Command PixiEditor.Layer.CreateMask}"/>
-                <MenuItem Header="Delete mask" Command="{cmds:Command PixiEditor.Layer.DeleteMask}"/>
+                <MenuItem userControls:Translator.Key="CREATE_MASK" Command="{cmds:Command PixiEditor.Layer.CreateMask}"/>
+                <MenuItem userControls:Translator.Key="DELETE_MASK" Command="{cmds:Command PixiEditor.Layer.DeleteMask}"/>
                 <MenuItem 
                     IsCheckable="True" 
                     IsChecked="{Binding PlacementTarget.Tag.Folder.MaskIsVisibleBindable, RelativeSource={RelativeSource AncestorType=ContextMenu}}" 
                     IsEnabled="{Binding PlacementTarget.Tag.Folder.HasMaskBindable, RelativeSource={RelativeSource AncestorType=ContextMenu}}"
-                    Header="Enable mask"/>
+                    userControls:Translator.Key="ENABLE_MASK"/>
                 <Separator/>
-                <MenuItem Header="Merge selected" Command="{cmds:Command PixiEditor.Layer.MergeSelected}"/>
-                <MenuItem Header="Merge with above" Command="{cmds:Command PixiEditor.Layer.MergeWithAbove}"/>
-                <MenuItem Header="Merge with below" Command="{cmds:Command PixiEditor.Layer.MergeWithBelow}"/>
+                <MenuItem userControls:Translator.Key="MERGE_SELECTED" Command="{cmds:Command PixiEditor.Layer.MergeSelected}"/>
+                <MenuItem userControls:Translator.Key="MERGE_WITH_ABOVE" Command="{cmds:Command PixiEditor.Layer.MergeWithAbove}"/>
+                <MenuItem userControls:Translator.Key="MERGE_WITH_BELOW" Command="{cmds:Command PixiEditor.Layer.MergeWithBelow}"/>
             </ContextMenu>
         </Border.ContextMenu>
     </Border>

+ 15 - 14
src/PixiEditor/Views/UserControls/Layers/LayerControl.xaml

@@ -12,6 +12,7 @@
              xmlns:helpers="clr-namespace:PixiEditor.Helpers.UI"
              xmlns:cmds="clr-namespace:PixiEditor.Models.Commands.XAML"
              xmlns:sys="clr-namespace:System;assembly=mscorlib"
+             xmlns:views="clr-namespace:PixiEditor.Views"
              mc:Ignorable="d" 
              Focusable="True"
              d:DesignHeight="35" d:DesignWidth="250" Name="uc"
@@ -129,33 +130,33 @@
         </Grid>
         <Border.ContextMenu>
             <ContextMenu>
-                <MenuItem Header="Duplicate" Command="{cmds:Command PixiEditor.Layer.DuplicateSelectedLayer}"/>
-                <MenuItem Header="Delete" Command="{cmds:Command PixiEditor.Layer.DeleteAllSelected}"/>
-                <MenuItem Header="Rename" Click="RenameMenuItem_Click"/>
+                <MenuItem views:Translator.Key="DUPLICATE" Command="{cmds:Command PixiEditor.Layer.DuplicateSelectedLayer}"/>
+                <MenuItem views:Translator.Key="DELETE" Command="{cmds:Command PixiEditor.Layer.DeleteAllSelected}"/>
+                <MenuItem views:Translator.Key="RENAME" Click="RenameMenuItem_Click"/>
                 <MenuItem 
                     IsCheckable="True" 
                     IsChecked="{Binding PlacementTarget.Tag.Layer.ClipToMemberBelowEnabledBindable, RelativeSource={RelativeSource AncestorType=ContextMenu}}" 
-                    Header="Clip to layer below"/>
+                    views:Translator.Key="CLIP_LAYER_BELOW"/>
                 <MenuItem 
                     IsCheckable="True" 
                     IsChecked="{Binding PlacementTarget.Tag.Layer.LockTransparencyBindable, RelativeSource={RelativeSource AncestorType=ContextMenu}}" 
-                    Header="Lock transparency"/>
+                    views:Translator.Key="LOCK_TRANSPARENCY"/>
                 <Separator/>
-                <MenuItem Header="Move upwards" Command="{cmds:Command PixiEditor.Layer.MoveSelectedMemberUpwards}"/>
-                <MenuItem Header="Move downwards" Command="{cmds:Command PixiEditor.Layer.MoveSelectedMemberDownwards}"/>
+                <MenuItem views:Translator.Key="MOVE_UPWARDS" Command="{cmds:Command PixiEditor.Layer.MoveSelectedMemberUpwards}"/>
+                <MenuItem views:Translator.Key="MOVE_DOWNWARDS" Command="{cmds:Command PixiEditor.Layer.MoveSelectedMemberDownwards}"/>
                 <Separator/>
-                <MenuItem Header="Create mask" Command="{cmds:Command PixiEditor.Layer.CreateMask}"/>
-                <MenuItem Header="Delete mask" Command="{cmds:Command PixiEditor.Layer.DeleteMask}"/>
+                <MenuItem views:Translator.Key="CREATE_MASK" Command="{cmds:Command PixiEditor.Layer.CreateMask}"/>
+                <MenuItem views:Translator.Key="DELETE_MASK" Command="{cmds:Command PixiEditor.Layer.DeleteMask}"/>
                 <MenuItem 
                     IsCheckable="True" 
                     IsChecked="{Binding PlacementTarget.Tag.Layer.MaskIsVisibleBindable, RelativeSource={RelativeSource AncestorType=ContextMenu}}" 
                     IsEnabled="{Binding PlacementTarget.Tag.Layer.HasMaskBindable, RelativeSource={RelativeSource AncestorType=ContextMenu}}"
-                    Header="Enable mask"/>
-                <MenuItem Header="Apply mask" Command="{cmds:Command PixiEditor.Layer.ApplyMask}"/>
+                    views:Translator.Key="ENABLE_MASK"/>
+                <MenuItem views:Translator.Key="APPLY_MASK" Command="{cmds:Command PixiEditor.Layer.ApplyMask}"/>
                 <Separator/>
-                <MenuItem Header="Merge selected" Command="{cmds:Command PixiEditor.Layer.MergeSelected}"/>
-                <MenuItem Header="Merge with above" Command="{cmds:Command PixiEditor.Layer.MergeWithAbove}"/>
-                <MenuItem Header="Merge with below" Command="{cmds:Command PixiEditor.Layer.MergeWithBelow}"/>
+                <MenuItem views:Translator.Key="MERGE_SELECTED" Command="{cmds:Command PixiEditor.Layer.MergeSelected}"/>
+                <MenuItem views:Translator.Key="MERGE_WITH_ABOVE" Command="{cmds:Command PixiEditor.Layer.MergeWithAbove}"/>
+                <MenuItem views:Translator.Key="MERGE_WITH_BELOW" Command="{cmds:Command PixiEditor.Layer.MergeWithBelow}"/>
             </ContextMenu>
         </Border.ContextMenu>
     </Border>

+ 7 - 6
src/PixiEditor/Views/UserControls/Layers/LayersManager.xaml

@@ -27,7 +27,7 @@
                 <Button 
                     Command="{commands:Command PixiEditor.Layer.NewLayer}" 
                     DockPanel.Dock="Left"
-                    Height="24" Width="24" Cursor="Hand" ToolTip="New Layer"
+                    Height="24" Width="24" Cursor="Hand" vws:Translator.TooltipKey="NEW_LAYER"
                     HorizontalAlignment="Stretch" Margin="0,0,5,0"
                     Style="{StaticResource ToolButtonStyle}">
                     <Button.Background>
@@ -36,7 +36,7 @@
                 </Button>
                 <Button 
                     Command="{commands:Command PixiEditor.Layer.NewFolder}" 
-                    Height="24" Width="24" ToolTip="New Folder" Cursor="Hand"
+                    Height="24" Width="24" vws:Translator.TooltipKey="NEW_FOLDER" Cursor="Hand"
                     DockPanel.Dock="Left"
                     HorizontalAlignment="Stretch"  Margin="0,0,5,0"
                     Style="{StaticResource ToolButtonStyle}">
@@ -45,7 +45,8 @@
                     </Button.Background>
                 </Button>
                 <Button 
-                    Command="{commands:Command PixiEditor.Layer.DeleteSelected}" Height="24" Width="24" ToolTip="Delete selected" Cursor="Hand"
+                    Command="{commands:Command PixiEditor.Layer.DeleteSelected}" Height="24" Width="24" vws:Translator.TooltipKey="LAYER_DELETE_ALL_SELECTED"
+                    Cursor="Hand"
                     HorizontalAlignment="Stretch" Margin="0,0,5,0"
                     DockPanel.Dock="Left"
                     Style="{StaticResource ToolButtonStyle}">
@@ -54,7 +55,7 @@
                     </Button.Background>
                 </Button>
                 <Button 
-                    Command="{commands:Command PixiEditor.Layer.MergeWithBelow}" Height="24" Width="24" ToolTip="Merge with below" Cursor="Hand"
+                    Command="{commands:Command PixiEditor.Layer.MergeWithBelow}" Height="24" Width="24" vws:Translator.TooltipKey="MERGE_WITH_BELOW" Cursor="Hand"
                     DockPanel.Dock="Right"
                     HorizontalAlignment="Stretch" Margin="5,0,0,0"
                     Style="{StaticResource ToolButtonStyle}">
@@ -63,7 +64,7 @@
                     </Button.Background>
                 </Button>
                 <Button 
-                    Height="24" Width="24" ToolTip="Create mask" Cursor="Hand"
+                    Height="24" Width="24" vws:Translator.TooltipKey="CREATE_MASK" Cursor="Hand"
                     DockPanel.Dock="Right"
                     HorizontalAlignment="Stretch" Margin="5,0,0,0"
                     Style="{StaticResource ToolButtonStyle}"
@@ -73,7 +74,7 @@
                     </Button.Background>
                 </Button>
                 <Button 
-                    Height="24" Width="24" ToolTip="Lock transparency" Cursor="Hand"
+                    Height="24" Width="24" vws:Translator.TooltipKey="LOCK_TRANSPARENCY" Cursor="Hand"
                     DockPanel.Dock="Right"
                     HorizontalAlignment="Stretch" Margin="5,0,0,0"
                     Style="{StaticResource ToolButtonStyle}"

+ 4 - 3
src/PixiEditor/Views/UserControls/Palettes/ColorReplacer.xaml

@@ -10,6 +10,7 @@
              xmlns:i="http://schemas.microsoft.com/xaml/behaviors"
              xmlns:behaviours="clr-namespace:PixiEditor.Helpers.Behaviours"
              xmlns:colorPicker="clr-namespace:ColorPicker;assembly=ColorPicker"
+             xmlns:views="clr-namespace:PixiEditor.Views"
              mc:Ignorable="d" Name="uc"
              d:DesignHeight="50" d:DesignWidth="300">
     <Border BorderBrush="{StaticResource DarkerAccentColor}" 
@@ -30,7 +31,7 @@
                     <local:PaletteColor Color="{Binding ElementName=uc, Path=ColorToReplace}"
                             Height="35" 
                             Width="35" 
-                            ToolTip="Right click on palette color and choose 'Replace' or drop it here."
+                            views:Translator.TooltipKey="REPLACER_TOOLTIP"
                             AllowDrop="True" Drop="UIElement_OnDrop"/>
                     <Image Source="/Images/Arrow-right.png" Height="20" Width="20" Margin="10 0"/>
                     <colorPicker:PortableColorPicker 
@@ -40,9 +41,9 @@
                         Height="37"
                         Style="{StaticResource DefaultColorPickerStyle}"
                         Width="37" Focusable="False" Margin="0 0 10 0"
-                        ToolTip="Click to choose the color"
+                        views:Translator.TooltipKey="CLICK_TO_CHOOSE_COLOR"
                         ShowAlpha="False"/>
-                    <Button Click="ReplaceButton_OnClick" ToolTip="Replace color"
+                    <Button Click="ReplaceButton_OnClick" views:Translator.TooltipKey="REPLACE_COLOR"
                             Style="{StaticResource ToolButtonStyle}" Cursor="Hand" Height="20" Width="20">
                         <Button.Background>
                             <ImageBrush ImageSource="/Images/Replace.png"/>

+ 2 - 1
src/PixiEditor/Views/UserControls/Palettes/CompactPaletteViewer.xaml

@@ -7,6 +7,7 @@
              xmlns:local="clr-namespace:PixiEditor.Views.UserControls.Palettes" 
              xmlns:b="http://schemas.microsoft.com/xaml/behaviors" 
              xmlns:converters="clr-namespace:PixiEditor.Helpers.Converters"
+             xmlns:views="clr-namespace:PixiEditor.Views"
              mc:Ignorable="d"  Name="compactPaletteViewer"
              d:DesignHeight="900" d:DesignWidth="30">
     <Grid Background="{StaticResource AccentColor}">
@@ -31,7 +32,7 @@
                 </ItemsControl.ItemsPanel>
                 <ItemsControl.ItemTemplate>
                     <DataTemplate>
-                        <local:PaletteColor ToolTip="Click to select as main color. Drag and drop onto another palette color to swap them."
+                        <local:PaletteColor views:Translator.TooltipKey="PALETTE_COLOR_TOOLTIP"
                                             Cursor="Hand"
                                             Color="{Binding}" Width="20" Height="20" CornerRadius="0">
                             <b:Interaction.Triggers>

+ 3 - 2
src/PixiEditor/Views/UserControls/Palettes/PaletteColorAdder.xaml

@@ -6,6 +6,7 @@
              xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
              xmlns:local="clr-namespace:PixiEditor.Views.UserControls" 
              xmlns:colorpicker="clr-namespace:ColorPicker;assembly=ColorPicker"
+             xmlns:views="clr-namespace:PixiEditor.Views"
              mc:Ignorable="d" Name="paletteColorAdder"
              d:DesignHeight="36" d:DesignWidth="120">
     <StackPanel Orientation="Horizontal" VerticalAlignment="Center">
@@ -17,7 +18,7 @@
             ShowAlpha="False"/>
         <Button Name="AddButton" Margin="0" Width="24" Height="24" 
                 Style="{StaticResource ToolButtonStyle}"
-                ToolTip="Add color"
+                views:Translator.TooltipKey="ADD_COLOR_TO_PALETTE"
                 Cursor="Hand"  Click="Button_Click">
             <Button.Background>
                 <ImageBrush ImageSource="/Images/Plus-square.png"/>
@@ -25,7 +26,7 @@
         </Button>
         <Button Name="AddFromSwatches" Margin="10 2 0 0" Width="24" Height="24" 
                 Style="{StaticResource ToolButtonStyle}" 
-                ToolTip="Add from swatches"
+                views:Translator.TooltipKey="ADD_FROM_SWATCHES"
                 Cursor="Hand"  Click="AddFromSwatches_OnClick">
             <Button.Background>
                 <ImageBrush ImageSource="/Images/CopyAdd.png"/>

+ 6 - 4
src/PixiEditor/Views/UserControls/Palettes/PaletteItem.xaml

@@ -29,7 +29,9 @@
                 <userControls:EditableTextBlock x:Name="titleTextBlock" OnSubmit="EditableTextBlock_OnSubmit" 
                                          Text="{Binding Palette.Name, ElementName=paletteItem, Mode=TwoWay}" 
                                          Foreground="White" FontSize="20" MaxChars="50"/>
-                <Button Visibility="{Binding ElementName=paletteItem, Path=IsMouseOver, Converter={StaticResource BoolToVisibilityConverter}}" Click="RenameButton_Click" Style="{StaticResource ImageButtonStyle}" Cursor="Hand" Width="20" Height="20">
+                <Button Visibility="{Binding ElementName=paletteItem, Path=IsMouseOver, Converter={StaticResource BoolToVisibilityConverter}}"
+                        Click="RenameButton_Click"
+                        Style="{StaticResource ImageButtonStyle}" Cursor="Hand" Width="20" Height="20">
                     <Image Source="/Images/Edit.png"/>
                 </Button>
             </StackPanel>
@@ -63,7 +65,7 @@
                     </Style>
                 </Border.Style>
                 <Button
-                    ToolTip="Use in current image" Cursor="Hand" 
+                    views:Translator.TooltipKey="USE_IN_CURRENT_IMAGE" Cursor="Hand"
                     Style="{StaticResource ToolButtonStyle}"
                     Margin="0 3 0 0" Width="24" Height="24"
                     CommandParameter="{Binding ElementName=paletteItem, Path=Palette.Colors}"
@@ -90,7 +92,7 @@
                 <Button
                     Command="{Binding ElementName=paletteItem, Path=ToggleFavouriteCommand}"
                     CommandParameter="{Binding ElementName=paletteItem, Path=Palette}"
-                    ToolTip="Add to favorites">
+                    views:Translator.TooltipKey="ADD_TO_FAVORITES">
                     <Button.Style>
                         <Style BasedOn="{StaticResource ImageButtonStyle}" TargetType="Button">
                             <Style.Triggers>
@@ -129,7 +131,7 @@
                 </Border.Style>
                 <Button Command="{Binding DeletePaletteCommand, ElementName=paletteItem}"
                         CommandParameter="{Binding ElementName=paletteItem, Path=Palette}"
-                ToolTip="Delete" Width="24" Height="24" Margin="0"
+                views:Translator.TooltipKey="DELETE" Width="24" Height="24" Margin="0"
                 Style="{StaticResource ToolButtonStyle}" Cursor="Hand">
                     <Button.Background>
                         <ImageBrush ImageSource="/Images/Trash.png"/>

+ 7 - 4
src/PixiEditor/Views/UserControls/Palettes/PaletteViewer.xaml

@@ -8,6 +8,7 @@
              xmlns:b="http://schemas.microsoft.com/xaml/behaviors"
              xmlns:converters="clr-namespace:PixiEditor.Helpers.Converters" xmlns:palettes="clr-namespace:PixiEditor.Views.UserControls.Palettes"
              xmlns:helpers="clr-namespace:PixiEditor.Helpers"
+             xmlns:views="clr-namespace:PixiEditor.Views"
              mc:Ignorable="d" 
              d:DesignHeight="450" d:DesignWidth="300" Name="paletteControl">
     <DockPanel>
@@ -29,19 +30,19 @@
                                             Colors="{Binding ElementName=paletteControl, Path=Colors}"/>
                     <StackPanel Margin="0, 0, 5, 0" HorizontalAlignment="Right" Width="85" VerticalAlignment="Center" Orientation="Horizontal">
                         <Button Margin="0, 0, 5, 0" Style="{StaticResource ToolButtonStyle}" Click="BrowsePalettes_Click"
-                Cursor="Hand" Height="24" Width="24" ToolTip="Browse Palettes">
+                Cursor="Hand" Height="24" Width="24" views:Translator.TooltipKey="BROWSE_PALETTES">
                             <Button.Background>
                                 <ImageBrush ImageSource="/Images/Database.png"/>
                             </Button.Background>
                         </Button>
                         <Button Margin="0, 0, 5, 0" Style="{StaticResource ToolButtonStyle}" 
-                Cursor="Hand" Height="24" Width="24"  ToolTip="Load Palette" Click="ImportPalette_OnClick">
+                Cursor="Hand" Height="24" Width="24" views:Translator.TooltipKey="LOAD_PALETTE" Click="ImportPalette_OnClick">
                             <Button.Background>
                                 <ImageBrush ImageSource="/Images/Folder.png"/>
                             </Button.Background>
                         </Button>
                         <Button Height="24" Width="24" Margin="0" Style="{StaticResource ToolButtonStyle}" 
-                Cursor="Hand" ToolTip="Save Palette" Click="SavePalette_OnClick">
+                Cursor="Hand" views:Translator.TooltipKey="SAVE_PALETTE" Click="SavePalette_OnClick">
                             <Button.Background>
                                 <ImageBrush ImageSource="/Images/Save.png"/>
                             </Button.Background>
@@ -74,7 +75,9 @@
                     </ItemsControl.ItemsPanel>
                     <ItemsControl.ItemTemplate>
                         <DataTemplate>
-                            <palettes:PaletteColor Cursor="Hand" ToolTip="Click to select as main color. Drag and drop onto another palette color to swap them." AllowDrop="True" Color="{Binding}" 
+                            <palettes:PaletteColor Cursor="Hand"
+                                                   views:Translator.TooltipKey="PALETTE_COLOR_TOOLTIP"
+                                                   AllowDrop="True" Color="{Binding}"
                                                Margin="2.5"
                                             Drop="PaletteColor_Drop"
                                             AssociatedKey="{Binding Path=(ItemsControl.AlternationIndex), 

+ 3 - 1
src/PixiEditor/Views/UserControls/PrependTextBlock.xaml

@@ -30,6 +30,8 @@
             </TextBlock.Text>
         </TextBlock>
 
-        <TextBlock IsEnabled="{Binding ElementName=uc, Path=IsEnabled}" Visibility="{Binding HidePrepend, ElementName=uc, Converter={InverseBoolToVisibilityConverter}}" Text="{Binding Append, ElementName=uc}" Foreground="{Binding ElementName=uc, Path=AppendColor}"/>
+        <TextBlock IsEnabled="{Binding ElementName=uc, Path=IsEnabled}"
+                   Visibility="{Binding HidePrepend, ElementName=uc, Converter={InverseBoolToVisibilityConverter}}"
+                   Text="{Binding Append, ElementName=uc}" Foreground="{Binding ElementName=uc, Path=AppendColor}"/>
     </StackPanel>
 </UserControl>

+ 8 - 7
src/PixiEditor/Views/UserControls/ShortcutBox.cs

@@ -1,6 +1,7 @@
 using System.Windows;
 using System.Windows.Controls;
 using System.Windows.Input;
+using PixiEditor.Localization;
 using PixiEditor.Models.Commands;
 using PixiEditor.Models.Commands.Commands;
 using PixiEditor.Models.DataHolders;
@@ -53,21 +54,21 @@ internal class ShortcutBox : ContentControl
                 var oldShortcut = Command.Shortcut;
                 bool enableSwap = oldShortcut is not { Key: Key.None, Modifiers: ModifierKeys.None };
 
-                string text = enableSwap ?
-                    $"This shortcut is already assigned to '{oldCommand.DisplayName}'\nDo you want to replace the existing shortcut or swap the two?" :
-                    $"This shortcut is already assigned to '{oldCommand.DisplayName}'\nDo you want to replace the existing shortcut?";
-                OptionsDialog<string> dialog = new("Already assigned", text);
+                LocalizedString text = enableSwap ?
+                    new LocalizedString("SHORTCUT_ALREADY_ASSIGNED_SWAP", oldCommand.DisplayName) :
+                    new LocalizedString("SHORTCUT_ALREADY_ASSIGNED_OVERWRITE", oldCommand.DisplayName);
+                OptionsDialog<string> dialog = new("ALREADY_ASSIGNED", text);
 
-                dialog.Add("Replace", x => controller.ReplaceShortcut(Command, e));
+                dialog.Add(new LocalizedString("REPLACE"), x => controller.ReplaceShortcut(Command, e));
                 if (enableSwap)
                 {
-                    dialog.Add("Swap", x =>
+                    dialog.Add(new LocalizedString("SWAP"), x =>
                     {
                         controller.ReplaceShortcut(Command, e);
                         controller.ReplaceShortcut(oldCommand, oldShortcut);
                     });
                 }
-                dialog.Add("Cancel", x => box.KeyCombination = Command.Shortcut);
+                dialog.Add(new LocalizedString("CANCEL"), x => box.KeyCombination = Command.Shortcut);
 
                 dialog.ShowDialog();
                 changingCombination = false;