Add TranslationChecker to check for incorrect format strings

This commit is contained in:
Duncan Ogilvie 2022-07-06 02:09:10 +02:00
parent 84297c2143
commit 0c0ff25cb0
10 changed files with 776 additions and 8 deletions

1
.gitignore vendored
View File

@ -2,5 +2,4 @@
*.qm *.qm
*.jar *.jar
*.zip *.zip
*.exe
translations/ translations/

BIN
TranslationChecker.exe Normal file

Binary file not shown.

10
TranslationChecker/.gitignore vendored Normal file
View File

@ -0,0 +1,10 @@
.vs/
bin/
obj/
Release/
Debug/
x64/
*.aps
*.vcxproj.user
packages/
*.user

View File

@ -0,0 +1,25 @@

Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 17
VisualStudioVersion = 17.2.32616.157
MinimumVisualStudioVersion = 10.0.40219.1
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TranslationChecker", "TranslationChecker\TranslationChecker.csproj", "{7D7EEC8B-34FF-43E7-AC2B-CB0A68334CFA}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Release|Any CPU = Release|Any CPU
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{7D7EEC8B-34FF-43E7-AC2B-CB0A68334CFA}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{7D7EEC8B-34FF-43E7-AC2B-CB0A68334CFA}.Debug|Any CPU.Build.0 = Debug|Any CPU
{7D7EEC8B-34FF-43E7-AC2B-CB0A68334CFA}.Release|Any CPU.ActiveCfg = Release|Any CPU
{7D7EEC8B-34FF-43E7-AC2B-CB0A68334CFA}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {ED6D654E-2A4F-46AD-9001-5E0E306691A6}
EndGlobalSection
EndGlobal

View File

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<startup>
<supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.7.2" />
</startup>
</configuration>

View File

@ -0,0 +1,337 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace TranslationChecker
{
static class LanguageCodes
{
public static string GetLanguageName(string code)
{
if (Values.ContainsKey(code))
return Values[code];
return $"Unknown, {code}";
}
// https://developer.crowdin.com/language-codes/
public static Dictionary<string, string> Values = new Dictionary<string, string>
{
{ "ach", "Acholi" },
{ "aa", "Afar" },
{ "af", "Afrikaans" },
{ "ak", "Akan" },
{ "tw", "Akan, Twi" },
{ "sq", "Albanian" },
{ "am", "Amharic" },
{ "ar", "Arabic" },
{ "ar-BH", "Arabic, Bahrain" },
{ "ar-EG", "Arabic, Egypt" },
{ "ar-SA", "Arabic, Saudi Arabia" },
{ "ar-YE", "Arabic, Yemen" },
{ "an", "Aragonese" },
{ "hy-AM", "Armenian" },
{ "frp", "Arpitan" },
{ "as", "Assamese" },
{ "ast", "Asturian" },
{ "tay", "Atayal" },
{ "av", "Avaric" },
{ "ae", "Avestan" },
{ "ay", "Aymara" },
{ "az", "Azerbaijani" },
{ "ban", "Balinese" },
{ "bal", "Balochi" },
{ "bm", "Bambara" },
{ "ba", "Bashkir" },
{ "eu", "Basque" },
{ "be", "Belarusian" },
{ "bn", "Bengali" },
{ "bn-IN", "Bengali, India" },
{ "ber", "Berber" },
{ "bh", "Bihari" },
{ "bfo", "Birifor" },
{ "bi", "Bislama" },
{ "bs", "Bosnian" },
{ "br-FR", "Breton" },
{ "bg", "Bulgarian" },
{ "my", "Burmese" },
{ "ca", "Catalan" },
{ "ceb", "Cebuano" },
{ "ch", "Chamorro" },
{ "ce", "Chechen" },
{ "chr", "Cherokee" },
{ "ny", "Chewa" },
{ "zh-CN", "Chinese Simplified" },
{ "zh-TW", "Chinese Traditional" },
{ "zh-HK", "Chinese Traditional, Hong Kong" },
{ "zh-MO", "Chinese Traditional, Macau" },
{ "zh-SG", "Chinese Traditional, Singapore" },
{ "cv", "Chuvash" },
{ "kw", "Cornish" },
{ "co", "Corsican" },
{ "cr", "Cree" },
{ "hr", "Croatian" },
{ "cs", "Czech" },
{ "da", "Danish" },
{ "fa-AF", "Dari" },
{ "dv", "Dhivehi" },
{ "nl", "Dutch" },
{ "nl-BE", "Dutch, Belgium" },
{ "nl-SR", "Dutch, Suriname" },
{ "dz", "Dzongkha" },
{ "en", "English" },
{ "en-UD", "English (upside down)" },
{ "en-AR", "English, Arabia" },
{ "en-AU", "English, Australia" },
{ "en-BZ", "English, Belize" },
{ "en-CA", "English, Canada" },
{ "en-CB", "English, Caribbean" },
{ "en-CN", "English, China" },
{ "en-DK", "English, Denmark" },
{ "en-HK", "English, Hong Kong" },
{ "en-IN", "English, India" },
{ "en-ID", "English, Indonesia" },
{ "en-IE", "English, Ireland" },
{ "en-JM", "English, Jamaica" },
{ "en-JA", "English, Japan" },
{ "en-MY", "English, Malaysia" },
{ "en-NZ", "English, New Zealand" },
{ "en-NO", "English, Norway" },
{ "en-PH", "English, Philippines" },
{ "en-PR", "English, Puerto Rico" },
{ "en-SG", "English, Singapore" },
{ "en-ZA", "English, South Africa" },
{ "en-SE", "English, Sweden" },
{ "en-GB", "English, United Kingdom" },
{ "en-US", "English, United States" },
{ "en-ZW", "English, Zimbabwe" },
{ "eo", "Esperanto" },
{ "et", "Estonian" },
{ "ee", "Ewe" },
{ "fo", "Faroese" },
{ "fj", "Fijian" },
{ "fil", "Filipino" },
{ "fi", "Finnish" },
{ "vls-BE", "Flemish" },
{ "fra-DE", "Franconian" },
{ "fr", "French" },
{ "fr-BE", "French, Belgium" },
{ "fr-CA", "French, Canada" },
{ "fr-LU", "French, Luxembourg" },
{ "fr-QC", "French, Quebec" },
{ "fr-CH", "French, Switzerland" },
{ "fy-NL", "Frisian" },
{ "fur-IT", "Friulian" },
{ "ff", "Fula" },
{ "gaa", "Ga" },
{ "gl", "Galician" },
{ "ka", "Georgian" },
{ "de", "German" },
{ "de-AT", "German, Austria" },
{ "de-BE", "German, Belgium" },
{ "de-LI", "German, Liechtenstein" },
{ "de-LU", "German, Luxembourg" },
{ "de-CH", "German, Switzerland" },
{ "got", "Gothic" },
{ "el", "Greek" },
{ "el-CY", "Greek, Cyprus" },
{ "kl", "Greenlandic" },
{ "gn", "Guarani" },
{ "gu-IN", "Gujarati" },
{ "ht", "Haitian Creole" },
{ "ha", "Hausa" },
{ "haw", "Hawaiian" },
{ "he", "Hebrew" },
{ "hz", "Herero" },
{ "hil", "Hiligaynon" },
{ "hi", "Hindi" },
{ "ho", "Hiri Motu" },
{ "hmn", "Hmong" },
{ "hu", "Hungarian" },
{ "is", "Icelandic" },
{ "ido", "Ido" },
{ "ig", "Igbo" },
{ "ilo", "Ilokano" },
{ "id", "Indonesian" },
{ "iu", "Inuktitut" },
{ "ga-IE", "Irish" },
{ "it", "Italian" },
{ "it-CH", "Italian, Switzerland" },
{ "ja", "Japanese" },
{ "jv", "Javanese" },
{ "quc", "K'iche'" },
{ "kab", "Kabyle" },
{ "kn", "Kannada" },
{ "pam", "Kapampangan" },
{ "ks", "Kashmiri" },
{ "ks-PK", "Kashmiri, Pakistan" },
{ "csb", "Kashubian" },
{ "kk", "Kazakh" },
{ "km", "Khmer" },
{ "rw", "Kinyarwanda" },
{ "tlh-AA", "Klingon" },
{ "kv", "Komi" },
{ "kg", "Kongo" },
{ "kok", "Konkani" },
{ "ko", "Korean" },
{ "ku", "Kurdish" },
{ "kmr", "Kurmanji (Kurdish)" },
{ "kj", "Kwanyama" },
{ "ky", "Kyrgyz" },
{ "lol", "LOLCAT" },
{ "lo", "Lao" },
{ "la-LA", "Latin" },
{ "lv", "Latvian" },
{ "lij", "Ligurian" },
{ "li", "Limburgish" },
{ "ln", "Lingala" },
{ "lt", "Lithuanian" },
{ "jbo", "Lojban" },
{ "nds", "Low German" },
{ "dsb-DE", "Lower Sorbian" },
{ "lg", "Luganda" },
{ "luy", "Luhya" },
{ "lb", "Luxembourgish" },
{ "mk", "Macedonian" },
{ "mai", "Maithili" },
{ "mg", "Malagasy" },
{ "ms", "Malay" },
{ "ms-BN", "Malay, Brunei" },
{ "ml-IN", "Malayalam" },
{ "mt", "Maltese" },
{ "gv", "Manx" },
{ "mi", "Maori" },
{ "arn", "Mapudungun" },
{ "mr", "Marathi" },
{ "mh", "Marshallese" },
{ "moh", "Mohawk" },
{ "mn", "Mongolian" },
{ "sr-Cyrl-ME", "Montenegrin (Cyrillic)" },
{ "me", "Montenegrin (Latin)" },
{ "mos", "Mossi" },
{ "na", "Nauru" },
{ "ng", "Ndonga" },
{ "ne-NP", "Nepali" },
{ "ne-IN", "Nepali, India" },
{ "pcm", "Nigerian Pidgin" },
{ "se", "Northern Sami" },
{ "nso", "Northern Sotho" },
{ "no", "Norwegian" },
{ "nb", "Norwegian Bokmal" },
{ "nn-NO", "Norwegian Nynorsk" },
{ "oc", "Occitan" },
{ "or", "Odia" },
{ "oj", "Ojibwe" },
{ "om", "Oromo" },
{ "os", "Ossetian" },
{ "pi", "Pali" },
{ "pap", "Papiamento" },
{ "ps", "Pashto" },
{ "fa", "Persian" },
{ "en-PT", "Pirate English" },
{ "pl", "Polish" },
{ "pt-PT", "Portuguese" },
{ "pt-BR", "Portuguese, Brazilian" },
{ "pa-IN", "Punjabi" },
{ "pa-PK", "Punjabi, Pakistan" },
{ "qu", "Quechua" },
{ "qya-AA", "Quenya" },
{ "ro", "Romanian" },
{ "rm-CH", "Romansh" },
{ "rn", "Rundi" },
{ "ru", "Russian" },
{ "ru-BY", "Russian, Belarus" },
{ "ru-MD", "Russian, Moldova" },
{ "ru-UA", "Russian, Ukraine" },
{ "ry-UA", "Rusyn" },
{ "sah", "Sakha" },
{ "sg", "Sango" },
{ "sa", "Sanskrit" },
{ "sat", "Santali" },
{ "sc", "Sardinian" },
{ "sco", "Scots" },
{ "gd", "Scottish Gaelic" },
{ "sr", "Serbian (Cyrillic)" },
{ "sr-CS", "Serbian (Latin)" },
{ "sh", "Serbo-Croatian" },
{ "crs", "Seychellois Creole" },
{ "sn", "Shona" },
{ "ii", "Sichuan Yi" },
{ "sd", "Sindhi" },
{ "si-LK", "Sinhala" },
{ "sk", "Slovak" },
{ "sl", "Slovenian" },
{ "so", "Somali" },
{ "son", "Songhay" },
{ "ckb", "Sorani (Kurdish)" },
{ "nr", "Southern Ndebele" },
{ "sma", "Southern Sami" },
{ "st", "Southern Sotho" },
{ "es-ES", "Spanish" },
{ "es-EM", "Spanish (Modern)" },
{ "es-AR", "Spanish, Argentina" },
{ "es-BO", "Spanish, Bolivia" },
{ "es-CL", "Spanish, Chile" },
{ "es-CO", "Spanish, Colombia" },
{ "es-CR", "Spanish, Costa Rica" },
{ "es-DO", "Spanish, Dominican Republic" },
{ "es-EC", "Spanish, Ecuador" },
{ "es-SV", "Spanish, El Salvador" },
{ "es-GT", "Spanish, Guatemala" },
{ "es-HN", "Spanish, Honduras" },
{ "es-419", "Spanish, Latin America" },
{ "es-MX", "Spanish, Mexico" },
{ "es-NI", "Spanish, Nicaragua" },
{ "es-PA", "Spanish, Panama" },
{ "es-PY", "Spanish, Paraguay" },
{ "es-PE", "Spanish, Peru" },
{ "es-PR", "Spanish, Puerto Rico" },
{ "es-US", "Spanish, United States" },
{ "es-UY", "Spanish, Uruguay" },
{ "es-VE", "Spanish, Venezuela" },
{ "su", "Sundanese" },
{ "sw", "Swahili" },
{ "sw-KE", "Swahili, Kenya" },
{ "sw-TZ", "Swahili, Tanzania" },
{ "ss", "Swati" },
{ "sv-SE", "Swedish" },
{ "sv-FI", "Swedish, Finland" },
{ "syc", "Syriac" },
{ "tl", "Tagalog" },
{ "ty", "Tahitian" },
{ "tg", "Tajik" },
{ "tzl", "Talossan" },
{ "ta", "Tamil" },
{ "tt-RU", "Tatar" },
{ "te", "Telugu" },
{ "kdh", "Tem (Kotokoli)" },
{ "th", "Thai" },
{ "bo-BT", "Tibetan" },
{ "ti", "Tigrinya" },
{ "ts", "Tsonga" },
{ "tn", "Tswana" },
{ "tr", "Turkish" },
{ "tr-CY", "Turkish, Cyprus" },
{ "tk", "Turkmen" },
{ "uk", "Ukrainian" },
{ "hsb-DE", "Upper Sorbian" },
{ "ur-IN", "Urdu (India)" },
{ "ur-PK", "Urdu (Pakistan)" },
{ "ug", "Uyghur" },
{ "uz", "Uzbek" },
{ "val-ES", "Valencian" },
{ "ve", "Venda" },
{ "vec", "Venetian" },
{ "vi", "Vietnamese" },
{ "wa", "Walloon" },
{ "cy", "Welsh" },
{ "wo", "Wolof" },
{ "xh", "Xhosa" },
{ "yi", "Yiddish" },
{ "yo", "Yoruba" },
{ "zea", "Zeelandic" },
{ "zu", "Zulu" },
};
}
}

View File

@ -0,0 +1,296 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;
using System.Threading.Tasks;
using System.Xml;
using System.Xml.Serialization;
namespace TranslationChecker
{
// https://json2csharp.com/code-converters/xml-to-csharp
[XmlRoot(ElementName = "translation")]
public class Translation
{
[XmlAttribute(AttributeName = "type")]
public string Type { get; set; }
[XmlElement(ElementName = "numerusform")]
public List<string> Numerusforms { get; set; }
[XmlText]
public string Text { get; set; }
}
[XmlRoot(ElementName = "location")]
public class Location
{
[XmlAttribute(AttributeName = "filename")]
public string Filename { get; set; }
[XmlAttribute(AttributeName = "line")]
public int Line { get; set; }
}
[XmlRoot(ElementName = "message")]
public class Message
{
[XmlAttribute(AttributeName = "numerus")]
public string Numerus { get; set; }
[XmlElement(ElementName = "location")]
public List<Location> Locations { get; set; }
[XmlElement(ElementName = "source")]
public string Source { get; set; }
[XmlElement(ElementName = "translation")]
public Translation Translation { get; set; }
}
[XmlRoot(ElementName = "context")]
public class Context
{
[XmlElement(ElementName = "name")]
public string Name { get; set; }
[XmlElement(ElementName = "message")]
public List<Message> Messages { get; set; }
}
[XmlRoot(ElementName = "TS")]
public class QtTranslations
{
[XmlElement(ElementName = "context")]
public List<Context> Contexts { get; set; }
[XmlAttribute(AttributeName = "version")]
public string Version { get; set; }
[XmlAttribute(AttributeName = "language")]
public string Language { get; set; }
[XmlAttribute(AttributeName = "sourcelanguage")]
public string Sourcelanguage { get; set; }
}
public class Utf8StringWriter : StringWriter
{
public override Encoding Encoding { get { return new UTF8Encoding(false); } }
}
internal class Program
{
/// <summary>
/// Checks if the file serializes back from deserialized form without any differences.
/// </summary>
static bool CheckSerialize(string tsFile)
{
var utf8 = new UTF8Encoding(false);
var xml = File.ReadAllText(tsFile, utf8);
XmlSerializer serializer = new XmlSerializer(typeof(QtTranslations));
using (var reader = new StringReader(xml))
{
var test = (QtTranslations)serializer.Deserialize(reader);
using (var writer = new Utf8StringWriter())
using (var xw = XmlWriter.Create(writer, new XmlWriterSettings
{
Indent = true,
}))
{
xw.WriteDocType("TS", null, null, null);
var ns = new XmlSerializerNamespaces();
ns.Add("", "");
serializer.Serialize(xw, test, ns);
var xml2 = writer.ToString().Replace(" />", "/>").Replace("<!DOCTYPE TS >", "<!DOCTYPE TS>").Replace("\r", "") + "\n";
if (xml2 != xml)
{
File.WriteAllText(tsFile + ".serialized", xml2, utf8);
Console.WriteLine($"Serialization error with file: {tsFile}");
return false;
}
}
}
return true;
}
// References:
// - https://en.cppreference.com/w/cpp/io/c/fprintf
// - https://docs.microsoft.com/en-us/cpp/c-runtime-library/format-specification-syntax-printf-and-wprintf-functions?view=msvc-170#flags
static Regex FormatSpecifierRegex = new Regex(@"(%[-+ #]*(\d+|\*)?(.\d+|.\*)?(hh|h|l|ll|j|z|t|L|I|I32|I64|w)?[csdioxXufFeEaAgGp]|%n|%%)");
static Regex QtArgumentRegex = new Regex(@"(%\d+)");
static bool CheckTranslation(string original, string translation)
{
// This checks for context menu keys, disable for now because there is a lot of noise
#if false
if (original.Count(c => c == '&') != translation.Count(c => c == '&'))
return false;
#endif
// Check if original has the same format strings as the translation
var originalMatches = FormatSpecifierRegex.Matches(original);
var translationMatches = FormatSpecifierRegex.Matches(translation);
if (originalMatches.Count != translationMatches.Count)
{
return false;
}
for (var i = 0; i < originalMatches.Count; i++)
{
var originalMatch = originalMatches[i];
var translationMatch = translationMatches[i];
if (originalMatch.ToString() != translationMatch.ToString())
return false;
}
if (originalMatches.Count == 0)
{
originalMatches = QtArgumentRegex.Matches(original);
translationMatches = QtArgumentRegex.Matches(translation);
string SortedArgs(MatchCollection matches)
{
var sorted = new List<string>();
for (var i = 0; i < matches.Count; i++)
sorted.Add(matches[i].ToString());
sorted.Sort();
return string.Join(" ", sorted);
}
return SortedArgs(originalMatches) == SortedArgs(translationMatches);
}
return true;
}
static string FixTranslation(string original, string translation)
{
// TODO: try to fix almost-correct format strings (like %hello -> %shello)
return original;
}
static int ErrorCount = 0;
static bool CheckFile(string tsFile, bool fix)
{
if (!CheckSerialize(tsFile))
{
return false;
}
var utf8 = new UTF8Encoding(false);
var xml = File.ReadAllText(tsFile, utf8);
var success = true;
void ReportError(Message message, string translation)
{
var location = message.Locations.First();
Console.WriteLine($" Format string error ({location.Filename}:{location.Line})\n Source:\n '{message.Source}'\n Translation:\n '{translation}'");
success = false;
ErrorCount++;
}
XmlSerializer serializer = new XmlSerializer(typeof(QtTranslations));
using (var reader = new StringReader(xml))
{
var ts = (QtTranslations)serializer.Deserialize(reader);
var url = $"https://crowdin.com/translate/x64dbg/1/en-{ts.Language.ToLower().Replace("-", "")}";
Console.WriteLine($"Checking {tsFile} ({LanguageCodes.GetLanguageName(ts.Language)}) => {url}");
foreach (var context in ts.Contexts)
{
foreach (var message in context.Messages)
{
var original = message.Source;
if (message.Translation.Type == "unfinished")
{
continue;
}
if (message.Numerus == "yes")
{
for (var i = 0; i < message.Translation.Numerusforms.Count; i++)
{
var translation = message.Translation.Numerusforms[i];
if (!CheckTranslation(original, translation))
{
ReportError(message, translation);
if (fix)
message.Translation.Numerusforms[i] = FixTranslation(original, translation);
}
}
}
else
{
var translation = message.Translation.Text;
if (!CheckTranslation(original, translation))
{
ReportError(message, translation);
if (fix)
message.Translation.Text = FixTranslation(original, translation);
}
}
}
}
if (!success && fix)
{
using (var writer = new Utf8StringWriter())
using (var xw = XmlWriter.Create(writer, new XmlWriterSettings
{
Indent = true,
}))
{
xw.WriteDocType("TS", null, null, null);
var ns = new XmlSerializerNamespaces();
ns.Add("", "");
serializer.Serialize(xw, ts, ns);
var xml2 = writer.ToString().Replace(" />", "/>").Replace("<!DOCTYPE TS >", "<!DOCTYPE TS>").Replace("\r", "") + "\n";
File.WriteAllText(tsFile, xml2, utf8);
}
}
}
return success;
}
static int Main(string[] args)
{
Console.OutputEncoding = Encoding.UTF8;
if (args.Length < 1)
{
Console.WriteLine("Usage: TranslationChecker x64dbg.ts [--fix]");
return 1;
}
var fix = false;
var folder = false;
for (var i = 1; i < args.Length; i++)
{
if (args[i] == "--fix")
fix = true;
else if (args[i] == "--folder")
folder = true;
}
var success = true;
if (folder)
{
foreach (var tsFile in Directory.EnumerateFiles(args[0], "*.ts", SearchOption.AllDirectories))
{
if (!CheckFile(tsFile, fix))
success = false;
}
}
else
{
success = CheckFile(args[0], fix);
}
Console.WriteLine($"\nTotal errors: {ErrorCount}");
return success ? 0 : 1;
}
}
}

View File

@ -0,0 +1,36 @@
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
// General Information about an assembly is controlled through the following
// set of attributes. Change these attribute values to modify the information
// associated with an assembly.
[assembly: AssemblyTitle("TranslationChecker")]
[assembly: AssemblyDescription("")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("")]
[assembly: AssemblyProduct("TranslationChecker")]
[assembly: AssemblyCopyright("Copyright © 2022")]
[assembly: AssemblyTrademark("")]
[assembly: AssemblyCulture("")]
// Setting ComVisible to false makes the types in this assembly not visible
// to COM components. If you need to access a type in this assembly from
// COM, set the ComVisible attribute to true on that type.
[assembly: ComVisible(false)]
// The following GUID is for the ID of the typelib if this project is exposed to COM
[assembly: Guid("7d7eec8b-34ff-43e7-ac2b-cb0a68334cfa")]
// Version information for an assembly consists of the following four values:
//
// Major Version
// Minor Version
// Build Number
// Revision
//
// You can specify all the values or you can default the Build and Revision Numbers
// by using the '*' as shown below:
// [assembly: AssemblyVersion("1.0.*")]
[assembly: AssemblyVersion("1.0.0.0")]
[assembly: AssemblyFileVersion("1.0.0.0")]

View File

@ -0,0 +1,54 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
<PropertyGroup>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
<ProjectGuid>{7D7EEC8B-34FF-43E7-AC2B-CB0A68334CFA}</ProjectGuid>
<OutputType>Exe</OutputType>
<RootNamespace>TranslationChecker</RootNamespace>
<AssemblyName>TranslationChecker</AssemblyName>
<TargetFrameworkVersion>v4.7.2</TargetFrameworkVersion>
<FileAlignment>512</FileAlignment>
<AutoGenerateBindingRedirects>true</AutoGenerateBindingRedirects>
<Deterministic>true</Deterministic>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
<PlatformTarget>AnyCPU</PlatformTarget>
<DebugSymbols>true</DebugSymbols>
<DebugType>full</DebugType>
<Optimize>false</Optimize>
<OutputPath>bin\Debug\</OutputPath>
<DefineConstants>DEBUG;TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
<PlatformTarget>AnyCPU</PlatformTarget>
<DebugType>pdbonly</DebugType>
<Optimize>true</Optimize>
<OutputPath>bin\Release\</OutputPath>
<DefineConstants>TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
</PropertyGroup>
<ItemGroup>
<Reference Include="System" />
<Reference Include="System.Core" />
<Reference Include="System.Xml.Linq" />
<Reference Include="System.Data.DataSetExtensions" />
<Reference Include="Microsoft.CSharp" />
<Reference Include="System.Data" />
<Reference Include="System.Net.Http" />
<Reference Include="System.Xml" />
</ItemGroup>
<ItemGroup>
<Compile Include="LanguageCodes.cs" />
<Compile Include="Program.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
</ItemGroup>
<ItemGroup>
<None Include="App.config" />
</ItemGroup>
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
</Project>

View File

@ -1,17 +1,22 @@
@echo off @echo off
if "%QT64PATH%"=="" set QT64PATH=c:\Qt\qt-5.6.2-x64-msvc2013\5.6\msvc2013_64\bin if "%QT64PATH%"=="" set QT64PATH=c:\Qt\qt-5.6.2-x64-msvc2013\5.6\msvc2013_64\bin
SET PATH=%PATH%;%QT64PATH%;c:\Program Files (x86)\7-Zip SET PATH=%PATH%;%QT64PATH%;c:\Program Files (x86)\7-Zip
del /S /Q *.qm del /S /Q *.qm >nul 2>&1
curl -k https://api.crowdin.com/api/project/x64dbg/export?key=%CROWDIN_API_KEY% curl -s -k https://api.crowdin.com/api/project/x64dbg/export?key=%CROWDIN_API_KEY%
curl -k -o translations.zip https://api.crowdin.com/api/project/x64dbg/download/all.zip?key=%CROWDIN_API_KEY% curl -s -k -o translations.zip https://api.crowdin.com/api/project/x64dbg/download/all.zip?key=%CROWDIN_API_KEY%
rmdir /S /Q translations rmdir /S /Q translations >nul 2>&1
7z x -otranslations translations.zip 7z x -otranslations translations.zip >nul 2>&1
TranslationChecker.exe translations --folder --fix
set CHECKER_ERRORLEVEL=%ERRORLEVEL%
cd translations cd translations
for /D %%a in (*) do (set fname=%%a) & call :rename for /D %%a in (*) do (set fname=%%a) & call :rename
move *.qm ..\ move *.qm ..\ >nul 2>&1
cd .. cd ..
exit /b %CHECKER_ERRORLEVEL%
goto :eof goto :eof
:rename :rename
set trname=x64dbg_%fname:-=_%.ts set trname=x64dbg_%fname:-=_%.ts
copy %fname%\x64dbg.ts %trname% copy %fname%\x64dbg.ts %trname% >nul 2>&1
lrelease -nounfinished %trname% lrelease -nounfinished %trname%