Локализация в Delphi с использованием .mo файлов: особенности и ограничения
В процессе разработки приложений на Delphi часто возникает необходимость в поддержке нескольких языков. Одной из техник локализации является использование файлов .mo, сгенерированных из файлов .po с помощью утилиты msgfmt. В этой статье мы рассмотрим особенности и ограничения использования .mo файлов для локализации в Delphi, опираясь на опыт сообщества и обсуждения на форумах.
Проблема, с которой столкнулся автор исходного поста (paule32):
Автор столкнулся с проблемой, когда строки, определенные в ресурсах приложения (rsDone), не переводились при использовании .mo файлов. Несмотря на наличие правильно сформированного .po файла и сгенерированного .mo файла, локализация не работала. Первоначально автор решил проблему, используя функцию tr() и создав собственный класс TMoTranslate. Однако, последующий анализ показал, что проблема заключается в том, как Delphi обрабатывает .mo файлы по сравнению с .po файлами.
Решение, предложенное автором (paule32):
Автор обнаружил, что TranslateUnitResourceStrings не работает с .mo файлами, а работает только с .po файлами. Его решение заключалось в создании собственного класса TMoTranslate и использовании функции tr(), которая загружает и переводит строки из .mo файла. Пример кода:
{$mode delphi}
unit globals;
interface
uses
SysUtils, LCLIntf, LCLType, LCLProc, Translations, GetText, Dialogs;
type
TMoTranslate = class(TMoFile)
private
FLangID: String;
public
constructor Create(ALang: String; ACheck: Boolean = false); overload;
destructor Destroy; override;
function getLangID: String;
end;
function tr(AString: String): String;
implementation
var
motr: TMoTranslate;
function tr(AString: String): String;
begin
if motr = nil then
motr := TMoTranslate.Create('de', false);
result := motr.Translate(AString);
end;
constructor TMoTranslate.Create(ALang: String; ACheck: Boolean);
var
filePath: String;
begin
if ACheck then
begin
FLangID := GetEnvironmentVariable('LANG');
if FLangID = '' then
FLangID := 'en' else
FLangID := Copy(FLangID, 1, 2);
end else
begin
FLangID := ALang;
end;
filePath := ExtractFilePath(ParamStr(0)) + 'locale/' + FLangID + '.mo';
if FileExists(filePath) then
begin
inherited Create(filePath);
end else
begin
raise Exception.Create('translator file not found: ' + filePath);
end;
end;
destructor TMoTranslate.Destroy;
begin
inherited Destroy;
end;
function TMoTranslate.getLangID: String;
begin
result := FLangID;
end;
end.
Альтернативное решение, предложенное wp:
wp предложил более простое решение: использовать функцию Translations.TranslateUnitResourceStrings для .po файлов и GetText.TranslateUnitResourceStrings для .mo файлов, явно указывая расширение файла. Это позволяет избежать конфликта функций с одинаковым именем.
procedure LoadLanguage(const LangCode: string; ATranslationExt: String);
var
TranslatedFilePath: string;
begin
TranslatedFilePath := ExtractFilePath(ParamStr(0)) + 'locale/' + LangCode + ATranslationExt;
if FileExists(TranslatedFilePath) then
case ATranslationExt of
'.po': Translations.TranslateUnitResourceStrings('globals', TranslatedFilePath);
'.mo': GetText.TranslateUnitResourceStrings('globals', TranslatedFilePath);
else raise Exception.Create('Illegal translator file format.');
end
else
raise Exception.Create('Translator file not found: ' + TranslatedFilePath);
end;
Обсуждение в сообществе:
wp, ссылаясь на ответ Maxim Ganetsky (разработчика системы перевода), объяснил, что .mo файлы ищут перевод не по ID строки (как это происходит с .po файлами), а по текущему значению строки. Это означает, что перевод будет найден только в том случае, если исходный язык приложения - английский. Если исходный язык другой, то перевод найти будет невозможно.
Рекомендации Maxim Ganetsky:
Использовать только .po файлы и отказаться от .mo файлов.
Если требуется вернуться к английскому языку, не использовать .en.po файл, а просто предоставить .pot файл.
Если все же настаивать на использовании .mo файлов, использовать только ресурсные строки и не использовать LCLTranslator.
Преимущества и недостатки использования .mo файлов:
Преимущества (по мнению paule32):
Разделение данных от исполняемого файла.
Потенциально более высокая производительность (за счет прямого доступа к бинарным данным).
Недостатки:
Ограниченная поддержка в Delphi (требуется обходной путь с использованием собственного класса TMoTranslate).
Несовместимость с некоторыми функциями локализации в Delphi.
Проблемы при использовании языков, отличных от английского.
Вывод:
Использование .mo файлов для локализации в Delphi может быть сложным и проблематичным. Несмотря на потенциальные преимущества в производительности и разделении данных, ограничения в поддержке и несовместимость с некоторыми функциями локализации делают использование .po файлов более предпочтительным вариантом. Если все же необходимо использовать .mo файлы, следует учитывать ограничения и использовать обходные пути, предложенные сообществом. Рекомендуется следить за обновлениями системы перевода в Delphi и учитывать рекомендации разработчиков, таких как Maxim Ganetsky. В большинстве случаев, использование .po файлов является более простым и надежным решением для локализации приложений на Delphi.
В статье рассматриваются особенности и ограничения использования .mo файлов для локализации в Delphi, предлагаются решения для работы с ними и обсуждаются альтернативные подходы с использованием .po файлов.
Комментарии и вопросы
Получайте свежие новости и обновления по Object Pascal, Delphi и Lazarus прямо в свой смартфон. Подпишитесь на наш Telegram-канал delphi_kansoftware и будьте в курсе последних тенденций в разработке под Linux, Windows, Android и iOS
Материалы статей собраны из открытых источников, владелец сайта не претендует на авторство. Там где авторство установить не удалось, материал подаётся без имени автора. В случае если Вы считаете, что Ваши права нарушены, пожалуйста, свяжитесь с владельцем сайта.