Commit d8c986e0 authored by Alexandre Julliard's avatar Alexandre Julliard

wrc: Add support for loading translations from po files.

parent 10700bf6
...@@ -701,6 +701,270 @@ void write_po_files( const char *outname ) ...@@ -701,6 +701,270 @@ void write_po_files( const char *outname )
} }
} }
static resource_t *new_top, *new_tail;
static version_t *get_dup_version( language_t *lang )
{
/* English "translations" take precedence over the original rc contents */
return new_version( is_english( lang ) ? 1 : -1 );
}
static name_id_t *dup_name_id( name_id_t *id )
{
name_id_t *new;
if (!id || id->type != name_str) return id;
new = new_name_id();
*new = *id;
new->name.s_name = convert_string( id->name.s_name, str_unicode, 1252 );
return new;
}
static resource_t *dup_resource( resource_t *res, language_t *lang )
{
resource_t *new = xmalloc( sizeof(*new) );
*new = *res;
new->lan = lang;
new->next = new->prev = NULL;
new->name = dup_name_id( res->name );
switch (res->type)
{
case res_dlg:
new->res.dlg = xmalloc( sizeof(*(new)->res.dlg) );
*new->res.dlg = *res->res.dlg;
new->res.dlg->lvc.language = lang;
new->res.dlg->lvc.version = get_dup_version( lang );
break;
case res_men:
new->res.men = xmalloc( sizeof(*(new)->res.men) );
*new->res.men = *res->res.men;
new->res.men->lvc.language = lang;
new->res.men->lvc.version = get_dup_version( lang );
break;
case res_stt:
new->res.stt = xmalloc( sizeof(*(new)->res.stt) );
*new->res.stt = *res->res.stt;
new->res.stt->lvc.language = lang;
new->res.stt->lvc.version = get_dup_version( lang );
break;
default:
assert(0);
}
return new;
}
static string_t *translate_string( po_file_t po, string_t *str, int *found )
{
po_message_t msg;
po_message_iterator_t iterator;
string_t *new;
const char *transl;
int res;
char *buffer, *msgid, *context;
if (!str->size || !(buffer = convert_msgid_ascii( str, 0 )))
return convert_string( str, str_unicode, 1252 );
msgid = buffer;
context = get_message_context( &msgid );
msg = find_message( po, msgid, context, &iterator );
po_message_iterator_free( iterator );
if (msg) (*found)++;
transl = msg ? po_message_msgstr( msg ) : msgid;
new = xmalloc( sizeof(*new) );
new->type = str_unicode;
new->size = wine_utf8_mbstowcs( 0, transl, strlen(transl), NULL, 0 );
new->str.wstr = xmalloc( (new->size+1) * sizeof(WCHAR) );
res = wine_utf8_mbstowcs( MB_ERR_INVALID_CHARS, transl, strlen(transl), new->str.wstr, new->size );
if (res == -2)
error( "Invalid utf-8 character in string '%s'\n", transl );
new->str.wstr[new->size] = 0;
free( buffer );
return new;
}
static control_t *translate_controls( po_file_t po, control_t *ctrl, int *found )
{
control_t *new, *head = NULL, *tail = NULL;
while (ctrl)
{
new = xmalloc( sizeof(*new) );
*new = *ctrl;
if (control_has_title( ctrl ))
{
new->title = new_name_id();
*new->title = *ctrl->title;
new->title->name.s_name = translate_string( po, ctrl->title->name.s_name, found );
}
else new->title = dup_name_id( ctrl->title );
new->ctlclass = dup_name_id( ctrl->ctlclass );
if (tail) tail->next = new;
else head = new;
new->next = NULL;
new->prev = tail;
tail = new;
ctrl = ctrl->next;
}
return head;
}
static menu_item_t *translate_items( po_file_t po, menu_item_t *item, int *found )
{
menu_item_t *new, *head = NULL, *tail = NULL;
while (item)
{
new = xmalloc( sizeof(*new) );
*new = *item;
if (item->name) new->name = translate_string( po, item->name, found );
if (item->popup) new->popup = translate_items( po, item->popup, found );
if (tail) tail->next = new;
else head = new;
new->next = NULL;
new->prev = tail;
tail = new;
item = item->next;
}
return head;
}
static stringtable_t *translate_stringtable( po_file_t po, stringtable_t *stt,
language_t *lang, int *found )
{
stringtable_t *new, *head = NULL, *tail = NULL;
int i;
while (stt)
{
new = xmalloc( sizeof(*new) );
*new = *stt;
new->lvc.language = lang;
new->lvc.version = get_dup_version( lang );
new->entries = xmalloc( new->nentries * sizeof(*new->entries) );
memcpy( new->entries, stt->entries, new->nentries * sizeof(*new->entries) );
for (i = 0; i < stt->nentries; i++)
if (stt->entries[i].str)
new->entries[i].str = translate_string( po, stt->entries[i].str, found );
if (tail) tail->next = new;
else head = new;
new->next = NULL;
new->prev = tail;
tail = new;
stt = stt->next;
}
return head;
}
static void translate_dialog( po_file_t po, dialog_t *dlg, dialog_t *new, int *found )
{
if (dlg->title) new->title = translate_string( po, dlg->title, found );
if (dlg->font)
{
new->font = xmalloc( sizeof(*dlg->font) );
new->font = dlg->font;
new->font->name = translate_string( po, dlg->font->name, found );
}
new->controls = translate_controls( po, dlg->controls, found );
}
static void translate_resources( po_file_t po, language_t *lang )
{
resource_t *res;
for (res = resource_top; res; res = res->next)
{
resource_t *new = NULL;
int found = 0;
if (!is_english( res->lan )) continue;
switch (res->type)
{
case res_acc:
/* FIXME */
break;
case res_dlg:
new = dup_resource( res, lang );
translate_dialog( po, res->res.dlg, new->res.dlg, &found );
break;
case res_men:
new = dup_resource( res, lang );
new->res.men->items = translate_items( po, res->res.men->items, &found );
break;
case res_stt:
new = dup_resource( res, lang );
new->res.stt = translate_stringtable( po, res->res.stt, lang, &found );
break;
case res_msg:
/* FIXME */
break;
default:
break;
}
if (new && found)
{
if (new_tail) new_tail->next = new;
else new_top = new;
new->prev = new_tail;
new_tail = new;
}
}
}
void add_translations( const char *po_dir )
{
resource_t *res;
po_file_t po;
char buffer[256];
char *p, *tok, *name;
unsigned int i;
FILE *f;
/* first check if we have English resources to translate */
for (res = resource_top; res; res = res->next) if (is_english( res->lan )) break;
if (!res) return;
new_top = new_tail = NULL;
name = strmake( "%s/LINGUAS", po_dir );
if (!(f = fopen( name, "r" ))) return;
free( name );
while (fgets( buffer, sizeof(buffer), f ))
{
if ((p = strchr( buffer, '#' ))) *p = 0;
for (tok = strtok( buffer, " \t\r\n" ); tok; tok = strtok( NULL, " \t\r\n" ))
{
for (i = 0; i < sizeof(languages)/sizeof(languages[0]); i++)
if (!strcmp( tok, languages[i].name )) break;
if (i == sizeof(languages)/sizeof(languages[0]))
error( "unknown language '%s'\n", tok );
name = strmake( "%s/%s.po", po_dir, tok );
if (!(po = po_file_read( name, &po_xerror_handler )))
error( "cannot load po file for language '%s'\n", tok );
translate_resources( po, new_language(languages[i].id, languages[i].sub) );
po_file_free( po );
free( name );
}
}
fclose( f );
/* prepend the translated resources to the global list */
if (new_tail)
{
new_tail->next = resource_top;
resource_top->prev = new_tail;
resource_top = new_top;
}
}
#else /* HAVE_LIBGETTEXTPO */ #else /* HAVE_LIBGETTEXTPO */
void write_pot_file( const char *outname ) void write_pot_file( const char *outname )
...@@ -713,4 +977,8 @@ void write_po_files( const char *outname ) ...@@ -713,4 +977,8 @@ void write_po_files( const char *outname )
error( "PO files not supported in this wrc build\n" ); error( "PO files not supported in this wrc build\n" );
} }
void add_translations( const char *po_dir )
{
}
#endif #endif
...@@ -71,6 +71,7 @@ static const char usage[] = ...@@ -71,6 +71,7 @@ static const char usage[] =
" -o, --output=FILE Output to file (default is infile.res)\n" " -o, --output=FILE Output to file (default is infile.res)\n"
" -O, --output-format=FORMAT The output format (`po', `pot', `res', or `res16`)\n" " -O, --output-format=FORMAT The output format (`po', `pot', `res', or `res16`)\n"
" --pedantic Enable pedantic warnings\n" " --pedantic Enable pedantic warnings\n"
" --po-dir=DIR Directory containing po files for translations\n"
" --preprocessor Specifies the preprocessor to use, including arguments\n" " --preprocessor Specifies the preprocessor to use, including arguments\n"
" -r Ignored for compatibility with rc\n" " -r Ignored for compatibility with rc\n"
" -U, --undefine id Undefine preprocessor identifier id\n" " -U, --undefine id Undefine preprocessor identifier id\n"
...@@ -171,6 +172,7 @@ enum long_options_values ...@@ -171,6 +172,7 @@ enum long_options_values
LONG_OPT_NOSTDINC = 1, LONG_OPT_NOSTDINC = 1,
LONG_OPT_TMPFILE, LONG_OPT_TMPFILE,
LONG_OPT_NOTMPFILE, LONG_OPT_NOTMPFILE,
LONG_OPT_PO_DIR,
LONG_OPT_PREPROCESSOR, LONG_OPT_PREPROCESSOR,
LONG_OPT_VERSION, LONG_OPT_VERSION,
LONG_OPT_DEBUG, LONG_OPT_DEBUG,
...@@ -195,6 +197,7 @@ static const struct option long_options[] = { ...@@ -195,6 +197,7 @@ static const struct option long_options[] = {
{ "output", 1, NULL, 'o' }, { "output", 1, NULL, 'o' },
{ "output-format", 1, NULL, 'O' }, { "output-format", 1, NULL, 'O' },
{ "pedantic", 0, NULL, LONG_OPT_PEDANTIC }, { "pedantic", 0, NULL, LONG_OPT_PEDANTIC },
{ "po-dir", 1, NULL, LONG_OPT_PO_DIR },
{ "preprocessor", 1, NULL, LONG_OPT_PREPROCESSOR }, { "preprocessor", 1, NULL, LONG_OPT_PREPROCESSOR },
{ "target", 1, NULL, 'F' }, { "target", 1, NULL, 'F' },
{ "undefine", 1, NULL, 'U' }, { "undefine", 1, NULL, 'U' },
...@@ -334,6 +337,7 @@ int main(int argc,char *argv[]) ...@@ -334,6 +337,7 @@ int main(int argc,char *argv[])
int i; int i;
int cmdlen; int cmdlen;
int po_mode = 0; int po_mode = 0;
char *po_dir = NULL;
char **files = xmalloc( argc * sizeof(*files) ); char **files = xmalloc( argc * sizeof(*files) );
signal(SIGSEGV, segvhandler); signal(SIGSEGV, segvhandler);
...@@ -378,6 +382,9 @@ int main(int argc,char *argv[]) ...@@ -378,6 +382,9 @@ int main(int argc,char *argv[])
case LONG_OPT_NOTMPFILE: case LONG_OPT_NOTMPFILE:
if (debuglevel) warning("--no-use-temp-file option not yet supported, ignored.\n"); if (debuglevel) warning("--no-use-temp-file option not yet supported, ignored.\n");
break; break;
case LONG_OPT_PO_DIR:
po_dir = xstrdup( optarg );
break;
case LONG_OPT_PREPROCESSOR: case LONG_OPT_PREPROCESSOR:
if (strcmp(optarg, "cat") == 0) no_preprocess = 1; if (strcmp(optarg, "cat") == 0) no_preprocess = 1;
else fprintf(stderr, "-P option not yet supported, ignored.\n"); else fprintf(stderr, "-P option not yet supported, ignored.\n");
...@@ -555,6 +562,7 @@ int main(int argc,char *argv[]) ...@@ -555,6 +562,7 @@ int main(int argc,char *argv[])
output_name = NULL; output_name = NULL;
exit(0); exit(0);
} }
if (po_dir) add_translations( po_dir );
/* Convert the internal lists to binary data */ /* Convert the internal lists to binary data */
resources2res(resource_top); resources2res(resource_top);
......
...@@ -58,6 +58,7 @@ extern language_t *currentlanguage; ...@@ -58,6 +58,7 @@ extern language_t *currentlanguage;
void verify_translations(resource_t *top); void verify_translations(resource_t *top);
void write_pot_file( const char *outname ); void write_pot_file( const char *outname );
void write_po_files( const char *outname ); void write_po_files( const char *outname );
void add_translations( const char *po_dir );
void write_resfile(char *outname, resource_t *top); void write_resfile(char *outname, resource_t *top);
static inline void set_location( location_t *loc ) static inline void set_location( location_t *loc )
......
...@@ -107,6 +107,12 @@ input. ...@@ -107,6 +107,12 @@ input.
Enable pedantic warnings. Notably redefinition of #define statements can Enable pedantic warnings. Notably redefinition of #define statements can
be discovered with this option. be discovered with this option.
.TP .TP
.I \fB\-\-po-dir=\fIdir\fR
Enable the generation of resource translations based on po files
loaded from the specified directory. That directory must follow the
gettext convention, in particular in must contain one .po file for
each language, and a LINGUAS file listing the available languages.
.TP
.I \fB\-r\fR .I \fB\-r\fR
Ignored for compatibility with \fIrc\fR. Ignored for compatibility with \fIrc\fR.
.TP .TP
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment