wrc: Add support for loading translations from po files.
This commit is contained in:
parent
10700bf6b2
commit
d8c986e062
268
tools/wrc/po.c
268
tools/wrc/po.c
|
@ -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 */
|
||||
|
||||
void write_pot_file( const char *outname )
|
||||
|
@ -713,4 +977,8 @@ void write_po_files( const char *outname )
|
|||
error( "PO files not supported in this wrc build\n" );
|
||||
}
|
||||
|
||||
void add_translations( const char *po_dir )
|
||||
{
|
||||
}
|
||||
|
||||
#endif
|
||||
|
|
|
@ -71,6 +71,7 @@ static const char usage[] =
|
|||
" -o, --output=FILE Output to file (default is infile.res)\n"
|
||||
" -O, --output-format=FORMAT The output format (`po', `pot', `res', or `res16`)\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"
|
||||
" -r Ignored for compatibility with rc\n"
|
||||
" -U, --undefine id Undefine preprocessor identifier id\n"
|
||||
|
@ -171,6 +172,7 @@ enum long_options_values
|
|||
LONG_OPT_NOSTDINC = 1,
|
||||
LONG_OPT_TMPFILE,
|
||||
LONG_OPT_NOTMPFILE,
|
||||
LONG_OPT_PO_DIR,
|
||||
LONG_OPT_PREPROCESSOR,
|
||||
LONG_OPT_VERSION,
|
||||
LONG_OPT_DEBUG,
|
||||
|
@ -195,6 +197,7 @@ static const struct option long_options[] = {
|
|||
{ "output", 1, NULL, 'o' },
|
||||
{ "output-format", 1, NULL, 'O' },
|
||||
{ "pedantic", 0, NULL, LONG_OPT_PEDANTIC },
|
||||
{ "po-dir", 1, NULL, LONG_OPT_PO_DIR },
|
||||
{ "preprocessor", 1, NULL, LONG_OPT_PREPROCESSOR },
|
||||
{ "target", 1, NULL, 'F' },
|
||||
{ "undefine", 1, NULL, 'U' },
|
||||
|
@ -334,6 +337,7 @@ int main(int argc,char *argv[])
|
|||
int i;
|
||||
int cmdlen;
|
||||
int po_mode = 0;
|
||||
char *po_dir = NULL;
|
||||
char **files = xmalloc( argc * sizeof(*files) );
|
||||
|
||||
signal(SIGSEGV, segvhandler);
|
||||
|
@ -378,6 +382,9 @@ int main(int argc,char *argv[])
|
|||
case LONG_OPT_NOTMPFILE:
|
||||
if (debuglevel) warning("--no-use-temp-file option not yet supported, ignored.\n");
|
||||
break;
|
||||
case LONG_OPT_PO_DIR:
|
||||
po_dir = xstrdup( optarg );
|
||||
break;
|
||||
case LONG_OPT_PREPROCESSOR:
|
||||
if (strcmp(optarg, "cat") == 0) no_preprocess = 1;
|
||||
else fprintf(stderr, "-P option not yet supported, ignored.\n");
|
||||
|
@ -555,6 +562,7 @@ int main(int argc,char *argv[])
|
|||
output_name = NULL;
|
||||
exit(0);
|
||||
}
|
||||
if (po_dir) add_translations( po_dir );
|
||||
|
||||
/* Convert the internal lists to binary data */
|
||||
resources2res(resource_top);
|
||||
|
|
|
@ -58,6 +58,7 @@ extern language_t *currentlanguage;
|
|||
void verify_translations(resource_t *top);
|
||||
void write_pot_file( 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);
|
||||
|
||||
static inline void set_location( location_t *loc )
|
||||
|
|
|
@ -107,6 +107,12 @@ input.
|
|||
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
|
||||
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
|
||||
Ignored for compatibility with \fIrc\fR.
|
||||
.TP
|
||||
|
|
Loading…
Reference in New Issue