2 * Copyright (C) 2003-2004 Free Software Foundation, Inc.
3 * Written by Bruno Haible <bruno@clisp.org>, 2003.
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2, or (at your option)
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write to the Free Software Foundation,
17 * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
21 * This program dumps a GettextResourceSet subclass (in a satellite assembly)
22 * or a .resources file as a PO file.
25 using System
; /* Object, String, Type, Console, Exception */
26 using System
.Reflection
; /* Assembly, MethodInfo, ConstructorInfo */
27 using System
.Collections
; /* Hashtable, DictionaryEntry */
28 using System
.IO
; /* BufferedStream, StreamWriter, TextWriter, FileNotFoundException, Path */
29 using System
.Text
; /* StringBuilder, UTF8Encoding */
30 using System
.Resources
; /* ResourceReader */
31 using GNU
.Gettext
; /* GettextResourceSet */
33 namespace GNU
.Gettext
{
34 public class DumpResource
{
35 private TextWriter Out
;
36 private void DumpString (String str
) {
39 for (int i
= 0; i
< n
; i
++) {
42 Out
.Write('\\'); Out
.Write('b');
43 } else if (c
== 0x000c) {
44 Out
.Write('\\'); Out
.Write('f');
45 } else if (c
== 0x000a) {
46 Out
.Write('\\'); Out
.Write('n');
47 } else if (c
== 0x000d) {
48 Out
.Write('\\'); Out
.Write('r');
49 } else if (c
== 0x0009) {
50 Out
.Write('\\'); Out
.Write('t');
51 } else if (c
== '\\' || c
== '"') {
52 Out
.Write('\\'); Out
.Write(c
);
58 private void DumpMessage (String msgid
, String msgid_plural
, Object msgstr
) {
59 Out
.Write("msgid "); DumpString(msgid
); Out
.Write('\n');
60 if (msgid_plural
!= null) {
61 Out
.Write("msgid_plural "); DumpString(msgid_plural
); Out
.Write('\n');
62 for (int i
= 0; i
< (msgstr
as String
[]).Length
; i
++) {
63 Out
.Write("msgstr[" + i
+ "] ");
64 DumpString((msgstr
as String
[])[i
]);
68 Out
.Write("msgstr "); DumpString(msgstr
as String
); Out
.Write('\n');
73 // ---------------- Dumping a GettextResourceSet ----------------
75 private void Dump (GettextResourceSet catalog
) {
76 MethodInfo pluralMethod
=
77 catalog
.GetType().GetMethod("GetMsgidPluralTable", Type
.EmptyTypes
);
78 // Search for the header entry.
80 Object header_entry
= catalog
.GetObject("");
81 // If there is no header entry, fake one.
82 // FIXME: This is not needed; right after po_lex_charset_init set
83 // the PO charset to UTF-8.
84 if (header_entry
== null)
85 header_entry
= "Content-Type: text/plain; charset=UTF-8\n";
86 DumpMessage("", null, header_entry
);
88 // Now the other messages.
90 Hashtable plural
= null;
91 if (pluralMethod
!= null)
92 plural
= pluralMethod
.Invoke(catalog
, new Object
[0]) as Hashtable
;
93 foreach (String key
in catalog
.Keys
)
94 if (!"".Equals(key
)) {
95 Object
value = catalog
.GetObject(key
);
97 (plural
!= null && value is String
[] ? plural
[key
] as String
: null);
98 DumpMessage(key
, key_plural
, value);
102 // Essentially taken from class GettextResourceManager.
103 private static Assembly
GetSatelliteAssembly (String baseDirectory
, String resourceName
, String cultureName
) {
104 String satelliteExpectedLocation
=
106 + Path
.DirectorySeparatorChar
+ cultureName
107 + Path
.DirectorySeparatorChar
+ resourceName
+ ".resources.dll";
108 return Assembly
.LoadFrom(satelliteExpectedLocation
);
110 // Taken from class GettextResourceManager.
111 private static String
ConstructClassName (String resourceName
) {
112 // We could just return an arbitrary fixed class name, like "Messages",
113 // assuming that every assembly will only ever contain one
114 // GettextResourceSet subclass, but this assumption would break the day
115 // we want to support multi-domain PO files in the same format...
116 bool valid
= (resourceName
.Length
> 0);
117 for (int i
= 0; valid
&& i
< resourceName
.Length
; i
++) {
118 char c
= resourceName
[i
];
119 if (!((c
>= 'A' && c
<= 'Z') || (c
>= 'a' && c
<= 'z') || (c
== '_')
120 || (i
> 0 && c
>= '0' && c
<= '9')))
126 // Use hexadecimal escapes, using the underscore as escape character.
127 String hexdigit
= "0123456789abcdef";
128 StringBuilder b
= new StringBuilder();
129 b
.Append("__UESCAPED__");
130 for (int i
= 0; i
< resourceName
.Length
; i
++) {
131 char c
= resourceName
[i
];
132 if (c
>= 0xd800 && c
< 0xdc00
133 && i
+1 < resourceName
.Length
134 && resourceName
[i
+1] >= 0xdc00 && resourceName
[i
+1] < 0xe000) {
135 // Combine two UTF-16 words to a character.
136 char c2
= resourceName
[i
+1];
137 int uc
= 0x10000 + ((c
- 0xd800) << 10) + (c2
- 0xdc00);
140 b
.Append(hexdigit
[(uc
>> 28) & 0x0f]);
141 b
.Append(hexdigit
[(uc
>> 24) & 0x0f]);
142 b
.Append(hexdigit
[(uc
>> 20) & 0x0f]);
143 b
.Append(hexdigit
[(uc
>> 16) & 0x0f]);
144 b
.Append(hexdigit
[(uc
>> 12) & 0x0f]);
145 b
.Append(hexdigit
[(uc
>> 8) & 0x0f]);
146 b
.Append(hexdigit
[(uc
>> 4) & 0x0f]);
147 b
.Append(hexdigit
[uc
& 0x0f]);
149 } else if (!((c
>= 'A' && c
<= 'Z') || (c
>= 'a' && c
<= 'z')
150 || (c
>= '0' && c
<= '9'))) {
154 b
.Append(hexdigit
[(uc
>> 12) & 0x0f]);
155 b
.Append(hexdigit
[(uc
>> 8) & 0x0f]);
156 b
.Append(hexdigit
[(uc
>> 4) & 0x0f]);
157 b
.Append(hexdigit
[uc
& 0x0f]);
164 // Essentially taken from class GettextResourceManager.
165 private static GettextResourceSet
InstantiateResourceSet (Assembly satelliteAssembly
, String resourceName
, String cultureName
) {
166 Type clazz
= satelliteAssembly
.GetType(ConstructClassName(resourceName
)+"_"+cultureName
.Replace('-','_'));
167 ConstructorInfo constructor
= clazz
.GetConstructor(Type
.EmptyTypes
);
168 return constructor
.Invoke(null) as GettextResourceSet
;
170 public DumpResource (String baseDirectory
, String resourceName
, String cultureName
) {
171 // We are only interested in the messages belonging to the locale
172 // itself, not in the inherited messages. Therefore we instantiate just
173 // the GettextResourceSet, not a GettextResourceManager.
174 Assembly satelliteAssembly
=
175 GetSatelliteAssembly(baseDirectory
, resourceName
, cultureName
);
176 GettextResourceSet catalog
=
177 InstantiateResourceSet(satelliteAssembly
, resourceName
, cultureName
);
178 BufferedStream stream
= new BufferedStream(Console
.OpenStandardOutput());
179 Out
= new StreamWriter(stream
, new UTF8Encoding());
185 // ----------------- Dumping a .resources file ------------------
187 public DumpResource (String filename
) {
188 BufferedStream stream
= new BufferedStream(Console
.OpenStandardOutput());
189 Out
= new StreamWriter(stream
, new UTF8Encoding());
191 if (filename
.Equals("-")) {
192 BufferedStream input
= new BufferedStream(Console
.OpenStandardInput());
193 // A temporary output stream is needed because ResourceReader expects
194 // to be able to seek in the Stream.
197 MemoryStream tmpstream
= new MemoryStream();
198 byte[] buf
= new byte[1024];
200 int n
= input
.Read(buf
, 0, 1024);
203 tmpstream
.Write(buf
, 0, n
);
205 contents
= tmpstream
.ToArray();
208 MemoryStream tmpinput
= new MemoryStream(contents
);
209 rr
= new ResourceReader(tmpinput
);
211 rr
= new ResourceReader(filename
);
213 foreach (DictionaryEntry entry
in rr
) // uses rr.GetEnumerator()
214 DumpMessage(entry
.Key
as String
, null, entry
.Value
as String
);
220 // --------------------------------------------------------------
222 public static int Main (String
[] args
) {
225 new DumpResource(args
[0], args
[1], args
[2]);
227 new DumpResource(args
[0]);
228 } catch (Exception e
) {
229 Console
.Error
.WriteLine(e
);
230 Console
.Error
.WriteLine(e
.StackTrace
);