Internationalization
From FreemedDeveloperWiki, the FreeMED developers' Wiki.
FreeMED is designed to be a fully internationalized project, using GettextXML.
Contents |
Web interface
The web interface currently uses an XML encapsulation of gettext, called GettextXML, for i18n. (This currently involves editing the XML by hand.) This is used to get around PHP's dodgy handling of gettext---gettext support is not included in most packaged versions of PHP.
There is a translation site, powered by Pootle, which allows for collaborative editing of GNU gettext. Through a set of conversion routines, we can pull the latest translation source from the code tree and into Pootle. The translation site is located at http://i18n.freemedsoftware.org/ . A simple registration is required to edit translations.
Updating the Pootle Catalogs
These are the bash commands necessary to update the translation templates. They assume that pootle is installed in /usr/share/pootle, FreeMED is checked out as freemed-export:
cd freemed-export
svn update
./scripts/GettextXML_Catalog.pl
cd locale/template
for i in *.xml; do
j=${i%xml}pot
echo "$i => $j"
../../scripts/gettext_dexmlize.pl "${i}" > "/usr/share/pootle/po/freemed/template/${j}"
done
The Pootle catalogs must be updated from templates by an administrator to merge in the new changes for all languages. Please do this off-hours, as it takes a bit of time to merge new translations for all languages.
Extracting a Translation from Pootle
This is still a work in progress, but basically the steps (here, done for French, localized for France):
cd freemed-export
cvs update
mkdir -p locale/fr_FR/
for i in /usr/share/pootle/po/freemed/fr/template/*.po; do
j=$(basename "${i%po}xml")
./scripts/gettext_xmlize.pl "${i}" > "locale/fr_FR/${j}"
done
The freemed.xml file then has to be manually edited to contain the appropriate translation catalog information, which is in the <information/> section:
<Locale>fr_FR</Locale> <LocaleName>Francais</LocaleName>
Locale should be the directory name under locale/, and LocaleName should be the textual name to appear in the select box. These should be entity encoded if they contain any non-standard characters. The following can be used to prepare text, if necessary:
perl -MHTML::Entities -e "print HTML::Entities::encode_entities_numeric('string here', '<>\"&\200-\377')"
GUI interface
The GUI interface uses GettextXML via the Locale::Framework [1] interface to Wx::Locale [2] [3]. It's currently in progress, but will eventually create GNU standard .pot/.po files, which can be translated with a standard .po editor. The locale can be set at login time, as with the web interface.
Directory structure
Wx::Locale is finicky about the directory structure it likes; it took some trial and error to get this working on Windows. We are using i18n instead of the standard locale due to conflicts in Windows' ability (or inability in this case) to deal with case-sensitive path names.
freemed-perl/
|
+----DrugSamples.pl
+----...
+----po/
| +----freemed-perl.pot
| +----en.po
| +----fr.po
| +----...
|
\----i18n/
+----en/
| \----freemed-perl.xml
+----fr/
| \----freemed-perl.xml
\----(other languages...)/
To load the catalog freemed-perl, the following is done:
FreeMED::i18n::Initialize($main::language);
Updating
Automatic
There now exist two shell scripts. po/generate-po.sh updates the template, freemed-perl.pot, and merges it with any existing .po files. po/generate-xml.sh generates the XML files from the .po files. The results shouldn't be committed to the repository, because they can be platform-dependent. Thus, to have internationalization, you must run po/generate-xml.sh before running any scripts.
Manual
There's a way to update the template file freemed-perl.pot without clobbering the old messages, and furthermore, there's a way to merge it into the translated files fr.po and so on without clobbering what's been done for them. I'll describe that method just as soon as I work it out.
1. Rerun xgettext, telling it to join the existing file. (Directory structure is not set in stone.)
[.../freemed-perl] $ xgettext -k_T -LPerl *pm FreeMED/UI/*pm --join-existing --output=po/freemed-perl.pot
2. For each locale, update the corresponding .po-file.
[.../freemed-perl/i18n] $ msgmerge fr.po freemed-perl.pot --update
3. Edit the new .po files to bring them up to date.
4. Recreate the .xml files for each locale.
[.../freemed-perl/po] $ ./generate-xml.sh fr.po > ../i18n/fr/freemed-perl.xml
The updated strings should appear the next time the application is run.
Internationalizing a module
To internationalize a module, do the following.
1. Add this include to the top of the module.
use Locale::Framework; use FreeMED::i18n;
And add this to the ->new method.
FreeMED::i18n::Initialize($main::language);
2. Wrap all translatable text in _T(). So
my $mainframe = DrugSamplesMainFrame->new("FreeMED Drug Samples");
would become
my $mainframe = DrugSamplesMainFrame->new(_T("FreeMED Drug Samples"));
3. Regenerate the .pot file, the .po files, update the translations if necessary.
