scummvm/tools/po2c
Eugene Sandulenko 859212df25 Implement translation support for ScummVM GUI.
Based on patch #2903830: "Updated Translation Prototype" by alexbevi
which in turn is based on patch #1739965 by jvprat.

Currently it builds all translations right into ScummVM. Once the
feature will be accepted more widely, i.e. more translations will
pop up, it will be trivial to move translation strings to external
file.

Finished translation: Russian
Unfinished translation: Hungarian

Things which are nice to do:
 - Language code -> language mapping for more user friendness
 - Specifying fonts to be used with language
 - Updating of interface language without restart. It will require
   moving of much code to reflowLayout() methods for each dialog

The .po files must be in single byte encodings. I.e. no support
for Unicode.

svn-id: r49759
2010-06-15 10:44:51 +00:00

281 lines
5.2 KiB
Perl
Executable File

#!/usr/bin/perl
#
# po2c - Converts .po files to C code
#
# Copyright (C) 2004 Angel Ortega <angel@triptico.com>
#
# 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 of the License, 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.
#
# http://www.triptico.com
#
$VERSION = "1.0.2";
if(scalar(@ARGV) == 0)
{
print "Usage: po2c {po file[s]}\n";
exit 1;
}
%msgs = ();
%msgids = ();
# stage 1: loading
# arguments are .po files
foreach my $f (@ARGV)
{
my ($lang);
my ($langDesc);
next unless(($lang) = ($f =~ /([^\/]+)\.po$/));
if(open F, $f)
{
my ($msgid, $val, %a);
while(<F>)
{
chomp;
# ignore blank lines or comments
next if /^$/ or /^#/;
if(/^msgid\s+\"(.*)\"\s*$/)
{
# store previous msgid
if(defined($msgid))
{
$a{$msgid} = $val;
$msgids{$msgid} ++;
}
# start of msgid
$val = $1;
}
elsif(/^msgstr\s+\"(.*)\"\s*$/)
{
# store previous msgid
$msgid = $val;
# start of msgstr
$val = $1;
}
elsif(/^\"(.*)\"\s*$/)
{
# add to current value
$val .= $1;
}
}
# store previous msgid
if(defined($msgid))
{
$a{$msgid} = $val;
$msgids{$msgid} ++;
}
close F;
# add to the global message pool
$msgs{$lang} = \%a;
}
}
# stage 2: convert the data
# stores all sorted msgids into @msgids
@msgids = sort(keys(%msgids));
# travels again, storing indexes into %msgids
for(my $n = 0;$n < scalar(@msgids);$n++)
{
$msgids{$msgids[$n]} = $n;
}
# stage 3: dump as C code
print "/* generated by po2c $VERSION - Do not modify */\n\n";
print "#include <stdio.h>\n";
print "#include <string.h>\n\n";
# dump first the msgid array
print "static const char * _po2c_msgids[] = {\n";
for(my $n = 0;$n < scalar(@msgids);$n++)
{
print "\t/* $n */ \"" . $msgids[$n] . "\",\n";
}
print "\tNULL\n};\n\n";
# dump the lang structure
print "struct _po2c_msg {\n";
print "\tint msgid;\n";
print "\tconst char * msgstr;\n";
print "};\n\n";
# dump now each language
foreach my $l (keys(%msgs))
{
print "static struct _po2c_msg _po2c_lang_${l}\[\] = {\n";
# get the translation table for the language $l
my ($m) = $msgs{$l};
# while (my ($msgstr, $msgid) = each (%$m))
foreach my $msgid (sort(keys(%$m)))
{
my ($msgstr) = "";
# make it 7-bit safe
foreach $c (split(//, $m->{$msgid})) {
if (ord($c) > 0x7f) {
$msgstr .= sprintf("\\%o", ord($c));
} else {
$msgstr .= $c;
}
}
print "\t{ " . $msgids{$msgid} . ", \"" . $msgstr . "\" },\n"
if $msgstr;
}
print "\t{ -1, NULL }\n};\n\n";
}
# finally, dump the languages
print "static struct {\n";
print "\tconst char * lang;\n";
print "\tconst char * charset;\n";
print "\tstruct _po2c_msg * msgs;\n";
print "} _po2c_langs[] = {\n";
foreach my $l (keys(%msgs))
{
$header = $msgs{$l}->{""};
$header =~ /charset=([^\\]+)/;
$charset = $1;
print "\t{ \"" . $l . "\", \"" . $charset . "\", _po2c_lang_${l} },\n";
}
print "\t{ NULL, NULL, NULL }\n};\n\n";
print "/* code */\n";
print << 'EOF';
static struct _po2c_msg * _po2c_lang=NULL;
static int _po2c_lang_size=0;
static const char * _po2c_charset=NULL;
void po2c_setlang(const char * lang)
{
int n;
_po2c_lang=NULL;
_po2c_lang_size=0;
_po2c_charset=NULL;
/* if lang is NULL or "", deactivate it */
if(lang == NULL || *lang == '\0')
return;
/* searches for a valid language array */
for(n=0;_po2c_lang == NULL && _po2c_langs[n].lang != NULL;n++)
{
if(strcmp(lang, _po2c_langs[n].lang) == 0) {
_po2c_lang=_po2c_langs[n].msgs;
_po2c_charset=_po2c_langs[n].charset;
}
}
/* try partial searches */
for(n=0;_po2c_lang == NULL && _po2c_langs[n].lang != NULL;n++)
{
if(strncmp(lang, _po2c_langs[n].lang, 2) == 0) {
_po2c_lang=_po2c_langs[n].msgs;
_po2c_charset=_po2c_langs[n].charset;
}
}
/* if found, count entries */
if(_po2c_lang != NULL)
{
struct _po2c_msg * m;
for(m=_po2c_lang;m->msgid != -1;m++)
_po2c_lang_size++;
}
}
const char * po2c_gettext(const char * msgid)
{
struct _po2c_msg * m;
int b, t, n, c;
/* if no language is set or msgid is empty, return msgid as is */
if(_po2c_lang == NULL || *msgid == '\0')
return(msgid);
/* binary-search for the msgid */
b=0; t=_po2c_lang_size - 1;
while(t >= b)
{
n=(b + t) / 2;
m=&_po2c_lang[n];
c=strcmp(msgid, _po2c_msgids[m->msgid]);
if(c == 0)
return(m->msgstr);
else
if(c < 0)
t=n - 1;
else
b=n + 1;
}
return(msgid);
}
const char * po2c_getcharset(void)
{
if (_po2c_charset)
return _po2c_charset;
else
return "ASCII";
}
int po2c_getnumlangs(void)
{
int n = 0;
while (_po2c_langs[n].lang)
n++;
return n;
}
const char * po2c_getlang(int num)
{
return _po2c_langs[num].lang;
}
EOF
exit 0;