Цель лекции: Научиться создавать приложения с поддержкой нескольких языков пользовательского интерфейса
Независимо от того, собираетесь ли вы выпускать ваш программу под свободной лицензией, либо как коммерческий продукт, наверняка вы стремитесь к тому, чтобы она получила как можно более широкое распространение. Понятно, что возможность менять язык интерфейса приложения и наличие переведённой на различные языки документации потенциально значительно расширяют число её пользователей.
Локализация приложения — это его адаптация к языковым и культурным особенностям страны или стран, отличных от места разработки программы, в которых оно будет использоваться. Локализация включает в себя перевод интерфейса программы, её документации, адаптацию алгоритмов алфавитной сортировки и предоставления данных (например, отображение разделителя дробной части десятичных дробей). В этой главе мы остановимся лишь на переводе интерфейса приложения.
Реализовать эту задачу можно двумя различными способами.
Например, можно поступить так, как мы это сделали в лекции 15 , когда переведённые строки вставляются прямо в код. Однако, во-первых, это неправильно идеологически, т.к. нарушает принцип инкапсуляции: либо переводчик должен получить доступ к исходным текстам программы, либо новый перевод должен переносить в код программист. Кроме того, тем самым резко затрудняется добавление новых языков: ведь для этого программу придётся каждый раз компилировать заново.
Поэтому большее распространение получил второй способ, когда программа считывает строки перевода из файла, соответствующему языку, выбранному пользователем. При добавлении новых файлов перевода в заданный каталог в меню выбора языков программы появляются новые пункты. Подобный подход, помимо облегчения добавления новых языков интерфейса, позволяет разделить труд переводчика и программиста.
В библиотеке Juce для реализации последнего подхода существует специальный класс, LocalisedStrings. Объект этого класса загружает строки перевода из текстового файла и заменяет ими эквивалентные строки, используемые в приложении.
Файл перевода имеет следующий формат:
language: Russian countries: ru "hello" = "привет" "goodbye" = "до свидания"
В том случае, если в текст локализации требуется включить кавычки, в файл перевода необходимо вставить двойные кавычки ("").
Для замены в программе интерфейсных строк строками перевода можно воспользоваться двумя способами.
Во-первых, класс LocalisedStrings включает метод const String LocalisedStrings::translate(const String text) const, который заменяет строки интерфейса программы их локализованными версиями. В том случае, если в файле перевода не окажется подходящей строки, то будет возвращена оригинальная.
Во-вторых, в Juce определён макрос ).
#define TRANS(stringLiteral) LocalisedStrings::translateWithCurrentMappings(stringLiteral
Метод пытается перевести строку text, получаемую в качестве параметра, используя заданное отображение (mapping). Отображение задаётся с помощью метода static void LocalisedStrings::setCurrentMappings(LocalisedStrings* newTranslations).
В том случае, если никакого отображения не было задано, translateWithCurrentMappings возвращает оригинальный текст.
Рассмотрим оба способа локализации интерфейса приложения на простых примерах.
Первая демонстрационная программа будет выводить на ярлык приветствующую мир надпись, а также включать закрывающую кнопку (рис 21.1).
(рис 21.1) Программа, использующая строки из файла для перевода пользовательского интерфейса
Строки для перевода надписей на ярлыке и кнопке наша программа будет загружать из файла набора, который мы подготовим заранее. Выбор файла с тем или иным языком перевода будет осуществляться в зависимости от языковых настроек целевой операционной системы.
Собственно перевод будет осуществлять глобальный объект класса ).
#ifndef _TTranslator_h_
#define _TTranslator_h_
//--------------------------------------------------
#include "../JuceLibraryCode/JuceHeader.h"
//--------------------------------------------------
class TTranslator : public LocalisedStrings
{
static TTranslator* pSelf;
protected:
TTranslator(const File FileToLoad) : LocalisedStrings(FileToLoad){}
public:
static TTranslator* pInstance(const File FileToLoad)
{
if(!pSelf) pSelf = new TTranslator(FileToLoad);
return pSelf;
}
};
//--------------------------------------------------
TTranslator* TTranslator::pSelf = NULL;
//--------------------------------------------------
#endif
Теперь мы можем задать в качестве отображения (mapping) для перевода интерфейса программы посредством макроса ). Его название соответствует первым двум буквам локали, т.е. в случае, если языком операционной системы является русский (локаль ru_RU), то файл перевода будет называться "ru.lng". Разумеется, для имени файла вы можете выбрать и другое расширение.
#include "TCentralComponent.h"
#include "TTranslator.h"
#include <locale>
//------------------------------------------------------
TCentralComponent::TCentralComponent() : Component ("Central Component"),
pHelloLabel(0),
pCloseButton(0)
{
// Получаем первые две буквы текущей локали
String sLocale(setlocale(LC_ALL, ""));
sLocale = sLocale.substring(0, 2);
sLocale = sLocale.toLowerCase();
// Находим путь к исполняемому файлу нашей программы
File Path = Path.getSpecialLocation(File::currentExecutableFile);
// Получаем путь к программе
String sPath = Path.getFullPathName();
sPath = sPath.trimCharactersAtEnd(Path.getFileName());
// Получаем полное имя файла перевода, исходя из текущей локали
sPath += sLocale;
sPath += ".lng";
// Загружаем строки файл перевода...
File Translations(sPath);
// и устанавливаем их в качестве отображения (mapping)
LocalisedStrings::setCurrentMappings(TTranslator::pInstance(Translations));
// Переводим интерфейс программы с помощью макроса TRANS
pHelloLabel = new Label("Hello Label", TRANS("Hello world!"));
addAndMakeVisible(pHelloLabel);
pHelloLabel->setFont(Font(38.0000f, Font::bold));
pHelloLabel->setJustificationType(Justification::centred);
pHelloLabel->setEditable(false, false, false);
pHelloLabel->setColour(Label::textColourId, Colours::blue);
pHelloLabel->setColour(TextEditor::backgroundColourId, Colours::azure);
pCloseButton = new TextButton("Close Button");
addAndMakeVisible(pCloseButton);
pCloseButton->setButtonText(TRANS("Close"));
pCloseButton->addListener(this);
setSize (400, 200);
}
Недостатком вышеприведённого примера является то, что интерфейс программы переводится только один раз; при этом невозможно выбирать язык приложения вручную, что неудобно в случаях, когда его требуется запускать на операционной системе с локалью, отличной от родного языка пользователя.
Данный недостаток исправлен во втором примере. В нём, как и в предыдущем, язык интерфейса по умолчанию выбирается на основании текущей локали операционной системы, однако есть возможность использовать и другие файлы перевода, выбирая соответствующий язык в группе радиокнопок (рис 21.2).
(рис 21.2) Программа, позволяющая выбирать язык пользовательского интерфейса
Смена языка интерфейса будет осуществляться в функции-члене класса компонента содержимого ).
void TCentralComponent::LanguageChange(LocalisedStrings sTr)
{
// Переводим интерфейс приложения с помощью нового "переводчика"
pLanguageGroup->setText(sTr.translate("Languages"));
pHelloLabel->setText(sTr.translate(L"Hello world!"), false);
pCloseButton->setButtonText(sTr.translate(L"Close"));
}
Для облегчения создания нового "переводчика", исходя из пути к файлу перевода для того или иного языка, предусмотрим в нашем классе метод ).
LocalisedStrings TCentralComponent::NewTranslator(String sPath)
{
// Получаем файл перевода, исходя из пути к нему
File Translations(sPath);
// Создаём новый "переводчик"
LocalisedStrings sTr(Translations);
return sTr;
}
Теперь мы можем выполнить собственно перевод интерфейса посредством нашего метода ).
#include "TCentralComponent.h"
#include <locale>
//--------------------------------------------------
#define tr(s) String::fromUTF8(s)
//--------------------------------------------------
TCentralComponent::TCentralComponent() : Component("Central Component"),
pLanguageGroup(0),
pEnglishButton(0),
pRussianButton(0),
pItalianButton(0),
pHelloLabel(0),
pCloseButton(0)
{
String sLocale(setlocale(LC_ALL, ""));
sLocale = sLocale.substring(0, 2);
sLocale = sLocale.toLowerCase();
File Path = Path.getSpecialLocation(File::currentExecutableFile);
String sPath = Path.getFullPathName();
sPath = sPath.trimCharactersAtEnd(Path.getFileName());
sPath += sLocale;
sPath += ".lng";
pLanguageGroup = new GroupComponent(L"LanguageGroup", L"Languages");
addAndMakeVisible(pLanguageGroup);
pEnglishButton = new ToggleButton(L"English Button");
pEnglishButton->setButtonText(L"English");
pEnglishButton->setRadioGroupId(1234);
pEnglishButton->addListener(this);
addAndMakeVisible(pEnglishButton);
// Если язык операционной системы английский, отмечаем радиокнопку
if(sLocale == "en") pEnglishButton->setToggleState(true, false);
pRussianButton = new ToggleButton(L"Russian Button");
pRussianButton->setButtonText(tr("Русский"));
pRussianButton->setRadioGroupId(1234);
pRussianButton->addListener(this);
addAndMakeVisible(pRussianButton);
// Если язык операционной системы русский, отмечаем радиокнопку
if(sLocale == "ru") pRussianButton->setToggleState(true, false);
pItalianButton = new ToggleButton(L"Italian Button");
pItalianButton->setButtonText(L"Italiano");
pItalianButton->setRadioGroupId(1234);
pItalianButton->addListener(this);
addAndMakeVisible(pItalianButton);
// Если язык операционной системы итальянский, отмечаем радиокнопку
if(sLocale == "it") pItalianButton->setToggleState(true, false);
pHelloLabel = new Label(L"Hello Label", L"Hello world!");
pHelloLabel->setFont(Font (40.0000f, Font::bold));
pHelloLabel->setJustificationType(Justification::centred);
pHelloLabel->setEditable(false, false, false);
pHelloLabel->setColour(Label::textColourId, Colours::blue);
pHelloLabel->setColour(Label::backgroundColourId, Colours::azure);
addAndMakeVisible(pHelloLabel);
pCloseButton = new TextButton(L"Close Button");
pCloseButton->addListener(this);
addAndMakeVisible(pCloseButton);
setSize(600, 250);
// Создаём нового "переводчика"
LocalisedStrings sTr = NewTranslator(sPath);
// Смена языка интерфейса
LanguageChange(sTr);
}
//---------------------------------------------
void TCentralComponent::buttonClicked(Button* pButton)
{
File Path = Path.getSpecialLocation(File::currentExecutableFile);
String sPath = Path.getFullPathName();
sPath = sPath.trimCharactersAtEnd(Path.getFileName());
// Если нажата кнопка "Закрыть"...
if(pButton == pCloseButton)
{
// выходим из программы
JUCEApplication::quit();
}
// Задаём путь к файлу перевода,
// исходя из выбранного пользователем языка интерфейса
else if(pButton == pEnglishButton)
{
sPath += "en.lng";
}
else if(pButton == pRussianButton)
{
sPath += "ru.lng";
}
else if(pButton == pItalianButton)
{
sPath += "it.lng";
}
// Создаём нового "переводчика"...
LocalisedStrings sTr = NewTranslator(sPath);
// ...и с его помощью выполняем перевод
LanguageChange(sTr);
}//------------------------------------------------------
Понятно, что подобным образом можно реализовать динамически меняющееся меню "Языки", в котором новые пункты будут появляться автоматически при добавлении новых файлов перевода в заданный каталог. При этом названия языков — пунктов меню можно получать непосредственно из файлов перевода посредством метода const String LocalisedStrings::getLanguageName() const. Кроме того, в распространяемых приложениях необходимо предусмотреть обработку ситуации, когда первые буквы локали и названия целевого языка не совпадают (например, pl_PL и Polsky).
Для локализации приложения в Juce используется класс LocalisedStrins. Для перевода строк интерфейса программы можно либо обратиться к методу translateWithCurrentMappings класса (макрос TRANS), либо к его же методу translate.
Напишите простейший текстовый редактор с меню, включая контекстное и панелью инструментов. Предусмотрите возможность динамической (на этапе выполнения программы) смены языка её интерфейса.
Архив с исходными текстами примеров Вы можете скачать здесь
Для получения официальных документов о завершении программы дополнительного профессионального образования (удостоверения о повышении квалификации, дипломов о профессиональной переподготовке и MBA) необходимо предоставить:
Внимание! Вы можете не заказывать доставку бумажной версии официального документы, а скачать его в электронном виде и распечатать самостоятельно. Информация о выданном документе в течение 1 месяца загружается в Федеральную информационную систему «Федеральный реестр сведений о документах об образовании и (или) о квалификации, документах об обучении» - ФИС ФРДО.
Доступ на новый сайт осуществляется с использованием адреса электронной почты, который был указан вами при регистрации на "старом". Мы постарались перенести все ваши данные с прежнего ресурса, однако не исключена вероятность потери части информации.
При возникновении проблемы со входом, воспользуйтесь функцией сброса пароля
Если вы обнаружите несоответствия, пожалуйста, сообщите нам.