Internationalization of Fractal (part 2)

A textual program using gettext

For my investigations, I first tried to write a textual program that works with gettext. I spent quite some time to figure out how all of this works but I finally was able to make it work. And that means that we should be able to implement i18n for Fractal using gettext!

I created a new Rust binary project with cargo and I added gettext-rs as a dependency in the Cargo.toml file by adding this line:

gettext-rs = { git = "https://github.com/Koka/gettext-rs", features = ["gettext-system"] }

I edited the main.rs source file to have a basic test to see if gettext-rs works correctly, here is its content:

extern crate gettextrs;
use gettextrs::*;

fn main() {
     setlocale(LocaleCategory::LcAll, "");
     bindtextdomain("test", "./po");
     textdomain("test");

    println!("Original: Open");
    println!("Translated: {}", gettext("Open"));

    println!("Original: Text Editor");
    println!("Translated singular: {}", ngettext("Text Editor", "Text Editors", 1));
    println!("Translated plural: {}", ngettext("Text Editor", "Text Editors", 2));
}

In the root directory of the project, I created a directory named “po”, then a subdirectory named “fr” (as I want to do a little French translation) inside “po” and then again, a directory “LC_MESSAGES” inside “fr”. So that I had the same folder hierarchy inside my “po” directory as in “/usr/share/locale”, because gettext will look for the translations in MO files that are in directories like “[TARGET]/[LANGUAGE]/LC_MESSAGES/[PACKAGE].mo”. [TARGET] and [PACKAGE] are defined by the call “bindtextdomain([PACKAGE], [TARGET]);” in the program. So here, the package name is “test” and the targeted directory is “./po”.

Next, I used the utility xgettext to extract all the translatable strings from the source file with this command:

xgettext -o po/template.pot src/*

And I edited template.pot to replace “CHARSET” with “UTF-8”. To get the file in which I would do the French translation, I used:

msginit --locale=fr --input=po/template.pot --output=po/fr/test.po

Finally, I added the translations of the strings on the lines with “msgid” in the lines with “msgstr” and my PO file had this content:

# French translations for PACKAGE package.
# Copyright (C) 2018 THE PACKAGE'S COPYRIGHT HOLDER
# This file is distributed under the same license as the PACKAGE package.
# Eisha <eisha@ernesto>, 2018.
#
msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2018-05-15 12:33+0200\n"
"PO-Revision-Date: 2018-05-15 12:34+0200\n"
"Last-Translator: Eisha <eisha@ernesto>\n"
"Language-Team: French\n"
"Language: fr\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=2; plural=(n > 1);\n"

#: ../src/main.rs:10
msgid "Open"
msgstr "Ouvrir"

#: ../src/main.rs:13 ../src/main.rs:14
msgid "Text Editor"
msgid_plural "Text Editors"
msgstr[0] "Un éditeur de texte"
msgstr[1] "Des éditeurs de texte"

I compiled the MO file like this:

mgsfmt po/fr/test.po  -o po/fr/LC_MESSAGES/test.mo

The the program was finally ready to test, so when launching:

LANG=en_US cargo run

I get this output:

Original: Open
Translated: Open
Original: Text Editor
Translated singular: Text Editor
Translated plural: Text Editors

And when I launch it like this:

LANG=fr_FR cargo run

I get the expected output:

Original: Open
Translated: Ouvrir
Original: Text Editor
Translated singular: Un éditeur de texte
Translated plural: Des éditeurs de texte

You can find the program I have written in this repository.

A GTK program using gettext

When I got this textual program working, I had to test it with a GTK program in order to know if gettext-rs could still translate the strings in the source file and most importantly, if/how it could also translate strings from Glade files.

So I wrote a GTK program (with the crate gtk-rs) for which I created an empty window with the following title “A window title from Glade” with Glade, I saved this minimal UI in the file ui/main_window.glade. And I wrote the following code in main.rs:

extern crate gtk;
extern crate gettextrs;

use gtk::prelude::*;

use gettextrs::*;

fn main() {
    setlocale(LocaleCategory::LcAll, "");
    bindtextdomain("test", "./po");
    textdomain("test");

    gtk::init().unwrap();

    let window: gtk::Window = gtk::Builder::new_from_file("./ui/main_window.glade")
        .get_object("main_window").expect("Failed to load the main window");

    // UI initialization
    let label = gtk::Label::new(gettext("A label from the source code").as_str());
    window.add(&label);
    window.show_all();

    window.connect_delete_event(|_, _| {
        gtk::main_quit();
        Inhibit(false)
    });

    gtk::main();
}

Then, I followed the same procedure as before to generate the POT, PO and MO files, except that I also indicated to xgettext to parse the Glade file like this:

xgettext -o po/template.pot src/* ui/*

So that the content of po/fr/test.po was (at the end of the procedure):

# French translations for PACKAGE package.
# Copyright (C) 2018 THE PACKAGE'S COPYRIGHT HOLDER
# This file is distributed under the same license as the PACKAGE package.
# Eisha <eisha@ernesto>, 2018.
#
msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2018-05-15 19:16+0200\n"
"PO-Revision-Date: 2018-05-15 19:17+0200\n"
"Last-Translator: Eisha <eisha@ernesto>\n"
"Language-Team: French\n"
"Language: fr\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=2; plural=(n > 1);\n"

#: ../src/main.rs:19
msgid "A label from the source code"
msgstr "Une étiquette du code source"

#: ../ui/main_window.glade:7
msgid "A window title from Glade"
msgstr "Un titre de fenêtre de Glade"

When running this program with:

LANG=en_US cargo run

The window opened is like this:

Capture du 2018-05-16 11-09-51

And when running it with:

LANG=fr_FR cargo run

The window opened is like this:

Capture du 2018-05-16 11-11-54

So it seems that GtkBuilder is calling gettext when parsing the UI file.

You can find this other program I have written in this repository.

So now I can start to implement i18n support within Fractal! Although I will also have to figure out how to properly integrate this with meson.

Advertisements

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google+ photo

You are commenting using your Google+ account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

w

Connecting to %s