1 /******************************************************************************
2 PO-MERGE - A simple po merge/update tool
4 Copyright (C) 2006 - Dongsheng Song <dongsheng.song@gmail.com>
6 This program is free software; you can redistribute it and/or
7 modify it under the terms of the GNU General Public License
8 as published by the Free Software Foundation; either version 2
9 of the License, or (at your option) any later version.
11 This program is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 GNU General Public License for more details.
16 You should have received a copy of the GNU General Public License
17 along with this program; if not, write to the Free Software Foundation, Inc.,
18 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA
21 This program will replace the translation in old.po file with update.po file,
22 write the result to new.po file. Only msgstr in old.po file be modified, msgid
23 become unwound. Strings(msgstr) that are not found in the update.po are left
26 The two input file(update.po & old.po) must use UTF-8 enconding, the output
27 file(new.po) enconding is UTF-8 too, without any byte-order-mark (BOM).
30 po-merge update.po old.po new.po
32 ******************************************************************************/
35 using System
.Collections
;
41 private static void Usage() {
42 Console
.WriteLine("Usage:\n\tpo-merge update.po old.po new.po\n" +
44 "This program will replace the translation in old.po file with " +
45 "update.po file, write the result to new.po file.\n\n" +
46 "Only msgstr in old.po file be modified, all msgid/msgstr become unwound. " +
47 "Strings(msgstr) that are not found in the update.po are left untouched.\n"
51 public static void Main(String
[] args
) {
52 if (args
.Length
!= 3) {
57 Hashtable msg
= ParseTranslation(args
[0]);
59 UpdateTranslation(args
[1], args
[2], msg
);
63 Read translation entry from the file and return a Hashtable
64 with the msgid and msgstr.The msgid and msgstr are strings.
66 private static Hashtable
ParseTranslation(String filename
) {
67 Hashtable msg
= new Hashtable(2047);
68 StreamReader sr
= new StreamReader(filename
, Encoding
.UTF8
);
70 bool isFinished
= false;
71 String line
, msgid
, msgstr
;
82 // May be non-empty msgid ?
85 if (line
.Length
>= 8 && line
[0] != '#')
92 if (!line
.StartsWith("msgid \"") || (line
[line
.Length
- 1] != '\"')) {
93 Console
.Error
.WriteLine("parse error before msgid, file {0}, line {1:d}:\n\t", filename
, n
);
94 throw new Exception("parse error");
98 if (line
.Length
<= 8) {
101 msgid
= line
.Substring(7, line
.Length
- 8);
106 line
= sr
.ReadLine();
108 Console
.Error
.WriteLine("parse msgid error, file {0}, line {1:d}:\n\t", filename
, n
);
109 throw new Exception("parse error");
113 if (line
.Length
== 0 || line
[0] != '"')
116 msgid
+= line
.Substring(1, line
.Length
- 2);
119 if (!line
.StartsWith("msgstr \"") || (line
[line
.Length
- 1] != '\"')) {
120 Console
.Error
.WriteLine("parse error after msgid, file {0}, line {1:d}:\n\t", filename
, n
);
121 throw new Exception("parse error");
125 if (line
.Length
<= 9) {
128 msgstr
= line
.Substring(8, line
.Length
- 9);
133 line
= sr
.ReadLine();
134 if (line
== null) break;
137 if (line
.Length
== 0 || line
[0] != '"')
140 if (msgid
.Length
== 0)
143 msgstr
+= line
.Substring(1, line
.Length
- 2);
148 // Console.WriteLine("msgid: [{0}]\nmsgstr: [{1}]\n", msgid, msgstr);
153 if (line
.Length
!= 0 && line
[0] != '#') {
154 Console
.Error
.WriteLine("parse error after msgstr, file {0}, line {1:d}:\n\t", filename
, n
);
155 throw new Exception("parse error");
165 Read translation entry from the file and update the msgid and msgstr, write to new file.
167 private static void UpdateTranslation(String filename
, String newfilename
, Hashtable msg
) {
168 StreamReader sr
= new StreamReader(filename
, Encoding
.UTF8
);
169 StreamWriter sw
= new StreamWriter(newfilename
, false);
171 int n
= 0, total
= 0, tr
= 0, ut
= 0, mt
= 0;
172 bool isFinished
= false;
173 String line
, msgid
, msgstr
, str
= "";
178 line
= sr
.ReadLine();
186 // May be non-empty msgid ?
187 if (str
.Length
>= 8 && str
[0] != '#')
190 if (total
== 0 && str
.Equals("#, fuzzy"))
191 line
= ""; // Remove PO file header fuzzy
196 if (isFinished
) break;
198 if (!str
.StartsWith("msgid \"") || (str
[str
.Length
- 1] != '\"')) {
199 Console
.Error
.WriteLine("parse error before msgid, file {0}, line {1:d}:\n\t", filename
, n
);
200 throw new Exception("parse error");
204 if (str
.Length
<= 8) {
207 msgid
= str
.Substring(7, str
.Length
- 8);
212 line
= sr
.ReadLine();
215 Console
.Error
.WriteLine("parse msgid error, file {0}, line {1:d}:\n\t", filename
, n
);
216 throw new Exception("parse error");
221 if (str
.Length
== 0 || line
[0] != '"')
224 msgid
+= str
.Substring(1, str
.Length
- 2);
227 if (!str
.StartsWith("msgstr \"") || (str
[str
.Length
- 1] != '\"')) {
228 Console
.Error
.WriteLine("parse error after msgid, file {0}, line {1:d}:\n\t", filename
, n
);
229 throw new Exception("parse error");
233 if (str
.Length
<= 9) {
236 msgstr
= str
.Substring(8, str
.Length
- 9);
241 line
= sr
.ReadLine();
247 if (str
.Length
== 0 || str
[0] != '"')
250 if (msgid
.Length
== 0)
253 msgstr
+= str
.Substring(1, str
.Length
- 2);
256 if (msgid
.Length
> 0)
259 str
= (String
) msg
[msgid
];
261 // Console.WriteLine("msgid: [{0}]\nmsgstr: [{1}]\nupdate: [{2}]\n", msgid, msgstr, str);
262 if (msgid
.Length
<= 0) {
263 sw
.WriteLine("msgid \"{0}\"", msgid
);
264 sw
.WriteLine("msgstr \"{0}\"\n", msgstr
);
265 } else if (str
!= null && str
.Length
> 0) {
266 if (msgid
.Length
!= 0) {
267 if (! msgstr
.Equals(str
)) mt
++;
270 sw
.WriteLine("msgid \"{0}\"", msgid
);
271 sw
.WriteLine("msgstr \"{0}\"\n", str
);
273 if (msgstr
.Length
== 0) {
278 sw
.WriteLine("msgid \"{0}\"", msgid
);
279 sw
.WriteLine("msgstr \"{0}\"\n", msgstr
);
286 if (line
.Length
!= 0 && line
[0] != '#') {
287 Console
.Error
.WriteLine("parse error after msgstr, file {0}, line {1:d}:\n\t", filename
, n
);
288 throw new Exception("parse error");
292 Console
.Error
.WriteLine("Total {0} messages, {1} translated, {2} updated, {3} untranslated.",