diff --git a/.gitignore b/.gitignore index 7f53c4f..8168521 100644 --- a/.gitignore +++ b/.gitignore @@ -2,5 +2,4 @@ *.qm *.jar *.zip -*.exe translations/ diff --git a/TranslationChecker.exe b/TranslationChecker.exe new file mode 100644 index 0000000..3bb2231 Binary files /dev/null and b/TranslationChecker.exe differ diff --git a/TranslationChecker/.gitignore b/TranslationChecker/.gitignore new file mode 100644 index 0000000..2e6ffad --- /dev/null +++ b/TranslationChecker/.gitignore @@ -0,0 +1,10 @@ +.vs/ +bin/ +obj/ +Release/ +Debug/ +x64/ +*.aps +*.vcxproj.user +packages/ +*.user diff --git a/TranslationChecker/TranslationChecker.sln b/TranslationChecker/TranslationChecker.sln new file mode 100644 index 0000000..0161a02 --- /dev/null +++ b/TranslationChecker/TranslationChecker.sln @@ -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 diff --git a/TranslationChecker/TranslationChecker/App.config b/TranslationChecker/TranslationChecker/App.config new file mode 100644 index 0000000..5754728 --- /dev/null +++ b/TranslationChecker/TranslationChecker/App.config @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/TranslationChecker/TranslationChecker/LanguageCodes.cs b/TranslationChecker/TranslationChecker/LanguageCodes.cs new file mode 100644 index 0000000..7f08f9b --- /dev/null +++ b/TranslationChecker/TranslationChecker/LanguageCodes.cs @@ -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 Values = new Dictionary + { + { "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" }, + }; + } +} diff --git a/TranslationChecker/TranslationChecker/Program.cs b/TranslationChecker/TranslationChecker/Program.cs new file mode 100644 index 0000000..c7930aa --- /dev/null +++ b/TranslationChecker/TranslationChecker/Program.cs @@ -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 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 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 Messages { get; set; } + } + + [XmlRoot(ElementName = "TS")] + public class QtTranslations + { + [XmlElement(ElementName = "context")] + public List 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 + { + /// + /// Checks if the file serializes back from deserialized form without any differences. + /// + 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("", "").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(); + 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("", "").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; + } + } +} diff --git a/TranslationChecker/TranslationChecker/Properties/AssemblyInfo.cs b/TranslationChecker/TranslationChecker/Properties/AssemblyInfo.cs new file mode 100644 index 0000000..e5d9432 --- /dev/null +++ b/TranslationChecker/TranslationChecker/Properties/AssemblyInfo.cs @@ -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")] diff --git a/TranslationChecker/TranslationChecker/TranslationChecker.csproj b/TranslationChecker/TranslationChecker/TranslationChecker.csproj new file mode 100644 index 0000000..37a031d --- /dev/null +++ b/TranslationChecker/TranslationChecker/TranslationChecker.csproj @@ -0,0 +1,54 @@ + + + + + Debug + AnyCPU + {7D7EEC8B-34FF-43E7-AC2B-CB0A68334CFA} + Exe + TranslationChecker + TranslationChecker + v4.7.2 + 512 + true + true + + + AnyCPU + true + full + false + bin\Debug\ + DEBUG;TRACE + prompt + 4 + + + AnyCPU + pdbonly + true + bin\Release\ + TRACE + prompt + 4 + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/crowdin-sync.bat b/crowdin-sync.bat index 6cf740c..87c9198 100644 --- a/crowdin-sync.bat +++ b/crowdin-sync.bat @@ -1,17 +1,22 @@ @echo off 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 -del /S /Q *.qm -curl -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% -rmdir /S /Q translations -7z x -otranslations translations.zip +del /S /Q *.qm >nul 2>&1 +curl -s -k https://api.crowdin.com/api/project/x64dbg/export?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 >nul 2>&1 +7z x -otranslations translations.zip >nul 2>&1 +TranslationChecker.exe translations --folder --fix +set CHECKER_ERRORLEVEL=%ERRORLEVEL% cd translations for /D %%a in (*) do (set fname=%%a) & call :rename -move *.qm ..\ +move *.qm ..\ >nul 2>&1 cd .. +exit /b %CHECKER_ERRORLEVEL% + goto :eof + :rename set trname=x64dbg_%fname:-=_%.ts -copy %fname%\x64dbg.ts %trname% +copy %fname%\x64dbg.ts %trname% >nul 2>&1 lrelease -nounfinished %trname%