-
Notifications
You must be signed in to change notification settings - Fork 3
/
gettext-project.page.in
237 lines (181 loc) · 14.3 KB
/
gettext-project.page.in
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
<page xmlns="http://projectmallard.org/1.0/"
type="topic"
id="gettext-project">
<info>
<credit type="author maintainer copyright">
<name>Philip Chimento</name>
<email>[email protected]</email>
<years>2008-2012</years>
</credit>
<license href="http://creativecommons.org/licenses/by-nc-sa/3.0/">
<p>This work is licensed under a <link href="http://creativecommons.org/licenses/by-nc-sa/3.0/">Creative Commons Attribution-NonCommercial-ShareAlike 3.0 Unported License</link>.</p>
</license>
<link type="guide" xref="real-life-app-setup" group="#default"/>
<desc>Allowing your program to be translated into other languages</desc>
</info>
<title>Setting up <app>Gettext</app></title>
<synopsis>
<p>In this tutorial, you will learn:</p>
<list>
<item><p>How to translate your program's source code using <app>Gettext</app></p></item>
<item><p>How to translate other files using <app>Intltool</app></p></item>
</list>
<p>This tutorial is part of the <em>Setting up a real-life GTK application</em> series.
If you don't want to follow along with the previous parts, simply copy the <link href="../app-skeleton3"><file>app-skeleton3</file></link> directory from the tutorial's code examples.
Or, you can start from the <link xref="real-life-app-setup">beginning</link>.</p>
</synopsis>
<p>There is one more piece of infrastructure to add before we start writing real code.
That is allowing the program to be translated into other languages.
You might think that translation is something to be done only after the application is finished, but it's much easier to keep translation in mind while you are writing the application.</p>
<p>Do you have to learn thirty languages to do this?
No.
The way it usually works in an open source project is that you write the program in English (or, strictly speaking, in the "C locale".)
Another tool, <cmd>gettext</cmd>, extracts all the words and phrases from the program that will be displayed to users, and puts them in a <em>translation template</em> or <file>.pot</file> file.</p>
<p>You then recruit translators to your project (this is the hard part) and they translate some or all of the phrases in the translation template, producing a <file>.po</file> file.
Generally, you give your translators commit access to your source code repository, so they just commit the <file>.po</file> files whenever they are done.
The <cmd>make install</cmd> process takes care of installing the translations in the proper places in the user's system.</p>
<p>When the program is started, it looks at the value of the <code>LANG</code> environment variable.
If this contains a language code for a language which the program is available in, then the program will display the translated phrases for that language.
If not, the program will simply be in English.
Also, if the translator did not finish translating the entire translation template, then the program will be in the other language as much as possible, and any untranslated phrases will still be in English.
This allows translation to be an ongoing process, and translators can contribute as much as they have time for.
Also, if you add a new message in a new version of the program, and the translator is on vacation, it won't mean the whole translation is invalidated.</p>
<section id="using-gettext">
<title>Using <cmd>gettext</cmd></title>
<p>First, copy the project that we have so far to a new <file>app-skeleton4</file> directory, and change the version number if you like.
To add <cmd>gettext</cmd> to our build system, add the following lines to the "Shopping List" section of <file>configure.ac</file>:</p>
<!--{{{app-skeleton4/configure.ac:14-15%autoconf}}}-->
<p>Also, add <code>po/Makefile.in</code> to the <code>AC_CONFIG_FILES</code> call in the "Output" section, and <code>po</code> to the <code>SUBDIRS</code> variable in <file>Makefile.am</file>.
(The <file>po</file> subdirectory is where the <cmd>gettext</cmd> files are placed by default.)</p>
<note>
<p>It is slightly confusing that we should put a file named <code>Makefile.in</code> into <code>AC_CONFIG_FILES</code>.
Didn't <cmd>configure</cmd> transform <file>Makefile.in</file> into <file>Makefile</file>?
In this case, <cmd>gettext</cmd> installs a <file>Makefile.in.in</file> which we transform into <file>Makefile.in</file>.
Then, code generated by the <code>AM_GNU_GETTEXT</code> macro transforms the <file>Makefile.in</file> into <file>Makefile</file> automatically.
One hopes that things won't get any more convoluted in the future, with tools that install a <file>Makefile.in.in.in.in</file>.</p>
</note>
<p>To install the <cmd>gettext</cmd> files, simply run the bootstrapping command: <cmd>autoreconf -i</cmd>.
Then, before running <cmd>configure</cmd>, there are a few files we have to add to the <file>po</file> directory.
The first is called <file>POTFILES.in</file>.
This is a list of files that contain translatable messages for <cmd>gettext</cmd> to extract.
The next file is called <file>LINGUAS</file>.
This file contains a space-separated list of all available translations.
Create both files and leave them empty for now, we will fill them later.</p>
<p>The last file to create is called <file>Makevars</file>.
This file contains some customizable variables that get inserted into the <file>po</file> directory's <file>Makefile</file>.
After running <cmd>autoreconf</cmd>, there should be a template for this file called <file>Makevars.template</file>.
Copy this to <file>Makevars</file> and edit it.
It doesn't need much changing; I changed the <code>COPYRIGHT_HOLDER</code> variable to my name, and the <code>MSGID_BUGS_ADDRESS</code> to <code>$(PACKAGE_BUGREPORT)</code>, so that translators report bugs in the messages to the same place that regular bugs should be reported.
(In a large project, you might want to separate these.)</p>
<note>
<p>Autoconf sets the <code>PACKAGE_BUGREPORT</code> variable to the bug report address that we specified in the call to <code>AC_INIT</code> in <file>configure.ac</file>.</p>
</note>
<p>Next, we must mark all the user interface strings in <file>hello-world.c</file> for translation.
First, add at the top of the file:</p>
<!--{{{app-skeleton4/src/hello-world.c:1}}}-->
<p>This is a special GLib header file where the interface to <cmd>gettext</cmd> is defined.
(<code>i18n</code> stands for "internationalization", because there are eighteen letters between the initial I and the final N.)
In particular, this header defines the <code>_()</code> macro with which we mark our strings.
(If <cmd>gettext</cmd> support is disabled, <code>_()</code> does nothing; otherwise it is an alias for the <code>gettext()</code> function.
It is called <code>_</code> so as to save typing and not to distract the eye while reading the code.)</p>
<p>Then, add the following code to the top of the <code>main()</code> function, at line 40:</p>
<!--{{{app-skeleton4/src/hello-world.c:40-43}}}-->
<p>This sets up the program to read the translated message files. Finally, add the following line to the makefile so that the program knows where to find the message files:</p>
<!--{{{app-skeleton4/src/Makefile.am:2%automake}}}-->
<p>There are four strings that we need to mark for translation:
one in <code>print_hello()</code>,
one in <code>on_delete_event()</code>,
the argument of <code>gtk_window_set_title()</code> in <code>main()</code>,
and the argument of <code>gtk_button_new_with_label()</code> in <code>main()</code>.
Surround these strings with a call to <code>_()</code>, so that <code>"Hello World"</code> becomes <code>_("Hello World")</code>.</p>
<note>
<p>Note that not <em>all</em> the strings should be translated!
For example, the names of the signals in <code>g_signal_connect()</code> and the icon name in <code>gtk_window_set_icon_name()</code> should be left as they are.
They are not displayed to the user; instead, they have an internal meaning to the program.
If we were to translate the <code>destroy</code> signal into German, then the program would stop working, since it would try to connect to the <code>zerstören</code> signal, which of course it has never heard of.</p>
</note>
<p>Last of all, we need to add our source file with the strings marked for translation to <file>POTFILES.in</file>, so <cmd>gettext</cmd> knows to look there:</p>
<!--{{{app-skeleton4/po/POTFILES.in:1%plain}}}-->
<p>If we now change to the <file>po</file> directory and run <cmd>make update-po</cmd>, an <file>app-skeleton.pot</file> should be generated.
This is the template that translators can base their translations on.</p>
</section>
<section id="translating-the-program">
<title>Translating the program</title>
<p>Now, we will translate the program into another language and test it.
If you speak another language than English, why not try translating it into that language?
As an example, I will use Dutch (language code <code>nl</code>) here.</p>
<p>The first thing to do is to create a <file>.po</file> file for your chosen language.
Change to the <file>po</file> directory and type:</p>
<screen><input>msginit -l nl</input></screen>
<p>This will create a file named <file>nl.po</file>, based on the translation template.
You can edit it with your favorite text editor, although for bigger projects you might want to use a dedicated translation editor such as <link href="http://www.google.com">Virtaal</link> or <link href="http://www.google.com">Poedit</link>.
Translate the messages as you see fit, or use my translations:</p>
<!-- gettext and intltool format their .po files a little differently -->
<listing>
<title><file>app-skeleton4/po/nl.po</file></title>
<code mime="text/x-gettext"><![CDATA[
#: src/hello-world.c:11
msgid "Hello World\n"
msgstr "Hallo Wereld\n"
#: src/hello-world.c:27
msgid "delete event occurred\n"
msgstr "delete event heeft plaatsgevonden\n"
#: src/hello-world.c:52
msgid "Hello"
msgstr "Hallo"
#: src/hello-world.c:76
msgid "Hello World"
msgstr "Hallo Wereld"
]]></code>
</listing>
<p>After translating, add the language code to the <file>LINGUAS</file> file:</p>
<!--{{{app-skeleton4/po/LINGUAS:1%plain}}}-->
<p>Then change to the <file>po</file> directory and run <cmd>make update-po</cmd> again.
It is important to do this whenever a translator gives you an updated <file>.po</file> file, otherwise the new translations will not get installed.
Then install the program once again using <cmd>make install</cmd>.</p>
<p>You should now be able to run the program in Dutch (or whatever language you chose.)
If your system locale is set to Dutch, then simply running the program should work.
If you have your system in English or a different locale, you can test the translation by running the program with the <code>LANG</code> environment variable set to <code>nl</code>.
(Just type <code>LANG=nl</code> before the program name on the command line.)</p>
</section>
<section id="translating-non-source-code">
<title>Translating non-source code</title>
<p>The <cmd>gettext</cmd> program has one shortcoming:
it only works in program code, where it can call the <code>gettext()</code> function to fetch its translations.
Data files, such as a GUI definition in XML, or the <link xref="desktop-file">desktop file</link>, don't get translated.
This is why <cmd>intltool</cmd> was invented.
It takes translatable strings from these files and adds them to the translation template.
Then, when they have been translated, it merges the strings back into the data files.
We will now add <cmd>intltool</cmd> to our program and use it to translate the desktop file.</p>
<p>First of all, <cmd>intltool</cmd> has its own bootstrapping program, called <cmd>intltoolize</cmd>.
It needs to be run after <cmd>autoreconf</cmd>.
Since using <cmd>intltool</cmd> means we can't bootstrap the build system simply by using <cmd>autoreconf -i</cmd> anymore, we will make a <link xref="autoconf-project">bootstrap script</link>.
Create a file in called <file>autogen.sh</file>, make it executable, and write in it:</p>
<!--{{{app-skeleton4/autogen.sh:1-7%shell}}}-->
<p>We need the <cmd>--force</cmd> options because <cmd>autoreconf</cmd> and <cmd>intltoolize</cmd> overwrite each other's <file>po/Makefile.in.in</file> files.
Go ahead and run the bootstrap script now.</p>
<p>Then, add to the "Shopping List" section in <file>configure.ac</file>:</p>
<!--{{{app-skeleton4/configure.ac:16%autoconf}}}-->
<p>Below the "Libraries" section, add a new section called "Variables."
To use <cmd>intltool</cmd>, we need to define a variable called <code>GETTEXT_PACKAGE</code> that contains the name of the program as it is known to <cmd>gettext</cmd>:</p>
<!--{{{app-skeleton4/configure.ac:29-31%autoconf}}}-->
<p>The call to <code>AC_SUBST</code> makes sure that any occurrences of <code>@GETTEXT_PACKAGE@</code> in the makefiles are replaced with the contents of the variable. Now run <cmd>make</cmd> again.</p>
<p>Next, we need to tell <cmd>intltool</cmd> which data files contain translatable strings.
There is actually a script, <cmd>intltool-prepare</cmd> that will automate that for us.
Change to the project root directory and run <cmd>intltool-prepare</cmd>.
You will notice that it automatically adds the desktop file to <file>po/POTFILES.in</file> and it changes the install rule for the desktop file in <file>Makefile.am</file>.</p>
<p>It also creates a new file, <file>app-skeleton.desktop.in</file>.
If you are using a source control system for your project, remove the <file>app-skeleton.desktop</file> from your repository as the script suggests; it is now automatically generated from <file>app-skeleton.desktop.in</file>, which you should add to your repository.
From now on, when you want to edit the desktop file, edit <file>app-skeleton.desktop.in</file> instead.</p>
<p>To translate the desktop file, go to the <file>po/</file> directory and run <cmd>make update-po</cmd> once more.
You will see two new untranslated strings added to the <file>.pot</file> and <file>.po</file> files.
Translate them yourself, or use my Dutch translation:</p>
<!--{{{app-skeleton4/po/nl.po:46-52%gettext}}}-->
<p>Then, run <cmd>make update-po</cmd> once more, then <cmd>make</cmd>.
You can look inside the generated <file>app-skeleton.desktop</file> file to ascertain that your translations have been merged.
If you now run <cmd>make install</cmd>, the translated file will be installed.</p>
<p>With this, all our build infrastructure is in place.
Now we can begin writing some real code.
You are ready to start on the next tutorial, <link xref="real-life-app-writing">Writing a real-life GTK application</link>.</p>
</section>
</page>