OSDN Git Service

merge original branch.
[tortoisegit/TortoiseGitJp.git] / contrib / Utils / po-merge.cs
1 /******************************************************************************\r
2 PO-MERGE - A simple po merge/update tool\r
3 \r
4 Copyright (C) 2006 - Dongsheng Song <dongsheng.song@gmail.com>\r
5 \r
6 This program is free software; you can redistribute it and/or\r
7 modify it under the terms of the GNU General Public License\r
8 as published by the Free Software Foundation; either version 2\r
9 of the License, or (at your option) any later version.\r
10 \r
11 This program is distributed in the hope that it will be useful,\r
12 but WITHOUT ANY WARRANTY; without even the implied warranty of\r
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\r
14 GNU General Public License for more details.\r
15 \r
16 You should have received a copy of the GNU General Public License\r
17 along with this program; if not, write to the Free Software Foundation, Inc.,\r
18 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA\r
19 \r
20 Description:\r
21 This program will replace the translation in old.po file with update.po file,\r
22 write the result to new.po file. Only msgstr in old.po file be modified, msgid\r
23 become unwound. Strings(msgstr) that are not found in the update.po are left\r
24 untouched.\r
25 \r
26 The two input file(update.po & old.po)  must use UTF-8 enconding, the output\r
27 file(new.po) enconding is UTF-8 too, without any byte-order-mark (BOM).\r
28 \r
29 Use:\r
30     po-merge update.po old.po new.po\r
31 \r
32 ******************************************************************************/\r
33 \r
34 using System;\r
35 using System.Collections;\r
36 using System.IO;\r
37 using System.Text;\r
38 \r
39 class Program\r
40 {\r
41     private static void Usage() {\r
42         Console.WriteLine("Usage:\n\tpo-merge update.po old.po new.po\n" +\r
43                 "\n" +\r
44                 "This program will replace the translation in old.po file with " +\r
45                 "update.po file, write the result to new.po file.\n\n" +\r
46                 "Only msgstr in old.po file be modified, all msgid/msgstr become unwound. " +\r
47                 "Strings(msgstr) that are not found in the update.po are left untouched.\n"\r
48                 );\r
49     }\r
50 \r
51     public static void Main(String[] args) {\r
52         if (args.Length != 3) {\r
53             Usage();\r
54             return;\r
55         }\r
56 \r
57         Hashtable msg = ParseTranslation(args[0]);\r
58 \r
59         UpdateTranslation(args[1], args[2], msg);\r
60     }\r
61 \r
62     /*\r
63     Read translation entry from the file and return a Hashtable\r
64     with the msgid and msgstr.The msgid and msgstr are strings.\r
65     */\r
66     private static Hashtable ParseTranslation(String filename) {\r
67         Hashtable msg = new Hashtable(2047);\r
68         StreamReader sr = new StreamReader(filename, Encoding.UTF8);\r
69         int n = 0;\r
70         bool isFinished = false;\r
71         String line, msgid, msgstr;\r
72 \r
73         while (true) {\r
74             while (true) {\r
75                 n++;\r
76                 line = sr.ReadLine();\r
77                 if (line == null) {\r
78                     isFinished = true;\r
79                     break;\r
80                 }\r
81 \r
82                 // May be non-empty msgid ?\r
83                 line = line.Trim();\r
84 \r
85                 if (line.Length >= 8 && line[0] != '#')\r
86                     break;\r
87             }\r
88 \r
89             if (isFinished)\r
90                 break;\r
91 \r
92             if (!line.StartsWith("msgid \"") || (line[line.Length - 1] != '\"')) {\r
93                 Console.Error.WriteLine("parse error before msgid, file {0}, line {1:d}:\n\t", filename, n);\r
94                 throw new Exception("parse error");\r
95             }\r
96 \r
97             // Get msgid\r
98             if (line.Length <= 8) {\r
99                 msgid = "";\r
100             } else {\r
101                 msgid = line.Substring(7, line.Length - 8);\r
102             }\r
103 \r
104             while (true) {\r
105                 n++;\r
106                 line = sr.ReadLine();\r
107                 if (line == null) {\r
108                     Console.Error.WriteLine("parse msgid error, file {0}, line {1:d}:\n\t", filename, n);\r
109                     throw new Exception("parse error");\r
110                 }\r
111 \r
112                 line = line.Trim();\r
113                 if (line.Length == 0 || line[0] != '"')\r
114                     break;\r
115 \r
116                 msgid += line.Substring(1, line.Length - 2);\r
117             }\r
118 \r
119             if (!line.StartsWith("msgstr \"") || (line[line.Length - 1] != '\"')) {\r
120                 Console.Error.WriteLine("parse error after msgid, file {0}, line {1:d}:\n\t", filename, n);\r
121                 throw new Exception("parse error");\r
122             }\r
123 \r
124             // Get msgstr\r
125             if (line.Length <= 9) {\r
126                 msgstr = "";\r
127             } else {\r
128                 msgstr = line.Substring(8, line.Length - 9);\r
129             }\r
130 \r
131             while (true) {\r
132                 n++;\r
133                 line = sr.ReadLine();\r
134                 if (line == null) break;\r
135 \r
136                 line = line.Trim();\r
137                 if (line.Length == 0 || line[0] != '"')\r
138                     break;\r
139 \r
140                 if (msgid.Length == 0)\r
141                     msgstr += "\"\n\"";\r
142 \r
143                 msgstr += line.Substring(1, line.Length - 2);\r
144             }\r
145 \r
146             msg[msgid] = msgstr;\r
147 \r
148             // Console.WriteLine("msgid: [{0}]\nmsgstr: [{1}]\n", msgid, msgstr);\r
149 \r
150             if (line == null)\r
151                 break;\r
152 \r
153             if (line.Length != 0 && line[0] != '#') {\r
154                 Console.Error.WriteLine("parse error after msgstr, file {0}, line {1:d}:\n\t", filename, n);\r
155                 throw new Exception("parse error");\r
156             }\r
157         }\r
158 \r
159         sr.Close();\r
160 \r
161         return msg;\r
162     }\r
163 \r
164     /*\r
165     Read translation entry from the file and update the msgid and msgstr, write to new file.\r
166     */\r
167     private static void UpdateTranslation(String filename, String newfilename, Hashtable msg) {\r
168         StreamReader sr = new StreamReader(filename, Encoding.UTF8);\r
169         StreamWriter sw = new StreamWriter(newfilename, false);\r
170 \r
171         int n = 0, total = 0, tr = 0, ut = 0, mt = 0;\r
172         bool isFinished = false;\r
173         String line, msgid, msgstr, str = "";\r
174 \r
175         while (true) {\r
176             while (true) {\r
177                 n++;\r
178                 line = sr.ReadLine();\r
179                 if (line == null) {\r
180                     isFinished = true;\r
181                     break;\r
182                 }\r
183 \r
184                 str = line.Trim();\r
185 \r
186                 // May be non-empty msgid ?\r
187                 if (str.Length >= 8 && str[0] != '#')\r
188                     break;\r
189 \r
190                 if (total == 0 && str.Equals("#, fuzzy"))\r
191                     line = ""; // Remove PO file header fuzzy\r
192 \r
193                 sw.WriteLine(line);\r
194             }\r
195 \r
196             if (isFinished) break;\r
197 \r
198             if (!str.StartsWith("msgid \"") || (str[str.Length - 1] != '\"')) {\r
199                 Console.Error.WriteLine("parse error before msgid, file {0}, line {1:d}:\n\t", filename, n);\r
200                 throw new Exception("parse error");\r
201             }\r
202 \r
203             // Get msgid\r
204             if (str.Length <= 8) {\r
205                 msgid = "";\r
206             } else {\r
207                 msgid = str.Substring(7, str.Length - 8);\r
208             }\r
209 \r
210             while (true) {\r
211                 n++;\r
212                 line = sr.ReadLine();\r
213                 if (line == null)\r
214                 {\r
215                     Console.Error.WriteLine("parse msgid error, file {0}, line {1:d}:\n\t", filename, n);\r
216                     throw new Exception("parse error");\r
217                 }\r
218 \r
219                 str = line.Trim();\r
220 \r
221                 if (str.Length == 0 || line[0] != '"')\r
222                     break;\r
223 \r
224                 msgid += str.Substring(1, str.Length - 2);\r
225             }\r
226 \r
227             if (!str.StartsWith("msgstr \"") || (str[str.Length - 1] != '\"')) {\r
228                 Console.Error.WriteLine("parse error after msgid, file {0}, line {1:d}:\n\t", filename, n);\r
229                 throw new Exception("parse error");\r
230             }\r
231 \r
232             // Get msgstr\r
233             if (str.Length <= 9) {\r
234                 msgstr = "";\r
235             } else {\r
236                 msgstr = str.Substring(8, str.Length - 9);\r
237             }\r
238 \r
239             while (true) {\r
240                 n++;\r
241                 line = sr.ReadLine();\r
242                 if (line == null)\r
243                     break;\r
244 \r
245                 str = line.Trim();\r
246 \r
247                 if (str.Length == 0 || str[0] != '"')\r
248                     break;\r
249 \r
250                 if (msgid.Length == 0)\r
251                     msgstr += "\"\n\"";\r
252 \r
253                 msgstr += str.Substring(1, str.Length - 2);\r
254             }\r
255 \r
256             if (msgid.Length > 0)\r
257                 total++;\r
258 \r
259             str = (String) msg[msgid];\r
260 \r
261             // Console.WriteLine("msgid: [{0}]\nmsgstr: [{1}]\nupdate: [{2}]\n", msgid, msgstr, str);\r
262             if (msgid.Length <= 0) {\r
263                 sw.WriteLine("msgid \"{0}\"", msgid);\r
264                 sw.WriteLine("msgstr \"{0}\"\n", msgstr);\r
265             } else if (str != null && str.Length > 0) {\r
266                 if (msgid.Length != 0) {\r
267                     if (! msgstr.Equals(str)) mt ++;\r
268                     tr ++;\r
269                 }\r
270                 sw.WriteLine("msgid \"{0}\"", msgid);\r
271                 sw.WriteLine("msgstr \"{0}\"\n", str);\r
272             } else {\r
273                 if (msgstr.Length == 0) {\r
274                     ut ++;\r
275                 } else {\r
276                     tr++;\r
277                 }\r
278                 sw.WriteLine("msgid \"{0}\"", msgid);\r
279                 sw.WriteLine("msgstr \"{0}\"\n", msgstr);\r
280             }\r
281 \r
282             if (line == null) {\r
283                 break;\r
284             }\r
285 \r
286             if (line.Length != 0 && line[0] != '#') {\r
287                 Console.Error.WriteLine("parse error after msgstr, file {0}, line {1:d}:\n\t", filename, n);\r
288                 throw new Exception("parse error");\r
289             }\r
290         }\r
291 \r
292         Console.Error.WriteLine("Total {0} messages, {1} translated, {2} updated, {3} untranslated.",\r
293                 total, tr, mt, ut);\r
294 \r
295         sr.Close();\r
296         sw.Close();\r
297 \r
298         return;\r
299     }\r
300 }\r