Ver Fonte

Localized remaining viewmodels

Krzysztof Krysiński há 2 anos atrás
pai
commit
433396f1fe

+ 86 - 5
src/PixiEditor/Data/Localization/Languages/en.json

@@ -1,7 +1,7 @@
 {
   "FILE": "File",
   "RECENT_FILES": "Recent Files",
-  "OPEN_FILE": "Open",
+  "OPEN": "Open",
   "NEW_FILE": "New",
   "RECENT_EMPTY_TEXT": "So much empty space",
   
@@ -19,7 +19,18 @@
   "LAYERS": "{0} layers",
 
   "KEY_BINDINGS": "Key Bindings",
+
   "MISC": "Misc",
+  "DOCUMENTATION": "Documentation",
+  "WEBSITE": "Website",
+  "OPEN_WEBSITE": "Open website",
+  "REPOSITORY": "Repository",
+  "OPEN_REPOSITORY": "Open repository",
+  "LICENSE": "License",
+  "OPEN_LICENSE": "Open license",
+  "THIRD_PARTY_LICENSES": "Third party licenses",
+  "OPEN_THIRD_PARTY_LICENSES": "Open third party licenses",
+
   "SHOW_STARTUP_WINDOW": "Show Startup Window",
   "SHOW_IMAGE_PREVIEW_TASKBAR": "Show image preview in taskbar",
   "RECENT_FILE_LENGTH": "Recent file list length",
@@ -28,11 +39,23 @@
   "WIDTH": "Width",
   "HEIGHT": "Height",
   "TOOLS": "Tools",
+  "APPLY_TRANSFORM": "Apply transform",
+  "INCREASE_TOOL_SIZE": "Increase tool size",
+  "DECREASE_TOOL_SIZE": "Decrease tool size",
   "ENABLE_SHARED_TOOLBAR": "Enable shared toolbar",
   "AUTOMATIC_UPDATES": "Automatic Updates",
   "CHECK_FOR_UPDATES": "Check updates on startup",
   "UPDATE_STREAM": "Update stream",
-  "UPDATE_CHANNEL_HELP_TOOLTIP": "Update channels can only be changed in standalone version (downloaded from https://pixieditor.net).
Steam and Microsoft Store versions handle updates separately.",
+  "TO_INSTALL_UPDATE": "to install update {0}",
+  "UPDATE_CHANNEL_HELP_TOOLTIP": "Update channels can only be changed in standalone version (downloaded from https://pixieditor.net).\nSteam and Microsoft Store versions handle updates separately.",
+  "DOWNLOADING_UPDATE": "Downloading update...",
+  "UPDATE_READY": "Update is ready to be installed. Do you want to install it now?",
+  "NEW_UPDATE": "New update",
+  "COULD_NOT_UPDATE_WITHOUT_ADMIN": "Couldn't update without admin privileges. Please run PixiEditor as administrator.",
+  "INSUFFICIENT_PERMISSIONS": "Insufficient permissions",
+  "UPDATE_CHECK_FAILED": "Update check failed",
+  "COULD_NOT_CHECK_FOR_UPDATES": "Could not check if there is an update available.",
+  "VERSION": "Version {0}",
 
   "DEBUG": "Debug",
   "ENABLE_DEBUG_MODE": "Enable Debug mode",
@@ -65,7 +88,13 @@
   "FILE_NOT_FOUND_PATH_FULL_PATH": "File {0} does not exist\n(Full Path: {1})",
   "ARE_YOU_SURE": "Are you sure?",
   "ARE_YOU_SURE_PATH_FULL_PATH": "Are you sure you want to delete {0}?\nThis data will be lost for all installations.\n(Full Path: {1})",
-  
+
+  "OPEN_FILE": "Open file",
+  "FAILED_TO_OPEN_FILE": "Failed to open the file",
+
+  "OLD_FILE_FORMAT": "Old file format",
+  "OLD_FILE_FORMAT_DESCRIPTION": "This .pixi file uses the old format,\n which is no longer supported and can't be opened.",
+
   "DISCORD_RICH_PRESENCE": "Rich Presence",
   "ENABLED": "Enabled",
   "SHOW_IMAGE_NAME": "Show image name",
@@ -80,6 +109,7 @@
   
   "NOTHING_FOUND": "Nothing found",
   "EXPORT": "Export",
+  "EXPORT_IMAGE": "Export image",
   "IMPORT": "Import",
   "SHORTCUT_TEMPLATES": "Shortcut templates",
   "RESET_ALL": "Reset all",
@@ -95,7 +125,14 @@
   "CREATE_NEW_FOLDER": "Create new folder",
   "NEW_LAYER": "New layer",
   "CREATE_NEW_LAYER": "Create new layer",
-  
+  "NEW_IMAGE": "New image",
+  "CREATE_NEW_IMAGE": "Create new image",
+
+  "SAVE": "Save",
+  "SAVE_AS": "Save as...",
+  "SAVE_IMAGE": "Save image",
+  "SAVE_IMAGE_AS": "Save image as new",
+
   "DUPLICATE_SELECTED_LAYER": "Duplicate selected layer",
   
   "CREATE_MASK": "Create mask",
@@ -193,6 +230,8 @@
   "SELECT_COLOR_9": "Select color 9",
   "SELECT_COLOR_10": "Select color 10",
 
+  "SELECT_TOOL": "Select {0} Tool",
+
   "SELECT_COLOR_1_DESCRIPTIVE": "Select the first color in the palette",
   "SELECT_COLOR_2_DESCRIPTIVE": "Select the second color in the palette",
   "SELECT_COLOR_3_DESCRIPTIVE": "Select the third color in the palette",
@@ -208,13 +247,55 @@
   "SWAP_COLORS_DESCRIPTIVE": "Swap primary and secondary colors",
 
   "SEARCH": "Search",
+  "COMMAND_SEARCH": "Command search",
+  "OPEN_COMMAND_SEARCH": "Open command search window",
 
   "SELECTION": "Selection",
+  "SELECT_ALL": "Select all",
+  "SELECT_ALL_DESCRIPTIVE": "Select everything",
+  "CLEAR_SELECTION": "Clear selection",
+  "INVERT_SELECTION": "Invert selection",
+  "INVERT_SELECTION_DESCRIPTIVE": "Invert the selected area",
+  "TRANSFORM_SELECTED_AREA": "Transform selected area",
+  "NUDGE_SELECTED_LEFT": "Nudge selected object left",
+  "NUDGE_SELECTED_RIGHT": "Nudge selected object right",
+  "NUDGE_SELECTED_UP": "Nudge selected object up",
+  "NUDGE_SELECTED_DOWN": "Nudge selected object down",
+  "MASK_FROM_SELECTION": "New mask from selection",
+  "MASK_FROM_SELECTION_DESCRIPTIVE": "Selection to new mask",
+  "ADD_SELECTION_TO_MASK": "Add selection to mask",
+  "SUBTRACT_SELECTION_FROM_MASK": "Subtract selection from mask",
+  "INTEREST_SELECTION_MASK": "Intersect selection with mask",
+  "SELECTION_TO_MASK": "Selection to mask",
 
   "STYLUS": "Stylus",
+  "TOGGLE_PEN_MODE": "Toggle pen mode",
 
   "UNDO": "Undo",
+  "UNDO_DESCRIPTIVE": "Undo last action",
   "REDO": "Redo",
+  "REDO_DESCRIPTIVE": "Redo last action",
+  "WINDOWS": "Windows",
+
+  "TOGGLE_GRIDLINES": "Toggle gridlines",
+  "ZOOM_IN": "Zoom in",
+  "ZOOM_OUT": "Zoom out",
+  "NEW_WINDOW_FOR_IMG": "New window for current image",
+  "CENTER_ACTIVE_VIEWPORT": "Center active viewport",
+  "FLIP_VIEWPORT_HORIZONTALLY": "Flip viewport horizontally",
+  "FLIP_VIEWPORT_VERTICALLY": "Flip viewport vertically",
+  "OPEN_SETTINGS": "Open settings",
+  "OPEN_SETTINGS_DESCRIPTIVE": "Open settings window",
+
+  "OPEN_STARTUP_WINDOW": "Open startup window",
+  "OPEN_SHORTCUT_WINDOW": "Open shortcut window",
+  "OPEN_ABOUT_WINDOW": "Open about window",
+  "OPEN_NAVIGATION_WINDOW": "Open navigation window",
 
-  "WINDOWS": "Windows"
+  "ERROR": "Error",
+  "INTERNAL_ERROR": "Internal error",
+  "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."
 }

+ 4 - 0
src/PixiEditor/Localization/LocalizedString.cs

@@ -10,7 +10,11 @@ public struct LocalizedString
         set
         {
             key = value;
+            #if DEBUG
+            Value = key;
+            #else
             Value = GetValue(value);
+            #endif
         }
     }
     public string Value { get; private set; }

+ 5 - 2
src/PixiEditor/Models/Commands/CommandController.cs

@@ -3,6 +3,7 @@ using System.Reflection;
 using System.Windows.Media;
 using Microsoft.Extensions.DependencyInjection;
 using Newtonsoft.Json;
+using PixiEditor.Localization;
 using PixiEditor.Models.Commands.Commands;
 using PixiEditor.Models.Commands.Evaluators;
 using PixiEditor.Models.DataHolders;
@@ -155,11 +156,13 @@ internal class CommandController
 
             string internalName = $"PixiEditor.Tools.Select.{type.Name}";
 
+            LocalizedString displayName = new("SELECT_TOOL", toolInstance.DisplayName);
+
             var command = new Command.ToolCommand()
             {
                 InternalName = internalName,
-                DisplayName = $"Select {toolInstance.DisplayName} Tool",
-                Description = $"Select {toolInstance.DisplayName} Tool",
+                DisplayName = displayName,
+                Description = displayName,
                 IconPath = $"@{toolInstance.ImagePath}",
                 IconEvaluator = IconEvaluator.Default,
                 TransientKey = toolAttr.Transient,

+ 11 - 11
src/PixiEditor/ViewModels/SubViewModels/Main/FileViewModel.cs

@@ -112,7 +112,7 @@ internal class FileViewModel : SubViewModel<ViewModelMain>
         string path = (string)parameter;
         if (!File.Exists(path))
         {
-            NoticeDialog.Show("The file does not exist", "Failed to open the file");
+            NoticeDialog.Show("FILE_NOT_FOUND", "FAILED_TO_OPEN_FILE");
             RecentlyOpened.Remove(path);
             IPreferences.Current.UpdateLocalPreference(PreferencesConstants.RecentlyOpened, RecentlyOpened.Select(x => x.FilePath));
             return;
@@ -121,7 +121,7 @@ internal class FileViewModel : SubViewModel<ViewModelMain>
         OpenFromPath(path);
     }
 
-    [Command.Basic("PixiEditor.File.Open", "Open", "Open file", Key = Key.O, Modifiers = ModifierKeys.Control)]
+    [Command.Basic("PixiEditor.File.Open", "OPEN", "OPEN_FILE", Key = Key.O, Modifiers = ModifierKeys.Control)]
     public void OpenFromOpenFileDialog()
     {
         string filter = SupportedFilesHelper.BuildOpenFilter();
@@ -173,11 +173,11 @@ internal class FileViewModel : SubViewModel<ViewModelMain>
         }
         catch (CorruptedFileException ex)
         {
-            NoticeDialog.Show(ex.Message, "Failed to open the file");
+            NoticeDialog.Show(ex.Message, "FAILED_TO_OPEN_FILE");
         }
         catch (OldFileFormatException)
         {
-            NoticeDialog.Show("This .pixi file uses the old format,\n which is no longer supported and can't be opened.", "Old file format");
+            NoticeDialog.Show("OLD_FILE_FORMAT_DESCRIPTION", "OLD_FILE_FORMAT");
         }
     }
 
@@ -226,7 +226,7 @@ internal class FileViewModel : SubViewModel<ViewModelMain>
         }
     }
 
-    [Command.Basic("PixiEditor.File.New", "New image", "Create new image", Key = Key.N, Modifiers = ModifierKeys.Control)]
+    [Command.Basic("PixiEditor.File.New", "NEW_IMAGE", "CREATE_NEW_IMAGE", Key = Key.N, Modifiers = ModifierKeys.Control)]
     public void CreateFromNewFileDialog()
     {
         NewFileDialog newFile = new NewFileDialog();
@@ -254,8 +254,8 @@ internal class FileViewModel : SubViewModel<ViewModelMain>
         Owner.WindowSubViewModel.MakeDocumentViewportActive(doc);
     }
 
-    [Command.Basic("PixiEditor.File.Save", false, "Save", "Save image", CanExecute = "PixiEditor.HasDocument", Key = Key.S, Modifiers = ModifierKeys.Control, IconPath = "Save.png")]
-    [Command.Basic("PixiEditor.File.SaveAsNew", true, "Save as...", "Save image as new", CanExecute = "PixiEditor.HasDocument", Key = Key.S, Modifiers = ModifierKeys.Control | ModifierKeys.Shift, IconPath = "Save.png")]
+    [Command.Basic("PixiEditor.File.Save", false, "SAVE", "SAVE_IMAGE", CanExecute = "PixiEditor.HasDocument", Key = Key.S, Modifiers = ModifierKeys.Control, IconPath = "Save.png")]
+    [Command.Basic("PixiEditor.File.SaveAsNew", true, "SAVE_AS", "SAVE_IMAGE_AS", CanExecute = "PixiEditor.HasDocument", Key = Key.S, Modifiers = ModifierKeys.Control | ModifierKeys.Shift, IconPath = "Save.png")]
     public bool SaveActiveDocument(bool asNew)
     {
         DocumentViewModel doc = Owner.DocumentManagerSubViewModel.ActiveDocument;
@@ -300,7 +300,7 @@ internal class FileViewModel : SubViewModel<ViewModelMain>
     ///     Generates export dialog or saves directly if save data is known.
     /// </summary>
     /// <param name="parameter">CommandProperty.</param>
-    [Command.Basic("PixiEditor.File.Export", "Export", "Export image", CanExecute = "PixiEditor.HasDocument", Key = Key.S, Modifiers = ModifierKeys.Control | ModifierKeys.Alt | ModifierKeys.Shift)]
+    [Command.Basic("PixiEditor.File.Export", "EXPORT", "EXPORT_IMAGE", CanExecute = "PixiEditor.HasDocument", Key = Key.S, Modifiers = ModifierKeys.Control | ModifierKeys.Alt | ModifierKeys.Shift)]
     public void ExportFile()
     {
         DocumentViewModel doc = Owner.DocumentManagerSubViewModel.ActiveDocument;
@@ -323,13 +323,13 @@ internal class FileViewModel : SubViewModel<ViewModelMain>
         switch (result)
         {
             case DialogSaveResult.InvalidPath:
-                NoticeDialog.Show("Error", "Couldn't save the file to the specified location");
+                NoticeDialog.Show("ERROR", "ERROR_SAVE_LOCATION");
                 break;
             case DialogSaveResult.ConcurrencyError:
-                NoticeDialog.Show("Internal error", "An internal error occured while saving. Please try again.");
+                NoticeDialog.Show("INTERNAL_ERROR", "ERROR_WHILE_SAVING");
                 break;
             case DialogSaveResult.UnknownError:
-                NoticeDialog.Show("Error", "An error occured while saving.");
+                NoticeDialog.Show("ERROR", "UNKNOWN_ERROR_SAVING");
                 break;
         }
     }

+ 5 - 5
src/PixiEditor/ViewModels/SubViewModels/Main/MiscViewModel.cs

@@ -14,11 +14,11 @@ internal class MiscViewModel : SubViewModel<ViewModelMain>
     }
 
     [Command.Internal("PixiEditor.Links.OpenHyperlink")]
-    [Command.Basic("PixiEditor.Links.OpenDocumentation", "https://pixieditor.net/docs/introduction", "Documentation", "Open Documentation", IconPath = "Globe.png")]
-    [Command.Basic("PixiEditor.Links.OpenWebsite", "https://pixieditor.net", "Website", "Open Website", IconPath = "Globe.png")]
-    [Command.Basic("PixiEditor.Links.OpenRepository", "https://github.com/PixiEditor/PixiEditor", "Repository", "Open Repository", IconPath = "Globe.png")]
-    [Command.Basic("PixiEditor.Links.OpenLicense", "https://github.com/PixiEditor/PixiEditor/blob/master/LICENSE", "License", "Open License", IconPath = "Globe.png")]
-    [Command.Basic("PixiEditor.Links.OpenOtherLicenses", "https://pixieditor.net/docs/Third-party-licenses", "Third Party Licenses", "Open Third Party Licenses", IconPath = "Globe.png")]
+    [Command.Basic("PixiEditor.Links.OpenDocumentation", "https://pixieditor.net/docs/introduction", "DOCUMENTATION", "Open Documentation", IconPath = "Globe.png")]
+    [Command.Basic("PixiEditor.Links.OpenWebsite", "https://pixieditor.net", "WEBSITE", "OPEN_WEBSITE", IconPath = "Globe.png")]
+    [Command.Basic("PixiEditor.Links.OpenRepository", "https://github.com/PixiEditor/PixiEditor", "REPOSITORY", "OPEN_REPOSITORY", IconPath = "Globe.png")]
+    [Command.Basic("PixiEditor.Links.OpenLicense", "https://github.com/PixiEditor/PixiEditor/blob/master/LICENSE", "LICENSE", "OPEN_LICENSE", IconPath = "Globe.png")]
+    [Command.Basic("PixiEditor.Links.OpenOtherLicenses", "https://pixieditor.net/docs/Third-party-licenses", "THIRD_PARTY_LICENSES", "OPEN_THIRD_PARTY_LICENSES", IconPath = "Globe.png")]
     public static void OpenHyperlink(string url)
     {
         ProcessHelpers.ShellExecute(url);

+ 2 - 2
src/PixiEditor/ViewModels/SubViewModels/Main/RegistryViewModel.cs

@@ -41,7 +41,7 @@ internal class RegistryViewModel : SubViewModel<ViewModelMain>
         }
         catch
         {
-            NoticeDialog.Show("Failed to associate Lospec Palette protocol", "Error");
+            NoticeDialog.Show("FAILED_ASSOCIATE_LOSPEC", "ERROR");
         }
     }
 
@@ -65,7 +65,7 @@ internal class RegistryViewModel : SubViewModel<ViewModelMain>
         }
         catch
         {
-            NoticeDialog.Show("Failed to associate lospec-palette protocol", "Error");
+            NoticeDialog.Show("FAILED_ASSOCIATE_LOSPEC", "ERROR");
         }
     }
 

+ 1 - 1
src/PixiEditor/ViewModels/SubViewModels/Main/SearchViewModel.cs

@@ -34,7 +34,7 @@ internal class SearchViewModel : SubViewModel<ViewModelMain>
     [Evaluator.CanExecute("PixiEditor.Search.CanOpenSearchWindow")]
     public bool CanToggleSeachWindow() => !ViewModelMain.Current?.DocumentManagerSubViewModel.ActiveDocument?.Busy ?? true;
 
-    [Command.Basic("PixiEditor.Search.Toggle", "", "Command Search", "Open the command search window", Key = Key.K, Modifiers = ModifierKeys.Control, CanExecute = "PixiEditor.Search.CanOpenSearchWindow")]
+    [Command.Basic("PixiEditor.Search.Toggle", "", "COMMAND_SEARCH", "OPEN_COMMAND_SEARCH", Key = Key.K, Modifiers = ModifierKeys.Control, CanExecute = "PixiEditor.Search.CanOpenSearchWindow")]
     public void ToggleSearchWindow(string searchTerm)
     {
         SelectAll = true;

+ 13 - 13
src/PixiEditor/ViewModels/SubViewModels/Main/SelectionViewModel.cs

@@ -13,7 +13,7 @@ internal class SelectionViewModel : SubViewModel<ViewModelMain>
     {
     }
 
-    [Command.Basic("PixiEditor.Selection.SelectAll", "Select all", "Select everything", CanExecute = "PixiEditor.HasDocument", Key = Key.A, Modifiers = ModifierKeys.Control)]
+    [Command.Basic("PixiEditor.Selection.SelectAll", "SELECT_ALL", "SELECT_ALL_DESCRIPTIVE", CanExecute = "PixiEditor.HasDocument", Key = Key.A, Modifiers = ModifierKeys.Control)]
     public void SelectAll()
     {
         var doc = Owner.DocumentManagerSubViewModel.ActiveDocument;
@@ -22,7 +22,7 @@ internal class SelectionViewModel : SubViewModel<ViewModelMain>
         doc.Operations.SelectAll();
     }
 
-    [Command.Basic("PixiEditor.Selection.Clear", "Clear selection", "Clear selection", CanExecute = "PixiEditor.Selection.IsNotEmpty", Key = Key.D, Modifiers = ModifierKeys.Control)]
+    [Command.Basic("PixiEditor.Selection.Clear", "CLEAR_SELECTION", "CLEAR_SELECTION", CanExecute = "PixiEditor.Selection.IsNotEmpty", Key = Key.D, Modifiers = ModifierKeys.Control)]
     public void ClearSelection()
     {
         var doc = Owner.DocumentManagerSubViewModel.ActiveDocument;
@@ -31,7 +31,7 @@ internal class SelectionViewModel : SubViewModel<ViewModelMain>
         doc.Operations.ClearSelection();
     }
 
-    [Command.Basic("PixiEditor.Selection.InvertSelection", "Invert selection", "Invert the selected area", CanExecute = "PixiEditor.Selection.IsNotEmpty", Key = Key.I, Modifiers = ModifierKeys.Control)]
+    [Command.Basic("PixiEditor.Selection.InvertSelection", "INVERT_SELECTION", "INVERT_SELECTION_DESCRIPTIVE", CanExecute = "PixiEditor.Selection.IsNotEmpty", Key = Key.I, Modifiers = ModifierKeys.Control)]
     public void InvertSelection()
     {
         Owner.DocumentManagerSubViewModel.ActiveDocument?.Operations.InvertSelection();
@@ -49,27 +49,27 @@ internal class SelectionViewModel : SubViewModel<ViewModelMain>
         return SelectionIsNotEmpty() && (Owner.DocumentManagerSubViewModel.ActiveDocument?.SelectedStructureMember?.HasMaskBindable ?? false);
     }
 
-    [Command.Basic("PixiEditor.Selection.TransformArea", "Transform selected area", "Transform selected area", CanExecute = "PixiEditor.Selection.IsNotEmpty", Key = Key.T, Modifiers = ModifierKeys.Control)]
+    [Command.Basic("PixiEditor.Selection.TransformArea", "TRANSFORM_SELECTED_AREA", "TRANSFORM_SELECTED_AREA", CanExecute = "PixiEditor.Selection.IsNotEmpty", Key = Key.T, Modifiers = ModifierKeys.Control)]
     public void TransformSelectedArea()
     {
         Owner.DocumentManagerSubViewModel.ActiveDocument?.Operations.TransformSelectedArea(false);
     }
 
-    [Command.Basic("PixiEditor.Selection.NudgeSelectedObjectLeft", "Nudge selected object left", "Nudge selected object left", Key = Key.Left, Parameter = new int[] { -1, 0 }, IconPath = "E76B", IconEvaluator = "PixiEditor.FontIcon", CanExecute = "PixiEditor.Selection.CanNudgeSelectedObject")]
-    [Command.Basic("PixiEditor.Selection.NudgeSelectedObjectRight", "Nudge selected object right", "Nudge selected object right", Key = Key.Right, Parameter = new int[] { 1, 0 }, IconPath = "E76C", IconEvaluator = "PixiEditor.FontIcon", CanExecute = "PixiEditor.Selection.CanNudgeSelectedObject")]
-    [Command.Basic("PixiEditor.Selection.NudgeSelectedObjectUp", "Nudge selected object up", "Nudge selected object up", Key = Key.Up, Parameter = new int[] { 0, -1 }, IconPath = "E70E", IconEvaluator = "PixiEditor.FontIcon", CanExecute = "PixiEditor.Selection.CanNudgeSelectedObject")]
-    [Command.Basic("PixiEditor.Selection.NudgeSelectedObjectDown", "Nudge selected object down", "Nudge selected object down", Key = Key.Down, Parameter = new int[] { 0, 1 }, IconPath = "E70D", IconEvaluator = "PixiEditor.FontIcon", CanExecute = "PixiEditor.Selection.CanNudgeSelectedObject")]
+    [Command.Basic("PixiEditor.Selection.NudgeSelectedObjectLeft", "NUDGE_SELECTED_LEFT", "NUDGE_SELECTED_LEFT", Key = Key.Left, Parameter = new int[] { -1, 0 }, IconPath = "E76B", IconEvaluator = "PixiEditor.FontIcon", CanExecute = "PixiEditor.Selection.CanNudgeSelectedObject")]
+    [Command.Basic("PixiEditor.Selection.NudgeSelectedObjectRight", "NUDGE_SELECTED_RIGHT", "NUDGE_SELECTED_RIGHT", Key = Key.Right, Parameter = new int[] { 1, 0 }, IconPath = "E76C", IconEvaluator = "PixiEditor.FontIcon", CanExecute = "PixiEditor.Selection.CanNudgeSelectedObject")]
+    [Command.Basic("PixiEditor.Selection.NudgeSelectedObjectUp", "NUDGE_SELECTED_UP", "NUDGE_SELECTED_UP", Key = Key.Up, Parameter = new int[] { 0, -1 }, IconPath = "E70E", IconEvaluator = "PixiEditor.FontIcon", CanExecute = "PixiEditor.Selection.CanNudgeSelectedObject")]
+    [Command.Basic("PixiEditor.Selection.NudgeSelectedObjectDown", "NUDGE_SELECTED_DOWN", "NUDGE_SELECTED_DOWN", Key = Key.Down, Parameter = new int[] { 0, 1 }, IconPath = "E70D", IconEvaluator = "PixiEditor.FontIcon", CanExecute = "PixiEditor.Selection.CanNudgeSelectedObject")]
     public void NudgeSelectedObject(int[] dist)
     {
         VecI distance = new(dist[0], dist[1]);
         Owner.DocumentManagerSubViewModel.ActiveDocument?.Operations.NudgeSelectedObject(distance);
     }
 
-    [Command.Basic("PixiEditor.Selection.NewToMask", SelectionMode.New, "New mask from selection", "Selection to new mask", CanExecute = "PixiEditor.Selection.IsNotEmpty")]
-    [Command.Basic("PixiEditor.Selection.AddToMask", SelectionMode.Add, "Add selection to mask", "Add selection to mask", CanExecute = "PixiEditor.Selection.IsNotEmpty")]
-    [Command.Basic("PixiEditor.Selection.SubtractFromMask", SelectionMode.Subtract, "Subtract selection from mask", "Subtract selection from mask", CanExecute = "PixiEditor.Selection.IsNotEmptyAndHasMask")]
-    [Command.Basic("PixiEditor.Selection.IntersectSelectionMask", SelectionMode.Intersect, "Intersect selection with mask", "Intersect selection with mask", CanExecute = "PixiEditor.Selection.IsNotEmptyAndHasMask")]
-    [Command.Filter("PixiEditor.Selection.ToMaskMenu", "Selection to mask", "Selection to mask", Key = Key.M, Modifiers = ModifierKeys.Control)]
+    [Command.Basic("PixiEditor.Selection.NewToMask", SelectionMode.New, "MASK_FROM_SELECTION", "MASK_FROM_SELECTION_DESCRIPTIVE", CanExecute = "PixiEditor.Selection.IsNotEmpty")]
+    [Command.Basic("PixiEditor.Selection.AddToMask", SelectionMode.Add, "ADD_SELECTION_TO_MASK", "ADD_SELECTION_TO_MASK", CanExecute = "PixiEditor.Selection.IsNotEmpty")]
+    [Command.Basic("PixiEditor.Selection.SubtractFromMask", SelectionMode.Subtract, "SUBTRACT_SELECTION_FROM_MASK", "SUBTRACT_SELECTION_FROM_MASK", CanExecute = "PixiEditor.Selection.IsNotEmptyAndHasMask")]
+    [Command.Basic("PixiEditor.Selection.IntersectSelectionMask", SelectionMode.Intersect, "INTEREST_SELECTION_MASK", "INTEREST_SELECTION_MASK", CanExecute = "PixiEditor.Selection.IsNotEmptyAndHasMask")]
+    [Command.Filter("PixiEditor.Selection.ToMaskMenu", "SELECTION_TO_MASK", "SELECTION_TO_MASK", Key = Key.M, Modifiers = ModifierKeys.Control)]
     public void SelectionToMask(SelectionMode mode)
     {
         Owner.DocumentManagerSubViewModel.ActiveDocument?.Operations.SelectionToMask(mode);

+ 1 - 1
src/PixiEditor/ViewModels/SubViewModels/Main/StylusViewModel.cs

@@ -48,7 +48,7 @@ internal class StylusViewModel : SubViewModel<ViewModelMain>
         UpdateUseTouchGesture();
     }
 
-    [Command.Basic("PixiEditor.Stylus.TogglePenMode", "Toggle Pen Mode", "Toggle Pen Mode", IconPath = "penMode.png")]
+    [Command.Basic("PixiEditor.Stylus.TogglePenMode", "TOGGLE_PEN_MODE", "TOGGLE_PEN_MODE", IconPath = "penMode.png")]
     public void TogglePenMode()
     {
         IsPenModeEnabled = !IsPenModeEnabled;

+ 3 - 3
src/PixiEditor/ViewModels/SubViewModels/Main/ToolsViewModel.cs

@@ -83,7 +83,7 @@ internal class ToolsViewModel : SubViewModel<ViewModelMain>
         SetActiveTool(typeof(T), transient);
     }
 
-    [Command.Basic("PixiEditor.Tools.ApplyTransform", "Apply transform", "", Key = Key.Enter)]
+    [Command.Basic("PixiEditor.Tools.ApplyTransform", "APPLY_TRANSFORM", "", Key = Key.Enter)]
     public void ApplyTransform()
     {
         DocumentViewModel? doc = Owner.DocumentManagerSubViewModel.ActiveDocument;
@@ -157,8 +157,8 @@ internal class ToolsViewModel : SubViewModel<ViewModelMain>
         SetActiveTool(tool.GetType(), false);
     }
 
-    [Command.Basic("PixiEditor.Tools.IncreaseSize", 1, "Increase Tool Size", "Increase Tool Size", Key = Key.OemCloseBrackets)]
-    [Command.Basic("PixiEditor.Tools.DecreaseSize", -1, "Decrease Tool Size", "Decrease Tool Size", Key = Key.OemOpenBrackets)]
+    [Command.Basic("PixiEditor.Tools.IncreaseSize", 1, "INCREASE_TOOL_SIZE", "INCREASE_TOOL_SIZE", Key = Key.OemCloseBrackets)]
+    [Command.Basic("PixiEditor.Tools.DecreaseSize", -1, "DECREASE_TOOL_SIZE", "DECREASE_TOOL_SIZE", Key = Key.OemOpenBrackets)]
     public void ChangeToolSize(int increment)
     {
         if (ActiveTool?.Toolbar is not BasicToolbar toolbar)

+ 2 - 2
src/PixiEditor/ViewModels/SubViewModels/Main/UndoViewModel.cs

@@ -16,7 +16,7 @@ internal class UndoViewModel : SubViewModel<ViewModelMain>
     /// <summary>
     ///     Redo last action.
     /// </summary>
-    [Command.Basic("PixiEditor.Undo.Redo", "Redo", "Redo next step", CanExecute = "PixiEditor.Undo.CanRedo", Key = Key.Y, Modifiers = ModifierKeys.Control,
+    [Command.Basic("PixiEditor.Undo.Redo", "REDO", "REDO_DESCRIPTIVE", CanExecute = "PixiEditor.Undo.CanRedo", Key = Key.Y, Modifiers = ModifierKeys.Control,
         IconPath = "E7A6", IconEvaluator = "PixiEditor.FontIcon")]
     public void Redo()
     {
@@ -29,7 +29,7 @@ internal class UndoViewModel : SubViewModel<ViewModelMain>
     /// <summary>
     ///     Undo last action.
     /// </summary>
-    [Command.Basic("PixiEditor.Undo.Undo", "Undo", "Undo previous step", CanExecute = "PixiEditor.Undo.CanUndo", Key = Key.Z, Modifiers = ModifierKeys.Control,
+    [Command.Basic("PixiEditor.Undo.Undo", "UNDO", "UNDO_DESCRIPTIVE", CanExecute = "PixiEditor.Undo.CanUndo", Key = Key.Z, Modifiers = ModifierKeys.Control,
         IconPath = "E7A7", IconEvaluator = "PixiEditor.FontIcon")]
     public void Undo()
     {

+ 9 - 8
src/PixiEditor/ViewModels/SubViewModels/Main/UpdateViewModel.cs

@@ -6,6 +6,7 @@ using System.Linq;
 using System.Threading.Tasks;
 using System.Windows;
 using PixiEditor.Helpers;
+using PixiEditor.Localization;
 using PixiEditor.Models.Commands.Attributes;
 using PixiEditor.Models.Commands.Attributes.Commands;
 using PixiEditor.Models.Dialogs;
@@ -43,7 +44,7 @@ internal class UpdateViewModel : SubViewModel<ViewModelMain>
             RaisePropertyChanged(nameof(UpdateReadyToInstall));
             if (value)
             {
-                VersionText = $"to install update ({UpdateChecker.LatestReleaseInfo.TagName})"; // Button shows "Restart" before this text
+                VersionText = new LocalizedString("TO_INSTALL_UPDATE", UpdateChecker.LatestReleaseInfo.TagName); // Button shows "Restart" before this text
             }
         }
     }
@@ -75,7 +76,7 @@ internal class UpdateViewModel : SubViewModel<ViewModelMain>
         if (updateAvailable && updateFileDoesNotExists && updateExeDoesNotExists)
         {
             UpdateReadyToInstall = false;
-            VersionText = "Downloading update...";
+            VersionText = new LocalizedString("DOWNLOADING_UPDATE");
             if (updateCompatible)
             {
                 await UpdateDownloader.DownloadReleaseZip(UpdateChecker.LatestReleaseInfo);
@@ -119,7 +120,7 @@ internal class UpdateViewModel : SubViewModel<ViewModelMain>
                 if (updateFileExists || updateExeExists)
                 {
                     ViewModelMain.Current.UpdateSubViewModel.UpdateReadyToInstall = true;
-                    var result = ConfirmationDialog.Show("Update is ready to be installed. Do you want to install it now?", "New update");
+                    var result = ConfirmationDialog.Show("UPDATE_READY", "NEW_UPDATE");
                     if (result == Models.Enums.ConfirmationType.Yes)
                     {
                         if (updateFileExists && File.Exists(updaterPath))
@@ -146,8 +147,8 @@ internal class UpdateViewModel : SubViewModel<ViewModelMain>
         catch (Win32Exception)
         {
             NoticeDialog.Show(
-                "Couldn't update without administrator rights.",
-                "Insufficient permissions");
+                "COULD_NOT_UPDATE_WITHOUT_ADMIN",
+                "INSUFFICIENT_PERMISSIONS");
         }
     }
 
@@ -182,7 +183,7 @@ internal class UpdateViewModel : SubViewModel<ViewModelMain>
         }
         catch (Win32Exception)
         {
-            NoticeDialog.Show("Couldn't update without administrator rights.", "Insufficient permissions");
+            NoticeDialog.Show("COULD_NOT_UPDATE_WITHOUT_ADMIN", "INSUFFICIENT_PERMISSIONS");
         }
     }
 
@@ -202,7 +203,7 @@ internal class UpdateViewModel : SubViewModel<ViewModelMain>
             }
             catch (System.Net.Http.HttpRequestException)
             {
-                NoticeDialog.Show("Could not check if there is an update available", "Update check failed");
+                NoticeDialog.Show("COULD_NOT_CHECK_FOR_UPDATES", "UPDATE_CHECK_FAILED");
             }
 
             AskToInstall();
@@ -229,7 +230,7 @@ internal class UpdateViewModel : SubViewModel<ViewModelMain>
 
         string version = VersionHelpers.GetCurrentAssemblyVersionString();
         UpdateChecker = new UpdateChecker(version, GetUpdateChannel(updateChannel));
-        VersionText = $"Version {version}";
+        VersionText = new LocalizedString("VERSION", version);
     }
 
     private UpdateChannel GetUpdateChannel(string channelName)

+ 3 - 3
src/PixiEditor/ViewModels/SubViewModels/Main/ViewOptionsViewModel.cs

@@ -18,14 +18,14 @@ internal class ViewOptionsViewModel : SubViewModel<ViewModelMain>
     {
     }
 
-    [Command.Basic("PixiEditor.View.ToggleGrid", "Toggle gridlines", "Toggle gridlines", Key = Key.OemTilde, Modifiers = ModifierKeys.Control)]
+    [Command.Basic("PixiEditor.View.ToggleGrid", "TOGGLE_GRIDLINES", "TOGGLE_GRIDLINES", Key = Key.OemTilde, Modifiers = ModifierKeys.Control)]
     public void ToggleGridLines()
     {
         GridLinesEnabled = !GridLinesEnabled;
     }
 
-    [Command.Basic("PixiEditor.View.ZoomIn", 1, "Zoom in", "Zoom in", CanExecute = "PixiEditor.HasDocument", Key = Key.OemPlus)]
-    [Command.Basic("PixiEditor.View.Zoomout", -1, "Zoom out", "Zoom out", CanExecute = "PixiEditor.HasDocument", Key = Key.OemMinus)]
+    [Command.Basic("PixiEditor.View.ZoomIn", 1, "ZOOM_IN", "ZOOM_IN", CanExecute = "PixiEditor.HasDocument", Key = Key.OemPlus)]
+    [Command.Basic("PixiEditor.View.Zoomout", -1, "ZOOM_OUT", "ZOOM_OUT", CanExecute = "PixiEditor.HasDocument", Key = Key.OemMinus)]
     public void ZoomViewport(double zoom)
     {
         ViewportWindowViewModel? viewport = Owner.WindowSubViewModel.ActiveWindow as ViewportWindowViewModel;

+ 9 - 9
src/PixiEditor/ViewModels/SubViewModels/Main/WindowViewModel.cs

@@ -44,7 +44,7 @@ internal class WindowViewModel : SubViewModel<ViewModelMain>
         this.commandController = commandController;
     }
 
-    [Command.Basic("PixiEditor.Window.CreateNewViewport", "New window for current image", "New window for current image", CanExecute = "PixiEditor.HasDocument")]
+    [Command.Basic("PixiEditor.Window.CreateNewViewport", "NEW_WINDOW_FOR_IMG", "NEW_WINDOW_FOR_IMG", CanExecute = "PixiEditor.HasDocument")]
     public void CreateNewViewport()
     {
         var doc = ViewModelMain.Current?.DocumentManagerSubViewModel.ActiveDocument;
@@ -53,14 +53,14 @@ internal class WindowViewModel : SubViewModel<ViewModelMain>
         CreateNewViewport(doc);
     }
     
-    [Command.Basic("PixiEditor.Window.CenterActiveViewport", "Center active viewport", "Center active viewport", CanExecute = "PixiEditor.HasDocument")]
+    [Command.Basic("PixiEditor.Window.CenterActiveViewport", "CENTER_ACTIVE_VIEWPORT", "CENTER_ACTIVE_VIEWPORT", CanExecute = "PixiEditor.HasDocument")]
     public void CenterCurrentViewport()
     {
         if (ActiveWindow is ViewportWindowViewModel viewport)
             viewport.CenterViewportTrigger.Execute(this, viewport.Document.SizeBindable);
     }
     
-    [Command.Basic("PixiEditor.Window.FlipHorizontally", "Flip Viewport Horizontally", "Flip Viewport Horizontally", CanExecute = "PixiEditor.HasDocument", IconPath = "FlipHorizontal.png")]
+    [Command.Basic("PixiEditor.Window.FlipHorizontally", "FLIP_VIEWPORT_HORIZONTALLY", "FLIP_VIEWPORT_HORIZONTALLY", CanExecute = "PixiEditor.HasDocument", IconPath = "FlipHorizontal.png")]
     public void FlipViewportHorizontally()
     {
         if (ActiveWindow is ViewportWindowViewModel viewport)
@@ -69,7 +69,7 @@ internal class WindowViewModel : SubViewModel<ViewModelMain>
         }
     }
     
-    [Command.Basic("PixiEditor.Window.FlipVertically", "Flip Viewport Vertically", "Flip Viewport Vertically", CanExecute = "PixiEditor.HasDocument", IconPath = "FlipVertical.png")]
+    [Command.Basic("PixiEditor.Window.FlipVertically", "FLIP_VIEWPORT_VERTICALLY", "FLIP_VIEWPORT_VERTICALLY", CanExecute = "PixiEditor.HasDocument", IconPath = "FlipVertical.png")]
     public void FlipViewportVertically()
     {
         if (ActiveWindow is ViewportWindowViewModel viewport)
@@ -132,7 +132,7 @@ internal class WindowViewModel : SubViewModel<ViewModelMain>
         }
     }
 
-    [Command.Basic("PixiEditor.Window.OpenSettingsWindow", "Open Settings", "Open Settings Window", Key = Key.OemComma, Modifiers = ModifierKeys.Control)]
+    [Command.Basic("PixiEditor.Window.OpenSettingsWindow", "OPEN_SETTINGS", "OPEN_SETTINGS_DESCRIPTIVE", Key = Key.OemComma, Modifiers = ModifierKeys.Control)]
     public static void OpenSettingsWindow(int page)
     {
         if (page < 0)
@@ -144,13 +144,13 @@ internal class WindowViewModel : SubViewModel<ViewModelMain>
         settings.Show();
     }
 
-    [Command.Basic("PixiEditor.Window.OpenStartupWindow", "Open Startup Window", "Open Startup Window")]
+    [Command.Basic("PixiEditor.Window.OpenStartupWindow", "OPEN_STARTUP_WINDOW", "OPEN_STARTUP_WINDOW")]
     public void OpenHelloThereWindow()
     {
         new HelloTherePopup(Owner.FileSubViewModel).Show();
     }
 
-    [Command.Basic("PixiEditor.Window.OpenShortcutWindow", "Open Shortcut Window", "Open Shortcut Window", Key = Key.F1)]
+    [Command.Basic("PixiEditor.Window.OpenShortcutWindow", "OPEN_SHORTCUT_WINDOW", "OPEN_SHORTCUT_WINDOW", Key = Key.F1)]
     public void ShowShortcutWindow()
     {
         ShortcutPopup.Show();
@@ -158,13 +158,13 @@ internal class WindowViewModel : SubViewModel<ViewModelMain>
     }
     
         
-    [Command.Basic("PixiEditor.Window.OpenAboutWindow", "Open About Window", "Open About Window")]
+    [Command.Basic("PixiEditor.Window.OpenAboutWindow", "OPEN_ABOUT_WINDOW", "OPEN_ABOUT_WINDOW")]
     public void OpenAboutWindow()
     {
         new AboutPopup().Show();
     }
 
-    [Command.Basic("PixiEditor.Window.OpenNavigationWindow", "navigation", "Open Navigation Window", "Open Navigation Window")]
+    [Command.Basic("PixiEditor.Window.OpenNavigationWindow", "navigation", "OPEN_NAVIGATION_WINDOW", "OPEN_NAVIGATION_WINDOW")]
     public static void ShowAvalonDockWindow(string id)
     {
         if (MainWindow.Current?.LayoutRoot?.Manager?.Layout == null) return;