From f4ec097c9b1f20e7ffd6aca6225c6a8a5fb1552a Mon Sep 17 00:00:00 2001 From: Alexandre Julliard Date: Sat, 22 Oct 2011 18:21:04 +0200 Subject: [PATCH] wrc: Load translations from mo files instead of po. --- Make.rules.in | 6 +- Make.vars.in | 2 +- configure | 7 +- configure.ac | 5 +- tools/wrc/po.c | 171 +++++++++++++++++++++++++++++++------------ tools/wrc/wrc.c | 2 +- tools/wrc/wrc.man.in | 4 +- 7 files changed, 140 insertions(+), 57 deletions(-) diff --git a/Make.rules.in b/Make.rules.in index 993d47e4534..7c4a65c4aa6 100644 --- a/Make.rules.in +++ b/Make.rules.in @@ -17,7 +17,7 @@ INCLUDES = -I$(srcdir) -I. -I$(top_srcdir)/include -I$(top_builddir)/include ALLCFLAGS = $(INCLUDES) $(DEFS) $(DLLFLAGS) $(EXTRACFLAGS) $(CPPFLAGS) $(CFLAGS) $(MODCFLAGS) ALLLINTFLAGS = $(INCLUDES) $(DEFS) $(LINTFLAGS) IDLFLAGS = $(INCLUDES) $(DEFS) $(EXTRAIDLFLAGS) -RCFLAGS = --nostdinc --po-dir=$(top_srcdir)/po $(TARGETFLAGS) $(INCLUDES) $(DEFS) $(EXTRARCFLAGS) +RCFLAGS = --nostdinc $(PORCFLAGS) $(TARGETFLAGS) $(INCLUDES) $(DEFS) $(EXTRARCFLAGS) IDL_GEN_C_SRCS = $(IDL_C_SRCS:.idl=_c.c) $(IDL_I_SRCS:.idl=_i.c) \ $(IDL_P_SRCS:.idl=_p.c) $(IDL_S_SRCS:.idl=_s.c) @@ -189,9 +189,9 @@ rsrc.pot: $(WRC) msg.pot: $(WMC) $(LDPATH) $(WMC) -O pot -o $@ $(MC_SRCS) -$(MC_SRCS:.mc=.res): $(WMC) $(ALL_PO_FILES) +$(MC_SRCS:.mc=.res): $(WMC) $(ALL_MO_FILES) $(RC_SRCS:.rc=.res): $(WRC) -$(PO_SRCS:.rc=.res): $(ALL_PO_FILES) +$(PO_SRCS:.rc=.res): $(ALL_MO_FILES) # Misc. rules diff --git a/Make.vars.in b/Make.vars.in index d082d98db51..100594b372e 100644 --- a/Make.vars.in +++ b/Make.vars.in @@ -53,8 +53,8 @@ ICOTOOL = @ICOTOOL@ MSGFMT = @MSGFMT@ CROSSTARGET = @CROSSTARGET@ LINGUAS = @LINGUAS@ -ALL_PO_FILES = $(LINGUAS:%=@top_srcdir@/po/%.po) ALL_MO_FILES = $(LINGUAS:%=@top_builddir@/po/%.mo) +PORCFLAGS = @PORCFLAGS@ CROSSAR = $(CROSSTARGET)-ar CROSSRANLIB = $(CROSSTARGET)-ranlib MKINSTALLDIRS = $(top_srcdir)/tools/mkinstalldirs -m 755 diff --git a/configure b/configure index 9bf8db62cfc..37d8cbac9ab 100755 --- a/configure +++ b/configure @@ -604,6 +604,7 @@ ac_includes_default="\ ac_subst_vars='LTLIBOBJS LIBOBJS +PORCFLAGS LINGUAS ALL_TEST_RESOURCES LDAPLIBS @@ -15432,7 +15433,7 @@ then @echo timestamp > \$@ ALL_POT_FILES =$ALL_POT_FILES -\$(ALL_PO_FILES): \$(srcdir)/po/wine.pot +\$(LINGUAS:%=$srcdir/po/%.po): \$(srcdir)/po/wine.pot msgmerge -q \$@ \$(srcdir)/po/wine.pot | msgattrib --no-obsolete -o \$@.new && mv \$@.new \$@ \$(srcdir)/po/wine.pot: \$(ALL_POT_FILES) msgcat -o \$@ \$(ALL_POT_FILES)" @@ -15440,7 +15441,11 @@ fi if test "$MSGFMT" != false then + PORCFLAGS="--po-dir=\$(top_builddir)/po" + wine_fn_append_rule ALL_MAKEFILE_DEPENDS "__builddeps__: \$(ALL_MO_FILES)" +else + LINGUAS= fi if test "x$enable_tools" != xno diff --git a/configure.ac b/configure.ac index 67bf4a5fa41..4b1f7185f7c 100644 --- a/configure.ac +++ b/configure.ac @@ -3066,7 +3066,7 @@ then @echo timestamp > \$[@] ALL_POT_FILES =$ALL_POT_FILES -\$(ALL_PO_FILES): \$(srcdir)/po/wine.pot +\$(LINGUAS:%=$srcdir/po/%.po): \$(srcdir)/po/wine.pot msgmerge -q \$[@] \$(srcdir)/po/wine.pot | msgattrib --no-obsolete -o \$[@].new && mv \$[@].new \$[@] \$(srcdir)/po/wine.pot: \$(ALL_POT_FILES) msgcat -o \$[@] \$(ALL_POT_FILES)]) @@ -3074,7 +3074,10 @@ fi if test "$MSGFMT" != false then + AC_SUBST([PORCFLAGS],["--po-dir=\$(top_builddir)/po"]) WINE_APPEND_RULE([ALL_MAKEFILE_DEPENDS],[__builddeps__: \$(ALL_MO_FILES)]) +else + LINGUAS= fi if test "x$enable_tools" != xno diff --git a/tools/wrc/po.c b/tools/wrc/po.c index fd55ba4bb8a..9cb2b42a44c 100644 --- a/tools/wrc/po.c +++ b/tools/wrc/po.c @@ -19,6 +19,7 @@ */ #include "config.h" +#include "wine/port.h" #include #include @@ -43,6 +44,16 @@ static resource_t *new_top, *new_tail; +struct mo_file +{ + unsigned int magic; + unsigned int revision; + unsigned int count; + unsigned int msgid_off; + unsigned int msgstr_off; + /* ... rest of file data here */ +}; + static int is_english( const language_t *lan ) { return lan->id == LANG_ENGLISH && lan->sub == SUBLANG_DEFAULT; @@ -393,20 +404,6 @@ static const struct #ifndef HAVE_LIBGETTEXTPO -static const char *get_msgstr( const char *msgid, const char *context, int *found ) -{ - if (context) (*found)++; - return msgid; -} - -static void load_po_file( const char *name ) -{ -} - -static void free_po_file(void) -{ -} - void write_pot_file( const char *outname ) { error( "PO files not supported in this wrc build\n" ); @@ -419,8 +416,6 @@ void write_po_files( const char *outname ) #else /* HAVE_LIBGETTEXTPO */ -static po_file_t po_file; - static void po_xerror( int severity, po_message_t message, const char *filename, size_t lineno, size_t column, int multiline_p, const char *message_text ) @@ -473,35 +468,6 @@ static po_message_t find_message( po_file_t po, const char *msgid, const char *m return msg; } -static const char *get_msgstr( const char *msgid, const char *context, int *found ) -{ - const char *ret = msgid; - po_message_t msg; - po_message_iterator_t iterator; - - msg = find_message( po_file, msgid, context, &iterator ); - if (msg && !po_message_is_fuzzy( msg )) - { - ret = po_message_msgstr( msg ); - if (!ret[0]) ret = msgid; /* ignore empty strings */ - else (*found)++; - } - po_message_iterator_free( iterator ); - return ret; -} - -static void load_po_file( const char *name ) -{ - if (!(po_file = po_file_read( name, &po_xerror_handler ))) - error( "cannot load po file '%s'\n", name ); -} - -static void free_po_file(void) -{ - po_file_free( po_file ); - po_file = NULL; -} - static void add_po_string( po_file_t po, const string_t *msgid, const string_t *msgstr, const language_t *lang ) { @@ -820,6 +786,108 @@ void write_po_files( const char *outname ) #endif /* HAVE_LIBGETTEXTPO */ +static struct mo_file *mo_file; + +static void byteswap( unsigned int *data, unsigned int count ) +{ + unsigned int i; + + for (i = 0; i < count; i++) + data[i] = data[i] >> 24 | (data[i] >> 8 & 0xff00) | (data[i] << 8 & 0xff0000) | data[i] << 24; +} + +static void load_mo_file( const char *name ) +{ + struct stat st; + int res, fd; + + fd = open( name, O_RDONLY | O_BINARY ); + if (fd == -1) fatal_perror( "Failed to open %s", name ); + fstat( fd, &st ); + mo_file = xmalloc( st.st_size ); + res = read( fd, mo_file, st.st_size ); + if (res == -1) fatal_perror( "Failed to read %s", name ); + else if (res != st.st_size) error( "Failed to read %s\n", name ); + close( fd ); + + /* sanity checks */ + + if (st.st_size < sizeof(*mo_file)) + error( "%s is not a valid .mo file\n", name ); + if (mo_file->magic == 0xde120495) + byteswap( &mo_file->revision, 4 ); + else if (mo_file->magic != 0x950412de) + error( "%s is not a valid .mo file\n", name ); + if ((mo_file->revision >> 16) > 1) + error( "%s: unsupported file version %x\n", name, mo_file->revision ); + if (mo_file->msgid_off >= st.st_size || + mo_file->msgstr_off >= st.st_size || + st.st_size < sizeof(*mo_file) + 2 * 8 * mo_file->count) + error( "%s: corrupted file\n", name ); + + if (mo_file->magic == 0xde120495) + { + byteswap( (unsigned int *)((char *)mo_file + mo_file->msgid_off), 2 * mo_file->count ); + byteswap( (unsigned int *)((char *)mo_file + mo_file->msgstr_off), 2 * mo_file->count ); + } +} + +static void free_mo_file(void) +{ + free( mo_file ); + mo_file = NULL; +} + +static inline const char *get_mo_msgid( int index ) +{ + const char *base = (const char *)mo_file; + const unsigned int *offsets = (const unsigned int *)(base + mo_file->msgid_off); + return base + offsets[2 * index + 1]; +} + +static inline const char *get_mo_msgstr( int index ) +{ + const char *base = (const char *)mo_file; + const unsigned int *offsets = (const unsigned int *)(base + mo_file->msgstr_off); + return base + offsets[2 * index + 1]; +} + +static const char *get_msgstr( const char *msgid, const char *context, int *found ) +{ + int pos, res, min, max; + const char *ret = msgid; + char *id = NULL; + + if (!mo_file) /* strings containing a context still need to be transformed */ + { + if (context) (*found)++; + return ret; + } + + if (context) id = strmake( "%s%c%s", context, 4, msgid ); + min = 0; + max = mo_file->count - 1; + while (min <= max) + { + pos = (min + max) / 2; + res = strcmp( get_mo_msgid(pos), msgid ); + if (!res) + { + const char *str = get_mo_msgstr( pos ); + if (str[0]) /* ignore empty strings */ + { + ret = str; + (*found)++; + } + break; + } + if (res > 0) max = pos - 1; + else min = pos + 1; + } + free( id ); + return ret; +} + static string_t *translate_string( string_t *str, int *found ) { string_t *new; @@ -988,6 +1056,12 @@ void add_translations( const char *po_dir ) for (res = resource_top; res; res = res->next) if (is_english( res->lan )) break; if (!res) return; + if (!po_dir) /* run through the translation process to remove msg contexts */ + { + translate_resources( new_language( LANG_ENGLISH, SUBLANG_DEFAULT )); + goto done; + } + new_top = new_tail = NULL; name = strmake( "%s/LINGUAS", po_dir ); @@ -1008,15 +1082,16 @@ void add_translations( const char *po_dir ) if (i == sizeof(languages)/sizeof(languages[0])) error( "unknown language '%s'\n", tok ); - name = strmake( "%s/%s.po", po_dir, tok ); - load_po_file( name ); + name = strmake( "%s/%s.mo", po_dir, tok ); + load_mo_file( name ); translate_resources( new_language(languages[i].id, languages[i].sub) ); - free_po_file(); + free_mo_file(); free( name ); } } fclose( f ); +done: /* prepend the translated resources to the global list */ if (new_tail) { diff --git a/tools/wrc/wrc.c b/tools/wrc/wrc.c index 2eb5380cd83..7d5ad89be80 100644 --- a/tools/wrc/wrc.c +++ b/tools/wrc/wrc.c @@ -562,7 +562,7 @@ int main(int argc,char *argv[]) output_name = NULL; exit(0); } - if (po_dir) add_translations( po_dir ); + add_translations( po_dir ); /* Convert the internal lists to binary data */ resources2res(resource_top); diff --git a/tools/wrc/wrc.man.in b/tools/wrc/wrc.man.in index cfdf800d892..ed1fd820e5f 100644 --- a/tools/wrc/wrc.man.in +++ b/tools/wrc/wrc.man.in @@ -108,9 +108,9 @@ Enable pedantic warnings. Notably redefinition of #define statements can be discovered with this option. .TP .I \fB\-\-po-dir=\fIdir\fR -Enable the generation of resource translations based on po files +Enable the generation of resource translations based on mo files loaded from the specified directory. That directory must follow the -gettext convention, in particular in must contain one .po file for +gettext convention, in particular in must contain one .mo file for each language, and a LINGUAS file listing the available languages. .TP .I \fB\-r\fR