Top.Mail.Ru
Мастера DELPHI, Delphi programming community Титульная страница Поиск, карта сайта Написать письмо 
| Новости | Поиск | FAQ | Статьи | Книги | Новости VCL |
| Форумы ЧАТ | Орешник | KOL и MCK |
Форумы -» Основная («Начинающим»)/ Базы / WinAPI / Компоненты / Сети / Media / Игры / Corba и COM / KOL / FreePascal / .Net / Прочее / RSDN.ORG

« предыдущая ветвь | форум | следующая ветвь »

Мультиязычность интерфейса. [D5, WinME, NT4, Win2k, WinXP]


d_xaos ©   (2007-01-02 14:55:00) 

Появилась необходимость перевести софтину на другие языки. Решил пойти по пути наименьшего сопротивления:

1) Создал lng файл следующего вормата
название_формы - название_компонента - название_Properties - значение

2) при загрузке считываю считываю строки и в строковых переменных Form_name, component_name, Properties_name, Properties_value имею всё, что надо.

3) При помощи FindComponent можно менять всё, но там надо явно указывать:

 With TLabel(Form1.FindComponent(component_name)) do begin
        caption:=Some_Value;
        hint:=Some_Value;
 end;

Вопрос в следующем:
Как имея в переменных нужные значения, поменять свойства компонентов на форме?


Дмитрий Белькевич   (2007-01-05 04:35:00) [1]

p.p.s. почти ровный. Код отдаю за фри, можете пользовать где хотите (если хоть кто-то на это смотрит), насколько знаю, Igor Izotov его отдавал так же. Буду _очень_ признателен за найденные глюки (код релизный, но мало ли) и дополнения.
Выдирает все (или большинство, если что-то найдете, пишите) строковые свойства всех компонент на всех (или на нужных) формах.
Раньше переводил по словарю, но из-за специфических глюков кодировок американской винды переписан в текущий вид. Нет глюка, который (иногда) проявляется при переводах по словарю - каждое свойство переводиться отдельно, зато при изменении свойств нужно (не забывать) переводить повторно. Проблема достаточно просто решается, но не было времени заниматься. Короче, пробуйте, господа!


Дмитрий Белькевич   (2007-01-05 04:23:00) [2]

Пробуй то, чем сам перевожу. Если будут вопросы, пиши в мыло. Если найдешь глюки - пиши. Лишние компоненты/библиотеки закомменть.

Юзать в основном модуле (dpr):

Internat: TInternationalization;

for i := 0 to Screen.FormCount - 1 do
 Internat.Execute(Screen.Forms[i]);

p.s. очень предпросмотра не хватает. Вот пощу код и думаю - будет нормальный или кривой?


//--------------------------------------------------------//
//                                                        //
//  Revised:  Dmitry Belkevich                            //
//  Version: 1.3                                          //
//  Based on TInternationalisation by Igor Izotov         //
//                                                        //
//          Copyright (c) 2004, 2006 Dmitry Belkevich     //
//           http://www.makhaon.com                      //
//                                                       //
//--------------------------------------------------------//

//--------------------------------------------------------//
//                                                        //
//  Author:  Igor Izotov                                  //
//  Company: proTeam Spb.                                 //
//  Version: 1.0                                          //
//                                                        //
//          Copyright (c) 2002 proTeam Spb.               //
//           http://www.rugdesign.spb.ru                 //
//                                                        //
//--------------------------------------------------------//

//--------------------------------------------------------//
// Visual component for Internationalization feature      //
// in your application.                                   //
// Just drop this component onto your Form,               //
// start your application, close application,             //
// copy text from messages_XXX.log file into              //
// new created file messages_XXX.prop and edit this       //
// file by the adding required strings values like this:  //
//   Zoom In = Óâåëè÷èòü                                  //
//                                                        //
// XXX is an extension defines a local language setting   //
// on a working computer                                  //
//                                                        //
// En empty file messages_XXX.log means you've finished   //
// localization process                                   //
//--------------------------------------------------------//

unit Internationalization;

interface

uses
Classes,
JvPageListTreeView,
JclFileUtils,
ActnMan,
ComCtrls,
DBCtrls,
ExtCtrls,
Forms,
SysUtils,
MakStrUtils,
TypInfo,
StdCtrls,
Buttons,
CoolGrid,
Dialogs,
TBXExtItems,
Controls,
Windows;

type
TInternationalization = object
private
 FForm: TForm;
 Translated: TStringList;
 NotTranslated: TStringList;
public
 procedure Execute(Form: TForm);
 procedure CheckAndSetComponentPropertyString(var s: string; const PropName: string); overload;
 procedure CheckAndSetComponentPropertyString(const prop: string; var cm: TObject); overload;
 procedure CheckAndSetComponentPropertyStrings(prop: TStrings;const PropName: string);
 function GetString(const CompName, Value: string): string;
end;

implementation

uses DBGridEh;

procedure TInternationalization.CheckAndSetComponentPropertyString(var s: string; const PropName: string);
var
TempInt: integer;
strValue: string;
begin
if (s '') and (s '-') and not TryStrToInt(s, TempInt) then
begin
 strValue := GetString(PropName, s);
 if strValue '' then
  s := strValue;
end;
end;

procedure TInternationalization.CheckAndSetComponentPropertyString(const prop: string; var cm: TObject);
var
TempInt: integer;
strValue: string;
begin
if assigned(GetPropInfo(cm, prop)) then
begin
 strValue := GetPropValue(cm, prop);
 if (strValue '') and (strValue '-') and not TryStrToInt(strValue, TempInt) then
 begin
  strValue := GetString(FForm.Name + '.' + (Cm as TComponent).Name + '.' + prop, strValue);
  if strValue '' then
   SetPropValue(cm, prop, strValue);
 end;
end;
end;

procedure TInternationalization.CheckAndSetComponentPropertyStrings(prop: TStrings;const PropName: string);
var
i: Integer;
TempInt: integer;
Strings: TStringList;
strValue: string;
begin
Strings := TStringList.Create;
try
 Strings.Assign(prop);
 for i := 0 to Strings.Count - 1 do
 begin
  strValue := Strings[i];
  if (strValue '') and (strValue '-') and not TryStrToInt(strValue, TempInt) then
  begin
   strValue := GetString(Format('%s[%d]', [PropName, i]), strValue);
   if strValue '' then
    prop[i] := strValue;
  end;
 end;
finally
 Strings.Free;
end;
end;


Дмитрий Белькевич   (2007-01-05 04:23:00) [3]


procedure TInternationalization.Execute;
var
prop_file, prop_file_log: string;
i, j, k: Integer;
cm, cm2: TObject;
strValue: string;
s: string;
begin
prop_file := ChangeFileExt(Application.ExeName, '.itf');
prop_file_log := PathGetTempPath + ExtractFileName(ChangeFileExt(Application.ExeName, '.itf'));
if not FileExists(prop_file) then
 Exit;
Translated := TStringList.Create;
NotTranslated := TStringList.Create;
Translated.LoadFromFile(prop_file);
if FileExists(prop_file_log) then
 NotTranslated.LoadFromFile(prop_file_log);
FForm := Form;
Form.Caption := GetString(FForm.Name + '.Caption', Form.Caption);
for i := 0 to Form.ComponentCount - 1 do
begin
 try
  cm := Form.Components[i];
  CheckAndSetComponentPropertyString('Caption', cm);
  CheckAndSetComponentPropertyString('Hint', cm);
  CheckAndSetComponentPropertyString('Title', cm);
  CheckAndSetComponentPropertyString('Text', cm);
  if cm is TLabeledEdit then
  begin
   strValue := (cm as TLabeledEdit).EditLabel.Caption;
   if (strValue <> '') and (strValue <> '-') then
   begin
    strValue := GetString(FForm.Name + '.' + (Cm as TComponent).Name + '.Caption', strValue);
    if strValue <> '' then
     (cm as TLabeledEdit).EditLabel.Caption := strValue
   end;
  end;
  if cm is TActionManager then
   with TActionManager(cm) do
    for j := 0 to ActionBars.Count - 1 do
     with ActionBars[j] do
      for k := 0 to Items.Count - 1 do
      begin
       cm2 := Items[k];
      end;
  if cm is TOpenDialog then
   CheckAndSetComponentPropertyString('Filter', cm);
  if cm is TSaveDialog then
   CheckAndSetComponentPropertyString('Filter', cm);
  if cm is TLabeledEdit then
  begin
   cm2 := TLabeledEdit(cm).EditLabel;
   CheckAndSetComponentPropertyString('Caption', cm2);
  end;
  if cm is TTreeView then
   with TTreeView(cm) do
    for j := 0 to Items.Count - 1 do
    begin
     cm2 := Items[j];
     CheckAndSetComponentPropertyString('Text', cm2);
    end;
  if cm is TListView then
   with TListView(cm) do
    for j := 0 to Columns.Count - 1 do
    begin
     s := Columns[j].Caption;
     CheckAndSetComponentPropertyString(s, FForm.Name + '.' + (Cm as TComponent).Name + '.Columns[' + IntToStr(j) +
      '].Caption');
     Columns[j].Caption := s;
    end;
  if cm is TCoolGrid then
   with TCoolGrid(cm) do
    for j := 0 to Columns.Count - 1 do
    begin
     s := Columns[j].Title.Caption;
     CheckAndSetComponentPropertyString(s, FForm.Name + '.' + (Cm as TComponent).Name + '.Columns[' + IntToStr(j) +
      '].Caption');
     Columns[j].Title.Caption := s;
    end;
  if cm is TJvSettingsTreeView then
   with TJvSettingsTreeView(cm) do
    for j := 0 to Items.Count - 1 do
    begin
     s := Items[j].Text;
     CheckAndSetComponentPropertyString(s, FForm.Name + '.' + (Cm as TComponent).Name + '.Items[' + IntToStr(j) +
      '].Caption');
     Items[j].Text := s;
    end;
  if cm is TTBXComboBoxItem then
  begin
   CheckAndSetComponentPropertyStrings((cm as TTBXComboBoxItem).Strings, FForm.Name + '.' + (Cm as TComponent).Name +
    '.Strings');
  end;
  if cm is TComboBox then
  begin
   CheckAndSetComponentPropertyStrings((cm as TComboBox).Items, FForm.Name + '.' + (Cm as TComponent).Name +
    '.Items');
  end;
  if cm is TRadioGroup then
  begin
   CheckAndSetComponentPropertyStrings((cm as TRadioGroup).Items, FForm.Name + '.' + (Cm as TComponent).Name +
    '.Items');
  end;
  if cm is TTabControl then
  begin
   CheckAndSetComponentPropertyStrings((cm as TTabControl).Tabs, FForm.Name + '.' + (Cm as TComponent).Name +
    '.Tabs');
  end;
  if cm is TDBNavigator then
  begin
   CheckAndSetComponentPropertyStrings((cm as TDBNavigator).Hints, FForm.Name + '.' + (Cm as TComponent).Name +
    '.Hints');
  end;
  if cm is TDBGridEh then
  begin
   for j := 0 to (cm as TDBGridEh).Columns.Count - 1 do
   begin
    strValue := (cm as TDBGridEh).Columns[j].Title.Caption;
    if (strValue <> '') and (strValue <> '-') then
    begin
     strValue := GetString(FForm.Name + '.' + (Cm as TComponent).Name + '.Columns[' + IntToStr(j) + '].Caption',
      strValue);
     if strValue <> '' then
      (cm as TDBGridEh).Columns[j].Title.Caption := strValue;
    end;
    strValue := (cm as TDBGridEh).Columns[j].Title.Hint;
    if (strValue <> '') and (strValue <> '-') then
    begin
     strValue := GetString(FForm.Name + '.' + (Cm as TComponent).Name + '.Columns[' + IntToStr(j) + '].Hint',
      strValue);
     if strValue <> '' then
      (cm as TDBGridEh).Columns[j].Title.Hint := strValue;
    end;
   end;
  end;
 except
 end;
end;
Translated.Free;
if NotTranslated.Count > 0 then
 NotTranslated.SaveToFile(prop_file_log);
NotTranslated.Free;
end;

function TInternationalization.GetString(const CompName, Value: string): string;
var
i: Integer;
function IndexOf(const Value: string): integer;
var
 i: integer;
begin
 Result := -1;
 for i := 0 to Translated.Count - 1 do
  if Pos(Value, Translated[i]) <> 0 then
  begin
   Result := i;
   Break;
  end;
end;
begin
i := IndexOf(CompName);
if i = -1 then
begin
 NotTranslated.Add(CompName + '=' + Value);
 Result := Value;
end
else
begin
 Result := Copy(Translated[i], Length(CompName) + 2, MaxInt);
 Translated.Delete(i);
end;
end;

end.


azl   (2007-01-02 20:28:00) [4]

Palladin, это ты к чему?


Palladin   (2007-01-02 20:17:00) [5]


> [10] azl ©

с ума сойти, а что ж с простым циклом то справиться не можешь...


azl   (2007-01-02 20:05:00) [6]

Я делал для своей программы мультиязычный интерфейс. Делал все при помощи ini-файлов.
Создавал ini-файлы с нужным мне языком. Считывал, какие есть языковые файлы для создания списка доступных языков. Потом в программе из списка выбирал нужный мне язык и просто считывал из него и все.


Anatoly Podgoretsky   (2007-01-02 18:07:00) [7]

> d_xaos  (02.01.2007 18:04:08)  [8]

Изучай Дельфи в части resourcestring


d_xaos   (2007-01-02 18:04:00) [8]

Да, а как Translation Manager справляется с поиском в коде всяческих MessageBox и т.д.


d_xaos   (2007-01-02 17:55:00) [9]

Так первод на мультиязычность - это 3% от общей работы :(


Anatoly Podgoretsky   (2007-01-02 17:46:00) [10]

> d_xaos  (02.01.2007 17:33:05)  [5]

Translation Manager и никаких 50 вечеров не потребуется.


d_xaos   (2007-01-02 17:33:00) [11]

Насчёт времени - это точно. Надо за 50 вечеров перелопатить то, над чем в своё время сидел пол года :(

А есть ли толковые/вменяемые бесплатные компоненты для русификации??


Eraser   (2007-01-02 16:39:00) [12]

> [3] Джо ©   (02.01.07 16:26)

согласен,
но очень просто не хочется изобретать велосипед ) ни времени, ни желания нету ))


Джо   (2007-01-02 16:26:00) [13]

> Вопрос в следующем:
> Как имея в переменных нужные значения, поменять свойства
> компонентов на форме?

Смотри TypInfo.pas.


> [2] Eraser ©   (02.01.07 15:17)

Лучший компонент — свой собственный! :)


Eraser   (2007-01-02 15:17:00) [14]

> [1] Anatoly Podgoretsky ©   (02.01.07 15:04)

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

кстати к слову! :) вот тоже планирую реализовать поддержку нескольких языков в проекте, посоветуйте пожалуйста хороший компонент, понимаю, что на torry.net много всего есть, но хочется именно проверенное решение.


Anatoly Podgoretsky   (2007-01-02 15:04:00) [15]

Это далеко не наименьшее сопротивление.
Хочешь наименьшего сопротивления, то используй встроеный Translation Manager


Наверх

Титульная страница Поиск, карта сайта Написать письмо