+2003-10-12 Bruno Haible <bruno@clisp.org>
+
+ * msgattrib.texi: Document --stringtable-input and --stringtable-output
+ options.
+ * msgcat.texi: Likewise.
+ * msgcomm.texi: Likewise.
+ * msgconv.text: Likewise.
+ * msgen.texi: Likewise.
+ * msgfilter.texi: Likewise.
+ * msgrep.texi: Likewise.
+ * msginit.texi: Likewise.
+ * msgmerge.texi: Likewise.
+ * msguniq.texi: Likewise.
+ * msgcmp.texi: Document --stringtable-input option.
+ * msgexec.texi: Likewise.
+ * msgfmt.texi: Likewise.
+ * msgunfmt.texi: Document --stringtable-output option.
+ * xgettext.texi: Likewise. Document --language=NXStringTable.
+
2003-10-11 Bruno Haible <bruno@clisp.org>
* gettext.texi (PHP): Drop phplib from list of RPMs.
Assume the input file is a Java ResourceBundle in Java @code{.properties}
syntax, not in PO file syntax.
+@item --stringtable-input
+@opindex --stringtable-input@r{, @code{msgattrib} option}
+Assume the input file is a NeXTstep/GNUstep localized resource file in
+@code{.strings} syntax, not in PO file syntax.
+
@end table
@subsection Output details
that this file format doesn't support plural forms and silently drops
obsolete messages.
+@item --stringtable-output
+@opindex --stringtable-output@r{, @code{msgattrib} option}
+Write out a NeXTstep/GNUstep localized resource file in @code{.strings} syntax.
+Note that this file format doesn't support plural forms.
+
@item -w @var{number}
@itemx --width=@var{number}
@opindex -w@r{, @code{msgattrib} option}
Assume the input files are Java ResourceBundles in Java @code{.properties}
syntax, not in PO file syntax.
+@item --stringtable-input
+@opindex --stringtable-input@r{, @code{msgcat} option}
+Assume the input files are NeXTstep/GNUstep localized resource files in
+@code{.strings} syntax, not in PO file syntax.
+
@end table
@subsection Output details
that this file format doesn't support plural forms and silently drops
obsolete messages.
+@item --stringtable-output
+@opindex --stringtable-output@r{, @code{msgcat} option}
+Write out a NeXTstep/GNUstep localized resource file in @code{.strings} syntax.
+Note that this file format doesn't support plural forms.
+
@item -w @var{number}
@itemx --width=@var{number}
@opindex -w@r{, @code{msgcat} option}
Assume the input files are Java ResourceBundles in Java @code{.properties}
syntax, not in PO file syntax.
+@item --stringtable-input
+@opindex --stringtable-input@r{, @code{msgcmp} option}
+Assume the input files are NeXTstep/GNUstep localized resource files in
+@code{.strings} syntax, not in PO file syntax.
+
@end table
@subsection Informative output
Assume the input files are Java ResourceBundles in Java @code{.properties}
syntax, not in PO file syntax.
+@item --stringtable-input
+@opindex --stringtable-input@r{, @code{msgcomm} option}
+Assume the input files are NeXTstep/GNUstep localized resource files in
+@code{.strings} syntax, not in PO file syntax.
+
@end table
@subsection Output details
that this file format doesn't support plural forms and silently drops
obsolete messages.
+@item --stringtable-output
+@opindex --stringtable-output@r{, @code{msgcomm} option}
+Write out a NeXTstep/GNUstep localized resource file in @code{.strings} syntax.
+Note that this file format doesn't support plural forms.
+
@item -w @var{number}
@itemx --width=@var{number}
@opindex -w@r{, @code{msgcomm} option}
Assume the input file is a Java ResourceBundle in Java @code{.properties}
syntax, not in PO file syntax.
+@item --stringtable-input
+@opindex --stringtable-input@r{, @code{msgonv} option}
+Assume the input file is a NeXTstep/GNUstep localized resource file in
+@code{.strings} syntax, not in PO file syntax.
+
@end table
@subsection Output details
that this file format doesn't support plural forms and silently drops
obsolete messages.
+@item --stringtable-output
+@opindex --stringtable-output@r{, @code{msgconv} option}
+Write out a NeXTstep/GNUstep localized resource file in @code{.strings} syntax.
+Note that this file format doesn't support plural forms.
+
@item -w @var{number}
@itemx --width=@var{number}
@opindex -w@r{, @code{msgconv} option}
Assume the input file is a Java ResourceBundle in Java @code{.properties}
syntax, not in PO file syntax.
+@item --stringtable-input
+@opindex --stringtable-input@r{, @code{msgen} option}
+Assume the input file is a NeXTstep/GNUstep localized resource file in
+@code{.strings} syntax, not in PO file syntax.
+
@end table
@subsection Output details
that this file format doesn't support plural forms and silently drops
obsolete messages.
+@item --stringtable-output
+@opindex --stringtable-output@r{, @code{msgen} option}
+Write out a NeXTstep/GNUstep localized resource file in @code{.strings} syntax.
+Note that this file format doesn't support plural forms.
+
@item -w @var{number}
@itemx --width=@var{number}
@opindex -w@r{, @code{msgen} option}
Assume the input file is a Java ResourceBundle in Java @code{.properties}
syntax, not in PO file syntax.
+@item --stringtable-input
+@opindex --stringtable-input@r{, @code{msgexec} option}
+Assume the input file is a NeXTstep/GNUstep localized resource file in
+@code{.strings} syntax, not in PO file syntax.
+
@end table
@subsection Informative output
Assume the input file is a Java ResourceBundle in Java @code{.properties}
syntax, not in PO file syntax.
+@item --stringtable-input
+@opindex --stringtable-input@r{, @code{msgfilter} option}
+Assume the input file is a NeXTstep/GNUstep localized resource file in
+@code{.strings} syntax, not in PO file syntax.
+
@end table
@subsection Output details
that this file format doesn't support plural forms and silently drops
obsolete messages.
+@item --stringtable-output
+@opindex --stringtable-output@r{, @code{msgfilter} option}
+Write out a NeXTstep/GNUstep localized resource file in @code{.strings} syntax.
+Note that this file format doesn't support plural forms.
+
@item -w @var{number}
@itemx --width=@var{number}
@opindex -w@r{, @code{msgfilter} option}
Assume the input files are Java ResourceBundles in Java @code{.properties}
syntax, not in PO file syntax.
+@item --stringtable-input
+@opindex --stringtable-input@r{, @code{msgfmt} option}
+Assume the input files are NeXTstep/GNUstep localized resource files in
+@code{.strings} syntax, not in PO file syntax.
+
@end table
@subsection Input file interpretation
Assume the input file is a Java ResourceBundle in Java @code{.properties}
syntax, not in PO file syntax.
+@item --stringtable-input
+@opindex --stringtable-input@r{, @code{msggrep} option}
+Assume the input file is a NeXTstep/GNUstep localized resource file in
+@code{.strings} syntax, not in PO file syntax.
+
@end table
@subsection Output details
that this file format doesn't support plural forms and silently drops
obsolete messages.
+@item --stringtable-output
+@opindex --stringtable-output@r{, @code{msggrep} option}
+Write out a NeXTstep/GNUstep localized resource file in @code{.strings} syntax.
+Note that this file format doesn't support plural forms.
+
@item -w @var{number}
@itemx --width=@var{number}
@opindex -w@r{, @code{msggrep} option}
Assume the input file is a Java ResourceBundle in Java @code{.properties}
syntax, not in PO file syntax.
+@item --stringtable-input
+@opindex --stringtable-input@r{, @code{msginit} option}
+Assume the input file is a NeXTstep/GNUstep localized resource file in
+@code{.strings} syntax, not in PO file syntax.
+
@end table
@subsection Output details
that this file format doesn't support plural forms and silently drops
obsolete messages.
+@item --stringtable-output
+@opindex --stringtable-output@r{, @code{msginit} option}
+Write out a NeXTstep/GNUstep localized resource file in @code{.strings} syntax.
+Note that this file format doesn't support plural forms.
+
@item -w @var{number}
@itemx --width=@var{number}
@opindex -w@r{, @code{msginit} option}
Assume the input files are Java ResourceBundles in Java @code{.properties}
syntax, not in PO file syntax.
+@item --stringtable-input
+@opindex --stringtable-input@r{, @code{msgmerge} option}
+Assume the input files are NeXTstep/GNUstep localized resource files in
+@code{.strings} syntax, not in PO file syntax.
+
@end table
@subsection Output details
that this file format doesn't support plural forms and silently drops
obsolete messages.
+@item --stringtable-output
+@opindex --stringtable-output@r{, @code{msgmerge} option}
+Write out a NeXTstep/GNUstep localized resource file in @code{.strings} syntax.
+Note that this file format doesn't support plural forms.
+
@item -w @var{number}
@itemx --width=@var{number}
@opindex -w@r{, @code{msgmerge} option}
that this file format doesn't support plural forms and silently drops
obsolete messages.
+@item --stringtable-output
+@opindex --stringtable-output@r{, @code{msgunfmt} option}
+Write out a NeXTstep/GNUstep localized resource file in @code{.strings} syntax.
+Note that this file format doesn't support plural forms.
+
@item -w @var{number}
@itemx --width=@var{number}
@opindex -w@r{, @code{msgunfmt} option}
Assume the input file is a Java ResourceBundle in Java @code{.properties}
syntax, not in PO file syntax.
+@item --stringtable-input
+@opindex --stringtable-input@r{, @code{msguniq} option}
+Assume the input file is a NeXTstep/GNUstep localized resource file in
+@code{.strings} syntax, not in PO file syntax.
+
@end table
@subsection Output details
that this file format doesn't support plural forms and silently drops
obsolete messages.
+@item --stringtable-output
+@opindex --stringtable-output@r{, @code{msguniq} option}
+Write out a NeXTstep/GNUstep localized resource file in @code{.strings} syntax.
+Note that this file format doesn't support plural forms.
+
@item -w @var{number}
@itemx --width=@var{number}
@opindex -w@r{, @code{msguniq} option}
are @code{C}, @code{C++}, @code{ObjectiveC}, @code{PO}, @code{Python},
@code{Lisp}, @code{EmacsLisp}, @code{librep}, @code{Smalltalk}, @code{Java},
@code{JavaProperties}, @code{awk}, @code{YCP}, @code{Tcl}, @code{Perl},
-@code{PHP}, @code{GCC-source}, @code{RST}, @code{Glade}.
+@code{PHP}, @code{GCC-source}, @code{NXStringTable}, @code{RST}, @code{Glade}.
@item -C
@itemx --c++
that this file format doesn't support plural forms and silently drops
obsolete messages.
+@item --stringtable-output
+@opindex --stringtable-output@r{, @code{xgettext} option}
+Write out a NeXTstep/GNUstep localized resource file in @code{.strings} syntax.
+Note that this file format doesn't support plural forms.
+
@item -w @var{number}
@itemx --width=@var{number}
@opindex -w@r{, @code{xgettext} option}
+2003-10-14 Bruno Haible <bruno@clisp.org>
+
+ * POTFILES.in: Add src/read-stringtable.c.
+
2003-10-09 Bruno Haible <bruno@clisp.org>
* POTFILES.in: Add src/java.c.
src/read-mo.c
src/read-po.c
src/read-properties.c
+src/read-stringtable.c
src/read-tcl.c
src/urlget.c
src/write-java.c
+2003-10-18 Bruno Haible <bruno@clisp.org>
+
+ Support for GNUstep .strings format.
+ * read-stringtable.h: New file.
+ * read-stringtable.c: New file.
+ * read-po-abstract.h (enum input_syntax_ty): New item
+ syntax_stringtable.
+ (po_callback_comment_dispatcher): Renamed from po_callback_comment.
+ (po_callback_comment, po_callback_comment_special): New declarations.
+ * read-po-abstract.c: Include read-stringtable.h.
+ (po_scan): Add support for syntax_stringtable.
+ (po_callback_comment_dispatcher): Renamed from po_callback_comment.
+ (po_callback_comment, po_callback_comment_dot): New functions.
+ (po_callback_comment_special): New function.
+ * read-po.c (read_po): Set mdlp->encoding for syntax_stringtable too.
+ * po-gram-gen.y: Update po_callback_comment_dispatcher call.
+ * read-properties.c (properties_parse): Likewise.
+ * write-stringtable.h: New file.
+ * write-stringtable.c: New file.
+ * write-po.h (make_format_description_string, significant_format_p):
+ New declarations.
+ (message_print_syntax_stringtable): New declaration.
+ * write-po.c: Include write-stringtable.h.
+ (make_format_description_string, significant_format_p): Make
+ non-static.
+ (use_syntax_stringtable): New variable.
+ (message_print_syntax_stringtable): New function.
+ (msgdomain_list_print): Add check for plural forms for
+ syntax_stringtable too. Add support for writing the .strings format.
+ * msgattrib.c (long_options): Add --stringtable-input/output.
+ (main): Handle them.
+ (usage): Document options --stringtable-input/output.
+ * msgcat.c (long_options): Add --stringtable-input/output.
+ (main): Handle them. If option --stringtable-output is used, convert to
+ UTF-8 and ignore the to_code.
+ (usage): Document options --stringtable-input/output.
+ * msgcmp.c (long_options): Add --stringtable-input.
+ (main): Handle it.
+ (usage): Document option --stringtable-input.
+ * msgcomm.c (long_options): Add --stringtable-input/output.
+ (main): Handle them.
+ (usage): Document options --stringtable-input/output.
+ * msgconv.c (long_options): Add --stringtable-input/output.
+ (main): Handle them. If option --stringtable-output is used, ignore the
+ to_code.
+ (usage): Document options --stringtable-input/output.
+ * msgen.c (long_options): Add --stringtable-input/output.
+ (main): Handle them.
+ (usage): Document options --stringtable-input/output.
+ * msgexec.c: Include limits.h.
+ (long_options): Add --stringtable-input.
+ (main): Handle it.
+ (usage): Document option --stringtable-input.
+ * msgfilter.c (long_options): Add --stringtable-input/output.
+ (main): Handle them.
+ (usage): Document options --stringtable-input/output.
+ * msgfmt.c (long_options): Add --stringtable-input.
+ (main): Handle it.
+ (usage): Document option --stringtable-input.
+ * msggrep.c (long_options): Add --stringtable-input/output.
+ (main): Handle them.
+ (usage): Document options --stringtable-input/output.
+ * msginit.c (long_options): Add --stringtable-input/output.
+ (main): Handle them.
+ (usage): Document options --stringtable-input/output.
+ * msgmerge.c (long_options): Add --stringtable-input/output.
+ (main): Handle them. In update mode, --stringtable-input implies
+ --stringtable-output.
+ (usage): Document options --stringtable-input/output.
+ * msgunfmt.c (long_options): Add --stringtable-output.
+ (main): Handle it.
+ (usage): Document option --stringtable-output.
+ * msguniq.c (long_options): Add --stringtable-input/output.
+ (main): Handle them.
+ (usage): Document options --stringtable-input/output.
+ * x-stringtable.h: New file.
+ * x-po.c: Include x-stringtable.h.
+ (extract_stringtable): New function.
+ * xgettext.c: Include x-stringtable.h.
+ (long_options): Add --stringtable-output.
+ (main): Handle it.
+ (usage): Document options -L NXStringTable and --stringtable-output.
+ (finalize_header): If --stringtable-output was given, set the charset.
+ (language_to_extractor): Add support for .strings format.
+ (extension_to_language): Likewise.
+ * Makefile.am (noinst_HEADERS): Add read-stringtable.h,
+ write-stringtable.h, x-stringtable.h.
+ (COMMON_SOURCE): Add read-stringtable.c.
+ (libgettextsrc_la_SOURCES): Add write-stringtable.c.
+ * Makefile.msvc (OBJECTS): Add read-stringtable.obj,
+ write-stringtable.obj.
+ (read-stringtable.obj, write-stringtable.obj): New rules.
+ * Makefile.vms (OBJECTS): Add read-stringtable.obj,
+ write-stringtable.obj.
+ (read-stringtable.obj, write-stringtable.obj): New rules.
+ * FILES: Update.
+
2003-10-21 Bruno Haible <bruno@clisp.org>
* hostname.c (xgethostname): Add support for native Woe32 API.
write-properties.h
write-properties.c
Output of a list-of-messages to a Java .properties file.
+write-stringtable.h
+write-stringtable.c
+ Output of a list-of-messages to a NeXTstep/GNUstep .strings
+ file.
+-------------- Reading PO files
| open-po.h
| po-gram-gen.y
| read-properties.h
| read-properties.c
+| read-stringtable.h
+| read-stringtable.c
| read-po-abstract.c
-| Parsing of PO files and Java .properties files.
+| Parsing of PO files and Java .properties and NeXTstep/GNUstep
+| .strings files.
| read-po-abstract.h
| General parser structure.
| po-hash.h
| read-properties.h
| read-properties.c
| Parsing of Java .properties files.
+| read-stringtable.h
+| read-stringtable.c
+| Parsing of NeXTstep/GNUstep .strings files.
| read-po-abstract.c
| Top-level parser functions and callbacks.
|
| String extractor for C.
| x-po.h
| x-properties.h
+| x-stringtable.h
| x-po.c
-| String extractor from PO files and Java .properties files.
+| String extractor from PO files and Java .properties and
+| NeXTstep/GNUstep .strings files.
| x-sh.h
| x-sh.c
| String extractor for Shell.
include_HEADERS = gettext-po.h
noinst_HEADERS = pos.h message.h po-gram.h po-hash.h po-charset.h po-lex.h \
-open-po.h read-po-abstract.h read-po.h read-properties.h str-list.h \
-write-po.h write-properties.h dir-list.h file-list.h po-gram-gen.h \
-po-gram-gen2.h po-hash-gen.h msgl-charset.h msgl-equal.h msgl-iconv.h \
-msgl-ascii.h msgl-cat.h msgl-english.h msgfmt.h msgunfmt.h plural-count.h \
-read-mo.h write-mo.h read-java.h write-java.h read-tcl.h write-tcl.h \
-po-time.h plural-table.h format.h xgettext.h x-c.h x-po.h x-sh.h x-python.h \
-x-lisp.h x-elisp.h x-librep.h x-smalltalk.h x-java.h x-properties.h x-awk.h \
-x-ycp.h x-tcl.h x-perl.h x-php.h x-rst.h x-glade.h
+open-po.h read-po-abstract.h read-po.h read-properties.h read-stringtable.h \
+str-list.h write-po.h write-properties.h write-stringtable.h dir-list.h \
+file-list.h po-gram-gen.h po-gram-gen2.h po-hash-gen.h msgl-charset.h \
+msgl-equal.h msgl-iconv.h msgl-ascii.h msgl-cat.h msgl-english.h msgfmt.h \
+msgunfmt.h plural-count.h read-mo.h write-mo.h read-java.h write-java.h \
+read-tcl.h write-tcl.h po-time.h plural-table.h format.h xgettext.h x-c.h \
+x-po.h x-sh.h x-python.h x-lisp.h x-elisp.h x-librep.h x-smalltalk.h x-java.h \
+x-properties.h x-awk.h x-ycp.h x-tcl.h x-perl.h x-php.h x-stringtable.h \
+x-rst.h x-glade.h
EXTRA_DIST += FILES project-id ChangeLog.0
# (read-po-abstract.c <--> po-hash-gen.y <--> po-gram-gen.y <--> po-lex.c) -> message.c -> str-list.c.
COMMON_SOURCE = message.c \
read-po-abstract.c po-lex.c po-gram-gen.y po-hash-gen.y po-charset.c \
-read-properties.c open-po.c dir-list.c str-list.c
+read-properties.c read-stringtable.c open-po.c dir-list.c str-list.c
# xgettext and msgfmt deal with format strings.
FORMAT_SOURCE = format.c format-invalid.h \
# libgettextsrc contains all code that is needed by at least two programs.
libgettextsrc_la_SOURCES = \
-$(COMMON_SOURCE) read-po.c write-properties.c write-po.c msgl-ascii.c \
-msgl-iconv.c msgl-equal.c msgl-cat.c msgl-english.c file-list.c \
+$(COMMON_SOURCE) read-po.c write-properties.c write-stringtable.c write-po.c \
+msgl-ascii.c msgl-iconv.c msgl-equal.c msgl-cat.c msgl-english.c file-list.c \
msgl-charset.c po-time.c plural.c plural-table.c $(FORMAT_SOURCE)
# libgettextpo contains the public API for PO files.
po-hash-gen.obj \
po-charset.obj \
read-properties.obj \
+ read-stringtable.obj \
open-po.obj \
dir-list.obj \
str-list.obj \
read-po.obj \
write-properties.obj \
+ write-stringtable.obj \
write-po.obj \
msgl-ascii.obj \
msgl-iconv.obj \
read-properties.obj : read-properties.c
$(CC) $(INCLUDES) $(CFLAGS) $(PICFLAGS) -c read-properties.c
+read-stringtable.obj : read-stringtable.c
+ $(CC) $(INCLUDES) $(CFLAGS) $(PICFLAGS) -c read-stringtable.c
+
open-po.obj : open-po.c
$(CC) $(INCLUDES) $(CFLAGS) $(PICFLAGS) -c open-po.c
write-properties.obj : write-properties.c
$(CC) $(INCLUDES) $(CFLAGS) $(PICFLAGS) -c write-properties.c
+write-stringtable.obj : write-stringtable.c
+ $(CC) $(INCLUDES) $(CFLAGS) $(PICFLAGS) -c write-stringtable.c
+
write-po.obj : write-po.c
$(CC) $(INCLUDES) $(CFLAGS) $(PICFLAGS) -c write-po.c
po-hash-gen.obj, \
po-charset.obj, \
read-properties.obj, \
+ read-stringtable.obj, \
open-po.obj, \
dir-list.obj, \
str-list.obj, \
read-po.obj, \
write-properties.obj, \
+ write-stringtable.obj, \
write-po.obj, \
msgl-ascii.obj, \
msgl-iconv.obj, \
read-properties.obj : read-properties.c
$(CC) $(INCLUDES) $(CFLAGS) /define=($(DEFS)) read-properties.c
+read-stringtable.obj : read-stringtable.c
+ $(CC) $(INCLUDES) $(CFLAGS) /define=($(DEFS)) read-stringtable.c
+
open-po.obj : open-po.c
$(CC) $(INCLUDES) $(CFLAGS) /define=($(DEFS)) open-po.c
write-properties.obj : write-properties.c
$(CC) $(INCLUDES) $(CFLAGS) /define=($(DEFS)) write-properties.c
+write-stringtable.obj : write-stringtable.c
+ $(CC) $(INCLUDES) $(CFLAGS) /define=($(DEFS)) write-stringtable.c
+
write-po.obj : write-po.c
$(CC) $(INCLUDES) $(CFLAGS) /define=($(DEFS)) write-po.c
{ "set-obsolete", no_argument, NULL, CHAR_MAX + 9 },
{ "sort-by-file", no_argument, NULL, 'F' },
{ "sort-output", no_argument, NULL, 's' },
+ { "stringtable-input", no_argument, NULL, CHAR_MAX + 16 },
+ { "stringtable-output", no_argument, NULL, CHAR_MAX + 17 },
{ "strict", no_argument, NULL, 'S' },
{ "translated", no_argument, NULL, CHAR_MAX + 1 },
{ "untranslated", no_argument, NULL, CHAR_MAX + 2 },
ignore_file = optarg;
break;
+ case CHAR_MAX + 16: /* --stringtable-input */
+ input_syntax = syntax_stringtable;
+ break;
+
+ case CHAR_MAX + 17: /* --stringtable-output */
+ message_print_syntax_stringtable ();
+ break;
+
default:
usage (EXIT_FAILURE);
/* NOTREACHED */
Input file syntax:\n"));
printf (_("\
-P, --properties-input input file is in Java .properties syntax\n"));
+ printf (_("\
+ --stringtable-input input file is in NeXTstep/GNUstep .strings syntax\n"));
printf ("\n");
printf (_("\
Output details:\n"));
printf (_("\
-p, --properties-output write out a Java .properties file\n"));
printf (_("\
+ --stringtable-output write out a NeXTstep/GNUstep .strings file\n"));
+ printf (_("\
-w, --width=NUMBER set output page width\n"));
printf (_("\
--no-wrap do not break long message lines, longer than\n\
{ "sort-by-file", no_argument, NULL, 'F' },
{ "sort-output", no_argument, NULL, 's' },
{ "strict", no_argument, NULL, 'S' },
+ { "stringtable-input", no_argument, NULL, CHAR_MAX + 3 },
+ { "stringtable-output", no_argument, NULL, CHAR_MAX + 4 },
{ "to-code", required_argument, NULL, 't' },
{ "unique", no_argument, NULL, 'u' },
{ "use-first", no_argument, NULL, CHAR_MAX + 1 },
message_page_width_ignore ();
break;
+ case CHAR_MAX + 3: /* --stringtable-input */
+ input_syntax = syntax_stringtable;
+ break;
+
+ case CHAR_MAX + 4: /* --stringtable-output */
+ message_print_syntax_stringtable ();
+ break;
+
default:
usage (EXIT_FAILURE);
/* NOTREACHED */
/* Read input files, then filter, convert and merge messages. */
result = catenate_msgdomain_list (file_list,
output_syntax != syntax_properties
+ && output_syntax != syntax_stringtable
? to_code
: "UTF-8");
Input file syntax:\n"));
printf (_("\
-P, --properties-input input files are in Java .properties syntax\n"));
+ printf (_("\
+ --stringtable-input input files are in NeXTstep/GNUstep .strings\n\
+ syntax\n"));
printf ("\n");
printf (_("\
Output details:\n"));
printf (_("\
-p, --properties-output write out a Java .properties file\n"));
printf (_("\
+ --stringtable-output write out a NeXTstep/GNUstep .strings file\n"));
+ printf (_("\
-w, --width=NUMBER set output page width\n"));
printf (_("\
--no-wrap do not break long message lines, longer than\n\
{ "help", no_argument, NULL, 'h' },
{ "multi-domain", no_argument, NULL, 'm' },
{ "properties-input", no_argument, NULL, 'P' },
+ { "stringtable-input", no_argument, NULL, CHAR_MAX + 1 },
{ "version", no_argument, NULL, 'V' },
{ NULL, 0, NULL, 0 }
};
do_version = true;
break;
+ case CHAR_MAX + 1: /* --stringtable-input */
+ input_syntax = syntax_stringtable;
+ break;
+
default:
usage (EXIT_FAILURE);
break;
Input file syntax:\n"));
printf (_("\
-P, --properties-input input files are in Java .properties syntax\n"));
+ printf (_("\
+ --stringtable-input input files are in NeXTstep/GNUstep .strings\n\
+ syntax\n"));
printf ("\n");
printf (_("\
Informative output:\n"));
{ "sort-by-file", no_argument, NULL, 'F' },
{ "sort-output", no_argument, NULL, 's' },
{ "strict", no_argument, NULL, 'S' },
+ { "stringtable-input", no_argument, NULL, CHAR_MAX + 3 },
+ { "stringtable-output", no_argument, NULL, CHAR_MAX + 4 },
{ "to-code", required_argument, NULL, 't' },
{ "unique", no_argument, NULL, 'u' },
{ "version", no_argument, NULL, 'V' },
message_page_width_ignore ();
break;
+ case CHAR_MAX + 3: /* --stringtable-input */
+ input_syntax = syntax_stringtable;
+ break;
+
+ case CHAR_MAX + 4: /* --stringtable-output */
+ message_print_syntax_stringtable ();
+ break;
+
default:
usage (EXIT_FAILURE);
/* NOTREACHED */
Input file syntax:\n"));
printf (_("\
-P, --properties-input input files are in Java .properties syntax\n"));
+ printf (_("\
+ --stringtable-input input files are in NeXTstep/GNUstep .strings\n\
+ syntax\n"));
printf ("\n");
printf (_("\
Output details:\n"));
printf (_("\
-p, --properties-output write out a Java .properties file\n"));
printf (_("\
+ --stringtable-output write out a NeXTstep/GNUstep .strings file\n"));
+ printf (_("\
-w, --width=NUMBER set output page width\n"));
printf (_("\
--no-wrap do not break long message lines, longer than\n\
{ "sort-by-file", no_argument, NULL, 'F' },
{ "sort-output", no_argument, NULL, 's' },
{ "strict", no_argument, NULL, 'S' },
+ { "stringtable-input", no_argument, NULL, CHAR_MAX + 2 },
+ { "stringtable-output", no_argument, NULL, CHAR_MAX + 3 },
{ "to-code", required_argument, NULL, 't' },
{ "version", no_argument, NULL, 'V' },
{ "width", required_argument, NULL, 'w', },
message_page_width_ignore ();
break;
+ case CHAR_MAX + 2: /* --stringtable-input */
+ input_syntax = syntax_stringtable;
+ break;
+
+ case CHAR_MAX + 3: /* --stringtable-output */
+ message_print_syntax_stringtable ();
+ break;
+
default:
usage (EXIT_FAILURE);
break;
result = read_po_file (input_file);
/* Convert if and only if the output syntax supports different encodings. */
- if (output_syntax != syntax_properties)
+ if (output_syntax != syntax_properties
+ && output_syntax != syntax_stringtable)
result = iconv_msgdomain_list (result, to_code, input_file);
/* Sort the results. */
Input file syntax:\n"));
printf (_("\
-P, --properties-input input file is in Java .properties syntax\n"));
+ printf (_("\
+ --stringtable-input input file is in NeXTstep/GNUstep .strings syntax\n"));
printf ("\n");
printf (_("\
Output details:\n"));
printf (_("\
-p, --properties-output write out a Java .properties file\n"));
printf (_("\
+ --stringtable-output write out a NeXTstep/GNUstep .strings file\n"));
+ printf (_("\
-w, --width=NUMBER set output page width\n"));
printf (_("\
--no-wrap do not break long message lines, longer than\n\
{ "sort-by-file", no_argument, NULL, 'F' },
{ "sort-output", no_argument, NULL, 's' },
{ "strict", no_argument, NULL, 'S' },
+ { "stringtable-input", no_argument, NULL, CHAR_MAX + 2 },
+ { "stringtable-output", no_argument, NULL, CHAR_MAX + 3 },
{ "version", no_argument, NULL, 'V' },
{ "width", required_argument, NULL, 'w', },
{ NULL, 0, NULL, 0 }
message_page_width_ignore ();
break;
+ case CHAR_MAX + 2: /* --stringtable-input */
+ input_syntax = syntax_stringtable;
+ break;
+
+ case CHAR_MAX + 3: /* --stringtable-output */
+ message_print_syntax_stringtable ();
+ break;
+
default:
usage (EXIT_FAILURE);
break;
Input file syntax:\n"));
printf (_("\
-P, --properties-input input file is in Java .properties syntax\n"));
+ printf (_("\
+ --stringtable-input input file is in NeXTstep/GNUstep .strings syntax\n"));
printf ("\n");
printf (_("\
Output details:\n"));
printf (_("\
-p, --properties-output write out a Java .properties file\n"));
printf (_("\
+ --stringtable-output write out a NeXTstep/GNUstep .strings file\n"));
+ printf (_("\
-w, --width=NUMBER set output page width\n"));
printf (_("\
--no-wrap do not break long message lines, longer than\n\
#include <errno.h>
#include <getopt.h>
+#include <limits.h>
#include <locale.h>
#include <stdio.h>
#include <stdlib.h>
{ "help", no_argument, NULL, 'h' },
{ "input", required_argument, NULL, 'i' },
{ "properties-input", no_argument, NULL, 'P' },
+ { "stringtable-input", no_argument, NULL, CHAR_MAX + 1 },
{ "version", no_argument, NULL, 'V' },
{ NULL, 0, NULL, 0 }
};
do_version = true;
break;
+ case CHAR_MAX + 1: /* --stringtable-input */
+ input_syntax = syntax_stringtable;
+ break;
+
default:
usage (EXIT_FAILURE);
break;
Input file syntax:\n"));
printf (_("\
-P, --properties-input input file is in Java .properties syntax\n"));
+ printf (_("\
+ --stringtable-input input file is in NeXTstep/GNUstep .strings syntax\n"));
printf ("\n");
printf (_("\
Informative output:\n"));
{ "sort-by-file", no_argument, NULL, 'F' },
{ "sort-output", no_argument, NULL, 's' },
{ "strict", no_argument, NULL, 'S' },
+ { "stringtable-input", no_argument, NULL, CHAR_MAX + 4 },
+ { "stringtable-output", no_argument, NULL, CHAR_MAX + 5 },
{ "version", no_argument, NULL, 'V' },
{ "width", required_argument, NULL, 'w', },
{ NULL, 0, NULL, 0 }
message_page_width_ignore ();
break;
+ case CHAR_MAX + 4: /* --stringtable-input */
+ input_syntax = syntax_stringtable;
+ break;
+
+ case CHAR_MAX + 5: /* --stringtable-output */
+ message_print_syntax_stringtable ();
+ break;
+
default:
usage (EXIT_FAILURE);
break;
Input file syntax:\n"));
printf (_("\
-P, --properties-input input file is in Java .properties syntax\n"));
+ printf (_("\
+ --stringtable-input input file is in NeXTstep/GNUstep .strings syntax\n"));
printf ("\n");
printf (_("\
Output details:\n"));
printf (_("\
-p, --properties-output write out a Java .properties file\n"));
printf (_("\
+ --stringtable-output write out a NeXTstep/GNUstep .strings file\n"));
+ printf (_("\
-w, --width=NUMBER set output page width\n"));
printf (_("\
--no-wrap do not break long message lines, longer than\n\
{ "resource", required_argument, NULL, 'r' },
{ "statistics", no_argument, &do_statistics, 1 },
{ "strict", no_argument, NULL, 'S' },
+ { "stringtable-input", no_argument, NULL, CHAR_MAX + 8 },
{ "tcl", no_argument, NULL, CHAR_MAX + 7 },
{ "use-fuzzy", no_argument, NULL, 'f' },
{ "verbose", no_argument, NULL, 'v' },
case 'V':
do_version = true;
break;
- case CHAR_MAX + 1:
+ case CHAR_MAX + 1: /* --check-accelerators */
check_accelerators = true;
if (optarg != NULL)
{
"--check-accelerators");
}
break;
- case CHAR_MAX + 2:
+ case CHAR_MAX + 2: /* --check-domain */
check_domain = true;
break;
- case CHAR_MAX + 3:
+ case CHAR_MAX + 3: /* --check-format */
check_format_strings = true;
break;
- case CHAR_MAX + 4:
+ case CHAR_MAX + 4: /* --check-header */
check_header = true;
break;
- case CHAR_MAX + 5:
+ case CHAR_MAX + 5: /* --java2 */
java_mode = true;
assume_java2 = true;
break;
- case CHAR_MAX + 6:
+ case CHAR_MAX + 6: /* --no-hash */
no_hash_table = true;
break;
- case CHAR_MAX + 7:
+ case CHAR_MAX + 7: /* --tcl */
tcl_mode = true;
break;
+ case CHAR_MAX + 8: /* --stringtable-input */
+ input_syntax = syntax_stringtable;
+ break;
default:
usage (EXIT_FAILURE);
break;
++optind;
}
- /* We know a priori that properties_parse() converts strings to UTF-8. */
- canon_encoding = (input_syntax == syntax_properties ? po_charset_utf8 : NULL);
+ /* We know a priori that properties_parse() and stringtable_parse() convert
+ strings to UTF-8. */
+ canon_encoding =
+ (input_syntax == syntax_properties || input_syntax == syntax_stringtable
+ ? po_charset_utf8
+ : NULL);
/* Remove obsolete messages. They were only needed for duplicate
checking. */
Input file syntax:\n"));
printf (_("\
-P, --properties-input input files are in Java .properties syntax\n"));
+ printf (_("\
+ --stringtable-input input files are in NeXTstep/GNUstep .strings\n\
+ syntax\n"));
printf ("\n");
printf (_("\
Input file interpretation:\n"));
{ "sort-by-file", no_argument, NULL, CHAR_MAX + 4 },
{ "sort-output", no_argument, NULL, CHAR_MAX + 5 },
{ "strict", no_argument, NULL, 'S' },
+ { "stringtable-input", no_argument, NULL, CHAR_MAX + 7 },
+ { "stringtable-output", no_argument, NULL, CHAR_MAX + 8 },
{ "version", no_argument, NULL, 'V' },
{ "width", required_argument, NULL, 'w' },
{ NULL, 0, NULL, 0 }
message_page_width_ignore ();
break;
+ case CHAR_MAX + 7: /* --stringtable-input */
+ input_syntax = syntax_stringtable;
+ break;
+
+ case CHAR_MAX + 8: /* --stringtable-output */
+ message_print_syntax_stringtable ();
+ break;
+
default:
usage (EXIT_FAILURE);
break;
Input file syntax:\n"));
printf (_("\
-P, --properties-input input file is in Java .properties syntax\n"));
+ printf (_("\
+ --stringtable-input input file is in NeXTstep/GNUstep .strings syntax\n"));
printf ("\n");
printf (_("\
Output details:\n"));
printf (_("\
-p, --properties-output write out a Java .properties file\n"));
printf (_("\
+ --stringtable-output write out a NeXTstep/GNUstep .strings file\n"));
+ printf (_("\
-w, --width=NUMBER set output page width\n"));
printf (_("\
--no-wrap do not break long message lines, longer than\n\
{ "output-file", required_argument, NULL, 'o' },
{ "properties-input", no_argument, NULL, 'P' },
{ "properties-output", no_argument, NULL, 'p' },
+ { "stringtable-input", no_argument, NULL, CHAR_MAX + 3 },
+ { "stringtable-output", no_argument, NULL, CHAR_MAX + 4 },
{ "version", no_argument, NULL, 'V' },
{ "width", required_argument, NULL, 'w' },
{ NULL, 0, NULL, 0 }
message_page_width_ignore ();
break;
+ case CHAR_MAX + 3: /* --stringtable-input */
+ input_syntax = syntax_stringtable;
+ break;
+
+ case CHAR_MAX + 4: /* --stringtable-output */
+ message_print_syntax_stringtable ();
+ break;
+
default:
usage (EXIT_FAILURE);
break;
Input file syntax:\n"));
printf (_("\
-P, --properties-input input file is in Java .properties syntax\n"));
+ printf (_("\
+ --stringtable-input input file is in NeXTstep/GNUstep .strings syntax\n"));
printf ("\n");
printf (_("\
Output details:\n"));
printf (_("\
-p, --properties-output write out a Java .properties file\n"));
printf (_("\
+ --stringtable-output write out a NeXTstep/GNUstep .strings file\n"));
+ printf (_("\
-w, --width=NUMBER set output page width\n"));
printf (_("\
--no-wrap do not break long message lines, longer than\n\
{ "sort-output", no_argument, NULL, 's' },
{ "silent", no_argument, NULL, 'q' },
{ "strict", no_argument, NULL, CHAR_MAX + 2 },
+ { "stringtable-input", no_argument, NULL, CHAR_MAX + 5 },
+ { "stringtable-output", no_argument, NULL, CHAR_MAX + 6 },
{ "suffix", required_argument, NULL, CHAR_MAX + 3 },
{ "update", no_argument, NULL, 'U' },
{ "verbose", no_argument, NULL, 'v' },
message_page_width_ignore ();
break;
+ case CHAR_MAX + 5: /* --stringtable-input */
+ input_syntax = syntax_stringtable;
+ break;
+
+ case CHAR_MAX + 6: /* --stringtable-output */
+ message_print_syntax_stringtable ();
+ break;
+
default:
usage (EXIT_FAILURE);
break;
/* In update mode, --properties-input implies --properties-output. */
if (update_mode && input_syntax == syntax_properties)
message_print_syntax_properties ();
+ /* In update mode, --stringtable-input implies --stringtable-output. */
+ if (update_mode && input_syntax == syntax_stringtable)
+ message_print_syntax_stringtable ();
/* Merge the two files. */
result = merge (argv[optind], argv[optind + 1], &def);
Input file syntax:\n"));
printf (_("\
-P, --properties-input input files are in Java .properties syntax\n"));
+ printf (_("\
+ --stringtable-input input files are in NeXTstep/GNUstep .strings\n\
+ syntax\n"));
printf ("\n");
printf (_("\
Output details:\n"));
printf (_("\
-p, --properties-output write out a Java .properties file\n"));
printf (_("\
+ --stringtable-output write out a NeXTstep/GNUstep .strings file\n"));
+ printf (_("\
-w, --width=NUMBER set output page width\n"));
printf (_("\
--no-wrap do not break long message lines, longer than\n\
{ "resource", required_argument, NULL, 'r' },
{ "sort-output", no_argument, NULL, 's' },
{ "strict", no_argument, NULL, 'S' },
+ { "stringtable-output", no_argument, NULL, CHAR_MAX + 3 },
{ "tcl", no_argument, NULL, CHAR_MAX + 1 },
{ "verbose", no_argument, NULL, 'v' },
{ "version", no_argument, NULL, 'V' },
message_page_width_ignore ();
break;
+ case CHAR_MAX + 3: /* --stringtable-output */
+ message_print_syntax_stringtable ();
+ break;
+
default:
usage (EXIT_FAILURE);
break;
printf (_("\
-p, --properties-output write out a Java .properties file\n"));
printf (_("\
+ --stringtable-output write out a NeXTstep/GNUstep .strings file\n"));
+ printf (_("\
-w, --width=NUMBER set output page width\n"));
printf (_("\
--no-wrap do not break long message lines, longer than\n\
{ "sort-by-file", no_argument, NULL, 'F' },
{ "sort-output", no_argument, NULL, 's' },
{ "strict", no_argument, NULL, 'S' },
+ { "stringtable-input", no_argument, NULL, CHAR_MAX + 3 },
+ { "stringtable-output", no_argument, NULL, CHAR_MAX + 4 },
{ "to-code", required_argument, NULL, 't' },
{ "unique", no_argument, NULL, 'u' },
{ "use-first", no_argument, NULL, CHAR_MAX + 1 },
message_page_width_ignore ();
break;
+ case CHAR_MAX + 3: /* --stringtable-input */
+ input_syntax = syntax_stringtable;
+ break;
+
+ case CHAR_MAX + 4: /* --stringtable-output */
+ message_print_syntax_stringtable ();
+ break;
+
default:
usage (EXIT_FAILURE);
/* NOTREACHED */
Input file syntax:\n"));
printf (_("\
-P, --properties-input input file is in Java .properties syntax\n"));
+ printf (_("\
+ --stringtable-input input file is in NeXTstep/GNUstep .strings syntax\n"));
printf ("\n");
printf (_("\
Output details:\n"));
printf (_("\
-p, --properties-output write out a Java .properties file\n"));
printf (_("\
+ --stringtable-output write out a NeXTstep/GNUstep .strings file\n"));
+ printf (_("\
-w, --width=NUMBER set output page width\n"));
printf (_("\
--no-wrap do not break long message lines, longer than\n\
comment
: COMMENT
{
- po_callback_comment ($1.string);
+ po_callback_comment_dispatcher ($1.string);
}
;
#include "po-gram.h"
#include "po-hash.h"
#include "read-properties.h"
+#include "read-stringtable.h"
#include "xalloc.h"
#include "gettext.h"
properties_parse (pop, fp, real_filename, logical_filename);
po_scan_end (pop);
break;
+ case syntax_stringtable:
+ po_scan_start (pop);
+ stringtable_parse (pop, fp, real_filename, logical_filename);
+ po_scan_end (pop);
+ break;
default:
abort ();
}
}
+void
+po_callback_comment (const char *s)
+{
+ /* assert(callback_arg); */
+ call_comment (callback_arg, s);
+}
+
+
+void
+po_callback_comment_dot (const char *s)
+{
+ /* assert(callback_arg); */
+ call_comment_dot (callback_arg, s);
+}
+
+
/* This function is called by po_parse_comment_filepos(), once for each
filename. */
void
}
+void
+po_callback_comment_special (const char *s)
+{
+ /* assert(callback_arg); */
+ call_comment_special (callback_arg, s);
+}
+
+
/* Parse a special comment and put the result in *fuzzyp, formatp, *wrapp. */
void
po_parse_comment_special (const char *s,
call_comment_filepos (via po_parse_comment_filepos), or
call_comment_special. */
void
-po_callback_comment (const char *s)
+po_callback_comment_dispatcher (const char *s)
{
- /* assert(callback_arg); */
if (*s == '.')
- call_comment_dot (callback_arg, s + 1);
+ po_callback_comment_dot (s + 1);
else if (*s == ':')
{
/* Parse the file location string. If the parse succeeds, the
if (po_parse_comment_filepos (s + 1) == 0)
/* Do nothing, it is a GNU-style file pos line. */ ;
else
- call_comment (callback_arg, s + 1);
+ po_callback_comment (s + 1);
}
else if (*s == ',' || *s == '!')
{
/* Get all entries in the special comment line. */
- call_comment_special (callback_arg, s + 1);
+ po_callback_comment_special (s + 1);
}
else
{
&& po_parse_comment_filepos (s) == 0)
/* Do nothing, it is a Sun-style file pos line. */ ;
else
- call_comment (callback_arg, s);
+ po_callback_comment (s);
}
}
enum input_syntax_ty
{
syntax_po,
- syntax_properties
+ syntax_properties,
+ syntax_stringtable
};
typedef enum input_syntax_ty input_syntax_ty;
extern void po_callback_comment (const char *s);
extern void po_callback_comment_dot (const char *s);
extern void po_callback_comment_filepos (const char *s, size_t line);
+extern void po_callback_comment_special (const char *s);
+extern void po_callback_comment_dispatcher (const char *s);
/* Parse a special comment and put the result in *fuzzyp, formatp, *wrapp. */
extern void po_parse_comment_special (const char *s, bool *fuzzyp,
pop->allow_duplicates_if_same_msgstr = false;
pop->mdlp = msgdomain_list_alloc (!pop->allow_duplicates);
pop->mlp = msgdomain_list_sublist (pop->mdlp, pop->domain, true);
- if (input_syntax == syntax_properties)
- /* We know a priori that properties_parse() converts strings to UTF-8. */
+ if (input_syntax == syntax_properties || input_syntax == syntax_stringtable)
+ /* We know a priori that properties_parse() and stringtable_parse()
+ convert strings to UTF-8. */
pop->mdlp->encoding = po_charset_utf8;
po_lex_pass_obsolete_entries (true);
po_scan ((abstract_po_reader_ty *) pop, fp, real_filename, logical_filename,
}
buffer[buflen] = '\0';
- po_callback_comment (conv_from_java (conv_from_iso_8859_1 (buffer)));
+ po_callback_comment_dispatcher (conv_from_java (conv_from_iso_8859_1 (buffer)));
}
else
{
--- /dev/null
+/* Reading NeXTstep/GNUstep .strings files.
+ Copyright (C) 2003 Free Software Foundation, Inc.
+ Written by Bruno Haible <bruno@clisp.org>, 2003.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2, or (at your option)
+ any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software Foundation,
+ Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+/* Specification. */
+#include "read-stringtable.h"
+
+#include <assert.h>
+#include <errno.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "error.h"
+#include "error-progname.h"
+#include "read-po-abstract.h"
+#include "po-hash.h"
+#include "xalloc.h"
+#include "exit.h"
+#include "utf8-ucs4.h"
+#include "ucs4-utf8.h"
+#include "gettext.h"
+
+#define _(str) gettext (str)
+
+/* The format of NeXTstep/GNUstep .strings files is documented in
+ gnustep-base-1.8.0/Tools/make_strings/Using.txt
+ and in the comments of method propertyListFromStringsFileFormat in
+ gnustep-base-1.8.0/Source/NSString.m
+ In summary, it's a Objective-C like file with pseudo-assignments of the form
+ "key" = "value";
+ where the key is the msgid and the value is the msgstr.
+
+ The implementation of the parser of .strings files is in
+ gnustep-base-1.8.0/Source/NSString.m
+ function GSPropertyListFromStringsFormat
+ (indirectly called from NSBundle's method localizedStringForKey).
+
+ A test case is in
+ gnustep-base-1.8.0/Testing/English.lproj/NXStringTable.example
+ */
+
+/* Handling of comments: We copy all comments from the .strings file to
+ the PO file. This is not really needed; it's a service for translators
+ who don't like PO files and prefer to maintain the .strings file. */
+
+
+/* Real filename, used in error messages about the input file. */
+static const char *real_file_name;
+
+/* File name and line number. */
+extern lex_pos_ty gram_pos;
+
+/* The input file stream. */
+static FILE *fp;
+
+
+/* Phase 1: Read a byte.
+ Max. 4 pushback characters. */
+
+static unsigned char phase1_pushback[4];
+static int phase1_pushback_length;
+
+static int
+phase1_getc ()
+{
+ int c;
+
+ if (phase1_pushback_length)
+ return phase1_pushback[--phase1_pushback_length];
+
+ c = getc (fp);
+
+ if (c == EOF)
+ {
+ if (ferror (fp))
+ error (EXIT_FAILURE, errno, _("error while reading \"%s\""),
+ real_file_name);
+ return EOF;
+ }
+
+ return c;
+}
+
+static void
+phase1_ungetc (int c)
+{
+ if (c != EOF)
+ phase1_pushback[phase1_pushback_length++] = c;
+}
+
+
+/* Phase 2: Read an UCS-4 character.
+ Max. 2 pushback characters. */
+
+/* End-of-file indicator for functions returning an UCS-4 character. */
+#define UEOF -1
+
+static int phase2_pushback[4];
+static int phase2_pushback_length;
+
+/* The input file can be in Unicode encoding (UCS-2BE, UCS-2LE, UTF-8, each
+ with a BOM!), or otherwise the locale-dependent default encoding is used.
+ Since we don't want to depend on the locale here, we use ISO-8859-1
+ instead. */
+enum enc
+{
+ enc_undetermined,
+ enc_ucs2be,
+ enc_ucs2le,
+ enc_utf8,
+ enc_iso8859_1
+};
+static enum enc encoding;
+
+static int
+phase2_getc ()
+{
+ if (phase2_pushback_length)
+ return phase2_pushback[--phase2_pushback_length];
+
+ if (encoding == enc_undetermined)
+ {
+ /* Determine the input file's encoding. */
+ int c0, c1;
+
+ c0 = phase1_getc ();
+ if (c0 == EOF)
+ return UEOF;
+ c1 = phase1_getc ();
+ if (c1 == EOF)
+ {
+ phase1_ungetc (c0);
+ encoding = enc_iso8859_1;
+ }
+ else if (c0 == 0xfe && c1 == 0xff)
+ encoding = enc_ucs2be;
+ else if (c0 == 0xff && c1 == 0xfe)
+ encoding = enc_ucs2le;
+ else
+ {
+ int c2;
+
+ c2 = phase1_getc ();
+ if (c2 == EOF)
+ {
+ phase1_ungetc (c1);
+ phase1_ungetc (c0);
+ encoding = enc_iso8859_1;
+ }
+ else if (c0 == 0xef && c1 == 0xbb && c2 == 0xbf)
+ encoding = enc_utf8;
+ else
+ {
+ phase1_ungetc (c2);
+ phase1_ungetc (c1);
+ phase1_ungetc (c0);
+ encoding = enc_iso8859_1;
+ }
+ }
+ }
+
+ switch (encoding)
+ {
+ case enc_ucs2be:
+ /* Read an UCS-2BE encoded character. */
+ {
+ int c0, c1;
+
+ c0 = phase1_getc ();
+ if (c0 == EOF)
+ return UEOF;
+ c1 = phase1_getc ();
+ if (c1 == EOF)
+ return UEOF;
+ return (c0 << 8) + c1;
+ }
+
+ case enc_ucs2le:
+ /* Read an UCS-2LE encoded character. */
+ {
+ int c0, c1;
+
+ c0 = phase1_getc ();
+ if (c0 == EOF)
+ return UEOF;
+ c1 = phase1_getc ();
+ if (c1 == EOF)
+ return UEOF;
+ return c0 + (c1 << 8);
+ }
+
+ case enc_utf8:
+ /* Read an UTF-8 encoded character. */
+ {
+ unsigned char buf[6];
+ unsigned int count;
+ int c;
+ unsigned int uc;
+
+ c = phase1_getc ();
+ if (c == EOF)
+ return UEOF;
+ buf[0] = c;
+ count = 1;
+
+ if (buf[0] >= 0xc0)
+ {
+ c = phase1_getc ();
+ if (c == EOF)
+ return UEOF;
+ buf[1] = c;
+ count = 2;
+ }
+
+ if (buf[0] >= 0xe0
+ && ((buf[1] ^ 0x80) < 0x40))
+ {
+ c = phase1_getc ();
+ if (c == EOF)
+ return UEOF;
+ buf[2] = c;
+ count = 3;
+ }
+
+ if (buf[0] >= 0xf0
+ && ((buf[1] ^ 0x80) < 0x40)
+ && ((buf[2] ^ 0x80) < 0x40))
+ {
+ c = phase1_getc ();
+ if (c == EOF)
+ return UEOF;
+ buf[3] = c;
+ count = 4;
+ }
+
+ if (buf[0] >= 0xf8
+ && ((buf[1] ^ 0x80) < 0x40)
+ && ((buf[2] ^ 0x80) < 0x40)
+ && ((buf[3] ^ 0x80) < 0x40))
+ {
+ c = phase1_getc ();
+ if (c == EOF)
+ return UEOF;
+ buf[4] = c;
+ count = 5;
+ }
+
+ if (buf[0] >= 0xfc
+ && ((buf[1] ^ 0x80) < 0x40)
+ && ((buf[2] ^ 0x80) < 0x40)
+ && ((buf[3] ^ 0x80) < 0x40)
+ && ((buf[4] ^ 0x80) < 0x40))
+ {
+ c = phase1_getc ();
+ if (c == EOF)
+ return UEOF;
+ buf[5] = c;
+ count = 6;
+ }
+
+ u8_mbtouc (&uc, buf, count);
+ return uc;
+ }
+
+ case enc_iso8859_1:
+ /* Read an ISO-8859-1 encoded character. */
+ {
+ int c = phase1_getc ();
+
+ if (c == EOF)
+ return UEOF;
+ return c;
+ }
+
+ default:
+ abort ();
+ }
+}
+
+static void
+phase2_ungetc (int c)
+{
+ if (c != UEOF)
+ phase2_pushback[phase2_pushback_length++] = c;
+}
+
+
+/* Phase 3: Read an UCS-4 character, with line number handling. */
+
+static int
+phase3_getc ()
+{
+ int c = phase2_getc ();
+
+ if (c == '\n')
+ gram_pos.line_number++;
+
+ return c;
+}
+
+static void
+phase3_ungetc (int c)
+{
+ if (c == '\n')
+ --gram_pos.line_number;
+ phase2_ungetc (c);
+}
+
+
+/* Convert from UCS-4 to UTF-8. */
+static char *
+conv_from_ucs4 (const int *buffer, size_t buflen)
+{
+ unsigned char *utf8_string;
+ size_t pos;
+ unsigned char *q;
+
+ /* Each UCS-4 word needs 6 bytes at worst. */
+ utf8_string = (unsigned char *) xmalloc (6 * buflen + 1);
+
+ for (pos = 0, q = utf8_string; pos < buflen; )
+ {
+ unsigned int uc;
+ int n;
+
+ uc = buffer[pos++];
+ n = u8_uctomb (q, uc, 6);
+ assert (n > 0);
+ q += n;
+ }
+ *q = '\0';
+ assert (q - utf8_string <= 6 * buflen);
+
+ return (char *) utf8_string;
+}
+
+
+/* Parse a string enclosed in double-quotes. Input is UCS-4 encoded.
+ Return the string in UTF-8 encoding, or NULL if the input doesn't represent
+ a valid string enclosed in double-quotes. */
+static char *
+parse_escaped_string (const int *string, size_t length)
+{
+ static int *buffer;
+ static size_t bufmax;
+ static size_t buflen;
+ const int *string_limit = string + length;
+ int c;
+
+ if (string == string_limit)
+ return NULL;
+ c = *string++;
+ if (c != '"')
+ return NULL;
+ buflen = 0;
+ for (;;)
+ {
+ if (string == string_limit)
+ return NULL;
+ c = *string++;
+ if (c == '"')
+ break;
+ if (c == '\\')
+ {
+ if (string == string_limit)
+ return NULL;
+ c = *string++;
+ if (c >= '0' && c <= '7')
+ {
+ unsigned int n = 0;
+ int j = 0;
+ for (;;)
+ {
+ n = n * 8 + (c - '0');
+ if (++j == 3)
+ break;
+ if (string == string_limit)
+ break;
+ c = *string;
+ if (!(c >= '0' && c <= '7'))
+ break;
+ string++;
+ }
+ c = n;
+ }
+ else if (c == 'u' || c == 'U')
+ {
+ unsigned int n = 0;
+ int j;
+ for (j = 0; j < 4; j++)
+ {
+ if (string == string_limit)
+ break;
+ c = *string;
+ if (c >= '0' && c <= '9')
+ n = n * 16 + (c - '0');
+ else if (c >= 'A' && c <= 'F')
+ n = n * 16 + (c - 'A' + 10);
+ else if (c >= 'a' && c <= 'f')
+ n = n * 16 + (c - 'a' + 10);
+ else
+ break;
+ string++;
+ }
+ c = n;
+ }
+ else
+ switch (c)
+ {
+ case 'a': c = '\a'; break;
+ case 'b': c = '\b'; break;
+ case 't': c = '\t'; break;
+ case 'r': c = '\r'; break;
+ case 'n': c = '\n'; break;
+ case 'v': c = '\v'; break;
+ case 'f': c = '\f'; break;
+ }
+ }
+ if (buflen >= bufmax)
+ {
+ bufmax = 2 * bufmax + 10;
+ buffer = xrealloc (buffer, bufmax * sizeof (int));
+ }
+ buffer[buflen++] = c;
+ }
+
+ return conv_from_ucs4 (buffer, buflen);
+}
+
+
+/* Accumulating flag comments. */
+
+static char *special_comment;
+
+static inline void
+special_comment_reset ()
+{
+ if (special_comment != NULL)
+ free (special_comment);
+ special_comment = NULL;
+}
+
+static void
+special_comment_add (const char *flag)
+{
+ if (special_comment == NULL)
+ special_comment = xstrdup (flag);
+ else
+ {
+ size_t total_len = strlen (special_comment) + 2 + strlen (flag) + 1;
+ special_comment = xrealloc (special_comment, total_len);
+ strcat (special_comment, ", ");
+ strcat (special_comment, flag);
+ }
+}
+
+static inline void
+special_comment_finish ()
+{
+ if (special_comment != NULL)
+ {
+ po_callback_comment_special (special_comment);
+ free (special_comment);
+ special_comment = NULL;
+ }
+}
+
+
+/* Accumulating comments. */
+
+static int *buffer;
+static size_t bufmax;
+static size_t buflen;
+static bool next_is_obsolete;
+static bool next_is_fuzzy;
+static char *fuzzy_msgstr;
+static bool expect_fuzzy_msgstr_as_c_comment;
+static bool expect_fuzzy_msgstr_as_cxx_comment;
+
+static inline void
+comment_start ()
+{
+ buflen = 0;
+}
+
+static inline void
+comment_add (int c)
+{
+ if (buflen >= bufmax)
+ {
+ bufmax = 2 * bufmax + 10;
+ buffer = xrealloc (buffer, bufmax * sizeof (int));
+ }
+ buffer[buflen++] = c;
+}
+
+static inline void
+comment_line_end (size_t chars_to_remove, bool test_for_fuzzy_msgstr)
+{
+ char *line;
+
+ buflen -= chars_to_remove;
+ /* Drop trailing white space, but not EOLs. */
+ while (buflen >= 1
+ && (buffer[buflen - 1] == ' ' || buffer[buflen - 1] == '\t'))
+ --buflen;
+
+ /* At special positions we interpret a comment of the form
+ = "escaped string"
+ with an optional trailing semicolon as being the fuzzy msgstr, not a
+ regular comment. */
+ if (test_for_fuzzy_msgstr
+ && buflen > 2 && buffer[0] == '=' && buffer[1] == ' '
+ && (fuzzy_msgstr =
+ parse_escaped_string (buffer + 2,
+ buflen - (buffer[buflen - 1] == ';') - 2)))
+ return;
+
+ line = conv_from_ucs4 (buffer, buflen);
+
+ if (strcmp (line, "Flag: untranslated") == 0)
+ {
+ special_comment_add ("fuzzy");
+ next_is_fuzzy = true;
+ }
+ else if (strcmp (line, "Flag: unmatched") == 0)
+ next_is_obsolete = true;
+ else if (strlen (line) >= 6 && memcmp (line, "Flag: ", 6) == 0)
+ special_comment_add (line + 6);
+ else if (strlen (line) >= 9 && memcmp (line, "Comment: ", 9) == 0)
+ /* A comment extracted from the source. */
+ po_callback_comment_dot (line + 9);
+ else
+ {
+ char *last_colon;
+ unsigned long number;
+ char *endp;
+
+ if (strlen (line) >= 6 && memcmp (line, "File: ", 6) == 0
+ && (last_colon = strrchr (line + 6, ':')) != NULL
+ && *(last_colon + 1) != '\0'
+ && (number = strtoul (last_colon + 1, &endp, 10), *endp == '\0'))
+ {
+ /* A "File: <filename>:<number>" type comment. */
+ *last_colon = '\0';
+ po_callback_comment_filepos (line + 6, number);
+ }
+ else
+ po_callback_comment (line);
+ }
+}
+
+
+/* Phase 4: Replace each comment that is not inside a string with a space
+ character. */
+
+static int
+phase4_getc ()
+{
+ int c;
+
+ c = phase3_getc ();
+ if (c != '/')
+ return c;
+ c = phase3_getc ();
+ switch (c)
+ {
+ default:
+ phase3_ungetc (c);
+ return '/';
+
+ case '*':
+ /* C style comment. */
+ {
+ bool last_was_star;
+ size_t trailing_stars;
+ bool seen_newline;
+
+ comment_start ();
+ last_was_star = false;
+ trailing_stars = 0;
+ seen_newline = false;
+ /* Drop additional stars at the beginning of the comment. */
+ for (;;)
+ {
+ c = phase3_getc ();
+ if (c != '*')
+ break;
+ last_was_star = true;
+ }
+ phase3_ungetc (c);
+ for (;;)
+ {
+ c = phase3_getc ();
+ if (c == UEOF)
+ break;
+ /* We skip all leading white space, but not EOLs. */
+ if (!(buflen == 0 && (c == ' ' || c == '\t')))
+ comment_add (c);
+ switch (c)
+ {
+ case '\n':
+ seen_newline = true;
+ comment_line_end (1, false);
+ comment_start ();
+ last_was_star = false;
+ trailing_stars = 0;
+ continue;
+
+ case '*':
+ last_was_star = true;
+ trailing_stars++;
+ continue;
+
+ case '/':
+ if (last_was_star)
+ {
+ /* Drop additional stars at the end of the comment. */
+ comment_line_end (trailing_stars + 1,
+ expect_fuzzy_msgstr_as_c_comment
+ && !seen_newline);
+ break;
+ }
+ /* FALLTHROUGH */
+
+ default:
+ last_was_star = false;
+ trailing_stars = 0;
+ continue;
+ }
+ break;
+ }
+ return ' ';
+ }
+
+ case '/':
+ /* C++ style comment. */
+ comment_start ();
+ for (;;)
+ {
+ c = phase3_getc ();
+ if (c == '\n' || c == UEOF)
+ break;
+ /* We skip all leading white space, but not EOLs. */
+ if (!(buflen == 0 && (c == ' ' || c == '\t')))
+ comment_add (c);
+ }
+ comment_line_end (0, expect_fuzzy_msgstr_as_cxx_comment);
+ return '\n';
+ }
+}
+
+static inline void
+phase4_ungetc (int c)
+{
+ phase3_ungetc (c);
+}
+
+
+/* Return true if a character is considered as whitespace. */
+static bool
+is_whitespace (int c)
+{
+ return (c == ' ' || c == '\t' || c == '\r' || c == '\n' || c == '\f'
+ || c == '\b');
+}
+
+/* Return true if a character needs quoting, i.e. cannot be used in unquoted
+ tokens. */
+static bool
+is_quotable (int c)
+{
+ if ((c >= '0' && c <= '9')
+ || (c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z'))
+ return false;
+ switch (c)
+ {
+ case '!': case '#': case '$': case '%': case '&': case '*':
+ case '+': case '-': case '.': case '/': case ':': case '?':
+ case '@': case '|': case '~': case '_': case '^':
+ return false;
+ default:
+ return true;
+ }
+}
+
+
+/* Read a key or value string.
+ Return the string in UTF-8 encoding, or NULL if no string is seen.
+ Return the start position of the string in *pos. */
+static char *
+read_string (lex_pos_ty *pos)
+{
+ static int *buffer;
+ static size_t bufmax;
+ static size_t buflen;
+ int c;
+
+ /* Skip whitespace before the string. */
+ do
+ c = phase4_getc ();
+ while (is_whitespace (c));
+
+ if (c == UEOF)
+ /* No more string. */
+ return NULL;
+
+ *pos = gram_pos;
+ buflen = 0;
+ if (c == '"')
+ {
+ /* Read a string enclosed in double-quotes. */
+ for (;;)
+ {
+ c = phase3_getc ();
+ if (c == UEOF || c == '"')
+ break;
+ if (c == '\\')
+ {
+ c = phase3_getc ();
+ if (c == UEOF)
+ break;
+ if (c >= '0' && c <= '7')
+ {
+ unsigned int n = 0;
+ int j = 0;
+ for (;;)
+ {
+ n = n * 8 + (c - '0');
+ if (++j == 3)
+ break;
+ c = phase3_getc ();
+ if (!(c >= '0' && c <= '7'))
+ {
+ phase3_ungetc (c);
+ break;
+ }
+ }
+ c = n;
+ }
+ else if (c == 'u' || c == 'U')
+ {
+ unsigned int n = 0;
+ int j;
+ for (j = 0; j < 4; j++)
+ {
+ c = phase3_getc ();
+ if (c >= '0' && c <= '9')
+ n = n * 16 + (c - '0');
+ else if (c >= 'A' && c <= 'F')
+ n = n * 16 + (c - 'A' + 10);
+ else if (c >= 'a' && c <= 'f')
+ n = n * 16 + (c - 'a' + 10);
+ else
+ {
+ phase3_ungetc (c);
+ break;
+ }
+ }
+ c = n;
+ }
+ else
+ switch (c)
+ {
+ case 'a': c = '\a'; break;
+ case 'b': c = '\b'; break;
+ case 't': c = '\t'; break;
+ case 'r': c = '\r'; break;
+ case 'n': c = '\n'; break;
+ case 'v': c = '\v'; break;
+ case 'f': c = '\f'; break;
+ }
+ }
+ if (buflen >= bufmax)
+ {
+ bufmax = 2 * bufmax + 10;
+ buffer = xrealloc (buffer, bufmax * sizeof (int));
+ }
+ buffer[buflen++] = c;
+ }
+ if (c == UEOF)
+ {
+ error_with_progname = false;
+ error (0, 0, _("%s:%d: warning: unterminated string"),
+ real_file_name, gram_pos.line_number);
+ error_with_progname = true;
+ }
+ }
+ else
+ {
+ /* Read a token outside quotes. */
+ if (is_quotable (c))
+ {
+ error_with_progname = false;
+ error (0, 0, _("%s:%d: warning: syntax error"),
+ real_file_name, gram_pos.line_number);
+ error_with_progname = true;
+ }
+ for (; c != UEOF && !is_quotable (c); c = phase4_getc ())
+ {
+ if (buflen >= bufmax)
+ {
+ bufmax = 2 * bufmax + 10;
+ buffer = xrealloc (buffer, bufmax * sizeof (int));
+ }
+ buffer[buflen++] = c;
+ }
+ }
+
+ return conv_from_ucs4 (buffer, buflen);
+}
+
+
+/* Read a .strings file from a stream, and dispatch to the various
+ abstract_po_reader_class_ty methods. */
+void
+stringtable_parse (abstract_po_reader_ty *pop, FILE *file,
+ const char *real_filename, const char *logical_filename)
+{
+ fp = file;
+ real_file_name = real_filename;
+ gram_pos.file_name = xstrdup (real_file_name);
+ gram_pos.line_number = 1;
+ encoding = enc_undetermined;
+ expect_fuzzy_msgstr_as_c_comment = false;
+ expect_fuzzy_msgstr_as_cxx_comment = false;
+
+ for (;;)
+ {
+ char *msgid;
+ lex_pos_ty msgid_pos;
+ char *msgstr;
+ lex_pos_ty msgstr_pos;
+ int c;
+
+ /* Prepare for next msgid/msgstr pair. */
+ special_comment_reset ();
+ next_is_obsolete = false;
+ next_is_fuzzy = false;
+ fuzzy_msgstr = NULL;
+
+ /* Read the key and all the comments preceding it. */
+ msgid = read_string (&msgid_pos);
+ if (msgid == NULL)
+ break;
+
+ special_comment_finish ();
+
+ /* Skip whitespace. */
+ do
+ c = phase4_getc ();
+ while (is_whitespace (c));
+
+ /* Expect a '=' or ';'. */
+ if (c == UEOF)
+ {
+ error_with_progname = false;
+ error (0, 0, _("%s:%d: warning: unterminated key/value pair"),
+ real_file_name, gram_pos.line_number);
+ error_with_progname = true;
+ break;
+ }
+ if (c == ';')
+ {
+ /* "key"; is an abbreviation for "key"=""; and does not
+ necessarily designate an untranslated entry. */
+ msgstr = "";
+ msgstr_pos = msgid_pos;
+ po_callback_message (msgid, &msgid_pos, NULL,
+ msgstr, strlen (msgstr) + 1, &msgstr_pos,
+ false, next_is_obsolete);
+ }
+ else if (c == '=')
+ {
+ /* Read the value. */
+ msgstr = read_string (&msgstr_pos);
+ if (msgstr == NULL)
+ {
+ error_with_progname = false;
+ error (0, 0, _("%s:%d: warning: unterminated key/value pair"),
+ real_file_name, gram_pos.line_number);
+ error_with_progname = true;
+ break;
+ }
+
+ /* Skip whitespace. But for fuzzy key/value pairs, look for the
+ tentative msgstr in the form of a C style comment. */
+ expect_fuzzy_msgstr_as_c_comment = next_is_fuzzy;
+ do
+ {
+ c = phase4_getc ();
+ if (fuzzy_msgstr != NULL)
+ expect_fuzzy_msgstr_as_c_comment = false;
+ }
+ while (is_whitespace (c));
+ expect_fuzzy_msgstr_as_c_comment = false;
+
+ /* Expect a ';'. */
+ if (c == ';')
+ {
+ /* But for fuzzy key/value pairs, look for the tentative msgstr
+ in the form of a C++ style comment. */
+ if (fuzzy_msgstr == NULL && next_is_fuzzy)
+ {
+ do
+ c = phase3_getc ();
+ while (c == ' ');
+ phase3_ungetc (c);
+
+ expect_fuzzy_msgstr_as_cxx_comment = true;
+ c = phase4_getc ();
+ phase4_ungetc (c);
+ expect_fuzzy_msgstr_as_cxx_comment = false;
+ }
+ if (fuzzy_msgstr != NULL && strcmp (msgstr, msgid) == 0)
+ msgstr = fuzzy_msgstr;
+
+ /* A key/value pair. */
+ po_callback_message (msgid, &msgid_pos, NULL,
+ msgstr, strlen (msgstr) + 1, &msgstr_pos,
+ false, next_is_obsolete);
+ }
+ else
+ {
+ error_with_progname = false;
+ error (0, 0, _("\
+%s:%d: warning: syntax error, expected ';' after string"),
+ real_file_name, gram_pos.line_number);
+ error_with_progname = true;
+ break;
+ }
+ }
+ else
+ {
+ error_with_progname = false;
+ error (0, 0, _("\
+%s:%d: warning: syntax error, expected '=' or ';' after string"),
+ real_file_name, gram_pos.line_number);
+ error_with_progname = true;
+ break;
+ }
+ }
+
+ fp = NULL;
+ real_file_name = NULL;
+ gram_pos.line_number = 0;
+}
--- /dev/null
+/* Reading NeXTstep/GNUstep .strings files.
+ Copyright (C) 2003 Free Software Foundation, Inc.
+ Written by Bruno Haible <bruno@clisp.org>, 2003.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2, or (at your option)
+ any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software Foundation,
+ Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
+
+#ifndef _READ_STRINGTABLE_H
+#define _READ_STRINGTABLE_H
+
+#include "read-po-abstract.h"
+
+/* Read a .strings file from a stream, and dispatch to the various
+ abstract_po_reader_class_ty methods. */
+extern void stringtable_parse (abstract_po_reader_ty *pop, FILE *fp,
+ const char *real_filename,
+ const char *logical_filename);
+
+#endif /* _READ_STRINGTABLE_H */
#include "linebreak.h"
#include "msgl-ascii.h"
#include "write-properties.h"
+#include "write-stringtable.h"
#include "xalloc.h"
#include "strstr.h"
#include "fwriteerror.h"
/* Convert IS_FORMAT in the context of programming language LANG to a flag
string for use in #, flags. */
-static const char *
+const char *
make_format_description_string (enum is_format is_format, const char *lang,
bool debug)
{
/* Return true if IS_FORMAT is worth mentioning in a #, flags list. */
-static bool
+bool
significant_format_p (enum is_format is_format)
{
return is_format != undecided && is_format != impossible;
}
+/* Whether to output a file in NeXTstep/GNUstep .strings syntax. */
+static bool use_syntax_stringtable = false;
+
+void
+message_print_syntax_stringtable ()
+{
+ use_syntax_stringtable = true;
+}
+
+
/* ================ msgdomain_list_print() and subroutines. ================ */
}
/* Check whether the output format can accomodate all messages. */
- if (use_syntax_properties)
+ if (use_syntax_properties || use_syntax_stringtable)
{
if (mdlp->nitems > 1)
- error (EXIT_FAILURE, 0, _("Cannot output multiple translation domains into a single file with Java .properties syntax. Try using PO file syntax instead."));
+ {
+ if (use_syntax_properties)
+ error (EXIT_FAILURE, 0, _("Cannot output multiple translation domains into a single file with Java .properties syntax. Try using PO file syntax instead."));
+ if (use_syntax_stringtable)
+ error (EXIT_FAILURE, 0, _("Cannot output multiple translation domains into a single file with NeXTstep/GNUstep .strings syntax."));
+ }
if (mdlp->nitems == 1)
{
message_list_ty *mlp = mdlp->item[0]->messages;
if (has_plural != NULL)
{
error_with_progname = false;
- error_at_line (EXIT_FAILURE, 0,
- has_plural->file_name, has_plural->line_number,
- _("message catalog has plural form translations, but the output format does not support them. Try generating a Java class using \"msgfmt --java\", instead of a properties file."));
+ if (use_syntax_properties)
+ error_at_line (EXIT_FAILURE, 0,
+ has_plural->file_name, has_plural->line_number,
+ _("message catalog has plural form translations, but the output format does not support them. Try generating a Java class using \"msgfmt --java\", instead of a properties file."));
+ if (use_syntax_stringtable)
+ error_at_line (EXIT_FAILURE, 0,
+ has_plural->file_name, has_plural->line_number,
+ _("message catalog has plural form translations, but the output format does not support them."));
error_with_progname = true;
}
}
if (use_syntax_properties)
msgdomain_list_print_properties (mdlp, fp, page_width, debug);
+ else if (use_syntax_stringtable)
+ msgdomain_list_print_stringtable (mdlp, fp, page_width, debug);
else
msgdomain_list_print_po (mdlp, fp, debug);
#endif
+/* These functions are used to output a #, flags line. */
+extern const char *
+ make_format_description_string (enum is_format is_format,
+ const char *lang, bool debug);
+extern bool
+ significant_format_p (enum is_format is_format);
+
+
/* These functions output parts of a message, as comments. */
extern void
message_print_comment (const message_ty *mp, FILE *fp);
message_print_style_escape (bool flag);
extern void
message_print_syntax_properties (void);
+extern void
+ message_print_syntax_stringtable (void);
/* Output MDLP into a PO file with the given FILENAME, according to the
parameters set by the functions above. */
--- /dev/null
+/* Writing NeXTstep/GNUstep .strings files.
+ Copyright (C) 2003 Free Software Foundation, Inc.
+ Written by Bruno Haible <bruno@clisp.org>, 2003.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2, or (at your option)
+ any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software Foundation,
+ Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+/* Specification. */
+#include "write-stringtable.h"
+
+#include <stdbool.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "message.h"
+#include "msgl-ascii.h"
+#include "msgl-iconv.h"
+#include "po-charset.h"
+#include "strstr.h"
+#include "write-po.h"
+
+/* The format of NeXTstep/GNUstep .strings files is documented in
+ gnustep-base-1.8.0/Tools/make_strings/Using.txt
+ and in the comments of method propertyListFromStringsFileFormat in
+ gnustep-base-1.8.0/Source/NSString.m
+ In summary, it's a Objective-C like file with pseudo-assignments of the form
+ "key" = "value";
+ where the key is the msgid and the value is the msgstr.
+ */
+
+/* Handling of comments: We copy all comments from the PO file to the
+ .strings file. This is not really needed; it's a service for translators
+ who don't like PO files and prefer to maintain the .strings file. */
+
+/* Since the interpretation of text files in GNUstep depends on the locale's
+ encoding if they don't have a BOM, we choose one of three encodings with
+ a BOM: UCS-2BE, UCS-2LE, UTF-8. Since the first two of these don't cope
+ with all of Unicode and we don't know whether GNUstep will switch to
+ UTF-16 instead of UCS-2, we use UTF-8 with BOM. BOMs are bad because they
+ get in the way when concatenating files, but here we have no choice. */
+
+/* Writes a key or value to the file, without newline. */
+static void
+write_escaped_string (FILE *fp, const char *str)
+{
+ const char *str_limit = str + strlen (str);
+
+ putc ('"', fp);
+ while (str < str_limit)
+ {
+ unsigned char c = (unsigned char) *str++;
+
+ if (c == '\t')
+ {
+ putc ('\\', fp);
+ putc ('t', fp);
+ }
+ else if (c == '\n')
+ {
+ putc ('\\', fp);
+ putc ('n', fp);
+ }
+ else if (c == '\r')
+ {
+ putc ('\\', fp);
+ putc ('r', fp);
+ }
+ else if (c == '\f')
+ {
+ putc ('\\', fp);
+ putc ('f', fp);
+ }
+ else if (c == '\\' || c == '"')
+ {
+ putc ('\\', fp);
+ putc (c, fp);
+ }
+ else
+ putc (c, fp);
+ }
+ putc ('"', fp);
+}
+
+/* Writes a message to the file. */
+static void
+write_message (FILE *fp, const message_ty *mp, size_t page_width, bool debug)
+{
+ /* Print translator comment if available. */
+ if (mp->comment != NULL)
+ {
+ size_t j;
+
+ for (j = 0; j < mp->comment->nitems; ++j)
+ {
+ const char *s = mp->comment->item[j];
+
+ /* Test whether it is safe to output the comment in C style, or
+ whether we need C++ style for it. */
+ if (strstr (s, "*/") == NULL)
+ {
+ fputs ("/*", fp);
+ if (*s != '\0' && *s != '\n' && *s != ' ')
+ putc (' ', fp);
+ fputs (s, fp);
+ fputs (" */\n", fp);
+ }
+ else
+ do
+ {
+ const char *e;
+ fputs ("//", fp);
+ if (*s != '\0' && *s != '\n' && *s != ' ')
+ putc (' ', fp);
+ e = strchr (s, '\n');
+ if (e == NULL)
+ {
+ fputs (s, fp);
+ s = NULL;
+ }
+ else
+ {
+ fwrite (s, 1, e - s, fp);
+ s = e + 1;
+ }
+ putc ('\n', fp);
+ }
+ while (s != NULL);
+ }
+ }
+
+ /* Print xgettext extracted comments. */
+ if (mp->comment_dot != NULL)
+ {
+ size_t j;
+
+ for (j = 0; j < mp->comment_dot->nitems; ++j)
+ {
+ const char *s = mp->comment_dot->item[j];
+
+ /* Test whether it is safe to output the comment in C style, or
+ whether we need C++ style for it. */
+ if (strstr (s, "*/") == NULL)
+ {
+ fputs ("/* Comment: ", fp);
+ fputs (s, fp);
+ fputs (" */\n", fp);
+ }
+ else
+ {
+ bool first = true;
+ do
+ {
+ const char *e;
+ fputs ("//", fp);
+ if (first || (*s != '\0' && *s != '\n' && *s != ' '))
+ putc (' ', fp);
+ if (first)
+ fputs ("Comment: ", fp);
+ e = strchr (s, '\n');
+ if (e == NULL)
+ {
+ fputs (s, fp);
+ s = NULL;
+ }
+ else
+ {
+ fwrite (s, 1, e - s, fp);
+ s = e + 1;
+ }
+ putc ('\n', fp);
+ first = false;
+ }
+ while (s != NULL);
+ }
+ }
+ }
+
+ /* Print the file position comments. */
+ if (mp->filepos_count != 0)
+ {
+ size_t j;
+
+ for (j = 0; j < mp->filepos_count; ++j)
+ {
+ lex_pos_ty *pp = &mp->filepos[j];
+ char *cp = pp->file_name;
+ while (cp[0] == '.' && cp[1] == '/')
+ cp += 2;
+ fprintf (fp, "/* File: %s:%ld */\n", cp, (long) pp->line_number);
+ }
+ }
+
+ /* Print flag information in special comment. */
+ if (mp->is_fuzzy || mp->msgstr[0] == '\0')
+ fputs ("/* Flag: untranslated */\n", fp);
+ if (mp->obsolete)
+ fputs ("/* Flag: unmatched */\n", fp);
+ {
+ size_t i;
+ for (i = 0; i < NFORMATS; i++)
+ if (significant_format_p (mp->is_format[i]))
+ {
+ fputs ("/* Flag:", fp);
+ fputs (make_format_description_string (mp->is_format[i],
+ format_language[i], debug),
+ fp);
+ fputs (" */\n", fp);
+ }
+ }
+
+ /* Now write the untranslated string and the translated string. */
+ write_escaped_string (fp, mp->msgid);
+ fputs (" = ", fp);
+ if (mp->msgstr[0] != '\0')
+ {
+ if (mp->is_fuzzy)
+ {
+ /* Output the msgid as value, so that at runtime the untranslated
+ string is returned. */
+ write_escaped_string (fp, mp->msgid);
+
+ /* Output the msgstr as a comment, so that at runtime
+ propertyListFromStringsFileFormat ignores it. */
+ if (strstr (mp->msgstr, "*/") == NULL)
+ {
+ fputs (" /* = ", fp);
+ write_escaped_string (fp, mp->msgstr);
+ fputs (" */", fp);
+ }
+ else
+ {
+ fputs ("; // = ", fp);
+ write_escaped_string (fp, mp->msgstr);
+ }
+ }
+ else
+ write_escaped_string (fp, mp->msgstr);
+ }
+ else
+ {
+ /* Output the msgid as value, so that at runtime the untranslated
+ string is returned. */
+ write_escaped_string (fp, mp->msgid);
+ }
+ putc (';', fp);
+
+ putc ('\n', fp);
+}
+
+/* Writes an entire message list to the file. */
+static void
+write_stringtable (FILE *fp, message_list_ty *mlp, const char *canon_encoding,
+ size_t page_width, bool debug)
+{
+ bool blank_line;
+ size_t j;
+
+ /* Convert the messages to Unicode. */
+ iconv_message_list (mlp, canon_encoding, po_charset_utf8, NULL);
+
+ /* Output the BOM. */
+ if (!is_ascii_message_list (mlp))
+ fputs ("\xef\xbb\xbf", fp);
+
+ /* Loop through the messages. */
+ blank_line = false;
+ for (j = 0; j < mlp->nitems; ++j)
+ {
+ const message_ty *mp = mlp->item[j];
+
+ if (mp->msgid_plural == NULL)
+ {
+ if (blank_line)
+ putc ('\n', fp);
+
+ write_message (fp, mp, page_width, debug);
+
+ blank_line = true;
+ }
+ }
+}
+
+/* Output the contents of a PO file in .strings syntax. */
+void
+msgdomain_list_print_stringtable (msgdomain_list_ty *mdlp, FILE *fp,
+ size_t page_width, bool debug)
+{
+ message_list_ty *mlp;
+
+ if (mdlp->nitems == 1)
+ mlp = mdlp->item[0]->messages;
+ else
+ mlp = message_list_alloc (false);
+ write_stringtable (fp, mlp, mdlp->encoding, page_width, debug);
+}
--- /dev/null
+/* Writing NeXTstep/GNUstep .strings files.
+ Copyright (C) 2003 Free Software Foundation, Inc.
+ Written by Bruno Haible <bruno@clisp.org>, 2003.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2, or (at your option)
+ any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software Foundation,
+ Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
+
+#ifndef _WRITE_STRINGTABLE_H
+#define _WRITE_STRINGTABLE_H
+
+#include <stdbool.h>
+#include <stdio.h>
+#include <stddef.h>
+
+#include "message.h"
+
+/* Output the contents of a PO file in .strings syntax. */
+extern void
+ msgdomain_list_print_stringtable (msgdomain_list_ty *mdlp, FILE *fp,
+ size_t page_width, bool debug);
+
+#endif /* _WRITE_STRINGTABLE_H */
#include "xgettext.h"
#include "x-po.h"
#include "x-properties.h"
+#include "x-stringtable.h"
#include "xalloc.h"
#include "read-po.h"
#include "po-lex.h"
{
extract (fp, real_filename, logical_filename, syntax_properties, mdlp);
}
+
+
+void
+extract_stringtable (FILE *fp,
+ const char *real_filename, const char *logical_filename,
+ flag_context_list_table_ty *flag_table,
+ msgdomain_list_ty *mdlp)
+{
+ extract (fp, real_filename, logical_filename, syntax_stringtable, mdlp);
+}
--- /dev/null
+/* xgettext NXStringTable backend.
+ Copyright (C) 2003 Free Software Foundation, Inc.
+ Written by Bruno Haible <bruno@clisp.org>, 2003.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2, or (at your option)
+ any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software Foundation,
+ Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
+
+
+#define EXTENSIONS_STRINGTABLE \
+ { "strings", "NXStringTable" }, \
+
+#define SCANNERS_STRINGTABLE \
+ { "NXStringTable", extract_stringtable, NULL, NULL, NULL }, \
+
+/* Scan a JavaProperties file and add its translatable strings to mdlp. */
+extern void extract_stringtable (FILE *fp, const char *real_filename,
+ const char *logical_filename,
+ flag_context_list_table_ty *flag_table,
+ msgdomain_list_ty *mdlp);
#include "x-tcl.h"
#include "x-perl.h"
#include "x-php.h"
+#include "x-stringtable.h"
#include "x-rst.h"
#include "x-glade.h"
/* Directory in which output files are created. */
static char *output_dir;
-/* The output syntax: .pot or .properties. */
+/* The output syntax: .pot or .properties or .strings. */
static input_syntax_ty output_syntax = syntax_po;
/* If nonzero omit header with information about this run. */
{ "exclude-file", required_argument, NULL, 'x' },
{ "extract-all", no_argument, NULL, 'a' },
{ "files-from", required_argument, NULL, 'f' },
- { "flag", required_argument, NULL, CHAR_MAX + 7 },
+ { "flag", required_argument, NULL, CHAR_MAX + 8 },
{ "force-po", no_argument, &force_po, 1 },
{ "foreign-user", no_argument, NULL, CHAR_MAX + 2 },
{ "from-code", required_argument, NULL, CHAR_MAX + 3 },
{ "sort-output", no_argument, NULL, 's' },
{ "strict", no_argument, NULL, 'S' },
{ "string-limit", required_argument, NULL, 'l' },
+ { "stringtable-output", no_argument, NULL, CHAR_MAX + 7 },
{ "trigraphs", no_argument, NULL, 'T' },
{ "version", no_argument, NULL, 'V' },
{ "width", required_argument, NULL, 'w', },
message_print_syntax_properties ();
output_syntax = syntax_properties;
break;
- case CHAR_MAX + 7: /* --flag */
+ case CHAR_MAX + 7: /* --stringtable-output */
+ message_print_syntax_stringtable ();
+ output_syntax = syntax_stringtable;
+ break;
+ case CHAR_MAX + 8: /* --flag */
xgettext_record_flag (optarg);
break;
default:
(C, C++, ObjectiveC, PO, Shell, Python, Lisp,\n\
EmacsLisp, librep, Smalltalk, Java,\n\
JavaProperties, awk, YCP, Tcl, Perl, PHP,\n\
- GCC-source, RST, Glade)\n"));
+ GCC-source, NXStringTable, RST, Glade)\n"));
printf (_("\
-C, --c++ shorthand for --language=C++\n"));
printf (_("\
printf (_("\
--properties-output write out a Java .properties file\n"));
printf (_("\
+ --stringtable-output write out a NeXTstep/GNUstep .strings file\n"));
+ printf (_("\
-w, --width=NUMBER set output page width\n"));
printf (_("\
--no-wrap do not break long message lines, longer than\n\
has_nonascii = true;
}
- if (has_nonascii || output_syntax == syntax_properties)
+ if (has_nonascii
+ || output_syntax == syntax_properties
+ || output_syntax == syntax_stringtable)
{
message_list_ty *mlp = mdlp->item[0]->messages;
SCANNERS_TCL
SCANNERS_PERL
SCANNERS_PHP
+ SCANNERS_STRINGTABLE
SCANNERS_RST
SCANNERS_GLADE
/* Here will follow more languages and their scanners: pike, C#, etc...
EXTENSIONS_TCL
EXTENSIONS_PERL
EXTENSIONS_PHP
+ EXTENSIONS_STRINGTABLE
EXTENSIONS_RST
EXTENSIONS_GLADE
/* Here will follow more file extensions: cs ... */
+2003-10-12 Bruno Haible <bruno@clisp.org>
+
+ * msgcat-13: New file.
+ * xgettext-33: New file.
+ * Makefile.am (TESTS): Add them.
+
2003-10-12 Bruno Haible <bruno@clisp.org>
* xgettext-32: New file.
msgattrib-6 msgattrib-7 msgattrib-8 msgattrib-9 msgattrib-10 \
msgattrib-11 msgattrib-12 msgattrib-13 msgattrib-14 msgattrib-15 \
msgcat-1 msgcat-2 msgcat-3 msgcat-4 msgcat-5 msgcat-6 msgcat-7 \
- msgcat-8 msgcat-9 msgcat-10 msgcat-11 msgcat-12 \
+ msgcat-8 msgcat-9 msgcat-10 msgcat-11 msgcat-12 msgcat-13 \
msgcmp-1 msgcmp-2 msgcmp-3 \
msgcomm-1 msgcomm-2 msgcomm-3 msgcomm-4 msgcomm-5 msgcomm-6 msgcomm-7 \
msgcomm-8 msgcomm-9 msgcomm-10 msgcomm-11 msgcomm-12 msgcomm-13 \
xgettext-18 xgettext-19 xgettext-20 xgettext-21 xgettext-22 \
xgettext-23 xgettext-24 xgettext-25 xgettext-26 xgettext-27 \
xgettext-28 xgettext-29 xgettext-30 xgettext-31 xgettext-32 \
+ xgettext-33 \
format-awk-1 format-awk-2 \
format-c-1 format-c-2 format-c-3 format-c-4 \
format-elisp-1 format-elisp-2 \
--- /dev/null
+#! /bin/sh
+
+# Test reading in NeXTstep/GNUstep .strings syntax.
+
+tmpfiles=""
+trap 'rm -fr $tmpfiles' 1 2 3 15
+
+tmpfiles="$tmpfiles mcat-test13.in"
+cat <<\EOF > mcat-test13.in
+/* This is an example of a string table file. Everything inside a comment
+is completely ignored, even if in "quotes", or \escape characters, etc.
+*/
+
+"title" = "pattern II target 1";
+
+/* This is an example of excape codes in the string table, codes */
+/* that are not one of abfnrtv are stripped of the \ character */
+"escapes" = "This is a tab \t and a return \n or a \a but not a \p";
+"escapes2" = "Well how about a \0? Guess not.";
+
+/* more parameters, white space between tokens is ignored */
+"actualSize"
+ =
+ "0.000250 0.000250";
+
+/* a key with no value assumes the value is the same as the key */
+"hoe322070.element";
+EOF
+
+tmpfiles="$tmpfiles mcat-test13.out"
+rm -f mcat-test13.out
+: ${MSGCAT=msgcat}
+${MSGCAT} --stringtable-input mcat-test13.in -o mcat-test13.out
+test $? = 0 || { rm -fr $tmpfiles; exit 1; }
+
+tmpfiles="$tmpfiles mcat-test13.ok"
+cat << \EOF > mcat-test13.ok
+# This is an example of a string table file. Everything inside a comment
+# is completely ignored, even if in "quotes", or \escape characters, etc.
+#
+msgid "title"
+msgstr "pattern II target 1"
+
+# This is an example of excape codes in the string table, codes
+# that are not one of abfnrtv are stripped of the \ character
+msgid "escapes"
+msgstr ""
+"This is a tab \t and a return \n"
+" or a \a but not a p"
+
+msgid "escapes2"
+msgstr "Well how about a "
+
+# more parameters, white space between tokens is ignored
+msgid "actualSize"
+msgstr "0.000250 0.000250"
+
+# a key with no value assumes the value is the same as the key
+msgid "hoe322070.element"
+msgstr "hoe322070.element"
+EOF
+
+: ${DIFF=diff}
+${DIFF} mcat-test13.ok mcat-test13.out
+result=$?
+
+rm -fr $tmpfiles
+
+exit $result
--- /dev/null
+#! /bin/sh
+
+# Test extractor of NeXTstep/GNUstep .strings files.
+
+tmpfiles=""
+trap 'rm -fr $tmpfiles' 1 2 3 15
+
+tmpfiles="$tmpfiles xg-test33.strings"
+cat <<\EOF > xg-test33.strings
+/***
+German.lproj/Localizable.strings
+updated by make_strings 2003-10-12 15:31:50 +0200
+add comments above this one
+***/
+
+
+/*** Strings from Hello.m ***/
+/* File: Hello.m:57 */
+/* File: Hello.m:56 */
+"Hello, world!" = "Hallo Welt!";
+/* File: Hello.m:67 */
+"This program is running as process number %d."
+= "Dieses Programm läuft mit der Prozess-Nummer %d.";
+EOF
+
+tmpfiles="$tmpfiles xg-test33.tmp xg-test33.po"
+: ${XGETTEXT=xgettext}
+${XGETTEXT} xg-test33.strings -o xg-test33.tmp
+test $? = 0 || { rm -fr $tmpfiles; exit 1; }
+grep -v 'POT-Creation-Date' < xg-test33.tmp > xg-test33.po
+
+tmpfiles="$tmpfiles xg-test33.ok"
+cat <<\EOF > xg-test33.ok
+# SOME DESCRIPTIVE TITLE.
+# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
+# This file is distributed under the same license as the PACKAGE package.
+# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
+#
+#, fuzzy
+msgid ""
+msgstr ""
+"Project-Id-Version: PACKAGE VERSION\n"
+"Report-Msgid-Bugs-To: \n"
+"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
+"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
+"Language-Team: LANGUAGE <LL@li.org>\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+
+#
+# German.lproj/Localizable.strings
+# updated by make_strings 2003-10-12 15:31:50 +0200
+# add comments above this one
+#
+# Strings from Hello.m
+#: Hello.m:57 Hello.m:56
+msgid "Hello, world!"
+msgstr "Hallo Welt!"
+
+#: Hello.m:67
+msgid "This program is running as process number %d."
+msgstr "Dieses Programm läuft mit der Prozess-Nummer %d."
+EOF
+
+: ${DIFF=diff}
+${DIFF} xg-test33.ok xg-test33.po
+result=$?
+
+rm -fr $tmpfiles
+
+exit $result