GNU gettext for Delphi, C++ Builder and Kylix 1.1.1
dxgettext
Lars B.Dybdahl
Jacquez GarciaVasquez
SandroWendt
Version 1.1
2003
2003
Lars B. Dybdahl and others
Permission to use, copy, modify and distribute
this document for any purpose and
without fee is hereby granted in perpetuity, provided that the above
copyright notice and this paragraph appear in all copies.
Some parts are Copyright (C) by Free Software Foundation, Inc.
Preface
GNU gettext for Delphi, Kylix and C++ Builder, is a port of the general purpose translation tool named GNU gettext.
There are several po file editors out there. In this manual, poedit will be assumed, because it's the most widely used program for Delphi programmers.
Please note that this manual is for both Windows and Linux users.
Introduction
How does GNU gettext work?
GNU gettext is based on the mo file format (explained later), and
everything is based on a function named gettext(), that can look up the English text in
such a file, and find the translation which is also in this file.
The simplest (but not easiest) way to put a translated caption on a label would therefore be:
Label1.Caption:=gettext('Enter username:');
If there is a translation, the translation will be assigned to the label. If there is
no translation, or the translation cannot be read for some reason, the text inside the ( ) is
used instead.
In order to create an mo file, you write a po file and compile it
using the msgfmt compiler. A po file looks like this:
msgid ""
msgstr ""
"Project-Id-Version: Delphi 6 runtime translation\n"
"POT-Creation-Date: 2003-03-04 17:49\n"
"PO-Revision-Date: 2003-04-02 17:48+0100\n"
"Last-Translator: Lars B. Dybdahl <lars@dybdahl.dk>>\n"
"Language-Team: Dansk <da@li.org>\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"License: Freeware\n"
# This is a comment from the programmer to the translator
#. Programmer's name for it: SInvalidCreateWidget
#: Clx/QConsts.pas:22
msgid "Class %s could not create QT widget"
msgstr "Klassen % kunne ikke oprette en QT widget"
#. Programmer's name for it: STooManyMessageBoxButtons
#: Clx/QConsts.pas:23
msgid "Too many buttons specified for message box"
msgstr "For mange knapper angivet for meddelelsesvindue"
Here, msgid marks the English original text,
and msgstr marks the Danish translation. You can write
this file using a text editor like notepad, wordpad etc.
As you might already guess, this system can be extended in many ways.
The most obvious is that you can have several mo and po files. If an application
has more than one file for each language, these are named text domains. The name
of a text domain is also the name of an mo file, without the extension.
The default text domain is name "default" an must therefore be in the file
"default.mo", a compilation of "default.po".
Creating po files
Instead of typing the whole po file yourself, you can create it by scanning
the source code. There are several ways of doing that:
There is a command line tool named dxgettext that can
scan Delphi pas, dfm, xfm, rc files
The dxgettext commandline tool is also able to extract all the
resourcestrings from an executable file on Windows - like .exe files, .dll files etc.
The xgettext commandline tool can scan C and C++ files.
On Windows, you can right-click a file folder in Windows Explorer and choose "Extract strings"
in the popup menu. From the window that then pops up, you can scan all types of files that can be
scanned with the command line tools mentioned above.
These scanners are very advanced. They don't pick up every string they find in a .pas file
- only the strings that are surrounded by calls to gettext() and similar functions. This means that
in this example, the string will not be extracted:
a:='Hello, World';
a:=gettext(a);
Actually, the system will give a warning that a is unknown.
But in this example, it will get extracted:
a:=gettext('Hello, World');
This means that each single string has to be put in between the ( ) of the function call. In order to
save you some typing, an alias for gettext() has been made, and is named _(). The above example can
there also be written this way:
a:=_('Hello, World');
In order to have access to this function, you must include gnugettext.pas in your Delphi or C++ Builder
project. This file is part of the installation.
More gettext functions
Since you can have several mo files, there must be ways to choose between them.
The default setting is to use the "default" domain, which means the file default.mo, if present.
You can change the default text domain using the textdomain() procedure.
You can also ask for a single translation from another textdomain like this:
a:=dgettext('languagenames','Danish');
Here, the string "Danish" will be looked up in languagenames.mo, and if it exists,
the translation will be assigned to a. If no translation exists,
or if the languagenames.mo file doesn't exist for the current language, the string
"Danish" will be assigned to a.
If you want to use a special language, you can switch the default language using
the UseLanguage() procedure, which takes the two-letter language code as
parameter:
UseLanguage ('da'); // selects Danish
You can also specify a language and a country using the two-letter
language and the two letter country code like this:
UseLanguage ('en_US'); // selects American English
The default is to use the language setting in Windows or Linux.
Resourcestrings
If you have tried the Integrated Translation Environment of Borland Delphi,
you will know about the resourcestring keyword. It works like this:
resourcestring
msg='Hello, World';
begin
ShowMessage (msg);
end.
Delphi automatically replaces the reference to msg with a call to the system function
LoadResString(), which retrieves the string "Hello, World" from the resource
part of the executable. The Delphi ITE achieves its translation mechanism by redirecting these
fetches to other files.
By including gnugettext.pas in your project, these fetches are replaced with another function,
which translates all the strings. The default is, that resourcestrings are translated using the
default textdomain, i.e. default.mo. In case you want the system to search other mo files, if the
translation isn't found in default.mo just make some calls to the AddDomainForResourceString.
It is very common to have the Delphi runtime translation in a file named delphi.mo, and this line
in the .dpr file to make sure that the Delphi runtime is translated:
AddDomainForResourceString ('delphi');
Using resourcestring is not recommended, because
Delphi only handles ansistring this way - not Unicode. But it works.
Forms
It would not be practical to use _() function calls to assign all
strings used in a form. If a string has 200 translatable strings, you would
have to type 200 lines of code that assigns strings. Therefore, a procedure
named TranslateComponent was created. This procedure
translates all string properties of a specified component, and all
components owned by this component. You can use this to translate an entire
form by putting this line into the OnCreate event of a form:
procedure TMyForm.FormCreate (Sender: TObject);
begin
TranslateComponent (self);
end;
This single function call replaces lots of _() function calls, which
is nice. And since the strings are put into dfm files, which are already
extracted, everything works perfectly, except that some string properties
should not be translated.
A default setting is that the .Name property of TComponents are not
translated. That wouldn't make much sense, and would break your program.
You can specify additional string properties, that should not be translated
using the TP_GlobalIgnoreClass and TP_GlobalIgnoreClassProperty procedures:
TP_GlobalIgnoreClass(TParam);
TP_GlobalIgnoreClassProperty(TField,'FieldName');
In this example, no TParams will be translated at all, and the
Fieldname property of TField objects will not be translated either. These
are global settings and should therefore be placed at a global place in your
program. It is recommended to put these lines into your .dpr file, see
. You can
also specify a custom handler for a class like this:
TP_GlobalHandleClass (TSpecialClass,Myhandler);
You can read more about typical ignores in .
Action
Localize your first application
Assuming that you have created a simple application in Delphi, this
section will show you how to localize it.
First, you must add gnugettext.pas to your project.
It is recommended to copy this file to your project directory, making it
part of your application.
In order to translate your forms and datamodules, you must add
gnugettext to the uses-clause of all units that have a
form or datamodule, and put this line into the OnCreate event of your forms
and datamodules:
TranslateComponent (self);
In this line, self refers to the form or datamodule
to which this event belongs. The TranslateComponent
procedure then translates all string properties of all components on the
form or datamodule. Please note that all subcomponents are translated - for
a TDataset component this includes the TField subcomponents etc.
Unfortunately, some string properties will raise an Exception or create
unwanted side effects if they
are translated. For instance, if you translate the IndexFieldName property
of a TClientDataset to something that is not the name of an index, it will
raise an Exception. In order to instruct the
TranslateComponent procedure that it should not translate
certain string properties, you should add procedure calls like the ones
specified in just before the
TranslateComponent(self) call in your main form
or in the .dpr file. You can see the source code for a small, localized
application in
Now, you must ensure that all strings inside your source code are
translated properly. If you have a line like this:
ShowMessage ('Hello, World');
Then you must add _() around the string, like
this:
ShowMessage (_('Hello, World'));
Now your program is internationalized, but it hasn't been localized,
yet. This means that your program can run in another language, if a
translation would be present, but we didn't make a translation, yet. In
order to make a translation, we have to get a list of messages first.
In order to get this list, click with the right mouse button on the
file folder that contains your Delphi source code, and choose "Extract
translations to template", just as you can see it in .
This will create a file named default.po with all the texts to be translated. Because it has no translations
in it, it is named a "translation template". You should download poEdit
and use this program to translate the messages in the po file.
When you have translated the po file, click with your right mouse button on the filename in
the Windows Explorer, and choose "Compile to mo file". This will compile the po file and generate the
binary mo file needed by your application. If your application is located in
c:\my\program\path\myprogram.exe, you must put the default.mo file in
c:\my\program\path\locale\##\LC_MESSAGES\default.mo. In this path,
## represents the two-letter language code that you can find in .
That should be it - your application now uses the translation when the Windows settings
corresponds to the two-letter language code that you chose.
New program versions, old translation files
After you have made your first translation, you will find that your old translation file
needs to be updated for this new version. This is the procedure:
Extract strings from the new source code. The po file that was generated by this,
is called the "template", because it determines which messages that the final translation must contain.
You can then update the old translation file to the new translation template using the merge functionality.
Linux users have to use the msgmerge program. Windows users both have a command line
tool, but can also click with the right mouse button on the old translation file in Windows Explorer
and choose "Merge template". Here, you can pick the template file and do the merge.
The result is a file that contains the messages of the template, with the automated comments from
the template and the translations from the translation files.
Since the string extraction tools always extract in the same order, and since the merging
will preserve the order from the template, po files are very suitable to be stored in source code
version control systems like CVS, FreeVCS, SourceSafe, PVCS, TeamSource etc. Just make sure that
you merge with the template before putting a new copy into the version control system.
Create a single language application with localized runtime library
A typical problem with Delphi is to create a program in a non-English language,
because the runtime library is in English. You can easily put a chinese caption on a button,
but you cannot easily make the "Division by zero" message turn chinese,
because that message is hidden deeply in the Delphi runtime library.
With GNU gettext for Delphi, you can easily solve this problem:
Install GNU gettext for Delphi
Add gnugettext.pas to your project.
Get the default.po file for the Delphi runtime libraries and
translate it to your language. Maybe there is already a translation for your
language online.
Compile the default.po file to a default.mo file by clicking on it
with the right mouse button and choose "compile to .mo file".
Create a locale\LL\LC_MESSAGES subdirectory to where your exe
file is, where LL is the two letter ISO 639 language code (see ) of the language that your
program uses (da for danish, de for german etc.) and put your default.mo file there.
Give your translations to us, so that other people may use your translation.
In order to have one .exe file that contains everything, including the translations,
you can now click with the right mouse button on the executable, and choose "Embed translations".
This will append the default.mo file, that is in the
locale\LL\LC_MESSAGES subdirectory into the .exe file.
Please note that you have to do this
every time that you have compiled and generated a new .exe file.
Uses clauses
The order, with which Delphi executes the initialization sections of your units depend on
the order that the units are included in your application. A typical application has a .dpr file
that looks like this:
program Project1;
uses
Forms,
Unit1 in 'Unit1.pas' {Form1};
{$R *.res}
begin
Application.Initialize;
Application.CreateForm(TForm1, Form1);
Application.Run;
end.
Here, the initialization section of the Forms unit will be executed before the initialization
section of the unit named Unit1. All units that are used by the Forms unit
will also have their initialization section executed before the section of Unit1.
Not all units have an initialization section, but the gnugettext.pas file does.
It detects the language, starts the resourcestring translation etc. Therefore, all resourcestrings, that
are fetched before this intitialization section has been run, will not be translated, but all that are
fetched afterwards, will. If you include the DBClient unit, and this unit fails to
initialize because there is no MIDAS.DLL, then it will show an error message in English
if gnugettext was later in the uses list, but it will show it in the local language if gnugettext was first.
A translated application's .dpr file could look like this:
program Project1;
uses
gnugettext in 'gnugettext.pas',
Forms,
Unit1 in 'Unit1.pas' {Form1};
{$R *.res}
begin
// Add extra domain for runtime library translations
AddDomainForResourceString ('delphi');
// Force program to use Danish instead of the current Windows settings
UseLanguage ('da');
// Put ignores on the properties that cannot be translated
TP_GlobalIgnoreClassProperty (TMyComponent1,'property1');
TP_GlobalIgnoreClassProperty (TMyComponent2,'property2');
TP_GlobalIgnoreClassProperty (TMyComponent3,'property3');
TP_GlobalIgnoreClassProperty (TMyComponent4,'property4');
Application.Initialize;
Application.CreateForm(TForm1, Form1);
Application.Run;
end.
This works very, very well for most situations, but if you want translations to start as early as possible,
your .dpr file should look like this:
program Project1;
uses
gnugettext in 'gnugettext.pas',
gginitializer in 'gginitializer.pas',
Forms,
Unit1 in 'Unit1.pas' {Form1};
{$R *.res}
begin
// Put ignores on the properties that cannot be translated
// These just have to be placed before the first call
// to TranslateComponents()
TP_GlobalIgnoreClassProperty (TMyComponent1,'property1');
TP_GlobalIgnoreClassProperty (TMyComponent2,'property2');
TP_GlobalIgnoreClassProperty (TMyComponent3,'property3');
TP_GlobalIgnoreClassProperty (TMyComponent4,'property4');
Application.Initialize;
Application.CreateForm(TForm1, Form1);
Application.Run;
end.
In this example, gginitializer sets all the necessary settings in gnugettext.pas.
It could look like this:
unit gginitializer;
interface
implementation
uses
gnugettext;
initialization
// Add extra domain for runtime library translations
AddDomainForResourceString ('delphi');
// Force program to use Danish instead of the current Windows settings
UseLanguage ('da');
// Do not put TP_GlobalIgnoreClass* statements here, because
// that would require this unit to use other units than gnugettext,
// and then all these units would have their initialization
// section executed before this one.
end.
In this example, the initialization section of gginitializer will be run before the initialization sections
of units like Forms and Unit1 are run.
Solving ambiguities
Sometimes it may happen that the same English message should be translated to two
different messages in another language. A very wellknown ambiguity is the word "free",
which can be like free beer or free speech. For instance, if you have two radiobutton groups
describing a piece of software, and the first is about the software price and the second is
about the software type, both may include the choice "free". The software price "free" would be
translated to "gratis" in Danish, and the software type "free" would be translated to "fri" in Danish.
It is the programmer's responsibility to ensure, that one msgid only can result in one translation.
Sometimes this fails - and then the translator has to report back that something has to be changed.
There are several ways to solve this
Change words
The first solution is to change the words. Instead of the price option "free" you could give the
option "$0", or you could write "free software" in one of the choices. Do a brainstorm and pick the best.
Adding spaces
You could add a space to one of the strings. This would give the translations "free"->"fri" and "free "->"gratis".
Spaces are not visible in a radiogroup. If you use this technique inside the source code, you may want to remove
the space before the space. In this case, you should provide a comment to the translator that the space
needs to be preserved:
// Preserve the initial space in the translation.
dataset.FieldByName('Name').DisplayLabel:=copy(_(' Name'),2,maxint);
A good translator should always preserve leading and trailing spaces, but sometimes it is useful
to give the translator a hint anyway.
Using domains
In some occasions, the solution to ambiguities can be to use several text domains. In the case with
two radio button groups, you would exclude one of them from TranslateComponent()
with TP_Ignore(), and then translate it separately afterwards using
TranslateComponent(MyRadioButtonGroup,'separatedomain');.
Using trailing comments
Some people using trailing comments to include a comment within the msgid:
function stripafterdot(s:widestring):widestring;
var
p:integer;
begin
p:=pos('.',s);
if p<>0 then
s:=copy(s,1,p-1);
Result:=s;
end;
myfield.DisplayLabel:=stripafterdot(_('Name.Displaylabel for the field named "name"'));
This solution requires the translator to know about this notation.
Plural forms
The ngettext() function is a very powerful function for handling plural forms.
In order to understand this function, you should first understand the gettext()
function in .
A well known problem is to specify the number of files in a list of filenames. With this function,
it can be done like this:
LabelCount.Caption:=format(ngettext('%s file','%s files',filelist.Count),[filelist.Count]);
If no translations are available, the ngettext() function will return
'%s file' if filelist.Count=1, and it will return '%s files' otherwise. The format()
function will then put the actual number of files in place of the %s, and the result will be something
like '0 files', '1 file', '2 files', '3 files' etc.
If you would want to translate this to french, the entry in the po file should look like this:
msgid "%s file"
msgid_plural "%s files"
msgstr[0] "%s fichier"
msgstr[1] "%s fichiers"
The idea with ngettext is, that it doesn't just translate "%s file" to "%s fichier", but it takes
into account, that the french use numbers differently. The English say "0 files", but the french use singular
to describe the value zero: "0 fichier". So in the above example, the french version would be: '0 fichier',
'1 fichier', '2 fichiers', '3 fichiers'...
Some languages are even more complicated. In Polish, there are three plural forms, and the translation would
look like this:
msgid "%s file"
msgid_plural "%s files"
msgstr[0] "%s plik"
msgstr[1] "%s pliki"
msgstr[2] "%s plików"
When counting files, it will become: '0 plików', '1 plik', '2 pliki', '3 pliki', '4 pliki', '5 plików'.
Confused? Don't be. Just use ngettext() wherever your text depends on a number,
and the translator will provide the correct translations.
Not all tools handle msgid_plural well
Please note, that not all tools handle msgid_plural well. This includes poEdit and KBabel.
If a po file contains msgid_plural translations, you should use a text editor to edit it/translate.
A good text editor for po files is UniRed.
How does it work?
The ngettext and dngettext functions use gettext(singular+#0+plural) to get a #0-separated
list of plural forms.
Because some tools don't handle msgid_plural forms well, you should put all plural
forms translations into a separate po file. You can do this using dngettext(),
which is equivalent to ngettext() except that it takes a text domain name as
first parameter:
LabelCount.Caption:=format(dngettext('plurals','%s file','%s files',filelist.Count),[filelist.Count]);
In this case, the source code string extraction will put the translation into a file
named plurals.po, and the dngettext() function will
retrieve the translations from plurals.mo. You can then ask the translator
to use notepad to translate the plurals.po file. Notepad is not always
very handy, but it's surely compatible with the msgid_plural notation.
Database applications
Preventing unwanted translations
When you create a database application, there will be a lot of component
properties that you don't want to be translated when using Translatecomponent().
Typically, the field names, table names and even database names of a database have
meaningful translations, and the translation file from the translator may include translations
for field names. Also, SQL statements should not be translated. They will get extracted, but
if the translator modifies them, it would most likely break your program if this translation
would be applied.
Therefore, it is very important, that you consult
to see what ignores you should add to your application. Make sure that these ignores are executed
before the first call to TranslateComponent(). The list in that appendix
may not be complete - especially not if you use database components that are not mentioned.
Therefore you must have a look at your components and make sure, that all properties, that
should not be translated, have a corresponding
TP_GlobalIgnoreClassProperty() call.
DisplayLabel property explained
Delphi is very good for creating a single-language database application, fast, if
the application only needs one language. A typical single-language application uses field
names that are easily understood, like "Name", "Address" etc. Let's assume that you use
a query component with an SQL statement like this:
select * from customer order by Name
In this case, the name of the Name field of the customer
table will propagate through all components, will become the column heading of a
TDBGrid etc. Your grid column for names will read "Name". This is desired in an English language
application, but absolutely not in all other languages.
The solution is to modify the .DisplayLabel property of the TField
components, that your dataset possesses. Every TDataset descendant has a .Fields[]
property that refers to the fields in the dataset, and all these field components are
descendants of TField, which has the TField.DisplayLabel property.
Delphi provides two ways to modify this DisplayLabel property: At runtime and at
design time, which leads to two different ways of handling the localization of it.
Setting displaylabel at runtime
The runtime assignment solution is to assign a displaylabel at runtime like this:
procedure TForm1.Query1AfterOpen(DataSet: TDataSet);
begin
Dataset.FieldByName('Name').DisplayLabel:=_('Name');
end;
This way, the field names are always the same in the database and the entire application,
but the user will see a localized name. This works for all dataset type components, including
table components, TClientDataset etc.
Display label at design time
Creating design-time DisplayLabel properties goes like this:
Double-click the dataset component. This brings up the field list window.
Click with your right mouse button on the field list window and choose "Add all fields".
This requires your dataset to be able to actually fetch data from the database at design time, but will
add all the fields of the dataset to the field list window.
Make sure that the form (or datamodule) that the dataset component is on, is translated
using TranslateComponent() before the dataset is opened. This means that the dataset
needs to be closed by default, and that you have to open the dataset at runtime using .Open
or .Active:=True.
By doing this, the field names will be present in the DFM files, and will therefore be extracted
for the translator to be translated. Now, TranslateComponent() will translate the
TField.DisplayLabel values:
procedure TForm1.FormCreate(Sender: TObject);
begin
TranslateComponent (self);
Query.Open;
end;
Multiple field name translations
Sometimes it is very handy to have field names translated to something else than what
you put into the .DisplayLabel property. For instance, you may want to have
a short version for exported ASCII files, another version for exporting XML files, one
version for reports, and again another version for the .DisplayLabel property.
This is easily done by using several po files (which is the same as multiple text domains).
The part of your program that writes a column header to an ascii file might look like this:
write (mytextfile,dgettext('fieldnames',myfield.FieldName));
This will find the field name in the fieldnames.mo file
and output the translation. The fieldnames.po template can be written
by hand (using notepad), or sometimes be extracted from a datamodule. Often, the number of field
names is so low that the quickest solution is to write the po file by hand.
Translation repositories
It is often very useful to create one po file that has all the translations
from all other po files included. For instance, if you are the producer of software
for chemical laboratories, and you have 5 different products, there will probably be
a lot of common translations for the different products, and it would save you a lot
of work if you don't have to translate it all again for each new product.
In order to merge several po files, you can use the msgcat tool:
C:\translations\da>msgcat -o result.po -t utf-8 --use-first delphi7.po kylix3.po
C:\translations\da>
This set of parameters will put the result into result.po, use utf-8 encoding,
and only take the first translation from each po file.
Project management
Introduction
Localizing an application is not a simple task. In Germany, the first floor is the first one above
ground. In USA, it's at ground level. How do you put that into a database? If you make an integer field named
floor that contains 0 for ground level and 1
for the first floor above ground, it is very easy to understand in Germany, but you'll have to modify the user
interface quite a bit to make the same data accessible by Americans in a way that they understand easily.
The GNU gettext system only helps you with translating messages, but this can go wrong, too. For
instance, the Math unit in Delphi has functionality to convert between different units. The names of these
units can be translated, but if you translate two unit names to the same thing, all programs that use the Math
unit will generate an exception during initialization. Actually, this already happened. The first Danish translation
of the Borland Delphi 7 runtime library had "Hektar" as the translation for both "SquareHectometers" and "Hectares".
It's the same thing, and the correct word is "Hektar", so why not use it? Now that the program doesn't work, whose fault is it?
The answer is very simple: It's the programmer's fault. He didn't live up to the responsibility of ensuring that the translator
couldn't do anything to break the program. It might take unnecessary extra programming to ensure this, but it can be very, very
difficult to debug the program if this is not ensured.
Let's take another example: The translator translates the application, but the users cannot find out how to operate
the program. The English version works well and users know how to operate the English version. Whose fault is this?
Here, the answer is more complex: it might be the translator that made a bad translation, but it could also be the
programmer that designed a user interface that is hard to translate. Often, user interfaces use concepts that can be described with
one word in the programmer's own language, but if it takes 10 words to describe the same concept in another language, people that
only know that other language might not understand the concept. For instance, zip-files could be represented by a zipper
in English (Windows XP does that), but in other languages thsi relationship makes absolutely no sense. In Danish, zip-files are named
"zip-filer", and zipper is "lynlaas". There is absolutely no relationship between "zip-filer" and "lynlaas", and whoever invented that icon
definitely didn't think of internationalization.
The leader of an internationalized project has to:
Ensure that all programmers understand the concept of internationalization.
Ensure that translators can give feedback to the programmers in order to ensure that everything is localizable.
Ensure that there is a release procedure with beta-testing for each language. Having debugged
the program in one language doesn't mean that it is bugfree in other languages, too.
Ensure that the beta-testers know, that they must give feedback on the translation, too.
Coordinating translations
Besides having programmers and translators, it is very important
that somebody is appointed to manage the localization
process. This person must ensure correct archiving of
translations and other po files, and ensure that the translations are tested.
Typically, this assignment is given to a programmer with localization knowledge.
The translator
It can be difficult to find a good translator. A good translator
understands the application, the local market and is good at finding
translations for concepts that maybe doesn't make a lot of sense in the
native language. It is also important that multiple applications are translated the
same way.
There are many small companies out there that have specialized in
translating software. Use one of those - it pays off. One of the good things
about GNU gettext for Delphi, Kylix and C++ Builder, is that the file format
is standardized and known by most translation companies.
Experienced programmers' topics
Determinism and responsibility
It is very important for the software development process, that the
programmer decides, what gets translated, and what doesn't. Also, it is
important for the programmer to ensure, that the translator cannot break the
application, no matter how bad the translations are made.
GNU gettext is based on function calls (gettext, dgettext etc.),
comments to the translator and a proper localization release procedure.
The system doesn't translate anything that isn't put through the
dgettext() function in a way or the other. The programmer fully controls
what gets translated, and what doesn't, and has the responsibility to
ensure, that everything that gets translated, can be translated to virtually
anything without breaking the program. For instance, if the caption of a
label on a form is made translatable by the programmer, the translator can
only change the look of the label. But if the component name of a label
would be translatable, the translator could break the program by translating
the name of a label to something that is already the name of another
component.
In order to get a program translated, the programmer must provide
information to the translator about what gets translated and where to find
it. He also needs to ensure, that the texts are unambigious, that one msgid
cannot be translated into two different things in another language. Since it
may not always be clear to the translator, in which context a message is
used, the programmer can provide comments to the translator, even access to
the source code locations, if the translator is able to read source code.
The comments should also make the translator able to run the program with
his/her translations, and find the place where a particular text can be
found inside the program.
The translator is often also a beta-tester for a specific language.
The translator is often the only one that is able to control the program
while running in the other language, and is often also the only one
internally in the organization that is capable to see if labels are put
correctly, translations are made correctly etc. GNU gettext helps out here
by making the translator able to apply his or her translation without
involving the programmer.
Text domain management
Different text domains are basically different po files. Usually, one
application uses one text domain, but often, several applications share one
text domain. Sometimes it is necessary to use an extra text domain for a
special purpose.
As a project leader, it is important to know, that you can always easily
split a project into two parts. The key is to create two template files instead
of one. Just merge the old translation file with the new templates, and you have
two new, smaller translation files.
It is a bit trickier to assemble two smaller subprojects into one big
translation project with just one file. There might be messages, that were
translated differently in the two projects, and this has to be taken care of.
Several tools can assemble two po files, and these include msgcat
and msghack. See for more information.
The better alternative to resourcestring
Instead of using resourcestrings, there is a better alternative:
ShowMessage (_('Action aborted'));
The _() is a pascal function inside gnugettext.pas, which translates the text. It returns a widestring, unlike resourcestring, which is limited to ansistring. You can use _() together with ansistrings as you like, because Delphi automatically converts widestrings to strings when needed. Another benefit of this is that you can write comments, that the translator can use to make better translations:
// Message shown to the user after the user clicks Abort button
ShowMessage (_('Action aborted'));
You can also write the comment in the same line:
ShowMessage (_('Action aborted')); // Message to user when clicking Abort button
But only the // style comment is supported - you cannot use { } or (* *) comments for this purpose.
Good comments normally lead to good translations. If the translator has a copy of the source code, poedit and kbabel can both show the location in the source code to the translator. This makes sense with _(), because the translator might get a good idea, what this is about, even if the translator isn't a programmer.
In other words, there are many reasons to use _() instead of resourcestrings. If you create a new application, don't even think about resourcestrings - just go directly for the _() solution.
Debugging
You will typically experience two types of errors:
Something is not translated
An error occurs when using TranslateComponent()
The first item often happens because the translation files (.mo files) are not present for the
current language, not placed where they are expected to be etc. The second occurs because a property
of some component should not be translated. When possible, you should get an Exception that is easy
to understand, but sometimes you don't. This section is about finding the error anyway.
The gnugettext.pas file has a logging system for debugging built-in.
You activate it by defining the conditional define DXGETTEXTDEBUG. You can also
find the first occurence of this string in gnugettext.pas - here you will find
the following code:
// DEBUGGING stuff. If the conditional define DXGETTEXTDEBUG is defined, it is activated.
{ $define DXGETTEXTDEBUG}
{$ifdef DXGETTEXTDEBUG}
const
DebugLogFilename='c:\dxgettext-log.txt';
{$endif}
One way to activate the debugging log is to change { $define DXGETTEXTDEBUG}
to {$define DXGETTEXTDEBUG} by removing the space. As you can see, you can also here
modify the location where the log-file is written to.
The log-file contains a lot of information. At the beginning, you will find very useful information about
what settings the system uses:
Debug log started 21-08-2003 10:32:08
UseLanguage(''); called
LANG env variable is ''.
Found Windows language code to be 'da_DK'.
Language code that will be set is 'da_DK'.
Plural form for the language was not found. English plurality system assumed.
Text domain "default" is now located at "C:\source\sf\dxgettext-devel\dxgettext\sample\locale\"
Changed text domain to "default"
Globally, the NAME property of class TComponent is being ignored.
Globally, the PROPNAME property of class TCollection is being ignored.
Extra domain for resourcestring: delphi
Globally, class TFont is being ignored.
In this example, we can see that the program was running on a Danish language Windows, which uses
the same plurality system as English. It also tells us where it will look for .mo files, and what ignore settings
were specified. When TranslateComponent() is used, it looks like this:
======================================================================
TranslateComponent() was called for a component with name FormMain.
A retranslator was created.
----------------------------------------------------------------------
TranslateProperties() was called for an object of class TFormMain with domain "".
Reading .mo data from file 'C:\source\sf\dxgettext-devel\dxgettext\sample\locale\da_DK\LC_MESSAGES\default.mo'
Found in .mo (default): ""->"Project-Id-Version: PACKAGE VERSIONPOT-Creation-Date: 2003-02-16 21:36PO-Revision-Date: 2003-02-17 23:01+0100Last-Translator: Lars B. Dybdahl <lars@dybdahl.dk>Language-Team: <>MIME-Version: 1.0Content-Type: text/plain; charset=UTF-8Content-Transfer-Encoding: 8bit"
GetTranslationProperty(CONTENT-TYPE: ) returns 'text/plain; charset=UTF-8'.
Found in .mo (default): "GNU gettext sample application"->"GNU gettext eksempel"
Found in .mo (default): "Click me"->"Klik mig"
Found in .mo (default): "Click me"->"Klik mig"
----------------------------------------------------------------------
This is the first time, that this component has been translated. A retranslator component has been created for this component.
======================================================================
The log simply specifies exactly the filename from which translations are fetched, and it
also specifies exactly, which strings are translated to what using which text domain. The "retranslator component"
is a component that is added to the form to make it remember the untranslated properties, in case
you want to do a language switch at runtime.
The first time that the application wants to translate an "OK" button, it looks like this:
Loaded resourcestring: OK
Translation not found in .mo file (default) : "OK"
Reading .mo data from file 'C:\source\sf\dxgettext-devel\dxgettext\sample\locale\da_DK\LC_MESSAGES\delphi.mo'
Found in .mo (delphi): ""->"Project-Id-Version: Delphi 7 RTLPOT-Creation-Date: 2003-03-02 18:54PO-Revision-Date: 2003-03-03 00:31+0100Last-Translator: Lars B. Dybdahl >lars@dybdahl.dk<Language-Team: Dansk >da@li.org<MIME-Version: 1.0Content-Type: text/plain; charset=UTF-8Content-Transfer-Encoding: 8bit"
GetTranslationProperty(CONTENT-TYPE: ) returns 'text/plain; charset=UTF-8'.
Found in .mo (delphi): "OK"->"O.k."
Here you can see, that it first searches default.mo for a translation, but doesn't
find one. Because it's a resourcestring translation, and because we specified the "delphi" textdomain to be searched
for resourcestrings, it decides to try out delphi.mo. This file has not been opened, yet, and therefore
the file is opened at this point, and the full filename is written to the log file. The first action when opening
a new .mo file is to check wether the Content-Type is set to use utf-8. Once it found out that this is the case,
it looks up "OK" in the delphi.mo file, and finds the Danish "O.k." translation.
Log-files can be huge if your program runs for a long time. If that happens, load the logfile into an editor
that is capable of handling huge files, and search for the keywords that you saw in this section.
If your program breaks because a string property is translated that shouldn't, try to search for the
Exception error message in the log file (error messages are translated, too, and are also mentioned in the
log file). The last translation mentioned before that error mesage is probably the one that made your program fail.
When you have identified a property that should not be translated, you can either specify it globally
that it should not be translated, by calling TP_GlobalIgnoreClassProperty(), or you can
disable it only for the next call to TranslateComponent() using TP_Ignore.
Directives
It is possible to control the string extraction from Pascal source code using directives,
very much like the compiler directives. Currently, the directives only support extraction
of string constants defined using the const keyword, into a specified text domain:
{gnugetext: scan-all [text-domain='domain name'] }
.
. in this section the string constants will be extracted
.
{gnugettext: reset }
{gnugettext: scan-all } is named the 'start directive' in this document.
{gnugettext: reset } is named the 'end directive' in this document.
If a start directive exists, the end one must exist in the same file.
The directives are local to a file.
Several start directive can exists before the end one and is used to change the target domain.
This directive actually works for constants only! Initialized variables are not taken in care.
The domain name, if present, must be enclosed by single quote.
If the domain name include a single quote (but remember that the domain name will becomes a file name), it must be doubled.
There are no check on the domain name. It is assumed that you know what you type!
Example:
{gnugettext: scan-all text-domain='toto' }
Const
a = 'toto';
{gnugettext: scan-all text-domain='titi' }
b = 'titi';
{gnugettext: reset }
In this example, the msgid 'toto' will be put into toto.po
and 'titi' will be put into the file named titi.po.
It is the plan to extend the directive system in the future to disable/enable string
extraction etc.
Advanced topics
Convert from Delphi ITE
In order to convert Delphi ITE translations to GNU gettext, you must use the ixtopo
or dfntopo command line utilities. In order to get more help on these programs, run
the programs from a commandline without parameters.
Translation statistics
The msgfmt program is able to output some statistics about the contents
of a po file:
C:\source\sf\DXGETT~1\TRANSL~1\de>msgfmt --statistics kylix3.po
1815 translated messages, 113 fuzzy translations, 53 untranslated messages.
Additionally, the msgshowheader tool is able to show the header of
a compiled mo file:
C:\source\sf\DXGETT~1\TRANSL~1\de>msgshowheader kylix3.mo
Project-Id-Version: Kylix 3 German
POT-Creation-Date: 2003-03-02 18:54
PO-Revision-Date: 2003-07-02 17:20+0100
Last-Translator: Sandro Wendt <info@xan.de>
Language-Team: XAN <info@xan.de>
MIME-Version: 1.0
Content-Type: text/plain; charset=utf-8
Content-Transfer-Encoding: 8bit
In order to generate statistics about translations, we just need to be able to create a web page
by iterating over all po files and parse the output from the above programs. There is a
Python script in the translations CVS module on SourceForge for this
purpose.
For further information on this topic, ask in our
forum.
Multiple instances
This chapter will contain an explanation of the TGnuGettextInstance class.
Multithreading issues
The procedures in gnugettext.pas are not multithreading safe by themselves.
If you want to create a threadsafe application, you will need to create one TGnuGettextInstance object
for each thread and make sure that each thread only uses its own object.
API reference
procedure AddDomainForResourceString (domain:string);
The initialization section of the gnugettext unit hooks into several
Delphi runtime functions and replaces those functions. One of the functions
that are replaced, is the function LoadResString, which retrieves resourcestring
strings from the resource-part of the .exe file. The replacement first finds
the string from the resource-part of the .exe file, and then translates it
using the 'default' text domain. You can instruct the system to search for a translation in
other text domains, too, if it isn't found in the default text domain, by
using this procedure. Simply write this to make LoadStr search delphi.mo,
too, if it isn't found in default.mo:
AddDomainForResourcestring ('delphi');
procedure RemoveDomainForResourceString (domain:string);
If a text domain has been added using AddDomainForResourcestring(),
you can remove it again using this function.
function LoadResString(ResStringRec: PResStringRec): widestring;
This function is 100% identical to LoadResString.
function LoadResStringW(ResStringRec: PResStringRec): widestring;
LoadResStringW is a replacement of the system unit function named LoadResString,
which works exactly the same way, except that the string fetched is translated (if possible) and
the translation is returned as a widestring.
function LoadResStringA(ResStringRec: PResStringRec): ansistring;
This function is identical to LoadResStringW, except that the string is returned
using ansistring. There should be no need for this function - it only exists because this
is exactly the function that is used when the resourcestring keyword is used in Delphi.
var ExecutableFilename:string;
Don't modify this variable. It contains the full path and filename of the .exe file,
if gnugettext.pas is compiled into an .exe file, and it contains the
full path and filename to the .dll file, if gnugettext.pas is compiled into
a .dll file. This variable is used to find any embedded translations, if present.
procedure HookIntoResourceStrings (enabled:boolean=true; SupportPackages:boolean=false);
This procedure lets you control, whether resourcestring retrieval should be translated automatically
or not. The default is to have this enabled, but there may be situations, where you want to disable it.
Also, this procedure lets you hook deeper into the runtime library, which is needed when creating
packages. Please note, that package support requires you to keep track of designtime and runtime.
You may only call this function with the second parameter set to True during runtime. Setting it to
true during design time may make your package conflict with other packages, that also hook into the runtime
libraries.
The problem with packages are, that calling a function from the runtime library doesn't call the
functions directly - instead it calls into an address, where you will find a machine code jump to the real
function, which can be shared between packages. When you load and unload packages inside the Delphi ITE,
the packages are sharing runtime libraries. If one package would hook into the runtime library, and another package
does the same, and the packages are unloaded FIFO style, the "unhooking" of the runtime library done by last
package that is unloaded, will make your system unstable. It is very important to understand this when
creating packages, because your system might look as if it just works, but it might not work with your customer
unless you do it right.
const DebugLogFilename='c:\dxgettext-log.txt';
This is the full path of the log output when doing debugging. See
for more information.
TExecutable
This class is not for end-user usage. Please ignore it.
TGetPluralForm
This class is not for end-user usage. Please ignore it.
TGnuGettextInstance class
The entire functionality of the gnugettext.pas unit
is encapsulated in the TGnuGettextInstance class. This way you
can instantiate multiple instances if you need an instance with different settings
than the ones from the default instance. The default instance is put into a variable
named DefaultInstance, and almost all non-class procedures and functions
in the unit refer to this instance.
procedure UseLanguage(LanguageCode: string);
When an application starts up, the initialization section of gnugettext.pas
makes this call:
UseLanguage('');
This call examines the system language settings and sets the language values accordingly.
On Windows, GetLocaleInfo() is used to determine the language settings, although on Windows 95,
GetThreadLocale() is used because GetLocaleInfo() wasn't implemented in that version.
The OS language settings can always be overridden by setting the LANG
environment variable like this (example is for Windows):
set LANG=de_DE
myapp
or on Linux:
LANG=de_DE && ./myapp
When a language has been set, UseLanguage will examine the first two letters and find out
which plural forms that apply to this language. See for more
information on this topic.
All mo files that were open are closed. mo files are opened again when the text domains
are accessed.
A language code usually consists of a two-letter lowercase language code, an underscore,
and a two-letter uppercase country code. On Windows, this is not case sensitive, but on Linux, it is.
German in Germany becomes de_DE, English in England becomes en_GB
and flamish becomes nl_BE. If no translations can be found or a 5-character
language code, the system automatically attempts to use only the first two digits. This means that
if there is no nl_BE/default.mo file, nl/default.mo will be used
instead, if present.
You can find the two-letter ISO 639 language codes in and the two-letter ISO 3166
country codes in .
function _(msg:widestring):widestring;
The _() function is an alias to the gettext function. Please
see for further information.
function GetCurrentLanguage:string;
This function returns the language code that was specified to the last call of UseLanguage().
If UseLanguage() has not been called, yet, or was called with an empty string as parameter,
it returns the language code that was derived from the operating system language settings.
See for more information.
function gettext(msg:widestring):widestring;
This function is the most important function of them all, and is the function
that names GNU gettext. It takes a string as parameter and returns the translation
of that string, if a translation can be found. It is used like this:
MessageDlg (gettext('Hello, World'),mtInformation,[mbOK],0);
Because the _() function is the same as gettext(),
the line above is normally written like this:
MessageDlg (_('Hello, World'),mtInformation,[mbOK],0);
In version 1.1, the string parameter may only contain ascii characters. If it contains non-ascii characters,
the parameter will also be the result value of the function. In order to get support for
non-ascii characters, you must use a later version. The only reason that the parameter
is specified as widestring, is because it is planned to make it possible to specify non-ascii
strings in the future.
The result value is a widestring. If you use this in context with the Delphi
string type (ansistring), Delphi 6 and later
will automatically convert the string types. The MessageDlg example above show this,
the first parameter in MessageDlg() is a string,
not a widestring. Delphi converts between string and
widestring using Windows API calls, and therefore converts perfectly
to and from multibyte character sets. On Linux, the behaviour is identical.
The gettext() functions looks up translations in the default.mo
file (the default text domain) unless the textdomain() procedure has
been called.
Special case: The empty string
Please note that you should never try to translate an empty string (_('')).
The translation of the empty string is the message header, which contains information about the translator,
the character set, modification date etc.
Performance
The gettext() function is very fast at looking up translations.
It does a binary search on the translation data, which are sorted in binary,
and the search is case sensitive and only a perfect match will give a translation.
The translation files are memory mapped on Windows and read into memory on Linux, and
therefore it doesn't take much time to find a translation. However, you should consider
to store a translated value if you need it a lot of times, like when populating a big array.
function dgettext(Domain: string; MsgId: widestring): widestring;
This function is identical to gettext(), except that it
looks up translations in the text domain that is specified as the first parameter.
You should always specify a string literal as first parameter - don't use variable
names etc.:
LanguageList.Add(dgettext('languagenames','Danish'));
In this example, Danish is looked up in languagenames.mo.
During extraction of strings from Delphi/ObjectPascal source code, 'Danish' will be put into
languagenames.po.
Special note for C/C++ programmers
The string extraction tool that is used for C and C++ source code extracts differently
than the extraction tool for ObjectPascal source code. In the example above, the string
'Danish' will be put in the main default.po file, mixing everything up.
For further information on this subject, please see .
function ngettext(const singular,plural:widestring;Number:longint):widestring;
This function is explained in .
function dngettext(Domain,singular,plural:widestring;Number:longint):widestring;
This function is explained in .
function getcurrenttextdomain:string;
This function returns the current text domain. The default value for
the current text domain is 'default', but can be changed using textdomain().
See for further information.
procedure textdomain(Domain:string);
This procedure changes the current textdomain, i.e. the text domain that is used by
gettext(), _() and ngettext(). The
default text domain, if textdomain() is not called, is 'default'.
Usually, libraries, modules and other pieces of software that are not developed together with
the application that they are included in, should use another text domain. For instance, if the programmer
of program A uses module B, then module B should use another text domain that 'default'.
procedure bindtextdomain(Domain:string; Directory:string);
Each text domain can be located in a separate directory. The default is to search
for all mo files as applicationdir/locale/XX/LC_MESSAGES/domainname.mo, where applicationdir
is the directory where the .exe file or .dll file is, and XX is the language code. You can
specify an alternative location for a domain here. Example:
bindtextdomain ('moduleB','c:\moduleB\locale\');
In this example, all translations done using the text domain 'moduleB' will be looked
up in c:\moduleB\locale\XX\LC_MESSAGES\moduleB.mo. This can be very useful
if you link a module into your program file, but the module translations are somewhere else on your harddisk.
procedure bindtextdomainToFile (Domain,Filename:string);
If you want to use gettext() as a string translation table that is independent of
languages, you might want to bind a domain to a specific file on the harddisk. For instance, you might
want to translate the ISO language codes to English language names. This could be done like this:
function GetLanguageName (isocode:string):string;
var
EnglishLanguageName:string;
begin
bindtextdomainToFile ('isocodes','c:\isocodes.mo');
EnglishLanguageName:=dgettext('isocodes',isocode);
Result:=dgettext('languagenames',EnglishLanguageName);
end;
There are many ways this can be used. For instance, the msgshowheader command line
tool uses bindtextdomainToFile() to fetch the header from a specific file that is given
as parameter. It is done something like this:
bindtextdomainToFile ('parameterfile',paramstr(1));
writeln (dgettext('parameterfile',''));
The translation of the empty string is always the header.
procedure GetListOfLanguages (domain:string; list:TStrings);
This procedures searches the directory structure and the embedded translations
for any valid translation files for the specified domain. It uses the bindtextdomain()
directory. The result is a list of language codes that it puts into the list parameter.
It is then up to the programmer to convert these language codes into language names. Example:
// Put the language codes into a listbox
ListBox.Items.Clear;
DefaultInstance.GetListOfLanguages ('default',ListBox.Items);
// Convert the language names to an English language name using isotolanguagenames.mo
DefaultInstance.BindtextdomainToFile ('isotolanguagenames',extractfilepath(paramstr(0))+'isotolanguagenames.mo');
DefaultInstance.TranslateProperties (ListBox,'isotolanguagenames');
// Translate the English language name to a localized language name
DefaultInstance.TranslateProperties (ListBox,'languagenames');
function GetTranslationProperty (Propertyname:string):WideString;
The translation files contain a header as the translation of an empty string. A typical
header looks like this:
msgid ""
msgstr ""
"Project-Id-Version: Delphi 7 RTL\n"
"POT-Creation-Date: 2003-03-02 18:54\n"
"PO-Revision-Date: 2003-06-01 11:52--100\n"
"Last-Translator: Lars B. Dybdahl <Lars@dybdahl.dk>\n"
"Language-Team: Dansk <da@li.org>\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"License: You may use this file any way you want\n"
As you can see, all lines consist of a keyword, a colon, a space and a value. In order
to retrieve these informations easily, you can use the GetTranslationProperty() function
like this:
LabelTranslationLicense.Caption:=DefaultInstance.GetTranslationProperty('License');
function GetTranslatorNameAndEmail:widestring;
These two lines do the same thing:
LabelTranslator.Caption:=DefaultInstance.GetTranslationProperty('Last-Translator');
LabelTranslator.Caption:=DefaultInstance.GetTranslatorNameAndEmail;
In other words, this function is just an easy way to fetch the translator name, but not the only one.
See for more information.
procedure SaveUntranslatedMsgids(filename: string);
This function was originally meant to be used for debugging,
but much better tools are available now.
Please do not use this function - expect it to become deprecated in future versions.
procedure TranslateProperties(AnObject:TObject; textdomain:string='');
This procedure iterates through all properties of AnObject and
all it's subcomponents, and translates all string and widestring properties using the specified
text domain. If no text domain is specified, the current text domain is used.
It will skip all properties that were marked to be ignored by one of the TP_*
procedures. If no translation is found for a string, it is not translated.
procedure TranslateComponent(AnObject: TComponent; TextDomain:string='');
This procedure will basically do the same as TranslateProperties(),
but it will add a subcomponent to AnObject, which remembers all untranslated
strings. This makes it possible to retranslate AnObject to another language
later.
The second time that TranslateComponent() is called for a specific
object, the subcomponent (that remembers all untranslated strings) is detected, and a retranslation is done.
The retranslation retranslates exactly those properties that were translated the first time -
it does not recurse all subcomponents to redetect string properties. Therefore, you must be careful
that all subcomponents that were present at the first translation, also are present at the retranslation.
function TP_CreateRetranslator:TExecutable;
This function is only for internal use. Don't use it yourself.
procedure TP_Ignore(AnObject:TObject; const name:string);
This procedure only affects the next execution of TranslateProperties()
or TranslateComponent(). The first parameter must be the same as the first
parameter in those other two procedures, and the name parameter specifies, which property you
don't want to be translated. Several syntaxes are allowed:
TP_Ignore (self,'ButtonOK.Caption'); // Ignores caption on ButtonOK
TP_Ignore (self,'MyDBGrid'); // Ignores all properties on component MyDBGrid
TP_Ignore (self,'.Caption'); // Ignores self's caption
Since this procedure only has effect on the upcoming translation, it should always be
used just before TranslateProperties()
or TranslateComponent(). Example:
procedure TFormMain.FormCreate(Sender: TObject);
begin
TP_Ignore (self, 'Listbox1.Items');
TranslateComponent (self);
end;
In this example, that shows an implementation in the form's FormCreate event handler,
all components on the form except the items in Listbox1 are translated.
procedure TP_GlobalIgnoreClass (IgnClass:TClass);
This procedure puts a class on a global ignore list, so that all objects of this
type or of types descending from this type won't be translated by
TranslateProperties() or TranslateComponent().
For instance, it is not very useful to have TFont objects translated, and therefore this
line would make a lot of sense in most applications:
TP_GlobalIgnoreClass (TFont);
A good place to put this line is in the .dpr file. See for
further information.
procedure TP_GlobalIgnoreClassProperty (IgnClass:TClass;propertyname:string);
This procedure puts one property of a class on a global ignore list,
so that this property won't be translated by
TranslateProperties() or TranslateComponent()
for all objects of this type or of a type that descends from this type.
For instance, it is not very useful to have TField.Fieldname translated, and therefore this
line would make a lot of sense in most database applications:
TP_GlobalIgnoreClassProperty (TField,'Fieldname');
A good place to put this line is in the .dpr file. See for
further information.
procedure TP_GlobalHandleClass (HClass:TClass;Handler:TTranslator);
This procedure makes it possible implement another translation handling of a class
than what you can control with
TranslateProperties(), TranslateComponent()
and the TP_* procedures. For instance, if you create a new class that does not use
the published keyword, its properties cannot be iterated by
TranslateProperties() and TranslateComponent().
Another example is, when you get 3rd party components, that fail when you translate it's
properties, but if you do something special to them, they translate well.
Basically, if you cannot translate a component using
TranslateComponent, but you can write a procedure yourself
that can, then use TP_GlobalHandleClass to make
TranslateComponent use your procedure.
"Hello, World" source code
Sample.dpr
program Sample;
uses
gnugettext in 'gnugettext.pas',
gginitializer in 'gginitializer.pas',
Forms,
Graphics,
SampleForm in 'SampleForm.pas' {FormMain};
{$R *.res}
begin
// This is the list of ignores for this project. The list of
// ignores has to come before the first call to TranslateComponent().
TP_GlobalIgnoreClass(TFont);
Application.Initialize;
Application.CreateForm(TFormMain, FormMain);
Application.Run;
end.
gginitializer.pas
unit gginitializer;
interface
implementation
uses
gnugettext;
initialization
// Use delphi.mo for runtime library translations, if it is there
// by putting this line into this separate unit, we can execute it
// before the initialization sections of the other units are executed.
AddDomainForResourceString('delphi');
end.
SampleForm.pas
unit SampleForm;
interface
uses
Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms,
Dialogs, StdCtrls;
type
TFormMain = class(TForm)
ButtonTestGettext: TButton;
ButtonTestResourcestring: TButton;
procedure ButtonTestGettextClick(Sender: TObject);
procedure ButtonTestResourcestringClick(Sender: TObject);
procedure FormCreate(Sender: TObject);
private
{ Private declarations }
public
{ Public declarations }
end;
var
FormMain: TFormMain;
implementation
uses
gnugettext;
{$R *.dfm}
procedure TFormMain.FormCreate(Sender: TObject);
begin
TranslateComponent (self);
end;
resourcestring
MessageToUser='Thank you for clicking this button';
procedure TFormMain.ButtonTestResourcestringClick(Sender: TObject);
begin
// This is a demonstration of automatic resourcestring translation
MessageDlg (MessageToUser,mtInformation,[mbOK],0);
end;
procedure TFormMain.ButtonTestGettextClick(Sender: TObject);
begin
// This is a demo of the _() syntax
MessageDlg (_('Thank you for clicking this button'),mtInformation,[mbOK],0);
end;
end.
SampleForm.dfm
object FormMain: TFormMain
Width = 354
Height = 179
Caption = 'GNU gettext sample application'
OnCreate = FormCreate
Font.Charset = DEFAULT_CHARSET
Font.Color = clWindowText
Font.Height = -11
Font.Name = 'MS Sans Serif'
Font.Style = []
object ButtonTestGettext: TButton
Left = 64
Top = 48
Width = 75
Height = 25
Caption = 'Click me'
OnClick = ButtonTestGettextClick
end
object ButtonTestResourcestring: TButton
Left = 144
Top = 48
Width = 75
Height = 25
Caption = 'Click me'
OnClick = ButtonTestResourcestringClick
end
end
Dxgettext command-line tools reference
assemble
This tool embeds your translations in your executable. When your programs work, including
translations, type this:
assemble --dxgettext applicationfilename.exe
This will find all translation files (mo files) in the locale subdirectory and append them
to the exe file. It also works with dll files. This system should coexist nicely with other
tools that append something to the .exe file, as long as the other tool knows how to coexist with
other tools. If you are using another tool to append something, that does not coexist with other tools,
use that tool before you embed translations.
dfntopo
This tool converts your Delphi ITE translations to PO files. This is the help
screen that appears when you run the program without parameters:
DFNToPO: extracts strings from DFN and RC files and insert any translations into a PO file
This program is subject to the Mozilla Public License
Version 1.1 (the "License"); you may not use this program except
in compliance with the License. You may obtain a copy of the License at
http://www.mozilla.org/MPL/MPL-1.1.html
Portions created by Peter Thornqvist are
Copyright (c) 2003 by Peter Thornqvist; all rights reserved.
Usage:
DFNToPO [options]
where [options] can be any of the following:
-s : searches in sub-folders also (defaults to FALSE)
-f : force creation of DFN/RC items not found in po (defaults to FALSE)
-m : merge TStrings items into single item delimited by \n (defaults to FALSE)
-p<POFile> : full path and filename of the po file (defaults to "default.po" in current dir).
NOTE: Filenames with spaces must be enclosed in quotes.
-d<DFNPath> : full path to the dfn files. Do NOT include a filemask (defaults to current dir).
NOTE: Paths with spaces must be enclosed in quotes.
See for further information.
dxgettext
This will extract all texts from the specified files and save them in
a file named default.po:
dxgettext usage:
dxgettext *.pas *.dfm *.dpr -r
dxgettext -b c:\source\myprogram --delphi
The following file formats are supported:
ObjectPascal source code: *.pas, *.inc and *.dpr.
DFM/XFM files: *.dfm and *.xfm.
C/C++ source code: *.c and *.cpp. These are extracted using the xgettext command line tool
(see ).
RC files: *.rc.
Executables: *.exe, *.dll and *.bpl. (only on Windows)
This is the help text that appears when you run the program without parameters:
dxgettext 1.1.1
dxgettext usage:
dxgettext *.pas *.dfm *.dpr -r
dxgettext -b c:\source\myprogram --delphi
This will extract all texts from the specified files and save them
in a file named default.po.
Options:
--delphi Adds the wildcards: *.pas *.inc *.rc *.dpr *.xfm *.dfm
--kylix Adds the wildcards: *.pas *.inc *.rc *.dpr *.xfm
-r Recurse subdirectories
-b dir Use directory as base directory for filenames.
You can specify several base dirs, that will all be scanned.
-o:msgid Order by msgid
-o:occ Order by occurence (default)
-o dir Output directory for .po files
-q Quiet: Reduces output to absolute minimum.
--codepage nnn Assume the specified codepage. Default is CP_ACP.
--nowc Assume wildcards to be part of filenames
--ignore-constreplace Suppresses warnings about CRLF
If a filename is preceded with @, it is assumed to contain a list of
filenames or file masks.
dxgreg
This Windows only tool can do three different things:
Starting it with no parameters will make it register
the shell extensions with your Windows. This
is normally done during installation and requires administrative rights
with your computer. If you lose the desktop integrations
for some reason, just run this program and your Windows Explorer will be
fully integrated with GNU gettext for Delphi, C++ Builder and Kylix again.
If you start it with the --reset-user parameter, it will
delete all user-specific file extension associations done to the file types used by this system.
This only makes sense on Windows 2000, XP and later. It does not require administrative rights.
If you start it with the --uninstall parameter, it will delete
all shell integration features that make use of executable files in the directory where
dxgreg.exe was started. This is usually done when uninstalling this system.
ixtopo
This tool is useful for converting Delphi ITE translations to GNU
gettext. This is the text that appears when you run the program without parameters:
IXTOPO 1.0: extracts strings from an ITE xml file and inserts any translations into a PO file.
This program is subject to the Mozilla Public License
Version 1.1 (the "License"); you may not use this program except
in compliance with the License. You may obtain a copy of the License at
http://www.mozilla.org/MPL/MPL-1.1.html
Portions created by Peter Thornqvist are
Copyright (c) 2003 by Peter Thornqvist; all rights reserved.
Usage:
ixtopo <xmlfile> <pofile> <locale> [-f]
where:
<xmlfile> is the xml file to read from (REQUIRED)
<pofile> is the po file to write to (REQUIRED)
<locale> is the locale to extract and insert into the po file (REQUIRED)
-f forces the creation of new entries in the po file if not found (OPTIONAL, defaults to FALSE)
NOTE:
Since locale names in XML files contains spaces, you must
put quotes around the locale, i.e use "US en" to extract
the American english translations
See for further information.
msgimport
Version 1.2
This tool is only available from version 1.2 and later.
This tool makes it possible to create a po file from an ascii tabulated
text file. It does not support multiline msgstr values.
In order to use it, it must conform to these rules:
Be an utf-8 encoded text file
It may not contain any header lines etc.
It must contain exactly two columns, separated by a tabulator (ascii 9), where
the first column contains the msgid, and the second column contains the msgstr.
If you have something in tabular form that you want to convert to a po file, do it like this:
Load the tabulated data into a spreadsheet. If you don't have a spreadsheet on your computer,
get it at openoffice.org.
Delete all columns except the two that contain msgid and msgstr. Make sure the first column is msgstr.
Delete all rows that do not contains data to be converted.
Save the file as a text-file, tabulator separated with no text delimiters.
Load the text file into an utf-8 capable text editor like Unired.
Save the text file as an utf-8 text file with "no BOM".
Run msgimport textfile.txt -o output.po and you got a valid .po file.
msgmergePOT
This tool can merge two templates to become a translation file. It
looks at the comments in each template to find out, which msgids belong
together.
Currently, there is a problem with this, because dxgettext version 1.1 enforces ascii
as the only valid character set for po files. In order to use this tool, you must install
the old version 1.0 or version 1.2 or later.
This is the help screen that appears when you run the tool with no parameters:
msgmergePOT 1.1.1
msgmergePOT usage:
msgmergePOT english.po otherlanguage.po destination.po
This will create a po file from two identical po extracts that
only differ by the language, for instance the German and English
runtime source code from Borland.
msgmkignore
This tool tries to guess, which msgids should not be translated, and
writes those to a separate file. The result of this can be used with
msgremove to remove unnecessary msgids from a template before sending it to
the translator.
msgmkignore uses several methods to find out, which messages aren't
translatable. These include finding messages without a single letter in it and
finding messages with letters and digits but no spaces.
This is the help screen that appears when you run the program with no parameters:
msgmkignore 1.1.1
Usage:
msgmkignore default.po -o ignore.po
This will extract texts from default.po that this program
thinks should not be translated by a translator
msgremove
msgremove can remove all messages from a PO file that are contained in
a second PO file. With this, you can remove a lot of unnecessary entries
from your automatically generated template before sending it to the
translator. This is the text that appears when you run the program without
any parameters:
msgremove 1.1.1
msgremove usage:
msgremove default.po -i ignore.po -o output.po
This will generate the file output.po as a copy of default.po,
but without all the MsgIds that are listed in ignore.po.
msgsplitTStrings
This tool is only intended to be used when upgrading from versions of
dxgettext that are older than version 1.0. It takes all multiline messages
and adds each line without the linebreak as a new message.
msgstripCR
This tool is only intended to be used when upgrading from versions of
dxgettext that are older than version 1.0. It simply removes all '\r' escape
sequences from messages.
GNU Command-line tools reference
msgattrib
Changes the fuzzy/obsolete/translated status of messages in a PO file.
NAME
msgattrib - attribute matching and manipulation on message catalog
SYNOPSIS
msgattrib [OPTION] [INPUTFILE]
DESCRIPTION
Filters the messages of a translation catalog according to their
attributes, and manipulates the attributes.
Mandatory arguments to long options are mandatory for short options
too.
Input file location:
INPUTFILE
input PO file
-D, --directory=DIRECTORY
add DIRECTORY to list for input files search
If no input file is given or if it is -, standard input is read.
Output file location:
-o, --output-file=FILE
write output to specified file
The results are written to standard output if no output file is speci-
fied or if it is -.
Message selection:
--translated
keep translated, remove untranslated messages
--untranslated
keep untranslated, remove translated messages
--no-fuzzy
remove 'fuzzy' marked messages
--only-fuzzy
keep 'fuzzy' marked messages
--no-obsolete
remove obsolete #~ messages
--only-obsolete
keep obsolete #~ messages
Attribute manipulation:
--set-fuzzy
set all messages 'fuzzy'
--clear-fuzzy
set all messages non-'fuzzy'
--set-obsolete
set all messages obsolete
--clear-obsolete
set all messages non-obsolete
--fuzzy
synonym for --only-fuzzy --clear-fuzzy
--obsolete
synonym for --only-obsolete --clear-obsolete
Output details:
-e, --no-escape
do not use C escapes in output (default)
-E, --escape
use C escapes in output, no extended chars
--force-po
write PO file even if empty
-i, --indent
write the .po file using indented style
--no-location
do not write '#: filename:line' lines
-n, --add-location
generate '#: filename:line' lines (default)
--strict
write out strict Uniforum conforming .po file
-w, --width=NUMBER
set output page width
--no-wrap
do not break long message lines, longer than the output page
width, into several lines
-s, --sort-output
generate sorted output
-F, --sort-by-file
sort output by file location
Informative output:
-h, --help
display this help and exit
-V, --version
output version information and exit
AUTHOR
Written by Bruno Haible.
REPORTING BUGS
Report bugs to <bug-gnu-gettext@gnu.org>
COPYRIGHT
Copyright (C) 2001-2002 Free Software Foundation, Inc.
This is free software; see the source for copying conditions. There is
NO warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR
PURPOSE.
SEE ALSO
The full documentation for msgattrib is maintained as a Texinfo manual.
If the info and msgattrib programs are properly installed at your site,
the command
info msgattrib
should give you access to the complete manual.
msgcat
This tool merges several PO files. It can also sort the files and
change the output format.
NAME
msgcat - combines several message catalogs
SYNOPSIS
msgcat [OPTION] [INPUTFILE]...
DESCRIPTION
Concatenates and merges the specified PO files. Find messages which
are common to two or more of the specified PO files. By using the
--more-than option, greater commonality may be requested before mes-
sages are printed. Conversely, the --less-than option may be used to
specify less commonality before messages are printed (i.e.
--less-than=2 will only print the unique messages). Translations, com-
ments and extract comments will be cumulated, except that if
--use-first is specified, they will be taken from the first PO file to
define them. File positions from all PO files will be cumulated.
Mandatory arguments to long options are mandatory for short options
too.
Input file location:
INPUTFILE ...
input files
-f, --files-from=FILE
get list of input files from FILE
-D, --directory=DIRECTORY
add DIRECTORY to list for input files search
If input file is -, standard input is read.
Output file location:
-o, --output-file=FILE
write output to specified file
The results are written to standard output if no output file is speci-
fied or if it is -.
Message selection:
-<, --less-than=NUMBER
print messages with less than this many definitions, defaults to
infinite if not set
->, --more-than=NUMBER
print messages with more than this many definitions, defaults to
0 if not set
-u, --unique
shorthand for --less-than=2, requests that only unique messages
be printed
Output details:
-t, --to-code=NAME
encoding for output
--use-first
use first available translation for each message, don't merge
several translations
-e, --no-escape
do not use C escapes in output (default)
-E, --escape
use C escapes in output, no extended chars
--force-po
write PO file even if empty
-i, --indent
write the .po file using indented style
--no-location
do not write '#: filename:line' lines
-n, --add-location
generate '#: filename:line' lines (default)
--strict
write out strict Uniforum conforming .po file
-w, --width=NUMBER
set output page width
--no-wrap
do not break long message lines, longer than the output page
width, into several lines
-s, --sort-output
generate sorted output
-F, --sort-by-file
sort output by file location
Informative output:
-h, --help
display this help and exit
-V, --version
output version information and exit
AUTHOR
Written by Bruno Haible.
REPORTING BUGS
Report bugs to <bug-gnu-gettext@gnu.org>.
COPYRIGHT
Copyright (C) 2001-2002 Free Software Foundation, Inc.
This is free software; see the source for copying conditions. There is
NO warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR
PURPOSE.
SEE ALSO
The full documentation for msgcat is maintained as a Texinfo manual.
If the info and msgcat programs are properly installed at your site,
the command
info msgcat
should give you access to the complete manual.
msgcmp
This compares two PO files, and is often used to check that everything
has been translated.
NAME
msgcmp - compare message catalog and template
SYNOPSIS
msgcmp [OPTION] def.po ref.pot
DESCRIPTION
Compare two Uniforum style .po files to check that both contain the
same set of msgid strings. The def.po file is an existing PO file with
the translations. The ref.pot file is the last created PO file, or a
PO Template file (generally created by xgettext). This is useful for
checking that you have translated each and every message in your pro-
gram. Where an exact match cannot be found, fuzzy matching is used to
produce better diagnostics.
Mandatory arguments to long options are mandatory for short options
too.
Input file location:
def.po translations
ref.pot
references to the sources
-D, --directory=DIRECTORY
add DIRECTORY to list for input files search
Operation modifiers:
-m, --multi-domain
apply ref.pot to each of the domains in def.po
Informative output:
-h, --help
display this help and exit
-V, --version
output version information and exit
AUTHOR
Written by Peter Miller.
REPORTING BUGS
Report bugs to <bug-gnu-gettext@gnu.org>.
COPYRIGHT
Copyright (C) 1995-1998, 2000-2002 Free Software Foundation, Inc.
This is free software; see the source for copying conditions. There is
NO warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR
PURPOSE.
SEE ALSO
The full documentation for msgcmp is maintained as a Texinfo manual.
If the info and msgcmp programs are properly installed at your site,
the command
info msgcmp
should give you access to the complete manual.
msgcomm
This tool lists all messages that are common for two or more PO files.
NAME
msgcomm - match two message catalogs
SYNOPSIS
msgcomm [OPTION] [INPUTFILE]...
DESCRIPTION
Find messages which are common to two or more of the specified PO
files. By using the --more-than option, greater commonality may be
requested before messages are printed. Conversely, the --less-than
option may be used to specify less commonality before messages are
printed (i.e. --less-than=2 will only print the unique messages).
Translations, comments and extract comments will be preserved, but only
from the first PO file to define them. File positions from all PO
files will be cumulated.
Mandatory arguments to long options are mandatory for short options
too.
Input file location:
INPUTFILE ...
input files
-f, --files-from=FILE
get list of input files from FILE
-D, --directory=DIRECTORY
add DIRECTORY to list for input files search
If input file is -, standard input is read.
Output file location:
-o, --output-file=FILE
write output to specified file
The results are written to standard output if no output file is speci-
fied or if it is -.
Message selection:
-<, --less-than=NUMBER
print messages with less than this many definitions, defaults to
infinite if not set
->, --more-than=NUMBER
print messages with more than this many definitions, defaults to
1 if not set
-u, --unique
shorthand for --less-than=2, requests that only unique messages
be printed
Output details:
-e, --no-escape
do not use C escapes in output (default)
-E, --escape
use C escapes in output, no extended chars
--force-po
write PO file even if empty
-i, --indent
write the .po file using indented style
--no-location
do not write '#: filename:line' lines
-n, --add-location
generate '#: filename:line' lines (default)
--strict
write out strict Uniforum conforming .po file
-w, --width=NUMBER
set output page width
--no-wrap
do not break long message lines, longer than the output page
width, into several lines
-s, --sort-output
generate sorted output
-F, --sort-by-file
sort output by file location
--omit-header
don't write header with `msgid ""' entry
Informative output:
-h, --help
display this help and exit
-V, --version
output version information and exit
AUTHOR
Written by Peter Miller.
REPORTING BUGS
Report bugs to <bug-gnu-gettext@gnu.org>.
COPYRIGHT
Copyright (C) 1995-1998, 2000-2002 Free Software Foundation, Inc.
This is free software; see the source for copying conditions. There is
NO warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR
PURPOSE.
SEE ALSO
The full documentation for msgcomm is maintained as a Texinfo manual.
If the info and msgcomm programs are properly installed at your site,
the command
info msgcomm
should give you access to the complete manual.
msgen
Very simple program that creates an English translation file, where
all translations are a copy of the original, but marked as fuzzy. A
translator can then run through all messages and correct any typing errors
or improve any words, if needed.
NAME
msgen - create English message catalog
SYNOPSIS
msgen [OPTION] INPUTFILE
DESCRIPTION
Creates an English translation catalog. The input file is the last
created English PO file, or a PO Template file (generally created by
xgettext). Untranslated entries are assigned a translation that is
identical to the msgid, and are marked fuzzy.
Mandatory arguments to long options are mandatory for short options
too.
Input file location:
INPUTFILE
input PO or POT file
-D, --directory=DIRECTORY
add DIRECTORY to list for input files search
If input file is -, standard input is read.
Output file location:
-o, --output-file=FILE
write output to specified file
The results are written to standard output if no output file is speci-
fied or if it is -.
Output details:
-e, --no-escape
do not use C escapes in output (default)
-E, --escape
use C escapes in output, no extended chars
--force-po
write PO file even if empty
-i, --indent
indented output style
--no-location
suppress '#: filename:line' lines
--add-location
preserve '#: filename:line' lines (default)
--strict
strict Uniforum output style
-w, --width=NUMBER
set output page width
--no-wrap
do not break long message lines, longer than the output page
width, into several lines
-s, --sort-output
generate sorted output
-F, --sort-by-file
sort output by file location
Informative output:
-h, --help
display this help and exit
-V, --version
output version information and exit
AUTHOR
Written by Bruno Haible.
REPORTING BUGS
Report bugs to <bug-gnu-gettext@gnu.org>.
COPYRIGHT
Copyright (C) 2001-2002 Free Software Foundation, Inc.
This is free software; see the source for copying conditions. There is
NO warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR
PURPOSE.
SEE ALSO
The full documentation for msgen is maintained as a Texinfo manual. If
the info and msgen programs are properly installed at your site, the
command
info msgen
should give you access to the complete manual.
msgexec
Runs an external command line tool once for each message in a PO file.
NAME
msgexec - process translations of message catalog
SYNOPSIS
msgexec [OPTION] COMMAND [COMMAND-OPTION]
DESCRIPTION
Applies a command to all translations of a translation catalog. The
COMMAND can be any program that reads a translation from standard
input. It is invoked once for each translation. Its output becomes
msgexec's output. msgexec's return code is the maximum return code
across all invocations.
A special builtin command called '0' outputs the translation, followed
by a null byte. The output of "msgexec 0" is suitable as input for
"xargs -0".
Mandatory arguments to long options are mandatory for short options
too.
Input file location:
-i, --input=INPUTFILE
input PO file
-D, --directory=DIRECTORY
add DIRECTORY to list for input files search
If no input file is given or if it is -, standard input is read.
Informative output:
-h, --help
display this help and exit
-V, --version
output version information and exit
AUTHOR
Written by Bruno Haible.
REPORTING BUGS
Report bugs to <bug-gnu-gettext@gnu.org>.
COPYRIGHT
Copyright (C) 2001-2002 Free Software Foundation, Inc.
This is free software; see the source for copying conditions. There is
NO warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR
PURPOSE.
SEE ALSO
The full documentation for msgexec is maintained as a Texinfo manual.
If the info and msgexec programs are properly installed at your site,
the command
info msgexec
should give you access to the complete manual.
msgfilter
Runs an external command line tool once for each message, but uses it
to make changes to each translation. The external tool takes the translation
as input on stdin, and writes the new translation out on stdout.
NAME
msgfilter - edit translations of message catalog
SYNOPSIS
msgfilter [OPTION] FILTER [FILTER-OPTION]
DESCRIPTION
Applies a filter to all translations of a translation catalog.
Mandatory arguments to long options are mandatory for short options
too.
Input file location:
-i, --input=INPUTFILE
input PO file
-D, --directory=DIRECTORY
add DIRECTORY to list for input files search
If no input file is given or if it is -, standard input is read.
Output file location:
-o, --output-file=FILE
write output to specified file
The results are written to standard output if no output file is speci-
fied or if it is -.
The FILTER can be any program that reads a translation from standard
input and writes a modified translation to standard output.
Useful FILTER-OPTIONs when the FILTER is 'sed':
-e, --expression=SCRIPT
add SCRIPT to the commands to be executed
-f, --file=SCRIPTFILE
add the contents of SCRIPTFILE to the commands to be executed
-n, --quiet, --silent
suppress automatic printing of pattern space
Output details:
--no-escape
do not use C escapes in output (default)
-E, --escape
use C escapes in output, no extended chars
--force-po
write PO file even if empty
--indent
indented output style
--keep-header
keep header entry unmodified, don't filter it
--no-location
suppress '#: filename:line' lines
--add-location
preserve '#: filename:line' lines (default)
--strict
strict Uniforum output style
-w, --width=NUMBER
set output page width
--no-wrap
do not break long message lines, longer than the output page
width, into several lines
-s, --sort-output
generate sorted output
-F, --sort-by-file
sort output by file location
Informative output:
-h, --help
display this help and exit
-V, --version
output version information and exit
AUTHOR
Written by Bruno Haible.
REPORTING BUGS
Report bugs to <bug-gnu-gettext@gnu.org>.
COPYRIGHT
Copyright (C) 2001-2002 Free Software Foundation, Inc.
This is free software; see the source for copying conditions. There is
NO warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR
PURPOSE.
SEE ALSO
The full documentation for msgfilter is maintained as a Texinfo manual.
If the info and msgfilter programs are properly installed at your site,
the command
info msgfilter
should give you access to the complete manual.
msgfmt
Compiles a PO file to an MO file.
NAME
msgfmt - compile message catalog to binary format
SYNOPSIS
msgfmt [OPTION] filename.po ...
DESCRIPTION
Generate binary message catalog from textual translation description.
Mandatory arguments to long options are mandatory for short options
too.
Input file location:
filename.po ...
input files
-D, --directory=DIRECTORY
add DIRECTORY to list for input files search
If input file is -, standard input is read.
Operation mode:
-j, --java
Java mode: generate a Java ResourceBundle class
--java2
like --java, and assume Java2 (JDK 1.2 or higher)
--tcl Tcl mode: generate a tcl/msgcat .msg file
Output file location:
-o, --output-file=FILE
write output to specified file
--strict
enable strict Uniforum mode
If output file is -, output is written to standard output.
Output file location in Java mode:
-r, --resource=RESOURCE
resource name
-l, --locale=LOCALE
locale name, either language or language_COUNTRY
-d DIRECTORY
base directory of classes directory hierarchy
The class name is determined by appending the locale name to the
resource name, separated with an underscore. The -d option is manda-
tory. The class is written under the specified directory.
Output file location in Tcl mode:
-l, --locale=LOCALE
locale name, either language or language_COUNTRY
-d DIRECTORY
base directory of .msg message catalogs
The -l and -d options are mandatory. The .msg file is written in the
specified directory.
Input file interpretation:
-c, --check
perform all the checks implied by --check-format,
--check-header, --check-domain
--check-format
check language dependent format strings
--check-header
verify presence and contents of the header entry
--check-domain
check for conflicts between domain directives and the --out-
put-file option
-C, --check-compatibility
check that GNU msgfmt behaves like X/Open msgfmt
--check-accelerators[=CHAR]
check presence of keyboard accelerators for menu items
-f, --use-fuzzy
use fuzzy entries in output
Output details:
-a, --alignment=NUMBER
align strings to NUMBER bytes (default: 1)
--no-hash
binary file will not include the hash table
Informative output:
-h, --help
display this help and exit
-V, --version
output version information and exit
--statistics
print statistics about translations
-v, --verbose
increase verbosity level
AUTHOR
Written by Ulrich Drepper.
REPORTING BUGS
Report bugs to <bug-gnu-gettext@gnu.org>.
COPYRIGHT
Copyright (C) 1995-1998, 2000-2002 Free Software Foundation, Inc.
This is free software; see the source for copying conditions. There is
NO warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR
PURPOSE.
SEE ALSO
The full documentation for msgfmt is maintained as a Texinfo manual.
If the info and msgfmt programs are properly installed at your site,
the command
info msgfmt
should give you access to the complete manual.
msggrep
Extracts all messages of a translation catalog that match a given
pattern
or belong to some given source files.
NAME
msggrep - pattern matching on message catalog
SYNOPSIS
msggrep [OPTION] [INPUTFILE]
DESCRIPTION
Extracts all messages of a translation catalog that match a given pat-
tern or belong to some given source files.
Mandatory arguments to long options are mandatory for short options
too.
Input file location:
INPUTFILE
input PO file
-D, --directory=DIRECTORY
add DIRECTORY to list for input files search
If no input file is given or if it is -, standard input is read.
Output file location:
-o, --output-file=FILE
write output to specified file
The results are written to standard output if no output file is speci-
fied or if it is -.
Message selection:
[-N SOURCEFILE]... [-M DOMAINNAME]... [-K MSGID-PATTERN] [-T
MSGSTR-PATTERN] [-C COMMENT-PATTERN]
A message is selected if it comes from one of the specified source
files, or if it comes from one of the specified domains, or if -K is
given and its key (msgid or msgid_plural) matches MSGID-PATTERN, or if
-T is given and its translation (msgstr) matches MSGSTR-PATTERN, or if
-C is given and the translator's comment matches COMMENT-PATTERN.
When more than one selection criterion is specified, the set of
selected messages is the union of the selected messages of each crite-
rion.
MSGID-PATTERN or MSGSTR-PATTERN syntax:
[-E | -F] [-e PATTERN | -f FILE]...
PATTERNs are basic regular expressions by default, or extended regular
expressions if -E is given, or fixed strings if -F is given.
-N, --location=SOURCEFILE
select messages extracted from SOURCEFILE
-M, --domain=DOMAINNAME
select messages belonging to domain DOMAINNAME
-K, --msgid
start of patterns for the msgid
-T, --msgstr
start of patterns for the msgstr
-E, --extended-regexp
PATTERN is an extended regular expression
-F, --fixed-strings
PATTERN is a set of newline-separated strings
-e, --regexp=PATTERN
use PATTERN as a regular expression
-f, --file=FILE
obtain PATTERN from FILE
-i, --ignore-case
ignore case distinctions
Output details:
--no-escape
do not use C escapes in output (default)
--escape
use C escapes in output, no extended chars
--force-po
write PO file even if empty
--indent
indented output style
--no-location
suppress '#: filename:line' lines
--add-location
preserve '#: filename:line' lines (default)
--strict
strict Uniforum output style
-w, --width=NUMBER
set output page width
--no-wrap
do not break long message lines, longer than the output page
width, into several lines
--sort-output
generate sorted output
--sort-by-file
sort output by file location
Informative output:
-h, --help
display this help and exit
-V, --version
output version information and exit
AUTHOR
Written by Bruno Haible.
REPORTING BUGS
Report bugs to <bug-gnu-gettext@gnu.org>.
COPYRIGHT
Copyright (C) 2001-2002 Free Software Foundation, Inc.
This is free software; see the source for copying conditions. There is
NO warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR
PURPOSE.
SEE ALSO
The full documentation for msggrep is maintained as a Texinfo manual.
If the info and msggrep programs are properly installed at your site,
the command
info msggrep
should give you access to the complete manual.
msghack
This very special tool can swap msgid and msgstr in a PO file, remove
all translations in order to convert a translation file into a template
file, and can also append the contents of one PO file to another.
It is part of Red Hat Linux 9, but hasn't been seen compiled for Windows yet. It
also doesn't have a man-page on Red Hat Linux, but here you can see the help when running msghack --help:
Usage: /usr/bin/msghack [OPTION] file.po [ref.po]
This program can be used to alter .po files in ways no sane mind would think about.
-o result will be written to FILE
--invert invert a po file by switching msgid and msgstr
--master join any number of files in a master-formatted catalog
--empty empty the contents of the .po file, creating a .pot
--append append entries from ref.po that don't exist in file.po
Note: It is just a replacement of msghack for backward support.
msginit
NAME
msginit - initialize a message catalog
SYNOPSIS
msginit [OPTION]
DESCRIPTION
Creates a new PO file, initializing the meta information with values
from the user's environment.
Mandatory arguments to long options are mandatory for short options
too.
Input file location:
-i, --input=INPUTFILE
input POT file
If no input file is given, the current directory is searched for the
POT file. If it is -, standard input is read.
Output file location:
-o, --output-file=FILE
write output to specified PO file
If no output file is given, it depends on the --locale option or the
user's locale setting. If it is -, the results are written to standard
output.
Output details:
-l, --locale=LL_CC
set target locale
--no-translator
assume the PO file is automatically generated
-w, --width=NUMBER
set output page width
--no-wrap
do not break long message lines, longer than the output page
width, into several lines
Informative output:
-h, --help
display this help and exit
-V, --version
output version information and exit
AUTHOR
Written by Bruno Haible.
REPORTING BUGS
Report bugs to <bug-gnu-gettext@gnu.org>.
COPYRIGHT
Copyright (C) 2001-2002 Free Software Foundation, Inc.
This is free software; see the source for copying conditions. There is
NO warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR
PURPOSE.
SEE ALSO
The full documentation for msginit is maintained as a Texinfo manual.
If the info and msginit programs are properly installed at your site,
the command
info msginit
should give you access to the complete manual.
msgmerge
This tool can merge two PO files, where it takes the msgids from one
file and the translations from another file. It is normally used to update a
translation that was made for one version of a program, to be useful for the
next version of the program, by merging the translation with a template that
was extracted from the new source code.
NAME
msgmerge - merge message catalog and template
SYNOPSIS
msgmerge [OPTION] def.po ref.pot
DESCRIPTION
Merges two Uniforum style .po files together. The def.po file is an
existing PO file with translations which will be taken over to the
newly created file as long as they still match; comments will be pre-
served, but extracted comments and file positions will be discarded.
The ref.pot file is the last created PO file with up-to-date source
references but old translations, or a PO Template file (generally cre-
ated by xgettext); any translations or comments in the file will be
discarded, however dot comments and file positions will be preserved.
Where an exact match cannot be found, fuzzy matching is used to produce
better results.
Mandatory arguments to long options are mandatory for short options
too.
Input file location:
def.po translations referring to old sources
ref.pot
references to new sources
-D, --directory=DIRECTORY
add DIRECTORY to list for input files search
-C, --compendium=FILE
additional library of message translations, may be specified
more than once
Operation mode:
-U, --update
update def.po, do nothing if def.po already up to date
Output file location:
-o, --output-file=FILE
write output to specified file
The results are written to standard output if no output file is speci-
fied or if it is -.
Output file location in update mode: The result is written back to
def.po.
--backup=CONTROL
make a backup of def.po
--suffix=SUFFIX
override the usual backup suffix
The version control method may be selected via the --backup option or
through the VERSION_CONTROL environment variable. Here are the values:
none, off
never make backups (even if --backup is given)
numbered, t
make numbered backups
existing, nil
numbered if numbered backups exist, simple otherwise
simple, never
always make simple backups
The backup suffix is `~', unless set with --suffix or the SIM-
PLE_BACKUP_SUFFIX environment variable.
Operation modifiers:
-m, --multi-domain
apply ref.pot to each of the domains in def.po
Output details:
-e, --no-escape
do not use C escapes in output (default)
-E, --escape
use C escapes in output, no extended chars
--force-po
write PO file even if empty
-i, --indent
indented output style
--no-location
suppress '#: filename:line' lines
--add-location
preserve '#: filename:line' lines (default)
--strict
strict Uniforum output style
-w, --width=NUMBER
set output page width
--no-wrap
do not break long message lines, longer than the output page
width, into several lines
-s, --sort-output
generate sorted output
-F, --sort-by-file
sort output by file location
Informative output:
-h, --help
display this help and exit
-V, --version
output version information and exit
-v, --verbose
increase verbosity level
-q, --quiet, --silent
suppress progress indicators
AUTHOR
Written by Peter Miller.
REPORTING BUGS
Report bugs to <bug-gnu-gettext@gnu.org>.
COPYRIGHT
Copyright (C) 1995-1998, 2000-2002 Free Software Foundation, Inc.
This is free software; see the source for copying conditions. There is
NO warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR
PURPOSE.
SEE ALSO
The full documentation for msgmerge is maintained as a Texinfo manual.
If the info and msgmerge programs are properly installed at your site,
the command
info msgmerge
should give you access to the complete manual.
msgunfmt
This tool decompiles an MO file to a PO file.
NAME
msgunfmt - uncompile message catalog from binary format
SYNOPSIS
msgunfmt [OPTION] [FILE]...
DESCRIPTION
Convert binary message catalog to Uniforum style .po file.
Mandatory arguments to long options are mandatory for short options
too.
Operation mode:
-j, --java
Java mode: input is a Java ResourceBundle class
--tcl Tcl mode: input is a tcl/msgcat .msg file
Input file location:
FILE ...
input .mo files
If no input file is given or if it is -, standard input is read.
Input file location in Java mode:
-r, --resource=RESOURCE
resource name
-l, --locale=LOCALE
locale name, either language or language_COUNTRY
The class name is determined by appending the locale name to the
resource name, separated with an underscore. The class is located
using the CLASSPATH.
Input file location in Tcl mode:
-l, --locale=LOCALE
locale name, either language or language_COUNTRY
-d DIRECTORY
base directory of .msg message catalogs
The -l and -d options are mandatory. The .msg file is located in the
specified directory.
Output file location:
-o, --output-file=FILE
write output to specified file
The results are written to standard output if no output file is speci-
fied or if it is -.
Output details:
-e, --no-escape
do not use C escapes in output (default)
-E, --escape
use C escapes in output, no extended chars
--force-po
write PO file even if empty
-i, --indent
write indented output style
--strict
write strict uniforum style
-w, --width=NUMBER
set output page width
--no-wrap
do not break long message lines, longer than the output page
width, into several lines
-s, --sort-output
generate sorted output
Informative output:
-h, --help
display this help and exit
-V, --version
output version information and exit
-v, --verbose
increase verbosity level
AUTHOR
Written by Ulrich Drepper.
REPORTING BUGS
Report bugs to <bug-gnu-gettext@gnu.org>.
COPYRIGHT
Copyright (C) 1995-1998, 2000-2002 Free Software Foundation, Inc.
This is free software; see the source for copying conditions. There is
NO warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR
PURPOSE.
SEE ALSO
The full documentation for msgunfmt is maintained as a Texinfo manual.
If the info and msgunfmt programs are properly installed at your site,
the command
info msgunfmt
should give you access to the complete manual.
msguniq
In case you did something to your PO files, that makes them contain
duplicate messages, you can use msguniq to unify those duplicates.
NAME
msguniq - unify duplicate translations in message catalog
SYNOPSIS
msguniq [OPTION] [INPUTFILE]
DESCRIPTION
Unifies duplicate translations in a translation catalog. Finds dupli-
cate translations of the same message ID. Such duplicates are invalid
input for other programs like msgfmt, msgmerge or msgcat. By default,
duplicates are merged together. When using the --repeated option, only
duplicates are output, and all other messages are discarded. Comments
and extracted comments will be cumulated, except that if --use-first is
specified, they will be taken from the first translation. File posi-
tions will be cumulated. When using the --unique option, duplicates
are discarded.
Mandatory arguments to long options are mandatory for short options
too.
Input file location:
INPUTFILE
input PO file
-D, --directory=DIRECTORY
add DIRECTORY to list for input files search
If no input file is given or if it is -, standard input is read.
Output file location:
-o, --output-file=FILE
write output to specified file
The results are written to standard output if no output file is speci-
fied or if it is -.
Message selection:
-d, --repeated
print only duplicates
-u, --unique
print only unique messages, discard duplicates
Output details:
-t, --to-code=NAME
encoding for output
--use-first
use first available translation for each message, don't merge
several translations
-e, --no-escape
do not use C escapes in output (default)
-E, --escape
use C escapes in output, no extended chars
--force-po
write PO file even if empty
-i, --indent
write the .po file using indented style
--no-location
do not write '#: filename:line' lines
-n, --add-location
generate '#: filename:line' lines (default)
--strict
write out strict Uniforum conforming .po file
-w, --width=NUMBER
set output page width
--no-wrap
do not break long message lines, longer than the output page
width, into several lines
-s, --sort-output
generate sorted output
-F, --sort-by-file
sort output by file location
Informative output:
-h, --help
display this help and exit
-V, --version
output version information and exit
AUTHOR
Written by Bruno Haible.
REPORTING BUGS
Report bugs to <bug-gnu-gettext@gnu.org>.
COPYRIGHT
Copyright (C) 2001-2002 Free Software Foundation, Inc.
This is free software; see the source for copying conditions. There is
NO warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR
PURPOSE.
SEE ALSO
The full documentation for msguniq is maintained as a Texinfo manual.
If the info and msguniq programs are properly installed at your site,
the command
info msguniq
should give you access to the complete manual.
xgettext
This tool is the GNU equivalent to the dxgettext command line tool. It does not work with Delphi,
but it works with C and C++.
NAME
xgettext - extract gettext strings from source
SYNOPSIS
xgettext [OPTION] [INPUTFILE]...
DESCRIPTION
Extract translatable strings from given input files.
Mandatory arguments to long options are mandatory for short options
too. Similarly for optional arguments.
Input file location:
INPUTFILE ...
input files
-f, --files-from=FILE
get list of input files from FILE
-D, --directory=DIRECTORY
add DIRECTORY to list for input files search
If input file is -, standard input is read.
Output file location:
-d, --default-domain=NAME
use NAME.po for output (instead of messages.po)
-o, --output=FILE
write output to specified file
-p, --output-dir=DIR
output files will be placed in directory DIR
If output file is -, output is written to standard output.
Choice of input file language:
-L, --language=NAME
recognise the specified language (C, C++, ObjectiveC, PO,
Python, Lisp, EmacsLisp, librep, Java, awk, YCP, Tcl, RST,
Glade)
-C, --c++
shorthand for --language=C++
By default the language is guessed depending on the input file name
extension.
Operation mode:
-j, --join-existing
join messages with existing file
-x, --exclude-file=FILE.po
entries from FILE.po are not extracted
-c, --add-comments[=TAG]
place comment block with TAG (or those preceding keyword lines)
in output file
Language=C/C++ specific options:
-a, --extract-all
extract all strings
-k, --keyword[=WORD]
additional keyword to be looked for (without WORD means not to
use default keywords)
-T, --trigraphs
understand ANSI C trigraphs for input
--debug
more detailed formatstring recognition result
Output details:
-e, --no-escape
do not use C escapes in output (default)
-E, --escape
use C escapes in output, no extended chars
--force-po
write PO file even if empty
-i, --indent
write the .po file using indented style
--no-location
do not write '#: filename:line' lines
-n, --add-location
generate '#: filename:line' lines (default)
--strict
write out strict Uniforum conforming .po file
-w, --width=NUMBER
set output page width
--no-wrap
do not break long message lines, longer than the output page
width, into several lines
-s, --sort-output
generate sorted output
-F, --sort-by-file
sort output by file location
--omit-header
don't write header with `msgid ""' entry
--copyright-holder=STRING
set copyright holder in output
--foreign-user
omit FSF copyright in output for foreign user
-m, --msgstr-prefix[=STRING]
use STRING or "" as prefix for msgstr entries
-M, --msgstr-suffix[=STRING]
use STRING or "" as suffix for msgstr entries
Informative output:
-h, --help
display this help and exit
-V, --version
output version information and exit
AUTHOR
Written by Ulrich Drepper.
REPORTING BUGS
Report bugs to <bug-gnu-gettext@gnu.org>.
COPYRIGHT
Copyright (C) 1995-1998, 2000-2002 Free Software Foundation, Inc.
This is free software; see the source for copying conditions. There is
NO warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR
PURPOSE.
SEE ALSO
The full documentation for xgettext is maintained as a Texinfo manual.
If the info and xgettext programs are properly installed at your site,
the command
info xgettext
should give you access to the complete manual.
GUI tools reference
PO files
In Windows Explorer, you can click with your right mouse button on po files
and choose "Compile to mo file" and "Merge template". If you have installed
a po file editor, like poEdit, you
can also just open the po file.
MO files
In Windows Explorer, you can click with your right mouse button on po files
and choose "Decompile to po file".
Executables (DLL, EXE files)
In Windows Explorer, you can click with your right mouse button on po files
and choose "Extract Strings" and "Embed translations". The first will extract
all strings from the resource part of the file into a po file, and the second
will append all translation files (mo files) from the locale subdirectory to
the executable file.
File folders
In Windows Explorer, you can click with your right mouse button on a file folder
and choose "Extract translations to template". This will enable you to scan a lot of
source code files for strings to translate.
Standards
ISO 639 language codes
aa Afar
ab Abkhazian
ae Avestan
af Afrikaans
ak Akan
am Amharic
an Aragonese
ar Arabic
as Assamese
av Avaric
ay Aymara
az Azerbaijani
ba Bashkir
be Belarusian
bg Bulgarian
bh Bihari
bi Bislama
bm Bambara
bn Bengali
bo Tibetan
br Breton
bs Bosnian
ca Catalan
ce Chechen
ch Chamorro
co Corsican
cr Cree
cs Czech
cv Chuvash
cy Welsh
da Danish
de German
dv Divehi
dz Dzongkha
ee Ewe
el Greek
en English
en_US American English
en_GB British English
en_AU Australian English
eo Esperanto
es Spanish
et Estonian
eu Basque
fa Persian
ff Fulah
fi Finnish
fj Fijian
fo Faroese
fr French
fr_BE Walloon
fy Frisian
ga Irish
gd Gaelic
gl Gallegan
gn Guarani
gu Gujarati
gv Manx
ha Hausa
he Hebrew
hi Hindi
ho Hiri Motu
hr Croatian
ht Haitian
hu Hungarian
hy Armenian
hz Herero
ia Interlingua
id Indonesian
ie Interlingue
ig Igbo
ii Sichuan Yi
ik Inupiaq
io Ido
is Icelandic
it Italian
iu Inuktitut
ja Japanese
jv Javanese
ka Georgian
kg Kongo
ki Kikuyu
kj Kuanyama
kk Kazakh
kl Greenlandic
km Khmer
kn Kannada
ko Korean
kr Kanuri
ks Kashmiri
ku Kurdish
kw Cornish
kv Komi
ky Kirghiz
la Latin
lb Luxembourgish
lg Ganda
li Limburgan
ln Lingala
lo Lao
lt Lithuanian
lu Luba-Katanga
lv Latvian
mg Malagasy
mh Marshallese
mi Maori
mk Macedonian
ml Malayalam
mn Mongolian
mo Moldavian
mr Marathi
ms Malay
mt Maltese
my Burmese
na Nauru
nb Norwegian Bokmaal
nd Ndebele, North
ne Nepali
ng Ndonga
nl Dutch
nl_BE Flemish
nn Norwegian Nynorsk
no Norwegian
nr Ndebele, South
nv Navajo
ny Chichewa
oc Occitan
oj Ojibwa
om Oromo
or Oriya
os Ossetian
pa Panjabi
pi Pali
pl Polish
ps Pushto
pt Portuguese
qu Quechua
rm Raeto-Romance
rn Rundi
ro Romanian
ru Russian
rw Kinyarwanda
sa Sanskrit
sc Sardinian
sd Sindhi
se Northern Sami
sg Sango
si Sinhalese
sk Slovak
sl Slovenian
sm Samoan
sn Shona
so Somali
sq Albanian
sr Serbian
ss Swati
st Sotho, Southern
su Sundanese
sv Swedish
sw Swahili
ta Tamil
te Telugu
tg Tajik
th Thai
ti Tigrinya
tk Turkmen
tl Tagalog
tn Tswana
to Tonga
tr Turkish
ts Tsonga
tt Tatar
tw Twi
ty Tahitian
ug Uighur
uk Ukrainian
ur Urdu
uz Uzbek
ve Venda
vi Vietnamese
vo Volapuk
wa Walloon
wo Wolof
xh Xhosa
yi Yiddish
yo Yoruba
za Zhuang
zh Chinese
zu Zulu
ISO 3166 country codes
Country
Code
AFGHANISTAN
AF
ALBANIA
AL
ALGERIA
DZ
AMERICAN SAMOA
AS
ANDORRA
AD
ANGOLA
AO
ANGUILLA
AI
ANTARCTICA
AQ
ANTIGUA AND BARBUDA
AG
ARGENTINA
AR
ARMENIA
AM
ARUBA
AW
AUSTRALIA
AU
AUSTRIA
AT
AZERBAIJAN
AZ
BAHAMAS
BS
BAHRAIN
BH
BANGLADESH
BD
BARBADOS
BB
BELARUS
BY
BELGIUM
BE
BELIZE
BZ
BENIN
BJ
BERMUDA
BM
BHUTAN
BT
BOLIVIA
BO
BOSNIA AND HERZEGOVINA
BA
BOTSWANA
BW
BOUVET ISLAND
BV
BRAZIL
BR
BRITISH INDIAN OCEAN TERRITORY
IO
BRUNEI DARUSSALAM
BN
BULGARIA
BG
BURKINA FASO
BF
BURUNDI
BI
CAMBODIA
KH
CAMEROON
CM
CANADA
CA
CAPE VERDE
CV
CAYMAN ISLANDS
KY
CENTRAL AFRICAN REPUBLIC
CF
CHAD
TD
CHILE
CL
CHINA
CN
CHRISTMAS ISLAND
CX
COCOS (KEELING) ISLANDS
CC
COLOMBIA
CO
COMOROS
KM
CONGO
CG
CONGO, THE DEMOCRATIC REPUBLIC OF THE
CD
COOK ISLANDS
CK
COSTA RICA
CR
COTE D'IVOIRE
CI
CROATIA
HR
CUBA
CU
CYPRUS
CY
CZECH REPUBLIC
CZ
DENMARK
DK
DJIBOUTI
DJ
DOMINICA
DM
DOMINICAN REPUBLIC
DO
ECUADOR
EC
EGYPT
EG
EL SALVADOR
SV
EQUATORIAL GUINEA
GQ
ERITREA
ER
ESTONIA
EE
ETHIOPIA
ET
FALKLAND ISLANDS (MALVINAS)
FK
FAROE ISLANDS
FO
FIJI
FJ
FINLAND
FI
FRANCE
FR
FRENCH GUIANA
GF
FRENCH POLYNESIA
PF
FRENCH SOUTHERN TERRITORIES
TF
GABON
GA
GAMBIA
GM
GEORGIA
GE
GERMANY
DE
GHANA
GH
GIBRALTAR
GI
GREECE
GR
GREENLAND
GL
GRENADA
GD
GUADELOUPE
GP
GUAM
GU
GUATEMALA
GT
GUINEA
GN
GUINEA-BISSAU
GW
GUYANA
GY
HAITI
HT
HEARD ISLAND AND MCDONALD ISLANDS
HM
HOLY SEE (VATICAN CITY STATE)
VA
HONDURAS
HN
HONG KONG
HK
HUNGARY
HU
ICELAND
IS
INDIA
IN
INDONESIA
ID
IRAN, ISLAMIC REPUBLIC OF
IR
IRAQ
IQ
IRELAND
IE
ISRAEL
IL
ITALY
IT
JAMAICA
JM
JAPAN
JP
JORDAN
JO
KAZAKHSTAN
KZ
KENYA
KE
KIRIBATI
KI
KOREA, DEMOCRATIC PEOPLE'S REPUBLIC OF
KP
KOREA, REPUBLIC OF
KR
KUWAIT
KW
KYRGYZSTAN
KG
LAO PEOPLE'S DEMOCRATIC REPUBLIC
LA
LATVIA
LV
LEBANON
LB
LESOTHO
LS
LIBERIA
LR
LIBYAN ARAB JAMAHIRIYA
LY
LIECHTENSTEIN
LI
LITHUANIA
LT
LUXEMBOURG
LU
MACAO
MO
MACEDONIA, THE FORMER YUGOSLAV REPUBLIC OF
MK
MADAGASCAR
MG
MALAWI
MW
MALAYSIA
MY
MALDIVES
MV
MALI
ML
MALTA
MT
MARSHALL ISLANDS
MH
MARTINIQUE
MQ
MAURITANIA
MR
MAURITIUS
MU
MAYOTTE
YT
MEXICO
MX
MICRONESIA, FEDERATED STATES OF
FM
MOLDOVA, REPUBLIC OF
MD
MONACO
MC
MONGOLIA
MN
MONTSERRAT
MS
MOROCCO
MA
MOZAMBIQUE
MZ
MYANMAR
MM
NAMIBIA
NA
NAURU
NR
NEPAL
NP
NETHERLANDS
NL
NETHERLANDS ANTILLES
AN
NEW CALEDONIA
NC
NEW ZEALAND
NZ
NICARAGUA
NI
NIGER
NE
NIGERIA
NG
NIUE
NU
NORFOLK ISLAND
NF
NORTHERN MARIANA ISLANDS
MP
NORWAY
NO
OMAN
OM
PAKISTAN
PK
PALAU
PW
PALESTINIAN TERRITORY, OCCUPIED
PS
PANAMA
PA
PAPUA NEW GUINEA
PG
PARAGUAY
PY
PERU
PE
PHILIPPINES
PH
PITCAIRN
PN
POLAND
PL
PORTUGAL
PT
PUERTO RICO
PR
QATAR
QA
REUNION
RE
ROMANIA
RO
RUSSIAN FEDERATION
RU
RWANDA
RW
SAINT HELENA
SH
SAINT KITTS AND NEVIS
KN
SAINT LUCIA
LC
SAINT PIERRE AND MIQUELON
PM
SAINT VINCENT AND THE GRENADINES
VC
SAMOA
WS
SAN MARINO
SM
SAO TOME AND PRINCIPE
ST
SAUDI ARABIA
SA
SENEGAL
SN
SERBIA AND MONTENEGRO
CS
SEYCHELLES
SC
SIERRA LEONE
SL
SINGAPORE
SG
SLOVAKIA
SK
SLOVENIA
SI
SOLOMON ISLANDS
SB
SOMALIA
SO
SOUTH AFRICA
ZA
SOUTH GEORGIA AND THE SOUTH SANDWICH ISLANDS
GS
SPAIN
ES
SRI LANKA
LK
SUDAN
SD
SURINAME
SR
SVALBARD AND JAN MAYEN
SJ
SWAZILAND
SZ
SWEDEN
SE
SWITZERLAND
CH
SYRIAN ARAB REPUBLIC
SY
TAIWAN, PROVINCE OF CHINA
TW
TAJIKISTAN
TJ
TANZANIA, UNITED REPUBLIC OF
TZ
THAILAND
TH
TIMOR-LESTE
TL
TOGO
TG
TOKELAU
TK
TONGA
TO
TRINIDAD AND TOBAGO
TT
TUNISIA
TN
TURKEY
TR
TURKMENISTAN
TM
TURKS AND CAICOS ISLANDS
TC
TUVALU
TV
UGANDA
UG
UKRAINE
UA
UNITED ARAB EMIRATES
AE
UNITED KINGDOM
GB
UNITED STATES
US
UNITED STATES MINOR OUTLYING ISLANDS
UM
URUGUAY
UY
UZBEKISTAN
UZ
VANUATU
VU
VENEZUELA
VE
VIET NAM
VN
VIRGIN ISLANDS, BRITISH
VG
VIRGIN ISLANDS, U.S.
VI
WALLIS AND FUTUNA
WF
WESTERN SAHARA
EH
YEMEN
YE
ZAMBIA
ZM
ZIMBABWE
ZW
File formats
The format of GNU PO files
This section was last updated august 2nd, 2003 by copying
the appropriate section from the GNU gettext manual.
A PO file is made up of many entries, each entry holding the relation
between an original untranslated string and its corresponding
translation. All entries in a given PO file usually pertain
to a single project, and all translations are expressed in a single
target language. One PO file entry has the following schematic
structure:
white-space
# translator-comments
#. automatic-comments
#: reference...
#, flag...
msgid "untranslated-string"
msgstr "translated-string"
The general structure of a PO file should be well understood by
the translator. When using PO mode, very little has to be known
about the format details, as PO mode takes care of them for her.
Entries begin with some optional white space. Usually, when generated
through GNU gettext tools, there is exactly one blank line
between entries. Then comments follow, on lines all starting with the
character #. There are two kinds of comments: those which have
some white space immediately following the #, which comments are
created and maintained exclusively by the translator, and those which
have some non-white character just after the #, which comments
are created and maintained automatically by GNU gettext tools.
All comments, of either kind, are optional.
After white space and comments, entries show two strings, namely
first the untranslated string as it appears in the original program
sources, and then, the translation of this string. The original
string is introduced by the keyword msgid, and the translation,
by msgstr. The two strings, untranslated and translated,
are quoted in various ways in the PO file, using "
delimiters and \ escapes, but the translator does not really
have to pay attention to the precise quoting format, as PO mode fully
takes care of quoting for her.
The msgid strings, as well as automatic comments, are produced
and managed by other GNU gettext tools, and PO mode does not
provide means for the translator to alter these. The most she can
do is merely deleting them, and only by deleting the whole entry.
On the other hand, the msgstr string, as well as translator
comments, are really meant for the translator, and PO mode gives her
the full control she needs.
The comment lines beginning with #, are special because they are
not completely ignored by the programs as comments generally are. The
comma separated list of flags is used by the msgfmt
program to give the user some better diagnostic messages. Currently
there are two forms of flags defined:
fuzzy
This flag can be generated by the msgmerge program or it can be
inserted by the translator herself. It shows that the msgstr
string might not be a correct translation (anymore). Only the translator
can judge if the translation requires further modification, or is
acceptable as is. Once satisfied with the translation, she then removes
this fuzzy attribute. The msgmerge program inserts this
when it combined the msgid and msgstr entries after fuzzy
search only.
c-format
These flags should not be added by a human. Instead only the
xgettext program adds them. In an automated PO file processing
system as proposed here the user changes would be thrown away again as
soon as the xgettext program generates a new template file.
In case the c-format flag is given for a string the msgfmt
does some more tests to check to validity of the translation.
A different kind of entries is used for translations which involve
plural forms.
white-space
# translator-comments
#. automatic-comments
#: reference...
#, flag...
msgid untranslated-string-singular
msgid_plural untranslated-string-plural
msgstr[0] "translated-string-case-0"
...
msgstr[N] "translated-string-case-n"
It happens that some lines, usually whitespace or comments, follow the
very last entry of a PO file. Such lines are not part of any entry,
and PO mode is unable to take action on those lines. By using the
PO mode function M-x po-normalize, the translator may get
rid of those spurious lines.
The remainder of this section may be safely skipped by those using
PO mode, yet it may be interesting for everybody to have a better
idea of the precise format of a PO file. On the other hand, those
not having Emacs handy should carefully continue reading on.
Each of "untranslated-string and "translated-string" respects
the C syntax for a character string, including the surrounding quotes
and embedded backslashed escape sequences. When the time comes
to write multi-line strings, one should not use escaped newlines.
Instead, a closing quote should follow the last character on the
line to be continued, and an opening quote should resume the string
at the beginning of the following PO file line. For example:
msgid ""
"Here is an example of how one might continue a very long string\n"
"for the common case the string represents multi-line output.\n"
In this example, the empty string is used on the first line, to
allow better alignment of the H from the word Here
over the f from the word for. In this example, the
msgid keyword is followed by three strings, which are meant
to be concatenated. Concatenating the empty string does not change
the resulting overall string, but it is a way for us to comply with
the necessity of msgid to be followed by a string on the same
line, while keeping the multi-line presentation left-justified, as
we find this to be a cleaner disposition. The empty string could have
been omitted, but only if the string starting with Here was
promoted on the first line, right after msgid. It was not really necessary
either to switch between the two last quoted strings immediately after
the newline \n, the switch could have occurred after any
other character, we just did it this way because it is neater.
One should carefully distinguish between end of lines marked as
\n inside quotes, which are part of the represented
string, and end of lines in the PO file itself, outside string quotes,
which have no incidence on the represented string.
Outside strings, white lines and comments may be used freely.
Comments start at the beginning of a line with # and extend
until the end of the PO file line. Comments written by translators
should have the initial # immediately followed by some white
space. If the # is not immediately followed by white space,
this comment is most likely generated and managed by specialized GNU
tools, and might disappear or be replaced unexpectedly when the PO
file is given to msgmerge.
The format of GNU MO files
This section was last updated august 2nd, 2003 by copying
the appropriate section from the GNU gettext manual.
The format of the generated MO files is best described by a picture,
which appears below.
The first two words serve the identification of the file. The magic
number will always signal GNU MO files. The number is stored in the
byte order of the generating machine, so the magic number really is
two numbers: 0x950412de and 0xde120495. The second
word describes the current revision of the file format. For now the
revision is 0. This might change in future versions, and ensures
that the readers of MO files can distinguish new formats from old
ones, so that both can be handled correctly. The version is kept
separate from the magic number, instead of using different magic
numbers for different formats, mainly because /etc/magic is
not updated often. It might be better to have magic separated from
internal format version identification.
Follow a number of pointers to later tables in the file, allowing
for the extension of the prefix part of MO files without having to
recompile programs reading them. This might become useful for later
inserting a few flag bits, indication about the charset used, new
tables, or other things.
Then, at offset O and offset T in the picture, two tables
of string descriptors can be found. In both tables, each string
descriptor uses two 32 bits integers, one for the string length,
another for the offset of the string in the MO file, counting in bytes
from the start of the file. The first table contains descriptors
for the original strings, and is sorted so the original strings
are in increasing lexicographical order. The second table contains
descriptors for the translated strings, and is parallel to the first
table: to find the corresponding translation one has to access the
array slot in the second array with the same index.
Having the original strings sorted enables the use of simple binary
search, for when the MO file does not contain an hashing table, or
for when it is not practical to use the hashing table provided in
the MO file. This also has another advantage, as the empty string
in a PO file GNU gettext is usually translated into
some system information attached to that particular MO file, and the
empty string necessarily becomes the first in both the original and
translated tables, making the system information very easy to find.
The size S of the hash table can be zero. In this case, the
hash table itself is not contained in the MO file. Some people might
prefer this because a precomputed hashing table takes disk space, and
does not win that much speed. The hash table contains indices
to the sorted array of strings in the MO file. Conflict resolution is
done by double hashing. The precise hashing algorithm used is fairly
dependent on GNU gettext code, and is not documented here.
As for the strings themselves, they follow the hash file, and each
is terminated with a NUL, and this NUL is not counted in
the length which appears in the string descriptor. The msgfmt
program has an option selecting the alignment for MO file strings.
With this option, each string is separately aligned so it starts at
an offset which is a multiple of the alignment value. On some RISC
machines, a correct alignment will speed things up.
Plural forms are stored by letting the plural of the original string
follow the singular of the original string, separated through a
NUL byte. The length which appears in the string descriptor
includes both. However, only the singular of the original string
takes part in the hash table lookup. The plural variants of the
translation are all stored consecutively, separated through a
NUL byte. Here also, the length in the string descriptor
includes all of them.
Nothing prevents a MO file from having embedded NULs in strings.
However, the program interface currently used already presumes
that strings are NUL terminated, so embedded NULs are
somewhat useless. But the MO file format is general enough so other
interfaces would be later possible, if for example, we ever want to
implement wide characters right in MO files, where NUL bytes may
accidently appear. (No, we don't want to have wide characters in MO
files. They would make the file unnecessarily large, and the wchar_t
type being platform dependent, MO files would be
platform dependent as well.)
This particular issue has been strongly debated in the GNU
gettext development forum, and it is expectable that MO file
format will evolve or change over time. It is even possible that many
formats may later be supported concurrently. But surely, we have to
start somewhere, and the MO file format described here is a good start.
Nothing is cast in concrete, and the format may later evolve fairly
easily, so we should feel comfortable with the current approach.
byte
+------------------------------------------+
0 | magic number = 0x950412de |
| |
4 | file format revision = 0 |
| |
8 | number of strings | == N
| |
12 | offset of table with original strings | == O
| |
16 | offset of table with translation strings | == T
| |
20 | size of hashing table | == S
| |
24 | offset of hashing table | == H
| |
. .
. (possibly more entries later) .
. .
| |
O | length & offset 0th string ----------------.
O + 8 | length & offset 1st string ------------------.
... ... | |
O + ((N-1)*8)| length & offset (N-1)th string | | |
| | | |
T | length & offset 0th translation ---------------.
T + 8 | length & offset 1st translation -----------------.
... ... | | | |
T + ((N-1)*8)| length & offset (N-1)th translation | | | | |
| | | | | |
H | start hash table | | | | |
... ... | | | |
H + S * 4 | end hash table | | | | |
| | | | | |
| NUL terminated 0th string <----------------' | | |
| | | | |
| NUL terminated 1st string <------------------' | |
| | | |
... ... | |
| | | |
| NUL terminated 0th translation <---------------' |
| | |
| NUL terminated 1st translation <-----------------'
| |
... ...
| |
+------------------------------------------+
How to handle specific classes
This appendix contains documentation on how to handle various classes that do not translate easily
using TranslateComponent() or TranslateProperties(). Most classes are
handled easily by just putting an ignore on some of their properties, while other classes need more
advanced handling.
Please note, that the TComponent.Name property is always ignored by default, and
doesn't need to be specified.
VCL, important ones
TP_GlobalIgnoreClassProperty(TAction,'Category');
TP_GlobalIgnoreClassProperty(TControl,'HelpKeyword');
TP_GlobalIgnoreClassProperty(TNotebook,'Pages');
VCL, not so important
These are normally not needed.
TP_GlobalIgnoreClassProperty(TControl,'ImeName');
TP_GlobalIgnoreClass(TFont);
Database (DB unit)
Field names and table names often tend to have names that are also used for other purposes
elsewhere in the program. Therefore, it is very wise to add this somewhere in your program if
you are using databases.
TP_GlobalIgnoreClassProperty(TField,'DefaultExpression');
TP_GlobalIgnoreClassProperty(TField,'FieldName');
TP_GlobalIgnoreClassProperty(TField,'KeyFields');
TP_GlobalIgnoreClassProperty(TField,'DisplayName');
TP_GlobalIgnoreClassProperty(TField,'LookupKeyFields');
TP_GlobalIgnoreClassProperty(TField,'LookupResultField');
TP_GlobalIgnoreClassProperty(TField,'Origin');
TP_GlobalIgnoreClass(TParam);
TP_GlobalIgnoreClassProperty(TFieldDef,'Name');
MIDAS/Datasnap
TP_GlobalIgnoreClassProperty(TClientDataset,'CommandText');
TP_GlobalIgnoreClassProperty(TClientDataset,'Filename');
TP_GlobalIgnoreClassProperty(TClientDataset,'Filter');
TP_GlobalIgnoreClassProperty(TClientDataset,'IndexFieldnames');
TP_GlobalIgnoreClassProperty(TClientDataset,'IndexName');
TP_GlobalIgnoreClassProperty(TClientDataset,'MasterFields');
TP_GlobalIgnoreClassProperty(TClientDataset,'Params');
TP_GlobalIgnoreClassProperty(TClientDataset,'ProviderName');
Database controls
TP_GlobalIgnoreClassProperty(TDBComboBox,'DataField');
TP_GlobalIgnoreClassProperty(TDBCheckBox,'DataField');
TP_GlobalIgnoreClassProperty(TDBEdit,'DataField');
TP_GlobalIgnoreClassProperty(TDBImage,'DataField');
TP_GlobalIgnoreClassProperty(TDBListBox,'DataField');
TP_GlobalIgnoreClassProperty(TDBLookupControl,'DataField');
TP_GlobalIgnoreClassProperty(TDBLookupControl,'KeyField');
TP_GlobalIgnoreClassProperty(TDBLookupControl,'ListField');
TP_GlobalIgnoreClassProperty(TDBMemo,'DataField');
TP_GlobalIgnoreClassProperty(TDBRadioGroup,'DataField');
TP_GlobalIgnoreClassProperty(TDBRichEdit,'DataField');
TP_GlobalIgnoreClassProperty(TDBText,'DataField');
Interbase Express (IBX)
TP_GlobalIgnoreClass(TIBDatabase);
TP_GlobalIgnoreClass(TIBDatabase);
TP_GlobalIgnoreClass(TIBTransaction);
TP_GlobalIgnoreClassProperty(TIBSQL,'UniqueRelationName');
Borland Database Engine (BDE)
TP_GlobalIgnoreClass(TSession);
TP_GlobalIgnoreClass(TDatabase);
ADO components
TP_GlobalIgnoreClass (TADOConnection);
TP_GlobalIgnoreClassProperty(TADOQuery,'CommandText');
TP_GlobalIgnoreClassProperty(TADOQuery,'ConnectionString');
TP_GlobalIgnoreClassProperty(TADOQuery,'DatasetField');
TP_GlobalIgnoreClassProperty(TADOQuery,'Filter');
TP_GlobalIgnoreClassProperty(TADOQuery,'IndexFieldNames');
TP_GlobalIgnoreClassProperty(TADOQuery,'IndexName');
TP_GlobalIgnoreClassProperty(TADOQuery,'MasterFields');
TP_GlobalIgnoreClassProperty(TADOTable,'IndexFieldNames');
TP_GlobalIgnoreClassProperty(TADOTable,'IndexName');
TP_GlobalIgnoreClassProperty(TADOTable,'MasterFields');
TP_GlobalIgnoreClassProperty(TADOTable,'TableName');
TP_GlobalIgnoreClassProperty(TADODataset,'CommandText');
TP_GlobalIgnoreClassProperty(TADODataset,'ConnectionString');
TP_GlobalIgnoreClassProperty(TADODataset,'DatasetField');
TP_GlobalIgnoreClassProperty(TADODataset,'Filter');
TP_GlobalIgnoreClassProperty(TADODataset,'IndexFieldNames');
TP_GlobalIgnoreClassProperty(TADODataset,'IndexName');
TP_GlobalIgnoreClassProperty(TADODataset,'MasterFields');
ActiveX stuff
TP_GlobalIgnoreClass (TWebBrowser);
Turbopower Orpheus
TP_GlobalIgnoreClassProperty(TO32FlexEdit,'About');
TP_GlobalIgnoreClassProperty(TO32FlexEdit,'Validation');
TP_GlobalIgnoreClassProperty(TOvcTimeEdit,'About');
TP_GlobalIgnoreClassProperty(TOvcTimeEdit,'NowString');
Turbopower Essentials
TP_GlobalIgnoreClassProperty(TEsDateEdit,'TodayString');
TMS Software TAdvStringGrid
The TAdvStringGrid is not compatible with TranslateComponent(). You should therefore put it on ignore:
TP_GlobalIgnoreClass (TAdvStringGrid);
But there is a solution to get it translated. The following text was provided by
Sandro Wendt:
As I needed to translate one of its descendants ( a TAdvColumnGrid ), I checked with Bruno
and found that the reason for the exceptions "Controls '' has no parent window" were probably the inplace
editors of the grid. There are two you can get at through properties, but several you cannot as they are
only contained in private member fields. However, using the components array of the grid, you can get
at these as well.
This is the code I used:
var
i: integer;
FCompList: TObjectList;
begin
FCompList := TObjectList.Create( false );
try
for i := 0 to FieldDefinition_grid.ComponentCount - 1 do begin
if FieldDefinition_grid.Components[i] is TWinControl then begin
if TWinControl( FieldDefinition_grid.Components[i] ).Parent = nil then begin
TWinControl( FieldDefinition_grid.Components[i] ).Parent := FieldDefinition_grid;
FCompList.Add( FieldDefinition_grid.Components[i] );
end;
end;
end;
TranslateComponent (self);
for i := 0 to FCompList.Count - 1 do begin
TWinControl( FCompList[i] ).Parent := nil;
end;
finally
FCompList.Free;
end;
end;
You need to set the Parent's back to nil because otherwise especially the RichEdit editors will display on
top of the grid. Also, most inplace editors actually have a parent assigned, so you only want to nil the ones you
gave a parent in order for the translation to succeed.
Frequently Asked Questions
About this project
Is anybody using this?
Definitely. The concept, file formats and many of the tools are exactly
the same as are used to localize most Linux, KDE, Gnome, Unix software.
It is also emerging as a very common
translation tool on Windows. Many thousands of programs have been
localized using GNU gettext.
Is it productive?
Definitely. It goes a long way to reduce the amount of work that
has to be done by the translator. Several tools, including KBabel, even provide
automated raw translations based on online dictionaries,
so that the translator's job is reduced
to finding incorrect translations. You'll be amazed at how productive
this translation environment is once you get started.
I use the Delphi Integrated Translation Environment (ITE) - should I switch?
A switch means that you have to change something, and all changes have a cost.
But if you still do development on your application, or if you are starting up
something completely new, the chance is very high that a switch pays off quickly
in terms of Return of Investment. If you are part of a large programming group,
and you use the Delphi ITE, try to ask your Boss how much money you spend on
translators, and whether a 50% reduction on these costs for every future release
would be a nice thing.
Why is this project called dxgettext?
The original GNU gettext software includes a tool named xgettext, that
extracts strings from a lot of different file formats, including C and C++
source code. The main tool in this project is the one that extracts strings
from Delphi source code, so that tool was named dxgettext.
What's the catch? (Why is it free?)
There is no catch. Developing a product is only a very small part of bringing
a product to the market. If the authors of this translation system would try
to earn money on it, there would a lot of work involved:
Raising money to pay us. At least one guy needs to be fulltime on a commercial project
Marketing - this costs money when doing it commercially. Nobody wants to pay for software they
weren't told about.
Documentation - the current documentation is not near anything that a commercial product requires.
Much commercial software also sells better if you make printed documentation, which means
that you actually have to make printed documentation to make your project succeed.
CDs - customer's don't like receiving all software online, some simply want CDs. And they don't want
CD-R's, they want real CDs. That costs money.
Release process - commercial software requires you to test your software with all
possible environments before releasing it. That's a lot of work.
Software design - in order to make software sell easily, you need to
design the software for it. Screenshots are everything, and GUIs are needed everywhere,
and the GUIs need to use the latest GUI features from Microsoft. Sometimes a graphical
designer is needed to make things look expensive.
Administration - all the above has to be administrated. When money is involved, somebody has to
be in charge etc.
What we do now is much easier:
No money needed. We use our spare time to do it. If we don't have spare time,
nothing is being done on the project.
Marketing. When things are free, people talk about it. SourceForge tells about it.
We can announce it in newsgroups. We are very well placed on Google, probably because of
being on SourceForge. It doesn't cost money and doesn't take time.
Documentation - well, documentation is always boring, but people can live with
less when they don't pay for it. Hopefully we have enough documentation, otherwise let us know.
Release process - the first releases were quite buggy because of lack of testing. But
the users commented on it, and in cooperation the quality of the releases have improved a lot,
and the last many releases should not have any serious bugs inside.
This would never have worked this way commercially, but it does as long as it is free.
Software design. Free software only has one goal: Being the best choice for those who
use it. Since that's how we prefer software, too, there is no conflict between marketing
and programmers on how the software should be.
Administration - SourceForge and YahooGroups make much of our administration easier,
and both would be unthinkable for commercial projects. We don't need to create a formal
organization, because if somebody gets angry at the project he can just take all
the source-code and start another project as a branch of this project.
Of course we hope that this never happens, but this mechanism means that people actually
cooperate.
If you want to know more about why Open Source software works, Eric S. Raymond
has written a free book about it: The
Cathedral and the Bazaar
When Delphi developers cooperate on making Delphi better, we make Delphi a better choice,
and thus make us Delphi developers a better choice than those programmers using other tools.
I have a question that is not on this list
Send an empty e-mail to dxgettext-subscribe@yahoogroups.com to join our mailing list, and then write your question to
dxgettext@yahoogroups.com
You can also send an e-mail directly to the maintainer of this FAQ: Lars@dybdahl.dk
We're always glad to help you out, and always happy to get feedback from our users.
Considering to use this software
My program is not in English. What do I do?
Very easy, you do this:
Extract all strings from your program as it is now.
Translate your program to English.
Extract all strings from your program afterwards.
Use the msgmergePOT tool to create an English->YourLanguage translation file.
Now you have an English language program with a translation to the language that your
program used before.
Can I use this to translate a German language program to English?
This is not possible with version 1.1, because it enforces all untranslated messages
to use only ascii characters. Version 1.2RC1 and later have support for non-ascii character
translations, but you can also have a look at the question above.
Does this support Unicode or widestrings?
Yes, this system supports widestrings all along, and actually does it much
better than the Delphi ITE. But please note,
that Delphi has built-in limitations. Delphi's VCL does not do Unicode,
and resourcestring retrieval isn't Unicode either.
But if you get Unicode components and use the features of GNU gettext for Delphi,
you can create an all-through Unicode program.
Does it work without Unicode or widestrings?
Yes. The gettext() function returns widestring, and Delphi will automatically
convert the strings to the local 8-bit character set when you use gettext() where
you don't use widestrings otherwise. For instance, if you assign:
MyButton.Caption:=gettext('New caption');
Then gettext will return a widestring and Delphi will convert that to
the local 8-bit character set before it is assigned to the caption.
My program uses resourcestrings - can gettext handle this?
Yes. It will automatically extract resourcestrings into the .po files,
and it will automatic translate the resourcestrings when they are used
in your source code.
Something is not being translated
I want to know exactly why something isn't translated
As of version 1.1, there is a setting in gnugettext.pas, that will
output a lot of details about the translation system into a logfile.
Switch on this feature and you can see exactly how the translation system
sees the world.
I use TObject derivatives that are not derived from TComponent, how do I translate them?
The "TranslateProperties()" function handles all kinds of objects well,
even TCollections, as long as they are derived from TPersistent.
This includes report components, network components etc.
My 'This is version '+Version+' of my program' is not extracted
Dxgettext has some logic, but it can't read Pascal code like the Delphi
compiler does. Therefore, in the above example, Version must be defined
in the same unit, otherwise dxgettext isn't able to extract it well.
Some Delphi things are not translated. How do I translate these?
Get a translation for the library you need to have translated. The homepage
provides translations for several libraries and for the Delphi runtime library in
several languages, but you can also do it yourself, if you have the source code.
If you get a delphi.mo file with the translations for the Delphi runtime library
and the VCL, put it together with your own translation and call:
AddDomainForResourceString ('delphi');
Somewhere in your program before you start translating forms etc.
If you still need support on this subject, please join our mailing list
and get instructions there.
How do I translate the Delphi runtime library messages?
See this link for instructions.
My menu items are not translated. Why?
Please note that a TMainMenu has a property named AutoHotkeys.
Set this to maManual, or it will automatically change the captions
of the menu items, and thus make it impossible to translate the menus
correctly.
I have some 3rd party components without source. How do I translate those?
All resourcestrings in the 3rd party components will be translated automatically,
so if you can make a list of them and add put them in resourcestrings in your
source code, gnugettext.pas will extract the texts and translate them properly.
But if you cannot make a list, or if the 3rd party component contains
form resources (like those in a dfm file), you could compile your program with separate packages
and include the translation for that package together with the package.
If your application uses the xyz package and is translated to German (language code "de"), you might
end up with the following files:
appdir\application.exe (your program)
appdir\locale\de\LC_MESSAGES\default.mo (the translation of your program)
appdir\locale\de\LC_MESSAGES\delphi.mo (the translation of the Delphi VCL)
winsysdir\xyz.bpl (the xyz package)
winsysdir\xyz.de (the translation of the xyz package)
Something in my forms isn't translated. What do I do?
The extraction tool doesn't extract all properties from forms in order to
make the life for the translators much easier. But even though it extracts a lot
of texts, not all text may be translated once you run the program. You can solve
this by assigning the text in the Form's OnCreate event like this:
component.property:=gettext('This text wasn''t translated in the first place');
Concepts
How does gettext handle two different translations of the same English word?
Experience with thousands of programs shows, that this is extremely rare.
When it does happen, it is usually an error. There are a few ways out, though, and the
easiest one is to put that text into another domain. Another solution is to add a whitespace
and then programmatically remove it again. This will make the text differ from the other
English word.
How should I choose text domains?
Most applications only use one text domain, and this set of tools
assumes that that text domain is named "default". Normally, only very large
projects need multiple text domains, and since it is fairly easy to split a domain into
two, you shouldn't worry until your project gets too big for one domain, and
then you will probably know by the structure of your project, how to divide the
domain into smaller domains. Many projects that have several executables, still only use
one single domain for all the applications, because it reduces the amount of work
that is needed by the translator.
Why are memos extracted line by line?
Delphi stores memos as a list of strings in dfm files, and does not put
any information into the dfm files to tell that these strings come from a TStrings object.
Therefore, it is not possible for the string extraction tools to see, that these strings should
be assembled into one, big, multiline string in the po file. And since the po file doesn't contain
the full memo text as one string, but as several lines, the TranslateComponent()
procedure cannot do anything else than translate the memos line by line.
Another problem is that some programmers use the TMemo.Lines.Objects[] array to store
objects. When you translate such a memo, it is important not to destroy these objects, which would be the case if
a translation was assigned to the TMemo.Lines.Text property. By assigning translations to each
TMemo.Lines.Strings[] index, this is avoided.
There is way to make it all work, though: Instead of putting the memo contents into the user interface
at design time, you can assign the string at runtime like this:
MyMemo.Lines.Text:=
_('This is a demo of my multiline memo translation, where '+
'the entire memo is translated as one big message.'+sLineBreak+
'This message even contains a programmed line break using '+
'the sLineBreak constant, which is equivalent to #13#10 in '+
'Delphi and #10 in Kylix.');
Here, the string extractor will take the entire string as one big message, the translator
will translate it all as one big message, and at runtime it will be assigned as one big message.
Using it
I want to force my program to use another language than Windows settings, how?
Insert this line as the first in your .dpr main program block:
UseLanguage ('fr');
Here, 'fr' means french. Please note that you can not just switch to
a language that uses characters that isn't supported in your Windows.
Switching to greek, russian or chinese without the proper fonts etc. won't work.
There is also another possibility: You can set the environment variable "LANG" to
the desired language code, e.g. "set LANG=fr". This will override the detection of the Windows language
settings.
How do I switch language at runtime?
See the source of the TntSample application that is included in
the download for Delphi.
I want to use language XXX but my controls do not support Unicode, what do I do?
The gettext functions return widestrings, which will automatically be converted to the
local character set by Delphi when you assign it to Control properties of type string or
ansistring. This way, the local 8-bit characterset will be support automatically.
But if you really should need Unicode support in your controls, look for TNT controls.
They are free and do Unicode on Windows NT/2000/XP.
Errors
In Kylix and CLX apps, gettext('Test') cannot compile
In CLX, each form has a gettext() function that will
be chosen instead of gnugettext.gettext(). Use _() instead:
a:=_('Test');
I got an Access Violation. Why?
One of the forms that you translate using TranslateComponent() probably has a
component that doesn't like one of its properties translated or has been programmed very badly
and cannot be analyzed by TranslateComponent(). The solution is to ignore this component. See the list at
for more information, or contact our e-mail list for further information.
If you send an e-mail on that list you will most likely get your problem solved quickly.