Программа с многоязычным интерфейсом

{codecitation class=»brush: pascal; gutter: false;» width=»600px»}

Hа уроке классической литературы. Учительница:

— Кто может привести пример языка, на котором никто сегодня не говорит, но который является фундаментом других языков?

Вовочка:

— HТМL.

Автор благодарит читателя этих уроков, программиста Alexey Salo за идею, без которой написание данного материала было бы невозможным. Сразу отмечу, что урок получился не совсем для начинающих, количество написанного кода в примерах испугало даже меня, но тем не менее, попробуйте разобраться. Повсюду даны описание команд, некоторые мы рассматривали в предыдущих уроках. Возможно непонятные моменты рассмотрены более подробно.

Для начала немного теории

Ваша собственная программа может быть полезна не только вам, но и вашим друзьям, организации, где вы работаете. Если вы работаете не только на себя. Живя в нашем веке компьютерных технологий, информация может распространяться с довольно большой скоростью. Примером тому служат нашумевшие недавно волны интернет-вирусов, за считанные дни облетевшие по многим серверам мира. Так же дело и обстоит с полезными программами. Отличием полезной программы от вредоносной есть сам метод переноса от компьютера к компьютеру. По степени ее уникальности и полезности она может понравиться многим. Я не буду говорить о методах рекламы программных продуктов, они такие же самые, как и реклама обычных продуктов, будь то интернет-ресурс или обычных хозяйственный товар. Дело в том, что ваша программа, выпущенная в свободное распространение (выложенная на сайте, отправленная друзьям по почте и т.п.) абсолютно независимо от вашего желания может попасть любому человеку. Этот человек может быть другой национальности, абсолютно не понимающ

ий русского языка.

Если ваша программа изначально рассчитана на свободное распространение, свободное распространение с ограниченными функциями для последующего приобретения, если ваша программа может оказаться полезной для многих (утилита, игра, экранная заставка), то надо стараться изначально ее оформлять с англоязычным интерфейсом. Все дело в том, что большинство пользователей компьютеров в полной мере или частично знакомы с английским языком. Следовательно, разобраться в такой вашей программе смогут больше человек в мире, чем, скажем в программе с белорусским языковым интерфейсом. Здесь имеется в виду ни что иное, как глобальное внедрение вашего приложения в масштабе целой планеты, а не касательно, скажем, ваших знакомых. Но делая программу, даже с языком, являющимся международным (даже с китайским языком :), трудно рассчитывать на популярность во многих странах.

Большую популярность получили программы с многоязычным интерфейсом. Я имею в виду, что описанные выше программы получают большую популярность, чем аналогичные с однотипным диалоговым языком. К примеру, это некоторые командные оболочки (FAR, Windows Commander), антивирус DrWeb, интернет броузер Opera. В таких программах нужный язык можно выбрать из списка в окнах настройки.

В большинстве случаев, весь языковой интерфейс хранится в отдельном файле. И это правильно. Не стоит загромождать исполняемый EXE файл излишком информации, а настройка и адаптация нового языка, первоначально не включенного в такой программный пакет, происходит быстро и без перекомпиляции всего проекта.

Какой же метод выбрать для хранения и последующего считывания данных из файла языков. В большей степени для этого подходит рассмотренные нами в 16 уроке ini файлы. Такие файлы легко переносятся на другой компьютер вместе с самой программой, возможно редактировать из любого текстового редактора, изменить текст может любой человек.

Различные секции в таком ini-файле будут хранить в себе отдельные языковые интерфейсы. Например, секция [RUSSIAN] будет озаглавливать русскоязычный внешний вид программы, [ENGLISH] — англоязычный, и т.д. Я думаю, что с этим проблем у пользователя не будет.

Названия хранимых параметров состоят из названия окна (формы), в котором находится компонент плюс название самого компонента. Параметр должен состоять из одного слова. Хранимая величина — текст, который отображается на экране на этом компоненте. Это может быть свойство Caption или свойство Text, в зависимости от типа (класс) компонента. Например, для компонента Button1, находящегося в окне Form1 записываемый параметр и значение выглядит:

Form1Button1=Кнопка1

В нашей программе при чтении такого параметра должна произойти замена:

Form1.Button1.Caption := ‘Кнопка1’;

Естественно, это делается автоматически для всех визуальных компонентов, на каких есть текст. Проблема может состоять в том, что таких компонентов на каждой форме может быть, скажем 200. Тогда это очень загромоздит программный код. При оперативном исправлении такой программы (добавление, удаление компонентов), необходимо будет исправлять и эту часть кода. Выходом из создавшейся проблемы может быть свойства для определенного окна ComponentCount и Components. Свойство Components позволяет через массив получить доступ к любому элементу управления формы. Свойтсво ComponentCount показывает, сколько этих элементов управления (компонентов) у нас присутствует в окне. Нам нужно будет просто организовать цикл от 1 до ComponentsCount и для каждого компонента прочитать соответствующее значение Caption или Text из INI файла.

Внутри такого цикла нужно определять тип компонента. Ведь для кнопки (Button, BitBtn, SpeedButton), метки (Label, StaticText), флажка (CheckBox, RadioButton) и пр. свойство Caption определяет текст, который будет виден на этом компоненте. Для Edit, Memo, ComboBox и пр. свойтсво Text. Следовательно, очень важно верно определить тип, выбранного из цикла компонента, чтобы в последствии правильно занести соответствующее значение в соответствующее свойство.

Следующим этапом, когда мы определили тип компонента, следует само чтение данных из ini-файла. Вот примерный кусок кода такой программы:

// если в окне есть хотя бы один элемент управления (компонент)

if ComponentCount0 then

// цикл от 1 до кол-ва компонентов

for i:=1 to ComponentCount do

// если текущий элемент является элементом класса TButton, то

if Components[i-1].ClassType = TButton then

(Components[i-1] as TButton).Caption:= ЧТЕНИЕ_ДАННЫХ_ИЗ_INI

Разъясню последнюю строчку из этого примера. Через

(Components[i-1] as TButton)

Мы получаем доступ к свойствам компонента, представляя его к классу TButton. Для этого в предпоследней строке примера мы и производим проверку класса выбранного циклом компонента. Если такую проверку не производить, то во время выполнения программы при обращении, скажем к компоненту класса TEdit к свойству Caption, появится сообщение об ошибке (У TEdit свойство Text!).

(i-1) как вы наверное уже догадались, список массива элементов управления формы начинается с нуля. А заканчивается ComponentCount-1.

Еще один нюанс. Если такой многоязычный ini-файл был потерян в процессе эксплуатации, то пользователя рискует остаться вообще без каких либо намеков в вашей программе. Поэтому рекомендую все-таки изначально давать текст вашим компонентам как в обычной программе. В случае невозможности чтения данных из INI файла текст на этих компонентах останется нетронутым.

Теперь я даю вам универсальный модуль, который должен присутствовать в каждом оконном модуле вашей программы, где необходимо менять язык по указанию пользователя. Я являюсь автором этого куска программы, поэтому, возможно, что-то можно и сократить, тем не менее…

В раздел Uses необходимо дописать модуль для работы с ini-файлами:

uses IniFiles;

Раздел public дописываем одну строку объявления процедуры:

public

{ Public declarations }

procedure ChangeLang(LangSection: string);

Сама процедура пишется изначально вручную вместе с заголовком:

procedure TForm1.ChangeLang(LangSection: string);

var

// временная числовая переменная для выборки всех компонентов

i: Integer;

LangIniFile: TIniFile;

// строковая переменная для получения каталога, где находится запущенный EXE файл

ProgramPath: string;

begin

// если в окне больше одного компонента

if ComponentCount 0 then

begin

// получаем каталог, где лежит запущенный EXE файл

ProgramPath := ExtractFileDir(Application.ExeName);

// гарантированно устанавливаем последний символ ‘\’ в конце строки

if ProgramPath[Length(ProgramPath)] ‘\’ then

ProgramPath := ProgramPath ‘\’;

// подготавливаем INI файл. Он должен иметь название lang.ini

// и должен находиться в каталоге программы

LangIniFile:=TIniFile.Create(ProgramPath ‘lang.ini’);

// читаем заголовок окна

Caption:=LangIniFile.ReadString(LangSection,name,Caption);

// перебираем все компоненты в этом окне

for i:=1 to ComponentCount do

begin

// если выбран из массива компонент Button, то изменяем текст на кнопке

if Components[i-1].ClassType = TButton then

(Components[i-1] as TButton).Caption := LangIniFile.ReadString(LangSection,

name Components[i-1].name, (Components[i-1] as TButton).Caption);

// Напомню описание функции ReadString:

// ====================================

// LangIniFile.ReadString( СЕКЦИЯ, ПАРАМЕТР, ЗНАЧЕНИЕ_ПО_УМОЛЧАНИЮ );

// 1. LangSection — передаваемый параметр в процедуру.

// В процедуру передается название секции для выбранного языка

// 2. Name Components[i-1].Name — Name — название формы,

// Components[i-1].Name — название компонента

// 3. (Components[i-1] as TButton).Caption — в случае неудачного чтения этого

// параметра из ini файла (нет такого параметра), то ничего меняться не будет

// аналогично для других типов:

if Components[i-1].ClassType = TLabel then

(Components[i-1] as TLabel).Caption := LangIniFile.ReadString(LangSection,

name Components[i-1].name, (Components[i-1] as TLabel).Caption);

if Components[i-1].ClassType = TEdit then

(Components[i-1] as TEdit).Text := LangIniFile.ReadString(LangSection,

name Components[i-1].name, (Components[i-1] as TEdit).Text);

// …

// …

// …

end;

LangIniFile.Free; // освобождаем ресурс

end;

end;

Обратите внимание, в программе два окна. В каждом модуле для каждого отдельного окна присутствует эта вышеописанная процедура.

Вместо строк // … вы можете добавлять другие типы компонентов, например, можете описать тип компонента TCheckBox, если таковой имеет место в вашей программе. В идеале описывайте по шаблону все или большинство типов компонентов, имеющиеся в наличие в delphi. Для этого вам понадобится несколько десятков строк программного кода, но зато вы гарантированно можете применять эту процедуру не только в одной вашей программе, не проверяя наличие всех используемых типов компонентов.

Напомню, аналогичную вышеприведенную процедуру, без изменений, вы должны вписать в каждый модуль вашей программы. При смене языка, например, в программе с тремя окнами (Form1, Form2, Form3) происходит следующим куском программного кода:

Form1.ChangeLang(‘RUSSIAN’);

Form2.ChangeLang(‘RUSSIAN’);

Form3.ChangeLang(‘RUSSIAN’);

Поскольку процедуру смена языка мы объявляем в разделе public, то доступ к этой процедуре мы можем получить из любого места программы. Как вы заметили из примера, для переключения на русский язык указана имя секции RUSSIAN.

Теперь рассмотрим содержание самого INI файла. Его вы должны создавать самостоятельно. Во-первых, нужно помнить правила орфографии windows ini-файла, во-вторых, названия параметров должны соответствовать имени формы плюс названия компонента, хранимое значение следует за знаком равенства. Для примера, ini-файл с двумя языками, с двумя окнами, на каждом окне находится по две кнопки.

; начало файла lang.ini

[RUSSIAN]

Form1Button1=Кнопка 1 на форме 1

Form1Button2=Кнопка 2 на форме 1

Form2Button1=Кнопка 1 на форме 2

Form2Button2=Кнопка 2 на форме 2

[ENGLISH]

Form1Button1=Button 1 on form 1

Form1Button2=Button 2 on form 1

Form2Button1=Button 1 on form 2

Form2Button2=Button 2 on form 2

; конец файла lang.ini

Кроме текста на визуальных элементах управления вашей программы, текст содержится еще в заголовке программы, в панели задач. Еще могут присутствовать всплывающие подсказки Hint. Полномасштабный перевод всех компонентов на несколько языков в ini файле может несколько затруднить сам процесс программирования. Но ради гибкости настройки интерфейса программы пожертвовуйте лишним временем. Ведь для изменения текста, добавления нового языка (возможно даже это делаете не вы) не нужно перекомпилировать проект. Это максимально упрощает языковую настройку. Можете вообще сделать ini-файл одноязычным. Перевести его можете после окончания проектирования, или вообще предоставьте это профессиональному переводчику.

Одним из слабых мест в такой программе есть необходимость помещать процедуру смены языка в каждый модуль. Если у вас в программе множество окон, это довольно сильно загромождает программный код, следовательно, увеличивает размер программы, следовательно, замедляет ее работу. Как же сделать так, чтобы весь процесс смены языка во всем приложении умещался с одной процедуре. Очень просто. А может и не просто. А в общем, через компонент Application. Он является по своей сути самой программой, значит, содержит в себе все компоненты форм (уже упоминалось в первых уроках, что сама форма и есть компонент). Таким образом:

// если в приложении есть компоненты форм (не консольное приложение)

if Application.ComponentCount 0 then

// перебираем все компоненты

for i := 1 to Application.ComponentCount do

// если выбранный компонент является подклассом окна, то

if Application.Components[i-1].ClassParent = TForm then

begin

// обработка переключения языка для этого окна

end;

Очень похоже на предыдущий пример…

Извините, что на первый взгляд эта тема урока кажется, немного сложноватой для начинающего, но тут важно разобраться в общих принципах, в нескольких свойствах, внимательно перечитать весь урок снова и все может стать понятным. Обязательно рассмотрите прилагаемый первый пример, прежде чем продолжать рассматривать этот урок далее.

Опять ищем слабые места в программе. Для начинающих программистов может быть новостью, что в одной программе не может быть двух и более окон с одинаковыми названиями. А как же дело обстоит с MDI приложениями. Дочернее окно проектируется в единственном варианте, а в внутри родительской формы, во время работы программы, оно может создаваться теоретически в неограниченном количестве. Имена же самой вновь создаваемой дочерней форме присваиваются системой автоматически. Следовательно, читать параметр из ini-файла по свойству Name не подойдет. Таким методом можно максимально прочитать язык для компонентов только для одного дочернего MDI-окна. Отсюда следует, что нужно читать данные согласно свойству ClassName, которое является уникальным для отдельного класса окна. Например, для окна Form1, являющегося главным MDI-окном такой класс TForm1. Для окна Form2, дочернего MDI-окна класс TForm2. Вот, вы наконец и узнали, что же это за такая туква Т, стоящая в начале названия компонента. Это надкласс, объединяющий однотипны

е компоненты (в том числе и окно программы) в единую группу, с одинаковыми вложенными свойствами. Это краткое описание, можно сказать, своими словами.

Эти все «наваяния» организованы во втором примере. Там смена языка происходит из одной процедуры абсолютно для всех окон. Тем самым мы уменьшили программный код за счет увеличения качества, увеличили количество вложенных циклов. Программа примера номер 2 является незаконченным каркасом MDI-приложения. Не удивляйтесь, почему программа не выполняет функции редактора.

Оъявление. Автор уроков для начинающих по delphi ищет темы, какие вам было бы интересно узнать. Свои предложения отсылайте мне, Semen’у, по адресу mailto:semen@krovatka.net?subject=предложение, указав в теме письма слово «предложение». Ваше предложение не должно быть очень сложным для программного решения, понятным для начинающего, тема не должна отклоняться от тематики ведения уроков (например, не рассматривается управление базами данных, SQL, internet и пр.). Материал, написанный по вашему предложению, ориентировочно должен быть дан в объеме одного урока. Предложение в текущий урок должно быть отправлено до пятницы.

По вашему дополнительному пожеланию ссылка на автора идеи будет помещена в начале урока. В таком случае обязательно делайте соответствующую пометку и указывайте свое имя с обратным адресом в письме с предложением.

{/codecitation}

Получить язык MS Word

{codecitation class=»brush: pascal; gutter: false;» width=»600px»}

Оформил: DeeCo

Автор: http://www.swissdelphicenter.ch

// Question:

// How can I read the default language of installed MS Word application?

// Answer:

// You may initialize Word.Application instance and read a CountryID:

var

word: OLEVariant;

begin

word := CreateOLEObject(‘Word.Application’);

CountryID := word.System.Country;

word.Quit;

word := UnAssigned;

end;

// After that check this CountryID with next values:

wdUS = $00000001;

wdCanada = $00000002;

wdLatinAmerica = $00000003;

wdNetherlands = $0000001F;

wdFrance = $00000021;

wdSpain = $00000022;

wdItaly = $00000027;

wdUK = $0000002C;

wdDenmark = $0000002D;

wdSweden = $0000002E;

wdNorway = $0000002F;

wdGermany = $00000031;

wdPeru = $00000033;

wdMexico = $00000034;

wdArgentina = $00000036;

wdBrazil = $00000037;

wdChile = $00000038;

wdVenezuela = $0000003A;

wdJapan = $00000051;

wdTaiwan = $00000376;

wdChina = $00000056;

wdKorea = $00000052;

wdFinland = $00000166;

wdIceland = $00000162;

{/codecitation}

Поддержка многоязычного интерфейса

{codecitation class=»brush: pascal; gutter: false;» width=»600px»}

Программирование на С у госслужащих — как секс у подростков:

— все об этом думают;

— все об этом говорят;

— все думают, что их ближний это делает;

— почти никто этого не делает;

— тот, кто это делает, делает это плохо;

— все думают, что в следующий раз лучше получится;

— никто не принимает мер безопасности;

— любому стыдно признаться в том, что он чего-то не знает;

— если у кого-то что-то получается, от этого всегда много шума.

Подчас бывает актуально встроить в разрабатываемую программу поддержку нескольких языков. Существует множество средств и компонентов для осуществления подобных задач. У всех этих средств один недостаток — они слишком сложны и тяжеловесны. Предлагаем рассмотреть, как можно обеспечить поддержку многоязычности используя более простой и прозрачный метод.

Первое, что нужно выяснить — это язык, на котором разрабатывать интерфейс первоначально. Есть веские причины за то, чтобы использовать для этого именно тот язык, на котором написана эта статья. Дело в том, что русский язык менее лаконичен других европейских языков. При переводе на английский или немецкий 90% фраз будет компактнее и интерфейс вашей программы искажен не будет.

Для поддержки нескольких языков предлагается следующий простой подход. Интерфейс оформляется на родном языке — русском. Для всех остальных языков составляется словарь в виде:

Строка на языке 1=Строка на языке 2

Строка на языке 1=Строка на языке 2

Например:

Файл=File

Выход=Exit

Отмена=Cancel

И так для всех ресурсов приложения. Словарь поместим в отдельный текстовый файл.

Далее, нам необходимо для каждого текстового свойства любого компонента приложения поискать перевод в нашем словаре. Здесь не обойтись без Delphi RTTI. Через Component.ClassInfo получим ссылку на информацию типа, а затем GetTypeData(TypeInf) даст нам указатель на структуру с его описанием.

TypeInf := Component.ClassInfo;

AName := TypeInf^.name;

TypeData := GetTypeData(TypeInf);

NumProps := TypeData^.PropCount;

Далее проходимся по всем свойствам данного (классового) типа:

GetMem(PropList, NumProps * sizeof(pointer));

try

GetPropInfos(TypeInf, PropList);

for i := 0 to NumProps-1 do

begin

PropName := PropList^[i]^.name;

PropTypeInf := PropList^[i]^.PropType^;

PropInfo := PropList^[i];

case PropTypeInf^.Kind of

tkString, tkLString: //… это то, что нам нужно

if PropName ‘Name’ then { Переводить свойство Name не следует }

begin

{ Получение значения свойства и поиск перевода в словаре }

StringPropValue := GetStrProp(Component, PropInfo);

SetStrProp(Component, PropInfo, TranslateString(StringPropValue));

end;

Отдельный случай — списки TStrings и коллекции типа TTReeNodes и TListItems. Их придется обработать персонально.

tkClass:

begin

PropObject := GetObjectProp(Component, PropInfo{, TPersistent});

if Assigned(PropObject)then

begin

{ Для дочерних свойств-классов вызов просмотра свойств }

if (PropObject is TPersistent) then

UpdateComponent(PropObject as TPersistent);

{ Индивидуальный подход к некоторым классам }

if (PropObject is TStrings) then

begin

for j := 0 to (PropObject as TStrings).Count-1 do

TStrings(PropObject)[j] := TranslateString(TStrings(PropObject)[j]);

end;

if (PropObject is TTreeNodes) then

begin

for j := 0 to (PropObject as TTreeNodes).Count-1 do

TTreeNodes(PropObject).Item[j].Text :=

TranslateString(TTreeNodes(PropObject).Item[j].Text);

end;

if (PropObject is TListItems) then

begin

for j := 0 to (PropObject as TListItems).Count-1 do

TListItems(PropObject).Item[j].Caption

:= TranslateString(TListItems(PropObject).Item[j].Caption);

end;

{ Здесь можно добавить обработку остальных классов }

end;

end;

Объединяя все написанное, получим компонент для перевода строковых ресурсов.

unit glLanguageLoader;

interface

{$I glDEF.INC}

uses

Windows, Messages, SysUtils, Classes, Graphics,

Controls, Forms, Dialogs, comctrls, grids;

type

TLanguageLoaderOptions = set of (lofTrimSpaces);

{опция удаления начальных и завершающих пробелов}

TglLanguageLoader = class(TComponent)

private

sl: TStringList;

FOptions: TLanguageLoaderOptions;

function TranslateString(sString: string): string;

protected

procedure UpdateComponent(Component: TPersistent); virtual;

public

{main function}

procedure LoadLanguage(Component: TComponent; FileName: string);

published

property Options: TLanguageLoaderOptions read FOptions write FOptions;

end;

procedure LoadLanguage(Component: TComponent; FileName: string;

Options: TLanguageLoaderOptions);

procedure register;

implementation

uses

TypInfo, dsgnintf;

procedure register;

begin

RegisterComponents(‘Gl Components’, [TglLanguageLoader]);

end;

{Ф-ия для загрузки словаря без предварительного создания компонента}

procedure LoadLanguage(Component: TComponent; FileName: string;

Options: TLanguageLoaderOptions);

var

LanguageLoader: TglLanguageLoader;

begin

LanguageLoader := TglLanguageLoader.Create(nil);

try

LanguageLoader.LoadLanguage(Component, FileName);

finally

LanguageLoader.Free;

end;

end;

{ TglLanguageLoader }

{ Загрузка словаря, обход указанного компонента и }

{ всех его дочерних компонентов }

procedure TglLanguageLoader.LoadLanguage(Component: TComponent; FileName: string);

procedure UpdateAllComponents(Component: TComponent);

var

i: integer;

begin

{ обработка своцств компонента }

UpdateComponent(Component);

for i := 0 to Component.ComponentCount-1 do

UpdateAllComponents(Component.Components[i]);

end;

begin

sl := TStringList.Create;

try

{ Загрузка словаря из заданного файла }

sl.LoadFromFile(FileName);

sl.Sorted := true;

UpdateAllComponents(Component);

finally

sl.Free;

end;

end;

{ Проход по всем свойствам компонента }

{ Для всех строковых свойств — загрузка перевода из сооваря }

procedure TglLanguageLoader.UpdateComponent(Component: TPersistent);

var

PropInfo: PPropInfo;

TypeInf, PropTypeInf: PTypeInfo;

TypeData: PTypeData;

i, j: integer;

AName, PropName, StringPropValue: string;

PropList: PPropList;

NumProps: word;

PropObject: TObject;

begin

{ Playing with RTTI }

TypeInf := Component.ClassInfo;

AName := TypeInf^.name;

TypeData := GetTypeData(TypeInf);

NumProps := TypeData^.PropCount;

GetMem(PropList, NumProps*sizeof(pointer));

try

GetPropInfos(TypeInf, PropList);

for i := 0 to NumProps-1 do

begin

PropName := PropList^[i]^.name;

PropTypeInf := PropList^[i]^.PropType^;

PropInfo := PropList^[i];

case PropTypeInf^.Kind of

tkString, tkLString:

if PropName ‘Name’ then { Переводить свойство Name не следует }

begin

{ Получение значения свойства и поиск перевода в словаре }

StringPropValue := GetStrProp( Component, PropInfo );

SetStrProp( Component, PropInfo, TranslateString(StringPropValue) );

end;

tkClass:

begin

PropObject := GetObjectProp(Component, PropInfo{, TPersistent});

if Assigned(PropObject)then

begin

{ Для дочерних свойств-классов вызов просмотра свойств }

if (PropObject is TPersistent) then

UpdateComponent(PropObject as TPersistent);

{ Индивидуальный подход к некоторым классам }

if (PropObject is TStrings) then

begin

for j := 0 to (PropObject as TStrings).Count-1 do

TStrings(PropObject)[j] := TranslateString(TStrings(PropObject)[j]);

end;

if (PropObject is TTreeNodes) then

begin

for j := 0 to (PropObject as TTreeNodes).Count-1 do

TTreeNodes(PropObject).Item[j].Text :=

TranslateString(TTreeNodes(PropObject).Item[j].Text);

end;

if (PropObject is TListItems) then

begin

for j := 0 to (PropObject as TListItems).Count-1 do

TListItems(PropObject).Item[j].Caption :=

TranslateString(TListItems(PropObject).Item[j].Caption);

end;

{ Здесь можно добавить обработку остальных классов }

end;

end;

end;

end;

finally

FreeMem(PropList, NumProps*sizeof(pointer));

end;

end;

{ Поиск перевода для заданной строки в словаре }

function TglLanguageLoader.TranslateString(sString: string): string;

begin

if lofTrimSpaces in Options then

sString := trim(sString);

if sString = » then

begin

Result := »;

exit;

end;

if sl.IndexOfName(sString) -1 then

Result := sl.Values[sString]

else

Result := sString;

end;

end.

{/codecitation}

Компонент TMultiProperty

{codecitation class=»brush: pascal; gutter: false;» width=»600px»}

Оформил: DeeCo

Автор: Рустам Кафаров

Kомпонент TMultiProperty предназначен для создания мультиязычных и мультиинтерфейсных приложений. Особенность его состоит в том, что конечный пользователь создаваемого продукта сможет сам изменять внешний вид программы без изменения EXE-файла. С компонентом поставляется специальный редактор MultiEditor.

Kомпонент вляется Shareware и поставляется только в спомпилированном виде. Активные тестеры будут поощраться скидками на продукт. Вопросы цены и вариантов доставки — на сайте автора. Более подробная информация там же.

Описание

Компонент TMultiProperty предназначен для создания многоязыковых и мультиинтерфейсных приложений. Он позволяет изменить свойства компонентов после компиляции проекта. Принцип его работы основан на записи перечня всех компонентов со всеми их свойствами в файл-слепок проекта и значений этих свойств в файл значений. Эту процедуру можно осуществить прямо в дизайн-тайме, а при необходимости и в ран-тайме. Файл значений представляет собой не что иное, как двоичный DFM файл, полученный при помощи последовательного вызова методов WriteComponent() объекта TWriter. Поэтому он может легко конвертироваться в текстовый формат и обратно. Для редактирования этого файла вместе с компонентом поставляется специальный редактор MultiEditor. С его помощью можно будет создавать новые файлы значений. Чтоб это можно было сделать в отсутствии самого приложения, используется файл-слепок. С его помощью пользователь сам легко сможет изменить текст в метке, или поменять положение кнопки, изменить шрифт и цвет колонки ДБ-грида и т.д. Для

работы уже самого приложения нужен лишь файл значений, а файл-слепок необходим только для его создания. Таким образом, сам конечный пользователь, даже далекий от программирования, сможет изменять внешний вид приложения БЕЗ изменения EXE-файла. Преимуществами этого продукта перед его аналогами являются:

Инструмент действует на уровне самого приложения, т.е. является его частью. Он не требует перекомпиляции или перезагрузки приложения. При этом он может управляться параметрами командной строки (временно отключаться/включаться, пересоздавать файлы, загружать файлы свойств).

Компонент не сканирует исходные коды на наличие строковых констант. Таким образом, изменять можно все свойства (кроме бинарных) любых компонентов, в том числе и нестандартных, не визуальных, со сложными вложенными объектными свойствами. При этом есть возможность фильтровать компоненты по именам и классам на момент создания файла-слепка.

Создание файлов значений, т.е. непосредственный перевод, может осуществляться непосредственно самим пользователем. При этом программистом могут накладывать ограничения на возможности пользователя редактировать те или иные значения свойств. Свойства или целые компоненты можно сделать невидимыми для пользователя или только для чтения.

Формат файлов значений является стандартным форматом DFM-файлов, и может использоваться и другими программами. К тому же размер их достаточно мал. Следует учесть, что хоть размер файла-слепка сравнительно большой (достигает десятков мегабайт для достаточно большого приложения).

Загрузка файла значений может происходить при инициализации приложения (свойство ValueFile читается из DFM, реестра, командной строки), либо в любой момент выполнения программы.

Скачать компонент:

С домашней страницы автора:

Скачать shareware версию компонента (67К)

Скачать shareware версию редактора (291К)

{/codecitation}

Как узнать, какой язык активен в Windows

{codecitation class=»brush: pascal; gutter: false;» width=»600px»}

Программиста спрашивают:

— Как вам удалось так быстро выучить английский язык?!!

— Да, ерунда какая. Они там почти все слова из Delphi взяли.

function WhichLanguage:string;

var

ID: LangID;

Language: array [0..100] of char;

begin

ID := GetSystemDefaultLangID;

VerLanguageName(ID, Language, 100);

Result := string(Language);

end;

procedure TForm1.Button1Click(Sender: TObject);

begin

Edit1.Text := WhichLanguage;

end;

{/codecitation}

Как программно переключить раскладку клавиатуры

{codecitation class=»brush: pascal; gutter: false;» width=»600px»}

Microsoft выпустила новую клавиатуру с дополнительной педалью, которая нажимает сразу три клавиши, и новый корпус, у которого по кнопке Reset можно бить кулаком.

//На русский

procedure TForm1.Button1Click(Sender: TObject);

var

Layout: array[0.. KL_NAMELENGTH] of char;

begin

LoadKeyboardLayout( StrCopy(Layout,’00000419′),KLF_ACTIVATE);

end;

//На английский

procedure TForm1.Button2Click(Sender: TObject);

var

Layout: array[0.. KL_NAMELENGTH] of char;

begin

LoadKeyboardLayout(StrCopy(Layout,’00000409′),KLF_ACTIVATE);

end;

{/codecitation}

Как проводить локализацию своих приложений 2

{codecitation class=»brush: pascal; gutter: false;» width=»600px»}

В Delphi 3 и 4 есть специальные механизмы, позволяющие приложение «переделать» на любой язык после компиляции. Для D3 надо посмотреть в хелпе, по-моему, internationalization или что-то в этом роде. Для D4 вообще все делается ОЧЕHЬ просто:

берется проект, компилируется;

тут-же, не закрывая проект, вызвается New|Resource DLL Wizard, в нем указывается, какие формы и модули должны подвергнуться переводу на другой язык;

в результате работы Wizard появляется проект (sic!) с RC и DFM. Открываем формы, и переделываем все сообщения размер (соотв. длине сообщений);

Компилируем. В результате получается файл xxxxxxx.rus, где xxxxxxx — название исходного проекта;

Запускаем xxxxxxx.exe. Видим некий не наш язык. Подкладываем в каталог с этим exe изготовленный файл xxxxxxx.rus, и запускаем exe повторно. Видим абсолютно ВЕЗДЕ переведенные сообщения.

p.s. файл RUS можно подставлять и убирать по вкусу.

{/codecitation}

Как проводить локализацию своих приложений

{codecitation class=»brush: pascal; gutter: false;» width=»600px»}

Автор: Dmitry Kuzmenko

Несколько перлов из жизни российских компьютерных переводчиков:

These ports are not supported — Эти порты не поддерживаются.

Assign nickname to authority — Дать кличку авторитету.

3-character resource type — Трехбуквенный тип ресурса.

В Delphi есть специальные механизмы, позволяющие приложение «переделать» на любой язык после компиляции. Для D3 надо посмотреть в хелпе, по-моему, internationalization или что-то в этом роде. Для D4 вообще все делается ОЧЕНЬ просто:

берется проект, компилируется

тут-же не закрывая проект вызвается New|Resource DLL Wizard в нем указывается какие формы и модули должны подвергнуться переводу на другой язык.

в результате работы Wizard появляется проект (!) с RC и DFM. Открываем формы, и переделываем все сообщения размер (соотв. длине сообщений). Компилируем. В результате получается файл xxxxxxx.rus, где xxxxxxx — название исходного проекта.

Запускаем xxxxxxx.exe. Видим некий не наш язык. Подкладываем в каталог с этим exe изготовленный файл xxxxxxx.rus, и запускаем exe повторно. Видим абсолютно ВЕЗДЕ переведенные сообщения.

p.s. файл RUS можно подставлять и убирать по вкусу.

В Delphi3. Вот, случайно набpели в хэлпе. Если нужно изменить pесуpсы какого-либо модуля, то это можно делать с помощью нехитpой опеpации:

Вынимаете pесуpсы из этого модуля.

Пеpеводите их на дpугой язык. (напpимеp pусский)

Создаете в Delphi свой пpоект Dll-ки (с именем того модуля, из котоpого вы вынули pесуpсы, напpимеp vcl30), в котоpый включаете _пеpеведенные_ pесуpсы: {$R vcl30rus.res}

Собиpаете все это.

Пеpеименовываете полученную vcl30.Dll в vcl30.rus и кидаете ее в System.

Если вы хотите, пpиложение «говоpило» по pусски только тогда, когда в pегиональных установках стоит Russia — то тогда это все. Если же вы хотите, чтобы ваше пpиложение _всегда_ поднимало pусские pесуpсы, то необходимо сделать следующее добавление в Registry: HKEY_CURRENT_USER\SOFTWARE\Borland\Delphi\Locales «X:\MyProject\MyApp.exe» = «rus»

Тепеpь, когда ваше пpиложение будет поднимать pakages, то всегда будут бpаться pусские pесуpсы. Дpугие пpиложения, напpимеp Delphi — это не затpонет. Таким обpазом можно заменять даже DFM-ки из пpоекта.

Более подpобно об этом — см Help — Index — Localizing…

{/codecitation}

Как отловить изменение раскладки клавиатуры

{codecitation class=»brush: pascal; gutter: false;» width=»600px»}

Автор: Александр

Клавиатура Калашникова: Del-Del-Del-Del-…..

Нужно ловить сообщение WM_INPUTLANGCHANGEREQUEST

или

procedure TForm1.Timer1Timer(Sender: TObject);

var

Layout: array [0.. KL_NAMELENGTH] of char;

begin

GetKeyboardLayoutName(Layout);

if Layout = ‘00000409’ then

label1.caption:=’en’

else

label1.caption:=’ru’;

end;

{/codecitation}

Как из программы переключать языки

{codecitation class=»brush: pascal; gutter: false;» width=»600px»}

Объявление в газету в разделе трудоустройство. Для ухода за пожилым программистом требуется приятная женщина, говорящая на FORTRAN, BASIC и С .

procedure SetRU;

var

Layout: array [0.. KL_NAMELENGTH] of char;

begin

LoadKeyboardLayout(StrCopy(Layout, ‘00000419’), KLF_ACTIVATE);

end;

procedure SetEN;

var

Layout: array [0.. KL_NAMELENGTH] of char;

begin

LoadKeyboardLayout(StrCopy(Layout, ‘00000409’), KLF_ACTIVATE);

end;

или

var

rus, lat: HKL;

rus := LoadKeyboardLayout(‘00000419’, 0);

lat := LoadKeyboardLayout(‘00000409’, 0);

SetActiveKeyboardLayout(rus);

{/codecitation}