Skip to content

Commit

Permalink
utility that generates localization files by extracting localized str…
Browse files Browse the repository at this point in the history
…ings from the code files (*.cpp, *.h, *.lua) #13
  • Loading branch information
HarpyWar committed Jun 28, 2014
1 parent f40ac4b commit 671c5ad
Show file tree
Hide file tree
Showing 3 changed files with 326 additions and 0 deletions.
311 changes: 311 additions & 0 deletions scripts/localize/pvpgn_localize_generator.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,311 @@
/*
Copyright (c) 2014 HarpyWar ([email protected])
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
{
/// <summary>
/// C++ localization function name
/// </summary>
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 ([email protected])", 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();
}

/// <summary>
/// Parse a single file
/// </summary>
/// <param name="filepath"></param>
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);
}
}
}

/// <summary>
/// Return a text from the first string argument of the func_name
/// </summary>
/// <param name="line"></param>
/// <returns>text or null</returns>
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);
}

/// <summary>
/// Filter text corresponding XML rules
/// </summary>
/// <param name="text"></param>
/// <returns>text or null (if null passed)</returns>
private static string escape_text(string text)
{
if (text == null)
return null;

text = text.Replace("\\\"", "\"");
text = text.Replace("<", "&lt;");
text = text.Replace(">", "&gt;");
return text;
}


/// <summary>
/// Is word a function?
/// </summary>
/// <param name="line"></param>
/// <returns>string or null</returns>
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<StringItem>();
}

[XmlElement("meta")]
public Meta meta = new Meta();

[XmlArray("items"), XmlArrayItem(typeof(StringItem), ElementName = "item")]
public List<StringItem> Items { get; set; }

public class Meta
{
public Meta()
{
language = new LanguageItem();
Authors = new List<AuthorItem>() {
new AuthorItem()
};
}

[XmlElement("language"), DefaultValue("change_me")]
public LanguageItem language { get; set; }

[XmlArray("authors"), XmlArrayItem(typeof(AuthorItem), ElementName = "author")]
public List<AuthorItem> 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="[email protected]";
}
}


public class StringItem
{
[XmlAttribute("file")]
public string File;
[XmlAttribute("function")]
public string Function;
[XmlElement("original")]
public string Original;
[XmlElement("translate")]
public string Translate;
}

#endregion

}
}
Binary file added scripts/localize/pvpgn_localize_generator.exe
Binary file not shown.
15 changes: 15 additions & 0 deletions scripts/localize/update.bat
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
@echo off

pvpgn_localize_generator.exe ..\..\ ..\..\conf\i18n\common-deDE.xml
pvpgn_localize_generator.exe ..\..\ ..\..\conf\i18n\common-csCZ.xml
pvpgn_localize_generator.exe ..\..\ ..\..\conf\i18n\common-esES.xml
pvpgn_localize_generator.exe ..\..\ ..\..\conf\i18n\common-frFR.xml
pvpgn_localize_generator.exe ..\..\ ..\..\conf\i18n\common-itIT.xml
pvpgn_localize_generator.exe ..\..\ ..\..\conf\i18n\common-jaJA.xml
pvpgn_localize_generator.exe ..\..\ ..\..\conf\i18n\common-koKR.xml
pvpgn_localize_generator.exe ..\..\ ..\..\conf\i18n\common-plPL.xml
pvpgn_localize_generator.exe ..\..\ ..\..\conf\i18n\common-ruRU.xml
pvpgn_localize_generator.exe ..\..\ ..\..\conf\i18n\common-zhCN.xml
pvpgn_localize_generator.exe ..\..\ ..\..\conf\i18n\common-zhTW.xml

pause

0 comments on commit 671c5ad

Please sign in to comment.