2 * resgen: 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)
14 using System.Collections;
15 using System.Resources;
16 using System.Reflection;
25 * We load the ResX format stuff on demand, since the classes are in
26 * System.Windows.Forms (!!!) and we can't depend on that assembly in mono, yet.
28 static void LoadResX () {
32 swf = Assembly.Load (Consts.AssemblySystem_Windows_Forms);
33 resxr = swf.GetType ("System.Resources.ResXResourceReader");
34 resxw = swf.GetType ("System.Resources.ResXResourceWriter");
35 } catch (Exception e) {
36 throw new Exception ("Cannot load support for ResX format: " + e.Message);
40 static void Usage () {
41 string Usage = @"Mono Resource Generator version 0.1
43 resgen source.ext [dest.ext]
44 resgen /compile source.ext[,dest.resources] [...]
46 Convert a resource file from one format to another.
47 The currently supported formats are: '.txt' '.resources' '.resx' '.po'.
48 If the destination file is not specified, source.resources will be used.
49 The /compile option takes a list of .resX or .txt files to convert to
50 .resources files in one bulk operation, replacing .ext with .resources for
53 Console.WriteLine( Usage );
56 static IResourceReader GetReader (Stream stream, string name) {
57 string format = Path.GetExtension (name);
58 switch (format.ToLower ()) {
60 return new PoResourceReader (stream);
63 return new TxtResourceReader (stream);
65 return new ResourceReader (stream);
68 return (IResourceReader)Activator.CreateInstance (resxr, new object[] {stream});
70 throw new Exception ("Unknown format in file " + name);
74 static IResourceWriter GetWriter (Stream stream, string name) {
75 string format = Path.GetExtension (name);
76 switch (format.ToLower ()) {
78 return new PoResourceWriter (stream);
81 return new TxtResourceWriter (stream);
83 return new ResourceWriter (stream);
86 return (IResourceWriter)Activator.CreateInstance (resxw, new object[] {stream});
88 throw new Exception ("Unknown format in file " + name);
92 static int CompileResourceFile(string sname, string dname ) {
93 FileStream source, dest;
94 IResourceReader reader;
95 IResourceWriter writer;
98 source = new FileStream (sname, FileMode.Open, FileAccess.Read);
100 reader = GetReader (source, sname);
102 dest = new FileStream (dname, FileMode.Create, FileAccess.Write);
103 writer = GetWriter (dest, dname);
106 foreach (DictionaryEntry e in reader) {
108 object val = e.Value;
110 writer.AddResource ((string)e.Key, (string)e.Value);
112 writer.AddResource ((string)e.Key, e.Value);
114 Console.WriteLine( "Read in {0} resources from '{1}'", rescount, sname );
118 Console.WriteLine("Writing resource file... Done.");
119 } catch (Exception e) {
120 Console.WriteLine ("Error: {0}", e.Message);
121 Exception inner = e.InnerException;
123 Console.WriteLine ("Inner exception: {0}", inner.Message);
129 static int Main (string[] args) {
130 string sname = "", dname = "";
131 if ((int) args.Length < 1 || args[0] == "-h" || args[0] == "-?" || args[0] == "/h" || args[0] == "/?") {
135 if (args[0] == "/compile" || args[0] == "-compile") {
136 for ( int i=1; i< args.Length; i++ ) {
137 if ( args[i].IndexOf(",") != -1 ){
138 string[] pair = args[i].Split(',');
142 Console.WriteLine(@"error: You must specify an input & outfile file name like this:");
143 Console.WriteLine("inFile.txt,outFile.resources." );
144 Console.WriteLine("You passed in '{0}'.", args[i] );
149 dname = Path.ChangeExtension (sname, "resources");
151 int ret = CompileResourceFile( sname, dname );
159 else if (args.Length == 1) {
161 dname = Path.ChangeExtension (sname, "resources");
162 } else if (args.Length != 2) {
169 return CompileResourceFile( sname, dname );
173 class TxtResourceWriter : IResourceWriter {
176 public TxtResourceWriter (Stream stream) {
177 s = new StreamWriter (stream);
180 public void AddResource (string name, byte[] value) {
181 throw new Exception ("Binary data not valid in a text resource file");
184 public void AddResource (string name, object value) {
185 if (value is string) {
186 AddResource (name, (string)value);
189 throw new Exception ("Objects not valid in a text resource file");
192 public void AddResource (string name, string value) {
193 s.WriteLine ("{0}={1}", name, Escape (value));
197 static string Escape (string value)
199 StringBuilder b = new StringBuilder ();
200 for (int i = 0; i < value.Length; i++) {
215 b.Append (value [i]);
219 return b.ToString ();
222 public void Close () {
226 public void Dispose () {}
228 public void Generate () {}
231 class TxtResourceReader : IResourceReader {
235 public TxtResourceReader (Stream stream) {
236 data = new Hashtable ();
241 public virtual void Close () {
244 public IDictionaryEnumerator GetEnumerator() {
245 return data.GetEnumerator ();
249 StreamReader reader = new StreamReader (s);
250 string line, key, val;
251 int epos, line_num = 0;
252 while ((line = reader.ReadLine ()) != null) {
255 if (line.Length == 0 || line [0] == '#' ||
258 epos = line.IndexOf ('=');
260 throw new Exception ("Invalid format at line " + line_num);
261 key = line.Substring (0, epos);
262 val = line.Substring (epos + 1);
266 throw new Exception ("Key is empty at line " + line_num);
268 val = Unescape (val);
270 throw new Exception (String.Format ("Unsupported escape character in value of key '{0}'.", key));
278 static string Unescape (string value)
280 StringBuilder b = new StringBuilder ();
282 for (int i = 0; i < value.Length; i++) {
283 if (value [i] == '\\') {
284 if (i == value.Length - 1)
306 b.Append (value [i]);
310 return b.ToString ();
313 IEnumerator IEnumerable.GetEnumerator () {
314 return ((IResourceReader) this).GetEnumerator();
317 void IDisposable.Dispose () {}
320 class PoResourceReader : IResourceReader {
325 public PoResourceReader (Stream stream)
327 data = new Hashtable ();
332 public virtual void Close ()
337 public IDictionaryEnumerator GetEnumerator()
339 return data.GetEnumerator ();
342 string GetValue (string line)
344 int begin = line.IndexOf ('"');
346 throw new FormatException (String.Format ("No begin quote at line {0}: {1}", line_num, line));
348 int end = line.LastIndexOf ('"');
350 throw new FormatException (String.Format ("No closing quote at line {0}: {1}", line_num, line));
352 return line.Substring (begin + 1, end - begin - 1);
357 StreamReader reader = new StreamReader (s);
360 string msgstr = null;
361 bool ignoreNext = false;
363 while ((line = reader.ReadLine ()) != null) {
366 if (line.Length == 0)
369 if (line [0] == '#') {
370 if (line.Length == 1 || line [1] != ',')
373 if (line.IndexOf ("fuzzy") != -1) {
377 throw new FormatException ("Error. Line: " + line_num);
378 data.Add (msgid, msgstr);
386 if (line.StartsWith ("msgid ")) {
387 if (msgid == null && msgstr != null)
388 throw new FormatException ("Found 2 consecutive msgid. Line: " + line_num);
390 if (msgstr != null) {
392 data.Add (msgid, msgstr);
399 msgid = GetValue (line);
403 if (line.StartsWith ("msgstr ")) {
405 throw new FormatException ("msgstr with no msgid. Line: " + line_num);
407 msgstr = GetValue (line);
411 if (line [0] == '"') {
412 if (msgid == null || msgstr == null)
413 throw new FormatException ("Invalid format. Line: " + line_num);
415 msgstr += GetValue (line);
419 throw new FormatException ("Unexpected data. Line: " + line_num);
424 throw new FormatException ("Expecting msgstr. Line: " + line_num);
427 data.Add (msgid, msgstr);
431 IEnumerator IEnumerable.GetEnumerator ()
433 return GetEnumerator();
436 void IDisposable.Dispose ()
448 class PoResourceWriter : IResourceWriter
453 public PoResourceWriter (Stream stream)
455 s = new StreamWriter (stream);
458 public void AddResource (string name, byte [] value)
460 throw new InvalidOperationException ("Binary data not valid in a po resource file");
463 public void AddResource (string name, object value)
465 if (value is string) {
466 AddResource (name, (string) value);
469 throw new InvalidOperationException ("Objects not valid in a po resource file");
472 StringBuilder ebuilder = new StringBuilder ();
474 public string Escape (string ns)
478 foreach (char c in ns){
482 ebuilder.Append ('\\');
486 ebuilder.Append ("\\a");
489 ebuilder.Append ("\\n");
492 ebuilder.Append ("\\r");
499 return ebuilder.ToString ();
502 public void AddResource (string name, string value)
504 if (!headerWritten) {
505 headerWritten = true;
509 s.WriteLine ("msgid \"{0}\"", Escape (name));
510 s.WriteLine ("msgstr \"{0}\"", Escape (value));
516 s.WriteLine ("msgid \"\"");
517 s.WriteLine ("msgstr \"\"");
518 s.WriteLine ("\"MIME-Version: 1.0\\n\"");
519 s.WriteLine ("\"Content-Type: text/plain; charset=UTF-8\\n\"");
520 s.WriteLine ("\"Content-Transfer-Encoding: 8bit\\n\"");
521 s.WriteLine ("\"X-Generator: Mono resgen 0.1\\n\"");
522 s.WriteLine ("#\"Project-Id-Version: FILLME\\n\"");
523 s.WriteLine ("#\"POT-Creation-Date: yyyy-MM-dd HH:MM+zzzz\\n\"");
524 s.WriteLine ("#\"PO-Revision-Date: yyyy-MM-dd HH:MM+zzzz\\n\"");
525 s.WriteLine ("#\"Last-Translator: FILLME\\n\"");
526 s.WriteLine ("#\"Language-Team: FILLME\\n\"");
527 s.WriteLine ("#\"Report-Msgid-Bugs-To: \\n\"");
536 public void Dispose () { }
538 public void Generate () {}