Le mot de la Fin.
Mike Massonnet | May 19, 2006
A rapid gettext introduction in C
Mike Massonnet | April 28, 2006
I was playing around PHP with gettext and believe me it is a pain in the ass. You can't just use fr_FR as a locale name. Instead PHP implies that you use one of the *exact* locales you have on your system (ie. fr_FR@euro or fr_FR.UTF-8 or ...).
As I was fed up with PHP I tried gettext with the C language. The functions remain the sames. The initialization is done by calling three functions and next you use the gettext() function for every strings. This function will print messages in the user's locale only if it is traduced in a po file otherwise it prints the original messages from the gettext() function. So the work of a translator is to write po files.
First you will see the details of the initialization, next you will get an example source and finally you will see tools to write po files (the traductions).
Initialization of gettext
The first function will set the environment variable LC_ALL to nothing ("") which means you are going to use the locale defined by the user.
NB: There are different LC environment variables and LC_ALL is a catch-all. Example of categories: LC_MONETARY, LC_TIME and so on. Type locale in your terminal to see all the categories.
setlocale(LC_ALL, "");
The next function is usefull if you are _not_ going to install the po files in the system's default locale directory (/usr/share/locale or /usr/local/share/locale). First argument is commonly the name of the application and the second is the locale's root directory.
bindtextdomain("example", "./locale");
Finally you are calling a function which tells gettext to look for an appropriate po file. As before it is commonly the name of the application.
textdomain("example");
The C example
#include <stdio.h> #include <locale.h> #include <libintl.h> #define _(str) gettext(str) int main (int argc, char *argv[]) { setlocale(LC_ALL, ""); bindtextdomain("example", "./locale"); textdomain("example"); printf(_("This is the most simple gettext example I can show :)\n")); printf(_("And don't think about a stupid \"hello world\"...\n")); return 0; }
As you can see it is definitely easy to internationalize an application. The #define helps to fine down the calls to gettext(). locale.h includes the LC definitions and libintl.h includes the gettext functions.
You can already compile the source however I will show this at the end.
The po files
As there are several text editors and C compilers there are several applications to work on translations. I will show command line tools and a graphical application.
A po file has to be compiled in an mo file. This is the file which will be installed into the locale directory.
Command line
First you run a command which generates the po file from the sources:
xgettext -k_ example.c -o example.po
The po file will contain msgid items with the original strings and just under them will be msgstr items with the (un)translated strings. This means you have to edit the file by hand and translate each strings. You would also have to define the charset in the header Content-Type.
When you have done the translation you run the next command to compile the po file into an mo file:
msgfmt locale.po -o locale.mo
Now you move the compiled mo file to the locale dir, in our example it will reside at: ./locale/fr/LC_MESSAGES/example.mo.
NB: fr could also be de, ru, br and so on.
A nice gui-frontend
A nice gui-frontend is poedit. You first need to create the locale directory, between locale/fr/LC_MESSAGES.
NB (bis): fr could also be de, ru, br and so on.
Launch poedit (the first time it will ask for your name and email) and create a File > New catalog. In Settings > Project info set Language, Country, Charset and Source code charset. Then in Settings > Paths set the Base path to the root directory of the C source and add "." in the Paths. In Settings > Keywords you can add function names in addition of _() and gettext().
Valid OK and save the po file to the LC_MESSAGES directory as example.po. Now you are able to translate each strings. You can navigate with ctrl+up/down. When you are done save the catalog and it will generate the compiled mo file.
Compile and Run
Compile the source and run it. The first time it will print the messages in your default locale. Next if you set LANG=C it will print the original messages.
$ gcc locale.c -o output $ ./output Ceci est l'exemple le plus simple de gettext que je puis démontrer :) Et ne pensez pas à un stupide "hello world"... $ LANG=C $ ./output This is the most simple gettext example I can show :) And don't think about a stupid "hello world"...
MPD OSD
Mike Massonnet | April 26, 2006
I wrote me a script to display an OSD of the current played track by MPD. Next I also wrote a script to display an OSD (using the first script) when the track changes.
Each script uses mpc. I set a key binding on a useless multimedia touch to the first script. The second script should be run in background.
mpd-osd.sh
#!/bin/sh # Display an OSD of the current track track () { title=`mpc | head -1 | sed 's/^volume: [0-9]*%.*/(nothing)/'` position=`mpc | head -2 | tail -1 | sed 's/.*\(([0-9]*%)\)/\1/'` [[ "$title" == "(nothing)" ]] && echo "$title" || echo "$title $position" } pid=`ps ax | grep osd_cat | sed '/grep/d' | awk '{print $1}'` [ "$pid" ] && kill $pid echo -e "Playing:\n"`track`" " | \ osd_cat -f '-bitstream-bitstream charter-bold-r-*-*-*-250-*-*-*-*-*-*' \ -p bottom -o 10 -l 3 -A right -c '#89B83F' -s 2 -d 4
mpd-new-track.sh
#!/bin/sh # Display an OSD when the track is changed function setNewOSD () { tmp=/tmp/mpd-new-track.sh currentTrack=`mpc | head -1` lastTrack=`cat $tmp` echo "$currentTrack" > $tmp [ "$currentTrack" != "$lastTrack" ] && mpd-osd.sh& } while sleep 1 do setNewOSD done