diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml
index f6844dedb2..a247679f18 100644
--- a/.github/workflows/release.yml
+++ b/.github/workflows/release.yml
@@ -144,7 +144,7 @@ jobs:
           GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
         with:
           upload_url: ${{ steps.release_data.outputs.upload_url }}
-          asset_path: _build/out/AutoUpdater/Release/bin/AutoUpdater.exe
+          asset_path: _build/repack/Release/AutoUpdater.exe
           asset_name: AutoUpdater.exe
           asset_content_type: application/vnd.microsoft.portable-executable
 
diff --git a/AutoUpdate/CKAN-autoupdate.csproj b/AutoUpdate/CKAN-autoupdate.csproj
index 7ca711475a..55cf555e7c 100644
--- a/AutoUpdate/CKAN-autoupdate.csproj
+++ b/AutoUpdate/CKAN-autoupdate.csproj
@@ -1,45 +1,36 @@
-<?xml version="1.0" encoding="utf-8"?>
-<Project ToolsVersion="14.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+<Project>
   <PropertyGroup>
-    <AssemblyName>AutoUpdater</AssemblyName>
+    <AssemblyName>CKAN-AutoUpdateHelper</AssemblyName>
     <OutputPath>..\_build\out\$(AssemblyName)\$(Configuration)\bin\</OutputPath>
     <BaseIntermediateOutputPath>..\_build\out\$(AssemblyName)\$(Configuration)\obj\</BaseIntermediateOutputPath>
   </PropertyGroup>
-  <Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
+  <Import Project="Sdk.props" Sdk="Microsoft.NET.Sdk" />
   <PropertyGroup>
-    <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
-    <Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
     <ProjectGuid>{E5B1C768-349E-4DAF-A134-56E4ECF1EEEF}</ProjectGuid>
     <OutputType>Exe</OutputType>
-    <RootNamespace>AutoUpdater</RootNamespace>
+    <RootNamespace>CKAN.AutoUpdateHelper</RootNamespace>
+    <GenerateAssemblyInfo>false</GenerateAssemblyInfo>
+    <AutoGenerateBindingRedirects>true</AutoGenerateBindingRedirects>
+    <Deterministic>true</Deterministic>
+    <Configurations>Debug;Release</Configurations>
+    <Prefer32Bit>false</Prefer32Bit>
+    <LangVersion>7</LangVersion>
     <ApplicationIcon>..\assets\ckan.ico</ApplicationIcon>
+    <TargetFramework>net45</TargetFramework>
     <TargetFrameworkVersion>v4.5</TargetFrameworkVersion>
     <FileAlignment>512</FileAlignment>
-    <AutoGenerateBindingRedirects>true</AutoGenerateBindingRedirects>
-    <Deterministic>true</Deterministic>
     <ErrorReport>prompt</ErrorReport>
     <WarningLevel>4</WarningLevel>
-    <Prefer32Bit>false</Prefer32Bit>
-    <LangVersion>7</LangVersion>
-  </PropertyGroup>
-  <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
-    <PlatformTarget>AnyCPU</PlatformTarget>
-    <DebugSymbols>true</DebugSymbols>
-    <DebugType>full</DebugType>
-    <Optimize>false</Optimize>
-    <DefineConstants>DEBUG;TRACE</DefineConstants>
-  </PropertyGroup>
-  <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
-    <PlatformTarget>AnyCPU</PlatformTarget>
-    <DebugType>pdbonly</DebugType>
-    <Optimize>true</Optimize>
-    <DefineConstants>TRACE</DefineConstants>
   </PropertyGroup>
   <ItemGroup>
     <Reference Include="System" />
     <Reference Include="System.Core" />
     <Reference Include="System.Windows.Forms" />
   </ItemGroup>
+  <ItemGroup>
+    <PackageReference Include="ILRepack" Version="2.0.18" />
+    <PackageReference Include="ILRepack.MSBuild.Task" Version="2.0.13" />
+  </ItemGroup>
   <ItemGroup>
     <Compile Include="..\_build\meta\GlobalAssemblyVersionInfo.cs">
       <Link>Properties\GlobalAssemblyVersionInfo.cs</Link>
@@ -47,15 +38,31 @@
     <Compile Include="..\GlobalAssemblyInfo.cs">
       <Link>Properties\GlobalAssemblyInfo.cs</Link>
     </Compile>
-    <Compile Include="Main.cs" />
-    <Compile Include="Properties\AssemblyInfo.cs" />
   </ItemGroup>
   <ItemGroup>
     <None Include="App.config" />
   </ItemGroup>
-  <Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
+  <Import Project="Sdk.targets" Sdk="Microsoft.NET.Sdk" />
   <Target Name="BeforeBuild">
     <Exec Command="powershell ../build.ps1 Generate-GlobalAssemblyVersionInfo" Condition="!Exists('../_build/meta/GlobalAssemblyVersionInfo.cs') And '$(OS)' == 'Windows_NT'" />
     <Exec Command="sh ../build Generate-GlobalAssemblyVersionInfo" Condition="!Exists('../_build/meta/GlobalAssemblyVersionInfo.cs') And '$(OS)' == 'Unix'" />
   </Target>
-</Project>
\ No newline at end of file
+  <Target Name="ILRepack" AfterTargets="Build">
+    <ItemGroup>
+      <InputAssemblies Include="fr-FR\$(AssemblyName).resources.dll" />
+      <InputAssemblies Include="ru-RU\$(AssemblyName).resources.dll" />
+    </ItemGroup>
+    <ILRepack OutputAssembly="AutoUpdater.exe"
+              MainAssembly="$(AssemblyName).exe"
+              InputAssemblies="@(InputAssemblies)"
+              OutputType="$(OutputType)"
+              WorkingDirectory="$(OutputPath)"
+              Internalize="true"
+              Parallel="true" />
+    <ItemGroup>
+      <Repacked Include="$(OutputPath)AutoUpdater.*" />
+    </ItemGroup>
+    <Copy SourceFiles="@(Repacked)"
+          DestinationFolder="..\_build\repack\$(Configuration)" />
+  </Target>
+</Project>
diff --git a/AutoUpdate/Main.cs b/AutoUpdate/Main.cs
index 2349d0a5c7..bf9c59e2e2 100644
--- a/AutoUpdate/Main.cs
+++ b/AutoUpdate/Main.cs
@@ -16,7 +16,7 @@
  * AutoUpdate.exe <running CKAN PID> <running CKAN path> <updated CKAN path> <launch>
  */
 
-namespace AutoUpdater
+namespace CKAN.AutoUpdateHelper
 {
     public class Program
     {
@@ -26,7 +26,7 @@ public static int Main(string[] args)
 
             if (args.Length != 4)
             {
-                ReportError("Usage: AutoUpdater.exe pid oldPath newPath [no]launch");
+                ReportError("{0}: AutoUpdater.exe pid oldPath newPath [no]launch", Properties.Resources.Usage);
                 return ExitBADOPT;
             }
 
@@ -39,7 +39,7 @@ public static int Main(string[] args)
 
             if (!File.Exists(updated_path))
             {
-                ReportError($"Downloaded ckan.exe not found at: {updated_path}");
+                ReportError(Properties.Resources.DownloadNotFound, updated_path);
                 return ExitBADOPT;
             }
 
@@ -71,7 +71,7 @@ public static int Main(string[] args)
             }
             catch (Exception exc)
             {
-                ReportError($"Failed to wait for CKAN to close: {exc.Message}");
+                ReportError(Properties.Resources.FailedToWait, exc.Message);
                 return ExitERROR;
             }
 
@@ -86,7 +86,7 @@ public static int Main(string[] args)
                 {
                     if (retry == maxRetries - 1)
                     {
-                        ReportError($"Failed to delete {local_path}: {exc.Message}");
+                        ReportError(Properties.Resources.FailedToDelete, local_path, exc.Message);
                         if (fromGui)
                         {
                             // Launch the old EXE that we can't delete
@@ -172,20 +172,21 @@ private static bool IsOnWindows()
         /// <param name="e">Info about the exception</param>
         private static void UnhandledExceptionEventHandler(Object sender, UnhandledExceptionEventArgs e)
         {
-            ReportError($"Unhandled exception:\r\n{e.ExceptionObject}");
+            ReportError(Properties.Resources.UnhandledException, e.ExceptionObject);
         }
 
         /// <summary>
         /// It's nice to tell the user when something goes wrong!
         /// </summary>
         /// <param name="err">Description of the problem that happened</param>
-        private static void ReportError(string err)
+        private static void ReportError(string message, params object[] args)
         {
+            string err = string.Format(message, args);
             Console.Error.WriteLine(err);
             if (fromGui)
             {
                 // Show a popup in case the console isn't open
-                MessageBox.Show(err, "Fatal AutoUpdater Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
+                MessageBox.Show(err, Properties.Resources.FatalErrorTitle, MessageBoxButtons.OK, MessageBoxIcon.Error);
             }
         }
 
diff --git a/AutoUpdate/Properties/AssemblyInfo.cs b/AutoUpdate/Properties/AssemblyInfo.cs
index 750441911a..95d9d1fb74 100644
--- a/AutoUpdate/Properties/AssemblyInfo.cs
+++ b/AutoUpdate/Properties/AssemblyInfo.cs
@@ -1,4 +1,10 @@
-using System.Reflection;
+using System.Resources;
+using System.Reflection;
+using System.Runtime.CompilerServices;
 
-[assembly: AssemblyTitle("AutoUpdater")]
+[assembly: AssemblyTitle("CKAN-AutoUpdater")]
 [assembly: AssemblyDescription("CKAN Auto-update Executable")]
+[assembly: NeutralResourcesLanguage("en-GB")]
+
+[assembly: InternalsVisibleTo("Tests")]
+[assembly: InternalsVisibleTo("CKAN.Tests")]
diff --git a/AutoUpdate/Properties/Resources.Designer.cs b/AutoUpdate/Properties/Resources.Designer.cs
new file mode 100644
index 0000000000..10ca3ba154
--- /dev/null
+++ b/AutoUpdate/Properties/Resources.Designer.cs
@@ -0,0 +1,82 @@
+//------------------------------------------------------------------------------
+// <auto-generated>
+//     This code was generated by a tool.
+//     Runtime Version:4.0.30319.42000
+//
+//     Changes to this file may cause incorrect behavior and will be lost if
+//     the code is regenerated. (I WISH!)
+// </auto-generated>
+//------------------------------------------------------------------------------
+
+namespace CKAN.AutoUpdateHelper.Properties {
+    using System;
+
+
+    /// <summary>
+    ///   A strongly-typed resource class, for looking up localized strings, etc.
+    /// </summary>
+    // This class was auto-generated by the StronglyTypedResourceBuilder
+    // class via a tool like ResGen or Visual Studio.
+    // To add or remove a member, edit your .ResX file then rerun ResGen
+    // with the /str option, or rebuild your VS project.
+    [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "4.0.0.0")]
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
+    [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
+    internal class Resources {
+
+        private static global::System.Resources.ResourceManager resourceMan;
+
+        private static global::System.Globalization.CultureInfo resourceCulture;
+
+        [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
+        internal Resources() { }
+
+        /// <summary>
+        ///   Returns the cached ResourceManager instance used by this class.
+        /// </summary>
+        [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
+        internal static global::System.Resources.ResourceManager ResourceManager {
+            get {
+                if (object.ReferenceEquals(resourceMan, null))
+                {
+                    resourceMan = new SingleAssemblyResourceManager("CKAN.AutoUpdateHelper.Properties.Resources", typeof(Resources).Assembly);
+                }
+                return resourceMan;
+            }
+        }
+
+        /// <summary>
+        ///   Overrides the current thread's CurrentUICulture property for all
+        ///   resource lookups using this strongly typed resource class.
+        /// </summary>
+        [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
+        internal static global::System.Globalization.CultureInfo Culture {
+            get {
+                return resourceCulture;
+            }
+            set {
+                resourceCulture = value;
+            }
+        }
+
+        internal static string Usage {
+            get { return (string)(ResourceManager.GetObject("Usage", resourceCulture)); }
+        }
+        internal static string DownloadNotFound {
+            get { return (string)(ResourceManager.GetObject("DownloadNotFound", resourceCulture)); }
+        }
+        internal static string FailedToWait {
+            get { return (string)(ResourceManager.GetObject("FailedToWait", resourceCulture)); }
+        }
+        internal static string FailedToDelete {
+            get { return (string)(ResourceManager.GetObject("FailedToDelete", resourceCulture)); }
+        }
+        internal static string UnhandledException {
+            get { return (string)(ResourceManager.GetObject("UnhandledException", resourceCulture)); }
+        }
+        internal static string FatalErrorTitle {
+            get { return (string)(ResourceManager.GetObject("FatalErrorTitle", resourceCulture)); }
+        }
+
+    }
+}
diff --git a/AutoUpdate/Properties/Resources.fr-FR.resx b/AutoUpdate/Properties/Resources.fr-FR.resx
new file mode 100644
index 0000000000..c446321325
--- /dev/null
+++ b/AutoUpdate/Properties/Resources.fr-FR.resx
@@ -0,0 +1,127 @@
+<?xml version="1.0" encoding="utf-8"?>
+<root>
+  <!--
+    Microsoft ResX Schema
+
+    Version 2.0
+
+    The primary goals of this format is to allow a simple XML format
+    that is mostly human readable. The generation and parsing of the
+    various data types are done through the TypeConverter classes
+    associated with the data types.
+
+    Example:
+
+    ... ado.net/XML headers & schema ...
+    <resheader name="resmimetype">text/microsoft-resx</resheader>
+    <resheader name="version">2.0</resheader>
+    <resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
+    <resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
+    <data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
+    <data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
+    <data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
+        <value>[base64 mime encoded serialized .NET Framework object]</value>
+    </data>
+    <data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
+        <value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
+        <comment>This is a comment</comment>
+    </data>
+
+    There are any number of "resheader" rows that contain simple
+    name/value pairs.
+
+    Each data row contains a name, and value. The row also contains a
+    type or mimetype. Type corresponds to a .NET class that support
+    text/value conversion through the TypeConverter architecture.
+    Classes that don't support this are serialized and stored with the
+    mimetype set.
+
+    The mimetype is used for serialized objects, and tells the
+    ResXResourceReader how to depersist the object. This is currently not
+    extensible. For a given mimetype the value must be set accordingly:
+
+    Note - application/x-microsoft.net.object.binary.base64 is the format
+    that the ResXResourceWriter will generate, however the reader can
+    read any of the formats listed below.
+
+    mimetype: application/x-microsoft.net.object.binary.base64
+    value   : The object must be serialized with
+            : System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
+            : and then encoded with base64 encoding.
+
+    mimetype: application/x-microsoft.net.object.soap.base64
+    value   : The object must be serialized with
+            : System.Runtime.Serialization.Formatters.Soap.SoapFormatter
+            : and then encoded with base64 encoding.
+
+    mimetype: application/x-microsoft.net.object.bytearray.base64
+    value   : The object must be serialized into a byte array
+            : using a System.ComponentModel.TypeConverter
+            : and then encoded with base64 encoding.
+    -->
+  <xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
+    <xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
+    <xsd:element name="root" msdata:IsDataSet="true">
+      <xsd:complexType>
+        <xsd:choice maxOccurs="unbounded">
+          <xsd:element name="metadata">
+            <xsd:complexType>
+              <xsd:sequence>
+                <xsd:element name="value" type="xsd:string" minOccurs="0" />
+              </xsd:sequence>
+              <xsd:attribute name="name" use="required" type="xsd:string" />
+              <xsd:attribute name="type" type="xsd:string" />
+              <xsd:attribute name="mimetype" type="xsd:string" />
+              <xsd:attribute ref="xml:space" />
+            </xsd:complexType>
+          </xsd:element>
+          <xsd:element name="assembly">
+            <xsd:complexType>
+              <xsd:attribute name="alias" type="xsd:string" />
+              <xsd:attribute name="name" type="xsd:string" />
+            </xsd:complexType>
+          </xsd:element>
+          <xsd:element name="data">
+            <xsd:complexType>
+              <xsd:sequence>
+                <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
+                <xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
+              </xsd:sequence>
+              <xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
+              <xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
+              <xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
+              <xsd:attribute ref="xml:space" />
+            </xsd:complexType>
+          </xsd:element>
+          <xsd:element name="resheader">
+            <xsd:complexType>
+              <xsd:sequence>
+                <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
+              </xsd:sequence>
+              <xsd:attribute name="name" type="xsd:string" use="required" />
+            </xsd:complexType>
+          </xsd:element>
+        </xsd:choice>
+      </xsd:complexType>
+    </xsd:element>
+  </xsd:schema>
+  <resheader name="resmimetype">
+    <value>text/microsoft-resx</value>
+  </resheader>
+  <resheader name="version">
+    <value>2.0</value>
+  </resheader>
+  <resheader name="reader">
+    <value>System.Resources.ResXResourceReader, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
+  </resheader>
+  <resheader name="writer">
+    <value>System.Resources.ResXResourceWriter, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
+  </resheader>
+  <data name="Usage" xml:space="preserve"><value>Utilisation</value></data>
+  <data name="DownloadNotFound" xml:space="preserve"><value>Le fichier téléchargé ckan.exe n'a pas été trouvé à : {0}</value></data>
+  <data name="FailedToWait" xml:space="preserve"><value>Impossible d'attendre que CKAN se ferme : {0}</value></data>
+  <data name="FailedToDelete" xml:space="preserve"><value>Échec de la suppression de {0} : {1}</value></data>
+  <data name="UnhandledException" xml:space="preserve"><value>Exception non-gérée :
+{0}</value></data>
+  <data name="FatalErrorTitle" xml:space="preserve"><value>Erreur fatale de la mise à jour automatique</value></data>
+</root>
diff --git a/AutoUpdate/Properties/Resources.resx b/AutoUpdate/Properties/Resources.resx
new file mode 100644
index 0000000000..26edfc58f9
--- /dev/null
+++ b/AutoUpdate/Properties/Resources.resx
@@ -0,0 +1,127 @@
+<?xml version="1.0" encoding="utf-8"?>
+<root>
+  <!--
+    Microsoft ResX Schema
+
+    Version 2.0
+
+    The primary goals of this format is to allow a simple XML format
+    that is mostly human readable. The generation and parsing of the
+    various data types are done through the TypeConverter classes
+    associated with the data types.
+
+    Example:
+
+    ... ado.net/XML headers & schema ...
+    <resheader name="resmimetype">text/microsoft-resx</resheader>
+    <resheader name="version">2.0</resheader>
+    <resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
+    <resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
+    <data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
+    <data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
+    <data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
+        <value>[base64 mime encoded serialized .NET Framework object]</value>
+    </data>
+    <data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
+        <value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
+        <comment>This is a comment</comment>
+    </data>
+
+    There are any number of "resheader" rows that contain simple
+    name/value pairs.
+
+    Each data row contains a name, and value. The row also contains a
+    type or mimetype. Type corresponds to a .NET class that support
+    text/value conversion through the TypeConverter architecture.
+    Classes that don't support this are serialized and stored with the
+    mimetype set.
+
+    The mimetype is used for serialized objects, and tells the
+    ResXResourceReader how to depersist the object. This is currently not
+    extensible. For a given mimetype the value must be set accordingly:
+
+    Note - application/x-microsoft.net.object.binary.base64 is the format
+    that the ResXResourceWriter will generate, however the reader can
+    read any of the formats listed below.
+
+    mimetype: application/x-microsoft.net.object.binary.base64
+    value   : The object must be serialized with
+            : System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
+            : and then encoded with base64 encoding.
+
+    mimetype: application/x-microsoft.net.object.soap.base64
+    value   : The object must be serialized with
+            : System.Runtime.Serialization.Formatters.Soap.SoapFormatter
+            : and then encoded with base64 encoding.
+
+    mimetype: application/x-microsoft.net.object.bytearray.base64
+    value   : The object must be serialized into a byte array
+            : using a System.ComponentModel.TypeConverter
+            : and then encoded with base64 encoding.
+    -->
+  <xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
+    <xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
+    <xsd:element name="root" msdata:IsDataSet="true">
+      <xsd:complexType>
+        <xsd:choice maxOccurs="unbounded">
+          <xsd:element name="metadata">
+            <xsd:complexType>
+              <xsd:sequence>
+                <xsd:element name="value" type="xsd:string" minOccurs="0" />
+              </xsd:sequence>
+              <xsd:attribute name="name" use="required" type="xsd:string" />
+              <xsd:attribute name="type" type="xsd:string" />
+              <xsd:attribute name="mimetype" type="xsd:string" />
+              <xsd:attribute ref="xml:space" />
+            </xsd:complexType>
+          </xsd:element>
+          <xsd:element name="assembly">
+            <xsd:complexType>
+              <xsd:attribute name="alias" type="xsd:string" />
+              <xsd:attribute name="name" type="xsd:string" />
+            </xsd:complexType>
+          </xsd:element>
+          <xsd:element name="data">
+            <xsd:complexType>
+              <xsd:sequence>
+                <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
+                <xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
+              </xsd:sequence>
+              <xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
+              <xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
+              <xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
+              <xsd:attribute ref="xml:space" />
+            </xsd:complexType>
+          </xsd:element>
+          <xsd:element name="resheader">
+            <xsd:complexType>
+              <xsd:sequence>
+                <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
+              </xsd:sequence>
+              <xsd:attribute name="name" type="xsd:string" use="required" />
+            </xsd:complexType>
+          </xsd:element>
+        </xsd:choice>
+      </xsd:complexType>
+    </xsd:element>
+  </xsd:schema>
+  <resheader name="resmimetype">
+    <value>text/microsoft-resx</value>
+  </resheader>
+  <resheader name="version">
+    <value>2.0</value>
+  </resheader>
+  <resheader name="reader">
+    <value>System.Resources.ResXResourceReader, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
+  </resheader>
+  <resheader name="writer">
+    <value>System.Resources.ResXResourceWriter, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
+  </resheader>
+  <data name="Usage" xml:space="preserve"><value>Usage</value></data>
+  <data name="DownloadNotFound" xml:space="preserve"><value>Downloaded ckan.exe not found at: {0}</value></data>
+  <data name="FailedToWait" xml:space="preserve"><value>Failed to wait for CKAN to close: {0}</value></data>
+  <data name="FailedToDelete" xml:space="preserve"><value>Failed to delete {0}: {1}</value></data>
+  <data name="UnhandledException" xml:space="preserve"><value>Unhandled exception:
+{0}</value></data>
+  <data name="FatalErrorTitle" xml:space="preserve"><value>Fatal AutoUpdater Error</value></data>
+</root>
diff --git a/AutoUpdate/Properties/Resources.ru-RU.resx b/AutoUpdate/Properties/Resources.ru-RU.resx
new file mode 100644
index 0000000000..e756420925
--- /dev/null
+++ b/AutoUpdate/Properties/Resources.ru-RU.resx
@@ -0,0 +1,127 @@
+<?xml version="1.0" encoding="utf-8"?>
+<root>
+  <!--
+    Microsoft ResX Schema
+
+    Version 2.0
+
+    The primary goals of this format is to allow a simple XML format
+    that is mostly human readable. The generation and parsing of the
+    various data types are done through the TypeConverter classes
+    associated with the data types.
+
+    Example:
+
+    ... ado.net/XML headers & schema ...
+    <resheader name="resmimetype">text/microsoft-resx</resheader>
+    <resheader name="version">2.0</resheader>
+    <resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
+    <resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
+    <data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
+    <data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
+    <data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
+        <value>[base64 mime encoded serialized .NET Framework object]</value>
+    </data>
+    <data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
+        <value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
+        <comment>This is a comment</comment>
+    </data>
+
+    There are any number of "resheader" rows that contain simple
+    name/value pairs.
+
+    Each data row contains a name, and value. The row also contains a
+    type or mimetype. Type corresponds to a .NET class that support
+    text/value conversion through the TypeConverter architecture.
+    Classes that don't support this are serialized and stored with the
+    mimetype set.
+
+    The mimetype is used for serialized objects, and tells the
+    ResXResourceReader how to depersist the object. This is currently not
+    extensible. For a given mimetype the value must be set accordingly:
+
+    Note - application/x-microsoft.net.object.binary.base64 is the format
+    that the ResXResourceWriter will generate, however the reader can
+    read any of the formats listed below.
+
+    mimetype: application/x-microsoft.net.object.binary.base64
+    value   : The object must be serialized with
+            : System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
+            : and then encoded with base64 encoding.
+
+    mimetype: application/x-microsoft.net.object.soap.base64
+    value   : The object must be serialized with
+            : System.Runtime.Serialization.Formatters.Soap.SoapFormatter
+            : and then encoded with base64 encoding.
+
+    mimetype: application/x-microsoft.net.object.bytearray.base64
+    value   : The object must be serialized into a byte array
+            : using a System.ComponentModel.TypeConverter
+            : and then encoded with base64 encoding.
+    -->
+  <xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
+    <xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
+    <xsd:element name="root" msdata:IsDataSet="true">
+      <xsd:complexType>
+        <xsd:choice maxOccurs="unbounded">
+          <xsd:element name="metadata">
+            <xsd:complexType>
+              <xsd:sequence>
+                <xsd:element name="value" type="xsd:string" minOccurs="0" />
+              </xsd:sequence>
+              <xsd:attribute name="name" use="required" type="xsd:string" />
+              <xsd:attribute name="type" type="xsd:string" />
+              <xsd:attribute name="mimetype" type="xsd:string" />
+              <xsd:attribute ref="xml:space" />
+            </xsd:complexType>
+          </xsd:element>
+          <xsd:element name="assembly">
+            <xsd:complexType>
+              <xsd:attribute name="alias" type="xsd:string" />
+              <xsd:attribute name="name" type="xsd:string" />
+            </xsd:complexType>
+          </xsd:element>
+          <xsd:element name="data">
+            <xsd:complexType>
+              <xsd:sequence>
+                <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
+                <xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
+              </xsd:sequence>
+              <xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
+              <xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
+              <xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
+              <xsd:attribute ref="xml:space" />
+            </xsd:complexType>
+          </xsd:element>
+          <xsd:element name="resheader">
+            <xsd:complexType>
+              <xsd:sequence>
+                <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
+              </xsd:sequence>
+              <xsd:attribute name="name" type="xsd:string" use="required" />
+            </xsd:complexType>
+          </xsd:element>
+        </xsd:choice>
+      </xsd:complexType>
+    </xsd:element>
+  </xsd:schema>
+  <resheader name="resmimetype">
+    <value>text/microsoft-resx</value>
+  </resheader>
+  <resheader name="version">
+    <value>2.0</value>
+  </resheader>
+  <resheader name="reader">
+    <value>System.Resources.ResXResourceReader, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
+  </resheader>
+  <resheader name="writer">
+    <value>System.Resources.ResXResourceWriter, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
+  </resheader>
+  <data name="Usage" xml:space="preserve"><value>Использование</value></data>
+  <data name="DownloadNotFound" xml:space="preserve"><value>Загруженный ckan.exe не найден по пути: {0}</value></data>
+  <data name="FailedToWait" xml:space="preserve"><value>Не удалось дождаться закрытия CKAN: {0}</value></data>
+  <data name="FailedToDelete" xml:space="preserve"><value>Не удалось удалить {0}: {1}</value></data>
+  <data name="UnhandledException" xml:space="preserve"><value>Необработанное исключение:
+{0}</value></data>
+  <data name="FatalErrorTitle" xml:space="preserve"><value>Критическая ошибка AutoUpdater</value></data>
+</root>
diff --git a/AutoUpdate/SingleAssemblyResourceManager.cs b/AutoUpdate/SingleAssemblyResourceManager.cs
new file mode 100644
index 0000000000..b7d6321317
--- /dev/null
+++ b/AutoUpdate/SingleAssemblyResourceManager.cs
@@ -0,0 +1,58 @@
+using System.IO;
+using System.Globalization;
+using System.Resources;
+using System.Collections;
+using System.Reflection;
+using System.Collections.Generic;
+
+namespace CKAN.AutoUpdateHelper
+{
+    // Thanks and credit to this guy: https://stackoverflow.com/q/1952638/2422988
+
+    class SingleAssemblyResourceManager : ResourceManager
+    {
+        public SingleAssemblyResourceManager(string basename, Assembly assembly) : base(basename, assembly)
+        {
+        }
+
+        protected override ResourceSet InternalGetResourceSet(CultureInfo culture,
+            bool createIfNotExists, bool tryParents)
+        {
+            ResourceSet rs;
+            if (!myResourceSets.TryGetValue(culture, out rs))
+            {
+                // Lazy-load default language (without caring about duplicate assignment in race conditions, no harm done)
+                if (neutralResourcesCulture == null)
+                {
+                    neutralResourcesCulture = GetNeutralResourcesLanguage(this.MainAssembly);
+                }
+
+                // If we're asking for the default language, then ask for the
+                // invariant (non-specific) resources.
+                if (neutralResourcesCulture.Equals(culture))
+                {
+                    culture = CultureInfo.InvariantCulture;
+                }
+                string resourceFileName = GetResourceFileName(culture);
+
+                Stream store = this.MainAssembly.GetManifestResourceStream(resourceFileName);
+
+                // If we found the appropriate resources in the local assembly
+                if (store != null)
+                {
+                    rs = new ResourceSet(store);
+                    // Save for later
+                    myResourceSets.Add(culture, rs);
+                }
+                else
+                {
+                    rs = base.InternalGetResourceSet(culture, createIfNotExists, tryParents);
+                }
+            }
+            return rs;
+        }
+
+        private CultureInfo neutralResourcesCulture;
+        private Dictionary<CultureInfo, ResourceSet> myResourceSets = new Dictionary<CultureInfo, ResourceSet>();
+    }
+}
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 2fc8a0c34c..bf7ad6b224 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -11,6 +11,7 @@ All notable changes to this project will be documented in this file.
 - [GUI] Context sensitive help (#3563 by: HebaruSan; reviewed: techman83)
 - [Multiple] Add install size to metadata and display in clients (#3568 by: HebaruSan; reviewed: techman83)
 - [CLI] Create a system menu entry for command prompt (#3622 by: HebaruSan; reviewed: techman83)
+- [Multiple] Internationalize Core, CmdLine, ConsoleUI, and AutoUpdater (#3482 by: HebaruSan; reviewed: techman83)
 
 ## Bugfixes
 
diff --git a/Cmdline/Action/AuthToken.cs b/Cmdline/Action/AuthToken.cs
index d5bd9ff60c..71222c5497 100644
--- a/Cmdline/Action/AuthToken.cs
+++ b/Cmdline/Action/AuthToken.cs
@@ -66,6 +66,8 @@ public int RunSubCommand(GameInstanceManager manager, CommonOptions opts, SubCom
 
         private int listAuthTokens(CommonOptions opts)
         {
+            string hostHeader  = Properties.Resources.AuthTokenHostHeader;
+            string tokenHeader = Properties.Resources.AuthTokenTokenHeader;
             List<string> hosts  = new List<string>(ServiceLocator.Container.Resolve<IConfiguration>().GetAuthTokenHosts());
             if (hosts.Count > 0)
             {
@@ -119,11 +121,8 @@ private int removeAuthToken(RemoveAuthTokenOptions opts)
             return Exit.OK;
         }
 
-        private const string hostHeader  = "Host";
-        private const string tokenHeader = "Token";
-
-        private IUser      user;
-        private static readonly ILog log = LogManager.GetLogger(typeof(AuthToken));
+        private                 IUser user;
+        private static readonly ILog  log = LogManager.GetLogger(typeof(AuthToken));
     }
 
     internal class AuthTokenSubOptions : VerbCommandOptions
@@ -145,8 +144,8 @@ public string GetUsage(string verb)
             ht.AddPreOptionsLine(" ");
             if (string.IsNullOrEmpty(verb))
             {
-                ht.AddPreOptionsLine("ckan authtoken - Manage authentication tokens");
-                ht.AddPreOptionsLine($"Usage: ckan authtoken <command> [options]");
+                ht.AddPreOptionsLine($"ckan authtoken - {Properties.Resources.AuthTokenHelpSummary}");
+                ht.AddPreOptionsLine($"{Properties.Resources.Usage}: ckan authtoken <{Properties.Resources.Command}> [{Properties.Resources.Options}]");
             }
             else
             {
@@ -154,13 +153,13 @@ public string GetUsage(string verb)
                 switch (verb)
                 {
                     case "add":
-                        ht.AddPreOptionsLine($"Usage: ckan authtoken {verb} [options] host token");
+                        ht.AddPreOptionsLine($"{Properties.Resources.Usage}: ckan authtoken {verb} [{Properties.Resources.Options}] host token");
                         break;
                     case "remove":
-                        ht.AddPreOptionsLine($"Usage: ckan authtoken {verb} [options] host");
+                        ht.AddPreOptionsLine($"{Properties.Resources.Usage}: ckan authtoken {verb} [{Properties.Resources.Options}] host");
                         break;
                     case "list":
-                        ht.AddPreOptionsLine($"Usage: ckan authtoken {verb} [options]");
+                        ht.AddPreOptionsLine($"{Properties.Resources.Usage}: ckan authtoken {verb} [{Properties.Resources.Options}]");
                         break;
                 }
             }
diff --git a/Cmdline/Action/Available.cs b/Cmdline/Action/Available.cs
index ae445fe6d9..4b8becd8ff 100644
--- a/Cmdline/Action/Available.cs
+++ b/Cmdline/Action/Available.cs
@@ -12,16 +12,17 @@ public Available(IUser user)
             this.user = user;
         }
 
-        public int RunCommand(CKAN.GameInstance ksp, object raw_options)
+        public int RunCommand(CKAN.GameInstance instance, object raw_options)
         {
             AvailableOptions opts       = (AvailableOptions)raw_options;
-            IRegistryQuerier registry   = RegistryManager.Instance(ksp).registry;
+            IRegistryQuerier registry   = RegistryManager.Instance(instance).registry;
             
             var compatible = registry
-                .CompatibleModules(ksp.VersionCriteria())
+                .CompatibleModules(instance.VersionCriteria())
                 .Where(m => !m.IsDLC);
 
-            user.RaiseMessage("Modules compatible with KSP {0}", ksp.Version());
+            user.RaiseMessage(string.Format(Properties.Resources.AvailableHeader,
+                instance.game.ShortName, instance.Version()));
             user.RaiseMessage("");
 
             if (opts.detail)
diff --git a/Cmdline/Action/Cache.cs b/Cmdline/Action/Cache.cs
index 36c43f8de9..a61f3e6bad 100644
--- a/Cmdline/Action/Cache.cs
+++ b/Cmdline/Action/Cache.cs
@@ -34,8 +34,8 @@ public string GetUsage(string verb)
             ht.AddPreOptionsLine(" ");
             if (string.IsNullOrEmpty(verb))
             {
-                ht.AddPreOptionsLine("ckan cache - Manage the download cache path of CKAN");
-                ht.AddPreOptionsLine($"Usage: ckan cache <command> [options]");
+                ht.AddPreOptionsLine($"ckan cache - {Properties.Resources.CacheHelpSummary}");
+                ht.AddPreOptionsLine($"{Properties.Resources.Usage}: ckan cache <{Properties.Resources.Command}> [{Properties.Resources.Options}]");
             }
             else
             {
@@ -44,10 +44,10 @@ public string GetUsage(string verb)
                 {
                     // First the commands with one string argument
                     case "set":
-                        ht.AddPreOptionsLine($"Usage: ckan cache {verb} [options] path");
+                        ht.AddPreOptionsLine($"{Properties.Resources.Usage}: ckan cache {verb} [{Properties.Resources.Options}] path");
                         break;
                     case "setlimit":
-                        ht.AddPreOptionsLine($"Usage: ckan cache {verb} [options] megabytes");
+                        ht.AddPreOptionsLine($"{Properties.Resources.Usage}: ckan cache {verb} [{Properties.Resources.Options}] megabytes");
                         break;
 
                     // Now the commands with only --flag type options
@@ -56,7 +56,7 @@ public string GetUsage(string verb)
                     case "reset":
                     case "showlimit":
                     default:
-                        ht.AddPreOptionsLine($"Usage: ckan cache {verb} [options]");
+                        ht.AddPreOptionsLine($"{Properties.Resources.Usage}: ckan cache {verb} [{Properties.Resources.Options}]");
                         break;
                 }
             }
@@ -135,7 +135,7 @@ public int RunSubCommand(GameInstanceManager mgr, CommonOptions opts, SubCommand
                             break;
 
                         default:
-                            user.RaiseMessage("Unknown command: cache {0}", option);
+                            user.RaiseMessage("{0}: cache {1}", Properties.Resources.UnknownCommand, option);
                             exitCode = Exit.BADOPT;
                             break;
                     }
@@ -156,7 +156,7 @@ private int SetCacheDirectory(SetOptions options)
         {
             if (string.IsNullOrEmpty(options.Path))
             {
-                user.RaiseError("set <path> - argument missing, perhaps you forgot it?");
+                user.RaiseError("set <{0}> - {1}", Properties.Resources.Path, Properties.Resources.ArgumentMissing);
                 return Exit.BADOPT;
             }
 
@@ -164,13 +164,13 @@ private int SetCacheDirectory(SetOptions options)
             if (manager.TrySetupCache(options.Path, out failReason))
             {
                 IConfiguration cfg = ServiceLocator.Container.Resolve<IConfiguration>();
-                user.RaiseMessage($"Download cache set to {cfg.DownloadCacheDir}");
+                user.RaiseMessage(Properties.Resources.CacheSet, cfg.DownloadCacheDir);
                 printCacheInfo();
                 return Exit.OK;
             }
             else
             {
-                user.RaiseError($"Invalid path: {failReason}");
+                user.RaiseError(Properties.Resources.CacheInvalidPath, failReason);
                 return Exit.BADOPT;
             }
         }
@@ -178,7 +178,7 @@ private int SetCacheDirectory(SetOptions options)
         private int ClearCacheDirectory(CommonOptions options)
         {
             manager.Cache.RemoveAll();
-            user.RaiseMessage("Download cache cleared.");
+            user.RaiseMessage(Properties.Resources.CacheCleared);
             printCacheInfo();
             return Exit.OK;
         }
@@ -189,12 +189,12 @@ private int ResetCacheDirectory(CommonOptions options)
             if (manager.TrySetupCache("", out failReason))
             {
                 IConfiguration cfg = ServiceLocator.Container.Resolve<IConfiguration>();
-                user.RaiseMessage($"Download cache reset to {cfg.DownloadCacheDir}");
+                user.RaiseMessage(Properties.Resources.CacheReset, cfg.DownloadCacheDir);
                 printCacheInfo();
             }
             else
             {
-                user.RaiseError($"Can't reset cache path: {failReason}");
+                user.RaiseError(Properties.Resources.CacheResetFailed, failReason);
             }
             return Exit.OK;
         }
@@ -208,7 +208,7 @@ private int ShowCacheSizeLimit(CommonOptions options)
             }
             else
             {
-                user.RaiseMessage("Unlimited");
+                user.RaiseMessage(Properties.Resources.CacheUnlimited);
             }
             return Exit.OK;
         }
@@ -232,11 +232,11 @@ private void printCacheInfo()
             int fileCount;
             long bytes;
             manager.Cache.GetSizeInfo(out fileCount, out bytes);
-            user.RaiseMessage($"{fileCount} files, {CkanModule.FmtSize(bytes)}");
+            user.RaiseMessage(Properties.Resources.CacheInfo, fileCount, CkanModule.FmtSize(bytes));
         }
 
+        private IUser               user;
         private GameInstanceManager manager;
-        private IUser      user;
 
         private static readonly ILog log = LogManager.GetLogger(typeof(Cache));
     }
diff --git a/Cmdline/Action/Compare.cs b/Cmdline/Action/Compare.cs
index 328358a8a7..46b7182bf9 100644
--- a/Cmdline/Action/Compare.cs
+++ b/Cmdline/Action/Compare.cs
@@ -18,7 +18,7 @@ public int RunCommand(object rawOptions)
 
             if (options.Left != null && options.Right != null)
             {
-                var leftVersion = new ModuleVersion(options.Left);
+                var leftVersion  = new ModuleVersion(options.Left);
                 var rightVersion = new ModuleVersion(options.Right);
 
                 int compareResult = leftVersion.CompareTo(rightVersion);
@@ -29,29 +29,24 @@ public int RunCommand(object rawOptions)
                 }
                 else if (compareResult == 0)
                 {
-                    user.RaiseMessage(
-                        "\"{0}\" and \"{1}\" are the same versions.", leftVersion, rightVersion);
+                    user.RaiseMessage(Properties.Resources.CompareSame, leftVersion, rightVersion);
                 }
                 else if (compareResult < 0)
                 {
-                    user.RaiseMessage(
-                        "\"{0}\" is lower than \"{1}\".", leftVersion, rightVersion);
+                    user.RaiseMessage(Properties.Resources.CompareLower, leftVersion, rightVersion);
                 }
                 else if (compareResult > 0)
                 {
-                    user.RaiseMessage(
-                        "\"{0}\" is higher than \"{1}\".", leftVersion, rightVersion);
+                    user.RaiseMessage(Properties.Resources.CompareHigher, leftVersion, rightVersion);
                 }
                 else
                 {
-                    user.RaiseMessage(
-                        "Usage: ckan compare version1 version2");
+                    user.RaiseMessage("{0}: ckan compare version1 version2", Properties.Resources.Usage);
                 }
             }
             else
             {
-                user.RaiseMessage(
-                    "Usage: ckan compare version1 version2");
+                user.RaiseMessage("{0}: ckan compare version1 version2", Properties.Resources.Usage);
                 return Exit.BADOPT;
             }
 
diff --git a/Cmdline/Action/Compat.cs b/Cmdline/Action/Compat.cs
index d86d39d61a..0818730f67 100644
--- a/Cmdline/Action/Compat.cs
+++ b/Cmdline/Action/Compat.cs
@@ -24,8 +24,8 @@ public string GetUsage(string verb)
             ht.AddPreOptionsLine(" ");
             if (string.IsNullOrEmpty(verb))
             {
-                ht.AddPreOptionsLine("ckan compat - Manage KSP version compatibility");
-                ht.AddPreOptionsLine($"Usage: ckan compat <command> [options]");
+                ht.AddPreOptionsLine($"ckan compat - {Properties.Resources.CompatHelpSummary}");
+                ht.AddPreOptionsLine($"{Properties.Resources.Usage}: ckan compat <{Properties.Resources.Command}> [{Properties.Resources.Options}]");
             }
             else
             {
@@ -35,13 +35,13 @@ public string GetUsage(string verb)
                     // First the commands with one string argument
                     case "add":
                     case "forget":
-                        ht.AddPreOptionsLine($"Usage: ckan compat {verb} [options] version");
+                        ht.AddPreOptionsLine($"{Properties.Resources.Usage}: ckan compat {verb} [{Properties.Resources.Options}] version");
                         break;
 
                     // Now the commands with only --flag type options
                     case "list":
                     default:
-                        ht.AddPreOptionsLine($"Usage: ckan compat {verb} [options]");
+                        ht.AddPreOptionsLine($"{Properties.Resources.Usage}: ckan compat {verb} [{Properties.Resources.Options}]");
                         break;
                 }
             }
@@ -86,12 +86,12 @@ public int RunSubCommand(GameInstanceManager manager, CommonOptions opts, SubCom
                     {
                         case "list":
                             {
-                                var ksp = MainClass.GetGameInstance(_kspManager);
+                                var instance = MainClass.GetGameInstance(_kspManager);
 
-                                const string versionHeader = "Version";
-                                const string actualHeader  = "Actual";
+                                string versionHeader = Properties.Resources.CompatVersionHeader;
+                                string actualHeader  = Properties.Resources.CompatActualHeader;
 
-                                var output = ksp
+                                var output = instance
                                     .GetCompatibleVersions()
                                     .Select(i => new
                                     {
@@ -102,7 +102,7 @@ public int RunSubCommand(GameInstanceManager manager, CommonOptions opts, SubCom
 
                                 output.Add(new
                                 {
-                                    Version = ksp.Version(),
+                                    Version = instance.Version(),
                                     Actual = true
                                 });
 
@@ -135,29 +135,29 @@ public int RunSubCommand(GameInstanceManager manager, CommonOptions opts, SubCom
 
                                 foreach (var line in output)
                                 {
-                                    _user.RaiseMessage(string.Format(columnFormat,
+                                    _user.RaiseMessage(columnFormat,
                                        line.Version.ToString().PadRight(versionWidth),
                                        line.Actual.ToString().PadRight(actualWidth)
-                                   ));
+                                    );
                                 }
                             }
                             break;
 
                         case "add":
                             {
-                                var ksp = MainClass.GetGameInstance(_kspManager);
+                                var instance = MainClass.GetGameInstance(_kspManager);
                                 var addOptions = (CompatAddOptions)suboptions;
 
                                 GameVersion GameVersion;
                                 if (GameVersion.TryParse(addOptions.Version, out GameVersion))
                                 {
-                                    var newCompatibleVersion = ksp.GetCompatibleVersions();
+                                    var newCompatibleVersion = instance.GetCompatibleVersions();
                                     newCompatibleVersion.Add(GameVersion);
-                                    ksp.SetCompatibleVersions(newCompatibleVersion);
+                                    instance.SetCompatibleVersions(newCompatibleVersion);
                                 }
                                 else
                                 {
-                                    _user.RaiseError("ERROR: Invalid KSP version.");
+                                    _user.RaiseError(Properties.Resources.CompatInvalid);
                                     exitCode = Exit.ERROR;
                                 }
                             }
@@ -165,27 +165,27 @@ public int RunSubCommand(GameInstanceManager manager, CommonOptions opts, SubCom
 
                         case "forget":
                             {
-                                var ksp = MainClass.GetGameInstance(_kspManager);
+                                var instance = MainClass.GetGameInstance(_kspManager);
                                 var addOptions = (CompatForgetOptions)suboptions;
 
                                 GameVersion GameVersion;
                                 if (GameVersion.TryParse(addOptions.Version, out GameVersion))
                                 {
-                                    if (GameVersion != ksp.Version())
+                                    if (GameVersion != instance.Version())
                                     {
-                                        var newCompatibleVersion = ksp.GetCompatibleVersions();
+                                        var newCompatibleVersion = instance.GetCompatibleVersions();
                                         newCompatibleVersion.RemoveAll(i => i == GameVersion);
-                                        ksp.SetCompatibleVersions(newCompatibleVersion);
+                                        instance.SetCompatibleVersions(newCompatibleVersion);
                                     }
                                     else
                                     {
-                                        _user.RaiseError("ERROR: Cannot forget actual KSP version.");
+                                        _user.RaiseError(Properties.Resources.CompatCantForget);
                                         exitCode = Exit.ERROR;
                                     }
                                 }
                                 else
                                 {
-                                    _user.RaiseError("ERROR: Invalid KSP version.");
+                                    _user.RaiseError(Properties.Resources.CompatInvalid);
                                     exitCode = Exit.ERROR;
                                 }
                             }
@@ -200,7 +200,7 @@ public int RunSubCommand(GameInstanceManager manager, CommonOptions opts, SubCom
             return exitCode;
         }
 
+        private IUser               _user;
         private GameInstanceManager _kspManager;
-        private IUser      _user;
     }
 }
diff --git a/Cmdline/Action/Filter.cs b/Cmdline/Action/Filter.cs
index 4ceb741934..aca59458e9 100644
--- a/Cmdline/Action/Filter.cs
+++ b/Cmdline/Action/Filter.cs
@@ -60,7 +60,7 @@ public int RunSubCommand(GameInstanceManager mgr, CommonOptions opts, SubCommand
                             break;
 
                         default:
-                            user.RaiseMessage("Unknown command: filter {0}", option);
+                            user.RaiseMessage("{0}: filter {1}", Properties.Resources.UnknownCommand, option);
                             exitCode = Exit.BADOPT;
                             break;
                     }
@@ -78,7 +78,7 @@ private int ListFilters(FilterListOptions opts, string verb)
             }
 
             var cfg = ServiceLocator.Container.Resolve<Configuration.IConfiguration>();
-            user.RaiseMessage("Global filters:");
+            user.RaiseMessage(Properties.Resources.FilterListGlobalHeader);
             foreach (string filter in cfg.GlobalInstallFilters)
             {
                 user.RaiseMessage("\t- {0}", filter);
@@ -86,7 +86,7 @@ private int ListFilters(FilterListOptions opts, string verb)
             user.RaiseMessage("");
 
             var instance = MainClass.GetGameInstance(manager);
-            user.RaiseMessage("Instance filters:");
+            user.RaiseMessage(Properties.Resources.FilterListInstanceHeader);
             foreach (string filter in instance.InstallFilters)
             {
                 user.RaiseMessage("\t- {0}", filter);
@@ -98,7 +98,7 @@ private int AddFilters(FilterAddOptions opts, string verb)
         {
             if (opts.filters.Count < 1)
             {
-                user.RaiseMessage("Usage: ckan filter {0} filter1 [filter2 ...]", verb);
+                user.RaiseMessage("{0}: ckan filter {1} filter1 [filter2 ...]", Properties.Resources.Usage, verb);
                 return Exit.BADOPT;
             }
 
@@ -117,7 +117,7 @@ private int AddFilters(FilterAddOptions opts, string verb)
                 if (duplicates.Length > 0)
                 {
                     user.RaiseError(
-                        "Global filters already set: {0}",
+                        Properties.Resources.FilterAddGlobalDuplicateError,
                         string.Join(", ", duplicates)
                     );
                     return Exit.BADOPT;
@@ -139,7 +139,7 @@ private int AddFilters(FilterAddOptions opts, string verb)
                     if (duplicates.Length > 0)
                     {
                         user.RaiseError(
-                            "Instance filters already set: {0}",
+                            Properties.Resources.FilterAddInstanceDuplicateError,
                             string.Join(", ", duplicates)
                         );
                         return Exit.BADOPT;
@@ -159,7 +159,7 @@ private int RemoveFilters(FilterRemoveOptions opts, string verb)
         {
             if (opts.filters.Count < 1)
             {
-                user.RaiseMessage("Usage: ckan filter {0} filter1 [filter2 ...]", verb);
+                user.RaiseMessage("{0}: ckan filter {1} filter1 [filter2 ...]", Properties.Resources.Usage, verb);
                 return Exit.BADOPT;
             }
 
@@ -178,7 +178,7 @@ private int RemoveFilters(FilterRemoveOptions opts, string verb)
                 if (notFound.Length > 0)
                 {
                     user.RaiseError(
-                        "Global filters not found: {0}",
+                        Properties.Resources.FilterRemoveGlobalNotFoundError,
                         string.Join(", ", notFound)
                     );
                     return Exit.BADOPT;
@@ -199,7 +199,7 @@ private int RemoveFilters(FilterRemoveOptions opts, string verb)
                 if (notFound.Length > 0)
                 {
                     user.RaiseError(
-                        "Instance filters not found: {0}",
+                        Properties.Resources.FilterRemoveInstanceNotFoundError,
                         string.Join(", ", notFound)
                     );
                     return Exit.BADOPT;
@@ -239,8 +239,8 @@ public string GetUsage(string verb)
             ht.AddPreOptionsLine(" ");
             if (string.IsNullOrEmpty(verb))
             {
-                ht.AddPreOptionsLine("ckan filter - View or edit installation filters");
-                ht.AddPreOptionsLine($"Usage: ckan filter <command> [options]");
+                ht.AddPreOptionsLine($"ckan filter - {Properties.Resources.FilterHelpSummary}");
+                ht.AddPreOptionsLine($"{Properties.Resources.Usage}: ckan filter <{Properties.Resources.Command}> [{Properties.Resources.Options}]");
             }
             else
             {
@@ -248,15 +248,15 @@ public string GetUsage(string verb)
                 switch (verb)
                 {
                     case "list":
-                        ht.AddPreOptionsLine($"Usage: ckan filter {verb}");
+                        ht.AddPreOptionsLine($"{Properties.Resources.Usage}: ckan filter {verb}");
                         break;
 
                     case "add":
-                        ht.AddPreOptionsLine($"Usage: ckan filter {verb} [options] filter1 [filter2 ...]");
+                        ht.AddPreOptionsLine($"{Properties.Resources.Usage}: ckan filter {verb} [{Properties.Resources.Options}] filter1 [filter2 ...]");
                         break;
 
                     case "remove":
-                        ht.AddPreOptionsLine($"Usage: ckan filter {verb} [options] filter1 [filter2 ...]");
+                        ht.AddPreOptionsLine($"{Properties.Resources.Usage}: ckan filter {verb} [{Properties.Resources.Options}] filter1 [filter2 ...]");
                         break;
                 }
             }
diff --git a/Cmdline/Action/GameInstance.cs b/Cmdline/Action/GameInstance.cs
index 44bbef3a73..8d8198f3c9 100644
--- a/Cmdline/Action/GameInstance.cs
+++ b/Cmdline/Action/GameInstance.cs
@@ -41,8 +41,8 @@ public string GetUsage(string verb)
             ht.AddPreOptionsLine(" ");
             if (string.IsNullOrEmpty(verb))
             {
-                ht.AddPreOptionsLine("ckan instance - Manage game instances");
-                ht.AddPreOptionsLine($"Usage: ckan instance <command> [options]");
+                ht.AddPreOptionsLine($"ckan instance - {Properties.Resources.InstanceHelpSummary}");
+                ht.AddPreOptionsLine($"{Properties.Resources.Usage}: ckan instance <{Properties.Resources.Command}> [{Properties.Resources.Options}]");
             }
             else
             {
@@ -51,19 +51,19 @@ public string GetUsage(string verb)
                 {
                     // First the commands with three string arguments
                     case "fake":
-                        ht.AddPreOptionsLine($"Usage: ckan instance {verb} [options] name path version [--MakingHistory <version>] [--BreakingGround <version>]");
+                        ht.AddPreOptionsLine($"{Properties.Resources.Usage}: ckan instance {verb} [{Properties.Resources.Options}] name path version [--MakingHistory <version>] [--BreakingGround <version>]");
                         break;
 
                     case "clone":
-                        ht.AddPreOptionsLine($"Usage: ckan instance {verb} [options] instanceNameOrPath newname newpath");
+                        ht.AddPreOptionsLine($"{Properties.Resources.Usage}: ckan instance {verb} [{Properties.Resources.Options}] instanceNameOrPath newname newpath");
                         break;
 
                     // Second the commands with two string arguments
                     case "add":
-                        ht.AddPreOptionsLine($"Usage: ckan instance {verb} [options] name url");
+                        ht.AddPreOptionsLine($"{Properties.Resources.Usage}: ckan instance {verb} [{Properties.Resources.Options}] name url");
                         break;
                     case "rename":
-                        ht.AddPreOptionsLine($"Usage: ckan instance {verb} [options] oldname newname");
+                        ht.AddPreOptionsLine($"{Properties.Resources.Usage}: ckan instance {verb} [{Properties.Resources.Options}] oldname newname");
                         break;
 
                     // Now the commands with one string argument
@@ -71,13 +71,13 @@ public string GetUsage(string verb)
                     case "forget":
                     case "use":
                     case "default":
-                        ht.AddPreOptionsLine($"Usage: ckan instance {verb} [options] name");
+                        ht.AddPreOptionsLine($"{Properties.Resources.Usage}: ckan instance {verb} [{Properties.Resources.Options}] name");
                         break;
 
                     // Now the commands with only --flag type options
                     case "list":
                     default:
-                        ht.AddPreOptionsLine($"Usage: ckan instance {verb} [options]");
+                        ht.AddPreOptionsLine($"{Properties.Resources.Usage}: ckan instance {verb} [{Properties.Resources.Options}]");
                         break;
 
                 }
@@ -207,7 +207,7 @@ public int RunSubCommand(GameInstanceManager manager, CommonOptions opts, SubCom
                             break;
 
                         default:
-                            User.RaiseMessage("Unknown command: instance {0}", option);
+                            User.RaiseMessage("{0}: instance {1}", Properties.Resources.UnknownCommand, option);
                             exitCode = Exit.BADOPT;
                             break;
                     }
@@ -216,8 +216,8 @@ public int RunSubCommand(GameInstanceManager manager, CommonOptions opts, SubCom
             return exitCode;
         }
 
+        private IUser               User    { get; set; }
         private GameInstanceManager Manager { get; set; }
-        private IUser      User    { get; set; }
 
         #region option functions
 
@@ -230,46 +230,48 @@ private int ListInstalls()
                 .Select(i => new
                 {
                     Name = i.Key,
-                    Version = i.Value.Version()?.ToString() ?? "<NONE>",
-                    Default = i.Value.Name == Manager.AutoStartInstance ? "Yes" : "No",
+                    Version = i.Value.Version()?.ToString() ?? Properties.Resources.InstanceListNoVersion,
+                    Default = i.Value.Name == Manager.AutoStartInstance
+                        ? Properties.Resources.InstanceListYes
+                        : Properties.Resources.InstanceListNo,
                     Path = i.Value.GameDir()
                 })
                 .ToList();
 
-            const string nameHeader = "Name";
-            const string versionHeader = "Version";
-            const string defaultHeader = "Default";
-            const string pathHeader = "Path";
+            string nameHeader    = Properties.Resources.InstanceListNameHeader;
+            string versionHeader = Properties.Resources.InstanceListVersionHeader;
+            string defaultHeader = Properties.Resources.InstanceListDefaultHeader;
+            string pathHeader    = Properties.Resources.InstanceListPathHeader;
 
-            var nameWidth = Enumerable.Repeat(nameHeader, 1).Concat(output.Select(i => i.Name)).Max(i => i.Length);
+            var nameWidth    = Enumerable.Repeat(nameHeader, 1).Concat(output.Select(i => i.Name)).Max(i => i.Length);
             var versionWidth = Enumerable.Repeat(versionHeader, 1).Concat(output.Select(i => i.Version)).Max(i => i.Length);
             var defaultWidth = Enumerable.Repeat(defaultHeader, 1).Concat(output.Select(i => i.Default)).Max(i => i.Length);
-            var pathWidth = Enumerable.Repeat(pathHeader, 1).Concat(output.Select(i => i.Path)).Max(i => i.Length);
+            var pathWidth    = Enumerable.Repeat(pathHeader, 1).Concat(output.Select(i => i.Path)).Max(i => i.Length);
 
             const string columnFormat = "{0}  {1}  {2}  {3}";
 
-            User.RaiseMessage(string.Format(columnFormat,
+            User.RaiseMessage(columnFormat,
                 nameHeader.PadRight(nameWidth),
                 versionHeader.PadRight(versionWidth),
                 defaultHeader.PadRight(defaultWidth),
                 pathHeader.PadRight(pathWidth)
-            ));
+            );
 
-            User.RaiseMessage(string.Format(columnFormat,
+            User.RaiseMessage(columnFormat,
                 new string('-', nameWidth),
                 new string('-', versionWidth),
                 new string('-', defaultWidth),
                 new string('-', pathWidth)
-            ));
+            );
 
             foreach (var line in output)
             {
-                User.RaiseMessage(string.Format(columnFormat,
-                   line.Name.PadRight(nameWidth),
-                   line.Version.PadRight(versionWidth),
-                   line.Default.PadRight(defaultWidth),
-                   line.Path.PadRight(pathWidth)
-               ));
+                User.RaiseMessage(columnFormat,
+                    line.Name.PadRight(nameWidth),
+                    line.Version.PadRight(versionWidth),
+                    line.Default.PadRight(defaultWidth),
+                    line.Path.PadRight(pathWidth)
+                );
             }
 
             return Exit.OK;
@@ -279,13 +281,13 @@ private int AddInstall(AddOptions options)
         {
             if (options.name == null || options.path == null)
             {
-                User.RaiseMessage("add <name> <path> - argument missing, perhaps you forgot it?");
+                User.RaiseMessage("add <name> <{0}> - {1}", Properties.Resources.Path, Properties.Resources.ArgumentMissing);
                 return Exit.BADOPT;
             }
 
             if (Manager.HasInstance(options.name))
             {
-                User.RaiseMessage("Install with name \"{0}\" already exists, aborting..", options.name);
+                User.RaiseMessage(Properties.Resources.InstanceAddDuplicate, options.name);
                 return Exit.BADOPT;
             }
 
@@ -293,12 +295,12 @@ private int AddInstall(AddOptions options)
             {
                 string path = options.path;
                 Manager.AddInstance(path, options.name, User);
-                User.RaiseMessage("Added \"{0}\" with root \"{1}\" to known installs", options.name, options.path);
+                User.RaiseMessage(Properties.Resources.InstanceAdded, options.name, options.path);
                 return Exit.OK;
             }
             catch (NotKSPDirKraken ex)
             {
-                User.RaiseMessage("Sorry, {0} does not appear to be a game instance", ex.path);
+                User.RaiseMessage(Properties.Resources.InstanceNotInstance, ex.path);
                 return Exit.BADOPT;
             }
         }
@@ -307,7 +309,8 @@ private int CloneInstall(CloneOptions options)
         {
             if (options.nameOrPath == null || options.new_name == null || options.new_path == null)
             {
-                User.RaiseMessage("instance clone <nameOrPathExistingInstance> <newName> <newPath> - argument(s) missing");
+                User.RaiseMessage("instance clone <nameOrPathExistingInstance> <newName> <newPath> - {0}",
+                    Properties.Resources.ArgumentMissing);
                 return Exit.BADOPT;
             }
 
@@ -375,13 +378,13 @@ private int CloneInstall(CloneOptions options)
             }
             catch (NoGameInstanceKraken)
             {
-                User.RaiseError(String.Format("No instance with this name or at this path: {0}\n See below for a list of known instances:\n", instanceNameOrPath));
+                User.RaiseError(Properties.Resources.InstanceCloneNotFound, instanceNameOrPath);
                 ListInstalls();
                 return Exit.ERROR;
             }
             catch (InstanceNameTakenKraken kraken)
             {
-                User.RaiseError("This instance name is already taken: {0}", kraken.instName);
+                User.RaiseError(Properties.Resources.InstanceDuplicate, kraken.instName);
                 return Exit.BADOPT;
             }
 
@@ -394,8 +397,7 @@ private int CloneInstall(CloneOptions options)
             }
             else
             {
-                User.RaiseMessage("Something went wrong. Please look if the new directory has been created.\n",
-                    "Try to add the new instance manually with \"ckan instance add\".\n");
+                User.RaiseMessage(Properties.Resources.InstanceCloneFailed);
                 return Exit.ERROR;
             }
         }
@@ -404,19 +406,19 @@ private int RenameInstall(RenameOptions options)
         {
             if (options.old_name == null || options.new_name == null)
             {
-                User.RaiseMessage("rename <old_name> <new_name> - argument missing, perhaps you forgot it?");
+                User.RaiseMessage("rename <old_name> <new_name> - {0}", Properties.Resources.ArgumentMissing);
                 return Exit.BADOPT;
             }
 
             if (!Manager.HasInstance(options.old_name))
             {
-                User.RaiseMessage("Couldn't find install with name \"{0}\", aborting..", options.old_name);
+                User.RaiseMessage(Properties.Resources.InstanceNotFound, options.old_name);
                 return Exit.BADOPT;
             }
 
             Manager.RenameInstance(options.old_name, options.new_name);
 
-            User.RaiseMessage("Successfully renamed \"{0}\" to \"{1}\"", options.old_name, options.new_name);
+            User.RaiseMessage(Properties.Resources.InstanceRenamed, options.old_name, options.new_name);
             return Exit.OK;
         }
 
@@ -424,19 +426,19 @@ private int ForgetInstall(ForgetOptions options)
         {
             if (options.name == null)
             {
-                User.RaiseMessage("forget <name> - argument missing, perhaps you forgot it?");
+                User.RaiseMessage("forget <name> - {0}", Properties.Resources.ArgumentMissing);
                 return Exit.BADOPT;
             }
 
             if (!Manager.HasInstance(options.name))
             {
-                User.RaiseMessage("Couldn't find install with name \"{0}\", aborting..", options.name);
+                User.RaiseMessage(Properties.Resources.InstanceNotFound, options.name);
                 return Exit.BADOPT;
             }
 
             Manager.RemoveInstance(options.name);
 
-            User.RaiseMessage("Successfully removed \"{0}\"", options.name);
+            User.RaiseMessage(Properties.Resources.InstanceForgot, options.name);
             return Exit.OK;
         }
 
@@ -447,7 +449,7 @@ private int SetDefaultInstall(DefaultOptions options)
             if (name == null)
             {
                 // No input argument from the user. Present a list of the possible instances.
-                string message = "default <name> - argument missing, please select from the list below.";
+                string message = $"default <name> - {Properties.Resources.InstanceDefaultArgumentMissing}";
 
                 // Check if there is a default instance.
                 string defaultInstance = Manager.Configuration.AutoStartInstance;
@@ -465,7 +467,7 @@ private int SetDefaultInstall(DefaultOptions options)
                 {
                     var instance = Manager.Instances.ElementAt(i);
 
-                    keys[i + defaultInstancePresent] = String.Format("\"{0}\" - {1}", instance.Key, instance.Value.GameDir());
+                    keys[i + defaultInstancePresent] = string.Format("\"{0}\" - {1}", instance.Key, instance.Value.GameDir());
                 }
 
                 // Mark the default instance for the user.
@@ -495,7 +497,7 @@ private int SetDefaultInstall(DefaultOptions options)
 
             if (!Manager.Instances.ContainsKey(name))
             {
-                User.RaiseMessage("Couldn't find install with name \"{0}\", aborting..", name);
+                User.RaiseMessage(Properties.Resources.InstanceNotFound, name);
                 return Exit.BADOPT;
             }
 
@@ -505,11 +507,11 @@ private int SetDefaultInstall(DefaultOptions options)
             }
             catch (NotKSPDirKraken k)
             {
-                User.RaiseMessage("Sorry, {0} does not appear to be a game instance", k.path);
+                User.RaiseMessage(Properties.Resources.InstanceNotInstance, k.path);
                 return Exit.BADOPT;
             }
 
-            User.RaiseMessage("Successfully set \"{0}\" as the default game instance", name);
+            User.RaiseMessage(Properties.Resources.InstanceDefaultSet, name);
             return Exit.OK;
         }
 
@@ -522,22 +524,22 @@ private int FakeNewGameInstance(FakeOptions options)
             int error()
             {
                 log.Debug("Instance faking failed, see console output for details.");
-                User.RaiseMessage("--Error--");
                 return Exit.ERROR;
             }
 
             int badArgument()
             {
                 log.Debug("Instance faking failed: bad argument(s). See console output for details.");
-                User.RaiseMessage("--Error: bad argument(s)--");
+                User.RaiseMessage(Properties.Resources.InstanceFakeBadArguments);
                 return Exit.BADOPT;
             }
 
 
             if (options.name == null || options.path == null || options.version == null)
             {
-                User.RaiseMessage("instance fake <name> <path> <version> " +
-                    "[--MakingHistory <version>] [--BreakingGround <version>] - argument(s) missing");
+                User.RaiseMessage("instance fake <name> <{0}> <version> " +
+                	"[--MakingHistory <version>] [--BreakingGround <version>] - {1}",
+                    Properties.Resources.Path, Properties.Resources.ArgumentMissing);
                 return badArgument();
             }
 
@@ -558,7 +560,7 @@ int badArgument()
                 }
                 else
                 {
-                    User.RaiseError("Please check the Making History DLC version argument - Format it like Maj.Min.Patch - e.g. 1.1.0");
+                    User.RaiseError(Properties.Resources.InstanceFakeMakingHistory);
                     return badArgument();
                 }
             }
@@ -570,7 +572,7 @@ int badArgument()
                 }
                 else
                 {
-                    User.RaiseError("Please check the Breaking Ground DLC version argument - Format it like Maj.Min.Patch - e.g. 1.1.0");
+                    User.RaiseError(Properties.Resources.InstanceFakeBreakingGround);
                     return badArgument();
                 }
             }
@@ -583,7 +585,7 @@ int badArgument()
             catch (FormatException)
             {
                 // Thrown if there is anything besides numbers and points in the version string or a different syntactic error.
-                User.RaiseError("Please check the version argument - Format it like Maj.Min.Patch[.Build] - e.g. 1.6.0 or 1.2.2.1622");
+                User.RaiseError(Properties.Resources.InstanceFakeVersion);
                 return badArgument();
             }
 
@@ -594,17 +596,17 @@ int badArgument()
             }
             catch (BadGameVersionKraken)
             {
-                User.RaiseError("Couldn't find a valid game version for your input.\n" +
-                    "Make sure to enter the at least the version major and minor values in the form Maj.Min - e.g. 1.5");
+                User.RaiseError(Properties.Resources.InstanceFakeBadGameVersion);
                 return badArgument();
             }
             catch (CancelledActionKraken)
             {
-                User.RaiseError("Selection cancelled! Please call 'ckan instance fake' again.");
+                User.RaiseError(Properties.Resources.InstanceFakeCancelled);
                 return error();
             }
 
-            User.RaiseMessage(String.Format("Creating new fake game instance {0} at {1} with version {2}", installName, path, version.ToString()));
+            User.RaiseMessage(Properties.Resources.InstanceFakeCreating,
+                installName, path, version.ToString());
             log.Debug("Faking instance...");
 
             try
@@ -613,13 +615,13 @@ int badArgument()
                 Manager.FakeInstance(new KerbalSpaceProgram(), installName, path, version, dlcs);
                 if (setDefault)
                 {
-                    User.RaiseMessage("Setting new instance to default...");
+                    User.RaiseMessage(Properties.Resources.InstanceFakeDefault);
                     Manager.SetAutoStart(installName);
                 }
             }
             catch (InstanceNameTakenKraken kraken)
             {
-                User.RaiseError("This instance name is already taken: {0}", kraken.instName);
+                User.RaiseError(Properties.Resources.InstanceDuplicate, kraken.instName);
                 return badArgument();
             }
             catch (BadInstallLocationKraken kraken)
@@ -639,6 +641,7 @@ int badArgument()
                 // Something went wrong adding the new instance to the registry,
                 // most likely because the newly created directory is somehow not valid.
                 log.Error(kraken);
+                User.RaiseError(kraken.Message);
                 return error();
             }
             catch (InvalidKSPInstanceKraken)
@@ -652,13 +655,12 @@ int badArgument()
             // No need to test if valid, because this is done in AddInstance().
             if (Manager.HasInstance(installName))
             {
-                User.RaiseMessage("--Done--");
+                User.RaiseMessage(Properties.Resources.InstanceFakeDone);
                 return Exit.OK;
             }
             else
             {
-                User.RaiseError("Something went wrong. Try to add the instance yourself with \"ckan instance add\".",
-                    "Also look if the new directory has been created.");
+                User.RaiseError(Properties.Resources.InstanceFakeFailed);
                 return error();
             }
         }
diff --git a/Cmdline/Action/Import.cs b/Cmdline/Action/Import.cs
index 044d9170a7..7934290e98 100644
--- a/Cmdline/Action/Import.cs
+++ b/Cmdline/Action/Import.cs
@@ -39,7 +39,7 @@ public int RunCommand(CKAN.GameInstance ksp, object options)
                 HashSet<FileInfo> toImport = GetFiles(opts);
                 if (toImport.Count < 1)
                 {
-                    user.RaiseMessage("Usage: ckan import path [path2, ...]");
+                    user.RaiseMessage($"{Properties.Resources.Usage}: ckan import {Properties.Resources.Path} [path2, ...]");
                     return Exit.ERROR;
                 }
                 else
@@ -64,7 +64,7 @@ ref possibleConfigOnlyDirs
             }
             catch (Exception ex)
             {
-                user.RaiseError("Import error: {0}", ex.Message);
+                user.RaiseError(Properties.Resources.ImportError, ex.Message);
                 return Exit.ERROR;
             }
         }
@@ -100,7 +100,7 @@ private void AddFile(HashSet<FileInfo> files, string filename)
             }
             else
             {
-                user.RaiseMessage("File not found: {0}", filename);
+                user.RaiseMessage(Properties.Resources.ImportNotFound, filename);
             }
         }
 
diff --git a/Cmdline/Action/Install.cs b/Cmdline/Action/Install.cs
index 0f20276b20..8f662580da 100644
--- a/Cmdline/Action/Install.cs
+++ b/Cmdline/Action/Install.cs
@@ -27,12 +27,12 @@ public Install(GameInstanceManager mgr, IUser user)
         /// <summary>
         /// Installs a module, if available
         /// </summary>
-        /// <param name="ksp">Game instance into which to install</param>
+        /// <param name="instance">Game instance into which to install</param>
         /// <param name="raw_options">Command line options object</param>
         /// <returns>
         /// Exit code for shell environment
         /// </returns>
-        public int RunCommand(CKAN.GameInstance ksp, object raw_options)
+        public int RunCommand(CKAN.GameInstance instance, object raw_options)
         {
             InstallOptions options = (InstallOptions) raw_options;
 
@@ -55,8 +55,7 @@ public int RunCommand(CKAN.GameInstance ksp, object raw_options)
                         else
                         {
                             // We have no further ideas as what we can do with this Uri, tell the user.
-                            user.RaiseError("Can not find file \"{0}\".", ckan_file);
-                            user.RaiseError("Exiting.");
+                            user.RaiseError(Properties.Resources.InstallNotFound, ckan_file);
                             return Exit.ERROR;
                         }
                     }
@@ -84,7 +83,7 @@ public int RunCommand(CKAN.GameInstance ksp, object raw_options)
                     // Parse the JSON file.
                     try
                     {
-                        CkanModule m = MainClass.LoadCkanFromFile(ksp, filename);
+                        CkanModule m = MainClass.LoadCkanFromFile(instance, filename);
                         options.modules.Add($"{m.identifier}={m.version}");
                     }
                     catch (Kraken kraken)
@@ -101,14 +100,14 @@ public int RunCommand(CKAN.GameInstance ksp, object raw_options)
             }
             else
             {
-                Search.AdjustModulesCase(ksp, options.modules);
+                Search.AdjustModulesCase(instance, options.modules);
             }
 
             if (options.modules.Count == 0)
             {
                 // What? No files specified?
                 user.RaiseMessage(
-                    "Usage: ckan install [--with-suggests] [--with-all-suggests] [--no-recommends] [--headless] Mod [Mod2, ...]");
+                    $"{Properties.Resources.Usage}: ckan install [--with-suggests] [--with-all-suggests] [--no-recommends] [--headless] Mod [Mod2, ...]");
                 return Exit.BADOPT;
             }
 
@@ -127,7 +126,7 @@ public int RunCommand(CKAN.GameInstance ksp, object raw_options)
                 install_ops.without_enforce_consistency = true;
             }
 
-            RegistryManager regMgr = RegistryManager.Instance(ksp);
+            RegistryManager regMgr = RegistryManager.Instance(instance);
             List<string> modules = options.modules;
 
             for (bool done = false; !done; )
@@ -136,7 +135,7 @@ public int RunCommand(CKAN.GameInstance ksp, object raw_options)
                 try
                 {
                     HashSet<string> possibleConfigOnlyDirs = null;
-                    var installer = new ModuleInstaller(ksp, manager.Cache, user);
+                    var installer = new ModuleInstaller(instance, manager.Cache, user);
                     installer.InstallList(modules, install_ops, regMgr, ref possibleConfigOnlyDirs);
                     user.RaiseMessage("");
                     done = true;
@@ -144,32 +143,27 @@ public int RunCommand(CKAN.GameInstance ksp, object raw_options)
                 catch (DependencyNotSatisfiedKraken ex)
                 {
                     user.RaiseError(ex.Message);
-                    user.RaiseMessage("If you're lucky, you can do a `ckan update` and try again.");
-                    user.RaiseMessage("Try `ckan install --no-recommends` to skip installation of recommended modules.");
-                    user.RaiseMessage("Or `ckan install --allow-incompatible` to ignore module compatibility.");
+                    user.RaiseMessage(Properties.Resources.InstallTryAgain);
                     return Exit.ERROR;
                 }
                 catch (ModuleNotFoundKraken ex)
                 {
                     if (ex.version == null)
                     {
-                        user.RaiseError("Module {0} required but it is not listed in the index, or not available for your version of KSP.",
-                            ex.module);
+                        user.RaiseError(Properties.Resources.InstallUnversionedDependencyNotSatisfied,
+                            ex.module, instance.game.ShortName);
                     }
                     else
                     {
-                        user.RaiseError("Module {0} {1} required but it is not listed in the index, or not available for your version of KSP.",
-                            ex.module, ex.version);
+                        user.RaiseError(Properties.Resources.InstallVersionedDependencyNotSatisfied,
+                            ex.module, ex.version, instance.game.ShortName);
                     }
-                    user.RaiseMessage("If you're lucky, you can do a `ckan update` and try again.");
-                    user.RaiseMessage("Try `ckan install --no-recommends` to skip installation of recommended modules.");
-                    user.RaiseMessage("Or `ckan install --allow-incompatible` to ignore module compatibility.");
+                    user.RaiseMessage(Properties.Resources.InstallTryAgain);
                     return Exit.ERROR;
                 }
                 catch (BadMetadataKraken ex)
                 {
-                    user.RaiseError("Bad metadata detected for module {0}: {1}",
-                        ex.module, ex.Message);
+                    user.RaiseError(Properties.Resources.InstallBadMetadata, ex.module, ex.Message);
                     return Exit.ERROR;
                 }
                 catch (TooManyModsProvideKraken ex)
@@ -204,49 +198,29 @@ public int RunCommand(CKAN.GameInstance ksp, object raw_options)
                 {
                     if (ex.owningModule != null)
                     {
-                        user.RaiseError(
-                            "Oh no! We tried to overwrite a file owned by another mod!\r\n"+
-                            "Please try a `ckan update` and try again.\r\n\r\n"+
-                            "If this problem re-occurs, then it maybe a packaging bug.\r\n"+
-                            "Please report it at:\r\n\r\n" +
-                            "https://github.com/KSP-CKAN/NetKAN/issues/new\r\n\r\n" +
-                            "Please including the following information in your report:\r\n\r\n" +
-                            "File           : {0}\r\n" +
-                            "Installing Mod : {1}\r\n" +
-                            "Owning Mod     : {2}\r\n" +
-                            "CKAN Version   : {3}\r\n",
+                        user.RaiseError(Properties.Resources.InstallFileConflictOwned,
                             ex.filename, ex.installingModule, ex.owningModule,
-                            Meta.GetVersion(VersionFormat.Full)
-                        );
+                            Meta.GetVersion(VersionFormat.Full));
                     }
                     else
                     {
-                        user.RaiseError(
-                            "Oh no!\r\n\r\n"+
-                            "It looks like you're trying to install a mod which is already installed,\r\n"+
-                            "or which conflicts with another mod which is already installed.\r\n\r\n"+
-                            "As a safety feature, CKAN will *never* overwrite or alter a file\r\n"+
-                            "that it did not install itself.\r\n\r\n"+
-                            "If you wish to install {0} via CKAN,\r\n"+
-                            "then please manually uninstall the mod which owns:\r\n\r\n"+
-                            "{1}\r\n\r\n"+"and try again.\r\n",
-                            ex.installingModule, ex.filename
-                        );
+                        user.RaiseError(Properties.Resources.InstallFileConflictUnowned,
+                            ex.installingModule, ex.filename);
                     }
 
-                    user.RaiseMessage("Your GameData has been returned to its original state.");
+                    user.RaiseMessage(Properties.Resources.InstallGamedataReturned, instance.game.PrimaryModDirectoryRelative);
                     return Exit.ERROR;
                 }
                 catch (InconsistentKraken ex)
                 {
                     // The prettiest Kraken formats itself for us.
                     user.RaiseError(ex.InconsistenciesPretty);
-                    user.RaiseMessage("Install canceled. Your files have been returned to their initial state.");
+                    user.RaiseMessage(Properties.Resources.InstallCancelled);
                     return Exit.ERROR;
                 }
                 catch (CancelledActionKraken k)
                 {
-                    user.RaiseError("Installation aborted: {0}", k.Message);
+                    user.RaiseError(Properties.Resources.InstallAborted, k.Message);
                     return Exit.ERROR;
                 }
                 catch (MissingCertificateKraken kraken)
@@ -258,13 +232,12 @@ public int RunCommand(CKAN.GameInstance ksp, object raw_options)
                 catch (DownloadThrottledKraken kraken)
                 {
                     user.RaiseError(kraken.ToString());
-                    user.RaiseMessage("Try the authtoken command. See {0} for details.",
-                        kraken.infoUrl);
+                    user.RaiseMessage(Properties.Resources.InstallTryAuthToken, kraken.infoUrl);
                     return Exit.ERROR;
                 }
                 catch (DownloadErrorsKraken)
                 {
-                    user.RaiseError("One or more files failed to download, stopped.");
+                    user.RaiseError(Properties.Resources.InstallDownloadFailed);
                     return Exit.ERROR;
                 }
                 catch (ModuleDownloadErrorsKraken kraken)
@@ -279,15 +252,14 @@ public int RunCommand(CKAN.GameInstance ksp, object raw_options)
                 }
                 catch (ModuleIsDLCKraken kraken)
                 {
-                    user.RaiseError("CKAN can't install expansion '{0}' for you.",
-                        kraken.module.name);
+                    user.RaiseError(Properties.Resources.InstallDLC, kraken.module.name);
                     var res = kraken?.module?.resources;
                     var storePagesMsg = new Uri[] { res?.store, res?.steamstore }
                         .Where(u => u != null)
                         .Aggregate("", (a, b) => $"{a}\r\n- {b}");
                     if (!string.IsNullOrEmpty(storePagesMsg))
                     {
-                        user.RaiseMessage($"To install this expansion, purchase it from one of its store pages:\r\n{storePagesMsg}");
+                        user.RaiseMessage(Properties.Resources.InstallDLCStorePage, storePagesMsg);
                     }
                     return Exit.ERROR;
                 }
diff --git a/Cmdline/Action/List.cs b/Cmdline/Action/List.cs
index 04b0220202..9f28713902 100644
--- a/Cmdline/Action/List.cs
+++ b/Cmdline/Action/List.cs
@@ -18,11 +18,11 @@ public List(IUser user)
             this.user = user;
         }
 
-        public int RunCommand(CKAN.GameInstance ksp, object raw_options)
+        public int RunCommand(CKAN.GameInstance instance, object raw_options)
         {
             ListOptions options = (ListOptions) raw_options;
 
-            IRegistryQuerier registry = RegistryManager.Instance(ksp).registry;
+            IRegistryQuerier registry = RegistryManager.Instance(instance).registry;
 
             ExportFileType? exportFileType = null;
 
@@ -32,16 +32,19 @@ public int RunCommand(CKAN.GameInstance ksp, object raw_options)
 
                 if (exportFileType == null)
                 {
-                    user.RaiseError("Unknown export format: {0}", options.export);
+                    user.RaiseError(Properties.Resources.ListUnknownFormat, options.export);
                 }
             }
 
             if (!(options.porcelain) && exportFileType == null)
             {
-                user.RaiseMessage("\r\nKSP found at {0}\r\n", ksp.GameDir());
-                user.RaiseMessage("KSP Version: {0}\r\n", ksp.Version());
-
-                user.RaiseMessage("Installed Modules:\r\n");
+                user.RaiseMessage("");
+                user.RaiseMessage(Properties.Resources.ListGameFound, instance.game.ShortName, instance.GameDir());
+                user.RaiseMessage("");
+                user.RaiseMessage(Properties.Resources.ListGameVersion, instance.game.ShortName, instance.Version());
+                user.RaiseMessage("");
+                user.RaiseMessage(Properties.Resources.ListGameModulesHeader);
+                user.RaiseMessage("");
             }
 
             if (exportFileType == null)
@@ -70,7 +73,7 @@ public int RunCommand(CKAN.GameInstance ksp, object raw_options)
                         {
                             // Check if upgrades are available, and show appropriately.
                             log.DebugFormat("Check if upgrades are available for {0}", mod.Key);
-                            CkanModule latest = registry.LatestAvailable(mod.Key, ksp.VersionCriteria());
+                            CkanModule latest = registry.LatestAvailable(mod.Key, instance.VersionCriteria());
                             CkanModule current = registry.GetInstalledVersion(mod.Key);
                             InstalledModule inst = registry.InstalledModule(mod.Key);
 
@@ -79,12 +82,15 @@ public int RunCommand(CKAN.GameInstance ksp, object raw_options)
                                 // Not compatible!
                                 log.InfoFormat("Latest {0} is not compatible", mod.Key);
                                 bullet = "X";
-                                if ( current == null ) log.DebugFormat( " {0} installed version not found in registry", mod.Key);
+                                if (current == null)
+                                {
+                                    log.DebugFormat(" {0} installed version not found in registry", mod.Key);
+                                }
                                     
                                 // Check if mod is replaceable
                                 if (current.replaced_by != null)
                                 {
-                                    ModuleReplacement replacement = registry.GetReplacement(mod.Key, ksp.VersionCriteria());
+                                    ModuleReplacement replacement = registry.GetReplacement(mod.Key, instance.VersionCriteria());
                                     if (replacement != null)
                                     {
                                         // Replaceable!
@@ -101,7 +107,7 @@ public int RunCommand(CKAN.GameInstance ksp, object raw_options)
                                 // Check if mod is replaceable
                                 if (current.replaced_by != null)
                                 {
-                                    ModuleReplacement replacement = registry.GetReplacement(latest.identifier, ksp.VersionCriteria());
+                                    ModuleReplacement replacement = registry.GetReplacement(latest.identifier, instance.VersionCriteria());
                                     if (replacement != null)
                                     {
                                         // Replaceable!
@@ -135,7 +141,8 @@ public int RunCommand(CKAN.GameInstance ksp, object raw_options)
 
             if (!(options.porcelain) && exportFileType == null)
             {
-                user.RaiseMessage("\r\nLegend: -: Up to date. +:Auto-installed. X: Incompatible. ^: Upgradable. >: Replaceable\r\n        A: Autodetected. ?: Unknown. *: Broken. ");
+                user.RaiseMessage("");
+                user.RaiseMessage(Properties.Resources.ListLegend);
                 // Broken mods are in a state that CKAN doesn't understand, and therefore can't handle automatically
             }
 
diff --git a/Cmdline/Action/Mark.cs b/Cmdline/Action/Mark.cs
index da793fb372..19e73f524c 100644
--- a/Cmdline/Action/Mark.cs
+++ b/Cmdline/Action/Mark.cs
@@ -46,15 +46,15 @@ public int RunSubCommand(GameInstanceManager mgr, CommonOptions opts, SubCommand
                     switch (option)
                     {
                         case "auto":
-                            exitCode = MarkAuto((MarkAutoOptions)suboptions, true, option, "auto-installed");
+                            exitCode = MarkAuto((MarkAutoOptions)suboptions, true, option, Properties.Resources.MarkAutoInstalled);
                             break;
 
                         case "user":
-                            exitCode = MarkAuto((MarkAutoOptions)suboptions, false, option, "user-selected");
+                            exitCode = MarkAuto((MarkAutoOptions)suboptions, false, option, Properties.Resources.MarkUserSelected);
                             break;
 
                         default:
-                            user.RaiseMessage("Unknown command: mark {0}", option);
+                            user.RaiseMessage(Properties.Resources.MarkUnknownCommand, option);
                             exitCode = Exit.BADOPT;
                             break;
                     }
@@ -67,7 +67,7 @@ private int MarkAuto(MarkAutoOptions opts, bool value, string verb, string descr
         {
             if (opts.modules.Count < 1)
             {
-                user.RaiseMessage("Usage: ckan mark {0} Mod [Mod2 ...]", verb);
+                user.RaiseMessage("{0}: ckan mark {1} Mod [Mod2 ...]", Properties.Resources.Usage, verb);
                 return Exit.BADOPT;
             }
 
@@ -86,15 +86,15 @@ private int MarkAuto(MarkAutoOptions opts, bool value, string verb, string descr
                 InstalledModule im = regMgr.registry.InstalledModule(id);
                 if (im == null)
                 {
-                    user.RaiseError("{0} is not installed.", id);
+                    user.RaiseError(Properties.Resources.MarkNotInstalled, id);
                 }
                 else if (im.AutoInstalled == value)
                 {
-                    user.RaiseError("{0} is already marked as {1}.", id, descrip);
+                    user.RaiseError(Properties.Resources.MarkAlready, id, descrip);
                 }
                 else
                 {
-                    user.RaiseMessage("Marking {0} as {1}...", id, descrip);
+                    user.RaiseMessage(Properties.Resources.Marking, id, descrip);
                     try
                     {
                         im.AutoInstalled = value;
@@ -102,7 +102,7 @@ private int MarkAuto(MarkAutoOptions opts, bool value, string verb, string descr
                     }
                     catch (ModuleIsDLCKraken kraken)
                     {
-                        user.RaiseMessage($"Can't mark expansion '{kraken.module.name}' as auto-installed.");
+                        user.RaiseMessage(Properties.Resources.MarkDLC, kraken.module.name);
                         return Exit.BADOPT;
                     }
                 }
@@ -110,13 +110,13 @@ private int MarkAuto(MarkAutoOptions opts, bool value, string verb, string descr
             if (needSave)
             {
                 regMgr.Save(false);
-                user.RaiseMessage("Changes made!");
+                user.RaiseMessage(Properties.Resources.MarkChanged);
             }
             return Exit.OK;
         }
 
+        private IUser               user    { get; set; }
         private GameInstanceManager manager { get; set; }
-        private IUser      user    { get; set; }
 
         private static readonly ILog log = LogManager.GetLogger(typeof(Mark));
     }
@@ -137,20 +137,20 @@ public string GetUsage(string verb)
             ht.AddPreOptionsLine(" ");
             if (string.IsNullOrEmpty(verb))
             {
-                ht.AddPreOptionsLine("ckan mark - Edit flags on modules");
-                ht.AddPreOptionsLine($"Usage: ckan mark <command> [options]");
+                ht.AddPreOptionsLine($"ckan mark - {Properties.Resources.MarkHelpSummary}");
+                ht.AddPreOptionsLine($"{Properties.Resources.Usage}: ckan mark <{Properties.Resources.Command}> [{Properties.Resources.Options}]");
             }
             else
             {
-                ht.AddPreOptionsLine("ksp " + verb + " - " + GetDescription(verb));
+                ht.AddPreOptionsLine("mark " + verb + " - " + GetDescription(verb));
                 switch (verb)
                 {
                     case "auto":
-                        ht.AddPreOptionsLine($"Usage: ckan mark {verb} [options] Mod [Mod2 ...]");
+                        ht.AddPreOptionsLine($"{Properties.Resources.Usage}: ckan mark {verb} [{Properties.Resources.Options}] Mod [Mod2 ...]");
                         break;
 
                     case "user":
-                        ht.AddPreOptionsLine($"Usage: ckan mark {verb} [options] Mod [Mod2 ...]");
+                        ht.AddPreOptionsLine($"{Properties.Resources.Usage}: ckan mark {verb} [{Properties.Resources.Options}] Mod [Mod2 ...]");
                         break;
                 }
             }
diff --git a/Cmdline/Action/Prompt.cs b/Cmdline/Action/Prompt.cs
index 62a9d88b02..b9cd803636 100644
--- a/Cmdline/Action/Prompt.cs
+++ b/Cmdline/Action/Prompt.cs
@@ -24,10 +24,7 @@ public int RunCommand(object raw_options)
             // Print an intro if not in headless mode
             if (!headless)
             {
-                Console.WriteLine("Welcome to CKAN!");
-                Console.WriteLine("");
-                Console.WriteLine("To get help, type help and press enter.");
-                Console.WriteLine("");
+                Console.WriteLine(Properties.Resources.PromptWelcome, exitCommand);
             }
             ReadLine.AutoCompletionHandler = GetSuggestions;
             bool done = false;
@@ -38,8 +35,9 @@ public int RunCommand(object raw_options)
                 {
                     Console.Write(
                         manager.CurrentInstance != null
-                            ? $"CKAN {Meta.GetVersion()}: {manager.CurrentInstance.game.ShortName} {manager.CurrentInstance.Version()} ({manager.CurrentInstance.Name})> "
-                            : $"CKAN {Meta.GetVersion()}> "
+                            ? string.Format(Properties.Resources.PromptWithInstance,
+                                Meta.GetVersion(), manager.CurrentInstance.game.ShortName, manager.CurrentInstance.Version(), manager.CurrentInstance.Name)
+                            : string.Format(Properties.Resources.PromptWithoutInstance, Meta.GetVersion())
                     );
                 }
                 try
@@ -66,9 +64,7 @@ public int RunCommand(object raw_options)
                 }
                 catch (NoGameInstanceKraken)
                 {
-                    Console.WriteLine("");
-                    Console.WriteLine("No game instance selected, identifier completion not available.");
-                    Console.WriteLine("Use the `instance default` command to choose an instance.");
+                    Console.WriteLine(Properties.Resources.CompletionNotAvailable);
                 }
             }
             return Exit.OK;
diff --git a/Cmdline/Action/Remove.cs b/Cmdline/Action/Remove.cs
index d25a186707..c8770b2aa8 100644
--- a/Cmdline/Action/Remove.cs
+++ b/Cmdline/Action/Remove.cs
@@ -27,15 +27,15 @@ public Remove(GameInstanceManager mgr, IUser user)
         /// <summary>
         /// Uninstalls a module, if it exists.
         /// </summary>
-        /// <param name="ksp">Game instance from which to remove</param>
+        /// <param name="instance">Game instance from which to remove</param>
         /// <param name="raw_options">Command line options object</param>
         /// <returns>
         /// Exit code for shell environment
         /// </returns>
-        public int RunCommand(CKAN.GameInstance ksp, object raw_options)
+        public int RunCommand(CKAN.GameInstance instance, object raw_options)
         {
             RemoveOptions options = (RemoveOptions) raw_options;
-            RegistryManager regMgr = RegistryManager.Instance(ksp);
+            RegistryManager regMgr = RegistryManager.Instance(instance);
 
             // Use one (or more!) regex to select the modules to remove
             if (options.regex)
@@ -77,39 +77,38 @@ public int RunCommand(CKAN.GameInstance ksp, object raw_options)
                 try
                 {
                     HashSet<string> possibleConfigOnlyDirs = null;
-                    var installer = new ModuleInstaller(ksp, manager.Cache, user);
-                    Search.AdjustModulesCase(ksp, options.modules);
+                    var installer = new ModuleInstaller(instance, manager.Cache, user);
+                    Search.AdjustModulesCase(instance, options.modules);
                     installer.UninstallList(options.modules, ref possibleConfigOnlyDirs, regMgr);
                     user.RaiseMessage("");
                 }
                 catch (ModNotInstalledKraken kraken)
                 {
-                    user.RaiseMessage("I can't do that, {0} isn't installed.", kraken.mod);
-                    user.RaiseMessage("Try `ckan list` for a list of installed mods.");
+                    user.RaiseMessage(Properties.Resources.RemoveNotInstalled, kraken.mod);
                     return Exit.BADOPT;
                 }
                 catch (ModuleIsDLCKraken kraken)
                 {
-                    user.RaiseMessage($"CKAN can't remove expansion '{kraken.module.name}' for you.");
+                    user.RaiseMessage(Properties.Resources.RemoveDLC, kraken.module.name);
                     var res = kraken?.module?.resources;
                     var storePagesMsg = new Uri[] { res?.store, res?.steamstore }
                         .Where(u => u != null)
                         .Aggregate("", (a, b) => $"{a}\r\n- {b}");
                     if (!string.IsNullOrEmpty(storePagesMsg))
                     {
-                        user.RaiseMessage($"To remove this expansion, follow the instructions for the store page from which you purchased it:\r\n{storePagesMsg}");
+                        user.RaiseMessage(Properties.Resources.RemoveDLCStorePage, storePagesMsg);
                     }
                     return Exit.BADOPT;
                 }
                 catch (CancelledActionKraken k)
                 {
-                    user.RaiseMessage("Remove aborted: {0}", k.Message);
+                    user.RaiseMessage(Properties.Resources.RemoveCancelled, k.Message);
                     return Exit.ERROR;
                 }
             }
             else
             {
-                user.RaiseMessage("No mod selected, nothing to do");
+                user.RaiseMessage(Properties.Resources.RemoveNothing);
                 return Exit.BADOPT;
             }
 
diff --git a/Cmdline/Action/Repair.cs b/Cmdline/Action/Repair.cs
index 9468f1adf2..d91ad9ef93 100644
--- a/Cmdline/Action/Repair.cs
+++ b/Cmdline/Action/Repair.cs
@@ -16,8 +16,8 @@ public string GetUsage(string verb)
             ht.AddPreOptionsLine(" ");
             if (string.IsNullOrEmpty(verb))
             {
-                ht.AddPreOptionsLine("ckan repair - Attempt various automatic repairs");
-                ht.AddPreOptionsLine($"Usage: ckan repair <command> [options]");
+                ht.AddPreOptionsLine($"ckan repair - {Properties.Resources.RepairHelpSummary}");
+                ht.AddPreOptionsLine($"{Properties.Resources.Usage}: ckan repair <{Properties.Resources.Command}> [{Properties.Resources.Options}]");
             }
             else
             {
@@ -27,7 +27,7 @@ public string GetUsage(string verb)
                     // Commands with only --flag type options
                     case "registry":
                     default:
-                        ht.AddPreOptionsLine($"Usage: ckan repair {verb} [options]");
+                        ht.AddPreOptionsLine($"{Properties.Resources.Usage}: ckan repair {verb} [{Properties.Resources.Options}]");
                         break;
                 }
             }
@@ -66,7 +66,7 @@ public int RunSubCommand(GameInstanceManager manager, CommonOptions opts, SubCom
                             break;
 
                         default:
-                            User.RaiseMessage("Unknown command: repair {0}", option);
+                            User.RaiseMessage(Properties.Resources.RepairUnknownCommand, option);
                             exitCode = Exit.BADOPT;
                             break;
                     }
@@ -85,7 +85,7 @@ private int Registry(CKAN.GameInstance ksp)
             RegistryManager manager = RegistryManager.Instance(ksp);
             manager.registry.Repair();
             manager.Save();
-            User.RaiseMessage("Registry repairs attempted. Hope it helped.");
+            User.RaiseMessage(Properties.Resources.Repaired);
             return Exit.OK;
         }
     }
diff --git a/Cmdline/Action/Replace.cs b/Cmdline/Action/Replace.cs
index 8631e72a79..060744d48b 100644
--- a/Cmdline/Action/Replace.cs
+++ b/Cmdline/Action/Replace.cs
@@ -20,19 +20,19 @@ public Replace(CKAN.GameInstanceManager mgr, IUser user)
 
         private GameInstanceManager manager;
 
-        public int RunCommand(CKAN.GameInstance ksp, object raw_options)
+        public int RunCommand(CKAN.GameInstance instance, object raw_options)
         {
             ReplaceOptions options = (ReplaceOptions) raw_options;
 
             if (options.ckan_file != null)
             {
-                options.modules.Add(MainClass.LoadCkanFromFile(ksp, options.ckan_file).identifier);
+                options.modules.Add(MainClass.LoadCkanFromFile(instance, options.ckan_file).identifier);
             }
 
             if (options.modules.Count == 0 && ! options.replace_all)
             {
                 // What? No mods specified?
-                User.RaiseMessage("Usage: ckan replace Mod [Mod2, ...]");
+                User.RaiseMessage("{0}: ckan replace Mod [Mod2, ...]", Properties.Resources.Usage);
                 User.RaiseMessage("  or   ckan replace --all");
                 return Exit.BADOPT;
             }
@@ -46,7 +46,7 @@ public int RunCommand(CKAN.GameInstance ksp, object raw_options)
                     allow_incompatible = options.allow_incompatible
                 };
 
-            var regMgr = RegistryManager.Instance(ksp);
+            var regMgr = RegistryManager.Instance(instance);
             var registry = regMgr.registry;
             var to_replace = new List<ModuleReplacement>();
 
@@ -70,7 +70,7 @@ public int RunCommand(CKAN.GameInstance ksp, object raw_options)
                             log.DebugFormat("Testing {0} {1} for possible replacement", mod.Key, mod.Value);
                             // Check if replacement is available
 
-                            ModuleReplacement replacement = registry.GetReplacement(mod.Key, ksp.VersionCriteria());
+                            ModuleReplacement replacement = registry.GetReplacement(mod.Key, instance.VersionCriteria());
                             if (replacement != null)
                             {
                                 // Replaceable
@@ -102,7 +102,7 @@ public int RunCommand(CKAN.GameInstance ksp, object raw_options)
                             try
                             {
                                 // Check if replacement is available
-                                ModuleReplacement replacement = registry.GetReplacement(modToReplace.identifier, ksp.VersionCriteria());
+                                ModuleReplacement replacement = registry.GetReplacement(modToReplace.identifier, instance.VersionCriteria());
                                 if (replacement != null)
                                 {
                                     // Replaceable
@@ -131,25 +131,27 @@ public int RunCommand(CKAN.GameInstance ksp, object raw_options)
                     }
                     catch (ModuleNotFoundKraken kraken)
                     {
-                        User.RaiseMessage("Module {0} not found", kraken.module);
+                        User.RaiseMessage(Properties.Resources.ReplaceModuleNotFound, kraken.module);
                     }
                 }
             }
             if (to_replace.Count() != 0)
             {
-                User.RaiseMessage("\r\nReplacing modules...\r\n");
+                User.RaiseMessage("");
+                User.RaiseMessage(Properties.Resources.Replacing);
+                User.RaiseMessage("");
                 foreach (ModuleReplacement r in to_replace)
                 {
-                    User.RaiseMessage("Replacement {0} {1} found for {2} {3}",
+                    User.RaiseMessage(Properties.Resources.ReplaceFound,
                         r.ReplaceWith.identifier, r.ReplaceWith.version,
                         r.ToReplace.identifier, r.ToReplace.version);
                 }
 
-                bool ok = User.RaiseYesNoDialog("Continue?");
+                bool ok = User.RaiseYesNoDialog(Properties.Resources.ReplaceContinuePrompt);
 
                 if (!ok)
                 {
-                    User.RaiseMessage("Replacements canceled at user request.");
+                    User.RaiseMessage(Properties.Resources.ReplaceCancelled);
                     return Exit.ERROR;
                 }
 
@@ -157,17 +159,18 @@ public int RunCommand(CKAN.GameInstance ksp, object raw_options)
                 try
                 {
                     HashSet<string> possibleConfigOnlyDirs = null;
-                    new ModuleInstaller(ksp, manager.Cache, User).Replace(to_replace, replace_ops, new NetAsyncModulesDownloader(User, manager.Cache), ref possibleConfigOnlyDirs, regMgr);
+                    new ModuleInstaller(instance, manager.Cache, User).Replace(to_replace, replace_ops, new NetAsyncModulesDownloader(User, manager.Cache), ref possibleConfigOnlyDirs, regMgr);
                     User.RaiseMessage("");
                 }
                 catch (DependencyNotSatisfiedKraken ex)
                 {
-                    User.RaiseMessage("Dependencies not satisfied for replacement, {0} requires {1} {2} but it is not listed in the index, or not available for your version of KSP.", ex.parent, ex.module, ex.version);
+                    User.RaiseMessage(Properties.Resources.ReplaceDependencyNotSatisfied,
+                        ex.parent, ex.module, ex.version, instance.game.ShortName);
                 }
             }
             else
             {
-                User.RaiseMessage("No replacements found.");
+                User.RaiseMessage(Properties.Resources.ReplaceNotFound);
                 return Exit.OK;
             }
 
diff --git a/Cmdline/Action/Repo.cs b/Cmdline/Action/Repo.cs
index dbabc7c865..d357e6bed9 100644
--- a/Cmdline/Action/Repo.cs
+++ b/Cmdline/Action/Repo.cs
@@ -34,8 +34,8 @@ public string GetUsage(string verb)
             ht.AddPreOptionsLine(" ");
             if (string.IsNullOrEmpty(verb))
             {
-                ht.AddPreOptionsLine("ckan repo - Manage CKAN repositories");
-                ht.AddPreOptionsLine($"Usage: ckan repo <command> [options]");
+                ht.AddPreOptionsLine($"ckan repo - {Properties.Resources.RepoHelpSummary}");
+                ht.AddPreOptionsLine($"{Properties.Resources.Usage}: ckan repo <{Properties.Resources.Command}> [{Properties.Resources.Options}]");
             }
             else
             {
@@ -44,21 +44,21 @@ public string GetUsage(string verb)
                 {
                     // First the commands with two arguments
                     case "add":
-                        ht.AddPreOptionsLine($"Usage: ckan repo {verb} [options] name url");
+                        ht.AddPreOptionsLine($"{Properties.Resources.Usage}: ckan repo {verb} [{Properties.Resources.Options}] name url");
                         break;
 
                     // Then the commands with one argument
                     case "remove":
                     case "forget":
                     case "default":
-                        ht.AddPreOptionsLine($"Usage: ckan repo {verb} [options] name");
+                        ht.AddPreOptionsLine($"{Properties.Resources.Usage}: ckan repo {verb} [{Properties.Resources.Options}] name");
                         break;
 
                     // Now the commands with only --flag type options
                     case "available":
                     case "list":
                     default:
-                        ht.AddPreOptionsLine($"Usage: ckan repo {verb} [options]");
+                        ht.AddPreOptionsLine($"{Properties.Resources.Usage}: ckan repo {verb} [{Properties.Resources.Options}]");
                         break;
                 }
             }
@@ -148,7 +148,7 @@ public int RunSubCommand(GameInstanceManager manager, CommonOptions opts, SubCom
                             break;
 
                         default:
-                            User.RaiseMessage("Unknown command: repo {0}", option);
+                            User.RaiseMessage(Properties.Resources.RepoUnknownCommand, option);
                             exitCode = Exit.BADOPT;
                             break;
                     }
@@ -170,7 +170,7 @@ private RepositoryList FetchMasterRepositoryList(Uri master_uri = null)
 
         private int AvailableRepositories()
         {
-            User.RaiseMessage("Listing all (canonical) available CKAN repositories:");
+            User.RaiseMessage(Properties.Resources.RepoAvailableHeader);
             RepositoryList repositories;
 
             try
@@ -179,7 +179,7 @@ private int AvailableRepositories()
             }
             catch
             {
-                User.RaiseError("Couldn't fetch CKAN repositories master list from {0}", MainClass.GetGameInstance(Manager).game.RepositoryListURL.ToString());
+                User.RaiseError(Properties.Resources.RepoAvailableFailed, MainClass.GetGameInstance(Manager).game.RepositoryListURL.ToString());
                 return Exit.ERROR;
             }
 
@@ -200,7 +200,7 @@ private int AvailableRepositories()
         private int ListRepositories()
         {
             var manager = RegistryManager.Instance(MainClass.GetGameInstance(Manager));
-            User.RaiseMessage("Listing all known repositories:");
+            User.RaiseMessage(Properties.Resources.RepoListHeader);
             SortedDictionary<string, Repository> repositories = manager.registry.Repositories;
 
             int maxNameLen = 0;
@@ -223,7 +223,7 @@ private int AddRepository(RepoAddOptions options)
 
             if (options.name == null)
             {
-                User.RaiseMessage("add <name> [ <uri> ] - argument missing, perhaps you forgot it?");
+                User.RaiseMessage("add <name> [ <uri> ] - {0}", Properties.Resources.ArgumentMissing);
                 return Exit.BADOPT;
             }
 
@@ -237,7 +237,7 @@ private int AddRepository(RepoAddOptions options)
                 }
                 catch
                 {
-                    User.RaiseError("Couldn't fetch CKAN repositories master list from {0}", Manager.CurrentInstance.game.RepositoryListURL.ToString());
+                    User.RaiseError(Properties.Resources.RepoAvailableFailed, Manager.CurrentInstance.game.RepositoryListURL.ToString());
                     return Exit.ERROR;
                 }
 
@@ -253,7 +253,7 @@ private int AddRepository(RepoAddOptions options)
                 // Nothing found in the master list?
                 if (options.uri == null)
                 {
-                    User.RaiseMessage("Name {0} not found in master list, please provide name and uri.", options.name);
+                    User.RaiseMessage(Properties.Resources.RepoAddNotFound, options.name);
                     return Exit.BADOPT;
                 }
             }
@@ -263,13 +263,13 @@ private int AddRepository(RepoAddOptions options)
 
             if (repositories.ContainsKey(options.name))
             {
-                User.RaiseMessage("Repository with name \"{0}\" already exists, aborting..", options.name);
+                User.RaiseMessage(Properties.Resources.RepoAddDuplicate, options.name);
                 return Exit.BADOPT;
             }
 
             repositories.Add(options.name, new Repository(options.name, options.uri));
 
-            User.RaiseMessage("Added repository '{0}' - '{1}'", options.name, options.uri);
+            User.RaiseMessage(Properties.Resources.RepoAdded, options.name, options.uri);
             manager.Save();
 
             return Exit.OK;
@@ -279,7 +279,7 @@ private int ForgetRepository(RepoForgetOptions options)
         {
             if (options.name == null)
             {
-                User.RaiseError("forget <name> - argument missing, perhaps you forgot it?");
+                User.RaiseError("forget <name> - {0}", Properties.Resources.ArgumentMissing);
                 return Exit.BADOPT;
             }
 
@@ -295,14 +295,14 @@ private int ForgetRepository(RepoForgetOptions options)
                 name = repos.Keys.FirstOrDefault(repo => repo.Equals(options.name, StringComparison.OrdinalIgnoreCase));
                 if (name == null)
                 {
-                    User.RaiseMessage("Couldn't find repository with name \"{0}\", aborting..", options.name);
+                    User.RaiseMessage(Properties.Resources.RepoForgetNotFound, options.name);
                     return Exit.BADOPT;
                 }
-                User.RaiseMessage("Removing insensitive match \"{0}\"", name);
+                User.RaiseMessage(Properties.Resources.RepoForgetRemoving, name);
             }
 
             registry.Repositories.Remove(name);
-            User.RaiseMessage("Successfully removed \"{0}\"", options.name);
+            User.RaiseMessage(Properties.Resources.RepoForgetRemoved, options.name);
             manager.Save();
 
             return Exit.OK;
@@ -314,7 +314,7 @@ private int DefaultRepository(RepoDefaultOptions options)
 
             if (options.uri == null)
             {
-                User.RaiseMessage("default <uri> - argument missing, perhaps you forgot it?");
+                User.RaiseMessage("default <uri> - {0}", Properties.Resources.ArgumentMissing);
                 return Exit.BADOPT;
             }
 
@@ -329,7 +329,7 @@ private int DefaultRepository(RepoDefaultOptions options)
             repositories.Add(Repository.default_ckan_repo_name, new Repository(
                     Repository.default_ckan_repo_name, options.uri));
 
-            User.RaiseMessage("Set {0} repository to '{1}'", Repository.default_ckan_repo_name, options.uri);
+            User.RaiseMessage(Properties.Resources.RepoSet, Repository.default_ckan_repo_name, options.uri);
             manager.Save();
 
             return Exit.OK;
diff --git a/Cmdline/Action/Search.cs b/Cmdline/Action/Search.cs
index 5fc4d40c95..508481669c 100644
--- a/Cmdline/Action/Search.cs
+++ b/Cmdline/Action/Search.cs
@@ -22,8 +22,7 @@ public int RunCommand(CKAN.GameInstance ksp, object raw_options)
             // Check the input.
             if (String.IsNullOrWhiteSpace(options.search_term) && String.IsNullOrWhiteSpace(options.author_term))
             {
-                user.RaiseError("No search term?");
-
+                user.RaiseError(Properties.Resources.SearchNoTerm);
                 return Exit.BADOPT;
             }
 
@@ -37,7 +36,7 @@ public int RunCommand(CKAN.GameInstance ksp, object raw_options)
             // Show how many matches we have.
             if (options.all && !String.IsNullOrWhiteSpace(options.author_term))
             {
-                user.RaiseMessage("Found {0} compatible and {1} incompatible mods matching \"{2}\" by \"{3}\".",
+                user.RaiseMessage(Properties.Resources.SearchFoundByAuthorWithIncompat,
                     matching_compatible.Count().ToString(),
                     matching_incompatible.Count().ToString(),
                     options.search_term,
@@ -45,21 +44,21 @@ public int RunCommand(CKAN.GameInstance ksp, object raw_options)
             }
             else if (options.all && String.IsNullOrWhiteSpace(options.author_term))
             {
-                user.RaiseMessage("Found {0} compatible and {1} incompatible mods matching \"{2}\".",
+                user.RaiseMessage(Properties.Resources.SearchFoundWithIncompat,
                     matching_compatible.Count().ToString(),
                     matching_incompatible.Count().ToString(),
                     options.search_term);
             }
             else if (!options.all && !String.IsNullOrWhiteSpace(options.author_term))
             {
-                user.RaiseMessage("Found {0} compatible mods matching \"{1}\" by \"{2}\".",
+                user.RaiseMessage(Properties.Resources.SearchFoundByAuthor,
                     matching_compatible.Count().ToString(),
                     options.search_term,
                     options.author_term);
             }
             else if (!options.all && String.IsNullOrWhiteSpace(options.author_term))
             {
-                user.RaiseMessage("Found {0} compatible mods matching \"{1}\".",
+                user.RaiseMessage(Properties.Resources.SearchFound,
                     matching_compatible.Count().ToString(),
                     options.search_term);
             }
@@ -72,10 +71,10 @@ public int RunCommand(CKAN.GameInstance ksp, object raw_options)
 
             if (options.detail)
             {
-                user.RaiseMessage("Matching compatible mods:");
+                user.RaiseMessage(Properties.Resources.SearchCompatibleModsHeader);
                 foreach (CkanModule mod in matching_compatible)
                 {
-                    user.RaiseMessage("* {0} ({1}) - {2} by {3} - {4}",
+                    user.RaiseMessage(Properties.Resources.SearchCompatibleMod,
                         mod.identifier,
                         mod.version,
                         mod.name,
@@ -85,13 +84,13 @@ public int RunCommand(CKAN.GameInstance ksp, object raw_options)
 
                 if (matching_incompatible.Any())
                 {
-                    user.RaiseMessage("Matching incompatible mods:");
+                    user.RaiseMessage(Properties.Resources.SearchIncompatibleModsHeader);
                     foreach (CkanModule mod in matching_incompatible)
                     {
                         Registry.GetMinMaxVersions(new List<CkanModule> { mod } , out _, out _, out var minKsp, out var maxKsp);
                         string GameVersion = Versioning.GameVersionRange.VersionSpan(ksp.game, minKsp, maxKsp).ToString();
 
-                        user.RaiseMessage("* {0} ({1} - {2}) - {3} by {4} - {5}",
+                        user.RaiseMessage(Properties.Resources.SearchIncompatibleMod,
                             mod.identifier,
                             mod.version,
                             GameVersion,
diff --git a/Cmdline/Action/Show.cs b/Cmdline/Action/Show.cs
index 7bd6e4cce0..a4364cf5f5 100644
--- a/Cmdline/Action/Show.cs
+++ b/Cmdline/Action/Show.cs
@@ -14,19 +14,19 @@ public Show(IUser user)
             this.user = user;
         }
 
-        public int RunCommand(CKAN.GameInstance ksp, object raw_options)
+        public int RunCommand(CKAN.GameInstance instance, object raw_options)
         {
             ShowOptions options = (ShowOptions) raw_options;
             if (options.modules == null || options.modules.Count < 1)
             {
                 // empty argument
-                user.RaiseMessage("show <module> - module name argument missing, perhaps you forgot it?");
+                user.RaiseMessage("show <module> - {0}", Properties.Resources.ArgumentMissing);
                 return Exit.BADOPT;
             }
 
             int combined_exit_code = Exit.OK;
             // Check installed modules for an exact match.
-            var registry = RegistryManager.Instance(ksp).registry;
+            var registry = RegistryManager.Instance(instance).registry;
             foreach (string modName in options.modules)
             {
                 var installedModuleToShow = registry.InstalledModule(modName);
@@ -39,7 +39,7 @@ public int RunCommand(CKAN.GameInstance ksp, object raw_options)
                     );
                     if (options.with_versions)
                     {
-                        ShowVersionTable(ksp, registry.AvailableByIdentifier(installedModuleToShow.identifier).ToList());
+                        ShowVersionTable(instance, registry.AvailableByIdentifier(installedModuleToShow.identifier).ToList());
                     }
                     user.RaiseMessage("");
                     continue;
@@ -48,7 +48,7 @@ public int RunCommand(CKAN.GameInstance ksp, object raw_options)
                 // Module was not installed, look for an exact match in the available modules,
                 // either by "name" (the user-friendly display name) or by identifier
                 CkanModule moduleToShow = registry
-                                          .CompatibleModules(ksp.VersionCriteria())
+                                          .CompatibleModules(instance.VersionCriteria())
                                           .SingleOrDefault(
                                                 mod => mod.name       == modName
                                                     || mod.identifier == modName
@@ -56,22 +56,20 @@ public int RunCommand(CKAN.GameInstance ksp, object raw_options)
                 if (moduleToShow == null)
                 {
                     // No exact match found. Try to look for a close match for this KSP version.
-                    user.RaiseMessage(
-                        "{0} not installed or compatible with {1} {2}.",
+                    user.RaiseMessage(Properties.Resources.ShowNotInstalledOrCompatible,
                         modName,
-                        ksp.game.ShortName,
-                        string.Join(", ", ksp.VersionCriteria().Versions.Select(v => v.ToString()))
-                    );
-                    user.RaiseMessage("Looking for close matches in compatible mods...");
+                        instance.game.ShortName,
+                        string.Join(", ", instance.VersionCriteria().Versions.Select(v => v.ToString())));
+                    user.RaiseMessage(Properties.Resources.ShowLookingForClose);
 
                     Search search = new Search(user);
-                    var matches = search.PerformSearch(ksp, modName);
+                    var matches = search.PerformSearch(instance, modName);
 
                     // Display the results of the search.
                     if (!matches.Any())
                     {
                         // No matches found.
-                        user.RaiseMessage("No close matches found.");
+                        user.RaiseMessage(Properties.Resources.ShowNoClose);
                         combined_exit_code = CombineExitCodes(combined_exit_code, Exit.BADOPT);
                         user.RaiseMessage("");
                         continue;
@@ -79,7 +77,7 @@ public int RunCommand(CKAN.GameInstance ksp, object raw_options)
                     else if (matches.Count() == 1)
                     {
                         // If there is only 1 match, display it.
-                        user.RaiseMessage("Found 1 close match: {0}", matches[0].name);
+                        user.RaiseMessage(Properties.Resources.ShowFoundOne, matches[0].name);
                         user.RaiseMessage("");
 
                         moduleToShow = matches[0];
@@ -88,9 +86,8 @@ public int RunCommand(CKAN.GameInstance ksp, object raw_options)
                     {
                         // Display the found close matches.
                         int selection = user.RaiseSelectionDialog(
-                            "Close matches:",
-                            matches.Select(m => m.name).ToArray()
-                        );
+                            Properties.Resources.ShowClosePrompt,
+                            matches.Select(m => m.name).ToArray());
                         user.RaiseMessage("");
                         if (selection < 0)
                         {
@@ -109,7 +106,7 @@ public int RunCommand(CKAN.GameInstance ksp, object raw_options)
                 );
                 if (options.with_versions)
                 {
-                    ShowVersionTable(ksp, registry.AvailableByIdentifier(moduleToShow.identifier).ToList());
+                    ShowVersionTable(instance, registry.AvailableByIdentifier(moduleToShow.identifier).ToList());
                 }
                 user.RaiseMessage("");
             }
@@ -136,7 +133,7 @@ private int ShowMod(InstalledModule module, ShowOptions opts)
                 }
 
                 user.RaiseMessage("");
-                user.RaiseMessage("Showing {0} installed files:", files.Count);
+                user.RaiseMessage(Properties.Resources.ShowFilesHeader, files.Count);
                 foreach (string file in files)
                 {
                     user.RaiseMessage("  - {0}", file);
@@ -177,33 +174,33 @@ private int ShowMod(CkanModule module, ShowOptions opts)
             {
                 #region General info (author, version...)
                 user.RaiseMessage("");
-                user.RaiseMessage("Module info:");
-                user.RaiseMessage("  Version:\t{0}", module.version);
+                user.RaiseMessage(Properties.Resources.ShowModuleInfoHeader);
+                user.RaiseMessage(Properties.Resources.ShowVersion, module.version);
                 
                 if (module.author != null)
                 {
-                    user.RaiseMessage("  Authors:\t{0}", string.Join(", ", module.author));
+                    user.RaiseMessage(Properties.Resources.ShowAuthor, string.Join(", ", module.author));
                 }
                 else
                 {
                     // Did you know that authors are optional in the spec?
                     // You do now. #673.
-                    user.RaiseMessage("  Authors:\tUNKNOWN");
+                    user.RaiseMessage(Properties.Resources.ShowAuthorUnknown);
                 }
                 
                 if (module.release_status != null)
                 {
-                    user.RaiseMessage("  Status:\t{0}", module.release_status);
+                    user.RaiseMessage(Properties.Resources.ShowStatus, module.release_status);
                 }
-                user.RaiseMessage("  License:\t{0}", string.Join(", ", module.license));
+                user.RaiseMessage(Properties.Resources.ShowLicence, string.Join(", ", module.license));
                 if (module.Tags != null && module.Tags.Count > 0)
                 {
                     // Need an extra space before the tab to line up with other fields
-                    user.RaiseMessage("  Tags: \t{0}", string.Join(", ", module.Tags));
+                    user.RaiseMessage(Properties.Resources.ShowTags, string.Join(", ", module.Tags));
                 }
                 if (module.localizations != null && module.localizations.Length > 0)
                 {
-                    user.RaiseMessage("  Languages:\t{0}", string.Join(", ", module.localizations.OrderBy(l => l)));
+                    user.RaiseMessage(Properties.Resources.ShowLanguages, string.Join(", ", module.localizations.OrderBy(l => l)));
                 }
                 #endregion
             }
@@ -214,7 +211,7 @@ private int ShowMod(CkanModule module, ShowOptions opts)
                 if (module.depends != null && module.depends.Count > 0)
                 {
                     user.RaiseMessage("");
-                    user.RaiseMessage("Depends:");
+                    user.RaiseMessage(Properties.Resources.ShowDependsHeader);
                     foreach (RelationshipDescriptor dep in module.depends)
                     {
                         user.RaiseMessage("  - {0}", RelationshipToPrintableString(dep));
@@ -224,7 +221,7 @@ private int ShowMod(CkanModule module, ShowOptions opts)
                 if (module.recommends != null && module.recommends.Count > 0)
                 {
                     user.RaiseMessage("");
-                    user.RaiseMessage("Recommends:");
+                    user.RaiseMessage(Properties.Resources.ShowRecommendsHeader);
                     foreach (RelationshipDescriptor dep in module.recommends)
                     {
                         user.RaiseMessage("  - {0}", RelationshipToPrintableString(dep));
@@ -234,7 +231,7 @@ private int ShowMod(CkanModule module, ShowOptions opts)
                 if (module.suggests != null && module.suggests.Count > 0)
                 {
                     user.RaiseMessage("");
-                    user.RaiseMessage("Suggests:");
+                    user.RaiseMessage(Properties.Resources.ShowSuggestsHeader);
                     foreach (RelationshipDescriptor dep in module.suggests)
                     {
                         user.RaiseMessage("  - {0}", RelationshipToPrintableString(dep));
@@ -244,7 +241,7 @@ private int ShowMod(CkanModule module, ShowOptions opts)
                 if (module.provides != null && module.provides.Count > 0)
                 {
                     user.RaiseMessage("");
-                    user.RaiseMessage("Provides:");
+                    user.RaiseMessage(Properties.Resources.ShowProvidesHeader);
                     foreach (string prov in module.provides)
                     {
                         user.RaiseMessage("  - {0}", prov);
@@ -256,42 +253,42 @@ private int ShowMod(CkanModule module, ShowOptions opts)
             if (!opts.without_resources && module.resources != null)
             {
                 user.RaiseMessage("");
-                user.RaiseMessage("Resources:");
+                user.RaiseMessage(Properties.Resources.ShowResourcesHeader);
                 if (module.resources.homepage != null)
                 {
-                    user.RaiseMessage("  Home page:\t{0}", Uri.EscapeUriString(module.resources.homepage.ToString()));
+                    user.RaiseMessage(Properties.Resources.ShowHomePage, Uri.EscapeUriString(module.resources.homepage.ToString()));
                 }
                 if (module.resources.manual != null)
                 {
-                    user.RaiseMessage("  Manual:\t{0}", Uri.EscapeUriString(module.resources.manual.ToString()));
+                    user.RaiseMessage(Properties.Resources.ShowManual, Uri.EscapeUriString(module.resources.manual.ToString()));
                 }
                 if (module.resources.spacedock != null)
                 {
-                    user.RaiseMessage("  SpaceDock:\t{0}", Uri.EscapeUriString(module.resources.spacedock.ToString()));
+                    user.RaiseMessage(Properties.Resources.ShowSpaceDock, Uri.EscapeUriString(module.resources.spacedock.ToString()));
                 }
                 if (module.resources.repository != null)
                 {
-                    user.RaiseMessage("  Repository:\t{0}", Uri.EscapeUriString(module.resources.repository.ToString()));
+                    user.RaiseMessage(Properties.Resources.ShowRepository, Uri.EscapeUriString(module.resources.repository.ToString()));
                 }
                 if (module.resources.bugtracker != null)
                 {
-                    user.RaiseMessage("  Bug tracker:\t{0}", Uri.EscapeUriString(module.resources.bugtracker.ToString()));
+                    user.RaiseMessage(Properties.Resources.ShowBugTracker, Uri.EscapeUriString(module.resources.bugtracker.ToString()));
                 }
                 if (module.resources.curse != null)
                 {
-                    user.RaiseMessage("  Curse:\t{0}", Uri.EscapeUriString(module.resources.curse.ToString()));
+                    user.RaiseMessage(Properties.Resources.ShowCurse, Uri.EscapeUriString(module.resources.curse.ToString()));
                 }
                 if (module.resources.store != null)
                 {
-                    user.RaiseMessage("  Store:\t{0}", Uri.EscapeUriString(module.resources.store.ToString()));
+                    user.RaiseMessage(Properties.Resources.ShowStore, Uri.EscapeUriString(module.resources.store.ToString()));
                 }
                 if (module.resources.steamstore != null)
                 {
-                    user.RaiseMessage("  Steam store:\t{0}", Uri.EscapeUriString(module.resources.steamstore.ToString()));
+                    user.RaiseMessage(Properties.Resources.ShowSteamStore, Uri.EscapeUriString(module.resources.steamstore.ToString()));
                 }
                 if (module.resources.remoteAvc != null)
                 {
-                    user.RaiseMessage("  Version file:\t{0}", Uri.EscapeUriString(module.resources.remoteAvc.ToString()));
+                    user.RaiseMessage(Properties.Resources.ShowVersionFile, Uri.EscapeUriString(module.resources.remoteAvc.ToString()));
                 }
             }
 
@@ -302,7 +299,7 @@ private int ShowMod(CkanModule module, ShowOptions opts)
                 string file_name = CkanModule.StandardName(module.identifier, module.version);
                 
                 user.RaiseMessage("");
-                user.RaiseMessage("Filename: {0}", file_uri_hash + "-" + file_name);
+                user.RaiseMessage(Properties.Resources.ShowFileName, file_uri_hash + "-" + file_name);
             }
 
             return Exit.OK;
@@ -323,7 +320,10 @@ private void ShowVersionTable(CKAN.GameInstance inst, List<CkanModule> modules)
                 Registry.GetMinMaxVersions(new List<CkanModule>() { m }, out _, out _, out minKsp, out maxKsp);
                 return GameVersionRange.VersionSpan(inst.game, minKsp, maxKsp);
             }).ToList();
-            string[] headers = new string[] { "Version", "Game Versions" };
+            string[] headers = new string[] {
+                Properties.Resources.ShowVersionHeader,
+                Properties.Resources.ShowGameVersionsHeader
+            };
             int versionLength     = Math.Max(headers[0].Length, versions.Max(v => v.Length));
             int gameVersionLength = Math.Max(headers[1].Length, gameVersions.Max(v => v.Length));
             user.RaiseMessage("");
diff --git a/Cmdline/Action/Update.cs b/Cmdline/Action/Update.cs
index cbb8d5bd8b..7bb669663a 100644
--- a/Cmdline/Action/Update.cs
+++ b/Cmdline/Action/Update.cs
@@ -22,12 +22,12 @@ public Update(GameInstanceManager mgr, IUser user)
         /// <summary>
         /// Update the registry
         /// </summary>
-        /// <param name="ksp">Game instance to update</param>
+        /// <param name="instance">Game instance to update</param>
         /// <param name="raw_options">Command line options object</param>
         /// <returns>
         /// Exit code for shell environment
         /// </returns>
-        public int RunCommand(CKAN.GameInstance ksp, object raw_options)
+        public int RunCommand(CKAN.GameInstance instance, object raw_options)
         {
             UpdateOptions options = (UpdateOptions) raw_options;
 
@@ -36,8 +36,8 @@ public int RunCommand(CKAN.GameInstance ksp, object raw_options)
             if (options.list_changes)
             {
                 // Get a list of compatible modules prior to the update.
-                var registry = RegistryManager.Instance(ksp).registry;
-                compatible_prior = registry.CompatibleModules(ksp.VersionCriteria()).ToList();
+                var registry = RegistryManager.Instance(instance).registry;
+                compatible_prior = registry.CompatibleModules(instance.VersionCriteria()).ToList();
             }
 
             // If no repository is selected, select all.
@@ -50,16 +50,16 @@ public int RunCommand(CKAN.GameInstance ksp, object raw_options)
             {
                 if (options.update_all)
                 {
-                    UpdateRepository(ksp);
+                    UpdateRepository(instance);
                 }
                 else
                 {
-                    UpdateRepository(ksp, options.repo);
+                    UpdateRepository(instance, options.repo);
                 }
             }
             catch (ReinstallModuleKraken rmk)
             {
-                Upgrade.UpgradeModules(manager, user, ksp, false, rmk.Modules);
+                Upgrade.UpgradeModules(manager, user, instance, false, rmk.Modules);
             }
             catch (MissingCertificateKraken kraken)
             {
@@ -70,8 +70,8 @@ public int RunCommand(CKAN.GameInstance ksp, object raw_options)
 
             if (options.list_changes)
             {
-                var registry = RegistryManager.Instance(ksp).registry;
-                PrintChanges(compatible_prior, registry.CompatibleModules(ksp.VersionCriteria()).ToList());
+                var registry = RegistryManager.Instance(instance).registry;
+                PrintChanges(compatible_prior, registry.CompatibleModules(instance.VersionCriteria()).ToList());
             }
 
             return Exit.OK;
@@ -92,25 +92,26 @@ private void PrintChanges(List<CkanModule> modules_prior, List<CkanModule> modul
             var removed = new HashSet<CkanModule>(prior.Except(post, new NameComparer()));
 
 
-            var unchanged = post.Intersect(prior);//Default compare includes versions
+            // Default compare includes versions
+            var unchanged = post.Intersect(prior);
             var updated = post.Except(unchanged).Except(added).Except(removed).ToList();
 
             // Print the changes.
-            user.RaiseMessage("Found {0} new modules, {1} removed modules and {2} updated modules.", added.Count(), removed.Count(), updated.Count());
+            user.RaiseMessage(Properties.Resources.UpdateChangesSummary, added.Count(), removed.Count(), updated.Count());
 
             if (added.Count > 0)
             {
-                PrintModules("New modules [Name (CKAN identifier)]:", added);
+                PrintModules(Properties.Resources.UpdateAddedHeader, added);
             }
 
             if (removed.Count > 0)
             {
-                PrintModules("Removed modules [Name (CKAN identifier)]:", removed);
+                PrintModules(Properties.Resources.UpdateRemovedHeader, removed);
             }
 
             if (updated.Count > 0)
             {
-                PrintModules("Updated modules [Name (CKAN identifier)]:", updated);
+                PrintModules(Properties.Resources.UpdateUpdatedHeader, updated);
             }
         }
 
@@ -145,17 +146,17 @@ private void PrintModules(string message, IEnumerable<CkanModule> modules)
         /// <summary>
         /// Updates the repository.
         /// </summary>
-        /// <param name="ksp">The KSP instance to work on.</param>
+        /// <param name="instance">The KSP instance to work on.</param>
         /// <param name="repository">Repository to update. If null all repositories are used.</param>
-        private void UpdateRepository(CKAN.GameInstance ksp, string repository = null)
+        private void UpdateRepository(CKAN.GameInstance instance, string repository = null)
         {
-            RegistryManager registry_manager = RegistryManager.Instance(ksp);
+            RegistryManager registry_manager = RegistryManager.Instance(instance);
 
             var updated = repository == null
-                ? CKAN.Repo.UpdateAllRepositories(registry_manager, ksp, manager.Cache, user) != CKAN.RepoUpdateResult.Failed
-                : CKAN.Repo.Update(registry_manager, ksp, user, repository);
+                ? CKAN.Repo.UpdateAllRepositories(registry_manager, instance, manager.Cache, user) != CKAN.RepoUpdateResult.Failed
+                : CKAN.Repo.Update(registry_manager, instance, user, repository);
 
-            user.RaiseMessage("Updated information on {0} compatible modules", registry_manager.registry.CompatibleModules(ksp.VersionCriteria()).Count());
+            user.RaiseMessage(Properties.Resources.UpdateSummary, registry_manager.registry.CompatibleModules(instance.VersionCriteria()).Count());
         }
     }
 }
diff --git a/Cmdline/Action/Upgrade.cs b/Cmdline/Action/Upgrade.cs
index 246a65ecd0..e359c5689e 100644
--- a/Cmdline/Action/Upgrade.cs
+++ b/Cmdline/Action/Upgrade.cs
@@ -21,31 +21,31 @@ public class Upgrade : ICommand
         /// <param name="user">IUser object for interaction</param>
         public Upgrade(GameInstanceManager mgr, IUser user)
         {
-            manager   = mgr;
-            User = user;
+            manager = mgr;
+            User    = user;
         }
 
         /// <summary>
         /// Upgrade an installed module
         /// </summary>
-        /// <param name="ksp">Game instance from which to remove</param>
+        /// <param name="instance">Game instance from which to remove</param>
         /// <param name="raw_options">Command line options object</param>
         /// <returns>
         /// Exit code for shell environment
         /// </returns>
-        public int RunCommand(CKAN.GameInstance ksp, object raw_options)
+        public int RunCommand(CKAN.GameInstance instance, object raw_options)
         {
             UpgradeOptions options = (UpgradeOptions) raw_options;
 
             if (options.ckan_file != null)
             {                
-                options.modules.Add(MainClass.LoadCkanFromFile(ksp, options.ckan_file).identifier);
+                options.modules.Add(MainClass.LoadCkanFromFile(instance, options.ckan_file).identifier);
             }
 
             if (options.modules.Count == 0 && !options.upgrade_all)
             {
                 // What? No files specified?
-                User.RaiseMessage("Usage: ckan upgrade Mod [Mod2, ...]");
+                User.RaiseMessage("{0}: ckan upgrade Mod [Mod2, ...]", Properties.Resources.Usage);
                 User.RaiseMessage("  or   ckan upgrade --all");
                 if (AutoUpdate.CanUpdate)
                 {
@@ -56,27 +56,28 @@ public int RunCommand(CKAN.GameInstance ksp, object raw_options)
 
             if (!options.upgrade_all && options.modules[0] == "ckan" && AutoUpdate.CanUpdate)
             {
-                User.RaiseMessage("Querying the latest CKAN version");
+                User.RaiseMessage(Properties.Resources.UpgradeQueryingCKAN);
                 AutoUpdate.Instance.FetchLatestReleaseInfo();
                 var latestVersion = AutoUpdate.Instance.latestUpdate.Version;
                 var currentVersion = new ModuleVersion(Meta.GetVersion(VersionFormat.Short));
 
                 if (latestVersion.IsGreaterThan(currentVersion))
                 {
-                    User.RaiseMessage("New CKAN version available - " + latestVersion);
+                    User.RaiseMessage(Properties.Resources.UpgradeNewCKANAvailable, latestVersion);
                     var releaseNotes = AutoUpdate.Instance.latestUpdate.ReleaseNotes;
                     User.RaiseMessage(releaseNotes);
-                    User.RaiseMessage("\r\n");
+                    User.RaiseMessage("");
+                    User.RaiseMessage("");
 
-                    if (User.RaiseYesNoDialog("Proceed with install?"))
+                    if (User.RaiseYesNoDialog(Properties.Resources.UpgradeProceed))
                     {
-                        User.RaiseMessage("Upgrading CKAN, please wait..");
+                        User.RaiseMessage(Properties.Resources.UpgradePleaseWait);
                         AutoUpdate.Instance.StartUpdateProcess(false);
                     }
                 }
                 else
                 {
-                    User.RaiseMessage("You already have the latest version.");
+                    User.RaiseMessage(Properties.Resources.UpgradeAlreadyHaveLatest);
                 }
 
                 return Exit.OK;
@@ -84,7 +85,7 @@ public int RunCommand(CKAN.GameInstance ksp, object raw_options)
 
             try
             {
-                var regMgr = RegistryManager.Instance(ksp);
+                var regMgr = RegistryManager.Instance(instance);
                 var registry = regMgr.registry;
                 if (options.upgrade_all)
                 {
@@ -95,7 +96,7 @@ public int RunCommand(CKAN.GameInstance ksp, object raw_options)
                         try
                         {
                             // Check if upgrades are available
-                            var latest = registry.LatestAvailable(mod.Key, ksp.VersionCriteria());
+                            var latest = registry.LatestAvailable(mod.Key, instance.VersionCriteria());
 
                             // This may be an unindexed mod. If so,
                             // skip rather than crash. See KSP-CKAN/CKAN#841.
@@ -119,23 +120,23 @@ public int RunCommand(CKAN.GameInstance ksp, object raw_options)
                                 mod.Key);
                         }
                     }
-                    UpgradeModules(manager, User, ksp, true, to_upgrade);
+                    UpgradeModules(manager, User, instance, true, to_upgrade);
                 }
                 else
                 {
-                    Search.AdjustModulesCase(ksp, options.modules);
-                    UpgradeModules(manager, User, ksp, options.modules);
+                    Search.AdjustModulesCase(instance, options.modules);
+                    UpgradeModules(manager, User, instance, options.modules);
                 }
                 User.RaiseMessage("");
             }
             catch (CancelledActionKraken k)
             {
-                User.RaiseMessage("Upgrade aborted: {0}", k.Message);
+                User.RaiseMessage(Properties.Resources.UpgradeAborted, k.Message);
                 return Exit.ERROR;
             }
             catch (ModuleNotFoundKraken kraken)
             {
-                User.RaiseMessage("Module {0} not found", kraken.module);
+                User.RaiseMessage(Properties.Resources.UpgradeNotFound, kraken.module);
                 return Exit.ERROR;
             }
             catch (InconsistentKraken kraken)
@@ -145,14 +146,14 @@ public int RunCommand(CKAN.GameInstance ksp, object raw_options)
             }
             catch (ModuleIsDLCKraken kraken)
             {
-                User.RaiseMessage($"CKAN can't upgrade expansion '{kraken.module.name}' for you.");
+                User.RaiseMessage(Properties.Resources.UpgradeDLC, kraken.module.name);
                 var res = kraken?.module?.resources;
                 var storePagesMsg = new Uri[] { res?.store, res?.steamstore }
                     .Where(u => u != null)
                     .Aggregate("", (a, b) => $"{a}\r\n- {b}");
                 if (!string.IsNullOrEmpty(storePagesMsg))
                 {
-                    User.RaiseMessage($"To upgrade this expansion, download any updates from the store page from which you purchased it:\r\n{storePagesMsg}");
+                    User.RaiseMessage(Properties.Resources.UpgradeDLCStorePage, storePagesMsg);
                 }
                 return Exit.ERROR;
             }
@@ -165,11 +166,11 @@ public int RunCommand(CKAN.GameInstance ksp, object raw_options)
         /// </summary>
         /// <param name="manager">Game instance manager to use</param>
         /// <param name="user">IUser object for output</param>
-        /// <param name="ksp">Game instance to use</param>
+        /// <param name="instance">Game instance to use</param>
         /// <param name="modules">List of modules to upgrade</param>
-        public static void UpgradeModules(GameInstanceManager manager, IUser user, CKAN.GameInstance ksp, bool ConfirmPrompt, List<CkanModule> modules)
+        public static void UpgradeModules(GameInstanceManager manager, IUser user, CKAN.GameInstance instance, bool ConfirmPrompt, List<CkanModule> modules)
         {
-            UpgradeModules(manager, user, ksp,
+            UpgradeModules(manager, user, instance,
                 (ModuleInstaller installer, NetAsyncModulesDownloader downloader, RegistryManager regMgr, ref HashSet<string> possibleConfigOnlyDirs) =>
                     installer.Upgrade(modules, downloader,
                         ref possibleConfigOnlyDirs, regMgr, true, true, ConfirmPrompt),
@@ -182,11 +183,11 @@ public static void UpgradeModules(GameInstanceManager manager, IUser user, CKAN.
         /// </summary>
         /// <param name="manager">Game instance manager to use</param>
         /// <param name="user">IUser object for output</param>
-        /// <param name="ksp">Game instance to use</param>
+        /// <param name="instance">Game instance to use</param>
         /// <param name="identsAndVersions">List of identifier[=version] to upgrade</param>
-        public static void UpgradeModules(GameInstanceManager manager, IUser user, CKAN.GameInstance ksp, List<string> identsAndVersions)
+        public static void UpgradeModules(GameInstanceManager manager, IUser user, CKAN.GameInstance instance, List<string> identsAndVersions)
         {
-            UpgradeModules(manager, user, ksp,
+            UpgradeModules(manager, user, instance,
                 (ModuleInstaller installer, NetAsyncModulesDownloader downloader, RegistryManager regMgr, ref HashSet<string> possibleConfigOnlyDirs) =>
                     installer.Upgrade(identsAndVersions, downloader,
                         ref possibleConfigOnlyDirs, regMgr, true),
@@ -205,18 +206,18 @@ public static void UpgradeModules(GameInstanceManager manager, IUser user, CKAN.
         /// </summary>
         /// <param name="manager">Game instance manager to use</param>
         /// <param name="user">IUser object for output</param>
-        /// <param name="ksp">Game instance to use</param>
+        /// <param name="instance">Game instance to use</param>
         /// <param name="attemptUpgradeCallback">Function to call to try to perform the actual upgrade, may throw TooManyModsProvideKraken</param>
         /// <param name="addUserChoiceCallback">Function to call when the user has requested a new module added to the change set in response to TooManyModsProvideKraken</param>
         private static void UpgradeModules(
-            GameInstanceManager manager, IUser user, CKAN.GameInstance ksp,
+            GameInstanceManager manager, IUser user, CKAN.GameInstance instance,
             AttemptUpgradeAction attemptUpgradeCallback,
             System.Action<CkanModule> addUserChoiceCallback)
         {
             using (TransactionScope transact = CkanTransaction.CreateTransactionScope()) {
-                var installer  = new ModuleInstaller(ksp, manager.Cache, user);
+                var installer  = new ModuleInstaller(instance, manager.Cache, user);
                 var downloader = new NetAsyncModulesDownloader(user, manager.Cache);
-                var regMgr     = RegistryManager.Instance(ksp);
+                var regMgr     = RegistryManager.Instance(instance);
                 HashSet<string> possibleConfigOnlyDirs = null;
                 bool done = false;
                 while (!done)
diff --git a/Cmdline/CKAN-cmdline.csproj b/Cmdline/CKAN-cmdline.csproj
index daf863e5e8..ecab63f86f 100644
--- a/Cmdline/CKAN-cmdline.csproj
+++ b/Cmdline/CKAN-cmdline.csproj
@@ -1,40 +1,27 @@
-<?xml version="1.0" encoding="utf-8"?>
-<Project ToolsVersion="14.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+<Project>
   <PropertyGroup>
-    <AssemblyName>CmdLine</AssemblyName>
+    <AssemblyName>CKAN-CmdLine</AssemblyName>
     <OutputPath>..\_build\out\$(AssemblyName)\$(Configuration)\bin\</OutputPath>
     <BaseIntermediateOutputPath>..\_build\out\$(AssemblyName)\$(Configuration)\obj\</BaseIntermediateOutputPath>
   </PropertyGroup>
-  <Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
+  <Import Project="Sdk.props" Sdk="Microsoft.NET.Sdk" />
   <PropertyGroup>
-    <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
-    <Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
     <ProjectGuid>{E97D81F6-85E2-4F1F-906D-BE21766602E5}</ProjectGuid>
     <OutputType>Exe</OutputType>
     <RootNamespace>CKAN.CmdLine</RootNamespace>
+    <GenerateAssemblyInfo>false</GenerateAssemblyInfo>
+    <AutoGenerateBindingRedirects>true</AutoGenerateBindingRedirects>
+    <Deterministic>true</Deterministic>
+    <Configurations>Debug;Release</Configurations>
+    <Prefer32Bit>false</Prefer32Bit>
+    <LangVersion>7</LangVersion>
+    <TargetFramework>net45</TargetFramework>
     <StartupObject>CKAN.CmdLine.MainClass</StartupObject>
     <ApplicationIcon>..\assets\ckan.ico</ApplicationIcon>
     <TargetFrameworkVersion>v4.5</TargetFrameworkVersion>
     <FileAlignment>512</FileAlignment>
-    <AutoGenerateBindingRedirects>true</AutoGenerateBindingRedirects>
-    <Deterministic>true</Deterministic>
     <ErrorReport>prompt</ErrorReport>
     <WarningLevel>4</WarningLevel>
-    <Prefer32Bit>false</Prefer32Bit>
-    <LangVersion>7</LangVersion>
-  </PropertyGroup>
-  <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
-    <PlatformTarget>AnyCPU</PlatformTarget>
-    <DebugSymbols>true</DebugSymbols>
-    <DebugType>full</DebugType>
-    <Optimize>false</Optimize>
-    <DefineConstants>DEBUG;TRACE</DefineConstants>
-  </PropertyGroup>
-  <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
-    <PlatformTarget>AnyCPU</PlatformTarget>
-    <DebugType>pdbonly</DebugType>
-    <Optimize>true</Optimize>
-    <DefineConstants>TRACE</DefineConstants>
   </PropertyGroup>
   <ItemGroup>
     <PackageReference Include="Autofac" Version="4.9.4" />
@@ -54,34 +41,6 @@
     <Compile Include="..\GlobalAssemblyInfo.cs">
       <Link>Properties\GlobalAssemblyInfo.cs</Link>
     </Compile>
-    <Compile Include="Action\AuthToken.cs" />
-    <Compile Include="Action\Available.cs" />
-    <Compile Include="Action\Cache.cs" />
-    <Compile Include="Action\Compare.cs" />
-    <Compile Include="Action\Compat.cs" />
-    <Compile Include="Action\Filter.cs" />
-    <Compile Include="Action\ICommand.cs" />
-    <Compile Include="Action\Import.cs" />
-    <Compile Include="Action\Install.cs" />
-    <Compile Include="Action\ISubCommand.cs" />
-    <Compile Include="Action\GameInstance.cs" />
-    <Compile Include="Action\List.cs" />
-    <Compile Include="Action\Mark.cs" />
-    <Compile Include="Action\Prompt.cs" />
-    <Compile Include="Action\Remove.cs" />
-    <Compile Include="Action\Repair.cs" />
-    <Compile Include="Action\Replace.cs" />
-    <Compile Include="Action\Repo.cs" />
-    <Compile Include="Action\Search.cs" />
-    <Compile Include="Action\Show.cs" />
-    <Compile Include="Action\Update.cs" />
-    <Compile Include="Action\Upgrade.cs" />
-    <Compile Include="ConsoleUser.cs" />
-    <Compile Include="Exit.cs" />
-    <Compile Include="Main.cs" />
-    <Compile Include="Options.cs" />
-    <Compile Include="ProgressReporter.cs" />
-    <Compile Include="Properties\AssemblyInfo.cs" />
   </ItemGroup>
   <ItemGroup>
     <None Include="app.config" />
@@ -101,11 +60,12 @@
     </ProjectReference>
   </ItemGroup>
   <ItemGroup>
+    <None Include="app.config" />
     <Content Include="log4net.xml">
       <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
     </Content>
   </ItemGroup>
-  <Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
+  <Import Project="Sdk.targets" Sdk="Microsoft.NET.Sdk" />
   <Target Name="BeforeBuild">
     <Exec Command="powershell ../build.ps1 Generate-GlobalAssemblyVersionInfo" Condition="!Exists('../_build/meta/GlobalAssemblyVersionInfo.cs') And '$(OS)' == 'Windows_NT'" />
     <Exec Command="sh ../build Generate-GlobalAssemblyVersionInfo" Condition="!Exists('../_build/meta/GlobalAssemblyVersionInfo.cs') And '$(OS)' == 'Unix'" />
diff --git a/Cmdline/ConsoleUser.cs b/Cmdline/ConsoleUser.cs
index 4e596a5a9f..a4e7f249fd 100644
--- a/Cmdline/ConsoleUser.cs
+++ b/Cmdline/ConsoleUser.cs
@@ -20,7 +20,7 @@ public class ConsoleUser : IUser
         /// Initializes a new instance of the <see cref="T:CKAN.CmdLine.ConsoleUser"/> class.
         /// </summary>
         /// <param name="headless">If set to <c>true</c>, supress interactive dialogs like Yes/No-Dialog or SelectionDialog</param>
-        public ConsoleUser (bool headless)
+        public ConsoleUser(bool headless)
         {
             Headless = headless;
         }
@@ -42,7 +42,7 @@ public bool RaiseYesNoDialog(string question)
                 return true;
             }
 
-            Console.Write("\r\n{0} [Y/n] ", question);
+            Console.Write("\r\n{0} {1} ", question, Properties.Resources.UserYesNoPromptSuffix);
             while (true)
             {
                 var input = Console.In.ReadLine();
@@ -55,11 +55,11 @@ public bool RaiseYesNoDialog(string question)
 
                 input = input.ToLower().Trim();
 
-                if (input.Equals("y") || input.Equals("yes"))
+                if (input.Equals(Properties.Resources.UserYesNoY) || input.Equals(Properties.Resources.UserYesNoYes))
                 {
                     return true;
                 }
-                if (input.Equals("n") || input.Equals("no"))
+                if (input.Equals(Properties.Resources.UserYesNoN) || input.Equals(Properties.Resources.UserYesNoNo))
                 {
                     return false;
                 }
@@ -69,7 +69,7 @@ public bool RaiseYesNoDialog(string question)
                     return true;
                 }
 
-                Console.Write("Invalid input. Please enter yes or no");
+                Console.Write(Properties.Resources.UserYesNoInvalid);
             }
         }
 
@@ -154,23 +154,16 @@ public int RaiseSelectionDialog(string message, params object[] args)
                 RaiseMessage(CurrentRow);
             }
 
-            // Create message string.
-            string output = String.Format("Enter a number between {0} and {1} (To cancel press \"c\" or \"n\".", 1, args.Length);
-
-            if (defaultSelection >= 0)
-            {
-                output += String.Format(" \"Enter\" will select {0}.", defaultSelection + 1);
-            }
-
-            output += "): ";
-
-            RaiseMessage(output);
-
             bool valid = false;
             int result = 0;
 
             while (!valid)
             {
+                // Print message string
+                RaiseMessage(defaultSelection >= 0
+                    ? string.Format(Properties.Resources.UserSelectionPromptWithDefault, 1, args.Length, defaultSelection + 1)
+                    : string.Format(Properties.Resources.UserSelectionPromptWithoutDefault,1, args.Length));
+
                 // Wait for input from the command line.
                 string input = Console.In.ReadLine();
 
@@ -183,57 +176,47 @@ public int RaiseSelectionDialog(string message, params object[] args)
                 input = input.Trim().ToLower();
 
                 // Check for default selection.
-                if (String.IsNullOrEmpty(input))
+                if (String.IsNullOrEmpty(input) && defaultSelection >= 0)
                 {
-                    if (defaultSelection >= 0)
-                    {
-                        return defaultSelection;
-                    }
+                    return defaultSelection;
                 }
 
                 // Check for cancellation characters.
-                if (input == "c" || input == "n")
+                if (input == Properties.Resources.UserSelectionC || input == Properties.Resources.UserSelectionN)
                 {
-                    RaiseMessage("Selection cancelled.");
-
+                    RaiseMessage(Properties.Resources.UserSelectionCancelled);
                     return return_cancel;
                 }
 
                 // Attempt to parse the input.
                 try
                 {
-                    result = Convert.ToInt32(input);
+                    // The list we provide is index 1 based, but the array is index 0 based.
+                    result = Convert.ToInt32(input) - 1;
                 }
                 catch (FormatException)
                 {
-                    RaiseMessage("The input is not a number.");
+                    RaiseMessage(Properties.Resources.UserSelectionNotNumber);
                     continue;
                 }
                 catch (OverflowException)
                 {
-                    RaiseMessage("The number in the input is too large.");
+                    RaiseMessage(Properties.Resources.UserSelectionTooLarge);
                     continue;
                 }
 
                 // Check the input against the boundaries.
-                if (result > args.Length)
+                if (result > args.Length - 1)
                 {
-                    RaiseMessage("The number in the input is too large.");
-                    RaiseMessage(output);
-
+                    RaiseMessage(Properties.Resources.UserSelectionTooLarge);
                     continue;
                 }
-                else if (result < 1)
+                else if (result < 0)
                 {
-                    RaiseMessage("The number in the input is too small.");
-                    RaiseMessage(output);
-
+                    RaiseMessage(Properties.Resources.UserSelectionTooSmall);
                     continue;
                 }
 
-                // The list we provide is index 1 based, but the array is index 0 based.
-                result--;
-
                 // We have checked for all errors and have gotten a valid result. Stop the input loop.
                 valid = true;
             }
@@ -279,8 +262,7 @@ public void RaiseProgress(string message, int percent)
                 if (!Headless || percent != previousPercent)
                 {
                     // The \r at the front here causes download messages to *overwrite* each other.
-                    Console.Write(
-                        "\r{0} - {1}%           ", message, percent);
+                    Console.Write("\r{0} - {1}%           ", message, percent);
                     previousPercent = percent;
                 }
             }
diff --git a/Cmdline/Main.cs b/Cmdline/Main.cs
index 6b27f97972..69374010b8 100644
--- a/Cmdline/Main.cs
+++ b/Cmdline/Main.cs
@@ -152,7 +152,8 @@ public static int Execute(GameInstanceManager manager, CommonOptions opts, strin
         public static int AfterHelp()
         {
             // Our help screen will already be shown. Let's add some extra data.
-            new ConsoleUser(false).RaiseMessage("You are using CKAN version {0}", Meta.GetVersion(VersionFormat.Full));
+            new ConsoleUser(false).RaiseMessage(
+                Properties.Resources.MainVersion, Meta.GetVersion(VersionFormat.Full));
             return Exit.BADOPT;
         }
 
@@ -234,7 +235,7 @@ private static int RunSimpleAction(Options cmdline, CommonOptions options, strin
                         return (new Compare(user)).RunCommand(cmdline.options);
 
                     default:
-                        user.RaiseMessage("Unknown command, try --help");
+                        user.RaiseMessage(Properties.Resources.MainUnknownCommand);
                         return Exit.BADOPT;
                 }
             }
@@ -266,8 +267,7 @@ internal static CkanModule LoadCkanFromFile(CKAN.GameInstance current_instance,
 
         private static int printMissingInstanceError(IUser user)
         {
-            user.RaiseMessage("I don't know where a game instance is installed.");
-            user.RaiseMessage("Use 'ckan instance help' for assistance in setting this.");
+            user.RaiseMessage(Properties.Resources.MainMissingInstance);
             return Exit.ERROR;
         }
 
@@ -276,7 +276,7 @@ private static int Gui(GameInstanceManager manager, GuiOptions options, string[]
             // TODO: Sometimes when the GUI exits, we get a System.ArgumentException,
             // but trying to catch it here doesn't seem to help. Dunno why.
 
-            GUI.Main_(args, manager, options.ShowConsole);
+            GUI.GUI.Main_(args, manager, options.ShowConsole);
 
             return Exit.OK;
         }
@@ -317,13 +317,11 @@ private static int Scan(CKAN.GameInstance inst, IUser user, string next_command
                 if (next_command == null)
                 {
                     user.RaiseError(kraken.InconsistenciesPretty);
-                    user.RaiseError("The repo has not been saved.");
+                    user.RaiseError(Properties.Resources.ScanNotSaved);
                 }
                 else
                 {
-                    user.RaiseMessage("Preliminary scanning shows that the install is in a inconsistent state.");
-                    user.RaiseMessage("Use ckan.exe scan for more details");
-                    user.RaiseMessage("Proceeding with {0} in case it fixes it.\r\n", next_command);
+                    user.RaiseMessage(Properties.Resources.ScanPreliminaryInconsistent, next_command);
                 }
 
                 return Exit.ERROR;
diff --git a/Cmdline/Options.cs b/Cmdline/Options.cs
index bc9edccfef..8e95bb1d2e 100644
--- a/Cmdline/Options.cs
+++ b/Cmdline/Options.cs
@@ -1,5 +1,6 @@
 using System;
 using System.IO;
+using System.Linq;
 using System.Reflection;
 using System.Collections.Generic;
 using System.Text.RegularExpressions;
@@ -124,40 +125,49 @@ public string GetUsage(string verb)
             HelpText ht = HelpText.AutoBuild(this, verb);
 
             // Add a usage prefix line
-            ht.AddPreOptionsLine(" ");
             if (string.IsNullOrEmpty(verb))
             {
-                ht.AddPreOptionsLine($"Usage: ckan <command> [options]");
+                ht.AddPreOptionsLine(" ");
+                ht.AddPreOptionsLine($"{Properties.Resources.Usage}: ckan <{Properties.Resources.Command}> [{Properties.Resources.Options}]");
             }
             else
             {
-                ht.AddPreOptionsLine(verb + " - " + GetDescription(verb));
+                string descr = GetDescription(verb);
+                if (!string.IsNullOrEmpty(descr))
+                {
+                    ht.AddPreOptionsLine(" ");
+                    ht.AddPreOptionsLine($"ckan {verb} - {descr}");
+                }
                 switch (verb)
                 {
-                    // First the commands that deal with mods
+                    // Commands that don't need a header
+                    case "help":
+                        break;
+
+                    // Commands that deal with mods
                     case "add":
                     case "install":
                     case "remove":
                     case "uninstall":
                     case "upgrade":
-                        ht.AddPreOptionsLine($"Usage: ckan {verb} [options] modules");
+                        ht.AddPreOptionsLine($"{Properties.Resources.Usage}: ckan {verb} [{Properties.Resources.Options}] modules");
                         break;
                     case "show":
-                        ht.AddPreOptionsLine($"Usage: ckan {verb} [options] module");
+                        ht.AddPreOptionsLine($"{Properties.Resources.Usage}: ckan {verb} [{Properties.Resources.Options}] module");
                         break;
 
-                    // Now the commands with other string arguments
+                    // Commands with other string arguments
                     case "search":
-                        ht.AddPreOptionsLine($"Usage: ckan {verb} [options] substring");
+                        ht.AddPreOptionsLine($"{Properties.Resources.Usage}: ckan {verb} [{Properties.Resources.Options}] substring");
                         break;
                     case "compare":
-                        ht.AddPreOptionsLine($"Usage: ckan {verb} [options] version1 version2");
+                        ht.AddPreOptionsLine($"{Properties.Resources.Usage}: ckan {verb} [{Properties.Resources.Options}] version1 version2");
                         break;
                     case "import":
-                        ht.AddPreOptionsLine($"Usage: ckan {verb} [options] paths");
+                        ht.AddPreOptionsLine($"{Properties.Resources.Usage}: ckan {verb} [{Properties.Resources.Options}] paths");
                         break;
 
-                    // Now the commands with only --flag type options
+                    // Commands with only --flag type options
                     case "gui":
                     case "available":
                     case "list":
@@ -166,7 +176,7 @@ public string GetUsage(string verb)
                     case "clean":
                     case "version":
                     default:
-                        ht.AddPreOptionsLine($"Usage: ckan {verb} [options]");
+                        ht.AddPreOptionsLine($"{Properties.Resources.Usage}: ckan {verb} [{Properties.Resources.Options}]");
                         break;
                 }
             }
@@ -179,15 +189,11 @@ public abstract class VerbCommandOptions
     {
         protected string GetDescription(string verb)
         {
-            var info = this.GetType().GetProperties();
-            foreach (var property in info)
-            {
-                BaseOptionAttribute attrib = (BaseOptionAttribute)Attribute.GetCustomAttribute(
-                    property, typeof(BaseOptionAttribute), false);
-                if (attrib != null && attrib.LongName == verb)
-                    return attrib.HelpText;
-            }
-            return "";
+            return GetType().GetProperties()
+                .Select(property => (BaseOptionAttribute)Attribute.GetCustomAttribute(
+                    property, typeof(BaseOptionAttribute), false))
+                .FirstOrDefault(attrib => attrib?.LongName == verb)
+                ?.HelpText;
         }
     }
 
@@ -230,12 +236,12 @@ public virtual int Handle(GameInstanceManager manager, IUser user)
             {
                 if (!AsRoot)
                 {
-                    user.RaiseError("You are trying to run CKAN as root.\r\nThis is a bad idea and there is absolutely no good reason to do it. Please run CKAN from a user account (or use --asroot if you are feeling brave).");
+                    user.RaiseError(Properties.Resources.OptionsRootError);
                     return Exit.ERROR;
                 }
                 else
                 {
-                    user.RaiseMessage("Warning: Running CKAN as root!");
+                    user.RaiseMessage(Properties.Resources.OptionsRootWarning);
                 }
             }
 
@@ -298,12 +304,9 @@ private static void CheckMonoVersion(IUser user, int rec_major, int rec_minor, i
 
                         if (major < rec_major || (major == rec_major && minor < rec_minor))
                         {
-                            user.RaiseMessage(
-                                "Warning. Detected mono runtime of {0} is less than the recommended version of {1}\r\n",
+                            user.RaiseMessage(Properties.Resources.OptionsMonoWarning,
                                 String.Join(".", major, minor, patch),
-                                String.Join(".", rec_major, rec_minor, rec_patch)
-                                );
-                            user.RaiseMessage("Update recommend\r\n");
+                                String.Join(".", rec_major, rec_minor, rec_patch));
                         }
                     }
                 }
@@ -333,7 +336,7 @@ public override int Handle(GameInstanceManager manager, IUser user)
                 // User provided game instance
                 if (Gamedir != null && Instance != null)
                 {
-                    user.RaiseMessage("--instance and --gamedir can't be specified at the same time");
+                    user.RaiseMessage(Properties.Resources.OptionsInstanceAndGameDir);
                     return Exit.BADOPT;
                 }
 
@@ -352,12 +355,12 @@ public override int Handle(GameInstanceManager manager, IUser user)
                 }
                 catch (NotKSPDirKraken k)
                 {
-                    user.RaiseMessage("Sorry, {0} does not appear to be a game instance", k.path);
+                    user.RaiseMessage(Properties.Resources.InstanceNotInstance, k.path);
                     return Exit.BADOPT;
                 }
                 catch (InvalidKSPInstanceKraken k)
                 {
-                    user.RaiseMessage("Invalid game instance specified \"{0}\", use '--gamedir' to specify by path, or 'instance list' to see known game instances", k.instance);
+                    user.RaiseMessage(Properties.Resources.OptionsInvalidInstance, k.instance);
                     return Exit.BADOPT;
                 }
             }
@@ -387,7 +390,7 @@ public SubCommandOptions(string[] args)
 
     internal class InstallOptions : InstanceSpecificOptions
     {
-        [OptionArray('c', "ckanfiles", HelpText = "Local CKAN files to process")]
+        [OptionArray('c', "ckanfiles", HelpText = "Local CKAN files or URLs to process")]
         public string[] ckan_files { get; set; }
 
         [Option("no-recommends", DefaultValue = false, HelpText = "Do not install recommended modules")]
diff --git a/Cmdline/ProgressReporter.cs b/Cmdline/ProgressReporter.cs
index 655eddff18..02989d0cfe 100644
--- a/Cmdline/ProgressReporter.cs
+++ b/Cmdline/ProgressReporter.cs
@@ -16,10 +16,8 @@ public static void FormattedDownloads(string message, int progress, IUser user)
         {
             if (Regex.IsMatch(message, "download", RegexOptions.IgnoreCase))
             {
-                user.RaiseMessage(
-                    // The \r at the front here causes download messages to *overwrite* each other.
-                    String.Format("\r{0} - {1}%           ", message, progress)
-                );
+                // The \r at the front causes download messages to *overwrite* each other.
+                user.RaiseMessage("\r{0} - {1}%           ", message, progress);
             }
             else
             {
@@ -31,4 +29,3 @@ public static void FormattedDownloads(string message, int progress, IUser user)
         }
     }
 }
-
diff --git a/Cmdline/Properties/AssemblyInfo.cs b/Cmdline/Properties/AssemblyInfo.cs
index 3e3faac6af..f4caa4ba48 100644
--- a/Cmdline/Properties/AssemblyInfo.cs
+++ b/Cmdline/Properties/AssemblyInfo.cs
@@ -1,4 +1,10 @@
+using System.Resources;
 using System.Reflection;
+using System.Runtime.CompilerServices;
 
-[assembly: AssemblyTitle("CKAN")] // TODO: Does not match assembly name
+[assembly: AssemblyTitle("CKAN-CmdLine")]
 [assembly: AssemblyDescription("CKAN CLI Client")]
+[assembly: NeutralResourcesLanguage("en-GB")]
+
+[assembly: InternalsVisibleTo("Tests")]
+[assembly: InternalsVisibleTo("CKAN.Tests")]
diff --git a/Cmdline/Properties/Resources.Designer.cs b/Cmdline/Properties/Resources.Designer.cs
new file mode 100644
index 0000000000..269c9a939d
--- /dev/null
+++ b/Cmdline/Properties/Resources.Designer.cs
@@ -0,0 +1,688 @@
+//------------------------------------------------------------------------------
+// <auto-generated>
+//     This code was generated by a tool.
+//     Runtime Version:4.0.30319.42000
+//
+//     Changes to this file may cause incorrect behavior and will be lost if
+//     the code is regenerated. (I WISH!)
+// </auto-generated>
+//------------------------------------------------------------------------------
+
+namespace CKAN.CmdLine.Properties {
+    using System;
+
+
+    /// <summary>
+    ///   A strongly-typed resource class, for looking up localized strings, etc.
+    /// </summary>
+    // This class was auto-generated by the StronglyTypedResourceBuilder
+    // class via a tool like ResGen or Visual Studio.
+    // To add or remove a member, edit your .ResX file then rerun ResGen
+    // with the /str option, or rebuild your VS project.
+    [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "4.0.0.0")]
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
+    [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
+    internal class Resources {
+
+        private static global::System.Resources.ResourceManager resourceMan;
+
+        private static global::System.Globalization.CultureInfo resourceCulture;
+
+        [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
+        internal Resources() { }
+
+        /// <summary>
+        ///   Returns the cached ResourceManager instance used by this class.
+        /// </summary>
+        [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
+        internal static global::System.Resources.ResourceManager ResourceManager {
+            get {
+                if (object.ReferenceEquals(resourceMan, null))
+                {
+                    resourceMan = new SingleAssemblyResourceManager("CKAN.CmdLine.Properties.Resources", typeof(Resources).Assembly);
+                }
+                return resourceMan;
+            }
+        }
+
+        /// <summary>
+        ///   Overrides the current thread's CurrentUICulture property for all
+        ///   resource lookups using this strongly typed resource class.
+        /// </summary>
+        [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
+        internal static global::System.Globalization.CultureInfo Culture {
+            get {
+                return resourceCulture;
+            }
+            set {
+                resourceCulture = value;
+            }
+        }
+
+        internal static string Usage {
+            get { return (string)(ResourceManager.GetObject("Usage", resourceCulture)); }
+        }
+        internal static string Command {
+            get { return (string)(ResourceManager.GetObject("Command", resourceCulture)); }
+        }
+        internal static string UnknownCommand {
+            get { return (string)(ResourceManager.GetObject("UnknownCommand", resourceCulture)); }
+        }
+        internal static string Options {
+            get { return (string)(ResourceManager.GetObject("Options", resourceCulture)); }
+        }
+        internal static string ArgumentMissing {
+            get { return (string)(ResourceManager.GetObject("ArgumentMissing", resourceCulture)); }
+        }
+        internal static string Path {
+            get { return (string)(ResourceManager.GetObject("Path", resourceCulture)); }
+        }
+
+        internal static string MainVersion {
+            get { return (string)(ResourceManager.GetObject("MainVersion", resourceCulture)); }
+        }
+        internal static string MainUnknownCommand {
+            get { return (string)(ResourceManager.GetObject("MainUnknownCommand", resourceCulture)); }
+        }
+        internal static string MainMissingInstance {
+            get { return (string)(ResourceManager.GetObject("MainMissingInstance", resourceCulture)); }
+        }
+        internal static string OptionsRootError {
+            get { return (string)(ResourceManager.GetObject("OptionsRootError", resourceCulture)); }
+        }
+        internal static string OptionsRootWarning {
+            get { return (string)(ResourceManager.GetObject("OptionsRootWarning", resourceCulture)); }
+        }
+        internal static string OptionsMonoWarning {
+            get { return (string)(ResourceManager.GetObject("OptionsMonoWarning", resourceCulture)); }
+        }
+        internal static string OptionsInstanceAndGameDir {
+            get { return (string)(ResourceManager.GetObject("OptionsInstanceAndGameDir", resourceCulture)); }
+        }
+        internal static string OptionsInvalidInstance {
+            get { return (string)(ResourceManager.GetObject("OptionsInvalidInstance", resourceCulture)); }
+        }
+        internal static string InstanceNotInstance {
+            get { return (string)(ResourceManager.GetObject("InstanceNotInstance", resourceCulture)); }
+        }
+        internal static string InstanceDuplicate {
+            get { return (string)(ResourceManager.GetObject("InstanceDuplicate", resourceCulture)); }
+        }
+
+        internal static string UserYesNoPromptSuffix {
+            get { return (string)(ResourceManager.GetObject("UserYesNoPromptSuffix", resourceCulture)); }
+        }
+        internal static string UserYesNoY {
+            get { return (string)(ResourceManager.GetObject("UserYesNoY", resourceCulture)); }
+        }
+        internal static string UserYesNoYes {
+            get { return (string)(ResourceManager.GetObject("UserYesNoYes", resourceCulture)); }
+        }
+        internal static string UserYesNoN {
+            get { return (string)(ResourceManager.GetObject("UserYesNoN", resourceCulture)); }
+        }
+        internal static string UserYesNoNo {
+            get { return (string)(ResourceManager.GetObject("UserYesNoNo", resourceCulture)); }
+        }
+        internal static string UserYesNoInvalid {
+            get { return (string)(ResourceManager.GetObject("UserYesNoInvalid", resourceCulture)); }
+        }
+        internal static string UserSelectionPromptWithDefault {
+            get { return (string)(ResourceManager.GetObject("UserSelectionPromptWithDefault", resourceCulture)); }
+        }
+        internal static string UserSelectionPromptWithoutDefault {
+            get { return (string)(ResourceManager.GetObject("UserSelectionPromptWithoutDefault", resourceCulture)); }
+        }
+        internal static string UserSelectionC {
+            get { return (string)(ResourceManager.GetObject("UserSelectionC", resourceCulture)); }
+        }
+        internal static string UserSelectionN {
+            get { return (string)(ResourceManager.GetObject("UserSelectionN", resourceCulture)); }
+        }
+        internal static string UserSelectionCancelled {
+            get { return (string)(ResourceManager.GetObject("UserSelectionCancelled", resourceCulture)); }
+        }
+        internal static string UserSelectionNotNumber {
+            get { return (string)(ResourceManager.GetObject("UserSelectionNotNumber", resourceCulture)); }
+        }
+        internal static string UserSelectionTooLarge {
+            get { return (string)(ResourceManager.GetObject("UserSelectionTooLarge", resourceCulture)); }
+        }
+        internal static string UserSelectionTooSmall {
+            get { return (string)(ResourceManager.GetObject("UserSelectionTooSmall", resourceCulture)); }
+        }
+
+        internal static string AuthTokenHostHeader {
+            get { return (string)(ResourceManager.GetObject("AuthTokenHostHeader", resourceCulture)); }
+        }
+        internal static string AuthTokenTokenHeader {
+            get { return (string)(ResourceManager.GetObject("AuthTokenTokenHeader", resourceCulture)); }
+        }
+        internal static string AuthTokenHelpSummary {
+            get { return (string)(ResourceManager.GetObject("AuthTokenHelpSummary", resourceCulture)); }
+        }
+
+        internal static string AvailableHeader {
+            get { return (string)(ResourceManager.GetObject("AvailableHeader", resourceCulture)); }
+        }
+
+        internal static string CacheHelpSummary {
+            get { return (string)(ResourceManager.GetObject("CacheHelpSummary", resourceCulture)); }
+        }
+        internal static string CacheSet {
+            get { return (string)(ResourceManager.GetObject("CacheSet", resourceCulture)); }
+        }
+        internal static string CacheInvalidPath {
+            get { return (string)(ResourceManager.GetObject("CacheInvalidPath", resourceCulture)); }
+        }
+        internal static string CacheCleared {
+            get { return (string)(ResourceManager.GetObject("CacheCleared", resourceCulture)); }
+        }
+        internal static string CacheReset {
+            get { return (string)(ResourceManager.GetObject("CacheReset", resourceCulture)); }
+        }
+        internal static string CacheResetFailed {
+            get { return (string)(ResourceManager.GetObject("CacheResetFailed", resourceCulture)); }
+        }
+        internal static string CacheUnlimited {
+            get { return (string)(ResourceManager.GetObject("CacheUnlimited", resourceCulture)); }
+        }
+        internal static string CacheInfo {
+            get { return (string)(ResourceManager.GetObject("CacheInfo", resourceCulture)); }
+        }
+
+        internal static string CompareSame {
+            get { return (string)(ResourceManager.GetObject("CompareSame", resourceCulture)); }
+        }
+        internal static string CompareLower {
+            get { return (string)(ResourceManager.GetObject("CompareLower", resourceCulture)); }
+        }
+        internal static string CompareHigher {
+            get { return (string)(ResourceManager.GetObject("CompareHigher", resourceCulture)); }
+        }
+        internal static string CompatHelpSummary {
+            get { return (string)(ResourceManager.GetObject("CompatHelpSummary", resourceCulture)); }
+        }
+        internal static string CompatVersionHeader {
+            get { return (string)(ResourceManager.GetObject("CompatVersionHeader", resourceCulture)); }
+        }
+        internal static string CompatActualHeader {
+            get { return (string)(ResourceManager.GetObject("CompatActualHeader", resourceCulture)); }
+        }
+        internal static string CompatInvalid {
+            get { return (string)(ResourceManager.GetObject("CompatInvalid", resourceCulture)); }
+        }
+        internal static string CompatCantForget {
+            get { return (string)(ResourceManager.GetObject("CompatCantForget", resourceCulture)); }
+        }
+
+        internal static string InstanceHelpSummary {
+            get { return (string)(ResourceManager.GetObject("InstanceHelpSummary", resourceCulture)); }
+        }
+        internal static string InstanceListNoVersion {
+            get { return (string)(ResourceManager.GetObject("InstanceListNoVersion", resourceCulture)); }
+        }
+        internal static string InstanceListYes {
+            get { return (string)(ResourceManager.GetObject("InstanceListYes", resourceCulture)); }
+        }
+        internal static string InstanceListNo {
+            get { return (string)(ResourceManager.GetObject("InstanceListNo", resourceCulture)); }
+        }
+        internal static string InstanceListNameHeader {
+            get { return (string)(ResourceManager.GetObject("InstanceListNameHeader", resourceCulture)); }
+        }
+        internal static string InstanceListVersionHeader {
+            get { return (string)(ResourceManager.GetObject("InstanceListVersionHeader", resourceCulture)); }
+        }
+        internal static string InstanceListDefaultHeader {
+            get { return (string)(ResourceManager.GetObject("InstanceListDefaultHeader", resourceCulture)); }
+        }
+        internal static string InstanceListPathHeader {
+            get { return (string)(ResourceManager.GetObject("InstanceListPathHeader", resourceCulture)); }
+        }
+        internal static string InstanceAddDuplicate {
+            get { return (string)(ResourceManager.GetObject("InstanceAddDuplicate", resourceCulture)); }
+        }
+        internal static string InstanceAdded {
+            get { return (string)(ResourceManager.GetObject("InstanceAdded", resourceCulture)); }
+        }
+        internal static string InstanceCloneNotFound {
+            get { return (string)(ResourceManager.GetObject("InstanceCloneNotFound", resourceCulture)); }
+        }
+        internal static string InstanceCloneFailed {
+            get { return (string)(ResourceManager.GetObject("InstanceCloneFailed", resourceCulture)); }
+        }
+        internal static string InstanceNotFound {
+            get { return (string)(ResourceManager.GetObject("InstanceNotFound", resourceCulture)); }
+        }
+        internal static string InstanceRenamed {
+            get { return (string)(ResourceManager.GetObject("InstanceRenamed", resourceCulture)); }
+        }
+        internal static string InstanceForgot {
+            get { return (string)(ResourceManager.GetObject("InstanceForgot", resourceCulture)); }
+        }
+        internal static string InstanceDefaultArgumentMissing {
+            get { return (string)(ResourceManager.GetObject("InstanceDefaultArgumentMissing", resourceCulture)); }
+        }
+        internal static string InstanceDefaultSet {
+            get { return (string)(ResourceManager.GetObject("InstanceDefaultSet", resourceCulture)); }
+        }
+        internal static string InstanceFakeFailed {
+            get { return (string)(ResourceManager.GetObject("InstanceFakeFailed", resourceCulture)); }
+        }
+        internal static string InstanceFakeBadArguments {
+            get { return (string)(ResourceManager.GetObject("InstanceFakeBadArguments", resourceCulture)); }
+        }
+        internal static string InstanceFakeMakingHistory {
+            get { return (string)(ResourceManager.GetObject("InstanceFakeMakingHistory", resourceCulture)); }
+        }
+        internal static string InstanceFakeBreakingGround {
+            get { return (string)(ResourceManager.GetObject("InstanceFakeBreakingGround", resourceCulture)); }
+        }
+        internal static string InstanceFakeVersion {
+            get { return (string)(ResourceManager.GetObject("InstanceFakeVersion", resourceCulture)); }
+        }
+        internal static string InstanceFakeBadGameVersion {
+            get { return (string)(ResourceManager.GetObject("InstanceFakeBadGameVersion", resourceCulture)); }
+        }
+        internal static string InstanceFakeCancelled {
+            get { return (string)(ResourceManager.GetObject("InstanceFakeCancelled", resourceCulture)); }
+        }
+        internal static string InstanceFakeCreating {
+            get { return (string)(ResourceManager.GetObject("InstanceFakeCreating", resourceCulture)); }
+        }
+        internal static string InstanceFakeDefault {
+            get { return (string)(ResourceManager.GetObject("InstanceFakeDefault", resourceCulture)); }
+        }
+        internal static string InstanceFakeDone {
+            get { return (string)(ResourceManager.GetObject("InstanceFakeDone", resourceCulture)); }
+        }
+
+        internal static string ImportError {
+            get { return (string)(ResourceManager.GetObject("ImportError", resourceCulture)); }
+        }
+        internal static string ImportNotFound {
+            get { return (string)(ResourceManager.GetObject("ImportNotFound", resourceCulture)); }
+        }
+
+        internal static string InstallNotFound {
+            get { return (string)(ResourceManager.GetObject("InstallNotFound", resourceCulture)); }
+        }
+        internal static string InstallTryAgain {
+            get { return (string)(ResourceManager.GetObject("InstallTryAgain", resourceCulture)); }
+        }
+        internal static string InstallUnversionedDependencyNotSatisfied {
+            get { return (string)(ResourceManager.GetObject("InstallUnversionedDependencyNotSatisfied", resourceCulture)); }
+        }
+        internal static string InstallVersionedDependencyNotSatisfied {
+            get { return (string)(ResourceManager.GetObject("InstallVersionedDependencyNotSatisfied", resourceCulture)); }
+        }
+        internal static string InstallBadMetadata {
+            get { return (string)(ResourceManager.GetObject("InstallBadMetadata", resourceCulture)); }
+        }
+        internal static string InstallFileConflictOwned {
+            get { return (string)(ResourceManager.GetObject("InstallFileConflictOwned", resourceCulture)); }
+        }
+        internal static string InstallFileConflictUnowned {
+            get { return (string)(ResourceManager.GetObject("InstallFileConflictUnowned", resourceCulture)); }
+        }
+        internal static string InstallGamedataReturned {
+            get { return (string)(ResourceManager.GetObject("InstallGamedataReturned", resourceCulture)); }
+        }
+        internal static string InstallCancelled {
+            get { return (string)(ResourceManager.GetObject("InstallCancelled", resourceCulture)); }
+        }
+        internal static string InstallAborted {
+            get { return (string)(ResourceManager.GetObject("InstallAborted", resourceCulture)); }
+        }
+        internal static string InstallTryAuthToken {
+            get { return (string)(ResourceManager.GetObject("InstallTryAuthToken", resourceCulture)); }
+        }
+        internal static string InstallDownloadFailed {
+            get { return (string)(ResourceManager.GetObject("InstallDownloadFailed", resourceCulture)); }
+        }
+        internal static string InstallDLC {
+            get { return (string)(ResourceManager.GetObject("InstallDLC", resourceCulture)); }
+        }
+        internal static string InstallDLCStorePage {
+            get { return (string)(ResourceManager.GetObject("InstallDLCStorePage", resourceCulture)); }
+        }
+
+        internal static string ListUnknownFormat {
+            get { return (string)(ResourceManager.GetObject("ListUnknownFormat", resourceCulture)); }
+        }
+        internal static string ListGameFound {
+            get { return (string)(ResourceManager.GetObject("ListGameFound", resourceCulture)); }
+        }
+        internal static string ListGameVersion {
+            get { return (string)(ResourceManager.GetObject("ListGameVersion", resourceCulture)); }
+        }
+        internal static string ListGameModulesHeader {
+            get { return (string)(ResourceManager.GetObject("ListGameModulesHeader", resourceCulture)); }
+        }
+        internal static string ListLegend {
+            get { return (string)(ResourceManager.GetObject("ListLegend", resourceCulture)); }
+        }
+
+        internal static string MarkHelpSummary {
+            get { return (string)(ResourceManager.GetObject("MarkHelpSummary", resourceCulture)); }
+        }
+        internal static string MarkUnknownCommand {
+            get { return (string)(ResourceManager.GetObject("MarkUnknownCommand", resourceCulture)); }
+        }
+        internal static string MarkNotInstalled {
+            get { return (string)(ResourceManager.GetObject("MarkNotInstalled", resourceCulture)); }
+        }
+        internal static string MarkAlready {
+            get { return (string)(ResourceManager.GetObject("MarkAlready", resourceCulture)); }
+        }
+        internal static string Marking {
+            get { return (string)(ResourceManager.GetObject("Marking", resourceCulture)); }
+        }
+        internal static string MarkDLC {
+            get { return (string)(ResourceManager.GetObject("MarkDLC", resourceCulture)); }
+        }
+        internal static string MarkChanged {
+            get { return (string)(ResourceManager.GetObject("MarkChanged", resourceCulture)); }
+        }
+        internal static string MarkAutoInstalled {
+            get { return (string)(ResourceManager.GetObject("MarkAutoInstalled", resourceCulture)); }
+        }
+        internal static string MarkUserSelected {
+            get { return (string)(ResourceManager.GetObject("MarkUserSelected", resourceCulture)); }
+        }
+
+        internal static string PromptWelcome {
+            get { return (string)(ResourceManager.GetObject("PromptWelcome", resourceCulture)); }
+        }
+        internal static string PromptWithInstance {
+            get { return (string)(ResourceManager.GetObject("PromptWithInstance", resourceCulture)); }
+        }
+        internal static string PromptWithoutInstance {
+            get { return (string)(ResourceManager.GetObject("PromptWithoutInstance", resourceCulture)); }
+        }
+
+        internal static string RemoveNotInstalled {
+            get { return (string)(ResourceManager.GetObject("RemoveNotInstalled", resourceCulture)); }
+        }
+        internal static string RemoveDLC {
+            get { return (string)(ResourceManager.GetObject("RemoveDLC", resourceCulture)); }
+        }
+        internal static string RemoveDLCStorePage {
+            get { return (string)(ResourceManager.GetObject("RemoveDLCStorePage", resourceCulture)); }
+        }
+        internal static string RemoveCancelled {
+            get { return (string)(ResourceManager.GetObject("RemoveCancelled", resourceCulture)); }
+        }
+        internal static string RemoveNothing {
+            get { return (string)(ResourceManager.GetObject("RemoveNothing", resourceCulture)); }
+        }
+
+        internal static string RepairHelpSummary {
+            get { return (string)(ResourceManager.GetObject("RepairHelpSummary", resourceCulture)); }
+        }
+        internal static string RepairUnknownCommand {
+            get { return (string)(ResourceManager.GetObject("RepairUnknownCommand", resourceCulture)); }
+        }
+        internal static string Repaired {
+            get { return (string)(ResourceManager.GetObject("Repaired", resourceCulture)); }
+        }
+        internal static string ReplaceModuleNotFound {
+            get { return (string)(ResourceManager.GetObject("ReplaceModuleNotFound", resourceCulture)); }
+        }
+        internal static string Replacing {
+            get { return (string)(ResourceManager.GetObject("Replacing", resourceCulture)); }
+        }
+        internal static string ReplaceFound {
+            get { return (string)(ResourceManager.GetObject("ReplaceFound", resourceCulture)); }
+        }
+        internal static string ReplaceContinuePrompt {
+            get { return (string)(ResourceManager.GetObject("ReplaceContinuePrompt", resourceCulture)); }
+        }
+        internal static string ReplaceCancelled {
+            get { return (string)(ResourceManager.GetObject("ReplaceCancelled", resourceCulture)); }
+        }
+        internal static string ReplaceDependencyNotSatisfied {
+            get { return (string)(ResourceManager.GetObject("ReplaceDependencyNotSatisfied", resourceCulture)); }
+        }
+        internal static string ReplaceNotFound {
+            get { return (string)(ResourceManager.GetObject("ReplaceNotFound", resourceCulture)); }
+        }
+
+        internal static string RepoHelpSummary {
+            get { return (string)(ResourceManager.GetObject("RepoHelpSummary", resourceCulture)); }
+        }
+        internal static string RepoUnknownCommand {
+            get { return (string)(ResourceManager.GetObject("RepoUnknownCommand", resourceCulture)); }
+        }
+        internal static string RepoAvailableHeader {
+            get { return (string)(ResourceManager.GetObject("RepoAvailableHeader", resourceCulture)); }
+        }
+        internal static string RepoAvailableFailed {
+            get { return (string)(ResourceManager.GetObject("RepoAvailableFailed", resourceCulture)); }
+        }
+        internal static string RepoListHeader {
+            get { return (string)(ResourceManager.GetObject("RepoListHeader", resourceCulture)); }
+        }
+        internal static string RepoAddNotFound {
+            get { return (string)(ResourceManager.GetObject("RepoAddNotFound", resourceCulture)); }
+        }
+        internal static string RepoAddDuplicate {
+            get { return (string)(ResourceManager.GetObject("RepoAddDuplicate", resourceCulture)); }
+        }
+        internal static string RepoAdded {
+            get { return (string)(ResourceManager.GetObject("RepoAdded", resourceCulture)); }
+        }
+        internal static string RepoForgetNotFound {
+            get { return (string)(ResourceManager.GetObject("RepoForgetNotFound", resourceCulture)); }
+        }
+        internal static string RepoForgetRemoving {
+            get { return (string)(ResourceManager.GetObject("RepoForgetRemoving", resourceCulture)); }
+        }
+        internal static string RepoForgetRemoved {
+            get { return (string)(ResourceManager.GetObject("RepoForgetRemoved", resourceCulture)); }
+        }
+        internal static string RepoSet {
+            get { return (string)(ResourceManager.GetObject("RepoSet", resourceCulture)); }
+        }
+
+        internal static string SearchNoTerm {
+            get { return (string)(ResourceManager.GetObject("SearchNoTerm", resourceCulture)); }
+        }
+        internal static string SearchFoundByAuthorWithIncompat {
+            get { return (string)(ResourceManager.GetObject("SearchFoundByAuthorWithIncompat", resourceCulture)); }
+        }
+        internal static string SearchFoundWithIncompat {
+            get { return (string)(ResourceManager.GetObject("SearchFoundWithIncompat", resourceCulture)); }
+        }
+        internal static string SearchFoundByAuthor {
+            get { return (string)(ResourceManager.GetObject("SearchFoundByAuthor", resourceCulture)); }
+        }
+        internal static string SearchFound {
+            get { return (string)(ResourceManager.GetObject("SearchFound", resourceCulture)); }
+        }
+        internal static string SearchCompatibleModsHeader {
+            get { return (string)(ResourceManager.GetObject("SearchCompatibleModsHeader", resourceCulture)); }
+        }
+        internal static string SearchCompatibleMod {
+            get { return (string)(ResourceManager.GetObject("SearchCompatibleMod", resourceCulture)); }
+        }
+        internal static string SearchIncompatibleModsHeader {
+            get { return (string)(ResourceManager.GetObject("SearchIncompatibleModsHeader", resourceCulture)); }
+        }
+        internal static string SearchIncompatibleMod {
+            get { return (string)(ResourceManager.GetObject("SearchIncompatibleMod", resourceCulture)); }
+        }
+        internal static string ShowNotInstalledOrCompatible {
+            get { return (string)(ResourceManager.GetObject("ShowNotInstalledOrCompatible", resourceCulture)); }
+        }
+        internal static string ShowLookingForClose {
+            get { return (string)(ResourceManager.GetObject("ShowLookingForClose", resourceCulture)); }
+        }
+        internal static string ShowNoClose {
+            get { return (string)(ResourceManager.GetObject("ShowNoClose", resourceCulture)); }
+        }
+        internal static string ShowFoundOne {
+            get { return (string)(ResourceManager.GetObject("ShowFoundOne", resourceCulture)); }
+        }
+        internal static string ShowClosePrompt {
+            get { return (string)(ResourceManager.GetObject("ShowClosePrompt", resourceCulture)); }
+        }
+        internal static string ShowFilesHeader {
+            get { return (string)(ResourceManager.GetObject("ShowFilesHeader", resourceCulture)); }
+        }
+        internal static string ShowModuleInfoHeader {
+            get { return (string)(ResourceManager.GetObject("ShowModuleInfoHeader", resourceCulture)); }
+        }
+        internal static string ShowVersion {
+            get { return (string)(ResourceManager.GetObject("ShowVersion", resourceCulture)); }
+        }
+        internal static string ShowAuthor {
+            get { return (string)(ResourceManager.GetObject("ShowAuthor", resourceCulture)); }
+        }
+        internal static string ShowAuthorUnknown {
+            get { return (string)(ResourceManager.GetObject("ShowAuthorUnknown", resourceCulture)); }
+        }
+        internal static string ShowStatus {
+            get { return (string)(ResourceManager.GetObject("ShowStatus", resourceCulture)); }
+        }
+        internal static string ShowLicence {
+            get { return (string)(ResourceManager.GetObject("ShowLicence", resourceCulture)); }
+        }
+        internal static string ShowTags {
+            get { return (string)(ResourceManager.GetObject("ShowTags", resourceCulture)); }
+        }
+        internal static string ShowLanguages {
+            get { return (string)(ResourceManager.GetObject("ShowLanguages", resourceCulture)); }
+        }
+        internal static string ShowDependsHeader {
+            get { return (string)(ResourceManager.GetObject("ShowDependsHeader", resourceCulture)); }
+        }
+        internal static string ShowRecommendsHeader {
+            get { return (string)(ResourceManager.GetObject("ShowRecommendsHeader", resourceCulture)); }
+        }
+        internal static string ShowSuggestsHeader {
+            get { return (string)(ResourceManager.GetObject("ShowSuggestsHeader", resourceCulture)); }
+        }
+        internal static string ShowProvidesHeader {
+            get { return (string)(ResourceManager.GetObject("ShowProvidesHeader", resourceCulture)); }
+        }
+        internal static string ShowResourcesHeader {
+            get { return (string)(ResourceManager.GetObject("ShowResourcesHeader", resourceCulture)); }
+        }
+        internal static string ShowHomePage {
+            get { return (string)(ResourceManager.GetObject("ShowHomePage", resourceCulture)); }
+        }
+        internal static string ShowManual {
+            get { return (string)(ResourceManager.GetObject("ShowManual", resourceCulture)); }
+        }
+        internal static string ShowSpaceDock {
+            get { return (string)(ResourceManager.GetObject("ShowSpaceDock", resourceCulture)); }
+        }
+        internal static string ShowRepository {
+            get { return (string)(ResourceManager.GetObject("ShowRepository", resourceCulture)); }
+        }
+        internal static string ShowBugTracker {
+            get { return (string)(ResourceManager.GetObject("ShowBugTracker", resourceCulture)); }
+        }
+        internal static string ShowCurse {
+            get { return (string)(ResourceManager.GetObject("ShowCurse", resourceCulture)); }
+        }
+        internal static string ShowStore {
+            get { return (string)(ResourceManager.GetObject("ShowStore", resourceCulture)); }
+        }
+        internal static string ShowSteamStore {
+            get { return (string)(ResourceManager.GetObject("ShowSteamStore", resourceCulture)); }
+        }
+        internal static string ShowVersionFile {
+            get { return (string)(ResourceManager.GetObject("ShowVersionFile", resourceCulture)); }
+        }
+        internal static string ShowFileName {
+            get { return (string)(ResourceManager.GetObject("ShowFileName", resourceCulture)); }
+        }
+        internal static string ShowVersionHeader {
+            get { return (string)(ResourceManager.GetObject("ShowVersionHeader", resourceCulture)); }
+        }
+        internal static string ShowGameVersionsHeader {
+            get { return (string)(ResourceManager.GetObject("ShowGameVersionsHeader", resourceCulture)); }
+        }
+
+        internal static string UpdateChangesSummary {
+            get { return (string)(ResourceManager.GetObject("UpdateChangesSummary", resourceCulture)); }
+        }
+        internal static string UpdateAddedHeader {
+            get { return (string)(ResourceManager.GetObject("UpdateAddedHeader", resourceCulture)); }
+        }
+        internal static string UpdateRemovedHeader {
+            get { return (string)(ResourceManager.GetObject("UpdateRemovedHeader", resourceCulture)); }
+        }
+        internal static string UpdateUpdatedHeader {
+            get { return (string)(ResourceManager.GetObject("UpdateUpdatedHeader", resourceCulture)); }
+        }
+        internal static string UpdateSummary {
+            get { return (string)(ResourceManager.GetObject("UpdateSummary", resourceCulture)); }
+        }
+
+        internal static string UpgradeQueryingCKAN {
+            get { return (string)(ResourceManager.GetObject("UpgradeQueryingCKAN", resourceCulture)); }
+        }
+        internal static string UpgradeNewCKANAvailable {
+            get { return (string)(ResourceManager.GetObject("UpgradeNewCKANAvailable", resourceCulture)); }
+        }
+        internal static string UpgradeProceed {
+            get { return (string)(ResourceManager.GetObject("UpgradeProceed", resourceCulture)); }
+        }
+        internal static string UpgradePleaseWait {
+            get { return (string)(ResourceManager.GetObject("UpgradePleaseWait", resourceCulture)); }
+        }
+        internal static string UpgradeAlreadyHaveLatest {
+            get { return (string)(ResourceManager.GetObject("UpgradeAlreadyHaveLatest", resourceCulture)); }
+        }
+        internal static string UpgradeAborted {
+            get { return (string)(ResourceManager.GetObject("UpgradeAborted", resourceCulture)); }
+        }
+        internal static string UpgradeNotFound {
+            get { return (string)(ResourceManager.GetObject("UpgradeNotFound", resourceCulture)); }
+        }
+        internal static string UpgradeDLC {
+            get { return (string)(ResourceManager.GetObject("UpgradeDLC", resourceCulture)); }
+        }
+        internal static string UpgradeDLCStorePage {
+            get { return (string)(ResourceManager.GetObject("UpgradeDLCStorePage", resourceCulture)); }
+        }
+
+        internal static string ScanNotSaved {
+            get { return (string)(ResourceManager.GetObject("ScanNotSaved", resourceCulture)); }
+        }
+        internal static string ScanPreliminaryInconsistent {
+            get { return (string)(ResourceManager.GetObject("ScanPreliminaryInconsistent", resourceCulture)); }
+        }
+
+        internal static string FilterListGlobalHeader {
+            get { return (string)(ResourceManager.GetObject("FilterListGlobalHeader", resourceCulture)); }
+        }
+        internal static string FilterListInstanceHeader {
+            get { return (string)(ResourceManager.GetObject("FilterListInstanceHeader", resourceCulture)); }
+        }
+        internal static string FilterAddGlobalDuplicateError {
+            get { return (string)(ResourceManager.GetObject("FilterAddGlobalDuplicateError", resourceCulture)); }
+        }
+        internal static string FilterAddInstanceDuplicateError {
+            get { return (string)(ResourceManager.GetObject("FilterAddInstanceDuplicateError", resourceCulture)); }
+        }
+        internal static string FilterRemoveGlobalNotFoundError {
+            get { return (string)(ResourceManager.GetObject("FilterRemoveGlobalNotFoundError", resourceCulture)); }
+        }
+        internal static string FilterRemoveInstanceNotFoundError {
+            get { return (string)(ResourceManager.GetObject("FilterRemoveInstanceNotFoundError", resourceCulture)); }
+        }
+        internal static string FilterHelpSummary {
+            get { return (string)(ResourceManager.GetObject("FilterHelpSummary", resourceCulture)); }
+        }
+
+        internal static string CompletionNotAvailable {
+            get { return (string)(ResourceManager.GetObject("CompletionNotAvailable", resourceCulture)); }
+        }
+
+    }
+}
diff --git a/Cmdline/Properties/Resources.en-US.resx b/Cmdline/Properties/Resources.en-US.resx
new file mode 100644
index 0000000000..b0fddd5121
--- /dev/null
+++ b/Cmdline/Properties/Resources.en-US.resx
@@ -0,0 +1,121 @@
+<?xml version="1.0" encoding="utf-8"?>
+<root>
+  <!--
+    Microsoft ResX Schema
+
+    Version 2.0
+
+    The primary goals of this format is to allow a simple XML format
+    that is mostly human readable. The generation and parsing of the
+    various data types are done through the TypeConverter classes
+    associated with the data types.
+
+    Example:
+
+    ... ado.net/XML headers & schema ...
+    <resheader name="resmimetype">text/microsoft-resx</resheader>
+    <resheader name="version">2.0</resheader>
+    <resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
+    <resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
+    <data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
+    <data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
+    <data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
+        <value>[base64 mime encoded serialized .NET Framework object]</value>
+    </data>
+    <data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
+        <value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
+        <comment>This is a comment</comment>
+    </data>
+
+    There are any number of "resheader" rows that contain simple
+    name/value pairs.
+
+    Each data row contains a name, and value. The row also contains a
+    type or mimetype. Type corresponds to a .NET class that support
+    text/value conversion through the TypeConverter architecture.
+    Classes that don't support this are serialized and stored with the
+    mimetype set.
+
+    The mimetype is used for serialized objects, and tells the
+    ResXResourceReader how to depersist the object. This is currently not
+    extensible. For a given mimetype the value must be set accordingly:
+
+    Note - application/x-microsoft.net.object.binary.base64 is the format
+    that the ResXResourceWriter will generate, however the reader can
+    read any of the formats listed below.
+
+    mimetype: application/x-microsoft.net.object.binary.base64
+    value   : The object must be serialized with
+            : System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
+            : and then encoded with base64 encoding.
+
+    mimetype: application/x-microsoft.net.object.soap.base64
+    value   : The object must be serialized with
+            : System.Runtime.Serialization.Formatters.Soap.SoapFormatter
+            : and then encoded with base64 encoding.
+
+    mimetype: application/x-microsoft.net.object.bytearray.base64
+    value   : The object must be serialized into a byte array
+            : using a System.ComponentModel.TypeConverter
+            : and then encoded with base64 encoding.
+    -->
+  <xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
+    <xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
+    <xsd:element name="root" msdata:IsDataSet="true">
+      <xsd:complexType>
+        <xsd:choice maxOccurs="unbounded">
+          <xsd:element name="metadata">
+            <xsd:complexType>
+              <xsd:sequence>
+                <xsd:element name="value" type="xsd:string" minOccurs="0" />
+              </xsd:sequence>
+              <xsd:attribute name="name" use="required" type="xsd:string" />
+              <xsd:attribute name="type" type="xsd:string" />
+              <xsd:attribute name="mimetype" type="xsd:string" />
+              <xsd:attribute ref="xml:space" />
+            </xsd:complexType>
+          </xsd:element>
+          <xsd:element name="assembly">
+            <xsd:complexType>
+              <xsd:attribute name="alias" type="xsd:string" />
+              <xsd:attribute name="name" type="xsd:string" />
+            </xsd:complexType>
+          </xsd:element>
+          <xsd:element name="data">
+            <xsd:complexType>
+              <xsd:sequence>
+                <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
+                <xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
+              </xsd:sequence>
+              <xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
+              <xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
+              <xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
+              <xsd:attribute ref="xml:space" />
+            </xsd:complexType>
+          </xsd:element>
+          <xsd:element name="resheader">
+            <xsd:complexType>
+              <xsd:sequence>
+                <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
+              </xsd:sequence>
+              <xsd:attribute name="name" type="xsd:string" use="required" />
+            </xsd:complexType>
+          </xsd:element>
+        </xsd:choice>
+      </xsd:complexType>
+    </xsd:element>
+  </xsd:schema>
+  <resheader name="resmimetype">
+    <value>text/microsoft-resx</value>
+  </resheader>
+  <resheader name="version">
+    <value>2.0</value>
+  </resheader>
+  <resheader name="reader">
+    <value>System.Resources.ResXResourceReader, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
+  </resheader>
+  <resheader name="writer">
+    <value>System.Resources.ResXResourceWriter, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
+  </resheader>
+  <data name="ShowLicence" xml:space="preserve"><value>  License:	{0}</value></data>
+</root>
diff --git a/Cmdline/Properties/Resources.fr-FR.resx b/Cmdline/Properties/Resources.fr-FR.resx
new file mode 100644
index 0000000000..b1288b3b38
--- /dev/null
+++ b/Cmdline/Properties/Resources.fr-FR.resx
@@ -0,0 +1,368 @@
+<?xml version="1.0" encoding="utf-8"?>
+<root>
+  <!--
+    Microsoft ResX Schema
+
+    Version 2.0
+
+    The primary goals of this format is to allow a simple XML format
+    that is mostly human readable. The generation and parsing of the
+    various data types are done through the TypeConverter classes
+    associated with the data types.
+
+    Example:
+
+    ... ado.net/XML headers & schema ...
+    <resheader name="resmimetype">text/microsoft-resx</resheader>
+    <resheader name="version">2.0</resheader>
+    <resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
+    <resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
+    <data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
+    <data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
+    <data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
+        <value>[base64 mime encoded serialized .NET Framework object]</value>
+    </data>
+    <data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
+        <value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
+        <comment>This is a comment</comment>
+    </data>
+
+    There are any number of "resheader" rows that contain simple
+    name/value pairs.
+
+    Each data row contains a name, and value. The row also contains a
+    type or mimetype. Type corresponds to a .NET class that support
+    text/value conversion through the TypeConverter architecture.
+    Classes that don't support this are serialized and stored with the
+    mimetype set.
+
+    The mimetype is used for serialized objects, and tells the
+    ResXResourceReader how to depersist the object. This is currently not
+    extensible. For a given mimetype the value must be set accordingly:
+
+    Note - application/x-microsoft.net.object.binary.base64 is the format
+    that the ResXResourceWriter will generate, however the reader can
+    read any of the formats listed below.
+
+    mimetype: application/x-microsoft.net.object.binary.base64
+    value   : The object must be serialized with
+            : System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
+            : and then encoded with base64 encoding.
+
+    mimetype: application/x-microsoft.net.object.soap.base64
+    value   : The object must be serialized with
+            : System.Runtime.Serialization.Formatters.Soap.SoapFormatter
+            : and then encoded with base64 encoding.
+
+    mimetype: application/x-microsoft.net.object.bytearray.base64
+    value   : The object must be serialized into a byte array
+            : using a System.ComponentModel.TypeConverter
+            : and then encoded with base64 encoding.
+    -->
+  <xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
+    <xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
+    <xsd:element name="root" msdata:IsDataSet="true">
+      <xsd:complexType>
+        <xsd:choice maxOccurs="unbounded">
+          <xsd:element name="metadata">
+            <xsd:complexType>
+              <xsd:sequence>
+                <xsd:element name="value" type="xsd:string" minOccurs="0" />
+              </xsd:sequence>
+              <xsd:attribute name="name" use="required" type="xsd:string" />
+              <xsd:attribute name="type" type="xsd:string" />
+              <xsd:attribute name="mimetype" type="xsd:string" />
+              <xsd:attribute ref="xml:space" />
+            </xsd:complexType>
+          </xsd:element>
+          <xsd:element name="assembly">
+            <xsd:complexType>
+              <xsd:attribute name="alias" type="xsd:string" />
+              <xsd:attribute name="name" type="xsd:string" />
+            </xsd:complexType>
+          </xsd:element>
+          <xsd:element name="data">
+            <xsd:complexType>
+              <xsd:sequence>
+                <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
+                <xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
+              </xsd:sequence>
+              <xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
+              <xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
+              <xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
+              <xsd:attribute ref="xml:space" />
+            </xsd:complexType>
+          </xsd:element>
+          <xsd:element name="resheader">
+            <xsd:complexType>
+              <xsd:sequence>
+                <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
+              </xsd:sequence>
+              <xsd:attribute name="name" type="xsd:string" use="required" />
+            </xsd:complexType>
+          </xsd:element>
+        </xsd:choice>
+      </xsd:complexType>
+    </xsd:element>
+  </xsd:schema>
+  <resheader name="resmimetype">
+    <value>text/microsoft-resx</value>
+  </resheader>
+  <resheader name="version">
+    <value>2.0</value>
+  </resheader>
+  <resheader name="reader">
+    <value>System.Resources.ResXResourceReader, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
+  </resheader>
+  <resheader name="writer">
+    <value>System.Resources.ResXResourceWriter, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
+  </resheader>
+  <data name="Usage" xml:space="preserve"><value>Utilisation </value></data>
+  <data name="Command" xml:space="preserve"><value>commande</value></data>
+  <data name="UnknownCommand" xml:space="preserve"><value>Commande inconnue</value></data>
+  <data name="Options" xml:space="preserve"><value>options</value></data>
+  <data name="ArgumentMissing" xml:space="preserve"><value>argument manquant, peut-être l'avez-vous oublié ?</value></data>
+  <data name="Path" xml:space="preserve"><value>chemin</value></data>
+  <data name="MainVersion" xml:space="preserve"><value>Vous utilisez CKAN version {0}</value></data>
+  <data name="MainUnknownCommand" xml:space="preserve"><value>Commande inconnue, essayez --help</value></data>
+  <data name="MainMissingInstance" xml:space="preserve"><value>Je ne sais pas où l'instance de jeu est installée.
+Utilisez 'ckan instance help' pour être aidé dans cette configuration.</value></data>
+  <data name="OptionsRootError" xml:space="preserve"><value>Vous essayer d'utiliser CKAN en tant que super-utilisateur.
+C'est une mauvaise idée et il n'y a absolument aucune raison de le faire. Veuillez utilisez CKAN depuis un compte utilisateur (ou utilisez --asroot si vous aimez le risque).</value></data>
+  <data name="OptionsRootWarning" xml:space="preserve"><value>Attention : CKAN utilisé par un super-utilisateur !</value></data>
+  <data name="OptionsMonoWarning" xml:space="preserve"><value>Attention. Un runtime Mono {0} a été détecté, ce qui est inférieur à la version recommandée qui est {1}.
+Mise à jour conseillée !</value></data>
+  <data name="OptionsInstanceAndGameDir" xml:space="preserve"><value>--instance et --gamedir ne peuvent pas être spécifiées en même temps</value></data>
+  <data name="OptionsInvalidInstance" xml:space="preserve"><value>Instance de jeu spécifiée invalide "{0}", utilisez '--gamedir' pour spécifier un chemin, ou 'instance list' pour voir les instances de jeu connues</value></data>
+  <data name="InstanceNotInstance" xml:space="preserve"><value>Désolé, {0} n'a pas l'air d'être une instance de jeu</value></data>
+  <data name="InstanceDuplicate" xml:space="preserve"><value>Ce nom d'instance est déjà pris : {0}</value></data>
+  <data name="UserYesNoPromptSuffix" xml:space="preserve"><value>[O/n]</value></data>
+  <data name="UserYesNoY" xml:space="preserve"><value>o</value></data>
+  <data name="UserYesNoYes" xml:space="preserve"><value>oui</value></data>
+  <data name="UserYesNoN" xml:space="preserve"><value>n</value></data>
+  <data name="UserYesNoNo" xml:space="preserve"><value>non</value></data>
+  <data name="UserYesNoInvalid" xml:space="preserve"><value>Saisie invalide. Veuillez entrer oui ou non</value></data>
+  <data name="UserSelectionPromptWithDefault" xml:space="preserve"><value>Entrez un nombre entre {0} et {1} (Pour annuler appuyez sur "c" ou "n". "Entré" va sélectionner {2}.) : </value></data>
+  <data name="UserSelectionPromptWithoutDefault" xml:space="preserve"><value>Entrez un nombre entre {0} et {1} (Pour annuler appuyez sur "c" ou "n".) : </value></data>
+  <data name="UserSelectionC" xml:space="preserve"><value>c</value></data>
+  <data name="UserSelectionN" xml:space="preserve"><value>n</value></data>
+  <data name="UserSelectionCancelled" xml:space="preserve"><value>Sélection annulée</value></data>
+  <data name="UserSelectionNotNumber" xml:space="preserve"><value>La saisie n'est pas un nombre</value></data>
+  <data name="UserSelectionTooLarge" xml:space="preserve"><value>Le nombre saisi est trop grand</value></data>
+  <data name="UserSelectionTooSmall" xml:space="preserve"><value>Le nombre saisi est trop petit</value></data>
+  <data name="AuthTokenHostHeader" xml:space="preserve"><value>Hôte</value></data>
+  <data name="AuthTokenTokenHeader" xml:space="preserve"><value>Jeton</value></data>
+  <data name="AuthTokenHelpSummary" xml:space="preserve"><value>Gérer les jetons d'authentification</value></data>
+  <data name="AvailableHeader" xml:space="preserve"><value>Modules compatible avec {0} {1}</value></data>
+  <data name="CacheHelpSummary" xml:space="preserve"><value>Gérer le chemin du cache des téléchargements de CKAN</value></data>
+  <data name="CacheSet" xml:space="preserve"><value>Cache des téléchargements réglé à {0}</value></data>
+  <data name="CacheInvalidPath" xml:space="preserve"><value>Chemin invalide : {0}</value></data>
+  <data name="CacheCleared" xml:space="preserve"><value>Cache des téléchargements nettoyé</value></data>
+  <data name="CacheReset" xml:space="preserve"><value>Cache des téléchargements réinitialisé à {0}</value></data>
+  <data name="CacheResetFailed" xml:space="preserve"><value>Impossible de réinitialiser le chemin du cache : {0}</value></data>
+  <data name="CacheUnlimited" xml:space="preserve"><value>Illimité</value></data>
+  <data name="CacheInfo" xml:space="preserve"><value>{0} fichiers, {1}</value></data>
+  <data name="CompareSame" xml:space="preserve"><value>"{0}" et "{1}" ont les mêmes versions.</value></data>
+  <data name="CompareLower" xml:space="preserve"><value>"{0}" est plus bas que "{1}".</value></data>
+  <data name="CompareHigher" xml:space="preserve"><value>"{0}" est plus haut que "{1}".</value></data>
+  <data name="CompatHelpSummary" xml:space="preserve"><value>Gérer les versions de jeu compatibles</value></data>
+  <data name="CompatVersionHeader" xml:space="preserve"><value>Version</value></data>
+  <data name="CompatActualHeader" xml:space="preserve"><value>Actuelle</value></data>
+  <data name="CompatInvalid" xml:space="preserve"><value>ERREUR : Version de jeu invalide</value></data>
+  <data name="CompatCantForget" xml:space="preserve"><value>ERREUR : Impossible d'oublier la version de jeu actuelle</value></data>
+  <data name="InstanceHelpSummary" xml:space="preserve"><value>Gérer les instances de jeu</value></data>
+  <data name="InstanceListNoVersion" xml:space="preserve"><value>&lt;AUCUNE&gt;</value></data>
+  <data name="InstanceListYes" xml:space="preserve"><value>Oui</value></data>
+  <data name="InstanceListNo" xml:space="preserve"><value>Non</value></data>
+  <data name="InstanceListNameHeader" xml:space="preserve"><value>Nom</value></data>
+  <data name="InstanceListVersionHeader" xml:space="preserve"><value>Version</value></data>
+  <data name="InstanceListDefaultHeader" xml:space="preserve"><value>Défaut</value></data>
+  <data name="InstanceListPathHeader" xml:space="preserve"><value>Chemin</value></data>
+  <data name="InstanceAddDuplicate" xml:space="preserve"><value>Une installation avec le nom "{0}" existe déjà, annulation</value></data>
+  <data name="InstanceAdded" xml:space="preserve"><value>Ajout de "{0}" de racine "{1}" aux installations connues</value></data>
+  <data name="InstanceCloneNotFound" xml:space="preserve"><value>Aucune instance avec ce nom ou avec ce chemin : {0}
+Voir ci-dessous une liste des instances connues :
+</value></data>
+  <data name="InstanceCloneFailed" xml:space="preserve"><value>Quelque chose s'est mal passé. Veuillez vérifier que le nouveau dossier a bien été créé.
+Essayez d'ajouter l'instance manuellement avec "ckan instance add".
+</value></data>
+  <data name="InstanceNotFound" xml:space="preserve"><value>Impossible de trouver une installation avec le nom "{0}", annulation</value></data>
+  <data name="InstanceRenamed" xml:space="preserve"><value>"{0}" a été renommée "{1}" avec succès</value></data>
+  <data name="InstanceForgot" xml:space="preserve"><value>"{0}" a été retirée avec succès</value></data>
+  <data name="InstanceDefaultArgumentMissing" xml:space="preserve"><value>argument manquant, veuillez sélectionnez depuis la liste ci-dessous</value></data>
+  <data name="InstanceDefaultSet" xml:space="preserve"><value>"{0}" a été définie comme instance de jeu par défaut</value></data>
+  <data name="InstanceFakeFailed" xml:space="preserve"><value>Quelque chose s'est mal passé. Essayez d'ajouter l'instance manuellement avec "ckan instance add". Vérifiez aussi si le nouveau dossier a bien été créé.</value></data>
+  <data name="InstanceFakeBadArguments" xml:space="preserve"><value>--Erreur: mauvais argument·s--</value></data>
+  <data name="InstanceFakeMakingHistory" xml:space="preserve"><value>Veuillez vérifier l'argument de version du DLC Making History - Le format est du type Maj.Min.Patch - ex: 1.1.0</value></data>
+  <data name="InstanceFakeBreakingGround" xml:space="preserve"><value>Veuillez vérifier l'argument de version du DLC Breaking Ground - Le format est du type Maj.Min.Patch - ex: 1.1.0</value></data>
+  <data name="InstanceFakeVersion" xml:space="preserve"><value>Veuillez vérifier l'argument de version - Le format est du type Maj.Min.Patch[.Build] - ex: 1.6.0 ou 1.2.2.1622</value></data>
+  <data name="InstanceFakeBadGameVersion" xml:space="preserve"><value>Impossible de trouver une version de jeu valide pour votre saisie.
+Soyez sûr d'avoir saisi au moins les valeurs de version majeure et mineure au format Maj.Min - ex: 1.5</value></data>
+  <data name="InstanceFakeCancelled" xml:space="preserve"><value>Sélection annulée ! Veuillez saisir 'ckan instance fake' à nouveau</value></data>
+  <data name="InstanceFakeCreating" xml:space="preserve"><value>Création d'une instance de jeu artificielle {0} à {1} de version {2}</value></data>
+  <data name="InstanceFakeDefault" xml:space="preserve"><value>Définition de la nouvelle instance comme par défaut...</value></data>
+  <data name="InstanceFakeDone" xml:space="preserve"><value>--Fini--</value></data>
+  <data name="ImportError" xml:space="preserve"><value>Erreur d'importation : {0}</value></data>
+  <data name="ImportNotFound" xml:space="preserve"><value>Fichier introuvable : {0}</value></data>
+  <data name="InstallNotFound" xml:space="preserve"><value>Fichier introuvable, sortie : {0}</value></data>
+  <data name="InstallTryAgain" xml:space="preserve"><value>Si vous êtes chanceux, vous pouvez taper `ckan update` et réessayer.
+Essayez `ckan install --no-recommends` pour ignorer l'installation des modules recommandés.
+Ou `ckan install --allow-incompatible` pour ignorer la compatibilité des modules.</value></data>
+  <data name="InstallUnversionedDependencyNotSatisfied" xml:space="preserve"><value>Le module {0} est requis mais il n'est pas listé dans l'index, ou indisponible pour votre version de {1}</value></data>
+  <data name="InstallVersionedDependencyNotSatisfied" xml:space="preserve"><value>Le module {0} {1} est requis mais il n'est pas listé dans l'index, ou indisponible pour votre version de {2}</value></data>
+  <data name="InstallBadMetadata" xml:space="preserve"><value>Mauvaises métadonnées détectées pour le module {0} : {1}</value></data>
+  <data name="InstallFileConflictOwned" xml:space="preserve"><value>Oh non ! Nous avons essayé de remplacer un fichier appartenant à un autre mod !
+Veuillez taper `ckan update` puis réessayez.
+
+Si ce problème se produit de nouveau, c'est peut-être un bogue d'emballage.
+Veuillez le signaler à :
+
+https://github.com/KSP-CKAN/NetKAN/issues/new
+
+Veuillez inclure les informations suivantes dans votre rapport :
+
+Fichier             : {0}
+Mod en installation : {1}
+Mod propriétaire    : {2}
+Version CKAN        : {3}
+</value></data>
+  <data name="InstallFileConflictUnowned" xml:space="preserve"><value>Oh non !
+
+On dirait que vous essayez d'installer un mod qui est déjà installé,
+ou qui est en conflit avec un autre mod déjà installé.
+
+Par mesure de précaution, CKAN ne remplace ou n'altère *jamais*
+un fichier qu'il n'a pas installé lui-même.
+
+Si vous souhaitez installer {0} avec CKAN,
+alors veuillez désinstaller manuellement le mod auquel appartient :
+
+{1}
+
+et réessayez.
+</value></data>
+  <data name="InstallGamedataReturned" xml:space="preserve"><value>Votre {0} a été remis dans son état original</value></data>
+  <data name="InstallCancelled" xml:space="preserve"><value>Installation annulée. Vos fichiers ont remis dans leur état initial.</value></data>
+  <data name="InstallAborted" xml:space="preserve"><value>Installation annulée : {0}</value></data>
+  <data name="InstallTryAuthToken" xml:space="preserve"><value>Essayez la commande authtoken . Regardez {0} pour plus de détails.</value></data>
+  <data name="InstallDownloadFailed" xml:space="preserve"><value>Un ou plusieurs fichier·s n'ont pu être téléchargés, arrêt</value></data>
+  <data name="InstallDLC" xml:space="preserve"><value>CKAN ne peut pas installer l'extension '{0}' pour vous.</value></data>
+  <data name="InstallDLCStorePage" xml:space="preserve"><value>Pour installer cette extension, achetez-la depuis l'une de ces boutiques :
+{0}</value></data>
+  <data name="ListUnknownFormat" xml:space="preserve"><value>Format d'exportation inconnu : {0}</value></data>
+  <data name="ListGameFound" xml:space="preserve"><value>{0} trouvé à : {1}</value></data>
+  <data name="ListGameVersion" xml:space="preserve"><value>Version de {0} : {1}</value></data>
+  <data name="ListGameModulesHeader" xml:space="preserve"><value>Modules installés :</value></data>
+  <data name="ListLegend" xml:space="preserve"><value>Légende : -: À jour. +: Auto-installé. X: Incompatible. ^: À mettre à jour. >: Remplaçable.
+        A: Auto-détecté. ?: Inconnu. *: Cassé.</value></data>
+  <data name="MarkHelpSummary" xml:space="preserve"><value>Éditer les attributs des modules</value></data>
+  <data name="MarkUnknownCommand" xml:space="preserve"><value>Commande inconnue : mark {0}</value></data>
+  <data name="MarkNotInstalled" xml:space="preserve"><value>{0} n'est pas installé</value></data>
+  <data name="MarkAlready" xml:space="preserve"><value>{0} est déjà marqué comme {1}</value></data>
+  <data name="Marking" xml:space="preserve"><value>Marquage de {0} comme {1}...</value></data>
+  <data name="MarkDLC" xml:space="preserve"><value>Impossible de marquer l'extension '{0}' comme auto-installé</value></data>
+  <data name="MarkChanged" xml:space="preserve"><value>Changements complétés !</value></data>
+  <data name="MarkAutoInstalled" xml:space="preserve"><value>auto-installé</value></data>
+  <data name="MarkUserSelected" xml:space="preserve"><value>Sélectionné par l'utilisateur</value></data>
+  <data name="PromptWelcome" xml:space="preserve"><value>Bienvenue sur CKAN!
+
+Pour obtenir de l'aide, tapez help et appuyez sur entré. Pour quitter, tapez {0} et appuyez sur entré.
+</value></data>
+  <data name="PromptWithInstance" xml:space="preserve"><value>CKAN {0} : {1} {2} ({3})&gt; </value></data>
+  <data name="PromptWithoutInstance" xml:space="preserve"><value>CKAN {0}&gt; </value></data>
+  <data name="RemoveNotInstalled" xml:space="preserve"><value>Impossible, {0} n'est pas installé.
+Essayez `ckan list` pour une liste des mods installés.</value></data>
+  <data name="RemoveDLC" xml:space="preserve"><value>CKAN ne peut pas retirer l'extension '{0}' pour vous.</value></data>
+  <data name="RemoveDLCStorePage" xml:space="preserve"><value>Pour retirer cette extension, suivez les instructions pour la boutique depuis laquelle vous l'avez achetée :
+{0}</value></data>
+  <data name="RemoveCancelled" xml:space="preserve"><value>Retrait annulé : {0}</value></data>
+  <data name="RemoveNothing" xml:space="preserve"><value>Aucun mod sélectionné, rien à faire</value></data>
+  <data name="RepairHelpSummary" xml:space="preserve"><value>Essayer des réparations automatiques diverses</value></data>
+  <data name="RepairUnknownCommand" xml:space="preserve"><value>Commande inconnue : repair {0}</value></data>
+  <data name="Repaired" xml:space="preserve"><value>Les réparations du registre ont été tentées. Nous espérons qu'elles ont marché.</value></data>
+  <data name="ReplaceModuleNotFound" xml:space="preserve"><value>Module {0} introuvable</value></data>
+  <data name="Replacing" xml:space="preserve"><value>Remplacement des modules...</value></data>
+  <data name="ReplaceFound" xml:space="preserve"><value>Le remplacement {0} {1} a été trouvé pour {2} {3}</value></data>
+  <data name="ReplaceContinuePrompt" xml:space="preserve"><value>Continuer ?</value></data>
+  <data name="ReplaceCancelled" xml:space="preserve"><value>Remplacements annulés à la demande de l'utilisateur</value></data>
+  <data name="ReplaceDependencyNotSatisfied" xml:space="preserve"><value>Dépendances insatisfaites pour le remplacement, {0} nécessite {1} {2} mais il n'est pas listé dans l'index, ou indisponible pour votre version de {3}</value></data>
+  <data name="ReplaceNotFound" xml:space="preserve"><value>Aucun remplacement trouvé</value></data>
+  <data name="RepoHelpSummary" xml:space="preserve"><value>Gérer les répertoires CKAN</value></data>
+  <data name="RepoUnknownCommand" xml:space="preserve"><value>Commande inconnue : repo {0}</value></data>
+  <data name="RepoAvailableHeader" xml:space="preserve"><value>Liste de tous les répertoires officiels de CKAN :</value></data>
+  <data name="RepoAvailableFailed" xml:space="preserve"><value>Impossible d'obtenir la liste maîtresse des répertoires CKAN depuis {0}</value></data>
+  <data name="RepoListHeader" xml:space="preserve"><value>Liste de tous les répertoires connus :</value></data>
+  <data name="RepoAddNotFound" xml:space="preserve"><value>Le nom {0} n'a pas été trouvé dans la liste maîtresse, veuillez fournir un nom et une adresse</value></data>
+  <data name="RepoAddDuplicate" xml:space="preserve"><value>Un répertoire avec le nom "{0}" existe déjà, annulation</value></data>
+  <data name="RepoAdded" xml:space="preserve"><value>Ajout du répertoire '{0}' - '{1}'</value></data>
+  <data name="RepoForgetNotFound" xml:space="preserve"><value>Impossible de trouver un répertoire avec le nom "{0}", annulation</value></data>
+  <data name="RepoForgetRemoving" xml:space="preserve"><value>Retrait du résultat insensible à la casse "{0}"</value></data>
+  <data name="RepoForgetRemoved" xml:space="preserve"><value>Retrait de "{0}" avec succès</value></data>
+  <data name="RepoSet" xml:space="preserve"><value>Mettre {0} le répertoire à '{1}'</value></data>
+  <data name="SearchNoTerm" xml:space="preserve"><value>Aucun terme à chercher ?</value></data>
+  <data name="SearchFoundByAuthorWithIncompat" xml:space="preserve"><value>{0} mod·s compatible·s et {1} mod·s incompatible·s correspondent à "{2}" par "{3}"</value></data>
+  <data name="SearchFoundWithIncompat" xml:space="preserve"><value>{0} mod·s compatible·s et {1} mod·s incompatible·s correspondent à "{2}"</value></data>
+  <data name="SearchFoundByAuthor" xml:space="preserve"><value>{0} mod·s compatible·s correspond·ent à "{1}" par "{2}"</value></data>
+  <data name="SearchFound" xml:space="preserve"><value>{0} mod·s compatible·s correspond·ent à "{1}"</value></data>
+  <data name="SearchCompatibleModsHeader" xml:space="preserve"><value>Mods compatibles correspondants :</value></data>
+  <data name="SearchCompatibleMod" xml:space="preserve"><value>* {0} ({1}) - {2} par {3} - {4}</value></data>
+  <data name="SearchIncompatibleModsHeader" xml:space="preserve"><value>Mods incompatibles correspondants :</value></data>
+  <data name="SearchIncompatibleMod" xml:space="preserve"><value>* {0} ({1} - {2}) - {3} par {4} - {5}</value></data>
+  <data name="ShowNotInstalledOrCompatible" xml:space="preserve"><value>{0} n'est pas installé ou incompatible avec {1} {2}</value></data>
+  <data name="ShowLookingForClose" xml:space="preserve"><value>Recherche de concordances proches parmi les mods compatibles...</value></data>
+  <data name="ShowNoClose" xml:space="preserve"><value>Aucune concordance proche trouvée</value></data>
+  <data name="ShowFoundOne" xml:space="preserve"><value>Une correspondance proche a été trouvée : {0}</value></data>
+  <data name="ShowClosePrompt" xml:space="preserve"><value>Correspondances proches :</value></data>
+  <data name="ShowFilesHeader" xml:space="preserve"><value>Affichage de {0} fichiers installés :</value></data>
+  <data name="ShowModuleInfoHeader" xml:space="preserve"><value>Info du module :</value></data>
+  <data name="ShowVersion" xml:space="preserve"><value>  Version :	{0}</value></data>
+  <data name="ShowAuthor" xml:space="preserve"><value>  Auteur·s :	{0}</value></data>
+  <data name="ShowAuthorUnknown" xml:space="preserve"><value>  Auteur :	INCONNU</value></data>
+  <data name="ShowStatus" xml:space="preserve"><value>  Statut :	{0}</value></data>
+  <data name="ShowLicence" xml:space="preserve"><value>  Licence :	{0}</value></data>
+  <data name="ShowTags" xml:space="preserve"><value>  Étiquettes : 	{0}</value></data>
+  <data name="ShowLanguages" xml:space="preserve"><value>  Langues :	{0}</value></data>
+  <data name="ShowDependsHeader" xml:space="preserve"><value>Dépend de :</value></data>
+  <data name="ShowRecommendsHeader" xml:space="preserve"><value>Recommande :</value></data>
+  <data name="ShowSuggestsHeader" xml:space="preserve"><value>Suggère :</value></data>
+  <data name="ShowProvidesHeader" xml:space="preserve"><value>Fournit :</value></data>
+  <data name="ShowResourcesHeader" xml:space="preserve"><value>Ressources :</value></data>
+  <data name="ShowHomePage" xml:space="preserve"><value>  Page principale :	{0}</value></data>
+  <data name="ShowManual" xml:space="preserve"><value>  Manuel :	{0}</value></data>
+  <data name="ShowSpaceDock" xml:space="preserve"><value>  SpaceDock :	{0}</value></data>
+  <data name="ShowRepository" xml:space="preserve"><value>  Répertoire :	{0}</value></data>
+  <data name="ShowBugTracker" xml:space="preserve"><value>  Traqueur de bogues :	{0}</value></data>
+  <data name="ShowCurse" xml:space="preserve"><value>  Curse :	{0}</value></data>
+  <data name="ShowStore" xml:space="preserve"><value>  Boutique :	{0}</value></data>
+  <data name="ShowSteamStore" xml:space="preserve"><value>  Magasin Steam :	{0}</value></data>
+  <data name="ShowVersionFile" xml:space="preserve"><value>  Fichier Version :	{0}</value></data>
+  <data name="ShowFileName" xml:space="preserve"><value>Nom du fichier : {0}</value></data>
+  <data name="ShowVersionHeader" xml:space="preserve"><value>Version</value></data>
+  <data name="ShowGameVersionsHeader" xml:space="preserve"><value>Versions de Jeu</value></data>
+  <data name="UpdateChangesSummary" xml:space="preserve"><value>{0} nouveaux modules, {1} modules retirés and {2} modules mis à jour ont été trouvés</value></data>
+  <data name="UpdateAddedHeader" xml:space="preserve"><value>Nouveaux modules [Nom (identifiant CKAN)] :</value></data>
+  <data name="UpdateRemovedHeader" xml:space="preserve"><value>Modules retirés [Nom (identifiant CKAN)] :</value></data>
+  <data name="UpdateUpdatedHeader" xml:space="preserve"><value>Modules mis à jour [Nom (identifiant CKAN)] :</value></data>
+  <data name="UpdateSummary" xml:space="preserve"><value>Informations sur {0} modules compatibles mises à jour</value></data>
+  <data name="UpgradeQueryingCKAN" xml:space="preserve"><value>Requête de la dernière version CKAN</value></data>
+  <data name="UpgradeNewCKANAvailable" xml:space="preserve"><value>Nouvelle version de CKAN disponible - {0}</value></data>
+  <data name="UpgradeProceed" xml:space="preserve"><value>Poursuivre l'installation ?</value></data>
+  <data name="UpgradePleaseWait" xml:space="preserve"><value>Mise à niveau de CKAN, veuillez patienter...</value></data>
+  <data name="UpgradeAlreadyHaveLatest" xml:space="preserve"><value>Vous avez déjà la dernière version</value></data>
+  <data name="UpgradeAborted" xml:space="preserve"><value>Mise à niveau annulée : {0}</value></data>
+  <data name="UpgradeNotFound" xml:space="preserve"><value>Le module {0} est introuvable</value></data>
+  <data name="UpgradeDLC" xml:space="preserve"><value>CKAN ne peut pas mettre à niveau l'extension '{0}' pour vous</value></data>
+  <data name="UpgradeDLCStorePage" xml:space="preserve"><value>Pour mettre à niveau cette extension, téléchargez les éventuelles mises à jour depuis la boutique où vous l'avez achetée :
+{0}</value></data>
+  <data name="ScanNotSaved" xml:space="preserve"><value>Le répertoire n'a pas été sauvegardé</value></data>
+  <data name="ScanPreliminaryInconsistent" xml:space="preserve"><value>L'analyse préliminaire montre que l'installation est dans un état incohérent.
+Utilisez ckan.exe scan pour plus de détails
+Exécution de {0} au cas où cela réglerai le problème.</value></data>
+  <data name="FilterListGlobalHeader" xml:space="preserve"><value>Filtres globaux :</value></data>
+  <data name="FilterListInstanceHeader" xml:space="preserve"><value>Filtre d'instance Instance :</value></data>
+  <data name="FilterAddGlobalDuplicateError" xml:space="preserve"><value>Filtres globaux existant déjà : {0}</value></data>
+  <data name="FilterAddInstanceDuplicateError" xml:space="preserve"><value>Filtres d'instance existant déjà : {0}</value></data>
+  <data name="FilterRemoveGlobalNotFoundError" xml:space="preserve"><value>Filtres globaux introuvables : {0}</value></data>
+  <data name="FilterRemoveInstanceNotFoundError" xml:space="preserve"><value>Filtres d'instance introuvables : {0}</value></data>
+  <data name="FilterHelpSummary" xml:space="preserve"><value>Afficher ou modifier les filtres d'installation</value></data>
+</root>
diff --git a/Cmdline/Properties/Resources.resx b/Cmdline/Properties/Resources.resx
new file mode 100644
index 0000000000..10ab523c94
--- /dev/null
+++ b/Cmdline/Properties/Resources.resx
@@ -0,0 +1,372 @@
+<?xml version="1.0" encoding="utf-8"?>
+<root>
+  <!--
+    Microsoft ResX Schema
+
+    Version 2.0
+
+    The primary goals of this format is to allow a simple XML format
+    that is mostly human readable. The generation and parsing of the
+    various data types are done through the TypeConverter classes
+    associated with the data types.
+
+    Example:
+
+    ... ado.net/XML headers & schema ...
+    <resheader name="resmimetype">text/microsoft-resx</resheader>
+    <resheader name="version">2.0</resheader>
+    <resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
+    <resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
+    <data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
+    <data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
+    <data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
+        <value>[base64 mime encoded serialized .NET Framework object]</value>
+    </data>
+    <data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
+        <value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
+        <comment>This is a comment</comment>
+    </data>
+
+    There are any number of "resheader" rows that contain simple
+    name/value pairs.
+
+    Each data row contains a name, and value. The row also contains a
+    type or mimetype. Type corresponds to a .NET class that support
+    text/value conversion through the TypeConverter architecture.
+    Classes that don't support this are serialized and stored with the
+    mimetype set.
+
+    The mimetype is used for serialized objects, and tells the
+    ResXResourceReader how to depersist the object. This is currently not
+    extensible. For a given mimetype the value must be set accordingly:
+
+    Note - application/x-microsoft.net.object.binary.base64 is the format
+    that the ResXResourceWriter will generate, however the reader can
+    read any of the formats listed below.
+
+    mimetype: application/x-microsoft.net.object.binary.base64
+    value   : The object must be serialized with
+            : System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
+            : and then encoded with base64 encoding.
+
+    mimetype: application/x-microsoft.net.object.soap.base64
+    value   : The object must be serialized with
+            : System.Runtime.Serialization.Formatters.Soap.SoapFormatter
+            : and then encoded with base64 encoding.
+
+    mimetype: application/x-microsoft.net.object.bytearray.base64
+    value   : The object must be serialized into a byte array
+            : using a System.ComponentModel.TypeConverter
+            : and then encoded with base64 encoding.
+    -->
+  <xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
+    <xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
+    <xsd:element name="root" msdata:IsDataSet="true">
+      <xsd:complexType>
+        <xsd:choice maxOccurs="unbounded">
+          <xsd:element name="metadata">
+            <xsd:complexType>
+              <xsd:sequence>
+                <xsd:element name="value" type="xsd:string" minOccurs="0" />
+              </xsd:sequence>
+              <xsd:attribute name="name" use="required" type="xsd:string" />
+              <xsd:attribute name="type" type="xsd:string" />
+              <xsd:attribute name="mimetype" type="xsd:string" />
+              <xsd:attribute ref="xml:space" />
+            </xsd:complexType>
+          </xsd:element>
+          <xsd:element name="assembly">
+            <xsd:complexType>
+              <xsd:attribute name="alias" type="xsd:string" />
+              <xsd:attribute name="name" type="xsd:string" />
+            </xsd:complexType>
+          </xsd:element>
+          <xsd:element name="data">
+            <xsd:complexType>
+              <xsd:sequence>
+                <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
+                <xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
+              </xsd:sequence>
+              <xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
+              <xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
+              <xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
+              <xsd:attribute ref="xml:space" />
+            </xsd:complexType>
+          </xsd:element>
+          <xsd:element name="resheader">
+            <xsd:complexType>
+              <xsd:sequence>
+                <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
+              </xsd:sequence>
+              <xsd:attribute name="name" type="xsd:string" use="required" />
+            </xsd:complexType>
+          </xsd:element>
+        </xsd:choice>
+      </xsd:complexType>
+    </xsd:element>
+  </xsd:schema>
+  <resheader name="resmimetype">
+    <value>text/microsoft-resx</value>
+  </resheader>
+  <resheader name="version">
+    <value>2.0</value>
+  </resheader>
+  <resheader name="reader">
+    <value>System.Resources.ResXResourceReader, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
+  </resheader>
+  <resheader name="writer">
+    <value>System.Resources.ResXResourceWriter, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
+  </resheader>
+  <data name="Usage" xml:space="preserve"><value>Usage</value></data>
+  <data name="Command" xml:space="preserve"><value>command</value></data>
+  <data name="UnknownCommand" xml:space="preserve"><value>Unknown command</value></data>
+  <data name="Options" xml:space="preserve"><value>options</value></data>
+  <data name="ArgumentMissing" xml:space="preserve"><value>argument missing, perhaps you forgot it?</value></data>
+  <data name="Path" xml:space="preserve"><value>path</value></data>
+  <data name="MainVersion" xml:space="preserve"><value>You are using CKAN version {0}</value></data>
+  <data name="MainUnknownCommand" xml:space="preserve"><value>Unknown command, try --help</value></data>
+  <data name="MainMissingInstance" xml:space="preserve"><value>I don't know where a game instance is installed.
+Use 'ckan instance help' for assistance in setting this.</value></data>
+  <data name="OptionsRootError" xml:space="preserve"><value>You are trying to run CKAN as root.
+This is a bad idea and there is absolutely no good reason to do it. Please run CKAN from a user account (or use --asroot if you are feeling brave).</value></data>
+  <data name="OptionsRootWarning" xml:space="preserve"><value>Warning: Running CKAN as root!</value></data>
+  <data name="OptionsMonoWarning" xml:space="preserve"><value>Warning. Detected mono runtime of {0} is less than the recommended version of {1}.
+Update recommended!</value></data>
+  <data name="OptionsInstanceAndGameDir" xml:space="preserve"><value>--instance and --gamedir can't be specified at the same time</value></data>
+  <data name="OptionsInvalidInstance" xml:space="preserve"><value>Invalid game instance specified "{0}", use '--gamedir' to specify by path, or 'instance list' to see known game instances</value></data>
+  <data name="InstanceNotInstance" xml:space="preserve"><value>Sorry, {0} does not appear to be a game instance</value></data>
+  <data name="InstanceDuplicate" xml:space="preserve"><value>This instance name is already taken: {0}</value></data>
+  <data name="UserYesNoPromptSuffix" xml:space="preserve"><value>[Y/n]</value></data>
+  <data name="UserYesNoY" xml:space="preserve"><value>y</value></data>
+  <data name="UserYesNoYes" xml:space="preserve"><value>yes</value></data>
+  <data name="UserYesNoN" xml:space="preserve"><value>n</value></data>
+  <data name="UserYesNoNo" xml:space="preserve"><value>no</value></data>
+  <data name="UserYesNoInvalid" xml:space="preserve"><value>Invalid input. Please enter yes or no</value></data>
+  <data name="UserSelectionPromptWithDefault" xml:space="preserve"><value>Enter a number between {0} and {1} (To cancel press "c" or "n". "Enter" will select {2}.): </value></data>
+  <data name="UserSelectionPromptWithoutDefault" xml:space="preserve"><value>Enter a number between {0} and {1} (To cancel press "c" or "n".): </value></data>
+  <data name="UserSelectionC" xml:space="preserve"><value>c</value></data>
+  <data name="UserSelectionN" xml:space="preserve"><value>n</value></data>
+  <data name="UserSelectionCancelled" xml:space="preserve"><value>Selection cancelled</value></data>
+  <data name="UserSelectionNotNumber" xml:space="preserve"><value>The input is not a number</value></data>
+  <data name="UserSelectionTooLarge" xml:space="preserve"><value>The number in the input is too large</value></data>
+  <data name="UserSelectionTooSmall" xml:space="preserve"><value>The number in the input is too small</value></data>
+  <data name="AuthTokenHostHeader" xml:space="preserve"><value>Host</value></data>
+  <data name="AuthTokenTokenHeader" xml:space="preserve"><value>Token</value></data>
+  <data name="AuthTokenHelpSummary" xml:space="preserve"><value>Manage authentication tokens</value></data>
+  <data name="AvailableHeader" xml:space="preserve"><value>Modules compatible with {0} {1}</value></data>
+  <data name="CacheHelpSummary" xml:space="preserve"><value>Manage the download cache path of CKAN</value></data>
+  <data name="CacheSet" xml:space="preserve"><value>Download cache set to {0}</value></data>
+  <data name="CacheInvalidPath" xml:space="preserve"><value>Invalid path: {0}</value></data>
+  <data name="CacheCleared" xml:space="preserve"><value>Download cache cleared</value></data>
+  <data name="CacheReset" xml:space="preserve"><value>Download cache reset to {0}</value></data>
+  <data name="CacheResetFailed" xml:space="preserve"><value>Can't reset cache path: {0}</value></data>
+  <data name="CacheUnlimited" xml:space="preserve"><value>Unlimited</value></data>
+  <data name="CacheInfo" xml:space="preserve"><value>{0} files, {1}</value></data>
+  <data name="CompareSame" xml:space="preserve"><value>"{0}" and "{1}" are the same versions.</value></data>
+  <data name="CompareLower" xml:space="preserve"><value>"{0}" is lower than "{1}".</value></data>
+  <data name="CompareHigher" xml:space="preserve"><value>"{0}" is higher than "{1}".</value></data>
+  <data name="CompatHelpSummary" xml:space="preserve"><value>Manage game version compatibility</value></data>
+  <data name="CompatVersionHeader" xml:space="preserve"><value>Version</value></data>
+  <data name="CompatActualHeader" xml:space="preserve"><value>Actual</value></data>
+  <data name="CompatInvalid" xml:space="preserve"><value>ERROR: Invalid game version</value></data>
+  <data name="CompatCantForget" xml:space="preserve"><value>ERROR: Cannot forget actual game version</value></data>
+  <data name="InstanceHelpSummary" xml:space="preserve"><value>Manage game instances</value></data>
+  <data name="InstanceListNoVersion" xml:space="preserve"><value>&lt;NONE&gt;</value></data>
+  <data name="InstanceListYes" xml:space="preserve"><value>Yes</value></data>
+  <data name="InstanceListNo" xml:space="preserve"><value>No</value></data>
+  <data name="InstanceListNameHeader" xml:space="preserve"><value>Name</value></data>
+  <data name="InstanceListVersionHeader" xml:space="preserve"><value>Version</value></data>
+  <data name="InstanceListDefaultHeader" xml:space="preserve"><value>Default</value></data>
+  <data name="InstanceListPathHeader" xml:space="preserve"><value>Path</value></data>
+  <data name="InstanceAddDuplicate" xml:space="preserve"><value>Install with name "{0}" already exists, aborting</value></data>
+  <data name="InstanceAdded" xml:space="preserve"><value>Added "{0}" with root "{1}" to known installs</value></data>
+  <data name="InstanceCloneNotFound" xml:space="preserve"><value>No instance with this name or at this path: {0}
+See below for a list of known instances:
+</value></data>
+  <data name="InstanceCloneFailed" xml:space="preserve"><value>Something went wrong. Please look if the new directory has been created.
+Try to add the new instance manually with "ckan instance add".
+</value></data>
+  <data name="InstanceNotFound" xml:space="preserve"><value>Couldn't find install with name "{0}", aborting</value></data>
+  <data name="InstanceRenamed" xml:space="preserve"><value>Successfully renamed "{0}" to "{1}"</value></data>
+  <data name="InstanceForgot" xml:space="preserve"><value>Successfully Removed "{0}"</value></data>
+  <data name="InstanceDefaultArgumentMissing" xml:space="preserve"><value>argument missing, please select from the list below</value></data>
+  <data name="InstanceDefaultSet" xml:space="preserve"><value>Successfully set "{0}" as the default game instance</value></data>
+  <data name="InstanceFakeFailed" xml:space="preserve"><value>Something went wrong. Try to add the instance yourself with "ckan instance add". Also look if the new directory has been created.</value></data>
+  <data name="InstanceFakeBadArguments" xml:space="preserve"><value>--Error: bad argument(s)--</value></data>
+  <data name="InstanceFakeMakingHistory" xml:space="preserve"><value>Please check the Making History DLC version argument - Format it like Maj.Min.Patch - e.g. 1.1.0</value></data>
+  <data name="InstanceFakeBreakingGround" xml:space="preserve"><value>Please check the Breaking Ground DLC version argument - Format it like Maj.Min.Patch - e.g. 1.1.0</value></data>
+  <data name="InstanceFakeVersion" xml:space="preserve"><value>Please check the version argument - Format it like Maj.Min.Patch[.Build] - e.g. 1.6.0 or 1.2.2.1622</value></data>
+  <data name="InstanceFakeBadGameVersion" xml:space="preserve"><value>Couldn't find a valid game version for your input.
+Make sure to enter at least the version major and minor values in the form Maj.Min - e.g. 1.5</value></data>
+  <data name="InstanceFakeCancelled" xml:space="preserve"><value>Selection cancelled! Please call 'ckan instance fake' again</value></data>
+  <data name="InstanceFakeCreating" xml:space="preserve"><value>Creating new fake game instance {0} at {1} with version {2}</value></data>
+  <data name="InstanceFakeDefault" xml:space="preserve"><value>Setting new instance to default...</value></data>
+  <data name="InstanceFakeDone" xml:space="preserve"><value>--Done--</value></data>
+  <data name="ImportError" xml:space="preserve"><value>Import error: {0}</value></data>
+  <data name="ImportNotFound" xml:space="preserve"><value>File not found: {0}</value></data>
+  <data name="InstallNotFound" xml:space="preserve"><value>File not found, exiting: {0}</value></data>
+  <data name="InstallTryAgain" xml:space="preserve"><value>If you're lucky, you can do a `ckan update` and try again.
+Try `ckan install --no-recommends` to skip installation of recommended modules.
+Or `ckan install --allow-incompatible` to ignore module compatibility.</value></data>
+  <data name="InstallUnversionedDependencyNotSatisfied" xml:space="preserve"><value>Module {0} required but it is not listed in the index, or not available for your version of {1}</value></data>
+  <data name="InstallVersionedDependencyNotSatisfied" xml:space="preserve"><value>Module {0} {1} required but it is not listed in the index, or not available for your version of {2}</value></data>
+  <data name="InstallBadMetadata" xml:space="preserve"><value>Bad metadata detected for module {0}: {1}</value></data>
+  <data name="InstallFileConflictOwned" xml:space="preserve"><value>Oh no! We tried to overwrite a file owned by another mod!
+Please try a `ckan update` and try again.
+
+If this problem re-occurs, then it maybe a packaging bug.
+Please report it at:
+
+https://github.com/KSP-CKAN/NetKAN/issues/new
+
+Please including the following information in your report:
+
+File           : {0}
+Installing Mod : {1}
+Owning Mod     : {2}
+CKAN Version   : {3}
+</value></data>
+  <data name="InstallFileConflictUnowned" xml:space="preserve"><value>Oh no!
+
+It looks like you're trying to install a mod which is already installed,
+or which conflicts with another mod which is already installed.
+
+As a safety feature, CKAN will *never* overwrite or alter a file
+that it did not install itself.
+
+If you wish to install {0} via CKAN,
+then please manually uninstall the mod which owns:
+
+{1}
+
+and try again.
+</value></data>
+  <data name="InstallGamedataReturned" xml:space="preserve"><value>Your {0} has been returned to its original state</value></data>
+  <data name="InstallCancelled" xml:space="preserve"><value>Install canceled. Your files have been returned to their initial state.</value></data>
+  <data name="InstallAborted" xml:space="preserve"><value>Installation aborted: {0}</value></data>
+  <data name="InstallTryAuthToken" xml:space="preserve"><value>Try the authtoken command. See {0} for details.</value></data>
+  <data name="InstallDownloadFailed" xml:space="preserve"><value>One or more files failed to download, stopped</value></data>
+  <data name="InstallDLC" xml:space="preserve"><value>CKAN can't install expansion '{0}' for you.</value></data>
+  <data name="InstallDLCStorePage" xml:space="preserve"><value>To install this expansion, purchase it from one of its store pages:
+{0}</value></data>
+  <data name="ListUnknownFormat" xml:space="preserve"><value>Unknown export format: {0}</value></data>
+  <data name="ListGameFound" xml:space="preserve"><value>{0} found at: {1}</value></data>
+  <data name="ListGameVersion" xml:space="preserve"><value>{0} version: {1}</value></data>
+  <data name="ListGameModulesHeader" xml:space="preserve"><value>Installed modules:</value></data>
+  <data name="ListLegend" xml:space="preserve"><value>Legend: -: Up to date. +:Auto-installed. X: Incompatible. ^: Upgradable. >: Replaceable
+        A: Autodetected. ?: Unknown. *: Broken.</value></data>
+  <data name="MarkHelpSummary" xml:space="preserve"><value>Edit flags on modules</value></data>
+  <data name="MarkUnknownCommand" xml:space="preserve"><value>Unknown command: mark {0}</value></data>
+  <data name="MarkNotInstalled" xml:space="preserve"><value>{0} is not installed</value></data>
+  <data name="MarkAlready" xml:space="preserve"><value>{0} is already marked as {1}</value></data>
+  <data name="Marking" xml:space="preserve"><value>Marking {0} as {1}...</value></data>
+  <data name="MarkDLC" xml:space="preserve"><value>Can't mark expansion '{0}' as auto-installed</value></data>
+  <data name="MarkChanged" xml:space="preserve"><value>Changes made!</value></data>
+  <data name="MarkAutoInstalled" xml:space="preserve"><value>auto-installed</value></data>
+  <data name="MarkUserSelected" xml:space="preserve"><value>user-selected</value></data>
+  <data name="PromptWelcome" xml:space="preserve"><value>Welcome to CKAN!
+
+To get help, type help and press enter. To quit, type {0} and press enter.
+Press tab to auto-complete commands and modules.
+</value></data>
+  <data name="PromptWithInstance" xml:space="preserve"><value>CKAN {0}: {1} {2} ({3})&gt; </value></data>
+  <data name="PromptWithoutInstance" xml:space="preserve"><value>CKAN {0}&gt; </value></data>
+  <data name="RemoveNotInstalled" xml:space="preserve"><value>I can't do that, {0} isn't installed.
+Try `ckan list` for a list of installed mods.</value></data>
+  <data name="RemoveDLC" xml:space="preserve"><value>CKAN can't remove expansion '{0}' for you.</value></data>
+  <data name="RemoveDLCStorePage" xml:space="preserve"><value>To remove this expansion, follow the instructions for the store page from which you purchased it:
+{0}</value></data>
+  <data name="RemoveCancelled" xml:space="preserve"><value>Remove aborted: {0}</value></data>
+  <data name="RemoveNothing" xml:space="preserve"><value>No mod selected, nothing to do</value></data>
+  <data name="RepairHelpSummary" xml:space="preserve"><value>Attempt various automatic repairs</value></data>
+  <data name="RepairUnknownCommand" xml:space="preserve"><value>Unknown command: repair {0}</value></data>
+  <data name="Repaired" xml:space="preserve"><value>Registry repairs attempted. Hope it helped.</value></data>
+  <data name="ReplaceModuleNotFound" xml:space="preserve"><value>Module {0} not found</value></data>
+  <data name="Replacing" xml:space="preserve"><value>Replacing modules...</value></data>
+  <data name="ReplaceFound" xml:space="preserve"><value>Replacement {0} {1} found for {2} {3}</value></data>
+  <data name="ReplaceContinuePrompt" xml:space="preserve"><value>Continue?</value></data>
+  <data name="ReplaceCancelled" xml:space="preserve"><value>Replacements canceled at user request</value></data>
+  <data name="ReplaceDependencyNotSatisfied" xml:space="preserve"><value>Dependencies not satisfied for replacement, {0} requires {1} {2} but it is not listed in the index, or not available for your version of {3}</value></data>
+  <data name="ReplaceNotFound" xml:space="preserve"><value>No replacements found</value></data>
+  <data name="RepoHelpSummary" xml:space="preserve"><value>Manage CKAN repositories</value></data>
+  <data name="RepoUnknownCommand" xml:space="preserve"><value>Unknown command: repo {0}</value></data>
+  <data name="RepoAvailableHeader" xml:space="preserve"><value>Listing all (canonical) available CKAN repositories:</value></data>
+  <data name="RepoAvailableFailed" xml:space="preserve"><value>Couldn't fetch CKAN repositories master list from {0}</value></data>
+  <data name="RepoListHeader" xml:space="preserve"><value>Listing all known repositories:</value></data>
+  <data name="RepoAddNotFound" xml:space="preserve"><value>Name {0} not found in master list, please provide name and uri</value></data>
+  <data name="RepoAddDuplicate" xml:space="preserve"><value>Repository with name "{0}" already exists, aborting</value></data>
+  <data name="RepoAdded" xml:space="preserve"><value>Added repository '{0}' - '{1}'</value></data>
+  <data name="RepoForgetNotFound" xml:space="preserve"><value>Couldn't find repository with name "{0}", aborting</value></data>
+  <data name="RepoForgetRemoving" xml:space="preserve"><value>Removing insensitive match "{0}"</value></data>
+  <data name="RepoForgetRemoved" xml:space="preserve"><value>Successfully removed "{0}"</value></data>
+  <data name="RepoSet" xml:space="preserve"><value>Set {0} repository to '{1}'</value></data>
+  <data name="SearchNoTerm" xml:space="preserve"><value>No search term?</value></data>
+  <data name="SearchFoundByAuthorWithIncompat" xml:space="preserve"><value>Found {0} compatible and {1} incompatible mods matching "{2}" by "{3}"</value></data>
+  <data name="SearchFoundWithIncompat" xml:space="preserve"><value>Found {0} compatible and {1} incompatible mods matching "{2}"</value></data>
+  <data name="SearchFoundByAuthor" xml:space="preserve"><value>Found {0} compatible mods matching "{1}" by "{2}"</value></data>
+  <data name="SearchFound" xml:space="preserve"><value>Found {0} compatible mods matching "{1}"</value></data>
+  <data name="SearchCompatibleModsHeader" xml:space="preserve"><value>Matching compatible mods:</value></data>
+  <data name="SearchCompatibleMod" xml:space="preserve"><value>* {0} ({1}) - {2} by {3} - {4}</value></data>
+  <data name="SearchIncompatibleModsHeader" xml:space="preserve"><value>Matching incompatible mods:</value></data>
+  <data name="SearchIncompatibleMod" xml:space="preserve"><value>* {0} ({1} - {2}) - {3} by {4} - {5}</value></data>
+  <data name="ShowNotInstalledOrCompatible" xml:space="preserve"><value>{0} not installed or compatible with {1} {2}</value></data>
+  <data name="ShowLookingForClose" xml:space="preserve"><value>Looking for close matches in compatible mods...</value></data>
+  <data name="ShowNoClose" xml:space="preserve"><value>No close matches found</value></data>
+  <data name="ShowFoundOne" xml:space="preserve"><value>Found 1 close match: {0}</value></data>
+  <data name="ShowClosePrompt" xml:space="preserve"><value>Close matches:</value></data>
+  <data name="ShowFilesHeader" xml:space="preserve"><value>Showing {0} installed files:</value></data>
+  <data name="ShowModuleInfoHeader" xml:space="preserve"><value>Module info:</value></data>
+  <data name="ShowVersion" xml:space="preserve"><value>  Version:	{0}</value></data>
+  <data name="ShowAuthor" xml:space="preserve"><value>  Authors:	{0}</value></data>
+  <data name="ShowAuthorUnknown" xml:space="preserve"><value>  Authors:	UNKNOWN</value></data>
+  <data name="ShowStatus" xml:space="preserve"><value>  Status:	{0}</value></data>
+  <data name="ShowLicence" xml:space="preserve"><value>  Licence:	{0}</value></data>
+  <data name="ShowTags" xml:space="preserve"><value>  Tags: 	{0}</value></data>
+  <data name="ShowLanguages" xml:space="preserve"><value>  Languages:	{0}</value></data>
+  <data name="ShowDependsHeader" xml:space="preserve"><value>Depends:</value></data>
+  <data name="ShowRecommendsHeader" xml:space="preserve"><value>Recommends:</value></data>
+  <data name="ShowSuggestsHeader" xml:space="preserve"><value>Suggests:</value></data>
+  <data name="ShowProvidesHeader" xml:space="preserve"><value>Provides:</value></data>
+  <data name="ShowResourcesHeader" xml:space="preserve"><value>Resources:</value></data>
+  <data name="ShowHomePage" xml:space="preserve"><value>  Home page:	{0}</value></data>
+  <data name="ShowManual" xml:space="preserve"><value>  Manual:	{0}</value></data>
+  <data name="ShowSpaceDock" xml:space="preserve"><value>  SpaceDock:	{0}</value></data>
+  <data name="ShowRepository" xml:space="preserve"><value>  Repository:	{0}</value></data>
+  <data name="ShowBugTracker" xml:space="preserve"><value>  Bug tracker:	{0}</value></data>
+  <data name="ShowCurse" xml:space="preserve"><value>  Curse:	{0}</value></data>
+  <data name="ShowStore" xml:space="preserve"><value>  Store:	{0}</value></data>
+  <data name="ShowSteamStore" xml:space="preserve"><value>  Steam store:	{0}</value></data>
+  <data name="ShowVersionFile" xml:space="preserve"><value>  Version file:	{0}</value></data>
+  <data name="ShowFileName" xml:space="preserve"><value>Filename: {0}</value></data>
+  <data name="ShowVersionHeader" xml:space="preserve"><value>Version</value></data>
+  <data name="ShowGameVersionsHeader" xml:space="preserve"><value>Game Versions</value></data>
+  <data name="UpdateChangesSummary" xml:space="preserve"><value>Found {0} new modules, {1} removed modules and {2} updated modules</value></data>
+  <data name="UpdateAddedHeader" xml:space="preserve"><value>New modules [Name (CKAN identifier)]:</value></data>
+  <data name="UpdateRemovedHeader" xml:space="preserve"><value>Removed modules [Name (CKAN identifier)]:</value></data>
+  <data name="UpdateUpdatedHeader" xml:space="preserve"><value>Updated modules [Name (CKAN identifier)]:</value></data>
+  <data name="UpdateSummary" xml:space="preserve"><value>Updated information on {0} compatible modules</value></data>
+  <data name="UpgradeQueryingCKAN" xml:space="preserve"><value>Querying the latest CKAN version</value></data>
+  <data name="UpgradeNewCKANAvailable" xml:space="preserve"><value>New CKAN version available - {0}</value></data>
+  <data name="UpgradeProceed" xml:space="preserve"><value>Proceed with install?</value></data>
+  <data name="UpgradePleaseWait" xml:space="preserve"><value>Upgrading CKAN, please wait...</value></data>
+  <data name="UpgradeAlreadyHaveLatest" xml:space="preserve"><value>You already have the latest version</value></data>
+  <data name="UpgradeAborted" xml:space="preserve"><value>Upgrade aborted: {0}</value></data>
+  <data name="UpgradeNotFound" xml:space="preserve"><value>Module {0} not found</value></data>
+  <data name="UpgradeDLC" xml:space="preserve"><value>CKAN can't upgrade expansion '{0}' for you</value></data>
+  <data name="UpgradeDLCStorePage" xml:space="preserve"><value>To upgrade this expansion, download any updates from the store page from which you purchased it:
+{0}</value></data>
+  <data name="ScanNotSaved" xml:space="preserve"><value>The repo has not been saved</value></data>
+  <data name="ScanPreliminaryInconsistent" xml:space="preserve"><value>Preliminary scanning shows that the install is in a inconsistent state.
+Use ckan.exe scan for more details
+Proceeding with {0} in case it fixes it.</value></data>
+  <data name="FilterListGlobalHeader" xml:space="preserve"><value>Global filters:</value></data>
+  <data name="FilterListInstanceHeader" xml:space="preserve"><value>Instance filters:</value></data>
+  <data name="FilterAddGlobalDuplicateError" xml:space="preserve"><value>Global filters already set: {0}</value></data>
+  <data name="FilterAddInstanceDuplicateError" xml:space="preserve"><value>Instance filters already set: {0}</value></data>
+  <data name="FilterRemoveGlobalNotFoundError" xml:space="preserve"><value>Global filters not found: {0}</value></data>
+  <data name="FilterRemoveInstanceNotFoundError" xml:space="preserve"><value>Instance filters not found: {0}</value></data>
+  <data name="FilterHelpSummary" xml:space="preserve"><value>View or edit installation filters</value></data>
+  <data name="CompletionNotAvailable" xml:space="preserve"><value>
+No game instance selected, identifier completion not available.
+Use the `instance default` command to choose an instance.</value></data>
+</root>
diff --git a/Cmdline/Properties/Resources.ru-RU.resx b/Cmdline/Properties/Resources.ru-RU.resx
new file mode 100644
index 0000000000..8197cf96bc
--- /dev/null
+++ b/Cmdline/Properties/Resources.ru-RU.resx
@@ -0,0 +1,361 @@
+<?xml version="1.0" encoding="utf-8"?>
+<root>
+  <!--
+    Microsoft ResX Schema
+
+    Version 2.0
+
+    The primary goals of this format is to allow a simple XML format
+    that is mostly human readable. The generation and parsing of the
+    various data types are done through the TypeConverter classes
+    associated with the data types.
+
+    Example:
+
+    ... ado.net/XML headers & schema ...
+    <resheader name="resmimetype">text/microsoft-resx</resheader>
+    <resheader name="version">2.0</resheader>
+    <resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
+    <resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
+    <data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
+    <data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
+    <data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
+        <value>[base64 mime encoded serialized .NET Framework object]</value>
+    </data>
+    <data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
+        <value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
+        <comment>This is a comment</comment>
+    </data>
+
+    There are any number of "resheader" rows that contain simple
+    name/value pairs.
+
+    Each data row contains a name, and value. The row also contains a
+    type or mimetype. Type corresponds to a .NET class that support
+    text/value conversion through the TypeConverter architecture.
+    Classes that don't support this are serialized and stored with the
+    mimetype set.
+
+    The mimetype is used for serialized objects, and tells the
+    ResXResourceReader how to depersist the object. This is currently not
+    extensible. For a given mimetype the value must be set accordingly:
+
+    Note - application/x-microsoft.net.object.binary.base64 is the format
+    that the ResXResourceWriter will generate, however the reader can
+    read any of the formats listed below.
+
+    mimetype: application/x-microsoft.net.object.binary.base64
+    value   : The object must be serialized with
+            : System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
+            : and then encoded with base64 encoding.
+
+    mimetype: application/x-microsoft.net.object.soap.base64
+    value   : The object must be serialized with
+            : System.Runtime.Serialization.Formatters.Soap.SoapFormatter
+            : and then encoded with base64 encoding.
+
+    mimetype: application/x-microsoft.net.object.bytearray.base64
+    value   : The object must be serialized into a byte array
+            : using a System.ComponentModel.TypeConverter
+            : and then encoded with base64 encoding.
+    -->
+  <xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
+    <xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
+    <xsd:element name="root" msdata:IsDataSet="true">
+      <xsd:complexType>
+        <xsd:choice maxOccurs="unbounded">
+          <xsd:element name="metadata">
+            <xsd:complexType>
+              <xsd:sequence>
+                <xsd:element name="value" type="xsd:string" minOccurs="0" />
+              </xsd:sequence>
+              <xsd:attribute name="name" use="required" type="xsd:string" />
+              <xsd:attribute name="type" type="xsd:string" />
+              <xsd:attribute name="mimetype" type="xsd:string" />
+              <xsd:attribute ref="xml:space" />
+            </xsd:complexType>
+          </xsd:element>
+          <xsd:element name="assembly">
+            <xsd:complexType>
+              <xsd:attribute name="alias" type="xsd:string" />
+              <xsd:attribute name="name" type="xsd:string" />
+            </xsd:complexType>
+          </xsd:element>
+          <xsd:element name="data">
+            <xsd:complexType>
+              <xsd:sequence>
+                <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
+                <xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
+              </xsd:sequence>
+              <xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
+              <xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
+              <xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
+              <xsd:attribute ref="xml:space" />
+            </xsd:complexType>
+          </xsd:element>
+          <xsd:element name="resheader">
+            <xsd:complexType>
+              <xsd:sequence>
+                <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
+              </xsd:sequence>
+              <xsd:attribute name="name" type="xsd:string" use="required" />
+            </xsd:complexType>
+          </xsd:element>
+        </xsd:choice>
+      </xsd:complexType>
+    </xsd:element>
+  </xsd:schema>
+  <resheader name="resmimetype">
+    <value>text/microsoft-resx</value>
+  </resheader>
+  <resheader name="version">
+    <value>2.0</value>
+  </resheader>
+  <resheader name="reader">
+    <value>System.Resources.ResXResourceReader, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
+  </resheader>
+  <resheader name="writer">
+    <value>System.Resources.ResXResourceWriter, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
+  </resheader>
+  <data name="Usage" xml:space="preserve"><value>Использование</value></data>
+  <data name="Command" xml:space="preserve"><value>команда</value></data>
+  <data name="UnknownCommand" xml:space="preserve"><value>Неизвестная команда</value></data>
+  <data name="Options" xml:space="preserve"><value>параметры</value></data>
+  <data name="ArgumentMissing" xml:space="preserve"><value>отсутствует параметр</value></data>
+  <data name="Path" xml:space="preserve"><value>путь</value></data>
+  <data name="MainVersion" xml:space="preserve"><value>Вы используете CKAN версии {0}</value></data>
+  <data name="MainUnknownCommand" xml:space="preserve"><value>Неизвестная команда, --help для справки</value></data>
+  <data name="MainMissingInstance" xml:space="preserve"><value>CKAN не знает, где установлена сборка игры.
+Используйте 'ckan instance help' для справки.</value></data>
+  <data name="OptionsRootError" xml:space="preserve"><value>Вы пытаетесь запустить CKAN с правами администратора.
+Пожалуйста, запускайте CKAN с правами обычного пользователя (либо, если это действительно необходимо, используйте --asroot).</value></data>
+  <data name="OptionsRootWarning" xml:space="preserve"><value>Внимание: CKAN запущен с правами администратора!</value></data>
+  <data name="OptionsMonoWarning" xml:space="preserve"><value>Винмание. Обнаруженная версия mono {0} ниже, чем рекомендуемая версия {1}.
+Рекомендовано обновление!</value></data>
+  <data name="OptionsInstanceAndGameDir" xml:space="preserve"><value>--instance и --gamedir не могут быть заданы в одно и то же время</value></data>
+  <data name="OptionsInvalidInstance" xml:space="preserve"><value>Указана неверная сборка "{0}", используйте '--gamedir' для указания пути, либо 'instance list' для просмотра сборок игры</value></data>
+  <data name="InstanceNotInstance" xml:space="preserve"><value>Извините, {0} не являтся сборкой игры</value></data>
+  <data name="InstanceDuplicate" xml:space="preserve"><value>Данное имя сборки уже занято: {0}</value></data>
+  <data name="UserYesNoPromptSuffix" xml:space="preserve"><value>[Д/н]</value></data>
+  <data name="UserYesNoY" xml:space="preserve"><value>д</value></data>
+  <data name="UserYesNoYes" xml:space="preserve"><value>да</value></data>
+  <data name="UserYesNoN" xml:space="preserve"><value>н</value></data>
+  <data name="UserYesNoNo" xml:space="preserve"><value>нет</value></data>
+  <data name="UserYesNoInvalid" xml:space="preserve"><value>Пожалуйста, введите «да» или «нет»</value></data>
+  <data name="UserSelectionPromptWithDefault" xml:space="preserve"><value>Введите число между {0} и {1} (Для отмены нажмите "о" или "н". "Enter" выберет {2}.): </value></data>
+  <data name="UserSelectionPromptWithoutDefault" xml:space="preserve"><value>Введите число между {0} и {1} (Для отмены нажмите "о" или "н".): </value></data>
+  <data name="UserSelectionC" xml:space="preserve"><value>о</value></data>
+  <data name="UserSelectionN" xml:space="preserve"><value>н</value></data>
+  <data name="UserSelectionCancelled" xml:space="preserve"><value>Выбор отменён</value></data>
+  <data name="UserSelectionNotNumber" xml:space="preserve"><value>Введенное значение не является числом</value></data>
+  <data name="UserSelectionTooLarge" xml:space="preserve"><value>Введённое число слишком велико</value></data>
+  <data name="UserSelectionTooSmall" xml:space="preserve"><value>Введённое число слишком мало</value></data>
+  <data name="AuthTokenHostHeader" xml:space="preserve"><value>Хост</value></data>
+  <data name="AuthTokenTokenHeader" xml:space="preserve"><value>Токен</value></data>
+  <data name="AuthTokenHelpSummary" xml:space="preserve"><value>Управление токенами аутентификации</value></data>
+  <data name="AvailableHeader" xml:space="preserve"><value>Модули, совместимые с {0} {1}</value></data>
+  <data name="CacheHelpSummary" xml:space="preserve"><value>Управление папкой кэша загрузок CKAN</value></data>
+  <data name="CacheSet" xml:space="preserve"><value>Кэш загрузок находится в {0}</value></data>
+  <data name="CacheInvalidPath" xml:space="preserve"><value>Неверный путь: {0}</value></data>
+  <data name="CacheCleared" xml:space="preserve"><value>Кэш загрузок очищен</value></data>
+  <data name="CacheReset" xml:space="preserve"><value>Кэш загрузок сброшен в {0}</value></data>
+  <data name="CacheResetFailed" xml:space="preserve"><value>Не удалось сбросить путь к кэшу: {0}</value></data>
+  <data name="CacheUnlimited" xml:space="preserve"><value>Неограничено</value></data>
+  <data name="CacheInfo" xml:space="preserve"><value>{0} файлов, {1}</value></data>
+  <data name="CompareSame" xml:space="preserve"><value>"{0}" и "{1}" одинаковы.</value></data>
+  <data name="CompareLower" xml:space="preserve"><value>"{0}" ниже, чем "{1}".</value></data>
+  <data name="CompareHigher" xml:space="preserve"><value>"{0}" выше, чем "{1}".</value></data>
+  <data name="CompatHelpSummary" xml:space="preserve"><value>Управление версиями игры</value></data>
+  <data name="CompatVersionHeader" xml:space="preserve"><value>Версия</value></data>
+  <data name="CompatActualHeader" xml:space="preserve"><value>Актуальная</value></data>
+  <data name="CompatInvalid" xml:space="preserve"><value>ОШИБКА: Неверно указана версия игры</value></data>
+  <data name="CompatCantForget" xml:space="preserve"><value>ОШИБКА: Не удалось забыть актуальную версию игры</value></data>
+  <data name="InstanceHelpSummary" xml:space="preserve"><value>Управление сборками игры</value></data>
+  <data name="InstanceListNoVersion" xml:space="preserve"><value>&lt;НЕТ&gt;</value></data>
+  <data name="InstanceListYes" xml:space="preserve"><value>Да</value></data>
+  <data name="InstanceListNo" xml:space="preserve"><value>Нет</value></data>
+  <data name="InstanceListNameHeader" xml:space="preserve"><value>Имя</value></data>
+  <data name="InstanceListVersionHeader" xml:space="preserve"><value>Версия</value></data>
+  <data name="InstanceListDefaultHeader" xml:space="preserve"><value>По умолч.</value></data>
+  <data name="InstanceListPathHeader" xml:space="preserve"><value>Путь</value></data>
+  <data name="InstanceAddDuplicate" xml:space="preserve"><value>Сборка с именем "{0}" уже существует, операция не выполнена</value></data>
+  <data name="InstanceAdded" xml:space="preserve"><value>Добавлена "{0}" с корнем "{1}"</value></data>
+  <data name="InstanceCloneNotFound" xml:space="preserve"><value>Не найдено сборок с таким именем или по такому пути: {0}
+Проверьте список известных сборок:
+</value></data>
+  <data name="InstanceCloneFailed" xml:space="preserve"><value>Что-то пошло не так. Проверьте, произошло ли создание новой папки.
+Попробуйте вручную добавить сборку с помощью "ckan instance add".
+</value></data>
+  <data name="InstanceNotFound" xml:space="preserve"><value>Не удалось найти сборку с именем "{0}", операция не выполнена</value></data>
+  <data name="InstanceRenamed" xml:space="preserve"><value>Сборка "{0}" переименована в "{1}"</value></data>
+  <data name="InstanceForgot" xml:space="preserve"><value>Сборка "{0}" удалена</value></data>
+  <data name="InstanceDefaultArgumentMissing" xml:space="preserve"><value>отсуствует параметр, выберите из списка ниже</value></data>
+  <data name="InstanceDefaultSet" xml:space="preserve"><value>"{0}" установлена как сборка по умолчанию</value></data>
+  <data name="InstanceFakeFailed" xml:space="preserve"><value>Что-то пошло не так. Попробуйте вручную добавить сборку с помощью "ckan instance add". Проверьте, была ли создана новая папка.</value></data>
+  <data name="InstanceFakeBadArguments" xml:space="preserve"><value>--Ошибка: неверный(-е) параметр(-ы)--</value></data>
+  <data name="InstanceFakeMakingHistory" xml:space="preserve"><value>Проверьте параметр версии DLC Making History - правильный формат: Maj.Min.Patch - пример: 1.1.0</value></data>
+  <data name="InstanceFakeBreakingGround" xml:space="preserve"><value>Проверьте параметр версии DLC Breaking Ground - правильный формат: Maj.Min.Patch - пример: 1.1.0</value></data>
+  <data name="InstanceFakeVersion" xml:space="preserve"><value>Проверьте параметр версии игры - правильный формат: Maj.Min.Patch[.Build] - пример: 1.6.0 или 1.2.2.1622</value></data>
+  <data name="InstanceFakeBadGameVersion" xml:space="preserve"><value>Не удалось найти соответствующую версию игры.
+Проверьте формат введённой версии - правильный формат: Maj.Min - пример: 1.5</value></data>
+  <data name="InstanceFakeCancelled" xml:space="preserve"><value>Выбор отменён! Снова вызовите 'ckan instance fake'</value></data>
+  <data name="InstanceFakeCreating" xml:space="preserve"><value>Создание новой псевдосборки "{0}" по пути {1} с версией {2}</value></data>
+  <data name="InstanceFakeDefault" xml:space="preserve"><value>Setting new instance to default...</value></data>
+  <data name="InstanceFakeDone" xml:space="preserve"><value>--Готово--</value></data>
+  <data name="ImportError" xml:space="preserve"><value>Ошибка импортирования: {0}</value></data>
+  <data name="ImportNotFound" xml:space="preserve"><value>Файл не найден: {0}</value></data>
+  <data name="InstallNotFound" xml:space="preserve"><value>Файл не найден, выход: {0}</value></data>
+  <data name="InstallTryAgain" xml:space="preserve"><value>Попробуйте совершить `ckan update` и попытайтесь заново.
+Используйте `ckan install --no-recommends`, чтобы пропустить установку рекомендуемых модулей.
+Либо используйте `ckan install --allow-incompatible`, чтобы игнорировать проблемы совместимости.</value></data>
+  <data name="InstallUnversionedDependencyNotSatisfied" xml:space="preserve"><value>Необходим модуль {0}, но он отсутствует в указателе либо недоступен для вашей версии {1}</value></data>
+  <data name="InstallVersionedDependencyNotSatisfied" xml:space="preserve"><value>Необходим модуль {0} {1}, но он отсутствует в указателе либо недоступен для вашей версии {2}</value></data>
+  <data name="InstallBadMetadata" xml:space="preserve"><value>Обнаружены некорректные метаданные для модуля {0}: {1}</value></data>
+  <data name="InstallFileConflictOwned" xml:space="preserve"><value>О нет! Была произведена попытка перезаписать файл, принадлежащий другой модификации!
+Произведите `ckan update` и попытайтесь заново.
+
+Если проблема повторяется, это может означать ошибку упаковки пакета.
+Пожалуйста, отправьте отчёт об ошибке на:
+
+https://github.com/KSP-CKAN/NetKAN/issues/new
+
+Включите в отчёт следующую информацию:
+
+File           : {0} (Файл)
+Installing Mod : {1} (Устанавливающаяся модификация)
+Owning Mod     : {2} (Модификация-владелец файла)
+CKAN Version   : {3} (Версия CKAN)
+</value></data>
+  <data name="InstallFileConflictUnowned" xml:space="preserve"><value>О нет!
+
+Похоже, что вы пытаетесь установить модификацию, которая уже установлена
+или конфликтует с другой уже установленной модификацией.
+
+В качестве меры безопасности CKAN *никогда* не меняет файлы,
+которые не установила сама.
+
+Если вы хотите установить {0} с помощью CKAN,
+вручную удалите модификацию, которой принадлежит файл:
+
+{1}
+
+и попытайтесь заново.
+</value></data>
+  <data name="InstallGamedataReturned" xml:space="preserve"><value>{0} возвращена в изначальное состояние</value></data>
+  <data name="InstallCancelled" xml:space="preserve"><value>Установка отменена. Ваши файлы возвращены в изначальное состояние.</value></data>
+  <data name="InstallAborted" xml:space="preserve"><value>Установка отменена: {0}</value></data>
+  <data name="InstallTryAuthToken" xml:space="preserve"><value>Используйте команду authtoken. Используйте {0} для справки.</value></data>
+  <data name="InstallDownloadFailed" xml:space="preserve"><value>Не удалось загрузить один или несколько файлов, остановка</value></data>
+  <data name="InstallDLC" xml:space="preserve"><value>CKAN не может установить дополнение '{0}'.</value></data>
+  <data name="InstallDLCStorePage" xml:space="preserve"><value>Для установки дополнений приобретите их в соответствующем магазине:
+{0}</value></data>
+  <data name="ListUnknownFormat" xml:space="preserve"><value>Неизвестный формат экспорта: {0}</value></data>
+  <data name="ListGameFound" xml:space="preserve"><value>{0} найден по пути: {1}</value></data>
+  <data name="ListGameVersion" xml:space="preserve"><value>{0} версия: {1}</value></data>
+  <data name="ListGameModulesHeader" xml:space="preserve"><value>Установленные модули:</value></data>
+  <data name="ListLegend" xml:space="preserve"><value>Legend: -: Обновлено. +: Авто-уст. X: Несовместимо. ^: Возм. обнов. >: Есть замена
+        A: Обнаружено. ?: Неизв. *: Поврежд.</value></data>
+  <data name="MarkHelpSummary" xml:space="preserve"><value>Изменение флагов модулей</value></data>
+  <data name="MarkUnknownCommand" xml:space="preserve"><value>Неизвестная команда: mark {0}</value></data>
+  <data name="MarkNotInstalled" xml:space="preserve"><value>{0} не установлен</value></data>
+  <data name="MarkAlready" xml:space="preserve"><value>{0} уже отмечен как {1}</value></data>
+  <data name="Marking" xml:space="preserve"><value>Отметка {0} как {1}...</value></data>
+  <data name="MarkDLC" xml:space="preserve"><value>Дополнение '{0}' нельзя отметить как авто-устанавливаемое</value></data>
+  <data name="MarkChanged" xml:space="preserve"><value>Изменения совершены!</value></data>
+  <data name="MarkAutoInstalled" xml:space="preserve"><value>авто-устанавливаемое</value></data>
+  <data name="MarkUserSelected" xml:space="preserve"><value>выбранное пользователем</value></data>
+  <data name="PromptWelcome" xml:space="preserve"><value>Добро пожаловать в CKAN!
+
+Для справки введите `help` и нажмите Enter. Чтобы выйти, введите {0} и нажмите Enter.
+</value></data>
+  <data name="PromptWithInstance" xml:space="preserve"><value>CKAN {0}: {1} {2} ({3})&gt; </value></data>
+  <data name="PromptWithoutInstance" xml:space="preserve"><value>CKAN {0}&gt; </value></data>
+  <data name="RemoveNotInstalled" xml:space="preserve"><value>Невозможно выполнить, {0} не установлена.
+Используйте `ckan list` для вывода списка установленных модификаций.</value></data>
+  <data name="RemoveDLC" xml:space="preserve"><value>CKAN не может удалить дополнение '{0}'.</value></data>
+  <data name="RemoveDLCStorePage" xml:space="preserve"><value>Чтобы удалить дополнение, следуйте инструкциям на странице магазина, где оно было приобретено:
+{0}</value></data>
+  <data name="RemoveCancelled" xml:space="preserve"><value>Удаление отменено: {0}</value></data>
+  <data name="RemoveNothing" xml:space="preserve"><value>Не выбрано ни обной модификации</value></data>
+  <data name="RepairHelpSummary" xml:space="preserve"><value>Способы автоматического восстановления</value></data>
+  <data name="RepairUnknownCommand" xml:space="preserve"><value>Неизвестная команда: repair {0}</value></data>
+  <data name="Repaired" xml:space="preserve"><value>Произведена попытка восстановления реестра.</value></data>
+  <data name="ReplaceModuleNotFound" xml:space="preserve"><value>Модуль {0} не найден</value></data>
+  <data name="Replacing" xml:space="preserve"><value>Замена модулей...</value></data>
+  <data name="ReplaceFound" xml:space="preserve"><value>Для {2} {3} найден заменяющий модуль {0} {1}</value></data>
+  <data name="ReplaceContinuePrompt" xml:space="preserve"><value>Продолжить?</value></data>
+  <data name="ReplaceCancelled" xml:space="preserve"><value>Операция замены отменена по запросу пользователя</value></data>
+  <data name="ReplaceDependencyNotSatisfied" xml:space="preserve"><value>Не удовлетворены зависимости для замены, {0} требует {1} {2}, но его нет в указателе либо он недоступен для вашей версии {3}</value></data>
+  <data name="ReplaceNotFound" xml:space="preserve"><value>Замен не найдено</value></data>
+  <data name="RepoHelpSummary" xml:space="preserve"><value>Управление репозиториями CKAN</value></data>
+  <data name="RepoUnknownCommand" xml:space="preserve"><value>Неизвестная команда: repo {0}</value></data>
+  <data name="RepoAvailableHeader" xml:space="preserve"><value>Все доступные (основные) репозитории CKAN:</value></data>
+  <data name="RepoAvailableFailed" xml:space="preserve"><value>Не удалось загрузить список репозиториев CKAN из {0}</value></data>
+  <data name="RepoListHeader" xml:space="preserve"><value>Список известных репозиториев:</value></data>
+  <data name="RepoAddNotFound" xml:space="preserve"><value>{0} не найден в основном списке, предоставьте имя и URI</value></data>
+  <data name="RepoAddDuplicate" xml:space="preserve"><value>Репозиторий с именем "{0}" уже существует, операция не выполнена</value></data>
+  <data name="RepoAdded" xml:space="preserve"><value>Добавлен репозиторий '{0}' - '{1}'</value></data>
+  <data name="RepoForgetNotFound" xml:space="preserve"><value>Не удалось найти репозиторий с именем "{0}", операция не выполнена</value></data>
+  <data name="RepoForgetRemoving" xml:space="preserve"><value>Удаление нечувствительного совпадения "{0}"</value></data>
+  <data name="RepoForgetRemoved" xml:space="preserve"><value>"{0}" успешно удален</value></data>
+  <data name="RepoSet" xml:space="preserve"><value>Репозиторий {0} установлен в '{1}'</value></data>
+  <data name="SearchNoTerm" xml:space="preserve"><value>Отсутствует запрос?</value></data>
+  <data name="SearchFoundByAuthorWithIncompat" xml:space="preserve"><value>Найдено {0} совместимых и {1} несовместимых модификаций, совпадающих с "{2}" от "{3}"</value></data>
+  <data name="SearchFoundWithIncompat" xml:space="preserve"><value>Найдено {0} совместимых и {1} несовместимых модификаций, совпадающих с "{2}"</value></data>
+  <data name="SearchFoundByAuthor" xml:space="preserve"><value>Найдено {0} совместимых модификаций, совпадающих с "{1}" от "{2}"</value></data>
+  <data name="SearchFound" xml:space="preserve"><value>Найдено {0} совместимых модификаций, совпадающих с "{1}"</value></data>
+  <data name="SearchCompatibleModsHeader" xml:space="preserve"><value>Совпадающие совместимые модификации:</value></data>
+  <data name="SearchCompatibleMod" xml:space="preserve"><value>* {0} ({1}) - {2} от {3} - {4}</value></data>
+  <data name="SearchIncompatibleModsHeader" xml:space="preserve"><value>Совпадающие несовместимые модификации:</value></data>
+  <data name="SearchIncompatibleMod" xml:space="preserve"><value>* {0} ({1} - {2}) - {3} от {4} - {5}</value></data>
+  <data name="ShowNotInstalledOrCompatible" xml:space="preserve"><value>{0} не установлена или несовместима с {1} {2}</value></data>
+  <data name="ShowLookingForClose" xml:space="preserve"><value>Поиск совпадений в совместимых модификациях...</value></data>
+  <data name="ShowNoClose" xml:space="preserve"><value>Совпадений не найдено</value></data>
+  <data name="ShowFoundOne" xml:space="preserve"><value>Найдено одно совпадение: {0}</value></data>
+  <data name="ShowClosePrompt" xml:space="preserve"><value>Совпадения:</value></data>
+  <data name="ShowFilesHeader" xml:space="preserve"><value>Показ {0} установленных файлов:</value></data>
+  <data name="ShowModuleInfoHeader" xml:space="preserve"><value>Информация о модуле:</value></data>
+  <data name="ShowVersion" xml:space="preserve"><value>  Версия:	{0}</value></data>
+  <data name="ShowAuthor" xml:space="preserve"><value>  Авторы:	{0}</value></data>
+  <data name="ShowAuthorUnknown" xml:space="preserve"><value>  Авторы:	НЕИЗВЕСТНО</value></data>
+  <data name="ShowStatus" xml:space="preserve"><value>  Статус:	{0}</value></data>
+  <data name="ShowLicence" xml:space="preserve"><value>  Лицензия:	{0}</value></data>
+  <data name="ShowTags" xml:space="preserve"><value>  Теги: 	{0}</value></data>
+  <data name="ShowLanguages" xml:space="preserve"><value>  Языки:	{0}</value></data>
+  <data name="ShowDependsHeader" xml:space="preserve"><value>Зависит:</value></data>
+  <data name="ShowRecommendsHeader" xml:space="preserve"><value>Рекоммендует:</value></data>
+  <data name="ShowSuggestsHeader" xml:space="preserve"><value>Предлагает:</value></data>
+  <data name="ShowProvidesHeader" xml:space="preserve"><value>Предоставляет:</value></data>
+  <data name="ShowResourcesHeader" xml:space="preserve"><value>Ресурсы:</value></data>
+  <data name="ShowHomePage" xml:space="preserve"><value>  Страница:	{0}</value></data>
+  <data name="ShowManual" xml:space="preserve"><value>  Инструкция:	{0}</value></data>
+  <data name="ShowSpaceDock" xml:space="preserve"><value>  SpaceDock:	{0}</value></data>
+  <data name="ShowRepository" xml:space="preserve"><value>  Репозиторий:	{0}</value></data>
+  <data name="ShowBugTracker" xml:space="preserve"><value>  Багтрекер:	{0}</value></data>
+  <data name="ShowCurse" xml:space="preserve"><value>  Curse:	{0}</value></data>
+  <data name="ShowStore" xml:space="preserve"><value>  Магазин:	{0}</value></data>
+  <data name="ShowSteamStore" xml:space="preserve"><value>  Магазин Steam:	{0}</value></data>
+  <data name="ShowVersionFile" xml:space="preserve"><value>  Файл версии:	{0}</value></data>
+  <data name="ShowFileName" xml:space="preserve"><value>Имя файла: {0}</value></data>
+  <data name="ShowVersionHeader" xml:space="preserve"><value>Версия</value></data>
+  <data name="ShowGameVersionsHeader" xml:space="preserve"><value>Версии игры</value></data>
+  <data name="UpdateChangesSummary" xml:space="preserve"><value>Найдено {0} новых модулей, {1} удалённых модулей и {2} обновлённых модулей</value></data>
+  <data name="UpdateAddedHeader" xml:space="preserve"><value>Новые модули [Имя (идентификатор CKAN)]:</value></data>
+  <data name="UpdateRemovedHeader" xml:space="preserve"><value>Удалённые модули [Имя (идентификатор CKAN)]:</value></data>
+  <data name="UpdateUpdatedHeader" xml:space="preserve"><value>Обновлённые модули [Имя (идентификатор CKAN)]:</value></data>
+  <data name="UpdateSummary" xml:space="preserve"><value>Обновлены сведения о {0} совместимых модулях</value></data>
+  <data name="UpgradeQueryingCKAN" xml:space="preserve"><value>Запрос последней версии CKAN</value></data>
+  <data name="UpgradeNewCKANAvailable" xml:space="preserve"><value>Доступна новая версия CKAN - {0}</value></data>
+  <data name="UpgradeProceed" xml:space="preserve"><value>Продолжить установку?</value></data>
+  <data name="UpgradePleaseWait" xml:space="preserve"><value>Обновление CKAN, пожалуйста, подождите...</value></data>
+  <data name="UpgradeAlreadyHaveLatest" xml:space="preserve"><value>Ваша версия уже является последней</value></data>
+  <data name="UpgradeAborted" xml:space="preserve"><value>Обновление отменено: {0}</value></data>
+  <data name="UpgradeNotFound" xml:space="preserve"><value>Модуль {0} не найден</value></data>
+  <data name="UpgradeDLC" xml:space="preserve"><value>CKAN не может обновить дополнение '{0}'u</value></data>
+  <data name="UpgradeDLCStorePage" xml:space="preserve"><value>Для обновления дополнений загрузите их со страницы магазина, в котором они были приобретены:
+{0}</value></data>
+  <data name="ScanNotSaved" xml:space="preserve"><value>Репозиторий не сохранён</value></data>
+  <data name="ScanPreliminaryInconsistent" xml:space="preserve"><value>Предварительное сканирование показывает, что сборка находится в несогласованном состоянии.
+Используйте ckan.exe для сканирования.
+Продолжение использования {0}.</value></data>
+</root>
diff --git a/Cmdline/SingleAssemblyResourceManager.cs b/Cmdline/SingleAssemblyResourceManager.cs
new file mode 100644
index 0000000000..f7345f9aac
--- /dev/null
+++ b/Cmdline/SingleAssemblyResourceManager.cs
@@ -0,0 +1,58 @@
+using System.IO;
+using System.Globalization;
+using System.Resources;
+using System.Collections;
+using System.Reflection;
+using System.Collections.Generic;
+
+namespace CKAN.CmdLine
+{
+    // Thanks and credit to this guy: https://stackoverflow.com/q/1952638/2422988
+
+    class SingleAssemblyResourceManager : ResourceManager
+    {
+        public SingleAssemblyResourceManager(string basename, Assembly assembly) : base(basename, assembly)
+        {
+        }
+
+        protected override ResourceSet InternalGetResourceSet(CultureInfo culture,
+            bool createIfNotExists, bool tryParents)
+        {
+            ResourceSet rs;
+            if (!myResourceSets.TryGetValue(culture, out rs))
+            {
+                // Lazy-load default language (without caring about duplicate assignment in race conditions, no harm done)
+                if (neutralResourcesCulture == null)
+                {
+                    neutralResourcesCulture = GetNeutralResourcesLanguage(this.MainAssembly);
+                }
+
+                // If we're asking for the default language, then ask for the
+                // invariant (non-specific) resources.
+                if (neutralResourcesCulture.Equals(culture))
+                {
+                    culture = CultureInfo.InvariantCulture;
+                }
+                string resourceFileName = GetResourceFileName(culture);
+
+                Stream store = this.MainAssembly.GetManifestResourceStream(resourceFileName);
+
+                // If we found the appropriate resources in the local assembly
+                if (store != null)
+                {
+                    rs = new ResourceSet(store);
+                    // Save for later
+                    myResourceSets.Add(culture, rs);
+                }
+                else
+                {
+                    rs = base.InternalGetResourceSet(culture, createIfNotExists, tryParents);
+                }
+            }
+            return rs;
+        }
+
+        private CultureInfo neutralResourcesCulture;
+        private Dictionary<CultureInfo, ResourceSet> myResourceSets = new Dictionary<CultureInfo, ResourceSet>();
+    }
+}
diff --git a/ConsoleUI/AuthTokenAddDialog.cs b/ConsoleUI/AuthTokenAddDialog.cs
index 03d109dd6c..3d6f606966 100644
--- a/ConsoleUI/AuthTokenAddDialog.cs
+++ b/ConsoleUI/AuthTokenAddDialog.cs
@@ -15,7 +15,7 @@ public class AuthTokenAddDialog : ConsoleDialog {
         /// </summary>
         public AuthTokenAddDialog() : base()
         {
-            CenterHeader = () => "Create Authentication Key";
+            CenterHeader = () => Properties.Resources.AuthTokenAddTitle;
 
             int top = (Console.WindowHeight - height) / 2;
             SetDimensions(6, top, -6, top + height - 1);
@@ -27,7 +27,7 @@ public AuthTokenAddDialog() : base()
 
             AddObject(new ConsoleLabel(
                 l + 2, t + 2, l + 2 + labelW,
-                () => "Host:",
+                () => Properties.Resources.AuthTokenAddHost,
                 th => th.PopupBg,
                 th => th.PopupFg
             ));
@@ -35,13 +35,13 @@ public AuthTokenAddDialog() : base()
             hostEntry = new ConsoleField(
                 l + 2 + labelW + wPad, t + 2, r - 3
             ) {
-                GhostText = () => "<Enter a host name>"
+                GhostText = () => Properties.Resources.AuthTokenAddHostGhostText
             };
             AddObject(hostEntry);
 
             AddObject(new ConsoleLabel(
                 l + 2, t + 4, l + 2 + labelW,
-                () => "Token:",
+                () => Properties.Resources.AuthTokenAddToken,
                 th => th.PopupBg,
                 th => th.PopupFg
             ));
@@ -49,14 +49,14 @@ public AuthTokenAddDialog() : base()
             tokenEntry = new ConsoleField(
                 l + 2 + labelW + wPad, t + 4, r - 3
             ) {
-                GhostText = () => "<Enter an authentication token>"
+                GhostText = () => Properties.Resources.AuthTokenAddTokenGhostText
             };
             AddObject(tokenEntry);
 
-            AddTip("Esc", "Cancel");
+            AddTip(Properties.Resources.Esc, Properties.Resources.Cancel);
             AddBinding(Keys.Escape, (object sender, ConsoleTheme theme) => false);
 
-            AddTip("Enter", "Accept", validKey);
+            AddTip(Properties.Resources.Enter, Properties.Resources.Accept, validKey);
             AddBinding(Keys.Enter, (object sender, ConsoleTheme theme) => {
                 if (validKey()) {
                     ServiceLocator.Container.Resolve<IConfiguration>().SetAuthToken(hostEntry.Value, tokenEntry.Value);
@@ -81,7 +81,10 @@ private bool validKey()
         private ConsoleField tokenEntry;
 
         private const int wPad   = 2;
-        private const int labelW = 6;
+        private int labelW => Math.Max(6, Math.Max(
+            Properties.Resources.AuthTokenAddHost.Length,
+            Properties.Resources.AuthTokenAddToken.Length
+        ));
         private const int height = 7;
     }
 
diff --git a/ConsoleUI/AuthTokenListScreen.cs b/ConsoleUI/AuthTokenListScreen.cs
index d3747252a8..7ead4bb9f5 100644
--- a/ConsoleUI/AuthTokenListScreen.cs
+++ b/ConsoleUI/AuthTokenListScreen.cs
@@ -18,14 +18,14 @@ public class AuthTokenScreen : ConsoleScreen {
         public AuthTokenScreen() : base()
         {
             mainMenu = new ConsolePopupMenu(new List<ConsoleMenuOption>() {
-                new ConsoleMenuOption("Make a GitHub API token", "",
-                    "Open the web page for creating GitHub API authentication tokens",
+                new ConsoleMenuOption(Properties.Resources.AuthTokenListGitHubLink, "",
+                    Properties.Resources.AuthTokenListGitHubLinkTip,
                     true, openGitHubURL)
             });
 
             AddObject(new ConsoleLabel(
                 1, 2, -1,
-                () => "Authentication tokens for downloads:"
+                () => Properties.Resources.AuthTokenListLabel
             ));
 
             tokenList = new ConsoleListBox<string>(
@@ -33,18 +33,18 @@ public AuthTokenScreen() : base()
                 new List<string>(ServiceLocator.Container.Resolve<IConfiguration>().GetAuthTokenHosts()),
                 new List<ConsoleListBoxColumn<string>>() {
                     new ConsoleListBoxColumn<string>() {
-                        Header   = "Host",
+                        Header   = Properties.Resources.AuthTokenListHostHeader,
                         Width    = 20,
                         Renderer = (string s) => s
                     },
                     new ConsoleListBoxColumn<string>() {
-                        Header   = "Token",
+                        Header   = Properties.Resources.AuthTokenListTokenHeader,
                         Width    = 50,
                         Renderer = (string s) => {
                             string token;
                             return ServiceLocator.Container.Resolve<IConfiguration>().TryGetAuthToken(s, out token)
                                 ? token
-                                : missingTokenValue;
+                                : Properties.Resources.AuthTokenListMissingToken;
                         }
                     }
                 },
@@ -54,15 +54,15 @@ public AuthTokenScreen() : base()
 
             AddObject(new ConsoleLabel(
                 3, -1, -1,
-                () => "NOTE: These values are private! Do not share screenshots of this screen!",
+                () => Properties.Resources.AuthTokenListWarning,
                 null,
                 th => th.AlertFrameFg
             ));
 
-            AddTip("Esc", "Back");
+            AddTip(Properties.Resources.Esc, Properties.Resources.Back);
             AddBinding(Keys.Escape, (object sender, ConsoleTheme theme) => false);
 
-            tokenList.AddTip("A", "Add");
+            tokenList.AddTip("A", Properties.Resources.Add);
             tokenList.AddBinding(Keys.A, (object sender, ConsoleTheme theme) => {
                 AuthTokenAddDialog ad = new AuthTokenAddDialog();
                 ad.Run(theme);
@@ -71,7 +71,7 @@ public AuthTokenScreen() : base()
                 return true;
             });
 
-            tokenList.AddTip("R", "Remove", () => tokenList.Selection != null);
+            tokenList.AddTip("R", Properties.Resources.Remove, () => tokenList.Selection != null);
             tokenList.AddBinding(Keys.R, (object sender, ConsoleTheme theme) => {
                 if (tokenList.Selection != null) {
                     ServiceLocator.Container.Resolve<IConfiguration>().SetAuthToken(tokenList.Selection, null);
@@ -94,7 +94,7 @@ protected override string LeftHeader()
         /// </summary>
         protected override string CenterHeader()
         {
-            return "Authentication Tokens";
+            return Properties.Resources.AuthTokenListTitle;
         }
 
         private bool openGitHubURL(ConsoleTheme theme)
@@ -105,8 +105,7 @@ private bool openGitHubURL(ConsoleTheme theme)
 
         private ConsoleListBox<string> tokenList;
 
-        private const           string missingTokenValue = "<ERROR>";
-        private static readonly Uri    githubTokenURL    = new Uri("https://github.com/settings/tokens");
+        private static readonly Uri githubTokenURL = new Uri("https://github.com/settings/tokens");
     }
 
 }
diff --git a/ConsoleUI/CKAN-ConsoleUI.csproj b/ConsoleUI/CKAN-ConsoleUI.csproj
index ecbb0b4262..cf61fd57b0 100644
--- a/ConsoleUI/CKAN-ConsoleUI.csproj
+++ b/ConsoleUI/CKAN-ConsoleUI.csproj
@@ -1,40 +1,28 @@
-<?xml version="1.0" encoding="utf-8"?>
-<Project ToolsVersion="14.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+<Project>
   <PropertyGroup>
     <AssemblyName>CKAN-ConsoleUI</AssemblyName>
     <OutputPath>..\_build\out\$(AssemblyName)\$(Configuration)\bin\</OutputPath>
     <BaseIntermediateOutputPath>..\_build\out\$(AssemblyName)\$(Configuration)\obj\</BaseIntermediateOutputPath>
   </PropertyGroup>
-  <Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
+  <Import Project="Sdk.props" Sdk="Microsoft.NET.Sdk" />
   <PropertyGroup>
-    <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
-    <Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
     <ProjectGuid>{DA5C7023-9A3B-4204-AE2F-BBA6C388B436}</ProjectGuid>
-    <OutputType>WinExe</OutputType>
-    <RootNamespace>CKAN</RootNamespace>
+    <OutputType>Exe</OutputType>
+    <RootNamespace>CKAN.ConsoleUI</RootNamespace>
+    <GenerateAssemblyInfo>false</GenerateAssemblyInfo>
+    <AutoGenerateBindingRedirects>true</AutoGenerateBindingRedirects>
+    <Deterministic>true</Deterministic>
+    <Configurations>Debug;Release</Configurations>
+    <Prefer32Bit>false</Prefer32Bit>
+    <LangVersion>7</LangVersion>
+    <TargetFramework>net45</TargetFramework>
     <ApplicationIcon>..\assets\ckan.ico</ApplicationIcon>
     <TargetFrameworkVersion>v4.5</TargetFrameworkVersion>
     <FileAlignment>512</FileAlignment>
-    <AutoGenerateBindingRedirects>true</AutoGenerateBindingRedirects>
-    <Deterministic>true</Deterministic>
-    <DocumentationFile>$(OutputPath)$(AssemblyName).xml</DocumentationFile>
     <ErrorReport>prompt</ErrorReport>
     <WarningLevel>4</WarningLevel>
-    <Prefer32Bit>false</Prefer32Bit>
-    <LangVersion>7</LangVersion>
-  </PropertyGroup>
-  <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
-    <PlatformTarget>AnyCPU</PlatformTarget>
-    <DebugSymbols>true</DebugSymbols>
-    <DebugType>full</DebugType>
-    <Optimize>false</Optimize>
-    <DefineConstants>DEBUG;TRACE</DefineConstants>
-  </PropertyGroup>
-  <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
-    <PlatformTarget>AnyCPU</PlatformTarget>
-    <DebugType>pdbonly</DebugType>
-    <Optimize>true</Optimize>
-    <DefineConstants>TRACE</DefineConstants>
+    <DocumentationFile>$(OutputPath)$(AssemblyName).xml</DocumentationFile>
+    <TreatWarningsAsErrors>true</TreatWarningsAsErrors>
   </PropertyGroup>
   <ItemGroup>
     <PackageReference Include="Autofac" Version="4.9.4" />
@@ -45,6 +33,12 @@
     <Reference Include="System.ServiceModel" />
     <Reference Include="System.Transactions" />
   </ItemGroup>
+  <ItemGroup>
+    <ProjectReference Include="..\Core\CKAN-core.csproj">
+      <Project>{3B9AEA22-FA3B-4E43-9283-EABDD81CF271}</Project>
+      <Name>CKAN-core</Name>
+    </ProjectReference>
+  </ItemGroup>
   <ItemGroup>
     <Compile Include="..\_build\meta\GlobalAssemblyVersionInfo.cs">
       <Link>Properties\GlobalAssemblyVersionInfo.cs</Link>
@@ -52,72 +46,13 @@
     <Compile Include="..\GlobalAssemblyInfo.cs">
       <Link>Properties\GlobalAssemblyInfo.cs</Link>
     </Compile>
-    <Compile Include="AuthTokenAddDialog.cs" />
-    <Compile Include="AuthTokenListScreen.cs" />
-    <Compile Include="CompatibleVersionDialog.cs" />
-    <Compile Include="ConsoleCKAN.cs" />
-    <Compile Include="DependencyScreen.cs" />
-    <Compile Include="DownloadImportDialog.cs" />
-    <Compile Include="ExitScreen.cs" />
-    <Compile Include="InstallFilterAddDialog.cs" />
-    <Compile Include="InstallFiltersScreen.cs" />
-    <Compile Include="InstallScreen.cs" />
-    <Compile Include="GameInstanceAddScreen.cs" />
-    <Compile Include="GameInstanceEditScreen.cs" />
-    <Compile Include="GameInstanceListScreen.cs" />
-    <Compile Include="GameInstanceScreen.cs" />
-    <Compile Include="ModInfoScreen.cs" />
-    <Compile Include="ModListHelpDialog.cs" />
-    <Compile Include="ModListScreen.cs" />
-    <Compile Include="Program.cs" />
-    <Compile Include="ProgressScreen.cs" />
-    <Compile Include="Properties\AssemblyInfo.cs" />
-    <Compile Include="Properties\Settings.Designer.cs">
-      <AutoGen>True</AutoGen>
-      <DependentUpon>Settings.settings</DependentUpon>
-      <DesignTimeSharedInput>True</DesignTimeSharedInput>
-    </Compile>
-    <Compile Include="RepoAddScreen.cs" />
-    <Compile Include="RepoEditScreen.cs" />
-    <Compile Include="RepoScreen.cs" />
-    <Compile Include="SplashScreen.cs" />
-    <Compile Include="Toolkit\ConsoleButton.cs" />
-    <Compile Include="Toolkit\ConsoleChoiceDialog.cs" />
-    <Compile Include="Toolkit\ConsoleDialog.cs" />
-    <Compile Include="Toolkit\ConsoleDoubleFrame.cs" />
-    <Compile Include="Toolkit\ConsoleField.cs" />
-    <Compile Include="Toolkit\ConsoleFileMultiSelectDialog.cs" />
-    <Compile Include="Toolkit\ConsoleFrame.cs" />
-    <Compile Include="Toolkit\ConsoleLabel.cs" />
-    <Compile Include="Toolkit\ConsoleListBox.cs" />
-    <Compile Include="Toolkit\ConsoleMessageDialog.cs" />
-    <Compile Include="Toolkit\ConsolePopupMenu.cs" />
-    <Compile Include="Toolkit\ConsoleProgressBar.cs" />
-    <Compile Include="Toolkit\ConsoleScreen.cs" />
-    <Compile Include="Toolkit\ConsoleTextBox.cs" />
-    <Compile Include="Toolkit\ConsoleTheme.cs" />
-    <Compile Include="Toolkit\Formatting.cs" />
-    <Compile Include="Toolkit\Keys.cs" />
-    <Compile Include="Toolkit\ScreenContainer.cs" />
-    <Compile Include="Toolkit\ScreenObject.cs" />
-    <Compile Include="Toolkit\Symbols.cs" />
   </ItemGroup>
   <ItemGroup>
     <None Include="App.config" />
-    <None Include="Properties\Settings.settings">
-      <Generator>SettingsSingleFileGenerator</Generator>
-      <LastGenOutput>Settings.Designer.cs</LastGenOutput>
-    </None>
-  </ItemGroup>
-  <ItemGroup>
-    <ProjectReference Include="..\Core\CKAN-core.csproj">
-      <Project>{3B9AEA22-FA3B-4E43-9283-EABDD81CF271}</Project>
-      <Name>CKAN-core</Name>
-    </ProjectReference>
   </ItemGroup>
-  <Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
+  <Import Project="Sdk.targets" Sdk="Microsoft.NET.Sdk" />
   <Target Name="BeforeBuild">
     <Exec Command="powershell ../build.ps1 Generate-GlobalAssemblyVersionInfo" Condition="!Exists('../_build/meta/GlobalAssemblyVersionInfo.cs') And '$(OS)' == 'Windows_NT'" />
     <Exec Command="sh ../build Generate-GlobalAssemblyVersionInfo" Condition="!Exists('../_build/meta/GlobalAssemblyVersionInfo.cs') And '$(OS)' == 'Unix'" />
   </Target>
-</Project>
\ No newline at end of file
+</Project>
diff --git a/ConsoleUI/CompatibleVersionDialog.cs b/ConsoleUI/CompatibleVersionDialog.cs
index f878748de0..dbcd9fc760 100644
--- a/ConsoleUI/CompatibleVersionDialog.cs
+++ b/ConsoleUI/CompatibleVersionDialog.cs
@@ -30,7 +30,7 @@ public CompatibleVersionDialog(IGame game) : base()
                 options,
                 new List<ConsoleListBoxColumn<GameVersion>>() {
                     new ConsoleListBoxColumn<GameVersion>() {
-                        Header   = "Predefined Version",
+                        Header   = Properties.Resources.CompatibleVersionsListHeader,
                         Width    = r - l - 5,
                         Renderer = v => v.ToString(),
                         Comparer = (v1, v2) => v1.CompareTo(v2)
@@ -39,7 +39,7 @@ public CompatibleVersionDialog(IGame game) : base()
                 0, 0, ListSortDirection.Descending
             );
             AddObject(choices);
-            choices.AddTip("Enter", "Select version");
+            choices.AddTip(Properties.Resources.Enter, Properties.Resources.CompatibleVersionsListAcceptTip);
             choices.AddBinding(Keys.Enter, (object sender, ConsoleTheme theme) => {
                 choice = choices.Selection;
                 return false;
@@ -48,10 +48,10 @@ public CompatibleVersionDialog(IGame game) : base()
             manualEntry = new ConsoleField(
                 l + 2, b - 2, r - 2
             ) {
-                GhostText = () => "<Enter a version>"
+                GhostText = () => Properties.Resources.CompatibleVersionsGhostText
             };
             AddObject(manualEntry);
-            manualEntry.AddTip("Enter", "Accept value", () => GameVersion.TryParse(manualEntry.Value, out choice));
+            manualEntry.AddTip(Properties.Resources.Enter, Properties.Resources.CompatibleVersionsEntryAcceptTip, () => GameVersion.TryParse(manualEntry.Value, out choice));
             manualEntry.AddBinding(Keys.Enter, (object sender, ConsoleTheme theme) => {
                 if (GameVersion.TryParse(manualEntry.Value, out choice)) {
                     // Good value, done running
@@ -62,13 +62,13 @@ public CompatibleVersionDialog(IGame game) : base()
                 }
             });
 
-            AddTip("Esc", "Cancel");
+            AddTip(Properties.Resources.Esc, Properties.Resources.Cancel);
             AddBinding(Keys.Escape, (object sender, ConsoleTheme theme) => {
                 choice = null;
                 return false;
             });
 
-            CenterHeader = () => "Select Compatible Version";
+            CenterHeader = () => Properties.Resources.CompatibleVersionsTitle;
         }
 
         /// <summary>
diff --git a/ConsoleUI/ConsoleCKAN.cs b/ConsoleUI/ConsoleCKAN.cs
index c8d2dbd38c..779c1c857f 100644
--- a/ConsoleUI/ConsoleCKAN.cs
+++ b/ConsoleUI/ConsoleCKAN.cs
@@ -48,8 +48,8 @@ public ConsoleCKAN(GameInstanceManager mgr, string themeName, bool debug)
             }
             else
             {
-                Console.WriteLine("No such theme: {0}", themeName);
-                Console.WriteLine("Available themes: {0}", string.Join(", ",
+                Console.WriteLine(Properties.Resources.ThemeNotFound, themeName);
+                Console.WriteLine(Properties.Resources.ThemeList, string.Join(", ",
                     ConsoleTheme.Themes.Keys.OrderBy(th => th)));
             }
         }
diff --git a/ConsoleUI/DependencyScreen.cs b/ConsoleUI/DependencyScreen.cs
index 4040df4177..3b8349e185 100644
--- a/ConsoleUI/DependencyScreen.cs
+++ b/ConsoleUI/DependencyScreen.cs
@@ -30,7 +30,7 @@ public DependencyScreen(GameInstanceManager mgr, ChangePlan cp, HashSet<string>
 
             AddObject(new ConsoleLabel(
                 1, 2, -1,
-                () => "Additional mods are recommended or suggested:"
+                () => Properties.Resources.RecommendationsLabel
             ));
 
             HashSet<CkanModule> sourceModules = new HashSet<CkanModule>();
@@ -46,30 +46,30 @@ public DependencyScreen(GameInstanceManager mgr, ChangePlan cp, HashSet<string>
                 new List<Dependency>(dependencies.Values),
                 new List<ConsoleListBoxColumn<Dependency>>() {
                     new ConsoleListBoxColumn<Dependency>() {
-                        Header   = "Install",
+                        Header   = Properties.Resources.RecommendationsInstallHeader,
                         Width    = 7,
                         Renderer = (Dependency d) => StatusSymbol(d.module)
                     },
                     new ConsoleListBoxColumn<Dependency>() {
-                        Header   = "Name",
+                        Header   = Properties.Resources.RecommendationsNameHeader,
                         Width    = 36,
                         Renderer = (Dependency d) => d.module.ToString()
                     },
                     new ConsoleListBoxColumn<Dependency>() {
-                        Header   = "Sources",
+                        Header   = Properties.Resources.RecommendationsSourcesHeader,
                         Width    = 42,
                         Renderer = (Dependency d) => string.Join(", ", d.dependents)
                     }
                 },
                 1, 0, ListSortDirection.Descending
             );
-            dependencyList.AddTip("+", "Toggle");
+            dependencyList.AddTip("+", Properties.Resources.Toggle);
             dependencyList.AddBinding(Keys.Plus, (object sender, ConsoleTheme theme) => {
                 ChangePlan.toggleContains(accepted, dependencyList.Selection.module);
                 return true;
             });
 
-            dependencyList.AddTip("Ctrl+A", "Select all");
+            dependencyList.AddTip($"{Properties.Resources.Ctrl}+A", Properties.Resources.SelectAll);
             dependencyList.AddBinding(Keys.CtrlA, (object sender, ConsoleTheme theme) => {
                 foreach (var kvp in dependencies) {
                     if (!accepted.Contains(kvp.Key)) {
@@ -79,13 +79,13 @@ public DependencyScreen(GameInstanceManager mgr, ChangePlan cp, HashSet<string>
                 return true;
             });
 
-            dependencyList.AddTip("Ctrl+D", "Deselect all", () => accepted.Count > 0);
+            dependencyList.AddTip($"{Properties.Resources.Ctrl}+D", Properties.Resources.DeselectAll, () => accepted.Count > 0);
             dependencyList.AddBinding(Keys.CtrlD, (object sender, ConsoleTheme theme) => {
                 accepted.Clear();
                 return true;
             });
 
-            dependencyList.AddTip("Enter", "Details");
+            dependencyList.AddTip(Properties.Resources.Enter, Properties.Resources.Details);
             dependencyList.AddBinding(Keys.Enter, (object sender, ConsoleTheme theme) => {
                 if (dependencyList.Selection != null) {
                     LaunchSubScreen(theme, new ModInfoScreen(
@@ -99,7 +99,7 @@ public DependencyScreen(GameInstanceManager mgr, ChangePlan cp, HashSet<string>
 
             AddObject(dependencyList);
 
-            AddTip("Esc", "Cancel");
+            AddTip(Properties.Resources.Esc, Properties.Resources.Cancel);
             AddBinding(Keys.Escape, (object sender, ConsoleTheme theme) => {
                 // Add everything to rejected
                 foreach (var kvp in dependencies) {
@@ -108,7 +108,7 @@ public DependencyScreen(GameInstanceManager mgr, ChangePlan cp, HashSet<string>
                 return false;
             });
 
-            AddTip("F9", "Accept");
+            AddTip("F9", Properties.Resources.Accept);
             AddBinding(Keys.F9, (object sender, ConsoleTheme theme) => {
                 foreach (CkanModule mod in accepted) {
                     plan.Install.Add(mod);
@@ -136,7 +136,7 @@ protected override string LeftHeader()
         /// </summary>
         protected override string CenterHeader()
         {
-            return "Recommendations & Suggestions";
+            return Properties.Resources.RecommendationsTitle;
         }
 
         /// <summary>
@@ -197,21 +197,19 @@ private IEnumerable<string> ReplacementIdentifiers(IEnumerable<string> replaced_
 
         private string StatusSymbol(CkanModule mod)
         {
-            if (accepted.Contains(mod)) {
-                return installing;
-            } else {
-                return notinstalled;
-            }
+            return accepted.Contains(mod)
+                ? installing
+                : notinstalled;
         }
 
         private HashSet<CkanModule> accepted = new HashSet<CkanModule>();
-        private HashSet<string> rejected;
+        private HashSet<string>     rejected;
 
-        private IRegistryQuerier registry;
-        private GameInstanceManager       manager;
-        private ModuleInstaller  installer;
-        private ChangePlan       plan;
-        private bool             debug;
+        private IRegistryQuerier    registry;
+        private GameInstanceManager manager;
+        private ModuleInstaller     installer;
+        private ChangePlan          plan;
+        private bool                debug;
 
         private Dictionary<CkanModule, Dependency> dependencies = new Dictionary<CkanModule, Dependency>();
         private ConsoleListBox<Dependency>         dependencyList;
@@ -228,17 +226,17 @@ public class Dependency {
         /// <summary>
         /// Identifier of mod
         /// </summary>
-        public CkanModule       module;
+        public CkanModule   module;
 
         /// <summary>
         /// True if we default to installing, false otherwise
         /// </summary>
-        public bool             defaultInstall;
+        public bool         defaultInstall;
 
         /// <summary>
         /// List of mods that recommended or suggested this mod
         /// </summary>
-        public List<string>     dependents = new List<string>();
+        public List<string> dependents = new List<string>();
     }
 
 }
diff --git a/ConsoleUI/DownloadImportDialog.cs b/ConsoleUI/DownloadImportDialog.cs
index 5161d05689..e9e9cde8ec 100644
--- a/ConsoleUI/DownloadImportDialog.cs
+++ b/ConsoleUI/DownloadImportDialog.cs
@@ -21,15 +21,17 @@ public static class DownloadImportDialog {
         public static void ImportDownloads(ConsoleTheme theme, GameInstance gameInst, NetModuleCache cache, ChangePlan cp)
         {
             ConsoleFileMultiSelectDialog cfmsd = new ConsoleFileMultiSelectDialog(
-                "Import Downloads",
+                Properties.Resources.ImportSelectTitle,
                 FindDownloadsPath(gameInst),
                 "*.zip",
-                "Import"
+                Properties.Resources.ImportSelectHeader
             );
             HashSet<FileInfo> files = cfmsd.Run(theme);
 
             if (files.Count > 0) {
-                ProgressScreen  ps   = new ProgressScreen("Importing Downloads", "Calculating...");
+                ProgressScreen  ps   = new ProgressScreen(
+                    Properties.Resources.ImportProgressTitle,
+                    Properties.Resources.ImportProgressMessage);
                 ModuleInstaller inst = new ModuleInstaller(gameInst, cache, ps);
                 ps.Run(theme, (ConsoleTheme th) => inst.ImportFiles(files, ps,
                     (CkanModule mod) => cp.Install.Add(mod), RegistryManager.Instance(gameInst).registry));
diff --git a/ConsoleUI/ExitScreen.cs b/ConsoleUI/ExitScreen.cs
index 77c8b88732..2dead81959 100644
--- a/ConsoleUI/ExitScreen.cs
+++ b/ConsoleUI/ExitScreen.cs
@@ -1,4 +1,6 @@
 using System;
+using System.Linq;
+using System.Collections.Generic;
 using CKAN.ConsoleUI.Toolkit;
 
 namespace CKAN.ConsoleUI {
@@ -45,56 +47,36 @@ private void Draw(ConsoleTheme theme)
             }
             Console.Clear();
 
-            FancyLinePiece ckanPiece = new FancyLinePiece("CKAN", theme.ExitInnerBg, theme.ExitHighlightFg);
-    
+            // Specially formatted snippets
+            var ckanPiece = new FancyLinePiece("CKAN", theme.ExitInnerBg, theme.ExitHighlightFg);
+            var ckanVersionPiece = new FancyLinePiece($"CKAN {Meta.GetVersion()}", theme.ExitInnerBg, theme.ExitHighlightFg);
+            var releaseLinkPiece = new FancyLinePiece("https://github.com/KSP-CKAN/CKAN/releases/latest", theme.ExitInnerBg, theme.ExitLinkFg);
+            var issuesLinkPiece = new FancyLinePiece("https://github.com/KSP-CKAN/CKAN/issues", theme.ExitInnerBg, theme.ExitLinkFg);
+            var authorsLinkPiece = new FancyLinePiece("https://github.com/KSP-CKAN/CKAN/graphs/contributors", theme.ExitInnerBg, theme.ExitLinkFg);
+
             FancyLinePiece[][] lines = new FancyLinePiece[][] {
+                new FancyLinePiece(Properties.Resources.ExitTitle, theme.ExitInnerBg, theme.ExitNormalFg)
+                    .Replace("{0}", ckanPiece).ToArray(),
                 new FancyLinePiece[] {
-                    ckanPiece,
-                    new FancyLinePiece(", the Comprehensive Kerbal Archive Network", theme.ExitInnerBg, theme.ExitNormalFg)
-                }, new FancyLinePiece[] {
                     new FancyLinePiece(
                         new string(Symbols.horizLine, Console.WindowWidth - 2 -2 * horizMargin),
                         theme.ExitInnerBg, theme.ExitNormalFg)
-                }, new FancyLinePiece[] {
-                    new FancyLinePiece("YOU ARE USING ", theme.ExitInnerBg, theme.ExitNormalFg),
-                    new FancyLinePiece($"CKAN {Meta.GetVersion()}", theme.ExitInnerBg, theme.ExitHighlightFg),
-                    new FancyLinePiece(".", theme.ExitInnerBg, theme.ExitNormalFg)
-                }, new FancyLinePiece[] {
-                }, new FancyLinePiece[] {
-                    new FancyLinePiece("Thanks for downloading ", theme.ExitInnerBg, theme.ExitNormalFg),
-                    ckanPiece,
-                    new FancyLinePiece(". We hope you have as", theme.ExitInnerBg, theme.ExitNormalFg)
-                }, new FancyLinePiece[] {
-                    new FancyLinePiece("much fun using it as we had (and have) making it.", theme.ExitInnerBg, theme.ExitNormalFg)
-                }, new FancyLinePiece[] {
-                }, new FancyLinePiece[] {
-                    new FancyLinePiece("If you have paid for ", theme.ExitInnerBg, theme.ExitNormalFg),
-                    ckanPiece,
-                    new FancyLinePiece(", try to get your money back,", theme.ExitInnerBg, theme.ExitNormalFg)
-                }, new FancyLinePiece[] {
-                    new FancyLinePiece("because you can download ", theme.ExitInnerBg, theme.ExitNormalFg),
-                    ckanPiece,
-                    new FancyLinePiece(" for free from", theme.ExitInnerBg, theme.ExitNormalFg)
-                }, new FancyLinePiece[] {
-                    new FancyLinePiece("https://github.com/KSP-CKAN/CKAN/releases/latest", theme.ExitInnerBg, theme.ExitLinkFg)
-                }, new FancyLinePiece[] {
-                }, new FancyLinePiece[] {
-                    new FancyLinePiece("If you have any problems using ", theme.ExitInnerBg, theme.ExitNormalFg),
-                    ckanPiece,
-                    new FancyLinePiece(", please send us an issue at", theme.ExitInnerBg, theme.ExitNormalFg)
-                }, new FancyLinePiece[] {
-                    new FancyLinePiece("https://github.com/KSP-CKAN/CKAN/issues", theme.ExitInnerBg, theme.ExitLinkFg)
-                }, new FancyLinePiece[] {
-                }, new FancyLinePiece[] {
-                    ckanPiece,
-                    new FancyLinePiece(" WAS CREATED BY THE ", theme.ExitInnerBg, theme.ExitNormalFg),
-                    ckanPiece,
-                    new FancyLinePiece(" AUTHORS:", theme.ExitInnerBg, theme.ExitNormalFg)
-                }, new FancyLinePiece[] {
-                    new FancyLinePiece("https://github.com/KSP-CKAN/CKAN/graphs/contributors", theme.ExitInnerBg, theme.ExitLinkFg)
-                }, new FancyLinePiece[] {
-                }
-            };
+                },
+            }.Concat(
+                // Parse the single multi-line resource into array of array of FancyLinePiece
+                Properties.Resources.ExitBody.Split(new string[] { "\r\n" }, StringSplitOptions.None)
+                    // Each line generates one array of FancyLinePiece
+                    .Select(ln => new FancyLinePiece(ln, theme.ExitInnerBg, theme.ExitNormalFg)
+                        // This turns one FancyLinePiece into a sequence
+                        .Replace("{0}", ckanPiece)
+                        // From here on we go from sequence to sequence, flattening at each step
+                        .SelectMany(flp => flp.Replace("{1}", ckanVersionPiece))
+                        .SelectMany(flp => flp.Replace("{2}", releaseLinkPiece))
+                        .SelectMany(flp => flp.Replace("{3}", issuesLinkPiece))
+                        .SelectMany(flp => flp.Replace("{4}", authorsLinkPiece))
+                        .ToArray()
+                    )
+                ).ToArray();
 
             for (int i = 0; i < lines.Length; ++i) {
                 drawLine(theme, i, lines[i]);
@@ -149,6 +131,49 @@ public FancyLinePiece(string text, ConsoleColor bg, ConsoleColor fg)
             Foreground = fg;
         }
 
+        /// <summary>
+        /// Replace found tokens with a FancyLinePiece
+        /// </summary>
+        /// <param name="tokens">Values for which to search the string</param>
+        /// <param name="replacement">FancyLinePiece that should take the place of the tokens</param>
+        /// <returns>
+        /// FancyLinePiece array containing replacement where the tokens used to be
+        /// </returns>
+        public IEnumerable<FancyLinePiece> Replace(string[] tokens, FancyLinePiece replacement)
+        {
+            // Lambas can't yield return, and a separate method couldn't access our inputs
+            IEnumerable<FancyLinePiece> InjectReplacement(string p, int i)
+            {
+                if (i > 0) {
+                    // Return the replacement in between elements that weren't the token
+                    yield return replacement;
+                }
+                if (p.Length > 0) {
+                    // Skip empty pieces
+                    yield return new FancyLinePiece(p, Background, Foreground);
+                }
+            }
+            // We need to keep empty pieces from the split to handle tokens at the start
+            var pieces = Text.Split(tokens, StringSplitOptions.None);
+            return pieces.Length <= 1
+                // Stop making new objects if no tokens found
+                ? Enumerable.Repeat<FancyLinePiece>(this, 1)
+                : pieces.SelectMany(InjectReplacement);
+        }
+
+        /// <summary>
+        /// Replace found token with a FancyLinePiece
+        /// </summary>
+        /// <param name="token">Value for which to search the string</param>
+        /// <param name="replacement">FancyLinePiece that should take the place of the token</param>
+        /// <returns>
+        /// FancyLinePiece array containing replacement where the token used to be
+        /// </returns>
+        public IEnumerable<FancyLinePiece> Replace(string token, FancyLinePiece replacement)
+        {
+            return Replace(new string[] { token }, replacement);
+        }
+
         /// <summary>
         /// Draw this piece at the current cursor location.
         /// </summary>
diff --git a/ConsoleUI/GameInstanceAddScreen.cs b/ConsoleUI/GameInstanceAddScreen.cs
index 290bf620c4..b882e40cec 100644
--- a/ConsoleUI/GameInstanceAddScreen.cs
+++ b/ConsoleUI/GameInstanceAddScreen.cs
@@ -17,7 +17,7 @@ public GameInstanceAddScreen(GameInstanceManager mgr) : base(mgr)
         {
             AddObject(new ConsoleLabel(
                 labelWidth, pathRow + 1, -1,
-                () => $"Example: {examplePath}",
+                () => string.Format(Properties.Resources.InstanceAddExample, examplePath),
                 null, th => th.DimLabelFg
             ));
         }
@@ -39,7 +39,7 @@ protected override bool Valid()
         /// </summary>
         protected override string CenterHeader()
         {
-            return "Add Game Instance";
+            return Properties.Resources.InstanceAddTitle;
         }
 
         /// <summary>
diff --git a/ConsoleUI/GameInstanceEditScreen.cs b/ConsoleUI/GameInstanceEditScreen.cs
index 4ac8a6ff07..360be8920f 100644
--- a/ConsoleUI/GameInstanceEditScreen.cs
+++ b/ConsoleUI/GameInstanceEditScreen.cs
@@ -45,8 +45,8 @@ public GameInstanceEditScreen(GameInstanceManager mgr, GameInstance k)
                 // I'm not a huge fan of this layout, but I think it's better than just a label
                 AddObject(new ConsoleDoubleFrame(
                     1, repoFrameTop, -1, compatFrameBottom, compatFrameTop,
-                    () => $"Mod List Sources",
-                    () => $"Additional Compatible Versions",
+                    () => Properties.Resources.InstanceEditRepoFrameTitle,
+                    () => Properties.Resources.InstanceEditCompatFrameTitle,
                     th => th.LabelFg
                 ));
 
@@ -55,15 +55,15 @@ public GameInstanceEditScreen(GameInstanceManager mgr, GameInstance k)
                     new List<Repository>(repoEditList.Values),
                     new List<ConsoleListBoxColumn<Repository>>() {
                         new ConsoleListBoxColumn<Repository>() {
-                            Header   = "Index",
+                            Header   = Properties.Resources.InstanceEditRepoIndexHeader,
                             Renderer = r => r.priority.ToString(),
                             Width    = 7
                         }, new ConsoleListBoxColumn<Repository>() {
-                            Header   = "Name",
+                            Header   = Properties.Resources.InstanceEditRepoNameHeader,
                             Renderer = r => r.name,
                             Width    = 16
                         }, new ConsoleListBoxColumn<Repository>() {
-                            Header   = "URL",
+                            Header   = Properties.Resources.InstanceEditRepoURLHeader,
                             Renderer = r => r.uri.ToString(),
                             Width    = 50
                         }
@@ -71,13 +71,13 @@ public GameInstanceEditScreen(GameInstanceManager mgr, GameInstance k)
                     1, 0, ListSortDirection.Ascending
                 );
                 AddObject(repoList);
-                repoList.AddTip("A", "Add");
+                repoList.AddTip("A", Properties.Resources.Add);
                 repoList.AddBinding(Keys.A, (object sender, ConsoleTheme theme) => {
                     LaunchSubScreen(theme, new RepoAddScreen(ksp.game, repoEditList));
                     repoList.SetData(new List<Repository>(repoEditList.Values));
                     return true;
                 });
-                repoList.AddTip("R", "Remove");
+                repoList.AddTip("R", Properties.Resources.Remove);
                 repoList.AddBinding(Keys.R, (object sender, ConsoleTheme theme) => {
                     int oldPrio = repoList.Selection.priority;
                     repoEditList.Remove(repoList.Selection.name);
@@ -90,13 +90,13 @@ public GameInstanceEditScreen(GameInstanceManager mgr, GameInstance k)
                     repoList.SetData(new List<Repository>(repoEditList.Values));
                     return true;
                 });
-                repoList.AddTip("E", "Edit");
+                repoList.AddTip("E", Properties.Resources.Edit);
                 repoList.AddBinding(Keys.E, (object sender, ConsoleTheme theme) => {
                     LaunchSubScreen(theme, new RepoEditScreen(ksp.game, repoEditList, repoList.Selection));
                     repoList.SetData(new List<Repository>(repoEditList.Values));
                     return true;
                 });
-                repoList.AddTip("-", "Up");
+                repoList.AddTip("-", Properties.Resources.Up);
                 repoList.AddBinding(Keys.Minus, (object sender, ConsoleTheme theme) => {
                     if (repoList.Selection.priority > 0) {
                         Repository prev = SortedDictFind(repoEditList,
@@ -109,7 +109,7 @@ public GameInstanceEditScreen(GameInstanceManager mgr, GameInstance k)
                     }
                     return true;
                 });
-                repoList.AddTip("+", "Down");
+                repoList.AddTip("+", Properties.Resources.Down);
                 repoList.AddBinding(Keys.Plus, (object sender, ConsoleTheme theme) => {
                     Repository next = SortedDictFind(repoEditList,
                         r => r.priority == repoList.Selection.priority + 1);
@@ -126,7 +126,7 @@ public GameInstanceEditScreen(GameInstanceManager mgr, GameInstance k)
                     compatEditList,
                     new List<ConsoleListBoxColumn<GameVersion>>() {
                         new ConsoleListBoxColumn<GameVersion>() {
-                            Header   = "Version",
+                            Header   = Properties.Resources.InstanceEditCompatVersionHeader,
                             Width    = 10,
                             Renderer = v => v.ToString(),
                             Comparer = (a, b) => a.CompareTo(b)
@@ -136,7 +136,7 @@ public GameInstanceEditScreen(GameInstanceManager mgr, GameInstance k)
                 );
                 AddObject(compatList);
 
-                compatList.AddTip("A", "Add");
+                compatList.AddTip("A", Properties.Resources.Add);
                 compatList.AddBinding(Keys.A, (object sender, ConsoleTheme theme) => {
                     CompatibleVersionDialog vd = new CompatibleVersionDialog(ksp.game);
                     GameVersion newVersion = vd.Run(theme);
@@ -147,7 +147,7 @@ public GameInstanceEditScreen(GameInstanceManager mgr, GameInstance k)
                     }
                     return true;
                 });
-                compatList.AddTip("R", "Remove", () => compatList.Selection != null);
+                compatList.AddTip("R", Properties.Resources.Remove, () => compatList.Selection != null);
                 compatList.AddBinding(Keys.R, (object sender, ConsoleTheme theme) => {
                     compatEditList.Remove(compatList.Selection);
                     compatList.SetData(compatEditList);
@@ -159,7 +159,7 @@ public GameInstanceEditScreen(GameInstanceManager mgr, GameInstance k)
                 // Notify the user that the registry doesn't parse
                 AddObject(new ConsoleLabel(
                     1, repoFrameTop, -1,
-                    () => $"Failed to extract mod list sources from {ksp.Name}."
+                    () => string.Format(Properties.Resources.InstanceEditRegistryParseError, ksp.Name)
                 ));
 
             }
@@ -180,7 +180,7 @@ private static V SortedDictFind<K, V>(SortedDictionary<K, V> dict, Func<V, bool>
         /// </summary>
         protected override string CenterHeader()
         {
-            return "Edit Game Instance";
+            return Properties.Resources.InstanceEditTitle;
         }
 
         /// <summary>
diff --git a/ConsoleUI/GameInstanceListScreen.cs b/ConsoleUI/GameInstanceListScreen.cs
index d72d20fd74..ceca90932a 100644
--- a/ConsoleUI/GameInstanceListScreen.cs
+++ b/ConsoleUI/GameInstanceListScreen.cs
@@ -23,7 +23,7 @@ public GameInstanceListScreen(GameInstanceManager mgr, bool first = false)
 
             AddObject(new ConsoleLabel(
                 1, 2, -1,
-                () => "Select or add a game instance:"
+                () => Properties.Resources.InstanceListLabel
             ));
 
             instanceList = new ConsoleListBox<GameInstance>(
@@ -31,24 +31,24 @@ public GameInstanceListScreen(GameInstanceManager mgr, bool first = false)
                 manager.Instances.Values,
                 new List<ConsoleListBoxColumn<GameInstance>>() {
                     new ConsoleListBoxColumn<GameInstance>() {
-                        Header   = "Default",
+                        Header   = Properties.Resources.InstanceListDefaultHeader,
                         Width    = 7,
                         Renderer = StatusSymbol
                     }, new ConsoleListBoxColumn<GameInstance>() {
-                        Header   = "Name",
+                        Header   = Properties.Resources.InstanceListNameHeader,
                         Width    = 20,
                         Renderer = k => k.Name
                     }, new ConsoleListBoxColumn<GameInstance>() {
-                        Header   = "Game",
+                        Header   = Properties.Resources.InstanceListGameHeader,
                         Width    = 5,
                         Renderer = k => k.game.ShortName
                     }, new ConsoleListBoxColumn<GameInstance>() {
-                        Header   = "Version",
+                        Header   = Properties.Resources.InstanceListVersionHeader,
                         Width    = 12,
-                        Renderer = k => k.Version()?.ToString() ?? noVersion,
+                        Renderer = k => k.Version()?.ToString() ?? Properties.Resources.InstanceListNoVersion,
                         Comparer = (a, b) => a.Version()?.CompareTo(b.Version() ?? GameVersion.Any) ?? 1
                     }, new ConsoleListBoxColumn<GameInstance>() {
-                        Header   = "Path",
+                        Header   = Properties.Resources.InstanceListPathHeader,
                         Width    = 70,
                         Renderer = k => k.GameDir()
                     }
@@ -57,19 +57,19 @@ public GameInstanceListScreen(GameInstanceManager mgr, bool first = false)
             );
 
             if (first) {
-                AddTip("Ctrl+Q", "Quit");
+                AddTip($"{Properties.Resources.Ctrl}+Q", Properties.Resources.Quit);
                 AddBinding(Keys.AltX,  (object sender, ConsoleTheme theme) => false);
                 AddBinding(Keys.CtrlQ, (object sender, ConsoleTheme theme) => false);
             } else {
-                AddTip("Esc", "Quit");
+                AddTip(Properties.Resources.Esc, Properties.Resources.Quit);
                 AddBinding(Keys.Escape, (object sender, ConsoleTheme theme) => false);
             }
 
-            AddTip("Enter", "Select");
+            AddTip(Properties.Resources.Enter, Properties.Resources.Select);
             AddBinding(Keys.Enter, (object sender, ConsoleTheme theme) => {
 
                 ConsoleMessageDialog d = new ConsoleMessageDialog(
-                    $"Loading instance {instanceList.Selection.Name}...",
+                    string.Format(Properties.Resources.InstanceListLoadingInstance, instanceList.Selection.Name),
                     new List<string>()
                 );
 
@@ -87,23 +87,23 @@ public GameInstanceListScreen(GameInstanceManager mgr, bool first = false)
                 }
             });
 
-            instanceList.AddTip("A", "Add");
+            instanceList.AddTip("A", Properties.Resources.Add);
             instanceList.AddBinding(Keys.A, (object sender, ConsoleTheme theme) => {
                 LaunchSubScreen(theme, new GameInstanceAddScreen(manager));
                 instanceList.SetData(manager.Instances.Values);
                 return true;
             });
-            instanceList.AddTip("R", "Remove");
+            instanceList.AddTip("R", Properties.Resources.Remove);
             instanceList.AddBinding(Keys.R, (object sender, ConsoleTheme theme) => {
                 manager.RemoveInstance(instanceList.Selection.Name);
                 instanceList.SetData(manager.Instances.Values);
                 return true;
             });
-            instanceList.AddTip("E", "Edit");
+            instanceList.AddTip("E", Properties.Resources.Edit);
             instanceList.AddBinding(Keys.E, (object sender, ConsoleTheme theme) => {
 
                 ConsoleMessageDialog d = new ConsoleMessageDialog(
-                    $"Loading instance {instanceList.Selection.Name}...",
+                    string.Format(Properties.Resources.InstanceListLoadingInstance, instanceList.Selection.Name),
                     new List<string>()
                 );
                 TryGetInstance(theme, instanceList.Selection, (ConsoleTheme th) => { d.Run(theme, (ConsoleTheme thm) => {}); });
@@ -114,7 +114,7 @@ public GameInstanceListScreen(GameInstanceManager mgr, bool first = false)
                 return true;
             });
 
-            instanceList.AddTip("D", "Default");
+            instanceList.AddTip("D", Properties.Resources.InstanceListDefaultToggle);
             instanceList.AddBinding(Keys.D, (object sender, ConsoleTheme theme) => {
                 string name = instanceList.Selection.Name;
                 if (name == manager.AutoStartInstance) {
@@ -124,8 +124,8 @@ public GameInstanceListScreen(GameInstanceManager mgr, bool first = false)
                         manager.SetAutoStart(name);
                     } catch (NotKSPDirKraken k) {
                         ConsoleMessageDialog errd = new ConsoleMessageDialog(
-                            $"Error loading {k.path}:\n{k.Message}",
-                            new List<string>() {"OK"}
+                            string.Format(Properties.Resources.InstanceListLoadingError, k.path, k.Message),
+                            new List<string>() { Properties.Resources.OK }
                         );
                         errd.Run(theme);
                     }
@@ -150,7 +150,7 @@ protected override string LeftHeader()
         /// </summary>
         protected override string CenterHeader()
         {
-            return "Game Instances";
+            return Properties.Resources.InstanceListTitle;
         }
 
         /// <summary>
@@ -158,7 +158,7 @@ protected override string CenterHeader()
         /// </summary>
         protected override string MenuTip()
         {
-            return "Sort";
+            return Properties.Resources.Sort;
         }
 
         /// <summary>
@@ -185,11 +185,11 @@ public static bool TryGetInstance(ConsoleTheme theme, GameInstance ksp, Action<C
                 } catch (RegistryInUseKraken k) {
 
                     ConsoleMessageDialog md = new ConsoleMessageDialog(
-                        $"Lock file with live process ID found at {k.lockfilePath}\n\n"
-                        + "This means that another instance of CKAN probably is accessing this instance."
-                        + " You can delete the file to continue, but data corruption is very likely.\n\n"
-                        + "Do you want to delete this lock file to force access?",
-                        new List<string>() {"Cancel", "Force"}
+                        string.Format(Properties.Resources.InstanceListLocked, k.lockfilePath),
+                        new List<string>() {
+                            Properties.Resources.Cancel,
+                            Properties.Resources.Force
+                        }
                     );
                     if (md.Run(theme) == 1) {
                         // Delete it
@@ -203,8 +203,8 @@ public static bool TryGetInstance(ConsoleTheme theme, GameInstance ksp, Action<C
                 } catch (NotKSPDirKraken k) {
 
                     ConsoleMessageDialog errd = new ConsoleMessageDialog(
-                        $"Error loading {ksp.GameDir()}:\n{k.Message}",
-                        new List<string>() {"OK"}
+                        string.Format(Properties.Resources.InstanceListLoadingError, ksp.GameDir(), k.Message),
+                        new List<string>() { Properties.Resources.OK }
                     );
                     errd.Run(theme);
                     return false;
@@ -212,8 +212,8 @@ public static bool TryGetInstance(ConsoleTheme theme, GameInstance ksp, Action<C
                 } catch (Exception e) {
 
                     ConsoleMessageDialog errd = new ConsoleMessageDialog(
-                        $"Error loading {Path.Combine(ksp.CkanDir(), "registry.json")}:\n{e.Message}",
-                        new List<string>() {"OK"}
+                        string.Format(Properties.Resources.InstanceListLoadingError, Path.Combine(ksp.CkanDir(), "registry.json"), e.Message),
+                        new List<string>() { Properties.Resources.OK }
                     );
                     errd.Run(theme);
                     return false;
@@ -236,7 +236,6 @@ private string StatusSymbol(GameInstance k)
         private GameInstanceManager          manager;
         private ConsoleListBox<GameInstance> instanceList;
 
-        private const string noVersion = "<NONE>";
         private static readonly string defaultMark = Symbols.checkmark;
     }
 
diff --git a/ConsoleUI/GameInstanceScreen.cs b/ConsoleUI/GameInstanceScreen.cs
index 6be65334f7..060368527a 100644
--- a/ConsoleUI/GameInstanceScreen.cs
+++ b/ConsoleUI/GameInstanceScreen.cs
@@ -1,3 +1,4 @@
+using System;
 using System.IO;
 using CKAN.ConsoleUI.Toolkit;
 
@@ -18,7 +19,7 @@ protected GameInstanceScreen(GameInstanceManager mgr, string initName = "", stri
         {
             manager = mgr;
 
-            AddTip("F2", "Accept");
+            AddTip("F2", Properties.Resources.Accept);
             AddBinding(Keys.F2, (object sender, ConsoleTheme theme) => {
                 if (Valid()) {
                     Save();
@@ -30,22 +31,22 @@ protected GameInstanceScreen(GameInstanceManager mgr, string initName = "", stri
                 }
             });
 
-            AddTip("Esc", "Cancel");
+            AddTip(Properties.Resources.Esc, Properties.Resources.Cancel);
             AddBinding(Keys.Escape, (object sender, ConsoleTheme theme) => {
                 // Discard changes
                 return false;
             });
 
             name = new ConsoleField(labelWidth, nameRow, -1, initName) {
-                GhostText = () => "<Enter the name to use for this game instance>"
+                GhostText = () => Properties.Resources.InstanceNameGhostText
             };
             path = new ConsoleField(labelWidth, pathRow, -1, initPath) {
-                GhostText = () => "<Enter the location of this game instance on disk>"
+                GhostText = () => Properties.Resources.InstancePathGhostText
             };
 
-            AddObject(new ConsoleLabel(1, nameRow, labelWidth, () => "Name:"));
+            AddObject(new ConsoleLabel(1, nameRow, labelWidth, () => Properties.Resources.InstanceNameLabel));
             AddObject(name);
-            AddObject(new ConsoleLabel(1, pathRow, labelWidth, () => "Path to game instance:"));
+            AddObject(new ConsoleLabel(1, pathRow, labelWidth, () => Properties.Resources.InstancePathLabel));
             AddObject(path);
         }
 
@@ -76,12 +77,12 @@ protected bool nameValid()
         {
             if (string.IsNullOrEmpty(name.Value)) {
                 // Complain about empty name
-                RaiseError("Name cannot be empty!");
+                RaiseError(Properties.Resources.InstanceNameEmptyError);
                 SetFocus(name);
                 return false;
             } else if (manager.HasInstance(name.Value)) {
                 // Complain about duplicate name
-                RaiseError($"{name.Value} already exists!");
+                RaiseError(Properties.Resources.InstanceNameDuplicateError, name.Value);
                 SetFocus(name);
                 return false;
             } else {
@@ -108,7 +109,7 @@ protected bool pathValid()
                 // Pretend DirectoryInfo constructed an instance that made IsGameInstanceDir return false
             }
             // Complain about non-KSP path
-            RaiseError("Path does not correspond to a game folder!");
+            RaiseError(Properties.Resources.InstancePathNotGameFolderError);
             SetFocus(path);
             return false;
         }
@@ -130,7 +131,10 @@ protected bool pathValid()
         /// <summary>
         /// Number of columns reserved at left of screen for labels
         /// </summary>
-        protected const int labelWidth = 24;
+        protected int labelWidth => Math.Max(24, Math.Max(
+            Properties.Resources.InstanceNameLabel.Length,
+            Properties.Resources.InstancePathLabel.Length
+        ));
         private   const int nameRow    = 2;
         /// <summary>
         /// Y coordinate of path field
diff --git a/ConsoleUI/InstallFilterAddDialog.cs b/ConsoleUI/InstallFilterAddDialog.cs
index 748a23d9de..94d5c500f6 100644
--- a/ConsoleUI/InstallFilterAddDialog.cs
+++ b/ConsoleUI/InstallFilterAddDialog.cs
@@ -28,22 +28,22 @@ public InstallFilterAddDialog() : base()
             manualEntry = new ConsoleField(
                 l + 2, b - 2, r - 2
             ) {
-                GhostText = () => "<Enter a filter>"
+                GhostText = () => Properties.Resources.FilterAddGhostText
             };
             AddObject(manualEntry);
-            manualEntry.AddTip("Enter", "Accept value");
+            manualEntry.AddTip(Properties.Resources.Enter, Properties.Resources.FilterAddAcceptTip);
             manualEntry.AddBinding(Keys.Enter, (object sender, ConsoleTheme theme) => {
                 choice = manualEntry.Value;
                 return false;
             });
 
-            AddTip("Esc", "Cancel");
+            AddTip(Properties.Resources.Esc, Properties.Resources.Cancel);
             AddBinding(Keys.Escape, (object sender, ConsoleTheme theme) => {
                 choice = null;
                 return false;
             });
 
-            CenterHeader = () => "Add Filter";
+            CenterHeader = () => Properties.Resources.FilterAddTitle;
         }
 
         /// <summary>
diff --git a/ConsoleUI/InstallFiltersScreen.cs b/ConsoleUI/InstallFiltersScreen.cs
index e633e1acdf..6c71031c02 100644
--- a/ConsoleUI/InstallFiltersScreen.cs
+++ b/ConsoleUI/InstallFiltersScreen.cs
@@ -7,8 +7,16 @@
 
 namespace CKAN.ConsoleUI {
 
+    /// <summary>
+    /// A screen for editing the global and instance install filters
+    /// </summary>
     public class InstallFiltersScreen : ConsoleScreen {
 
+        /// <summary>
+        /// Initialize the screen
+        /// </summary>
+        /// <param name="globalConfig">Object holding the global configuration</param>
+        /// <param name="instance">The current instance</param>
         public InstallFiltersScreen(IConfiguration globalConfig, GameInstance instance)
         {
             this.globalConfig = globalConfig;
@@ -16,21 +24,21 @@ public InstallFiltersScreen(IConfiguration globalConfig, GameInstance instance)
             globalFilters   = globalConfig.GlobalInstallFilters.ToList();
             instanceFilters = instance.InstallFilters.ToList();
 
-            AddTip("F2", "Accept");
+            AddTip("F2", Properties.Resources.Accept);
             AddBinding(Keys.F2, (object sender, ConsoleTheme theme) => {
                 Save();
                 // Close screen
                 return false;
             });
-            AddTip("Esc", "Cancel");
+            AddTip(Properties.Resources.Esc, Properties.Resources.Cancel);
             AddBinding(Keys.Escape, (object sender, ConsoleTheme theme) => {
                 // Discard changes
                 return false;
             });
 
             mainMenu = new ConsolePopupMenu(new List<ConsoleMenuOption>() {
-                new ConsoleMenuOption("Add MiniAVC", "",
-                    "Prevent MiniAVC from being installed",
+                new ConsoleMenuOption(Properties.Resources.FiltersAddMiniAVCMenu, "",
+                    Properties.Resources.FiltersAddMiniAVCMenuTip,
                     true, AddMiniAVC),
             });
 
@@ -41,7 +49,7 @@ public InstallFiltersScreen(IConfiguration globalConfig, GameInstance instance)
                 globalFilters,
                 new List<ConsoleListBoxColumn<string>>() {
                     new ConsoleListBoxColumn<string>() {
-                        Header   = "Global Filters",
+                        Header   = Properties.Resources.FiltersGlobalHeader,
                         Width    = 40,
                         Renderer = f => f,
                     }
@@ -49,12 +57,12 @@ public InstallFiltersScreen(IConfiguration globalConfig, GameInstance instance)
                 0
             );
             AddObject(globalList);
-            globalList.AddTip("A", "Add");
+            globalList.AddTip("A", Properties.Resources.Add);
             globalList.AddBinding(Keys.A, (object sender, ConsoleTheme theme) => {
                 AddFilter(theme, globalList, globalFilters);
                 return true;
             });
-            globalList.AddTip("R", "Remove");
+            globalList.AddTip("R", Properties.Resources.Remove);
             globalList.AddBinding(Keys.R, (object sender, ConsoleTheme theme) => {
                 RemoveFilter(globalList, globalFilters);
                 return true;
@@ -64,7 +72,7 @@ public InstallFiltersScreen(IConfiguration globalConfig, GameInstance instance)
                 instanceFilters,
                 new List<ConsoleListBoxColumn<string>>() {
                     new ConsoleListBoxColumn<string>() {
-                        Header   = "Instance Filters",
+                        Header   = Properties.Resources.FiltersInstanceHeader,
                         Width    = 40,
                         Renderer = f => f,
                     }
@@ -72,12 +80,12 @@ public InstallFiltersScreen(IConfiguration globalConfig, GameInstance instance)
                 0
             );
             AddObject(instanceList);
-            instanceList.AddTip("A", "Add");
+            instanceList.AddTip("A", Properties.Resources.Add);
             instanceList.AddBinding(Keys.A, (object sender, ConsoleTheme theme) => {
                 AddFilter(theme, instanceList, instanceFilters);
                 return true;
             });
-            instanceList.AddTip("R", "Remove");
+            instanceList.AddTip("R", Properties.Resources.Remove);
             instanceList.AddBinding(Keys.R, (object sender, ConsoleTheme theme) => {
                 RemoveFilter(instanceList, instanceFilters);
                 return true;
@@ -97,7 +105,7 @@ protected override string LeftHeader()
         /// </summary>
         protected override string CenterHeader()
         {
-            return "Installation Filters";
+            return Properties.Resources.FiltersTitle;
         }
 
         private bool AddMiniAVC(ConsoleTheme theme)
diff --git a/ConsoleUI/InstallScreen.cs b/ConsoleUI/InstallScreen.cs
index d8233a2230..2b0f0dd36c 100644
--- a/ConsoleUI/InstallScreen.cs
+++ b/ConsoleUI/InstallScreen.cs
@@ -18,8 +18,8 @@ public class InstallScreen : ProgressScreen {
         /// <param name="dbg">True if debug options should be available, false otherwise</param>
         public InstallScreen(GameInstanceManager mgr, ChangePlan cp, bool dbg)
             : base(
-                "Installing, Updating, and Removing Mods",
-                "Calculating..."
+                Properties.Resources.InstallTitle,
+                Properties.Resources.InstallMessage
             )
         {
             debug   = dbg;
@@ -93,19 +93,17 @@ public override void Run(ConsoleTheme theme, Action<ConsoleTheme> process = null
                         RaiseError(ex.Message);
                     } catch (FileExistsKraken ex) {
                         if (ex.owningModule != null) {
-                            RaiseMessage($"{ex.installingModule} tried to install {ex.filename}, but {ex.owningModule} has already installed it.");
-                            RaiseMessage($"Please report this problem at https://github.com/KSP-CKAN/NetKAN/issues/new/choose");
+                            RaiseMessage(Properties.Resources.InstallOwnedFileConflict, ex.installingModule, ex.filename, ex.owningModule);
                         } else {
-                            RaiseMessage($"{ex.installingModule} tried to install {ex.filename}, but it is already installed.");
-                            RaiseMessage($"Please manually uninstall the mod that owns this file to install {ex.installingModule}.");
+                            RaiseMessage(Properties.Resources.InstallUnownedFileConflict, ex.installingModule, ex.filename, ex.installingModule);
                         }
-                        RaiseError("Game files reverted.");
+                        RaiseError(Properties.Resources.InstallFilesReverted);
                     } catch (DownloadErrorsKraken ex) {
                         RaiseError(ex.ToString());
                     } catch (ModuleDownloadErrorsKraken ex) {
                         RaiseError(ex.ToString());
                     } catch (DownloadThrottledKraken ex) {
-                        if (RaiseYesNoDialog($"{ex.ToString()}\n\nEdit authentication tokens now?")) {
+                        if (RaiseYesNoDialog(string.Format(Properties.Resources.InstallAuthTokenPrompt, ex.ToString()))) {
                             if (ex.infoUrl != null) {
                                 ModInfoScreen.LaunchURL(theme, ex.infoUrl);
                             }
@@ -119,7 +117,7 @@ public override void Run(ConsoleTheme theme, Action<ConsoleTheme> process = null
 
                         ConsoleChoiceDialog<CkanModule> ch = new ConsoleChoiceDialog<CkanModule>(
                             ex.Message,
-                            "Name",
+                            Properties.Resources.InstallTooManyModsNameHeader,
                             ex.modules,
                             (CkanModule mod) => mod.ToString()
                         );
@@ -132,13 +130,13 @@ public override void Run(ConsoleTheme theme, Action<ConsoleTheme> process = null
                         }
 
                     } catch (BadMetadataKraken ex) {
-                        RaiseError($"Bad metadata detected for {ex.module}: {ex.Message}");
+                        RaiseError(Properties.Resources.InstallBadMetadata, ex.module, ex.Message);
                     } catch (DependencyNotSatisfiedKraken ex) {
-                        RaiseError($"{ex.parent} requires {ex.module}, but it is not listed in the index, or not available for your version of the game.\r\n{ex.Message}");
+                        RaiseError(Properties.Resources.InstallUnsatisfiedDependency, ex.parent, ex.module, ex.Message);
                     } catch (ModuleNotFoundKraken ex) {
-                        RaiseError($"Module {ex.module} required but it is not listed in the index, or not available for your version of the game.\r\n{ex.Message}");
+                        RaiseError(Properties.Resources.InstallModuleNotFound, ex.module, ex.Message);
                     } catch (ModNotInstalledKraken ex) {
-                        RaiseError($"{ex.mod} is not installed, can't remove");
+                        RaiseError(Properties.Resources.InstallNotInstalled, ex.mod);
                     } catch (DllLocationMismatchKraken ex) {
                         RaiseError(ex.Message);
                     }
@@ -148,7 +146,7 @@ public override void Run(ConsoleTheme theme, Action<ConsoleTheme> process = null
 
         private void OnModInstalled(CkanModule mod)
         {
-            RaiseMessage($"{Symbols.checkmark} Successfully installed {mod.name} {ModuleInstaller.StripEpoch(mod.version)}");
+            RaiseMessage(Properties.Resources.InstallModInstalled, Symbols.checkmark, mod.name, ModuleInstaller.StripEpoch(mod.version));
         }
 
         private IEnumerable<ModuleReplacement> AllReplacements(IEnumerable<string> identifiers)
diff --git a/ConsoleUI/ModInfoScreen.cs b/ConsoleUI/ModInfoScreen.cs
index 832ace4bc9..db7231d7ae 100644
--- a/ConsoleUI/ModInfoScreen.cs
+++ b/ConsoleUI/ModInfoScreen.cs
@@ -38,7 +38,7 @@ public ModInfoScreen(GameInstanceManager mgr, ChangePlan cp, CkanModule m, bool
             ));
             AddObject(new ConsoleLabel(
                 1, 2, -1,
-                () => $"By {string.Join(", ", mod.author)}"
+                () => string.Format(Properties.Resources.ModInfoAuthors, string.Join(", ", mod.author))
             ));
 
             AddObject(new ConsoleFrame(
@@ -49,7 +49,7 @@ public ModInfoScreen(GameInstanceManager mgr, ChangePlan cp, CkanModule m, bool
             ));
             AddObject(new ConsoleLabel(
                 3, 4, 11,
-                () => "License:",
+                () => Properties.Resources.ModInfoLicence,
                 null,
                 th => th.DimLabelFg
             ));
@@ -60,7 +60,7 @@ public ModInfoScreen(GameInstanceManager mgr, ChangePlan cp, CkanModule m, bool
             ));
             AddObject(new ConsoleLabel(
                 3, 5, 12,
-                () => "Download:",
+                () => Properties.Resources.ModInfoDownload,
                 null,
                 th => th.DimLabelFg
             ));
@@ -91,7 +91,7 @@ public ModInfoScreen(GameInstanceManager mgr, ChangePlan cp, CkanModule m, bool
 
             AddObject(new ConsoleFrame(
                 1, Math.Max(depsBot, versBot) + 1, -1, -1,
-                () => "Description",
+                () => Properties.Resources.ModInfoDescriptionFrame,
                 th => th.NormalFrameFg,
                 false
             ));
@@ -108,15 +108,14 @@ public ModInfoScreen(GameInstanceManager mgr, ChangePlan cp, CkanModule m, bool
             }
             AddObject(tb);
             if (!ChangePlan.IsAnyAvailable(registry, mod.identifier)) {
-                tb.AddLine("\r\nNOTE: This mod is installed but no longer available.");
-                tb.AddLine("If you uninstall it, CKAN will not be able to re-install it.");
+                tb.AddLine(Properties.Resources.ModInfoUnavailableWarning);
             }
             tb.AddScrollBindings(this);
 
-            AddTip("Esc", "Back");
+            AddTip(Properties.Resources.Esc, Properties.Resources.Back);
             AddBinding(Keys.Escape, (object sender, ConsoleTheme theme) => false);
 
-            AddTip("Ctrl+D", "Download to cache (does not install)",
+            AddTip($"{Properties.Resources.Ctrl}+D", Properties.Resources.ModInfoDownloadToCache,
                 () => !manager.Cache.IsMaybeCachedZip(mod) && !mod.IsDLC
             );
             AddBinding(Keys.CtrlD, (object sender, ConsoleTheme theme) => {
@@ -131,49 +130,49 @@ public ModInfoScreen(GameInstanceManager mgr, ChangePlan cp, CkanModule m, bool
 
                 if (mod.resources.homepage != null) {
                     opts.Add(new ConsoleMenuOption(
-                        "Home page",  "", "Open the home page URL in a browser",
+                        Properties.Resources.ModInfoHomePage,  "", Properties.Resources.ModInfoHomePageTip,
                         true,
                         th => LaunchURL(th, mod.resources.homepage)
                     ));
                 }
                 if (mod.resources.repository != null) {
                     opts.Add(new ConsoleMenuOption(
-                        "Repository", "", "Open the repository URL in a browser",
+                        Properties.Resources.ModInfoRepository, "", Properties.Resources.ModInfoRepositoryTip,
                         true,
                         th => LaunchURL(th, mod.resources.repository)
                     ));
                 }
                 if (mod.resources.bugtracker != null) {
                     opts.Add(new ConsoleMenuOption(
-                        "Bugtracker", "", "Open the bug tracker URL in a browser",
+                        Properties.Resources.ModInfoBugtracker, "", Properties.Resources.ModInfoBugtrackerTip,
                         true,
                         th => LaunchURL(th, mod.resources.bugtracker)
                     ));
                 }
                 if (mod.resources.spacedock != null) {
                     opts.Add(new ConsoleMenuOption(
-                        "SpaceDock",  "", "Open the SpaceDock URL in a browser",
+                        Properties.Resources.ModInfoSpaceDock,  "", Properties.Resources.ModInfoSpaceDockTip,
                         true,
                         th => LaunchURL(th, mod.resources.spacedock)
                     ));
                 }
                 if (mod.resources.curse != null) {
                     opts.Add(new ConsoleMenuOption(
-                        "Curse",      "", "Open the Curse URL in a browser",
+                        Properties.Resources.ModInfoCurse,      "", Properties.Resources.ModInfoCurseTip,
                         true,
                         th => LaunchURL(th, mod.resources.curse)
                     ));
                 }
                 if (mod.resources.store != null) {
                     opts.Add(new ConsoleMenuOption(
-                        "Store",      "", "Open the Store URL in a browser",
+                        Properties.Resources.ModInfoStore,      "", Properties.Resources.ModInfoStoreTip,
                         true,
                         th => LaunchURL(th, mod.resources.store)
                     ));
                 }
                 if (mod.resources.steamstore != null) {
                     opts.Add(new ConsoleMenuOption(
-                        "Steam Store", "", "Open the Steam Store URL in a browser",
+                        Properties.Resources.ModInfoSteamStore, "", Properties.Resources.ModInfoSteamStoreTip,
                         true,
                         th => LaunchURL(th, mod.resources.steamstore)
                     ));
@@ -181,7 +180,7 @@ public ModInfoScreen(GameInstanceManager mgr, ChangePlan cp, CkanModule m, bool
                 if (debug) {
                     opts.Add(null);
                     opts.Add(new ConsoleMenuOption(
-                        "DEBUG: View metadata", "", "Display the full registry data for this mod",
+                        Properties.Resources.ModInfoViewMetadata, "", Properties.Resources.ModInfoViewMetadataTip,
                         true,
                         ViewMetadata
                     ));
@@ -206,7 +205,7 @@ protected override string LeftHeader()
         /// </summary>
         protected override string CenterHeader()
         {
-            return "Mod Details";
+            return Properties.Resources.ModInfoTitle;
         }
 
         /// <summary>
@@ -214,15 +213,15 @@ protected override string CenterHeader()
         /// </summary>
         protected override string MenuTip()
         {
-            return "Links";
+            return Properties.Resources.ModInfoMenuTip;
         }
 
         private bool ViewMetadata(ConsoleTheme theme)
         {
             ConsoleMessageDialog md = new ConsoleMessageDialog(
                 $"\"{mod.identifier}\": {registry.GetAvailableMetadata(mod.identifier)}",
-                new List<string> {"OK"},
-                () => $"{mod.name} Metadata",
+                new List<string> { Properties.Resources.OK },
+                () => string.Format(Properties.Resources.ModInfoViewMetadataTitle, mod.name),
                 TextAlign.Left
             );
             md.Run(theme);
@@ -247,7 +246,7 @@ public static bool LaunchURL(ConsoleTheme theme, Uri u)
             // support launching URLs!  .NET's API design has painted us into a corner.
             // So instead we display a popup dialog for the garbage to print all over,
             // then wait 1.5 seconds and refresh the screen when it closes.
-            ConsoleMessageDialog d = new ConsoleMessageDialog("Launching...", new List<string>());
+            ConsoleMessageDialog d = new ConsoleMessageDialog(Properties.Resources.ModInfoURLLaunching, new List<string>());
             d.Run(theme, (ConsoleTheme th) => {
                 Utilities.ProcessStartURL(u.ToString());
                 System.Threading.Thread.Sleep(1500);
@@ -269,14 +268,14 @@ private int addDependencies(int top = 8)
 
                 AddObject(new ConsoleFrame(
                     1, top, midL, top + h - 1,
-                    () => "Dependencies",
+                    () => Properties.Resources.ModInfoDependenciesFrame,
                     th => th.NormalFrameFg,
                     false
                 ));
                 if (numDeps > 0) {
                     AddObject(new ConsoleLabel(
                         3, top + 1, 3 + lblW - 1,
-                        () => $"Required ({numDeps}):",
+                        () => string.Format(Properties.Resources.ModInfoRequiredLabel, numDeps),
                         null,
                         th => th.DimLabelFg
                     ));
@@ -299,7 +298,7 @@ private int addDependencies(int top = 8)
                 if (numConfs > 0) {
                     AddObject(new ConsoleLabel(
                         3, top + 1 + depsH, 3 + lblW - 1,
-                        () => $"Conflicts ({numConfs}):",
+                        () => string.Format(Properties.Resources.ModInfoConflictsLabel, numConfs),
                         null,
                         th => th.DimLabelFg
                     ));
@@ -367,7 +366,7 @@ private int addVersionDisplay()
                             // Show replaced_by
                             addVersionBox(
                                 boxLeft, boxTop, boxRight, boxTop + boxH - 1,
-                                () => $"Replaced by {mr.ReplaceWith.identifier}",
+                                () => string.Format(Properties.Resources.ModInfoReplacedBy, mr.ReplaceWith.identifier),
                                 th => th.AlertFrameFg,
                                 false,
                                 new List<CkanModule>() {mr.ReplaceWith}
@@ -376,7 +375,9 @@ private int addVersionDisplay()
 
                             addVersionBox(
                                 boxLeft, boxTop, boxRight, boxTop + boxH - 1,
-                                () => $"Installed {instTime?.ToString("d") ?? "manually"}",
+                                () => instTime.HasValue
+                                    ? string.Format(Properties.Resources.ModInfoInstalledOn, instTime.Value.ToString("d"))
+                                    : Properties.Resources.ModInfoInstalledManually,
                                 th => th.ActiveFrameFg,
                                 true,
                                 new List<CkanModule>() {inst}
@@ -387,7 +388,9 @@ private int addVersionDisplay()
 
                             addVersionBox(
                                 boxLeft, boxTop, boxRight, boxTop + boxH - 1,
-                                () => $"Latest/Installed {instTime?.ToString("d") ?? "manually"}",
+                                () => instTime.HasValue
+                                    ? string.Format(Properties.Resources.ModInfoLatestInstalledOn, instTime.Value.ToString("d"))
+                                    : Properties.Resources.ModInfoLatestInstalledManually,
                                 th => th.ActiveFrameFg,
                                 true,
                                 new List<CkanModule>() {inst}
@@ -401,7 +404,7 @@ private int addVersionDisplay()
 
                         addVersionBox(
                             boxLeft, boxTop, boxRight, boxTop + boxH - 1,
-                            () => "Latest Version",
+                            () => Properties.Resources.ModInfoLatestVersion,
                             th => th.AlertFrameFg,
                             false,
                             new List<CkanModule>() {latest}
@@ -410,7 +413,9 @@ private int addVersionDisplay()
 
                         addVersionBox(
                             boxLeft, boxTop, boxRight, boxTop + boxH - 1,
-                            () => $"Installed {instTime?.ToString("d") ?? "manually"}",
+                                () => instTime.HasValue
+                                    ? string.Format(Properties.Resources.ModInfoInstalledOn, instTime.Value.ToString("d"))
+                                    : Properties.Resources.ModInfoInstalledManually,
                             th => th.ActiveFrameFg,
                             true,
                             new List<CkanModule>() {inst}
@@ -422,7 +427,7 @@ private int addVersionDisplay()
 
                     addVersionBox(
                         boxLeft, boxTop, boxRight, boxTop + boxH - 1,
-                        () => "Latest Version",
+                            () => Properties.Resources.ModInfoLatestVersion,
                         th => th.NormalFrameFg,
                         false,
                         new List<CkanModule>() {latest}
@@ -435,7 +440,7 @@ private int addVersionDisplay()
 
                     addVersionBox(
                         boxLeft, boxTop, boxRight, boxTop + boxH - 1,
-                        () => "Other Versions",
+                        () => Properties.Resources.ModInfoOtherVersions,
                         th => th.NormalFrameFg,
                         false,
                         others
@@ -451,7 +456,9 @@ private int addVersionDisplay()
                 // of the old info about it from when we installed it
                 addVersionBox(
                     boxLeft, boxTop, boxRight, boxTop + boxH - 1,
-                    () => $"UNAVAILABLE/Installed {instTime?.ToString("d") ?? "manually"}",
+                    () => instTime.HasValue
+                        ? string.Format(Properties.Resources.ModInfoUnavailableInstalledOn, instTime.Value.ToString("d"))
+                        : Properties.Resources.ModInfoUnavailableInstalledManually,
                     th => th.AlertFrameFg,
                     true,
                     new List<CkanModule>() {mod}
@@ -487,7 +494,7 @@ private void addVersionBox(int l, int t, int r, int b, Func<string> title, Func<
                 ));
                 AddObject(new ConsoleLabel(
                     l + 2, t + 2, r - 2,
-                    () => "Compatible with:",
+                    () => Properties.Resources.ModInfoCompatibleWith,
                     null,
                     th => th.DimLabelFg
                 ));
@@ -506,7 +513,7 @@ private string HostedOn()
             string dl = mod.download?.ToString() ?? "";
             foreach (var kvp in hostDomains) {
                 if (dl.IndexOf(kvp.Key, StringComparison.CurrentCultureIgnoreCase) >= 0) {
-                    return $"Hosted on {kvp.Value}";
+                    return string.Format(Properties.Resources.ModInfoHostedOn, kvp.Value);
                 }
             }
             if (mod.resources != null) {
@@ -514,7 +521,7 @@ private string HostedOn()
                     string bt = mod.resources.bugtracker.ToString();
                     foreach (var kvp in hostDomains) {
                         if (bt.IndexOf(kvp.Key, StringComparison.CurrentCultureIgnoreCase) >= 0) {
-                            return $"Report bugs on {kvp.Value}";
+                            return string.Format(Properties.Resources.ModInfoReportBugsOn, kvp.Value);
                         }
                     }
                 }
@@ -522,7 +529,7 @@ private string HostedOn()
                     string rep = mod.resources.repository.ToString();
                     foreach (var kvp in hostDomains) {
                         if (rep.IndexOf(kvp.Key, StringComparison.CurrentCultureIgnoreCase) >= 0) {
-                            return $"Repository on {kvp.Value}";
+                            return string.Format(Properties.Resources.ModInfoRepositoryOn, kvp.Value);
                         }
                     }
                 }
@@ -530,20 +537,15 @@ private string HostedOn()
                     string hp = mod.resources.homepage.ToString();
                     foreach (var kvp in hostDomains) {
                         if (hp.IndexOf(kvp.Key, StringComparison.CurrentCultureIgnoreCase) >= 0) {
-                            return $"Home page on {kvp.Value}";
+                            return string.Format(Properties.Resources.ModInfoHomePageOn, kvp.Value);
                         }
                     }
                 }
 
                 if (mod.resources.store != null || mod.resources.steamstore != null) {
-                    List<string> stores = new List<string>();
-                    if (mod.resources.store != null) {
-                        stores.Add("KSP store");
-                    }
-                    if (mod.resources.steamstore != null) {
-                        stores.Add("Steam store");
-                    }
-                    return $"Buy from {string.Join(" or ", stores)}";
+                    return mod.resources.steamstore == null ? Properties.Resources.ModInfoBuyFromKSPStore
+                        :  mod.resources.store      == null ? Properties.Resources.ModInfoBuyFromSteamStore
+                        :                                     Properties.Resources.ModInfoBuyFromKSPStoreOrSteamStore;
                 }
             }
             return mod.download?.Host ?? "";
@@ -551,7 +553,7 @@ private string HostedOn()
 
         private void Download(ConsoleTheme theme)
         {
-            ProgressScreen            ps   = new ProgressScreen($"Downloading {mod.identifier}");
+            ProgressScreen            ps   = new ProgressScreen(string.Format(Properties.Resources.ModInfoDownloading, mod.identifier));
             NetAsyncModulesDownloader dl   = new NetAsyncModulesDownloader(ps, manager.Cache);
             ModuleInstaller           inst = new ModuleInstaller(manager.CurrentInstance, manager.Cache, ps);
             LaunchSubScreen(
@@ -561,10 +563,10 @@ private void Download(ConsoleTheme theme)
                     try {
                         dl.DownloadModules(new List<CkanModule> {mod});
                         if (!manager.Cache.IsMaybeCachedZip(mod)) {
-                            ps.RaiseError("Download failed, file is corrupted");
+                            ps.RaiseError(Properties.Resources.ModInfoDownloadFailed);
                         }
                     } catch (Exception ex) {
-                        ps.RaiseError($"Download failed: {ex}");
+                        ps.RaiseError(Properties.Resources.ModInfoDownloadFailed, ex);
                     }
                 }
             );
diff --git a/ConsoleUI/ModListHelpDialog.cs b/ConsoleUI/ModListHelpDialog.cs
index ade60c16b0..5f110337ff 100644
--- a/ConsoleUI/ModListHelpDialog.cs
+++ b/ConsoleUI/ModListHelpDialog.cs
@@ -1,4 +1,6 @@
 using System;
+using System.Linq;
+using System.Text;
 using CKAN.ConsoleUI.Toolkit;
 
 namespace CKAN.ConsoleUI {
@@ -27,20 +29,26 @@ public ModListHelpDialog() : base()
                 th => th.PopupFg
             );
             AddObject(symbolTb);
-            symbolTb.AddLine("Status Symbols");
-            symbolTb.AddLine("==============");
-            symbolTb.AddLine($"{installed}           Installed");
-            symbolTb.AddLine($"{autoInstalled}      Auto-installed");
-            symbolTb.AddLine($"{upgradable}         Upgradeable");
-            symbolTb.AddLine($"{autodetected}  Manually installed");
-            symbolTb.AddLine($"{replaceable}         Replaceable");
-            symbolTb.AddLine($"!         Unavailable");
+            symbolTb.AddLine(LeftRightTable(
+                Properties.Resources.ModListHelpSymbolHeader,
+                new Tuple<string, string>[] {
+                    new Tuple<string, string>(installed,     Properties.Resources.ModListHelpInstalled),
+                    new Tuple<string, string>(autoInstalled, Properties.Resources.ModListHelpAutoInstalled),
+                    new Tuple<string, string>(upgradable,    Properties.Resources.ModListHelpUpgradeable),
+                    new Tuple<string, string>(autodetected,  Properties.Resources.ModListHelpManuallyInstalled),
+                    new Tuple<string, string>(replaceable,   Properties.Resources.ModListHelpReplaceable),
+                    new Tuple<string, string>("!",           Properties.Resources.ModListHelpUnavailable),
+                }
+            ));
             symbolTb.AddLine(" ");
-            symbolTb.AddLine("Basic Keys");
-            symbolTb.AddLine("==========");
-            symbolTb.AddLine("Tab            Move focus");
-            symbolTb.AddLine("Cursor keys    Select row");
-            symbolTb.AddLine("Escape       Clear search");
+            symbolTb.AddLine(LeftRightTable(
+                Properties.Resources.ModListHelpBasicKeysHeader,
+                new Tuple<string, string>[] {
+                    new Tuple<string, string>(Properties.Resources.Tab,        Properties.Resources.ModListHelpMoveFocus),
+                    new Tuple<string, string>(Properties.Resources.CursorKeys, Properties.Resources.ModListHelpSelectRow),
+                    new Tuple<string, string>(Properties.Resources.Esc,        Properties.Resources.ModListHelpClearSearch),
+                }
+            ));
 
             ConsoleTextBox searchTb = new ConsoleTextBox(
                 Console.WindowWidth / 2 + 1, GetTop() + 3, GetRight() - 2, GetBottom() - 4,
@@ -50,22 +58,43 @@ public ModListHelpDialog() : base()
                 th => th.PopupFg
             );
             AddObject(searchTb);
-            searchTb.AddLine("Special Searches");
-            searchTb.AddLine("================");
-            searchTb.AddLine("@author    Mods by author");
-            searchTb.AddLine("~i         Installed mods");
-            searchTb.AddLine("~u       Upgradeable mods");
-            searchTb.AddLine("~dname     Depend on name");
-            searchTb.AddLine("~cname   Conflict w/ name");
-            searchTb.AddLine("~n               New mods");
+            searchTb.AddLine(LeftRightTable(
+                Properties.Resources.ModListHelpSpecialSearchesHeader,
+                new Tuple<string, string>[] {
+                    new Tuple<string, string>($"@{Properties.Resources.ModListHelpAuthor}", Properties.Resources.ModListHelpSearchAuthor),
+                    new Tuple<string, string>("~i", Properties.Resources.ModListHelpSearchInstalled),
+                    new Tuple<string, string>("~u", Properties.Resources.ModListHelpSearchUpgradeable),
+                    new Tuple<string, string>($"~d{Properties.Resources.ModListHelpName}", Properties.Resources.ModListHelpSearchDepends),
+                    new Tuple<string, string>($"~c{Properties.Resources.ModListHelpName}", Properties.Resources.ModListHelpSearchConflicts),
+                    new Tuple<string, string>("~n", Properties.Resources.ModListHelpSearchNew),
+                }
+            ));
 
             AddObject(new ConsoleButton(
                 btnL, GetBottom() - 2, btnL + btnW - 1,
-                "OK",
+                Properties.Resources.OK,
                 Quit
             ));
         }
 
+        private string LeftRightTable(string header, Tuple<string, string>[] rows)
+        {
+            int leftW  = rows.Max(r => r.Item1.Length);
+            int rightW = rows.Max(r => r.Item2.Length);
+            int fullW  = Math.Max(leftW + rightW + tableSpacing, header.Length);
+            int midW   = fullW - leftW - rightW;
+            StringBuilder sb = new StringBuilder();
+            sb.AppendLine(header);
+            sb.AppendLine(new string('=', fullW));
+            string mid = new string(' ', midW);
+            foreach (var row in rows) {
+                sb.AppendLine(row.Item1.PadRight(leftW) + mid + row.Item2.PadLeft(rightW));
+            }
+            return sb.ToString();
+        }
+
+        private const int tableSpacing = 2;
+
         private static readonly string installed     = Symbols.checkmark;
         private static readonly string autoInstalled = Symbols.feminineOrdinal;
         private static readonly string upgradable    = Symbols.greaterEquals;
diff --git a/ConsoleUI/ModListScreen.cs b/ConsoleUI/ModListScreen.cs
index a2290a6889..45ae2735a3 100644
--- a/ConsoleUI/ModListScreen.cs
+++ b/ConsoleUI/ModListScreen.cs
@@ -36,17 +36,17 @@ public ModListScreen(GameInstanceManager mgr, bool dbg, ConsoleTheme regTheme)
                         Width    = 1,
                         Renderer = StatusSymbol
                     }, new ConsoleListBoxColumn<CkanModule>() {
-                        Header   = "Name",
+                        Header   = Properties.Resources.ModListNameHeader,
                         Width    = 44,
                         Renderer = m => m.name ?? ""
                     }, new ConsoleListBoxColumn<CkanModule>() {
-                        Header   = "Version",
+                        Header   = Properties.Resources.ModListVersionHeader,
                         Width    = 10,
                         Renderer = m => ModuleInstaller.StripEpoch(m.version?.ToString() ?? ""),
                         Comparer = (a, b) => a.version.CompareTo(b.version)
                     }, new ConsoleListBoxColumn<CkanModule>() {
-                        Header   = "Max game version",
-                        Width    = 17,
+                        Header   = Properties.Resources.ModListMaxGameVersionHeader,
+                        Width    = 20,
                         Renderer = m => registry.LatestCompatibleKSP(m.identifier)?.ToString() ?? "",
                         Comparer = (a, b) => registry.LatestCompatibleKSP(a.identifier).CompareTo(registry.LatestCompatibleKSP(b.identifier))
                     }
@@ -113,8 +113,8 @@ public ModListScreen(GameInstanceManager mgr, bool dbg, ConsoleTheme regTheme)
 
             searchBox = new ConsoleField(-searchWidth, 2, -1) {
                 GhostText = () => Focused() == searchBox
-                    ? "<Type to search>"
-                    : "<Ctrl+F to search>"
+                    ? Properties.Resources.ModListSearchFocusedGhostText
+                    : Properties.Resources.ModListSearchUnfocusedGhostText
             };
             searchBox.OnChange += (ConsoleField sender, string newValue) => {
                 moduleList.FilterString = newValue;
@@ -122,7 +122,7 @@ public ModListScreen(GameInstanceManager mgr, bool dbg, ConsoleTheme regTheme)
 
             AddObject(new ConsoleLabel(
                 1, 2, -searchWidth - 2,
-                () => $"{moduleList.VisibleRowCount()} mods"
+                () => string.Format(Properties.Resources.ModListCount, moduleList.VisibleRowCount())
             ));
             AddObject(searchBox);
             AddObject(moduleList);
@@ -158,7 +158,7 @@ public ModListScreen(GameInstanceManager mgr, bool dbg, ConsoleTheme regTheme)
                 return true;
             });
 
-            moduleList.AddTip("Enter", "Details",
+            moduleList.AddTip(Properties.Resources.Enter, Properties.Resources.Details,
                 () => moduleList.Selection != null
             );
             moduleList.AddBinding(Keys.Enter, (object sender, ConsoleTheme theme) => {
@@ -170,15 +170,15 @@ public ModListScreen(GameInstanceManager mgr, bool dbg, ConsoleTheme regTheme)
 
             // Conditionally show only one of these based on selected mod status
 
-            moduleList.AddTip("+", "Install",
+            moduleList.AddTip("+", Properties.Resources.ModListInstallTip,
                 () => moduleList.Selection != null && !moduleList.Selection.IsDLC
                     && !registry.IsInstalled(moduleList.Selection.identifier, false)
             );
-            moduleList.AddTip("+", "Upgrade",
+            moduleList.AddTip("+", Properties.Resources.ModListUpgradeTip,
                 () => moduleList.Selection != null && !moduleList.Selection.IsDLC
                     && registry.HasUpdate(moduleList.Selection.identifier, manager.CurrentInstance.VersionCriteria())
             );
-            moduleList.AddTip("+", "Replace",
+            moduleList.AddTip("+", Properties.Resources.ModListReplaceTip,
                 () => moduleList.Selection != null
                     && registry.GetReplacement(moduleList.Selection.identifier, manager.CurrentInstance.VersionCriteria()) != null
             );
@@ -196,7 +196,7 @@ public ModListScreen(GameInstanceManager mgr, bool dbg, ConsoleTheme regTheme)
                 return true;
             });
 
-            moduleList.AddTip("-", "Remove",
+            moduleList.AddTip("-", Properties.Resources.ModListRemoveTip,
                 () => moduleList.Selection != null && !moduleList.Selection.IsDLC
                     && registry.IsInstalled(moduleList.Selection.identifier, false)
                     && !registry.IsAutodetected(moduleList.Selection.identifier)
@@ -210,11 +210,11 @@ public ModListScreen(GameInstanceManager mgr, bool dbg, ConsoleTheme regTheme)
                 return true;
             });
 
-            moduleList.AddTip("F8", "Mark auto-installed",
+            moduleList.AddTip("F8", Properties.Resources.ModListAutoInstTip,
                 () => moduleList.Selection != null && !moduleList.Selection.IsDLC
                     && (!registry.InstalledModule(moduleList.Selection.identifier)?.AutoInstalled ?? false)
             );
-            moduleList.AddTip("F8", "Mark user-selected",
+            moduleList.AddTip("F8", Properties.Resources.ModListUserSelectedTip,
                 () => moduleList.Selection != null && !moduleList.Selection.IsDLC
                     && (registry.InstalledModule(moduleList.Selection.identifier)?.AutoInstalled ?? false)
             );
@@ -227,7 +227,7 @@ public ModListScreen(GameInstanceManager mgr, bool dbg, ConsoleTheme regTheme)
                 return true;
             });
 
-            AddTip("F9", "Apply changes", plan.NonEmpty);
+            AddTip("F9", Properties.Resources.ModListApplyChangesTip, plan.NonEmpty);
             AddBinding(Keys.F9, (object sender, ConsoleTheme theme) => {
                 ApplyChanges(theme);
                 return true;
@@ -236,7 +236,7 @@ public ModListScreen(GameInstanceManager mgr, bool dbg, ConsoleTheme regTheme)
             // Show total download size of all installed mods
             AddObject(new ConsoleLabel(
                 1, -1, searchWidth,
-                () => $"{CkanModule.FmtSize(totalInstalledDownloadSize())} installed",
+                () => string.Format(Properties.Resources.ModListSizeOnDisk, CkanModule.FmtSize(totalInstalledDownloadSize())),
                 null,
                 th => th.DimLabelFg
             ));
@@ -246,68 +246,64 @@ public ModListScreen(GameInstanceManager mgr, bool dbg, ConsoleTheme regTheme)
                 () => {
                     int days = daysSinceUpdated(registryFilePath());
                     return days <  1 ? ""
-                        :  days == 1 ? $"Updated at least {days} day ago"
-                        :              $"Updated at least {days} days ago";
+                        :  days == 1 ? string.Format(Properties.Resources.ModListUpdatedDayAgo,  days)
+                        :              string.Format(Properties.Resources.ModListUpdatedDaysAgo, days);
                 },
                 null,
                 (ConsoleTheme th) => {
                     int daysSince = daysSinceUpdated(registryFilePath());
-                    if (daysSince < daysTillStale) {
-                        return th.RegistryUpToDate;
-                    } else if (daysSince < daystillVeryStale) {
-                        return th.RegistryStale;
-                    } else {
-                        return th.RegistryVeryStale;
-                    }
+                    return daysSince < daysTillStale     ? th.RegistryUpToDate
+                        :  daysSince < daystillVeryStale ? th.RegistryStale
+                        :                                  th.RegistryVeryStale;
                 }
             ));
 
             List<ConsoleMenuOption> opts = new List<ConsoleMenuOption>() {
-                new ConsoleMenuOption("Sort...",                    "",
-                    "Change the sorting of the list of mods",
+                new ConsoleMenuOption(Properties.Resources.ModListSortMenu, "",
+                    Properties.Resources.ModListSortMenuTip,
                     true, null, null, moduleList.SortMenu()),
                 null,
-                new ConsoleMenuOption("Refresh mod list", "F5, Ctrl+R",
-                    "Refresh the list of mods",
+                new ConsoleMenuOption(Properties.Resources.ModListRefreshMenu, $"F5, {Properties.Resources.Ctrl}+R",
+                    Properties.Resources.ModListRefreshMenuTip,
                     true, (ConsoleTheme th) => UpdateRegistry(th)),
-                new ConsoleMenuOption("Upgrade all",          "Ctrl+U",
-                    "Mark all available updates for installation",
+                new ConsoleMenuOption(Properties.Resources.ModListUpgradeMenu, $"{Properties.Resources.Ctrl}+U",
+                    Properties.Resources.ModListUpgradeMenuTip,
                     true, UpgradeAll, null, null, HasAnyUpgradeable()),
-                new ConsoleMenuOption("Audit recommendations",      "",
-                    "List mods suggested and recommended by installed mods",
+                new ConsoleMenuOption(Properties.Resources.ModListAuditRecsMenu, "",
+                    Properties.Resources.ModListAuditRecsMenuTip,
                     true, ViewSuggestions),
-                new ConsoleMenuOption("Import downloads...",        "",
-                    "Select manually downloaded mods to import into CKAN",
+                new ConsoleMenuOption(Properties.Resources.ModListImportMenu, "",
+                    Properties.Resources.ModListImportMenuTip,
                     true, ImportDownloads),
-                new ConsoleMenuOption("Export installed...",        "",
-                    "Save your mod list",
+                new ConsoleMenuOption(Properties.Resources.ModListExportMenu, "",
+                    Properties.Resources.ModListExportMenuTip,
                     true, ExportInstalled),
                 null,
-                new ConsoleMenuOption("Game instance settings...",    "",
-                    "Configure the current game instance",
+                new ConsoleMenuOption(Properties.Resources.ModListInstanceSettingsMenu, "",
+                    Properties.Resources.ModListInstanceSettingsMenuTip,
                     true, InstanceSettings),
-                new ConsoleMenuOption("Select game instance...",      "",
-                    "Switch to a different game instance",
+                new ConsoleMenuOption(Properties.Resources.ModListSelectInstanceMenu, "",
+                    Properties.Resources.ModListSelectInstanceMenuTip,
                     true, SelectInstall),
-                new ConsoleMenuOption("Authentication tokens...",     "",
-                    "Edit authentication tokens sent to download servers",
+                new ConsoleMenuOption(Properties.Resources.ModListAuthTokenMenu, "",
+                    Properties.Resources.ModListAuthTokenMenuTip,
                     true, EditAuthTokens),
-                new ConsoleMenuOption("Installation filters...",     "",
-                    "Edit list of files and folders to exclude from installation",
+                new ConsoleMenuOption(Properties.Resources.ModListFilterMenu, "",
+                    Properties.Resources.ModListFilterMenuTip,
                     true, EditInstallFilters),
                 null,
-                new ConsoleMenuOption("Help",                  helpKey,
-                    "Tips & tricks",
+                new ConsoleMenuOption(Properties.Resources.ModListHelpMenu, helpKey,
+                    Properties.Resources.ModListHelpMenuTip,
                     true, Help),
                 null,
-                new ConsoleMenuOption("Quit",                 "Ctrl+Q",
-                    "Exit to DOS",
+                new ConsoleMenuOption(Properties.Resources.ModListQuitMenu, $"{Properties.Resources.Ctrl}+Q",
+                    Properties.Resources.ModListQuitMenuTip,
                     true, (ConsoleTheme th) => false)
             };
             if (debug) {
                 opts.Add(null);
-                opts.Add(new ConsoleMenuOption("DEBUG: Capture key...", "",
-                    "Print details of how your system reports a keystroke for debugging",
+                opts.Add(new ConsoleMenuOption(Properties.Resources.ModListCaptureKeyMenu, "",
+                    Properties.Resources.ModListCaptureKeyMenuTip,
                     true, CaptureKey));
             }
             mainMenu = new ConsolePopupMenu(opts);
@@ -333,7 +329,7 @@ protected override string CenterHeader()
         // an option other than F1 for terminals that open their own help.
         private static readonly string helpKey = Platform.IsMac
             ? "F1"
-            : "F1, Alt+H";
+            : $"F1, {Properties.Resources.Alt}+H";
 
         private bool ImportDownloads(ConsoleTheme theme)
         {
@@ -345,13 +341,13 @@ private bool ImportDownloads(ConsoleTheme theme)
         private bool CaptureKey(ConsoleTheme theme)
         {
             ConsoleKeyInfo k = default(ConsoleKeyInfo);
-            ConsoleMessageDialog keyprompt = new ConsoleMessageDialog("Press a key", new List<string>());
+            ConsoleMessageDialog keyprompt = new ConsoleMessageDialog(Properties.Resources.ModListPressAKey, new List<string>());
             keyprompt.Run(theme, (ConsoleTheme th) => {
                 k = Console.ReadKey(true);
             });
             ConsoleMessageDialog output = new ConsoleMessageDialog(
                 $"Key: {k.Key,18}\nKeyChar:           0x{(int)k.KeyChar:x2}\nModifiers: {k.Modifiers,12}",
-                new List<string> {"OK"}
+                new List<string> { Properties.Resources.OK }
             );
             output.Run(theme);
             return true;
@@ -406,7 +402,7 @@ private bool ViewSuggestions(ConsoleTheme theme)
                         RefreshList(theme);
                     }
                 } else {
-                    RaiseError("Installed mods have no unsatisfied recommendations or suggestions.");
+                    RaiseError(Properties.Resources.ModListAuditNotFound);
                 }
             } catch (ModuleNotFoundKraken k) {
                 RaiseError($"{k.module} {k.version}: {k.Message}");
@@ -426,7 +422,10 @@ private int daysSinceUpdated(string filename)
 
         private bool UpdateRegistry(ConsoleTheme theme, bool showNewModsPrompt = true)
         {
-            ProgressScreen ps = new ProgressScreen("Updating Registry", "Checking for updates");
+            ProgressScreen ps = new ProgressScreen(
+                Properties.Resources.ModListUpdateRegistryTitle,
+                Properties.Resources.ModListUpdateRegistryMessage
+            );
             LaunchSubScreen(theme, ps, (ConsoleTheme th) => {
                 HashSet<string> availBefore = new HashSet<string>(
                     Array.ConvertAll<CkanModule, string>(
@@ -474,8 +473,8 @@ private bool UpdateRegistry(ConsoleTheme theme, bool showNewModsPrompt = true)
         private string newModPrompt(int howMany)
         {
             return howMany == 1
-                ? $"{howMany} new mod available since last update. Show it?"
-                : $"{howMany} new mods available since last update. Show them?";
+                ? string.Format(Properties.Resources.ModListNewMod,  howMany)
+                : string.Format(Properties.Resources.ModListNewMods, howMany);
         }
 
         private bool ScanForMods()
@@ -484,7 +483,7 @@ private bool ScanForMods()
                 manager.CurrentInstance.Scan();
             } catch (InconsistentKraken ex) {
                 // Warn about inconsistent state
-                RaiseError(ex.InconsistenciesPretty + " The repo has not been saved.");
+                RaiseError(Properties.Resources.ModListScanBad, ex.InconsistenciesPretty);
             }
             return true;
         }
@@ -591,11 +590,11 @@ private bool ExportInstalled(ConsoleTheme theme)
                 RegistryManager.Instance(manager.CurrentInstance).Save(true);
                 string path = Path.Combine(
                     manager.CurrentInstance.CkanDir(),
-                    $"installed-{manager.CurrentInstance.Name}.ckan"
+                    $"{Properties.Resources.ModListExportPrefix}-{manager.CurrentInstance.Name}.ckan"
                 );
-                RaiseError($"Mod list exported to {path}");
+                RaiseError(Properties.Resources.ModListExported, path);
             } catch (Exception ex) {
-                RaiseError($"Export failed: {ex.Message}");
+                RaiseError(Properties.Resources.ModListExportFailed, ex.Message);
             }
             return true;
         }
@@ -675,7 +674,10 @@ private long totalInstalledDownloadSize()
         private ChangePlan      plan   = new ChangePlan();
         private HashSet<string> recent = new HashSet<string>();
 
-        private const int searchWidth       = 30;
+        private int searchWidth => Math.Max(30, Math.Max(
+            Properties.Resources.ModListSearchFocusedGhostText.Length,
+            Properties.Resources.ModListSearchUnfocusedGhostText.Length
+        ));
         private const int daysTillStale     = 7;
         private const int daystillVeryStale = 30;
 
diff --git a/ConsoleUI/ProgressScreen.cs b/ConsoleUI/ProgressScreen.cs
index 593975e716..e36e609eb2 100644
--- a/ConsoleUI/ProgressScreen.cs
+++ b/ConsoleUI/ProgressScreen.cs
@@ -21,8 +21,8 @@ public ProgressScreen(string descrip, string initMsg = "")
             // A nice frame to take up some of the blank space at the top
             AddObject(new ConsoleDoubleFrame(
                 1, 2, -1, -1, 8,
-                () => "Progress",
-                () => "Messages",
+                () => Properties.Resources.ProgressTitle,
+                () => Properties.Resources.ProgressMessages,
                 // Cheating because our IUser handler needs a theme context
                 th => { yesNoTheme = th; return th.NormalFrameFg; }
             ));
@@ -74,7 +74,10 @@ public override bool RaiseYesNoDialog(string question)
             ConsoleMessageDialog d = new ConsoleMessageDialog(
                 // The installer's questions include embedded newlines for spacing in CmdLine
                 question.Trim(),
-                new List<string>() {"Yes", "No"},
+                new List<string>() {
+                    Properties.Resources.Yes,
+                    Properties.Resources.No
+                },
                 null,
                 TextAlign.Center,
                 -Console.WindowHeight / 2
@@ -89,7 +92,7 @@ public override bool RaiseYesNoDialog(string question)
             });
 
             // Scroll messages
-            d.AddTip("Cursor keys", "Scroll messages");
+            d.AddTip(Properties.Resources.CursorKeys, Properties.Resources.ScrollMessages);
             messages.AddScrollBindings(d, true);
 
             bool val = d.Run(yesNoTheme) == 0;
diff --git a/ConsoleUI/Properties/AssemblyInfo.cs b/ConsoleUI/Properties/AssemblyInfo.cs
index 241d366188..7c6c544480 100644
--- a/ConsoleUI/Properties/AssemblyInfo.cs
+++ b/ConsoleUI/Properties/AssemblyInfo.cs
@@ -1,4 +1,10 @@
+using System.Resources;
 using System.Reflection;
+using System.Runtime.CompilerServices;
 
 [assembly: AssemblyTitle("CKAN-ConsoleUI")]
 [assembly: AssemblyDescription("CKAN ConsoleUI Client")]
+[assembly: NeutralResourcesLanguage("en-GB")]
+
+[assembly: InternalsVisibleTo("Tests")]
+[assembly: InternalsVisibleTo("CKAN.Tests")]
diff --git a/ConsoleUI/Properties/Resources.Designer.cs b/ConsoleUI/Properties/Resources.Designer.cs
new file mode 100644
index 0000000000..bd5668ad29
--- /dev/null
+++ b/ConsoleUI/Properties/Resources.Designer.cs
@@ -0,0 +1,844 @@
+//------------------------------------------------------------------------------
+// <auto-generated>
+//     This code was generated by a tool.
+//     Runtime Version:4.0.30319.42000
+//
+//     Changes to this file may cause incorrect behavior and will be lost if
+//     the code is regenerated. (I WISH!)
+// </auto-generated>
+//------------------------------------------------------------------------------
+
+namespace CKAN.ConsoleUI.Properties {
+    using System;
+
+
+    /// <summary>
+    ///   A strongly-typed resource class, for looking up localized strings, etc.
+    /// </summary>
+    // This class was auto-generated by the StronglyTypedResourceBuilder
+    // class via a tool like ResGen or Visual Studio.
+    // To add or remove a member, edit your .ResX file then rerun ResGen
+    // with the /str option, or rebuild your VS project.
+    [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "4.0.0.0")]
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
+    [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
+    internal class Resources {
+
+        private static global::System.Resources.ResourceManager resourceMan;
+
+        private static global::System.Globalization.CultureInfo resourceCulture;
+
+        [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
+        internal Resources() { }
+
+        /// <summary>
+        ///   Returns the cached ResourceManager instance used by this class.
+        /// </summary>
+        [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
+        internal static global::System.Resources.ResourceManager ResourceManager {
+            get {
+                if (object.ReferenceEquals(resourceMan, null))
+                {
+                    resourceMan = new SingleAssemblyResourceManager("CKAN.ConsoleUI.Properties.Resources", typeof(Resources).Assembly);
+                }
+                return resourceMan;
+            }
+        }
+
+        /// <summary>
+        ///   Overrides the current thread's CurrentUICulture property for all
+        ///   resource lookups using this strongly typed resource class.
+        /// </summary>
+        [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
+        internal static global::System.Globalization.CultureInfo Culture {
+            get {
+                return resourceCulture;
+            }
+            set {
+                resourceCulture = value;
+            }
+        }
+
+        internal static string Accept {
+            get { return (string)(ResourceManager.GetObject("Accept", resourceCulture)); }
+        }
+        internal static string Cancel {
+            get { return (string)(ResourceManager.GetObject("Cancel", resourceCulture)); }
+        }
+        internal static string DefaultGhostText {
+            get { return (string)(ResourceManager.GetObject("DefaultGhostText", resourceCulture)); }
+        }
+        internal static string Ascending {
+            get { return (string)(ResourceManager.GetObject("Ascending", resourceCulture)); }
+        }
+        internal static string AscendingSortTip {
+            get { return (string)(ResourceManager.GetObject("AscendingSortTip", resourceCulture)); }
+        }
+        internal static string Descending {
+            get { return (string)(ResourceManager.GetObject("Descending", resourceCulture)); }
+        }
+        internal static string DescendingSortTip {
+            get { return (string)(ResourceManager.GetObject("DescendingSortTip", resourceCulture)); }
+        }
+        internal static string ColumnNumber {
+            get { return (string)(ResourceManager.GetObject("ColumnNumber", resourceCulture)); }
+        }
+        internal static string ColumnNumberSortTip {
+            get { return (string)(ResourceManager.GetObject("ColumnNumberSortTip", resourceCulture)); }
+        }
+        internal static string ColumnNameSortTip {
+            get { return (string)(ResourceManager.GetObject("ColumnNameSortTip", resourceCulture)); }
+        }
+        internal static string CursorKeys {
+            get { return (string)(ResourceManager.GetObject("CursorKeys", resourceCulture)); }
+        }
+        internal static string Scroll {
+            get { return (string)(ResourceManager.GetObject("Scroll", resourceCulture)); }
+        }
+        internal static string ScrollMessages {
+            get { return (string)(ResourceManager.GetObject("ScrollMessages", resourceCulture)); }
+        }
+        internal static string Menu {
+            get { return (string)(ResourceManager.GetObject("Menu", resourceCulture)); }
+        }
+        internal static string Yes {
+            get { return (string)(ResourceManager.GetObject("Yes", resourceCulture)); }
+        }
+        internal static string No {
+            get { return (string)(ResourceManager.GetObject("No", resourceCulture)); }
+        }
+        internal static string OK {
+            get { return (string)(ResourceManager.GetObject("OK", resourceCulture)); }
+        }
+        internal static string Force {
+            get { return (string)(ResourceManager.GetObject("Force", resourceCulture)); }
+        }
+        internal static string Tab {
+            get { return (string)(ResourceManager.GetObject("Tab", resourceCulture)); }
+        }
+        internal static string Enter {
+            get { return (string)(ResourceManager.GetObject("Enter", resourceCulture)); }
+        }
+        internal static string Esc {
+            get { return (string)(ResourceManager.GetObject("Esc", resourceCulture)); }
+        }
+        internal static string Ctrl {
+            get { return (string)(ResourceManager.GetObject("Ctrl", resourceCulture)); }
+        }
+        internal static string Alt {
+            get { return (string)(ResourceManager.GetObject("Alt", resourceCulture)); }
+        }
+        internal static string Back {
+            get { return (string)(ResourceManager.GetObject("Back", resourceCulture)); }
+        }
+        internal static string Quit {
+            get { return (string)(ResourceManager.GetObject("Quit", resourceCulture)); }
+        }
+        internal static string Add {
+            get { return (string)(ResourceManager.GetObject("Add", resourceCulture)); }
+        }
+        internal static string Remove {
+            get { return (string)(ResourceManager.GetObject("Remove", resourceCulture)); }
+        }
+        internal static string Edit {
+            get { return (string)(ResourceManager.GetObject("Edit", resourceCulture)); }
+        }
+        internal static string Sort {
+            get { return (string)(ResourceManager.GetObject("Sort", resourceCulture)); }
+        }
+        internal static string Toggle {
+            get { return (string)(ResourceManager.GetObject("Toggle", resourceCulture)); }
+        }
+        internal static string Select {
+            get { return (string)(ResourceManager.GetObject("Select", resourceCulture)); }
+        }
+        internal static string SelectAll {
+            get { return (string)(ResourceManager.GetObject("SelectAll", resourceCulture)); }
+        }
+        internal static string DeselectAll {
+            get { return (string)(ResourceManager.GetObject("DeselectAll", resourceCulture)); }
+        }
+        internal static string Details {
+            get { return (string)(ResourceManager.GetObject("Details", resourceCulture)); }
+        }
+        internal static string Up {
+            get { return (string)(ResourceManager.GetObject("Up", resourceCulture)); }
+        }
+        internal static string Down {
+            get { return (string)(ResourceManager.GetObject("Down", resourceCulture)); }
+        }
+        internal static string FileSelectDirectory {
+            get { return (string)(ResourceManager.GetObject("FileSelectDirectory", resourceCulture)); }
+        }
+        internal static string FileSelectCountFooter {
+            get { return (string)(ResourceManager.GetObject("FileSelectCountFooter", resourceCulture)); }
+        }
+        internal static string FileSelectNameHeader {
+            get { return (string)(ResourceManager.GetObject("FileSelectNameHeader", resourceCulture)); }
+        }
+        internal static string FileSelectSizeHeader {
+            get { return (string)(ResourceManager.GetObject("FileSelectSizeHeader", resourceCulture)); }
+        }
+        internal static string FileSelectTimestampHeader {
+            get { return (string)(ResourceManager.GetObject("FileSelectTimestampHeader", resourceCulture)); }
+        }
+        internal static string FileSelectChangeDirectory {
+            get { return (string)(ResourceManager.GetObject("FileSelectChangeDirectory", resourceCulture)); }
+        }
+        internal static string FileSelectSelect {
+            get { return (string)(ResourceManager.GetObject("FileSelectSelect", resourceCulture)); }
+        }
+        internal static string FileSelectImport {
+            get { return (string)(ResourceManager.GetObject("FileSelectImport", resourceCulture)); }
+        }
+        internal static string FileSelectDirSize {
+            get { return (string)(ResourceManager.GetObject("FileSelectDirSize", resourceCulture)); }
+        }
+        internal static string AuthTokenListTitle {
+            get { return (string)(ResourceManager.GetObject("AuthTokenListTitle", resourceCulture)); }
+        }
+        internal static string AuthTokenListGitHubLink {
+            get { return (string)(ResourceManager.GetObject("AuthTokenListGitHubLink", resourceCulture)); }
+        }
+        internal static string AuthTokenListGitHubLinkTip {
+            get { return (string)(ResourceManager.GetObject("AuthTokenListGitHubLinkTip", resourceCulture)); }
+        }
+        internal static string AuthTokenListLabel {
+            get { return (string)(ResourceManager.GetObject("AuthTokenListLabel", resourceCulture)); }
+        }
+        internal static string AuthTokenListHostHeader {
+            get { return (string)(ResourceManager.GetObject("AuthTokenListHostHeader", resourceCulture)); }
+        }
+        internal static string AuthTokenListTokenHeader {
+            get { return (string)(ResourceManager.GetObject("AuthTokenListTokenHeader", resourceCulture)); }
+        }
+        internal static string AuthTokenListWarning {
+            get { return (string)(ResourceManager.GetObject("AuthTokenListWarning", resourceCulture)); }
+        }
+        internal static string AuthTokenListMissingToken {
+            get { return (string)(ResourceManager.GetObject("AuthTokenListMissingToken", resourceCulture)); }
+        }
+        internal static string AuthTokenAddTitle {
+            get { return (string)(ResourceManager.GetObject("AuthTokenAddTitle", resourceCulture)); }
+        }
+        internal static string AuthTokenAddHost {
+            get { return (string)(ResourceManager.GetObject("AuthTokenAddHost", resourceCulture)); }
+        }
+        internal static string AuthTokenAddHostGhostText {
+            get { return (string)(ResourceManager.GetObject("AuthTokenAddHostGhostText", resourceCulture)); }
+        }
+        internal static string AuthTokenAddToken {
+            get { return (string)(ResourceManager.GetObject("AuthTokenAddToken", resourceCulture)); }
+        }
+        internal static string AuthTokenAddTokenGhostText {
+            get { return (string)(ResourceManager.GetObject("AuthTokenAddTokenGhostText", resourceCulture)); }
+        }
+        internal static string CompatibleVersionsTitle {
+            get { return (string)(ResourceManager.GetObject("CompatibleVersionsTitle", resourceCulture)); }
+        }
+        internal static string CompatibleVersionsListHeader {
+            get { return (string)(ResourceManager.GetObject("CompatibleVersionsListHeader", resourceCulture)); }
+        }
+        internal static string CompatibleVersionsListAcceptTip {
+            get { return (string)(ResourceManager.GetObject("CompatibleVersionsListAcceptTip", resourceCulture)); }
+        }
+        internal static string CompatibleVersionsGhostText {
+            get { return (string)(ResourceManager.GetObject("CompatibleVersionsGhostText", resourceCulture)); }
+        }
+        internal static string CompatibleVersionsEntryAcceptTip {
+            get { return (string)(ResourceManager.GetObject("CompatibleVersionsEntryAcceptTip", resourceCulture)); }
+        }
+        internal static string ThemeNotFound {
+            get { return (string)(ResourceManager.GetObject("ThemeNotFound", resourceCulture)); }
+        }
+        internal static string ThemeList {
+            get { return (string)(ResourceManager.GetObject("ThemeList", resourceCulture)); }
+        }
+        internal static string RecommendationsLabel {
+            get { return (string)(ResourceManager.GetObject("RecommendationsLabel", resourceCulture)); }
+        }
+        internal static string RecommendationsInstallHeader {
+            get { return (string)(ResourceManager.GetObject("RecommendationsInstallHeader", resourceCulture)); }
+        }
+        internal static string RecommendationsNameHeader {
+            get { return (string)(ResourceManager.GetObject("RecommendationsNameHeader", resourceCulture)); }
+        }
+        internal static string RecommendationsSourcesHeader {
+            get { return (string)(ResourceManager.GetObject("RecommendationsSourcesHeader", resourceCulture)); }
+        }
+        internal static string RecommendationsTitle {
+            get { return (string)(ResourceManager.GetObject("RecommendationsTitle", resourceCulture)); }
+        }
+        internal static string ImportSelectTitle {
+            get { return (string)(ResourceManager.GetObject("ImportSelectTitle", resourceCulture)); }
+        }
+        internal static string ImportSelectHeader {
+            get { return (string)(ResourceManager.GetObject("ImportSelectHeader", resourceCulture)); }
+        }
+        internal static string ImportProgressTitle {
+            get { return (string)(ResourceManager.GetObject("ImportProgressTitle", resourceCulture)); }
+        }
+        internal static string ImportProgressMessage {
+            get { return (string)(ResourceManager.GetObject("ImportProgressMessage", resourceCulture)); }
+        }
+        internal static string InstanceNameLabel {
+            get { return (string)(ResourceManager.GetObject("InstanceNameLabel", resourceCulture)); }
+        }
+        internal static string InstanceNameGhostText {
+            get { return (string)(ResourceManager.GetObject("InstanceNameGhostText", resourceCulture)); }
+        }
+        internal static string InstancePathGhostText {
+            get { return (string)(ResourceManager.GetObject("InstancePathGhostText", resourceCulture)); }
+        }
+        internal static string InstancePathLabel {
+            get { return (string)(ResourceManager.GetObject("InstancePathLabel", resourceCulture)); }
+        }
+        internal static string InstanceNameEmptyError {
+            get { return (string)(ResourceManager.GetObject("InstanceNameEmptyError", resourceCulture)); }
+        }
+        internal static string InstanceNameDuplicateError {
+            get { return (string)(ResourceManager.GetObject("InstanceNameDuplicateError", resourceCulture)); }
+        }
+        internal static string InstancePathNotGameFolderError {
+            get { return (string)(ResourceManager.GetObject("InstancePathNotGameFolderError", resourceCulture)); }
+        }
+        internal static string InstanceAddExample {
+            get { return (string)(ResourceManager.GetObject("InstanceAddExample", resourceCulture)); }
+        }
+        internal static string InstanceAddTitle {
+            get { return (string)(ResourceManager.GetObject("InstanceAddTitle", resourceCulture)); }
+        }
+        internal static string InstanceEditTitle {
+            get { return (string)(ResourceManager.GetObject("InstanceEditTitle", resourceCulture)); }
+        }
+        internal static string InstanceEditRepoFrameTitle {
+            get { return (string)(ResourceManager.GetObject("InstanceEditRepoFrameTitle", resourceCulture)); }
+        }
+        internal static string InstanceEditCompatFrameTitle {
+            get { return (string)(ResourceManager.GetObject("InstanceEditCompatFrameTitle", resourceCulture)); }
+        }
+        internal static string InstanceEditRepoIndexHeader {
+            get { return (string)(ResourceManager.GetObject("InstanceEditRepoIndexHeader", resourceCulture)); }
+        }
+        internal static string InstanceEditRepoNameHeader {
+            get { return (string)(ResourceManager.GetObject("InstanceEditRepoNameHeader", resourceCulture)); }
+        }
+        internal static string InstanceEditRepoURLHeader {
+            get { return (string)(ResourceManager.GetObject("InstanceEditRepoURLHeader", resourceCulture)); }
+        }
+        internal static string InstanceEditCompatVersionHeader {
+            get { return (string)(ResourceManager.GetObject("InstanceEditCompatVersionHeader", resourceCulture)); }
+        }
+        internal static string InstanceEditRegistryParseError {
+            get { return (string)(ResourceManager.GetObject("InstanceEditRegistryParseError", resourceCulture)); }
+        }
+        internal static string InstanceListTitle {
+            get { return (string)(ResourceManager.GetObject("InstanceListTitle", resourceCulture)); }
+        }
+        internal static string InstanceListLabel {
+            get { return (string)(ResourceManager.GetObject("InstanceListLabel", resourceCulture)); }
+        }
+        internal static string InstanceListDefaultHeader {
+            get { return (string)(ResourceManager.GetObject("InstanceListDefaultHeader", resourceCulture)); }
+        }
+        internal static string InstanceListNameHeader {
+            get { return (string)(ResourceManager.GetObject("InstanceListNameHeader", resourceCulture)); }
+        }
+        internal static string InstanceListGameHeader {
+            get { return (string)(ResourceManager.GetObject("InstanceListGameHeader", resourceCulture)); }
+        }
+        internal static string InstanceListVersionHeader {
+            get { return (string)(ResourceManager.GetObject("InstanceListVersionHeader", resourceCulture)); }
+        }
+        internal static string InstanceListPathHeader {
+            get { return (string)(ResourceManager.GetObject("InstanceListPathHeader", resourceCulture)); }
+        }
+        internal static string InstanceListLoadingInstance {
+            get { return (string)(ResourceManager.GetObject("InstanceListLoadingInstance", resourceCulture)); }
+        }
+        internal static string InstanceListDefaultToggle {
+            get { return (string)(ResourceManager.GetObject("InstanceListDefaultToggle", resourceCulture)); }
+        }
+        internal static string InstanceListLoadingError {
+            get { return (string)(ResourceManager.GetObject("InstanceListLoadingError", resourceCulture)); }
+        }
+        internal static string InstanceListLocked {
+            get { return (string)(ResourceManager.GetObject("InstanceListLocked", resourceCulture)); }
+        }
+        internal static string InstanceListNoVersion {
+            get { return (string)(ResourceManager.GetObject("InstanceListNoVersion", resourceCulture)); }
+        }
+        internal static string InstallTitle {
+            get { return (string)(ResourceManager.GetObject("InstallTitle", resourceCulture)); }
+        }
+        internal static string InstallMessage {
+            get { return (string)(ResourceManager.GetObject("InstallMessage", resourceCulture)); }
+        }
+        internal static string InstallOwnedFileConflict {
+            get { return (string)(ResourceManager.GetObject("InstallOwnedFileConflict", resourceCulture)); }
+        }
+        internal static string InstallUnownedFileConflict {
+            get { return (string)(ResourceManager.GetObject("InstallUnownedFileConflict", resourceCulture)); }
+        }
+        internal static string InstallFilesReverted {
+            get { return (string)(ResourceManager.GetObject("InstallFilesReverted", resourceCulture)); }
+        }
+        internal static string InstallAuthTokenPrompt {
+            get { return (string)(ResourceManager.GetObject("InstallAuthTokenPrompt", resourceCulture)); }
+        }
+        internal static string InstallTooManyModsNameHeader {
+            get { return (string)(ResourceManager.GetObject("InstallTooManyModsNameHeader", resourceCulture)); }
+        }
+        internal static string InstallBadMetadata {
+            get { return (string)(ResourceManager.GetObject("InstallBadMetadata", resourceCulture)); }
+        }
+        internal static string InstallUnsatisfiedDependency {
+            get { return (string)(ResourceManager.GetObject("InstallUnsatisfiedDependency", resourceCulture)); }
+        }
+        internal static string InstallModuleNotFound {
+            get { return (string)(ResourceManager.GetObject("InstallModuleNotFound", resourceCulture)); }
+        }
+        internal static string InstallNotInstalled {
+            get { return (string)(ResourceManager.GetObject("InstallNotInstalled", resourceCulture)); }
+        }
+        internal static string InstallModInstalled {
+            get { return (string)(ResourceManager.GetObject("InstallModInstalled", resourceCulture)); }
+        }
+        internal static string ModInfoTitle {
+            get { return (string)(ResourceManager.GetObject("ModInfoTitle", resourceCulture)); }
+        }
+        internal static string ModInfoMenuTip {
+            get { return (string)(ResourceManager.GetObject("ModInfoMenuTip", resourceCulture)); }
+        }
+        internal static string ModInfoAuthors {
+            get { return (string)(ResourceManager.GetObject("ModInfoAuthors", resourceCulture)); }
+        }
+        internal static string ModInfoLicence {
+            get { return (string)(ResourceManager.GetObject("ModInfoLicence", resourceCulture)); }
+        }
+        internal static string ModInfoDownload {
+            get { return (string)(ResourceManager.GetObject("ModInfoDownload", resourceCulture)); }
+        }
+        internal static string ModInfoDescriptionFrame {
+            get { return (string)(ResourceManager.GetObject("ModInfoDescriptionFrame", resourceCulture)); }
+        }
+        internal static string ModInfoUnavailableWarning {
+            get { return (string)(ResourceManager.GetObject("ModInfoUnavailableWarning", resourceCulture)); }
+        }
+        internal static string ModInfoDownloadToCache {
+            get { return (string)(ResourceManager.GetObject("ModInfoDownloadToCache", resourceCulture)); }
+        }
+        internal static string ModInfoHomePage {
+            get { return (string)(ResourceManager.GetObject("ModInfoHomePage", resourceCulture)); }
+        }
+        internal static string ModInfoHomePageTip {
+            get { return (string)(ResourceManager.GetObject("ModInfoHomePageTip", resourceCulture)); }
+        }
+        internal static string ModInfoRepository {
+            get { return (string)(ResourceManager.GetObject("ModInfoRepository", resourceCulture)); }
+        }
+        internal static string ModInfoRepositoryTip {
+            get { return (string)(ResourceManager.GetObject("ModInfoRepositoryTip", resourceCulture)); }
+        }
+        internal static string ModInfoBugtracker {
+            get { return (string)(ResourceManager.GetObject("ModInfoBugtracker", resourceCulture)); }
+        }
+        internal static string ModInfoBugtrackerTip {
+            get { return (string)(ResourceManager.GetObject("ModInfoBugtrackerTip", resourceCulture)); }
+        }
+        internal static string ModInfoSpaceDock {
+            get { return (string)(ResourceManager.GetObject("ModInfoSpaceDock", resourceCulture)); }
+        }
+        internal static string ModInfoSpaceDockTip {
+            get { return (string)(ResourceManager.GetObject("ModInfoSpaceDockTip", resourceCulture)); }
+        }
+        internal static string ModInfoCurse {
+            get { return (string)(ResourceManager.GetObject("ModInfoCurse", resourceCulture)); }
+        }
+        internal static string ModInfoCurseTip {
+            get { return (string)(ResourceManager.GetObject("ModInfoCurseTip", resourceCulture)); }
+        }
+        internal static string ModInfoStore {
+            get { return (string)(ResourceManager.GetObject("ModInfoStore", resourceCulture)); }
+        }
+        internal static string ModInfoStoreTip {
+            get { return (string)(ResourceManager.GetObject("ModInfoStoreTip", resourceCulture)); }
+        }
+        internal static string ModInfoSteamStore {
+            get { return (string)(ResourceManager.GetObject("ModInfoSteamStore", resourceCulture)); }
+        }
+        internal static string ModInfoSteamStoreTip {
+            get { return (string)(ResourceManager.GetObject("ModInfoSteamStoreTip", resourceCulture)); }
+        }
+        internal static string ModInfoViewMetadata {
+            get { return (string)(ResourceManager.GetObject("ModInfoViewMetadata", resourceCulture)); }
+        }
+        internal static string ModInfoViewMetadataTip {
+            get { return (string)(ResourceManager.GetObject("ModInfoViewMetadataTip", resourceCulture)); }
+        }
+        internal static string ModInfoViewMetadataTitle {
+            get { return (string)(ResourceManager.GetObject("ModInfoViewMetadataTitle", resourceCulture)); }
+        }
+        internal static string ModInfoURLLaunching {
+            get { return (string)(ResourceManager.GetObject("ModInfoURLLaunching", resourceCulture)); }
+        }
+        internal static string ModInfoDependenciesFrame {
+            get { return (string)(ResourceManager.GetObject("ModInfoDependenciesFrame", resourceCulture)); }
+        }
+        internal static string ModInfoRequiredLabel {
+            get { return (string)(ResourceManager.GetObject("ModInfoRequiredLabel", resourceCulture)); }
+        }
+        internal static string ModInfoConflictsLabel {
+            get { return (string)(ResourceManager.GetObject("ModInfoConflictsLabel", resourceCulture)); }
+        }
+        internal static string ModInfoReplacedBy {
+            get { return (string)(ResourceManager.GetObject("ModInfoReplacedBy", resourceCulture)); }
+        }
+        internal static string ModInfoInstalledOn {
+            get { return (string)(ResourceManager.GetObject("ModInfoInstalledOn", resourceCulture)); }
+        }
+        internal static string ModInfoInstalledManually {
+            get { return (string)(ResourceManager.GetObject("ModInfoInstalledManually", resourceCulture)); }
+        }
+        internal static string ModInfoLatestInstalledOn {
+            get { return (string)(ResourceManager.GetObject("ModInfoLatestInstalledOn", resourceCulture)); }
+        }
+        internal static string ModInfoLatestInstalledManually {
+            get { return (string)(ResourceManager.GetObject("ModInfoLatestInstalledManually", resourceCulture)); }
+        }
+        internal static string ModInfoLatestVersion {
+            get { return (string)(ResourceManager.GetObject("ModInfoLatestVersion", resourceCulture)); }
+        }
+        internal static string ModInfoOtherVersions {
+            get { return (string)(ResourceManager.GetObject("ModInfoOtherVersions", resourceCulture)); }
+        }
+        internal static string ModInfoUnavailableInstalledOn {
+            get { return (string)(ResourceManager.GetObject("ModInfoUnavailableInstalledOn", resourceCulture)); }
+        }
+        internal static string ModInfoUnavailableInstalledManually {
+            get { return (string)(ResourceManager.GetObject("ModInfoUnavailableInstalledManually", resourceCulture)); }
+        }
+        internal static string ModInfoCompatibleWith {
+            get { return (string)(ResourceManager.GetObject("ModInfoCompatibleWith", resourceCulture)); }
+        }
+        internal static string ModInfoHostedOn {
+            get { return (string)(ResourceManager.GetObject("ModInfoHostedOn", resourceCulture)); }
+        }
+        internal static string ModInfoReportBugsOn {
+            get { return (string)(ResourceManager.GetObject("ModInfoReportBugsOn", resourceCulture)); }
+        }
+        internal static string ModInfoRepositoryOn {
+            get { return (string)(ResourceManager.GetObject("ModInfoRepositoryOn", resourceCulture)); }
+        }
+        internal static string ModInfoHomePageOn {
+            get { return (string)(ResourceManager.GetObject("ModInfoHomePageOn", resourceCulture)); }
+        }
+        internal static string ModInfoBuyFromKSPStore {
+            get { return (string)(ResourceManager.GetObject("ModInfoBuyFromKSPStore", resourceCulture)); }
+        }
+        internal static string ModInfoBuyFromSteamStore {
+            get { return (string)(ResourceManager.GetObject("ModInfoBuyFromSteamStore", resourceCulture)); }
+        }
+        internal static string ModInfoBuyFromKSPStoreOrSteamStore {
+            get { return (string)(ResourceManager.GetObject("ModInfoBuyFromKSPStoreOrSteamStore", resourceCulture)); }
+        }
+        internal static string ModInfoDownloading {
+            get { return (string)(ResourceManager.GetObject("ModInfoDownloading", resourceCulture)); }
+        }
+        internal static string ModInfoDownloadCorrupted {
+            get { return (string)(ResourceManager.GetObject("ModInfoDownloadCorrupted", resourceCulture)); }
+        }
+        internal static string ModInfoDownloadFailed {
+            get { return (string)(ResourceManager.GetObject("ModInfoDownloadFailed", resourceCulture)); }
+        }
+        internal static string ProgressTitle {
+            get { return (string)(ResourceManager.GetObject("ProgressTitle", resourceCulture)); }
+        }
+        internal static string ProgressMessages {
+            get { return (string)(ResourceManager.GetObject("ProgressMessages", resourceCulture)); }
+        }
+        internal static string SplashLoading {
+            get { return (string)(ResourceManager.GetObject("SplashLoading", resourceCulture)); }
+        }
+        internal static string SplashPressAnyKey {
+            get { return (string)(ResourceManager.GetObject("SplashPressAnyKey", resourceCulture)); }
+        }
+        internal static string RepoNameLabel {
+            get { return (string)(ResourceManager.GetObject("RepoNameLabel", resourceCulture)); }
+        }
+        internal static string RepoNameGhostText {
+            get { return (string)(ResourceManager.GetObject("RepoNameGhostText", resourceCulture)); }
+        }
+        internal static string RepoURLLabel {
+            get { return (string)(ResourceManager.GetObject("RepoURLLabel", resourceCulture)); }
+        }
+        internal static string RepoURLGhostText {
+            get { return (string)(ResourceManager.GetObject("RepoURLGhostText", resourceCulture)); }
+        }
+        internal static string RepoImportTip {
+            get { return (string)(ResourceManager.GetObject("RepoImportTip", resourceCulture)); }
+        }
+        internal static string RepoTitle {
+            get { return (string)(ResourceManager.GetObject("RepoTitle", resourceCulture)); }
+        }
+        internal static string RepoNameEmptyError {
+            get { return (string)(ResourceManager.GetObject("RepoNameEmptyError", resourceCulture)); }
+        }
+        internal static string RepoNameDuplicateError {
+            get { return (string)(ResourceManager.GetObject("RepoNameDuplicateError", resourceCulture)); }
+        }
+        internal static string RepoURLEmptyError {
+            get { return (string)(ResourceManager.GetObject("RepoURLEmptyError", resourceCulture)); }
+        }
+        internal static string ModListNameHeader {
+            get { return (string)(ResourceManager.GetObject("ModListNameHeader", resourceCulture)); }
+        }
+        internal static string ModListVersionHeader {
+            get { return (string)(ResourceManager.GetObject("ModListVersionHeader", resourceCulture)); }
+        }
+        internal static string ModListMaxGameVersionHeader {
+            get { return (string)(ResourceManager.GetObject("ModListMaxGameVersionHeader", resourceCulture)); }
+        }
+        internal static string ModListSearchFocusedGhostText {
+            get { return (string)(ResourceManager.GetObject("ModListSearchFocusedGhostText", resourceCulture)); }
+        }
+        internal static string ModListSearchUnfocusedGhostText {
+            get { return (string)(ResourceManager.GetObject("ModListSearchUnfocusedGhostText", resourceCulture)); }
+        }
+        internal static string ModListCount {
+            get { return (string)(ResourceManager.GetObject("ModListCount", resourceCulture)); }
+        }
+        internal static string ModListSizeOnDisk {
+            get { return (string)(ResourceManager.GetObject("ModListSizeOnDisk", resourceCulture)); }
+        }
+        internal static string ModListInstallTip {
+            get { return (string)(ResourceManager.GetObject("ModListInstallTip", resourceCulture)); }
+        }
+        internal static string ModListUpgradeTip {
+            get { return (string)(ResourceManager.GetObject("ModListUpgradeTip", resourceCulture)); }
+        }
+        internal static string ModListReplaceTip {
+            get { return (string)(ResourceManager.GetObject("ModListReplaceTip", resourceCulture)); }
+        }
+        internal static string ModListRemoveTip {
+            get { return (string)(ResourceManager.GetObject("ModListRemoveTip", resourceCulture)); }
+        }
+        internal static string ModListAutoInstTip {
+            get { return (string)(ResourceManager.GetObject("ModListAutoInstTip", resourceCulture)); }
+        }
+        internal static string ModListUserSelectedTip {
+            get { return (string)(ResourceManager.GetObject("ModListUserSelectedTip", resourceCulture)); }
+        }
+        internal static string ModListApplyChangesTip {
+            get { return (string)(ResourceManager.GetObject("ModListApplyChangesTip", resourceCulture)); }
+        }
+        internal static string ModListUpdatedDayAgo {
+            get { return (string)(ResourceManager.GetObject("ModListUpdatedDayAgo", resourceCulture)); }
+        }
+        internal static string ModListUpdatedDaysAgo {
+            get { return (string)(ResourceManager.GetObject("ModListUpdatedDaysAgo", resourceCulture)); }
+        }
+        internal static string ModListSortMenu {
+            get { return (string)(ResourceManager.GetObject("ModListSortMenu", resourceCulture)); }
+        }
+        internal static string ModListSortMenuTip {
+            get { return (string)(ResourceManager.GetObject("ModListSortMenuTip", resourceCulture)); }
+        }
+        internal static string ModListRefreshMenu {
+            get { return (string)(ResourceManager.GetObject("ModListRefreshMenu", resourceCulture)); }
+        }
+        internal static string ModListRefreshMenuTip {
+            get { return (string)(ResourceManager.GetObject("ModListRefreshMenuTip", resourceCulture)); }
+        }
+        internal static string ModListUpgradeMenu {
+            get { return (string)(ResourceManager.GetObject("ModListUpgradeMenu", resourceCulture)); }
+        }
+        internal static string ModListUpgradeMenuTip {
+            get { return (string)(ResourceManager.GetObject("ModListUpgradeMenuTip", resourceCulture)); }
+        }
+        internal static string ModListAuditRecsMenu {
+            get { return (string)(ResourceManager.GetObject("ModListAuditRecsMenu", resourceCulture)); }
+        }
+        internal static string ModListAuditRecsMenuTip {
+            get { return (string)(ResourceManager.GetObject("ModListAuditRecsMenuTip", resourceCulture)); }
+        }
+        internal static string ModListImportMenu {
+            get { return (string)(ResourceManager.GetObject("ModListImportMenu", resourceCulture)); }
+        }
+        internal static string ModListImportMenuTip {
+            get { return (string)(ResourceManager.GetObject("ModListImportMenuTip", resourceCulture)); }
+        }
+        internal static string ModListExportMenu {
+            get { return (string)(ResourceManager.GetObject("ModListExportMenu", resourceCulture)); }
+        }
+        internal static string ModListExportMenuTip {
+            get { return (string)(ResourceManager.GetObject("ModListExportMenuTip", resourceCulture)); }
+        }
+        internal static string ModListInstanceSettingsMenu {
+            get { return (string)(ResourceManager.GetObject("ModListInstanceSettingsMenu", resourceCulture)); }
+        }
+        internal static string ModListInstanceSettingsMenuTip {
+            get { return (string)(ResourceManager.GetObject("ModListInstanceSettingsMenuTip", resourceCulture)); }
+        }
+        internal static string ModListSelectInstanceMenu {
+            get { return (string)(ResourceManager.GetObject("ModListSelectInstanceMenu", resourceCulture)); }
+        }
+        internal static string ModListSelectInstanceMenuTip {
+            get { return (string)(ResourceManager.GetObject("ModListSelectInstanceMenuTip", resourceCulture)); }
+        }
+        internal static string ModListAuthTokenMenu {
+            get { return (string)(ResourceManager.GetObject("ModListAuthTokenMenu", resourceCulture)); }
+        }
+        internal static string ModListAuthTokenMenuTip {
+            get { return (string)(ResourceManager.GetObject("ModListAuthTokenMenuTip", resourceCulture)); }
+        }
+        internal static string ModListHelpMenu {
+            get { return (string)(ResourceManager.GetObject("ModListHelpMenu", resourceCulture)); }
+        }
+        internal static string ModListFilterMenu {
+            get { return (string)(ResourceManager.GetObject("ModListFilterMenu", resourceCulture)); }
+        }
+        internal static string ModListFilterMenuTip {
+            get { return (string)(ResourceManager.GetObject("ModListFilterMenuTip", resourceCulture)); }
+        }
+        internal static string ModListHelpMenuTip {
+            get { return (string)(ResourceManager.GetObject("ModListHelpMenuTip", resourceCulture)); }
+        }
+        internal static string ModListQuitMenu {
+            get { return (string)(ResourceManager.GetObject("ModListQuitMenu", resourceCulture)); }
+        }
+        internal static string ModListQuitMenuTip {
+            get { return (string)(ResourceManager.GetObject("ModListQuitMenuTip", resourceCulture)); }
+        }
+        internal static string ModListCaptureKeyMenu {
+            get { return (string)(ResourceManager.GetObject("ModListCaptureKeyMenu", resourceCulture)); }
+        }
+        internal static string ModListCaptureKeyMenuTip {
+            get { return (string)(ResourceManager.GetObject("ModListCaptureKeyMenuTip", resourceCulture)); }
+        }
+        internal static string ModListPressAKey {
+            get { return (string)(ResourceManager.GetObject("ModListPressAKey", resourceCulture)); }
+        }
+        internal static string ModListAuditNotFound {
+            get { return (string)(ResourceManager.GetObject("ModListAuditNotFound", resourceCulture)); }
+        }
+        internal static string ModListUpdateRegistryTitle {
+            get { return (string)(ResourceManager.GetObject("ModListUpdateRegistryTitle", resourceCulture)); }
+        }
+        internal static string ModListUpdateRegistryMessage {
+            get { return (string)(ResourceManager.GetObject("ModListUpdateRegistryMessage", resourceCulture)); }
+        }
+        internal static string ModListNewMod {
+            get { return (string)(ResourceManager.GetObject("ModListNewMod", resourceCulture)); }
+        }
+        internal static string ModListNewMods {
+            get { return (string)(ResourceManager.GetObject("ModListNewMods", resourceCulture)); }
+        }
+        internal static string ModListScanBad {
+            get { return (string)(ResourceManager.GetObject("ModListScanBad", resourceCulture)); }
+        }
+        internal static string ModListExportPrefix {
+            get { return (string)(ResourceManager.GetObject("ModListExportPrefix", resourceCulture)); }
+        }
+        internal static string ModListExported {
+            get { return (string)(ResourceManager.GetObject("ModListExported", resourceCulture)); }
+        }
+        internal static string ModListExportFailed {
+            get { return (string)(ResourceManager.GetObject("ModListExportFailed", resourceCulture)); }
+        }
+        internal static string ModListHelpSymbolHeader {
+            get { return (string)(ResourceManager.GetObject("ModListHelpSymbolHeader", resourceCulture)); }
+        }
+        internal static string ModListHelpInstalled {
+            get { return (string)(ResourceManager.GetObject("ModListHelpInstalled", resourceCulture)); }
+        }
+        internal static string ModListHelpAutoInstalled {
+            get { return (string)(ResourceManager.GetObject("ModListHelpAutoInstalled", resourceCulture)); }
+        }
+        internal static string ModListHelpUpgradeable {
+            get { return (string)(ResourceManager.GetObject("ModListHelpUpgradeable", resourceCulture)); }
+        }
+        internal static string ModListHelpManuallyInstalled {
+            get { return (string)(ResourceManager.GetObject("ModListHelpManuallyInstalled", resourceCulture)); }
+        }
+        internal static string ModListHelpReplaceable {
+            get { return (string)(ResourceManager.GetObject("ModListHelpReplaceable", resourceCulture)); }
+        }
+        internal static string ModListHelpUnavailable {
+            get { return (string)(ResourceManager.GetObject("ModListHelpUnavailable", resourceCulture)); }
+        }
+        internal static string ModListHelpBasicKeysHeader {
+            get { return (string)(ResourceManager.GetObject("ModListHelpBasicKeysHeader", resourceCulture)); }
+        }
+        internal static string ModListHelpMoveFocus {
+            get { return (string)(ResourceManager.GetObject("ModListHelpMoveFocus", resourceCulture)); }
+        }
+        internal static string ModListHelpSelectRow {
+            get { return (string)(ResourceManager.GetObject("ModListHelpSelectRow", resourceCulture)); }
+        }
+        internal static string ModListHelpClearSearch {
+            get { return (string)(ResourceManager.GetObject("ModListHelpClearSearch", resourceCulture)); }
+        }
+        internal static string ModListHelpSpecialSearchesHeader {
+            get { return (string)(ResourceManager.GetObject("ModListHelpSpecialSearchesHeader", resourceCulture)); }
+        }
+        internal static string ModListHelpAuthor {
+            get { return (string)(ResourceManager.GetObject("ModListHelpAuthor", resourceCulture)); }
+        }
+        internal static string ModListHelpName {
+            get { return (string)(ResourceManager.GetObject("ModListHelpName", resourceCulture)); }
+        }
+        internal static string ModListHelpSearchAuthor {
+            get { return (string)(ResourceManager.GetObject("ModListHelpSearchAuthor", resourceCulture)); }
+        }
+        internal static string ModListHelpSearchInstalled {
+            get { return (string)(ResourceManager.GetObject("ModListHelpSearchInstalled", resourceCulture)); }
+        }
+        internal static string ModListHelpSearchUpgradeable {
+            get { return (string)(ResourceManager.GetObject("ModListHelpSearchUpgradeable", resourceCulture)); }
+        }
+        internal static string ModListHelpSearchDepends {
+            get { return (string)(ResourceManager.GetObject("ModListHelpSearchDepends", resourceCulture)); }
+        }
+        internal static string ModListHelpSearchConflicts {
+            get { return (string)(ResourceManager.GetObject("ModListHelpSearchConflicts", resourceCulture)); }
+        }
+        internal static string ModListHelpSearchNew {
+            get { return (string)(ResourceManager.GetObject("ModListHelpSearchNew", resourceCulture)); }
+        }
+
+        internal static string FiltersAddMiniAVCMenu {
+            get { return (string)(ResourceManager.GetObject("FiltersAddMiniAVCMenu", resourceCulture)); }
+        }
+        internal static string FiltersAddMiniAVCMenuTip {
+            get { return (string)(ResourceManager.GetObject("FiltersAddMiniAVCMenuTip", resourceCulture)); }
+        }
+        internal static string FiltersGlobalHeader {
+            get { return (string)(ResourceManager.GetObject("FiltersGlobalHeader", resourceCulture)); }
+        }
+        internal static string FiltersInstanceHeader {
+            get { return (string)(ResourceManager.GetObject("FiltersInstanceHeader", resourceCulture)); }
+        }
+        internal static string FiltersTitle {
+            get { return (string)(ResourceManager.GetObject("FiltersTitle", resourceCulture)); }
+        }
+
+        internal static string FilterAddGhostText {
+            get { return (string)(ResourceManager.GetObject("FilterAddGhostText", resourceCulture)); }
+        }
+        internal static string FilterAddAcceptTip {
+            get { return (string)(ResourceManager.GetObject("FilterAddAcceptTip", resourceCulture)); }
+        }
+        internal static string FilterAddTitle {
+            get { return (string)(ResourceManager.GetObject("FilterAddTitle", resourceCulture)); }
+        }
+
+        internal static string ExitTitle {
+            get { return (string)(ResourceManager.GetObject("ExitTitle", resourceCulture)); }
+        }
+        internal static string ExitBody {
+            get { return (string)(ResourceManager.GetObject("ExitBody", resourceCulture)); }
+        }
+
+    }
+}
diff --git a/ConsoleUI/Properties/Resources.en-US.resx b/ConsoleUI/Properties/Resources.en-US.resx
new file mode 100644
index 0000000000..9022706a71
--- /dev/null
+++ b/ConsoleUI/Properties/Resources.en-US.resx
@@ -0,0 +1,121 @@
+<?xml version="1.0" encoding="utf-8"?>
+<root>
+  <!--
+    Microsoft ResX Schema
+
+    Version 2.0
+
+    The primary goals of this format is to allow a simple XML format
+    that is mostly human readable. The generation and parsing of the
+    various data types are done through the TypeConverter classes
+    associated with the data types.
+
+    Example:
+
+    ... ado.net/XML headers & schema ...
+    <resheader name="resmimetype">text/microsoft-resx</resheader>
+    <resheader name="version">2.0</resheader>
+    <resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
+    <resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
+    <data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
+    <data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
+    <data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
+        <value>[base64 mime encoded serialized .NET Framework object]</value>
+    </data>
+    <data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
+        <value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
+        <comment>This is a comment</comment>
+    </data>
+
+    There are any number of "resheader" rows that contain simple
+    name/value pairs.
+
+    Each data row contains a name, and value. The row also contains a
+    type or mimetype. Type corresponds to a .NET class that support
+    text/value conversion through the TypeConverter architecture.
+    Classes that don't support this are serialized and stored with the
+    mimetype set.
+
+    The mimetype is used for serialized objects, and tells the
+    ResXResourceReader how to depersist the object. This is currently not
+    extensible. For a given mimetype the value must be set accordingly:
+
+    Note - application/x-microsoft.net.object.binary.base64 is the format
+    that the ResXResourceWriter will generate, however the reader can
+    read any of the formats listed below.
+
+    mimetype: application/x-microsoft.net.object.binary.base64
+    value   : The object must be serialized with
+            : System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
+            : and then encoded with base64 encoding.
+
+    mimetype: application/x-microsoft.net.object.soap.base64
+    value   : The object must be serialized with
+            : System.Runtime.Serialization.Formatters.Soap.SoapFormatter
+            : and then encoded with base64 encoding.
+
+    mimetype: application/x-microsoft.net.object.bytearray.base64
+    value   : The object must be serialized into a byte array
+            : using a System.ComponentModel.TypeConverter
+            : and then encoded with base64 encoding.
+    -->
+  <xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
+    <xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
+    <xsd:element name="root" msdata:IsDataSet="true">
+      <xsd:complexType>
+        <xsd:choice maxOccurs="unbounded">
+          <xsd:element name="metadata">
+            <xsd:complexType>
+              <xsd:sequence>
+                <xsd:element name="value" type="xsd:string" minOccurs="0" />
+              </xsd:sequence>
+              <xsd:attribute name="name" use="required" type="xsd:string" />
+              <xsd:attribute name="type" type="xsd:string" />
+              <xsd:attribute name="mimetype" type="xsd:string" />
+              <xsd:attribute ref="xml:space" />
+            </xsd:complexType>
+          </xsd:element>
+          <xsd:element name="assembly">
+            <xsd:complexType>
+              <xsd:attribute name="alias" type="xsd:string" />
+              <xsd:attribute name="name" type="xsd:string" />
+            </xsd:complexType>
+          </xsd:element>
+          <xsd:element name="data">
+            <xsd:complexType>
+              <xsd:sequence>
+                <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
+                <xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
+              </xsd:sequence>
+              <xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
+              <xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
+              <xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
+              <xsd:attribute ref="xml:space" />
+            </xsd:complexType>
+          </xsd:element>
+          <xsd:element name="resheader">
+            <xsd:complexType>
+              <xsd:sequence>
+                <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
+              </xsd:sequence>
+              <xsd:attribute name="name" type="xsd:string" use="required" />
+            </xsd:complexType>
+          </xsd:element>
+        </xsd:choice>
+      </xsd:complexType>
+    </xsd:element>
+  </xsd:schema>
+  <resheader name="resmimetype">
+    <value>text/microsoft-resx</value>
+  </resheader>
+  <resheader name="version">
+    <value>2.0</value>
+  </resheader>
+  <resheader name="reader">
+    <value>System.Resources.ResXResourceReader, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
+  </resheader>
+  <resheader name="writer">
+    <value>System.Resources.ResXResourceWriter, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
+  </resheader>
+  <data name="ModInfoLicence" xml:space="preserve"><value>License:</value></data>
+</root>
diff --git a/ConsoleUI/Properties/Resources.fr-FR.resx b/ConsoleUI/Properties/Resources.fr-FR.resx
new file mode 100644
index 0000000000..10fd355b6e
--- /dev/null
+++ b/ConsoleUI/Properties/Resources.fr-FR.resx
@@ -0,0 +1,396 @@
+<?xml version="1.0" encoding="utf-8"?>
+<root>
+  <!--
+    Microsoft ResX Schema
+
+    Version 2.0
+
+    The primary goals of this format is to allow a simple XML format
+    that is mostly human readable. The generation and parsing of the
+    various data types are done through the TypeConverter classes
+    associated with the data types.
+
+    Example:
+
+    ... ado.net/XML headers & schema ...
+    <resheader name="resmimetype">text/microsoft-resx</resheader>
+    <resheader name="version">2.0</resheader>
+    <resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
+    <resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
+    <data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
+    <data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
+    <data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
+        <value>[base64 mime encoded serialized .NET Framework object]</value>
+    </data>
+    <data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
+        <value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
+        <comment>This is a comment</comment>
+    </data>
+
+    There are any number of "resheader" rows that contain simple
+    name/value pairs.
+
+    Each data row contains a name, and value. The row also contains a
+    type or mimetype. Type corresponds to a .NET class that support
+    text/value conversion through the TypeConverter architecture.
+    Classes that don't support this are serialized and stored with the
+    mimetype set.
+
+    The mimetype is used for serialized objects, and tells the
+    ResXResourceReader how to depersist the object. This is currently not
+    extensible. For a given mimetype the value must be set accordingly:
+
+    Note - application/x-microsoft.net.object.binary.base64 is the format
+    that the ResXResourceWriter will generate, however the reader can
+    read any of the formats listed below.
+
+    mimetype: application/x-microsoft.net.object.binary.base64
+    value   : The object must be serialized with
+            : System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
+            : and then encoded with base64 encoding.
+
+    mimetype: application/x-microsoft.net.object.soap.base64
+    value   : The object must be serialized with
+            : System.Runtime.Serialization.Formatters.Soap.SoapFormatter
+            : and then encoded with base64 encoding.
+
+    mimetype: application/x-microsoft.net.object.bytearray.base64
+    value   : The object must be serialized into a byte array
+            : using a System.ComponentModel.TypeConverter
+            : and then encoded with base64 encoding.
+    -->
+  <xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
+    <xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
+    <xsd:element name="root" msdata:IsDataSet="true">
+      <xsd:complexType>
+        <xsd:choice maxOccurs="unbounded">
+          <xsd:element name="metadata">
+            <xsd:complexType>
+              <xsd:sequence>
+                <xsd:element name="value" type="xsd:string" minOccurs="0" />
+              </xsd:sequence>
+              <xsd:attribute name="name" use="required" type="xsd:string" />
+              <xsd:attribute name="type" type="xsd:string" />
+              <xsd:attribute name="mimetype" type="xsd:string" />
+              <xsd:attribute ref="xml:space" />
+            </xsd:complexType>
+          </xsd:element>
+          <xsd:element name="assembly">
+            <xsd:complexType>
+              <xsd:attribute name="alias" type="xsd:string" />
+              <xsd:attribute name="name" type="xsd:string" />
+            </xsd:complexType>
+          </xsd:element>
+          <xsd:element name="data">
+            <xsd:complexType>
+              <xsd:sequence>
+                <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
+                <xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
+              </xsd:sequence>
+              <xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
+              <xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
+              <xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
+              <xsd:attribute ref="xml:space" />
+            </xsd:complexType>
+          </xsd:element>
+          <xsd:element name="resheader">
+            <xsd:complexType>
+              <xsd:sequence>
+                <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
+              </xsd:sequence>
+              <xsd:attribute name="name" type="xsd:string" use="required" />
+            </xsd:complexType>
+          </xsd:element>
+        </xsd:choice>
+      </xsd:complexType>
+    </xsd:element>
+  </xsd:schema>
+  <resheader name="resmimetype">
+    <value>text/microsoft-resx</value>
+  </resheader>
+  <resheader name="version">
+    <value>2.0</value>
+  </resheader>
+  <resheader name="reader">
+    <value>System.Resources.ResXResourceReader, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
+  </resheader>
+  <resheader name="writer">
+    <value>System.Resources.ResXResourceWriter, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
+  </resheader>
+  <data name="Accept" xml:space="preserve"><value>Accepter</value></data>
+  <data name="Cancel" xml:space="preserve"><value>Annuler</value></data>
+  <data name="DefaultGhostText" xml:space="preserve"><value>&lt;Entrez une valeur&gt;</value></data>
+  <data name="Ascending" xml:space="preserve"><value>Croissant</value></data>
+  <data name="AscendingSortTip" xml:space="preserve"><value>Trier la liste par ordre croissant</value></data>
+  <data name="Descending" xml:space="preserve"><value>Décroissant</value></data>
+  <data name="DescendingSortTip" xml:space="preserve"><value>Trier la liste par ordre décroissant</value></data>
+  <data name="ColumnNumber" xml:space="preserve"><value>Colonne #{0}</value></data>
+  <data name="ColumnNumberSortTip" xml:space="preserve"><value>Trier la liste par colonne #{0}</value></data>
+  <data name="ColumnNameSortTip" xml:space="preserve"><value>Trier la liste par la colonne {0}</value></data>
+  <data name="CursorKeys" xml:space="preserve"><value>Touches de curseur</value></data>
+  <data name="Scroll" xml:space="preserve"><value>Curseur</value></data>
+  <data name="ScrollMessages" xml:space="preserve"><value>Messages de Curseurs</value></data>
+  <data name="Menu" xml:space="preserve"><value>Menu</value></data>
+  <data name="Yes" xml:space="preserve"><value>Oui (Y)</value></data>
+  <data name="No" xml:space="preserve"><value>Non (N)</value></data>
+  <data name="OK" xml:space="preserve"><value>OK</value></data>
+  <data name="Force" xml:space="preserve"><value>Forcer</value></data>
+  <data name="Tab" xml:space="preserve"><value>Tabulation</value></data>
+  <data name="Enter" xml:space="preserve"><value>Entré</value></data>
+  <data name="Esc" xml:space="preserve"><value>Échappe</value></data>
+  <data name="Ctrl" xml:space="preserve"><value>Ctrl</value></data>
+  <data name="Alt" xml:space="preserve"><value>Alt</value></data>
+  <data name="Back" xml:space="preserve"><value>Retour</value></data>
+  <data name="Quit" xml:space="preserve"><value>Quitter</value></data>
+  <data name="Add" xml:space="preserve"><value>Ajouter</value></data>
+  <data name="Remove" xml:space="preserve"><value>Retirer</value></data>
+  <data name="Edit" xml:space="preserve"><value>Éditer</value></data>
+  <data name="Sort" xml:space="preserve"><value>Trier</value></data>
+  <data name="Toggle" xml:space="preserve"><value>Changer</value></data>
+  <data name="Select" xml:space="preserve"><value>Sélectionner</value></data>
+  <data name="SelectAll" xml:space="preserve"><value>Sélectionner tout</value></data>
+  <data name="DeselectAll" xml:space="preserve"><value>désélectionner tout</value></data>
+  <data name="Details" xml:space="preserve"><value>Détails</value></data>
+  <data name="Up" xml:space="preserve"><value>Haut</value></data>
+  <data name="Down" xml:space="preserve"><value>Bas</value></data>
+  <data name="FileSelectDirectory" xml:space="preserve"><value>Dossier</value></data>
+  <data name="FileSelectCountFooter" xml:space="preserve"><value>{0} sélectionné, {1}</value></data>
+  <data name="FileSelectNameHeader" xml:space="preserve"><value>Nom</value></data>
+  <data name="FileSelectSizeHeader" xml:space="preserve"><value>Taille</value></data>
+  <data name="FileSelectTimestampHeader" xml:space="preserve"><value>Accédé</value></data>
+  <data name="FileSelectChangeDirectory" xml:space="preserve"><value>Changer de dossier</value></data>
+  <data name="FileSelectSelect" xml:space="preserve"><value>Sélectionner</value></data>
+  <data name="FileSelectImport" xml:space="preserve"><value>Importer</value></data>
+  <data name="FileSelectDirSize" xml:space="preserve"><value>&lt;DOSSIER&gt;</value></data>
+  <data name="AuthTokenListTitle" xml:space="preserve"><value>Jetons d'Authentification</value></data>
+  <data name="AuthTokenListGitHubLink" xml:space="preserve"><value>Créer un jeton pour l'API GitHub</value></data>
+  <data name="AuthTokenListGitHubLinkTip" xml:space="preserve"><value>Ouvrir une page web pour créer un jeton d'authentification pour l'API GitHub</value></data>
+  <data name="AuthTokenListLabel" xml:space="preserve"><value>Jeton d'authentification pour les téléchargements :</value></data>
+  <data name="AuthTokenListHostHeader" xml:space="preserve"><value>Hôte</value></data>
+  <data name="AuthTokenListTokenHeader" xml:space="preserve"><value>Jeton</value></data>
+  <data name="AuthTokenListWarning" xml:space="preserve"><value>NOTE: Ces valeurs sont privées ! Ne partagez pas une capture de cet écran !</value></data>
+  <data name="AuthTokenListMissingToken" xml:space="preserve"><value>&lt;ERREUR&gt;</value></data>
+  <data name="AuthTokenAddTitle" xml:space="preserve"><value>Créer une clé d'authentification</value></data>
+  <data name="AuthTokenAddHost" xml:space="preserve"><value>Hôte :</value></data>
+  <data name="AuthTokenAddHostGhostText" xml:space="preserve"><value>&lt;Entrez un nom d'hôte&gt;</value></data>
+  <data name="AuthTokenAddToken" xml:space="preserve"><value>Jeton :</value></data>
+  <data name="AuthTokenAddTokenGhostText" xml:space="preserve"><value>&lt;Entrez un jeton d'authentification&gt;</value></data>
+  <data name="CompatibleVersionsTitle" xml:space="preserve"><value>Sélectionnez les Versions Compatibles</value></data>
+  <data name="CompatibleVersionsListHeader" xml:space="preserve"><value>Version Prédéfinie</value></data>
+  <data name="CompatibleVersionsListAcceptTip" xml:space="preserve"><value>Sélectionner cette version</value></data>
+  <data name="CompatibleVersionsGhostText" xml:space="preserve"><value>&lt;Entrez une version&gt;</value></data>
+  <data name="CompatibleVersionsEntryAcceptTip" xml:space="preserve"><value>Accepter cette value</value></data>
+  <data name="ThemeNotFound" xml:space="preserve"><value>Impossible de trouver ce thème : {0}</value></data>
+  <data name="ThemeList" xml:space="preserve"><value>Thèmes disponibles : {0}</value></data>
+  <data name="RecommendationsLabel" xml:space="preserve"><value>Des mods additionnels sont recommandés ou suggérés :</value></data>
+  <data name="RecommendationsInstallHeader" xml:space="preserve"><value>Installer</value></data>
+  <data name="RecommendationsNameHeader" xml:space="preserve"><value>Nom</value></data>
+  <data name="RecommendationsSourcesHeader" xml:space="preserve"><value>Sources</value></data>
+  <data name="RecommendationsTitle" xml:space="preserve"><value>Recommandations &amp; Suggestions</value></data>
+  <data name="ImportSelectTitle" xml:space="preserve"><value>Importer les téléchargements</value></data>
+  <data name="ImportSelectHeader" xml:space="preserve"><value>Importer</value></data>
+  <data name="ImportProgressTitle" xml:space="preserve"><value>Importation des Téléchargements</value></data>
+  <data name="ImportProgressMessage" xml:space="preserve"><value>Calcul en cours...</value></data>
+  <data name="InstanceNameLabel" xml:space="preserve"><value>Nom :</value></data>
+  <data name="InstanceNameGhostText" xml:space="preserve"><value>&lt;Entrez le nom à utiliser pour cette instance de jeu&gt;</value></data>
+  <data name="InstancePathGhostText" xml:space="preserve"><value>&lt;Entrez l'emplacement de cette instance de jeu sur le disque&gt;</value></data>
+  <data name="InstancePathLabel" xml:space="preserve"><value>Chemin de l'instance :</value></data>
+  <data name="InstanceNameEmptyError" xml:space="preserve"><value>Le nom ne peut pas être vide !</value></data>
+  <data name="InstanceNameDuplicateError" xml:space="preserve"><value>{0} existe déjà !</value></data>
+  <data name="InstancePathNotGameFolderError" xml:space="preserve"><value>Le chemin ne correspond à un dossier de jeu !</value></data>
+  <data name="InstanceAddExample" xml:space="preserve"><value>Exemple : {0}</value></data>
+  <data name="InstanceAddTitle" xml:space="preserve"><value>Ajouter Instance de Jeu</value></data>
+  <data name="InstanceEditTitle" xml:space="preserve"><value>Éditer Instance de Jeu</value></data>
+  <data name="InstanceEditRepoFrameTitle" xml:space="preserve"><value>Sources de la Liste de Mods</value></data>
+  <data name="InstanceEditCompatFrameTitle" xml:space="preserve"><value>Versions Compatibles Additionnelles</value></data>
+  <data name="InstanceEditRepoIndexHeader" xml:space="preserve"><value>Index</value></data>
+  <data name="InstanceEditRepoNameHeader" xml:space="preserve"><value>Nom</value></data>
+  <data name="InstanceEditRepoURLHeader" xml:space="preserve"><value>Adresse</value></data>
+  <data name="InstanceEditCompatVersionHeader" xml:space="preserve"><value>Version</value></data>
+  <data name="InstanceEditRegistryParseError" xml:space="preserve"><value>Échec de l'extraction des sources de la liste de mods à partir de {0}.</value></data>
+  <data name="InstanceListTitle" xml:space="preserve"><value>Instances de Jeu</value></data>
+  <data name="InstanceListLabel" xml:space="preserve"><value>Sélectionner ou ajouter une instance de jeu :</value></data>
+  <data name="InstanceListDefaultHeader" xml:space="preserve"><value>Défaut</value></data>
+  <data name="InstanceListNameHeader" xml:space="preserve"><value>Nom</value></data>
+  <data name="InstanceListGameHeader" xml:space="preserve"><value>Jeu</value></data>
+  <data name="InstanceListVersionHeader" xml:space="preserve"><value>Version</value></data>
+  <data name="InstanceListPathHeader" xml:space="preserve"><value>Chemin</value></data>
+  <data name="InstanceListLoadingInstance" xml:space="preserve"><value>Chargement de l'instance {0}...</value></data>
+  <data name="InstanceListDefaultToggle" xml:space="preserve"><value>Défaut</value></data>
+  <data name="InstanceListLoadingError" xml:space="preserve"><value>Erreur lors du chargement de {0} :
+{1}</value></data>
+  <data name="InstanceListLocked" xml:space="preserve"><value>Fichier verrou avec un ID de processus actif à {0}
+
+Cela veut probablement dire qu'une autre instance de CKAN s'occupe de cette instance. Vous pouvez supprimer ce fichier pour continuer, mais cela risque fortement de corrompre des données.
+
+Voulez-vous supprimez ce fichier verrou pour forcer l'accès ?</value></data>
+  <data name="InstanceListNoVersion" xml:space="preserve"><value>&lt;AUCUNE&gt;</value></data>
+  <data name="InstallTitle" xml:space="preserve"><value>Installation, Mise à Jour, et Retrait de Mods</value></data>
+  <data name="InstallMessage" xml:space="preserve"><value>Calcul en cours...</value></data>
+  <data name="InstallOwnedFileConflict" xml:space="preserve"><value>{0} a essayé d'installer {1}, mais {2} l'a déjà installé.
+Veuillez signaler ce problème à https://github.com/KSP-CKAN/NetKAN/issues/new/choose</value></data>
+  <data name="InstallUnownedFileConflict" xml:space="preserve"><value>{0} a essayé d'installer {1}, mais {2} l'a déjà installé.
+Veuillez désinstaller manuellement le mod auquel ce fichier appartient pour pouvoir installer {2}.</value></data>
+  <data name="InstallFilesReverted" xml:space="preserve"><value>Retour en arrière des fichiers de jeu.</value></data>
+  <data name="InstallAuthTokenPrompt" xml:space="preserve"><value>{0}
+
+Éditer les jetons d'authentification maintenant ?</value></data>
+  <data name="InstallTooManyModsNameHeader" xml:space="preserve"><value>Nom</value></data>
+  <data name="InstallBadMetadata" xml:space="preserve"><value>Métadonnées incorrectes détectées pour {0} : {1}</value></data>
+  <data name="InstallUnsatisfiedDependency" xml:space="preserve"><value>{0} nécessite {1}, mais il n'est pas listé dans l'index, ou indisponible pour votre version de jeu.
+{2}</value></data>
+  <data name="InstallModuleNotFound" xml:space="preserve"><value>Le module {0} est requis mais il n'est pas listé dans l'index, ou indisponible pour votre version de jeu.
+{1}</value></data>
+  <data name="InstallNotInstalled" xml:space="preserve"><value>{0} n'est pas installé, impossible de le retirer</value></data>
+  <data name="InstallModInstalled" xml:space="preserve"><value>{0} installé avec succès {1} {2}</value></data>
+  <data name="ModInfoTitle" xml:space="preserve"><value>Détails du Mod</value></data>
+  <data name="ModInfoMenuTip" xml:space="preserve"><value>Liens</value></data>
+  <data name="ModInfoAuthors" xml:space="preserve"><value>Par {0}</value></data>
+  <data name="ModInfoLicence" xml:space="preserve"><value>Licence :</value></data>
+  <data name="ModInfoDownload" xml:space="preserve"><value>Taille :</value></data>
+  <data name="ModInfoDescriptionFrame" xml:space="preserve"><value>Description</value></data>
+  <data name="ModInfoUnavailableWarning" xml:space="preserve"><value>
+NOTE : Ce mod est installé mais n'est plus disponible.
+Si vous le retirez, CKAN ne pourra pas le réinstaller.</value></data>
+  <data name="ModInfoDownloadToCache" xml:space="preserve"><value>Télécharger dans le cache (sans installer)</value></data>
+  <data name="ModInfoHomePage" xml:space="preserve"><value>Page Principale</value></data>
+  <data name="ModInfoHomePageTip" xml:space="preserve"><value>Ouvrir le lien de la page principale dans un navigateur</value></data>
+  <data name="ModInfoRepository" xml:space="preserve"><value>Répertoire</value></data>
+  <data name="ModInfoRepositoryTip" xml:space="preserve"><value>Ouvrir le lien du répertoire dans un navigateur</value></data>
+  <data name="ModInfoBugtracker" xml:space="preserve"><value>Traqueur de Bogues</value></data>
+  <data name="ModInfoBugtrackerTip" xml:space="preserve"><value>Ouvrir le lien du traqueur de bogues dans un navigateur</value></data>
+  <data name="ModInfoSpaceDock" xml:space="preserve"><value>SpaceDock</value></data>
+  <data name="ModInfoSpaceDockTip" xml:space="preserve"><value>Ouvrir le lien SpaceDock dans un navigateur</value></data>
+  <data name="ModInfoCurse" xml:space="preserve"><value>Curse</value></data>
+  <data name="ModInfoCurseTip" xml:space="preserve"><value>Ouvrir le lien Curse dans un navigateur</value></data>
+  <data name="ModInfoStore" xml:space="preserve"><value>Boutique</value></data>
+  <data name="ModInfoStoreTip" xml:space="preserve"><value>Ouvrir le lien de la boutique dans un navigateur</value></data>
+  <data name="ModInfoSteamStore" xml:space="preserve"><value>Magasin Steam</value></data>
+  <data name="ModInfoSteamStoreTip" xml:space="preserve"><value>Ouvrir le lien du magasin Steam dans un navigateur</value></data>
+  <data name="ModInfoViewMetadata" xml:space="preserve"><value>DÉBOGAGE : Voir les métadonnées</value></data>
+  <data name="ModInfoViewMetadataTip" xml:space="preserve"><value>Montrer toutes les données du registre pour ce mod</value></data>
+  <data name="ModInfoViewMetadataTitle" xml:space="preserve"><value>{0} Métadonnées</value></data>
+  <data name="ModInfoURLLaunching" xml:space="preserve"><value>Lancement...</value></data>
+  <data name="ModInfoDependenciesFrame" xml:space="preserve"><value>Dépendances</value></data>
+  <data name="ModInfoRequiredLabel" xml:space="preserve"><value>Requis ({0}) :</value></data>
+  <data name="ModInfoConflictsLabel" xml:space="preserve"><value>En conflit avec ({0}) :</value></data>
+  <data name="ModInfoReplacedBy" xml:space="preserve"><value>Remplacé par {0} :</value></data>
+  <data name="ModInfoInstalledOn" xml:space="preserve"><value>Installé le {0}</value></data>
+  <data name="ModInfoInstalledManually" xml:space="preserve"><value>Installé manuellement</value></data>
+  <data name="ModInfoLatestInstalledOn" xml:space="preserve"><value>Dernier/Installé {0}</value></data>
+  <data name="ModInfoLatestInstalledManually" xml:space="preserve"><value>Dernier/Installé manuellement</value></data>
+  <data name="ModInfoLatestVersion" xml:space="preserve"><value>Dernière Version</value></data>
+  <data name="ModInfoOtherVersions" xml:space="preserve"><value>Autres Versions</value></data>
+  <data name="ModInfoUnavailableInstalledOn" xml:space="preserve"><value>INDISPONIBLE/Installé {0}</value></data>
+  <data name="ModInfoUnavailableInstalledManually" xml:space="preserve"><value>INDISPONIBLE/Installé manuellement</value></data>
+  <data name="ModInfoCompatibleWith" xml:space="preserve"><value>Compatible avec :</value></data>
+  <data name="ModInfoHostedOn" xml:space="preserve"><value>Hébergé sur {0}</value></data>
+  <data name="ModInfoReportBugsOn" xml:space="preserve"><value>Signaler les bogues sur {0}</value></data>
+  <data name="ModInfoRepositoryOn" xml:space="preserve"><value>Répertoire sur {0}</value></data>
+  <data name="ModInfoHomePageOn" xml:space="preserve"><value>Page principale sur {0}</value></data>
+  <data name="ModInfoBuyFromKSPStore" xml:space="preserve"><value>Acheter depuis la boutique KSP</value></data>
+  <data name="ModInfoBuyFromSteamStore" xml:space="preserve"><value>Acheter depuis le magasin Steam</value></data>
+  <data name="ModInfoBuyFromKSPStoreOrSteamStore" xml:space="preserve"><value>Acheter depuis les boutiques KSP ou Steam</value></data>
+  <data name="ModInfoDownloading" xml:space="preserve"><value>Téléchargement de {0}</value></data>
+  <data name="ModInfoDownloadCorrupted" xml:space="preserve"><value>Échec du téléchargement, le fichier est corrompu</value></data>
+  <data name="ModInfoDownloadFailed" xml:space="preserve"><value>Échec du téléchargement : {0}</value></data>
+  <data name="ProgressTitle" xml:space="preserve"><value>Progression</value></data>
+  <data name="ProgressMessages" xml:space="preserve"><value>Messages</value></data>
+  <data name="SplashLoading" xml:space="preserve"><value>Chargement...</value></data>
+  <data name="SplashPressAnyKey" xml:space="preserve"><value>Appuyez sur n'importe quelle touche pour continuer</value></data>
+  <data name="RepoNameLabel" xml:space="preserve"><value>Nom :</value></data>
+  <data name="RepoNameGhostText" xml:space="preserve"><value>&lt;Entrez le nom à utiliser pour ce répertoire&gt;</value></data>
+  <data name="RepoURLLabel" xml:space="preserve"><value>Lien :</value></data>
+  <data name="RepoURLGhostText" xml:space="preserve"><value>&lt;Entrez le lien de ce répertoire&gt;</value></data>
+  <data name="RepoImportTip" xml:space="preserve"><value>Importer les valeurs depuis la source par défaut de la liste de mods {0}</value></data>
+  <data name="RepoTitle" xml:space="preserve"><value>Éditer la Source de la Liste de Mods</value></data>
+  <data name="RepoNameEmptyError" xml:space="preserve"><value>Le nom ne peut pas être vide !</value></data>
+  <data name="RepoNameDuplicateError" xml:space="preserve"><value>{0} existe déjà !</value></data>
+  <data name="RepoURLEmptyError" xml:space="preserve"><value>Le lien ne peut pas être vide !</value></data>
+  <data name="ModListNameHeader" xml:space="preserve"><value>Nom</value></data>
+  <data name="ModListVersionHeader" xml:space="preserve"><value>Version</value></data>
+  <data name="ModListMaxGameVersionHeader" xml:space="preserve"><value>Version de jeu max</value></data>
+  <data name="ModListSearchFocusedGhostText" xml:space="preserve"><value>&lt;Taper pour faire une recherche&gt;</value></data>
+  <data name="ModListSearchUnfocusedGhostText" xml:space="preserve"><value>&lt;Ctrl+F pour faire une recherche&gt;</value></data>
+  <data name="ModListCount" xml:space="preserve"><value>{0} mods</value></data>
+  <data name="ModListSizeOnDisk" xml:space="preserve"><value>{0} installés</value></data>
+  <data name="ModListInstallTip" xml:space="preserve"><value>Installer</value></data>
+  <data name="ModListUpgradeTip" xml:space="preserve"><value>Mettre à Jour</value></data>
+  <data name="ModListReplaceTip" xml:space="preserve"><value>Remplacer</value></data>
+  <data name="ModListRemoveTip" xml:space="preserve"><value>Retirer</value></data>
+  <data name="ModListAutoInstTip" xml:space="preserve"><value>Marquer le mod comme auto-installé</value></data>
+  <data name="ModListUserSelectedTip" xml:space="preserve"><value>Marquer le mod sélectionné comme installé par l'utilisateur</value></data>
+  <data name="ModListApplyChangesTip" xml:space="preserve"><value>Appliquer les changements</value></data>
+  <data name="ModListUpdatedDayAgo" xml:space="preserve"><value>Mis à jour il y a au moins {0} jour</value></data>
+  <data name="ModListUpdatedDaysAgo" xml:space="preserve"><value>Mis à jour il y a au moins {0} jours</value></data>
+  <data name="ModListSortMenu" xml:space="preserve"><value>Trier...</value></data>
+  <data name="ModListSortMenuTip" xml:space="preserve"><value>Changer le tri de la liste de mods</value></data>
+  <data name="ModListRefreshMenu" xml:space="preserve"><value>Rafraîchir liste de mods</value></data>
+  <data name="ModListRefreshMenuTip" xml:space="preserve"><value>Rafraîchir la liste de mods</value></data>
+  <data name="ModListUpgradeMenu" xml:space="preserve"><value>Tout mettre à niveau</value></data>
+  <data name="ModListUpgradeMenuTip" xml:space="preserve"><value>Marquer toutes les mises à jour prêtes à être installées</value></data>
+  <data name="ModListAuditRecsMenu" xml:space="preserve"><value>Auditionner les recommandations</value></data>
+  <data name="ModListAuditRecsMenuTip" xml:space="preserve"><value>Liste de tous les mods suggérés ou recommandés par les mods installés</value></data>
+  <data name="ModListImportMenu" xml:space="preserve"><value>Importer des téléchargements...</value></data>
+  <data name="ModListImportMenuTip" xml:space="preserve"><value>Sélectionnez des mods téléchargés manuellement à importer dans CKAN</value></data>
+  <data name="ModListExportMenu" xml:space="preserve"><value>Exporter les mods installés...</value></data>
+  <data name="ModListExportMenuTip" xml:space="preserve"><value>Sauvegarde de votre liste de mods installés</value></data>
+  <data name="ModListInstanceSettingsMenu" xml:space="preserve"><value>Paramètres de l'instance de jeu...</value></data>
+  <data name="ModListInstanceSettingsMenuTip" xml:space="preserve"><value>Configurer l'instance de jeu actuelle</value></data>
+  <data name="ModListSelectInstanceMenu" xml:space="preserve"><value>Sélectionner une instance de jeu...</value></data>
+  <data name="ModListSelectInstanceMenuTip" xml:space="preserve"><value>Passer à une instance de jeu différente</value></data>
+  <data name="ModListAuthTokenMenu" xml:space="preserve"><value>Jeton d'authentification...</value></data>
+  <data name="ModListAuthTokenMenuTip" xml:space="preserve"><value>Éditer les jetons d'authentication envoyés aux serveurs de téléchargements</value></data>
+  <data name="ModListHelpMenu" xml:space="preserve"><value>Aide</value></data>
+  <data name="ModListHelpMenuTip" xml:space="preserve"><value>Trucs &amp; astuces</value></data>
+  <data name="ModListQuitMenu" xml:space="preserve"><value>Quitter</value></data>
+  <data name="ModListQuitMenuTip" xml:space="preserve"><value>Sortir vers DOS</value></data>
+  <data name="ModListCaptureKeyMenu" xml:space="preserve"><value>DÉBOGAGE : Enregistrer les touches...</value></data>
+  <data name="ModListCaptureKeyMenuTip" xml:space="preserve"><value>Afficher les détails sur comment votre système signale les touches pressées, pour le débogage</value></data>
+  <data name="ModListPressAKey" xml:space="preserve"><value>Appuyez sur une touche</value></data>
+  <data name="ModListAuditNotFound" xml:space="preserve"><value>Les mods installés n'ont aucune recommandations ou suggestions supplémentaires.</value></data>
+  <data name="ModListUpdateRegistryTitle" xml:space="preserve"><value>Mise à Jour du Registre</value></data>
+  <data name="ModListUpdateRegistryMessage" xml:space="preserve"><value>Recherche de mises à jour</value></data>
+  <data name="ModListNewMod" xml:space="preserve"><value>{0} nouveau mod depuis la dernière mise à jour. L'afficher ?</value></data>
+  <data name="ModListNewMods" xml:space="preserve"><value>{0} nouveaux mods depuis la dernière mise à jour. Les afficher ?</value></data>
+  <data name="ModListScanBad" xml:space="preserve"><value>{0} Le répertoire n'a pas été sauvegardé.</value></data>
+  <data name="ModListExportPrefix" xml:space="preserve"><value>installer</value></data>
+  <data name="ModListExported" xml:space="preserve"><value>Liste de mods exportée vers {0}</value></data>
+  <data name="ModListExportFailed" xml:space="preserve"><value>Échec de l'export : {0}</value></data>
+  <data name="ModListHelpSymbolHeader" xml:space="preserve"><value>Symboles de statut</value></data>
+  <data name="ModListHelpInstalled" xml:space="preserve"><value>Installé</value></data>
+  <data name="ModListHelpAutoInstalled" xml:space="preserve"><value>Auto-installé</value></data>
+  <data name="ModListHelpUpgradeable" xml:space="preserve"><value>À mettre à jour</value></data>
+  <data name="ModListHelpManuallyInstalled" xml:space="preserve"><value>Manuellement installé</value></data>
+  <data name="ModListHelpReplaceable" xml:space="preserve"><value>Remplaçable</value></data>
+  <data name="ModListHelpUnavailable" xml:space="preserve"><value>Indisponible</value></data>
+  <data name="ModListHelpBasicKeysHeader" xml:space="preserve"><value>Touches de base</value></data>
+  <data name="ModListHelpMoveFocus" xml:space="preserve"><value>Changer l'attention</value></data>
+  <data name="ModListHelpSelectRow" xml:space="preserve"><value>Sélectionner la colonne</value></data>
+  <data name="ModListHelpClearSearch" xml:space="preserve"><value>Effacer la recherche</value></data>
+  <data name="ModListHelpSpecialSearchesHeader" xml:space="preserve"><value>Recherches Spéciales</value></data>
+  <data name="ModListHelpAuthor" xml:space="preserve"><value>auteur</value></data>
+  <data name="ModListHelpName" xml:space="preserve"><value>nom</value></data>
+  <data name="ModListHelpSearchAuthor" xml:space="preserve"><value>Mods par auteur</value></data>
+  <data name="ModListHelpSearchInstalled" xml:space="preserve"><value>Mods installés</value></data>
+  <data name="ModListHelpSearchUpgradeable" xml:space="preserve"><value>Mods à mettre à jour</value></data>
+  <data name="ModListHelpSearchDepends" xml:space="preserve"><value>Dépend de nom</value></data>
+  <data name="ModListHelpSearchConflicts" xml:space="preserve"><value>Conflit avec nom</value></data>
+  <data name="ModListHelpSearchNew" xml:space="preserve"><value>Nouveaux mods</value></data>
+  <data name="ExitTitle" xml:space="preserve"><value>{0}, le Réseau Compréhensif des Archives Kerbals</value></data>
+  <data name="ExitBody" xml:space="preserve"><value>Vous utilisez {1}.
+
+Merci d'avoir téléchargé {0}. Nous espérons que vous avez eu autant
+de plaisir à l'utiliser que nous en avons eu (et avons) à le développer.
+
+Si vous avez payé pour {0}, essayez d'en avoir pour votre argent,
+car vous pouvez télécharger {0} gratuitement sur
+{2}
+
+Si vous rencontrez des problèmes en utilisant {0}, veuillez nous le signaler sur
+{3}
+
+{0} A ÉTÉ CRÉÉ PAR LES AUTEURS DE {0} :
+{4}
+</value></data>
+</root>
diff --git a/ConsoleUI/Properties/Resources.resx b/ConsoleUI/Properties/Resources.resx
new file mode 100644
index 0000000000..80c1d6628b
--- /dev/null
+++ b/ConsoleUI/Properties/Resources.resx
@@ -0,0 +1,406 @@
+<?xml version="1.0" encoding="utf-8"?>
+<root>
+  <!--
+    Microsoft ResX Schema
+
+    Version 2.0
+
+    The primary goals of this format is to allow a simple XML format
+    that is mostly human readable. The generation and parsing of the
+    various data types are done through the TypeConverter classes
+    associated with the data types.
+
+    Example:
+
+    ... ado.net/XML headers & schema ...
+    <resheader name="resmimetype">text/microsoft-resx</resheader>
+    <resheader name="version">2.0</resheader>
+    <resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
+    <resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
+    <data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
+    <data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
+    <data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
+        <value>[base64 mime encoded serialized .NET Framework object]</value>
+    </data>
+    <data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
+        <value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
+        <comment>This is a comment</comment>
+    </data>
+
+    There are any number of "resheader" rows that contain simple
+    name/value pairs.
+
+    Each data row contains a name, and value. The row also contains a
+    type or mimetype. Type corresponds to a .NET class that support
+    text/value conversion through the TypeConverter architecture.
+    Classes that don't support this are serialized and stored with the
+    mimetype set.
+
+    The mimetype is used for serialized objects, and tells the
+    ResXResourceReader how to depersist the object. This is currently not
+    extensible. For a given mimetype the value must be set accordingly:
+
+    Note - application/x-microsoft.net.object.binary.base64 is the format
+    that the ResXResourceWriter will generate, however the reader can
+    read any of the formats listed below.
+
+    mimetype: application/x-microsoft.net.object.binary.base64
+    value   : The object must be serialized with
+            : System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
+            : and then encoded with base64 encoding.
+
+    mimetype: application/x-microsoft.net.object.soap.base64
+    value   : The object must be serialized with
+            : System.Runtime.Serialization.Formatters.Soap.SoapFormatter
+            : and then encoded with base64 encoding.
+
+    mimetype: application/x-microsoft.net.object.bytearray.base64
+    value   : The object must be serialized into a byte array
+            : using a System.ComponentModel.TypeConverter
+            : and then encoded with base64 encoding.
+    -->
+  <xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
+    <xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
+    <xsd:element name="root" msdata:IsDataSet="true">
+      <xsd:complexType>
+        <xsd:choice maxOccurs="unbounded">
+          <xsd:element name="metadata">
+            <xsd:complexType>
+              <xsd:sequence>
+                <xsd:element name="value" type="xsd:string" minOccurs="0" />
+              </xsd:sequence>
+              <xsd:attribute name="name" use="required" type="xsd:string" />
+              <xsd:attribute name="type" type="xsd:string" />
+              <xsd:attribute name="mimetype" type="xsd:string" />
+              <xsd:attribute ref="xml:space" />
+            </xsd:complexType>
+          </xsd:element>
+          <xsd:element name="assembly">
+            <xsd:complexType>
+              <xsd:attribute name="alias" type="xsd:string" />
+              <xsd:attribute name="name" type="xsd:string" />
+            </xsd:complexType>
+          </xsd:element>
+          <xsd:element name="data">
+            <xsd:complexType>
+              <xsd:sequence>
+                <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
+                <xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
+              </xsd:sequence>
+              <xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
+              <xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
+              <xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
+              <xsd:attribute ref="xml:space" />
+            </xsd:complexType>
+          </xsd:element>
+          <xsd:element name="resheader">
+            <xsd:complexType>
+              <xsd:sequence>
+                <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
+              </xsd:sequence>
+              <xsd:attribute name="name" type="xsd:string" use="required" />
+            </xsd:complexType>
+          </xsd:element>
+        </xsd:choice>
+      </xsd:complexType>
+    </xsd:element>
+  </xsd:schema>
+  <resheader name="resmimetype">
+    <value>text/microsoft-resx</value>
+  </resheader>
+  <resheader name="version">
+    <value>2.0</value>
+  </resheader>
+  <resheader name="reader">
+    <value>System.Resources.ResXResourceReader, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
+  </resheader>
+  <resheader name="writer">
+    <value>System.Resources.ResXResourceWriter, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
+  </resheader>
+  <data name="Accept" xml:space="preserve"><value>Accept</value></data>
+  <data name="Cancel" xml:space="preserve"><value>Cancel</value></data>
+  <data name="DefaultGhostText" xml:space="preserve"><value>&lt;Enter a value&gt;</value></data>
+  <data name="Ascending" xml:space="preserve"><value>Ascending</value></data>
+  <data name="AscendingSortTip" xml:space="preserve"><value>Sort the list in ascending order</value></data>
+  <data name="Descending" xml:space="preserve"><value>Descending</value></data>
+  <data name="DescendingSortTip" xml:space="preserve"><value>Sort the list in descending order</value></data>
+  <data name="ColumnNumber" xml:space="preserve"><value>Column #{0}</value></data>
+  <data name="ColumnNumberSortTip" xml:space="preserve"><value>Sort the list by column #{0}</value></data>
+  <data name="ColumnNameSortTip" xml:space="preserve"><value>Sort the list by the {0} column</value></data>
+  <data name="CursorKeys" xml:space="preserve"><value>Cursor keys</value></data>
+  <data name="Scroll" xml:space="preserve"><value>Scroll</value></data>
+  <data name="ScrollMessages" xml:space="preserve"><value>Scroll Messages</value></data>
+  <data name="Menu" xml:space="preserve"><value>Menu</value></data>
+  <data name="Yes" xml:space="preserve"><value>Yes</value></data>
+  <data name="No" xml:space="preserve"><value>No</value></data>
+  <data name="OK" xml:space="preserve"><value>OK</value></data>
+  <data name="Force" xml:space="preserve"><value>Force</value></data>
+  <data name="Tab" xml:space="preserve"><value>Tab</value></data>
+  <data name="Enter" xml:space="preserve"><value>Enter</value></data>
+  <data name="Esc" xml:space="preserve"><value>Esc</value></data>
+  <data name="Ctrl" xml:space="preserve"><value>Ctrl</value></data>
+  <data name="Alt" xml:space="preserve"><value>Alt</value></data>
+  <data name="Back" xml:space="preserve"><value>Back</value></data>
+  <data name="Quit" xml:space="preserve"><value>Quit</value></data>
+  <data name="Add" xml:space="preserve"><value>Add</value></data>
+  <data name="Remove" xml:space="preserve"><value>Remove</value></data>
+  <data name="Edit" xml:space="preserve"><value>Edit</value></data>
+  <data name="Sort" xml:space="preserve"><value>Sort</value></data>
+  <data name="Toggle" xml:space="preserve"><value>Toggle</value></data>
+  <data name="Select" xml:space="preserve"><value>Select</value></data>
+  <data name="SelectAll" xml:space="preserve"><value>Select all</value></data>
+  <data name="DeselectAll" xml:space="preserve"><value>Deselect all</value></data>
+  <data name="Details" xml:space="preserve"><value>Details</value></data>
+  <data name="Up" xml:space="preserve"><value>Up</value></data>
+  <data name="Down" xml:space="preserve"><value>Down</value></data>
+  <data name="FileSelectDirectory" xml:space="preserve"><value>Directory</value></data>
+  <data name="FileSelectCountFooter" xml:space="preserve"><value>{0} selected, {1}</value></data>
+  <data name="FileSelectNameHeader" xml:space="preserve"><value>Name</value></data>
+  <data name="FileSelectSizeHeader" xml:space="preserve"><value>Size</value></data>
+  <data name="FileSelectTimestampHeader" xml:space="preserve"><value>Accessed</value></data>
+  <data name="FileSelectChangeDirectory" xml:space="preserve"><value>Change directory</value></data>
+  <data name="FileSelectSelect" xml:space="preserve"><value>Select</value></data>
+  <data name="FileSelectImport" xml:space="preserve"><value>Import</value></data>
+  <data name="FileSelectDirSize" xml:space="preserve"><value>&lt;DIR&gt;</value></data>
+  <data name="AuthTokenListTitle" xml:space="preserve"><value>Authentication Tokens</value></data>
+  <data name="AuthTokenListGitHubLink" xml:space="preserve"><value>Make a GitHub API token</value></data>
+  <data name="AuthTokenListGitHubLinkTip" xml:space="preserve"><value>Open the web page for creating GitHub API authentication tokens</value></data>
+  <data name="AuthTokenListLabel" xml:space="preserve"><value>Authentication tokens for downloads:</value></data>
+  <data name="AuthTokenListHostHeader" xml:space="preserve"><value>Host</value></data>
+  <data name="AuthTokenListTokenHeader" xml:space="preserve"><value>Token</value></data>
+  <data name="AuthTokenListWarning" xml:space="preserve"><value>NOTE: These values are private! Do not share screenshots of this screen!</value></data>
+  <data name="AuthTokenListMissingToken" xml:space="preserve"><value>&lt;ERROR&gt;</value></data>
+  <data name="AuthTokenAddTitle" xml:space="preserve"><value>Create Authentication Key</value></data>
+  <data name="AuthTokenAddHost" xml:space="preserve"><value>Host:</value></data>
+  <data name="AuthTokenAddHostGhostText" xml:space="preserve"><value>&lt;Enter a host name&gt;</value></data>
+  <data name="AuthTokenAddToken" xml:space="preserve"><value>Token:</value></data>
+  <data name="AuthTokenAddTokenGhostText" xml:space="preserve"><value>&lt;Enter an authentication token&gt;</value></data>
+  <data name="CompatibleVersionsTitle" xml:space="preserve"><value>Select Compatible Version</value></data>
+  <data name="CompatibleVersionsListHeader" xml:space="preserve"><value>Predefined Version</value></data>
+  <data name="CompatibleVersionsListAcceptTip" xml:space="preserve"><value>Select version</value></data>
+  <data name="CompatibleVersionsGhostText" xml:space="preserve"><value>&lt;Enter a version&gt;</value></data>
+  <data name="CompatibleVersionsEntryAcceptTip" xml:space="preserve"><value>Accept value</value></data>
+  <data name="ThemeNotFound" xml:space="preserve"><value>No such theme: {0}</value></data>
+  <data name="ThemeList" xml:space="preserve"><value>Available themes: {0}</value></data>
+  <data name="RecommendationsLabel" xml:space="preserve"><value>Additional mods are recommended or suggested:</value></data>
+  <data name="RecommendationsInstallHeader" xml:space="preserve"><value>Install</value></data>
+  <data name="RecommendationsNameHeader" xml:space="preserve"><value>Name</value></data>
+  <data name="RecommendationsSourcesHeader" xml:space="preserve"><value>Sources</value></data>
+  <data name="RecommendationsTitle" xml:space="preserve"><value>Recommendations &amp; Suggestions</value></data>
+  <data name="ImportSelectTitle" xml:space="preserve"><value>Import Downloads</value></data>
+  <data name="ImportSelectHeader" xml:space="preserve"><value>Import</value></data>
+  <data name="ImportProgressTitle" xml:space="preserve"><value>Importing Downloads</value></data>
+  <data name="ImportProgressMessage" xml:space="preserve"><value>Calculating...</value></data>
+  <data name="InstanceNameLabel" xml:space="preserve"><value>Name:</value></data>
+  <data name="InstanceNameGhostText" xml:space="preserve"><value>&lt;Enter the name to use for this game instance&gt;</value></data>
+  <data name="InstancePathGhostText" xml:space="preserve"><value>&lt;Enter the location of this game instance on disk&gt;</value></data>
+  <data name="InstancePathLabel" xml:space="preserve"><value>Path to game instance:</value></data>
+  <data name="InstanceNameEmptyError" xml:space="preserve"><value>Name cannot be empty!</value></data>
+  <data name="InstanceNameDuplicateError" xml:space="preserve"><value>{0} already exists!</value></data>
+  <data name="InstancePathNotGameFolderError" xml:space="preserve"><value>Path does not correspond to a game folder!</value></data>
+  <data name="InstanceAddExample" xml:space="preserve"><value>Example: {0}</value></data>
+  <data name="InstanceAddTitle" xml:space="preserve"><value>Add Game Instance</value></data>
+  <data name="InstanceEditTitle" xml:space="preserve"><value>Edit Game Instance</value></data>
+  <data name="InstanceEditRepoFrameTitle" xml:space="preserve"><value>Mod List Sources</value></data>
+  <data name="InstanceEditCompatFrameTitle" xml:space="preserve"><value>Additional Compatible Versions</value></data>
+  <data name="InstanceEditRepoIndexHeader" xml:space="preserve"><value>Index</value></data>
+  <data name="InstanceEditRepoNameHeader" xml:space="preserve"><value>Name</value></data>
+  <data name="InstanceEditRepoURLHeader" xml:space="preserve"><value>URL</value></data>
+  <data name="InstanceEditCompatVersionHeader" xml:space="preserve"><value>Version</value></data>
+  <data name="InstanceEditRegistryParseError" xml:space="preserve"><value>Failed to extract mod list sources from {0}.</value></data>
+  <data name="InstanceListTitle" xml:space="preserve"><value>Game Instances</value></data>
+  <data name="InstanceListLabel" xml:space="preserve"><value>Select or add a game instance:</value></data>
+  <data name="InstanceListDefaultHeader" xml:space="preserve"><value>Default</value></data>
+  <data name="InstanceListNameHeader" xml:space="preserve"><value>Name</value></data>
+  <data name="InstanceListGameHeader" xml:space="preserve"><value>Game</value></data>
+  <data name="InstanceListVersionHeader" xml:space="preserve"><value>Version</value></data>
+  <data name="InstanceListPathHeader" xml:space="preserve"><value>Path</value></data>
+  <data name="InstanceListLoadingInstance" xml:space="preserve"><value>Loading instance {0}...</value></data>
+  <data name="InstanceListDefaultToggle" xml:space="preserve"><value>Default</value></data>
+  <data name="InstanceListLoadingError" xml:space="preserve"><value>Error loading {0}:
+{1}</value></data>
+  <data name="InstanceListLocked" xml:space="preserve"><value>Lock file with live process ID found at {0}
+
+This means that another instance of CKAN probably is accessing this instance. You can delete the file to continue, but data corruption is very likely.
+
+Do you want to delete this lock file to force access?</value></data>
+  <data name="InstanceListNoVersion" xml:space="preserve"><value>&lt;NONE&gt;</value></data>
+  <data name="InstallTitle" xml:space="preserve"><value>Installing, Upgrading, and Removing Mods</value></data>
+  <data name="InstallMessage" xml:space="preserve"><value>Calculating...</value></data>
+  <data name="InstallOwnedFileConflict" xml:space="preserve"><value>{0} tried to install {1}, but {2} has already installed it.
+Please report this problem at https://github.com/KSP-CKAN/NetKAN/issues/new/choose</value></data>
+  <data name="InstallUnownedFileConflict" xml:space="preserve"><value>{0} tried to install {1}, but it is already installed.
+Please manually uninstall the mod that owns this file to install {2}.</value></data>
+  <data name="InstallFilesReverted" xml:space="preserve"><value>Game files reverted.</value></data>
+  <data name="InstallAuthTokenPrompt" xml:space="preserve"><value>{0}
+
+Edit authentication tokens now?</value></data>
+  <data name="InstallTooManyModsNameHeader" xml:space="preserve"><value>Name</value></data>
+  <data name="InstallBadMetadata" xml:space="preserve"><value>Bad metadata detected for {0}: {1}</value></data>
+  <data name="InstallUnsatisfiedDependency" xml:space="preserve"><value>{0} requires {1}, but it is not listed in the index, or not available for your version of the game.
+{2}</value></data>
+  <data name="InstallModuleNotFound" xml:space="preserve"><value>Module {0} required but it is not listed in the index, or not available for your version of the game.
+{1}</value></data>
+  <data name="InstallNotInstalled" xml:space="preserve"><value>{0} is not installed, can't remove</value></data>
+  <data name="InstallModInstalled" xml:space="preserve"><value>{0} Successfully installed {1} {2}</value></data>
+  <data name="ModInfoTitle" xml:space="preserve"><value>Mod Details</value></data>
+  <data name="ModInfoMenuTip" xml:space="preserve"><value>Links</value></data>
+  <data name="ModInfoAuthors" xml:space="preserve"><value>By {0}</value></data>
+  <data name="ModInfoLicence" xml:space="preserve"><value>Licence:</value></data>
+  <data name="ModInfoDownload" xml:space="preserve"><value>Download:</value></data>
+  <data name="ModInfoDescriptionFrame" xml:space="preserve"><value>Description</value></data>
+  <data name="ModInfoUnavailableWarning" xml:space="preserve"><value>
+NOTE: This mod is installed but no longer available.
+If you uninstall it, CKAN will not be able to re-install it.</value></data>
+  <data name="ModInfoDownloadToCache" xml:space="preserve"><value>Download to cache (does not install)</value></data>
+  <data name="ModInfoHomePage" xml:space="preserve"><value>Home page</value></data>
+  <data name="ModInfoHomePageTip" xml:space="preserve"><value>Open the home page URL in a browser</value></data>
+  <data name="ModInfoRepository" xml:space="preserve"><value>Repository</value></data>
+  <data name="ModInfoRepositoryTip" xml:space="preserve"><value>Open the repository URL in a browser</value></data>
+  <data name="ModInfoBugtracker" xml:space="preserve"><value>Bugtracker</value></data>
+  <data name="ModInfoBugtrackerTip" xml:space="preserve"><value>Open the bug tracker URL in a browser</value></data>
+  <data name="ModInfoSpaceDock" xml:space="preserve"><value>SpaceDock</value></data>
+  <data name="ModInfoSpaceDockTip" xml:space="preserve"><value>Open the SpaceDock URL in a browser</value></data>
+  <data name="ModInfoCurse" xml:space="preserve"><value>Curse</value></data>
+  <data name="ModInfoCurseTip" xml:space="preserve"><value>Open the Curse URL in a browser</value></data>
+  <data name="ModInfoStore" xml:space="preserve"><value>Store</value></data>
+  <data name="ModInfoStoreTip" xml:space="preserve"><value>Open the Store URL in a browser</value></data>
+  <data name="ModInfoSteamStore" xml:space="preserve"><value>Steam Store</value></data>
+  <data name="ModInfoSteamStoreTip" xml:space="preserve"><value>Open the Steam Store URL in a browser</value></data>
+  <data name="ModInfoViewMetadata" xml:space="preserve"><value>DEBUG: View metadata</value></data>
+  <data name="ModInfoViewMetadataTip" xml:space="preserve"><value>Display the full registry data for this mod</value></data>
+  <data name="ModInfoViewMetadataTitle" xml:space="preserve"><value>{0} Metadata</value></data>
+  <data name="ModInfoURLLaunching" xml:space="preserve"><value>Launching...</value></data>
+  <data name="ModInfoDependenciesFrame" xml:space="preserve"><value>Dependencies</value></data>
+  <data name="ModInfoRequiredLabel" xml:space="preserve"><value>Required ({0}):</value></data>
+  <data name="ModInfoConflictsLabel" xml:space="preserve"><value>Conflicts ({0}):</value></data>
+  <data name="ModInfoReplacedBy" xml:space="preserve"><value>Replaced by {0}:</value></data>
+  <data name="ModInfoInstalledOn" xml:space="preserve"><value>Installed {0}</value></data>
+  <data name="ModInfoInstalledManually" xml:space="preserve"><value>Installed manually</value></data>
+  <data name="ModInfoLatestInstalledOn" xml:space="preserve"><value>Latest/Installed {0}</value></data>
+  <data name="ModInfoLatestInstalledManually" xml:space="preserve"><value>Latest/Installed manually</value></data>
+  <data name="ModInfoLatestVersion" xml:space="preserve"><value>Latest Version</value></data>
+  <data name="ModInfoOtherVersions" xml:space="preserve"><value>Other Versions</value></data>
+  <data name="ModInfoUnavailableInstalledOn" xml:space="preserve"><value>UNAVAILABLE/Installed {0}</value></data>
+  <data name="ModInfoUnavailableInstalledManually" xml:space="preserve"><value>UNAVAILABLE/Installed manually</value></data>
+  <data name="ModInfoCompatibleWith" xml:space="preserve"><value>Compatible with:</value></data>
+  <data name="ModInfoHostedOn" xml:space="preserve"><value>Hosted on {0}</value></data>
+  <data name="ModInfoReportBugsOn" xml:space="preserve"><value>Report bugs on {0}</value></data>
+  <data name="ModInfoRepositoryOn" xml:space="preserve"><value>Repository on {0}</value></data>
+  <data name="ModInfoHomePageOn" xml:space="preserve"><value>Home page on {0}</value></data>
+  <data name="ModInfoBuyFromKSPStore" xml:space="preserve"><value>Buy from KSP store</value></data>
+  <data name="ModInfoBuyFromSteamStore" xml:space="preserve"><value>Buy from Steam store</value></data>
+  <data name="ModInfoBuyFromKSPStoreOrSteamStore" xml:space="preserve"><value>Buy from KSP store or Steam store</value></data>
+  <data name="ModInfoDownloading" xml:space="preserve"><value>Downloading {0}</value></data>
+  <data name="ModInfoDownloadCorrupted" xml:space="preserve"><value>Download failed, file is corrupted</value></data>
+  <data name="ModInfoDownloadFailed" xml:space="preserve"><value>Download failed: {0}</value></data>
+  <data name="ProgressTitle" xml:space="preserve"><value>Progress</value></data>
+  <data name="ProgressMessages" xml:space="preserve"><value>Messages</value></data>
+  <data name="SplashLoading" xml:space="preserve"><value>Loading...</value></data>
+  <data name="SplashPressAnyKey" xml:space="preserve"><value>Press any key to continue</value></data>
+  <data name="RepoNameLabel" xml:space="preserve"><value>Name:</value></data>
+  <data name="RepoNameGhostText" xml:space="preserve"><value>&lt;Enter the name to use for this repository&gt;</value></data>
+  <data name="RepoURLLabel" xml:space="preserve"><value>URL:</value></data>
+  <data name="RepoURLGhostText" xml:space="preserve"><value>&lt;Enter the URL of this repository&gt;</value></data>
+  <data name="RepoImportTip" xml:space="preserve"><value>Import values from default mod list source {0}</value></data>
+  <data name="RepoTitle" xml:space="preserve"><value>Edit Mod List Source</value></data>
+  <data name="RepoNameEmptyError" xml:space="preserve"><value>Name cannot be empty!</value></data>
+  <data name="RepoNameDuplicateError" xml:space="preserve"><value>{0} already exists!</value></data>
+  <data name="RepoURLEmptyError" xml:space="preserve"><value>URL cannot be empty!</value></data>
+  <data name="ModListNameHeader" xml:space="preserve"><value>Name</value></data>
+  <data name="ModListVersionHeader" xml:space="preserve"><value>Version</value></data>
+  <data name="ModListMaxGameVersionHeader" xml:space="preserve"><value>Max game version</value></data>
+  <data name="ModListSearchFocusedGhostText" xml:space="preserve"><value>&lt;Type to search&gt;</value></data>
+  <data name="ModListSearchUnfocusedGhostText" xml:space="preserve"><value>&lt;Ctrl+F to search&gt;</value></data>
+  <data name="ModListCount" xml:space="preserve"><value>{0} mods</value></data>
+  <data name="ModListSizeOnDisk" xml:space="preserve"><value>{0} installed</value></data>
+  <data name="ModListInstallTip" xml:space="preserve"><value>Install</value></data>
+  <data name="ModListUpgradeTip" xml:space="preserve"><value>Upgrade</value></data>
+  <data name="ModListReplaceTip" xml:space="preserve"><value>Replace</value></data>
+  <data name="ModListRemoveTip" xml:space="preserve"><value>Remove</value></data>
+  <data name="ModListAutoInstTip" xml:space="preserve"><value>Mark auto-installed</value></data>
+  <data name="ModListUserSelectedTip" xml:space="preserve"><value>Mark user-selected</value></data>
+  <data name="ModListApplyChangesTip" xml:space="preserve"><value>Apply changes</value></data>
+  <data name="ModListUpdatedDayAgo" xml:space="preserve"><value>Updated at least {0} day ago</value></data>
+  <data name="ModListUpdatedDaysAgo" xml:space="preserve"><value>Updated at least {0} days ago</value></data>
+  <data name="ModListSortMenu" xml:space="preserve"><value>Sort...</value></data>
+  <data name="ModListSortMenuTip" xml:space="preserve"><value>Change the sorting of the list of mods</value></data>
+  <data name="ModListRefreshMenu" xml:space="preserve"><value>Refresh mod list</value></data>
+  <data name="ModListRefreshMenuTip" xml:space="preserve"><value>Refresh the list of mods</value></data>
+  <data name="ModListUpgradeMenu" xml:space="preserve"><value>Upgrade all</value></data>
+  <data name="ModListUpgradeMenuTip" xml:space="preserve"><value>Mark all available updates for installation</value></data>
+  <data name="ModListAuditRecsMenu" xml:space="preserve"><value>Audit recommendations</value></data>
+  <data name="ModListAuditRecsMenuTip" xml:space="preserve"><value>List mods suggested and recommended by installed mods</value></data>
+  <data name="ModListImportMenu" xml:space="preserve"><value>Import downloads...</value></data>
+  <data name="ModListImportMenuTip" xml:space="preserve"><value>Select manually downloaded mods to import into CKAN</value></data>
+  <data name="ModListExportMenu" xml:space="preserve"><value>Export installed...</value></data>
+  <data name="ModListExportMenuTip" xml:space="preserve"><value>Save your mod list</value></data>
+  <data name="ModListInstanceSettingsMenu" xml:space="preserve"><value>Game instance settings...</value></data>
+  <data name="ModListInstanceSettingsMenuTip" xml:space="preserve"><value>Configure the current game instance</value></data>
+  <data name="ModListSelectInstanceMenu" xml:space="preserve"><value>Select game instance...</value></data>
+  <data name="ModListSelectInstanceMenuTip" xml:space="preserve"><value>Switch to a different game instance</value></data>
+  <data name="ModListAuthTokenMenu" xml:space="preserve"><value>Authentication tokens...</value></data>
+  <data name="ModListAuthTokenMenuTip" xml:space="preserve"><value>Edit authentication tokens sent to download servers</value></data>
+  <data name="ModListFilterMenu" xml:space="preserve"><value>Installation filters...</value></data>
+  <data name="ModListFilterMenuTip" xml:space="preserve"><value>Edit list of files and folders to exclude from installation</value></data>
+  <data name="ModListHelpMenu" xml:space="preserve"><value>Help</value></data>
+  <data name="ModListHelpMenuTip" xml:space="preserve"><value>Tips &amp; tricks</value></data>
+  <data name="ModListQuitMenu" xml:space="preserve"><value>Quit</value></data>
+  <data name="ModListQuitMenuTip" xml:space="preserve"><value>Exit to DOS</value></data>
+  <data name="ModListCaptureKeyMenu" xml:space="preserve"><value>DEBUG: Capture key...</value></data>
+  <data name="ModListCaptureKeyMenuTip" xml:space="preserve"><value>Print details of how your system reports a keystroke for debugging</value></data>
+  <data name="ModListPressAKey" xml:space="preserve"><value>Press a key</value></data>
+  <data name="ModListAuditNotFound" xml:space="preserve"><value>Installed mods have no unsatisfied recommendations or suggestions.</value></data>
+  <data name="ModListUpdateRegistryTitle" xml:space="preserve"><value>Updating Registry</value></data>
+  <data name="ModListUpdateRegistryMessage" xml:space="preserve"><value>Checking for updates</value></data>
+  <data name="ModListNewMod" xml:space="preserve"><value>{0} new mod available since last update. Show it?</value></data>
+  <data name="ModListNewMods" xml:space="preserve"><value>{0} new mods available since last update. Show them?</value></data>
+  <data name="ModListScanBad" xml:space="preserve"><value>{0} The repo has not been saved.</value></data>
+  <data name="ModListExportPrefix" xml:space="preserve"><value>install</value></data>
+  <data name="ModListExported" xml:space="preserve"><value>Mod list exported to {0}</value></data>
+  <data name="ModListExportFailed" xml:space="preserve"><value>Export failed: {0}</value></data>
+  <data name="ModListHelpSymbolHeader" xml:space="preserve"><value>Status Symbols</value></data>
+  <data name="ModListHelpInstalled" xml:space="preserve"><value>Installed</value></data>
+  <data name="ModListHelpAutoInstalled" xml:space="preserve"><value>Auto-installed</value></data>
+  <data name="ModListHelpUpgradeable" xml:space="preserve"><value>Upgradeable</value></data>
+  <data name="ModListHelpManuallyInstalled" xml:space="preserve"><value>Manually installed</value></data>
+  <data name="ModListHelpReplaceable" xml:space="preserve"><value>Replaceable</value></data>
+  <data name="ModListHelpUnavailable" xml:space="preserve"><value>Unavailable</value></data>
+  <data name="ModListHelpBasicKeysHeader" xml:space="preserve"><value>Basic Keys</value></data>
+  <data name="ModListHelpMoveFocus" xml:space="preserve"><value>Move focus</value></data>
+  <data name="ModListHelpSelectRow" xml:space="preserve"><value>Select row</value></data>
+  <data name="ModListHelpClearSearch" xml:space="preserve"><value>Clear search</value></data>
+  <data name="ModListHelpSpecialSearchesHeader" xml:space="preserve"><value>Special Searches</value></data>
+  <data name="ModListHelpAuthor" xml:space="preserve"><value>author</value></data>
+  <data name="ModListHelpName" xml:space="preserve"><value>name</value></data>
+  <data name="ModListHelpSearchAuthor" xml:space="preserve"><value>Mods by author</value></data>
+  <data name="ModListHelpSearchInstalled" xml:space="preserve"><value>Installed mods</value></data>
+  <data name="ModListHelpSearchUpgradeable" xml:space="preserve"><value>Upgradeable mods</value></data>
+  <data name="ModListHelpSearchDepends" xml:space="preserve"><value>Depend on name</value></data>
+  <data name="ModListHelpSearchConflicts" xml:space="preserve"><value>Conflict w/ name</value></data>
+  <data name="ModListHelpSearchNew" xml:space="preserve"><value>New mods</value></data>
+  <data name="FiltersTitle" xml:space="preserve"><value>Installation Filters</value></data>
+  <data name="FiltersAddMiniAVCMenu" xml:space="preserve"><value>Add MiniAVC</value></data>
+  <data name="FiltersAddMiniAVCMenuTip" xml:space="preserve"><value>Prevent MiniAVC from being installed</value></data>
+  <data name="FiltersGlobalHeader" xml:space="preserve"><value>Global Filters</value></data>
+  <data name="FiltersInstanceHeader" xml:space="preserve"><value>Instance Filters</value></data>
+  <data name="FilterAddGhostText" xml:space="preserve"><value>&lt;Enter a filter&gt;</value></data>
+  <data name="FilterAddAcceptTip" xml:space="preserve"><value>Accept value</value></data>
+  <data name="FilterAddTitle" xml:space="preserve"><value>Add Filter</value></data>
+  <data name="ExitTitle" xml:space="preserve"><value>{0}, the Comprehensive Kerbal Archive Network</value></data>
+  <data name="ExitBody" xml:space="preserve"><value>YOU ARE USING {1}.
+
+Thanks for downloading {0}. We hope you have as
+much fun using it as we had (and have) making it.
+
+If you have paid for {0}, try to get your money back,
+because you can download {0} for free from
+{2}
+
+If you have any problems using {0}, please send us an issue at
+{3}
+
+{0} WAS CREATED BY THE {0} AUTHORS:
+{4}
+</value></data>
+</root>
diff --git a/ConsoleUI/Properties/Resources.ru-RU.resx b/ConsoleUI/Properties/Resources.ru-RU.resx
new file mode 100644
index 0000000000..8a8b0185a6
--- /dev/null
+++ b/ConsoleUI/Properties/Resources.ru-RU.resx
@@ -0,0 +1,389 @@
+<?xml version="1.0" encoding="utf-8"?>
+<root>
+  <!--
+    Microsoft ResX Schema
+
+    Version 2.0
+
+    The primary goals of this format is to allow a simple XML format
+    that is mostly human readable. The generation and parsing of the
+    various data types are done through the TypeConverter classes
+    associated with the data types.
+
+    Example:
+
+    ... ado.net/XML headers & schema ...
+    <resheader name="resmimetype">text/microsoft-resx</resheader>
+    <resheader name="version">2.0</resheader>
+    <resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
+    <resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
+    <data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
+    <data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
+    <data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
+        <value>[base64 mime encoded serialized .NET Framework object]</value>
+    </data>
+    <data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
+        <value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
+        <comment>This is a comment</comment>
+    </data>
+
+    There are any number of "resheader" rows that contain simple
+    name/value pairs.
+
+    Each data row contains a name, and value. The row also contains a
+    type or mimetype. Type corresponds to a .NET class that support
+    text/value conversion through the TypeConverter architecture.
+    Classes that don't support this are serialized and stored with the
+    mimetype set.
+
+    The mimetype is used for serialized objects, and tells the
+    ResXResourceReader how to depersist the object. This is currently not
+    extensible. For a given mimetype the value must be set accordingly:
+
+    Note - application/x-microsoft.net.object.binary.base64 is the format
+    that the ResXResourceWriter will generate, however the reader can
+    read any of the formats listed below.
+
+    mimetype: application/x-microsoft.net.object.binary.base64
+    value   : The object must be serialized with
+            : System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
+            : and then encoded with base64 encoding.
+
+    mimetype: application/x-microsoft.net.object.soap.base64
+    value   : The object must be serialized with
+            : System.Runtime.Serialization.Formatters.Soap.SoapFormatter
+            : and then encoded with base64 encoding.
+
+    mimetype: application/x-microsoft.net.object.bytearray.base64
+    value   : The object must be serialized into a byte array
+            : using a System.ComponentModel.TypeConverter
+            : and then encoded with base64 encoding.
+    -->
+  <xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
+    <xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
+    <xsd:element name="root" msdata:IsDataSet="true">
+      <xsd:complexType>
+        <xsd:choice maxOccurs="unbounded">
+          <xsd:element name="metadata">
+            <xsd:complexType>
+              <xsd:sequence>
+                <xsd:element name="value" type="xsd:string" minOccurs="0" />
+              </xsd:sequence>
+              <xsd:attribute name="name" use="required" type="xsd:string" />
+              <xsd:attribute name="type" type="xsd:string" />
+              <xsd:attribute name="mimetype" type="xsd:string" />
+              <xsd:attribute ref="xml:space" />
+            </xsd:complexType>
+          </xsd:element>
+          <xsd:element name="assembly">
+            <xsd:complexType>
+              <xsd:attribute name="alias" type="xsd:string" />
+              <xsd:attribute name="name" type="xsd:string" />
+            </xsd:complexType>
+          </xsd:element>
+          <xsd:element name="data">
+            <xsd:complexType>
+              <xsd:sequence>
+                <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
+                <xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
+              </xsd:sequence>
+              <xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
+              <xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
+              <xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
+              <xsd:attribute ref="xml:space" />
+            </xsd:complexType>
+          </xsd:element>
+          <xsd:element name="resheader">
+            <xsd:complexType>
+              <xsd:sequence>
+                <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
+              </xsd:sequence>
+              <xsd:attribute name="name" type="xsd:string" use="required" />
+            </xsd:complexType>
+          </xsd:element>
+        </xsd:choice>
+      </xsd:complexType>
+    </xsd:element>
+  </xsd:schema>
+  <resheader name="resmimetype">
+    <value>text/microsoft-resx</value>
+  </resheader>
+  <resheader name="version">
+    <value>2.0</value>
+  </resheader>
+  <resheader name="reader">
+    <value>System.Resources.ResXResourceReader, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
+  </resheader>
+  <resheader name="writer">
+    <value>System.Resources.ResXResourceWriter, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
+  </resheader>
+  <data name="Accept" xml:space="preserve"><value>Принять</value></data>
+  <data name="Cancel" xml:space="preserve"><value>Отмена</value></data>
+  <data name="DefaultGhostText" xml:space="preserve"><value>&lt;Введите значение&gt;</value></data>
+  <data name="Ascending" xml:space="preserve"><value>По возрастанию</value></data>
+  <data name="AscendingSortTip" xml:space="preserve"><value>Сортировка списка по возрастанию</value></data>
+  <data name="Descending" xml:space="preserve"><value>По убыванию</value></data>
+  <data name="DescendingSortTip" xml:space="preserve"><value>Сортировка списка по убыванию</value></data>
+  <data name="ColumnNumber" xml:space="preserve"><value>Столбец №{0}</value></data>
+  <data name="ColumnNumberSortTip" xml:space="preserve"><value>Сортировка списка по столбцу №{0}</value></data>
+  <data name="ColumnNameSortTip" xml:space="preserve"><value>Сортировка списка по столбцу «{0}» </value></data>
+  <data name="CursorKeys" xml:space="preserve"><value>Стрелки</value></data>
+  <data name="Scroll" xml:space="preserve"><value>Прокрутка</value></data>
+  <data name="ScrollMessages" xml:space="preserve"><value>Прокрутка сообщений</value></data>
+  <data name="Menu" xml:space="preserve"><value>Меню</value></data>
+  <data name="Yes" xml:space="preserve"><value>Да</value></data>
+  <data name="No" xml:space="preserve"><value>Нет</value></data>
+  <data name="OK" xml:space="preserve"><value>OK</value></data>
+  <data name="Force" xml:space="preserve"><value>Принуд.</value></data>
+  <data name="Tab" xml:space="preserve"><value>Tab</value></data>
+  <data name="Enter" xml:space="preserve"><value>Enter</value></data>
+  <data name="Esc" xml:space="preserve"><value>Esc</value></data>
+  <data name="Ctrl" xml:space="preserve"><value>Ctrl</value></data>
+  <data name="Alt" xml:space="preserve"><value>Alt</value></data>
+  <data name="Back" xml:space="preserve"><value>Назад</value></data>
+  <data name="Quit" xml:space="preserve"><value>Выйти</value></data>
+  <data name="Add" xml:space="preserve"><value>Добавить</value></data>
+  <data name="Remove" xml:space="preserve"><value>Удалить</value></data>
+  <data name="Edit" xml:space="preserve"><value>Изменить</value></data>
+  <data name="Sort" xml:space="preserve"><value>Сортировка</value></data>
+  <data name="Toggle" xml:space="preserve"><value>Переключить</value></data>
+  <data name="Select" xml:space="preserve"><value>Выбрать</value></data>
+  <data name="SelectAll" xml:space="preserve"><value>Выбрать все</value></data>
+  <data name="DeselectAll" xml:space="preserve"><value>Снять выделение</value></data>
+  <data name="Details" xml:space="preserve"><value>Подробно</value></data>
+  <data name="Up" xml:space="preserve"><value>Вверх</value></data>
+  <data name="Down" xml:space="preserve"><value>Вниз</value></data>
+  <data name="AuthTokenListTitle" xml:space="preserve"><value>Токены аутентификации</value></data>
+  <data name="AuthTokenListGitHubLink" xml:space="preserve"><value>Создать токен GitHub API</value></data>
+  <data name="AuthTokenListGitHubLinkTip" xml:space="preserve"><value>Открытие страницы для создания токенов аутентификации GitHub API</value></data>
+  <data name="AuthTokenListLabel" xml:space="preserve"><value>Токены аутентификации для загрузок:</value></data>
+  <data name="AuthTokenListHostHeader" xml:space="preserve"><value>Хост</value></data>
+  <data name="AuthTokenListTokenHeader" xml:space="preserve"><value>Токен</value></data>
+  <data name="AuthTokenListWarning" xml:space="preserve"><value>ВНИМАНИЕ: Не делитесь ни с кем этими значениями!</value></data>
+  <data name="AuthTokenListMissingToken" xml:space="preserve"><value>&lt;ОШИБКА&gt;</value></data>
+  <data name="AuthTokenAddTitle" xml:space="preserve"><value>Создать ключ аутентификации</value></data>
+  <data name="AuthTokenAddHost" xml:space="preserve"><value>Хост:</value></data>
+  <data name="AuthTokenAddHostGhostText" xml:space="preserve"><value>&lt;Введите имя хоста&gt;</value></data>
+  <data name="AuthTokenAddToken" xml:space="preserve"><value>Токен:</value></data>
+  <data name="AuthTokenAddTokenGhostText" xml:space="preserve"><value>&lt;Введите токен аутентификации&gt;</value></data>
+  <data name="CompatibleVersionsTitle" xml:space="preserve"><value>Выбор совместимой версии</value></data>
+  <data name="CompatibleVersionsListHeader" xml:space="preserve"><value>Версия игры</value></data>
+  <data name="CompatibleVersionsListAcceptTip" xml:space="preserve"><value>Выбор версии</value></data>
+  <data name="CompatibleVersionsGhostText" xml:space="preserve"><value>&lt;Введите номер версии&gt;</value></data>
+  <data name="CompatibleVersionsEntryAcceptTip" xml:space="preserve"><value>Принять значеие</value></data>
+  <data name="ThemeNotFound" xml:space="preserve"><value>Тема «{0}» не существует</value></data>
+  <data name="ThemeList" xml:space="preserve"><value>Доступные темы: {0}</value></data>
+  <data name="RecommendationsLabel" xml:space="preserve"><value>Рекомендуются или предлагаются дополнительные модификации:</value></data>
+  <data name="RecommendationsInstallHeader" xml:space="preserve"><value>Уст.</value></data>
+  <data name="RecommendationsNameHeader" xml:space="preserve"><value>Название</value></data>
+  <data name="RecommendationsSourcesHeader" xml:space="preserve"><value>Источники</value></data>
+  <data name="RecommendationsTitle" xml:space="preserve"><value>Рекомендации и предложения</value></data>
+  <data name="ImportSelectTitle" xml:space="preserve"><value>Импорт загруженных модификаций</value></data>
+  <data name="ImportSelectHeader" xml:space="preserve"><value>Импорт</value></data>
+  <data name="ImportProgressTitle" xml:space="preserve"><value>Импортирование загрузок</value></data>
+  <data name="ImportProgressMessage" xml:space="preserve"><value>Вычисление...</value></data>
+  <data name="InstanceNameLabel" xml:space="preserve"><value>Название:</value></data>
+  <data name="InstanceNameGhostText" xml:space="preserve"><value>&lt;Введите название данной сборки&gt;</value></data>
+  <data name="InstancePathGhostText" xml:space="preserve"><value>&lt;Введите путь к сборке&gt;</value></data>
+  <data name="InstancePathLabel" xml:space="preserve"><value>Расположение сборки:</value></data>
+  <data name="InstanceNameEmptyError" xml:space="preserve"><value>Имя не может быть пустым!</value></data>
+  <data name="InstanceNameDuplicateError" xml:space="preserve"><value>Сборка «{0}» уже существует!</value></data>
+  <data name="InstancePathNotGameFolderError" xml:space="preserve"><value>Путь не является папкой с игрой!</value></data>
+  <data name="InstanceAddExample" xml:space="preserve"><value>Пример: {0}</value></data>
+  <data name="InstanceAddTitle" xml:space="preserve"><value>Добавить сборку игры</value></data>
+  <data name="InstanceEditTitle" xml:space="preserve"><value>Изменить сборку игры</value></data>
+  <data name="InstanceEditRepoFrameTitle" xml:space="preserve"><value>Источники списка модификаций</value></data>
+  <data name="InstanceEditCompatFrameTitle" xml:space="preserve"><value>Дополнительные совместимые версии игры</value></data>
+  <data name="InstanceEditRepoIndexHeader" xml:space="preserve"><value>Указ.</value></data>
+  <data name="InstanceEditRepoNameHeader" xml:space="preserve"><value>Название</value></data>
+  <data name="InstanceEditRepoURLHeader" xml:space="preserve"><value>URL</value></data>
+  <data name="InstanceEditCompatVersionHeader" xml:space="preserve"><value>Версия</value></data>
+  <data name="InstanceEditRegistryParseError" xml:space="preserve"><value>Не удалось извлечь источники списка модификаций из {0}.</value></data>
+  <data name="InstanceListTitle" xml:space="preserve"><value>Сборки игры</value></data>
+  <data name="InstanceListLabel" xml:space="preserve"><value>Выберите или добавьте сборку игры:</value></data>
+  <data name="InstanceListDefaultHeader" xml:space="preserve"><value>Умл.</value></data>
+  <data name="InstanceListNameHeader" xml:space="preserve"><value>Название</value></data>
+  <data name="InstanceListGameHeader" xml:space="preserve"><value>Игра</value></data>
+  <data name="InstanceListVersionHeader" xml:space="preserve"><value>Версия</value></data>
+  <data name="InstanceListPathHeader" xml:space="preserve"><value>Путь</value></data>
+  <data name="InstanceListLoadingInstance" xml:space="preserve"><value>Загрузка сборки «{0}»...</value></data>
+  <data name="InstanceListDefaultToggle" xml:space="preserve"><value>По умолчанию</value></data>
+  <data name="InstanceListLoadingError" xml:space="preserve"><value>Ошибка загрузки «{0}»:
+{1}</value></data>
+  <data name="InstanceListLocked" xml:space="preserve"><value>Блокирующий файл с ID процесса найден в {0}
+
+Это может означать, что другой процесс CKAN работает с этой сборкой. 
+Вы можете удалить этот файл, но в таком случае вероятно повреждение данных.
+
+Хотите ли вы удалить блокирующий файл для получения доступа?</value></data>
+  <data name="InstanceListNoVersion" xml:space="preserve"><value>&lt;НЕТ&gt;</value></data>
+  <data name="InstallTitle" xml:space="preserve"><value>Установка, обновление и удаление модификаций</value></data>
+  <data name="InstallMessage" xml:space="preserve"><value>Вычисление...</value></data>
+  <data name="InstallOwnedFileConflict" xml:space="preserve"><value>{0} пытался установить {1}, но {2} уже установил его.
+Пожалуйста, сообщите о проблеме по адресу https://github.com/KSP-CKAN/NetKAN/issues/new/choose</value></data>
+  <data name="InstallUnownedFileConflict" xml:space="preserve"><value>{0} пытался установить {1}, но он был уже установлен.
+Для установки {2} вручную удалите модификацию, которой принадлежит этот файл.</value></data>
+  <data name="InstallFilesReverted" xml:space="preserve"><value>Файлы игры возвращены.</value></data>
+  <data name="InstallAuthTokenPrompt" xml:space="preserve"><value>{0}
+
+Изменить токены аутентификации?</value></data>
+  <data name="InstallTooManyModsNameHeader" xml:space="preserve"><value>Название</value></data>
+  <data name="InstallBadMetadata" xml:space="preserve"><value>Найдены некорректные метаданные {0}: {1}</value></data>
+  <data name="InstallUnsatisfiedDependency" xml:space="preserve"><value>{0} требует {1}, но он отсутствует в указателе или недоступен для вашей версии игры.
+{2}</value></data>
+  <data name="InstallModuleNotFound" xml:space="preserve"><value>Требуется модуль {0}, но он отсутствует в указателе или недоступен для вашей версии игры.
+{1}</value></data>
+  <data name="InstallNotInstalled" xml:space="preserve"><value>{0} не установлен, невозможно удалить</value></data>
+  <data name="InstallModInstalled" xml:space="preserve"><value>{0} Успешно установлен {1} {2}</value></data>
+  <data name="ModInfoTitle" xml:space="preserve"><value>О модификации</value></data>
+  <data name="ModInfoMenuTip" xml:space="preserve"><value>Ссылки</value></data>
+  <data name="ModInfoAuthors" xml:space="preserve"><value>Автор: {0}</value></data>
+  <data name="ModInfoLicence" xml:space="preserve"><value>Лицензия:</value></data>
+  <data name="ModInfoDownload" xml:space="preserve"><value>Размер:</value></data>
+  <data name="ModInfoDescriptionFrame" xml:space="preserve"><value>Описание модификации</value></data>
+  <data name="ModInfoUnavailableWarning" xml:space="preserve"><value>
+ВНИМАНИЕ: Эта модификация установлена, но более не доступна.
+Если вы удалите её, CKAN не сможет её переустановить.</value></data>
+  <data name="ModInfoDownloadToCache" xml:space="preserve"><value>Загрузить в кэш (без установки)</value></data>
+  <data name="ModInfoHomePage" xml:space="preserve"><value>Страница</value></data>
+  <data name="ModInfoHomePageTip" xml:space="preserve"><value>Открыть домашнюю страницу в браузере</value></data>
+  <data name="ModInfoRepository" xml:space="preserve"><value>Репозиторий</value></data>
+  <data name="ModInfoRepositoryTip" xml:space="preserve"><value>Открыть страницу репозитория в браузере</value></data>
+  <data name="ModInfoBugtracker" xml:space="preserve"><value>Багтрекер</value></data>
+  <data name="ModInfoBugtrackerTip" xml:space="preserve"><value>Открыть страницу багтрекера в браузере</value></data>
+  <data name="ModInfoSpaceDock" xml:space="preserve"><value>SpaceDock</value></data>
+  <data name="ModInfoSpaceDockTip" xml:space="preserve"><value>Открыть страницу SpaceDock в браузере</value></data>
+  <data name="ModInfoCurse" xml:space="preserve"><value>Curse</value></data>
+  <data name="ModInfoCurseTip" xml:space="preserve"><value>Открыть страницу Curse в браузере</value></data>
+  <data name="ModInfoStore" xml:space="preserve"><value>Магазин</value></data>
+  <data name="ModInfoStoreTip" xml:space="preserve"><value>Открыть страницу магазина в браузере</value></data>
+  <data name="ModInfoSteamStore" xml:space="preserve"><value>Магазин Steam</value></data>
+  <data name="ModInfoSteamStoreTip" xml:space="preserve"><value>Открыть страницу магазина Steam в браузере</value></data>
+  <data name="ModInfoViewMetadata" xml:space="preserve"><value>ОТЛАДКА: Просмотр метаданных</value></data>
+  <data name="ModInfoViewMetadataTip" xml:space="preserve"><value>Просмотр всех данных реестра для этой модификации</value></data>
+  <data name="ModInfoViewMetadataTitle" xml:space="preserve"><value>Метаданные {0}</value></data>
+  <data name="ModInfoURLLaunching" xml:space="preserve"><value>Запуск...</value></data>
+  <data name="ModInfoDependenciesFrame" xml:space="preserve"><value>Зависимости</value></data>
+  <data name="ModInfoRequiredLabel" xml:space="preserve"><value>Требует ({0}):</value></data>
+  <data name="ModInfoConflictsLabel" xml:space="preserve"><value>Конфликтует ({0}):</value></data>
+  <data name="ModInfoReplacedBy" xml:space="preserve"><value>Заменяется {0}:</value></data>
+  <data name="ModInfoInstalledOn" xml:space="preserve"><value>Установлен {0}</value></data>
+  <data name="ModInfoInstalledManually" xml:space="preserve"><value>Установлен вручную</value></data>
+  <data name="ModInfoLatestInstalledOn" xml:space="preserve"><value>Последняя версия/Установлен {0}</value></data>
+  <data name="ModInfoLatestInstalledManually" xml:space="preserve"><value>Последняя версия/Установлен вручную</value></data>
+  <data name="ModInfoLatestVersion" xml:space="preserve"><value>Последняя версия</value></data>
+  <data name="ModInfoOtherVersions" xml:space="preserve"><value>Другие версии</value></data>
+  <data name="ModInfoUnavailableInstalledOn" xml:space="preserve"><value>НЕДОСТУПЕН/Установлен {0}</value></data>
+  <data name="ModInfoUnavailableInstalledManually" xml:space="preserve"><value>НЕДОСТУПЕН/Установлен вручную</value></data>
+  <data name="ModInfoCompatibleWith" xml:space="preserve"><value>Совместимо с:</value></data>
+  <data name="ModInfoHostedOn" xml:space="preserve"><value>Находится в {0}</value></data>
+  <data name="ModInfoReportBugsOn" xml:space="preserve"><value>Сообщать об ошибках в {0}</value></data>
+  <data name="ModInfoRepositoryOn" xml:space="preserve"><value>Репозиторий в {0}</value></data>
+  <data name="ModInfoHomePageOn" xml:space="preserve"><value>Домашняя страница в {0}</value></data>
+  <data name="ModInfoBuyFromKSPStore" xml:space="preserve"><value>Купить в магазине KSP</value></data>
+  <data name="ModInfoBuyFromSteamStore" xml:space="preserve"><value>Купить в магазине Steam</value></data>
+  <data name="ModInfoBuyFromKSPStoreOrSteamStore" xml:space="preserve"><value>Купить в магазине KSP или Steam</value></data>
+  <data name="ModInfoDownloading" xml:space="preserve"><value>Загрузка «{0}»</value></data>
+  <data name="ModInfoDownloadCorrupted" xml:space="preserve"><value>Загрузка не завершена, файл повреждён</value></data>
+  <data name="ModInfoDownloadFailed" xml:space="preserve"><value>Загрузка не завершена: {0}</value></data>
+  <data name="ProgressTitle" xml:space="preserve"><value>Выполнение операции</value></data>
+  <data name="ProgressMessages" xml:space="preserve"><value>Сообщения</value></data>
+  <data name="SplashLoading" xml:space="preserve"><value>Загрузка...</value></data>
+  <data name="SplashPressAnyKey" xml:space="preserve"><value>Для продолжения нажмите любую клавишу</value></data>
+  <data name="RepoNameLabel" xml:space="preserve"><value>Название:</value></data>
+  <data name="RepoNameGhostText" xml:space="preserve"><value>&lt;Введите имя для данного репозитория&gt;</value></data>
+  <data name="RepoURLLabel" xml:space="preserve"><value>URL:</value></data>
+  <data name="RepoURLGhostText" xml:space="preserve"><value>&lt;Введите адрес репозитория&gt;</value></data>
+  <data name="RepoImportTip" xml:space="preserve"><value>Импорт значений из источника списка модификаций по умолчанию «{0}»</value></data>
+  <data name="RepoTitle" xml:space="preserve"><value>Изменить источник списка модификаций</value></data>
+  <data name="RepoNameEmptyError" xml:space="preserve"><value>Имя не может быть пустым!</value></data>
+  <data name="RepoNameDuplicateError" xml:space="preserve"><value>Репозиторий «{0}» уже существует!</value></data>
+  <data name="RepoURLEmptyError" xml:space="preserve"><value>Адрес не может быть пустым!</value></data>
+  <data name="ModListNameHeader" xml:space="preserve"><value>Название</value></data>
+  <data name="ModListVersionHeader" xml:space="preserve"><value>Версия</value></data>
+  <data name="ModListMaxGameVersionHeader" xml:space="preserve"><value>Макс.вер.</value></data>
+  <data name="ModListSearchFocusedGhostText" xml:space="preserve"><value>&lt;Введите текст для поиска...&gt;</value></data>
+  <data name="ModListSearchUnfocusedGhostText" xml:space="preserve"><value>&lt;Нажмите Ctrl+F для поиска&gt;</value></data>
+  <data name="ModListCount" xml:space="preserve"><value>{0} модификаций</value></data>
+  <data name="ModListSizeOnDisk" xml:space="preserve"><value>Занимает {0}</value></data>
+  <data name="ModListInstallTip" xml:space="preserve"><value>Установить</value></data>
+  <data name="ModListUpgradeTip" xml:space="preserve"><value>Обновить</value></data>
+  <data name="ModListReplaceTip" xml:space="preserve"><value>Заменить</value></data>
+  <data name="ModListRemoveTip" xml:space="preserve"><value>Удалить</value></data>
+  <data name="ModListAutoInstTip" xml:space="preserve"><value>Отметить как автоустановленное</value></data>
+  <data name="ModListUserSelectedTip" xml:space="preserve"><value>Отметить как выбранное</value></data>
+  <data name="ModListApplyChangesTip" xml:space="preserve"><value>Применить изменения</value></data>
+  <data name="ModListUpdatedDayAgo" xml:space="preserve"><value>Обновлено как минимум {0} день назад</value></data>
+  <data name="ModListUpdatedDaysAgo" xml:space="preserve"><value>Обновлено как минимум {0} дней назад</value></data>
+  <data name="ModListSortMenu" xml:space="preserve"><value>Сортировка...</value></data>
+  <data name="ModListSortMenuTip" xml:space="preserve"><value>Изменение сортировки списка модификаций</value></data>
+  <data name="ModListRefreshMenu" xml:space="preserve"><value>Обновить список модификаций</value></data>
+  <data name="ModListRefreshMenuTip" xml:space="preserve"><value>Обновление списка модификаций</value></data>
+  <data name="ModListUpgradeMenu" xml:space="preserve"><value>Обновить все модификации</value></data>
+  <data name="ModListUpgradeMenuTip" xml:space="preserve"><value>Установка всех возможных обновлений</value></data>
+  <data name="ModListAuditRecsMenu" xml:space="preserve"><value>Проверить рекомендации</value></data>
+  <data name="ModListAuditRecsMenuTip" xml:space="preserve"><value>Открытие списка модификаций, рекомендованных установленными модификациями</value></data>
+  <data name="ModListImportMenu" xml:space="preserve"><value>Импортировать загруженные модификации...</value></data>
+  <data name="ModListImportMenuTip" xml:space="preserve"><value>Импорт загруженных вручную модификаций в CKAN</value></data>
+  <data name="ModListExportMenu" xml:space="preserve"><value>Экспортировать модификации...</value></data>
+  <data name="ModListExportMenuTip" xml:space="preserve"><value>Сохранение списка установленных модификаций на диск</value></data>
+  <data name="ModListInstanceSettingsMenu" xml:space="preserve"><value>Настройки сборки игры...</value></data>
+  <data name="ModListInstanceSettingsMenuTip" xml:space="preserve"><value>Конфигурация текущей сборки игры</value></data>
+  <data name="ModListSelectInstanceMenu" xml:space="preserve"><value>Выбор сборки игры...</value></data>
+  <data name="ModListSelectInstanceMenuTip" xml:space="preserve"><value>Переключение на другую сборку игры</value></data>
+  <data name="ModListAuthTokenMenu" xml:space="preserve"><value>Токены аутентификации...</value></data>
+  <data name="ModListAuthTokenMenuTip" xml:space="preserve"><value>Изменение токенов аутентификации, отправляемых на серверы загрузки</value></data>
+  <data name="ModListHelpMenu" xml:space="preserve"><value>Помощь</value></data>
+  <data name="ModListHelpMenuTip" xml:space="preserve"><value>Подсказки по использованию CKAN</value></data>
+  <data name="ModListQuitMenu" xml:space="preserve"><value>Выйти</value></data>
+  <data name="ModListQuitMenuTip" xml:space="preserve"><value>Выход из программы</value></data>
+  <data name="ModListCaptureKeyMenu" xml:space="preserve"><value>ОТЛАДКА: Захват нажатий...</value></data>
+  <data name="ModListCaptureKeyMenuTip" xml:space="preserve"><value>Отладочная информация о том, как система отображает нажатия клавиш</value></data>
+  <data name="ModListPressAKey" xml:space="preserve"><value>Нажмите клавишу</value></data>
+  <data name="ModListAuditNotFound" xml:space="preserve"><value>Нет рекомендаций от установленных модификаций.</value></data>
+  <data name="ModListUpdateRegistryTitle" xml:space="preserve"><value>Обновление реестра</value></data>
+  <data name="ModListUpdateRegistryMessage" xml:space="preserve"><value>Проверка обновлений</value></data>
+  <data name="ModListNewMod" xml:space="preserve"><value>С последнего обновления доступна {0} новая модификация. Показать?</value></data>
+  <data name="ModListNewMods" xml:space="preserve"><value>С последнего обновления доступны {0} новые модификации. Показать?</value></data>
+  <data name="ModListScanBad" xml:space="preserve"><value>{0} Репозиторий не сохранён.</value></data>
+  <data name="ModListExportPrefix" xml:space="preserve"><value>установленные</value></data>
+  <data name="ModListExported" xml:space="preserve"><value>Список модификаций экспортирован в {0}</value></data>
+  <data name="ModListExportFailed" xml:space="preserve"><value>Экспорт не удался: {0}</value></data>
+  <data name="ModListHelpSymbolHeader" xml:space="preserve"><value>Символы статуса</value></data>
+  <data name="ModListHelpInstalled" xml:space="preserve"><value>Установлен</value></data>
+  <data name="ModListHelpAutoInstalled" xml:space="preserve"><value>Автоустановлен</value></data>
+  <data name="ModListHelpUpgradeable" xml:space="preserve"><value>Возм. обновление</value></data>
+  <data name="ModListHelpManuallyInstalled" xml:space="preserve"><value>Уст. вручную</value></data>
+  <data name="ModListHelpReplaceable" xml:space="preserve"><value>Возможна замена</value></data>
+  <data name="ModListHelpUnavailable" xml:space="preserve"><value>Недоступен</value></data>
+  <data name="ModListHelpBasicKeysHeader" xml:space="preserve"><value>Основные клавиши</value></data>
+  <data name="ModListHelpMoveFocus" xml:space="preserve"><value>Переместить фокус</value></data>
+  <data name="ModListHelpSelectRow" xml:space="preserve"><value>Выбрать строку</value></data>
+  <data name="ModListHelpClearSearch" xml:space="preserve"><value>Очистить строку поиска</value></data>
+  <data name="ModListHelpSpecialSearchesHeader" xml:space="preserve"><value>Особые параметры поиска</value></data>
+  <data name="ModListHelpAuthor" xml:space="preserve"><value>автор</value></data>
+  <data name="ModListHelpName" xml:space="preserve"><value>имя</value></data>
+  <data name="ModListHelpSearchAuthor" xml:space="preserve"><value>Модификации автора</value></data>
+  <data name="ModListHelpSearchInstalled" xml:space="preserve"><value>Установленные мод-ии</value></data>
+  <data name="ModListHelpSearchUpgradeable" xml:space="preserve"><value>Мод. с возм. обновления</value></data>
+  <data name="ModListHelpSearchDepends" xml:space="preserve"><value>Зависящие от</value></data>
+  <data name="ModListHelpSearchConflicts" xml:space="preserve"><value>Конфликтующие с</value></data>
+  <data name="ModListHelpSearchNew" xml:space="preserve"><value>Новые модификации</value></data>
+  <data name="ExitTitle" xml:space="preserve"><value>{0}, Всекербальская сеть архивов</value></data>
+  <data name="ExitBody" xml:space="preserve"><value>ВЫ ИСПОЛЬЗУЕТЕ {1}.
+
+Спасибо за загрузку {0}. Надеемся, вам нравится
+использовать её так же, как нам её создавать.
+
+Если вы заплатили за {0}, попытайтесь вернуть деньги,
+так как её можно бесплатно загрузить с
+{2}
+
+Если у вас возникают проблемы с использованием {0},
+пожалуйста, отправьте отчёт об ошибке на
+{3}
+
+СОЗДАТЕЛИ {0}:
+{4}
+</value></data>
+</root>
diff --git a/ConsoleUI/Properties/Settings.Designer.cs b/ConsoleUI/Properties/Settings.Designer.cs
deleted file mode 100644
index 1312cb453b..0000000000
--- a/ConsoleUI/Properties/Settings.Designer.cs
+++ /dev/null
@@ -1,26 +0,0 @@
-//------------------------------------------------------------------------------
-// <auto-generated>
-//     This code was generated by a tool.
-//     Runtime Version:4.0.30319.42000
-//
-//     Changes to this file may cause incorrect behavior and will be lost if
-//     the code is regenerated.
-// </auto-generated>
-//------------------------------------------------------------------------------
-
-namespace CKAN.Properties {
-    
-    
-    [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
-    [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "15.3.0.0")]
-    internal sealed partial class Settings : global::System.Configuration.ApplicationSettingsBase {
-        
-        private static Settings defaultInstance = ((Settings)(global::System.Configuration.ApplicationSettingsBase.Synchronized(new Settings())));
-        
-        public static Settings Default {
-            get {
-                return defaultInstance;
-            }
-        }
-    }
-}
diff --git a/ConsoleUI/Properties/Settings.settings b/ConsoleUI/Properties/Settings.settings
deleted file mode 100644
index 8e615f25fd..0000000000
--- a/ConsoleUI/Properties/Settings.settings
+++ /dev/null
@@ -1,5 +0,0 @@
-<?xml version='1.0' encoding='utf-8'?>
-<SettingsFile xmlns="http://schemas.microsoft.com/VisualStudio/2004/01/settings" CurrentProfile="(Default)">
-  <Profiles />
-  <Settings />
-</SettingsFile>
\ No newline at end of file
diff --git a/ConsoleUI/RepoScreen.cs b/ConsoleUI/RepoScreen.cs
index 0317516d5e..5fb671cae6 100644
--- a/ConsoleUI/RepoScreen.cs
+++ b/ConsoleUI/RepoScreen.cs
@@ -23,18 +23,18 @@ protected RepoScreen(IGame game, SortedDictionary<string, Repository> reps, stri
             defaultRepos = RepositoryList.DefaultRepositories(game);
 
             name = new ConsoleField(labelWidth, nameRow, -1, initName) {
-                GhostText = () => "<Enter the name to use for this repository>"
+                GhostText = () => Properties.Resources.RepoNameGhostText
             };
             url  = new ConsoleField(labelWidth, urlRow,  -1, initUrl) {
-                GhostText = () => "<Enter the URL of this repository>"
+                GhostText = () => Properties.Resources.RepoURLGhostText
             };
 
-            AddObject(new ConsoleLabel(1, nameRow, labelWidth, () => "Name:"));
+            AddObject(new ConsoleLabel(1, nameRow, labelWidth, () => Properties.Resources.RepoNameLabel));
             AddObject(name);
-            AddObject(new ConsoleLabel(1, urlRow,  labelWidth, () => "URL:"));
+            AddObject(new ConsoleLabel(1, urlRow,  labelWidth, () => Properties.Resources.RepoURLLabel));
             AddObject(url);
 
-            AddTip("F2", "Accept");
+            AddTip("F2", Properties.Resources.Accept);
             AddBinding(Keys.F2, (object sender, ConsoleTheme theme) => {
                 if (Valid()) {
                     Save();
@@ -44,7 +44,7 @@ protected RepoScreen(IGame game, SortedDictionary<string, Repository> reps, stri
                 }
             });
 
-            AddTip("Esc", "Cancel");
+            AddTip(Properties.Resources.Esc, Properties.Resources.Cancel);
             AddBinding(Keys.Escape, (object sender, ConsoleTheme theme) => {
                 return false;
             });
@@ -56,7 +56,7 @@ protected RepoScreen(IGame game, SortedDictionary<string, Repository> reps, stri
                     // This variable will be remembered correctly in our lambdas later
                     Repository repo = r;
                     opts.Add(new ConsoleMenuOption(
-                        repo.name, "", $"Import values from default mod list source {repo.name}",
+                        repo.name, "", string.Format(Properties.Resources.RepoImportTip, repo.name),
                         true, (ConsoleTheme theme) => {
                             name.Value    = repo.name;
                             name.Position = name.Value.Length;
@@ -83,7 +83,7 @@ protected override string LeftHeader()
         /// </summary>
         protected override string CenterHeader()
         {
-            return "Edit Mod List Source";
+            return Properties.Resources.RepoTitle;
         }
 
         /// <summary>
@@ -108,11 +108,11 @@ protected override string CenterHeader()
         protected bool nameValid()
         {
             if (string.IsNullOrEmpty(name.Value)) {
-                RaiseError("Name cannot be empty!");
+                RaiseError(Properties.Resources.RepoNameEmptyError);
                 SetFocus(name);
                 return false;
             } else if (editList.ContainsKey(name.Value)) {
-                RaiseError($"{name.Value} already exists!");
+                RaiseError(Properties.Resources.RepoNameDuplicateError, name.Value);
                 SetFocus(name);
                 return false;
             } else {
@@ -131,7 +131,7 @@ protected bool nameValid()
         protected bool urlValid()
         {
             if (string.IsNullOrEmpty(url.Value)) {
-                RaiseError("URL cannot be empty!");
+                RaiseError(Properties.Resources.RepoURLEmptyError);
                 SetFocus(url);
                 return false;
             }
@@ -154,7 +154,10 @@ protected bool urlValid()
 
         private RepositoryList defaultRepos;
 
-        private const int labelWidth = 8;
+        private int labelWidth => Math.Max(8, Math.Max(
+            Properties.Resources.RepoNameLabel.Length,
+            Properties.Resources.RepoURLLabel.Length
+        ));
         private const int nameRow    = 3;
         private const int urlRow     = 5;
     }
diff --git a/ConsoleUI/SingleAssemblyResourceManager.cs b/ConsoleUI/SingleAssemblyResourceManager.cs
new file mode 100644
index 0000000000..3f512754b8
--- /dev/null
+++ b/ConsoleUI/SingleAssemblyResourceManager.cs
@@ -0,0 +1,58 @@
+using System.IO;
+using System.Globalization;
+using System.Resources;
+using System.Collections;
+using System.Reflection;
+using System.Collections.Generic;
+
+namespace CKAN.ConsoleUI
+{
+    // Thanks and credit to this guy: https://stackoverflow.com/q/1952638/2422988
+
+    class SingleAssemblyResourceManager : ResourceManager
+    {
+        public SingleAssemblyResourceManager(string basename, Assembly assembly) : base(basename, assembly)
+        {
+        }
+
+        protected override ResourceSet InternalGetResourceSet(CultureInfo culture,
+            bool createIfNotExists, bool tryParents)
+        {
+            ResourceSet rs;
+            if (!myResourceSets.TryGetValue(culture, out rs))
+            {
+                // Lazy-load default language (without caring about duplicate assignment in race conditions, no harm done)
+                if (neutralResourcesCulture == null)
+                {
+                    neutralResourcesCulture = GetNeutralResourcesLanguage(this.MainAssembly);
+                }
+
+                // If we're asking for the default language, then ask for the
+                // invariant (non-specific) resources.
+                if (neutralResourcesCulture.Equals(culture))
+                {
+                    culture = CultureInfo.InvariantCulture;
+                }
+                string resourceFileName = GetResourceFileName(culture);
+
+                Stream store = this.MainAssembly.GetManifestResourceStream(resourceFileName);
+
+                // If we found the appropriate resources in the local assembly
+                if (store != null)
+                {
+                    rs = new ResourceSet(store);
+                    // Save for later
+                    myResourceSets.Add(culture, rs);
+                }
+                else
+                {
+                    rs = base.InternalGetResourceSet(culture, createIfNotExists, tryParents);
+                }
+            }
+            return rs;
+        }
+
+        private CultureInfo neutralResourcesCulture;
+        private Dictionary<CultureInfo, ResourceSet> myResourceSets = new Dictionary<CultureInfo, ResourceSet>();
+    }
+}
diff --git a/ConsoleUI/SplashScreen.cs b/ConsoleUI/SplashScreen.cs
index a912cf65ee..0a747d4011 100644
--- a/ConsoleUI/SplashScreen.cs
+++ b/ConsoleUI/SplashScreen.cs
@@ -86,9 +86,9 @@ private void Draw(ConsoleTheme theme, bool pressAny = false)
             drawCentered(19, "https://github.com/KSP-CKAN/CKAN/graphs/contributors");
 
             if (pressAny) {
-                drawCentered(21, "Press any key to continue");
+                drawCentered(21, Properties.Resources.SplashPressAnyKey);
             } else {
-                drawCentered(21, "Loading...");
+                drawCentered(21, Properties.Resources.SplashLoading);
             }
         }
 
diff --git a/ConsoleUI/Toolkit/ConsoleChoiceDialog.cs b/ConsoleUI/Toolkit/ConsoleChoiceDialog.cs
index cafd0c12d2..a9f493629b 100644
--- a/ConsoleUI/Toolkit/ConsoleChoiceDialog.cs
+++ b/ConsoleUI/Toolkit/ConsoleChoiceDialog.cs
@@ -59,12 +59,12 @@ public ConsoleChoiceDialog(string m, string hdr, List<ChoiceT> c, Func<ChoiceT,
                 0, 0, ListSortDirection.Ascending
             );
 
-            choices.AddTip("Enter", "Accept");
+            choices.AddTip(Properties.Resources.Enter, Properties.Resources.Accept);
             choices.AddBinding(Keys.Enter, (object sender, ConsoleTheme theme) => {
                 return false;
             });
 
-            choices.AddTip("Esc", "Cancel");
+            choices.AddTip(Properties.Resources.Esc, Properties.Resources.Cancel);
             choices.AddBinding(Keys.Escape, (object sender, ConsoleTheme theme) => {
                 cancelled = true;
                 return false;
diff --git a/ConsoleUI/Toolkit/ConsoleField.cs b/ConsoleUI/Toolkit/ConsoleField.cs
index 33d1ce8263..9e13ab1cf3 100644
--- a/ConsoleUI/Toolkit/ConsoleField.cs
+++ b/ConsoleUI/Toolkit/ConsoleField.cs
@@ -27,7 +27,7 @@ public ConsoleField(int l, int t, int r, string val = "")
         /// <summary>
         /// Function returning text to show when field is empty
         /// </summary>
-        public Func<string> GhostText = () => "<Enter a value>";
+        public Func<string> GhostText = () => Properties.Resources.DefaultGhostText;
         /// <summary>
         /// Current value displayed in field
         /// </summary>
diff --git a/ConsoleUI/Toolkit/ConsoleFileMultiSelectDialog.cs b/ConsoleUI/Toolkit/ConsoleFileMultiSelectDialog.cs
index 255334fdea..1597e2eec9 100644
--- a/ConsoleUI/Toolkit/ConsoleFileMultiSelectDialog.cs
+++ b/ConsoleUI/Toolkit/ConsoleFileMultiSelectDialog.cs
@@ -34,7 +34,7 @@ public ConsoleFileMultiSelectDialog(string title, string startPath, string filPa
 
             AddObject(new ConsoleLabel(
                 left + 2, top + 2, left + 2 + labelW - 1,
-                () => $"Directory:",
+                () => Properties.Resources.FileSelectDirectory,
                 th => th.PopupBg,
                 th => th.PopupFg
             ));
@@ -48,7 +48,7 @@ public ConsoleFileMultiSelectDialog(string title, string startPath, string filPa
 
             AddObject(new ConsoleLabel(
                 left + 2, bottom - 1, right - 2,
-                () => $"{chosenFiles.Count} selected, {CkanModule.FmtSize(totalChosenSize())}",
+                () => string.Format(Properties.Resources.FileSelectCountFooter, chosenFiles.Count, CkanModule.FmtSize(totalChosenSize())),
                 th => th.PopupBg,
                 th => th.PopupFg
             ));
@@ -63,12 +63,12 @@ public ConsoleFileMultiSelectDialog(string title, string startPath, string filPa
                         Width    = 8,
                         Renderer = getRowSymbol
                     }, new ConsoleListBoxColumn<FileSystemInfo>() {
-                        Header   = "Name",
+                        Header   = Properties.Resources.FileSelectNameHeader,
                         Width    = 36,
                         Renderer = getRowName,
                         Comparer = compareNames
                     }, new ConsoleListBoxColumn<FileSystemInfo>() {
-                        Header   = "Size",
+                        Header   = Properties.Resources.FileSelectSizeHeader,
                         // Longest: "1023.1 KB"
                         Width    = 9,
                         Renderer = (FileSystemInfo fi) => getLength(fi),
@@ -79,7 +79,7 @@ public ConsoleFileMultiSelectDialog(string title, string startPath, string filPa
                                 : (fb == null ? 1 : fa.Length.CompareTo(fb.Length));
                         }
                     }, new ConsoleListBoxColumn<FileSystemInfo>() {
-                        Header   = "Accessed",
+                        Header   = Properties.Resources.FileSelectTimestampHeader,
                         Width    = 10,
                         Renderer = (FileSystemInfo fi) => fi.LastWriteTime.ToString("yyyy-MM-dd"),
                         Comparer = (a, b) => a.LastWriteTime.CompareTo(b.LastWriteTime)
@@ -89,25 +89,25 @@ public ConsoleFileMultiSelectDialog(string title, string startPath, string filPa
             );
             AddObject(fileList);
 
-            AddTip("Esc", "Cancel");
+            AddTip(Properties.Resources.Esc, Properties.Resources.Cancel);
             AddBinding(Keys.Escape, (object sender, ConsoleTheme theme) => {
                 chosenFiles.Clear();
                 return false;
             });
 
-            AddTip("F10", "Sort");
+            AddTip("F10", Properties.Resources.Sort);
             AddBinding(Keys.F10, (object sender, ConsoleTheme theme) => {
                 fileList.SortMenu().Run(theme, right - 2, top + 2);
                 DrawBackground(theme);
                 return true;
             });
 
-            AddTip("Enter", "Change directory", () => fileList.Selection != null &&  isDir(fileList.Selection));
-            AddTip("Enter", "Select",           () => fileList.Selection != null && !isDir(fileList.Selection));
+            AddTip(Properties.Resources.Enter, Properties.Resources.FileSelectChangeDirectory, () => fileList.Selection != null &&  isDir(fileList.Selection));
+            AddTip(Properties.Resources.Enter, Properties.Resources.FileSelectSelect,          () => fileList.Selection != null && !isDir(fileList.Selection));
             AddBinding(Keys.Enter, (object sender, ConsoleTheme theme) => selectRow());
             AddBinding(Keys.Space, (object sender, ConsoleTheme theme) => selectRow());
 
-            AddTip("Ctrl+A", "Select all");
+            AddTip($"{Properties.Resources.Ctrl}+A", Properties.Resources.SelectAll);
             AddBinding(Keys.CtrlA, (object sender, ConsoleTheme theme) => {
                 foreach (FileSystemInfo fi in contents) {
                     if (!isDir(fi)) {
@@ -120,7 +120,7 @@ public ConsoleFileMultiSelectDialog(string title, string startPath, string filPa
                 return true;
             });
 
-            AddTip("Ctrl+D", "Deselect all", () => chosenFiles.Count > 0);
+            AddTip($"{Properties.Resources.Ctrl}+D", Properties.Resources.DeselectAll, () => chosenFiles.Count > 0);
             AddBinding(Keys.CtrlD, (object sender, ConsoleTheme theme) => {
                 if (chosenFiles.Count > 0) {
                     chosenFiles.Clear();
@@ -128,7 +128,7 @@ public ConsoleFileMultiSelectDialog(string title, string startPath, string filPa
                 return true;
             });
 
-            AddTip("F9", "Import", () => chosenFiles.Count > 0);
+            AddTip("F9", Properties.Resources.FileSelectImport, () => chosenFiles.Count > 0);
             AddBinding(Keys.F9, (object sender, ConsoleTheme theme) => {
                 return false;
             });
@@ -228,13 +228,13 @@ private static bool pathEquals(FileSystemInfo a, FileSystemInfo b)
         private string getLength(FileSystemInfo fi)
         {
             if (isDir(fi)) {
-                return dirSize;
+                return Properties.Resources.FileSelectDirSize;
             } else {
                 FileInfo file = fi as FileInfo;
                 if (file != null) {
                     return CkanModule.FmtSize(file.Length);
                 } else {
-                    return dirSize;
+                    return Properties.Resources.FileSelectDirSize;
                 }
             }
         }
@@ -308,10 +308,9 @@ private int compareNames(FileSystemInfo a, FileSystemInfo b)
         private string filePattern;
 
         private static readonly string chosen  = Symbols.checkmark;
-        private const           string dirSize = "<DIR>";
 
         private const int idealW = 76;
-        private const int labelW = 12;
+        private int labelW => Properties.Resources.FileSelectDirectory.Length;
         private const int hPad   = 2;
         private const int top    =  2;
         private const int bottom = -2;
diff --git a/ConsoleUI/Toolkit/ConsoleListBox.cs b/ConsoleUI/Toolkit/ConsoleListBox.cs
index f7e29eccef..38c4b6d44c 100644
--- a/ConsoleUI/Toolkit/ConsoleListBox.cs
+++ b/ConsoleUI/Toolkit/ConsoleListBox.cs
@@ -294,18 +294,24 @@ public ConsolePopupMenu SortMenu()
             if (sortMenu == null) {
                 List<ConsoleMenuOption> opts = new List<ConsoleMenuOption>() {
                     new ConsoleMenuOption(
-                        "Ascending", "",
-                        "Sort the list in ascending order",
+                        Properties.Resources.Ascending, "",
+                        Properties.Resources.AscendingSortTip,
                         true,
-                        (ConsoleTheme theme) => { SortDirection  =  ListSortDirection.Ascending; return true; },
-                        () => { return sortDir == ListSortDirection.Ascending;              }
+                        (ConsoleTheme theme) => {
+                            SortDirection = ListSortDirection.Ascending;
+                            return true;
+                        },
+                        () => sortDir == ListSortDirection.Ascending
                     ),
                     new ConsoleMenuOption(
-                        "Descending", "",
-                        "Sort the list in descending order",
+                        Properties.Resources.Descending, "",
+                        Properties.Resources.DescendingSortTip,
                         true,
-                        (ConsoleTheme theme) => { SortDirection  =  ListSortDirection.Descending; return true;},
-                        () => { return sortDir == ListSortDirection.Descending;             }
+                        (ConsoleTheme theme) => {
+                            SortDirection = ListSortDirection.Descending;
+                            return true;
+                        },
+                        () => sortDir == ListSortDirection.Descending
                     ),
                     null
                 };
@@ -314,15 +320,18 @@ public ConsolePopupMenu SortMenu()
                     int newIndex = i;
                     opts.Add(new ConsoleMenuOption(
                         string.IsNullOrEmpty(columns[i].Header)
-                            ? $"Column #{i+1}"
+                            ? string.Format(Properties.Resources.ColumnNumber, i + 1)
                             : columns[i].Header,
                         "",
                         string.IsNullOrEmpty(columns[i].Header)
-                            ? $"Sort the list by column #{i+1}"
-                            : $"Sort the list by the {columns[i].Header} column",
+                            ? string.Format(Properties.Resources.ColumnNumberSortTip, i + 1)
+                            : string.Format(Properties.Resources.ColumnNameSortTip, columns[i].Header),
                         true,
-                        (ConsoleTheme theme) => { SortColumnIndex = newIndex; return true; },
-                        () => { return sortColIndex == newIndex;         }
+                        (ConsoleTheme theme) => {
+                            SortColumnIndex = newIndex;
+                            return true;
+                        },
+                        () => sortColIndex == newIndex
                     ));
                 }
                 sortMenu = new ConsolePopupMenu(opts);
diff --git a/ConsoleUI/Toolkit/ConsoleMessageDialog.cs b/ConsoleUI/Toolkit/ConsoleMessageDialog.cs
index ca8848b0ec..ef8126ce06 100644
--- a/ConsoleUI/Toolkit/ConsoleMessageDialog.cs
+++ b/ConsoleUI/Toolkit/ConsoleMessageDialog.cs
@@ -76,7 +76,7 @@ public ConsoleMessageDialog(string m, List<string> btns, Func<string> hdr = null
 
             if (messageLines.Count > boxH) {
                 // Scroll
-                AddTip("Cursor keys", "Scroll");
+                AddTip(Properties.Resources.CursorKeys, Properties.Resources.Scroll);
                 tb.AddScrollBindings(this);
             }
 
diff --git a/ConsoleUI/Toolkit/ConsoleScreen.cs b/ConsoleUI/Toolkit/ConsoleScreen.cs
index 04060f9524..7eeb1cf8e5 100644
--- a/ConsoleUI/Toolkit/ConsoleScreen.cs
+++ b/ConsoleUI/Toolkit/ConsoleScreen.cs
@@ -65,7 +65,7 @@ protected virtual string CenterHeader()
         /// </summary>
         protected virtual string MenuTip()
         {
-            return "Menu";
+            return Properties.Resources.Menu;
         }
 
         /// <summary>
@@ -94,7 +94,10 @@ public virtual bool RaiseYesNoDialog(string question)
         {
             ConsoleMessageDialog d = new ConsoleMessageDialog(
                 string.Join("", messagePieces) + question,
-                new List<string>() {"Yes", "No"}
+                new List<string>() {
+                    Properties.Resources.Yes,
+                    Properties.Resources.No
+                }
             );
             d.AddBinding(Keys.Y, (object sender, ConsoleTheme theme) => {
                 d.PressButton(0);
@@ -144,7 +147,7 @@ public void RaiseError(string message, params object[] args)
         {
             ConsoleMessageDialog d = new ConsoleMessageDialog(
                 string.Join("", messagePieces) + string.Format(message, args),
-                new List<string>() {"OK"}
+                new List<string>() { Properties.Resources.OK }
             );
             messagePieces.Clear();
             d.Run(userTheme);
@@ -267,7 +270,7 @@ private static string LeftCenterRight(string left, string center, string right,
         }
 
         private ConsoleTheme userTheme;
-        private static readonly string hamburger    = $" {Symbols.hamburger} ";
+        private static readonly string hamburger = $" {Symbols.hamburger} ";
     }
 
 }
diff --git a/Core/CKANPathUtils.cs b/Core/CKANPathUtils.cs
index 08ab99c28c..030b675cec 100644
--- a/Core/CKANPathUtils.cs
+++ b/Core/CKANPathUtils.cs
@@ -144,12 +144,14 @@ public static string ToRelative(string path, string root)
 
             if (!Path.IsPathRooted(path))
             {
-                throw new PathErrorKraken(path, $"{path} is not an absolute path");
+                throw new PathErrorKraken(path, string.Format(
+                    Properties.Resources.PathUtilsNotAbsolute, path));
             }
 
             if (!path.StartsWith(root, StringComparison.CurrentCultureIgnoreCase))
             {
-                throw new PathErrorKraken(path, $"Oh snap. {path} isn't inside {root}");
+                throw new PathErrorKraken(path, string.Format(
+                    Properties.Resources.PathUtilsNotInside, path, root));
             }
 
             // Strip off the root, then remove any slashes at the beginning
@@ -176,7 +178,7 @@ public static string ToAbsolute(string path, string root)
             {
                 throw new PathErrorKraken(
                     path,
-                    String.Format("{0} is already absolute", path)
+                    String.Format(Properties.Resources.PathUtilsAlreadyAbsolute, path)
                 );
             }
 
@@ -184,7 +186,7 @@ public static string ToAbsolute(string path, string root)
             {
                 throw new PathErrorKraken(
                     root,
-                    String.Format("{0} isn't an absolute root", root)
+                    String.Format(Properties.Resources.PathUtilsNotRoot, root)
                 );
             }
 
diff --git a/Core/Converters/JsonRelationshipConverter.cs b/Core/Converters/JsonRelationshipConverter.cs
index e66a5180e6..53182711dc 100644
--- a/Core/Converters/JsonRelationshipConverter.cs
+++ b/Core/Converters/JsonRelationshipConverter.cs
@@ -30,7 +30,8 @@ public override object ReadJson(JsonReader reader, Type objectType, object exist
                         {
                             if (child.Property(forbiddenPropertyName) != null)
                             {
-                                throw new Kraken($"`any_of` should not be combined with `{forbiddenPropertyName}`");
+                                throw new Kraken(string.Format(
+                                    Properties.Resources.JsonRelationshipConverterAnyOfCombined, forbiddenPropertyName));
                             }
                         }
                         rels.Add(child.ToObject<AnyOfRelationshipDescriptor>());
diff --git a/Core/GameInstance.cs b/Core/GameInstance.cs
index bbc00c8cf1..98d918c328 100644
--- a/Core/GameInstance.cs
+++ b/Core/GameInstance.cs
@@ -102,13 +102,13 @@ private void SetupCkanDirectories(bool scan = true)
 
             if (!Directory.Exists(CkanDir()))
             {
-                User.RaiseMessage("Setting up CKAN for the first time...");
-                User.RaiseMessage("Creating {0}", CkanDir());
+                User.RaiseMessage(Properties.Resources.GameInstanceSettingUp);
+                User.RaiseMessage(Properties.Resources.GameInstanceCreatingDir, CkanDir());
                 txFileMgr.CreateDirectory(CkanDir());
 
                 if (scan)
                 {
-                    User.RaiseMessage("Scanning for installed mods...");
+                    User.RaiseMessage(Properties.Resources.GameInstanceScanning);
                     Scan();
                 }
             }
@@ -117,7 +117,7 @@ private void SetupCkanDirectories(bool scan = true)
 
             if (!Directory.Exists(InstallHistoryDir()))
             {
-                User.RaiseMessage("Creating {0}", InstallHistoryDir());
+                User.RaiseMessage(Properties.Resources.GameInstanceCreatingDir, InstallHistoryDir());
                 txFileMgr.CreateDirectory(InstallHistoryDir());
             }
 
@@ -318,8 +318,8 @@ public string CkanDir()
         {
             if (!Valid)
             {
-                log.Error("Could not find KSP version");
-                throw new NotKSPDirKraken(gameDir, "Could not find KSP version in buildID.txt or readme.txt");
+                log.Error("Could not find game version");
+                throw new NotKSPDirKraken(gameDir, Properties.Resources.GameInstanceVersionNotFound);
             }
             return CKANPathUtils.NormalizePath(
                 Path.Combine(GameDir(), "CKAN"));
@@ -475,7 +475,7 @@ public string DllPathToIdentifier(string relative_path)
 
         public override string ToString()
         {
-            return $"{game.ShortName} Install: {gameDir}";
+            return string.Format(Properties.Resources.GameInstanceToString, game.ShortName, gameDir);
         }
 
         public bool Equals(GameInstance other)
diff --git a/Core/GameInstanceManager.cs b/Core/GameInstanceManager.cs
index b93e63793f..a0df024e20 100644
--- a/Core/GameInstanceManager.cs
+++ b/Core/GameInstanceManager.cs
@@ -107,7 +107,7 @@ internal GameInstance _GetPreferredInstance()
 
                 if (path != null)
                 {
-                    GameInstance portableInst = new GameInstance(game, path, "portable", User);
+                    GameInstance portableInst = new GameInstance(game, path, Properties.Resources.GameInstanceManagerPortable, User);
                     if (portableInst.Valid)
                     {
                         return portableInst;
@@ -156,7 +156,7 @@ public GameInstance FindAndRegisterDefaultInstance()
                 {
                     string gamedir = GameInstance.FindGameDir(game);
                     GameInstance foundInst = new GameInstance(
-                        game, gamedir, $"Auto {game.ShortName}", User);
+                        game, gamedir, string.Format(Properties.Resources.GameInstanceManagerAuto, game.ShortName), User);
                     if (foundInst.Valid)
                     {
                         var inst = AddInstance(foundInst);
@@ -230,7 +230,8 @@ public void CloneInstance(GameInstance existingInstance, string newName, string
             }
             if (!existingInstance.Valid)
             {
-                throw new NotKSPDirKraken(existingInstance.GameDir(), "The specified instance is not a valid KSP instance.");
+                throw new NotKSPDirKraken(existingInstance.GameDir(), string.Format(
+                    Properties.Resources.GameInstanceCloneInvalid, existingInstance.game.ShortName));
             }
 
             log.Debug("Copying directory.");
@@ -264,11 +265,12 @@ public void FakeInstance(IGame game, string newName, string newPath, GameVersion
 
                 if (!version.InBuildMap(game))
                 {
-                    throw new BadGameVersionKraken(String.Format("The specified KSP version is not a known version: {0}", version.ToString()));
+                    throw new BadGameVersionKraken(string.Format(
+                        Properties.Resources.GameInstanceFakeBadVersion, game.ShortName, version));
                 }
                 if (Directory.Exists(newPath) && (Directory.GetFiles(newPath).Length != 0 || Directory.GetDirectories(newPath).Length != 0))
                 {
-                    throw new BadInstallLocationKraken("The specified folder already exists and is not empty.");
+                    throw new BadInstallLocationKraken(Properties.Resources.GameInstanceFakeNotEmpty);
                 }
 
                 log.DebugFormat("Creating folder structure and text files at {0} for KSP version {1}", Path.GetFullPath(newPath), version.ToString());
@@ -307,7 +309,8 @@ public void FakeInstance(IGame game, string newName, string newPath, GameVersion
                         if (!dlcDetector.AllowedOnBaseVersion(version))
                             throw new WrongGameVersionKraken(
                                 version,
-                                String.Format("KSP version {0} or above is needed for {1} DLC.",
+                                string.Format(Properties.Resources.GameInstanceFakeDLCNotAllowed,
+                                    game.ShortName,
                                     dlcDetector.ReleaseGameVersion,
                                     dlcDetector.IdentifierBaseName
                             ));
@@ -335,13 +338,13 @@ public void FakeInstance(IGame game, string newName, string newPath, GameVersion
         /// <exception cref="CKAN.Kraken">Could not find a valid name.</exception>
         public string GetNextValidInstanceName(string name)
         {
-            // Check if the current name is valid.
+            // Check if the current name is valid
             if (InstanceNameIsValid(name))
             {
                 return name;
             }
 
-            // Try appending a number to the name.
+            // Try appending a number to the name
             var validName = Enumerable.Repeat(name, 1000)
                 .Select((s, i) => s + " (" + i + ")")
                 .FirstOrDefault(InstanceNameIsValid);
@@ -350,7 +353,7 @@ public string GetNextValidInstanceName(string name)
                 return validName;
             }
 
-            // Check if a name with the current timestamp is valid.
+            // Check if a name with the current timestamp is valid
             validName = name + " (" + DateTime.Now + ")";
 
             if (InstanceNameIsValid(validName))
@@ -358,8 +361,8 @@ public string GetNextValidInstanceName(string name)
                 return validName;
             }
 
-            // Give up.
-            throw new Kraken("Could not return a valid name for the new instance.");
+            // Give up
+            throw new Kraken(Properties.Resources.GameInstanceNoValidName);
         }
 
         /// <summary>
@@ -432,7 +435,7 @@ public void SetCurrentInstanceByPath(string path)
 
                 case 1:
                     GameInstance ksp = new GameInstance(
-                        matchingGames.First(), path, "custom", User);
+                        matchingGames.First(), path, Properties.Resources.GameInstanceByPathName, User);
                     if (ksp.Valid)
                     {
                         CurrentInstance = ksp;
@@ -461,7 +464,7 @@ public GameInstance InstanceAt(string path, string name)
 
                 case 1:
                     return new GameInstance(
-                        matchingGames.First(), path, "custom", User);
+                        matchingGames.First(), path, Properties.Resources.GameInstanceByPathName, User);
 
                 default:
                     // TODO: Prompt user to choose
@@ -572,7 +575,7 @@ public bool TrySetupCache(string path, out string failureReason)
             }
             catch (DirectoryNotFoundKraken)
             {
-                failureReason = $"{path} does not exist";
+                failureReason = string.Format(Properties.Resources.GameInstancePathNotFound, path);
                 return false;
             }
             catch (PathErrorKraken ex)
diff --git a/Core/ModuleInstaller.cs b/Core/ModuleInstaller.cs
index 324bd4337a..d07042cf5a 100644
--- a/Core/ModuleInstaller.cs
+++ b/Core/ModuleInstaller.cs
@@ -53,7 +53,7 @@ public ModuleInstaller(GameInstance ksp, NetModuleCache cache, IUser user)
         /// </summary>
         public string Download(CkanModule module, string filename)
         {
-            User.RaiseProgress(String.Format("Downloading \"{0}\"", module.download), 0);
+            User.RaiseProgress(string.Format(Properties.Resources.ModuleInstallerDownloading, module.download), 0);
             return Download(module, filename, Cache);
         }
 
@@ -74,7 +74,7 @@ public static string Download(CkanModule module, string filename, NetModuleCache
         /// and returns the downloaded copy otherwise.
         ///
         /// If no filename is provided, the module's standard name will be used.
-        /// Chcecks the CKAN cache first.
+        /// Checks the CKAN cache first.
         /// </summary>
         public string CachedOrDownload(CkanModule module, string filename = null)
         {
@@ -132,7 +132,7 @@ public void InstallList(ICollection<CkanModule> modules, RelationshipResolverOpt
             // TODO: Break this up into smaller pieces! It's huge!
             if (modules.Count == 0)
             {
-                User.RaiseProgress("Nothing to install.", 100);
+                User.RaiseProgress(Properties.Resources.ModuleInstallerNothingToInstall, 100);
                 return;
             }
             var resolver = new RelationshipResolver(modules, null, options, registry_manager.registry, ksp.VersionCriteria());
@@ -142,7 +142,8 @@ public void InstallList(ICollection<CkanModule> modules, RelationshipResolverOpt
             // TODO: All this user-stuff should be happening in another method!
             // We should just be installing mods as a transaction.
 
-            User.RaiseMessage("About to install:\r\n");
+            User.RaiseMessage(Properties.Resources.ModuleInstallerAboutToInstall);
+            User.RaiseMessage("");
 
             foreach (CkanModule module in modsToInstall)
             {
@@ -158,13 +159,13 @@ public void InstallList(ICollection<CkanModule> modules, RelationshipResolverOpt
                 }
                 else
                 {
-                    User.RaiseMessage(" * {0} {1} (cached)", module.name, module.version);
+                    User.RaiseMessage(Properties.Resources.ModuleInstallerModuleCached, module.name, module.version);
                 }
             }
 
             if (ConfirmPrompt && !User.RaiseYesNoDialog("Continue?"))
             {
-                throw new CancelledActionKraken("User declined install list");
+                throw new CancelledActionKraken(Properties.Resources.ModuleInstallerUserDeclined);
             }
 
             if (downloads.Count > 0)
@@ -185,17 +186,17 @@ public void InstallList(ICollection<CkanModule> modules, RelationshipResolverOpt
                     // The post-install steps start at 70%, so count up to 60% for installation
                     int percent_complete = (i * 60) / modsToInstall.Count;
 
-                    User.RaiseProgress(String.Format("Installing mod \"{0}\"", modsToInstall[i]),
+                    User.RaiseProgress(string.Format(Properties.Resources.ModuleInstallerInstallingMod, modsToInstall[i]),
                                          percent_complete);
 
                     Install(modsToInstall[i], resolver.IsAutoInstalled(modsToInstall[i]), registry_manager.registry, ref possibleConfigOnlyDirs);
                 }
 
-                User.RaiseProgress("Updating registry", 70);
+                User.RaiseProgress(Properties.Resources.ModuleInstallerUpdatingRegistry, 70);
 
                 registry_manager.Save(!options.without_enforce_consistency);
 
-                User.RaiseProgress("Committing filesystem changes", 80);
+                User.RaiseProgress(Properties.Resources.ModuleInstallerCommitting, 80);
 
                 transaction.Complete();
 
@@ -208,12 +209,14 @@ public void InstallList(ICollection<CkanModule> modules, RelationshipResolverOpt
                 // We can scan GameData as a separate transaction. Installing the mods
                 // leaves everything consistent, and this is just gravy. (And ScanGameData
                 // acts as a Tx, anyway, so we don't need to provide our own.)
-                User.RaiseProgress("Rescanning GameData", 90);
+                User.RaiseProgress(
+                    string.Format(Properties.Resources.ModuleInstallerRescanning, ksp.game.PrimaryModDirectoryRelative),
+                    90);
                 log.Debug("Scanning after install");
                 ksp.Scan();
             }
 
-            User.RaiseProgress("Done!", 100);
+            User.RaiseProgress(Properties.Resources.ModuleInstallerDone, 100);
         }
 
         /// <summary>
@@ -264,7 +267,7 @@ private void Install(CkanModule module, bool autoInstalled, Registry registry, r
             // TODO: This really should be handled by higher-up code.
             if (version != null && !(version is UnmanagedModuleVersion))
             {
-                User.RaiseMessage("    {0} {1} already installed, skipped", module.identifier, version);
+                User.RaiseMessage(Properties.Resources.ModuleInstallerAlreadyInstalled, module.identifier, version);
                 return;
             }
 
@@ -276,7 +279,7 @@ private void Install(CkanModule module, bool autoInstalled, Registry registry, r
             {
                 throw new FileNotFoundKraken(
                     null,
-                    String.Format("Trying to install {0}, but it's not downloaded or download is corrupted", module)
+                    string.Format(Properties.Resources.ModuleInstallerZIPNotInCache, module)
                 );
             }
 
@@ -308,11 +311,11 @@ private static void CheckKindInstallationKraken(CkanModule module)
         {
             if (module.IsMetapackage)
             {
-                throw new BadCommandKraken("Metapackages cannot be installed!");
+                throw new BadCommandKraken(Properties.Resources.ModuleInstallerMetapackage);
             }
             if (module.IsDLC)
             {
-                throw new BadCommandKraken("DLC cannot be installed!");
+                throw new BadCommandKraken(Properties.Resources.ModuleInstallerDLC);
             }
         }
 
@@ -358,7 +361,8 @@ private IEnumerable<string> InstallModule(CkanModule module, string zip_filename
                         {
                             // Manually installed DLL is somewhere else where we're not installing files,
                             // probable bad install, alert user and abort
-                            throw new DllLocationMismatchKraken(dll, $"DLL for module {module.identifier} found at {dll}, but it's not where CKAN would install it. Aborting to prevent multiple copies of the same mod being installed. To install this module, uninstall it manually and try again.");
+                            throw new DllLocationMismatchKraken(dll, string.Format(
+                                Properties.Resources.ModuleInstallerBadDLLLocation, module.identifier, dll));
                         }
                         // Delete the manually installed DLL transaction-style because we believe we'll be replacing it
                         var toDelete = ksp.ToAbsoluteGameDir(dll);
@@ -377,14 +381,16 @@ private IEnumerable<string> InstallModule(CkanModule module, string zip_filename
                             var fileMsg = conflicting
                                 .OrderBy(c => c.Value)
                                 .Aggregate("", (a, b) =>
-                                    $"{a}\r\n- {ksp.ToRelativeGameDir(b.Key.destination)}  ({(b.Value ? "same" : "DIFFERENT")})");
-                            if (User.RaiseYesNoDialog($"Module {module.name} wants to overwrite the following manually installed files:\r\n{fileMsg}\r\n\r\nOverwrite?"))
+                                    $"{a}\r\n- {ksp.ToRelativeGameDir(b.Key.destination)}  ({(b.Value ? Properties.Resources.ModuleInstallerFileSame : Properties.Resources.ModuleInstallerFileDifferent)})");
+                            if (User.RaiseYesNoDialog(string.Format(
+                                Properties.Resources.ModuleInstallerOverwrite, module.name, fileMsg)))
                             {
                                 DeleteConflictingFiles(conflicting.Select(f => f.Key));
                             }
                             else
                             {
-                                throw new CancelledActionKraken($"Not overwriting manually installed files, can't install {module.name}.");
+                                throw new CancelledActionKraken(string.Format(
+                                    Properties.Resources.ModuleInstallerOverwriteCancelled, module.name));
                             }
                         }
                     }
@@ -603,7 +609,7 @@ internal static string CopyZipEntry(ZipFile zipfile, ZipEntry entry, string full
                 // We don't allow for the overwriting of files. See #208.
                 if (file_transaction.FileExists(fullPath))
                 {
-                    throw new FileExistsKraken(fullPath, string.Format("Trying to write '{0}' but it already exists.", fullPath));
+                    throw new FileExistsKraken(fullPath, string.Format(Properties.Resources.ModuleInstallerFileExists, fullPath));
                 }
 
                 // Snapshot whatever was there before. If there's nothing, this will just
@@ -680,7 +686,8 @@ public void UninstallList(
                 return;
             }
 
-            User.RaiseMessage("About to remove:\r\n");
+            User.RaiseMessage(Properties.Resources.ModuleInstallerAboutToRemove);
+            User.RaiseMessage("");
 
             foreach (string mod in goners)
             {
@@ -688,9 +695,9 @@ public void UninstallList(
                 User.RaiseMessage(" * {0} {1}", module.Module.name, module.Module.version);
             }
 
-            if (ConfirmPrompt && !User.RaiseYesNoDialog("Continue?"))
+            if (ConfirmPrompt && !User.RaiseYesNoDialog(Properties.Resources.ModuleInstallerContinuePrompt))
             {
-                throw new CancelledActionKraken("Mod removal aborted at user request");
+                throw new CancelledActionKraken(Properties.Resources.ModuleInstallerRemoveAborted);
             }
 
             using (var transaction = CkanTransaction.CreateTransactionScope())
@@ -699,7 +706,9 @@ public void UninstallList(
                 foreach (string mod in goners)
                 {
                     int percent_complete = (step++ * 100) / goners.Count;
-                    User.RaiseProgress($"Removing {mod}...", percent_complete);
+                    User.RaiseProgress(
+                        string.Format(Properties.Resources.ModuleInstallerRemovingMod, mod),
+                        percent_complete);
                     Uninstall(mod, ref possibleConfigOnlyDirs, registry_manager.registry);
                 }
 
@@ -710,7 +719,7 @@ public void UninstallList(
                 transaction.Complete();
             }
 
-            User.RaiseProgress("Done!", 100);
+            User.RaiseProgress(Properties.Resources.ModuleInstallerDone, 100);
         }
 
         /// <summary>
@@ -935,7 +944,9 @@ public void AddRemove(ref HashSet<string> possibleConfigOnlyDirs, RegistryManage
                 {
                     // The post-install steps start at 80%, so count up to 70% for installation
                     int percent_complete = (step++ * 70) / totSteps;
-                    User.RaiseProgress($"Removing \"{instMod.Module}\"", percent_complete);
+                    User.RaiseProgress(
+                        string.Format(Properties.Resources.ModuleInstallerRemovingMod, instMod.Module),
+                        percent_complete);
                     Uninstall(instMod.Module.identifier, ref possibleConfigOnlyDirs, registry_manager.registry);
                 }
 
@@ -943,14 +954,16 @@ public void AddRemove(ref HashSet<string> possibleConfigOnlyDirs, RegistryManage
                 {
                     var previous = remove?.FirstOrDefault(im => im.Module.identifier == module.identifier);
                     int percent_complete = (step++ * 70) / totSteps;
-                    User.RaiseProgress($"Installing \"{module}\"", percent_complete);
+                    User.RaiseProgress(
+                        string.Format(Properties.Resources.ModuleInstallerInstallingMod, module),
+                        percent_complete);
                     Install(module, previous?.AutoInstalled ?? false, registry_manager.registry, ref possibleConfigOnlyDirs);
                 }
 
-                User.RaiseProgress("Updating registry", 80);
+                User.RaiseProgress(Properties.Resources.ModuleInstallerUpdatingRegistry, 80);
                 registry_manager.Save(enforceConsistency);
 
-                User.RaiseProgress("Committing filesystem changes", 90);
+                User.RaiseProgress(Properties.Resources.ModuleInstallerCommitting, 90);
                 tx.Complete();
 
                 EnforceCacheSizeLimit(registry_manager.registry);
@@ -998,7 +1011,8 @@ public void Upgrade(IEnumerable<CkanModule> modules, IDownloader netAsyncDownloa
                 modules = resolver.ModList();
             }
 
-            User.RaiseMessage("About to upgrade:\r\n");
+            User.RaiseMessage(Properties.Resources.ModuleInstallerAboutToUpgrade);
+            User.RaiseMessage("");
 
             // Our upgrade involves removing everything that's currently installed, then
             // adding everything that needs installing (which may involve new mods to
@@ -1015,7 +1029,7 @@ public void Upgrade(IEnumerable<CkanModule> modules, IDownloader netAsyncDownloa
                 {
                     if (!Cache.IsMaybeCachedZip(module))
                     {
-                        User.RaiseMessage(" * Install: {0} {1} ({2}, {3})",
+                        User.RaiseMessage(Properties.Resources.ModuleInstallerUpgradeInstallingUncached,
                             module.name,
                             module.version,
                             module.download.Host,
@@ -1024,7 +1038,7 @@ public void Upgrade(IEnumerable<CkanModule> modules, IDownloader netAsyncDownloa
                     }
                     else
                     {
-                        User.RaiseMessage(" * Install: {0} {1} (cached)",
+                        User.RaiseMessage(Properties.Resources.ModuleInstallerUpgradeInstallingCached,
                             module.name, module.version);
                     }
                 }
@@ -1036,19 +1050,19 @@ public void Upgrade(IEnumerable<CkanModule> modules, IDownloader netAsyncDownloa
                     CkanModule installed = installed_mod.Module;
                     if (installed.version.IsEqualTo(module.version))
                     {
-                        User.RaiseMessage(" * Re-install: {0} {1}",
+                        User.RaiseMessage(Properties.Resources.ModuleInstallerUpgradeReinstalling,
                             module.name, module.version);
                     }
                     else if (installed.version.IsGreaterThan(module.version))
                     {
-                        User.RaiseMessage(" * Downgrade: {0} from {1} to {2}",
+                        User.RaiseMessage(Properties.Resources.ModuleInstallerUpgradeDowngrading,
                             module.name, installed.version, module.version);
                     }
                     else
                     {
                         if (!Cache.IsMaybeCachedZip(module))
                         {
-                            User.RaiseMessage(" * Upgrade: {0} {1} to {2} ({3}, {4})",
+                            User.RaiseMessage(Properties.Resources.ModuleInstallerUpgradeUpgradingUncached,
                                 module.name,
                                 installed.version,
                                 module.version,
@@ -1058,16 +1072,16 @@ public void Upgrade(IEnumerable<CkanModule> modules, IDownloader netAsyncDownloa
                         }
                         else
                         {
-                            User.RaiseMessage(" * Upgrade: {0} {1} to {2} (cached)",
+                            User.RaiseMessage(Properties.Resources.ModuleInstallerUpgradeUpgradingCached,
                                 module.name, installed.version, module.version);
                         }
                     }
                 }
             }
 
-            if (ConfirmPrompt && !User.RaiseYesNoDialog("Continue?"))
+            if (ConfirmPrompt && !User.RaiseYesNoDialog(Properties.Resources.ModuleInstallerContinuePrompt))
             {
-                throw new CancelledActionKraken("User declined upgrade list");
+                throw new CancelledActionKraken(Properties.Resources.ModuleInstallerUpgradeUserDeclined);
             }
 
             // Start by making sure we've downloaded everything.
@@ -1080,7 +1094,7 @@ public void Upgrade(IEnumerable<CkanModule> modules, IDownloader netAsyncDownloa
                 to_remove,
                 enforceConsistency
             );
-            User.RaiseProgress("Done!", 100);
+            User.RaiseProgress(Properties.Resources.ModuleInstallerDone, 100);
         }
 
         /// <summary>
@@ -1120,10 +1134,14 @@ public void Replace(IEnumerable<ModuleReplacement> replacements, RelationshipRes
                     //Maybe ModuleNotInstalled ?
                     if (registry_manager.registry.IsAutodetected(ident))
                     {
-                        throw new ModuleNotFoundKraken(ident, repl.ToReplace.version.ToString(), String.Format("Can't replace {0} as it was not installed by CKAN. \r\n Please remove manually before trying to install it.", ident));
+                        throw new ModuleNotFoundKraken(ident,
+                            repl.ToReplace.version.ToString(),
+                            string.Format(Properties.Resources.ModuleInstallerReplaceAutodetected, ident));
                     }
 
-                    throw new ModuleNotFoundKraken(ident, repl.ToReplace.version.ToString(), String.Format("Can't replace {0} as it is not installed. Please attempt to install {1} instead.", ident, repl.ReplaceWith.identifier));
+                    throw new ModuleNotFoundKraken(ident,
+                        repl.ToReplace.version.ToString(),
+                        string.Format(Properties.Resources.ModuleInstallerReplaceNotInstalled, ident, repl.ReplaceWith.identifier));
                 }
                 else
                 {
@@ -1170,7 +1188,7 @@ public void Replace(IEnumerable<ModuleReplacement> replacements, RelationshipRes
                 modsToRemove,
                 enforceConsistency
             );
-            User.RaiseProgress("Done!", 100);
+            User.RaiseProgress(Properties.Resources.ModuleInstallerDone, 100);
         }
 
         #endregion
@@ -1432,7 +1450,7 @@ public void ImportFiles(HashSet<FileInfo> files, IUser user, Action<CkanModule>
             foreach (FileInfo f in files)
             {
                 int percent = i * 100 / files.Count;
-                user.RaiseProgress($"Importing {f.Name}... ({percent}%)", percent);
+                user.RaiseProgress(string.Format(Properties.Resources.ModuleInstallerImporting, f.Name, percent), percent);
                 // Calc SHA-1 sum
                 string sha1 = Cache.GetFileHashSha1(f.FullName);
                 // Find SHA-1 sum in registry (potentially multiple)
@@ -1448,22 +1466,25 @@ public void ImportFiles(HashSet<FileInfo> files, IUser user, Action<CkanModule>
                         }
                         if (Cache.IsMaybeCachedZip(mod))
                         {
-                            user.RaiseMessage("Already cached: {0}", f.Name);
+                            user.RaiseMessage(Properties.Resources.ModuleInstallerImportAlreadyCached, f.Name);
                         }
                         else
                         {
-                            user.RaiseMessage($"Importing {mod.identifier} {StripEpoch(mod.version)}...");
+                            user.RaiseMessage(Properties.Resources.ModuleInstallerImportingMod,
+                                mod.identifier, StripEpoch(mod.version));
                             Cache.Store(mod, f.FullName);
                         }
                     }
                 }
                 else
                 {
-                    user.RaiseMessage("Not found in index: {0}", f.Name);
+                    user.RaiseMessage(Properties.Resources.ModuleInstallerImportNotFound, f.Name);
                 }
                 ++i;
             }
-            if (installable.Count > 0 && user.RaiseYesNoDialog($"Install {installable.Count} compatible imported mods in game instance {ksp.Name} ({ksp.GameDir()})?"))
+            if (installable.Count > 0 && user.RaiseYesNoDialog(string.Format(
+                Properties.Resources.ModuleInstallerImportInstallPrompt,
+                installable.Count, ksp.Name, ksp.GameDir())))
             {
                 // Install the imported mods
                 foreach (CkanModule mod in installable)
@@ -1471,7 +1492,8 @@ public void ImportFiles(HashSet<FileInfo> files, IUser user, Action<CkanModule>
                     installMod(mod);
                 }
             }
-            if (allowDelete && deletable.Count > 0 && user.RaiseYesNoDialog($"Import complete. Delete {deletable.Count} old files?"))
+            if (allowDelete && deletable.Count > 0 && user.RaiseYesNoDialog(string.Format(
+                Properties.Resources.ModuleInstallerImportDeletePrompt, deletable.Count)))
             {
                 // Delete old files
                 foreach (FileInfo f in deletable)
diff --git a/Core/Net/AutoUpdate.cs b/Core/Net/AutoUpdate.cs
index 9da567263a..1793a8aea6 100644
--- a/Core/Net/AutoUpdate.cs
+++ b/Core/Net/AutoUpdate.cs
@@ -137,7 +137,7 @@ public void StartUpdateProcess(bool launchCKANAfterUpdate, IUser user = null)
         {
             if (!IsFetched())
             {
-                throw new Kraken("We have not fetched the release info yet. Can't update.");
+                throw new Kraken(Properties.Resources.AutoUpdateNotFetched);
             }
 
             var pid = Process.GetCurrentProcess().Id;
diff --git a/Core/Net/Net.cs b/Core/Net/Net.cs
index 8366a522ac..6e4bd96563 100644
--- a/Core/Net/Net.cs
+++ b/Core/Net/Net.cs
@@ -83,7 +83,7 @@ public static string Download(string url, out string etag, string filename = nul
             TxFileManager FileTransaction = new TxFileManager();
 
             user = user ?? new NullUser();
-            user.RaiseMessage("Downloading {0}", url);
+            user.RaiseMessage(Properties.Resources.NetDownloading, url);
 
             // Generate a temporary file if none is provided.
             if (filename == null)
@@ -130,7 +130,9 @@ public static string Download(string url, out string etag, string filename = nul
                 // Look for an exception regarding the authentication.
                 if (Regex.IsMatch(exc.ToString(), "The authentication or decryption has failed."))
                 {
-                    throw new MissingCertificateKraken("Failed downloading " + url, exc);
+                    throw new MissingCertificateKraken(
+                        string.Format(Properties.Resources.NetMissingCertFailed, url),
+                        exc);
                 }
 
                 // Not the exception we were looking for! Throw it further upwards!
@@ -277,7 +279,7 @@ public static Uri ResolveRedirect(Uri uri)
                     }
                     else
                     {
-                        throw new Kraken("Invalid URL in Location header: " + location);
+                        throw new Kraken(string.Format(Properties.Resources.NetInvalidLocation, location));
                     }
                 }
             }
diff --git a/Core/Net/NetAsyncDownloader.cs b/Core/Net/NetAsyncDownloader.cs
index d600dc5ed5..f529d58ea5 100644
--- a/Core/Net/NetAsyncDownloader.cs
+++ b/Core/Net/NetAsyncDownloader.cs
@@ -155,7 +155,7 @@ private void DownloadModule(Net.DownloadTarget target)
                 downloads.Add(dl);
 
                 // Encode spaces to avoid confusing URL parsers
-                User.RaiseMessage("Downloading \"{0}\"",
+                User.RaiseMessage(Properties.Resources.NetAsyncDownloaderDownloading,
                     dl.target.url.ToString().Replace(" ", "%20"));
 
                 // Schedule for us to get back progress reports.
@@ -237,7 +237,7 @@ public void DownloadAndWait(ICollection<Net.DownloadTarget> urls)
                 }
 
                 // Signal to the caller that the user cancelled the download.
-                throw new CancelledActionKraken("Download cancelled by user");
+                throw new CancelledActionKraken(Properties.Resources.NetAsyncDownloaderCancelled);
             }
 
             // Check to see if we've had any errors. If so, then release the kraken!
@@ -367,7 +367,7 @@ private void FileProgressReport(int index, int percent, long bytesDownloaded, lo
             {
                 // Math.Ceiling was added to avoid showing 0 MiB left when finishing
                 User.RaiseProgress(
-                    String.Format("{0}/sec - downloading - {1} left",
+                    String.Format(Properties.Resources.NetAsyncDownloaderProgress,
                         CkanModule.FmtSize(totalBytesPerSecond),
                         CkanModule.FmtSize(totalBytesLeft)),
                     totalPercentage);
@@ -389,7 +389,7 @@ private void FileDownloadComplete(int index, Exception error)
                 {
                     log.InfoFormat("Trying fallback URL: {0}", downloads[index].target.fallbackUrl);
                     // Encode spaces to avoid confusing URL parsers
-                    User.RaiseMessage("Failed to download \"{0}\", trying fallback \"{1}\"",
+                    User.RaiseMessage(Properties.Resources.NetAsyncDownloaderTryingFallback,
                         downloads[index].target.url.ToString().Replace(" ", "%20"),
                         downloads[index].target.fallbackUrl.ToString().Replace(" ", "%20")
                     );
diff --git a/Core/Net/NetFileCache.cs b/Core/Net/NetFileCache.cs
index 6aec3f94cd..fd45a0d35e 100644
--- a/Core/Net/NetFileCache.cs
+++ b/Core/Net/NetFileCache.cs
@@ -63,7 +63,8 @@ public NetFileCache(string path)
             // Basic validation, our cache has to exist.
             if (!Directory.Exists(cachePath))
             {
-                throw new DirectoryNotFoundKraken(cachePath, $"Cannot find cache directory: {cachePath}");
+                throw new DirectoryNotFoundKraken(cachePath, string.Format(
+                    Properties.Resources.NetFileCacheCannotFind, cachePath));
             }
 
             // Establish a watch on our cache. This means we can cache the directory contents,
@@ -443,7 +444,9 @@ public static bool ZipValid(string filename, out string invalidReason)
                                 if (st != null && !st.EntryValid && !string.IsNullOrEmpty(msg))
                                 {
                                     // Capture the error string so we can return it
-                                    zipErr = $"Error in step {st.Operation} for {st.Entry?.Name}: {msg}";
+                                    zipErr = string.Format(
+                                        Properties.Resources.NetFileCacheZipError,
+                                        st.Operation, st.Entry?.Name, msg);
                                 }
                             }))
                         {
@@ -452,14 +455,14 @@ public static bool ZipValid(string filename, out string invalidReason)
                         }
                         else
                         {
-                            invalidReason = zipErr ?? "ZipFile.TestArchive(true) returned false";
+                            invalidReason = zipErr ?? Properties.Resources.NetFileCacheZipTestArchiveFalse;
                             return false;
                         }
                     }
                 }
                 else
                 {
-                    invalidReason = "Null file name";
+                    invalidReason = Properties.Resources.NetFileCacheNullFileName;
                     return false;
                 }
             }
@@ -477,7 +480,7 @@ public static bool ZipValid(string filename, out string invalidReason)
             catch (NotSupportedException nse) when (Platform.IsMono)
             {
                 // SharpZipLib throws this if your locale isn't installed on Mono
-                invalidReason = $"{nse.Message}\r\n\r\nInstall the `mono-complete` package or equivalent for your operating system.";
+                invalidReason = string.Format(Properties.Resources.NetFileCacheMonoNotSupported, nse.Message);
                 return false;
             }
         }
diff --git a/Core/Net/NetModuleCache.cs b/Core/Net/NetModuleCache.cs
index 6075255b81..8eb66d2bfc 100644
--- a/Core/Net/NetModuleCache.cs
+++ b/Core/Net/NetModuleCache.cs
@@ -123,14 +123,16 @@ public string Store(CkanModule module, string path, string description = null, b
 
             // Check file size
             if (module.download_size > 0 && fi.Length != module.download_size)
-                throw new InvalidModuleFileKraken(module, path,
-                    $"{module}: {path} has length {fi.Length}, should be {module.download_size}");
+                throw new InvalidModuleFileKraken(module, path, string.Format(
+                    Properties.Resources.NetModuleCacheBadLength,
+                    module, path, fi.Length, module.download_size));
 
             // Check valid CRC
             string invalidReason;
             if (!NetFileCache.ZipValid(path, out invalidReason))
-                throw new InvalidModuleFileKraken(module, path,
-                    $"{module}: {path} is not a valid ZIP file: {invalidReason}");
+                throw new InvalidModuleFileKraken(module, path, string.Format(
+                    Properties.Resources.NetModuleCacheNotValidZIP,
+                    module, path, invalidReason));
 
             // Some older metadata doesn't have hashes
             if (module.download_hash != null)
@@ -138,14 +140,16 @@ public string Store(CkanModule module, string path, string description = null, b
                 // Check SHA1 match
                 string sha1 = GetFileHashSha1(path);
                 if (sha1 != module.download_hash.sha1)
-                    throw new InvalidModuleFileKraken(module, path,
-                        $"{module}: {path} has SHA1 {sha1}, should be {module.download_hash.sha1}");
+                    throw new InvalidModuleFileKraken(module, path, string.Format(
+                        Properties.Resources.NetModuleCacheMismatchSHA1,
+                        module, path, sha1, module.download_hash.sha1));
 
                 // Check SHA256 match
                 string sha256 = GetFileHashSha256(path);
                 if (sha256 != module.download_hash.sha256)
-                    throw new InvalidModuleFileKraken(module, path,
-                        $"{module}: {path} has SHA256 {sha256}, should be {module.download_hash.sha256}");
+                    throw new InvalidModuleFileKraken(module, path, string.Format(
+                        Properties.Resources.NetModuleCacheMismatchSHA256,
+                        module, path, sha256, module.download_hash.sha256));
             }
 
             // If no exceptions, then everything is fine
diff --git a/Core/Net/Repo.cs b/Core/Net/Repo.cs
index 42a022464d..823a38acfc 100644
--- a/Core/Net/Repo.cs
+++ b/Core/Net/Repo.cs
@@ -37,18 +37,18 @@ public static class Repo
         public static RepoUpdateResult UpdateAllRepositories(RegistryManager registry_manager, GameInstance ksp, NetModuleCache cache, IUser user)
         {
             SortedDictionary<string, Repository> sortedRepositories = registry_manager.registry.Repositories;
-            user.RaiseProgress("Checking for updates", 0);
+            user.RaiseProgress(Properties.Resources.NetRepoCheckingForUpdates, 0);
             if (sortedRepositories.Values.All(repo => !string.IsNullOrEmpty(repo.last_server_etag) && repo.last_server_etag == Net.CurrentETag(repo.uri)))
             {
-                user.RaiseProgress("Already up to date", 100);
-                user.RaiseMessage("No changes since last update");
+                user.RaiseProgress(Properties.Resources.NetRepoAlreadyUpToDate, 100);
+                user.RaiseMessage(Properties.Resources.NetRepoNoChanges);
                 return RepoUpdateResult.NoChanges;
             }
             List<CkanModule> allAvail = new List<CkanModule>();
             int index = 0;
             foreach (KeyValuePair<string, Repository> repository in sortedRepositories)
             {
-                user.RaiseProgress($"Updating {repository.Value.name}",
+                user.RaiseProgress(string.Format(Properties.Resources.NetRepoUpdating, repository.Value.name),
                     10 + 80 * index / sortedRepositories.Count);
                 SortedDictionary<string, int> downloadCounts;
                 string newETag;
@@ -65,7 +65,7 @@ public static RepoUpdateResult UpdateAllRepositories(RegistryManager registry_ma
                     // Merge all the lists
                     allAvail.AddRange(avail);
                     repository.Value.last_server_etag = newETag;
-                    user.RaiseMessage("Updated {0} ({1} modules)",
+                    user.RaiseMessage(Properties.Resources.NetRepoUpdated,
                         repository.Value.name, avail.Count);
                 }
                 ++index;
@@ -73,7 +73,7 @@ public static RepoUpdateResult UpdateAllRepositories(RegistryManager registry_ma
             // Save allAvail to the registry if we found anything
             if (allAvail.Count > 0)
             {
-                user.RaiseProgress("Saving modules to registry", 90);
+                user.RaiseProgress(Properties.Resources.NetRepoSaving, 90);
                 using (var transaction = CkanTransaction.CreateTransactionScope())
                 {
                     // Save our changes.
@@ -92,14 +92,14 @@ public static RepoUpdateResult UpdateAllRepositories(RegistryManager registry_ma
 
                 // Registry.CompatibleModules is slow, just return success,
                 // caller can check it if it's really needed
-                user.RaiseProgress("Registry saved", 100);
-                user.RaiseMessage("Repositories updated");
+                user.RaiseProgress(Properties.Resources.NetRepoSaved, 100);
+                user.RaiseMessage(Properties.Resources.NetRepoUpdatedAll);
                 return RepoUpdateResult.Updated;
             }
             else
             {
                 // Return failure
-                user.RaiseMessage("No modules found!");
+                user.RaiseMessage(Properties.Resources.NetRepoNoModules);
                 return RepoUpdateResult.Failed;
             }
         }
@@ -121,7 +121,7 @@ private static List<CkanModule> UpdateRegistry(Uri repo, GameInstance ksp, IUser
             }
             catch (System.Net.WebException ex)
             {
-                user.RaiseError("Failed to download {0}: {1}", repo, ex.Message);
+                user.RaiseError(Properties.Resources.NetRepoFailedDownload, repo, ex.Message);
                 currentETag = null;
                 return null;
             }
@@ -203,12 +203,7 @@ private static void HandleModuleChanges(List<CkanModule> metadataChanges, IUser
                 sb.AppendLine(string.Format("- {0} {1}", module.identifier, module.version));
             }
 
-            if (user.RaiseYesNoDialog(string.Format(@"The following mods have had their metadata changed since last update:
-
-{0}
-You should reinstall them in order to preserve consistency with the repository.
-
-Do you wish to reinstall now?", sb)))
+            if (user.RaiseYesNoDialog(string.Format(Properties.Resources.NetRepoChangedModulesReinstallPrompt, sb)))
             {
                 throw new ReinstallModuleKraken(metadataChanges);
             }
@@ -530,7 +525,7 @@ private static void ShowUserInconsistencies(Registry registry, IUser user)
             {
                 var sanityMessage = new StringBuilder();
 
-                sanityMessage.AppendLine("The following inconsistencies were found:");
+                sanityMessage.AppendLine(Properties.Resources.NetRepoInconsistenciesHeader);
                 foreach (var sanityError in sanityErrors)
                 {
                     sanityMessage.Append("- ");
diff --git a/Core/Properties/AssemblyInfo.cs b/Core/Properties/AssemblyInfo.cs
index 9b100a5b1b..824627d0ad 100644
--- a/Core/Properties/AssemblyInfo.cs
+++ b/Core/Properties/AssemblyInfo.cs
@@ -1,8 +1,10 @@
+using System.Resources;
 using System.Reflection;
 using System.Runtime.CompilerServices;
 
 [assembly: AssemblyTitle("CKAN")]
 [assembly: AssemblyDescription("CKAN Core")]
+[assembly: NeutralResourcesLanguage("en-GB")]
 
 [assembly: InternalsVisibleTo("Tests")]
 [assembly: InternalsVisibleTo("CKAN.Tests")]
diff --git a/Core/Properties/Resources.Designer.cs b/Core/Properties/Resources.Designer.cs
new file mode 100644
index 0000000000..229d653537
--- /dev/null
+++ b/Core/Properties/Resources.Designer.cs
@@ -0,0 +1,561 @@
+//------------------------------------------------------------------------------
+// <auto-generated>
+//     This code was generated by a tool.
+//     Runtime Version:4.0.30319.42000
+//
+//     Changes to this file may cause incorrect behavior and will be lost if
+//     the code is regenerated. (I WISH!)
+// </auto-generated>
+//------------------------------------------------------------------------------
+
+namespace CKAN.Properties {
+    using System;
+
+
+    /// <summary>
+    ///   A strongly-typed resource class, for looking up localized strings, etc.
+    /// </summary>
+    // This class was auto-generated by the StronglyTypedResourceBuilder
+    // class via a tool like ResGen or Visual Studio.
+    // To add or remove a member, edit your .ResX file then rerun ResGen
+    // with the /str option, or rebuild your VS project.
+    [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "4.0.0.0")]
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
+    [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
+    internal class Resources {
+
+        private static global::System.Resources.ResourceManager resourceMan;
+
+        private static global::System.Globalization.CultureInfo resourceCulture;
+
+        [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
+        internal Resources() { }
+
+        /// <summary>
+        ///   Returns the cached ResourceManager instance used by this class.
+        /// </summary>
+        [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
+        internal static global::System.Resources.ResourceManager ResourceManager {
+            get {
+                if (object.ReferenceEquals(resourceMan, null))
+                {
+                    resourceMan = new SingleAssemblyResourceManager("CKAN.Properties.Resources", typeof(Resources).Assembly);
+                }
+                return resourceMan;
+            }
+        }
+
+        /// <summary>
+        ///   Overrides the current thread's CurrentUICulture property for all
+        ///   resource lookups using this strongly typed resource class.
+        /// </summary>
+        [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
+        internal static global::System.Globalization.CultureInfo Culture {
+            get {
+                return resourceCulture;
+            }
+            set {
+                resourceCulture = value;
+            }
+        }
+
+        internal static string AutoUpdateNotFetched {
+            get { return (string)(ResourceManager.GetObject("AutoUpdateNotFetched", resourceCulture)); }
+        }
+
+        internal static string NetDownloading {
+            get { return (string)(ResourceManager.GetObject("NetDownloading", resourceCulture)); }
+        }
+        internal static string NetMissingCertFailed {
+            get { return (string)(ResourceManager.GetObject("NetMissingCertFailed", resourceCulture)); }
+        }
+        internal static string NetInvalidLocation {
+            get { return (string)(ResourceManager.GetObject("NetInvalidLocation", resourceCulture)); }
+        }
+
+        internal static string NetAsyncDownloaderDownloading {
+            get { return (string)(ResourceManager.GetObject("NetAsyncDownloaderDownloading", resourceCulture)); }
+        }
+        internal static string NetAsyncDownloaderCancelled {
+            get { return (string)(ResourceManager.GetObject("NetAsyncDownloaderCancelled", resourceCulture)); }
+        }
+        internal static string NetAsyncDownloaderProgress {
+            get { return (string)(ResourceManager.GetObject("NetAsyncDownloaderProgress", resourceCulture)); }
+        }
+        internal static string NetAsyncDownloaderTryingFallback {
+            get { return (string)(ResourceManager.GetObject("NetAsyncDownloaderTryingFallback", resourceCulture)); }
+        }
+
+        internal static string NetFileCacheCannotFind {
+            get { return (string)(ResourceManager.GetObject("NetFileCacheCannotFind", resourceCulture)); }
+        }
+        internal static string NetFileCacheZipError {
+            get { return (string)(ResourceManager.GetObject("NetFileCacheZipError", resourceCulture)); }
+        }
+        internal static string NetFileCacheZipTestArchiveFalse {
+            get { return (string)(ResourceManager.GetObject("NetFileCacheZipTestArchiveFalse", resourceCulture)); }
+        }
+        internal static string NetFileCacheNullFileName {
+            get { return (string)(ResourceManager.GetObject("NetFileCacheNullFileName", resourceCulture)); }
+        }
+        internal static string NetFileCacheMonoNotSupported {
+            get { return (string)(ResourceManager.GetObject("NetFileCacheMonoNotSupported", resourceCulture)); }
+        }
+        internal static string NetModuleCacheBadLength {
+            get { return (string)(ResourceManager.GetObject("NetModuleCacheBadLength", resourceCulture)); }
+        }
+        internal static string NetModuleCacheNotValidZIP {
+            get { return (string)(ResourceManager.GetObject("NetModuleCacheNotValidZIP", resourceCulture)); }
+        }
+        internal static string NetModuleCacheMismatchSHA1 {
+            get { return (string)(ResourceManager.GetObject("NetModuleCacheMismatchSHA1", resourceCulture)); }
+        }
+        internal static string NetModuleCacheMismatchSHA256 {
+            get { return (string)(ResourceManager.GetObject("NetModuleCacheMismatchSHA256", resourceCulture)); }
+        }
+
+        internal static string NetRepoCheckingForUpdates {
+            get { return (string)(ResourceManager.GetObject("NetRepoCheckingForUpdates", resourceCulture)); }
+        }
+        internal static string NetRepoAlreadyUpToDate {
+            get { return (string)(ResourceManager.GetObject("NetRepoAlreadyUpToDate", resourceCulture)); }
+        }
+        internal static string NetRepoNoChanges {
+            get { return (string)(ResourceManager.GetObject("NetRepoNoChanges", resourceCulture)); }
+        }
+        internal static string NetRepoUpdating {
+            get { return (string)(ResourceManager.GetObject("NetRepoUpdating", resourceCulture)); }
+        }
+        internal static string NetRepoUpdated {
+            get { return (string)(ResourceManager.GetObject("NetRepoUpdated", resourceCulture)); }
+        }
+        internal static string NetRepoSaving {
+            get { return (string)(ResourceManager.GetObject("NetRepoSaving", resourceCulture)); }
+        }
+        internal static string NetRepoSaved {
+            get { return (string)(ResourceManager.GetObject("NetRepoSaved", resourceCulture)); }
+        }
+        internal static string NetRepoUpdatedAll {
+            get { return (string)(ResourceManager.GetObject("NetRepoUpdatedAll", resourceCulture)); }
+        }
+        internal static string NetRepoNoModules {
+            get { return (string)(ResourceManager.GetObject("NetRepoNoModules", resourceCulture)); }
+        }
+        internal static string NetRepoFailedDownload {
+            get { return (string)(ResourceManager.GetObject("NetRepoFailedDownload", resourceCulture)); }
+        }
+        internal static string NetRepoChangedModulesReinstallPrompt {
+            get { return (string)(ResourceManager.GetObject("NetRepoChangedModulesReinstallPrompt", resourceCulture)); }
+        }
+        internal static string NetRepoInconsistenciesHeader {
+            get { return (string)(ResourceManager.GetObject("NetRepoInconsistenciesHeader", resourceCulture)); }
+        }
+
+        internal static string JsonRelationshipConverterAnyOfCombined {
+            get { return (string)(ResourceManager.GetObject("JsonRelationshipConverterAnyOfCombined", resourceCulture)); }
+        }
+
+        internal static string RegistryFileConflict {
+            get { return (string)(ResourceManager.GetObject("RegistryFileConflict", resourceCulture)); }
+        }
+        internal static string RegistryFileNotRemoved {
+            get { return (string)(ResourceManager.GetObject("RegistryFileNotRemoved", resourceCulture)); }
+        }
+        internal static string RegistryManagerDirectoryNotFound {
+            get { return (string)(ResourceManager.GetObject("RegistryManagerDirectoryNotFound", resourceCulture)); }
+        }
+        internal static string RegistryManagerExportFilenamePrefix {
+            get { return (string)(ResourceManager.GetObject("RegistryManagerExportFilenamePrefix", resourceCulture)); }
+        }
+        internal static string RegistryManagerDefaultModpackAbstract {
+            get { return (string)(ResourceManager.GetObject("RegistryManagerDefaultModpackAbstract", resourceCulture)); }
+        }
+
+        internal static string CkanModuleDeserialisationError {
+            get { return (string)(ResourceManager.GetObject("CkanModuleDeserialisationError", resourceCulture)); }
+        }
+        internal static string CkanModuleUnsupportedSpec {
+            get { return (string)(ResourceManager.GetObject("CkanModuleUnsupportedSpec", resourceCulture)); }
+        }
+        internal static string CkanModuleMissingRequired {
+            get { return (string)(ResourceManager.GetObject("CkanModuleMissingRequired", resourceCulture)); }
+        }
+        internal static string CkanModuleKspVersionMixed {
+            get { return (string)(ResourceManager.GetObject("CkanModuleKspVersionMixed", resourceCulture)); }
+        }
+        internal static string CkanModuleNotAvailable {
+            get { return (string)(ResourceManager.GetObject("CkanModuleNotAvailable", resourceCulture)); }
+        }
+        internal static string CkanModuleNotInstalledOrAvailable {
+            get { return (string)(ResourceManager.GetObject("CkanModuleNotInstalledOrAvailable", resourceCulture)); }
+        }
+        internal static string CkanModuleAllVersions {
+            get { return (string)(ResourceManager.GetObject("CkanModuleAllVersions", resourceCulture)); }
+        }
+
+        internal static string LicenceInvalid {
+            get { return (string)(ResourceManager.GetObject("LicenceInvalid", resourceCulture)); }
+        }
+
+        internal static string ModuleInstallDescriptorMustHaveInstallTo {
+            get { return (string)(ResourceManager.GetObject("ModuleInstallDescriptorMustHaveInstallTo", resourceCulture)); }
+        }
+        internal static string ModuleInstallDescriptorRequireFileFind {
+            get { return (string)(ResourceManager.GetObject("ModuleInstallDescriptorRequireFileFind", resourceCulture)); }
+        }
+        internal static string ModuleInstallDescriptorTooManyFileFind {
+            get { return (string)(ResourceManager.GetObject("ModuleInstallDescriptorTooManyFileFind", resourceCulture)); }
+        }
+        internal static string ModuleInstallDescriptorTooManyFilterInclude {
+            get { return (string)(ResourceManager.GetObject("ModuleInstallDescriptorTooManyFilterInclude", resourceCulture)); }
+        }
+        internal static string ModuleInstallDescriptorInvalidInstallPath {
+            get { return (string)(ResourceManager.GetObject("ModuleInstallDescriptorInvalidInstallPath", resourceCulture)); }
+        }
+        internal static string ModuleInstallDescriptorUnknownInstallPath {
+            get { return (string)(ResourceManager.GetObject("ModuleInstallDescriptorUnknownInstallPath", resourceCulture)); }
+        }
+        internal static string ModuleInstallDescriptorNoFilesFound {
+            get { return (string)(ResourceManager.GetObject("ModuleInstallDescriptorNoFilesFound", resourceCulture)); }
+        }
+        internal static string ModuleInstallDescriptorAsNoPathSeparators {
+            get { return (string)(ResourceManager.GetObject("ModuleInstallDescriptorAsNoPathSeparators", resourceCulture)); }
+        }
+
+        internal static string RelationshipDescriptorMinVersionOnly {
+            get { return (string)(ResourceManager.GetObject("RelationshipDescriptorMinVersionOnly", resourceCulture)); }
+        }
+        internal static string RelationshipDescriptorMaxVersionOnly {
+            get { return (string)(ResourceManager.GetObject("RelationshipDescriptorMaxVersionOnly", resourceCulture)); }
+        }
+        internal static string RelationshipDescriptorAnyOfJoiner {
+            get { return (string)(ResourceManager.GetObject("RelationshipDescriptorAnyOfJoiner", resourceCulture)); }
+        }
+
+        internal static string ReleaseStatusInvalid {
+            get { return (string)(ResourceManager.GetObject("ReleaseStatusInvalid", resourceCulture)); }
+        }
+
+        internal static string RepositoryDefaultName {
+            get { return (string)(ResourceManager.GetObject("RepositoryDefaultName", resourceCulture)); }
+        }
+
+        internal static string CkanModuleVersionToString {
+            get { return (string)(ResourceManager.GetObject("CkanModuleVersionToString", resourceCulture)); }
+        }
+
+        internal static string GameVersionYalovAny {
+            get { return (string)(ResourceManager.GetObject("GameVersionYalovAny", resourceCulture)); }
+        }
+        internal static string GameVersionSelectNeedOne {
+            get { return (string)(ResourceManager.GetObject("GameVersionSelectNeedOne", resourceCulture)); }
+        }
+        internal static string GameVersionSelectHeader {
+            get { return (string)(ResourceManager.GetObject("GameVersionSelectHeader", resourceCulture)); }
+        }
+        internal static string GameVersionSelectBuildHeader {
+            get { return (string)(ResourceManager.GetObject("GameVersionSelectBuildHeader", resourceCulture)); }
+        }
+        internal static string GameVersionNotKnown {
+            get { return (string)(ResourceManager.GetObject("GameVersionNotKnown", resourceCulture)); }
+        }
+        internal static string GameVersionCriteriaToString {
+            get { return (string)(ResourceManager.GetObject("GameVersionCriteriaToString", resourceCulture)); }
+        }
+        internal static string GameVersionRangeMinOnly {
+            get { return (string)(ResourceManager.GetObject("GameVersionRangeMinOnly", resourceCulture)); }
+        }
+        internal static string GameVersionRangeMaxOnly {
+            get { return (string)(ResourceManager.GetObject("GameVersionRangeMaxOnly", resourceCulture)); }
+        }
+
+        internal static string ProvidesModuleVersionToString {
+            get { return (string)(ResourceManager.GetObject("ProvidesModuleVersionToString", resourceCulture)); }
+        }
+
+        internal static string UnmanagedModuleVersionUnknown {
+            get { return (string)(ResourceManager.GetObject("UnmanagedModuleVersionUnknown", resourceCulture)); }
+        }
+        internal static string UnmanagedModuleVersionKnown {
+            get { return (string)(ResourceManager.GetObject("UnmanagedModuleVersionKnown", resourceCulture)); }
+        }
+
+        internal static string PathUtilsNotAbsolute {
+            get { return (string)(ResourceManager.GetObject("PathUtilsNotAbsolute", resourceCulture)); }
+        }
+        internal static string PathUtilsNotInside {
+            get { return (string)(ResourceManager.GetObject("PathUtilsNotInside", resourceCulture)); }
+        }
+        internal static string PathUtilsAlreadyAbsolute {
+            get { return (string)(ResourceManager.GetObject("PathUtilsAlreadyAbsolute", resourceCulture)); }
+        }
+        internal static string PathUtilsNotRoot {
+            get { return (string)(ResourceManager.GetObject("PathUtilsNotRoot", resourceCulture)); }
+        }
+
+        internal static string GameInstanceSettingUp {
+            get { return (string)(ResourceManager.GetObject("GameInstanceSettingUp", resourceCulture)); }
+        }
+        internal static string GameInstanceCreatingDir {
+            get { return (string)(ResourceManager.GetObject("GameInstanceCreatingDir", resourceCulture)); }
+        }
+        internal static string GameInstanceScanning {
+            get { return (string)(ResourceManager.GetObject("GameInstanceScanning", resourceCulture)); }
+        }
+        internal static string GameInstanceVersionNotFound {
+            get { return (string)(ResourceManager.GetObject("GameInstanceVersionNotFound", resourceCulture)); }
+        }
+        internal static string GameInstanceToString {
+            get { return (string)(ResourceManager.GetObject("GameInstanceToString", resourceCulture)); }
+        }
+
+        internal static string GameInstanceManagerPortable {
+            get { return (string)(ResourceManager.GetObject("GameInstanceManagerPortable", resourceCulture)); }
+        }
+        internal static string GameInstanceManagerAuto {
+            get { return (string)(ResourceManager.GetObject("GameInstanceManagerAuto", resourceCulture)); }
+        }
+        internal static string GameInstanceCloneInvalid {
+            get { return (string)(ResourceManager.GetObject("GameInstanceCloneInvalid", resourceCulture)); }
+        }
+        internal static string GameInstanceFakeBadVersion {
+            get { return (string)(ResourceManager.GetObject("GameInstanceFakeBadVersion", resourceCulture)); }
+        }
+        internal static string GameInstanceFakeNotEmpty {
+            get { return (string)(ResourceManager.GetObject("GameInstanceFakeNotEmpty", resourceCulture)); }
+        }
+        internal static string GameInstanceFakeDLCNotAllowed {
+            get { return (string)(ResourceManager.GetObject("GameInstanceFakeDLCNotAllowed", resourceCulture)); }
+        }
+        internal static string GameInstanceNoValidName {
+            get { return (string)(ResourceManager.GetObject("GameInstanceNoValidName", resourceCulture)); }
+        }
+        internal static string GameInstanceByPathName {
+            get { return (string)(ResourceManager.GetObject("GameInstanceByPathName", resourceCulture)); }
+        }
+        internal static string GameInstancePathNotFound {
+            get { return (string)(ResourceManager.GetObject("GameInstancePathNotFound", resourceCulture)); }
+        }
+
+        internal static string ModuleInstallerDownloading {
+            get { return (string)(ResourceManager.GetObject("ModuleInstallerDownloading", resourceCulture)); }
+        }
+        internal static string ModuleInstallerNothingToInstall {
+            get { return (string)(ResourceManager.GetObject("ModuleInstallerNothingToInstall", resourceCulture)); }
+        }
+        internal static string ModuleInstallerAboutToInstall {
+            get { return (string)(ResourceManager.GetObject("ModuleInstallerAboutToInstall", resourceCulture)); }
+        }
+        internal static string ModuleInstallerModuleCached {
+            get { return (string)(ResourceManager.GetObject("ModuleInstallerModuleCached", resourceCulture)); }
+        }
+        internal static string ModuleInstallerUserDeclined {
+            get { return (string)(ResourceManager.GetObject("ModuleInstallerUserDeclined", resourceCulture)); }
+        }
+        internal static string ModuleInstallerInstallingMod {
+            get { return (string)(ResourceManager.GetObject("ModuleInstallerInstallingMod", resourceCulture)); }
+        }
+        internal static string ModuleInstallerUpdatingRegistry {
+            get { return (string)(ResourceManager.GetObject("ModuleInstallerUpdatingRegistry", resourceCulture)); }
+        }
+        internal static string ModuleInstallerCommitting {
+            get { return (string)(ResourceManager.GetObject("ModuleInstallerCommitting", resourceCulture)); }
+        }
+        internal static string ModuleInstallerRescanning {
+            get { return (string)(ResourceManager.GetObject("ModuleInstallerRescanning", resourceCulture)); }
+        }
+        internal static string ModuleInstallerDone {
+            get { return (string)(ResourceManager.GetObject("ModuleInstallerDone", resourceCulture)); }
+        }
+        internal static string ModuleInstallerAlreadyInstalled {
+            get { return (string)(ResourceManager.GetObject("ModuleInstallerAlreadyInstalled", resourceCulture)); }
+        }
+        internal static string ModuleInstallerZIPNotInCache {
+            get { return (string)(ResourceManager.GetObject("ModuleInstallerZIPNotInCache", resourceCulture)); }
+        }
+        internal static string ModuleInstallerMetapackage {
+            get { return (string)(ResourceManager.GetObject("ModuleInstallerMetapackage", resourceCulture)); }
+        }
+        internal static string ModuleInstallerDLC {
+            get { return (string)(ResourceManager.GetObject("ModuleInstallerDLC", resourceCulture)); }
+        }
+        internal static string ModuleInstallerBadDLLLocation {
+            get { return (string)(ResourceManager.GetObject("ModuleInstallerBadDLLLocation", resourceCulture)); }
+        }
+        internal static string ModuleInstallerFileSame {
+            get { return (string)(ResourceManager.GetObject("ModuleInstallerFileSame", resourceCulture)); }
+        }
+        internal static string ModuleInstallerFileDifferent {
+            get { return (string)(ResourceManager.GetObject("ModuleInstallerFileDifferent", resourceCulture)); }
+        }
+        internal static string ModuleInstallerOverwrite {
+            get { return (string)(ResourceManager.GetObject("ModuleInstallerOverwrite", resourceCulture)); }
+        }
+        internal static string ModuleInstallerOverwriteCancelled {
+            get { return (string)(ResourceManager.GetObject("ModuleInstallerOverwriteCancelled", resourceCulture)); }
+        }
+        internal static string ModuleInstallerFileExists {
+            get { return (string)(ResourceManager.GetObject("ModuleInstallerFileExists", resourceCulture)); }
+        }
+        internal static string ModuleInstallerAboutToRemove {
+            get { return (string)(ResourceManager.GetObject("ModuleInstallerAboutToRemove", resourceCulture)); }
+        }
+        internal static string ModuleInstallerContinuePrompt {
+            get { return (string)(ResourceManager.GetObject("ModuleInstallerContinuePrompt", resourceCulture)); }
+        }
+        internal static string ModuleInstallerRemoveAborted {
+            get { return (string)(ResourceManager.GetObject("ModuleInstallerRemoveAborted", resourceCulture)); }
+        }
+        internal static string ModuleInstallerRemovingMod {
+            get { return (string)(ResourceManager.GetObject("ModuleInstallerRemovingMod", resourceCulture)); }
+        }
+        internal static string ModuleInstallerAboutToUpgrade {
+            get { return (string)(ResourceManager.GetObject("ModuleInstallerAboutToUpgrade", resourceCulture)); }
+        }
+        internal static string ModuleInstallerUpgradeInstallingUncached {
+            get { return (string)(ResourceManager.GetObject("ModuleInstallerUpgradeInstallingUncached", resourceCulture)); }
+        }
+        internal static string ModuleInstallerUpgradeInstallingCached {
+            get { return (string)(ResourceManager.GetObject("ModuleInstallerUpgradeInstallingCached", resourceCulture)); }
+        }
+        internal static string ModuleInstallerUpgradeReinstalling {
+            get { return (string)(ResourceManager.GetObject("ModuleInstallerUpgradeReinstalling", resourceCulture)); }
+        }
+        internal static string ModuleInstallerUpgradeDowngrading {
+            get { return (string)(ResourceManager.GetObject("ModuleInstallerUpgradeDowngrading", resourceCulture)); }
+        }
+        internal static string ModuleInstallerUpgradeUpgradingUncached {
+            get { return (string)(ResourceManager.GetObject("ModuleInstallerUpgradeUpgradingUncached", resourceCulture)); }
+        }
+        internal static string ModuleInstallerUpgradeUpgradingCached {
+            get { return (string)(ResourceManager.GetObject("ModuleInstallerUpgradeUpgradingCached", resourceCulture)); }
+        }
+        internal static string ModuleInstallerUpgradeUserDeclined {
+            get { return (string)(ResourceManager.GetObject("ModuleInstallerUpgradeUserDeclined", resourceCulture)); }
+        }
+
+        internal static string ModuleInstallerReplaceAutodetected {
+            get { return (string)(ResourceManager.GetObject("ModuleInstallerReplaceAutodetected", resourceCulture)); }
+        }
+        internal static string ModuleInstallerReplaceNotInstalled {
+            get { return (string)(ResourceManager.GetObject("ModuleInstallerReplaceNotInstalled", resourceCulture)); }
+        }
+        internal static string ModuleInstallerImporting {
+            get { return (string)(ResourceManager.GetObject("ModuleInstallerImporting", resourceCulture)); }
+        }
+        internal static string ModuleInstallerImportAlreadyCached {
+            get { return (string)(ResourceManager.GetObject("ModuleInstallerImportAlreadyCached", resourceCulture)); }
+        }
+        internal static string ModuleInstallerImportingMod {
+            get { return (string)(ResourceManager.GetObject("ModuleInstallerImportingMod", resourceCulture)); }
+        }
+        internal static string ModuleInstallerImportNotFound {
+            get { return (string)(ResourceManager.GetObject("ModuleInstallerImportNotFound", resourceCulture)); }
+        }
+        internal static string ModuleInstallerImportInstallPrompt {
+            get { return (string)(ResourceManager.GetObject("ModuleInstallerImportInstallPrompt", resourceCulture)); }
+        }
+        internal static string ModuleInstallerImportDeletePrompt {
+            get { return (string)(ResourceManager.GetObject("ModuleInstallerImportDeletePrompt", resourceCulture)); }
+        }
+
+        internal static string KrakenDependencyNotSatisfied {
+            get { return (string)(ResourceManager.GetObject("KrakenDependencyNotSatisfied", resourceCulture)); }
+        }
+        internal static string KrakenDependencyModuleNotFound {
+            get { return (string)(ResourceManager.GetObject("KrakenDependencyModuleNotFound", resourceCulture)); }
+        }
+        internal static string KrakenParentDependencyNotSatisfied {
+            get { return (string)(ResourceManager.GetObject("KrakenParentDependencyNotSatisfied", resourceCulture)); }
+        }
+        internal static string KrakenAny {
+            get { return (string)(ResourceManager.GetObject("KrakenAny", resourceCulture)); }
+        }
+        internal static string KrakenProvidedByMoreThanOne {
+            get { return (string)(ResourceManager.GetObject("KrakenProvidedByMoreThanOne", resourceCulture)); }
+        }
+        internal static string KrakenInconsistenciesHeader {
+            get { return (string)(ResourceManager.GetObject("KrakenInconsistenciesHeader", resourceCulture)); }
+        }
+        internal static string KrakenMissingDependency {
+            get { return (string)(ResourceManager.GetObject("KrakenMissingDependency", resourceCulture)); }
+        }
+        internal static string KrakenConflictsWith {
+            get { return (string)(ResourceManager.GetObject("KrakenConflictsWith", resourceCulture)); }
+        }
+        internal static string KrakenDownloadErrorsHeader {
+            get { return (string)(ResourceManager.GetObject("KrakenDownloadErrorsHeader", resourceCulture)); }
+        }
+        internal static string KrakenModuleDownloadErrorsHeader {
+            get { return (string)(ResourceManager.GetObject("KrakenModuleDownloadErrorsHeader", resourceCulture)); }
+        }
+        internal static string KrakenModuleDownloadError {
+            get { return (string)(ResourceManager.GetObject("KrakenModuleDownloadError", resourceCulture)); }
+        }
+        internal static string KrakenNotInstalled {
+            get { return (string)(ResourceManager.GetObject("KrakenNotInstalled", resourceCulture)); }
+        }
+        internal static string KrakenMissingCertificateUnix {
+            get { return (string)(ResourceManager.GetObject("KrakenMissingCertificateUnix", resourceCulture)); }
+        }
+        internal static string KrakenMissingCertificateNotUnix {
+            get { return (string)(ResourceManager.GetObject("KrakenMissingCertificateNotUnix", resourceCulture)); }
+        }
+        internal static string KrakenDownloadThrottled {
+            get { return (string)(ResourceManager.GetObject("KrakenDownloadThrottled", resourceCulture)); }
+        }
+        internal static string KrakenAlreadyRunning {
+            get { return (string)(ResourceManager.GetObject("KrakenAlreadyRunning", resourceCulture)); }
+        }
+        internal static string KrakenReinstallModule {
+            get { return (string)(ResourceManager.GetObject("KrakenReinstallModule", resourceCulture)); }
+        }
+
+        internal static string RelationshipResolverConflictsWith {
+            get { return (string)(ResourceManager.GetObject("RelationshipResolverConflictsWith", resourceCulture)); }
+        }
+        internal static string RelationshipResolverRequiredButResolver {
+            get { return (string)(ResourceManager.GetObject("RelationshipResolverRequiredButResolver", resourceCulture)); }
+        }
+        internal static string RelationshipResolverRequiredButInstalled {
+            get { return (string)(ResourceManager.GetObject("RelationshipResolverRequiredButInstalled", resourceCulture)); }
+        }
+        internal static string RelationshipResolverAnUnmanaged {
+            get { return (string)(ResourceManager.GetObject("RelationshipResolverAnUnmanaged", resourceCulture)); }
+        }
+        internal static string RelationshipResolverUnmanaged {
+            get { return (string)(ResourceManager.GetObject("RelationshipResolverUnmanaged", resourceCulture)); }
+        }
+        internal static string RelationshipResolverModNotInList {
+            get { return (string)(ResourceManager.GetObject("RelationshipResolverModNotInList", resourceCulture)); }
+        }
+        internal static string RelationshipResolverInstalledReason {
+            get { return (string)(ResourceManager.GetObject("RelationshipResolverInstalledReason", resourceCulture)); }
+        }
+        internal static string RelationshipResolverUserReason {
+            get { return (string)(ResourceManager.GetObject("RelationshipResolverUserReason", resourceCulture)); }
+        }
+        internal static string RelationshipResolverNoLongerUsedReason {
+            get { return (string)(ResourceManager.GetObject("RelationshipResolverNoLongerUsedReason", resourceCulture)); }
+        }
+        internal static string RelationshipResolverReplacementReason {
+            get { return (string)(ResourceManager.GetObject("RelationshipResolverReplacementReason", resourceCulture)); }
+        }
+        internal static string RelationshipResolverSuggestedReason {
+            get { return (string)(ResourceManager.GetObject("RelationshipResolverSuggestedReason", resourceCulture)); }
+        }
+        internal static string RelationshipResolverDependsReason {
+            get { return (string)(ResourceManager.GetObject("RelationshipResolverDependsReason", resourceCulture)); }
+        }
+        internal static string RelationshipResolverRecommendedReason {
+            get { return (string)(ResourceManager.GetObject("RelationshipResolverRecommendedReason", resourceCulture)); }
+        }
+
+        internal static string SanityCheckerUnsatisfiedDependency {
+            get { return (string)(ResourceManager.GetObject("SanityCheckerUnsatisfiedDependency", resourceCulture)); }
+        }
+        internal static string SanityCheckerConflictsWith {
+            get { return (string)(ResourceManager.GetObject("SanityCheckerConflictsWith", resourceCulture)); }
+        }
+    }
+}
diff --git a/Core/Properties/Resources.de-DE.resx b/Core/Properties/Resources.de-DE.resx
new file mode 100644
index 0000000000..a00076a67a
--- /dev/null
+++ b/Core/Properties/Resources.de-DE.resx
@@ -0,0 +1,121 @@
+<?xml version="1.0" encoding="utf-8"?>
+<root>
+  <!--
+    Microsoft ResX Schema
+
+    Version 2.0
+
+    The primary goals of this format is to allow a simple XML format
+    that is mostly human readable. The generation and parsing of the
+    various data types are done through the TypeConverter classes
+    associated with the data types.
+
+    Example:
+
+    ... ado.net/XML headers & schema ...
+    <resheader name="resmimetype">text/microsoft-resx</resheader>
+    <resheader name="version">2.0</resheader>
+    <resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
+    <resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
+    <data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
+    <data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
+    <data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
+        <value>[base64 mime encoded serialized .NET Framework object]</value>
+    </data>
+    <data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
+        <value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
+        <comment>This is a comment</comment>
+    </data>
+
+    There are any number of "resheader" rows that contain simple
+    name/value pairs.
+
+    Each data row contains a name, and value. The row also contains a
+    type or mimetype. Type corresponds to a .NET class that support
+    text/value conversion through the TypeConverter architecture.
+    Classes that don't support this are serialized and stored with the
+    mimetype set.
+
+    The mimetype is used for serialized objects, and tells the
+    ResXResourceReader how to depersist the object. This is currently not
+    extensible. For a given mimetype the value must be set accordingly:
+
+    Note - application/x-microsoft.net.object.binary.base64 is the format
+    that the ResXResourceWriter will generate, however the reader can
+    read any of the formats listed below.
+
+    mimetype: application/x-microsoft.net.object.binary.base64
+    value   : The object must be serialized with
+            : System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
+            : and then encoded with base64 encoding.
+
+    mimetype: application/x-microsoft.net.object.soap.base64
+    value   : The object must be serialized with
+            : System.Runtime.Serialization.Formatters.Soap.SoapFormatter
+            : and then encoded with base64 encoding.
+
+    mimetype: application/x-microsoft.net.object.bytearray.base64
+    value   : The object must be serialized into a byte array
+            : using a System.ComponentModel.TypeConverter
+            : and then encoded with base64 encoding.
+    -->
+  <xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
+    <xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
+    <xsd:element name="root" msdata:IsDataSet="true">
+      <xsd:complexType>
+        <xsd:choice maxOccurs="unbounded">
+          <xsd:element name="metadata">
+            <xsd:complexType>
+              <xsd:sequence>
+                <xsd:element name="value" type="xsd:string" minOccurs="0" />
+              </xsd:sequence>
+              <xsd:attribute name="name" use="required" type="xsd:string" />
+              <xsd:attribute name="type" type="xsd:string" />
+              <xsd:attribute name="mimetype" type="xsd:string" />
+              <xsd:attribute ref="xml:space" />
+            </xsd:complexType>
+          </xsd:element>
+          <xsd:element name="assembly">
+            <xsd:complexType>
+              <xsd:attribute name="alias" type="xsd:string" />
+              <xsd:attribute name="name" type="xsd:string" />
+            </xsd:complexType>
+          </xsd:element>
+          <xsd:element name="data">
+            <xsd:complexType>
+              <xsd:sequence>
+                <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
+                <xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
+              </xsd:sequence>
+              <xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
+              <xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
+              <xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
+              <xsd:attribute ref="xml:space" />
+            </xsd:complexType>
+          </xsd:element>
+          <xsd:element name="resheader">
+            <xsd:complexType>
+              <xsd:sequence>
+                <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
+              </xsd:sequence>
+              <xsd:attribute name="name" type="xsd:string" use="required" />
+            </xsd:complexType>
+          </xsd:element>
+        </xsd:choice>
+      </xsd:complexType>
+    </xsd:element>
+  </xsd:schema>
+  <resheader name="resmimetype">
+    <value>text/microsoft-resx</value>
+  </resheader>
+  <resheader name="version">
+    <value>2.0</value>
+  </resheader>
+  <resheader name="reader">
+    <value>System.Resources.ResXResourceReader, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
+  </resheader>
+  <resheader name="writer">
+    <value>System.Resources.ResXResourceWriter, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
+  </resheader>
+  <data name="RelationshipResolverUserReason" xml:space="preserve"><value>Neue Mod-Installation ausgewählt vom Nutzer</value></data>
+</root>
diff --git a/Core/Properties/Resources.en-US.resx b/Core/Properties/Resources.en-US.resx
new file mode 100644
index 0000000000..ccb337634e
--- /dev/null
+++ b/Core/Properties/Resources.en-US.resx
@@ -0,0 +1,122 @@
+<?xml version="1.0" encoding="utf-8"?>
+<root>
+  <!--
+    Microsoft ResX Schema
+
+    Version 2.0
+
+    The primary goals of this format is to allow a simple XML format
+    that is mostly human readable. The generation and parsing of the
+    various data types are done through the TypeConverter classes
+    associated with the data types.
+
+    Example:
+
+    ... ado.net/XML headers & schema ...
+    <resheader name="resmimetype">text/microsoft-resx</resheader>
+    <resheader name="version">2.0</resheader>
+    <resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
+    <resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
+    <data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
+    <data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
+    <data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
+        <value>[base64 mime encoded serialized .NET Framework object]</value>
+    </data>
+    <data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
+        <value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
+        <comment>This is a comment</comment>
+    </data>
+
+    There are any number of "resheader" rows that contain simple
+    name/value pairs.
+
+    Each data row contains a name, and value. The row also contains a
+    type or mimetype. Type corresponds to a .NET class that support
+    text/value conversion through the TypeConverter architecture.
+    Classes that don't support this are serialized and stored with the
+    mimetype set.
+
+    The mimetype is used for serialized objects, and tells the
+    ResXResourceReader how to depersist the object. This is currently not
+    extensible. For a given mimetype the value must be set accordingly:
+
+    Note - application/x-microsoft.net.object.binary.base64 is the format
+    that the ResXResourceWriter will generate, however the reader can
+    read any of the formats listed below.
+
+    mimetype: application/x-microsoft.net.object.binary.base64
+    value   : The object must be serialized with
+            : System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
+            : and then encoded with base64 encoding.
+
+    mimetype: application/x-microsoft.net.object.soap.base64
+    value   : The object must be serialized with
+            : System.Runtime.Serialization.Formatters.Soap.SoapFormatter
+            : and then encoded with base64 encoding.
+
+    mimetype: application/x-microsoft.net.object.bytearray.base64
+    value   : The object must be serialized into a byte array
+            : using a System.ComponentModel.TypeConverter
+            : and then encoded with base64 encoding.
+    -->
+  <xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
+    <xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
+    <xsd:element name="root" msdata:IsDataSet="true">
+      <xsd:complexType>
+        <xsd:choice maxOccurs="unbounded">
+          <xsd:element name="metadata">
+            <xsd:complexType>
+              <xsd:sequence>
+                <xsd:element name="value" type="xsd:string" minOccurs="0" />
+              </xsd:sequence>
+              <xsd:attribute name="name" use="required" type="xsd:string" />
+              <xsd:attribute name="type" type="xsd:string" />
+              <xsd:attribute name="mimetype" type="xsd:string" />
+              <xsd:attribute ref="xml:space" />
+            </xsd:complexType>
+          </xsd:element>
+          <xsd:element name="assembly">
+            <xsd:complexType>
+              <xsd:attribute name="alias" type="xsd:string" />
+              <xsd:attribute name="name" type="xsd:string" />
+            </xsd:complexType>
+          </xsd:element>
+          <xsd:element name="data">
+            <xsd:complexType>
+              <xsd:sequence>
+                <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
+                <xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
+              </xsd:sequence>
+              <xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
+              <xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
+              <xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
+              <xsd:attribute ref="xml:space" />
+            </xsd:complexType>
+          </xsd:element>
+          <xsd:element name="resheader">
+            <xsd:complexType>
+              <xsd:sequence>
+                <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
+              </xsd:sequence>
+              <xsd:attribute name="name" type="xsd:string" use="required" />
+            </xsd:complexType>
+          </xsd:element>
+        </xsd:choice>
+      </xsd:complexType>
+    </xsd:element>
+  </xsd:schema>
+  <resheader name="resmimetype">
+    <value>text/microsoft-resx</value>
+  </resheader>
+  <resheader name="version">
+    <value>2.0</value>
+  </resheader>
+  <resheader name="reader">
+    <value>System.Resources.ResXResourceReader, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
+  </resheader>
+  <resheader name="writer">
+    <value>System.Resources.ResXResourceWriter, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
+  </resheader>
+  <data name="CkanModuleDeserialisationError" xml:space="preserve"><value>JSON deserialization error: {0}</value></data>
+  <data name="LicenceInvalid" xml:space="preserve"><value>The license {0} is invalid</value></data>
+</root>
diff --git a/Core/Properties/Resources.fr-FR.resx b/Core/Properties/Resources.fr-FR.resx
new file mode 100644
index 0000000000..3caccd36b1
--- /dev/null
+++ b/Core/Properties/Resources.fr-FR.resx
@@ -0,0 +1,297 @@
+<?xml version="1.0" encoding="utf-8"?>
+<root>
+  <!--
+    Microsoft ResX Schema
+
+    Version 2.0
+
+    The primary goals of this format is to allow a simple XML format
+    that is mostly human readable. The generation and parsing of the
+    various data types are done through the TypeConverter classes
+    associated with the data types.
+
+    Example:
+
+    ... ado.net/XML headers & schema ...
+    <resheader name="resmimetype">text/microsoft-resx</resheader>
+    <resheader name="version">2.0</resheader>
+    <resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
+    <resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
+    <data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
+    <data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
+    <data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
+        <value>[base64 mime encoded serialized .NET Framework object]</value>
+    </data>
+    <data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
+        <value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
+        <comment>This is a comment</comment>
+    </data>
+
+    There are any number of "resheader" rows that contain simple
+    name/value pairs.
+
+    Each data row contains a name, and value. The row also contains a
+    type or mimetype. Type corresponds to a .NET class that support
+    text/value conversion through the TypeConverter architecture.
+    Classes that don't support this are serialized and stored with the
+    mimetype set.
+
+    The mimetype is used for serialized objects, and tells the
+    ResXResourceReader how to depersist the object. This is currently not
+    extensible. For a given mimetype the value must be set accordingly:
+
+    Note - application/x-microsoft.net.object.binary.base64 is the format
+    that the ResXResourceWriter will generate, however the reader can
+    read any of the formats listed below.
+
+    mimetype: application/x-microsoft.net.object.binary.base64
+    value   : The object must be serialized with
+            : System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
+            : and then encoded with base64 encoding.
+
+    mimetype: application/x-microsoft.net.object.soap.base64
+    value   : The object must be serialized with
+            : System.Runtime.Serialization.Formatters.Soap.SoapFormatter
+            : and then encoded with base64 encoding.
+
+    mimetype: application/x-microsoft.net.object.bytearray.base64
+    value   : The object must be serialized into a byte array
+            : using a System.ComponentModel.TypeConverter
+            : and then encoded with base64 encoding.
+    -->
+  <xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
+    <xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
+    <xsd:element name="root" msdata:IsDataSet="true">
+      <xsd:complexType>
+        <xsd:choice maxOccurs="unbounded">
+          <xsd:element name="metadata">
+            <xsd:complexType>
+              <xsd:sequence>
+                <xsd:element name="value" type="xsd:string" minOccurs="0" />
+              </xsd:sequence>
+              <xsd:attribute name="name" use="required" type="xsd:string" />
+              <xsd:attribute name="type" type="xsd:string" />
+              <xsd:attribute name="mimetype" type="xsd:string" />
+              <xsd:attribute ref="xml:space" />
+            </xsd:complexType>
+          </xsd:element>
+          <xsd:element name="assembly">
+            <xsd:complexType>
+              <xsd:attribute name="alias" type="xsd:string" />
+              <xsd:attribute name="name" type="xsd:string" />
+            </xsd:complexType>
+          </xsd:element>
+          <xsd:element name="data">
+            <xsd:complexType>
+              <xsd:sequence>
+                <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
+                <xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
+              </xsd:sequence>
+              <xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
+              <xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
+              <xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
+              <xsd:attribute ref="xml:space" />
+            </xsd:complexType>
+          </xsd:element>
+          <xsd:element name="resheader">
+            <xsd:complexType>
+              <xsd:sequence>
+                <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
+              </xsd:sequence>
+              <xsd:attribute name="name" type="xsd:string" use="required" />
+            </xsd:complexType>
+          </xsd:element>
+        </xsd:choice>
+      </xsd:complexType>
+    </xsd:element>
+  </xsd:schema>
+  <resheader name="resmimetype">
+    <value>text/microsoft-resx</value>
+  </resheader>
+  <resheader name="version">
+    <value>2.0</value>
+  </resheader>
+  <resheader name="reader">
+    <value>System.Resources.ResXResourceReader, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
+  </resheader>
+  <resheader name="writer">
+    <value>System.Resources.ResXResourceWriter, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
+  </resheader>
+  <data name="AutoUpdateNotFetched" xml:space="preserve"><value>Nous n'avons pas encore reçu les informations de publication. Impossible de mettre à jour.</value></data>
+  <data name="NetDownloading" xml:space="preserve"><value>Téléchargement de {0}</value></data>
+  <data name="NetMissingCertFailed" xml:space="preserve"><value>Échec du téléchargement de {0}</value></data>
+  <data name="NetInvalidLocation" xml:space="preserve"><value>Adresse invalide dans l'en-tête d'emplacement : {0}</value></data>
+  <data name="NetAsyncDownloaderDownloading" xml:space="preserve"><value>Téléchargement de "{0}"</value></data>
+  <data name="NetAsyncDownloaderCancelled" xml:space="preserve"><value>Téléchargement annulé par l'utilisateur</value></data>
+  <data name="NetAsyncDownloaderProgress" xml:space="preserve"><value>{0}/sec - téléchargement en cours - {1} restant</value></data>
+  <data name="NetAsyncDownloaderTryingFallback" xml:space="preserve"><value>Échec du téléchargement de "{0}", essai du lien de secours "{1}"</value></data>
+  <data name="NetFileCacheCannotFind" xml:space="preserve"><value>Impossible de trouver le dossier du cache : {0}</value></data>
+  <data name="NetFileCacheZipError" xml:space="preserve"><value>Erreur à l'étape {0} pour {1} : {2}</value></data>
+  <data name="NetFileCacheZipTestArchiveFalse" xml:space="preserve"><value>ZipFile.TestArchive(true) a renvoyé faux</value></data>
+  <data name="NetFileCacheNullFileName" xml:space="preserve"><value>Nom de fichier null</value></data>
+  <data name="NetFileCacheMonoNotSupported" xml:space="preserve"><value>{0}
+
+Installez le paquet `mono-complete` ou un équivalent pour votre système d'exploitation.</value></data>
+  <data name="NetModuleCacheBadLength" xml:space="preserve"><value>{0} : {1} a une longueur de {2}, au lieu de {3}</value></data>
+  <data name="NetModuleCacheNotValidZIP" xml:space="preserve"><value>{0} : {1} n'est pas un fichier ZIP valide : {2}</value></data>
+  <data name="NetModuleCacheMismatchSHA1" xml:space="preserve"><value>{0} : {1} a un SHA1 de {2}, au lieu de {3}</value></data>
+  <data name="NetModuleCacheMismatchSHA256" xml:space="preserve"><value>{0} : {1} a un SHA256 {2}, au lieu de {3}</value></data>
+  <data name="NetRepoCheckingForUpdates" xml:space="preserve"><value>Recherche de mises à jour</value></data>
+  <data name="NetRepoAlreadyUpToDate" xml:space="preserve"><value>Déjà à jour</value></data>
+  <data name="NetRepoNoChanges" xml:space="preserve"><value>Aucun changement depuis la dernière mise à jour</value></data>
+  <data name="NetRepoUpdating" xml:space="preserve"><value>Mise à jour de {0}</value></data>
+  <data name="NetRepoUpdated" xml:space="preserve"><value>{0} a été mis à jour ({1} modules)</value></data>
+  <data name="NetRepoSaving" xml:space="preserve"><value>Sauvegarde des modules dans le registre</value></data>
+  <data name="NetRepoSaved" xml:space="preserve"><value>Registre mis à jour</value></data>
+  <data name="NetRepoUpdatedAll" xml:space="preserve"><value>Répertoire mis à jour</value></data>
+  <data name="NetRepoNoModules" xml:space="preserve"><value>Aucun module trouvé !</value></data>
+  <data name="NetRepoFailedDownload" xml:space="preserve"><value>Échec du téléchargement de {0} : {1}</value></data>
+  <data name="NetRepoChangedModulesReinstallPrompt" xml:space="preserve"><value>Les mods suivants ont eu leur métadonnées changées depuis la dernière mis à jour :
+
+{0}
+Vous devriez les réinstaller pour préserver la cohérence avec le répertoire.
+
+Souhaitez-vous réinstaller maintenant ?</value></data>
+  <data name="NetRepoInconsistenciesHeader" xml:space="preserve"><value>Les incohérences suivantes ont été trouvées :</value></data>
+  <data name="JsonRelationshipConverterAnyOfCombined" xml:space="preserve"><value>`any_of` ne devrait pas être combiné avec `{0}`</value></data>
+  <data name="RegistryFileConflict" xml:space="preserve"><value>{0} souhaite installer {1}, mais ce fichier appartient à {2}</value></data>
+  <data name="RegistryFileNotRemoved" xml:space="preserve"><value>{0} appartient à {1} mais n'a pas été supprimé !</value></data>
+  <data name="RegistryManagerDirectoryNotFound" xml:space="preserve"><value>Impossible de trouver un dossier dans {0}</value></data>
+  <data name="RegistryManagerExportFilenamePrefix" xml:space="preserve"><value>installé</value></data>
+  <data name="RegistryManagerDefaultModpackAbstract" xml:space="preserve"><value>Une liste des modules installés sur l'instance KSP {0} </value></data>
+  <data name="CkanModuleDeserialisationError" xml:space="preserve"><value>Erreur de désérialisation JSON : {0}</value></data>
+  <data name="CkanModuleUnsupportedSpec" xml:space="preserve"><value>{0} nécessite CKAN {1}, impossible de le lire.</value></data>
+  <data name="CkanModuleMissingRequired" xml:space="preserve"><value>{0} ne possède pas le champ obligatoire {1}</value></data>
+  <data name="CkanModuleKspVersionMixed" xml:space="preserve"><value>ksp_version mélangé avec ksp_version_(min|max)</value></data>
+  <data name="CkanModuleNotAvailable" xml:space="preserve"><value>Le module {0} version {1} n'est pas disponible</value></data>
+  <data name="CkanModuleNotInstalledOrAvailable" xml:space="preserve"><value>Le module {0} n'est pas installé ou indisponible</value></data>
+  <data name="CkanModuleAllVersions" xml:space="preserve"><value>Toutes les versions</value></data>
+  <data name="LicenceInvalid" xml:space="preserve"><value>La licence {0} est invalide</value></data>
+  <data name="ModuleInstallDescriptorMustHaveInstallTo" xml:space="preserve"><value>L'instance d'installation doit avoir une instruction install_to</value></data>
+  <data name="ModuleInstallDescriptorRequireFileFind" xml:space="preserve"><value>L'instance d'installation doit avoir une instruction file, find, ou find_regexp</value></data>
+  <data name="ModuleInstallDescriptorTooManyFileFind" xml:space="preserve"><value>L'instance d'installation doit inclure une seul instruction file, find, ou find_regexp</value></data>
+  <data name="ModuleInstallDescriptorTooManyFilterInclude" xml:space="preserve"><value>L'instance d'installation peut contenir une instruction filter ou include_only, mais pas les deux</value></data>
+  <data name="ModuleInstallDescriptorInvalidInstallPath" xml:space="preserve"><value>Chemin d'installation invalide : {0}</value></data>
+  <data name="ModuleInstallDescriptorUnknownInstallPath" xml:space="preserve"><value>install_to inconnu : {0}</value></data>
+  <data name="ModuleInstallDescriptorNoFilesFound" xml:space="preserve"><value>Aucun fichier correspondant à {0} n'a été trouvé pour installer !</value></data>
+  <data name="ModuleInstallDescriptorAsNoPathSeparators" xml:space="preserve"><value>`as` peut ne pas inclure les séparateurs de chemin</value></data>
+  <data name="RelationshipDescriptorMinVersionOnly" xml:space="preserve"><value>{0} {1} ou plus récente</value></data>
+  <data name="RelationshipDescriptorMaxVersionOnly" xml:space="preserve"><value>{0} {1} ou plus ancienne</value></data>
+  <data name="RelationshipDescriptorAnyOfJoiner" xml:space="preserve"><value>{0} OU {1}</value></data>
+  <data name="ReleaseStatusInvalid" xml:space="preserve"><value>{0} n'est pas un statut de publication valide</value></data>
+  <data name="RepositoryDefaultName" xml:space="preserve"><value>défaut</value></data>
+  <data name="CkanModuleVersionToString" xml:space="preserve"><value>{0} alias {1}</value></data>
+  <data name="GameVersionYalovAny" xml:space="preserve"><value>toutes</value></data>
+  <data name="GameVersionSelectNeedOne" xml:space="preserve"><value>Nécessite au moins un nombre Majeur et Mineur</value></data>
+  <data name="GameVersionSelectHeader" xml:space="preserve"><value>La version spécifiée n'est pas unique, veuillez en choisir une :</value></data>
+  <data name="GameVersionSelectBuildHeader" xml:space="preserve"><value>Le numéro de build est inconnu pour ce patch. Veuillez en sélectionner un :</value></data>
+  <data name="GameVersionNotKnown" xml:space="preserve"><value>Cette version est inconnue de CKAN.</value></data>
+  <data name="GameVersionCriteriaToString" xml:space="preserve"><value>[Versions : {0}]</value></data>
+  <data name="GameVersionRangeMinOnly" xml:space="preserve"><value>{0} {1} ou plus ancienne</value></data>
+  <data name="GameVersionRangeMaxOnly" xml:space="preserve"><value>{0} {1} ou plus récente</value></data>
+  <data name="ProvidesModuleVersionToString" xml:space="preserve"><value>{0} (fourni par {1})</value></data>
+  <data name="UnmanagedModuleVersionUnknown" xml:space="preserve"><value>(non géré)</value></data>
+  <data name="UnmanagedModuleVersionKnown" xml:space="preserve"><value>{0} (non géré)</value></data>
+  <data name="PathUtilsNotAbsolute" xml:space="preserve"><value>{0} n'est pas un chemin absolu</value></data>
+  <data name="PathUtilsNotInside" xml:space="preserve"><value>Oh zut. {0} n'est pas dans {1}</value></data>
+  <data name="PathUtilsAlreadyAbsolute" xml:space="preserve"><value>{0} est déjà absolu</value></data>
+  <data name="PathUtilsNotRoot" xml:space="preserve"><value>{0} n'est pas une racine absolu</value></data>
+  <data name="GameInstanceSettingUp" xml:space="preserve"><value>Mise en place de CKAN pour la première fois...</value></data>
+  <data name="GameInstanceCreatingDir" xml:space="preserve"><value>Création de {0}</value></data>
+  <data name="GameInstanceScanning" xml:space="preserve"><value>Analyse des mods installés...</value></data>
+  <data name="GameInstanceVersionNotFound" xml:space="preserve"><value>Version de jeu introuvable</value></data>
+  <data name="GameInstanceToString" xml:space="preserve"><value>{0} Installation : {1}</value></data>
+  <data name="GameInstanceManagerPortable" xml:space="preserve"><value>portable</value></data>
+  <data name="GameInstanceManagerAuto" xml:space="preserve"><value>Auto {0}</value></data>
+  <data name="GameInstanceCloneInvalid" xml:space="preserve"><value>L'instance spécifiée n'est pas une instance {0} valide</value></data>
+  <data name="GameInstanceFakeBadVersion" xml:space="preserve"><value>La version spécifiée {0} n'est pas une version connue : {1}</value></data>
+  <data name="GameInstanceFakeNotEmpty" xml:space="preserve"><value>Le dossier spécifié existe déjà est n'est pas vide</value></data>
+  <data name="GameInstanceFakeDLCNotAllowed" xml:space="preserve"><value>{0} version {1} ou plus récent est nécessaire pour le DLC {2}</value></data>
+  <data name="GameInstanceNoValidName" xml:space="preserve"><value>Impossible de renvoyer un nom valide pour la nouvelle instance</value></data>
+  <data name="GameInstanceByPathName" xml:space="preserve"><value>personnalisé</value></data>
+  <data name="GameInstancePathNotFound" xml:space="preserve"><value>{0} n'existe pas</value></data>
+  <data name="ModuleInstallerDownloading" xml:space="preserve"><value>Téléchargement de "{0}"</value></data>
+  <data name="ModuleInstallerNothingToInstall" xml:space="preserve"><value>Rien à installer</value></data>
+  <data name="ModuleInstallerAboutToInstall" xml:space="preserve"><value>Vont être installés :</value></data>
+  <data name="ModuleInstallerModuleCached" xml:space="preserve"><value> * {0} {1} (en cache)</value></data>
+  <data name="ModuleInstallerUserDeclined" xml:space="preserve"><value>L'utilisateur a refusé la liste d'installation</value></data>
+  <data name="ModuleInstallerInstallingMod" xml:space="preserve"><value>Installation du mod "{0}"</value></data>
+  <data name="ModuleInstallerUpdatingRegistry" xml:space="preserve"><value>Mise à jour du registre</value></data>
+  <data name="ModuleInstallerCommitting" xml:space="preserve"><value>Validation des changements du système de fichiers</value></data>
+  <data name="ModuleInstallerRescanning" xml:space="preserve"><value>Nouvelle analyse de {0}</value></data>
+  <data name="ModuleInstallerDone" xml:space="preserve"><value>Terminé !</value></data>
+  <data name="ModuleInstallerAlreadyInstalled" xml:space="preserve"><value>{0} {1} déjà installé, ignoré</value></data>
+  <data name="ModuleInstallerZIPNotInCache" xml:space="preserve"><value>Tentative d'installation de {0}, mais il n'est pas téléchargé ou le téléchargement est corrompu</value></data>
+  <data name="ModuleInstallerMetapackage" xml:space="preserve"><value>Les Métapaquets ne peuvent pas être installés !</value></data>
+  <data name="ModuleInstallerDLC" xml:space="preserve"><value>Un DLC ne peut pas être installé !</value></data>
+  <data name="ModuleInstallerBadDLLLocation" xml:space="preserve"><value>Le DLL du module {0} a été trouvé à {1}, mais ce n'est pas là où CKAN l'installerait. Annulation pour éviter que plusieurs copies du même mod soient installées. Pour installer ce module, désinstallez-le manuellement puis réessayez.</value></data>
+  <data name="ModuleInstallerFileSame" xml:space="preserve"><value>Pareil</value></data>
+  <data name="ModuleInstallerFileDifferent" xml:space="preserve"><value>DIFFÉRENT</value></data>
+  <data name="ModuleInstallerOverwrite" xml:space="preserve"><value>Le module {0} veut remplacer les fichiers manuellement installés suivants :
+
+{1}
+
+Remplacer ?</value></data>
+  <data name="ModuleInstallerOverwriteCancelled" xml:space="preserve"><value>Interdiction de remplacer les fichiers manuellement installés, impossible d'installer {0}</value></data>
+  <data name="ModuleInstallerFileExists" xml:space="preserve"><value>Tentative d'écrire {0} mais il existe déjà</value></data>
+  <data name="ModuleInstallerAboutToRemove" xml:space="preserve"><value>Vont être désinstallés :</value></data>
+  <data name="ModuleInstallerContinuePrompt" xml:space="preserve"><value>Continuer ?</value></data>
+  <data name="ModuleInstallerRemoveAborted" xml:space="preserve"><value>Désinstallation du mod annulée à la demande l'utilisateur</value></data>
+  <data name="ModuleInstallerRemovingMod" xml:space="preserve"><value>Désinstallation de {0}...</value></data>
+  <data name="ModuleInstallerAboutToUpgrade" xml:space="preserve"><value>Vont être mis à jour :</value></data>
+  <data name="ModuleInstallerUpgradeInstallingUncached" xml:space="preserve"><value> * Installation : {0} {1} ({2}, {3})</value></data>
+  <data name="ModuleInstallerUpgradeInstallingCached" xml:space="preserve"><value> * Installation : {0} {1} (en cache)</value></data>
+  <data name="ModuleInstallerUpgradeReinstalling" xml:space="preserve"><value> * Réinstallation : {0} {1}</value></data>
+  <data name="ModuleInstallerUpgradeDowngrading" xml:space="preserve"><value> * Rétrogradation : {0} de {1} vers {2}</value></data>
+  <data name="ModuleInstallerUpgradeUpgradingUncached" xml:space="preserve"><value> * Mise à jour : {0} {1} vers {2} ({3}, {4})</value></data>
+  <data name="ModuleInstallerUpgradeUpgradingCached" xml:space="preserve"><value> * Mise à jour : {0} {1} vers {2} (en cache)</value></data>
+  <data name="ModuleInstallerUpgradeUserDeclined" xml:space="preserve"><value>L'utilisateur a refusé la liste de mise à jour</value></data>
+  <data name="ModuleInstallerReplaceAutodetected" xml:space="preserve"><value>Impossible de remplacer {0} car il n'a pas été installé par CKAN.
+Veuillez le retirer manuellement avant d'essayer de l'installer.</value></data>
+  <data name="ModuleInstallerReplaceNotInstalled" xml:space="preserve"><value>Impossible de remplacer {0} car il n'est pas installé. Veuillez essayer d'installer {1} à la place.</value></data>
+  <data name="ModuleInstallerImporting" xml:space="preserve"><value>Importation de {0}... ({1}%)</value></data>
+  <data name="ModuleInstallerImportAlreadyCached" xml:space="preserve"><value>Already cached : {0}</value></data>
+  <data name="ModuleInstallerImportingMod" xml:space="preserve"><value>Importing {0} {1}...</value></data>
+  <data name="ModuleInstallerImportNotFound" xml:space="preserve"><value>Introuvable dans l'index : {0}</value></data>
+  <data name="ModuleInstallerImportInstallPrompt" xml:space="preserve"><value>Installer {0} les mods importés compatibles dans l'instance de jeu {1} ({2}) ?</value></data>
+  <data name="ModuleInstallerImportDeletePrompt" xml:space="preserve"><value>Importation terminée. Supprimer {0} vieux fichiers ?</value></data>
+  <data name="KrakenDependencyNotSatisfied" xml:space="preserve"><value>La dépendance sur {0} version {1} n'est pas satisfaite</value></data>
+  <data name="KrakenDependencyModuleNotFound" xml:space="preserve"><value>Module introuvable : {0} {1}</value></data>
+  <data name="KrakenParentDependencyNotSatisfied" xml:space="preserve"><value>La dépendance de {0} sur {1} version {2} n'est pas satisfaite</value></data>
+  <data name="KrakenAny" xml:space="preserve"><value>(any)</value></data>
+  <data name="KrakenProvidedByMoreThanOne" xml:space="preserve"><value>Le module {0} est fourni par plusieurs modules possibles. Veuillez en choisir un parmi les suivants :</value></data>
+  <data name="KrakenInconsistenciesHeader" xml:space="preserve"><value>Les incohérences suivantes ont été trouvées :</value></data>
+  <data name="KrakenMissingDependency" xml:space="preserve"><value>{0} n'a pas sa dépendance {1}</value></data>
+  <data name="KrakenConflictsWith" xml:space="preserve"><value>{0} est en conflit avec {1}</value></data>
+  <data name="KrakenDownloadErrorsHeader" xml:space="preserve"><value>Oh oh, Les choses suivantes se sont mal passées lors du téléchargement...</value></data>
+  <data name="KrakenModuleDownloadErrorsHeader" xml:space="preserve"><value>Un ou plusieurs téléchargement·s ont échoué :</value></data>
+  <data name="KrakenModuleDownloadError" xml:space="preserve"><value>Erreur lors du téléchargement de {0} : {1}</value></data>
+  <data name="KrakenNotInstalled" xml:space="preserve"><value>Le module {0} n'est pas installé !</value></data>
+  <data name="KrakenMissingCertificateUnix" xml:space="preserve"><value>Oh non ! Le téléchargement a échoué avec une erreur de certificat !
+
+Consultez cette page pour obtenir de l'aide :
+https://github.com/KSP-CKAN/CKAN/wiki/SSL-certificate-errors</value></data>
+  <data name="KrakenMissingCertificateNotUnix" xml:space="preserve"><value>Oh non ! Le téléchargement a échoué avec une erreur de certificat !</value></data>
+  <data name="KrakenDownloadThrottled" xml:space="preserve"><value>Le téléchargement depuis {0} a été bridé.
+Pensez à ajouter un jeton d'authentication pour augmenter la limite avant bridage.</value></data>
+  <data name="KrakenAlreadyRunning" xml:space="preserve"><value>CKAN est déjà lancé pour cette instance !
+
+Si vous êtes certain que ce n'est pas le cas, alors supprimez :
+"{0}"</value></data>
+  <data name="KrakenReinstallModule" xml:space="preserve"><value>Changement de Métadonnées, réinstallation recommandée : {0}</value></data>
+  <data name="RelationshipResolverConflictsWith" xml:space="preserve"><value>{0} est en conflit avec {1}</value></data>
+  <data name="RelationshipResolverRequiredButResolver" xml:space="preserve"><value>{0} nécessaire, mais une version incompatible est dans le résolveur</value></data>
+  <data name="RelationshipResolverRequiredButInstalled" xml:space="preserve"><value>{0} nécessaire, mais une version incompatible est installée</value></data>
+  <data name="RelationshipResolverAnUnmanaged" xml:space="preserve"><value>Un DLL ou DLC non-géré</value></data>
+  <data name="RelationshipResolverUnmanaged" xml:space="preserve"><value>Non-géré</value></data>
+  <data name="RelationshipResolverModNotInList" xml:space="preserve"><value>Le mod {0} n'est pas dans la liste</value></data>
+  <data name="RelationshipResolverInstalledReason" xml:space="preserve"><value>Installé actuellement</value></data>
+  <data name="RelationshipResolverUserReason" xml:space="preserve"><value>Demandé par l'utilisateur</value></data>
+  <data name="RelationshipResolverNoLongerUsedReason" xml:space="preserve"><value>Auto-installé, le module qui en dépendait a été désinstallé</value></data>
+  <data name="RelationshipResolverReplacementReason" xml:space="preserve"><value>Remplacement de {0}</value></data>
+  <data name="RelationshipResolverSuggestedReason" xml:space="preserve"><value>Suggéré par {0}</value></data>
+  <data name="RelationshipResolverDependsReason" xml:space="preserve"><value>Pour satisfaire la dépendance de {0}</value></data>
+  <data name="RelationshipResolverRecommendedReason" xml:space="preserve"><value>Recommandé par {0}</value></data>
+  <data name="SanityCheckerUnsatisfiedDependency" xml:space="preserve"><value>{0} a une dépendance non-satisfaite : {1} n'est pas installé</value></data>
+  <data name="SanityCheckerConflictsWith" xml:space="preserve"><value>{0} est en conflit avec {1}</value></data>
+</root>
diff --git a/Core/Properties/Resources.pt-BR.resx b/Core/Properties/Resources.pt-BR.resx
new file mode 100644
index 0000000000..8498c3ad09
--- /dev/null
+++ b/Core/Properties/Resources.pt-BR.resx
@@ -0,0 +1,121 @@
+<?xml version="1.0" encoding="utf-8"?>
+<root>
+  <!--
+    Microsoft ResX Schema
+
+    Version 2.0
+
+    The primary goals of this format is to allow a simple XML format
+    that is mostly human readable. The generation and parsing of the
+    various data types are done through the TypeConverter classes
+    associated with the data types.
+
+    Example:
+
+    ... ado.net/XML headers & schema ...
+    <resheader name="resmimetype">text/microsoft-resx</resheader>
+    <resheader name="version">2.0</resheader>
+    <resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
+    <resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
+    <data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
+    <data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
+    <data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
+        <value>[base64 mime encoded serialized .NET Framework object]</value>
+    </data>
+    <data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
+        <value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
+        <comment>This is a comment</comment>
+    </data>
+
+    There are any number of "resheader" rows that contain simple
+    name/value pairs.
+
+    Each data row contains a name, and value. The row also contains a
+    type or mimetype. Type corresponds to a .NET class that support
+    text/value conversion through the TypeConverter architecture.
+    Classes that don't support this are serialized and stored with the
+    mimetype set.
+
+    The mimetype is used for serialized objects, and tells the
+    ResXResourceReader how to depersist the object. This is currently not
+    extensible. For a given mimetype the value must be set accordingly:
+
+    Note - application/x-microsoft.net.object.binary.base64 is the format
+    that the ResXResourceWriter will generate, however the reader can
+    read any of the formats listed below.
+
+    mimetype: application/x-microsoft.net.object.binary.base64
+    value   : The object must be serialized with
+            : System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
+            : and then encoded with base64 encoding.
+
+    mimetype: application/x-microsoft.net.object.soap.base64
+    value   : The object must be serialized with
+            : System.Runtime.Serialization.Formatters.Soap.SoapFormatter
+            : and then encoded with base64 encoding.
+
+    mimetype: application/x-microsoft.net.object.bytearray.base64
+    value   : The object must be serialized into a byte array
+            : using a System.ComponentModel.TypeConverter
+            : and then encoded with base64 encoding.
+    -->
+  <xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
+    <xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
+    <xsd:element name="root" msdata:IsDataSet="true">
+      <xsd:complexType>
+        <xsd:choice maxOccurs="unbounded">
+          <xsd:element name="metadata">
+            <xsd:complexType>
+              <xsd:sequence>
+                <xsd:element name="value" type="xsd:string" minOccurs="0" />
+              </xsd:sequence>
+              <xsd:attribute name="name" use="required" type="xsd:string" />
+              <xsd:attribute name="type" type="xsd:string" />
+              <xsd:attribute name="mimetype" type="xsd:string" />
+              <xsd:attribute ref="xml:space" />
+            </xsd:complexType>
+          </xsd:element>
+          <xsd:element name="assembly">
+            <xsd:complexType>
+              <xsd:attribute name="alias" type="xsd:string" />
+              <xsd:attribute name="name" type="xsd:string" />
+            </xsd:complexType>
+          </xsd:element>
+          <xsd:element name="data">
+            <xsd:complexType>
+              <xsd:sequence>
+                <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
+                <xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
+              </xsd:sequence>
+              <xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
+              <xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
+              <xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
+              <xsd:attribute ref="xml:space" />
+            </xsd:complexType>
+          </xsd:element>
+          <xsd:element name="resheader">
+            <xsd:complexType>
+              <xsd:sequence>
+                <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
+              </xsd:sequence>
+              <xsd:attribute name="name" type="xsd:string" use="required" />
+            </xsd:complexType>
+          </xsd:element>
+        </xsd:choice>
+      </xsd:complexType>
+    </xsd:element>
+  </xsd:schema>
+  <resheader name="resmimetype">
+    <value>text/microsoft-resx</value>
+  </resheader>
+  <resheader name="version">
+    <value>2.0</value>
+  </resheader>
+  <resheader name="reader">
+    <value>System.Resources.ResXResourceReader, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
+  </resheader>
+  <resheader name="writer">
+    <value>System.Resources.ResXResourceWriter, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
+  </resheader>
+  <data name="RelationshipResolverUserReason" xml:space="preserve"><value>Nova instalação de mod selecionada pelo usuário</value></data>
+</root>
diff --git a/Core/Properties/Resources.resx b/Core/Properties/Resources.resx
new file mode 100644
index 0000000000..32e0ac7ec2
--- /dev/null
+++ b/Core/Properties/Resources.resx
@@ -0,0 +1,297 @@
+<?xml version="1.0" encoding="utf-8"?>
+<root>
+  <!--
+    Microsoft ResX Schema
+
+    Version 2.0
+
+    The primary goals of this format is to allow a simple XML format
+    that is mostly human readable. The generation and parsing of the
+    various data types are done through the TypeConverter classes
+    associated with the data types.
+
+    Example:
+
+    ... ado.net/XML headers & schema ...
+    <resheader name="resmimetype">text/microsoft-resx</resheader>
+    <resheader name="version">2.0</resheader>
+    <resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
+    <resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
+    <data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
+    <data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
+    <data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
+        <value>[base64 mime encoded serialized .NET Framework object]</value>
+    </data>
+    <data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
+        <value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
+        <comment>This is a comment</comment>
+    </data>
+
+    There are any number of "resheader" rows that contain simple
+    name/value pairs.
+
+    Each data row contains a name, and value. The row also contains a
+    type or mimetype. Type corresponds to a .NET class that support
+    text/value conversion through the TypeConverter architecture.
+    Classes that don't support this are serialized and stored with the
+    mimetype set.
+
+    The mimetype is used for serialized objects, and tells the
+    ResXResourceReader how to depersist the object. This is currently not
+    extensible. For a given mimetype the value must be set accordingly:
+
+    Note - application/x-microsoft.net.object.binary.base64 is the format
+    that the ResXResourceWriter will generate, however the reader can
+    read any of the formats listed below.
+
+    mimetype: application/x-microsoft.net.object.binary.base64
+    value   : The object must be serialized with
+            : System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
+            : and then encoded with base64 encoding.
+
+    mimetype: application/x-microsoft.net.object.soap.base64
+    value   : The object must be serialized with
+            : System.Runtime.Serialization.Formatters.Soap.SoapFormatter
+            : and then encoded with base64 encoding.
+
+    mimetype: application/x-microsoft.net.object.bytearray.base64
+    value   : The object must be serialized into a byte array
+            : using a System.ComponentModel.TypeConverter
+            : and then encoded with base64 encoding.
+    -->
+  <xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
+    <xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
+    <xsd:element name="root" msdata:IsDataSet="true">
+      <xsd:complexType>
+        <xsd:choice maxOccurs="unbounded">
+          <xsd:element name="metadata">
+            <xsd:complexType>
+              <xsd:sequence>
+                <xsd:element name="value" type="xsd:string" minOccurs="0" />
+              </xsd:sequence>
+              <xsd:attribute name="name" use="required" type="xsd:string" />
+              <xsd:attribute name="type" type="xsd:string" />
+              <xsd:attribute name="mimetype" type="xsd:string" />
+              <xsd:attribute ref="xml:space" />
+            </xsd:complexType>
+          </xsd:element>
+          <xsd:element name="assembly">
+            <xsd:complexType>
+              <xsd:attribute name="alias" type="xsd:string" />
+              <xsd:attribute name="name" type="xsd:string" />
+            </xsd:complexType>
+          </xsd:element>
+          <xsd:element name="data">
+            <xsd:complexType>
+              <xsd:sequence>
+                <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
+                <xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
+              </xsd:sequence>
+              <xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
+              <xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
+              <xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
+              <xsd:attribute ref="xml:space" />
+            </xsd:complexType>
+          </xsd:element>
+          <xsd:element name="resheader">
+            <xsd:complexType>
+              <xsd:sequence>
+                <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
+              </xsd:sequence>
+              <xsd:attribute name="name" type="xsd:string" use="required" />
+            </xsd:complexType>
+          </xsd:element>
+        </xsd:choice>
+      </xsd:complexType>
+    </xsd:element>
+  </xsd:schema>
+  <resheader name="resmimetype">
+    <value>text/microsoft-resx</value>
+  </resheader>
+  <resheader name="version">
+    <value>2.0</value>
+  </resheader>
+  <resheader name="reader">
+    <value>System.Resources.ResXResourceReader, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
+  </resheader>
+  <resheader name="writer">
+    <value>System.Resources.ResXResourceWriter, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
+  </resheader>
+  <data name="AutoUpdateNotFetched" xml:space="preserve"><value>We have not fetched the release info yet. Can't update.</value></data>
+  <data name="NetDownloading" xml:space="preserve"><value>Downloading {0}</value></data>
+  <data name="NetMissingCertFailed" xml:space="preserve"><value>Failed downloading {0}</value></data>
+  <data name="NetInvalidLocation" xml:space="preserve"><value>Invalid URL in Location header: {0}</value></data>
+  <data name="NetAsyncDownloaderDownloading" xml:space="preserve"><value>Downloading "{0}"</value></data>
+  <data name="NetAsyncDownloaderCancelled" xml:space="preserve"><value>Download cancelled by user</value></data>
+  <data name="NetAsyncDownloaderProgress" xml:space="preserve"><value>{0}/sec - downloading - {1} left</value></data>
+  <data name="NetAsyncDownloaderTryingFallback" xml:space="preserve"><value>Failed to download "{0}", trying fallback "{1}"</value></data>
+  <data name="NetFileCacheCannotFind" xml:space="preserve"><value>Cannot find cache directory: {0}</value></data>
+  <data name="NetFileCacheZipError" xml:space="preserve"><value>Error in step {0} for {1}: {2}</value></data>
+  <data name="NetFileCacheZipTestArchiveFalse" xml:space="preserve"><value>ZipFile.TestArchive(true) returned false</value></data>
+  <data name="NetFileCacheNullFileName" xml:space="preserve"><value>Null file name</value></data>
+  <data name="NetFileCacheMonoNotSupported" xml:space="preserve"><value>{0}
+
+Install the `mono-complete` package or equivalent for your operating system.</value></data>
+  <data name="NetModuleCacheBadLength" xml:space="preserve"><value>{0}: {1} has length {2}, should be {3}</value></data>
+  <data name="NetModuleCacheNotValidZIP" xml:space="preserve"><value>{0}: {1} is not a valid ZIP file: {2}</value></data>
+  <data name="NetModuleCacheMismatchSHA1" xml:space="preserve"><value>{0}: {1} has SHA1 {2}, should be {3}</value></data>
+  <data name="NetModuleCacheMismatchSHA256" xml:space="preserve"><value>{0}: {1} has SHA256 {2}, should be {3}</value></data>
+  <data name="NetRepoCheckingForUpdates" xml:space="preserve"><value>Checking for updates</value></data>
+  <data name="NetRepoAlreadyUpToDate" xml:space="preserve"><value>Already up to date</value></data>
+  <data name="NetRepoNoChanges" xml:space="preserve"><value>No changes since last update</value></data>
+  <data name="NetRepoUpdating" xml:space="preserve"><value>Updating {0}</value></data>
+  <data name="NetRepoUpdated" xml:space="preserve"><value>Updated {0} ({1} modules)</value></data>
+  <data name="NetRepoSaving" xml:space="preserve"><value>Saving modules to registry</value></data>
+  <data name="NetRepoSaved" xml:space="preserve"><value>Registry saved</value></data>
+  <data name="NetRepoUpdatedAll" xml:space="preserve"><value>Repositories updated</value></data>
+  <data name="NetRepoNoModules" xml:space="preserve"><value>No modules found!</value></data>
+  <data name="NetRepoFailedDownload" xml:space="preserve"><value>Failed to download {0}: {1}</value></data>
+  <data name="NetRepoChangedModulesReinstallPrompt" xml:space="preserve"><value>The following mods have had their metadata changed since last update:
+
+{0}
+You should reinstall them in order to preserve consistency with the repository.
+
+Do you wish to reinstall now?</value></data>
+  <data name="NetRepoInconsistenciesHeader" xml:space="preserve"><value>The following inconsistencies were found:</value></data>
+  <data name="JsonRelationshipConverterAnyOfCombined" xml:space="preserve"><value>`any_of` should not be combined with `{0}`</value></data>
+  <data name="RegistryFileConflict" xml:space="preserve"><value>{0} wishes to install {1}, but this file is registered to {2}</value></data>
+  <data name="RegistryFileNotRemoved" xml:space="preserve"><value>{0} is registered to {1} but has not been removed!</value></data>
+  <data name="RegistryManagerDirectoryNotFound" xml:space="preserve"><value>Can't find a directory in {0}</value></data>
+  <data name="RegistryManagerExportFilenamePrefix" xml:space="preserve"><value>installed</value></data>
+  <data name="RegistryManagerDefaultModpackAbstract" xml:space="preserve"><value>A list of modules installed on the {0} KSP instance</value></data>
+  <data name="CkanModuleDeserialisationError" xml:space="preserve"><value>JSON deserialisation error: {0}</value></data>
+  <data name="CkanModuleUnsupportedSpec" xml:space="preserve"><value>{0} requires CKAN {1}, we can't read it.</value></data>
+  <data name="CkanModuleMissingRequired" xml:space="preserve"><value>{0} missing required field {1}</value></data>
+  <data name="CkanModuleKspVersionMixed" xml:space="preserve"><value>ksp_version mixed with ksp_version_(min|max)</value></data>
+  <data name="CkanModuleNotAvailable" xml:space="preserve"><value>Module {0} version {1} not available</value></data>
+  <data name="CkanModuleNotInstalledOrAvailable" xml:space="preserve"><value>Module {0} not installed or available</value></data>
+  <data name="CkanModuleAllVersions" xml:space="preserve"><value>All versions</value></data>
+  <data name="LicenceInvalid" xml:space="preserve"><value>The licence {0} is invalid</value></data>
+  <data name="ModuleInstallDescriptorMustHaveInstallTo" xml:space="preserve"><value>Install stanzas must have an install_to</value></data>
+  <data name="ModuleInstallDescriptorRequireFileFind" xml:space="preserve"><value>Install stanzas require either a file, find, or find_regexp directive</value></data>
+  <data name="ModuleInstallDescriptorTooManyFileFind" xml:space="preserve"><value>Install stanzas must only include one of file, find, or find_regexp directive</value></data>
+  <data name="ModuleInstallDescriptorTooManyFilterInclude" xml:space="preserve"><value>Install stanzas can only contain filter or include_only directives, not both</value></data>
+  <data name="ModuleInstallDescriptorInvalidInstallPath" xml:space="preserve"><value>Invalid installation path: {0}</value></data>
+  <data name="ModuleInstallDescriptorUnknownInstallPath" xml:space="preserve"><value>Unknown install_to: {0}</value></data>
+  <data name="ModuleInstallDescriptorNoFilesFound" xml:space="preserve"><value>No files found matching {0} to install!</value></data>
+  <data name="ModuleInstallDescriptorAsNoPathSeparators" xml:space="preserve"><value>`as` may not include path separators</value></data>
+  <data name="RelationshipDescriptorMinVersionOnly" xml:space="preserve"><value>{0} {1} or later</value></data>
+  <data name="RelationshipDescriptorMaxVersionOnly" xml:space="preserve"><value>{0} {1} or earlier</value></data>
+  <data name="RelationshipDescriptorAnyOfJoiner" xml:space="preserve"><value>{0} OR {1}</value></data>
+  <data name="ReleaseStatusInvalid" xml:space="preserve"><value>{0} is not a valid release status</value></data>
+  <data name="RepositoryDefaultName" xml:space="preserve"><value>default</value></data>
+  <data name="CkanModuleVersionToString" xml:space="preserve"><value>{0} aka {1}</value></data>
+  <data name="GameVersionYalovAny" xml:space="preserve"><value>any</value></data>
+  <data name="GameVersionSelectNeedOne" xml:space="preserve"><value>Needs at least Major and Minor</value></data>
+  <data name="GameVersionSelectHeader" xml:space="preserve"><value>The specified version is not unique, please select one:</value></data>
+  <data name="GameVersionSelectBuildHeader" xml:space="preserve"><value>The build number is not known for this patch. Please select one:</value></data>
+  <data name="GameVersionNotKnown" xml:space="preserve"><value>The version is not known to CKAN.</value></data>
+  <data name="GameVersionCriteriaToString" xml:space="preserve"><value>[Versions: {0}]</value></data>
+  <data name="GameVersionRangeMinOnly" xml:space="preserve"><value>{0} {1} and earlier</value></data>
+  <data name="GameVersionRangeMaxOnly" xml:space="preserve"><value>{0} {1} and later</value></data>
+  <data name="ProvidesModuleVersionToString" xml:space="preserve"><value>{0} (provided by {1})</value></data>
+  <data name="UnmanagedModuleVersionUnknown" xml:space="preserve"><value>(unmanaged)</value></data>
+  <data name="UnmanagedModuleVersionKnown" xml:space="preserve"><value>{0} (unmanaged)</value></data>
+  <data name="PathUtilsNotAbsolute" xml:space="preserve"><value>{0} is not an absolute path</value></data>
+  <data name="PathUtilsNotInside" xml:space="preserve"><value>Oh snap. {0} isn't inside {1}</value></data>
+  <data name="PathUtilsAlreadyAbsolute" xml:space="preserve"><value>{0} is already absolute</value></data>
+  <data name="PathUtilsNotRoot" xml:space="preserve"><value>{0} isn't an absolute root</value></data>
+  <data name="GameInstanceSettingUp" xml:space="preserve"><value>Setting up CKAN for the first time...</value></data>
+  <data name="GameInstanceCreatingDir" xml:space="preserve"><value>Creating {0}</value></data>
+  <data name="GameInstanceScanning" xml:space="preserve"><value>Scanning for installed mods...</value></data>
+  <data name="GameInstanceVersionNotFound" xml:space="preserve"><value>Game version not found</value></data>
+  <data name="GameInstanceToString" xml:space="preserve"><value>{0} Install: {1}</value></data>
+  <data name="GameInstanceManagerPortable" xml:space="preserve"><value>portable</value></data>
+  <data name="GameInstanceManagerAuto" xml:space="preserve"><value>Auto {0}</value></data>
+  <data name="GameInstanceCloneInvalid" xml:space="preserve"><value>The specified instance is not a valid {0} instance</value></data>
+  <data name="GameInstanceFakeBadVersion" xml:space="preserve"><value>The specified {0} version is not a known version: {1}</value></data>
+  <data name="GameInstanceFakeNotEmpty" xml:space="preserve"><value>The specified folder already exists and is not empty</value></data>
+  <data name="GameInstanceFakeDLCNotAllowed" xml:space="preserve"><value>{0} version {1} or above is needed for {2} DLC</value></data>
+  <data name="GameInstanceNoValidName" xml:space="preserve"><value>Could not return a valid name for the new instance</value></data>
+  <data name="GameInstanceByPathName" xml:space="preserve"><value>custom</value></data>
+  <data name="GameInstancePathNotFound" xml:space="preserve"><value>{0} does not exist</value></data>
+  <data name="ModuleInstallerDownloading" xml:space="preserve"><value>Downloading "{0}"</value></data>
+  <data name="ModuleInstallerNothingToInstall" xml:space="preserve"><value>Nothing to install</value></data>
+  <data name="ModuleInstallerAboutToInstall" xml:space="preserve"><value>About to install:</value></data>
+  <data name="ModuleInstallerModuleCached" xml:space="preserve"><value> * {0} {1} (cached)</value></data>
+  <data name="ModuleInstallerUserDeclined" xml:space="preserve"><value>User declined install list</value></data>
+  <data name="ModuleInstallerInstallingMod" xml:space="preserve"><value>Installing mod "{0}"</value></data>
+  <data name="ModuleInstallerUpdatingRegistry" xml:space="preserve"><value>Updating registry</value></data>
+  <data name="ModuleInstallerCommitting" xml:space="preserve"><value>Committing filesystem changes</value></data>
+  <data name="ModuleInstallerRescanning" xml:space="preserve"><value>Rescanning {0}</value></data>
+  <data name="ModuleInstallerDone" xml:space="preserve"><value>Done!</value></data>
+  <data name="ModuleInstallerAlreadyInstalled" xml:space="preserve"><value>{0} {1} already installed, skipped</value></data>
+  <data name="ModuleInstallerZIPNotInCache" xml:space="preserve"><value>Trying to install {0}, but it's not downloaded or download is corrupted</value></data>
+  <data name="ModuleInstallerMetapackage" xml:space="preserve"><value>Metapackages cannot be installed!</value></data>
+  <data name="ModuleInstallerDLC" xml:space="preserve"><value>DLC cannot be installed!</value></data>
+  <data name="ModuleInstallerBadDLLLocation" xml:space="preserve"><value>DLL for module {0} found at {1}, but it's not where CKAN would install it. Aborting to prevent multiple copies of the same mod being installed. To install this module, uninstall it manually and try again.</value></data>
+  <data name="ModuleInstallerFileSame" xml:space="preserve"><value>same</value></data>
+  <data name="ModuleInstallerFileDifferent" xml:space="preserve"><value>DIFFERENT</value></data>
+  <data name="ModuleInstallerOverwrite" xml:space="preserve"><value>Module {0} wants to overwrite the following manually installed files:
+
+{1}
+
+Overwrite?</value></data>
+  <data name="ModuleInstallerOverwriteCancelled" xml:space="preserve"><value>Not overwriting manually installed files, can't install {0}</value></data>
+  <data name="ModuleInstallerFileExists" xml:space="preserve"><value>Trying to write {0} but it already exists</value></data>
+  <data name="ModuleInstallerAboutToRemove" xml:space="preserve"><value>About to remove:</value></data>
+  <data name="ModuleInstallerContinuePrompt" xml:space="preserve"><value>Continue?</value></data>
+  <data name="ModuleInstallerRemoveAborted" xml:space="preserve"><value>Mod removal aborted at user request</value></data>
+  <data name="ModuleInstallerRemovingMod" xml:space="preserve"><value>Removing {0}...</value></data>
+  <data name="ModuleInstallerAboutToUpgrade" xml:space="preserve"><value>About to upgrade:</value></data>
+  <data name="ModuleInstallerUpgradeInstallingUncached" xml:space="preserve"><value> * Install: {0} {1} ({2}, {3})</value></data>
+  <data name="ModuleInstallerUpgradeInstallingCached" xml:space="preserve"><value> * Install: {0} {1} (cached)</value></data>
+  <data name="ModuleInstallerUpgradeReinstalling" xml:space="preserve"><value> * Re-install: {0} {1}</value></data>
+  <data name="ModuleInstallerUpgradeDowngrading" xml:space="preserve"><value> * Downgrade: {0} from {1} to {2}</value></data>
+  <data name="ModuleInstallerUpgradeUpgradingUncached" xml:space="preserve"><value> * Upgrade: {0} {1} to {2} ({3}, {4})</value></data>
+  <data name="ModuleInstallerUpgradeUpgradingCached" xml:space="preserve"><value> * Upgrade: {0} {1} to {2} (cached)</value></data>
+  <data name="ModuleInstallerUpgradeUserDeclined" xml:space="preserve"><value>User declined upgrade list</value></data>
+  <data name="ModuleInstallerReplaceAutodetected" xml:space="preserve"><value>Can't replace {0} as it was not installed by CKAN.
+Please remove manually before trying to install it.</value></data>
+  <data name="ModuleInstallerReplaceNotInstalled" xml:space="preserve"><value>Can't replace {0} as it is not installed. Please attempt to install {1} instead.</value></data>
+  <data name="ModuleInstallerImporting" xml:space="preserve"><value>Importing {0}... ({1}%)</value></data>
+  <data name="ModuleInstallerImportAlreadyCached" xml:space="preserve"><value>Already cached: {0}</value></data>
+  <data name="ModuleInstallerImportingMod" xml:space="preserve"><value>Importing {0} {1}...</value></data>
+  <data name="ModuleInstallerImportNotFound" xml:space="preserve"><value>Not found in index: {0}</value></data>
+  <data name="ModuleInstallerImportInstallPrompt" xml:space="preserve"><value>Install {0} compatible imported mods in game instance {1} ({2})?</value></data>
+  <data name="ModuleInstallerImportDeletePrompt" xml:space="preserve"><value>Import complete. Delete {0} old files?</value></data>
+  <data name="KrakenDependencyNotSatisfied" xml:space="preserve"><value>Dependency on {0} version {1} not satisfied</value></data>
+  <data name="KrakenDependencyModuleNotFound" xml:space="preserve"><value>Module not found: {0} {1}</value></data>
+  <data name="KrakenParentDependencyNotSatisfied" xml:space="preserve"><value>{0} dependency on {1} version {2} not satisfied</value></data>
+  <data name="KrakenAny" xml:space="preserve"><value>(any)</value></data>
+  <data name="KrakenProvidedByMoreThanOne" xml:space="preserve"><value>Module {0} is provided by more than one available module. Please choose one of the following:</value></data>
+  <data name="KrakenInconsistenciesHeader" xml:space="preserve"><value>The following inconsistencies were found:</value></data>
+  <data name="KrakenMissingDependency" xml:space="preserve"><value>{0} missing dependency {1}</value></data>
+  <data name="KrakenConflictsWith" xml:space="preserve"><value>{0} conflicts with {1}</value></data>
+  <data name="KrakenDownloadErrorsHeader" xml:space="preserve"><value>Uh oh, the following things went wrong when downloading...</value></data>
+  <data name="KrakenModuleDownloadErrorsHeader" xml:space="preserve"><value>One or more downloads were unsuccessful:</value></data>
+  <data name="KrakenModuleDownloadError" xml:space="preserve"><value>Error downloading {0}: {1}</value></data>
+  <data name="KrakenNotInstalled" xml:space="preserve"><value>Module {0} is not installed!</value></data>
+  <data name="KrakenMissingCertificateUnix" xml:space="preserve"><value>Oh no! Our download failed with a certificate error!
+
+Consult this page for help:
+{0}</value></data>
+  <data name="KrakenMissingCertificateNotUnix" xml:space="preserve"><value>Oh no! Our download failed with a certificate error!</value></data>
+  <data name="KrakenDownloadThrottled" xml:space="preserve"><value>Download from {0} was throttled.
+Consider adding an authentication token to increase the throttling limit.</value></data>
+  <data name="KrakenAlreadyRunning" xml:space="preserve"><value>CKAN is already running for this instance!
+
+If you're certain this is not the case, then delete:
+"{0}"</value></data>
+  <data name="KrakenReinstallModule" xml:space="preserve"><value>Metadata changed, reinstallation recommended: {0}</value></data>
+  <data name="RelationshipResolverConflictsWith" xml:space="preserve"><value>{0} conflicts with {1}</value></data>
+  <data name="RelationshipResolverRequiredButResolver" xml:space="preserve"><value>{0} required, but an incompatible version is in the resolver</value></data>
+  <data name="RelationshipResolverRequiredButInstalled" xml:space="preserve"><value>{0} required, but an incompatible version is installed</value></data>
+  <data name="RelationshipResolverAnUnmanaged" xml:space="preserve"><value>an unmanaged DLL or DLC</value></data>
+  <data name="RelationshipResolverUnmanaged" xml:space="preserve"><value>Unmanaged</value></data>
+  <data name="RelationshipResolverModNotInList" xml:space="preserve"><value>Mod {0} is not in the list</value></data>
+  <data name="RelationshipResolverInstalledReason" xml:space="preserve"><value>Currently installed</value></data>
+  <data name="RelationshipResolverUserReason" xml:space="preserve"><value>Requested by user</value></data>
+  <data name="RelationshipResolverNoLongerUsedReason" xml:space="preserve"><value>Auto-installed, depending modules removed</value></data>
+  <data name="RelationshipResolverReplacementReason" xml:space="preserve"><value>Replacing {0}</value></data>
+  <data name="RelationshipResolverSuggestedReason" xml:space="preserve"><value>Suggested by {0}</value></data>
+  <data name="RelationshipResolverDependsReason" xml:space="preserve"><value>To satisfy dependency from {0}</value></data>
+  <data name="RelationshipResolverRecommendedReason" xml:space="preserve"><value>Recommended by {0}</value></data>
+  <data name="SanityCheckerUnsatisfiedDependency" xml:space="preserve"><value>{0} has an unsatisfied dependency: {1} is not installed</value></data>
+  <data name="SanityCheckerConflictsWith" xml:space="preserve"><value>{0} conflicts with {1}</value></data>
+</root>
diff --git a/Core/Properties/Resources.ru-RU.resx b/Core/Properties/Resources.ru-RU.resx
new file mode 100644
index 0000000000..90557b3910
--- /dev/null
+++ b/Core/Properties/Resources.ru-RU.resx
@@ -0,0 +1,297 @@
+<?xml version="1.0" encoding="utf-8"?>
+<root>
+  <!--
+    Microsoft ResX Schema
+
+    Version 2.0
+
+    The primary goals of this format is to allow a simple XML format
+    that is mostly human readable. The generation and parsing of the
+    various data types are done through the TypeConverter classes
+    associated with the data types.
+
+    Example:
+
+    ... ado.net/XML headers & schema ...
+    <resheader name="resmimetype">text/microsoft-resx</resheader>
+    <resheader name="version">2.0</resheader>
+    <resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
+    <resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
+    <data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
+    <data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
+    <data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
+        <value>[base64 mime encoded serialized .NET Framework object]</value>
+    </data>
+    <data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
+        <value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
+        <comment>This is a comment</comment>
+    </data>
+
+    There are any number of "resheader" rows that contain simple
+    name/value pairs.
+
+    Each data row contains a name, and value. The row also contains a
+    type or mimetype. Type corresponds to a .NET class that support
+    text/value conversion through the TypeConverter architecture.
+    Classes that don't support this are serialized and stored with the
+    mimetype set.
+
+    The mimetype is used for serialized objects, and tells the
+    ResXResourceReader how to depersist the object. This is currently not
+    extensible. For a given mimetype the value must be set accordingly:
+
+    Note - application/x-microsoft.net.object.binary.base64 is the format
+    that the ResXResourceWriter will generate, however the reader can
+    read any of the formats listed below.
+
+    mimetype: application/x-microsoft.net.object.binary.base64
+    value   : The object must be serialized with
+            : System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
+            : and then encoded with base64 encoding.
+
+    mimetype: application/x-microsoft.net.object.soap.base64
+    value   : The object must be serialized with
+            : System.Runtime.Serialization.Formatters.Soap.SoapFormatter
+            : and then encoded with base64 encoding.
+
+    mimetype: application/x-microsoft.net.object.bytearray.base64
+    value   : The object must be serialized into a byte array
+            : using a System.ComponentModel.TypeConverter
+            : and then encoded with base64 encoding.
+    -->
+  <xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
+    <xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
+    <xsd:element name="root" msdata:IsDataSet="true">
+      <xsd:complexType>
+        <xsd:choice maxOccurs="unbounded">
+          <xsd:element name="metadata">
+            <xsd:complexType>
+              <xsd:sequence>
+                <xsd:element name="value" type="xsd:string" minOccurs="0" />
+              </xsd:sequence>
+              <xsd:attribute name="name" use="required" type="xsd:string" />
+              <xsd:attribute name="type" type="xsd:string" />
+              <xsd:attribute name="mimetype" type="xsd:string" />
+              <xsd:attribute ref="xml:space" />
+            </xsd:complexType>
+          </xsd:element>
+          <xsd:element name="assembly">
+            <xsd:complexType>
+              <xsd:attribute name="alias" type="xsd:string" />
+              <xsd:attribute name="name" type="xsd:string" />
+            </xsd:complexType>
+          </xsd:element>
+          <xsd:element name="data">
+            <xsd:complexType>
+              <xsd:sequence>
+                <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
+                <xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
+              </xsd:sequence>
+              <xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
+              <xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
+              <xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
+              <xsd:attribute ref="xml:space" />
+            </xsd:complexType>
+          </xsd:element>
+          <xsd:element name="resheader">
+            <xsd:complexType>
+              <xsd:sequence>
+                <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
+              </xsd:sequence>
+              <xsd:attribute name="name" type="xsd:string" use="required" />
+            </xsd:complexType>
+          </xsd:element>
+        </xsd:choice>
+      </xsd:complexType>
+    </xsd:element>
+  </xsd:schema>
+  <resheader name="resmimetype">
+    <value>text/microsoft-resx</value>
+  </resheader>
+  <resheader name="version">
+    <value>2.0</value>
+  </resheader>
+  <resheader name="reader">
+    <value>System.Resources.ResXResourceReader, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
+  </resheader>
+  <resheader name="writer">
+    <value>System.Resources.ResXResourceWriter, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
+  </resheader>
+  <data name="AutoUpdateNotFetched" xml:space="preserve"><value>Не загружена информация об обновлении.</value></data>
+  <data name="NetDownloading" xml:space="preserve"><value>Загрузка {0}</value></data>
+  <data name="NetMissingCertFailed" xml:space="preserve"><value>Не удалось загрузить {0}</value></data>
+  <data name="NetInvalidLocation" xml:space="preserve"><value>Неверный URL в заголовке расположения: {0}</value></data>
+  <data name="NetAsyncDownloaderDownloading" xml:space="preserve"><value>Загрузка «{0}»</value></data>
+  <data name="NetAsyncDownloaderCancelled" xml:space="preserve"><value>Загрузка отменена пользователем</value></data>
+  <data name="NetAsyncDownloaderProgress" xml:space="preserve"><value>{0}/сек — загружается — осталось {1}</value></data>
+  <data name="NetAsyncDownloaderTryingFallback" xml:space="preserve"><value>Не удалось загрузить «{0}», будет использоваться альтернативный источник "{1}"</value></data>
+  <data name="NetFileCacheCannotFind" xml:space="preserve"><value>Не удалось найти папку кэша: {0}</value></data>
+  <data name="NetFileCacheZipError" xml:space="preserve"><value>Ошибка в шаге {0} для {1}: {2}</value></data>
+  <data name="NetFileCacheZipTestArchiveFalse" xml:space="preserve"><value>ZipFile.TestArchive(true) вернуло false</value></data>
+  <data name="NetFileCacheNullFileName" xml:space="preserve"><value>Пустое имя файла</value></data>
+  <data name="NetFileCacheMonoNotSupported" xml:space="preserve"><value>{0}
+
+Установите пакет `mono-complete` или его эквивалент для вашей ОС.</value></data>
+  <data name="NetModuleCacheBadLength" xml:space="preserve"><value>{0}: {1} имеет длину {2}, должно быть {3}</value></data>
+  <data name="NetModuleCacheNotValidZIP" xml:space="preserve"><value>{0}: {1} не является корректным ZIP-файлом: {2}</value></data>
+  <data name="NetModuleCacheMismatchSHA1" xml:space="preserve"><value>{0}: SHA1 для {1} — {2}, должно быть {3}</value></data>
+  <data name="NetModuleCacheMismatchSHA256" xml:space="preserve"><value>{0}: SHA256 для {1} — {2}, должно быть {3}</value></data>
+  <data name="NetRepoCheckingForUpdates" xml:space="preserve"><value>Поиск обновлений</value></data>
+  <data name="NetRepoAlreadyUpToDate" xml:space="preserve"><value>Уже обновлено</value></data>
+  <data name="NetRepoNoChanges" xml:space="preserve"><value>Нет изменений с последнего обновления</value></data>
+  <data name="NetRepoUpdating" xml:space="preserve"><value>Обновление «{0}»</value></data>
+  <data name="NetRepoUpdated" xml:space="preserve"><value>Обновлён «{0}» ({1} модулей)</value></data>
+  <data name="NetRepoSaving" xml:space="preserve"><value>Сохранение модулей в реестр</value></data>
+  <data name="NetRepoSaved" xml:space="preserve"><value>Реестр сохранён</value></data>
+  <data name="NetRepoUpdatedAll" xml:space="preserve"><value>Репозитории обновлены</value></data>
+  <data name="NetRepoNoModules" xml:space="preserve"><value>Не найдено ни одного модуля!</value></data>
+  <data name="NetRepoFailedDownload" xml:space="preserve"><value>Не удалось загрузить «{0}»: {1}</value></data>
+  <data name="NetRepoChangedModulesReinstallPrompt" xml:space="preserve"><value>С последнего обновления были изменены метаданные следующих модификаций:
+
+{0}
+Ради сохранения согласованности с репозиторием рекомендуется их переустановка.
+
+Хотите ли вы переустановить их сейчас?</value></data>
+  <data name="NetRepoInconsistenciesHeader" xml:space="preserve"><value>Найдены следующие несоответствия:</value></data>
+  <data name="JsonRelationshipConverterAnyOfCombined" xml:space="preserve"><value>`any_of` не должно сочетаться с `{0}`</value></data>
+  <data name="RegistryFileConflict" xml:space="preserve"><value>{0} желает установить {1}, который принадлежит {2}</value></data>
+  <data name="RegistryFileNotRemoved" xml:space="preserve"><value>{0} принадлежит {1}, но всё ещё не удалён!</value></data>
+  <data name="RegistryManagerDirectoryNotFound" xml:space="preserve"><value>Не удаётся найти папку в {0}</value></data>
+  <data name="RegistryManagerExportFilenamePrefix" xml:space="preserve"><value>установленные</value></data>
+  <data name="RegistryManagerDefaultModpackAbstract" xml:space="preserve"><value>Список модулей, установленных в сборке «{0}»</value></data>
+  <data name="CkanModuleDeserialisationError" xml:space="preserve"><value>Ошибка десериализации JSON: {0}</value></data>
+  <data name="CkanModuleUnsupportedSpec" xml:space="preserve"><value>{0} требует CKAN {1}, не удаётся прочитать.</value></data>
+  <data name="CkanModuleMissingRequired" xml:space="preserve"><value>В {0} не заполнено требуемое поле {1}</value></data>
+  <data name="CkanModuleKspVersionMixed" xml:space="preserve"><value>ksp_version используется вместе с ksp_version_(min|max)</value></data>
+  <data name="CkanModuleNotAvailable" xml:space="preserve"><value>Версия {1} модуля «{0}» недоступна</value></data>
+  <data name="CkanModuleNotInstalledOrAvailable" xml:space="preserve"><value>Модуль «{0}» не установлен или недоступен</value></data>
+  <data name="CkanModuleAllVersions" xml:space="preserve"><value>Все версии</value></data>
+  <data name="LicenceInvalid" xml:space="preserve"><value>Лицензия на {0} не является подлинной</value></data>
+  <data name="ModuleInstallDescriptorMustHaveInstallTo" xml:space="preserve"><value>Установочные записи должны включать в себя install_to</value></data>
+  <data name="ModuleInstallDescriptorRequireFileFind" xml:space="preserve"><value>Установочные записи должны включать в себя инструкцию file, find или find_regexp</value></data>
+  <data name="ModuleInstallDescriptorTooManyFileFind" xml:space="preserve"><value>Установочные записи должны включать в себя только одну из инструкций file, find или find_regexp</value></data>
+  <data name="ModuleInstallDescriptorTooManyFilterInclude" xml:space="preserve"><value>Установочные записи должны включать в себя только инструкцию filter либо include_only directives</value></data>
+  <data name="ModuleInstallDescriptorInvalidInstallPath" xml:space="preserve"><value>Неверный путь установки: {0}</value></data>
+  <data name="ModuleInstallDescriptorUnknownInstallPath" xml:space="preserve"><value>Не найдена install_to: {0}</value></data>
+  <data name="ModuleInstallDescriptorNoFilesFound" xml:space="preserve"><value>Не найдено файлов для установки, совпадающих с {0}!</value></data>
+  <data name="ModuleInstallDescriptorAsNoPathSeparators" xml:space="preserve"><value>`as` не может включать разделители пути</value></data>
+  <data name="RelationshipDescriptorMinVersionOnly" xml:space="preserve"><value>{0} {1} или позднее</value></data>
+  <data name="RelationshipDescriptorMaxVersionOnly" xml:space="preserve"><value>{0} {1} или ранее</value></data>
+  <data name="RelationshipDescriptorAnyOfJoiner" xml:space="preserve"><value>{0} ИЛИ {1}</value></data>
+  <data name="ReleaseStatusInvalid" xml:space="preserve"><value>{0} не является корректным статусом</value></data>
+  <data name="RepositoryDefaultName" xml:space="preserve"><value>стандартный</value></data>
+  <data name="CkanModuleVersionToString" xml:space="preserve"><value>{0} или {1}</value></data>
+  <data name="GameVersionYalovAny" xml:space="preserve"><value>любая</value></data>
+  <data name="GameVersionSelectNeedOne" xml:space="preserve"><value>Нужно указать как минимум наибольшую или наименьшую версию</value></data>
+  <data name="GameVersionSelectHeader" xml:space="preserve"><value>Указанная версия не является единственной, выберите подходящую:</value></data>
+  <data name="GameVersionSelectBuildHeader" xml:space="preserve"><value>Номер сборки не соответствует данной версии, выберите подходящий:</value></data>
+  <data name="GameVersionNotKnown" xml:space="preserve"><value>Версия не известна CKAN.</value></data>
+  <data name="GameVersionCriteriaToString" xml:space="preserve"><value>[Версии: {0}]</value></data>
+  <data name="GameVersionRangeMinOnly" xml:space="preserve"><value>{0} {1} и ранее</value></data>
+  <data name="GameVersionRangeMaxOnly" xml:space="preserve"><value>{0} {1} и позднее</value></data>
+  <data name="ProvidesModuleVersionToString" xml:space="preserve"><value>{0} (предоставлено {1})</value></data>
+  <data name="UnmanagedModuleVersionUnknown" xml:space="preserve"><value>(не управляется)</value></data>
+  <data name="UnmanagedModuleVersionKnown" xml:space="preserve"><value>{0} (не управляется)</value></data>
+  <data name="PathUtilsNotAbsolute" xml:space="preserve"><value>{0} не является абсолютным путём</value></data>
+  <data name="PathUtilsNotInside" xml:space="preserve"><value>Ой! {0} не находится внутри {1}</value></data>
+  <data name="PathUtilsAlreadyAbsolute" xml:space="preserve"><value>Путь {0} уже является абсолютным</value></data>
+  <data name="PathUtilsNotRoot" xml:space="preserve"><value>{0} не является абсолютным корнем</value></data>
+  <data name="GameInstanceSettingUp" xml:space="preserve"><value>Первичная настройка CKAN...</value></data>
+  <data name="GameInstanceCreatingDir" xml:space="preserve"><value>Создание {0}</value></data>
+  <data name="GameInstanceScanning" xml:space="preserve"><value>Поиск установленных модификаций...</value></data>
+  <data name="GameInstanceVersionNotFound" xml:space="preserve"><value>Не найдена сборка игры</value></data>
+  <data name="GameInstanceToString" xml:space="preserve"><value>{0} Установка: {1}</value></data>
+  <data name="GameInstanceManagerPortable" xml:space="preserve"><value>портативная</value></data>
+  <data name="GameInstanceManagerAuto" xml:space="preserve"><value>Авто {0}</value></data>
+  <data name="GameInstanceCloneInvalid" xml:space="preserve"><value>Указанная сборка не является корректной сборкой {0}</value></data>
+  <data name="GameInstanceFakeBadVersion" xml:space="preserve"><value>Указанная версия {0} не является известной версией: {1}</value></data>
+  <data name="GameInstanceFakeNotEmpty" xml:space="preserve"><value>Указанная папка уже существует и не является пустой</value></data>
+  <data name="GameInstanceFakeDLCNotAllowed" xml:space="preserve"><value>Для DLC «{2}» необходим {0} версии {1} или выше</value></data>
+  <data name="GameInstanceNoValidName" xml:space="preserve"><value>Не удалось вернуть корректное имя для новой сборки</value></data>
+  <data name="GameInstanceByPathName" xml:space="preserve"><value>своя</value></data>
+  <data name="GameInstancePathNotFound" xml:space="preserve"><value>{0} не существует</value></data>
+  <data name="ModuleInstallerDownloading" xml:space="preserve"><value>Загружается «{0}»</value></data>
+  <data name="ModuleInstallerNothingToInstall" xml:space="preserve"><value>Нечего устанавливать</value></data>
+  <data name="ModuleInstallerAboutToInstall" xml:space="preserve"><value>Будут установлены:</value></data>
+  <data name="ModuleInstallerModuleCached" xml:space="preserve"><value> * {0} {1} (кэшировано)</value></data>
+  <data name="ModuleInstallerUserDeclined" xml:space="preserve"><value>Пользователь отклонил установку</value></data>
+  <data name="ModuleInstallerInstallingMod" xml:space="preserve"><value>Установка модификации «{0}»</value></data>
+  <data name="ModuleInstallerUpdatingRegistry" xml:space="preserve"><value>Обновление реестра</value></data>
+  <data name="ModuleInstallerCommitting" xml:space="preserve"><value>Осуществление изменений файловой системы</value></data>
+  <data name="ModuleInstallerRescanning" xml:space="preserve"><value>Повторный поиск {0}</value></data>
+  <data name="ModuleInstallerDone" xml:space="preserve"><value>Готово!</value></data>
+  <data name="ModuleInstallerAlreadyInstalled" xml:space="preserve"><value>{0} {1} уже установлен, пропуск</value></data>
+  <data name="ModuleInstallerZIPNotInCache" xml:space="preserve"><value>Попытка установить {0}, но она не была загружена или оказалась повреждена</value></data>
+  <data name="ModuleInstallerMetapackage" xml:space="preserve"><value>Метапакеты не могут быть установлены!</value></data>
+  <data name="ModuleInstallerDLC" xml:space="preserve"><value>DLC не могут быть установлены!</value></data>
+  <data name="ModuleInstallerBadDLLLocation" xml:space="preserve"><value>DLL для модуля «{0}» найдена в {1}, но не там, где её бы установил CKAN. Во избежание установки нескольких копий модификации операция отменена. Вручную удалите модуль и попробуйте снова.</value></data>
+  <data name="ModuleInstallerFileSame" xml:space="preserve"><value>тот же</value></data>
+  <data name="ModuleInstallerFileDifferent" xml:space="preserve"><value>ДРУГОЙ</value></data>
+  <data name="ModuleInstallerOverwrite" xml:space="preserve"><value>Модуль «{0}» желает перезаписать следующие файлы, установленные вручную:
+
+{1}
+
+Перезаписать?</value></data>
+  <data name="ModuleInstallerOverwriteCancelled" xml:space="preserve"><value>Установленные вручную файлы не перезаписаны, установка невозможна {0}</value></data>
+  <data name="ModuleInstallerFileExists" xml:space="preserve"><value>Совершена попытка записать {0}, но файл уже существует</value></data>
+  <data name="ModuleInstallerAboutToRemove" xml:space="preserve"><value>Будут удалены:</value></data>
+  <data name="ModuleInstallerContinuePrompt" xml:space="preserve"><value>Продолжить?</value></data>
+  <data name="ModuleInstallerRemoveAborted" xml:space="preserve"><value>Пользователь отменил удаление</value></data>
+  <data name="ModuleInstallerRemovingMod" xml:space="preserve"><value>Удаление {0}...</value></data>
+  <data name="ModuleInstallerAboutToUpgrade" xml:space="preserve"><value>Будут обновлены:</value></data>
+  <data name="ModuleInstallerUpgradeInstallingUncached" xml:space="preserve"><value> * Установка: {0} {1} ({2}, {3})</value></data>
+  <data name="ModuleInstallerUpgradeInstallingCached" xml:space="preserve"><value> * Установка: {0} {1} (кэшировано)</value></data>
+  <data name="ModuleInstallerUpgradeReinstalling" xml:space="preserve"><value> * Переустановка: {0} {1}</value></data>
+  <data name="ModuleInstallerUpgradeDowngrading" xml:space="preserve"><value> * Откат: {0} от {1} к {2}</value></data>
+  <data name="ModuleInstallerUpgradeUpgradingUncached" xml:space="preserve"><value> * Обновление: {0} {1} до {2} ({3}, {4})</value></data>
+  <data name="ModuleInstallerUpgradeUpgradingCached" xml:space="preserve"><value> * Обновление: {0} {1} до {2} (кэшировано)</value></data>
+  <data name="ModuleInstallerUpgradeUserDeclined" xml:space="preserve"><value>Пользователь отменил список обновления</value></data>
+  <data name="ModuleInstallerReplaceAutodetected" xml:space="preserve"><value>Невозможно заменить {0}, так как он не был установлен CKAN.
+Удалите его вручную и попробуйте снова.</value></data>
+  <data name="ModuleInstallerReplaceNotInstalled" xml:space="preserve"><value>Невозможно заменить {0}, так как он не установлен. Сначала установите {1}.</value></data>
+  <data name="ModuleInstallerImporting" xml:space="preserve"><value>Импорт {0}... ({1}%)</value></data>
+  <data name="ModuleInstallerImportAlreadyCached" xml:space="preserve"><value>Уже кэшировано: {0}</value></data>
+  <data name="ModuleInstallerImportingMod" xml:space="preserve"><value>Импорт {0} {1}...</value></data>
+  <data name="ModuleInstallerImportNotFound" xml:space="preserve"><value>Не найдено в указателе: {0}</value></data>
+  <data name="ModuleInstallerImportInstallPrompt" xml:space="preserve"><value>Установить {0} совместимых импортированных модификаций в сборку игры {1} ({2})?</value></data>
+  <data name="ModuleInstallerImportDeletePrompt" xml:space="preserve"><value>Импортирование завершено. Удалить {0} старых файлов?</value></data>
+  <data name="KrakenDependencyNotSatisfied" xml:space="preserve"><value>Зависимость от {0} версии {1} не удовлетворена</value></data>
+  <data name="KrakenDependencyModuleNotFound" xml:space="preserve"><value>Модуль не найден: {0} {1}</value></data>
+  <data name="KrakenParentDependencyNotSatisfied" xml:space="preserve"><value>Зависимость {0} от {1} версии {2} не удовлетворена</value></data>
+  <data name="KrakenAny" xml:space="preserve"><value>(любая)</value></data>
+  <data name="KrakenProvidedByMoreThanOne" xml:space="preserve"><value>Модуль «{0}» предоставлен несколькими модулями. Выберите один из них:</value></data>
+  <data name="KrakenInconsistenciesHeader" xml:space="preserve"><value>Были найдены следующие несоответствия:</value></data>
+  <data name="KrakenMissingDependency" xml:space="preserve"><value>{0}: отсутствует зависимость {1}</value></data>
+  <data name="KrakenConflictsWith" xml:space="preserve"><value>{0} конфликтует с {1}</value></data>
+  <data name="KrakenDownloadErrorsHeader" xml:space="preserve"><value>Ой, при загрузке произошли следующие проблемы...</value></data>
+  <data name="KrakenModuleDownloadErrorsHeader" xml:space="preserve"><value>Одна или несколько загрузок завершились неудачно:</value></data>
+  <data name="KrakenModuleDownloadError" xml:space="preserve"><value>Ошибка загрузки {0}: {1}</value></data>
+  <data name="KrakenNotInstalled" xml:space="preserve"><value>Модуль «{0}» не установлен!</value></data>
+  <data name="KrakenMissingCertificateUnix" xml:space="preserve"><value>О нет! Загрузка не завершена из-за ошибки сертификата!
+
+Посетите эту страницу для получения справки:
+https://github.com/KSP-CKAN/CKAN/wiki/SSL-certificate-errors</value></data>
+  <data name="KrakenMissingCertificateNotUnix" xml:space="preserve"><value>О нет! Загрузка не завершена из-за ошибки сертификата!</value></data>
+  <data name="KrakenDownloadThrottled" xml:space="preserve"><value>Загрузка из {0} была ограничена.
+Для увеличения квоты добавьте токен аутентификации.</value></data>
+  <data name="KrakenAlreadyRunning" xml:space="preserve"><value>CKAN уже запущен для этой сборки!
+
+Если вы уверены, что это не так, удалите:
+"{0}"</value></data>
+  <data name="KrakenReinstallModule" xml:space="preserve"><value>Метаданные изменены, рекомендуется переустановка: {0}</value></data>
+  <data name="RelationshipResolverConflictsWith" xml:space="preserve"><value>{0} конфликтует с {1}</value></data>
+  <data name="RelationshipResolverRequiredButResolver" xml:space="preserve"><value>Необходима {0}, но в ресольвере несовместимая версия</value></data>
+  <data name="RelationshipResolverRequiredButInstalled" xml:space="preserve"><value>Необходима {0}, но установлена несовместимая версия</value></data>
+  <data name="RelationshipResolverAnUnmanaged" xml:space="preserve"><value>неуправляемая DLL или DLC</value></data>
+  <data name="RelationshipResolverUnmanaged" xml:space="preserve"><value>Не управляется</value></data>
+  <data name="RelationshipResolverModNotInList" xml:space="preserve"><value>Модификация «{0}» не в списке</value></data>
+  <data name="RelationshipResolverInstalledReason" xml:space="preserve"><value>Установлено в данный момент</value></data>
+  <data name="RelationshipResolverUserReason" xml:space="preserve"><value>Запрошено пользователем</value></data>
+  <data name="RelationshipResolverNoLongerUsedReason" xml:space="preserve"><value>Установлено автоматически, зависящие модули удалены</value></data>
+  <data name="RelationshipResolverReplacementReason" xml:space="preserve"><value>Заменяет {0}</value></data>
+  <data name="RelationshipResolverSuggestedReason" xml:space="preserve"><value>Предложено {0}</value></data>
+  <data name="RelationshipResolverDependsReason" xml:space="preserve"><value>Для удовлетворения зависимости {0}</value></data>
+  <data name="RelationshipResolverRecommendedReason" xml:space="preserve"><value>Рекомендуется {0}</value></data>
+  <data name="SanityCheckerUnsatisfiedDependency" xml:space="preserve"><value>{0} имеет неудовлетворённую зависимость: {1} не установлен</value></data>
+  <data name="SanityCheckerConflictsWith" xml:space="preserve"><value>{0} конфликтует с {1}</value></data>
+</root>
diff --git a/Core/Properties/Resources.zh-CN.resx b/Core/Properties/Resources.zh-CN.resx
new file mode 100644
index 0000000000..d0a23003e7
--- /dev/null
+++ b/Core/Properties/Resources.zh-CN.resx
@@ -0,0 +1,296 @@
+<?xml version="1.0" encoding="utf-8"?>
+<root>
+  <!--
+    Microsoft ResX Schema
+
+    Version 2.0
+
+    The primary goals of this format is to allow a simple XML format
+    that is mostly human readable. The generation and parsing of the
+    various data types are done through the TypeConverter classes
+    associated with the data types.
+
+    Example:
+
+    ... ado.net/XML headers & schema ...
+    <resheader name="resmimetype">text/microsoft-resx</resheader>
+    <resheader name="version">2.0</resheader>
+    <resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
+    <resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
+    <data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
+    <data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
+    <data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
+        <value>[base64 mime encoded serialized .NET Framework object]</value>
+    </data>
+    <data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
+        <value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
+        <comment>This is a comment</comment>
+    </data>
+
+    There are any number of "resheader" rows that contain simple
+    name/value pairs.
+
+    Each data row contains a name, and value. The row also contains a
+    type or mimetype. Type corresponds to a .NET class that support
+    text/value conversion through the TypeConverter architecture.
+    Classes that don't support this are serialized and stored with the
+    mimetype set.
+
+    The mimetype is used for serialized objects, and tells the
+    ResXResourceReader how to depersist the object. This is currently not
+    extensible. For a given mimetype the value must be set accordingly:
+
+    Note - application/x-microsoft.net.object.binary.base64 is the format
+    that the ResXResourceWriter will generate, however the reader can
+    read any of the formats listed below.
+
+    mimetype: application/x-microsoft.net.object.binary.base64
+    value   : The object must be serialized with
+            : System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
+            : and then encoded with base64 encoding.
+
+    mimetype: application/x-microsoft.net.object.soap.base64
+    value   : The object must be serialized with
+            : System.Runtime.Serialization.Formatters.Soap.SoapFormatter
+            : and then encoded with base64 encoding.
+
+    mimetype: application/x-microsoft.net.object.bytearray.base64
+    value   : The object must be serialized into a byte array
+            : using a System.ComponentModel.TypeConverter
+            : and then encoded with base64 encoding.
+    -->
+  <xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
+    <xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
+    <xsd:element name="root" msdata:IsDataSet="true">
+      <xsd:complexType>
+        <xsd:choice maxOccurs="unbounded">
+          <xsd:element name="metadata">
+            <xsd:complexType>
+              <xsd:sequence>
+                <xsd:element name="value" type="xsd:string" minOccurs="0" />
+              </xsd:sequence>
+              <xsd:attribute name="name" use="required" type="xsd:string" />
+              <xsd:attribute name="type" type="xsd:string" />
+              <xsd:attribute name="mimetype" type="xsd:string" />
+              <xsd:attribute ref="xml:space" />
+            </xsd:complexType>
+          </xsd:element>
+          <xsd:element name="assembly">
+            <xsd:complexType>
+              <xsd:attribute name="alias" type="xsd:string" />
+              <xsd:attribute name="name" type="xsd:string" />
+            </xsd:complexType>
+          </xsd:element>
+          <xsd:element name="data">
+            <xsd:complexType>
+              <xsd:sequence>
+                <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
+                <xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
+              </xsd:sequence>
+              <xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
+              <xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
+              <xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
+              <xsd:attribute ref="xml:space" />
+            </xsd:complexType>
+          </xsd:element>
+          <xsd:element name="resheader">
+            <xsd:complexType>
+              <xsd:sequence>
+                <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
+              </xsd:sequence>
+              <xsd:attribute name="name" type="xsd:string" use="required" />
+            </xsd:complexType>
+          </xsd:element>
+        </xsd:choice>
+      </xsd:complexType>
+    </xsd:element>
+  </xsd:schema>
+  <resheader name="resmimetype">
+    <value>text/microsoft-resx</value>
+  </resheader>
+  <resheader name="version">
+    <value>2.0</value>
+  </resheader>
+  <resheader name="reader">
+    <value>System.Resources.ResXResourceReader, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
+  </resheader>
+  <resheader name="writer">
+    <value>System.Resources.ResXResourceWriter, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
+  </resheader>
+  <data name="AutoUpdateNotFetched" xml:space="preserve"><value>未能拉取到发布信息。无法更新。</value></data>
+  <data name="NetDownloading" xml:space="preserve"><value>正在下载 {0} </value></data>
+  <data name="NetMissingCertFailed" xml:space="preserve"><value>下载 {0} 失败</value></data>
+  <data name="NetInvalidLocation" xml:space="preserve"><value>Location Header含有无效URL: {0}</value></data>
+  <data name="NetAsyncDownloaderDownloading" xml:space="preserve"><value>正在下载 "{0}" </value></data>
+  <data name="NetAsyncDownloaderCancelled" xml:space="preserve"><value>用户取消下载</value></data>
+  <data name="NetAsyncDownloaderProgress" xml:space="preserve"><value>{0}/s - 下载中 - 剩余 {1} </value></data>
+  <data name="NetAsyncDownloaderTryingFallback" xml:space="preserve"><value>下载 "{0}" 失败,尝试回滚到 "{1}"</value></data>
+  <data name="NetFileCacheCannotFind" xml:space="preserve"><value>无法找到缓存目录: {0}</value></data>
+  <data name="NetFileCacheZipError" xml:space="preserve"><value>在 {1} 的步骤 {0} 出现错误: {2}</value></data>
+  <data name="NetFileCacheZipTestArchiveFalse" xml:space="preserve"><value>ZipFile.TestArchive(true) 返回了 false</value></data>
+  <data name="NetFileCacheNullFileName" xml:space="preserve"><value>空文件名</value></data>
+  <data name="NetFileCacheMonoNotSupported" xml:space="preserve"><value>{0}
+
+请在您的操作系统安装 `mono-complete` 或其它等效应用。</value></data>
+  <data name="NetModuleCacheBadLength" xml:space="preserve"><value>{0}: {1} 的长度为 {2},但应该为 {3}</value></data>
+  <data name="NetModuleCacheNotValidZIP" xml:space="preserve"><value>{0}: {1} 并不是一个有效的 ZIP 文件: {2}</value></data>
+  <data name="NetModuleCacheMismatchSHA1" xml:space="preserve"><value>{0}: {1} 的 SHA1 值为 {2},但应该为 {3}</value></data>
+  <data name="NetModuleCacheMismatchSHA256" xml:space="preserve"><value>{0}: {1} 的 SHA256 值为 {2},但应该为 {3}</value></data>
+  <data name="NetRepoCheckingForUpdates" xml:space="preserve"><value>检查更新中</value></data>
+  <data name="NetRepoAlreadyUpToDate" xml:space="preserve"><value>已是最新版本</value></data>
+  <data name="NetRepoNoChanges" xml:space="preserve"><value>自上次更新以来无更改</value></data>
+  <data name="NetRepoUpdating" xml:space="preserve"><value>正在更新 {0}</value></data>
+  <data name="NetRepoUpdated" xml:space="preserve"><value>已更新 {0} ({1} 个模组)</value></data>
+  <data name="NetRepoSaving" xml:space="preserve"><value>保存模组到注册表中</value></data>
+  <data name="NetRepoSaved" xml:space="preserve"><value>注册表已保存</value></data>
+  <data name="NetRepoUpdatedAll" xml:space="preserve"><value>数据库已更新</value></data>
+  <data name="NetRepoNoModules" xml:space="preserve"><value>没有找到模组!</value></data>
+  <data name="NetRepoFailedDownload" xml:space="preserve"><value>下载 {0} 失败: {1}</value></data>
+  <data name="NetRepoChangedModulesReinstallPrompt" xml:space="preserve"><value>以下模组自上次更新后更改了它们的元数据:
+
+{0}
+您应该重新安装它们以保持与数据库的一致性。
+
+您想要现在重新安装吗?</value></data>
+  <data name="NetRepoInconsistenciesHeader" xml:space="preserve"><value>发现以下冲突:</value></data>
+  <data name="JsonRelationshipConverterAnyOfCombined" xml:space="preserve"><value>`any_of` 不应该与 `{0}` 结合使用</value></data>
+  <data name="RegistryFileConflict" xml:space="preserve"><value>{0} 想要安装 {1},但该文件已被 {2} 注册</value></data>
+  <data name="RegistryFileNotRemoved" xml:space="preserve"><value>{0} 已被 {1} 注册但还未被删除!</value></data>
+  <data name="RegistryManagerDirectoryNotFound" xml:space="preserve"><value>无法在 {0} 找到目录</value></data>
+  <data name="RegistryManagerExportFilenamePrefix" xml:space="preserve"><value>已安装</value></data>
+  <data name="RegistryManagerDefaultModpackAbstract" xml:space="preserve"><value>已安装在 KSP 实例 {0} 的模组列表</value></data>
+  <data name="CkanModuleDeserialisationError" xml:space="preserve"><value>JSON 反序列化错误: {0}</value></data>
+  <data name="CkanModuleUnsupportedSpec" xml:space="preserve"><value>{0} 需要 CKAN {1},我们无法读取它.</value></data>
+  <data name="CkanModuleMissingRequired" xml:space="preserve"><value>{0} 缺少需要的 field {1}</value></data>
+  <data name="CkanModuleKspVersionMixed" xml:space="preserve"><value>ksp_version 不能与 ksp_version_(min|max) 混用</value></data>
+  <data name="CkanModuleNotAvailable" xml:space="preserve"><value>模组 {0} 的版本 {1} 不可用</value></data>
+  <data name="CkanModuleNotInstalledOrAvailable" xml:space="preserve"><value>模组 {0} 未安装或不可用</value></data>
+  <data name="CkanModuleAllVersions" xml:space="preserve"><value>所有版本</value></data>
+  <data name="LicenceInvalid" xml:space="preserve"><value>许可 {0} 是无效的</value></data>
+  <data name="ModuleInstallDescriptorMustHaveInstallTo" xml:space="preserve"><value>Install 句法必须含有一个 install_to</value></data>
+  <data name="ModuleInstallDescriptorRequireFileFind" xml:space="preserve"><value>Install 句法必须含有 file, find, 或者 find_regexp 指令中的一个</value></data>
+  <data name="ModuleInstallDescriptorTooManyFileFind" xml:space="preserve"><value>Install 句法只能含有 file, find, 或者 find_regexp 指令中的一个</value></data>
+  <data name="ModuleInstallDescriptorTooManyFilterInclude" xml:space="preserve"><value>Install 句法只能含有 filter 或者 include_only 指令中的一个,而不是两个</value></data>
+  <data name="ModuleInstallDescriptorInvalidInstallPath" xml:space="preserve"><value>无效安装路径: {0}</value></data>
+  <data name="ModuleInstallDescriptorUnknownInstallPath" xml:space="preserve"><value>未知的 install_to: {0}</value></data>
+  <data name="ModuleInstallDescriptorNoFilesFound" xml:space="preserve"><value>没有找到匹配 {0} 的文件来安装!</value></data>
+  <data name="ModuleInstallDescriptorAsNoPathSeparators" xml:space="preserve"><value>`as` 不能含有路径分隔符</value></data>
+  <data name="RelationshipDescriptorMinVersionOnly" xml:space="preserve"><value>{0} {1} 或更新版本</value></data>
+  <data name="RelationshipDescriptorMaxVersionOnly" xml:space="preserve"><value>{0} {1} 或更旧版本</value></data>
+  <data name="RelationshipDescriptorAnyOfJoiner" xml:space="preserve"><value>{0} 或 {1}</value></data>
+  <data name="ReleaseStatusInvalid" xml:space="preserve"><value>{0} 不是有效的发布状态</value></data>
+  <data name="RepositoryDefaultName" xml:space="preserve"><value>默认数据库</value></data>
+  <data name="CkanModuleVersionToString" xml:space="preserve"><value>{0} aka {1}</value></data>
+  <data name="GameVersionYalovAny" xml:space="preserve"><value>任意版本</value></data>
+  <data name="GameVersionSelectNeedOne" xml:space="preserve"><value>需要 Major 和 Minor 中的至少一个</value></data>
+  <data name="GameVersionSelectHeader" xml:space="preserve"><value>选中的版本并不是独立的,请选择一个版本:</value></data>
+  <data name="GameVersionSelectBuildHeader" xml:space="preserve"><value>未在该版本中发现该 Build 版本号. 请选择一个 Build 版本:</value></data>
+  <data name="GameVersionNotKnown" xml:space="preserve"><value>CKAN 无法识别该版本</value></data>
+  <data name="GameVersionCriteriaToString" xml:space="preserve"><value>[版本: {0}]</value></data>
+  <data name="GameVersionRangeMinOnly" xml:space="preserve"><value>{0} {1} 及更旧版本</value></data>
+  <data name="GameVersionRangeMaxOnly" xml:space="preserve"><value>{0} {1} 及更新版本</value></data>
+  <data name="ProvidesModuleVersionToString" xml:space="preserve"><value>{0} (由 {1} 提供)</value></data>
+  <data name="UnmanagedModuleVersionUnknown" xml:space="preserve"><value>(未管理)</value></data>
+  <data name="UnmanagedModuleVersionKnown" xml:space="preserve"><value>{0} (未管理)</value></data>
+  <data name="PathUtilsNotAbsolute" xml:space="preserve"><value>{0} 并不是一个绝对路径</value></data>
+  <data name="PathUtilsNotInside" xml:space="preserve"><value> {0} 不在 {1} 中</value></data>
+  <data name="PathUtilsAlreadyAbsolute" xml:space="preserve"><value>{0} 已经是绝对路径</value></data>
+  <data name="PathUtilsNotRoot" xml:space="preserve"><value>{0} 不是绝对根路径</value></data>
+  <data name="GameInstanceSettingUp" xml:space="preserve"><value>首次配置 CKAN 中...</value></data>
+  <data name="GameInstanceCreatingDir" xml:space="preserve"><value>正在创建 {0}</value></data>
+  <data name="GameInstanceScanning" xml:space="preserve"><value>扫描已安装的模组中...</value></data>
+  <data name="GameInstanceVersionNotFound" xml:space="preserve"><value>未找到游戏版本</value></data>
+  <data name="GameInstanceToString" xml:space="preserve"><value>{0} 安装: {1}</value></data>
+  <data name="GameInstanceManagerPortable" xml:space="preserve"><value>可移动</value></data>
+  <data name="GameInstanceManagerAuto" xml:space="preserve"><value>自动 {0}</value></data>
+  <data name="GameInstanceCloneInvalid" xml:space="preserve"><value>所选实例并非有效的 {0} 实例</value></data>
+  <data name="GameInstanceFakeBadVersion" xml:space="preserve"><value>选中的 {0} 版本并非已知版本: {1}</value></data>
+  <data name="GameInstanceFakeNotEmpty" xml:space="preserve"><value>选中的文件夹已经存在且不是空文件夹</value></data>
+  <data name="GameInstanceFakeDLCNotAllowed" xml:space="preserve"><value>{0} 版本 {1} 或以上需要 {2} DLC</value></data>
+  <data name="GameInstanceNoValidName" xml:space="preserve"><value>无法为新实例返回有效的名称</value></data>
+  <data name="GameInstanceByPathName" xml:space="preserve"><value>自定义</value></data>
+  <data name="GameInstancePathNotFound" xml:space="preserve"><value>{0} 不存在</value></data>
+  <data name="ModuleInstallerDownloading" xml:space="preserve"><value>正在下载 "{0}"</value></data>
+  <data name="ModuleInstallerNothingToInstall" xml:space="preserve"><value>没有需要安装的</value></data>
+  <data name="ModuleInstallerAboutToInstall" xml:space="preserve"><value>中止安装:</value></data>
+  <data name="ModuleInstallerModuleCached" xml:space="preserve"><value> * {0} {1} (已缓存)</value></data>
+  <data name="ModuleInstallerUserDeclined" xml:space="preserve"><value>用户取消安装列表</value></data>
+  <data name="ModuleInstallerInstallingMod" xml:space="preserve"><value>安装模组 "{0}"</value></data>
+  <data name="ModuleInstallerUpdatingRegistry" xml:space="preserve"><value>正在更新注册表</value></data>
+  <data name="ModuleInstallerCommitting" xml:space="preserve"><value>正在提交文件系统更改</value></data>
+  <data name="ModuleInstallerRescanning" xml:space="preserve"><value>重新扫描 {0}</value></data>
+  <data name="ModuleInstallerDone" xml:space="preserve"><value>完成!</value></data>
+  <data name="ModuleInstallerAlreadyInstalled" xml:space="preserve"><value>{0} {1} 已经安装,跳过</value></data>
+  <data name="ModuleInstallerZIPNotInCache" xml:space="preserve"><value>尝试安装 {0},但它没有被下载或下载被中断了</value></data>
+  <data name="ModuleInstallerMetapackage" xml:space="preserve"><value>元安装包无法安装!</value></data>
+  <data name="ModuleInstallerDLC" xml:space="preserve"><value>DLC 无法安装!</value></data>
+  <data name="ModuleInstallerBadDLLLocation" xml:space="preserve"><value>在 {1} 发现了模组 {0} 的 DLL 文件 ,但 CKAN 无法在该位置安装它。正在中止安装以避免安装多个重复的模组。请手动卸载该模组并重试以安装它。</value></data>
+  <data name="ModuleInstallerFileSame" xml:space="preserve"><value>相同</value></data>
+  <data name="ModuleInstallerFileDifferent" xml:space="preserve"><value>不相同</value></data>
+  <data name="ModuleInstallerOverwrite" xml:space="preserve"><value>模组 {0} 想要覆盖以下手动安装文件:
+
+{1}
+
+覆盖吗?</value></data>
+  <data name="ModuleInstallerOverwriteCancelled" xml:space="preserve"><value>取消覆盖手动安装文件,无法安装 {0}</value></data>
+  <data name="ModuleInstallerFileExists" xml:space="preserve"><value>尝试写入 {0} 但它已经存在</value></data>
+  <data name="ModuleInstallerAboutToRemove" xml:space="preserve"><value>中止删除:</value></data>
+  <data name="ModuleInstallerContinuePrompt" xml:space="preserve"><value>继续?</value></data>
+  <data name="ModuleInstallerRemoveAborted" xml:space="preserve"><value>用户请求中止删除模组</value></data>
+  <data name="ModuleInstallerRemovingMod" xml:space="preserve"><value>正在删除 {0}...</value></data>
+  <data name="ModuleInstallerAboutToUpgrade" xml:space="preserve"><value>中止升级:</value></data>
+  <data name="ModuleInstallerUpgradeInstallingUncached" xml:space="preserve"><value> * 安装: {0} {1} ({2},{3})</value></data>
+  <data name="ModuleInstallerUpgradeInstallingCached" xml:space="preserve"><value> * 安装: {0} {1} (已缓存)</value></data>
+  <data name="ModuleInstallerUpgradeReinstalling" xml:space="preserve"><value> * 重新安装: {0} {1}</value></data>
+  <data name="ModuleInstallerUpgradeDowngrading" xml:space="preserve"><value> * 降级: {0} 从 {1} 到 {2}</value></data>
+  <data name="ModuleInstallerUpgradeUpgradingUncached" xml:space="preserve"><value> * 升级: {0} {1} 到 {2} ({3},{4})</value></data>
+  <data name="ModuleInstallerUpgradeUpgradingCached" xml:space="preserve"><value> * 升级: {0} {1} 到 {2} (已缓存)</value></data>
+  <data name="ModuleInstallerUpgradeUserDeclined" xml:space="preserve"><value>用户取消升级列表</value></data>
+  <data name="ModuleInstallerReplaceAutodetected" xml:space="preserve"><value>无法替换 {0} 因为它并不是由 CKAN 安装的。请在尝试安装前手动删除它。</value></data>
+  <data name="ModuleInstallerReplaceNotInstalled" xml:space="preserve"><value>无法替换 {0} 因为它还未安装。请尝试安装 {1}。</value></data>
+  <data name="ModuleInstallerImporting" xml:space="preserve"><value>导入 {0}... ({1}%)</value></data>
+  <data name="ModuleInstallerImportAlreadyCached" xml:space="preserve"><value>已缓存: {0}</value></data>
+  <data name="ModuleInstallerImportingMod" xml:space="preserve"><value>导入 {0} {1}...</value></data>
+  <data name="ModuleInstallerImportNotFound" xml:space="preserve"><value>未找到索引: {0}</value></data>
+  <data name="ModuleInstallerImportInstallPrompt" xml:space="preserve"><value>安装 {0} 个兼容的导入的模组到游戏实例 {1} ({2}) 吗?</value></data>
+  <data name="ModuleInstallerImportDeletePrompt" xml:space="preserve"><value>导入完成。 删除 {0} 个旧文件吗?</value></data>
+  <data name="KrakenDependencyNotSatisfied" xml:space="preserve"><value> {0} 版本 {1} 的依赖项未满足</value></data>
+  <data name="KrakenDependencyModuleNotFound" xml:space="preserve"><value>模组未找到: {0} {1}</value></data>
+  <data name="KrakenParentDependencyNotSatisfied" xml:space="preserve"><value>{0} 的依赖项 {1} 版本 {2} 未满足</value></data>
+  <data name="KrakenAny" xml:space="preserve"><value>(任意)</value></data>
+  <data name="KrakenProvidedByMoreThanOne" xml:space="preserve"><value>模组 {0} 由多个可用模组提供。请从下列列表中选择一个:</value></data>
+  <data name="KrakenInconsistenciesHeader" xml:space="preserve"><value>发现以下冲突:</value></data>
+  <data name="KrakenMissingDependency" xml:space="preserve"><value>{0} 缺少依赖 {1}</value></data>
+  <data name="KrakenConflictsWith" xml:space="preserve"><value>{0} 与 {1} 冲突</value></data>
+  <data name="KrakenDownloadErrorsHeader" xml:space="preserve"><value>Uh oh,下载时以下东西出现了问题...</value></data>
+  <data name="KrakenModuleDownloadErrorsHeader" xml:space="preserve"><value>一个或多个下载未能成功:</value></data>
+  <data name="KrakenModuleDownloadError" xml:space="preserve"><value>下载错误 {0}: {1}</value></data>
+  <data name="KrakenNotInstalled" xml:space="preserve"><value>模组 {0} 未安装!</value></data>
+  <data name="KrakenMissingCertificateUnix" xml:space="preserve"><value>Oh no! 我们的下载由于证书错误失败了!
+
+查看以下页面以获得帮助:
+https://github.com/KSP-CKAN/CKAN/wiki/SSL-certificate-errors</value></data>
+  <data name="KrakenMissingCertificateNotUnix" xml:space="preserve"><value>Oh no! 我们的下载由于证书错误失败了!</value></data>
+  <data name="KrakenDownloadThrottled" xml:space="preserve"><value>从 {0} 下载时被限速了。
+请考虑添加一个验证 token 来提高限制速率。</value></data>
+  <data name="KrakenAlreadyRunning" xml:space="preserve"><value>CKAN 已经运行在该实例下了!
+
+如果您确定这里不是,请删除:
+"{0}"</value></data>
+  <data name="KrakenReinstallModule" xml:space="preserve"><value>元数据已更改,推荐重新安装: {0}</value></data>
+  <data name="RelationshipResolverConflictsWith" xml:space="preserve"><value>{0} 与 {1} 冲突</value></data>
+  <data name="RelationshipResolverRequiredButResolver" xml:space="preserve"><value>需要 {0} ,但解析器内有一个不兼容版本</value></data>
+  <data name="RelationshipResolverRequiredButInstalled" xml:space="preserve"><value>需要 {0} ,但有一个已安装的不兼容版本</value></data>
+  <data name="RelationshipResolverAnUnmanaged" xml:space="preserve"><value>一个未管理的 DLL 或 DLC</value></data>
+  <data name="RelationshipResolverUnmanaged" xml:space="preserve"><value>未管理</value></data>
+  <data name="RelationshipResolverModNotInList" xml:space="preserve"><value>模组 {0} 不在列表中</value></data>
+  <data name="RelationshipResolverInstalledReason" xml:space="preserve"><value>已安装</value></data>
+  <data name="RelationshipResolverUserReason" xml:space="preserve"><value>用户请求</value></data>
+  <data name="RelationshipResolverNoLongerUsedReason" xml:space="preserve"><value>已自动安装,已删除依赖模组</value></data>
+  <data name="RelationshipResolverReplacementReason" xml:space="preserve"><value>正在替换 {0}</value></data>
+  <data name="RelationshipResolverSuggestedReason" xml:space="preserve"><value>由 {0} 建议</value></data>
+  <data name="RelationshipResolverDependsReason" xml:space="preserve"><value>满足 {0} 的依赖项</value></data>
+  <data name="RelationshipResolverRecommendedReason" xml:space="preserve"><value>由 {0} 推荐</value></data>
+  <data name="SanityCheckerUnsatisfiedDependency" xml:space="preserve"><value>{0} 有一个未满足的依赖项: {1} 未安装</value></data>
+  <data name="SanityCheckerConflictsWith" xml:space="preserve"><value>{0} 与 {1} 冲突</value></data>
+</root>
diff --git a/Core/Registry/Registry.cs b/Core/Registry/Registry.cs
index 2aa9d53fc0..c1b6f4dca9 100644
--- a/Core/Registry/Registry.cs
+++ b/Core/Registry/Registry.cs
@@ -767,7 +767,7 @@ public void RegisterModule(CkanModule mod, IEnumerable<string> absolute_files, G
                     // Woah! Registering an already owned file? Not cool!
                     // (Although if it existed, we should have thrown a kraken well before this.)
                     inconsistencies.Add(string.Format(
-                        "{0} wishes to install {1}, but this file is registered to {2}",
+                        Properties.Resources.RegistryFileConflict,
                         mod.identifier, file, owner
                     ));
                 }
@@ -819,7 +819,7 @@ public void DeregisterModule(GameInstance ksp, string module)
             foreach (var absolute_file in absolute_files.Where(File.Exists))
             {
                 inconsistencies.Add(string.Format(
-                    "{0} is registered to {1} but has not been removed!",
+                    Properties.Resources.RegistryFileNotRemoved,
                     absolute_file, module));
             }
 
diff --git a/Core/Registry/RegistryManager.cs b/Core/Registry/RegistryManager.cs
index 81d4fd3961..1d8bc8deac 100644
--- a/Core/Registry/RegistryManager.cs
+++ b/Core/Registry/RegistryManager.cs
@@ -409,7 +409,8 @@ public void Save(bool enforce_consistency = true)
             if (directoryPath == null)
             {
                 log.ErrorFormat("Failed to save registry, invalid path: {0}", path);
-                throw new DirectoryNotFoundKraken(path, "Can't find a directory in " + path);
+                throw new DirectoryNotFoundKraken(path, string.Format(
+                    Properties.Resources.RegistryManagerDirectoryNotFound, path));
             }
 
             if (!Directory.Exists(directoryPath))
@@ -433,8 +434,8 @@ public void Save(bool enforce_consistency = true)
             );
         }
 
-        public string LatestInstalledExportFilename() => $"installed-{ksp.SanitizedName}.ckan";
-        public string HistoricInstalledExportFilename() => $"installed-{ksp.SanitizedName}-{DateTime.Now:yyyy-MM-dd_HH-mm-ss}.ckan";
+        public string LatestInstalledExportFilename() => $"{Properties.Resources.RegistryManagerExportFilenamePrefix}-{ksp.SanitizedName}.ckan";
+        public string HistoricInstalledExportFilename() => $"{Properties.Resources.RegistryManagerExportFilenamePrefix}-{ksp.SanitizedName}-{DateTime.Now:yyyy-MM-dd_HH-mm-ss}.ckan";
 
         /// <summary>
         /// Save a custom .ckan file that contains all the currently
@@ -475,7 +476,7 @@ public CkanModule GenerateModpack(bool recommends = false, bool with_versions =
                 new ModuleVersion("v1.18"),
                 Identifier.Sanitize(name),
                 name,
-                $"A list of modules installed on the {kspInstanceName} KSP instance",
+                string.Format(Properties.Resources.RegistryManagerDefaultModpackAbstract, kspInstanceName),
                 null,
                 new List<string>() { System.Environment.UserName },
                 new List<License>() { new License("unknown") },
diff --git a/Core/Relationships/RelationshipResolver.cs b/Core/Relationships/RelationshipResolver.cs
index 2dc82b380a..d30a56c70f 100644
--- a/Core/Relationships/RelationshipResolver.cs
+++ b/Core/Relationships/RelationshipResolver.cs
@@ -265,8 +265,9 @@ private void AddModulesToInstall(IEnumerable<CkanModule> modules)
                     }
                     else
                     {
-                        throw new InconsistentKraken(
-                            $"{module} conflicts with {listed_mod}");
+                        throw new InconsistentKraken(string.Format(
+                            Properties.Resources.RelationshipResolverConflictsWith,
+                            module, listed_mod));
                     }
                 }
 
@@ -415,9 +416,9 @@ private void ResolveStanza(IEnumerable<RelationshipDescriptor> stanza, Selection
                     }
                     else
                     {
-                        throw new InconsistentKraken(
-                            $"{descriptor} required, but an incompatible version is in the resolver"
-                        );
+                        throw new InconsistentKraken(string.Format(
+                            Properties.Resources.RelationshipResolverRequiredButResolver,
+                            descriptor));
                     }
                 }
 
@@ -441,9 +442,9 @@ private void ResolveStanza(IEnumerable<RelationshipDescriptor> stanza, Selection
                     }
                     else
                     {
-                        throw new InconsistentKraken(
-                            $"{descriptor} required, but an incompatible version is installed"
-                        );
+                        throw new InconsistentKraken(string.Format(
+                            Properties.Resources.RelationshipResolverRequiredButInstalled,
+                            descriptor));
                     }
                 }
 
@@ -537,8 +538,9 @@ private void ResolveStanza(IEnumerable<RelationshipDescriptor> stanza, Selection
                     }
                     else
                     {
-                        throw new InconsistentKraken(
-                            $"{conflicting_mod} conflicts with {candidate}");
+                        throw new InconsistentKraken(string.Format(
+                            Properties.Resources.RelationshipResolverConflictsWith,
+                            conflicting_mod, candidate));
                     }
                 }
             }
@@ -671,10 +673,12 @@ public Dictionary<CkanModule, String> ConflictList
                                       .Distinct())
                     .ToDictionary(
                         kvp => kvp.Key,
-                        kvp => $"{kvp.Key} conflicts with " + (
-                            kvp.Value.Count() == 0
-                                ? "an unmanaged DLL or DLC"
-                                : string.Join(", ", kvp.Value)));
+                        kvp => string.Format(
+                            Properties.Resources.RelationshipResolverConflictsWith,
+                            kvp.Key, (
+                                kvp.Value.Count() == 0
+                                    ? Properties.Resources.RelationshipResolverAnUnmanaged
+                                    : string.Join(", ", kvp.Value))));
             }
         }
 
@@ -693,7 +697,7 @@ public string ReasonStringFor(CkanModule mod)
             if (mod == null)
             {
                 // If we don't have a CkanModule, it must be a DLL or DLC
-                return "Unmanaged";
+                return Properties.Resources.RelationshipResolverUnmanaged;
             }
             var reason = ReasonFor(mod);
             var is_root_type = reason.GetType() == typeof(SelectionReason.UserRequested)
@@ -708,7 +712,9 @@ public SelectionReason ReasonFor(CkanModule mod)
             if (mod == null) throw new ArgumentNullException();
             if (!reasons.ContainsKey(mod) && !ModList().Contains(mod))
             {
-                throw new ArgumentException("Mod " + mod.identifier + " is not in the list");
+                throw new ArgumentException(string.Format(
+                    Properties.Resources.RelationshipResolverModNotInList,
+                    mod.identifier));
             }
 
             return reasons[mod];
@@ -755,7 +761,7 @@ public override CkanModule Parent
 
             public override string Reason
             {
-                get { return "  Currently installed.\r\n"; }
+                get { return Properties.Resources.RelationshipResolverInstalledReason; }
             }
         }
 
@@ -772,7 +778,7 @@ public override CkanModule Parent
 
             public override string Reason
             {
-                get { return "  Requested by user.\r\n"; }
+                get { return Properties.Resources.RelationshipResolverUserReason; }
             }
         }
 
@@ -780,7 +786,7 @@ public class NoLongerUsed: SelectionReason
         {
             public override string Reason
             {
-                get { return "  Auto-installed, depending modules removed.\r\n"; }
+                get { return Properties.Resources.RelationshipResolverNoLongerUsedReason; }
             }
         }
 
@@ -794,7 +800,7 @@ public Replacement(CkanModule module)
 
             public override string Reason
             {
-                get { return "  Replacing " + Parent.name + ".\r\n"; }
+                get { return string.Format(Properties.Resources.RelationshipResolverReplacementReason, Parent.name); }
             }
         }
 
@@ -808,7 +814,7 @@ public Suggested(CkanModule module)
 
             public override string Reason
             {
-                get { return "  Suggested by " + Parent.name + ".\r\n"; }
+                get { return string.Format(Properties.Resources.RelationshipResolverSuggestedReason, Parent.name); }
             }
         }
 
@@ -822,7 +828,7 @@ public Depends(CkanModule module)
 
             public override string Reason
             {
-                get { return "  To satisfy dependency from " + Parent.name + ".\r\n"; }
+                get { return string.Format(Properties.Resources.RelationshipResolverDependsReason, Parent.name); }
             }
         }
 
@@ -836,7 +842,7 @@ public Recommended(CkanModule module)
 
             public override string Reason
             {
-                get { return "  Recommended by " + Parent.name + ".\r\n"; }
+                get { return string.Format(Properties.Resources.RelationshipResolverRecommendedReason, Parent.name); }
             }
         }
     }
diff --git a/Core/Relationships/SanityChecker.cs b/Core/Relationships/SanityChecker.cs
index 62b1e102f9..bfaf128002 100644
--- a/Core/Relationships/SanityChecker.cs
+++ b/Core/Relationships/SanityChecker.cs
@@ -30,11 +30,15 @@ IDictionary<string, ModuleVersion> dlc
             {
                 foreach (var kvp in unmetDepends)
                 {
-                    errors.Add($"{kvp.Key} has an unsatisfied dependency: {kvp.Value} is not installed");
+                    errors.Add(string.Format(
+                        Properties.Resources.SanityCheckerUnsatisfiedDependency,
+                        kvp.Key, kvp.Value));
                 }
                 foreach (var kvp in conflicts)
                 {
-                    errors.Add($"{kvp.Key} conflicts with {kvp.Value}");
+                    errors.Add(string.Format(
+                        Properties.Resources.SanityCheckerConflictsWith,
+                        kvp.Key, kvp.Value));
                 }
             }
             return errors;
diff --git a/Core/SingleAssemblyResourceManager.cs b/Core/SingleAssemblyResourceManager.cs
new file mode 100644
index 0000000000..54cc5879d6
--- /dev/null
+++ b/Core/SingleAssemblyResourceManager.cs
@@ -0,0 +1,58 @@
+using System.IO;
+using System.Globalization;
+using System.Resources;
+using System.Collections;
+using System.Reflection;
+using System.Collections.Generic;
+
+namespace CKAN
+{
+    // Thanks and credit to this guy: https://stackoverflow.com/q/1952638/2422988
+
+    class SingleAssemblyResourceManager : ResourceManager
+    {
+        public SingleAssemblyResourceManager(string basename, Assembly assembly) : base(basename, assembly)
+        {
+        }
+
+        protected override ResourceSet InternalGetResourceSet(CultureInfo culture,
+            bool createIfNotExists, bool tryParents)
+        {
+            ResourceSet rs;
+            if (!myResourceSets.TryGetValue(culture, out rs))
+            {
+                // Lazy-load default language (without caring about duplicate assignment in race conditions, no harm done)
+                if (neutralResourcesCulture == null)
+                {
+                    neutralResourcesCulture = GetNeutralResourcesLanguage(this.MainAssembly);
+                }
+
+                // If we're asking for the default language, then ask for the
+                // invariant (non-specific) resources.
+                if (neutralResourcesCulture.Equals(culture))
+                {
+                    culture = CultureInfo.InvariantCulture;
+                }
+                string resourceFileName = GetResourceFileName(culture);
+
+                Stream store = this.MainAssembly.GetManifestResourceStream(resourceFileName);
+
+                // If we found the appropriate resources in the local assembly
+                if (store != null)
+                {
+                    rs = new ResourceSet(store);
+                    // Save for later
+                    myResourceSets.Add(culture, rs);
+                }
+                else
+                {
+                    rs = base.InternalGetResourceSet(culture, createIfNotExists, tryParents);
+                }
+            }
+            return rs;
+        }
+
+        private CultureInfo neutralResourcesCulture;
+        private Dictionary<CultureInfo, ResourceSet> myResourceSets = new Dictionary<CultureInfo, ResourceSet>();
+    }
+}
diff --git a/Core/Types/CkanModule.cs b/Core/Types/CkanModule.cs
index 9117c51882..fc62df9b35 100644
--- a/Core/Types/CkanModule.cs
+++ b/Core/Types/CkanModule.cs
@@ -338,7 +338,9 @@ public CkanModule(string json, IGameComparator comparator)
             }
             catch (JsonException ex)
             {
-                throw new BadMetadataKraken(null, string.Format("JSON deserialization error: {0}", ex.Message), ex);
+                throw new BadMetadataKraken(null,
+                    string.Format(Properties.Resources.CkanModuleDeserialisationError, ex.Message),
+                    ex);
             }
             _comparator = comparator;
             CheckHealth();
@@ -352,7 +354,8 @@ private void CheckHealth()
         {
             if (!IsSpecSupported())
             {
-                throw new UnsupportedKraken($"{this} requires CKAN {spec_version}, we can't read it.");
+                throw new UnsupportedKraken(string.Format(
+                    Properties.Resources.CkanModuleUnsupportedSpec, this, spec_version));
             }
 
             // Check everything in the spec is defined.
@@ -371,7 +374,8 @@ private void CheckHealth()
 
                 if (value == null)
                 {
-                    throw new BadMetadataKraken(null, $"{identifier} missing required field {field}");
+                    throw new BadMetadataKraken(null, string.Format(
+                        Properties.Resources.CkanModuleMissingRequired, identifier, field));
                 }
             }
         }
@@ -415,7 +419,7 @@ private void DeSerialisationFixes(StreamingContext like_i_could_care)
             if (ksp_version != null && (ksp_version_max != null || ksp_version_min != null))
             {
                 // KSP version mixed with min/max.
-                throw new InvalidModuleAttributesException("ksp_version mixed with ksp_version_(min|max)", this);
+                throw new InvalidModuleAttributesException(Properties.Resources.CkanModuleKspVersionMixed, this);
             }
 
             license = license ?? new List<License> { License.UnknownLicense };
@@ -450,7 +454,7 @@ public static CkanModule FromIDandVersion(IRegistryQuerier registry, string mod,
                 if (module == null
                         || (ksp_version != null && !module.IsCompatibleKSP(ksp_version)))
                     throw new ModuleNotFoundKraken(ident, version,
-                        string.Format("Module {0} version {1} not available", ident, version));
+                        string.Format(Properties.Resources.CkanModuleNotAvailable, ident, version));
             }
             else
             {
@@ -459,7 +463,7 @@ public static CkanModule FromIDandVersion(IRegistryQuerier registry, string mod,
 
                 if (module == null)
                     throw new ModuleNotFoundKraken(mod, null,
-                        string.Format("Module {0} not installed or available", mod));
+                        string.Format(Properties.Resources.CkanModuleNotInstalledOrAvailable, mod));
             }
             return module;
         }
@@ -557,7 +561,7 @@ public string HighestCompatibleKSP()
         {
             GameVersion v = LatestCompatibleKSP();
             if (v.IsAny)
-                return "All versions";
+                return Properties.Resources.CkanModuleAllVersions;
             else
                 return v.ToString();
         }
diff --git a/Core/Types/Kraken.cs b/Core/Types/Kraken.cs
index b21540a9a0..4c730fa56f 100644
--- a/Core/Types/Kraken.cs
+++ b/Core/Types/Kraken.cs
@@ -60,14 +60,17 @@ public class ModuleNotFoundKraken : Kraken
 
         // TODO: Is there a way to set the stringify version of this?
         public ModuleNotFoundKraken(string module, string version, string reason, Exception innerException = null)
-            : base(reason ?? $"Dependency on {module} version {version} not satisfied", innerException)
+            : base(
+                reason ?? string.Format(Properties.Resources.KrakenDependencyNotSatisfied, module, version),
+                innerException)
         {
             this.module  = module;
             this.version = version;
         }
 
         public ModuleNotFoundKraken(string module, string version = null)
-            : this(module, version, $"Module not found: {module} {version ?? ""}")
+            : this(module, version,
+                string.Format(Properties.Resources.KrakenDependencyModuleNotFound, module, version ?? ""))
         { }
 
     }
@@ -92,7 +95,8 @@ public class DependencyNotSatisfiedKraken : ModuleNotFoundKraken
         public DependencyNotSatisfiedKraken(CkanModule parentModule,
             string module, string version = null, string reason = null, Exception innerException = null)
             : base(module, version,
-                reason ?? $"{parentModule.identifier} dependency on {module} version {version ?? "(any)"} not satisfied",
+                reason ?? string.Format(Properties.Resources.KrakenParentDependencyNotSatisfied,
+                    parentModule.identifier, module, version ?? Properties.Resources.KrakenAny),
                 innerException)
         {
             parent = parentModule;
@@ -165,7 +169,7 @@ private static string FormatMessage(string requested, List<CkanModule> modules,
         {
             return choice_help_text
                 ?? string.Format(
-                    "Module {0} is provided by more than one available module. Please choose one of the following:",
+                    Properties.Resources.KrakenProvidedByMoreThanOne,
                     requested);
         }
     }
@@ -182,7 +186,9 @@ public string InconsistenciesPretty
         {
             get
             {
-                return header + String.Join("\r\n", inconsistencies.Select(msg => $"* {msg}"));
+                return String.Join("\r\n",
+                    new string[] { Properties.Resources.KrakenInconsistenciesHeader }
+                    .Concat(inconsistencies.Select(msg => $"* {msg}")));
             }
         }
 
@@ -210,8 +216,6 @@ public override string ToString()
         {
             return InconsistenciesPretty + "\r\n\r\n" + StackTrace;
         }
-
-        private const string header = "The following inconsistencies were found:\r\n";
     }
 
     /// <summary>
@@ -223,10 +227,10 @@ public BadRelationshipsKraken(
             ICollection<KeyValuePair<CkanModule, RelationshipDescriptor>> depends,
             ICollection<KeyValuePair<CkanModule, RelationshipDescriptor>> conflicts
         ) : base(
-            (depends?.Select(dep => $"{dep.Key} missing dependency {dep.Value}")
+            (depends?.Select(dep => string.Format(Properties.Resources.KrakenMissingDependency, dep.Key, dep.Value))
                 ?? new string[] {}
             ).Concat(
-                conflicts?.Select(conf => $"{conf.Key} conflicts with {conf.Value}")
+                conflicts?.Select(conf => string.Format(Properties.Resources.KrakenConflictsWith, conf.Key, conf.Value))
                 ?? new string[] {}
             ).ToArray()
         )
@@ -278,7 +282,9 @@ public DownloadErrorsKraken(List<KeyValuePair<int, Exception>> errors) : base()
 
         public override string ToString()
         {
-            return "Uh oh, the following things went wrong when downloading...\r\n\r\n" + String.Join("\r\n", exceptions);
+            return String.Join("\r\n",
+                new string[] { Properties.Resources.KrakenDownloadErrorsHeader, "" }
+                .Concat(exceptions.Select(e => e.ToString())));
         }
     }
 
@@ -318,13 +324,12 @@ public override string ToString()
             if (builder == null)
             {
                 builder = new StringBuilder();
-                builder.AppendLine("One or more downloads were unsuccessful:");
+                builder.AppendLine(Properties.Resources.KrakenModuleDownloadErrorsHeader);
                 builder.AppendLine("");
                 foreach (KeyValuePair<CkanModule, Exception> kvp in exceptions)
                 {
-                    builder.AppendLine(
-                        $"Error downloading {kvp.Key.ToString()}: {kvp.Value.Message}"
-                    );
+                    builder.AppendLine(string.Format(
+                        Properties.Resources.KrakenModuleDownloadError, kvp.Key.ToString(), kvp.Value.Message));
                 }
             }
             return builder.ToString();
@@ -385,7 +390,7 @@ public class ModNotInstalledKraken : Kraken
 
         public override string Message
         {
-            get { return string.Format("Module {0} is not installed!", mod); }
+            get { return string.Format(Properties.Resources.KrakenNotInstalled, mod); }
         }
 
         // TODO: Since we override message, should we really allow users to pass in a reason
@@ -419,16 +424,9 @@ public MissingCertificateKraken(string reason = null, Exception innerException =
 
         public override string ToString()
         {
-            if (Platform.IsUnix)
-            {
-                return "Oh no! Our download failed with a certificate error!\r\n\r\n"
-                    + "Consult this page for help:\r\n\t"
-                    + HelpURLs.CertificateErrors;
-            }
-            else
-            {
-                return "Oh no! Our download failed with a certificate error!";
-            }
+            return Platform.IsUnix
+                ? string.Format(Properties.Resources.KrakenMissingCertificateUnix, HelpURLs.CertificateErrors)
+                : Properties.Resources.KrakenMissingCertificateNotUnix;
         }
     }
 
@@ -445,7 +443,7 @@ public DownloadThrottledKraken(Uri url, Uri info) : base()
 
         public override string ToString()
         {
-            return $"Download from {throttledUrl.Host} was throttled.\r\nConsider adding an authentication token to increase the throtting limit.";
+            return string.Format(Properties.Resources.KrakenDownloadThrottled, throttledUrl.Host);
         }
     }
 
@@ -461,7 +459,7 @@ public RegistryInUseKraken(string path, string reason = null, Exception inner_ex
 
         public override string ToString()
         {
-            return String.Format("CKAN is already running for this instance!\n\nIf you're certain this is not the case, then delete:\n\"{0}\"\n", lockfilePath);
+            return String.Format(Properties.Resources.KrakenAlreadyRunning, lockfilePath);
         }
     }
 
@@ -592,7 +590,7 @@ public class ReinstallModuleKraken : Kraken
         public readonly List<CkanModule> Modules;
 
         public ReinstallModuleKraken(List<CkanModule> modules)
-            : base(string.Format("Metadata changed, reinstallation recommended: {0}",
+            : base(string.Format(Properties.Resources.KrakenReinstallModule,
                 string.Join(", ", modules)))
         {
             Modules = modules;
diff --git a/Core/Types/License.cs b/Core/Types/License.cs
index f1eb38853d..88a2df4226 100644
--- a/Core/Types/License.cs
+++ b/Core/Types/License.cs
@@ -41,7 +41,7 @@ public License(string license)
             {
                 throw new BadMetadataKraken(
                     null,
-                    string.Format("The license {0} is invalid", license)
+                    string.Format(Properties.Resources.LicenceInvalid, license)
                 );
             }
 
diff --git a/Core/Types/ModuleInstallDescriptor.cs b/Core/Types/ModuleInstallDescriptor.cs
index 1e6167fc54..a194e745f1 100644
--- a/Core/Types/ModuleInstallDescriptor.cs
+++ b/Core/Types/ModuleInstallDescriptor.cs
@@ -73,7 +73,7 @@ internal void DeSerialisationFixes(StreamingContext like_i_could_care)
             // this check now that we're doing better json-fu above.
             if (install_to == null)
             {
-                throw new BadMetadataKraken(null, "Install stanzas must have an install_to");
+                throw new BadMetadataKraken(null, Properties.Resources.ModuleInstallDescriptorMustHaveInstallTo);
             }
 
             var setCount = new[] { file, find, find_regexp }.Count(i => i != null);
@@ -81,12 +81,12 @@ internal void DeSerialisationFixes(StreamingContext like_i_could_care)
             // Make sure we have either a `file`, `find`, or `find_regexp` stanza.
             if (setCount == 0)
             {
-                throw new BadMetadataKraken(null, "Install stanzas require either a file, find, or find_regexp directive");
+                throw new BadMetadataKraken(null, Properties.Resources.ModuleInstallDescriptorRequireFileFind);
             }
 
             if (setCount > 1)
             {
-                throw new BadMetadataKraken(null, "Install stanzas must only include one of file, find, or find_regexp directives");
+                throw new BadMetadataKraken(null, Properties.Resources.ModuleInstallDescriptorTooManyFileFind);
             }
 
             // Make sure only filter or include_only fields exist but not both at the same time
@@ -95,7 +95,7 @@ internal void DeSerialisationFixes(StreamingContext like_i_could_care)
 
             if (filterCount > 0 && includeOnlyCount > 0)
             {
-                throw new BadMetadataKraken(null, "Install stanzas can only contain filter or include_only directives, not both");
+                throw new BadMetadataKraken(null, Properties.Resources.ModuleInstallDescriptorTooManyFilterInclude);
             }
 
             // Normalize paths on load (note, doesn't cover assignment like in tests)
@@ -242,7 +242,7 @@ private void EnsurePattern()
                 }
                 else
                 {
-                    throw new UnsupportedKraken("Install stanzas requires `file` or `find` or `find_regexp`.");
+                    throw new UnsupportedKraken(Properties.Resources.ModuleInstallDescriptorRequireFileFind);
                 }
             }
         }
@@ -328,7 +328,8 @@ public List<InstallableFile> FindInstallableFiles(ZipFile zipfile, GameInstance
 
             // The installation path cannot contain updirs
             if (install_to.Contains("/../") || install_to.EndsWith("/.."))
-                throw new BadInstallLocationKraken("Invalid installation path: " + install_to);
+                throw new BadInstallLocationKraken(string.Format(
+                    Properties.Resources.ModuleInstallDescriptorInvalidInstallPath, install_to));
 
             if (ksp == null)
             {
@@ -359,7 +360,8 @@ public List<InstallableFile> FindInstallableFiles(ZipFile zipfile, GameInstance
                         }
                         else
                         {
-                            throw new BadInstallLocationKraken("Unknown install_to " + install_to);
+                            throw new BadInstallLocationKraken(string.Format(
+                                Properties.Resources.ModuleInstallDescriptorUnknownInstallPath, install_to));
                         }
                         break;
                 }
@@ -413,7 +415,8 @@ public List<InstallableFile> FindInstallableFiles(ZipFile zipfile, GameInstance
             if (files.Count == 0)
             {
                 // We have null as the first argument here, because we don't know which module we're installing
-                throw new BadMetadataKraken(null, String.Format("No files found matching {0} to install!", DescribeMatch()));
+                throw new BadMetadataKraken(null, String.Format(
+                    Properties.Resources.ModuleInstallDescriptorNoFilesFound, DescribeMatch()));
             }
 
             return files;
@@ -462,7 +465,7 @@ internal string TransformOutputName(IGame game, string outputName, string instal
             {
                 if (@as.Contains("/") || @as.Contains("\\"))
                 {
-                    throw new BadMetadataKraken(null, "`as` may not include path separators.");
+                    throw new BadMetadataKraken(null, Properties.Resources.ModuleInstallDescriptorAsNoPathSeparators);
                 }
                 // Replace first path component with @as
                 outputName = ReplaceFirstPiece(outputName, "/", @as);
diff --git a/Core/Types/ModuleResolution.cs b/Core/Types/ModuleResolution.cs
deleted file mode 100644
index ec4ecc0940..0000000000
--- a/Core/Types/ModuleResolution.cs
+++ /dev/null
@@ -1,45 +0,0 @@
-using System;
-using System.Collections;
-using System.Collections.Generic;
-using System.Linq;
-
-namespace CKAN.Types
-{
-    public class ModuleResolution : IEnumerable<CkanModule>
-    {
-        public List<CkanModule> CachedModules { get; set; }
-        public List<CkanModule> UncachedModules { get; set; }
-
-        public IEnumerable<CkanModule> All => CachedModules.Concat(UncachedModules);
-
-        public int Count => CachedModules.Count + UncachedModules.Count;
-
-        public ModuleResolution(IEnumerable<CkanModule> modules, Func<CkanModule, bool> isCached)
-        {
-            CachedModules = new List<CkanModule>();
-            UncachedModules = new List<CkanModule>();
-
-            foreach (var module in modules)
-            {
-                if (isCached(module))
-                {
-                    CachedModules.Add(module);
-                }
-                else
-                {
-                    UncachedModules.Add(module);
-                }
-            }
-        }
-
-        public IEnumerator<CkanModule> GetEnumerator()
-        {
-            return All.GetEnumerator();
-        }
-
-        IEnumerator IEnumerable.GetEnumerator()
-        {
-            return GetEnumerator();
-        }
-    }
-}
diff --git a/Core/Types/RelationshipDescriptor.cs b/Core/Types/RelationshipDescriptor.cs
index 6408244d6b..7c7d4c2b28 100644
--- a/Core/Types/RelationshipDescriptor.cs
+++ b/Core/Types/RelationshipDescriptor.cs
@@ -193,22 +193,6 @@ public override bool StartsWith(string prefix)
             return name.IndexOf(prefix, StringComparison.CurrentCultureIgnoreCase) == 0;
         }
 
-        /// <summary>
-        /// A user friendly message for what versions satisfies this descriptor.
-        /// </summary>
-        [JsonIgnore]
-        public string RequiredVersion
-        {
-            get
-            {
-                if (version != null)
-                    return version.ToString();
-                return string.Format("between {0} and {1} inclusive.",
-                    min_version != null ? min_version.ToString() : "any version",
-                    max_version != null ? max_version.ToString() : "any version");
-            }
-        }
-
         /// <summary>
         /// Generate a user readable description of the relationship
         /// </summary>
@@ -224,9 +208,11 @@ public override string ToString()
         {
             return
                   version     != null                        ? $"{name} {version}"
-                : min_version != null && max_version != null ? $"{name} {min_version} -- {max_version}"
-                : min_version != null                        ? $"{name} {min_version} or later"
-                : max_version != null                        ? $"{name} {max_version} or earlier"
+                : min_version != null && max_version != null ? $"{name} {min_version}–{max_version}"
+                : min_version != null
+                    ? string.Format(Properties.Resources.RelationshipDescriptorMinVersionOnly, name, min_version)
+                : max_version != null
+                    ? string.Format(Properties.Resources.RelationshipDescriptorMaxVersionOnly, name, max_version)
                 : name;
         }
 
@@ -304,7 +290,8 @@ public override bool StartsWith(string prefix)
         public override string ToString()
         {
             return any_of?.Select(r => r.ToString())
-                .Aggregate((a, b) => $"{a} OR {b}");
+                .Aggregate((a, b) =>
+                    string.Format(Properties.Resources.RelationshipDescriptorAnyOfJoiner, a, b));
         }
     }
 }
diff --git a/Core/Types/ReleaseStatus.cs b/Core/Types/ReleaseStatus.cs
index e513660287..aac8d0defe 100644
--- a/Core/Types/ReleaseStatus.cs
+++ b/Core/Types/ReleaseStatus.cs
@@ -46,7 +46,7 @@ public ReleaseStatus(string status)
             {
                 throw new BadMetadataKraken(
                     null,
-                    String.Format("{0} is not a valid release status", status)
+                    String.Format(Properties.Resources.ReleaseStatusInvalid, status)
                 );
             }
 
@@ -58,4 +58,4 @@ public override string ToString()
             return status;
         }
     }
-}
\ No newline at end of file
+}
diff --git a/Core/Types/Repository.cs b/Core/Types/Repository.cs
index dbc4df1aeb..6f0327f248 100644
--- a/Core/Types/Repository.cs
+++ b/Core/Types/Repository.cs
@@ -7,7 +7,7 @@ namespace CKAN
 {
     public class Repository : IEquatable<Repository>
     {
-        [JsonIgnore] public static readonly string default_ckan_repo_name = "default";
+        [JsonIgnore] public static string default_ckan_repo_name => Properties.Resources.RepositoryDefaultName;
 
         public string  name;
         public Uri     uri;
diff --git a/Core/Versioning/CkanModuleVersion.cs b/Core/Versioning/CkanModuleVersion.cs
index 346575708e..1e07f8faf8 100644
--- a/Core/Versioning/CkanModuleVersion.cs
+++ b/Core/Versioning/CkanModuleVersion.cs
@@ -22,7 +22,7 @@ public CkanModuleVersion(string version, string name)
             : base(version)
         {
             Name = name;
-            _string = $"{base.ToString()} aka {Name}";
+            _string = string.Format(Properties.Resources.CkanModuleVersionToString, base.ToString(), Name);
         }
 
         /// <summary>
diff --git a/Core/Versioning/GameVersion.cs b/Core/Versioning/GameVersion.cs
index 0d4a7b99df..192cb8e795 100644
--- a/Core/Versioning/GameVersion.cs
+++ b/Core/Versioning/GameVersion.cs
@@ -280,7 +280,7 @@ public string ToYalovString()
                 // 1.99.99
                 || (_major == VersionsMax[""].Major && VersionsMax.TryGetValue($"{_major}", out value) && _minor >= UptoNines(value.Minor)))
             {
-                return "any";
+                return Properties.Resources.GameVersionYalovAny;
             }
             else if (IsMinorDefined
                 && VersionsMax.TryGetValue($"{_major}.{_minor}", out value)
@@ -495,7 +495,7 @@ public bool InBuildMap(IGame game)
         /// </summary>
         /// <returns>A complete GameVersion object</returns>
         /// <param name="user">A IUser instance, to raise the corresponding dialog.</param>
-        public GameVersion RaiseVersionSelectionDialog (IGame game, IUser user)
+        public GameVersion RaiseVersionSelectionDialog(IGame game, IUser user)
         {
             if (IsFullyDefined && InBuildMap(game))
             {
@@ -505,7 +505,7 @@ public GameVersion RaiseVersionSelectionDialog (IGame game, IUser user)
             }
             else if (!IsMajorDefined || !IsMinorDefined)
             {
-                throw new BadGameVersionKraken("Needs at least Major and Minor");
+                throw new BadGameVersionKraken(Properties.Resources.GameVersionSelectNeedOne);
             }
             else
             {
@@ -514,7 +514,7 @@ public GameVersion RaiseVersionSelectionDialog (IGame game, IUser user)
                 List<GameVersion> possibleVersions = new List<GameVersion>();
 
                 // Default message passed to RaiseSelectionDialog.
-                string message = "The specified version is not unique, please select one:";
+                string message = Properties.Resources.GameVersionSelectHeader;
 
                 // Find the versions which are part of the range.
                 foreach (GameVersion ver in knownVersions)
@@ -539,7 +539,7 @@ public GameVersion RaiseVersionSelectionDialog (IGame game, IUser user)
                     // Only compare Major, Minor, Patch and adjust the message.
                     else
                     {
-                        message = "The build number is not known for this patch. Please select one:";
+                        message = Properties.Resources.GameVersionSelectBuildHeader;
                         if (Major == ver.Major && Minor == ver.Minor && Patch == ver.Patch)
                         {
                             possibleVersions.Add(ver);
@@ -551,7 +551,7 @@ public GameVersion RaiseVersionSelectionDialog (IGame game, IUser user)
                 if (possibleVersions.Count == 0)
                 {
                     // No version found in the map. Happens for future or other unknown versions.
-                    throw new BadGameVersionKraken("The version is not known to CKAN.");
+                    throw new BadGameVersionKraken(Properties.Resources.GameVersionNotKnown);
                 }
                 else if (possibleVersions.Count == 1)
                 {
diff --git a/Core/Versioning/GameVersionCriteria.cs b/Core/Versioning/GameVersionCriteria.cs
index 0b603d3cf5..e33ead8c76 100644
--- a/Core/Versioning/GameVersionCriteria.cs
+++ b/Core/Versioning/GameVersionCriteria.cs
@@ -68,7 +68,7 @@ public override String ToString()
             {
                 versionList.Add(version.ToString());
             }
-            return "[Versions: " + String.Join( ", ", versionList) + "]";
+            return string.Format(Properties.Resources.GameVersionCriteriaToString, string.Join( ", ", versionList));
         }
     }
 }
diff --git a/Core/Versioning/GameVersionRange.cs b/Core/Versioning/GameVersionRange.cs
index 71382c6ba0..74ce3d94ad 100644
--- a/Core/Versioning/GameVersionRange.cs
+++ b/Core/Versioning/GameVersionRange.cs
@@ -107,8 +107,10 @@ private static string SameVersionString(GameVersion v)
         public static string VersionSpan(IGame game, GameVersion minKsp, GameVersion maxKsp)
         {
             return minKsp == maxKsp ? $"{game.ShortName} {SameVersionString(minKsp)}"
-                :  minKsp.IsAny     ? $"{game.ShortName} {maxKsp} and earlier"
-                :  maxKsp.IsAny     ? $"{game.ShortName} {minKsp} and later"
+                :  minKsp.IsAny
+                    ? string.Format(Properties.Resources.GameVersionRangeMinOnly, game.ShortName, maxKsp)
+                :  maxKsp.IsAny
+                    ? string.Format(Properties.Resources.GameVersionRangeMaxOnly, game.ShortName, minKsp)
                 :                     $"{game.ShortName} {minKsp}–{maxKsp}";
         }
 
diff --git a/Core/Versioning/ProvidesModuleVersion.cs b/Core/Versioning/ProvidesModuleVersion.cs
index 9ee347c394..8ec52cf448 100644
--- a/Core/Versioning/ProvidesModuleVersion.cs
+++ b/Core/Versioning/ProvidesModuleVersion.cs
@@ -15,7 +15,7 @@ public sealed class ProvidesModuleVersion : ModuleVersion
         /// <param name="version">The version of the providing module.</param>
         public ProvidesModuleVersion(string identifier, string version) : base(version)
         {
-            _string = $"{version} (provided by {identifier})";
+            _string = string.Format(Properties.Resources.ProvidesModuleVersionToString, version, identifier);
         }
 
         /// <summary>
diff --git a/Core/Versioning/UnmanagedModuleVersion.cs b/Core/Versioning/UnmanagedModuleVersion.cs
index 1a7ac2f911..44f0113421 100644
--- a/Core/Versioning/UnmanagedModuleVersion.cs
+++ b/Core/Versioning/UnmanagedModuleVersion.cs
@@ -13,7 +13,9 @@ public sealed class UnmanagedModuleVersion : ModuleVersion
         public UnmanagedModuleVersion(string version) : base(version ?? "0")
         {
             IsUnknownVersion = version == null;
-            _string = version == null ? "(unmanaged)" : $"{version} (unmanaged)";
+            _string = version == null
+                ? Properties.Resources.UnmanagedModuleVersionUnknown
+                : string.Format(Properties.Resources.UnmanagedModuleVersionKnown, version);
         }
 
         public override string ToString()
diff --git a/GUI/CKAN-GUI.csproj b/GUI/CKAN-GUI.csproj
index 3276e6e847..a091288891 100644
--- a/GUI/CKAN-GUI.csproj
+++ b/GUI/CKAN-GUI.csproj
@@ -11,7 +11,7 @@
     <Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
     <ProjectGuid>{A79F9D54-315C-472B-928F-713A5860B2BE}</ProjectGuid>
     <OutputType>WinExe</OutputType>
-    <RootNamespace>CKAN</RootNamespace>
+    <RootNamespace>CKAN.GUI</RootNamespace>
     <ApplicationIcon>..\assets\ckan.ico</ApplicationIcon>
     <TargetFrameworkVersion>v4.5</TargetFrameworkVersion>
     <FileAlignment>512</FileAlignment>
@@ -937,37 +937,37 @@
       <DependentUpon>..\..\Dialogs\PluginsDialog.cs</DependentUpon>
     </EmbeddedResource>
     <EmbeddedResource Include="Properties\Resources.resx">
-      <LogicalName>CKAN.Properties.Resources.resources</LogicalName>
+      <LogicalName>CKAN.GUI.Properties.Resources.resources</LogicalName>
       <SubType>Designer</SubType>
       <Generator>ResXFileCodeGenerator</Generator>
       <LastGenOutput>Resources.Designer.cs</LastGenOutput>
     </EmbeddedResource>
     <EmbeddedResource Include="Properties\Resources.de-DE.resx">
-      <LogicalName>CKAN.Properties.Resources.de-DE.resources</LogicalName>
+      <LogicalName>CKAN.GUI.Properties.Resources.de-DE.resources</LogicalName>
       <SubType>Designer</SubType>
       <Generator>ResXFileCodeGenerator</Generator>
       <LastGenOutput>Resources.Designer.cs</LastGenOutput>
     </EmbeddedResource>
     <EmbeddedResource Include="Properties\Resources.fr-FR.resx">
-      <LogicalName>CKAN.Properties.Resources.fr-FR.resources</LogicalName>
+      <LogicalName>CKAN.GUI.Properties.Resources.fr-FR.resources</LogicalName>
       <SubType>Designer</SubType>
       <Generator>ResXFileCodeGenerator</Generator>
       <LastGenOutput>Resources.Designer.cs</LastGenOutput>
     </EmbeddedResource>
     <EmbeddedResource Include="Properties\Resources.zh-CN.resx">
-      <LogicalName>CKAN.Properties.Resources.zh-CN.resources</LogicalName>
+      <LogicalName>CKAN.GUI.Properties.Resources.zh-CN.resources</LogicalName>
       <SubType>Designer</SubType>
       <Generator>ResXFileCodeGenerator</Generator>
       <LastGenOutput>Resources.Designer.cs</LastGenOutput>
     </EmbeddedResource>
     <EmbeddedResource Include="Properties\Resources.en-US.resx">
-      <LogicalName>CKAN.Properties.Resources.en-US.resources</LogicalName>
+      <LogicalName>CKAN.GUI.Properties.Resources.en-US.resources</LogicalName>
       <SubType>Designer</SubType>
       <Generator>ResXFileCodeGenerator</Generator>
       <LastGenOutput>Resources.Designer.cs</LastGenOutput>
     </EmbeddedResource>
     <EmbeddedResource Include="Properties\Resources.pt-BR.resx">
-      <LogicalName>CKAN.Properties.Resources.pt-BR.resources</LogicalName>
+      <LogicalName>CKAN.GUI.Properties.Resources.pt-BR.resources</LogicalName>
       <SubType>Designer</SubType>
       <Generator>ResXFileCodeGenerator</Generator>
       <LastGenOutput>Resources.Designer.cs</LastGenOutput>
@@ -979,13 +979,13 @@
       <LastGenOutput>Resources.Designer.cs</LastGenOutput>
     </EmbeddedResource>
     <EmbeddedResource Include="Properties\Resources.ru-RU.resx">
-      <LogicalName>CKAN.Properties.Resources.ru-RU.resources</LogicalName>
+      <LogicalName>CKAN.GUI.Properties.Resources.ru-RU.resources</LogicalName>
       <SubType>Designer</SubType>
       <Generator>ResXFileCodeGenerator</Generator>
       <LastGenOutput>Resources.Designer.cs</LastGenOutput>
     </EmbeddedResource>
     <EmbeddedResource Include="Properties\Resources.ja-JP.resx">
-      <LogicalName>CKAN.Properties.Resources.ja-JP.resources</LogicalName>
+      <LogicalName>CKAN.GUI.Properties.Resources.ja-JP.resources</LogicalName>
       <SubType>Designer</SubType>
       <Generator>ResXFileCodeGenerator</Generator>
       <LastGenOutput>Resources.Designer.cs</LastGenOutput>
diff --git a/GUI/ControlFactory.cs b/GUI/ControlFactory.cs
index 5dc14746a1..a37fbd0fc2 100644
--- a/GUI/ControlFactory.cs
+++ b/GUI/ControlFactory.cs
@@ -1,7 +1,7 @@
 using System.Diagnostics;
 using System.Threading;
 
-namespace CKAN
+namespace CKAN.GUI
 {
     /// <summary>
     /// This class ensures that all controls are created from the same thread.
diff --git a/GUI/Controls/AllModVersions.Designer.cs b/GUI/Controls/AllModVersions.Designer.cs
index 0204f2bad0..aa271f8161 100644
--- a/GUI/Controls/AllModVersions.Designer.cs
+++ b/GUI/Controls/AllModVersions.Designer.cs
@@ -1,4 +1,4 @@
-namespace CKAN
+namespace CKAN.GUI
 {
     partial class AllModVersions
     {
diff --git a/GUI/Controls/AllModVersions.cs b/GUI/Controls/AllModVersions.cs
index 46d94fba5c..8fc6e2e48e 100644
--- a/GUI/Controls/AllModVersions.cs
+++ b/GUI/Controls/AllModVersions.cs
@@ -7,7 +7,7 @@
 
 using CKAN.Versioning;
 
-namespace CKAN
+namespace CKAN.GUI
 {
     public partial class AllModVersions : UserControl
     {
diff --git a/GUI/Controls/Changeset.Designer.cs b/GUI/Controls/Changeset.Designer.cs
index 9cc15111e4..a0a5703bca 100644
--- a/GUI/Controls/Changeset.Designer.cs
+++ b/GUI/Controls/Changeset.Designer.cs
@@ -1,4 +1,4 @@
-namespace CKAN
+namespace CKAN.GUI
 {
     partial class Changeset
     {
diff --git a/GUI/Controls/Changeset.cs b/GUI/Controls/Changeset.cs
index 6511bda499..0503e69eb5 100644
--- a/GUI/Controls/Changeset.cs
+++ b/GUI/Controls/Changeset.cs
@@ -5,7 +5,7 @@
 using System.Windows.Forms;
 using CKAN.Extensions;
 
-namespace CKAN
+namespace CKAN.GUI
 {
     public partial class Changeset : UserControl
     {
@@ -102,7 +102,7 @@ private ListViewItem makeItem(ModChange change)
             return new ListViewItem(new string[]
             {
                 change.NameAndStatus,
-                change.ChangeType.ToString(),
+                change.ChangeType.ToI18nString(),
                 warnLbl != null
                     ? string.Format(
                         Properties.Resources.MainChangesetWarningInstallingModuleWithLabel,
diff --git a/GUI/Controls/ChooseProvidedMods.Designer.cs b/GUI/Controls/ChooseProvidedMods.Designer.cs
index 1436b66584..eef0a0552c 100644
--- a/GUI/Controls/ChooseProvidedMods.Designer.cs
+++ b/GUI/Controls/ChooseProvidedMods.Designer.cs
@@ -1,4 +1,4 @@
-namespace CKAN
+namespace CKAN.GUI
 {
     partial class ChooseProvidedMods
     {
diff --git a/GUI/Controls/ChooseProvidedMods.cs b/GUI/Controls/ChooseProvidedMods.cs
index 33c38de114..a2610daaf3 100644
--- a/GUI/Controls/ChooseProvidedMods.cs
+++ b/GUI/Controls/ChooseProvidedMods.cs
@@ -4,7 +4,7 @@
 using System.Collections.Generic;
 using System.Windows.Forms;
 
-namespace CKAN
+namespace CKAN.GUI
 {
     public partial class ChooseProvidedMods : UserControl
     {
diff --git a/GUI/Controls/ChooseRecommendedMods.Designer.cs b/GUI/Controls/ChooseRecommendedMods.Designer.cs
index 66b65590fd..3e4a6ac06e 100644
--- a/GUI/Controls/ChooseRecommendedMods.Designer.cs
+++ b/GUI/Controls/ChooseRecommendedMods.Designer.cs
@@ -1,4 +1,4 @@
-namespace CKAN
+namespace CKAN.GUI
 {
     partial class ChooseRecommendedMods
     {
diff --git a/GUI/Controls/ChooseRecommendedMods.cs b/GUI/Controls/ChooseRecommendedMods.cs
index 493fb1a062..5dc5602e64 100644
--- a/GUI/Controls/ChooseRecommendedMods.cs
+++ b/GUI/Controls/ChooseRecommendedMods.cs
@@ -7,7 +7,7 @@
 using CKAN.Extensions;
 using CKAN.Versioning;
 
-namespace CKAN
+namespace CKAN.GUI
 {
     public partial class ChooseRecommendedMods : UserControl
     {
diff --git a/GUI/Controls/DeleteDirectories.Designer.cs b/GUI/Controls/DeleteDirectories.Designer.cs
index 21be2d6c5e..d1c0f4982e 100644
--- a/GUI/Controls/DeleteDirectories.Designer.cs
+++ b/GUI/Controls/DeleteDirectories.Designer.cs
@@ -1,4 +1,4 @@
-namespace CKAN
+namespace CKAN.GUI
 {
     partial class DeleteDirectories
     {
diff --git a/GUI/Controls/DeleteDirectories.cs b/GUI/Controls/DeleteDirectories.cs
index 5d89b7917e..4ca60fb9bf 100644
--- a/GUI/Controls/DeleteDirectories.cs
+++ b/GUI/Controls/DeleteDirectories.cs
@@ -6,7 +6,7 @@
 using System.Windows.Forms;
 using CKAN.Extensions;
 
-namespace CKAN
+namespace CKAN.GUI
 {
     public partial class DeleteDirectories : UserControl
     {
diff --git a/GUI/Controls/DropdownMenuButton.cs b/GUI/Controls/DropdownMenuButton.cs
index d3d105945a..f99fe7c70e 100644
--- a/GUI/Controls/DropdownMenuButton.cs
+++ b/GUI/Controls/DropdownMenuButton.cs
@@ -2,7 +2,7 @@
 using System.Drawing;
 using System.Windows.Forms;
 
-namespace CKAN
+namespace CKAN.GUI
 {
     /// <summary>
     /// Button with a Menu property that displays when you click
diff --git a/GUI/Controls/EditModSearch.Designer.cs b/GUI/Controls/EditModSearch.Designer.cs
index 4d5866e1b4..d59133353c 100644
--- a/GUI/Controls/EditModSearch.Designer.cs
+++ b/GUI/Controls/EditModSearch.Designer.cs
@@ -1,4 +1,4 @@
-namespace CKAN
+namespace CKAN.GUI
 {
     partial class EditModSearch
     {
@@ -34,9 +34,9 @@ private void InitializeComponent()
             this.ToolTip = new System.Windows.Forms.ToolTip();
             this.FilterCombinedLabel = new System.Windows.Forms.Label();
             this.FilterOrLabel = new System.Windows.Forms.Label();
-            this.FilterCombinedTextBox = new CKAN.HintTextBox();
+            this.FilterCombinedTextBox = new CKAN.GUI.HintTextBox();
             this.ExpandButton = new System.Windows.Forms.CheckBox();
-            this.SearchDetails = new CKAN.EditModSearchDetails();
+            this.SearchDetails = new CKAN.GUI.EditModSearchDetails();
             this.SuspendLayout();
             //
             // ToolTip
@@ -127,8 +127,8 @@ private void InitializeComponent()
         private System.Windows.Forms.ToolTip ToolTip;
         private System.Windows.Forms.Label FilterCombinedLabel;
         private System.Windows.Forms.Label FilterOrLabel;
-        private CKAN.HintTextBox FilterCombinedTextBox;
+        private CKAN.GUI.HintTextBox FilterCombinedTextBox;
         private System.Windows.Forms.CheckBox ExpandButton;
-        private CKAN.EditModSearchDetails SearchDetails;
+        private CKAN.GUI.EditModSearchDetails SearchDetails;
     }
 }
diff --git a/GUI/Controls/EditModSearch.cs b/GUI/Controls/EditModSearch.cs
index c34120bf89..366da72a37 100644
--- a/GUI/Controls/EditModSearch.cs
+++ b/GUI/Controls/EditModSearch.cs
@@ -6,7 +6,7 @@
 using Timer = System.Windows.Forms.Timer;
 using log4net;
 
-namespace CKAN
+namespace CKAN.GUI
 {
     /// <summary>
     /// A control for displaying and editing a search of mods.
diff --git a/GUI/Controls/EditModSearchDetails.Designer.cs b/GUI/Controls/EditModSearchDetails.Designer.cs
index 66264bd87d..cbd5484dae 100644
--- a/GUI/Controls/EditModSearchDetails.Designer.cs
+++ b/GUI/Controls/EditModSearchDetails.Designer.cs
@@ -1,4 +1,4 @@
-namespace CKAN
+namespace CKAN.GUI
 {
     partial class EditModSearchDetails
     {
@@ -31,37 +31,37 @@ private void InitializeComponent()
             this.components = new System.ComponentModel.Container();
             System.ComponentModel.ComponentResourceManager resources = new SingleAssemblyComponentResourceManager(typeof(EditModSearchDetails));
             this.FilterByNameLabel = new System.Windows.Forms.Label();
-            this.FilterByNameTextBox = new CKAN.HintTextBox();
+            this.FilterByNameTextBox = new CKAN.GUI.HintTextBox();
             this.FilterByAuthorLabel = new System.Windows.Forms.Label();
-            this.FilterByAuthorTextBox = new CKAN.HintTextBox();
+            this.FilterByAuthorTextBox = new CKAN.GUI.HintTextBox();
             this.FilterByDescriptionLabel = new System.Windows.Forms.Label();
-            this.FilterByDescriptionTextBox = new CKAN.HintTextBox();
+            this.FilterByDescriptionTextBox = new CKAN.GUI.HintTextBox();
             this.FilterByLanguageLabel = new System.Windows.Forms.Label();
-            this.FilterByLanguageTextBox = new CKAN.HintTextBox();
+            this.FilterByLanguageTextBox = new CKAN.GUI.HintTextBox();
             this.FilterByDependsLabel = new System.Windows.Forms.Label();
-            this.FilterByDependsTextBox = new CKAN.HintTextBox();
+            this.FilterByDependsTextBox = new CKAN.GUI.HintTextBox();
             this.FilterByRecommendsLabel = new System.Windows.Forms.Label();
-            this.FilterByRecommendsTextBox = new CKAN.HintTextBox();
+            this.FilterByRecommendsTextBox = new CKAN.GUI.HintTextBox();
             this.FilterBySuggestsLabel = new System.Windows.Forms.Label();
-            this.FilterBySuggestsTextBox = new CKAN.HintTextBox();
+            this.FilterBySuggestsTextBox = new CKAN.GUI.HintTextBox();
             this.FilterByConflictsLabel = new System.Windows.Forms.Label();
-            this.FilterByConflictsTextBox = new CKAN.HintTextBox();
+            this.FilterByConflictsTextBox = new CKAN.GUI.HintTextBox();
             this.FilterByTagsLabel = new System.Windows.Forms.Label();
-            this.FilterByTagsTextBox = new CKAN.HintTextBox();
+            this.FilterByTagsTextBox = new CKAN.GUI.HintTextBox();
             this.FilterByLabelsLabel = new System.Windows.Forms.Label();
-            this.FilterByLabelsTextBox = new CKAN.HintTextBox();
+            this.FilterByLabelsTextBox = new CKAN.GUI.HintTextBox();
             this.CompatibleLabel = new System.Windows.Forms.Label();
-            this.CompatibleToggle = new CKAN.TriStateToggle();
+            this.CompatibleToggle = new CKAN.GUI.TriStateToggle();
             this.InstalledLabel = new System.Windows.Forms.Label();
-            this.InstalledToggle = new CKAN.TriStateToggle();
+            this.InstalledToggle = new CKAN.GUI.TriStateToggle();
             this.CachedLabel = new System.Windows.Forms.Label();
-            this.CachedToggle = new CKAN.TriStateToggle();
+            this.CachedToggle = new CKAN.GUI.TriStateToggle();
             this.NewlyCompatibleLabel = new System.Windows.Forms.Label();
-            this.NewlyCompatibleToggle = new CKAN.TriStateToggle();
+            this.NewlyCompatibleToggle = new CKAN.GUI.TriStateToggle();
             this.UpgradeableLabel = new System.Windows.Forms.Label();
-            this.UpgradeableToggle = new CKAN.TriStateToggle();
+            this.UpgradeableToggle = new CKAN.GUI.TriStateToggle();
             this.ReplaceableLabel = new System.Windows.Forms.Label();
-            this.ReplaceableToggle = new CKAN.TriStateToggle();
+            this.ReplaceableToggle = new CKAN.GUI.TriStateToggle();
             this.SuspendLayout();
             //
             // FilterByNameLabel
@@ -448,36 +448,36 @@ private void InitializeComponent()
         #endregion
 
         private System.Windows.Forms.Label FilterByNameLabel;
-        internal CKAN.HintTextBox FilterByNameTextBox;
+        internal CKAN.GUI.HintTextBox FilterByNameTextBox;
         private System.Windows.Forms.Label FilterByAuthorLabel;
-        internal CKAN.HintTextBox FilterByAuthorTextBox;
+        internal CKAN.GUI.HintTextBox FilterByAuthorTextBox;
         private System.Windows.Forms.Label FilterByDescriptionLabel;
-        internal CKAN.HintTextBox FilterByDescriptionTextBox;
+        internal CKAN.GUI.HintTextBox FilterByDescriptionTextBox;
         private System.Windows.Forms.Label FilterByLanguageLabel;
-        internal CKAN.HintTextBox FilterByLanguageTextBox;
+        internal CKAN.GUI.HintTextBox FilterByLanguageTextBox;
         private System.Windows.Forms.Label FilterByDependsLabel;
-        internal CKAN.HintTextBox FilterByDependsTextBox;
+        internal CKAN.GUI.HintTextBox FilterByDependsTextBox;
         private System.Windows.Forms.Label FilterByRecommendsLabel;
-        internal CKAN.HintTextBox FilterByRecommendsTextBox;
+        internal CKAN.GUI.HintTextBox FilterByRecommendsTextBox;
         private System.Windows.Forms.Label FilterBySuggestsLabel;
-        internal CKAN.HintTextBox FilterBySuggestsTextBox;
+        internal CKAN.GUI.HintTextBox FilterBySuggestsTextBox;
         private System.Windows.Forms.Label FilterByConflictsLabel;
-        internal CKAN.HintTextBox FilterByConflictsTextBox;
+        internal CKAN.GUI.HintTextBox FilterByConflictsTextBox;
         private System.Windows.Forms.Label FilterByTagsLabel;
-        internal CKAN.HintTextBox FilterByTagsTextBox;
+        internal CKAN.GUI.HintTextBox FilterByTagsTextBox;
         private System.Windows.Forms.Label FilterByLabelsLabel;
-        internal CKAN.HintTextBox FilterByLabelsTextBox;
+        internal CKAN.GUI.HintTextBox FilterByLabelsTextBox;
         private System.Windows.Forms.Label CompatibleLabel;
-        internal CKAN.TriStateToggle CompatibleToggle;
+        internal CKAN.GUI.TriStateToggle CompatibleToggle;
         private System.Windows.Forms.Label InstalledLabel;
-        internal CKAN.TriStateToggle InstalledToggle;
+        internal CKAN.GUI.TriStateToggle InstalledToggle;
         private System.Windows.Forms.Label CachedLabel;
-        internal CKAN.TriStateToggle CachedToggle;
+        internal CKAN.GUI.TriStateToggle CachedToggle;
         private System.Windows.Forms.Label NewlyCompatibleLabel;
-        internal CKAN.TriStateToggle NewlyCompatibleToggle;
+        internal CKAN.GUI.TriStateToggle NewlyCompatibleToggle;
         private System.Windows.Forms.Label UpgradeableLabel;
-        internal CKAN.TriStateToggle UpgradeableToggle;
+        internal CKAN.GUI.TriStateToggle UpgradeableToggle;
         private System.Windows.Forms.Label ReplaceableLabel;
-        internal CKAN.TriStateToggle ReplaceableToggle;
+        internal CKAN.GUI.TriStateToggle ReplaceableToggle;
     }
 }
diff --git a/GUI/Controls/EditModSearchDetails.cs b/GUI/Controls/EditModSearchDetails.cs
index 370d5f03a9..ae69940bde 100644
--- a/GUI/Controls/EditModSearchDetails.cs
+++ b/GUI/Controls/EditModSearchDetails.cs
@@ -3,7 +3,7 @@
 using System.Windows.Forms;
 using log4net;
 
-namespace CKAN
+namespace CKAN.GUI
 {
     /// <summary>
     /// A control for displaying and editing a search of mods.
diff --git a/GUI/Controls/EditModSearches.Designer.cs b/GUI/Controls/EditModSearches.Designer.cs
index 450a662ebc..9a5e12cd99 100644
--- a/GUI/Controls/EditModSearches.Designer.cs
+++ b/GUI/Controls/EditModSearches.Designer.cs
@@ -1,4 +1,4 @@
-namespace CKAN
+namespace CKAN.GUI
 {
     partial class EditModSearches
     {
diff --git a/GUI/Controls/EditModSearches.cs b/GUI/Controls/EditModSearches.cs
index ce3e4eaba5..cf6f5b3590 100644
--- a/GUI/Controls/EditModSearches.cs
+++ b/GUI/Controls/EditModSearches.cs
@@ -3,7 +3,7 @@
 using System.Collections.Generic;
 using System.Windows.Forms;
 
-namespace CKAN
+namespace CKAN.GUI
 {
     /// <summary>
     /// A container control for the individual EditModSearch controls
diff --git a/GUI/Controls/EditModpack.Designer.cs b/GUI/Controls/EditModpack.Designer.cs
index 121725f132..e5a363e7a9 100644
--- a/GUI/Controls/EditModpack.Designer.cs
+++ b/GUI/Controls/EditModpack.Designer.cs
@@ -1,4 +1,4 @@
-namespace CKAN
+namespace CKAN.GUI
 {
     partial class EditModpack
     {
diff --git a/GUI/Controls/EditModpack.cs b/GUI/Controls/EditModpack.cs
index a4156b2871..fa5ca963b3 100644
--- a/GUI/Controls/EditModpack.cs
+++ b/GUI/Controls/EditModpack.cs
@@ -8,7 +8,7 @@
 using CKAN.GameVersionProviders;
 using CKAN.Types;
 
-namespace CKAN
+namespace CKAN.GUI
 {
     public partial class EditModpack : UserControl
     {
diff --git a/GUI/Controls/HintTextBox.Designer.cs b/GUI/Controls/HintTextBox.Designer.cs
index 3a491cce8c..2001a433fb 100644
--- a/GUI/Controls/HintTextBox.Designer.cs
+++ b/GUI/Controls/HintTextBox.Designer.cs
@@ -1,4 +1,4 @@
-namespace CKAN
+namespace CKAN.GUI
 {
     partial class HintTextBox
     {
@@ -36,7 +36,7 @@ private void InitializeComponent()
             this.ClearIcon.BackColor = System.Drawing.Color.Transparent;
             this.ClearIcon.Visible = false;
             this.ClearIcon.Cursor = System.Windows.Forms.Cursors.Hand;
-            this.ClearIcon.Image = global::CKAN.Properties.Resources.textClear;
+            this.ClearIcon.Image = global::CKAN.GUI.Properties.Resources.textClear;
             this.ClearIcon.SizeMode = System.Windows.Forms.PictureBoxSizeMode.CenterImage;
             this.ClearIcon.Size = new System.Drawing.Size(18, 18);
             this.ClearIcon.Click += HintClearIcon_Click;
diff --git a/GUI/Controls/HintTextBox.cs b/GUI/Controls/HintTextBox.cs
index 9a7c491d2e..c1b25057af 100644
--- a/GUI/Controls/HintTextBox.cs
+++ b/GUI/Controls/HintTextBox.cs
@@ -2,7 +2,7 @@
 using System.Drawing;
 using System.Windows.Forms;
 
-namespace CKAN
+namespace CKAN.GUI
 {
     /// <summary>
     /// A textbox which shows a "clear text" icon on the right side
diff --git a/GUI/Controls/ManageMods.Designer.cs b/GUI/Controls/ManageMods.Designer.cs
index 1a83ab9e49..3b366df6e9 100644
--- a/GUI/Controls/ManageMods.Designer.cs
+++ b/GUI/Controls/ManageMods.Designer.cs
@@ -1,6 +1,6 @@
 using System.Windows.Forms;
 
-namespace CKAN
+namespace CKAN.GUI
 {
     partial class ManageMods
     {
@@ -53,7 +53,7 @@ private void InitializeComponent()
             this.FilterTagsToolButton = new System.Windows.Forms.ToolStripMenuItem();
             this.NavBackwardToolButton = new System.Windows.Forms.ToolStripMenuItem();
             this.NavForwardToolButton = new System.Windows.Forms.ToolStripMenuItem();
-            this.EditModSearches = new CKAN.EditModSearches();
+            this.EditModSearches = new CKAN.GUI.EditModSearches();
             this.ModGrid = new System.Windows.Forms.DataGridView();
             this.InstallAllCheckbox = new System.Windows.Forms.CheckBox();
             this.Installed = new System.Windows.Forms.DataGridViewCheckBoxColumn();
@@ -119,7 +119,7 @@ private void InitializeComponent()
             //
             // launchGameToolStripMenuItem
             //
-            this.launchGameToolStripMenuItem.Image = global::CKAN.Properties.Resources.ksp;
+            this.launchGameToolStripMenuItem.Image = global::CKAN.GUI.Properties.Resources.ksp;
             this.launchGameToolStripMenuItem.ImageScaling = System.Windows.Forms.ToolStripItemImageScaling.None;
             this.launchGameToolStripMenuItem.Name = "launchGameToolStripMenuItem";
             this.launchGameToolStripMenuItem.Size = new System.Drawing.Size(146, 56);
@@ -129,7 +129,7 @@ private void InitializeComponent()
             //
             // RefreshToolButton
             //
-            this.RefreshToolButton.Image = global::CKAN.Properties.Resources.refresh;
+            this.RefreshToolButton.Image = global::CKAN.GUI.Properties.Resources.refresh;
             this.RefreshToolButton.ImageScaling = System.Windows.Forms.ToolStripItemImageScaling.None;
             this.RefreshToolButton.Name = "RefreshToolButton";
             this.RefreshToolButton.Size = new System.Drawing.Size(114, 56);
@@ -139,7 +139,7 @@ private void InitializeComponent()
             //
             // UpdateAllToolButton
             //
-            this.UpdateAllToolButton.Image = global::CKAN.Properties.Resources.update;
+            this.UpdateAllToolButton.Image = global::CKAN.GUI.Properties.Resources.update;
             this.UpdateAllToolButton.ImageScaling = System.Windows.Forms.ToolStripItemImageScaling.None;
             this.UpdateAllToolButton.Name = "UpdateAllToolButton";
             this.UpdateAllToolButton.Size = new System.Drawing.Size(232, 56);
@@ -149,7 +149,7 @@ private void InitializeComponent()
             //
             // ApplyToolButton
             //
-            this.ApplyToolButton.Image = global::CKAN.Properties.Resources.apply;
+            this.ApplyToolButton.Image = global::CKAN.GUI.Properties.Resources.apply;
             this.ApplyToolButton.ImageScaling = System.Windows.Forms.ToolStripItemImageScaling.None;
             this.ApplyToolButton.Name = "ApplyToolButton";
             this.ApplyToolButton.Size = new System.Drawing.Size(173, 56);
@@ -174,7 +174,7 @@ private void InitializeComponent()
             this.FilterTagsToolButton,
             this.FilterLabelsToolButton});
             this.FilterToolButton.DropDown.Opening += new System.ComponentModel.CancelEventHandler(FilterToolButton_DropDown_Opening);
-            this.FilterToolButton.Image = global::CKAN.Properties.Resources.filter;
+            this.FilterToolButton.Image = global::CKAN.GUI.Properties.Resources.filter;
             this.FilterToolButton.ImageScaling = System.Windows.Forms.ToolStripItemImageScaling.None;
             this.FilterToolButton.Name = "FilterToolButton";
             this.FilterToolButton.Size = new System.Drawing.Size(201, 56);
@@ -267,7 +267,7 @@ private void InitializeComponent()
             //
             // NavBackwardToolButton
             //
-            this.NavBackwardToolButton.Image = global::CKAN.Properties.Resources.backward;
+            this.NavBackwardToolButton.Image = global::CKAN.GUI.Properties.Resources.backward;
             this.NavBackwardToolButton.ImageScaling = System.Windows.Forms.ToolStripItemImageScaling.None;
             this.NavBackwardToolButton.Name = "NavBackwardToolButton";
             this.NavBackwardToolButton.Size = new System.Drawing.Size(44, 56);
@@ -277,7 +277,7 @@ private void InitializeComponent()
             //
             // NavForwardToolButton
             //
-            this.NavForwardToolButton.Image = global::CKAN.Properties.Resources.forward;
+            this.NavForwardToolButton.Image = global::CKAN.GUI.Properties.Resources.forward;
             this.NavForwardToolButton.ImageScaling = System.Windows.Forms.ToolStripItemImageScaling.None;
             this.NavForwardToolButton.Name = "NavForwardToolButton";
             this.NavForwardToolButton.Size = new System.Drawing.Size(44, 56);
@@ -608,6 +608,6 @@ private void InitializeComponent()
         private System.Windows.Forms.ToolStripMenuItem reinstallToolStripMenuItem;
         private System.Windows.Forms.ToolStripMenuItem downloadContentsToolStripMenuItem;
         private System.Windows.Forms.ToolStripMenuItem purgeContentsToolStripMenuItem;
-        private CKAN.EditModSearches EditModSearches;
+        private CKAN.GUI.EditModSearches EditModSearches;
     }
 }
diff --git a/GUI/Controls/ManageMods.cs b/GUI/Controls/ManageMods.cs
index 464235d97f..c44a978cb5 100644
--- a/GUI/Controls/ManageMods.cs
+++ b/GUI/Controls/ManageMods.cs
@@ -8,7 +8,7 @@
 using log4net;
 using CKAN.Versioning;
 
-namespace CKAN
+namespace CKAN.GUI
 {
     public partial class ManageMods : UserControl
     {
diff --git a/GUI/Controls/ModInfo.Designer.cs b/GUI/Controls/ModInfo.Designer.cs
index b45b5a812e..802c93d96b 100644
--- a/GUI/Controls/ModInfo.Designer.cs
+++ b/GUI/Controls/ModInfo.Designer.cs
@@ -1,4 +1,4 @@
-namespace CKAN
+namespace CKAN.GUI
 {
     partial class ModInfo
     {
@@ -71,7 +71,7 @@ private void InitializeComponent()
             this.ContentsOpenButton = new System.Windows.Forms.Button();
             this.NotCachedLabel = new System.Windows.Forms.Label();
             this.AllModVersionsTabPage = new System.Windows.Forms.TabPage();
-            this.AllModVersions = new CKAN.AllModVersions();
+            this.AllModVersions = new CKAN.GUI.AllModVersions();
             this.ModInfoTabControl.SuspendLayout();
             this.MetadataTabPage.SuspendLayout();
             ((System.ComponentModel.ISupportInitialize)(this.splitContainer2)).BeginInit();
@@ -455,17 +455,17 @@ private void InitializeComponent()
                 // ImageList's default makes icons look like garbage
                 ColorDepth = System.Windows.Forms.ColorDepth.Depth32Bit
             };
-            this.DependsGraphTree.ImageList.Images.Add("Root", global::CKAN.Properties.Resources.ksp);
-            this.DependsGraphTree.ImageList.Images.Add("Depends", global::CKAN.Properties.Resources.star);
-            this.DependsGraphTree.ImageList.Images.Add("Recommends", global::CKAN.Properties.Resources.thumbup);
-            this.DependsGraphTree.ImageList.Images.Add("Suggests", global::CKAN.Properties.Resources.info);
-            this.DependsGraphTree.ImageList.Images.Add("Supports", global::CKAN.Properties.Resources.smile);
-            this.DependsGraphTree.ImageList.Images.Add("Conflicts", global::CKAN.Properties.Resources.alert);
+            this.DependsGraphTree.ImageList.Images.Add("Root", global::CKAN.GUI.Properties.Resources.ksp);
+            this.DependsGraphTree.ImageList.Images.Add("Depends", global::CKAN.GUI.Properties.Resources.star);
+            this.DependsGraphTree.ImageList.Images.Add("Recommends", global::CKAN.GUI.Properties.Resources.thumbup);
+            this.DependsGraphTree.ImageList.Images.Add("Suggests", global::CKAN.GUI.Properties.Resources.info);
+            this.DependsGraphTree.ImageList.Images.Add("Supports", global::CKAN.GUI.Properties.Resources.smile);
+            this.DependsGraphTree.ImageList.Images.Add("Conflicts", global::CKAN.GUI.Properties.Resources.alert);
             //
             // LegendDependsImage
             //
             this.LegendDependsImage.BackColor = System.Drawing.SystemColors.Window;
-            this.LegendDependsImage.Image = global::CKAN.Properties.Resources.star;
+            this.LegendDependsImage.Image = global::CKAN.GUI.Properties.Resources.star;
             this.LegendDependsImage.Location = new System.Drawing.Point(6, 3);
             this.LegendDependsImage.SizeMode = System.Windows.Forms.PictureBoxSizeMode.StretchImage;
             this.LegendDependsImage.ClientSize = new System.Drawing.Size(14, 14);
@@ -473,7 +473,7 @@ private void InitializeComponent()
             // LegendRecommendsImage
             //
             this.LegendRecommendsImage.BackColor = System.Drawing.SystemColors.Window;
-            this.LegendRecommendsImage.Image = global::CKAN.Properties.Resources.thumbup;
+            this.LegendRecommendsImage.Image = global::CKAN.GUI.Properties.Resources.thumbup;
             this.LegendRecommendsImage.Location = new System.Drawing.Point(6, 21);
             this.LegendRecommendsImage.SizeMode = System.Windows.Forms.PictureBoxSizeMode.StretchImage;
             this.LegendRecommendsImage.ClientSize = new System.Drawing.Size(14, 14);
@@ -481,7 +481,7 @@ private void InitializeComponent()
             // LegendSuggestsImage
             //
             this.LegendSuggestsImage.BackColor = System.Drawing.SystemColors.Window;
-            this.LegendSuggestsImage.Image = global::CKAN.Properties.Resources.info;
+            this.LegendSuggestsImage.Image = global::CKAN.GUI.Properties.Resources.info;
             this.LegendSuggestsImage.Location = new System.Drawing.Point(6, 39);
             this.LegendSuggestsImage.SizeMode = System.Windows.Forms.PictureBoxSizeMode.StretchImage;
             this.LegendSuggestsImage.ClientSize = new System.Drawing.Size(14, 14);
@@ -489,7 +489,7 @@ private void InitializeComponent()
             // LegendSupportsImage
             //
             this.LegendSupportsImage.BackColor = System.Drawing.SystemColors.Window;
-            this.LegendSupportsImage.Image = global::CKAN.Properties.Resources.smile;
+            this.LegendSupportsImage.Image = global::CKAN.GUI.Properties.Resources.smile;
             this.LegendSupportsImage.Location = new System.Drawing.Point(6, 57);
             this.LegendSupportsImage.SizeMode = System.Windows.Forms.PictureBoxSizeMode.StretchImage;
             this.LegendSupportsImage.ClientSize = new System.Drawing.Size(14, 14);
@@ -497,7 +497,7 @@ private void InitializeComponent()
             // LegendConflictsImage
             //
             this.LegendConflictsImage.BackColor = System.Drawing.SystemColors.Window;
-            this.LegendConflictsImage.Image = global::CKAN.Properties.Resources.alert;
+            this.LegendConflictsImage.Image = global::CKAN.GUI.Properties.Resources.alert;
             this.LegendConflictsImage.Location = new System.Drawing.Point(6, 75);
             this.LegendConflictsImage.SizeMode = System.Windows.Forms.PictureBoxSizeMode.StretchImage;
             this.LegendConflictsImage.ClientSize = new System.Drawing.Size(14, 14);
@@ -683,6 +683,6 @@ private void InitializeComponent()
         private System.Windows.Forms.Button ContentsOpenButton;
         private System.Windows.Forms.Label NotCachedLabel;
         private System.Windows.Forms.TabPage AllModVersionsTabPage;
-        private AllModVersions AllModVersions;
+        private CKAN.GUI.AllModVersions AllModVersions;
     }
 }
diff --git a/GUI/Controls/ModInfo.cs b/GUI/Controls/ModInfo.cs
index 2abbc74a8e..4deffad168 100644
--- a/GUI/Controls/ModInfo.cs
+++ b/GUI/Controls/ModInfo.cs
@@ -7,7 +7,7 @@
 using System.IO;
 using CKAN.Versioning;
 
-namespace CKAN
+namespace CKAN.GUI
 {
     public enum RelationshipType
     {
diff --git a/GUI/Controls/PlayTime.Designer.cs b/GUI/Controls/PlayTime.Designer.cs
index 002288a99a..9049caf031 100755
--- a/GUI/Controls/PlayTime.Designer.cs
+++ b/GUI/Controls/PlayTime.Designer.cs
@@ -1,4 +1,4 @@
-namespace CKAN
+namespace CKAN.GUI
 {
     partial class PlayTime
     {
diff --git a/GUI/Controls/PlayTime.cs b/GUI/Controls/PlayTime.cs
index 09aaf27505..0a1acbca4f 100755
--- a/GUI/Controls/PlayTime.cs
+++ b/GUI/Controls/PlayTime.cs
@@ -8,7 +8,7 @@
 using CKAN.Versioning;
 using CKAN.Extensions;
 
-namespace CKAN
+namespace CKAN.GUI
 {
     /// <summary>
     /// Show the user's play time in each instance and allow editing
diff --git a/GUI/Controls/ThemedListView.cs b/GUI/Controls/ThemedListView.cs
index a163dae20f..8e45e884f7 100644
--- a/GUI/Controls/ThemedListView.cs
+++ b/GUI/Controls/ThemedListView.cs
@@ -1,7 +1,7 @@
 using System.Drawing;
 using System.Windows.Forms;
 
-namespace CKAN
+namespace CKAN.GUI
 {
     /// <summary>
     /// A ListView that obeys system colors to look less awful in a dark theme
diff --git a/GUI/Controls/ThemedTabControl.cs b/GUI/Controls/ThemedTabControl.cs
index 17503f4dac..90215ec0e5 100644
--- a/GUI/Controls/ThemedTabControl.cs
+++ b/GUI/Controls/ThemedTabControl.cs
@@ -2,7 +2,7 @@
 using System.Drawing;
 using System.Windows.Forms;
 
-namespace CKAN
+namespace CKAN.GUI
 {
     /// <summary>
     /// A TabControl that obeys system colors to look less awful in a dark theme
diff --git a/GUI/Controls/TransparentTextBox.cs b/GUI/Controls/TransparentTextBox.cs
index 0022a9b80c..750e462a69 100644
--- a/GUI/Controls/TransparentTextBox.cs
+++ b/GUI/Controls/TransparentTextBox.cs
@@ -1,4 +1,4 @@
-namespace CKAN
+namespace CKAN.GUI
 {
     /// <summary>
     /// Create a new TransparentTextBox control that allows the backcolor of textboxes to be transparent.
diff --git a/GUI/Controls/TriStateToggle.cs b/GUI/Controls/TriStateToggle.cs
index 0bc4d3fdf2..deaaee2052 100644
--- a/GUI/Controls/TriStateToggle.cs
+++ b/GUI/Controls/TriStateToggle.cs
@@ -2,7 +2,7 @@
 using System.Drawing;
 using System.Windows.Forms;
 
-namespace CKAN
+namespace CKAN.GUI
 {
     public class TriStateToggle : UserControl
     {
diff --git a/GUI/Controls/Wait.Designer.cs b/GUI/Controls/Wait.Designer.cs
index d7e7f4b3ca..a48c506d8f 100644
--- a/GUI/Controls/Wait.Designer.cs
+++ b/GUI/Controls/Wait.Designer.cs
@@ -1,4 +1,4 @@
-namespace CKAN
+namespace CKAN.GUI
 {
     partial class Wait
     {
diff --git a/GUI/Controls/Wait.cs b/GUI/Controls/Wait.cs
index 0cfe64a2e2..7fe1265fed 100644
--- a/GUI/Controls/Wait.cs
+++ b/GUI/Controls/Wait.cs
@@ -5,7 +5,7 @@
 using System.Windows.Forms;
 using Timer = System.Windows.Forms.Timer;
 
-namespace CKAN
+namespace CKAN.GUI
 {
     public partial class Wait : UserControl
     {
diff --git a/GUI/Dialogs/AboutDialog.Designer.cs b/GUI/Dialogs/AboutDialog.Designer.cs
index 1e21bce130..5fbb5861d1 100644
--- a/GUI/Dialogs/AboutDialog.Designer.cs
+++ b/GUI/Dialogs/AboutDialog.Designer.cs
@@ -1,7 +1,7 @@
 using System;
 using System.Windows.Forms;
 
-namespace CKAN
+namespace CKAN.GUI
 {
     partial class AboutDialog
     {
diff --git a/GUI/Dialogs/AboutDialog.cs b/GUI/Dialogs/AboutDialog.cs
index ce09e36761..1e515cfeff 100644
--- a/GUI/Dialogs/AboutDialog.cs
+++ b/GUI/Dialogs/AboutDialog.cs
@@ -1,6 +1,6 @@
 using System.Windows.Forms;
 
-namespace CKAN
+namespace CKAN.GUI
 {
     public partial class AboutDialog : FormCompatibility
     {
diff --git a/GUI/Dialogs/AskUserForAutoUpdatesDialog.Designer.cs b/GUI/Dialogs/AskUserForAutoUpdatesDialog.Designer.cs
index 6c3711121a..ea4abcf781 100644
--- a/GUI/Dialogs/AskUserForAutoUpdatesDialog.Designer.cs
+++ b/GUI/Dialogs/AskUserForAutoUpdatesDialog.Designer.cs
@@ -1,4 +1,4 @@
-namespace CKAN
+namespace CKAN.GUI
 {
     partial class AskUserForAutoUpdatesDialog
     {
diff --git a/GUI/Dialogs/AskUserForAutoUpdatesDialog.cs b/GUI/Dialogs/AskUserForAutoUpdatesDialog.cs
index f1df19bb23..65cfa1109e 100644
--- a/GUI/Dialogs/AskUserForAutoUpdatesDialog.cs
+++ b/GUI/Dialogs/AskUserForAutoUpdatesDialog.cs
@@ -1,6 +1,6 @@
 using System.Windows.Forms;
 
-namespace CKAN
+namespace CKAN.GUI
 {
     public partial class AskUserForAutoUpdatesDialog : Form
     {
diff --git a/GUI/Dialogs/CloneFakeGameDialog.Designer.cs b/GUI/Dialogs/CloneFakeGameDialog.Designer.cs
index 93e89d9e8a..29c46fd177 100644
--- a/GUI/Dialogs/CloneFakeGameDialog.Designer.cs
+++ b/GUI/Dialogs/CloneFakeGameDialog.Designer.cs
@@ -1,4 +1,4 @@
-namespace CKAN
+namespace CKAN.GUI
 {
     partial class CloneFakeGameDialog
     {
diff --git a/GUI/Dialogs/CloneFakeGameDialog.cs b/GUI/Dialogs/CloneFakeGameDialog.cs
index 7908e6a071..3399333d16 100644
--- a/GUI/Dialogs/CloneFakeGameDialog.cs
+++ b/GUI/Dialogs/CloneFakeGameDialog.cs
@@ -9,7 +9,7 @@
 using CKAN.Versioning;
 using CKAN.Games;
 
-namespace CKAN
+namespace CKAN.GUI
 {
     /// <summary>
     /// The GUI implementation of clone and fake.
diff --git a/GUI/Dialogs/CompatibleGameVersionsDialog.Designer.cs b/GUI/Dialogs/CompatibleGameVersionsDialog.Designer.cs
index 75f0fb1965..da220e86d6 100644
--- a/GUI/Dialogs/CompatibleGameVersionsDialog.Designer.cs
+++ b/GUI/Dialogs/CompatibleGameVersionsDialog.Designer.cs
@@ -1,4 +1,4 @@
-namespace CKAN
+namespace CKAN.GUI
 {
     partial class CompatibleGameVersionsDialog
     {
diff --git a/GUI/Dialogs/CompatibleGameVersionsDialog.cs b/GUI/Dialogs/CompatibleGameVersionsDialog.cs
index 60f37056ff..5e5e965670 100644
--- a/GUI/Dialogs/CompatibleGameVersionsDialog.cs
+++ b/GUI/Dialogs/CompatibleGameVersionsDialog.cs
@@ -8,7 +8,7 @@
 using CKAN.Versioning;
 using CKAN.GameVersionProviders;
 
-namespace CKAN
+namespace CKAN.GUI
 {
     public partial class CompatibleGameVersionsDialog : Form
     {
diff --git a/GUI/Dialogs/EditLabelsDialog.Designer.cs b/GUI/Dialogs/EditLabelsDialog.Designer.cs
index 1f401219fc..213ad6b1f7 100644
--- a/GUI/Dialogs/EditLabelsDialog.Designer.cs
+++ b/GUI/Dialogs/EditLabelsDialog.Designer.cs
@@ -1,4 +1,4 @@
-namespace CKAN
+namespace CKAN.GUI
 {
     partial class EditLabelsDialog
     {
diff --git a/GUI/Dialogs/EditLabelsDialog.cs b/GUI/Dialogs/EditLabelsDialog.cs
index 1f2dd8d3b9..6fffd78ae3 100644
--- a/GUI/Dialogs/EditLabelsDialog.cs
+++ b/GUI/Dialogs/EditLabelsDialog.cs
@@ -5,7 +5,7 @@
 using System.Windows.Forms;
 using log4net;
 
-namespace CKAN
+namespace CKAN.GUI
 {
     public partial class EditLabelsDialog : Form
     {
diff --git a/GUI/Dialogs/ErrorDialog.Designer.cs b/GUI/Dialogs/ErrorDialog.Designer.cs
index 8244bc294f..b1c8562789 100644
--- a/GUI/Dialogs/ErrorDialog.Designer.cs
+++ b/GUI/Dialogs/ErrorDialog.Designer.cs
@@ -1,4 +1,4 @@
-namespace CKAN
+namespace CKAN.GUI
 {
     partial class ErrorDialog
     {
diff --git a/GUI/Dialogs/ErrorDialog.cs b/GUI/Dialogs/ErrorDialog.cs
index 89db289f65..1dbc198d86 100644
--- a/GUI/Dialogs/ErrorDialog.cs
+++ b/GUI/Dialogs/ErrorDialog.cs
@@ -3,7 +3,7 @@
 using System.Windows.Forms;
 using log4net;
 
-namespace CKAN
+namespace CKAN.GUI
 {
     public partial class ErrorDialog : Form
     {
diff --git a/GUI/Dialogs/GameCommandLineOptionsDialog.Designer.cs b/GUI/Dialogs/GameCommandLineOptionsDialog.Designer.cs
index 8f4cea8217..1f24601b7a 100644
--- a/GUI/Dialogs/GameCommandLineOptionsDialog.Designer.cs
+++ b/GUI/Dialogs/GameCommandLineOptionsDialog.Designer.cs
@@ -1,4 +1,4 @@
-namespace CKAN
+namespace CKAN.GUI
 {
     partial class GameCommandLineOptionsDialog
     {
diff --git a/GUI/Dialogs/GameCommandLineOptionsDialog.cs b/GUI/Dialogs/GameCommandLineOptionsDialog.cs
index 3d85b80595..bd4c50a430 100644
--- a/GUI/Dialogs/GameCommandLineOptionsDialog.cs
+++ b/GUI/Dialogs/GameCommandLineOptionsDialog.cs
@@ -1,6 +1,6 @@
 using System.Windows.Forms;
 
-namespace CKAN
+namespace CKAN.GUI
 {
 
     public partial class GameCommandLineOptionsDialog : Form
diff --git a/GUI/Dialogs/InstallFiltersDialog.Designer.cs b/GUI/Dialogs/InstallFiltersDialog.Designer.cs
index a24d2ed168..a3c7ae8470 100644
--- a/GUI/Dialogs/InstallFiltersDialog.Designer.cs
+++ b/GUI/Dialogs/InstallFiltersDialog.Designer.cs
@@ -1,4 +1,4 @@
-namespace CKAN
+namespace CKAN.GUI
 {
     partial class InstallFiltersDialog
     {
diff --git a/GUI/Dialogs/InstallFiltersDialog.cs b/GUI/Dialogs/InstallFiltersDialog.cs
index 0b657bc4c3..8846b871fd 100644
--- a/GUI/Dialogs/InstallFiltersDialog.cs
+++ b/GUI/Dialogs/InstallFiltersDialog.cs
@@ -5,7 +5,7 @@
 
 using CKAN.Configuration;
 
-namespace CKAN
+namespace CKAN.GUI
 {
     public partial class InstallFiltersDialog : Form
     {
diff --git a/GUI/Dialogs/ManageGameInstancesDialog.Designer.cs b/GUI/Dialogs/ManageGameInstancesDialog.Designer.cs
index 0bac64dbb6..200e76101b 100644
--- a/GUI/Dialogs/ManageGameInstancesDialog.Designer.cs
+++ b/GUI/Dialogs/ManageGameInstancesDialog.Designer.cs
@@ -1,4 +1,4 @@
-namespace CKAN
+namespace CKAN.GUI
 {
     partial class ManageGameInstancesDialog
     {
@@ -41,7 +41,7 @@ private void InitializeComponent()
             this.GamePlayTime = ((System.Windows.Forms.ColumnHeader)(new System.Windows.Forms.ColumnHeader()));
             this.GameInstallPath = ((System.Windows.Forms.ColumnHeader)(new System.Windows.Forms.ColumnHeader()));
             this.SelectButton = new System.Windows.Forms.Button();
-            this.AddNewButton = new CKAN.DropdownMenuButton();
+            this.AddNewButton = new CKAN.GUI.DropdownMenuButton();
             this.AddNewMenu = new System.Windows.Forms.ContextMenuStrip(this.components);
             this.InstanceListContextMenuStrip = new System.Windows.Forms.ContextMenuStrip(this.components);
             this.openDirectoryMenuItem = new System.Windows.Forms.ToolStripMenuItem();
diff --git a/GUI/Dialogs/ManageGameInstancesDialog.cs b/GUI/Dialogs/ManageGameInstancesDialog.cs
index 3220fd40d2..cf4152fcf1 100644
--- a/GUI/Dialogs/ManageGameInstancesDialog.cs
+++ b/GUI/Dialogs/ManageGameInstancesDialog.cs
@@ -7,7 +7,7 @@
 
 using CKAN.Versioning;
 
-namespace CKAN
+namespace CKAN.GUI
 {
     public partial class ManageGameInstancesDialog : Form
     {
diff --git a/GUI/Dialogs/NewRepoDialog.Designer.cs b/GUI/Dialogs/NewRepoDialog.Designer.cs
index 60b90b0163..dfc8e2b0d3 100644
--- a/GUI/Dialogs/NewRepoDialog.Designer.cs
+++ b/GUI/Dialogs/NewRepoDialog.Designer.cs
@@ -1,4 +1,4 @@
-namespace CKAN
+namespace CKAN.GUI
 {
     partial class NewRepoDialog
     {
diff --git a/GUI/Dialogs/NewRepoDialog.cs b/GUI/Dialogs/NewRepoDialog.cs
index d55af89bf4..a51bf9012c 100644
--- a/GUI/Dialogs/NewRepoDialog.cs
+++ b/GUI/Dialogs/NewRepoDialog.cs
@@ -2,7 +2,7 @@
 using System.Linq;
 using System.Windows.Forms;
 
-namespace CKAN
+namespace CKAN.GUI
 {
     public partial class NewRepoDialog : Form
     {
diff --git a/GUI/Dialogs/NewUpdateDialog.Designer.cs b/GUI/Dialogs/NewUpdateDialog.Designer.cs
index f7b27594b8..a6185c2b0e 100644
--- a/GUI/Dialogs/NewUpdateDialog.Designer.cs
+++ b/GUI/Dialogs/NewUpdateDialog.Designer.cs
@@ -1,4 +1,4 @@
-namespace CKAN
+namespace CKAN.GUI
 {
     partial class NewUpdateDialog
     {
diff --git a/GUI/Dialogs/NewUpdateDialog.cs b/GUI/Dialogs/NewUpdateDialog.cs
index c5f443b3b4..85aea2decf 100644
--- a/GUI/Dialogs/NewUpdateDialog.cs
+++ b/GUI/Dialogs/NewUpdateDialog.cs
@@ -1,6 +1,6 @@
 using System.Windows.Forms;
 
-namespace CKAN
+namespace CKAN.GUI
 {
     public partial class NewUpdateDialog : Form
     {
diff --git a/GUI/Dialogs/PluginsDialog.Designer.cs b/GUI/Dialogs/PluginsDialog.Designer.cs
index d9d55b2f30..4820316f9d 100644
--- a/GUI/Dialogs/PluginsDialog.Designer.cs
+++ b/GUI/Dialogs/PluginsDialog.Designer.cs
@@ -1,4 +1,4 @@
-namespace CKAN
+namespace CKAN.GUI
 {
     partial class PluginsDialog
     {
diff --git a/GUI/Dialogs/PluginsDialog.cs b/GUI/Dialogs/PluginsDialog.cs
index 3fdc83ce0e..cf189a4247 100644
--- a/GUI/Dialogs/PluginsDialog.cs
+++ b/GUI/Dialogs/PluginsDialog.cs
@@ -1,7 +1,7 @@
 using System;
 using System.Windows.Forms;
 
-namespace CKAN
+namespace CKAN.GUI
 {
     public partial class PluginsDialog : Form
     {
diff --git a/GUI/Dialogs/RenameInstanceDialog.Designer.cs b/GUI/Dialogs/RenameInstanceDialog.Designer.cs
index 57d370b4fb..1ba866d4a2 100644
--- a/GUI/Dialogs/RenameInstanceDialog.Designer.cs
+++ b/GUI/Dialogs/RenameInstanceDialog.Designer.cs
@@ -1,4 +1,4 @@
-namespace CKAN
+namespace CKAN.GUI
 {
     partial class RenameInstanceDialog
     {
diff --git a/GUI/Dialogs/RenameInstanceDialog.cs b/GUI/Dialogs/RenameInstanceDialog.cs
index b6e7238e37..eab8d46edc 100644
--- a/GUI/Dialogs/RenameInstanceDialog.cs
+++ b/GUI/Dialogs/RenameInstanceDialog.cs
@@ -1,6 +1,6 @@
 using System.Windows.Forms;
 
-namespace CKAN
+namespace CKAN.GUI
 {
     public partial class RenameInstanceDialog : Form
     {
diff --git a/GUI/Dialogs/RenameInstanceDialog.resx b/GUI/Dialogs/RenameInstanceDialog.resx
index afd112238e..96e018c806 100644
--- a/GUI/Dialogs/RenameInstanceDialog.resx
+++ b/GUI/Dialogs/RenameInstanceDialog.resx
@@ -116,7 +116,7 @@
   </resheader>
   <resheader name="writer">
     <value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
-  </resheader>/>
+  </resheader>
   <data name="OKButton.Text" xml:space="preserve"><value>OK</value></data>
   <data name="CancelRenameInstanceButton.Text" xml:space="preserve"><value>Cancel</value></data>
   <data name="$this.Text" xml:space="preserve"><value>Rename installation</value></data>
diff --git a/GUI/Dialogs/SelectionDialog.Designer.cs b/GUI/Dialogs/SelectionDialog.Designer.cs
index 6a7cbef307..aa9bc5b22a 100644
--- a/GUI/Dialogs/SelectionDialog.Designer.cs
+++ b/GUI/Dialogs/SelectionDialog.Designer.cs
@@ -1,6 +1,6 @@
 using System;
 
-namespace CKAN
+namespace CKAN.GUI
 {
     partial class SelectionDialog
     {
diff --git a/GUI/Dialogs/SelectionDialog.cs b/GUI/Dialogs/SelectionDialog.cs
index 1a8f04278b..d70a234ddf 100644
--- a/GUI/Dialogs/SelectionDialog.cs
+++ b/GUI/Dialogs/SelectionDialog.cs
@@ -1,7 +1,7 @@
 using System;
 using System.Windows.Forms;
 
-namespace CKAN
+namespace CKAN.GUI
 {
     public partial class SelectionDialog : Form
     {
diff --git a/GUI/Dialogs/SettingsDialog.Designer.cs b/GUI/Dialogs/SettingsDialog.Designer.cs
index d74feecf92..7e169a9c21 100644
--- a/GUI/Dialogs/SettingsDialog.Designer.cs
+++ b/GUI/Dialogs/SettingsDialog.Designer.cs
@@ -1,4 +1,4 @@
-namespace CKAN
+namespace CKAN.GUI
 {
     partial class SettingsDialog
     {
@@ -51,7 +51,7 @@ private void InitializeComponent()
             this.CacheLimit = new System.Windows.Forms.TextBox();
             this.CacheLimitPostLabel = new System.Windows.Forms.Label();
             this.ChangeCacheButton = new System.Windows.Forms.Button();
-            this.ClearCacheButton = new CKAN.DropdownMenuButton();
+            this.ClearCacheButton = new CKAN.GUI.DropdownMenuButton();
             this.ClearCacheMenu = new System.Windows.Forms.ContextMenuStrip(this.components);
             this.PurgeToLimitMenuItem = new System.Windows.Forms.ToolStripMenuItem();
             this.PurgeAllMenuItem = new System.Windows.Forms.ToolStripMenuItem();
@@ -668,7 +668,7 @@ private void InitializeComponent()
         private System.Windows.Forms.TextBox CacheLimit;
         private System.Windows.Forms.Label CacheLimitPostLabel;
         private System.Windows.Forms.Button ChangeCacheButton;
-        private CKAN.DropdownMenuButton ClearCacheButton;
+        private CKAN.GUI.DropdownMenuButton ClearCacheButton;
         private System.Windows.Forms.ContextMenuStrip ClearCacheMenu;
         private System.Windows.Forms.ToolStripMenuItem PurgeToLimitMenuItem;
         private System.Windows.Forms.ToolStripMenuItem PurgeAllMenuItem;
diff --git a/GUI/Dialogs/SettingsDialog.cs b/GUI/Dialogs/SettingsDialog.cs
index 739b24d451..882a4e901d 100644
--- a/GUI/Dialogs/SettingsDialog.cs
+++ b/GUI/Dialogs/SettingsDialog.cs
@@ -9,7 +9,7 @@
 using CKAN.Versioning;
 using CKAN.Configuration;
 
-namespace CKAN
+namespace CKAN.GUI
 {
     public partial class SettingsDialog : Form
     {
diff --git a/GUI/Dialogs/YesNoDialog.Designer.cs b/GUI/Dialogs/YesNoDialog.Designer.cs
index dc99d70501..133d1a1c56 100644
--- a/GUI/Dialogs/YesNoDialog.Designer.cs
+++ b/GUI/Dialogs/YesNoDialog.Designer.cs
@@ -1,4 +1,4 @@
-namespace CKAN
+namespace CKAN.GUI
 {
     partial class YesNoDialog
     {
diff --git a/GUI/Dialogs/YesNoDialog.cs b/GUI/Dialogs/YesNoDialog.cs
index d50a6a41b1..5b92ce04d6 100644
--- a/GUI/Dialogs/YesNoDialog.cs
+++ b/GUI/Dialogs/YesNoDialog.cs
@@ -3,7 +3,7 @@
 using System.Windows.Forms;
 using System.Threading.Tasks;
 
-namespace CKAN
+namespace CKAN.GUI
 {
     public partial class YesNoDialog : Form
     {
diff --git a/GUI/FlatToolStripRenderer.cs b/GUI/FlatToolStripRenderer.cs
index c7054c2925..ad21418252 100644
--- a/GUI/FlatToolStripRenderer.cs
+++ b/GUI/FlatToolStripRenderer.cs
@@ -2,7 +2,7 @@
 using System.Drawing;
 using System.Windows.Forms;
 
-namespace CKAN
+namespace CKAN.GUI
 {
     /// <summary>
     /// Custom toolstrip renderer that just fills the background with the BackColor.
diff --git a/GUI/FormCompatibility.cs b/GUI/FormCompatibility.cs
index 22b7d2c8b7..2732bf1023 100644
--- a/GUI/FormCompatibility.cs
+++ b/GUI/FormCompatibility.cs
@@ -1,7 +1,7 @@
 using System.Drawing;
 using System.Windows.Forms;
 
-namespace CKAN
+namespace CKAN.GUI
 {
     /// <summary>
     /// Inheriting from this class ensures that forms are equally sized on Windows and on Linux/MacOSX
diff --git a/GUI/GUIUser.cs b/GUI/GUIUser.cs
index 9c97694726..2bb7e58aff 100644
--- a/GUI/GUIUser.cs
+++ b/GUI/GUIUser.cs
@@ -1,4 +1,4 @@
-namespace CKAN
+namespace CKAN.GUI
 {
     /// <summary>
     /// The GUI implementation of the IUser interface.
diff --git a/GUI/Labels/ModuleLabel.cs b/GUI/Labels/ModuleLabel.cs
index d986ee175d..3392ff6d72 100644
--- a/GUI/Labels/ModuleLabel.cs
+++ b/GUI/Labels/ModuleLabel.cs
@@ -3,7 +3,7 @@
 using System.Collections.Generic;
 using Newtonsoft.Json;
 
-namespace CKAN
+namespace CKAN.GUI
 {
     [JsonObject(MemberSerialization.OptIn)]
     public class ModuleLabel
diff --git a/GUI/Labels/ModuleLabelList.cs b/GUI/Labels/ModuleLabelList.cs
index f2d2747e4e..f58e542c5c 100644
--- a/GUI/Labels/ModuleLabelList.cs
+++ b/GUI/Labels/ModuleLabelList.cs
@@ -6,7 +6,7 @@
 using System.Runtime.Serialization;
 using Newtonsoft.Json;
 
-namespace CKAN
+namespace CKAN.GUI
 {
     [JsonObject(MemberSerialization.OptIn)]
     public class ModuleLabelList
diff --git a/GUI/Localization/fr-FR/Changeset.fr-FR.resx b/GUI/Localization/fr-FR/Changeset.fr-FR.resx
index a256a9b034..54a928dacf 100644
--- a/GUI/Localization/fr-FR/Changeset.fr-FR.resx
+++ b/GUI/Localization/fr-FR/Changeset.fr-FR.resx
@@ -120,6 +120,7 @@
   <data name="Mod.Text" xml:space="preserve"><value>Mod</value></data>
   <data name="ChangeType.Text" xml:space="preserve"><value>Changement</value></data>
   <data name="Reason.Text" xml:space="preserve"><value>Raison de l'action</value></data>
+  <data name="BackButton.Text" xml:space="preserve"><value>Retour</value></data>
   <data name="CancelChangesButton.Text" xml:space="preserve"><value>Effacer</value></data>
   <data name="ConfirmChangesButton.Text" xml:space="preserve"><value>Appliquer</value></data>
 </root>
diff --git a/GUI/Localization/fr-FR/CloneFakeGameDialog.fr-FR.resx b/GUI/Localization/fr-FR/CloneFakeGameDialog.fr-FR.resx
index 93721ad380..3e29695931 100644
--- a/GUI/Localization/fr-FR/CloneFakeGameDialog.fr-FR.resx
+++ b/GUI/Localization/fr-FR/CloneFakeGameDialog.fr-FR.resx
@@ -132,7 +132,7 @@
   <data name="labelNewName.Text" xml:space="preserve"><value>Nom de la nouvelle instance:</value></data>
   <data name="labelNewPath.Text" xml:space="preserve"><value>Chemin de la nouvelle instance:</value></data>
   <data name="buttonPathBrowser.Text" xml:space="preserve"><value>Parcourir</value></data>
-  <data name="checkBoxSetAsDefault.Text" xml:space="preserve"><value>Marquer comme par défaut</value></data>
+  <data name="checkBoxSetAsDefault.Text" xml:space="preserve"><value>Marquer par défaut</value></data>
   <data name="checkBoxSwitchInstance.Text" xml:space="preserve"><value>Basculer à la nouvelle instance</value></data>
   <data name="buttonOK.Text" xml:space="preserve"><value>Créer</value></data>
   <data name="buttonCancel.Text" xml:space="preserve"><value>Annuler</value></data>
diff --git a/GUI/Localization/fr-FR/ManageGameInstancesDialog.fr-FR.resx b/GUI/Localization/fr-FR/ManageGameInstancesDialog.fr-FR.resx
index 8882343cab..ec9c90a32b 100644
--- a/GUI/Localization/fr-FR/ManageGameInstancesDialog.fr-FR.resx
+++ b/GUI/Localization/fr-FR/ManageGameInstancesDialog.fr-FR.resx
@@ -121,7 +121,7 @@
   <data name="AddToCKANMenuItem.Text" xml:space="preserve"><value>Ajouter une instance à CKAN</value></data>
   <data name="CloneFakeInstanceMenuItem.Text" xml:space="preserve"><value>Cloner ou falsifier une nouvelle instance</value></data>
   <data name="RenameButton.Text" xml:space="preserve"><value>Renommer</value></data>
-  <data name="SetAsDefaultCheckbox.Text" xml:space="preserve"><value>Marquer comme par défaut</value></data>
+  <data name="SetAsDefaultCheckbox.Text" xml:space="preserve"><value>Marquer par défaut</value></data>
   <data name="ForgetButton.Text" xml:space="preserve"><value>Oublier</value></data>
   <data name="$this.Text" xml:space="preserve"><value>Gérer les Instances de Jeu</value></data>
 </root>
diff --git a/GUI/Localization/fr-FR/NewRepoDialog.fr-FR.resx b/GUI/Localization/fr-FR/NewRepoDialog.fr-FR.resx
index a4baf9474e..c689de17b4 100644
--- a/GUI/Localization/fr-FR/NewRepoDialog.fr-FR.resx
+++ b/GUI/Localization/fr-FR/NewRepoDialog.fr-FR.resx
@@ -123,7 +123,7 @@
   <data name="RepoOK.Text" xml:space="preserve"><value>&amp;Ajouter</value></data>
   <data name="RepoNameHeader.Text" xml:space="preserve"><value>Nom</value></data>
   <data name="RepoURLHeader.Text" xml:space="preserve"><value>URL</value></data>
-  <data name="RepoNameLabel.Text" xml:space="preserve"><value>Nom:</value></data>
-  <data name="RepoUrlLabel.Text" xml:space="preserve"><value>URL:</value></data>
+  <data name="RepoNameLabel.Text" xml:space="preserve"><value>Nom :</value></data>
+  <data name="RepoUrlLabel.Text" xml:space="preserve"><value>URL :</value></data>
   <data name="$this.Text" xml:space="preserve"><value>Ajouter un répertoire</value></data>
 </root>
diff --git a/GUI/Localization/fr-FR/RenameInstanceDialog.fr-FR.resx b/GUI/Localization/fr-FR/RenameInstanceDialog.fr-FR.resx
index 965a17a8c5..db327b1947 100644
--- a/GUI/Localization/fr-FR/RenameInstanceDialog.fr-FR.resx
+++ b/GUI/Localization/fr-FR/RenameInstanceDialog.fr-FR.resx
@@ -116,7 +116,7 @@
   </resheader>
   <resheader name="writer">
     <value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
-  </resheader>/>
+  </resheader>
   <data name="OKButton.Text" xml:space="preserve"><value>OK</value></data>
   <data name="CancelRenameInstanceButton.Text" xml:space="preserve"><value>Annuler</value></data>
   <data name="$this.Text" xml:space="preserve"><value>Renommer l'installation</value></data>
diff --git a/GUI/Localization/ja-JP/RenameInstanceDialog.ja-JP.resx b/GUI/Localization/ja-JP/RenameInstanceDialog.ja-JP.resx
index 2b51fd0a26..e597981158 100644
--- a/GUI/Localization/ja-JP/RenameInstanceDialog.ja-JP.resx
+++ b/GUI/Localization/ja-JP/RenameInstanceDialog.ja-JP.resx
@@ -116,7 +116,7 @@
   </resheader>
   <resheader name="writer">
     <value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
-  </resheader>/>
+  </resheader>
   <data name="OKButton.Text" xml:space="preserve"><value>OK</value></data>
   <data name="CancelRenameInstanceButton.Text" xml:space="preserve"><value>取消</value></data>
   <data name="$this.Text" xml:space="preserve"><value>インスタンス名前変更</value></data>
diff --git a/GUI/Localization/pt-BR/RenameInstanceDialog.pt-BR.resx b/GUI/Localization/pt-BR/RenameInstanceDialog.pt-BR.resx
index e41f173170..36c980f931 100644
--- a/GUI/Localization/pt-BR/RenameInstanceDialog.pt-BR.resx
+++ b/GUI/Localization/pt-BR/RenameInstanceDialog.pt-BR.resx
@@ -116,7 +116,7 @@
   </resheader>
   <resheader name="writer">
     <value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
-  </resheader>/>
+  </resheader>
   <data name="OKButton.Text" xml:space="preserve"><value>OK</value></data>
   <data name="CancelRenameInstanceButton.Text" xml:space="preserve"><value>Cancelar</value></data>
   <data name="$this.Text" xml:space="preserve"><value>Renomear Instalação</value></data>
diff --git a/GUI/Localization/ru-RU/RenameInstanceDialog.ru-RU.resx b/GUI/Localization/ru-RU/RenameInstanceDialog.ru-RU.resx
index 1310f93383..66f3779081 100644
--- a/GUI/Localization/ru-RU/RenameInstanceDialog.ru-RU.resx
+++ b/GUI/Localization/ru-RU/RenameInstanceDialog.ru-RU.resx
@@ -116,7 +116,7 @@
   </resheader>
   <resheader name="writer">
     <value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
-  </resheader>/>
+  </resheader>
   <data name="OKButton.Text" xml:space="preserve"><value>OK</value></data>
   <data name="CancelRenameInstanceButton.Text" xml:space="preserve"><value>Отмена</value></data>
   <data name="$this.Text" xml:space="preserve"><value>Переименование сборки игры</value></data>
diff --git a/GUI/Localization/zh-CN/RenameInstanceDialog.zh-CN.resx b/GUI/Localization/zh-CN/RenameInstanceDialog.zh-CN.resx
index a1cdc2598a..947ba338ef 100644
--- a/GUI/Localization/zh-CN/RenameInstanceDialog.zh-CN.resx
+++ b/GUI/Localization/zh-CN/RenameInstanceDialog.zh-CN.resx
@@ -116,7 +116,7 @@
   </resheader>
   <resheader name="writer">
     <value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
-  </resheader>/>
+  </resheader>
   <data name="OKButton.Text" xml:space="preserve"><value>确认</value></data>
   <data name="CancelRenameInstanceButton.Text" xml:space="preserve"><value>取消</value></data>
   <data name="$this.Text" xml:space="preserve"><value>重命名当前安装</value></data>
diff --git a/GUI/Main/Main.Designer.cs b/GUI/Main/Main.Designer.cs
index 1ab575c6db..88dfc1addc 100644
--- a/GUI/Main/Main.Designer.cs
+++ b/GUI/Main/Main.Designer.cs
@@ -1,4 +1,4 @@
-namespace CKAN
+namespace CKAN.GUI
 {
     partial class Main
     {
@@ -63,23 +63,23 @@ private void InitializeComponent()
             this.untaggedFilterToolStripSeparator = new System.Windows.Forms.ToolStripSeparator();
             this.labelToolStripSeparator = new System.Windows.Forms.ToolStripSeparator();
             this.editLabelsToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
-            this.ModInfo = new CKAN.ModInfo();
+            this.ModInfo = new CKAN.GUI.ModInfo();
             this.StatusLabel = new System.Windows.Forms.ToolStripStatusLabel();
             this.StatusInstanceLabel = new System.Windows.Forms.ToolStripStatusLabel();
             this.StatusProgress = new System.Windows.Forms.ToolStripProgressBar();
-            this.MainTabControl = new CKAN.MainTabControl();
+            this.MainTabControl = new CKAN.GUI.MainTabControl();
             this.ManageModsTabPage = new System.Windows.Forms.TabPage();
-            this.ManageMods = new CKAN.ManageMods();
+            this.ManageMods = new CKAN.GUI.ManageMods();
             this.ChangesetTabPage = new System.Windows.Forms.TabPage();
-            this.Changeset = new CKAN.Changeset();
+            this.Changeset = new CKAN.GUI.Changeset();
             this.WaitTabPage = new System.Windows.Forms.TabPage();
-            this.Wait = new CKAN.Wait();
+            this.Wait = new CKAN.GUI.Wait();
             this.ChooseRecommendedModsTabPage = new System.Windows.Forms.TabPage();
-            this.ChooseRecommendedMods = new CKAN.ChooseRecommendedMods();
+            this.ChooseRecommendedMods = new CKAN.GUI.ChooseRecommendedMods();
             this.PlayTimeTabPage = new System.Windows.Forms.TabPage();
-            this.PlayTime = new CKAN.PlayTime();
+            this.PlayTime = new CKAN.GUI.PlayTime();
             this.ChooseProvidedModsTabPage = new System.Windows.Forms.TabPage();
-            this.ChooseProvidedMods = new CKAN.ChooseProvidedMods();
+            this.ChooseProvidedMods = new CKAN.GUI.ChooseProvidedMods();
             this.minimizeNotifyIcon = new System.Windows.Forms.NotifyIcon(this.components);
             this.minimizedContextMenuStrip = new System.Windows.Forms.ContextMenuStrip(this.components);
             this.updatesToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
@@ -97,9 +97,9 @@ private void InitializeComponent()
             this.quitToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
             this.viewPlayTimeStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
             this.DeleteDirectoriesTabPage = new System.Windows.Forms.TabPage();
-            this.DeleteDirectories = new CKAN.DeleteDirectories();
+            this.DeleteDirectories = new CKAN.GUI.DeleteDirectories();
             this.EditModpackTabPage = new System.Windows.Forms.TabPage();
-            this.EditModpack = new CKAN.EditModpack();
+            this.EditModpack = new CKAN.GUI.EditModpack();
             this.menuStrip1.SuspendLayout();
             ((System.ComponentModel.ISupportInitialize)(this.splitContainer1)).BeginInit();
             this.splitContainer1.Panel1.SuspendLayout();
@@ -814,27 +814,27 @@ private void InitializeComponent()
         private System.Windows.Forms.ContextMenuStrip LabelsContextMenuStrip;
         private System.Windows.Forms.ToolStripSeparator labelToolStripSeparator;
         private System.Windows.Forms.ToolStripMenuItem editLabelsToolStripMenuItem;
-        private CKAN.ModInfo ModInfo;
+        private CKAN.GUI.ModInfo ModInfo;
         private System.Windows.Forms.ToolStripStatusLabel StatusLabel;
         private System.Windows.Forms.ToolStripStatusLabel StatusInstanceLabel;
         private System.Windows.Forms.ToolStripProgressBar StatusProgress;
-        private CKAN.MainTabControl MainTabControl;
+        private CKAN.GUI.MainTabControl MainTabControl;
         private System.Windows.Forms.TabPage ManageModsTabPage;
-        public CKAN.ManageMods ManageMods;
+        public CKAN.GUI.ManageMods ManageMods;
         private System.Windows.Forms.TabPage ChangesetTabPage;
-        private CKAN.Changeset Changeset;
+        private CKAN.GUI.Changeset Changeset;
         private System.Windows.Forms.TabPage WaitTabPage;
-        public CKAN.Wait Wait;
+        public CKAN.GUI.Wait Wait;
         private System.Windows.Forms.TabPage ChooseRecommendedModsTabPage;
+        private CKAN.GUI.ChooseRecommendedMods ChooseRecommendedMods;
         private System.Windows.Forms.TabPage PlayTimeTabPage;
-        private CKAN.ChooseRecommendedMods ChooseRecommendedMods;
-        private CKAN.PlayTime PlayTime;
+        private CKAN.GUI.PlayTime PlayTime;
         private System.Windows.Forms.TabPage ChooseProvidedModsTabPage;
-        private CKAN.ChooseProvidedMods ChooseProvidedMods;
+        private CKAN.GUI.ChooseProvidedMods ChooseProvidedMods;
         private System.Windows.Forms.TabPage DeleteDirectoriesTabPage;
-        private CKAN.DeleteDirectories DeleteDirectories;
+        private CKAN.GUI.DeleteDirectories DeleteDirectories;
         private System.Windows.Forms.TabPage EditModpackTabPage;
-        private CKAN.EditModpack EditModpack;
+        private CKAN.GUI.EditModpack EditModpack;
         private System.Windows.Forms.NotifyIcon minimizeNotifyIcon;
         private System.Windows.Forms.ContextMenuStrip minimizedContextMenuStrip;
         private System.Windows.Forms.ToolStripMenuItem updatesToolStripMenuItem;
diff --git a/GUI/Main/Main.cs b/GUI/Main/Main.cs
index d5ae565ea9..0feffd90b1 100644
--- a/GUI/Main/Main.cs
+++ b/GUI/Main/Main.cs
@@ -15,7 +15,7 @@
 using CKAN.Extensions;
 using CKAN.Versioning;
 
-namespace CKAN
+namespace CKAN.GUI
 {
     public partial class Main : Form
     {
diff --git a/GUI/Main/MainAutoUpdate.cs b/GUI/Main/MainAutoUpdate.cs
index 60d2080ee5..6b35f52cf8 100644
--- a/GUI/Main/MainAutoUpdate.cs
+++ b/GUI/Main/MainAutoUpdate.cs
@@ -3,7 +3,7 @@
 using System.Windows.Forms;
 using CKAN.Versioning;
 
-namespace CKAN
+namespace CKAN.GUI
 {
     public partial class Main
     {
diff --git a/GUI/Main/MainChangeset.cs b/GUI/Main/MainChangeset.cs
index 51d59d51cd..2193a1fd64 100644
--- a/GUI/Main/MainChangeset.cs
+++ b/GUI/Main/MainChangeset.cs
@@ -3,7 +3,7 @@
 using System.Collections.Generic;
 using System.Windows.Forms;
 
-namespace CKAN
+namespace CKAN.GUI
 {
     public partial class Main
     {
diff --git a/GUI/Main/MainDialogs.cs b/GUI/Main/MainDialogs.cs
index 16464247db..294e207e7a 100644
--- a/GUI/Main/MainDialogs.cs
+++ b/GUI/Main/MainDialogs.cs
@@ -1,7 +1,7 @@
 using System;
 using System.Windows.Forms;
 
-namespace CKAN
+namespace CKAN.GUI
 {
     public partial class Main
     {
diff --git a/GUI/Main/MainDownload.cs b/GUI/Main/MainDownload.cs
index f3c8499fe9..641a0e4dd5 100644
--- a/GUI/Main/MainDownload.cs
+++ b/GUI/Main/MainDownload.cs
@@ -3,7 +3,7 @@
 using System.ComponentModel;
 using System.Threading.Tasks;
 
-namespace CKAN
+namespace CKAN.GUI
 {
     public partial class Main
     {
diff --git a/GUI/Main/MainExport.cs b/GUI/Main/MainExport.cs
index 5f7ffa7b2e..425358c3a6 100644
--- a/GUI/Main/MainExport.cs
+++ b/GUI/Main/MainExport.cs
@@ -7,7 +7,7 @@
 using CKAN.Exporters;
 using CKAN.Types;
 
-namespace CKAN
+namespace CKAN.GUI
 {
     public partial class Main
     {
diff --git a/GUI/Main/MainImport.cs b/GUI/Main/MainImport.cs
index 6b19dd6564..32d0dc7a5e 100644
--- a/GUI/Main/MainImport.cs
+++ b/GUI/Main/MainImport.cs
@@ -3,7 +3,7 @@
 using System.Collections.Generic;
 using System.Windows.Forms;
 
-namespace CKAN
+namespace CKAN.GUI
 {
     public partial class Main
     {
diff --git a/GUI/Main/MainInstall.cs b/GUI/Main/MainInstall.cs
index 344c89236b..ac67ecdc66 100644
--- a/GUI/Main/MainInstall.cs
+++ b/GUI/Main/MainInstall.cs
@@ -5,7 +5,7 @@
 using System.Linq;
 using CKAN.Extensions;
 
-namespace CKAN
+namespace CKAN.GUI
 {
     using ModChanges = List<ModChange>;
     public partial class Main
diff --git a/GUI/Main/MainLabels.cs b/GUI/Main/MainLabels.cs
index 90fe08bed6..263aa7da34 100644
--- a/GUI/Main/MainLabels.cs
+++ b/GUI/Main/MainLabels.cs
@@ -3,7 +3,7 @@
 using System.Collections.Generic;
 using CKAN.Extensions;
 
-namespace CKAN
+namespace CKAN.GUI
 {
     public partial class Main
     {
diff --git a/GUI/Main/MainProvides.cs b/GUI/Main/MainProvides.cs
index 573ebc551a..ac13720205 100644
--- a/GUI/Main/MainProvides.cs
+++ b/GUI/Main/MainProvides.cs
@@ -1,6 +1,6 @@
 using System.Windows.Forms;
 
-namespace CKAN
+namespace CKAN.GUI
 {
     public partial class Main
     {
diff --git a/GUI/Main/MainRecommendations.cs b/GUI/Main/MainRecommendations.cs
index 8ac806615f..a272c93c45 100644
--- a/GUI/Main/MainRecommendations.cs
+++ b/GUI/Main/MainRecommendations.cs
@@ -6,7 +6,7 @@
 using CKAN.Extensions;
 using CKAN.Versioning;
 
-namespace CKAN
+namespace CKAN.GUI
 {
     using ModChanges = List<ModChange>;
 
diff --git a/GUI/Main/MainRepo.cs b/GUI/Main/MainRepo.cs
index e1c5f89934..464e2e7939 100644
--- a/GUI/Main/MainRepo.cs
+++ b/GUI/Main/MainRepo.cs
@@ -8,7 +8,7 @@
 using CKAN.Configuration;
 using Autofac;
 
-namespace CKAN
+namespace CKAN.GUI
 {
     public partial class Main
     {
diff --git a/GUI/Main/MainTabControl.cs b/GUI/Main/MainTabControl.cs
index d839949479..efb6f25a1d 100644
--- a/GUI/Main/MainTabControl.cs
+++ b/GUI/Main/MainTabControl.cs
@@ -1,6 +1,6 @@
 using System;
 
-namespace CKAN
+namespace CKAN.GUI
 {
     public class MainTabControl : ThemedTabControl
     {
diff --git a/GUI/Main/MainTime.cs b/GUI/Main/MainTime.cs
index d3204f63ee..d69a8dbecc 100644
--- a/GUI/Main/MainTime.cs
+++ b/GUI/Main/MainTime.cs
@@ -2,7 +2,7 @@
 using System.Diagnostics;
 using System.Windows.Forms;
 
-namespace CKAN
+namespace CKAN.GUI
 {
     public partial class Main
     {
diff --git a/GUI/Main/MainTrayIcon.cs b/GUI/Main/MainTrayIcon.cs
index 9118e765a4..44346d4c91 100644
--- a/GUI/Main/MainTrayIcon.cs
+++ b/GUI/Main/MainTrayIcon.cs
@@ -4,7 +4,7 @@
 using CKAN.Configuration;
 using Autofac;
 
-namespace CKAN
+namespace CKAN.GUI
 {
     public partial class Main
     {
diff --git a/GUI/Main/MainWait.cs b/GUI/Main/MainWait.cs
index 4a4e84ad22..a70c5199aa 100644
--- a/GUI/Main/MainWait.cs
+++ b/GUI/Main/MainWait.cs
@@ -1,7 +1,7 @@
 using System;
 using System.Windows.Forms;
 
-namespace CKAN
+namespace CKAN.GUI
 {
     public partial class Main
     {
diff --git a/GUI/Model/ExportOption.cs b/GUI/Model/ExportOption.cs
index 6fb9148e8c..f92845867b 100644
--- a/GUI/Model/ExportOption.cs
+++ b/GUI/Model/ExportOption.cs
@@ -1,6 +1,6 @@
 using CKAN.Types;
 
-namespace CKAN
+namespace CKAN.GUI
 {
     internal sealed class ExportOption
     {
diff --git a/GUI/Model/GUIConfiguration.cs b/GUI/Model/GUIConfiguration.cs
index ce47bc4461..17f49f7a2e 100644
--- a/GUI/Model/GUIConfiguration.cs
+++ b/GUI/Model/GUIConfiguration.cs
@@ -3,7 +3,7 @@
 using System.IO;
 using System.Xml.Serialization;
 
-namespace CKAN
+namespace CKAN.GUI
 {
     [XmlRootAttribute("Configuration")]
     public class GUIConfiguration
diff --git a/GUI/Model/GUIMod.cs b/GUI/Model/GUIMod.cs
index 0a642bd584..fe2f254f0c 100644
--- a/GUI/Model/GUIMod.cs
+++ b/GUI/Model/GUIMod.cs
@@ -6,7 +6,7 @@
 using System.Runtime.CompilerServices;
 using CKAN.Versioning;
 
-namespace CKAN
+namespace CKAN.GUI
 {
     public sealed class GUIMod : INotifyPropertyChanged
     {
diff --git a/GUI/Model/ModChange.cs b/GUI/Model/ModChange.cs
index 36219038e8..2cb1a30cbb 100644
--- a/GUI/Model/ModChange.cs
+++ b/GUI/Model/ModChange.cs
@@ -1,7 +1,7 @@
 using System;
 using System.Linq;
 
-namespace CKAN
+namespace CKAN.GUI
 {
     public enum GUIModChangeType
     {
@@ -12,6 +12,22 @@ public enum GUIModChangeType
         Replace = 4
     }
 
+    public static class GUIModChangeTypeExtensions
+    {
+        public static string ToI18nString(this GUIModChangeType val)
+        {
+            switch (val)
+            {
+                case GUIModChangeType.None:    return Properties.Resources.ChangeTypeNone;
+                case GUIModChangeType.Install: return Properties.Resources.ChangeTypeInstall;
+                case GUIModChangeType.Remove:  return Properties.Resources.ChangeTypeRemove;
+                case GUIModChangeType.Update:  return Properties.Resources.ChangeTypeUpdate;
+                case GUIModChangeType.Replace: return Properties.Resources.ChangeTypeReplace;
+            }
+            throw new NotImplementedException(val.ToString());
+        }
+    }
+
     /// <summary>
     /// Everything the GUI needs to know about a change, including
     /// the mod itself, the change we're making, and the reason why.
@@ -56,7 +72,7 @@ public override int GetHashCode()
 
         public override string ToString()
         {
-            return $"{ChangeType} {Mod} ({Reason})";
+            return $"{ChangeType.ToI18nString()} {Mod} ({Reason})";
         }
 
         protected string modNameAndStatus(CkanModule m)
@@ -81,18 +97,7 @@ public virtual string Description
         {
             get
             {
-                switch (ChangeType)
-                {
-                    case GUIModChangeType.Install:
-                        if (Reason is SelectionReason.UserRequested)
-                        {
-                            return Properties.Resources.MainChangesetNewInstall;
-                        }
-                        else goto default;
-
-                    default:
-                        return Reason.Reason.Trim();
-                }
+                return Reason.Reason.Trim();
             }
         }
     }
diff --git a/GUI/Model/ModList.cs b/GUI/Model/ModList.cs
index f5e49b46fa..23a96af02a 100644
--- a/GUI/Model/ModList.cs
+++ b/GUI/Model/ModList.cs
@@ -9,7 +9,7 @@
 using System.Windows.Forms;
 using CKAN.Versioning;
 
-namespace CKAN
+namespace CKAN.GUI
 {
     public enum GUIModFilter
     {
diff --git a/GUI/Model/ModSearch.cs b/GUI/Model/ModSearch.cs
index 05a5418b70..cc3d8df842 100644
--- a/GUI/Model/ModSearch.cs
+++ b/GUI/Model/ModSearch.cs
@@ -2,7 +2,7 @@
 using System.Linq;
 using System.Collections.Generic;
 
-namespace CKAN
+namespace CKAN.GUI
 {
     /// <summary>
     /// An object representing a search to be performed of the mod list.
diff --git a/GUI/NavigationHistory.cs b/GUI/NavigationHistory.cs
index f9513070d4..efcf537e52 100644
--- a/GUI/NavigationHistory.cs
+++ b/GUI/NavigationHistory.cs
@@ -1,4 +1,4 @@
-namespace CKAN
+namespace CKAN.GUI
 {
     using System;
     using System.Collections.Generic;
diff --git a/GUI/Plugins/IGUIPlugin.cs b/GUI/Plugins/IGUIPlugin.cs
index 9e29bea080..d12222bba4 100644
--- a/GUI/Plugins/IGUIPlugin.cs
+++ b/GUI/Plugins/IGUIPlugin.cs
@@ -1,7 +1,7 @@
 using System;
 using CKAN.Versioning;
 
-namespace CKAN
+namespace CKAN.GUI
 {
     public abstract class IGUIPlugin
     {
diff --git a/GUI/Plugins/PluginController.cs b/GUI/Plugins/PluginController.cs
index 58b2c70c90..006a57343c 100644
--- a/GUI/Plugins/PluginController.cs
+++ b/GUI/Plugins/PluginController.cs
@@ -5,7 +5,7 @@
 using System.Reflection;
 using log4net;
 
-namespace CKAN
+namespace CKAN.GUI
 {
     public class PluginController
     {
diff --git a/GUI/Program.cs b/GUI/Program.cs
index 56b229b18b..5ce4eae41c 100644
--- a/GUI/Program.cs
+++ b/GUI/Program.cs
@@ -2,7 +2,7 @@
 using System.Linq;
 using System.Windows.Forms;
 
-namespace CKAN
+namespace CKAN.GUI
 {
     public static class GUI
     {
@@ -42,7 +42,7 @@ public static void UnhandledExceptionEventHandler(Object sender, UnhandledExcept
 
             // Provide a stack backtrace, so our users and non-debugging devs can
             // see what's gone wrong.
-            CKAN.Main.Instance.ErrorDialog("Unhandled exception:\r\n{0} ", exception.ToString());
+            CKAN.GUI.Main.Instance.ErrorDialog("Unhandled exception:\r\n{0} ", exception.ToString());
         }
     }
 }
diff --git a/GUI/Properties/Resources.Designer.cs b/GUI/Properties/Resources.Designer.cs
index 753e97f53f..4dc22e3492 100644
--- a/GUI/Properties/Resources.Designer.cs
+++ b/GUI/Properties/Resources.Designer.cs
@@ -8,7 +8,7 @@
 // </auto-generated>
 //------------------------------------------------------------------------------
 
-namespace CKAN.Properties {
+namespace CKAN.GUI.Properties {
     using System;
 
 
@@ -39,7 +39,7 @@ internal Resources() { }
             get {
                 if (object.ReferenceEquals(resourceMan, null))
                 {
-                    resourceMan = new SingleAssemblyResourceManager("CKAN.Properties.Resources", typeof(Resources).Assembly);
+                    resourceMan = new SingleAssemblyResourceManager("CKAN.GUI.Properties.Resources", typeof(Resources).Assembly);
                 }
                 return resourceMan;
             }
@@ -470,9 +470,6 @@ internal static string MainChangesetHostSize {
         internal static string MainChangesetUpdateSelected {
             get { return (string)(ResourceManager.GetObject("MainChangesetUpdateSelected", resourceCulture)); }
         }
-        internal static string MainChangesetNewInstall {
-            get { return (string)(ResourceManager.GetObject("MainChangesetNewInstall", resourceCulture)); }
-        }
 
         internal static string MainImportTitle {
             get { return (string)(ResourceManager.GetObject("MainImportTitle", resourceCulture)); }
@@ -1033,5 +1030,20 @@ internal static string TotalPlayTime {
             get { return (string)(ResourceManager.GetObject("TotalPlayTime", resourceCulture)); }
         }
 
+        internal static string ChangeTypeNone {
+            get { return (string)(ResourceManager.GetObject("ChangeTypeNone", resourceCulture)); }
+        }
+        internal static string ChangeTypeInstall {
+            get { return (string)(ResourceManager.GetObject("ChangeTypeInstall", resourceCulture)); }
+        }
+        internal static string ChangeTypeRemove {
+            get { return (string)(ResourceManager.GetObject("ChangeTypeRemove", resourceCulture)); }
+        }
+        internal static string ChangeTypeUpdate {
+            get { return (string)(ResourceManager.GetObject("ChangeTypeUpdate", resourceCulture)); }
+        }
+        internal static string ChangeTypeReplace {
+            get { return (string)(ResourceManager.GetObject("ChangeTypeReplace", resourceCulture)); }
+        }
     }
 }
diff --git a/GUI/Properties/Resources.de-DE.resx b/GUI/Properties/Resources.de-DE.resx
index 35d746de6d..70a0241742 100644
--- a/GUI/Properties/Resources.de-DE.resx
+++ b/GUI/Properties/Resources.de-DE.resx
@@ -194,7 +194,6 @@ Möchten Sie es wirklich installieren?</value></data>
   <data name="AllModVersionsInstallNo" xml:space="preserve"><value>Abbrechen</value></data>
   <data name="MainChangesetMetapackage" xml:space="preserve"><value>{0} {1} (Metapacket)</value></data>
   <data name="MainChangesetUpdateSelected" xml:space="preserve"><value>Mod-Update ausgewählt vom Nutzer {0}.</value></data>
-  <data name="MainChangesetNewInstall" xml:space="preserve"><value>Neue Mod-Installation ausgewählt vom Nutzer.</value></data>
   <data name="MainImportTitle" xml:space="preserve"><value>Mod-Import</value></data>
   <data name="MainImportWaitTitle" xml:space="preserve"><value>Statuslog</value></data>
   <data name="MainInstallWaitTitle" xml:space="preserve"><value>Statuslog</value></data>
diff --git a/GUI/Properties/Resources.fr-FR.resx b/GUI/Properties/Resources.fr-FR.resx
index be8e56a89a..4b4ef51a7e 100644
--- a/GUI/Properties/Resources.fr-FR.resx
+++ b/GUI/Properties/Resources.fr-FR.resx
@@ -124,18 +124,18 @@
   <data name="CloneFakeKspDialogEnterName" xml:space="preserve"><value>Veuillez entrer un nom pour la nouvelle instance.</value></data>
   <data name="CloneFakeKspDialogEnterPath" xml:space="preserve"><value>Veuillez entrer un chemin pour la nouvelle instance.</value></data>
   <data name="CloneFakeKspDialogCloningInstance" xml:space="preserve"><value>"Clonage de l'instance..."</value></data>
-  <data name="CloneFakeKspDialogDlcVersionMalformatted" xml:space="preserve"><value>La version pour le DLC {0} est mal mal-formatée. Essayez "1.0.0" par exemple.</value></data>
-  <data name="CloneFakeKspDialogInstanceNotValid" xml:space="preserve"><value>L'instance que vous voulez cloner n'est pas valide: {0}</value></data>
-  <data name="CloneFakeKspDialogDestinationNotEmpty" xml:space="preserve"><value>Le dossier de destination n'est pas vide: {0}</value></data>
-  <data name="CloneFakeKspDialogCloneFailed" xml:space="preserve"><value>Échec du clonage: {0}</value></data>
+  <data name="CloneFakeKspDialogDlcVersionMalformatted" xml:space="preserve"><value>La version pour le DLC {0} est mal formatée. Essayez "1.0.0" par exemple.</value></data>
+  <data name="CloneFakeKspDialogInstanceNotValid" xml:space="preserve"><value>L'instance que vous voulez cloner n'est pas valide : {0}</value></data>
+  <data name="CloneFakeKspDialogDestinationNotEmpty" xml:space="preserve"><value>Le dossier de destination n'est pas vide : {0}</value></data>
+  <data name="CloneFakeKspDialogCloneFailed" xml:space="preserve"><value>Échec du clonage : {0}</value></data>
   <data name="CloneFakeKspDialogSuccessfulClone" xml:space="preserve"><value>Instance clonée avec succès.</value></data>
   <data name="CloneFakeKspDialogCreatingInstance" xml:space="preserve"><value>Création de la nouvelle instance...</value></data>
   <data name="CloneFakeKspDialogNameAlreadyUsed" xml:space="preserve"><value>Ce nom est déjà utilisé.</value></data>
-  <data name="CloneFakeKspDialogFakeFailed" xml:space="preserve"><value>Échec de la création d'une fausse instance: {0}</value></data>
+  <data name="CloneFakeKspDialogFakeFailed" xml:space="preserve"><value>Échec de la création d'une instance artificielle : {0}</value></data>
   <data name="CloneFakeKspDialogSuccessfulCreate" xml:space="preserve"><value>Instance créée avec succès.</value></data>
   <data name="CompatibleGameVersionsDialogNone" xml:space="preserve"><value>&lt;AUCUNE&gt;</value></data>
   <data name="CompatibleGameVersionsDialogGameUpdated" xml:space="preserve"><value>Le jeu a été mis à jour depuis que vous avez défini les versions de jeu compatibles. Veuillez vérifier ces paramètres.</value></data>
-  <data name="CompatibleGameVersionsDialogVersionDetails" xml:space="preserve"><value>{0} (version de jeu précédente: {1})</value></data>
+  <data name="CompatibleGameVersionsDialogVersionDetails" xml:space="preserve"><value>{0} (version de jeu précédente : {1})</value></data>
   <data name="CompatibleGameVersionsDialogInvalidFormat" xml:space="preserve"><value>La version a un format invalide</value></data>
   <data name="CompatibleGameVersionsDialogErrorTitle" xml:space="preserve"><value>Erreur</value></data>
   <data name="ConfigurationParseError" xml:space="preserve"><value>Erreur en essayant d'interpréter "{0}"{1}
@@ -144,7 +144,7 @@ Essayez de déplacer {2} en dehors de {3} et redémarrez CKAN.</value></data>
   <data name="GUIModUnknown" xml:space="preserve"><value>Inconnue</value></data>
   <data name="GUIModMethodNotCKAN" xml:space="preserve"><value>Cette Méthode ne peut pas être appelée sauf si IsCKAN</value></data>
   <data name="GUIModGameCompatibilityLong" xml:space="preserve"><value>{0} (en utilisant la version du mod {1})</value></data>
-  <data name="MainAutoUpdateFailed" xml:space="preserve"><value>Erreur durant la mis à jour auto:&#xD;&#xA;{0}</value></data>
+  <data name="MainAutoUpdateFailed" xml:space="preserve"><value>Erreur durant la mise à jour auto :&#xD;&#xA;{0}</value></data>
   <data name="MainQuitWithConflicts" xml:space="preserve"><value>Il y a des conflits. Voulez-vous vraiment quitter?&#xD;&#xA;&#xD;&#xA;{0}</value></data>
   <data name="MainQuit" xml:space="preserve"><value>Quitter</value></data>
   <data name="MainGoBack" xml:space="preserve"><value>Revenir</value></data>
@@ -152,7 +152,7 @@ Essayez de déplacer {2} en dehors de {3} et redémarrez CKAN.</value></data>
   <data name="MainQuitWithUnappliedChanges" xml:space="preserve"><value>Il y a des changements non pris en compte. Voulez-vous vraiment quitter?
 
 {0}</value></data>
-  <data name="MainUpgradingWaitTitle" xml:space="preserve"><value>Mis à jour de CKAN</value></data>
+  <data name="MainUpgradingWaitTitle" xml:space="preserve"><value>Mise à jour de CKAN</value></data>
   <data name="MainUpgradingTo" xml:space="preserve"><value>Mise à niveau de CKAN vers la {0}</value></data>
   <data name="MainDepNotSatisfied" xml:space="preserve"><value>{0} dépend de {1}, qui n'est pas compatible avec la version du jeu installée actuellement</value></data>
   <data name="MainFilterAll" xml:space="preserve"><value>Filtre (Tout)</value></data>
@@ -168,7 +168,7 @@ Essayez de déplacer {2} en dehors de {3} et redémarrez CKAN.</value></data>
   <data name="MainFilterLabel" xml:space="preserve"><value>Label ({0})</value></data>
   <data name="MainFilterTag" xml:space="preserve"><value>Étiquette ({0})</value></data>
   <data name="MainFilterUntagged" xml:space="preserve"><value>Étiquette (sans étiquette)</value></data>
-  <data name="MainLaunchWithIncompatible" xml:space="preserve"><value>Certains modules installés sont incompatibles! Il pourrait être dangereux de lancer le jeu. Voulez-vous vraiment le lancer?
+  <data name="MainLaunchWithIncompatible" xml:space="preserve"><value>Certains modules installés sont incompatibles ! Il pourrait être dangereux de lancer le jeu. Voulez-vous vraiment le lancer ?
 
 {0}</value></data>
   <data name="MainLaunch" xml:space="preserve"><value>Lancement</value></data>
@@ -183,12 +183,12 @@ Essayez de déplacer {2} en dehors de {3} et redémarrez CKAN.</value></data>
   <data name="MainCSV" xml:space="preserve"><value>Comma-separated values (*.csv)</value></data>
   <data name="MainTSV" xml:space="preserve"><value>Tab-separated values (*.tsv)</value></data>
   <data name="MainNotFound" xml:space="preserve"><value>Impossible de trouver.</value></data>
-  <data name="MainReinstallConfirm" xml:space="preserve"><value>Voulez-vous réinstaller {0}?</value></data>
-  <data name="MainCantInstallDLC" xml:space="preserve"><value>CKAN ne peut pas installer l'extension {0}!</value></data>
-  <data name="MainCorruptedRegistry" xml:space="preserve"><value>Registre corrompu archivé à {0}: {1}
+  <data name="MainReinstallConfirm" xml:space="preserve"><value>Voulez-vous réinstaller {0} ?</value></data>
+  <data name="MainCantInstallDLC" xml:space="preserve"><value>CKAN ne peut pas installer l'extension {0} !</value></data>
+  <data name="MainCorruptedRegistry" xml:space="preserve"><value>Registre corrompu archivé à {0} : {1}
 
 Cela veut dire que CKAN a oublié tous les mods que vous avez installé, mais ceux-ci sont toujours présents dans le dossier GameData. Vous pouvez les réinstaller en important le fichier {2}</value></data>
-  <data name="AllModVersionsInstallPrompt" xml:space="preserve"><value>{0} n'est pas supporté par votre version de jeu actuelle and pourraît ne pas marcher du tout. Si vous avez des problème avec, vous NE devez PAS demander de l'aide à ses développeurs.
+  <data name="AllModVersionsInstallPrompt" xml:space="preserve"><value>{0} n'est pas supporté par votre version de jeu actuelle et pourrait ne pas marcher du tout. Si vous avez des problème avec, vous NE devez PAS demander de l'aide à ses développeurs.
 
 Voulez-vous vraiment l'installer?</value></data>
   <data name="AllModVersionsInstallYes" xml:space="preserve"><value>Installer</value></data>
@@ -196,29 +196,28 @@ Voulez-vous vraiment l'installer?</value></data>
   <data name="MainChangesetMetapackage" xml:space="preserve"><value>{0} {1} (méta-paquet)</value></data>
   <data name="MainChangesetCached" xml:space="preserve"><value>{0} {1} (en cache)</value></data>
   <data name="MainChangesetHostSize" xml:space="preserve"><value>{0} {1} ({2}, {3})</value></data>
-  <data name="MainChangesetUpdateSelected" xml:space="preserve"><value>Mis à jour sélectionnée par l'utilisateur à la version {0}.</value></data>
-  <data name="MainChangesetNewInstall" xml:space="preserve"><value>Nouvelle installation de mod sélectionnée par l'utilisateur.</value></data>
+  <data name="MainChangesetUpdateSelected" xml:space="preserve"><value>Mise à jour demandée vers la version {0}.</value></data>
   <data name="MainImportTitle" xml:space="preserve"><value>Importer des Mods</value></data>
   <data name="MainImportFilter" xml:space="preserve"><value>Mods (*.zip)|*.zip</value></data>
   <data name="MainImportWaitTitle" xml:space="preserve"><value>Registre</value></data>
   <data name="MainInstallWaitTitle" xml:space="preserve"><value>Registre</value></data>
-  <data name="MainInstallDepNotSatisfied" xml:space="preserve"><value>{0} requiert {1} mais il n'est pas listé dans l'index, ou non-disponible pour votre version de jeu.</value></data>
-  <data name="MainInstallNotFound" xml:space="preserve"><value>Le module {0} est requis mais il n'est pas listé dans l'index, ou non-disponible pour votre version de jeu.</value></data>
-  <data name="MainInstallBadMetadata" xml:space="preserve"><value>Mauvaises métadonnées détectées pour le module {0}: {1}</value></data>
-  <data name="MainInstallFileExists" xml:space="preserve"><value>Oh non! Nous avons essayé de remplacer un fichier appartenant à un autre mod!
+  <data name="MainInstallDepNotSatisfied" xml:space="preserve"><value>{0} nécessite {1} mais il n'est pas listé dans l'index, ou indisponible pour votre version de jeu.</value></data>
+  <data name="MainInstallNotFound" xml:space="preserve"><value>Le module {0} est requis mais il n'est pas listé dans l'index, ou indisponible pour votre version de jeu.</value></data>
+  <data name="MainInstallBadMetadata" xml:space="preserve"><value>Mauvaises métadonnées détectées pour le module {0} : {1}</value></data>
+  <data name="MainInstallFileExists" xml:space="preserve"><value>Oh non! Nous avons essayé de remplacer un fichier appartenant à un autre mod !
 Veuillez essayer de mettre à jour CKAN puis de réessayer.
 
-Si ce problème se produit de nouveau, il pourrait s'agir d'un bug d'empaquettement.
+Si ce problème se produit de nouveau, il pourrait s'agir d'un bogue d'emballage.
 Veuillez le signaler à:
 
 https://github.com/KSP-CKAN/NetKAN/issues/new/choose
 
-Veuillez inclure les informations suivantes dans votre rapport:
+Veuillez inclure les informations suivantes dans votre rapport :
 
-Fichier      : {0}
-Mod en cours d'installation: {1}
-Mod propriétaire: {2}
-Version CKAN   : {3}</value></data>
+Fichier          : {0}
+Mod à installer  : {1}
+Mod propriétaire : {2}
+Version CKAN     : {3}</value></data>
   <data name="MainInstallUnownedFileExists" xml:space="preserve"><value>Oh non!
 
 On dirait que vous essayez d'installer un mod qui est déjà installé,
@@ -228,7 +227,7 @@ Par précaution, CKAN ne va *jamais* remplacer ou modifier un fichier
 qu'il n'a pas installé lui-même.
 
 Si vous souhaitez installer {0} via CKAN,
-alors veuillez désinstaller manuellement le mod qui possède:
+alors veuillez désinstaller manuellement le mod qui possède :
 
 {1}
 
@@ -236,42 +235,42 @@ et essayez de nouveau.</value></data>
   <data name="MainInstallGameDataReverted" xml:space="preserve"><value>Votre dossier GameData a été remis dans son état initial.</value></data>
   <data name="MainInstallOpenSettingsPrompt" xml:space="preserve"><value>{0}
 
-Ouvrir les paramètres maintenant?</value></data>
+Ouvrir les paramètres maintenant ?</value></data>
   <data name="MainInstallOpenSettings" xml:space="preserve"><value>Ouvrir les paramètres</value></data>
   <data name="MainInstallNo" xml:space="preserve"><value>Non</value></data>
   <data name="MainInstallModSuccess" xml:space="preserve"><value>Le module "{0}" a été installé avec succès</value></data>
-  <data name="MainInstallSuccess" xml:space="preserve"><value>Succès!</value></data>
+  <data name="MainInstallSuccess" xml:space="preserve"><value>Succès !</value></data>
   <data name="MainInstallCancelTooLate" xml:space="preserve"><value>Annulation trop tardive, le processus est terminé!</value></data>
   <data name="MainInstallCancelAfterInstall" xml:space="preserve"><value>L'utilisateur a annulé le processus manuellement, mais tous les mods ont déjà été (Dés)installés/mis à niveau.</value></data>
-  <data name="MainInstallProcessComplete" xml:space="preserve"><value>Processus terminé!</value></data>
-  <data name="MainInstallProcessCanceled" xml:space="preserve"><value>Processus annulé par l'utilisateur!</value></data>
-  <data name="MainInstallCanceledManually" xml:space="preserve"><value>L'utilisateur a annulé le processus manuellement!</value></data>
-  <data name="MainInstallInstallCanceled" xml:space="preserve"><value>(Dés)Installation/Mise à niveau annulée!</value></data>
-  <data name="MainInstallErrorInstalling" xml:space="preserve"><value>Erreur lors de l'installation!</value></data>
-  <data name="MainInstallUnknownError" xml:space="preserve"><value>Une erreur inconnue s'est produite, veuillez réessayer!</value></data>
+  <data name="MainInstallProcessComplete" xml:space="preserve"><value>Processus terminé !</value></data>
+  <data name="MainInstallProcessCanceled" xml:space="preserve"><value>Processus annulé par l'utilisateur !</value></data>
+  <data name="MainInstallCanceledManually" xml:space="preserve"><value>L'utilisateur a annulé le processus manuellement !</value></data>
+  <data name="MainInstallInstallCanceled" xml:space="preserve"><value>(Dés)Installation/Mise à niveau annulée !</value></data>
+  <data name="MainInstallErrorInstalling" xml:space="preserve"><value>Erreur lors de l'installation !</value></data>
+  <data name="MainInstallUnknownError" xml:space="preserve"><value>Une erreur inconnue s'est produite, veuillez réessayer !</value></data>
   <data name="MainInstallKnownError" xml:space="preserve"><value>Si le message ci-dessus indique une erreur de téléchargement, veuillez réessayer. Sinon, veuillez signaler le problème pour que nous puissions investiguer.
-Si vous suspectez un problème de métadonnées: https://github.com/KSP-CKAN/NetKAN/issues/new/choose
-Si vous suspectez un problème client: https://github.com/KSP-CKAN/CKAN/issues/new/choose</value></data>
-  <data name="MainInstallFailed" xml:space="preserve"><value>Échec de l'installation!</value></data>
-  <data name="MainInstallProvidedBy" xml:space="preserve"><value>Le module {0} est fourni par plus qu'un seul module, veuillez choisir l'un des mods suivants:</value></data>
+Si vous suspectez un problème de métadonnées : https://github.com/KSP-CKAN/NetKAN/issues/new/choose
+Si vous suspectez un problème client : https://github.com/KSP-CKAN/CKAN/issues/new/choose</value></data>
+  <data name="MainInstallFailed" xml:space="preserve"><value>Échec de l'installation !</value></data>
+  <data name="MainInstallProvidedBy" xml:space="preserve"><value>Le module {0} est fourni par plusieurs modules, veuillez choisir l'un des mods suivants :</value></data>
   <data name="MainInstallCantInstallDLC" xml:space="preserve"><value>CKAN ne peut pas installer l'extension "{0}" pour vous.</value></data>
   <data name="ModInfoNSlashA" xml:space="preserve"><value>N/A</value></data>
   <data name="ModInfoVirtual" xml:space="preserve"><value>{0} (virtuel)</value></data>
   <data name="ModInfoNotIndexed" xml:space="preserve"><value>{0} (non indexé)</value></data>
   <data name="ModInfoNotCached" xml:space="preserve"><value>Ce mod n'est pas dans le cache, cliquez "télécharger" pour voir son contenu</value></data>
   <data name="ModInfoCached" xml:space="preserve"><value>Le module est dans le cache, aperçu disponible</value></data>
-  <data name="ModInfoHomepageLabel" xml:space="preserve"><value>Page principale:</value></data>
-  <data name="ModInfoSpaceDockLabel" xml:space="preserve"><value>SpaceDock:</value></data>
-  <data name="ModInfoCurseLabel" xml:space="preserve"><value>Curse:</value></data>
-  <data name="ModInfoRepositoryLabel" xml:space="preserve"><value>Répertoire:</value></data>
-  <data name="ModInfoBugTrackerLabel" xml:space="preserve"><value>Traqueur de bogues:</value></data>
-  <data name="ModInfoContinuousIntegrationLabel" xml:space="preserve"><value>Intégration continue:</value></data>
-  <data name="ModInfoLicenseLabel" xml:space="preserve"><value>Licence:</value></data>
-  <data name="ModInfoManualLabel" xml:space="preserve"><value>Manuel:</value></data>
-  <data name="ModInfoMetanetkanLabel" xml:space="preserve"><value>Métanetkan:</value></data>
-  <data name="ModInfoRemoteAvcLabel" xml:space="preserve"><value>Fichier version distant:</value></data>
-  <data name="ModInfoStoreLabel" xml:space="preserve"><value>Magasin:</value></data>
-  <data name="ModInfoSteamStoreLabel" xml:space="preserve"><value>Magasin Steam:</value></data>
+  <data name="ModInfoHomepageLabel" xml:space="preserve"><value>Page principale :</value></data>
+  <data name="ModInfoSpaceDockLabel" xml:space="preserve"><value>SpaceDock :</value></data>
+  <data name="ModInfoCurseLabel" xml:space="preserve"><value>Curse :</value></data>
+  <data name="ModInfoRepositoryLabel" xml:space="preserve"><value>Répertoire :</value></data>
+  <data name="ModInfoBugTrackerLabel" xml:space="preserve"><value>Traqueur de bogues :</value></data>
+  <data name="ModInfoContinuousIntegrationLabel" xml:space="preserve"><value>Intégration continue :</value></data>
+  <data name="ModInfoLicenseLabel" xml:space="preserve"><value>Licence :</value></data>
+  <data name="ModInfoManualLabel" xml:space="preserve"><value>Manuel :</value></data>
+  <data name="ModInfoMetanetkanLabel" xml:space="preserve"><value>Métanetkan :</value></data>
+  <data name="ModInfoRemoteAvcLabel" xml:space="preserve"><value>Fichier version distant :</value></data>
+  <data name="ModInfoStoreLabel" xml:space="preserve"><value>Magasin :</value></data>
+  <data name="ModInfoSteamStoreLabel" xml:space="preserve"><value>Magasin Steam :</value></data>
   <data name="MainModListWaitTitle" xml:space="preserve"><value>Chargement des modules</value></data>
   <data name="MainModListLoadingRegistry" xml:space="preserve"><value>Chargement du registre...</value></data>
   <data name="MainModListLoadingInstalled" xml:space="preserve"><value>Chargement des modules installés...</value></data>
@@ -300,19 +299,19 @@ Si vous suspectez un problème client: https://github.com/KSP-CKAN/CKAN/issues/n
   <data name="MainRepoScanning" xml:space="preserve"><value>Détection des DLC et des modules installés manuellement...</value></data>
   <data name="MainRepoContacting" xml:space="preserve"><value>Contact du répertoire...</value></data>
   <data name="MainRepoUpdating" xml:space="preserve"><value>Mise à jour des répertoires...</value></data>
-  <data name="MainRepoFailedToConnect" xml:space="preserve"><value>Échec de la connection au répertoire. Exception: {0}</value></data>
+  <data name="MainRepoFailedToConnect" xml:space="preserve"><value>Échec de la connection au répertoire. Exception : {0}</value></data>
   <data name="MainRepoUpToDate" xml:space="preserve"><value>Répertoire déjà à jour.</value></data>
-  <data name="MainRepoFailed" xml:space="preserve"><value>Échec de la mise à jour du répertoire!</value></data>
+  <data name="MainRepoFailed" xml:space="preserve"><value>Échec de la mise à jour du répertoire !</value></data>
   <data name="MainRepoSuccess" xml:space="preserve"><value>Répertoires mis à jour avec succès.</value></data>
-  <data name="MainRepoAutoRefreshPrompt" xml:space="preserve"><value>Voulez-vous que CKAN rafraîchisse la liste de mod au lancement? (Vous pouvez toujours rafraîchir manuellement en utilisant le bouton en haut)</value></data>
-  <data name="MainRepoBalloonTipDetails" xml:space="preserve"><value>{0} mise(s) à jour disponible(s)</value></data>
+  <data name="MainRepoAutoRefreshPrompt" xml:space="preserve"><value>Voulez-vous que CKAN rafraîchisse la liste de mod au lancement ? (Vous pouvez toujours rafraîchir manuellement en utilisant le bouton en haut)</value></data>
+  <data name="MainRepoBalloonTipDetails" xml:space="preserve"><value>{0} mise·s à jour disponible·s</value></data>
   <data name="MainRepoBalloonTipTooltip" xml:space="preserve"><value>Cliquer pour mettre à niveau</value></data>
   <data name="MainTrayIconResume" xml:space="preserve"><value>Reprendre</value></data>
   <data name="MainTrayIconPause" xml:space="preserve"><value>Pause</value></data>
   <data name="MainTrayNoUpdates" xml:space="preserve"><value>Aucune mise à jour disponible</value></data>
-  <data name="MainTrayUpdatesAvailable" xml:space="preserve"><value>{0} mise(s) à jour disponible(s)</value></data>
+  <data name="MainTrayUpdatesAvailable" xml:space="preserve"><value>{0} mise·s à jour disponible·s</value></data>
   <data name="MainWaitPleaseWait" xml:space="preserve"><value>Veuillez patientez</value></data>
-  <data name="MainWaitDone" xml:space="preserve"><value>Terminé!</value></data>
+  <data name="MainWaitDone" xml:space="preserve"><value>Terminé !</value></data>
   <data name="ManageGameInstancesNotValid" xml:space="preserve"><value>Le répertoire {0} n'est pas un répertoire de jeu valide.</value></data>
   <data name="ManageGameInstancesDirectoryDeleted" xml:space="preserve"><value>Le répertoire "{0}" n'existe pas.</value></data>
     <data name="ManageGameInstancesNameColumnInvalid" xml:space="preserve"><value>{0} (INVALIDE)</value></data>
@@ -320,12 +319,12 @@ Si vous suspectez un problème client: https://github.com/KSP-CKAN/CKAN/issues/n
   <data name="NewRepoDialogFailed" xml:space="preserve"><value>Échec de la récupération de la liste maître.</value></data>
   <data name="PluginsDialogFilter" xml:space="preserve"><value>Plugins CKAN (*.dll)|*.dll</value></data>
   <data name="SettingsDialogSummmary" xml:space="preserve"><value>{0} fichiers, {1}</value></data>
-  <data name="SettingsDialogSummaryInvalid" xml:space="preserve"><value>Chemin invalide: {0}</value></data>
-  <data name="SettingsDialogCacheDescrip" xml:space="preserve"><value>Choisissez un dossier pour stocker les téléchargements de mods de CKAN:</value></data>
-  <data name="SettingsDialogDeleteConfirm" xml:space="preserve"><value>Voulez-vous vraiment supprimer {0} fichiers en cache, libérant {1}?</value></data>
+  <data name="SettingsDialogSummaryInvalid" xml:space="preserve"><value>Chemin invalide : {0}</value></data>
+  <data name="SettingsDialogCacheDescrip" xml:space="preserve"><value>Choisissez un dossier pour stocker les téléchargements de mods de CKAN :</value></data>
+  <data name="SettingsDialogDeleteConfirm" xml:space="preserve"><value>Voulez-vous vraiment supprimer {0} fichiers en cache, libérant {1} ?</value></data>
   <data name="AddAuthTokenTitle" xml:space="preserve"><value>Ajouter un jeton d'authentification</value></data>
-  <data name="AddAuthTokenHost" xml:space="preserve"><value>Hôte:</value></data>
-  <data name="AddAuthTokenToken" xml:space="preserve"><value>Jeton:</value></data>
+  <data name="AddAuthTokenHost" xml:space="preserve"><value>Hôte :</value></data>
+  <data name="AddAuthTokenToken" xml:space="preserve"><value>Jeton :</value></data>
   <data name="AddAuthTokenAccept" xml:space="preserve"><value>&amp;Ajouter</value></data>
   <data name="AddAuthTokenCancel" xml:space="preserve"><value>An&amp;nuler</value></data>
   <data name="AddAuthTokenHostRequired" xml:space="preserve"><value>Le champ de l'hôte est requis.</value></data>
@@ -334,18 +333,18 @@ Si vous suspectez un problème client: https://github.com/KSP-CKAN/CKAN/issues/n
   <data name="AddAuthTokenDupHost" xml:space="preserve"><value>{0} a déjà un jeton d'authentification.</value></data>
   <data name="SettingsDialogUpdateFailed" xml:space="preserve"><value>Erreur durant la mise à jour.
 Impossible de mettre à jour, car ckan.exe est en lecture-seule ou nous ne sommes pas autorisé à le remplacer. Veuillez mettre à jour manuellement via https://github.com/KSP-CKAN/CKAN/releases/latest.</value></data>
-  <data name="URLHandlersPrompt" xml:space="preserve"><value>CKAN requiert la permission d'ajouter un handler pour les URL ckan://
+  <data name="URLHandlersPrompt" xml:space="preserve"><value>CKAN demande la permission d'ajouter un handler pour les adresses ckan://
 Voulez-vous autoriser à faire cela? Si vous cliquer non, vous ne verrez plus ce message.</value></data>
   <data name="UtilCopyLink" xml:space="preserve"><value>&amp;Copier l'adresse du lien</value></data>
-  <data name="StatusInstanceLabelText" xml:space="preserve"><value>Instance de jeu: {0} ({1} {2})</value></data>
+  <data name="StatusInstanceLabelText" xml:space="preserve"><value>Instance de jeu : {0} ({1} {2})</value></data>
   <data name="ModuleLabelListFavourites" xml:space="preserve"><value>Favoris</value></data>
   <data name="ModuleLabelListHidden" xml:space="preserve"><value>Caché</value></data>
   <data name="ModuleLabelListGlobal" xml:space="preserve"><value>Global</value></data>
-  <data name="EditLabelsDialogConfirmDelete" xml:space="preserve"><value>Êtes-vous sûr de vouloir supprimer {0}? Il sera ensuite impossible de retourner en arrière!</value></data>
-  <data name="EditLabelsDialogSavePrompt" xml:space="preserve"><value>Sauvegarder les changements?</value></data>
-  <data name="EditLabelsDialogNoRecord" xml:space="preserve"><value>Aucun registre en édition!</value></data>
+  <data name="EditLabelsDialogConfirmDelete" xml:space="preserve"><value>Êtes-vous sûr de vouloir supprimer {0} ? Il sera ensuite impossible de retourner en arrière !</value></data>
+  <data name="EditLabelsDialogSavePrompt" xml:space="preserve"><value>Sauvegarder les changements ?</value></data>
+  <data name="EditLabelsDialogNoRecord" xml:space="preserve"><value>Aucun registre en édition !</value></data>
   <data name="EditLabelsDialogNameRequired" xml:space="preserve"><value>Nom requis!</value></data>
-  <data name="EditLabelsDialogAlreadyExists" xml:space="preserve"><value>{0} existe déjà dans {1}!</value></data>
+  <data name="EditLabelsDialogAlreadyExists" xml:space="preserve"><value>{0} existe déjà dans {1} !</value></data>
   <data name="EditLabelsDialogDelete" xml:space="preserve"><value>Supprimer</value></data>
   <data name="EditLabelsDialogCancel" xml:space="preserve"><value>Annuler</value></data>
   <data name="EditLabelsDialogSave" xml:space="preserve"><value>Sauvegarder</value></data>
@@ -359,19 +358,19 @@ Voulez-vous autoriser à faire cela? Si vous cliquer non, vous ne verrez plus ce
   <data name="EditLabelsToolTipRemoveOnChanges" xml:space="preserve"><value>Si coché, un module passant de incompatible à compatible sera retiré de ce label</value></data>
   <data name="EditLabelsToolTipAlertOnInstall" xml:space="preserve"><value>Si coché, le résumé des changements vous avertira si un mod s'apprête à être installé</value></data>
   <data name="EditLabelsToolTipRemoveOnInstall" xml:space="preserve"><value>Si coché, les modules seront retirés de ce label si ils sont installés</value></data>
-  <data name="MainLabelsUpdateMessage" xml:space="preserve"><value>Certains de vos modes surveillés ont été mis à jour:
+  <data name="MainLabelsUpdateMessage" xml:space="preserve"><value>Certains de vos modes surveillés ont été mis à jour :
 
 {0}</value></data>
   <data name="MainLabelsUpdateTitle" xml:space="preserve"><value>Notifications de mis à jour</value></data>
   <data name="MainLabelsUntagged" xml:space="preserve"><value>Sans étiquette ({0})</value></data>
-  <data name="EditModpackBadIdentifier" xml:space="preserve"><value>L'identifiant doit contenir UNIQUEMENT des letters, des nombres et des tirets, et doit commencer par une lettre ou un nombre</value></data>
+  <data name="EditModpackBadIdentifier" xml:space="preserve"><value>L'identifiant doit contenir UNIQUEMENT des lettres, des nombres et des tirets, et doit commencer par une lettre ou un nombre</value></data>
   <data name="EditModpackBadName" xml:space="preserve"><value>Nom requis</value></data>
   <data name="EditModpackBadVersion" xml:space="preserve"><value>Version requise</value></data>
   <data name="EditModpackBadGameVersions" xml:space="preserve"><value>Version de jeu minimum doit être inférieure ou égale à la version de jeu maximale</value></data>
   <data name="EditModpackTooltipIdentifier" xml:space="preserve"><value>Nom interne de ce modpack; lettres, nombres et tirets uniquement</value></data>
-  <data name="EditModpackTooltipName" xml:space="preserve"><value>Nom visible publiquement de ce modpack</value></data>
+  <data name="EditModpackTooltipName" xml:space="preserve"><value>Nom public de ce modpack</value></data>
   <data name="EditModpackTooltipAbstract" xml:space="preserve"><value>Courte description de ce modpack</value></data>
-  <data name="EditModpackTooltipVersion" xml:space="preserve"><value>Le numéro de version de ce modpack</value></data>
+  <data name="EditModpackTooltipVersion" xml:space="preserve"><value>Numéro de version de ce modpack</value></data>
   <data name="EditModpackTooltipGameVersionMin" xml:space="preserve"><value>Plus ancienne version de jeu compatible, vide pour toutes</value></data>
   <data name="EditModpackTooltipGameVersionMax" xml:space="preserve"><value>Dernière version de jeu compatible, vide pour toutes</value></data>
   <data name="EditModpackTooltipLicense" xml:space="preserve"><value>La licence de ce modpack</value></data>
@@ -394,16 +393,20 @@ Voulez-vous autoriser à faire cela? Si vous cliquer non, vous ne verrez plus ce
   <data name="ModSearchTagPrefix" xml:space="preserve"><value>étiquette:</value></data>
   <data name="ModSearchLabelPrefix" xml:space="preserve"><value>label:</value></data>
   <data name="ModSearchYesPrefix" xml:space="preserve"><value>est:</value></data>
-  <data name="ModSearchNoPrefix" xml:space="preserve"><value>pas:</value></data>
+  <data name="ModSearchNoPrefix" xml:space="preserve"><value>non:</value></data>
   <data name="ModSearchCompatibleSuffix" xml:space="preserve"><value>compatible</value></data>
   <data name="ModSearchInstalledSuffix" xml:space="preserve"><value>installé</value></data>
   <data name="ModSearchCachedSuffix" xml:space="preserve"><value>cache</value></data>
   <data name="ModSearchNewlyCompatibleSuffix" xml:space="preserve"><value>nouveau</value></data>
   <data name="ModSearchUpgradeableSuffix" xml:space="preserve"><value>MàJ</value></data>
-  <data name="ModSearchReplaceableSuffix" xml:space="preserve"><value>remplacable</value></data>
-
+  <data name="ModSearchReplaceableSuffix" xml:space="preserve"><value>remplaçable</value></data>
   <data name="EditModSearchTooltipExpandButton" xml:space="preserve"><value>Étendre ou réduire la taille du champ de recherche détaillé (Ctrl-Shift-F)</value></data>
   <data name="EditModSearchesTooltipAddSearchButton" xml:space="preserve"><value>Combiner une nouvelle recherche avec vos recherches actuelles</value></data>
   <data name="ManageModsInstallAllCheckboxTooltip" xml:space="preserve"><value>Décocher pour désinstaller tous les mods, cocher pour annuler les changements</value></data>
   <data name="TotalPlayTime" xml:space="preserve"><value>Votre Temps de Jeu Total: {0} Heures</value></data>
+  <data name="ChangeTypeNone" xml:space="preserve"><value>Aucun</value></data>
+  <data name="ChangeTypeInstall" xml:space="preserve"><value>Installation</value></data>
+  <data name="ChangeTypeRemove" xml:space="preserve"><value>Retrait</value></data>
+  <data name="ChangeTypeUpdate" xml:space="preserve"><value>Mise à jour</value></data>
+  <data name="ChangeTypeReplace" xml:space="preserve"><value>Remplacement</value></data>
 </root>
diff --git a/GUI/Properties/Resources.ja-JP.resx b/GUI/Properties/Resources.ja-JP.resx
index b7f5609495..f50c48fb46 100644
--- a/GUI/Properties/Resources.ja-JP.resx
+++ b/GUI/Properties/Resources.ja-JP.resx
@@ -198,7 +198,6 @@ Try to move {2} out of {3} and restart CKAN.</value></data>
   <data name="MainChangesetCached" xml:space="preserve"><value>{0} {1} (キャッシュ済)</value></data>
   <data name="MainChangesetHostSize" xml:space="preserve"><value>{0} {1} ({2}, {3})</value></data>
   <data name="MainChangesetUpdateSelected" xml:space="preserve"><value>選択されたものをバージョン{0}にアップデートする。</value></data>
-  <data name="MainChangesetNewInstall" xml:space="preserve"><value>New mod install selected by user.</value></data>
   <data name="MainImportTitle" xml:space="preserve"><value>Modインポート</value></data>
   <data name="MainImportFilter" xml:space="preserve"><value>Mod (*.zip)|*.zip</value></data>
   <data name="MainImportWaitTitle" xml:space="preserve"><value>ステータスログ</value></data>
diff --git a/GUI/Properties/Resources.pt-BR.resx b/GUI/Properties/Resources.pt-BR.resx
index 15d8b3cea8..fe979e9ed2 100644
--- a/GUI/Properties/Resources.pt-BR.resx
+++ b/GUI/Properties/Resources.pt-BR.resx
@@ -193,7 +193,6 @@ Você quer realmente instalá-la?</value></data>
   <data name="MainChangesetCached" xml:space="preserve"><value>{0} {1} (Em cache)</value></data>
   <data name="MainChangesetHostSize" xml:space="preserve"><value>{0} {1} ({2}, {3})</value></data>
   <data name="MainChangesetUpdateSelected" xml:space="preserve"><value>Atualização selecionada pelo usuário para a versão {0}.</value></data>
-  <data name="MainChangesetNewInstall" xml:space="preserve"><value>Nova instalação de mod selecionada pelo usuário.</value></data>
   <data name="MainImportTitle" xml:space="preserve"><value>Importar mods</value></data>
   <data name="MainImportFilter" xml:space="preserve"><value>Mods (*.zip)|*.zip</value></data>
   <data name="MainImportWaitTitle" xml:space="preserve"><value>Log de Status</value></data>
diff --git a/GUI/Properties/Resources.resx b/GUI/Properties/Resources.resx
index 553dafa016..3074340a50 100644
--- a/GUI/Properties/Resources.resx
+++ b/GUI/Properties/Resources.resx
@@ -218,7 +218,6 @@ Do you really want to install it?</value></data>
   <data name="MainChangesetCached" xml:space="preserve"><value>{0} {1} (cached)</value></data>
   <data name="MainChangesetHostSize" xml:space="preserve"><value>{0} {1} ({2}, {3})</value></data>
   <data name="MainChangesetUpdateSelected" xml:space="preserve"><value>Update selected by user to version {0}.</value></data>
-  <data name="MainChangesetNewInstall" xml:space="preserve"><value>New mod install selected by user.</value></data>
   <data name="MainImportTitle" xml:space="preserve"><value>Import Mods</value></data>
   <data name="MainImportFilter" xml:space="preserve"><value>Mods (*.zip)|*.zip</value></data>
   <data name="MainImportWaitTitle" xml:space="preserve"><value>Status log</value></data>
@@ -432,4 +431,9 @@ Do you want to allow CKAN to do this? If you click no you won't see this message
   <data name="EditModSearchesTooltipAddSearchButton" xml:space="preserve"><value>Combine a new search with your current searches</value></data>
   <data name="ManageModsInstallAllCheckboxTooltip" xml:space="preserve"><value>Uncheck to uninstall all mods, check to clear change set</value></data>
   <data name="TotalPlayTime" xml:space="preserve"><value>Total play time: {0} hours</value></data>
+  <data name="ChangeTypeNone" xml:space="preserve"><value>None</value></data>
+  <data name="ChangeTypeInstall" xml:space="preserve"><value>Install</value></data>
+  <data name="ChangeTypeRemove" xml:space="preserve"><value>Remove</value></data>
+  <data name="ChangeTypeUpdate" xml:space="preserve"><value>Update</value></data>
+  <data name="ChangeTypeReplace" xml:space="preserve"><value>Replace</value></data>
 </root>
diff --git a/GUI/Properties/Resources.ru-RU.resx b/GUI/Properties/Resources.ru-RU.resx
index d1b35b1c1b..370db3cc34 100644
--- a/GUI/Properties/Resources.ru-RU.resx
+++ b/GUI/Properties/Resources.ru-RU.resx
@@ -196,7 +196,6 @@
   <data name="MainChangesetCached" xml:space="preserve"><value>{0} {1} (загружено)</value></data>
   <data name="MainChangesetHostSize" xml:space="preserve"><value>{0} {1} ({2}, {3})</value></data>
   <data name="MainChangesetUpdateSelected" xml:space="preserve"><value>Обновить выбранное пользователем до версии {0}.</value></data>
-  <data name="MainChangesetNewInstall" xml:space="preserve"><value>Установка выбранной пользователем модификации.</value></data>
   <data name="MainImportTitle" xml:space="preserve"><value>Импортировать модификации</value></data>
   <data name="MainImportFilter" xml:space="preserve"><value>Модификации (*.zip)|*.zip</value></data>
   <data name="MainImportWaitTitle" xml:space="preserve"><value>Журнал</value></data>
@@ -226,7 +225,7 @@ https://github.com/KSP-CKAN/NetKAN/issues/new/choose
 Похоже, что модификация, которую вы пытаетесь установить, уже
 установлена или конфликтует с уже установленной модификацией.
 
-В качестве меры безопасности CKAN *никогда* не перезаписывает 
+В качестве меры безопасности CKAN *никогда* не перезаписывает
 или изменяет файлы, которые не установил сам.
 
 Если вы хотите установить {0} с помощью CKAN,
diff --git a/GUI/Properties/Resources.zh-CN.resx b/GUI/Properties/Resources.zh-CN.resx
index c25db31818..1116539100 100644
--- a/GUI/Properties/Resources.zh-CN.resx
+++ b/GUI/Properties/Resources.zh-CN.resx
@@ -192,7 +192,6 @@
   <data name="MainChangesetCached" xml:space="preserve"><value>{0} {1} (cached)</value></data>
   <data name="MainChangesetHostSize" xml:space="preserve"><value>{0} {1} ({2}, {3})</value></data>
   <data name="MainChangesetUpdateSelected" xml:space="preserve"><value>用户选择更新到 {0} 版本.</value></data>
-  <data name="MainChangesetNewInstall" xml:space="preserve"><value>用户选择安装的新Mod.</value></data>
   <data name="MainImportTitle" xml:space="preserve"><value>导入Mod</value></data>
   <data name="MainImportFilter" xml:space="preserve"><value>Mods (*.zip)|*.zip</value></data>
   <data name="MainImportWaitTitle" xml:space="preserve"><value>状态日志</value></data>
diff --git a/GUI/Properties/Settings.Designer.cs b/GUI/Properties/Settings.Designer.cs
index 4f225e45d2..cef4a7f7fc 100644
--- a/GUI/Properties/Settings.Designer.cs
+++ b/GUI/Properties/Settings.Designer.cs
@@ -8,7 +8,7 @@
 // </auto-generated>
 //------------------------------------------------------------------------------
 
-namespace CKAN.Properties {
+namespace CKAN.GUI.Properties {
     
     
     [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
diff --git a/GUI/SingleAssemblyComponentResourceManager.cs b/GUI/SingleAssemblyComponentResourceManager.cs
index 2c14ffffdf..26869fa715 100644
--- a/GUI/SingleAssemblyComponentResourceManager.cs
+++ b/GUI/SingleAssemblyComponentResourceManager.cs
@@ -6,7 +6,7 @@
 using System.Collections;
 using System.Collections.Generic;
 
-namespace CKAN
+namespace CKAN.GUI
 {
     // Thanks and credit to this guy: https://stackoverflow.com/q/1952638/2422988
 
diff --git a/GUI/SingleAssemblyResourceManager.cs b/GUI/SingleAssemblyResourceManager.cs
index 54cc5879d6..39dbd44f9c 100644
--- a/GUI/SingleAssemblyResourceManager.cs
+++ b/GUI/SingleAssemblyResourceManager.cs
@@ -5,7 +5,7 @@
 using System.Reflection;
 using System.Collections.Generic;
 
-namespace CKAN
+namespace CKAN.GUI
 {
     // Thanks and credit to this guy: https://stackoverflow.com/q/1952638/2422988
 
diff --git a/GUI/TabController.cs b/GUI/TabController.cs
index 789b09b484..0044a5f74e 100644
--- a/GUI/TabController.cs
+++ b/GUI/TabController.cs
@@ -3,7 +3,7 @@
 using System.Threading.Tasks;
 using System.Threading;
 
-namespace CKAN
+namespace CKAN.GUI
 {
 
     public class TabController
diff --git a/GUI/URLHandlers.cs b/GUI/URLHandlers.cs
index 1311125d11..f5ef13751b 100644
--- a/GUI/URLHandlers.cs
+++ b/GUI/URLHandlers.cs
@@ -8,7 +8,7 @@
 using IniParser.Model;
 using log4net;
 
-namespace CKAN
+namespace CKAN.GUI
 {
     public static class URLHandlers
     {
diff --git a/GUI/Util.cs b/GUI/Util.cs
index 2caf80f682..ad60d6b6bc 100644
--- a/GUI/Util.cs
+++ b/GUI/Util.cs
@@ -4,7 +4,7 @@
 using System.Drawing;
 using log4net;
 
-namespace CKAN
+namespace CKAN.GUI
 {
     using System.Runtime.InteropServices;
     using System.Windows.Forms;
diff --git a/GUI/X11.cs b/GUI/X11.cs
index d817735ac2..3af9a9d101 100644
--- a/GUI/X11.cs
+++ b/GUI/X11.cs
@@ -2,7 +2,7 @@
 using System.Reflection;
 using System.Runtime.InteropServices;
 
-namespace CKAN
+namespace CKAN.GUI
 {
     public struct XClassHint
     {
diff --git a/Tests/AutoUpdate/ResourcesTests.cs b/Tests/AutoUpdate/ResourcesTests.cs
new file mode 100644
index 0000000000..049c76a10c
--- /dev/null
+++ b/Tests/AutoUpdate/ResourcesTests.cs
@@ -0,0 +1,37 @@
+using System;
+using System.Linq;
+using System.Resources;
+using System.Globalization;
+using NUnit.Framework;
+
+namespace Tests.AutoUpdateHelper
+{
+    [TestFixture]
+    public class ResourcesTests
+    {
+        /// <summary>
+        /// .resx files should never have a $this.Language property because it
+        /// does not deserialize properly in Windows when serialized on Mono 6+
+        ///
+        /// This test covers the AutoUpdate/Properties/Resources.resx files.
+        /// </summary>
+        [Test]
+        public void PropertiesResources_LanguageResource_NotSet()
+        {
+            // Arrange
+            ResourceManager resources = new CKAN.AutoUpdateHelper.SingleAssemblyResourceManager(
+                "CKAN.AutoUpdateHelper.Properties.Resources", typeof(CKAN.AutoUpdateHelper.Properties.Resources).Assembly);
+
+            // Act/Assert
+            foreach (CultureInfo resourceCulture in cultures)
+            {
+                Assert.IsNull(resources.GetObject("$this.Language", resourceCulture));
+            }
+        }
+
+        // The cultures to test
+        private static CultureInfo[] cultures = CKAN.Utilities.AvailableLanguages
+            .Select(l => new CultureInfo(l))
+            .ToArray();
+    }
+}
diff --git a/Tests/CmdLine/ResourcesTests.cs b/Tests/CmdLine/ResourcesTests.cs
new file mode 100644
index 0000000000..83c6dad3dd
--- /dev/null
+++ b/Tests/CmdLine/ResourcesTests.cs
@@ -0,0 +1,37 @@
+using System;
+using System.Linq;
+using System.Resources;
+using System.Globalization;
+using NUnit.Framework;
+
+namespace Tests.CmdLine
+{
+    [TestFixture]
+    public class ResourcesTests
+    {
+        /// <summary>
+        /// .resx files should never have a $this.Language property because it
+        /// does not deserialize properly in Windows when serialized on Mono 6+
+        ///
+        /// This test covers the CmdLine/Properties/Resources.resx files.
+        /// </summary>
+        [Test]
+        public void PropertiesResources_LanguageResource_NotSet()
+        {
+            // Arrange
+            ResourceManager resources = new CKAN.CmdLine.SingleAssemblyResourceManager(
+                "CKAN.CmdLine.Properties.Resources", typeof(CKAN.CmdLine.Properties.Resources).Assembly);
+
+            // Act/Assert
+            foreach (CultureInfo resourceCulture in cultures)
+            {
+                Assert.IsNull(resources.GetObject("$this.Language", resourceCulture));
+            }
+        }
+
+        // The cultures to test
+        private static CultureInfo[] cultures = CKAN.Utilities.AvailableLanguages
+            .Select(l => new CultureInfo(l))
+            .ToArray();
+    }
+}
diff --git a/Tests/ConsoleUI/ResourcesTests.cs b/Tests/ConsoleUI/ResourcesTests.cs
new file mode 100644
index 0000000000..c943590414
--- /dev/null
+++ b/Tests/ConsoleUI/ResourcesTests.cs
@@ -0,0 +1,37 @@
+using System;
+using System.Linq;
+using System.Resources;
+using System.Globalization;
+using NUnit.Framework;
+
+namespace Tests.ConsoleUI
+{
+    [TestFixture]
+    public class ResourcesTests
+    {
+        /// <summary>
+        /// .resx files should never have a $this.Language property because it
+        /// does not deserialize properly in Windows when serialized on Mono 6+
+        ///
+        /// This test covers the ConsoleUI/Properties/Resources.resx files.
+        /// </summary>
+        [Test]
+        public void PropertiesResources_LanguageResource_NotSet()
+        {
+            // Arrange
+            ResourceManager resources = new CKAN.ConsoleUI.SingleAssemblyResourceManager(
+                "CKAN.ConsoleUI.Properties.Resources", typeof(CKAN.ConsoleUI.Properties.Resources).Assembly);
+
+            // Act/Assert
+            foreach (CultureInfo resourceCulture in cultures)
+            {
+                Assert.IsNull(resources.GetObject("$this.Language", resourceCulture));
+            }
+        }
+
+        // The cultures to test
+        private static CultureInfo[] cultures = CKAN.Utilities.AvailableLanguages
+            .Select(l => new CultureInfo(l))
+            .ToArray();
+    }
+}
diff --git a/Tests/Core/Net/AutoUpdateTests.cs b/Tests/Core/Net/AutoUpdateTests.cs
index c25455d839..eb3280ab76 100644
--- a/Tests/Core/Net/AutoUpdateTests.cs
+++ b/Tests/Core/Net/AutoUpdateTests.cs
@@ -22,7 +22,7 @@ public void FetchLatestReleaseInfo()
             // This is on by default in .NET 4.6, but not in 4.5.
             ServicePointManager.SecurityProtocol |= SecurityProtocolType.Tls12;
 
-            var updater = AutoUpdate.Instance;
+            var updater = CKAN.AutoUpdate.Instance;
 
             // Is is a *really* basic test to just make sure we get release info
             // if we ask for it.
diff --git a/Tests/Core/ResourcesTests.cs b/Tests/Core/ResourcesTests.cs
new file mode 100644
index 0000000000..611992344e
--- /dev/null
+++ b/Tests/Core/ResourcesTests.cs
@@ -0,0 +1,37 @@
+using System;
+using System.Linq;
+using System.Resources;
+using System.Globalization;
+using NUnit.Framework;
+
+namespace Tests.Core
+{
+    [TestFixture]
+    public class ResourcesTests
+    {
+        /// <summary>
+        /// .resx files should never have a $this.Language property because it
+        /// does not deserialize properly in Windows when serialized on Mono 6+
+        ///
+        /// This test covers the Core/Properties/Resources.resx files.
+        /// </summary>
+        [Test]
+        public void PropertiesResources_LanguageResource_NotSet()
+        {
+            // Arrange
+            ResourceManager resources = new CKAN.SingleAssemblyResourceManager(
+                "CKAN.Properties.Resources", typeof(CKAN.Properties.Resources).Assembly);
+
+            // Act/Assert
+            foreach (CultureInfo resourceCulture in cultures)
+            {
+                Assert.IsNull(resources.GetObject("$this.Language", resourceCulture));
+            }
+        }
+
+        // The cultures to test
+        private static CultureInfo[] cultures = CKAN.Utilities.AvailableLanguages
+            .Select(l => new CultureInfo(l))
+            .ToArray();
+    }
+}
diff --git a/Tests/GUI/GUIConfiguration.cs b/Tests/GUI/GUIConfiguration.cs
index b2d8acc260..a1623688cc 100644
--- a/Tests/GUI/GUIConfiguration.cs
+++ b/Tests/GUI/GUIConfiguration.cs
@@ -1,5 +1,6 @@
 using System.IO;
 using CKAN;
+using CKAN.GUI;
 using NUnit.Framework;
 using Tests.Data;
 
diff --git a/Tests/GUI/Model/GUIMod.cs b/Tests/GUI/Model/GUIMod.cs
index 25e85e7fd8..18fabac8f5 100644
--- a/Tests/GUI/Model/GUIMod.cs
+++ b/Tests/GUI/Model/GUIMod.cs
@@ -1,6 +1,7 @@
 using System;
 using System.Linq;
 using CKAN;
+using CKAN.GUI;
 using CKAN.Versioning;
 using NUnit.Framework;
 using Tests.Core;
diff --git a/Tests/GUI/Model/ModList.cs b/Tests/GUI/Model/ModList.cs
index cca875147e..bb1bc78643 100644
--- a/Tests/GUI/Model/ModList.cs
+++ b/Tests/GUI/Model/ModList.cs
@@ -10,6 +10,7 @@
 using Tests.Data;
 
 using CKAN;
+using CKAN.GUI;
 using CKAN.Versioning;
 
 namespace Tests.GUI
diff --git a/Tests/GUI/NavigationHistoryTests.cs b/Tests/GUI/NavigationHistoryTests.cs
index 0c0355f993..f42aa578f7 100644
--- a/Tests/GUI/NavigationHistoryTests.cs
+++ b/Tests/GUI/NavigationHistoryTests.cs
@@ -1,9 +1,10 @@
+using System;
+using CKAN;
+using CKAN.GUI;
+using NUnit.Framework;
+
 namespace Tests.GUI
 {
-    using System;
-    using CKAN;
-    using NUnit.Framework;
-
     [TestFixture]
     public class NavigationHistoryTests
     {
diff --git a/Tests/GUI/ResourcesTests.cs b/Tests/GUI/ResourcesTests.cs
index 7f514ba3ea..642def7907 100644
--- a/Tests/GUI/ResourcesTests.cs
+++ b/Tests/GUI/ResourcesTests.cs
@@ -1,5 +1,6 @@
 using System;
 using System.Linq;
+using System.Resources;
 using System.ComponentModel;
 using System.Globalization;
 using NUnit.Framework;
@@ -12,42 +13,64 @@ public class ResourcesTests
         /// <summary>
         /// .resx files should never have a $this.Language property because it
         /// does not deserialize properly in Windows when serialized on Mono 6+
+        ///
+        /// This test covers the GUI/Properties/Resources.resx files.
+        /// </summary>
+        [Test]
+        public void PropertiesResources_LanguageResource_NotSet()
+        {
+            // Arrange
+            ResourceManager resources = new CKAN.GUI.SingleAssemblyResourceManager(
+                "CKAN.GUI.Properties.Resources", typeof(CKAN.GUI.Properties.Resources).Assembly);
+
+            // Act/Assert
+            foreach (CultureInfo resourceCulture in cultures)
+            {
+                Assert.IsNull(resources.GetObject("$this.Language", resourceCulture));
+            }
+        }
+
+        /// <summary>
+        /// .resx files should never have a $this.Language property because it
+        /// does not deserialize properly in Windows when serialized on Mono 6+
+        ///
+        /// This test covers the controls and dialogs.
         /// </summary>
         [Test,
             // Controls
-            TestCase(typeof(CKAN.AllModVersions)),
-            TestCase(typeof(CKAN.Changeset)),
-            TestCase(typeof(CKAN.ChooseProvidedMods)),
-            TestCase(typeof(CKAN.ChooseRecommendedMods)),
-            TestCase(typeof(CKAN.DeleteDirectories)),
-            TestCase(typeof(CKAN.EditModpack)),
-            TestCase(typeof(CKAN.EditModSearch)),
-            TestCase(typeof(CKAN.HintTextBox)),
-            TestCase(typeof(CKAN.ManageMods)),
-            TestCase(typeof(CKAN.ModInfo)),
-            TestCase(typeof(CKAN.Wait)),
+            TestCase(typeof(CKAN.GUI.AllModVersions)),
+            TestCase(typeof(CKAN.GUI.Changeset)),
+            TestCase(typeof(CKAN.GUI.ChooseProvidedMods)),
+            TestCase(typeof(CKAN.GUI.ChooseRecommendedMods)),
+            TestCase(typeof(CKAN.GUI.DeleteDirectories)),
+            TestCase(typeof(CKAN.GUI.EditModpack)),
+            TestCase(typeof(CKAN.GUI.EditModSearch)),
+            TestCase(typeof(CKAN.GUI.HintTextBox)),
+            TestCase(typeof(CKAN.GUI.ManageMods)),
+            TestCase(typeof(CKAN.GUI.ModInfo)),
+            TestCase(typeof(CKAN.GUI.Wait)),
 
             // Dialogs
-            TestCase(typeof(CKAN.Main)),
-            TestCase(typeof(CKAN.AboutDialog)),
-            TestCase(typeof(CKAN.AskUserForAutoUpdatesDialog)),
-            TestCase(typeof(CKAN.CloneFakeGameDialog)),
-            TestCase(typeof(CKAN.CompatibleGameVersionsDialog)),
-            TestCase(typeof(CKAN.EditLabelsDialog)),
-            TestCase(typeof(CKAN.ErrorDialog)),
-            TestCase(typeof(CKAN.GameCommandLineOptionsDialog)),
-            TestCase(typeof(CKAN.ManageGameInstancesDialog)),
-            TestCase(typeof(CKAN.NewRepoDialog)),
-            TestCase(typeof(CKAN.NewUpdateDialog)),
-            TestCase(typeof(CKAN.PluginsDialog)),
-            TestCase(typeof(CKAN.RenameInstanceDialog)),
-            TestCase(typeof(CKAN.SelectionDialog)),
-            TestCase(typeof(CKAN.YesNoDialog)),
+            TestCase(typeof(CKAN.GUI.Main)),
+            TestCase(typeof(CKAN.GUI.AboutDialog)),
+            TestCase(typeof(CKAN.GUI.AskUserForAutoUpdatesDialog)),
+            TestCase(typeof(CKAN.GUI.CloneFakeGameDialog)),
+            TestCase(typeof(CKAN.GUI.CompatibleGameVersionsDialog)),
+            TestCase(typeof(CKAN.GUI.EditLabelsDialog)),
+            TestCase(typeof(CKAN.GUI.ErrorDialog)),
+            TestCase(typeof(CKAN.GUI.GameCommandLineOptionsDialog)),
+            TestCase(typeof(CKAN.GUI.ManageGameInstancesDialog)),
+            TestCase(typeof(CKAN.GUI.NewRepoDialog)),
+            TestCase(typeof(CKAN.GUI.NewUpdateDialog)),
+            TestCase(typeof(CKAN.GUI.PluginsDialog)),
+            TestCase(typeof(CKAN.GUI.RenameInstanceDialog)),
+            TestCase(typeof(CKAN.GUI.SelectionDialog)),
+            TestCase(typeof(CKAN.GUI.YesNoDialog)),
         ]
         public void ControlOrDialog_LanguageResource_NotSet(Type t)
         {
             // Arrange
-            ComponentResourceManager resources = new CKAN.SingleAssemblyComponentResourceManager(t);
+            ComponentResourceManager resources = new CKAN.GUI.SingleAssemblyComponentResourceManager(t);
 
             // Act/Assert
             foreach (CultureInfo resourceCulture in cultures)
diff --git a/Tests/Tests.csproj b/Tests/Tests.csproj
index 5e23ce140b..01a614d6bf 100644
--- a/Tests/Tests.csproj
+++ b/Tests/Tests.csproj
@@ -30,7 +30,7 @@
     </Otherwise>
   </Choose>
   <PropertyGroup Condition=" '$(TargetFramework)' == 'net5.0' ">
-    <DefaultItemExcludes>$(DefaultItemExcludes);NetKAN\**;GUI\**</DefaultItemExcludes>
+    <DefaultItemExcludes>$(DefaultItemExcludes);NetKAN\**;CmdLine\**;ConsoleUI\**;GUI\**;AutoUpdate\**</DefaultItemExcludes>
   </PropertyGroup>
   <PropertyGroup Condition=" '$(Configuration)' == 'Debug_NetCore' ">
     <DebugSymbols>true</DebugSymbols>
@@ -74,7 +74,10 @@
   <Choose>
     <When Condition=" '$(TargetFramework)' == 'net45' ">
       <ItemGroup>
+        <ProjectReference Include="..\Cmdline\CKAN-cmdline.csproj" />
+        <ProjectReference Include="..\ConsoleUI\CKAN-ConsoleUI.csproj" />
         <ProjectReference Include="..\GUI\CKAN-GUI.csproj" />
+        <ProjectReference Include="..\AutoUpdate\CKAN-autoupdate.csproj" />
         <ProjectReference Include="..\Netkan\CKAN-netkan.csproj" />
       </ItemGroup>
     </When>
diff --git a/build.cake b/build.cake
index cdbc1421d6..a7a1ce85c1 100644
--- a/build.cake
+++ b/build.cake
@@ -262,16 +262,16 @@ Task("Repack-Ckan")
     .IsDependentOn("Build-DotNet")
     .Does(() =>
 {
-    var cmdLineBinDirectory = outDirectory.Combine("CmdLine").Combine(configuration).Combine("bin");
+    var cmdLineBinDirectory = outDirectory.Combine("CKAN-CmdLine").Combine(configuration).Combine("bin").Combine(buildNetFramework);
     var assemblyPaths = GetFiles(string.Format("{0}/*.dll", cmdLineBinDirectory));
     assemblyPaths.Add(cmdLineBinDirectory.CombineWithFilePath("CKAN-GUI.exe"));
     assemblyPaths.Add(cmdLineBinDirectory.CombineWithFilePath("CKAN-ConsoleUI.exe"));
     assemblyPaths.Add(GetFiles(string.Format(
         "{0}/*/*.resources.dll",
-        outDirectory.Combine("CKAN-GUI").Combine(configuration).Combine("bin")
+        outDirectory.Combine("CKAN-CmdLine").Combine(configuration).Combine("bin").Combine(buildNetFramework)
     )));
 
-    ILRepack(ckanFile, cmdLineBinDirectory.CombineWithFilePath("CmdLine.exe"), assemblyPaths,
+    ILRepack(ckanFile, cmdLineBinDirectory.CombineWithFilePath("CKAN-CmdLine.exe"), assemblyPaths,
         new ILRepackSettings
         {
             Libs = new List<DirectoryPath> { cmdLineBinDirectory.ToString() },
diff --git a/doc/building.md b/doc/building.md
index d1c19d719e..5da51cc753 100644
--- a/doc/building.md
+++ b/doc/building.md
@@ -22,7 +22,7 @@ The following are the minimum requirements to build CKAN and NetKAN from source:
 ### Unix-like Systems
 - A POSIX compliant shell interpreter at `/bin/sh`
 - [Mono](http://www.mono-project.com/) >= 4.0.0
-- [cURL](https://curl.haxx.se/) accessible in PATH 
+- [cURL](https://curl.haxx.se/) accessible in PATH
 
 ### Windows Systems
 - PowerShell
@@ -32,7 +32,7 @@ The following are the minimum requirements to build CKAN and NetKAN from source:
 ## Build Directory
 
 A core philosophy of the build system is that builds should be "clean", that is it should be easy to restore the source
-directory to its original state after a build. As such **ALL** build artifacts should be output to a top-level 
+directory to its original state after a build. As such **ALL** build artifacts should be output to a top-level
 directory named `_build`. This directory is listed in `.gitignore` so that is not committed to source control. Removing
 this directory (`rm -r _build`) should return the source directory to its original state. Therefore any build artifacts
 that are output outside this directory should be considered a bug.
@@ -195,13 +195,13 @@ The basic operation of the actual build process is as follows:
     - These are simple tests designed to make sure there isn't anything grossly wrong with the build. All they do is
       execute the repacked `ckan.exe` and `netkan.exe` and make sure their version output matches the expected output.
 
-## Travis
+## GitHub Workflows
 
-Travis is the continuous integration (CI) environment that is used to automatically build CKAN and NetKAN. Travis's
-configuration is controlled by `.travis.yml` in the top-level directory. Upon every push Travis will execute the cross
-product of mono versions and build configurations. As there are currently three specified mono versions (`4.6.2`,
-`4.2.4`, and `4.0.5`) and two specified build configurations (`Debug`, `Release`), Travis will execute *six* builds per
-push. Travis's operation is pretty simple:
+GitHub Workflows is the continuous integration (CI) environment that is used to automatically build CKAN and NetKAN. Its
+configuration is controlled by `build.yml` in the `.github/workflows` directory. Upon every push GitHub will execute the cross
+product of mono versions and build configurations. As there are currently five specified mono versions (`5.20`,
+`6.4`, `6.6`, `6.8` and `latest`) and two specified build configurations (`Debug`, `Release`), GitHub will execute *six* builds per
+push. GitHub's operation is pretty simple:
 
 - A bunch of packages are installed to make the build work.
 - Some commands are executed to simulate a graphical environment for testing.
@@ -211,11 +211,11 @@ push. Travis's operation is pretty simple:
       thus behave nondeterministically (frustrating behavior for a CI system).
 - The built executables (`ckan.exe` and `netkan.exe`) are uploaded to Amazon S3 if the following conditions are met:
     - All previous steps were successful
-    - Its on the `master` branch
+    - It's on the `master` branch
     - The build configuraton is `Release`
-    - The mono version used is the latest (currently `4.6.2`)
+    - The mono version used is the latest
 - A GitHub release is generated if the following conditions are met:
     - All previous steps were successful
     - The commit is tagged
     - The build configuration is `Release`
-    - The mono version used is the latest (currently `4.6.2`)
\ No newline at end of file
+    - The mono version used is the latest