2 * monoresgen: convert between the resource formats (.txt, .resources, .resx).
4 * Copyright (c) 2002 Ximian, Inc
7 * Paolo Molaro (lupus@ximian.com)
8 * Gonzalo Paniagua Javier (gonzalo@ximian.com)
13 * * escape/unescape in the .txt reader/writer to be able to roundtrip values with newlines
14 * (unlike the MS ResGen utility)
20 using System
.Collections
;
21 using System
.Resources
;
22 using System
.Reflection
;
31 * We load the ResX format stuff on demand, since the classes are in
32 * System.Windows.Forms (!!!) and we can't depend on that assembly in mono, yet.
34 static void LoadResX () {
38 swf
= Assembly
.Load (Consts
.AssemblySystem_Windows_Forms
);
39 resxr
= swf
.GetType ("System.Resources.ResXResourceReader");
40 resxw
= swf
.GetType ("System.Resources.ResXResourceWriter");
41 } catch (Exception e
) {
42 throw new Exception ("Cannot load support for ResX format: " + e
.Message
);
46 static void Usage () {
47 string Usage
= @"Mono Resource Generator version 0.1
49 monoresgen source.ext [dest.ext]
50 monoresgen /compile source.ext[,dest.resources] [...]
52 Convert a resource file from one format to another.
53 The currently supported formats are: '.txt' '.resources' '.resx' '.po'.
54 If the destination file is not specified, source.resources will be used.
55 The /compile option takes a list of .resX or .txt files to convert to
56 .resources files in one bulk operation, replacing .ext with .resources for
59 Console
.WriteLine( Usage
);
62 static IResourceReader
GetReader (Stream stream
, string name
) {
63 string format
= Path
.GetExtension (name
);
64 switch (format
.ToLower ()) {
66 return new PoResourceReader (stream
);
69 return new TxtResourceReader (stream
);
71 return new ResourceReader (stream
);
74 return (IResourceReader
)Activator
.CreateInstance (resxr
, new object[] {stream}
);
76 throw new Exception ("Unknown format in file " + name
);
80 static IResourceWriter
GetWriter (Stream stream
, string name
) {
81 string format
= Path
.GetExtension (name
);
82 switch (format
.ToLower ()) {
84 return new PoResourceWriter (stream
);
87 return new TxtResourceWriter (stream
);
89 return new ResourceWriter (stream
);
92 return (IResourceWriter
)Activator
.CreateInstance (resxw
, new object[] {stream}
);
94 throw new Exception ("Unknown format in file " + name
);
98 static int CompileResourceFile(string sname
, string dname
) {
99 FileStream source
, dest
;
100 IResourceReader reader
;
101 IResourceWriter writer
;
104 source
= new FileStream (sname
, FileMode
.Open
, FileAccess
.Read
);
106 reader
= GetReader (source
, sname
);
108 dest
= new FileStream (dname
, FileMode
.OpenOrCreate
, FileAccess
.Write
);
109 writer
= GetWriter (dest
, dname
);
112 foreach (DictionaryEntry e
in reader
) {
114 object val
= e
.Value
;
116 writer
.AddResource ((string)e
.Key
, (string)e
.Value
);
118 writer
.AddResource ((string)e
.Key
, e
.Value
);
120 Console
.WriteLine( "Read in {0} resources from '{1}'", rescount
, sname
);
124 Console
.WriteLine("Writing resource file... Done.");
125 } catch (Exception e
) {
126 Console
.WriteLine ("Error: {0}", e
.Message
);
127 Exception inner
= e
.InnerException
;
129 Console
.WriteLine ("Inner exception: {0}", inner
.Message
);
135 static int Main (string[] args
) {
136 string sname
= "", dname
= "";
137 if ((int) args
.Length
< 1 || args
[0] == "-h" || args
[0] == "-?" || args
[0] == "/h" || args
[0] == "/?") {
141 if (args
[0] == "/compile" || args
[0] == "-compile") {
142 for ( int i
=1; i
< args
.Length
; i
++ ) {
143 if ( args
[i
].IndexOf(",") != -1 ){
144 string[] pair
= args
[i
].Split(',');
148 Console
.WriteLine(@"error: You must specify an input & outfile file name like this:");
149 Console
.WriteLine("inFile.txt,outFile.resources." );
150 Console
.WriteLine("You passed in '{0}'.", args
[i
] );
155 dname
= Path
.ChangeExtension (sname
, "resources");
157 int ret
= CompileResourceFile( sname
, dname
);
165 else if (args
.Length
== 1) {
167 dname
= Path
.ChangeExtension (sname
, "resources");
168 } else if (args
.Length
!= 2) {
175 return CompileResourceFile( sname
, dname
);
179 class TxtResourceWriter
: IResourceWriter
{
182 public TxtResourceWriter (Stream stream
) {
183 s
= new StreamWriter (stream
);
186 public void AddResource (string name
, byte[] value) {
187 throw new Exception ("Binary data not valid in a text resource file");
190 public void AddResource (string name
, object value) {
191 if (value is string) {
192 AddResource (name
, (string)value);
195 throw new Exception ("Objects not valid in a text resource file");
198 /* FIXME: handle newlines */
199 public void AddResource (string name
, string value) {
200 s
.WriteLine ("{0}={1}", name
, value);
203 public void Close () {
207 public void Dispose () {}
209 public void Generate () {}
212 class TxtResourceReader
: IResourceReader
{
216 public TxtResourceReader (Stream stream
) {
217 data
= new Hashtable ();
222 public virtual void Close () {
225 public IDictionaryEnumerator
GetEnumerator() {
226 return data
.GetEnumerator ();
230 StreamReader reader
= new StreamReader (s
);
231 string line
, key
, val
;
232 int epos
, line_num
= 0;
233 while ((line
= reader
.ReadLine ()) != null) {
236 if (line
.Length
== 0 || line
[0] == '#' ||
239 epos
= line
.IndexOf ('=');
241 throw new Exception ("Invalid format at line " + line_num
);
242 key
= line
.Substring (0, epos
);
243 val
= line
.Substring (epos
+ 1);
247 throw new Exception ("Key is empty at line " + line_num
);
252 IEnumerator IEnumerable
.GetEnumerator () {
253 return ((IResourceReader
) this).GetEnumerator();
256 void IDisposable
.Dispose () {}
259 class PoResourceReader
: IResourceReader
{
264 public PoResourceReader (Stream stream
)
266 data
= new Hashtable ();
271 public virtual void Close ()
276 public IDictionaryEnumerator
GetEnumerator()
278 return data
.GetEnumerator ();
281 string GetValue (string line
)
283 int begin
= line
.IndexOf ('"');
285 throw new FormatException (String
.Format ("No begin quote at line {0}: {1}", line_num
, line
));
287 int end
= line
.LastIndexOf ('"');
289 throw new FormatException (String
.Format ("No closing quote at line {0}: {1}", line_num
, line
));
291 return line
.Substring (begin
+ 1, end
- begin
- 1);
296 StreamReader reader
= new StreamReader (s
);
299 string msgstr
= null;
300 bool ignoreNext
= false;
302 while ((line
= reader
.ReadLine ()) != null) {
305 if (line
.Length
== 0)
308 if (line
[0] == '#') {
309 if (line
.Length
== 1 || line
[1] != ',')
312 if (line
.IndexOf ("fuzzy") != -1) {
316 throw new FormatException ("Error. Line: " + line_num
);
317 data
.Add (msgid
, msgstr
);
325 if (line
.StartsWith ("msgid ")) {
326 if (msgid
== null && msgstr
!= null)
327 throw new FormatException ("Found 2 consecutive msgid. Line: " + line_num
);
329 if (msgstr
!= null) {
331 data
.Add (msgid
, msgstr
);
338 msgid
= GetValue (line
);
342 if (line
.StartsWith ("msgstr ")) {
344 throw new FormatException ("msgstr with no msgid. Line: " + line_num
);
346 msgstr
= GetValue (line
);
350 if (line
[0] == '"') {
351 if (msgid
== null || msgstr
== null)
352 throw new FormatException ("Invalid format. Line: " + line_num
);
354 msgstr
+= GetValue (line
);
358 throw new FormatException ("Unexpected data. Line: " + line_num
);
363 throw new FormatException ("Expecting msgstr. Line: " + line_num
);
366 data
.Add (msgid
, msgstr
);
370 IEnumerator IEnumerable
.GetEnumerator ()
372 return GetEnumerator();
375 void IDisposable
.Dispose ()
387 class PoResourceWriter
: IResourceWriter
392 public PoResourceWriter (Stream stream
)
394 s
= new StreamWriter (stream
);
397 public void AddResource (string name
, byte [] value)
399 throw new InvalidOperationException ("Binary data not valid in a po resource file");
402 public void AddResource (string name
, object value)
404 if (value is string) {
405 AddResource (name
, (string) value);
408 throw new InvalidOperationException ("Objects not valid in a po resource file");
411 StringBuilder ebuilder
= new StringBuilder ();
413 public string Escape (string ns
)
417 foreach (char c
in ns
){
421 ebuilder
.Append ('\\');
425 ebuilder
.Append ("\\a");
428 ebuilder
.Append ("\\n");
431 ebuilder
.Append ("\\r");
438 return ebuilder
.ToString ();
441 public void AddResource (string name
, string value)
443 if (!headerWritten
) {
444 headerWritten
= true;
448 s
.WriteLine ("msgid \"{0}\"", Escape (name
));
449 s
.WriteLine ("msgstr \"{0}\"", Escape (value));
455 s
.WriteLine ("msgid \"\"");
456 s
.WriteLine ("msgstr \"\"");
457 s
.WriteLine ("\"MIME-Version: 1.0\\n\"");
458 s
.WriteLine ("\"Content-Type: text/plain; charset=UTF-8\\n\"");
459 s
.WriteLine ("\"Content-Transfer-Encoding: 8bit\\n\"");
460 s
.WriteLine ("\"X-Generator: monoresgen 0.1\\n\"");
461 s
.WriteLine ("#\"Project-Id-Version: FILLME\\n\"");
462 s
.WriteLine ("#\"POT-Creation-Date: yyyy-MM-dd HH:MM+zzzz\\n\"");
463 s
.WriteLine ("#\"PO-Revision-Date: yyyy-MM-dd HH:MM+zzzz\\n\"");
464 s
.WriteLine ("#\"Last-Translator: FILLME\\n\"");
465 s
.WriteLine ("#\"Language-Team: FILLME\\n\"");
466 s
.WriteLine ("#\"Report-Msgid-Bugs-To: \\n\"");
475 public void Dispose () { }
477 public void Generate () {}