Wstęp
Wiele aplikacji, które używamy na codzień mają interfejs dostępny w języku polskim, choć domyślnie nie zostały w nim napisane. Dzisiaj zajmiemy się mechanizmem tłumaczeń w aplikacjach korzystających z biblioteki Qt4, co - jak się przekonamy - nie jest wcale takie trudne.
Jak to działa?
Metoda działania jest bardzo prosta i można ją podzielić na kilka punktów:
- Programista umieszcza wszystkie napisy, które mają być przetłumaczalne w specjalnej metodzie tr().
- Programista dodaje kod odpowiedzialny za ładowanie tłumaczeń w funkcji main().
- Programista dodaje odpowiednie wpisy dla tłumaczeń w pliku projektu aplikacji.
- Programista generuje pliki źródłowe tłumaczeń (.ts).
- Osoba tłumacząca uzupełnia plik źródłowy (.ts) przetłumaczonymi komunikatami.
Tajemnicze tr()
Każda klasa, która dziedziczy pośrednio lub bezpośrednio po QObject posiada zaimplementowaną metodę tr() (skrót od translate). Posiada ona następujący prototyp:
QString tr( const char *tekstŹródłowy, const char *komentarz = 0, int n = -1 )
Pierwszy argument jest obowiązkowy i jest nim tekst, który chcemy przetłumaczyć. Drugi to komentarz, który będzie wyświetlany osobie tłumaczącej. Komentarze są bardzo przydatne, ponieważ wiele języków, w tym język polski, posiadają różne formy np. przymiotnika. Jeśli mielibyśmy do przetłumaczenia napis "blue", bylibyśmy zdezorientowani, ponieważ możemy go przetłumaczyć na różne sposoby - np. "niebieski", ale też "niebieska", czy "niebieskie". Dzięki komentarzowi możemy poznać kontekst, w jakim został użyty wyraz oraz czego dotyczy, dzięki czemu możemy jednoznacznie przetłumaczyć komunikat:
tr("blue", "colour of an eye");
Ostatni argument (n) jest liczbą całkowitą dodatnią (n >= 0). Każde wystąpienie w tekście napisu %n zostanie zastąpione przez wartość podanego argumentu n. Na przykład, gdy chcemy uzyskać komunikat informujący o ilości wysłanych wiadomości:
int ileWiadomosci = 3;
tr("Messages sent: %n.", "%n is number of sent messages", ileWiadomosci);
Powyższy komunikat po przetłumaczeniu na język polski będzie wyglądał następująco: Wysłano wiadomości: 3.
Jeśli nasza klasa nie dziedziczy po QObject lub używamy tekstu, który ma być przetłumaczalny np. w funkcji main(), możemy użyć metody tr() poprzedzając ją akcesorem dostępu do klasy QObject, ponieważ ta metoda jest jedną z metod statycznych klasy QObject:
int main(int argc. char *argv[])
{
// Część kodu pominięta
QString napis = QObject::tr("A string");
// Część kodu pominięta
}
Klasa QString
Klasa QString jest podstawową klasą Qt4 do reprezentacji i zarządzania napisami. Posiada wiele przydatnych metod do łatwego manipulowania napisami. Dokładniejszym jej opisem zajmiemy się w kolejnych częściach.
Ładowanie tłumaczeń
Aby załadować tłumaczenia dla odpowiedniego języka będziemu musieli posłużyć się dodatkowymi klasami: QLocale odpowiedzialną za ustawienia językowe oraz QTranslator odpowiedzialną za odszukiwanie przetłumaczonych napisów. Oto niezbędny kod do załadowania pliku z tłumaczeniami:
#include <QApplication>
#include <QLocale>
#include <QTranslator>
int main(int argc, char *argv[])
{
QApplication app(argc, argv);
QString kodJezyka = QLocale::system().name(); // 1.
QTranslator tlumacz; // 2.
tlumacz.load(QString("NazwaAplikacji_") + kodJezyka); // 3.
app.installTranslator(&tlumacz); // 4.
// Dalsza część kodu...
}
- Statyczna metoda system() klasy QLocale zwraca jej instancję (obiekt) zawierający konfigurację językową systemu, zaś metoda name() zwraca kod języka jako napis (QString), czyli dla polskiego będzie to pl_PL
- Tworzymy obiekt odpowiedzialny za załadowane tłumaczenia.
- Ładujemy plik z tłumaczeniami. Metoda load() jako pierwszy i obowiązkowy argument przyjmuje nazwę pliku do załadowania. Domyślnie plik jest wyszukiwany w katalogu z aplikacją, jednak można podać inną lokację, jako drugi argument metody. Jeśli używanym przez nas językiem byłby polski, to obiekt tlumacz próbowałby załadować plik NazwaAplikacji_pl_PL.qm. Rozszerzenie .qm jest automatycznie dodawane (pliki .qm są binarnymi plikami z tłumaczeniami).
- Metoda installTranslator instaluje obiekt tłumacza dla aplikacji. Od tego momentu, jeśli plik z tłumaczeniami został poprawnie wczytany, wszystkie przetłumaczalne napisy będą zastępowane tymi z pliku tłumaczeń. Wspomniana metoda przyjmuje jako argument wskaźnik do obiektu QTranslator.
Modyfikacja pliku projektu
Aby można było automatycznie wygenerować pliki źródłowe tłumaczeń, niezbędna jest modyfikacja pliku projektu naszej aplikacji. Należy w nim dodać zmienną TRANSLATIONS:
TRANSLATIONS += translations/MojaAplikacja_pl.ts \
translations/MojaAplikacja_fr.ts
W powyższym przypadku będziemy mogli wygenerować pliki tłumaczeń dla dwóch języków - polskiego i francuskiego, a pliki tłumaczeń znajdą się w folderze translations (można podać dowolny inny folder, jak również można nie podawać żadnego - wtedy tłumaczenia zostaną wygenerowane w folderze, gdzie znajduje się plik projektu).
Przy okazji możemy zobaczyć, jak można rozdzielać na wiele linii listę argumentów zmiennej - po ostatnim argumencie w linii należy dodać spację i ukośnik, po czym kolejny argument podajemy już w kolejnej linii. Po ostatnim argumencie nie stawiamy już spacji i ukośnika.
Generowanie tłumaczeń
Do generowania tłumaczeń służy narzędzie lupdate, dla którego, jako argument podajemy ścieżkę pliku projektu, dla którego chcemy wygenerować tłumaczenia. Jeśli pliki z tłumaczeniami już istnieją, zostają one zaktualizowane, przy czym stare tłumaczenia nie są usuwane. Tak więc, by wygenerować pliki źródłowe tłumaczeń dla przykładowego projektu MojaAplikacja, przy założeniu, że znajdujemy się w terminalu w folderze tego projektu, wydajemy polecenie:
lupdate ./MojaAplikacja.pro
Tłumaczenie komunikatów
Do przetłumaczenia komunikatów zawarych w pliku źródłowym tłumaczeń służy aplikacja QtLinguist dostarczana razem z Qt4. Dla przykładu posłużymy się prostą aplikacją pobierającą od użytkownika liczbę od 0 do 10, następnie wyświetlającą komunikat z podaną liczbą:
// Plik main.cpp:
#include <QApplication>
#include <QLocale>
#include <QTranslator>
#include <QInputDialog>
#include <QMessageBox>
int main(int argc, char *argv[])
{
QApplication aplikacja(argc, argv);
// Załadowanie tłumaczenia:
QString kodJezyka = QLocale::system().name();
QTranslator tlumacz;
tlumacz.load(QString("Translations_") + kodJezyka);
// Instalacja obiektu tłumacza:
aplikacja.installTranslator(&tlumacz);
// Pobranie danych od użytkownika:
int integer = QInputDialog::getInteger(0, QObject::tr("Books", "Dialog title"),
QObject::tr("How many books have you read last month?"), 0, 0, 10);
// Wyświetlenie komunikatu:
QMessageBox::information(0, QObject::tr("Result"),
QObject::tr("You have read %n book(s) last week.", 0, integer));
return 0;
}
# Plik projektu dla aplikacji Translations:
TEMPLATE = app
TARGET = Translations
CONFIG += qt
QT += gui
SOURCES += main.cpp
# Dodajemy wpis dla polskiego tłumaczenia:
TRANSLATIONS += Translations_pl.ts
Przy założeniu, że projekt nazwaliśmy i zapisaliśmy w folderze Translations, generujemy plik źródłowy tłumaczenia dla naszej aplikacji - będąc w terminalu w folderze projektu:
lupdate ./Translations.pro
Po wygenerowaniu tłumaczenia i uruchomieniu aplikacji QtLinguist (linguist lub linguist-qt4 - w zależności od systemu) otwieramy w niej wygenerowany plik Translations_pl.ts:
- Dokowalne okno z listą kontekstów tłumaczeń - kontekst jest nazwą klasy, w której tłumaczenie zostało użyte.
- Lista tłumaczeń wybranego kontekstu.
- Tekst źródłowy i tekst przetłumaczony.
- Okno, w którym zależnie od wybranej zakładki pokazywane są ostrzeżenia o błędach, podgląd kodu źródłowego aktualnego tłumaczenia lub podpowiedzi.
QtLinguist automatycznie sprawdza, czy użyte znaczniki kodu html, klawiszy skrótów, znaki interpunkcyjne na końcu linii się zgadzają. W przypadku, gdy przetłumaczyliśmy komunikat i wszystko się zgadza, obok tłumaczenia na liście pojawia się żółty znak zapytania, który, gdy klikniemy zatwierdza tłumaczenie, jako gotowe. Jeśli coś się nie zgadza, obok tłumaczenia pojawi się czerwony wykrzyknik (jak na zrzucie) - informacja o tym, co się nie zgadza dostępna jest w zakładce ostrzeżenia, lub w podpowiedzi po najechaniu kursorem myszy na tłumaczenie na liście. Walidację poszczególnych rzeczy można wyłączyć, i tak oto po wyłączeniu sprawdzania znaków interpunkcyjnych na końcu linii (poprzez menu Walidacja), wykrzyknik zamieniłby się w znak zapytania i moglibyśmy zatwierdzić tłumaczenie.
Jeśli w napisie, który ma być przetłumaczalny programista dodałby komentarz, byłby on widoczny zaraz pod tekstem źródłowym na niebieskim tle (w naszej przykładowej aplikacji pojawia się jeden komentarz, więc można to łatwo sprawdzić).
Obsługa Lingwisty Qt (QtLinguist) jest bardzo intuicyjna i nie ma potrzeby szczegółowo opisywać wszystkich funkcji. Zainteresowani mogą przejrzeć tutorial Lingwisty Qt po wybraniu odpowiedniej opcji spod menu Pomoc. Należy jeszcze wspomnieć o tłumaczeniach z argumentem liczbowym: Gdy komunikat zawiera liczbę jako argument - w naszym przypadku You have read %n book(s) last week. - Lingwista Qt daje nam możliwość podania różnych form przetłumaczonego tekstu w zależności od wybranego języka. Aby zmienić język dla tłumaczenia na polski wystarczy udać się do pozycji Ustawienia pliku z tłumaczeniami... z menu Edycja. W naszym tłumaczeniu będziemy mogli podać trzy różne formy:
- Forma liczby pojedynczej.
- Pierwsza forma liczby mnogiej (dla 2, 3, 4 w liczbach, np. dla: 2, 24, 33).
- Druga forma liczby mnogiej (pozostałe przypadki, np. dla: 0, 5, 6, 30).
Gotowy plik z tłumaczeniem możemy wydać w postaci binarnego pliku .qm z menu Plik:
- Wydaj - tworzy finalny plik tłumaczeń w tym samym folderze, w którym znajduje się plik źródłowy
- Wydaj jako... - tworzy finalny plik tłumaczeń w wybranym przez nas folderze
Po wydaniu tłumaczenia dla naszej przykładowej aplikacji i po jej uruchomieniu, zobaczymy interfejs w języku polskim: