From 671c5ad9c75ee108220887182d3e5d9a9f4aca7d Mon Sep 17 00:00:00 2001 From: HarpyWar Date: Sat, 28 Jun 2014 21:46:30 +0400 Subject: [PATCH] utility that generates localization files by extracting localized strings from the code files (*.cpp, *.h, *.lua) https://github.com/HarpyWar/pvpgn/issues/13 --- scripts/localize/pvpgn_localize_generator.cs | 311 ++++++++++++++++++ scripts/localize/pvpgn_localize_generator.exe | Bin 0 -> 12288 bytes scripts/localize/update.bat | 15 + 3 files changed, 326 insertions(+) create mode 100644 scripts/localize/pvpgn_localize_generator.cs create mode 100644 scripts/localize/pvpgn_localize_generator.exe create mode 100644 scripts/localize/update.bat diff --git a/scripts/localize/pvpgn_localize_generator.cs b/scripts/localize/pvpgn_localize_generator.cs new file mode 100644 index 0000000..b83b54e --- /dev/null +++ b/scripts/localize/pvpgn_localize_generator.cs @@ -0,0 +1,311 @@ +/* +Copyright (c) 2014 HarpyWar (harpywar@gmail.com) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. +*/ + +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.IO; +using System.Text; +using System.Xml.Serialization; + +namespace pvpgn_localize_generator +{ + class Program + { + /// + /// C++ localization function name + /// + private const string func_name = "localize"; + private const string outfile = "output.xml"; + + static Root _data = new Root(); + + static void Main(string[] args) + { + if (args.Length != 1) + { + Console.WriteLine("This utility generates XML file for next translation from hardcoded text arguments in function {0}(...) that in *.cpp files\n(c) 2014 HarpyWar (harpywar@gmail.com)", func_name); + Console.WriteLine("\nUsage: {0} [path to 'src/bnetd']\n", AppDomain.CurrentDomain.FriendlyName); + + Environment.Exit(0); + } + var dirpath = args[0]; + + // process all files in directory + foreach(var f in Directory.GetFiles(dirpath)) + { + parse_file(f); + } + + // serialize data to xml + var ser = new XmlSerializer(typeof(Root)); + using (var fs = new FileStream(outfile, FileMode.Create)) + { + ser.Serialize(fs, _data); + } + + Console.WriteLine("\n{0} items saved in {1}: ", _data.Items.Count, outfile); + Console.WriteLine("\nPress any key to exit..."); + Console.ReadKey(); + } + + /// + /// Parse a single file + /// + /// + private static void parse_file(string filepath) + { + string[] lines = File.ReadAllLines(filepath); + string filename = Path.GetFileName(filepath); + + string text, f, function = string.Empty; + int i = 0; + foreach (string s in lines) + { + i++; + try + { + if ((f = is_function(s)) != null) + function = f; // remember last function + + if ((text = find_localize_text(s)) == null) + continue; + + _data.Items.Add(new Root.StringItem() + { + File = filename, + Function = function, + Original = text, + Translate = " " + }); + + Console.WriteLine("{0}, {1}(): {2}", filename, function, text); + } + catch(Exception e) + { + Console.WriteLine("Error on parse file \"{0}\" on line #{1}: {2}", filename, i, s); + Console.WriteLine(e.Message); + } + } + } + + /// + /// Return a text from the first string argument of the func_name + /// + /// + /// text or null + private static string find_localize_text(string line) + { + int func_pos, bracket1_pos, quote1_pos, quote2_pos; + func_pos = bracket1_pos = quote1_pos = quote2_pos = -1; + + string text = null; + + for (int i = 0; i < line.Length; i++) + { + if (func_pos >= 0) + { + if (bracket1_pos > 0) + { + if (quote1_pos > 0) + { + if (quote2_pos > 0) + { + text = line.Substring(quote1_pos, quote2_pos - quote1_pos); + break; + } + // 3) find last quote + if (line.Substring(i, 1) == "\"" && line.Substring(i-1, 1) != "\\") + quote2_pos = i; + continue; + } + // 3) find first quote + if (line.Substring(i, 1) == "\"") + quote1_pos = ++i; + continue; + } + // 2) find first bracket + if (line.Substring(i, 1) == "(") + bracket1_pos = i; + continue; + } + // 1) find function name + if (line.Substring(i, (i+func_name.Length > line.Length) ? line.Length-i : func_name.Length) == func_name) + func_pos = i; + } + return escape_text(text); + } + + /// + /// Filter text corresponding XML rules + /// + /// + /// text or null (if null passed) + private static string escape_text(string text) + { + if (text == null) + return null; + + text = text.Replace("\\\"", "\""); + text = text.Replace("<", "<"); + text = text.Replace(">", ">"); + return text; + } + + + /// + /// Is word a function? + /// + /// + /// string or null + private static string is_function(string line) + { + int j; + line = line.Trim(); + if (line.Length == 0) + return null; + + // last line must have ) or { + if (line[line.Length - 1] == ')' || line[line.Length - 1] == '{') + { + string[] words = line.Split(); + if (words.Length > 0) + { + bool bad = false; + // exclude small words and reserved words that can not be at the beginning of the function definition + foreach (string r in reserved_words) + if (r.Length > words[0].Length) + continue; + else if (words[0].Substring(0, r.Length) == r) + bad = true; + + if (!bad) + { + // find function name in words + for (int i = 0; i < words.Length; i++) + { + if (words[i].Trim() == string.Empty) + continue; + + if (words[i][0] == '(') + return words[i - 1]; + else if ((j = words[i].IndexOf("(", 0)) != -1) + return words[i].Substring(0, j); + } + + } + } + } + return null; + } + + static string[] reserved_words = new string[] { + + "while", + "switch", + "class", + "new", + "goto", + "for", + "sizeof", + "struct", + "throw", + "try", + "catch", + "typedef", + "enum", + "if", "else", + "{","}", + "(",")", + "<<",">>", + "||","|", + "&&","&", + "//", "/", "*", // comments + "!", + }; + } + +#region Serializer Class + + [XmlRoot("root")] + public class Root + { + public Root() + { + Items = new List(); + } + + [XmlElement("meta")] + public Meta meta = new Meta(); + + [XmlArray("items"), XmlArrayItem(typeof(StringItem), ElementName = "item")] + public List Items { get; set; } + + public class Meta + { + public Meta() + { + language = new LanguageItem(); + Authors = new List() { + new AuthorItem() + }; + } + + [XmlElement("language"), DefaultValue("change_me")] + public LanguageItem language { get; set; } + + [XmlArray("authors"), XmlArrayItem(typeof(AuthorItem), ElementName = "author")] + public List Authors { get; set; } + + public class LanguageItem + { + [XmlAttribute("tag")] + public string Tag = "enUS"; + [XmlText] + public string Default = "English"; + } + public class AuthorItem + { + [XmlAttribute("name")] + public string Name = "nomad"; + [XmlAttribute("email")] + public string Email="nomad@example.com"; + } + } + + + public class StringItem + { + [XmlAttribute("file")] + public string File; + [XmlAttribute("function")] + public string Function; + [XmlElement("original")] + public string Original; + [XmlElement("translate")] + public string Translate; + } + +#endregion + + } +} diff --git a/scripts/localize/pvpgn_localize_generator.exe b/scripts/localize/pvpgn_localize_generator.exe new file mode 100644 index 0000000000000000000000000000000000000000..688e347543273b0b5ead2a06813a9aeaba56feea GIT binary patch literal 12288 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 Xv;GUuUj2Vg?!tdo`~T