<!doctype book PUBLIC "-//OASIS//DTD DocBook V3.1//EN" >
<book lang="en" id="index">
<title>GNU gettext for Delphi, C++ Builder and Kylix 1.2 beta</title>
<titleabbrev>dxgettext</titleabbrev>
<bookinfo id="bookinfo">
<authorgroup id="authorgroup">
<author><firstname>Lars B.</firstname><surname>Dybdahl</surname></author>
<author><firstname>Peter</firstname><surname>Thornqvist</surname></author>
<author><firstname>Jacquez Garcia</firstname><surname>Vasquez</surname></author>
<author><firstname>Sandro</firstname><surname>Wendt</surname></author>
</authorgroup>
<edition>Version 1.2</edition>
<pubdate>2003</pubdate>
<copyright><year>2003</year>
<holder>Lars B. Dybdahl, Free Software Foundation and others</holder>
</copyright>
<legalnotice id="legalnotice">
<para>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.</para>
<para>Some parts are Copyright (C) by Free Software Foundation, Inc.</para>
<!--
Free Software Foundation has the Copyright for the manpages for their tools and the MO/PO file descriptions
Peter Thornqvist wrote about ITE->dxgettext conversion. 
Jacquez Garcia Vazqes wrote about Directives. 
Sando Wendt wrote about TAdvStringGrid.
Lars B. Dybdahl wrote the rest.
-->
</legalnotice>
</bookinfo>



<preface id="preface">
<title>Preface</title>
<para>TODO: utf-8 appendix. license chapter. A chapter with special notes for C++ Builder, Kylix, Delphi etc. utf-8 appendix. More screenshots. Include unicode topics.</para>
<para>GNU gettext for Delphi, Kylix and C++ Builder, is a port of the general purpose translation tool named GNU gettext.</para>
<para>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.</para>
<para>Please note that this manual is for both Windows and Linux users.</para>
</preface>

<chapter id="introduction">
<title>Introduction</title>

<sect1 id="pofiles">
<title>How does GNU gettext work?</title>

<para>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.</para>

<para>The simplest (but not easiest) way to put a translated caption on a label would therefore be:</para>

<programlisting>Label1.Caption:=gettext('Enter username:');</programlisting>

<para>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.</para>

<para>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:</para>

<screen>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 &lt;lars@dybdahl.dk>&gt;\n"
"Language-Team: Dansk &lt;da@li.org&gt;\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"</screen>

<para>Here, <literal>msgid</literal> marks the English original text,
and <literal>msgstr</literal> marks the Danish translation. You can write
this file using a text editor like notepad, wordpad etc.</para>

<para>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".</para>
</sect1>


<sect1 id="scanningsource">
<title>Creating po files</title>

<para>Instead of typing the whole po file yourself, you can create it by scanning
the source code. There are several ways of doing that:</para>

<itemizedlist>
<listitem><para>There is a command line tool named <literal>dxgettext</literal> that can
scan Delphi pas, dfm, xfm, rc files</para></listitem>
<listitem><para>The <literal>dxgettext</literal> commandline tool is also able to extract all the
resourcestrings from an executable file on Windows - like .exe files, .dll files etc.</para></listitem>
<listitem><para>The <literal>xgettext</literal> commandline tool can scan C and C++ files.</para></listitem>
<listitem><para>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.</para></listitem>
</itemizedlist>

<para>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:</para>

<screen> a:='Hello, World';
 a:=gettext(a);</screen>
 
<para>Actually, the system will give a warning that <literal>a</literal> is unknown.
But in this example, it will get extracted:</para>

<screen> a:=gettext('Hello, World');</screen>

<para>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:</para>

<screen> a:=_('Hello, World');</screen>

<para>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.</para>

</sect1>


<sect1 id="advancedgettext">
<title>More gettext functions</title>

<para>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 <literal>textdomain()</literal> procedure.
You can also ask for a single translation from another textdomain like this:</para>

<screen> a:=dgettext('languagenames','Danish');</screen>

<para>Here, the string "Danish" will be looked up in languagenames.mo, and if it exists,
the translation will be assigned to <literal>a</literal>. If no translation exists,
or if the languagenames.mo file doesn't exist for the current language, the string
"Danish" will be assigned to <literal>a</literal>.</para>

<para>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:</para>

<screen>UseLanguage ('da'); // selects Danish</screen>

<para>You can also specify a language and a country using the two-letter
language and the two letter country code like this:</para>

<screen>UseLanguage ('en_US');  // selects American English</screen>

<para>The default is to use the language setting in Windows or Linux.</para>

</sect1>



<sect1 id="resourcestringtranslation">
<title>Resourcestrings</title>

<para>If you have tried the Integrated Translation Environment of Borland Delphi,
you will know about the <literal>resourcestring</literal> keyword. It works like this:</para>

<screen>resourcestring
  msg='Hello, World';

begin
  ShowMessage (msg);
end.</screen>

<para>Delphi automatically replaces the reference to msg with a call to the system function
<literal>LoadResString()</literal>, 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.</para>

<para>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 <literal>AddDomainForResourceString</literal>.
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:</para>

<screen>AddDomainForResourceString ('delphi');</screen>

<para>Using <literal>resourcestring</literal> is not recommended, because
Delphi only handles ansistring this way - not Unicode. But it works.</para>

</sect1>


<sect1 id="forms">
<title>Forms</title>

<para>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 <literal>TranslateComponent</literal> 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:</para>

<programlisting>procedure TMyForm.FormCreate (Sender: TObject);
begin
  TranslateComponent (self);
end;</programlisting>

<para>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.</para>

<para>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:</para>

<screen>TP_GlobalIgnoreClass(TParam);
TP_GlobalIgnoreClassProperty(TField,'FieldName');</screen>

<para>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
<xref linkend="helloworld-dpr" />. You can
also specify a custom handler for a class like this:</para>

<screen>TP_GlobalHandleClass (TSpecialClass,Myhandler);</screen>

<para>You can read more about typical ignores in <xref linkend="handlingclasses" />.</para>

</sect1>

</chapter>














<chapter id="howtochapter">
<title>Action</title>

<sect1 id="createyourfirst">
<title>Localize your first application</title>

<para>Assuming that you have created a simple application in Delphi, this
section will show you how to localize it.</para>

<para>First, you must add <literal>gnugettext.pas</literal> to your project.
It is recommended to copy this file to your project directory, making it
part of your application.</para>

<para>In order to translate your forms and datamodules, you must add
<literal>gnugettext</literal> 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:</para>

<programlisting>TranslateComponent (self);</programlisting>

<para>In this line, <literal>self</literal> refers to the form or datamodule
to which this event belongs. The <literal>TranslateComponent</literal>
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.</para>

<para>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
<literal>TranslateComponent</literal> procedure that it should not translate
certain string properties, you should add procedure calls like the ones
specified in <xref linkend="handlingclasses" /> just before the
<literal>TranslateComponent(self)</literal> call in your main form
or in the .dpr file. You can see the source code for a small, localized
application in <xref linkend="helloworldsource" /></para>

<para>Now, you must ensure that all strings inside your source code are
translated properly. If you have a line like this:</para>

<programlisting>ShowMessage ('Hello, World');</programlisting>

<para>Then you must add <literal>_()</literal> around the string, like
this:</para>

<programlisting>ShowMessage (_('Hello, World'));</programlisting>

<para>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.</para>

<figure id="screenshot-extractfiles">
<title>Explorer integration of string extraction</title>
<graphic fileref="screenshot-extractfiles.png" scale="25"></graphic>
</figure>

<para>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 <xref
linkend="screenshot-extractfiles" />.</para>

<para>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 <ulink url="http://poedit.sf.net/">poEdit</ulink>
and use this program to translate the messages in the po file.</para>

<para>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
<literal>c:\my\program\path\myprogram.exe</literal>, you must put the default.mo file in
<literal>c:\my\program\path\locale\##\LC_MESSAGES\default.mo</literal>. In this path,
<literal>##</literal> represents the two-letter language code that you can find in <xref linkend="iso639" />.</para>

<para>That should be it - your application now uses the translation when the Windows settings
corresponds to the two-letter language code that you chose.</para>
</sect1>

<sect1 id="merging">
<title>New program versions, old translation files</title>

<para>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:</para>

<para>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 <filename>msgmerge</filename> 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.</para>

<para>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.</para>

<para>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.</para>
</sect1>

<sect1 id="createsinglelanguageapp">
<title>Create a single language application with localized runtime library</title>

<para>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:</para>

<orderedlist>
<listitem><para>Install GNU gettext for Delphi</para></listitem>
<listitem><para>Add <filename>gnugettext.pas</filename> to your project.</para></listitem>
<listitem><para>Get the <filename>default.po</filename> file for the Delphi runtime libraries and
translate it to your language. Maybe there is already a translation for your
language <ulink url="http://dybdahl.dk/dxgettext/translations/">online</ulink>.</para></listitem>
<listitem><para>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".</para></listitem>
<listitem><para>Create a <filename>locale\LL\LC_MESSAGES</filename> subdirectory to where your exe
file is, where LL is the two letter ISO 639 language code (see <xref linkend="iso639" />) of the language that your
program uses (da for danish, de for german etc.) and put your <filename>default.mo</filename> file there.</para></listitem>
<listitem><para>Give your translations to us, so that other people may use your translation.</para></listitem>
</orderedlist>

<para>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 <filename>default.mo</filename> file, that is in the
<filename>locale\LL\LC_MESSAGES</filename> 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.</para>

</sect1>

<sect1 id="usesclauses">
<title>Uses clauses</title>
<para>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:</para>

<programlisting>program Project1;

uses
  Forms,
  Unit1 in 'Unit1.pas' {Form1};

{$R *.res}

begin
  Application.Initialize;
  Application.CreateForm(TForm1, Form1);
  Application.Run;
end.</programlisting>

<para>Here, the initialization section of the <literal>Forms</literal> unit will be executed before the initialization
section of the unit named <literal>Unit1</literal>. All units that are used by the <literal>Forms</literal> unit
will also have their initialization section executed before the section of <literal>Unit1</literal>.</para>

<para>Not all units have an initialization section, but the <literal>gnugettext.pas</literal> 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 <literal>DBClient</literal> unit, and this unit fails to
initialize because there is no <filename>MIDAS.DLL</filename>, 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.</para>

<para>A translated application's .dpr file could look like this:</para>

<programlisting>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.</programlisting>

<para>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:</para>

<programlisting>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.</programlisting>

<para>In this example, gginitializer sets all the necessary settings in <filename>gnugettext.pas</filename>.
It could look like this:</para>

<programlisting>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.</programlisting>

<para>In this example, the initialization section of gginitializer will be run before the initialization sections
of units like <literal>Forms</literal> and <literal>Unit1</literal> are run.</para>

</sect1>

<sect1 id="ambiguities">
<title>Solving ambiguities</title>

<para>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.</para>

<para>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</para>

<sect2 id="amb-changewords">
<title>Change words</title>

<para>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.</para>
</sect2>

<sect2 id="amb-addspaces">
<title>Adding spaces</title>

<para>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:</para>

<programlisting>// Preserve the initial space in the translation.
dataset.FieldByName('Name').DisplayLabel:=copy(_(' Name'),2,maxint);</programlisting>

<para>A good translator should always preserve leading and trailing spaces, but sometimes it is useful
to give the translator a hint anyway.</para>
</sect2>

<sect2 id="amb-domains">
<title>Using domains</title>

<para>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 <literal>TranslateComponent()</literal>
with <literal>TP_Ignore()</literal>, and then translate it separately afterwards using
<literal>TranslateComponent(MyRadioButtonGroup,'separatedomain');</literal>.</para>
</sect2>

<sect2 id="amb-usingcodes">
<title>Using trailing comments</title>

<para>Some people using trailing comments to include a comment within the msgid:</para>

<programlisting>function stripafterdot(s:widestring):widestring;
var
  p:integer;
begin
  p:=pos('.',s);
  if p&lt;&gt;0 then
    s:=copy(s,1,p-1);
  Result:=s;
end;

myfield.DisplayLabel:=stripafterdot(_('Name.Displaylabel for the field named "name"'));</programlisting>

<para>This solution requires the translator to know about this notation.</para>
</sect2>
</sect1>

<sect1 id="pluralforms">
<title>Plural forms</title>

<para>The <literal>ngettext()</literal> function is a very powerful function for handling plural forms.
In order to understand this function, you should first understand the <literal>gettext()</literal>
function in <xref linkend="api-gettext" />.</para>

<para>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:</para>

<programlisting>LabelCount.Caption:=format(ngettext('%s file','%s files',filelist.Count),[filelist.Count]);</programlisting>

<para>If no translations are available, the <literal>ngettext()</literal> function will return
'%s file' if filelist.Count=1, and it will return '%s files' otherwise. The <literal>format()</literal>
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.</para>

<para>If you would want to translate this to french, the entry in the po file should look like this:</para>

<screen>msgid "%s file"
msgid_plural "%s files"
msgstr[0] "%s fichier"
msgstr[1] "%s fichiers"</screen>                                                

<para>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'...</para>

<para>Some languages are even more complicated. In Polish, there are three plural forms, and the translation would
look like this:</para>

<screen>msgid "%s file"
msgid_plural "%s files"
msgstr[0] "%s plik"
msgstr[1] "%s pliki"
msgstr[2] "%s plików"</screen>

<para>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 <literal>ngettext()</literal> wherever your text depends on a number,
and the translator will provide the correct translations.</para>

<note>
<title>Not all tools handle msgid_plural well</title>
<para>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 <ulink url="http://unired.sf.net/">UniRed</ulink>.</para>
</note>

<note>
<title>How does it work?</title>
<para>The ngettext and dngettext functions use gettext(singular+#0+plural) to get a #0-separated
list of plural forms.</para>
</note>

<para>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 <literal>dngettext()</literal>,
which is equivalent to <literal>ngettext()</literal> except that it takes a text domain name as
first parameter:</para>

<programlisting>LabelCount.Caption:=format(dngettext('plurals','%s file','%s files',filelist.Count),[filelist.Count]);</programlisting>

<para>In this case, the source code string extraction will put the translation into a file
named <filename>plurals.po</filename>, and the <literal>dngettext()</literal> function will
retrieve the translations from <filename>plurals.mo</filename>. You can then ask the translator
to use notepad to translate the <filename>plurals.po</filename> file. Notepad is not always
very handy, but it's surely compatible with the msgid_plural notation.</para>
</sect1>

<sect1 id="databaseapps">
<title>Database applications</title>

<sect2 id="databaseappsignores">
<title>Preventing unwanted translations</title>

<para>When you create a database application, there will be a lot of component
properties that you don't want to be translated when using <literal>Translatecomponent()</literal>.
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.</para>

<para>Therefore, it is very important, that you consult <xref linkend="handlingclasses" />
to see what ignores you should add to your application. Make sure that these ignores are executed
before the first call to <literal>TranslateComponent()</literal>. 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
<literal>TP_GlobalIgnoreClassProperty()</literal> call.</para>
</sect2>

<sect2 id="displaylabelexplained">
<title>DisplayLabel property explained</title>

<para>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:</para>

<programlisting>select * from customer order by Name</programlisting>

<para>In this case, the name of the <literal>Name</literal> field of the <literal>customer</literal>
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.</para>

<para>The solution is to modify the <literal>.DisplayLabel</literal> property of the <literal>TField</literal>
components, that your dataset possesses. Every <literal>TDataset</literal> descendant has a <literal>.Fields[]</literal>
property that refers to the fields in the dataset, and all these field components are
descendants of <literal>TField</literal>, which has the <literal>TField.DisplayLabel</literal> property.</para>

<para>Delphi provides two ways to modify this <literal>DisplayLabel</literal> property: At runtime and at
design time, which leads to two different ways of handling the localization of it.</para>
</sect2>

<sect2 id="displaylabelatruntime">
<title>Setting displaylabel at runtime</title>

<para>The runtime assignment solution is to assign a displaylabel at runtime like this:</para>

<programlisting>procedure TForm1.Query1AfterOpen(DataSet: TDataSet);
begin
  Dataset.FieldByName('Name').DisplayLabel:=_('Name');
end;</programlisting>

<para>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.</para>
</sect2>

<sect2 id="fieldlistdisplaylabel">
<title>Display label at design time</title>
<para>Creating design-time DisplayLabel properties goes like this:</para>

<itemizedlist>
<listitem><para>Double-click the dataset component. This brings up the field list window.</para></listitem>
<listitem><para>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.</para></listitem>
<listitem><para>Make sure that the form (or datamodule) that the dataset component is on, is translated
using <literal>TranslateComponent()</literal> 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 <literal>.Open</literal>
or <literal>.Active:=True</literal>.</para></listitem>
</itemizedlist>

<para>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, <literal>TranslateComponent()</literal> will translate the
<literal>TField.DisplayLabel</literal> values:</para>

<programlisting>procedure TForm1.FormCreate(Sender: TObject);
begin
  TranslateComponent (self);
  Query.Open;
end;</programlisting>
</sect2>

<sect2 id="dbexportdisplaylabel">
<title>Multiple field name translations</title>

<para>Sometimes it is very handy to have field names translated to something else than what
you put into the <literal>.DisplayLabel</literal> 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 <literal>.DisplayLabel</literal> property.</para>

<para>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:</para>

<programlisting>write (mytextfile,dgettext('fieldnames',myfield.FieldName));</programlisting>

<para>This will find the field name in the <filename>fieldnames.mo</filename> file
and output the translation. The <filename>fieldnames.po</filename> 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.</para>
</sect2>

</sect1>

<sect1 id="repositories">
<title>Translation repositories</title>

<para>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.</para>

<para>In order to merge several po files, you can use the <literal>msgcat</literal> tool:</para>

<screen>C:\translations\da&gt;msgcat -o result.po -t utf-8 --use-first delphi7.po kylix3.po

C:\translations\da&gt;</screen>

<para>This set of parameters will put the result into <filename>result.po</filename>, use utf-8 encoding,
and only take the first translation from each po file.</para>

</sect1>
</chapter>


















<chapter id="projectleader">
<title>Project management</title>

<sect1 id="projectleader-introduction">
<title>Introduction</title>

<para>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
<literal>floor</literal> that contains <literal>0</literal> for ground level and <literal>1</literal>
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.</para>

<para>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?</para>

<para>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.</para>

<para>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?</para>

<para>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.</para>

<para>The leader of an internationalized project has to:</para>

<itemizedlist>
<listitem><para>Ensure that all programmers understand the concept of internationalization.</para></listitem>
<listitem><para>Ensure that translators can give feedback to the programmers in order to ensure that everything is localizable.</para></listitem>
<listitem><para>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.</para></listitem>
<listitem><para>Ensure that the beta-testers know, that they must give feedback on the translation, too.</para></listitem>
</itemizedlist>
</sect1>


<sect1 id="coordinatingtranslations">
<title>Coordinating translations</title>
<para>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.</para>
</sect1>

<sect1 id="translatortasks">
<title>The translator</title>
<para>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.</para>

<para>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.</para>
</sect1>

</chapter>





<chapter id="programmertasks">
<title>Experienced programmers' topics</title>

<sect1 id="determinism">
<title>Determinism and responsibility</title>

<para>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.</para>

<para>GNU gettext is based on function calls (gettext, dgettext etc.),
comments to the translator and a proper localization release procedure.</para>

<para>The system doesn't translate anything that isn't put through the
<literal>dgettext()</literal> 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.</para>

<para>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.</para>

<para>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.</para>
</sect1>

<sect1 id="textdomainmanagement">
<title>Text domain management</title>

<para>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.</para>

<para>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.</para>

<para>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 <literal>msgcat</literal>
and <literal>msghack</literal>. See <xref linkend="gnucommandlinereference" /> for more information.</para>
</sect1>

<sect1 id="gettextfunction">
<title>The better alternative to resourcestring</title>

<para>Instead of using resourcestrings, there is a better alternative:</para>

<programlisting>ShowMessage (_('Action aborted'));</programlisting>

<para>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:</para>

<programlisting>// Message shown to the user after the user clicks Abort button
ShowMessage (_('Action aborted'));</programlisting>

<para>You can also write the comment in the same line:</para>

<programlisting>ShowMessage (_('Action aborted')); // Message to user when clicking Abort button</programlisting>

<para>But only the // style comment is supported - you cannot use { } or (* *) comments for this purpose.</para>

<para>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.</para>

<para>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.</para>
</sect1>


<sect1 id="debugging">
<title>Debugging</title>

<para>You will typically experience two types of errors:</para>

<itemizedlist>
<listitem><para>Something is not translated</para></listitem>
<listitem><para>An error occurs when using <literal>TranslateComponent()</literal></para></listitem>
</itemizedlist>

<para>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.</para>

<para>The <filename>gnugettext.pas</filename> file has a logging system for debugging built-in.
You activate it by defining the conditional define <literal>DXGETTEXTDEBUG</literal>. You can also
find the first occurence of this string in <filename>gnugettext.pas</filename> - here you will find
the following code:</para>

<programlisting>// DEBUGGING stuff. If the conditional define DXGETTEXTDEBUG is defined, it is activated.
{ $define DXGETTEXTDEBUG}
{$ifdef DXGETTEXTDEBUG}
const
  DebugLogFilename='c:\dxgettext-log.txt';
{$endif}</programlisting>

<para>One way to activate the debugging log is to change <programlisting>{ $define DXGETTEXTDEBUG}</programlisting>
to <programlisting>{$define DXGETTEXTDEBUG}</programlisting> by removing the space. As you can see, you can also here
modify the location where the log-file is written to.</para>

<para>The log-file contains a lot of information. At the beginning, you will find very useful information about
what settings the system uses:</para>

<screen>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.</screen>

<para>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 <literal>TranslateComponent()</literal> is used, it looks like this:</para>

<screen>======================================================================
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): ""-&gt;"Project-Id-Version: PACKAGE VERSIONPOT-Creation-Date: 2003-02-16 21:36PO-Revision-Date: 2003-02-17 23:01+0100Last-Translator: Lars B. Dybdahl &lt;lars@dybdahl.dk&gt;Language-Team:  &lt;&gt;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"-&gt;"GNU gettext eksempel"
Found in .mo (default): "Click me"-&gt;"Klik mig"
Found in .mo (default): "Click me"-&gt;"Klik mig"
----------------------------------------------------------------------
This is the first time, that this component has been translated. A retranslator component has been created for this component.
======================================================================</screen>

<para>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.</para>

<para>The first time that the application wants to translate an "OK" button, it looks like this:</para>

<screen>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): ""-&gt;"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 &gt;lars@dybdahl.dk&lt;Language-Team: Dansk &gt;da@li.org&lt;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"-&gt;"O.k."</screen>

<para>Here you can see, that it first searches <filename>default.mo</filename> 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.</para>

<para>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.</para>

<para>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.</para>

<para>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 <literal>TP_GlobalIgnoreClassProperty()</literal>, or you can
disable it only for the next call to <literal>TranslateComponent()</literal> using <literal>TP_Ignore</literal>.</para>

</sect1>

<sect1 id="directives">
<title>Directives</title>

<para>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 <literal>const</literal> keyword, into a specified text domain:</para>

<screen>{gnugetext: scan-all [text-domain='domain name'] }
.
. in this section the string constants will be extracted
.
{gnugettext: reset }</screen>

<para><literal>{gnugettext: scan-all }</literal> is named the 'start directive' in this document.
<literal>{gnugettext: reset }</literal> is named the 'end directive' in this document.</para>

<para>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!
</para>

<note>
<title>Example:</title>

<screen>{gnugettext: scan-all text-domain='toto' }
Const
  a = 'toto';
{gnugettext: scan-all text-domain='titi' }
  b = 'titi';
{gnugettext: reset }</screen>

<para>In this example, the msgid 'toto' will be put into <filename>toto.po</filename>
and 'titi' will be put into the file named <filename>titi.po</filename>.</para>
</note>

<para>It is the plan to extend the directive system in the future to disable/enable string
extraction etc.</para>
</sect1>
</chapter>















<chapter id="advancedtopics">
<title>Advanced topics</title>



<sect1 id="convertfromdelphiite">
<title>Migrating from the ITE to dxgettext</title>

<para>This chapter was written by Peter Thornqvist.</para>

<sect2 id="delphiite-into">
<title>Introduction</title>

<para>Although I've been somewhat involved in the development of dxgettext - mainly
      donating a couple of tools to convert translations from ITE to
      dxgettext - I had not myself been ready to take the final step in moving any
      larger application over. It was both a matter of lack of time but also a
      hesitation about the usefulness of the po file format and the possible loss of
      information.</para>
    <para>Well, one day the ITE gave up on me (for the umpteenth time) and I just
      couldn't get it to compile my project anymore. It complained about "ancestor
      not found". Checking out the files from VCS and even going back in history
      didn't solve it. I was stranded with a non-compiling project and I had no
      solution how to get it to work again.</para>
    <para>It was time for me to get down and dirty with dxgettext and this is the report
      on how I did it, what I had to change and how it all worked out in the end.</para>
</sect2>

<sect2 id="delphiite-project">
<title>The project</title>
    <para>The application I am going to migrate is called EQ Plan and is a graphical
      planning tool mainly for manufacturing companies 
      (see <ulink url="http://www.timemetrics.se/">http://www.timemetrics.se/</ulink> for details and
      trial downloads). As far as localization is concerned, this is a mid-sized
      application consisting of about 45 forms, 5 or so frames (I avoided frames
      since
      I knew the ITE didn't like them) and about 10-15 additional files. Since I was
      using the ITE, all strings (except property values) were declared as
      resourcestrings and most of them was located in a single unit. Additionally,
      the application uses a wide variety of components and controls; some
      third-party
      components from JVCL and KWizard and a couple of custom made components (like
      the Gantt-chart and a VS.Net style treeview). The application is not
      Unicode enabled, limiting it's usability to western Windows versions. All
    string in the program are in English and we have translated it to Danish and
    Swedish. Translation to Norwegian and German is planned but not
      started
      (as of this writing), making this a good time to do the change.</para>
</sect2>

<sect2 id="delphiite-planning">
<title>Planning</title>
    <para>I didn't do much planning because the task was pretty straightforward but I
      at least made the following to-do list:</para>
    <itemizedlist>
      <listitem><para>Isolate any remaining strings used in the program and
      convert them to resourcestring. Since I already used resourcestrings and
        the
      program isn't Unicode anyway, I decided to stick with resourcestrings, although
        I probably would have used the _() syntax if this had been a new program (see the
      dxgettext documentation for an explanation of "_()")</para></listitem>
      <listitem><para>Extract any existing ITE translations from the dfn
      and rc files in the language subfolders.</para></listitem>
      <listitem><para>Since all forms already inherited from a common
      ancestor, I decided to use this ancestor to implement as much as possible of
      the basic translation functionality and add any special handling in each
      derived form or utility unit as needed.</para></listitem>
      <listitem><para>After successfully moving the translation, find all
      components and/or units that have untranslated strings even when running a
      localized version of the program. Add special handling of the components as
      necessary and add any missing strings as resourcestrings.</para></listitem>
      <listitem><para>Since gnugettext doesn't localize component bounds
      (top, left, width, height), go through all forms and check that texts and label
      alignments and the like looks good in all languages.</para></listitem>
      <listitem><para>Create installation packages for the language file(s).</para></listitem>
      <listitem><para>Implement a language switch functionality inside the
      program so the end-user easily can change the used language.</para></listitem>
      <listitem><para>Test the new translations on as many systems as possible, i.e. different Windows versions as well as
        different language
        versions of Windows.</para></listitem>
</itemizedlist>
</sect2>

<sect2 id="delphiite-tools">
<title>Tools I needed (and used)</title>
    <para>There are several tools I need to be able to migrate my application.
      Specifically, I used the following:</para>
    <itemizedlist>
      <listitem><para>dxgettext (or actually the shell integration) to
      extract strings and properties from the application sources to create the
      template po file</para></listitem>
      <listitem><para>dfntopo (include din dxgettext distribution) to
      extract the translations from the dfn and rc files and update the language
      specific po file.</para></listitem>
      <listitem><para>poEdit (<ulink url="http://poEdit.sourceforge.net/">http://poEdit.sourceforge.net/</ulink>)
        to
        view the po files, provide additional and compile the mo file (the
        binary translation file)</para></listitem>
</itemizedlist>
</sect2>

<sect2 id="delphiite-doingit">
<title>Doing the conversion</title>
    <para>I started with extracting the po template from my source folder: I
      right-clicked on the source folder and clicked on "Extract translations to
      template". A file named default.po was created in the selected folder and it
      contained all the original strings found by dxgettext. Since there were a lot
      of
      strings, there was no way I could determine how successful dxgettext had been. I
      just had to trust it for the time being. One thing that I didn't do at this
      stage (because I wasn't aware of it) but that I recommend you to do is this:
      Run
      the msgmkignore.exe on the default.po file. This will create a new file
      8typicaly named ignore.po) with all the strings that probably shouldn't be in
      default.po. This is strings like numbers, component names, font names etc. It
      does an amazingly good job too: all the strings it found in my sources were
      such
      that I did actually want to remove them. To actually remove the strings from
      default.po, I ran the command line tool mskremove.exe. Now the (first) template
      was ready for use.</para>
    <para>Since I now had a template, I needed to retrieve as many of the available
      translations as possible from the ITE. I certainly didn't want to translate all
      this again. I made this simple for myself. I copied the dfntopo.exe program
      along with the default.po file into each of my ITE language folders (where the
      dfn files are located) and ran dfntopo.exe from a command-prompt. I used the -f
      option to force dfntopo to add all new translations that it couldn't find in
      the
      po file. Although this will probably reinsert the items we just removed with
    msgremove, I wanted to make sure that all the translations I had, made it over
      into the po file. I can always run msgremove again at a later time.</para>
    <para>So, I now have a default.po in Danish and one in Swedish. I also have the
      template default.po file (the one without any translations) in the original
      source folder. This means that I am now actually ready to test the translations
      with gnugettext and this after only about an hours work! Of course, I still
      have
      to enable my application to read the translations and this is a three step
      process:</para>
    <itemizedlist>
      <listitem><para>Create the correct subfolder structure for my
      application folder and put the translated default.po files there.</para></listitem>
      <listitem><para>Open each po file with poEdit and save it to create
      the mo file.</para></listitem>
      <listitem><para>Add gnugettext.pas to my application so it can read the mo file (almost)
        automatically</para></listitem>
	</itemizedlist>
    <para>The subfolder structure on my machine looked like this after I've added the
    Swedish and Danish folders:</para>
    <mediaobject><imageobject><imagedata fileref="MigrateFolderStructure.jpg" scale="100" format="JPG"></imageobject></mediaobject>
    <para>Each of the translated default.po files are located in
      the language specific LC_MESSAGES folder. As you can see, the folder structure
      is somewhat convoluted and uses some predefined language identifiers (these 
      names are actually standard ISO639
      identifiers, sometimes combined with ISO3166 country abbreviations) to 
      enable gnugettext
      to automatically select the correct language file (this is based on the users
      current
      locale). I suppose one could modify gnugettext.pas to, as an example, place all
      the language files in one folder and call them se.po, da.po etc instead but I
      decided
      to use the default settings. If the po/mo files are correctly placed and it 
      still
      doesn't work at run-time, at least I know that it's something else that isn't
      working.</para>
    <para>Now I could open and save the po files with poEdit. This
      creates an mo file - a compiled binary version of the po file - in the same 
      folder as the po
      file and it's actually this file that gnugettext needs. The po file is only 
      used to allow
      humans
      to read and edit the content. Now I was ready to edit my project
      to
      read the translations.</para>
 <para>I had some errors when I opened the po files with poEdit: TABS (#9) had been translated to \x9\ but
    poEdit expected \x9 and embedded
  double-quotes weren't properly escaped (should have been \"
  instead of just "). The quote escaping I fixed by modifying the source of
    dfntopo and the \x9\ problem I fixed manually by doing a search and replace
    on the po files.</para>
</sect2>

<sect2 id="delphiite-commonancestors">
    <title>Common ancestor forms are good!</title>
    <para>Since all my forms already had a common ancestor form (a good coding
      practice, by the way: you loose nothing and can gain a lot), I decided to add 
      as
      much gnugettext functionality to that form. The other forms would then inherit
    the behavior and I would have to add the code everywhere. Additionally, any new
      forms I decide to add in the future will also have the functionality
      automatically. So I added the following overridden AfterConstruction code to my
      ancestor form:</para>
<programlisting>
var
  AlreadyDone:boolean = false;
  
procedure TfrmGnuGT.AfterConstruction;
begin 
  inherited; 
  if not AlreadyDone then
  // this should only be done once for the whole app
  begin
    TP_GlobalIgnoreClassProperty(TAction,'Category'); 
    TP_GlobalIgnoreClassProperty(TControl,'ImeName');
    TP_GlobalIgnoreClassProperty(TControl,'HelpKeyword');
    TP_GlobalIgnoreClass(TMonthCalendar);
    TP_GlobalIgnoreClass(TFont);
    // TP_GlobalIgnoreClass(TStatusBar); 
    // TP_GlobalIgnoreClass(TWebBrowser); 
    // TP_GlobalIgnoreClass(TNoteBook);  
    // TP_GlobalHandleClass(TCustomTreeView,HandleTreeView); 
    // TP_GlobalHandleClass(TKWizardCustomPage,HandleWizardPage); 
    AlreadyDone := true;
  end;
  TranslateComponent(self,'default');
end;
</programlisting>

    <para>The AlreadyDone variable is needed because this code will be called for each 
form in the application and gnugettext raises an exception if the TP_GlobalXXXX 
functions are called more than once for the same class. Personally, I think this 
is unnecessary. There is no risks involved adding 
these call many times as the class or property is to be ignored anyway. But 
since repeated calls to TP_GlobalHandleClass class <emphasis>should </emphasis>generate an 
exception in my opinion, I would still need the AlreadyDone variable, so this is no big 
issue with me.</para>
<para>The TranslateComponent call is the one doing all the magic: this function 
iterates all the components on the form and all it's subcomponents, hunting out 
published properties that can be translated. If a property is found, it searches 
the mo file for a translation and uses RTTI to change the property value (if it 
isn't read-only). Actually, there is another piece of hidden magic working for 
us as well: remember that I use resourcestrings? Well, these are also handled 
automatically thanks to a dynamic replacement of the LoadResString function 
in gnugettext.pas. This replacement function calls into gnugettext instead of 
into the resource DLL as the ITE does. Together, they cover almost everything 
that can be translated in an application (unless you are using Unicode in which 
case resourcestring translation won't work). </para>
    <para>When I ran the program, a lot of the strings were translated but not all. 
Among other things, neither menus, treeviews nor all strings in the KWizard we 
translated. Additionally, some of the forms I opened generated Access Violations 
and strange errors. Something seemed to be amiss and how to fix that is our next 
priority.</para>
</sect2>

<sect2 id="delphiite-doesnt">
<title>Handling components dxgettext doesn't handle</title>
    <para>If you read the documentation for dxgettext (and you should!), you soon 
realize that it doesn't handle <emphasis>everything</emphasis> that you throw at it. It can 
handle published properties of the components passed into TranslateComponent or 
published properties of components owned by that component. If you create components dynamically at run-time
    (with Owner set to nil), these components won't be translated unless you call
    TranslateComponent explicitly. Additionally, public properties won't be
    translated (they don't have any RTTI). Some components, like treeviews and listviews, have
    item list properties that you need to handle manually by adding your own procedure to
    explicitly iterate over the list and use _() to translate them. For a
    treeview, here's the code I added to my base form:</para>
    
<programlisting>type
  THackTreeView = class(TCustomTreView);

procedure TfrmGnuGT.HandleTreeView(Obj: TObject);
var N:TTreeNode;T:THackTreeView;
begin
  T := THackTreeView(Obj);
  N := T.Items.GetFirstNode;
  while N &lt;&gt; nil do
  begin
    N.Text := _(N.Text);
    N := N.GetNext;
  end;
end;
</programlisting>

    <para>I also had to tell dxgettext that I want to handle treeview translations myself, so I
    added a call to TP_GlobalHandleClass in AfterConstruction:</para>
    <programlisting>TP_GlobalHandleClass(TCustomTreeView,HandleTreeView);</programlisting>
    <para>I also added similar handlers for listviews and the KWizard.</para>
</sect2>

<sect2 id="delphiite-problems">
    <title>Problems</title>
 <para>When running the program, I noticed that some of my menu items weren't translated correctly. I use TActionLists exclusively
    and those items were correctly translated but the top level menu items (those without actions) weren't translated.
    I ran gnugettext in debug mode and found that it was caused by the menu component(s) having their AutoHotKeys property set to automatic.
    I changed this to manual and explicitly assigned
    hot keys (Alt+F, Alt+E etc) to all top level menu items. Since I didn't want to regenerate the
    po, I just added these new strings manually to the po file. Now these items
    were also translated correctly.</para>
    <para> I also noticed that the color combobox from JVCL that I was using didn't translate it's text captions (the ColorNameMap property). 
    After some searching, debugging and hair pulling I found a
    problem in the way the component retrieved the color names, fixed it
    (thankfully, I am a developer on JVCL so I can do these things!) and then it worked like a charm.</para>
    <para> Running the program again everything seemed to work fine until I tried to open the report form: this form uses a TWebBrowser
    (these are HTML reports and the TWebBrowser is used to preview and print
    them) and I got a nasty Exception telling me that the StatusText property of
    the control couldn't be translated. The error message also suggested how I should fix the problem (very nice!). Since there is no visible UI elements in TWebBrowser (except for the main
    window, which contains nothing to translate) I elected to add a TP_GlobalIgnoreClass
    for it and the form then opened without problems.</para>
    <para> Next, I started to work through all my menus, popups and forms one by
    one to see if everything was translated and worked as expected. I found that I had some comboboxes that lost their stored 
    ItemIndex when they were translated (it was reset to -1) so I added some code to set these programmatically. Also, I found some items that
    hadn't been translated in the po and I fixed these with poEdit.</para>
    <para> Everything seemed to work fine until I tried to open one of the forms: I got an unexpected AV. I tried to trace the AV but didn't get too far: 
    the code in gnugettext is highly recursive making it problematic to find the spot where an error occurs. Since the form used an "interposer class" - 
    a class declaration that overrides an existing class, I thought at first that this was the cause of the problem. I tried temporarily removing it but that didn't help. 
    After some more debugging I finally
    figured out that the AV was caused by a TNotebook on the form and after adding it to the ignore list, the error disappeared.
    Had I read the documentation a little more carefully, I probably wouldn't
    have had this problem since it mentions notebooks as one component that
    causes problems.</para>
    <para> The next weird error was with one of my toolbars: suddenly the rightmost buttons on the toolbar had switched event handlers! I couldn't believe this at first and had to check several times to make sure that I was seeing things right. It turned out that I had a button on the toolbar that was hidden at run-time
    and this seemed to cause gnugettext to somehow switch the buttons around, so what
    I thought was the fifth button was actually the sixth according to
    gnugettext. Since I didn't really need the hidden button, I removed it and didn't investigate
    it further but it might be something to be vary of.</para>
    <para> Next issue was the KWizard I was using: the button captions and everything on the pages
    was translated properly but the Header.Title.Text and Header.SubTitle.Text were not.
    I suspect this has to do with the fact that these properties use nested
    (TPersistent) classes and dxgettext doesn't handle that. I added a TP_GlobalHandleClass
    for TKWizardCustomPage to TfrmGnuGT and got it running fine.</para>
    <para> One other oddity in the wizard
    form was a TStaticText that disappeared when translated. I checked the debug log from gnugettext and the string was found and translated but nothing showed up at run-time. At first I thought it had to do with the anchoring (it was anchored
    left, bottom) but that wasn't it. I finally figured out that it had to do with AutoSize being set to
    true and setting it to false fixed the problem. Apparently, the label was resized to
    zero width when the Caption changed but it was never resized according to the width of the new Caption.</para>
</sect2>

<sect2 id="delphiite-conclusion">
<title>Conclusion</title>
    <para>The whole process of moving an app from ITE to dxgettext can be broken
    down into the following steps:</para>

    <itemizedlist>
      <listitem><para>Get and install the latest version of dxgettext (<ulink url="http://dxgettext.sf.net/">http://dxgettext.sf.net/</ulink>)</para></listitem>
      <listitem><para>Get and install latest version of poEdit (<ulink url="http://poedit.sf.net/">http://poedit.sf.net/</ulink>)</para></listitem>
      <listitem><para>Add a form (let's call it TfrmGnuGT) to your project. Use this form
        as the ancestor for all other forms in the application. Override the AfterConstruction
        method and add calls to TP_GlobalIgnoreClass, TP_GlobalHandleClass,TP_GlobalIgnoreClassProperty
        and to TranslateComponent as necessary.</para></listitem>
      <listitem><para>Add gnugettext.pas to the new form's uses clause. Make sure gnugettext is in your path or copy it to your project folder.</para></listitem>
      <listitem><para>Go through all the forms in the project and change their inheritance so they now inherit from TfrmGnuGT. 
      Add the TfrmGnuGT unit's name to the forms interface uses clause.</para></listitem>
      <listitem><para>In the Explorer, right-click your project folder and select "Extract translations to template". 
      Your sources are parsed and all found strings are put into a file named deafult.po</para></listitem>
      <listitem><para>Double-click the default.po file to open with poEdit. Verify that everything
        looks OK. Close it again.</para></listitem>
      <listitem><para>Copy dfntopo.exe and the default.po into (one of) your ITE language subfolders.
        Open a command prompt in that folder and type dfntopo &lt;ENTER&gt; to see the command-line switches for the tool.
	Run the tool to extract translations from the dfn and rc files in the folder. The resulting default.po should now contain at
	least some translated strings: open with poEdit to verify.</para></listitem>
      <listitem><para>Repeat above step for each of your languages</para></listitem>
      <listitem><para>Create a subfolder structure below your projects output folder for each of
        your languages using this format: 
&lt;root&gt;\&lt;langcode&gt;\LC_MESSAGES\ and put each of the previously created default.po files into each folder.</para></listitem>
      <listitem><para>Manually change "\x9\" in the po file to "\x9" and
        make sure quote characters are properly escaped.</para></listitem>
      <listitem><para>Create your own translation handlers for classes like treeviews,
        listviews and any third-party that refuses to translate.</para></listitem>
      <listitem><para>Disable handling of some classes (like TFont, TWebBrowser and
        TNotebook), that obviously shouldn't be translated or that can cause
        problems with dxgettext.</para></listitem>
    </itemizedlist>
    <para>I had initially planned to add dynamic switching functionality to the
    program but decided against it. Our users don't really need to switch
    languages at run-time since they once and for all deicde which language they
    want to use and stick with that.</para>

    <para>All in all, the migration went better than I had thought. There was some
    problems but nothing unsolvable and it took about 4-5 hours to do it from
    start to finish. In conclusion, well worth the effort.</para>
</sect2>

</sect1>



<sect1 id="translationstatistics">
<title>Translation statistics</title>

<para>The <literal>msgfmt</literal> program is able to output some statistics about the contents
of a po file:</para>

<screen>C:\source\sf\DXGETT~1\TRANSL~1\de>msgfmt --statistics kylix3.po
1815 translated messages, 113 fuzzy translations, 53 untranslated messages.</screen>

<para>Additionally, the <literal>msgshowheader</literal> tool is able to show the header of
a compiled mo file:</para>

<screen>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 &lt;info@xan.de&gt;
Language-Team: XAN &lt;info@xan.de&gt;
MIME-Version: 1.0
Content-Type: text/plain; charset=utf-8
Content-Transfer-Encoding: 8bit</screen>

<para>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
<ulink url="http://www.python.org/">Python</ulink> script in the translations CVS module on SourceForge for this
purpose.</para>

<para>For further information on this topic, ask in our
<ulink url="http://groups.yahoo.com/group/dxgettext/">forum</ulink>.</para>
</sect1>


<sect1 id="multipleinstances">
<title>Multiple instances</title>

<para>This chapter will contain an explanation of the TGnuGettextInstance class.</para>
</sect1>


<sect1 id="multithreading">
<title>Multithreading issues</title>

<para>The procedures in <filename>gnugettext.pas</filename> 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.</para>

</sect1>

</chapter>



<appendix id="apireference">
<title>API reference</title>

<sect1 id="api-adddomainforresourcestring">
<title>procedure AddDomainForResourceString (domain:string);</title>

<para>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 <filename>delphi.mo</filename>,
too, if it isn't found in <filename>default.mo</filename>:</para>

<programlisting>AddDomainForResourcestring ('delphi');</programlisting>
</sect1>

<sect1 id="api-removedomainforresourcestring">
<title>procedure RemoveDomainForResourceString (domain:string);</title>

<para>If a text domain has been added using AddDomainForResourcestring(),
you can remove it again using this function.</para>
</sect1>

<sect1 id="api-loadresstring">
<title>function LoadResString(ResStringRec: PResStringRec): widestring;</title>

<para>This function is 100% identical to LoadResString.</para>
</sect1>

<sect1 id="api-loadresstringw">
<title>function LoadResStringW(ResStringRec: PResStringRec): widestring;</title>

<para>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.</para>
</sect1>

<sect1 id="api-loadresstringa">
<title>function LoadResStringA(ResStringRec: PResStringRec): ansistring;</title>

<para>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.</para>
</sect1>

<sect1 id="api-executablefilename">
<title>var ExecutableFilename:string;</title>

<para>Don't modify this variable. It contains the full path and filename of the .exe file,
if <filename>gnugettext.pas</filename> is compiled into an .exe file, and it contains the
full path and filename to the .dll file, if <filename>gnugettext.pas</filename> is compiled into
a .dll file. This variable is used to find any embedded translations, if present.</para>
</sect1>

<sect1 id="api-hookintoresourcestrings">
<title>procedure HookIntoResourceStrings (enabled:boolean=true; SupportPackages:boolean=false);</title>

<para>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.</para>

<para>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.</para>

<para>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.</para>
</sect1>

<sect1 id="api-debuglogfilename">
<title>const DebugLogFilename='c:\dxgettext-log.txt';</title>

<para>This is the full path of the log output when doing debugging. See <xref linkend="debugging" />
for more information.</para>
</sect1>

<sect1 id="api-texecutable">
<title>TExecutable</title>

<para>This class is not for end-user usage. Please ignore it.</para>
</sect1>

<sect1 id="api-tgetpluralform">
<title>TGetPluralForm</title>

<para>This class is not for end-user usage. Please ignore it.</para>
</sect1>

<sect1 id="api-tgnugettextinstance">
<title>TGnuGettextInstance class</title>

<para>The entire functionality of the <filename>gnugettext.pas</filename> unit
is encapsulated in the <literal>TGnuGettextInstance</literal> 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 <literal>DefaultInstance</literal>, and almost all non-class procedures and functions
in the unit refer to this instance.</para>
</sect1>

<sect1 id="api-uselanguage">
<title>procedure UseLanguage(LanguageCode: string);</title>
<para>When an application starts up, the initialization section of <filename>gnugettext.pas</filename>
makes this call:</para>
<programlisting>UseLanguage('');</programlisting>
<para>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.</para>

<para>The OS language settings can always be overridden by setting the <literal>LANG</literal>
environment variable like this (example is for Windows):</para>

<screen>set LANG=de_DE
myapp</screen>

<para>or on Linux:</para>

<screen>LANG=de_DE &amp;&amp; ./myapp</screen>

<para>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 <xref linkend="pluralforms" /> for more
information on this topic.</para>

<para>All mo files that were open are closed. mo files are opened again when the text domains
are accessed.</para>

<para>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 <literal>de_DE</literal>, English in England becomes <literal>en_GB</literal>
and flamish becomes <literal>nl_BE</literal>. 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 <filename>nl_BE/default.mo</filename> file, <filename>nl/default.mo</filename> will be used
instead, if present.</para>

<para>You can find the two-letter ISO 639 language codes in <xref linkend="iso639" /> and the two-letter ISO 3166
country codes in <xref linkend="iso3166" />.</para>
</sect1>

<sect1 id="api-underscore">
<title>function _(msg:widestring):widestring;</title>

<para>The <literal>_()</literal> function is an alias to the gettext function. Please
see <xref linkend="api-gettext" /> for further information.</para>
</sect1>

<sect1 id="api-getcurrentlanguage">
<title>function GetCurrentLanguage:string;</title>
<para>This function returns the language code that was specified to the last call of <literal>UseLanguage()</literal>.
If <literal>UseLanguage()</literal> 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.</para>
<para>See <xref linkend="api-uselanguage" /> for more information.</para>
</sect1>

<sect1 id="api-gettext">
<title>function gettext(msg:widestring):widestring;</title>

<para>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:</para>

<programlisting>MessageDlg (gettext('Hello, World'),mtInformation,[mbOK],0);</programlisting>

<para>Because the <literal>_()</literal> function is the same as <literal>gettext()</literal>,
the line above is normally written like this:</para>

<programlisting>MessageDlg (_('Hello, World'),mtInformation,[mbOK],0);</programlisting>

<para>The string parameter should only contain ascii characters. If it contains non-ascii characters,
the conversion from ansistring to widestring may be sensitive to current Windows locale.</para>

<para>The result value is a widestring. If you use this in context with the Delphi
<literal>string</literal> type (<literal>ansistring</literal>), Delphi 6 and later
will automatically convert the string types. The MessageDlg example above show this,
the first parameter in <literal>MessageDlg()</literal> is a <literal>string</literal>,
not a <literal>widestring</literal>. Delphi converts between <literal>string</literal> and
<literal>widestring</literal> using Windows API calls, and therefore converts perfectly
to and from multibyte character sets. On Linux, the behaviour is identical.</para>

<para>The <literal>gettext()</literal> functions looks up translations in the default.mo
file (the default text domain) unless the <literal>textdomain()</literal> procedure has
been called.</para>

<note>
<title>Special case: The empty string</title>
<para>Please note that you should never try to translate an empty string (<literal>_('')</literal>).
The translation of the empty string is the message header, which contains information about the translator,
the character set, modification date etc.</para>
</note>

<note>
<title>Performance</title>
<para>The <literal>gettext()</literal> 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.</para>
</note>
</sect1>

<sect1 id="api-dgettext">
<title>function dgettext(Domain: string; MsgId: widestring): widestring;</title>

<para>This function is identical to <literal>gettext()</literal>, 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.:</para>

<programlisting>LanguageList.Add(dgettext('languagenames','Danish'));</programlisting>

<para>In this example, Danish is looked up in <filename>languagenames.mo</filename>.
During extraction of strings from Delphi/ObjectPascal source code, 'Danish' will be put into
<filename>languagenames.po</filename>.</para>

<note>
<title>Special note for C/C++ programmers</title>
<para>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
<literal>'Danish'</literal> will be put in the main default.po file, mixing everything up.</para>
</note>

<para>For further information on this subject, please see <xref linkend="api-gettext" />.</para>
</sect1>

<sect1 id="api-ngettext">
<title>function ngettext(const singular,plural:widestring;Number:longint):widestring;</title>

<para>This function is explained in <xref linkend="pluralforms" />.</para>
</sect1>

<sect1 id="api-dngettext">
<title>function dngettext(Domain,singular,plural:widestring;Number:longint):widestring;</title>

<para>This function is explained in <xref linkend="pluralforms" />.</para>
</sect1>

<sect1 id="api-getcurrenttextdomain">
<title>function getcurrenttextdomain:string;</title>

<para>This function returns the current text domain. The default value for
the current text domain is 'default', but can be changed using <literal>textdomain()</literal>.
See <xref linkend="api-textdomain" /> for further information.</para>
</sect1>

<sect1 id="api-textdomain">
<title>procedure textdomain(Domain:string);</title>
<para>This procedure changes the current textdomain, i.e. the text domain that is used by
<literal>gettext()</literal>, <literal>_()</literal> and <literal>ngettext()</literal>. The
default text domain, if <literal>textdomain()</literal> is not called, is 'default'.</para>

<para>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'.</para>
</sect1>

<sect1 id="api-bindtextdomain">
<title>procedure bindtextdomain(Domain:string; Directory:string);</title>
<para>Each text domain can be located in a separate directory. The default is to search
for all mo files as <filename>applicationdir/locale/XX/LC_MESSAGES/domainname.mo</filename>, 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:</para>

<programlisting>bindtextdomain ('moduleB','c:\moduleB\locale\');</programlisting>

<para>In this example, all translations done using the text domain 'moduleB' will be looked
up in <filename>c:\moduleB\locale\XX\LC_MESSAGES\moduleB.mo</filename>. This can be very useful
if you link a module into your program file, but the module translations are somewhere else on your harddisk.</para>
</sect1>

<sect1 id="api-bindtextdomaintofile">
<title>procedure bindtextdomainToFile (Domain,Filename:string);</title>
<para>If you want to use <literal>gettext()</literal> 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:</para>

<programlisting>function GetLanguageName (isocode:string):string;
var
  EnglishLanguageName:string;
begin
  bindtextdomainToFile ('isocodes','c:\isocodes.mo');
  EnglishLanguageName:=dgettext('isocodes',isocode);
  Result:=dgettext('languagenames',EnglishLanguageName);
end;</programlisting>

<para>There are many ways this can be used. For instance, the <literal>msgshowheader</literal> command line
tool uses <literal>bindtextdomainToFile()</literal> to fetch the header from a specific file that is given
as parameter. It is done something like this:</para>

<programlisting>bindtextdomainToFile ('parameterfile',paramstr(1));
writeln (dgettext('parameterfile',''));</programlisting>

<para>The translation of the empty string is always the header.</para>
</sect1>

<sect1 id="api-getlistoflanguages">
<title>procedure GetListOfLanguages (domain:string; list:TStrings);</title>
<para>This procedures searches the directory structure and the embedded translations
for any valid translation files for the specified domain. It uses the <literal>bindtextdomain()</literal>
directory. The result is a list of language codes that it puts into the list parameter.</para>

<para>It is then up to the programmer to convert these language codes into language names. Example:</para>

<programlisting>// 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');</programlisting>
</sect1>

<sect1 id="api-gettranslationproperty">
<title>function GetTranslationProperty (Propertyname:string):WideString;</title>

<para>The translation files contain a header as the translation of an empty string. A typical
header looks like this:</para>

<screen>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 &lt;Lars@dybdahl.dk&gt;\n"
 "Language-Team: Dansk &lt;da@li.org&gt;\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"</screen>

<para>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 <literal>GetTranslationProperty()</literal> function
like this:</para>

<programlisting>LabelTranslationLicense.Caption:=DefaultInstance.GetTranslationProperty('License');</programlisting>
</sect1>

<sect1 id="api-gettranslatornameandemail">
<title>function GetTranslatorNameAndEmail:widestring;</title>

<para>These two lines do the same thing:</para>

<programlisting>LabelTranslator.Caption:=DefaultInstance.GetTranslationProperty('Last-Translator');
LabelTranslator.Caption:=DefaultInstance.GetTranslatorNameAndEmail;</programlisting>

<para>In other words, this function is just an easy way to fetch the translator name, but not the only one.
See <xref linkend="api-gettranslationproperty" /> for more information.</para>
</sect1>

<sect1 id="api-saveuntranslatedmsgids">
<title>procedure SaveUntranslatedMsgids(filename: string);</title>

<para>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.</para>
</sect1>

<sect1 id="api-translateproperties">
<title>procedure TranslateProperties(AnObject:TObject; textdomain:string='');</title>

<para>This procedure iterates through all properties of <literal>AnObject</literal> 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.</para>

<para>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.</para>
</sect1>

<sect1 id="api-translatecomponent">
<title>procedure TranslateComponent(AnObject: TComponent; TextDomain:string='');</title>

<para>This procedure will basically do the same as <literal>TranslateProperties()</literal>,
but it will add a subcomponent to <literal>AnObject</literal>, which remembers all untranslated
strings. This makes it possible to retranslate <literal>AnObject</literal> to another language
later.</para>

<para>The second time that <literal>TranslateComponent()</literal> 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.</para>
</sect1>

<sect1 id="api-tp-createretranslator">
<title>function TP_CreateRetranslator:TExecutable;</title>
<para>This function is only for internal use. Don't use it yourself.</para>
</sect1>

<sect1 id="api-tp-ignore">
<title>procedure TP_Ignore(AnObject:TObject; const name:string);</title>
<para>This procedure only affects the next execution of <literal>TranslateProperties()</literal>
or <literal>TranslateComponent()</literal>. 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:</para>

<screen> 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</screen>

<para>Since this procedure only has effect on the upcoming translation, it should always be
used just before <literal>TranslateProperties()</literal>
or <literal>TranslateComponent()</literal>. Example:</para>

<programlisting>procedure TFormMain.FormCreate(Sender: TObject);
begin
  TP_Ignore (self, 'Listbox1.Items');
  TranslateComponent (self);
end;</programlisting>

<para>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.</para>
</sect1>

<sect1 id="api-tp-globalignoreclass">
<title>procedure TP_GlobalIgnoreClass (IgnClass:TClass);</title>
<para>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
<literal>TranslateProperties()</literal> or <literal>TranslateComponent()</literal>.
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:</para>

<programlisting>TP_GlobalIgnoreClass (TFont);</programlisting>

<para>A good place to put this line is in the .dpr file. See <xref linkend="helloworld-dpr" /> for
further information.</para>
</sect1>

<sect1 id="api-tp-globalignoreclassproperty">
<title>procedure TP_GlobalIgnoreClassProperty (IgnClass:TClass;propertyname:string);</title>
<para>This procedure puts one property of a class on a global ignore list,
so that this property won't be translated by
<literal>TranslateProperties()</literal> or <literal>TranslateComponent()</literal>
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:</para>

<programlisting>TP_GlobalIgnoreClassProperty (TField,'Fieldname');</programlisting>

<para>A good place to put this line is in the .dpr file. See <xref linkend="helloworld-dpr" /> for
further information.</para>
</sect1>

<sect1 id="api-tp-globalhandleclass">
<title>procedure TP_GlobalHandleClass (HClass:TClass;Handler:TTranslator);</title>

<para>This procedure makes it possible implement another translation handling of a class
than what you can control with
<literal>TranslateProperties()</literal>, <literal>TranslateComponent()</literal>
and the TP_* procedures. For instance, if you create a new class that does not use
the <literal>published</literal> keyword, its properties cannot be iterated by
<literal>TranslateProperties()</literal> and <literal>TranslateComponent()</literal>.
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.</para>

<para>Basically, if you cannot translate a component using
<literal>TranslateComponent</literal>, but you can write a procedure yourself
that can, then use <literal>TP_GlobalHandleClass</literal> to make
<literal>TranslateComponent</literal> use your procedure.</para>
</sect1>

</appendix>











<appendix id="helloworldsource">
<title>"Hello, World" source code</title>

<sect1 id="helloworld-dpr">
<title>Sample.dpr</title>
<programlisting>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.</programlisting>
</sect1>

<sect1 id="helloworld-gginitializer">
<title>gginitializer.pas</title>
<programlisting>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.</programlisting>
</sect1>

<sect1 id="helloworld-sampleform-pas">
<title>SampleForm.pas</title>
<programlisting>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.</programlisting>
</sect1>

<sect1 id="helloworld-sampleform-dfm">
<title>SampleForm.dfm</title>
<programlisting>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</programlisting>
</sect1>
</appendix>





<appendix id="dxgettextcommandlinereference">
<title>Dxgettext command-line tools reference</title>

<sect1 id="assemble">
<title>assemble</title>
<para>This tool embeds your translations in your executable. When your programs work, including
translations, type this:</para>

<screen>assemble --dxgettext applicationfilename.exe</screen>

<para>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.</para>
</sect1>

<sect1 id="dfntopo">
<title>dfntopo</title>
<para>This tool converts your Delphi ITE translations to PO files. This is the help
screen that appears when you run the program without parameters:</para>
<screen>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&lt;POFile&gt;  : 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&lt;DFNPath&gt; : full path to the dfn files. Do NOT include a filemask (defaults to current dir).
            NOTE: Paths with spaces must be enclosed in quotes.</screen>

<para>See <xref linkend="convertfromdelphiite" /> for further information.</para>
</sect1>

<sect1 id="dxgettext">
<title>dxgettext</title>

<para>This will extract all texts from the specified files and save them in
a file named default.po:</para>

<screen>dxgettext usage:
dxgettext *.pas *.dfm *.dpr -r
dxgettext -b c:\source\myprogram --delphi</screen>

<para>The following file formats are supported:</para>

<itemizedlist>
<listitem><para>ObjectPascal source code: *.pas, *.inc and *.dpr.</para></listitem>
<listitem><para>DFM/XFM files: *.dfm and *.xfm.</para></listitem>
<listitem><para>C/C++ source code: *.c and *.cpp. These are extracted using the xgettext command line tool
(see <xref linkend="xgettext" />).</para></listitem>
<listitem><para>RC files: *.rc.</para></listitem>
<listitem><para>Executables: *.exe, *.dll and *.bpl. (only on Windows)</para></listitem>
</itemizedlist>

<para>This is the help text that appears when you run the program without parameters:</para>

<screen>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.</screen>
</sect1>

<sect1 id="dxgreg">
<title>dxgreg</title>
<para>This Windows only tool can do three different things:</para>

<itemizedlist>
<listitem><para>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.</para></listitem>
<listitem><para>If you start it with the <literal>--reset-user</literal> 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.</para></listitem>
<listitem><para>If you start it with the <literal>--uninstall</literal> parameter, it will delete
all shell integration features that make use of executable files in the directory where
<filename>dxgreg.exe</filename> was started. This is usually done when uninstalling this system.</para></listitem>
</itemizedlist>
</sect1>

<sect1 id="ixtopo">
<title>ixtopo</title>
<para>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:</para>

<screen>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 &lt;xmlfile&gt; &lt;pofile&gt; &lt;locale&gt; [-f]

where:

&lt;xmlfile&gt;    is the xml file to read from (REQUIRED)
&lt;pofile&gt;     is the po file to write to (REQUIRED)
&lt;locale&gt;     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</screen>

<para>See <xref linkend="convertfromdelphiite" /> for further information.</para>
</sect1>

<sect1 id="msgimport">
<title>msgimport</title>

<para>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:</para>

<itemizedlist>
<listitem><para>Be an utf-8 encoded text file</para></listitem>
<listitem><para>It may not contain any header lines etc.</para></listitem>
<listitem><para>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.</para></listitem>
</itemizedlist>

<para>If you have something in tabular form that you want to convert to a po file, do it like this:</para>

<itemizedlist>
<listitem><para>Load the tabulated data into a spreadsheet. If you don't have a spreadsheet on your computer,
get it at <ulink url="http://www.openoffice.org/">openoffice.org</ulink>.</para></listitem>
<listitem><para>Delete all columns except the two that contain msgid and msgstr. Make sure the first column is msgstr.</para></listitem>
<listitem><para>Delete all rows that do not contains data to be converted.</para></listitem>
<listitem><para>Save the file as a text-file, tabulator separated with no text delimiters.</para></listitem>
<listitem><para>Load the text file into an utf-8 capable text editor like <ulink url="http://unired.sf.net/">Unired</ulink>.</para></listitem>
<listitem><para>Save the text file as an utf-8 text file with "no BOM".</para></listitem>
<listitem><para>Run <screen>msgimport textfile.txt -o output.po</screen> and you got a valid .po file.</para></listitem>
</itemizedlist>
</sect1>

<sect1 id="msgmergepot">
<title>msgmergePOT</title>
<para>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.</para>

<para>This is the help screen that appears when you run the tool with no parameters:</para>
<screen>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.</screen>
</sect1>

<sect1 id="msgmkignore">
<title>msgmkignore</title>
<para>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.</para>

<para>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.</para>

<para>This is the help screen that appears when you run the program with no parameters:</para>

<screen>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</screen>
</sect1>

<sect1 id="msgremove">
<title>msgremove</title>
<para>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:</para>

<screen>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.</screen>
</sect1>

<sect1 id="msgshowheader">
<title>msgshowheader</title>
<para>This tool takes an MO file as parameter and simply outputs the header
to standard out. This is the text that appears when you run the program
without any parameters:</para>

<screen>msgshowheader 1.1.1

msgshowheader usage:
  msgshowheader translation.mo

This will output the header of the translation to stdout, i.e. _('').</screen>
</sect1>

<sect1 id="msgsplittstrings">
<title>msgsplitTStrings</title>
<para>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.</para>
</sect1>

<sect1 id="msgstripcr">
<title>msgstripCR</title>
<para>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.</para>
</sect1>
</appendix>
                                                                                                          



<appendix id="gnucommandlinereference">
<title>GNU Command-line tools reference</title>

<sect1 id="msgattrib">
<title>msgattrib</title>
<para>Changes the fuzzy/obsolete/translated status of messages in a PO file.</para>

<screen>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 &lt;bug-gnu-gettext@gnu.org&gt;

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.</screen>
</sect1>

<sect1 id="msgcat">
<title>msgcat</title>
<para>This tool merges several PO files. It can also sort the files and
change the output format.</para>

<screen>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:
       -&lt;, --less-than=NUMBER
	      print messages with less than this many definitions, defaults to
	      infinite if not set

       -&gt;, --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 &lt;bug-gnu-gettext@gnu.org&gt;.

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.</screen>
</sect1>

<sect1 id="msgcmp">
<title>msgcmp</title>
<para>This compares two PO files, and is often used to check that everything
has been translated.</para>
<screen>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 &lt;bug-gnu-gettext@gnu.org&gt;.

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.</screen>
</sect1>

<sect1 id="msgcomm">
<title>msgcomm</title>
<para>This tool lists all messages that are common for two or more PO files.</para>
<screen>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:
       -&lt;, --less-than=NUMBER
	      print messages with less than this many definitions, defaults to
	      infinite if not set

       -&gt;, --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 &lt;bug-gnu-gettext@gnu.org&gt;.

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.</screen>
</sect1>

<sect1 id="msgen">
<title>msgen</title>
<para>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.</para>
<screen>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 &lt;bug-gnu-gettext@gnu.org&gt;.

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.</screen>
</sect1>

<sect1 id="msgexec">
<title>msgexec</title>
<para>Runs an external command line tool once for each message in a PO file.</para>
<screen>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 &lt;bug-gnu-gettext@gnu.org&gt;.

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.</screen>
</sect1>

<sect1 id="msgfilter">
<title>msgfilter</title>
<para>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.</para>
<screen>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 &lt;bug-gnu-gettext@gnu.org&gt;.

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.</screen>
</sect1>

<sect1 id="msgfmt">
<title>msgfmt</title>
<para>Compiles a PO file to an MO file.</para>
<screen>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 &lt;bug-gnu-gettext@gnu.org&gt;.

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.</screen>
</sect1>

<sect1 id="msggrep">
<title>msggrep</title>
<para>Extracts all messages of a translation catalog that match a given
pattern
or belong to some given source files.</para>
<screen>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 &lt;bug-gnu-gettext@gnu.org&gt;.

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.</screen>
</sect1>

<sect1 id="msghack">
<title>msghack</title>
<para>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.</para>
<para>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 <literal>msghack --help</literal>:</para>
<screen>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.</screen>
</sect1>

<sect1 id="msginit">
<title>msginit</title>
<screen>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 &lt;bug-gnu-gettext@gnu.org&gt;.

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.</screen>
</sect1>

<sect1 id="msgmerge">
<title>msgmerge</title>
<para>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.</para>
<screen>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 &lt;bug-gnu-gettext@gnu.org&gt;.

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.</screen>
</sect1>

<sect1 id="msgunfmt">
<title>msgunfmt</title>
<para>This tool decompiles an MO file to a PO file.</para>
<screen>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 &lt;bug-gnu-gettext@gnu.org&gt;.

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.</screen>
</sect1>

<sect1 id="msguniq">
<title>msguniq</title>
<para>In case you did something to your PO files, that makes them contain
duplicate messages, you can use msguniq to unify those duplicates.</para>
<screen>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 &lt;bug-gnu-gettext@gnu.org&gt;.

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.</screen>
</sect1>

<sect1 id="xgettext">
<title>xgettext</title>
<para>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++.</para>
<screen>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 &lt;bug-gnu-gettext@gnu.org&gt;.

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.</screen>
</sect1>

</appendix>






<appendix id="guitoolreference">
<title>GUI tools reference</title>

<sect1 id="gui-po">
<title>PO files</title>

<para>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 <ulink url="http://poedit.sf.net/">poEdit</ulink>, you
can also just open the po file.</para>
</sect1>

<sect1 id="gui-mo">
<title>MO files</title>

<para>In Windows Explorer, you can click with your right mouse button on po files
and choose "Decompile to po file".</para>
</sect1>

<sect1 id="gui-exe">
<title>Executables (DLL, EXE files)</title>

<para>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.</para>
</sect1>

<sect1 id="gui-folder">
<title>File folders</title>

<para>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.</para>
</sect1>
</appendix>








<appendix id="standards">
<title>Standards</title>

<sect1 id="iso639">
<title>ISO 639 language codes</title>

<screen>
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
</screen>
</sect1>





<sect1 id="iso3166">
<title>ISO 3166 country codes</title>
<segmentedlist>
<segtitle>Country</segtitle>
<segtitle>Code</segtitle>
<seglistitem>
<seg>AFGHANISTAN</seg>
<seg>AF</seg>
</seglistitem>
<seglistitem>
<seg>ALBANIA</seg>
<seg>AL</seg>
</seglistitem>
<seglistitem>
<seg>ALGERIA</seg>
<seg>DZ</seg>
</seglistitem>
<seglistitem>
<seg>AMERICAN SAMOA</seg>
<seg>AS</seg>
</seglistitem>
<seglistitem>
<seg>ANDORRA</seg>
<seg>AD</seg>
</seglistitem>
<seglistitem>
<seg>ANGOLA</seg>
<seg>AO</seg>
</seglistitem>
<seglistitem>
<seg>ANGUILLA</seg>
<seg>AI</seg>
</seglistitem>
<seglistitem>
<seg>ANTARCTICA</seg>
<seg>AQ</seg>
</seglistitem>
<seglistitem>
<seg>ANTIGUA AND BARBUDA</seg>
<seg>AG</seg>
</seglistitem>
<seglistitem>
<seg>ARGENTINA</seg>
<seg>AR</seg>
</seglistitem>
<seglistitem>
<seg>ARMENIA</seg>
<seg>AM</seg>
</seglistitem>
<seglistitem>
<seg>ARUBA</seg>
<seg>AW</seg>
</seglistitem>
<seglistitem>
<seg>AUSTRALIA</seg>
<seg>AU</seg>
</seglistitem>
<seglistitem>
<seg>AUSTRIA</seg>
<seg>AT</seg>
</seglistitem>
<seglistitem>
<seg>AZERBAIJAN</seg>
<seg>AZ</seg>
</seglistitem>
<seglistitem>
<seg>BAHAMAS</seg>
<seg>BS</seg>
</seglistitem>
<seglistitem>
<seg>BAHRAIN</seg>
<seg>BH</seg>
</seglistitem>
<seglistitem>
<seg>BANGLADESH</seg>
<seg>BD</seg>
</seglistitem>
<seglistitem>
<seg>BARBADOS</seg>
<seg>BB</seg>
</seglistitem>
<seglistitem>
<seg>BELARUS</seg>
<seg>BY</seg>
</seglistitem>
<seglistitem>
<seg>BELGIUM</seg>
<seg>BE</seg>
</seglistitem>
<seglistitem>
<seg>BELIZE</seg>
<seg>BZ</seg>
</seglistitem>
<seglistitem>
<seg>BENIN</seg>
<seg>BJ</seg>
</seglistitem>
<seglistitem>
<seg>BERMUDA</seg>
<seg>BM</seg>
</seglistitem>
<seglistitem>
<seg>BHUTAN</seg>
<seg>BT</seg>
</seglistitem>
<seglistitem>
<seg>BOLIVIA</seg>
<seg>BO</seg>
</seglistitem>
<seglistitem>
<seg>BOSNIA AND HERZEGOVINA</seg>
<seg>BA</seg>
</seglistitem>
<seglistitem>
<seg>BOTSWANA</seg>
<seg>BW</seg>
</seglistitem>
<seglistitem>
<seg>BOUVET ISLAND</seg>
<seg>BV</seg>
</seglistitem>
<seglistitem>
<seg>BRAZIL</seg>
<seg>BR</seg>
</seglistitem>
<seglistitem>
<seg>BRITISH INDIAN OCEAN TERRITORY</seg>
<seg>IO</seg>
</seglistitem>
<seglistitem>
<seg>BRUNEI DARUSSALAM</seg>
<seg>BN</seg>
</seglistitem>
<seglistitem>
<seg>BULGARIA</seg>
<seg>BG</seg>
</seglistitem>
<seglistitem>
<seg>BURKINA FASO</seg>
<seg>BF</seg>
</seglistitem>
<seglistitem>
<seg>BURUNDI</seg>
<seg>BI</seg>
</seglistitem>
<seglistitem>
<seg>CAMBODIA</seg>
<seg>KH</seg>
</seglistitem>
<seglistitem>
<seg>CAMEROON</seg>
<seg>CM</seg>
</seglistitem>
<seglistitem>
<seg>CANADA</seg>
<seg>CA</seg>
</seglistitem>
<seglistitem>
<seg>CAPE VERDE</seg>
<seg>CV</seg>
</seglistitem>
<seglistitem>
<seg>CAYMAN ISLANDS</seg>
<seg>KY</seg>
</seglistitem>
<seglistitem>
<seg>CENTRAL AFRICAN REPUBLIC</seg>
<seg>CF</seg>
</seglistitem>
<seglistitem>
<seg>CHAD</seg>
<seg>TD</seg>
</seglistitem>
<seglistitem>
<seg>CHILE</seg>
<seg>CL</seg>
</seglistitem>
<seglistitem>
<seg>CHINA</seg>
<seg>CN</seg>
</seglistitem>
<seglistitem>
<seg>CHRISTMAS ISLAND</seg>
<seg>CX</seg>
</seglistitem>
<seglistitem>
<seg>COCOS (KEELING) ISLANDS</seg>
<seg>CC</seg>
</seglistitem>
<seglistitem>
<seg>COLOMBIA</seg>
<seg>CO</seg>
</seglistitem>
<seglistitem>
<seg>COMOROS</seg>
<seg>KM</seg>
</seglistitem>
<seglistitem>
<seg>CONGO</seg>
<seg>CG</seg>
</seglistitem>
<seglistitem>
<seg>CONGO, THE DEMOCRATIC REPUBLIC OF THE</seg>
<seg>CD</seg>
</seglistitem>
<seglistitem>
<seg>COOK ISLANDS</seg>
<seg>CK</seg>
</seglistitem>
<seglistitem>
<seg>COSTA RICA</seg>
<seg>CR</seg>
</seglistitem>
<seglistitem>
<seg>COTE D'IVOIRE</seg>
<seg>CI</seg>
</seglistitem>
<seglistitem>
<seg>CROATIA</seg>
<seg>HR</seg>
</seglistitem>
<seglistitem>
<seg>CUBA</seg>
<seg>CU</seg>
</seglistitem>
<seglistitem>
<seg>CYPRUS</seg>
<seg>CY</seg>
</seglistitem>
<seglistitem>
<seg>CZECH REPUBLIC</seg>
<seg>CZ</seg>
</seglistitem>
<seglistitem>
<seg>DENMARK</seg>
<seg>DK</seg>
</seglistitem>
<seglistitem>
<seg>DJIBOUTI</seg>
<seg>DJ</seg>
</seglistitem>
<seglistitem>
<seg>DOMINICA</seg>
<seg>DM</seg>
</seglistitem>
<seglistitem>
<seg>DOMINICAN REPUBLIC</seg>
<seg>DO</seg>
</seglistitem>
<seglistitem>
<seg>ECUADOR</seg>
<seg>EC</seg>
</seglistitem>
<seglistitem>
<seg>EGYPT</seg>
<seg>EG</seg>
</seglistitem>
<seglistitem>
<seg>EL SALVADOR</seg>
<seg>SV</seg>
</seglistitem>
<seglistitem>
<seg>EQUATORIAL GUINEA</seg>
<seg>GQ</seg>
</seglistitem>
<seglistitem>
<seg>ERITREA</seg>
<seg>ER</seg>
</seglistitem>
<seglistitem>
<seg>ESTONIA</seg>
<seg>EE</seg>
</seglistitem>
<seglistitem>
<seg>ETHIOPIA</seg>
<seg>ET</seg>
</seglistitem>
<seglistitem>
<seg>FALKLAND ISLANDS (MALVINAS)</seg>
<seg>FK</seg>
</seglistitem>
<seglistitem>
<seg>FAROE ISLANDS</seg>
<seg>FO</seg>
</seglistitem>
<seglistitem>
<seg>FIJI</seg>
<seg>FJ</seg>
</seglistitem>
<seglistitem>
<seg>FINLAND</seg>
<seg>FI</seg>
</seglistitem>
<seglistitem>
<seg>FRANCE</seg>
<seg>FR</seg>
</seglistitem>
<seglistitem>
<seg>FRENCH GUIANA</seg>
<seg>GF</seg>
</seglistitem>
<seglistitem>
<seg>FRENCH POLYNESIA</seg>
<seg>PF</seg>
</seglistitem>
<seglistitem>
<seg>FRENCH SOUTHERN TERRITORIES</seg>
<seg>TF</seg>
</seglistitem>
<seglistitem>
<seg>GABON</seg>
<seg>GA</seg>
</seglistitem>
<seglistitem>
<seg>GAMBIA</seg>
<seg>GM</seg>
</seglistitem>
<seglistitem>
<seg>GEORGIA</seg>
<seg>GE</seg>
</seglistitem>
<seglistitem>
<seg>GERMANY</seg>
<seg>DE</seg>
</seglistitem>
<seglistitem>
<seg>GHANA</seg>
<seg>GH</seg>
</seglistitem>
<seglistitem>
<seg>GIBRALTAR</seg>
<seg>GI</seg>
</seglistitem>
<seglistitem>
<seg>GREECE</seg>
<seg>GR</seg>
</seglistitem>
<seglistitem>
<seg>GREENLAND</seg>
<seg>GL</seg>
</seglistitem>
<seglistitem>
<seg>GRENADA</seg>
<seg>GD</seg>
</seglistitem>
<seglistitem>
<seg>GUADELOUPE</seg>
<seg>GP</seg>
</seglistitem>
<seglistitem>
<seg>GUAM</seg>
<seg>GU</seg>
</seglistitem>
<seglistitem>
<seg>GUATEMALA</seg>
<seg>GT</seg>
</seglistitem>
<seglistitem>
<seg>GUINEA</seg>
<seg>GN</seg>
</seglistitem>
<seglistitem>
<seg>GUINEA-BISSAU</seg>
<seg>GW</seg>
</seglistitem>
<seglistitem>
<seg>GUYANA</seg>
<seg>GY</seg>
</seglistitem>
<seglistitem>
<seg>HAITI</seg>
<seg>HT</seg>
</seglistitem>
<seglistitem>
<seg>HEARD ISLAND AND MCDONALD ISLANDS</seg>
<seg>HM</seg>
</seglistitem>
<seglistitem>
<seg>HOLY SEE (VATICAN CITY STATE)</seg>
<seg>VA</seg>
</seglistitem>
<seglistitem>
<seg>HONDURAS</seg>
<seg>HN</seg>
</seglistitem>
<seglistitem>
<seg>HONG KONG</seg>
<seg>HK</seg>
</seglistitem>
<seglistitem>
<seg>HUNGARY</seg>
<seg>HU</seg>
</seglistitem>
<seglistitem>
<seg>ICELAND</seg>
<seg>IS</seg>
</seglistitem>
<seglistitem>
<seg>INDIA</seg>
<seg>IN</seg>
</seglistitem>
<seglistitem>
<seg>INDONESIA</seg>
<seg>ID</seg>
</seglistitem>
<seglistitem>
<seg>IRAN, ISLAMIC REPUBLIC OF</seg>
<seg>IR</seg>
</seglistitem>
<seglistitem>
<seg>IRAQ</seg>
<seg>IQ</seg>
</seglistitem>
<seglistitem>
<seg>IRELAND</seg>
<seg>IE</seg>
</seglistitem>
<seglistitem>
<seg>ISRAEL</seg>
<seg>IL</seg>
</seglistitem>
<seglistitem>
<seg>ITALY</seg>
<seg>IT</seg>
</seglistitem>
<seglistitem>
<seg>JAMAICA</seg>
<seg>JM</seg>
</seglistitem>
<seglistitem>
<seg>JAPAN</seg>
<seg>JP</seg>
</seglistitem>
<seglistitem>
<seg>JORDAN</seg>
<seg>JO</seg>
</seglistitem>
<seglistitem>
<seg>KAZAKHSTAN</seg>
<seg>KZ</seg>
</seglistitem>
<seglistitem>
<seg>KENYA</seg>
<seg>KE</seg>
</seglistitem>
<seglistitem>
<seg>KIRIBATI</seg>
<seg>KI</seg>
</seglistitem>
<seglistitem>
<seg>KOREA, DEMOCRATIC PEOPLE'S REPUBLIC OF</seg>
<seg>KP</seg>
</seglistitem>
<seglistitem>
<seg>KOREA, REPUBLIC OF</seg>
<seg>KR</seg>
</seglistitem>
<seglistitem>
<seg>KUWAIT</seg>
<seg>KW</seg>
</seglistitem>
<seglistitem>
<seg>KYRGYZSTAN</seg>
<seg>KG</seg>
</seglistitem>
<seglistitem>
<seg>LAO PEOPLE'S DEMOCRATIC REPUBLIC</seg>
<seg>LA</seg>
</seglistitem>
<seglistitem>
<seg>LATVIA</seg>
<seg>LV</seg>
</seglistitem>
<seglistitem>
<seg>LEBANON</seg>
<seg>LB</seg>
</seglistitem>
<seglistitem>
<seg>LESOTHO</seg>
<seg>LS</seg>
</seglistitem>
<seglistitem>
<seg>LIBERIA</seg>
<seg>LR</seg>
</seglistitem>
<seglistitem>
<seg>LIBYAN ARAB JAMAHIRIYA</seg>
<seg>LY</seg>
</seglistitem>
<seglistitem>
<seg>LIECHTENSTEIN</seg>
<seg>LI</seg>
</seglistitem>
<seglistitem>
<seg>LITHUANIA</seg>
<seg>LT</seg>
</seglistitem>
<seglistitem>
<seg>LUXEMBOURG</seg>
<seg>LU</seg>
</seglistitem>
<seglistitem>
<seg>MACAO</seg>
<seg>MO</seg>
</seglistitem>
<seglistitem>
<seg>MACEDONIA, THE FORMER YUGOSLAV REPUBLIC OF</seg>
<seg>MK</seg>
</seglistitem>
<seglistitem>
<seg>MADAGASCAR</seg>
<seg>MG</seg>
</seglistitem>
<seglistitem>
<seg>MALAWI</seg>
<seg>MW</seg>
</seglistitem>
<seglistitem>
<seg>MALAYSIA</seg>
<seg>MY</seg>
</seglistitem>
<seglistitem>
<seg>MALDIVES</seg>
<seg>MV</seg>
</seglistitem>
<seglistitem>
<seg>MALI</seg>
<seg>ML</seg>
</seglistitem>
<seglistitem>
<seg>MALTA</seg>
<seg>MT</seg>
</seglistitem>
<seglistitem>
<seg>MARSHALL ISLANDS</seg>
<seg>MH</seg>
</seglistitem>
<seglistitem>
<seg>MARTINIQUE</seg>
<seg>MQ</seg>
</seglistitem>
<seglistitem>
<seg>MAURITANIA</seg>
<seg>MR</seg>
</seglistitem>
<seglistitem>
<seg>MAURITIUS</seg>
<seg>MU</seg>
</seglistitem>
<seglistitem>
<seg>MAYOTTE</seg>
<seg>YT</seg>
</seglistitem>
<seglistitem>
<seg>MEXICO</seg>
<seg>MX</seg>
</seglistitem>
<seglistitem>
<seg>MICRONESIA, FEDERATED STATES OF</seg>
<seg>FM</seg>
</seglistitem>
<seglistitem>
<seg>MOLDOVA, REPUBLIC OF</seg>
<seg>MD</seg>
</seglistitem>
<seglistitem>
<seg>MONACO</seg>
<seg>MC</seg>
</seglistitem>
<seglistitem>
<seg>MONGOLIA</seg>
<seg>MN</seg>
</seglistitem>
<seglistitem>
<seg>MONTSERRAT</seg>
<seg>MS</seg>
</seglistitem>
<seglistitem>
<seg>MOROCCO</seg>
<seg>MA</seg>
</seglistitem>
<seglistitem>
<seg>MOZAMBIQUE</seg>
<seg>MZ</seg>
</seglistitem>
<seglistitem>
<seg>MYANMAR</seg>
<seg>MM</seg>
</seglistitem>
<seglistitem>
<seg>NAMIBIA</seg>
<seg>NA</seg>
</seglistitem>
<seglistitem>
<seg>NAURU</seg>
<seg>NR</seg>
</seglistitem>
<seglistitem>
<seg>NEPAL</seg>
<seg>NP</seg>
</seglistitem>
<seglistitem>
<seg>NETHERLANDS</seg>
<seg>NL</seg>
</seglistitem>
<seglistitem>
<seg>NETHERLANDS ANTILLES</seg>
<seg>AN</seg>
</seglistitem>
<seglistitem>
<seg>NEW CALEDONIA</seg>
<seg>NC</seg>
</seglistitem>
<seglistitem>
<seg>NEW ZEALAND</seg>
<seg>NZ</seg>
</seglistitem>
<seglistitem>
<seg>NICARAGUA</seg>
<seg>NI</seg>
</seglistitem>
<seglistitem>
<seg>NIGER</seg>
<seg>NE</seg>
</seglistitem>
<seglistitem>
<seg>NIGERIA</seg>
<seg>NG</seg>
</seglistitem>
<seglistitem>
<seg>NIUE</seg>
<seg>NU</seg>
</seglistitem>
<seglistitem>
<seg>NORFOLK ISLAND</seg>
<seg>NF</seg>
</seglistitem>
<seglistitem>
<seg>NORTHERN MARIANA ISLANDS</seg>
<seg>MP</seg>
</seglistitem>
<seglistitem>
<seg>NORWAY</seg>
<seg>NO</seg>
</seglistitem>
<seglistitem>
<seg>OMAN</seg>
<seg>OM</seg>
</seglistitem>
<seglistitem>
<seg>PAKISTAN</seg>
<seg>PK</seg>
</seglistitem>
<seglistitem>
<seg>PALAU</seg>
<seg>PW</seg>
</seglistitem>
<seglistitem>
<seg>PALESTINIAN TERRITORY, OCCUPIED</seg>
<seg>PS</seg>
</seglistitem>
<seglistitem>
<seg>PANAMA</seg>
<seg>PA</seg>
</seglistitem>
<seglistitem>
<seg>PAPUA NEW GUINEA</seg>
<seg>PG</seg>
</seglistitem>
<seglistitem>
<seg>PARAGUAY</seg>
<seg>PY</seg>
</seglistitem>
<seglistitem>
<seg>PERU</seg>
<seg>PE</seg>
</seglistitem>
<seglistitem>
<seg>PHILIPPINES</seg>
<seg>PH</seg>
</seglistitem>
<seglistitem>
<seg>PITCAIRN</seg>
<seg>PN</seg>
</seglistitem>
<seglistitem>
<seg>POLAND</seg>
<seg>PL</seg>
</seglistitem>
<seglistitem>
<seg>PORTUGAL</seg>
<seg>PT</seg>
</seglistitem>
<seglistitem>
<seg>PUERTO RICO</seg>
<seg>PR</seg>
</seglistitem>
<seglistitem>
<seg>QATAR</seg>
<seg>QA</seg>
</seglistitem>
<seglistitem>
<seg>REUNION</seg>
<seg>RE</seg>
</seglistitem>
<seglistitem>
<seg>ROMANIA</seg>
<seg>RO</seg>
</seglistitem>
<seglistitem>
<seg>RUSSIAN FEDERATION</seg>
<seg>RU</seg>
</seglistitem>
<seglistitem>
<seg>RWANDA</seg>
<seg>RW</seg>
</seglistitem>
<seglistitem>
<seg>SAINT HELENA</seg>
<seg>SH</seg>
</seglistitem>
<seglistitem>
<seg>SAINT KITTS AND NEVIS</seg>
<seg>KN</seg>
</seglistitem>
<seglistitem>
<seg>SAINT LUCIA</seg>
<seg>LC</seg>
</seglistitem>
<seglistitem>
<seg>SAINT PIERRE AND MIQUELON</seg>
<seg>PM</seg>
</seglistitem>
<seglistitem>
<seg>SAINT VINCENT AND THE GRENADINES</seg>
<seg>VC</seg>
</seglistitem>
<seglistitem>
<seg>SAMOA</seg>
<seg>WS</seg>
</seglistitem>
<seglistitem>
<seg>SAN MARINO</seg>
<seg>SM</seg>
</seglistitem>
<seglistitem>
<seg>SAO TOME AND PRINCIPE</seg>
<seg>ST</seg>
</seglistitem>
<seglistitem>
<seg>SAUDI ARABIA</seg>
<seg>SA</seg>
</seglistitem>
<seglistitem>
<seg>SENEGAL</seg>
<seg>SN</seg>
</seglistitem>
<seglistitem>
<seg>SERBIA AND MONTENEGRO</seg>
<seg>CS</seg>
</seglistitem>
<seglistitem>
<seg>SEYCHELLES</seg>
<seg>SC</seg>
</seglistitem>
<seglistitem>
<seg>SIERRA LEONE</seg>
<seg>SL</seg>
</seglistitem>
<seglistitem>
<seg>SINGAPORE</seg>
<seg>SG</seg>
</seglistitem>
<seglistitem>
<seg>SLOVAKIA</seg>
<seg>SK</seg>
</seglistitem>
<seglistitem>
<seg>SLOVENIA</seg>
<seg>SI</seg>
</seglistitem>
<seglistitem>
<seg>SOLOMON ISLANDS</seg>
<seg>SB</seg>
</seglistitem>
<seglistitem>
<seg>SOMALIA</seg>
<seg>SO</seg>
</seglistitem>
<seglistitem>
<seg>SOUTH AFRICA</seg>
<seg>ZA</seg>
</seglistitem>
<seglistitem>
<seg>SOUTH GEORGIA AND THE SOUTH SANDWICH ISLANDS</seg>
<seg>GS</seg>
</seglistitem>
<seglistitem>
<seg>SPAIN</seg>
<seg>ES</seg>
</seglistitem>
<seglistitem>
<seg>SRI LANKA</seg>
<seg>LK</seg>
</seglistitem>
<seglistitem>
<seg>SUDAN</seg>
<seg>SD</seg>
</seglistitem>
<seglistitem>
<seg>SURINAME</seg>
<seg>SR</seg>
</seglistitem>
<seglistitem>
<seg>SVALBARD AND JAN MAYEN</seg>
<seg>SJ</seg>
</seglistitem>
<seglistitem>
<seg>SWAZILAND</seg>
<seg>SZ</seg>
</seglistitem>
<seglistitem>
<seg>SWEDEN</seg>
<seg>SE</seg>
</seglistitem>
<seglistitem>
<seg>SWITZERLAND</seg>
<seg>CH</seg>
</seglistitem>
<seglistitem>
<seg>SYRIAN ARAB REPUBLIC</seg>
<seg>SY</seg>
</seglistitem>
<seglistitem>
<seg>TAIWAN, PROVINCE OF CHINA</seg>
<seg>TW</seg>
</seglistitem>
<seglistitem>
<seg>TAJIKISTAN</seg>
<seg>TJ</seg>
</seglistitem>
<seglistitem>
<seg>TANZANIA, UNITED REPUBLIC OF</seg>
<seg>TZ</seg>
</seglistitem>
<seglistitem>
<seg>THAILAND</seg>
<seg>TH</seg>
</seglistitem>
<seglistitem>
<seg>TIMOR-LESTE</seg>
<seg>TL</seg>
</seglistitem>
<seglistitem>
<seg>TOGO</seg>
<seg>TG</seg>
</seglistitem>
<seglistitem>
<seg>TOKELAU</seg>
<seg>TK</seg>
</seglistitem>
<seglistitem>
<seg>TONGA</seg>
<seg>TO</seg>
</seglistitem>
<seglistitem>
<seg>TRINIDAD AND TOBAGO</seg>
<seg>TT</seg>
</seglistitem>
<seglistitem>
<seg>TUNISIA</seg>
<seg>TN</seg>
</seglistitem>
<seglistitem>
<seg>TURKEY</seg>
<seg>TR</seg>
</seglistitem>
<seglistitem>
<seg>TURKMENISTAN</seg>
<seg>TM</seg>
</seglistitem>
<seglistitem>
<seg>TURKS AND CAICOS ISLANDS</seg>
<seg>TC</seg>
</seglistitem>
<seglistitem>
<seg>TUVALU</seg>
<seg>TV</seg>
</seglistitem>
<seglistitem>
<seg>UGANDA</seg>
<seg>UG</seg>
</seglistitem>
<seglistitem>
<seg>UKRAINE</seg>
<seg>UA</seg>
</seglistitem>
<seglistitem>
<seg>UNITED ARAB EMIRATES</seg>
<seg>AE</seg>
</seglistitem>
<seglistitem>
<seg>UNITED KINGDOM</seg>
<seg>GB</seg>
</seglistitem>
<seglistitem>
<seg>UNITED STATES</seg>
<seg>US</seg>
</seglistitem>
<seglistitem>
<seg>UNITED STATES MINOR OUTLYING ISLANDS</seg>
<seg>UM</seg>
</seglistitem>
<seglistitem>
<seg>URUGUAY</seg>
<seg>UY</seg>
</seglistitem>
<seglistitem>
<seg>UZBEKISTAN</seg>
<seg>UZ</seg>
</seglistitem>
<seglistitem>
<seg>VANUATU</seg>
<seg>VU</seg>
</seglistitem>
<seglistitem>
<seg>VENEZUELA</seg>
<seg>VE</seg>
</seglistitem>
<seglistitem>
<seg>VIET NAM</seg>
<seg>VN</seg>
</seglistitem>
<seglistitem>
<seg>VIRGIN ISLANDS, BRITISH</seg>
<seg>VG</seg>
</seglistitem>
<seglistitem>
<seg>VIRGIN ISLANDS, U.S.</seg>
<seg>VI</seg>
</seglistitem>
<seglistitem>
<seg>WALLIS AND FUTUNA</seg>
<seg>WF</seg>
</seglistitem>
<seglistitem>
<seg>WESTERN SAHARA</seg>
<seg>EH</seg>
</seglistitem>
<seglistitem>
<seg>YEMEN</seg>
<seg>YE</seg>
</seglistitem>
<seglistitem>
<seg>ZAMBIA</seg>
<seg>ZM</seg>
</seglistitem>
<seglistitem>
<seg>ZIMBABWE</seg>
<seg>ZW</seg>
</seglistitem>
</segmentedlist>
</sect1>

</appendix>



<appendix id="fileformats">
<title>File formats</title>

<sect1 id="fileformat-po">
<title>The format of GNU PO files</title>
<para>This section was last updated august 2nd, 2003 by copying
the appropriate section from the GNU gettext manual.</para>

<para>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:</para>

<screen>white-space
#  translator-comments
#. automatic-comments
#: reference...
#, flag...
msgid "untranslated-string"
msgstr "translated-string"
</screen>

<para>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.</para>

<para>Entries begin with some optional white space.  Usually, when generated
through GNU <literal>gettext</literal> tools, there is exactly one blank line
between entries.  Then comments follow, on lines all starting with the
character <literal>#</literal>.  There are two kinds of comments: those which have
some white space immediately following the <literal>#</literal>, which comments are
created and maintained exclusively by the translator, and those which
have some non-white character just after the <literal>#</literal>, which comments
are created and maintained automatically by GNU <literal>gettext</literal> tools. 
All comments, of either kind, are optional.</para>

<para>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 <literal>msgid</literal>, and the translation,
by <literal>msgstr</literal>.  The two strings, untranslated and translated,
are quoted in various ways in the PO file, using <literal>"</literal>
delimiters and <literal>\</literal> 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.</para>

<para>The <literal>msgid</literal> strings, as well as automatic comments, are produced
and managed by other GNU <literal>gettext</literal> 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 <literal>msgstr</literal> string, as well as translator
comments, are really meant for the translator, and PO mode gives her
the full control she needs.</para>

<para>The comment lines beginning with <literal>#,</literal> 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 <literal>msgfmt</literal>
program to give the user some better diagnostic messages.  Currently
there are two forms of flags defined:</para>

<note>
<title>fuzzy</title>
<para>This flag can be generated by the <literal>msgmerge</literal> program or it can be
inserted by the translator herself.  It shows that the <literal>msgstr</literal>
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 <literal>fuzzy</literal> attribute.  The <literal>msgmerge</literal> program inserts this
when it combined the <literal>msgid</literal> and <literal>msgstr</literal> entries after fuzzy
search only.</para>
</note>

<note>
<title>c-format</title>
<para>These flags should not be added by a human.  Instead only the
<literal>xgettext</literal> 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 <literal>xgettext</literal> program generates a new template file.</para>

<para>In case the <literal>c-format</literal> flag is given for a string the <literal>msgfmt</literal>
does some more tests to check to validity of the translation.</para>
</note>

<para>A different kind of entries is used for translations which involve
plural forms.</para>

<screen>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"</screen>

<para>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 <literal>M-x po-normalize</literal>, the translator may get
rid of those spurious lines. </para>

<para>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.</para>

<para>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:</para>

<screen>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"
</screen>

<para>In this example, the empty string is used on the first line, to
allow better alignment of the <literal>H</literal> from the word <literal>Here</literal>
over the <literal>f</literal> from the word <literal>for</literal>.  In this example, the
<literal>msgid</literal> 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 <literal>msgid</literal> 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 <literal>Here</literal> was
promoted on the first line, right after <literal>msgid</literal>. It was not really necessary
either to switch between the two last quoted strings immediately after
the newline <literal>\n</literal>, the switch could have occurred after any
other character, we just did it this way because it is neater.</para>

<para>One should carefully distinguish between end of lines marked as
<literal>\n</literal> 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.</para>

<para>Outside strings, white lines and comments may be used freely. 
Comments start at the beginning of a line with <literal>#</literal> and extend
until the end of the PO file line.  Comments written by translators
should have the initial <literal>#</literal> immediately followed by some white
space.  If the <literal>#</literal> 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 <literal>msgmerge</literal>.</para>

</sect1>





<sect1 id="fileformat-mo">
<title>The format of GNU MO files</title>

<para>This section was last updated august 2nd, 2003 by copying
the appropriate section from the GNU gettext manual.</para>

<para>The format of the generated MO files is best described by a picture,
which appears below.</para>

<para>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: <keycode>0x950412de</keycode> and <keycode>0xde120495</keycode>.  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 <filename>/etc/magic</filename> is
not updated often.  It might be better to have magic separated from
internal format version identification.</para>

<para>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.</para>

<para>Then, at offset <literal>O</literal> and offset <literal>T</literal> 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.</para>

<para>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 <keycode>gettext</keycode> 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.</para>

<para>The size <literal>S</literal> 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 <literal>gettext</literal> code, and is not documented here.</para>

<para>As for the strings themselves, they follow the hash file, and each
is terminated with a <literal>NUL</literal>, and this <literal>NUL</literal> is not counted in
the length which appears in the string descriptor.  The <keycode>msgfmt</keycode></para>

<para>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.</para>

<para>Plural forms are stored by letting the plural of the original string
follow the singular of the original string, separated through a
<literal>NUL</literal> 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
<literal>NUL</literal> byte. Here also, the length in the string descriptor 
includes all of them. </para>
 
<para>Nothing prevents a MO file from having embedded <literal>NUL</literal>s in strings. 
However, the program interface currently used already presumes 
that strings are <literal>NUL</literal> terminated, so embedded <literal>NUL</literal>s 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 <literal>NUL</literal> 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 <literal>wchar_t</literal>
type being platform dependent, MO files would be 
platform dependent as well.) </para>
 
<para>This particular issue has been strongly debated in the GNU 
<keycode>gettext</keycode> 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.</para>
 
<screen>        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 &#38; offset 0th string  ----------------. 
      O + 8  | length &#38; offset 1st string  ------------------. 
              ...                                    ...   | | 
O + ((N-1)*8)| length &#38; offset (N-1)th string           |  | | 
             |                                          |  | | 
          T  | length &#38; offset 0th translation  ---------------. 
      T + 8  | length &#38; offset 1st translation  -----------------. 
              ...                                    ...   | | | | 
T + ((N-1)*8)| length &#38; offset (N-1)th translation      |  | | | | 
             |                                          |  | | | | 
          H  | start hash table                         |  | | | | 
              ...                                    ...   | | | | 
  H + S * 4  | end hash table                           |  | | | | 
             |                                          |  | | | | 
             | NUL terminated 0th string  &#60;----------------' | | | 
             |                                          |    | | | 
             | NUL terminated 1st string  &#60;------------------' | | 
             |                                          |      | | 
              ...                                    ...       | | 
             |                                          |      | | 
             | NUL terminated 0th translation  &#60;---------------' | 
             |                                          |        | 
             | NUL terminated 1st translation  &#60;-----------------' 
             |                                          | 
              ...                                    ... 
             |                                          | 
             +------------------------------------------+ 
 
</screen>


</sect1>


</appendix>




<appendix id="handlingclasses">
<title>How to handle specific classes</title>

<para>This appendix contains documentation on how to handle various classes that do not translate easily
using <literal>TranslateComponent()</literal> or <literal>TranslateProperties()</literal>. Most classes are
handled easily by just putting an ignore on some of their properties, while other classes need more
advanced handling.</para>

<para>Please note, that the TComponent.Name property is always ignored by default, and
doesn't need to be specified.</para>

<sect1 id="ignore-vcl">
<title>VCL, important ones</title>

<ProgramListing>
TP_GlobalIgnoreClassProperty(TAction,'Category'); 
TP_GlobalIgnoreClassProperty(TControl,'HelpKeyword');
TP_GlobalIgnoreClassProperty(TNotebook,'Pages');
</ProgramListing>
</sect1>

<sect1 id="ignore-vcl-2">
<title>VCL, not so important</title>

<para>These are normally not needed.</para>

<ProgramListing>
TP_GlobalIgnoreClassProperty(TControl,'ImeName');
TP_GlobalIgnoreClass(TFont);
</ProgramListing>
</sect1>

<sect1 id="ignore-db">
<title>Database (DB unit)</title>

<para>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.</para>
<ProgramListing>
  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');
</ProgramListing>
</sect1>

<sect1 id="ignore-midas">
<title>MIDAS/Datasnap</title>

<ProgramListing>
  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');
</ProgramListing>
</sect1>

<sect1 id="ignore-dbctrls">
<title>Database controls</title>

<ProgramListing>
  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');
</ProgramListing>
</sect1>

<sect1 id="ignore-ibx">
<title>Interbase Express (IBX)</title>

<ProgramListing>
  TP_GlobalIgnoreClass(TIBDatabase);
  TP_GlobalIgnoreClass(TIBDatabase);
  TP_GlobalIgnoreClass(TIBTransaction);
  TP_GlobalIgnoreClassProperty(TIBSQL,'UniqueRelationName');
</ProgramListing>
</sect1>

<sect1 id="ignore-bde">
<title>Borland Database Engine (BDE)</title>

<ProgramListing>
  TP_GlobalIgnoreClass(TSession);
  TP_GlobalIgnoreClass(TDatabase);
</ProgramListing>
</sect1>

<sect1 id="ignore-ado">
<title>ADO components</title>

<ProgramListing>
  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');
</ProgramListing>
</sect1>

<sect1 id="ignore-activex">
<title>ActiveX stuff</title>

<ProgramListing>
  TP_GlobalIgnoreClass (TWebBrowser);
</ProgramListing>
</sect1>


<sect1 id="ignore-tporpheus">
<title>Turbopower Orpheus</title>

<programlisting>
  TP_GlobalIgnoreClassProperty(TO32FlexEdit,'About');
  TP_GlobalIgnoreClassProperty(TO32FlexEdit,'Validation');
  TP_GlobalIgnoreClassProperty(TOvcTimeEdit,'About');
  TP_GlobalIgnoreClassProperty(TOvcTimeEdit,'NowString');
</programlisting>
</sect1>

<sect1 id="ignore-tpessentials">
<title>Turbopower Essentials</title>

<programlisting>
  TP_GlobalIgnoreClassProperty(TEsDateEdit,'TodayString');
</programlisting>
</sect1>

<sect1 id="ignore-tadvstringgrid">
<title>TMS Software TAdvStringGrid</title>

<para>The TAdvStringGrid is not compatible with TranslateComponent(). You should therefore put it on ignore:</para>

<ProgramListing>
TP_GlobalIgnoreClass (TAdvStringGrid);
</ProgramListing>

<para>But there is a solution to get it translated. The following text was provided by
Sandro Wendt:</para>

<para>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.</para>

<para>This is the code I used:</para>

<programlisting>
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;
</programlisting>

<para>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.</para>

</sect1>


</appendix>




<appendix id="faq">
<title>Frequently Asked Questions</title>

<qandaset>

<qandadiv id="qaproject">
<title>About this project</title>

<qandaentry><question><para>Is anybody using this?</para></question>

<answer><para>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.</para></answer></qandaentry>


<qandaentry><question><para>Is it productive?</para></question>

<answer><para>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.</para></answer></qandaentry>


<qandaentry><question><para>I use the Delphi Integrated Translation Environment (ITE) - should I switch?</para></question>

<answer><para>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.</para></answer></qandaentry>


<qandaentry><question><para>Why is this project called dxgettext?</para></question> 
 
<answer><para>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.</para></answer></qandaentry> 
 
 
<qandaentry><question><para>What's the catch? (Why is it free?)</para></question> 
 
<answer><para>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:</para>
 
<ItemizedList> 
<listitem><para>Raising money to pay us. At least one guy needs to be fulltime on a commercial project</para></listitem> 
<listitem><para>Marketing - this costs money when doing it commercially. Nobody wants to pay for software they 
weren't told about.</para></listitem> 
<listitem><para>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.</para></listitem> 
<listitem><para>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.</para></listitem> 
 <listitem><para>Release process - commercial software requires you to test your software with all 
possible environments before releasing it. That's a lot of work.</para></listitem> 
<listitem><para>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.</para></listitem> 
<listitem><para>Administration - all the above has to be administrated. When money is involved, somebody has to 
be in charge etc.</para></listitem> 
</ItemizedList>
 
<para>What we do now is much easier:</para>
 
<ItemizedList> 
<listitem><para>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.</para></listitem> 
<listitem><para>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.</para></listitem> 
<listitem><para>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.</para></listitem> 
<listitem><para>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.</para></listitem> 
<listitem><para>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.</para></listitem> 
<listitem><para>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.</para></listitem> 
</ItemizedList> 
 
<para>If you want to know more about why Open Source software works, Eric S. Raymond 
has written a free book about it: <ulink url="http://www.firstmonday.dk/issues/issue3_3/raymond/">The 
Cathedral and the Bazaar</ulink></para>
 
<para>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.</para></answer></qandaentry> 
 
 
<qandaentry><question><para>I have a question that is not on this list</para></question> 
 
<answer><para>Send an empty e-mail to dxgettext-subscribe@yahoogroups.com to join our mailing list, and then write your question to 
dxgettext@yahoogroups.com</para>
 
<para>You can also send an e-mail directly to the maintainer of this FAQ: Lars@dybdahl.dk</para>
 
<para>We're always glad to help you out, and always happy to get feedback from our users.</para></answer></qandaentry>
</qandadiv>




<qandadiv id="qapreconditions">
<title>Considering to use this software</title>

<qandaentry><question><para>My program is not in English. What do I do?</para></question>

<answer><para>Very easy, you do this:</para>

<OrderedList>
<listitem><para>Extract all strings from your program as it is now.</para></listitem>
<listitem><para>Translate your program to English.</para></listitem>
<listitem><para>Extract all strings from your program afterwards.</para></listitem>
<listitem><para>Use the msgmergePOT tool to create an English->YourLanguage translation file.</para></listitem>
</OrderedList>

<para>Now you have an English language program with a translation to the language that your
program used before.</para></answer></qandaentry>

<qandaentry><question><para>Can I use this to translate a German language program to English?</para></question>

<answer><para>Yes, but it will only work on computers that use the same character set, i.e. iso-8859-1.
You can convert your application to English with the method specified in the question above.</para></answer></qandaentry>


<qandaentry><question><para>Does this support Unicode or widestrings?</para></question>

<answer><para>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.</para>

<para>But if you get Unicode components and use the features of GNU gettext for Delphi,
you can create an all-through Unicode program.</para></answer></qandaentry>


<qandaentry><question><para>Does it work without Unicode or widestrings?</para></question>

<answer><para>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:</para>

<ProgramListing> MyButton.Caption:=gettext('New caption');</ProgramListing>

<para>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.</para></answer></qandaentry>

<qandaentry><question><para>My program uses resourcestrings - can gettext handle this?</para></question>

<answer><para>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.</para></answer></qandaentry>


</qandadiv>





<qandadiv id="qanotranslation">
<title>Something is not being translated</title>
<qandaentry><question><para>I want to know exactly why something isn't translated</para></question>

<answer><para>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.</para></answer></qandaentry> 
 
 
<qandaentry><question><para>I use TObject derivatives that are not derived from TComponent, how do I translate them?</para></question> 
 
<answer><para>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.</para></answer></qandaentry> 
 
 
<qandaentry><question><para>My 'This is version '+Version+' of my program' is not extracted</para></question> 
 
<answer><para>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.</para></answer></qandaentry> 
 
 
<qandaentry><question><para>Some Delphi things are not translated. How do I translate these?</para></question> 
 
<answer><para>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.</para>
 
<para>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:</para>
 
<ProgramListing>AddDomainForResourceString ('delphi');</ProgramListing> 
 
<para>Somewhere in your program before you start translating forms etc.</para>
 
<para>If you still need support on this subject, please join our mailing list 
and get instructions there.</para></answer></qandaentry> 
 
<qandaentry><question><para>How do I translate the Delphi runtime library messages?</para></question> 
 
<answer><para>See <ulink url="http://dybdahl.dk/dxgettext/docs/howto-mono.php">this link</ulink> for instructions.</para></answer></qandaentry> 
 
 
<qandaentry><question><para>My menu items are not translated. Why?</para></question> 
 
<answer><para>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.</para></answer></qandaentry> 
 
 
<qandaentry><question><para>I have some 3rd party components without source. How do I translate those?</para></question> 
 
<answer><para>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.</para>
 
<para>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.</para>
 
<para>If your application uses the xyz package and is translated to German (language code "de"), you might 
end up with the following files:</para>
 
<screen>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)</screen></answer></qandaentry> 
 
<qandaentry><question><para>Something in my forms isn't translated. What do I do?</para></question> 
 
<answer><para>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:</para>
 
<ProgramListing>component.property:=gettext('This text wasn''t translated in the first place');</ProgramListing>
</answer></qandaentry> 
</qandadiv>






<qandadiv id="qaconcepts">
<title>Concepts</title>
<qandaentry><question><para>How does gettext handle two different translations of the same English word?</para></question> 
 
<answer><para>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.</para></answer></qandaentry> 
 
 
<qandaentry><question><para>How should I choose text domains?</para></question> 
 
<answer><para>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.</para></answer></qandaentry> 

<qandaentry><question><para>Why are memos extracted line by line?</para></question>
<answer><para>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 <literal>TranslateComponent()</literal>
procedure cannot do anything else than translate the memos line by line.</para>

<para>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.</para>

<para>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:</para>

<programlisting>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.');</programlisting>
    
<para>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.</para>
</answer></qandaentry>
</qandadiv>





<qandadiv id="qausage">
<title>Using it</title>

<qandaentry><question><para>I want to force my program to use another language than Windows settings, how?</para></question>

<answer><para>Insert this line as the first in your .dpr main program block:</para>

<ProgramListing>UseLanguage ('fr');</ProgramListing>

<para>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.</para>

<para>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.</para></answer></qandaentry>

<qandaentry><question><para>How do I switch language at runtime?</para></question> 
 
<answer><para>See the source of the TntSample application that is included in 
the download for Delphi.</para></answer></qandaentry> 
 
 
<qandaentry><question><para>I want to use language XXX but my controls do not support Unicode, what do I do?</para></question> 
 
<answer><para>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.</para>
 
<para>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.</para></answer></qandaentry> 
 
</qandadiv> 






<qandadiv id="qaerrors">
<title>Errors</title>
<qandaentry><question><para>In Kylix and CLX apps, gettext('Test') cannot compile</para></question> 
 <answer><para>In CLX, each form has a gettext() function that will 
be chosen instead of gnugettext.gettext(). Use _() instead:</para>
 
<ProgramListing>a:=_('Test');</ProgramListing></answer></qandaentry>

<qandaentry><question><para>I got an Access Violation. Why?</para></question>
<answer><para>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 <xref linkend="handlingclasses" />
for more information, or contact <ulink url="http://groups.yahoo.com/group/dxgettext/">our e-mail list</ulink> for further information.
If you send an e-mail on that list you will most likely get your problem solved quickly.</para></answer></qandaentry>



</qandadiv>

</qandaset>
</appendix>





<index>
</index>
</book>

