mirror of https://github.com/x64dbg/Translations
Add TranslationChecker to check for incorrect format strings
This commit is contained in:
parent
84297c2143
commit
0c0ff25cb0
|
@ -2,5 +2,4 @@
|
|||
*.qm
|
||||
*.jar
|
||||
*.zip
|
||||
*.exe
|
||||
translations/
|
||||
|
|
Binary file not shown.
|
@ -0,0 +1,10 @@
|
|||
.vs/
|
||||
bin/
|
||||
obj/
|
||||
Release/
|
||||
Debug/
|
||||
x64/
|
||||
*.aps
|
||||
*.vcxproj.user
|
||||
packages/
|
||||
*.user
|
|
@ -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
|
|
@ -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>
|
|
@ -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" },
|
||||
};
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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")]
|
|
@ -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>
|
|
@ -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%
|
||||
|
|
Loading…
Reference in New Issue