#!/usr/bin/perl # # po2c - Converts .po files to C code # # Copyright (C) 2004 Angel Ortega # # 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-scummvm"; 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() { 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"; # dump first the msgid array print "static const char * const _messageIds[] = {\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 PoMessageEntry {\n"; print "\tint msgid;\n"; print "\tconst char *msgstr;\n"; print "};\n\n"; # dump now each language foreach my $l (keys(%msgs)) { print "static const PoMessageEntry _translation_${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 "struct PoLangEntry {\n"; print "\tconst char *lang;\n"; print "\tconst char *charset;\n"; print "\tconst PoMessageEntry *msgs;\n"; print "};\n\n"; print "const PoLangEntry _translations[] = {\n"; foreach my $l (keys(%msgs)) { $header = $msgs{$l}->{""}; $header =~ /charset=([^\\]+)/; $charset = $1; print "\t{ \"" . $l . "\", \"" . $charset . "\", _translation_${l} },\n"; } print "\t{ NULL, NULL, NULL }\n};\n\n"; print "// code\n"; print << 'EOF'; static const PoMessageEntry *_currentTranslation = NULL; static int _currentTranslationMessageEntryCount = 0; static const char *_currentTranslationCharset = NULL; void po2c_setlang(const char *lang) { _currentTranslation = NULL; _currentTranslationMessageEntryCount = 0; _currentTranslationCharset = NULL; // if lang is NULL or "", deactivate it if (lang == NULL || *lang == '\0') return; // searches for a valid language array for (int i = 0; _currentTranslation == NULL && _translations[i].lang != NULL; ++i) { if (strcmp(lang, _translations[i].lang) == 0) { _currentTranslation = _translations[i].msgs; _currentTranslationCharset = _translations[i].charset; } } // try partial searches for (int i = 0; _currentTranslation == NULL && _translations[i].lang != NULL; ++i) { if (strncmp(lang, _translations[i].lang, 2) == 0) { _currentTranslation = _translations[i].msgs; _currentTranslationCharset = _translations[i].charset; } } // if found, count entries if (_currentTranslation != NULL) { for (const PoMessageEntry *m = _currentTranslation; m->msgid != -1; ++m) ++_currentTranslationMessageEntryCount; } } const char *po2c_gettext(const char *msgid) { // if no language is set or msgid is empty, return msgid as is if (_currentTranslation == NULL || *msgid == '\0') return msgid; // binary-search for the msgid int leftIndex = 0; int rightIndex = _currentTranslationMessageEntryCount - 1; while (rightIndex >= leftIndex) { const int midIndex = (leftIndex + rightIndex) / 2; const PoMessageEntry * const m = &_currentTranslation[midIndex]; const int compareResult = strcmp(msgid, _messageIds[m->msgid]); if (compareResult == 0) return m->msgstr; else if (compareResult < 0) rightIndex = midIndex - 1; else leftIndex = midIndex + 1; } return msgid; } const char *po2c_getcharset(void) { if (_currentTranslationCharset) return _currentTranslationCharset; else return "ASCII"; } int po2c_getnumlangs(void) { return ARRAYSIZE(_translations) - 1; } const char *po2c_getlang(const int num) { assert(num < ARRAYSIZE(_translations)); return _translations[num].lang; } EOF exit 0;