mirror of
https://github.com/MCCTeam/Minecraft-Console-Client
synced 2025-10-14 21:22:49 +00:00
Trim & Bug fix
This commit is contained in:
parent
f538b9e948
commit
2e18317f3f
9 changed files with 102 additions and 196 deletions
|
|
@ -3,6 +3,8 @@ using System.Collections.Generic;
|
||||||
using System.Drawing;
|
using System.Drawing;
|
||||||
using System.Globalization;
|
using System.Globalization;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
|
using System.Runtime.InteropServices;
|
||||||
|
using System.Runtime.Versioning;
|
||||||
using MinecraftClient.Mapping;
|
using MinecraftClient.Mapping;
|
||||||
using MinecraftClient.Protocol.Handlers;
|
using MinecraftClient.Protocol.Handlers;
|
||||||
using Tomlet.Attributes;
|
using Tomlet.Attributes;
|
||||||
|
|
@ -94,6 +96,8 @@ namespace MinecraftClient.ChatBots
|
||||||
if (!cachedMaps.ContainsKey(mapId))
|
if (!cachedMaps.ContainsKey(mapId))
|
||||||
return Translations.TryGet("bot.map.cmd.not_found", mapId);
|
return Translations.TryGet("bot.map.cmd.not_found", mapId);
|
||||||
|
|
||||||
|
if (OperatingSystem.IsWindows())
|
||||||
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
McMap map = cachedMaps[mapId];
|
McMap map = cachedMaps[mapId];
|
||||||
|
|
@ -104,6 +108,11 @@ namespace MinecraftClient.ChatBots
|
||||||
LogDebugToConsole(e.StackTrace!);
|
LogDebugToConsole(e.StackTrace!);
|
||||||
return Translations.TryGet("bot.map.failed_to_render", mapId);
|
return Translations.TryGet("bot.map.failed_to_render", mapId);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
LogToConsoleTranslated("bot.map.windows_only");
|
||||||
|
}
|
||||||
|
|
||||||
return "";
|
return "";
|
||||||
}
|
}
|
||||||
|
|
@ -152,9 +161,15 @@ namespace MinecraftClient.ChatBots
|
||||||
}
|
}
|
||||||
|
|
||||||
if (Config.Auto_Render_On_Update)
|
if (Config.Auto_Render_On_Update)
|
||||||
|
{
|
||||||
|
if (OperatingSystem.IsWindows())
|
||||||
GenerateMapImage(map);
|
GenerateMapImage(map);
|
||||||
|
else
|
||||||
|
LogToConsoleTranslated("bot.map.windows_only");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[SupportedOSPlatform("windows")]
|
||||||
private void GenerateMapImage(McMap map)
|
private void GenerateMapImage(McMap map)
|
||||||
{
|
{
|
||||||
string fileName = baseDirectory + "/Map_" + map.MapId + ".jpg";
|
string fileName = baseDirectory + "/Map_" + map.MapId + ".jpg";
|
||||||
|
|
@ -162,6 +177,8 @@ namespace MinecraftClient.ChatBots
|
||||||
if (File.Exists(fileName))
|
if (File.Exists(fileName))
|
||||||
File.Delete(fileName);
|
File.Delete(fileName);
|
||||||
|
|
||||||
|
/** Warning CA1416: Bitmap is only support Windows **/
|
||||||
|
/* https://learn.microsoft.com/en-us/dotnet/core/compatibility/core-libraries/6.0/system-drawing-common-windows-only */
|
||||||
Bitmap image = new(map.Width, map.Height);
|
Bitmap image = new(map.Width, map.Height);
|
||||||
|
|
||||||
for (int x = 0; x < map.Width; ++x)
|
for (int x = 0; x < map.Width; ++x)
|
||||||
|
|
@ -189,6 +206,8 @@ namespace MinecraftClient.ChatBots
|
||||||
image.Save(fileName);
|
image.Save(fileName);
|
||||||
LogToConsole(Translations.TryGet("bot.map.rendered", map.MapId, fileName));
|
LogToConsole(Translations.TryGet("bot.map.rendered", map.MapId, fileName));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[SupportedOSPlatform("windows")]
|
||||||
private Bitmap ResizeBitmap(Bitmap sourceBMP, int width, int height)
|
private Bitmap ResizeBitmap(Bitmap sourceBMP, int width, int height)
|
||||||
{
|
{
|
||||||
Bitmap result = new(width, height);
|
Bitmap result = new(width, height);
|
||||||
|
|
|
||||||
|
|
@ -64,10 +64,10 @@ namespace MinecraftClient
|
||||||
_ = "a".ToLower();
|
_ = "a".ToLower();
|
||||||
|
|
||||||
//Take advantage of Windows 10 / Mac / Linux UTF-8 console
|
//Take advantage of Windows 10 / Mac / Linux UTF-8 console
|
||||||
if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
|
if (OperatingSystem.IsWindows())
|
||||||
{
|
{
|
||||||
// If we're on windows, check if our version is Win10 or greater.
|
// If we're on windows, check if our version is Win10 or greater.
|
||||||
if (WindowsVersion.WinMajorVersion >= 10)
|
if (OperatingSystem.IsWindowsVersionAtLeast(10))
|
||||||
Console.OutputEncoding = Console.InputEncoding = Encoding.UTF8;
|
Console.OutputEncoding = Console.InputEncoding = Encoding.UTF8;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
|
@ -429,43 +429,13 @@ namespace MinecraftClient
|
||||||
|
|
||||||
if (result == ProtocolHandler.LoginResult.Success)
|
if (result == ProtocolHandler.LoginResult.Success)
|
||||||
{
|
{
|
||||||
if (Config.Main.General.AccountType == LoginType.microsoft && InternalConfig.Password != "-" && Config.Signature.LoginWithSecureProfile)
|
|
||||||
{
|
|
||||||
// Load cached profile key from disk if necessary
|
|
||||||
if (Config.Main.Advanced.ProfileKeyCache == CacheType.disk)
|
|
||||||
{
|
|
||||||
bool cacheKeyLoaded = KeysCache.InitializeDiskCache();
|
|
||||||
if (Config.Logging.DebugMessages)
|
|
||||||
Translations.WriteLineFormatted(cacheKeyLoaded ? "debug.keys_cache_ok" : "debug.keys_cache_fail");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (Config.Main.Advanced.ProfileKeyCache != CacheType.none && KeysCache.Contains(loginLower))
|
|
||||||
{
|
|
||||||
playerKeyPair = KeysCache.Get(loginLower);
|
|
||||||
if (playerKeyPair.NeedRefresh())
|
|
||||||
Translations.WriteLineFormatted("mcc.profile_key_invalid");
|
|
||||||
else
|
|
||||||
ConsoleIO.WriteLineFormatted(Translations.Get("mcc.profile_key_valid", session.PlayerName));
|
|
||||||
}
|
|
||||||
|
|
||||||
if (playerKeyPair == null || playerKeyPair.NeedRefresh())
|
|
||||||
{
|
|
||||||
Translations.WriteLineFormatted("mcc.fetching_key");
|
|
||||||
playerKeyPair = KeyUtils.GetNewProfileKeys(session.ID);
|
|
||||||
if (Config.Main.Advanced.ProfileKeyCache != CacheType.none && playerKeyPair != null)
|
|
||||||
{
|
|
||||||
KeysCache.Store(loginLower, playerKeyPair);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
InternalConfig.Username = session.PlayerName;
|
InternalConfig.Username = session.PlayerName;
|
||||||
bool isRealms = false;
|
bool isRealms = false;
|
||||||
|
|
||||||
if (Config.Main.Advanced.ConsoleTitle != "")
|
if (Config.Main.Advanced.ConsoleTitle != "")
|
||||||
Console.Title = Config.AppVar.ExpandVars(Config.Main.Advanced.ConsoleTitle);
|
Console.Title = Config.AppVar.ExpandVars(Config.Main.Advanced.ConsoleTitle);
|
||||||
|
|
||||||
if (Config.Main.Advanced.PlayerHeadAsIcon)
|
if (Config.Main.Advanced.PlayerHeadAsIcon && OperatingSystem.IsWindows())
|
||||||
ConsoleIcon.SetPlayerIconAsync(InternalConfig.Username);
|
ConsoleIcon.SetPlayerIconAsync(InternalConfig.Username);
|
||||||
|
|
||||||
if (Config.Logging.DebugMessages)
|
if (Config.Logging.DebugMessages)
|
||||||
|
|
@ -555,6 +525,37 @@ namespace MinecraftClient
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (Config.Main.General.AccountType == LoginType.microsoft && InternalConfig.Password != "-" &&
|
||||||
|
Config.Signature.LoginWithSecureProfile && protocolversion >= 759 /* 1.19 and above */)
|
||||||
|
{
|
||||||
|
// Load cached profile key from disk if necessary
|
||||||
|
if (Config.Main.Advanced.ProfileKeyCache == CacheType.disk)
|
||||||
|
{
|
||||||
|
bool cacheKeyLoaded = KeysCache.InitializeDiskCache();
|
||||||
|
if (Config.Logging.DebugMessages)
|
||||||
|
Translations.WriteLineFormatted(cacheKeyLoaded ? "debug.keys_cache_ok" : "debug.keys_cache_fail");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (Config.Main.Advanced.ProfileKeyCache != CacheType.none && KeysCache.Contains(loginLower))
|
||||||
|
{
|
||||||
|
playerKeyPair = KeysCache.Get(loginLower);
|
||||||
|
if (playerKeyPair.NeedRefresh())
|
||||||
|
Translations.WriteLineFormatted("mcc.profile_key_invalid");
|
||||||
|
else
|
||||||
|
ConsoleIO.WriteLineFormatted(Translations.Get("mcc.profile_key_valid", session.PlayerName));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (playerKeyPair == null || playerKeyPair.NeedRefresh())
|
||||||
|
{
|
||||||
|
Translations.WriteLineFormatted("mcc.fetching_key");
|
||||||
|
playerKeyPair = KeyUtils.GetNewProfileKeys(session.ID);
|
||||||
|
if (Config.Main.Advanced.ProfileKeyCache != CacheType.none && playerKeyPair != null)
|
||||||
|
{
|
||||||
|
KeysCache.Store(loginLower, playerKeyPair);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
//Force-enable Forge support?
|
//Force-enable Forge support?
|
||||||
if (!isRealms && (Config.Main.Advanced.EnableForge == ForgeConfigType.force) && forgeInfo == null)
|
if (!isRealms && (Config.Main.Advanced.EnableForge == ForgeConfigType.force) && forgeInfo == null)
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -584,6 +584,7 @@ bot.map.received_map=Received a new Map, with Id: {0}
|
||||||
bot.map.rendered=Succesfully rendered a map with id '{0}' to: '{1}'
|
bot.map.rendered=Succesfully rendered a map with id '{0}' to: '{1}'
|
||||||
bot.map.failed_to_render=Failed to render the map with id: '{0}'
|
bot.map.failed_to_render=Failed to render the map with id: '{0}'
|
||||||
bot.map.list_item=- Map id: {0} (Last Updated: {1})
|
bot.map.list_item=- Map id: {0} (Last Updated: {1})
|
||||||
|
bot.map.windows_only=Save to file is currently only available for the windows platform.
|
||||||
|
|
||||||
# ReplayCapture
|
# ReplayCapture
|
||||||
bot.replayCapture.cmd=replay command
|
bot.replayCapture.cmd=replay command
|
||||||
|
|
|
||||||
|
|
@ -584,6 +584,7 @@ bot.map.received_map=收到一个编号为 {0} 的新地图。
|
||||||
bot.map.rendered=成功接收到地图 '{0}' ,保存为 '{1}'
|
bot.map.rendered=成功接收到地图 '{0}' ,保存为 '{1}'
|
||||||
bot.map.failed_to_render=无法渲染编号为 '{0}' 的地图。
|
bot.map.failed_to_render=无法渲染编号为 '{0}' 的地图。
|
||||||
bot.map.list_item=- 地图编号:{0}(最近更新于:{1})
|
bot.map.list_item=- 地图编号:{0}(最近更新于:{1})
|
||||||
|
bot.map.windows_only=保存地图到文件功能目前仅可用于Windows平台。
|
||||||
|
|
||||||
# ReplayCapture
|
# ReplayCapture
|
||||||
bot.replayCapture.cmd=replay 命令
|
bot.replayCapture.cmd=replay 命令
|
||||||
|
|
|
||||||
|
|
@ -584,6 +584,7 @@ bot.map.received_map=收到一個編號為 {0} 的新地圖。
|
||||||
bot.map.rendered=成功接收到地圖 '{0}' ,儲存為 '{1}'
|
bot.map.rendered=成功接收到地圖 '{0}' ,儲存為 '{1}'
|
||||||
bot.map.failed_to_render=無法渲染編號為 '{0}' 的地圖。
|
bot.map.failed_to_render=無法渲染編號為 '{0}' 的地圖。
|
||||||
bot.map.list_item=- 地圖編號:{0}(最近更新於:{1})
|
bot.map.list_item=- 地圖編號:{0}(最近更新於:{1})
|
||||||
|
bot.map.windows_only=儲存地圖到檔案功能目前僅可用於Windows平臺。
|
||||||
|
|
||||||
# ReplayCapture
|
# ReplayCapture
|
||||||
bot.replayCapture.cmd=replay 命令
|
bot.replayCapture.cmd=replay 命令
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,5 @@
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Drawing.Drawing2D;
|
|
||||||
using System.Globalization;
|
using System.Globalization;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
|
|
@ -18,7 +17,6 @@ namespace MinecraftClient
|
||||||
{
|
{
|
||||||
private static readonly Dictionary<string, string> translations = new();
|
private static readonly Dictionary<string, string> translations = new();
|
||||||
private static readonly string translationFilePath = "lang" + Path.DirectorySeparatorChar + "mcc";
|
private static readonly string translationFilePath = "lang" + Path.DirectorySeparatorChar + "mcc";
|
||||||
private static readonly string defaultTranslation = "en.ini";
|
|
||||||
private static readonly Regex translationKeyRegex = new(@"\(\[(.*?)\]\)", RegexOptions.Compiled); // Extract string inside ([ ])
|
private static readonly Regex translationKeyRegex = new(@"\(\[(.*?)\]\)", RegexOptions.Compiled); // Extract string inside ([ ])
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
|
@ -645,18 +643,6 @@ namespace MinecraftClient
|
||||||
return translations;
|
return translations;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Write the default translation file (English) to the disk.
|
|
||||||
/// </summary>
|
|
||||||
private static void WriteDefaultTranslation()
|
|
||||||
{
|
|
||||||
string defaultPath = AppDomain.CurrentDomain.BaseDirectory + Path.DirectorySeparatorChar + translationFilePath + Path.DirectorySeparatorChar + defaultTranslation;
|
|
||||||
|
|
||||||
if (!Directory.Exists(translationFilePath))
|
|
||||||
Directory.CreateDirectory(translationFilePath);
|
|
||||||
File.WriteAllText(defaultPath, DefaultConfigResource.Translation_en, Encoding.UTF8);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void TrimAllTranslations()
|
public static void TrimAllTranslations()
|
||||||
{
|
{
|
||||||
string[] transEn = DefaultConfigResource.ResourceManager.GetString("Translation_en")!
|
string[] transEn = DefaultConfigResource.ResourceManager.GetString("Translation_en")!
|
||||||
|
|
|
||||||
|
|
@ -3,6 +3,7 @@ using System.Drawing;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Net.Http;
|
using System.Net.Http;
|
||||||
using System.Runtime.InteropServices;
|
using System.Runtime.InteropServices;
|
||||||
|
using System.Runtime.Versioning;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
|
@ -42,9 +43,8 @@ namespace MinecraftClient.WinAPI
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Asynchronously download the player's skin and set the head as console icon
|
/// Asynchronously download the player's skin and set the head as console icon
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
[SupportedOSPlatform("windows")]
|
||||||
public static void SetPlayerIconAsync(string playerName)
|
public static void SetPlayerIconAsync(string playerName)
|
||||||
{
|
|
||||||
if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
|
|
||||||
{
|
{
|
||||||
Thread t = new(new ThreadStart(delegate
|
Thread t = new(new ThreadStart(delegate
|
||||||
{
|
{
|
||||||
|
|
@ -92,7 +92,6 @@ namespace MinecraftClient.WinAPI
|
||||||
};
|
};
|
||||||
t.Start();
|
t.Start();
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Set the icon back to the default MCC icon
|
/// Set the icon back to the default MCC icon
|
||||||
|
|
|
||||||
|
|
@ -1,102 +0,0 @@
|
||||||
using System.Globalization;
|
|
||||||
using System.Runtime.InteropServices;
|
|
||||||
using Microsoft.Win32;
|
|
||||||
|
|
||||||
namespace MinecraftClient.WinAPI
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// Retrieve information about the current Windows version
|
|
||||||
/// </summary>
|
|
||||||
/// <remarks>
|
|
||||||
/// Environment.OSVersion does not work with Windows 10.
|
|
||||||
/// It returns 6.2 which is Windows 8
|
|
||||||
/// </remarks>
|
|
||||||
/// <seealso>
|
|
||||||
/// https://stackoverflow.com/a/37755503
|
|
||||||
/// </seealso>
|
|
||||||
class WindowsVersion
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// Returns the Windows major version number for this computer.
|
|
||||||
/// </summary>
|
|
||||||
public static uint WinMajorVersion
|
|
||||||
{
|
|
||||||
get
|
|
||||||
{
|
|
||||||
if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
|
|
||||||
{
|
|
||||||
// The 'CurrentMajorVersionNumber' string value in the CurrentVersion key is new for Windows 10,
|
|
||||||
// and will most likely (hopefully) be there for some time before MS decides to change this - again...
|
|
||||||
if (TryGetRegistryKey(@"SOFTWARE\Microsoft\Windows NT\CurrentVersion", "CurrentMajorVersionNumber", out dynamic? major))
|
|
||||||
return (uint)major;
|
|
||||||
|
|
||||||
// When the 'CurrentMajorVersionNumber' value is not present we fallback to reading the previous key used for this: 'CurrentVersion'
|
|
||||||
if (!TryGetRegistryKey(@"SOFTWARE\Microsoft\Windows NT\CurrentVersion", "CurrentVersion", out dynamic? version))
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
var versionParts = ((string)version!).Split('.');
|
|
||||||
if (versionParts.Length != 2) return 0;
|
|
||||||
return uint.TryParse(versionParts[0], NumberStyles.Any, CultureInfo.CurrentCulture, out uint majorAsUInt) ? majorAsUInt : 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Returns the Windows minor version number for this computer.
|
|
||||||
/// </summary>
|
|
||||||
public static uint WinMinorVersion
|
|
||||||
{
|
|
||||||
get
|
|
||||||
{
|
|
||||||
if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
|
|
||||||
{
|
|
||||||
// The 'CurrentMinorVersionNumber' string value in the CurrentVersion key is new for Windows 10,
|
|
||||||
// and will most likely (hopefully) be there for some time before MS decides to change this - again...
|
|
||||||
if (TryGetRegistryKey(@"SOFTWARE\Microsoft\Windows NT\CurrentVersion", "CurrentMinorVersionNumber", out dynamic? minor))
|
|
||||||
return (uint)minor;
|
|
||||||
|
|
||||||
// When the 'CurrentMinorVersionNumber' value is not present we fallback to reading the previous key used for this: 'CurrentVersion'
|
|
||||||
if (!TryGetRegistryKey(@"SOFTWARE\Microsoft\Windows NT\CurrentVersion", "CurrentVersion", out dynamic? version))
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
var versionParts = ((string)version!).Split('.');
|
|
||||||
if (versionParts.Length != 2) return 0;
|
|
||||||
return uint.TryParse(versionParts[1], NumberStyles.Any, CultureInfo.CurrentCulture, out uint minorAsUInt) ? minorAsUInt : 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Try retrieving a registry key
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="path">key path</param>
|
|
||||||
/// <param name="key">Key</param>
|
|
||||||
/// <param name="value">Value (output)</param>
|
|
||||||
/// <returns>TRUE if successfully retrieved</returns>
|
|
||||||
private static bool TryGetRegistryKey(string path, string key, out dynamic? value)
|
|
||||||
{
|
|
||||||
if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
|
|
||||||
{
|
|
||||||
value = null;
|
|
||||||
try
|
|
||||||
{
|
|
||||||
var rk = Registry.LocalMachine.OpenSubKey(path);
|
|
||||||
if (rk == null) return false;
|
|
||||||
value = rk.GetValue(key);
|
|
||||||
return value != null;
|
|
||||||
}
|
|
||||||
catch
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
value = null;
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -50,11 +50,11 @@ If you'd like to contribute to Minecraft Console Client, great, just fork the re
|
||||||
Check out: [How to update or add translations for MCC](https://mccteam.github.io/guide/contibuting.html#translations).
|
Check out: [How to update or add translations for MCC](https://mccteam.github.io/guide/contibuting.html#translations).
|
||||||
|
|
||||||
MCC now supports the following languages (Alphabetical order) :
|
MCC now supports the following languages (Alphabetical order) :
|
||||||
* `de.ini` (57.69% translated) : Deutsch - German
|
* `de.ini` (57.30% translated) : Deutsch - German
|
||||||
* `en.ini` : English - English
|
* `en.ini` : English - English
|
||||||
* `fr.ini` (57.69% translated) : Français (France) - French
|
* `fr.ini` (57.30% translated) : Français (France) - French
|
||||||
* `ru.ini` (56.77% translated) : Русский (Russkiy) - Russian
|
* `ru.ini` (56.36% translated) : Русский (Russkiy) - Russian
|
||||||
* `vi.ini` (56.77% translated) : Tiếng Việt (Việt Nam) - Vietnamese
|
* `vi.ini` (56.36% translated) : Tiếng Việt (Việt Nam) - Vietnamese
|
||||||
* `zh-Hans.ini` (100.00% translated) : 简体中文 - Chinese Simplified
|
* `zh-Hans.ini` (100.00% translated) : 简体中文 - Chinese Simplified
|
||||||
* `zh-Hant.ini` (100.00% translated) : 繁體中文 - Chinese Traditional
|
* `zh-Hant.ini` (100.00% translated) : 繁體中文 - Chinese Traditional
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue