/****************************************************************************** PO-MERGE - A simple po merge/update tool Copyright (C) 2006 - Dongsheng Song This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA Description: This program will replace the translation in old.po file with update.po file, write the result to new.po file. Only msgstr in old.po file be modified, msgid become unwound. Strings(msgstr) that are not found in the update.po are left untouched. The two input file(update.po & old.po) must use UTF-8 enconding, the output file(new.po) enconding is UTF-8 too, without any byte-order-mark (BOM). Use: po-merge update.po old.po new.po ******************************************************************************/ using System; using System.Collections; using System.IO; using System.Text; class Program { private static void Usage() { Console.WriteLine("Usage:\n\tpo-merge update.po old.po new.po\n" + "\n" + "This program will replace the translation in old.po file with " + "update.po file, write the result to new.po file.\n\n" + "Only msgstr in old.po file be modified, all msgid/msgstr become unwound. " + "Strings(msgstr) that are not found in the update.po are left untouched.\n" ); } public static void Main(String[] args) { if (args.Length != 3) { Usage(); return; } Hashtable msg = ParseTranslation(args[0]); UpdateTranslation(args[1], args[2], msg); } /* Read translation entry from the file and return a Hashtable with the msgid and msgstr.The msgid and msgstr are strings. */ private static Hashtable ParseTranslation(String filename) { Hashtable msg = new Hashtable(2047); StreamReader sr = new StreamReader(filename, Encoding.UTF8); int n = 0; bool isFinished = false; String line, msgid, msgstr; while (true) { while (true) { n++; line = sr.ReadLine(); if (line == null) { isFinished = true; break; } // May be non-empty msgid ? line = line.Trim(); if (line.Length >= 8 && line[0] != '#') break; } if (isFinished) break; if (!line.StartsWith("msgid \"") || (line[line.Length - 1] != '\"')) { Console.Error.WriteLine("parse error before msgid, file {0}, line {1:d}:\n\t", filename, n); throw new Exception("parse error"); } // Get msgid if (line.Length <= 8) { msgid = ""; } else { msgid = line.Substring(7, line.Length - 8); } while (true) { n++; line = sr.ReadLine(); if (line == null) { Console.Error.WriteLine("parse msgid error, file {0}, line {1:d}:\n\t", filename, n); throw new Exception("parse error"); } line = line.Trim(); if (line.Length == 0 || line[0] != '"') break; msgid += line.Substring(1, line.Length - 2); } if (!line.StartsWith("msgstr \"") || (line[line.Length - 1] != '\"')) { Console.Error.WriteLine("parse error after msgid, file {0}, line {1:d}:\n\t", filename, n); throw new Exception("parse error"); } // Get msgstr if (line.Length <= 9) { msgstr = ""; } else { msgstr = line.Substring(8, line.Length - 9); } while (true) { n++; line = sr.ReadLine(); if (line == null) break; line = line.Trim(); if (line.Length == 0 || line[0] != '"') break; if (msgid.Length == 0) msgstr += "\"\n\""; msgstr += line.Substring(1, line.Length - 2); } msg[msgid] = msgstr; // Console.WriteLine("msgid: [{0}]\nmsgstr: [{1}]\n", msgid, msgstr); if (line == null) break; if (line.Length != 0 && line[0] != '#') { Console.Error.WriteLine("parse error after msgstr, file {0}, line {1:d}:\n\t", filename, n); throw new Exception("parse error"); } } sr.Close(); return msg; } /* Read translation entry from the file and update the msgid and msgstr, write to new file. */ private static void UpdateTranslation(String filename, String newfilename, Hashtable msg) { StreamReader sr = new StreamReader(filename, Encoding.UTF8); StreamWriter sw = new StreamWriter(newfilename, false); int n = 0, total = 0, tr = 0, ut = 0, mt = 0; bool isFinished = false; String line, msgid, msgstr, str = ""; while (true) { while (true) { n++; line = sr.ReadLine(); if (line == null) { isFinished = true; break; } str = line.Trim(); // May be non-empty msgid ? if (str.Length >= 8 && str[0] != '#') break; if (total == 0 && str.Equals("#, fuzzy")) line = ""; // Remove PO file header fuzzy sw.WriteLine(line); } if (isFinished) break; if (!str.StartsWith("msgid \"") || (str[str.Length - 1] != '\"')) { Console.Error.WriteLine("parse error before msgid, file {0}, line {1:d}:\n\t", filename, n); throw new Exception("parse error"); } // Get msgid if (str.Length <= 8) { msgid = ""; } else { msgid = str.Substring(7, str.Length - 8); } while (true) { n++; line = sr.ReadLine(); if (line == null) { Console.Error.WriteLine("parse msgid error, file {0}, line {1:d}:\n\t", filename, n); throw new Exception("parse error"); } str = line.Trim(); if (str.Length == 0 || line[0] != '"') break; msgid += str.Substring(1, str.Length - 2); } if (!str.StartsWith("msgstr \"") || (str[str.Length - 1] != '\"')) { Console.Error.WriteLine("parse error after msgid, file {0}, line {1:d}:\n\t", filename, n); throw new Exception("parse error"); } // Get msgstr if (str.Length <= 9) { msgstr = ""; } else { msgstr = str.Substring(8, str.Length - 9); } while (true) { n++; line = sr.ReadLine(); if (line == null) break; str = line.Trim(); if (str.Length == 0 || str[0] != '"') break; if (msgid.Length == 0) msgstr += "\"\n\""; msgstr += str.Substring(1, str.Length - 2); } if (msgid.Length > 0) total++; str = (String) msg[msgid]; // Console.WriteLine("msgid: [{0}]\nmsgstr: [{1}]\nupdate: [{2}]\n", msgid, msgstr, str); if (msgid.Length <= 0) { sw.WriteLine("msgid \"{0}\"", msgid); sw.WriteLine("msgstr \"{0}\"\n", msgstr); } else if (str != null && str.Length > 0) { if (msgid.Length != 0) { if (! msgstr.Equals(str)) mt ++; tr ++; } sw.WriteLine("msgid \"{0}\"", msgid); sw.WriteLine("msgstr \"{0}\"\n", str); } else { if (msgstr.Length == 0) { ut ++; } else { tr++; } sw.WriteLine("msgid \"{0}\"", msgid); sw.WriteLine("msgstr \"{0}\"\n", msgstr); } if (line == null) { break; } if (line.Length != 0 && line[0] != '#') { Console.Error.WriteLine("parse error after msgstr, file {0}, line {1:d}:\n\t", filename, n); throw new Exception("parse error"); } } Console.Error.WriteLine("Total {0} messages, {1} translated, {2} updated, {3} untranslated.", total, tr, mt, ut); sr.Close(); sw.Close(); return; } }