В общем, эта статья призвана восполнить этот пробел (да, во многом это перевод справки, но далеко не всё).
Примечание: ITE работает для Win32 и Win64, но не для FireMonkey-приложений.
Обзор способов локализации
Локализация в Windows
В Windows каждый исполняемый модуль может содержать ресурсы - грубо говоря, это данные, вынесенные в отдельный блок внутри PE-файла. Бывают они разных типов, среди которых нас будут интересовать таблицы строк. Собственно, это ровно и есть то, что можно предположить по названию: таблица строк, т.е. - массив из строк. Сами строки идентифицируются по численному идентификатору (ID).Довольно подробно внутреннее устройство таблиц строк мы уже разбирали.
Сама возможность локализации заключается в том, что одна и та же строка в таблице может присутствовать в нескольких экземплярах, идентифицируемых по LangID - идентификатору языка. Т.е. конкретная строка идентифицируется с помощью 2-х параметров: ID строки и ID языка.
Иными словами, исполняемый модуль хранит свои данные в ресурсах (таблицах строк), каждая таблица содержит все переводы строки на разные языки. При загрузке строки из ресурсов, берётся подходящая строка на базе текущего языка пользовательского интерфейса (язык UI).
В действительности, это далеко не самый удобный способ, поскольку для перевода на доп. язык, надо модифицировать сам исполняемый модуль, язык нельзя добавить или удалить как "плагин". Сама ОС не слишком-то использует этот способ, тем не менее, он есть. В основном, как памятник философии старых времён: "но что, если это кому-нибудь нужно"?
Локализация в Delphi
В Delphi используется модификация базового механизма ОС. Заключается она в следующем. Мы по-прежнему используем ресурсы для хранения данных для локализации. Но в этот раз каждый исполняемый модуль содержит только строки для языка по умолчанию. Все прочие языки выносятся в отдельные модули - DLL, которые не содержат исполняемого кода, а содержат только ресурсы (так называемая ресурсная DLL или ресурсный модуль). Создаётся по одной DLL на каждый язык, каждая DLL содержит локализируемые данные программы, переведённые на соответствующий язык. Когда программа обращается к строке в таблице строк, берётся вариант строки из DLL, соответствующей текущему языку, а не из базового модуля.В том или ином варианте эта же техника применяется и другими программами (написанными не на Delphi), в том числе и самой ОС (например, MUI). В действительности, это весьма (если не самый) распространённый способ.
В WinAPI нет функций, реализующих такую схему (по той простой причине, что вся "схема" заключается в указании одного параметра функции загрузки строки - модуля), поэтому Delphi использует свою реализацию. За счёт "магии компилятора", обращение к строке в ресурсах приводит к вызову функции, выбирающей ресурсную DLL и загружающей строку из неё. Для подробностей работы этого механизма - см. функции LoadResString и FindResourceHInstance в System.pas.
Замечу, что этот механизм всё ещё не исключает совмещения с предыдущим. Поскольку таблица строк всё ещё может содержать несколько вариантов строк в разных языках. Например, базовый модуль может включать в себя русский и английский, а все остальные языки - быть реализованными через ресурсные DLL. К сожалению, этот последний вариант не поддерживается Delphi. Модуль с несколькими языками вам придётся собирать руками (что, впрочем, не так сложно). Как крайний вариант - вы можете включить все языки в один исполняемый модуль. Это если вы не хотите дополнительных файлов.
Другие средства
Другие средства используют самые разнообразные способы. Но достаточно редко при этом используются ресурсы. Чаще всего локализированные данные оформляются отдельным текстовым файлом (ini, XML и custom-форматы).Наиболее популярные сторонние решения:
- dxGetText - адаптация GNU GetText для Delphi.
- TJvTranslator из JVCL.
- TsiLang - платный, но зато и самый мощный.
Локализация приложений с использованием Translation Manager
Утилиты для переводов
Delphi включает в себя набор утилит для локализации приложений (Translation Tools). Они доступны как для Delphi, так и для C++ Builder-а и работают для проектов любых типов.Утилиты представлены в двух вариантах:
- Встроенный Translation Manager (Integrated Translation Manager - ITM) - является частью IDE. Он помогает вам локализовать ваши проекты. ITM может создавать локализированные версии проектов для разных языков. Состоит он из таких частей:
- Resource DLL Wizard (мастер создания ресурсных DLL) - создаёт проекты ресурсных DLL для выбранных вами языков. Вызывается через Project > Languages > Add.
- Translation Editor - показывает табличку ресурсов. В каждой колонке показан оригинал, перевод, комментарий и т.п. Именно в нём вы делаете перевод своей программы. Вызывается через View > Translation Manager > Translation Editor или просто щелчком по файлу с ресурсами в менеджере проектов.
- Translation Repository - предоставляет центральное хранилище для переводов. Попросту говоря, словарик. Это хранилище общее для всех проектов. Вы можете брать оттуда готовый перевод при локализации проекта или занести свой перевод в хранилище, когда вы закончили локализацию проекта. Вызывается по View > Translation Manager > Translation Repository.
- Translation Tools Options - настройки утилит локализации. Вызывается по Tools > Options > Translation Tools Options.
- Внешний Translation Manager (External Translation Manager - ETM) - это автономная программа, которая может быть запущена на любой машине, даже без Delphi. Вы можете использовать её для локализации своих проектов без IDE. У ETM практически такая же функциональность, как и у Translation Editor, но с несколькими дополнительными менюшками, обусловленными автономной природой. ETM не может создавать новые проекты, но может импортировать существующие проекты. Вы можете дать ETM переводчикам, чтобы они могли делать переводы вашей программы.
Подготовка проекта
Ну, прежде чем приступать к локализации, вам надо бы сохранить проект. Также убедитесь, что он вообще компилируется.Далее, вам нужно писать код специальным образом. Во-первых, если вы хороший программист, то вы не используете "волшебные значения" у себя в программе, а объявляете соответствующие константы. Например, вместо:
procedure TForm1.Button1Click(Sender: TObject); begin A := StrToFloat(Edit1.Text) * 3.14; ShowMessage('Результат: ' + FloatToStrF(A, ffFixed, ...)); end;
Вы пишете:
procedure TForm1.Button1Click(Sender: TObject); const Pi = 3.14; SResult = 'Результат: %f'; begin A := StrToFloat(Edit1.Text) * Pi; ShowMessage(Format(SResult, [A])); end;
Если вы так делаете - вам повезло. Нет - пора бы начинать делать :)
В любом случае, смысл в том, что все свои данные вы должны разделить на те, которые могут меняться в другом языке и те, которые нет. Как правило, почти все строковые данные относятся к первой категории, за исключением некоторых случаев. Например, если вы парсите XML, то '<' и '>' не должны меняться в зависимости от языка.
Все строковые данные в своём коде вы должны оформить в виде констант. Если строка должна локализироваться - помещайте её в секцию resourcestring. Если нет - пихайте в const. Например:
resourcestring rsResult = 'Результат: %f%s'; procedure TForm1.Button1Click(Sender: TObject); const Pi = 3.14; SDegrees = '°'; begin A := StrToFloat(Edit1.Text) * Pi; ShowMessage(Format(rsResult, [A, SDegrees])); end;
Правило стиля: если вы используете resourcestring, то идентификатор обычно начинается с "rs" или "S". Для строк в const такого правила нет, но префикс "S" выглядит достаточно удобно. Вы можете захотеть различать типы строк в коде (rs/S) или использовать всегда одинаковое наименование (S/S) - ваш выбор.
Рассматривайте строки в ресурсах как обычные константы. Вы можете использовать в тексте идентификатор строки и компилятор сам позаботится о загрузке этой строки из модуля ресурсов. Вам ничего не нужно для этого делать. Более того, вам также ничего не надо делать для получения локализованной версии строки. Никаких вызовов типа GetTranslatedString(SResult). Используйте константу напрямую, а магия компилятора сделает остальное - загрузит нужный язык и подставит перевод. Достаточно просто.
Блоки resourcestring обычно располагаются в модуле сразу после implementation или же их выносят в отдельный модуль типа StrConsts.pas. Поступайте как вам угодно.
Оформив код таким образом, вбейте значения для языка по умолчанию во все строковые константы, а также строковые свойства компонент на формах. Как правило, язык по умолчанию - это английский. Но для "домашней" программы это вполне может быть русский.
Ещё раз сохраните всё и убедитесь, что проект компилируется.
Resource DLL Wizard/добавление нового языка
Прежде чем приступить к собственно переводу, вам нужно добавить языки в ваш проект. Делается это с помощью Resource DLL Wizard. Выберите в меню Project > Languages > Add или File > New > Other > Delphi Projects > Resource DLL Wizard.На первом шаге вы выбираете проекты, для которых вы добавляете язык (обычно это только один проект; несколько их будет, если их несколько в группе проектов в менеджере проектов), и для каждого проекта вы указываете язык по умолчанию. Обычно это английский (409) или русский (419).
Язык по умолчанию - это тот язык, на котором будет программа, если не найден более подходящий язык.
Далее вы выбираете один или несколько языков, которые хотите добавить:
Полный список доступных языков можно посмотреть в справке.
На следующем шаге вам предложат сменить пути для проектов ресурсных DLL. По умолчанию, эти проекты размещаются в подпапках проекта. Имя подпапки = аббревиатуре языка по ISO 639x. Например, если ваш проект лежит в C:\...\Projects\My Project 1\, и вы выбрали три языка (русский, японский и итальянский), то Resource DLL Wizard создаст три новых проекта, разместив их в папках C:\...\My Project 1\RUS\, C:\...\My Project 1\JPN\ и C:\...\My Project 1\ITA\ соответственно. Достаточно разумный выбор, поэтому чаще всего вы пропускаете этот шаг.
После ещё нескольких маловажных вопросов (щёлкайте просто Next/Yes), Resource DLL Wizard создаст вам проекты:
Как видите, для одного головного проекта я добавил три языка, и Resource DLL Wizard создал мне три дополнительных проекта. В каждом проекте есть несколько файлов - по одному на каждый модуль с DFM или блоком resourcestring.
Вы можете запускать процедуру добавления нового языка в любой момент. Просто выбирайте эту же команду, щёлкайте на дополнительном языке - и Next/Next/Next. Также, язык можно удалить, если он стал не нужен - в том же меню есть команда Remove (она не удаляет файлы с диска, а просто удаляет проект из группы проектов).
Translation Editor/перевод проектов
После добавления языков в проект, вы можете использовать Translation Editor для просмотра и редактирования VCL-форм и строковых ресурсов.Проще всего дважды щёлкнуть по нужному файлу (dfm или rc) в проекте ресурсной DLL. Откроется такое окно:
Как вы видите, у вас есть несколько колонок с информацией о данных для локализации. Вы можете скрыть ненужные колонки, сортировать по любой колонке - делается это кнопками на тулбаре. Понятно, что самые важные колонки - это оригинал и перевод. Просто щёлкайте по ячейке, которую вы хотите перевести и вводите текст.
Также вы можете фильтровать строки таблицы (скажем, показать только не переведённые), менять статус строк, производить поиск (перейти к следующей не переведённой строке), переключать редактор в single line/multi line - всё это делается кнопками на тулбаре и из контекстного меню. Дополнительно, вы можете снабжать строки комментариями для переводчика.
Обратите внимание, что вы можете редактировать не только строки, но и любые другие свойства компонентов. Это важно, если в каком-то языке у вас не влезает какая-то надпись - в этом случае вы просто раздвигаете компоненты, чтобы она влезла. Две кнопки с рисунком форм в верхнем тулбаре позволяют вам показать исходную и транслированную версии форм. Т.е. вы можете посмотреть, как будет выглядеть окно, не запуская программу на выполнение. Более того, нажав на кнопку "Stay on top" чуть правее, вы можете зафиксировать формы, пока вы выполняете перевод. Любое изменение в Translation Editor тут же отображается на предпросмотре форм. Более того, вы можете не вводить свойства, а просто таскать компоненты по форме:
Очень удобно, надо сказать. Понятно, что если вы откроете на редактирование rc-файл, то никаких форм там не будет - просто строки и инструменты для строк.
В конце не забудьте нажать кнопочку Save (вы также можете настроить авто-сохранение в настройках ITE).
Translation Repository/использование хранилища
Translation Repository позволяет вам хранить базу данных переводов, которая разделяется между всеми проектами (и может разделяться несколькими разработчиками). Работая в Translation Editor, вы можете извлекать уже переведённые строки из хранилища Translation Repository и сохранять в него новые переводы.По умолчанию, каждый раз, когда вы создаёте/обновляете ресурсный модуль, все значения в нём извлекаются из словаря Translation Repository. Если же перевода в нём нет, то используется базовый вариант (строка из главного проекта). Вы также можете редактировать переводы прямо в Translation Repository через его собственный интерфейс:
Translation Repository хранит свои данные в XML-файле с расширением
.tmx
. По умолчанию, БД Translation Repository хранится в файле
default.tmx
в папке RAD Studio\bin
. К сожалению, это не слишком удачное место, потому что часто запись в эту папку запрещена. Поэтому я рекомендую вам сразу же скопировать этот файл куда-то в более пригодное место - в Мои документы или Application Data. Сделать это можно командой "Save as" в окне Translation Repository. Кроме того, этот файл указывается в диалоге настроек Translation Repository Options (Tools > Options > Translation Tools Options > Repository). External Translation Manager/делегация перевода другому человеку
ETM доступен только в новых Delphi (начиная с D2009 - скажите спасибо введению поддержки Unicode). Вы можете использовать ETM, если вам недоступна Delphi. Например, для выполнения перевода переводчиком, а не программистом ;)Подготовка ETM
Чтобы использовать ETM как автономное приложение, вам надо просто скопировать необходимые файлы на другую машину и зарегистрировать *.bpl в ETM:1. Создайте в любом месте папку для файлов ETM.
2. Скопируйте следующие обязательные файлы из вашей папки RAD Studio\n.n\Bin в папку, созданную на шаге 1:
- etm.exe
- designide<nnn>.bpl
- dfm<nnn>.bpl
- itecore<nnn>.bpl
- rc<nnn>.bpl
- vclide<nnn>.bpl
- dcl31w<nnn>.bpl
- dclact<nnn>.bpl
- dclado<nnn>.bpl
- dclbcbsmp<nnn>.bpl
- dclbde<nnn>.bpl
- dclDataSnapIndy10ServerTransport<nnn>.bpl
- dclDataSnapProviderClient<nnn>.bpl
- dclDataSnapServer<nnn>.bpl
- dcldb<nnn>.bpl
- dcldbx<nnn>.bpl
- dcldbxcds<nnn>.bpl
- dclib<nnn>.bpl
- dclIe<nnn>.bpl
- dclIndyCore<nnn>.bpl
- dclIndyProtocols<nnn>.bpl
- dclmcn<nnn>.bpl
- dclmid<nnn>.bpl
- dclnet<nnn>.bpl
- dclribbon<nnn>.bpl
- dclsmp<nnn>.bpl
- dclstd<nnn>.bpl
- dclTee8<nnn>.bpl
- dcltouch<nnn>.bpl
- dclwbm<nnn>.bpl
- rtl<nnn>.bpl
- vcl<nnn>.bpl
- vclactnband<nnn>.bpl
- vclimg<nnn>.bpl
- vclx<nnn>.bpl
- xmlrtl<nnn>.bpl
- *<nnn>.bpl
3. Упакуйте папку с ETM в архив и передайте человеку, который будет выполнять перевод.
P.S. Не спрашивайте меня, зачем нужны опциональные файлы - я понятия не имею, а справка молчит. Но есть подозрение, что это надо для приложений с пакетами. Хотя зачем - непонятно.
Установка ETM
1. Распакуйте архив с ETM в любую папку. Предпочтительно - в C:\Program Files\.2. Запустите ETM, дважды щёлкнув по etm.exe.
3. Выполните команду Tools > Options > Packages.
4. Нажмите кнопку "Add".
5. В открывшемся диалоге перейдите в папку с ETM и выберите все design-time пакеты (dcl*.bpl).
Подготовка проекта для перевода в ETM
Разработчик должен создать в проекте языки, как я описывал это выше. Файлы ресурсных DLL сохраняются в отдельных папках.Для перевода проекта, вам нужно передать переводчику файлы:
- Папку с ресурсной DLL для конкретного языка
- Файл проекта dproj/cbproj
- Хранилище переводов (*.tmx)
Использование ETM для перевода проектов
Собственно, вы запускаете ETM, открываете в нём файл проекта или группу проектов (как обычно: File/Open) и начинаете редактировать:Вкладка Project содержит настройки проекта. Вкладка Workspace - рабочие данные. Выбрав корневой узел, вы получите статистику, а выбрав конкретный файл - увидите обычный Translation Editor:
Вы должны выполнить перевод, сохранить его и отправить изменённые файлы (папку с ресурсной DLL) разработчику. Разработчик перезапишет свои файлы вашей версией и скомпилирует ресурсную DLL с локализованной версией программы.
Deploy проекта
Для этого вам надо сделать полную сборку всех проектов, включая ресурсные DLL. Желательно перед сборкой также выполнить обновление ресурсных DLL (см. ниже).В результате вы получите главный файл (exe или DLL) и по файлу на каждый язык. Эти файлы являются DLL, но имеют расширения, обозначающие язык. Например, Project1.exe и Project1.RUS, Project1.JPN и Project1.ITA (в некоторых случаях расширение может иметь вид en-US). Вы можете распространять все файлы вместе или же распространять только exe, а файлы языков выложить для отдельного скачивания - ваш выбор.
В любом случае, чтобы это работало, достаточно просто скинуть файлы языков в ту же папку, что и программа, и они автоматически подцепятся при следующем запуске программы.
К сожалению, выделить отдельную папку для хранения языковых файлов (типа подпапки Languages) нельзя (ну, не используя хаки, конечно же).
Прочее
Обновление переводов при изменениях проекта
После того, как вы закончили перевод проекта, вы можете потом ещё работать над ним и вносить какие-то изменения, в результате которых в проект добавятся новые данные для локализации (ну или удалятся). Любые такие изменения не появятся автоматически в других проектах.Чтобы обновить ресурсные DLL сохраните и скомпилируйте проект (если вы используете ETM - откройте проект заново), затем выполните команду: Project > Languages > Update localized projects (Project > Run updates в ETM). После этого ваши изменения будут отражены и в ресурсных DLL. Вы сможете дополнить перевод и пересобрать все проекты.
Файлы, создаваемые утилитами перевода
Расширение файла | Описание |
---|---|
.dfm | Утилиты перевода хранят для каждого целевого языка отдельный файл формы .dfm для каждой формы в вашем приложении. Эти файлы включают в себя данные (включая переводы строк), которые вы видите в Translation Editor. |
.rc | Resource DLL Wizard использует созданный компилятором .drc файл для создания .rc файла для каждого целевого языка. Эти .rc файлы содержат строковые ресурсы, которые были объявлены в блоках resourcestring в вашем коде. |
.tmx | Translation Repository хранит свою базу переводов в файле .tmx file. Вы можете завести больше одного словаря в нескольких .tmx файлах. |
.{bds}proj | Translation Manager хранит проекты ресурсных DLL в разных папках (указываются при создании языка), а ссылки на эти проекты хранятся в файле проекта .dproj , .cbproj или .bdsproj . |
Примечание: вам не следует редактировать эти файлы вручную.
Активный язык проекта
При запуске через F9 (Run/Run) загружается язык базового модуля. Но вы можете указать, что хотите загружать конкретный язык (ресурсную DLL). Для этого выполните команду Project > Languages > Set active. Укажите язык и при следующем запуске программы будет использоваться именно он. Для отмены этой настройки выполните эту же команду, но из списка доступных языков выберите базовый модуль.Заметьте, что эта настройка не влияет на язык, используемый при реальном выполнении программы вне отладчика на клиентских машинах. В свободном прогоне загружается ресурсная DLL, определяемая языком пользовательского интерфейса. Т.е., грубо говоря, если кнопки в MessageBox написаны на японском - будет выбран японский язык. На русском - загружаем русскую DLL. И т.д. Это не то же самое, что язык в региональных настройках, клавиатурные раскладки или язык для не-unicode программ - это всё другие, не связанные вещи. В младших редакциях Windows язык UI жёстко зашит в систему и сменить его нельзя. Хотите сменить язык - ставьте другую версию ОС. В старших - вы можете устанавливать языковые пакеты и менять язык интерфейса по требованию (MUI) - для смены языка особых прав не надо (в отличие от его установки), нужно только сделать re-logon (выйти/зайти). Более того, каждый пользователь в системе может иметь свою настройку языка интерфейса:
В большинстве случае такое поведение - это то, что вы хотите. Если пользователь работает в немецкой Windows, ему нет смысла запускать вашу программу на русском или английском. Иначе он давно бы поставил себе русскую/английскую Windows (или сменил бы язык UI, если его редакция Windows это позволяет).
Поэтому лично я считаю, что ручной выбор языка в программе - показатель того, что вы не можете написать нормально авто-определение языка, а не действительно необходимая фишка.
Тем не менее, если вы хотите это сделать, то вы можете это сделать. Для задания override на текущий язык вам нужно отредактировать ключ реестра Software\CodeGear\Locales (для старых версий Delphi - Software\Borland\Delphi\Locales или Software\Borland\Locales) в HKCU или HKLM (смотря на кого меняете настройку): добавив строковый (REG_SZ) параметр, имя которого равно полному пути к вашему exe или DLL библиотеке, а значение равно имени языка, например, ru-RU или en-US. В справке Delphi даже есть подходящая процедура для этого:
procedure SetLocalOverrides(const AFileName, ALocaleOverride: string); var Reg: TRegistry; begin Reg := TRegistry.Create; try if Reg.OpenKey('Software\CoderGear\Locales', True) then Reg.WriteString(ALocalOverride, AFileName); finally Reg.Free; end; end;
Вы можете посмотреть демонстрационную программу в комплекте демок Delphi: это проект RichEdit, в котором есть три языка (английский, французский и японский) и реализовано переключение между ними в run-time. Вы можете посмотреть, как это делается. Демку можно найти в папке демок. Это либо папка Demos в папке самой Delphi для старых Delphi, либо папка Demos в Моих документах (All users). В меню пуск на неё может быть создан ярлык. Или же вы можете её просто скачать c SVN.
Очень интересная статья, и правда, мало есть информации по "встроенному переводчику".
ОтветитьУдалитьХорошая статья. Спасибо.
ОтветитьУдалитьРодной Translation Editor мне кажется жутко не удобным. Все это даже на 22" мониторе жутко мелко и "непопадабельно".
Но зато формат dfm/dfn файлов удобен для автоматизации процесса.
Я даже умудрился полностью автоматизировать процесс, забрав переводы с Bing. Французы, по крайней мере, не жалуются на качество:)
А вообще напрашивается идея написания хорошего функционального редактора переводов.
Да нет, почему же. Ненужные колонки скрыть - и вполне нормально даже на ноутбуке. Понятно, что если при этом ещё и формы на экране, то лучше бы, конечно, два монитора.
ОтветитьУдалитьА если кажется мелко, так ведь шрифт можно побольше поставить. По-умолчанию там 8-ка. Поставьте 12-й - ячейки буду огромные.
Меня бесит только, что Ctrl + V не работает. Работает только Shift + Ins.
А вот экспорт/импорт в другие форматы не помешал бы.
При использовании данного способа наступил на грабли: если делать build проекту - то ресурсы в dll подтягиваются неправильно, если сделать compile - все в порядке. Из-за этого потерялась возможность собирать проект скриптом MSBuild.
ОтветитьУдалитьтут немного подробней (англ.)
Спасибо за ссылку.
ОтветитьУдалитьНе очень уловил, правда, в чём конкретно заключается проблема. Если сделать build и обновить ресурсные DLL - разве они не нормально соберутся после этого?
Если это баг - в QC не отправляли?
Нашёл вот такой suggestion.
ОтветитьУдалитьИли вот, кажется, эта и другие проблемы. Отчёт закрыт как "Need more info", потому что куча проблем в одном отчёте и все описаны кратко.
ОтветитьУдалитьЕсть конструктивное предложение запостить подробный отчёт по каждой найденной проблеме. Чем быстрее - тем лучше. Глядишь, исправят в D2011.
2 монитора. Не помогает. Там, где глубокая иерархия объектов ID не всегда во весь экран помещается. Поиск работает не лучшим образом. Выделения текста в ячейках- :( Постоянно промахиваешься. Подсветка т.н. непечатаемых символов не помешала бы. Переводы иногда из браузера тащить приходится...
ОтветитьУдалитьФильтр ресурсов по типу хотелось бы. Хороший импорт-экспорт в тот же Excel. Подсветка.
Много чего удобного могло бы быть...
@GunSmoker
ОтветитьУдалить>Не очень уловил, правда, в чём конкретно заключается проблема.
Похоже в том, что если в Delphi на одном и том же проекте (в котором используется не VCL компоненты) сделать build и compile мы получим разные .exe
Как обнаружил:
Есть проект с локализацией (основной язык русский, доп - английский). Английский проект DLL в актуальном состоянии. Делаем ему build.
Делаем buil основному проекту. После запуска проекта с английским интерфейсом видим что подписи на кнопках перепутаны.
Делаем compile основному проекту. После запуска подписи на кнопках в порядке.
Вот это и сломало процесс автоматической сборки.
Иными словами, проблема в том, что при автоматической сборке нельзя сделать обновление проектов ресурсных DLL?
ОтветитьУдалитьНет, проблема возникает раньше. Даже в ситуации с полностью обновленными DLL (обновил руками) мы делаем build проекту (тоже руками) - и получаем проблему.
ОтветитьУдалитьНа QC надо бы тогда отправить воспроизводимую демку.
ОтветитьУдалитьна StackOverflow видел вопрос про отличие двух билдов с одного исходника, которые поломали человеку процесс непрерывной интеграции (хэши фалов не совпадали когда делали билды с одних и тех-же исходников из svn) - там по ходу дискуссии были ссылки на QC с нерешенным и закрытым делом.
ОтветитьУдалитьПосле этого мне было лениво бодаться с производителем.
Так бинарник в любом случае совпадать не будет. Там же метки времени проставляются.
ОтветитьУдалитьТогда вопрос о чем в QC писать - что у меня локализация через ресурсы не работает или что build и compile дают разные .exe и .map файлы (и это имхо влияет на то, что при build локализация не работает)?
ОтветитьУдалитьЯ думаю, надо писать, что не работает локализация. Ибо это серьёзный баг (Basic functionality failure/Critical-Show stopper), и его, скорее всего, исправят. Главное, чтобы была воспроизводимая демка.
ОтветитьУдалитьА если писать про разницу в map-файлах, они скажут: ну и что? И буду, в общем-то, правы.
А вот то, что ID ресурсов перемешиваются - это совсем другой разговор.
В комментариях можно упомянуть доп. инфу - в том числе, любые свои подозрения и предположения.
2Alex Bozhko:
ОтветитьУдалитьЕсли у вас есть какой-либо интересный опыт по теме - им можно поделиться (например, у себя в блоге) :)
Не спрашивайте меня, зачем нужны опциональные файлы - я понятия не имею, а справка молчит.
ОтветитьУдалитьЕсть предположение, что они нужны чтобы отображать те самые превью-формочки. А то вдруг у вас в DFM какой-нибудь TJvEdit, как его отображать без соответствующей bpl-ки?
Спасибо. Очень интересно. Возьму на заметку.
ОтветитьУдалитьЯ одного не понял - как exe-файл, запущенный вне среды, определяет язык?
Когда идёт обращение к строке в таблице ресурсов, магия компилятора неявно вызывает обёртку для системной функции LoadString: LoadResString, которая вызывает LoadString с модулем, загруженным LoadResourceModule. Сам модуль для загрузки определяется функцией GetResourceModuleName, которая сначала проверяет override языка в реестре, а если его нет - берёт LANGID от функции GetUserDefaultUILanguage. Если последнее не получается - то используется аналогичная GetSystemDefaultUILanguage. Потом идёт куча проверок и поиск модулей с расширениями разных типов. В конечном итоге, если модуль не найден - возвращается базовый.
ОтветитьУдалитьЗабыта самая удобная библиотека локализации - DKLang (OpenSource).
ОтветитьУдалитьХорошо, что ее жизнь продолжается :-)
УдалитьDelphi XE10 Berlin поддерживается
https://github.com/yktoo/dklang
Очень удобная библиотека.
Спасибо огромное за статью. Действительно, о встроенном в Delphi механизме перевода в интернете намного меньше информации чем о сторонних инструментах. Настолько мало, что когда мне пришлось встраивать в свои проекты я даже не смотрел в сторону ITM, а вместо этого изучил все имеющиеся сторонние инструменты и не найдя нужного, написал свой. Сейчас я понимаю как это было глупо.
ОтветитьУдалитьВпрочем, специфика моих проектов такова, что часть форм программы хранится в базе данных, и я не уверен, что текст из этих форм можно как-то добавить к тексту сгенерированному мастером ITM.
А скажи пожалуйста, позволяет ли ITM изменять язык в процессе работы программы (без перезапуска)?
А ещё, можно ли использовать stand-alone редактор переводов, с файлами переводов из старых версий Delphi?
А самое интересно в следующем: вот предположим у меня есть форма на которой размещена одна кнопка Button1 с caption-ом "Accept" и шириной, в которую ничего кроме Accept-a не помещается. Предположим я решил перевести этот проект на русский язык. Заменил "Accept" на "Подтвердить", и увеличил ширину кнопки в переведённой версии. После этого открыл оригинальный проект, и добавил новую кнопку и изменил положение кнопки Button1. После обновил все ресурсы.
Что станет с формой и шириной кнопки Button1 в переведённом проекте?
>>> А скажи пожалуйста, позволяет ли ITM изменять язык в процессе работы программы (без перезапуска)?
ОтветитьУдалитьКак я уже сказал, сама идея смены языка UI per application выглядит довольно коряво, поэтому никаких специальных возможностей для этого в ITE нет. Можно в любой момент сменить язык вызовом SetLocalOverrides выше, но надо понимать, что изменения будут применены только во время следующей загрузке строк из ресурсов. Поэтому совместно с вызовом SetLocalOverrides нужно ещё как-то форсировать перезагрузку строк.
В общем виде эта задача решения не имеет с любым инструментом. Почему? Потому что локализованная строка может быть записана в кэш/промежуточный результат/сохранена в переменную и т.п. Мы меняем язык - это может и сменит, скажем, заголовки кнопок (положим, мы пересоздали форму), но уж никак не повлияет на внутренние переменные. Поэтому, эта идея достаточно рискована.
>>> Что станет с формой и шириной кнопки Button1 в переведённом проекте?
Останется как есть. Т.е. на локализованной форме просто появится новая (ещё не локализованная) кнопка. Первая кнопка будет иметь уже адаптированные размеры и заголовок.
P.S. По поводу "останется как есть" - если различать размер кнопки и её позицию, то кнопка изменит положение, но не размер.
ОтветитьУдалитьИными словами, если в строчке стоит статус Translated/Auto-translated - изменения в исходном проекте её не касаются. Если там стоит Untranslated - изменения исходного проекта применяются и к ресурсному модулю.
Логика достаточно прозрачная.
GunSmoker, спасибо за ответы.
ОтветитьУдалить> В общем виде эта задача решения не имеет с любым инструментом.
Зависит от устройства программы. У меня ни в одном из проектов нигде не сохраняется в переменных текст, нуждающийся в переводе. Правда там есть другие нюансы, вызванные тем же проектированием.
В ресурсной DLL созданной стандартными средствами, отсутствует версия.
ОтветитьУдалитьПричем в менеджере проектов версию указать можно, но эта информация не сохраняется.
Отправил на QC.
ОтветитьУдалитьЕщё обсуждение.
ОтветитьУдалитьДостаточно интересный локализатор Delphi Localizer от Daniel Wischnewski.
ОтветитьУдалитьДокументации нет, но есть вводный видеоролик. Решение бесплатное, понравилось красивым редактором, не зависящим от интегрированной среды
Vse uge pridumano - http://www2.multilizer.com/
ОтветитьУдалитьПро примеры забыл.
ОтветитьУдалитьИх можно взять в Borland\Delphi7\Demos\RichEdit - скомпилировать все проекты из папки RichEdit.
Это стандартный Борландовский пример локализации приложения на английский, немецкий и французский. Смена языков происходит в Run-Time.
Читал, читал, ничего не понял, не стал вчитываться. Ini файлы для локализации полностью устраивают. Создавать/переводить/загружать проще простого. Как-нибудь загляну/распечатаю для более вдумчивого чтения.
ОтветитьУдалить> Меня бесит только, что Ctrl + V не работает. Работает только Shift + Ins.
Можно использовать клавиатурный менеджер, например KeyMan и заменять одни клавиши на другие в нужных программах. С её помощью унифицировал клавиши Правка|Замена, чтобы везде работало и Ctrl+R и Ctrl+H.
Cпасибо за статью
ОтветитьУдалитьНачал локализацию.
ОтветитьУдалитьСоздал англ. ресурсы.Обновил языки.
Добавил новую форму. Обновил языки.
Но форма в языковом проекте не добавилась. Ладно, Добавил вручную.
Потом для проверки удалил ее. Обновил языки. Но форма из языкового проекта не удалилась.
(пожимает плечами)
ОтветитьУдалитьУ меня добавилось само. Но автоматом не удаляется, да. Удалял вручную.
Сейчас использую XE.
Спасибо! Очень помогла ваша статья.
ОтветитьУдалитьК сожалению, ETM сложно выдёргивать для переводчика, обучать его использованию всего этого хозяйства, все эти переключения проектов осуществлять. Даже строки все скопировать из него нельзя, как и сохранить в каком-либо формате, чтобы просто передать, например, Excel-файл переводчику.
Однако, если интересно, специально для этого я написал утилиту dfn2xls, которая просто выдирает все строки из dfn или rcn и передаёт их в Excel:
http://novikovmaxim.narod.ru/products/dfn2xls/dfn2xls.htm
Мне кажется, гораздо надёжнее отправить переводчику экселевский файл, и потом самому уже копировать переводы строк в свой проект, заодно проверяя, не удалены ли им по ошибке какие-либо управляющие символы или html-теги, если они применялись в строках.
Кроме того, возможно скоро будет готов и обратный конвертор xls2dfn, чтобы автоматизировать передачу перевода обратно в файл dfn. Мне кажется, это хорошая альтернатива выдёргиванию ETM.
Куда делся мой коммент?
ОтветитьУдалитьГм, BlogSpot в последнее время любит резать комменты, помечая их как спам. И никак это не отключить :(
ОтветитьУдалитьОн сначала появился, а потом пропал. А сейчас опять появился, спасибо за восстановление. Только вот неправильно я там ссылку прописал. Вот так будет правильно:
ОтветитьУдалитьdfn2xls
Гы. И вот сейчас тоже. Не любит он ссылки, видимо.
ОтветитьУдалитьВ установленной у меня Delphi 7 такого нет - я скачал ее из интернета. Сейчас обратил внимание, что на сайтах, где предлагают ее скачать, пишут "удалены устаревшие компоненты", и там же ITE перечисляют. Это можно где-нибудь скачать отдельно?
ОтветитьУдалитьПодскажите, не могу понять, как использовать язык, который по умолчанию, если добавляю в проект еще Английский, то все работает. Но он уже есть по умолчанию.
ОтветитьУдалитьВозникла проблема ITE с тем, что ITE использует абсолютные пути для поиска файлов языкового проекта. В результате работа нескольких разработчиков над одни проектам становится не возможной. Как его отучить от абсолютных путей?
ОтветитьУдалитьХочу предостеречь тех кто захочет рискнуть использовать ITE для локализации своих проектов. Вам гарантирована масса проблем. Команда Update Localized Project ведет себя не предсказуемо, и во многих случаях не сможет корректно обновить ресурсы. ITE полностью завязан на абсолютные пути к вашему проекту, вы не сможете перенести его на другой диск, в или в другую папку. В XE2 вас ждет еще больше сюрпризов, например вам придется отказаться от стандартных путей для вывода типа ".\$(Platform)\$(Config)" т.к. ITE не в состоянии их обрабатывать. По этим проблемам есть записи в QualityCentral Embarcadero. Но естественно на них никто не обращает внимания (некоторые висят еще с Delphi 2007).
ОтветитьУдалитьЕсли вы ищете практичный локализационный инструмент для перевода вашего сайта или мобильного приложения на другой язык, присмотритесь к https://poeditor.com/. Для достижения наилучших результатов он использует совместные переводы через веб-платформу.
ОтветитьУдалитьЭтот комментарий был удален автором.
ОтветитьУдалитьУважаемый GunSmoker, спасибо за статью, очень помогла в освоении ITE. Но осталась пара вопросов: если я в файле ресурсных строк сразу пишу на русском языке, то надо ли мне добавлять русский в опциях проекта (в случае наличия ещё нескольких языков)? Как сделать так, чтобы если текущего языка нету в моих библиотеках, то интерфейс был английский? При этом английская локализация присутствует, а в юнитах всё по русский в ресурсах. Заранее благодарен за помощь!
ОтветитьУдалитьЯзык, который вы используете в тексте самой программы - это язык по умолчанию. Именно он будет использоваться если языковая DLL не найдена.
ОтветитьУдалитьТаким образом, если вы пишете на русском, то вам не нужна DLL для русского языка. Кроме того, если язык UI не найден в ваших DLL, то будет использоваться русский.
Насколько я знаю, это поведение не изменяется.
Понятно, спасибо. В принципе, так и понял
ОтветитьУдалитьПопробовал повторить. При установке английского языка по умолчанию (Project > Languages > Set active) и запуске, выводится ошибка: resource tform not found delphi
ОтветитьУдалитьНикогда не использовал ресурсы. Тыкаюсь как слепой котенок. Может сделаете видео? Лучше один раз увидеть, чем сто раз прочитать.
ОтветитьУдалитьСпасибо за статью.
ОтветитьУдалитьИспользую следующий инструмент - http://delloc.narod.ru/index.html
Полезная вещь - свой словарь (Translation repository). Обычно закидываю туда переведенные строки (выделить сроку или несколько строк, ПКМ - Repository - Add strings to repository.). Действительно, при переносе проекта в другое место на диске проект локализации может слететь, т.к. прописаны абс. пути, и ручная правка файлов проекта (dpproj, grpiuproj и т.д.) бывало, что не помогала, т.к. пути прописаны в разных местах и не свегда всё сходу найдешь. Приходилось делать локализацию с нуля по-новой, но использование словаря крайне облегчало эту работу. Выделяешь все строки в Translation Editor и ПКМ - Repository - Get Strings from repository. Кстати, использую не словарь по-умолчанию, а свой, тематический для программ одной направленности, загрузить свой словарь можно прямо в Translation repository кнопкой "Open" и также можно его сохранить куда-нибудь.
ОтветитьУдалить>>>Александр Алексеев комментирует...
ОтветитьУдалить>>>Язык, который вы используете в тексте самой программы - это язык по умолчанию. Именно он >>>будет использоваться если языковая DLL не найдена.
>>>Таким образом, если вы пишете на русском, то вам не нужна DLL для русского языка. Кроме того, >>>если язык UI не найден в ваших DLL, то будет использоваться русский.
А как же тогда перевести тот же System.RTLConst без RUS библиотеки. Насколько я понимаю, языком по умолчанию не может быть русский.
Если вы в программе записываете русские константы, то в результате в скомпилированном файле часть строк будет на русском языке (те, что писали вы), а часть - на английском языке (код RTL, VCL и сторонних библиотек). В результате такая мешанина будет у вас "языком по умолчанию".
ОтветитьУдалитьСоответственно, локализовать строки RTL/VCL/стороннего кода можно либо использовав ресурсную DLL, либо подкладывая локализованные модули RTL/VCL/стороннего кода.
Великолепная статья. Я уже по этому поводу читал и Архангельского и справку билдеровскую, и делал даже пробные проекты под несколько локализаций но так и не мог понять смысл - как осуществляется выбор языка. Думал уже самостоятельно загружать форму из ресурсов. и ВЕДЬ НИГДЕ НЕ НАПИСАЛИ что выбираешь локализацию в винде и прога САМА переключает язык. )))))
ОтветитьУдалитьА как правильно локализовать библиотеки? Чтобы разработчики программ при использовании библиотеки могли автоматически использовать уже переведенные resourcestring константы в программе.
ОтветитьУдалитьПод библиотекой понимается DLL?
УдалитьС точки зрения Delphi IDE - это один или несколько пэкеджей т.е. bpl файлов (В исходном виде это dpk файлы которые содержат список pas файлов).
ОтветитьУдалитьВаша мысль от меня ускользает. DLL и BPL локализуются ровно так же как и EXE.
УдалитьУ меня ни в одной версии Дельфей не получилось выполнить данную операцию.
ОтветитьУдалитьПредлагаю посмотреть на попытку под XE5.
Delphi localization package
Здравствуйте. Пытаюсь разобраться с локализацией. Использую XE2. Почему-то когда запускаешь exe из Debug или Release, то не "подхватывется" локализация. Когда копируешь Prj.exe и Prj.RUS в другой каталог (или переименовываешь, например, Release в Release1), то работает. В чем может быть проблема? Такое впечатление, что система "закэшировала" где-то, что из данного каталога надо запускать англ. версию. Где это можно почистить?
ОтветитьУдалитьСам спросил, сам нашел (может кому-нибудь пригодится): нужно удалить соотв. путь из HKEY_CURRENT_USER\Software\Embarcadero\Locales
УдалитьДобрый день. Delphi7 проект средних размеров, недавно закончил перевод всех форм на английский. Обратил внимание на такую вещь, если в проекте не было добавлена форма (в составе проекта), а была использована напрямую через uses ... и Form1 := TForm.Create() то при попытке где-либо к ней обратиться, валиться с ошибкой поиска в ресурсах. То есть менеджеру трансляций должны быть известны все формы проекта (хотя я предполагал, что если какая-либо форма не переведена она будет выведена в исходном виде по-крайней мере без эксепшенов). Свои то формы я добавил, но вот у компонентов которые я использую в проекте (FastReport например) нет pas/dfm файлов, соответственно добавить в менеджер трансляций я её не могу. И теперь (после перевода) я не могу их больше использовать из-за этого эксепшена ((
ОтветитьУдалитьЭто при том, что например в FastReport есть свой механизм локализации и мне его переводить (даже если такая возможность была бы) - бессмысленно.
Я правильно понимаю, что после перевода у меня должны быть pas/dfm от всех ресурсов и ВСЁ в программе должно быть переведено?
Похоже, это проблема старых версий IDE. В новых версиях всё находит.
УдалитьНасчёт импорта перевода из сторонней библиотеки - почти всегда это будет жопа. Потому что для Delphi почти никто не использует хоть какой-то общеизвестный формат для хранения переводов. Почти все используют свои собственные форматы хранения. ITE - исключение, он использует .tmx - это формат Translation Memory eXchange.
УдалитьДумаю, может переписать стандартный конструктор TForm.Create (хотя это и моветон), но для меня сам механизм как это делает VCL/Delphi тайна во мраке..
УдалитьПроще использовать какой-нибудь другой инструмент для перевода.
Удалитьможет мне не удалось достаточно точно выразить мысль, могу пример воспроизводящий косяк выслать..
Удалитьесли интересно конечно)
Косяк в том, что в старых IDE ITE не видит ресурс формы, если эта форма не подключена к проекту явно через uses xxx in yyy. Раз не видит формы, то и ресурса для неё не создаёт.
УдалитьНикакими изменениями кода программы ("переписать стандартный конструктор TForm.Create") вы не добавите отсутствующий ресурс в скомпилированную программу. И никак не заставите ITE находить форму - за исключением, конечно же, добавления модуля формы в проект.
Новым же IDE по барабану, подключен ли модуль формы или нет. Они смотрят ресурсы в скомпилированной программе, поэтому они успешно увидят форму.
Автоматически (без хаков) не работает при использовании компонент FireDAC (Delphi 10.1 Berlin Update 2)
УдалитьПовторить: новое VCL Form Application Delphi, положить на форму FDGUIxErrorDialog; основной язык-английский, дополнительный - русский.
Установить Set active - Русский, Project Group-Build, Run - Resource TfrmFDGUIxFormsError not found
Александр, у меня вопрос:
ОтветитьУдалитьВозможно ли выполнить команду среды Update resource DLL при пакетной сборке проекта? Наблюдаю какой-то труднообъяснимый гемморой, когда в релизе слетают привязки к ресурсным строкам, несмотря на то что их я не трогал. Предполагаю что директивы условной компиляции -так влияют. Ручная команда update resource DLL и последующий ребилд ресурсов помогает, однако хз как это делать автоматически.
Ответ на этот вопрос я так и не нашёл. К сожалению, ID ресурсов меняются чаще, чем хотелось бы.
УдалитьЕсть небольшая программка, написана в Delphi 2006, понадобилось сделать её многоязычной, прочитав это блог решил использовать встроенные возможности, в качестве базового выбрал Английский, дополнительный Русский, перевел, скомпилировал, получил ресурсную DLL вида MyPrject.RUS.
ОтветитьУдалитьЗапускаю программу из среды, вижу переключением между языками при переключении Languages->Set Active...
Оставляю Languages->Set Active... русским
Запускаю из каталога проекта, все корректно, винда русская - язык русский, переношу exe-шник с ресурсной DLL в другой каталог на этой же винде, запускаю - язык английский. Меняю ради интереса расширение RUS на EN получаю русский язык....
Заново вхожу в среду, меняю Languages->Set Active... на английский, запускаю из каталога проект, вижу английский, копирую в другой каталог, опять английски... что противоречит написанному в блоге.
Что то я не понимаю как это должно работать, что я делаю не так? Или это фокусы именно Delphi 2006?
Languages/Set Active просто добавляет в реестр (ключ типа Software\CodeGear\Locales - в зависимости от вашей версии Delphi) запись "файл такой-то должен использовать такую-то локаль". Соответственно, если вы файл перемещаете - записи в реестре о вашем файле уже не будет.
УдалитьВ частности в том же ключе реестра может быть указано значение локали по-умолчанию. Обычно оно не задано - и именно поэтому используется системное. Но если оно задано, то оно оверрайдит системные настройки.
Посмотрите в модуле System функцию GetResourceModuleName. Включите Use Debug DCUs, сделайте полный Build, поставьте точку останова и отлаживайтесь. Именно эта функция определяет из какого модуля грузить ресурсы.
Статья полезная, спасибо. Но в последних версиях (например у мну Delphi 10.2) нет примера с richedit для переключения в runtime.
ОтветитьУдалитьВсе работает, но хочется еще возможность принудительного выбора языка в проге. Может дадите пример или ссылку где посмотреть пример? Поиском найти не смог, везде или сюда ссылки ведут или варианты для старых версий Delphi или сторонние компоненты. Спасибо.
Посмотрите: https://pastebin.com/dmZfhB9k
УдалитьВозникла та же проблема. Все компилируется вот оолько при запуске ругается на "Resource TfrmFDGUIxFormsError not found".
ОтветитьУдалитьDelphi 10.3, FireDAC+SQLite, FastReport