delphi3000.com - the free delphi knowledge platform
delphi3000.com - the free delphi knowledge platform
Have a look at your member-status

connecting people's knowledge


  - Recent ArticlesRSS feed for Recent Articles on delphi3000.com
  - List of All Articles
  - Top Viewed Articles
  - Articles (+Attachem.)
  - Articles Of Interest
  - Categories
  - Top Uploader
  - Search
  - Index

  - My Home
  - Submit an Article
  - My Articles
  - My Personal Data
  - My Bookmarks
  - Activities
  - Login/Logout

  - Sign Up
  - Why Sign Up
  - Newsletter

  - Press
  - Advertise

  - Contact
  - Feedback



Community
Borland
ClubeDelphi
Dr. Bob
UK-BUG
Delphi Meetings
Planeta Delphi








Share this article with friendsShare this article with friends
Rate this articleRate this article - to keep the quality of delphi3000.com !
Comment this article or read through previous comments (5)


How to create a Multilingual ProgramGo to manfred süsens's websiteFormat this article printer-friendly!Bookmark function is only available for registered users!
display all charsets of the world
Product:
Delphi 4.x (or higher)
Category:
System
Skill Level:
Scoring:
Last Update:
01/02/2004
Search Keys:
delphi delphi3000 article borland vcl code-snippet Multilingual language multilanguage charset unicode ansicode win2k winXP
Times Scored:
5
Visits:
6853
Uploader: manfred süsens
Company: DÜRR Systems GmbH
Reference: N/A
 
Question/Problem/Abstract:
This description tells you a system to create Delphi programs also in Delphi standard version with on-line language switch over and correct display of characters even in Asia writing.
Answer:



Note: There are a lot of possibilities to change language. Also the Delphi version with language Manager and language DLL’s. The plus of my description is, you have all functions in your hand and do not need any addional software. The output is only one EXE with no DLL’s.

UpDate at 02 Jan 2004 ... please look at the end...

TMainMenu
=========
Place a new Headline named ‘Language’ and arrange all the languages you like behind, first all in English writing. This Column is the only one that will not turn, because every one has to read his own language.

TToolBar
=======
If you like, you can use only or additionaly a TToolbar with Style tbsDropDown. You have to place a TPopupMenu with the same list of Languages on the form and tell the TToolButton what its PopupMenu.

Language Unit
==========
The best is to put a new unit in the program called uLanguage. Here we have all what has to do with language turn over.

Character Converting
===============
To store all text displaying into the program code, it is necessary to convert other than ANSI character to their special code page, because you cannot place Unicode writing in program code. In DELPHI3000 is a good article describing how to do that (the article 3198 is from Daniel Wischnewski, “Converting Text for different Code Pages”). Place anywhere in your program a Button and turn him to NOT VISIBLE. This button is only for you. Behind the Button is the call:

procedure TForm1.Button1Click(Sender: TObject);
var
  InList:WideString;
  OutList:TStringList;
  F: File of WideChar;
  s:WideChar;
begin
  //This part is only for developing to translate Chinese text to
  //code page. The Button is normaly unvisible !!!!!
  //working only until 2 GB Files (because of WideString Max)!!!
  OutList:=TStringList.Create;
  InList:='';
  AssignFile(F,'Program Translation.txt');
    Reset(F);
    Read(F,S);             //we do not need the unicode file mark
    while not EOF(F) do begin
      Read(F,S);
      InList:=InList+s;
    end;
  CloseFile(F);
  OutList.Text:=TransferUnicodeToCodePage(CODEPAGE_Chinese_PRC,InList);
  OutList.SaveToFile('Program Translation CodePage.txt');
  OutList.Free;
end;

Here the little bit modified version of the article 3198:

Uses ..............., ComObj;

const
  IID_MLangConvertCharset: TGUID = '{D66D6F98-CDAA-11D0-B822-00C04FC9B31F}';
  CLASS_MLangConvertCharset :TGUID = '{D66D6F99-CDAA-11D0-B822-00C04FC9B31F}';

type
  tagCODEPAGE = UINT;

  tagMLCONVCHARF = DWORD;

  IMLangConvertCharset = interface
    ['{D66D6F98-CDAA-11D0-B822-00C04FC9B31F}']
    function Initialize(uiSrcCodePage: tagCODEPAGE; uiDstCodePage: tagCODEPAGE;
      dwProperty: tagMLCONVCHARF ): HResult; stdcall;
    function DoConversionToUnicode(pSrcStr: PChar; pcSrcSize: PUINT;
      pDstStr: PWChar; pcDstSize: PUINT): HResult; stdcall;
    function DoConversionFromUnicode(pSrcStr: PWChar; pcSrcSize: PUINT;
      pDstStr: PChar; pcDstSize: PUINT): HResult; stdcall;
  end;

  CoMLangConvertCharset = class
    class function Create: IMLangConvertCharset;
    class function CreateRemote(const MachineName: string): IMLangConvertCharset;
  end;

Const
  CODEPAGE_Thai                  : tagCODEPAGE = 0874;
  CODEPAGE_Japanese              : tagCODEPAGE = 0932;
  CODEPAGE_Chinese_PRC           : tagCODEPAGE = 0936;
  CODEPAGE_Korean                : tagCODEPAGE = 0949;
  CODEPAGE_Chinese_Taiwan        : tagCODEPAGE = 0950;
  CODEPAGE_UniCode               : tagCODEPAGE = 1200;
  CODEPAGE_Windows_31_EastEurope : tagCODEPAGE = 1250;
  CODEPAGE_Windows_31_Cyrillic   : tagCODEPAGE = 1251;
  CODEPAGE_Windows_31_Latin1     : tagCODEPAGE = 1252;
  CODEPAGE_Windows_31_Greek      : tagCODEPAGE = 1253;
  CODEPAGE_Windows_31_Turkish    : tagCODEPAGE = 1254;
  CODEPAGE_Hebrew                : tagCODEPAGE = 1255;
  CODEPAGE_Arabic                : tagCODEPAGE = 1256;
  CODEPAGE_Baltic                : tagCODEPAGE = 1257;
  CODEPAGE_MSDOS_Latin1          : tagCODEPAGE = 0850;   //Multilingual
  CODEPAGE_MSDOS_Latin2          : tagCODEPAGE = 0852;   //Slavic

  MLCONVCHARF_AUTODETECT: tagMLCONVCHARF = 1;
  MLCONVCHARF_ENTITIZE  : tagMLCONVCHARF = 2;

Implementation

class function CoMLangConvertCharset.Create: IMLangConvertCharset;
begin
  Result := CreateComObject(CLASS_MLangConvertCharset) as IMLangConvertCharset;
end;

class function CoMLangConvertCharset.CreateRemote(
  const MachineName: string): IMLangConvertCharset;
begin
  Result := CreateRemoteComObject(
    MachineName, CLASS_MLangConvertCharset) as IMLangConvertCharset;
end;

function TransferUnicodeToCodePage(ToCP:tagCODEPAGE;SText:WideString):String;
var
  Conv: IMLangConvertCharset;
  Source: PWChar;
  Dest: PChar;
  SourceSize, DestSize: UINT;
begin
  Conv := CoMLangConvertCharset.Create;
  Conv.Initialize(CODEPAGE_UniCode,ToCP, MLCONVCHARF_ENTITIZE);
  Source := PWChar(SText);
  SourceSize := Succ(Length(SText));
  DestSize := 0;
  Conv.DoConversionFromUnicode(Source, @SourceSize, nil, @DestSize);
  GetMem(Dest, DestSize);
  try
    Conv.DoConversionFromUnicode(Source, @SourceSize, Dest, @DestSize);
    result:= Dest;
  finally
    FreeMem(Dest);
  end;
end;

function TransferCodePageToUnicode(FromCP:tagCODEPAGE;QText:String):WideString;
var
  Conv: IMLangConvertCharset;
  Dest: WideString;
  SourceSize, DestSize: UINT;
begin
  Conv:= CoMLangConvertCharset.Create;
  Conv.Initialize(FromCP, CODEPAGE_UniCode, MLCONVCHARF_ENTITIZE);
  SourceSize := length(QText);
  DestSize := 0;
  Conv.DoConversionToUnicode(PChar(QText),@SourceSize,nil,@DestSize);
  SetLength(Dest,DestSize);
  try
    Conv.DoConversionToUnicode(PChar(QText),@SourceSize,PWChar(Dest),@DestSize);
  finally
    result:=Dest;
    Finalize(Dest);
  end;
end;


Collecting all your Text
=================
To get all your text, you have do a little more work. After you have finished your Form, turn it to “Viewing as Text” (right mouse key on Form). Then pick out all text lines. Create in uLanguage a procedure for language switch over and place all texts, also in program used text (use text variables) in this procedure.

The Form in text mode:

object Form1: TForm1
  Left = 80
  Top = 258
  Width = 696
  Height = 480
  Caption = 'Form1' <== you can not display window title in other code pages
  Color = clBtnFace
  Font.Charset = DEFAULT_CHARSET
  Font.Color = clWindowText
  Font.Height = -11
  Font.Name = 'MS Sans Serif'
  Font.Style = []
  OldCreateOrder = False
  PixelsPerInch = 96
  TextHeight = 13
  object Label1: TLabel
    Left = 32
    Top = 112
    Width = 32
    Height = 13
    Caption = 'Label1' <== here is one text
  end
  object Label2: TLabel
    Left = 32
    Top = 136
    Width = 32
    Height = 13
    Caption = 'Label2' <== here is one text
  end
end

In the Form you have to place a variable e.g. called “ActualLanguage”. In the procedure “TForm1.FormCreate” you have to tell each menu language line his code. Best way is to place it in TAG property.

const
  ENGLISH   = (SUBLANG_ENGLISH_US shl 10) or LANG_ENGLISH;
  Spain     = (SUBLANG_SPANISH shl 10) or LANG_SPANISH;
  Frensh    = (SUBLANG_FRENCH shl 10) or LANG_FRENCH;
  CHINESE   = (SUBLANG_CHINESE_SIMPLIFIED shl 10) or LANG_CHINESE;

procedure Tform1.FormCreate(Sender: TObject);
begin
  //adjust the program language
  English1.Tag:=ENGLISH;
  Espanol1.Tag:=Spain;
  Francais1.Tag:=Frensh;
  Chinese1.Tag:=CHINESE;
  ActualLanguage:=ENGLISH; //default
  //possible read Registry and override
end;

The click of one language will start this procedure: (Tell all languages the same procedure)

procedure Tform1.English1Click(Sender: TObject);
begin
  ActualLanguage:=TMenuItem(Sender).Tag;
  English1.Checked:=English1.Tag= ActualLanguage;
  Espanol1.Checked:=Espanol1.Tag= ActualLanguage;
  Francais1.Checked:=Francais1.Tag= ActualLanguage;
  Chinese1.Checked:=Chinese1.Tag= ActualLanguage;
  TurnLanguage(ActualLanguage);
end;

Important is to design all components with the option: “ParentFont = true”. So we have to change only the font of the form (do this for each form) to turn all components. Except you like to give a component a special looking, you have to transfer the other font properties to the component separate. A nother way I told in the article "How to change a property of all components in one time". The procedure in the unit uLanguage will turn all:

procedure TurnLanguage(Lang:Longint);
var
  NewFont:TFont;
  procedure SetFont;
  begin
    screen.MenuFont:=NewFont;
    Form1.Font:=NewFont;
  end;
begin
  NewFont:=TFont.Create;
  NewFont.Name:='MS Sans Serif';
  NewFont.Charset:=ANSI_CHARSET;
  NewFont.Size:=8;
  SetFont;
  case Lang of
    ENGLISH: with Form1 do begin
      Label1.Caption = 'Log on';
      Label2.Caption = 'Log off';
    end;
    Spain:with Form1 do begin
      Label1.Caption = 'Entre';
      Label2.Caption = 'Término de sesión';
    end;
    Frensh:with Form1 do begin
      Label1.Caption = 'Entrez';
      Label2.Caption = 'Fermeture de session';
    end;
    CHINESE: with Form1 do begin
      NewFont.Name:='SimSun';
      NewFont.Charset:= GB2312_CHARSET;
      NewFont.Size:=12;
      SetFont;
      Label1.Caption = 'Ðû²¼';
      Label2.Caption = '×¢Ïú';
    end;
  end;
  LFont.Free;
end;

A good way to do all this is, first get all text and turn them to English part. Then use Microsoft Excel and place all English texts in the first column. Fill each column for each language with the translation (use http://babelfish. altavista.com/), also with e.g. Chinese writing (Excel can use Unicode). Now make the other language parts of the procedure. To get for example Chinese to your program, save the Excel file as Unicode text file and translate this file with the code page transformation I described above. Now you can finish all languages.

Language Menu Items
================
To display the MenuItems of Languages in the own language you have to tell each MenuItem not to get the Font of the parent (“ParentFont = false”). For this special displaying you have to write your own draw procedure (OnDrawItem =
Chinese1DrawItem):

procedure TForm1.Chinese1DrawItem(Sender: TObject; ACanvas: TCanvas;
  ARect: TRect; Selected: Boolean);
var
  Text:string;
  Glyph:TBitmap;
begin
  case TMenuItem(Sender).Tag of
    ENGLISH,Spain,Frensh:Begin
      ACanvas.Font.Name:='MS Sans Serif';
      ACanvas.Font.Charset:=ANSI_CHARSET;
      ACanvas.Font.Size:=8;
    end;
    CHINESE:begin
      ACanvas.Font.Name:='SimSun';
      ACanvas.Font.Charset:=GB2312_CHARSET;
      ACanvas.Font.Size:=12;
    end;
  end;
  Text:=TMenuItem(Sender).Caption;
  with ACanvas do begin
    if Selected then begin
      Brush.Style:=bsSolid;
      Brush.Color:=clHighlight;
      Pen.Color:=clHighlight;
      Font.Color := clBtnHighlight;
    end else begin
      Pen.Color:=clBtnFace;
      Brush.Style:=bsSolid;
      Font.Color := clMenuText;
    end;
    Rectangle(ARect);
    ARect.Left:=ARect.Left+20;
    DrawText(Handle, PChar(Text), Length(Text), ARect, DT_LEFT);
    { Draw a menu check }
    if NOT TMenuItem(Sender).Checked then exit;
    Glyph := TBitmap.Create;
    try
      Glyph.Transparent:=True;
      Glyph.Handle:=LoadBitmap(0, PChar(OBM_CHECK));
      Draw(ARect.Left-18,ARect.Top,Glyph);
    finally
      Glyph.Free;
    end;
  end;
end;

Special effect: Use Windows predefined Bitmaps
====================================
You will see above "Glyph.Handle:=LoadBitmap(0, PChar(OBM_CHECK));" that I use to show the check hook a Windows predefined Bitmap (also described at article 880). In Delphi at the folder "..\Delphi5\Demos\Resxplor\resxplor.exe" you have to compile the resource explorer and start. Load your EXE and see how much predefined Bitmaps already into the program.

Use Windows predefined translations
============================
You do not need to translate names of day, month, year and so on. These did Bill Gates already for you. You have just to read them and display for example in a ComboBox:

  //fill ComboBox of Month
  ComboBox1.Items.Clear;
  for n := 1 to 12 do
    ComboBox1.Items.Add(GetLocaleStr(ActualLanguage,LOCALE_SMONTHNAME1+n-1,''));

For the ComboBox you have to use a little trick ... normally is the ComboBox a Windows Component and use the System code page. But if you say the component: "Style = csOwnerDrawFixed" the component looking for the paint procedure. This procedure is nil because we do nothing. In this case Delphi paint the component with a default procedure and use (ParentFont = True) your adjustment of code page ("TurnLanguage").

You can get more information about the Date/Time predefined names at Help "Windows SDK" and search for "LCTYPE Constants".

Using of Windows Components
=======================
Some components cannot change the code page. This is especially for CheckBox and RadioButton, because these are windows components and not paint by Delphi. So use CheckBoxList and or RadioGroup instead. Also do not use TButton, better use TBitBtn. All other can change the code page or you have to draw by yourself. Here is the example for the PageControl:

Page Control Tabs
==============
procedure TForm1.PageControl1DrawTab(Control: TCustomTabControl;
  TabIndex: Integer; const Rect: TRect; Active: Boolean);
var
  Text:string;
  R:TRect;
begin
  Text:=TPageControl(Control).Pages[TabIndex].Caption;
  R:=Rect;
  with Control.Canvas do begin
    Font:=PageControl1.Font;
    Brush.Style:=bsClear;
    Font.Color := clMenuText;
    DrawText(Handle, PChar(Text), Length(Text), R, DT_CENTER OR DT_VCENTER);
  end;
end;

Menu Item
========
We change the Menu code page in the procedure "TurnLanguage" by the line “Screen.MenuFont:=NewFont”.

Hint Window
===========
To change the Hint Window Code Page you have to link the OnShowHint-Event with your procedure ("Application.OnShowHint:=MyShowHint;") in the procedure TForm1.FormCreate". In your procedure you have to find the HintWindow and to tell him what's the font:

procedure TForm1.MyShowHint(var HintStr:string;var CanShow: Boolean;var HintInfo: THintInfo);
var
  i : integer;
begin
  for i := 0 to Application.ComponentCount - 1 do
    if application.Components[i] is THintWindow then
      with THintWindow(application.Components[i]).Canvas do begin
        case ActualLanguage of
          ENGLISH,Spain,Frensh:Begin
            Font.Name:='MS Sans Serif';
            Font.Charset:=ANSI_CHARSET;
            Font.Size:=8;
            HintInfo.HintColor := $C0FFFF;
          end;
          CHINESE:begin
            Font.Name:='SimSun';
            Font.Charset:=GB2312_CHARSET;
            Font.Size:=12;
            HintInfo.HintColor := clWhite;
          end;
        end;
      end;
end;

Message Dialogs
=============

You can use the following function to get the actual code page writing, without changing of headline and button caption. Other wise you have to design your own message form. To change all resource strings is much more work and mostly not necessary, because the every one knows what means "OK". To prevent at not translated messages use for all possibilities the TRY function and show your own message in your Message Dialog.

function MessageDlgPosSetFont(const Msg: string; DlgType: TMsgDlgType;
  Buttons: TMsgDlgButtons; HelpCtx: Longint):Integer;
begin
  with CreateMessageDialog(Msg, DlgType, Buttons) do
    try
      HelpContext := HelpCtx;
      Left:=trunc(screen.Width/2-width/2);
      Top:=trunc(screen.height/2-height/2);
      case ActualLanguage of
        ENGLISH,Spain,Frensh:Begin
          Font.Name:='MS Sans Serif';
          Font.Charset:=ANSI_CHARSET;
          Font.Size:=8;
        end;
        CHINESE:begin
          Font.Name:='SimSun';
          Font.Charset:=GB2312_CHARSET;
          Font.Size:=12;
        end;
      end;
      Result := ShowModal;
    finally
      Free;
    end;
end;

===============================================================
Update at 02 Jan 2004

To write an own Component or a complete Program in Unicode, you have to create every paint routine by yourself. To store Unicode direct in the program use the "const"-Field (look at "TimeTextChinese"). How to get the Unicode numbers? Very easy: Let it translate with e.g. "http://babelfish. altavista.com/". Then let the translated htm page display as "source text" and get the numbers out of the htm text.
Tip: The most Windows functions have also a function with same name and "W" what means Wide or Unicode.

Here is a abbreviated example:

const
  TimeTextEnglish   :Array [0..5] of WideString =
                     ('1 day','6 h','1 h','30 min','10 min','1 min');
  TimeTextSpain     :Array [0..5] of WideString =
                     ('1 Día','6 H','1 H','30 Min','10 Min','1 Min');
  TimeTextFrensh    :Array [0..5] of WideString =
                     ('1 Jour','6 H','1 H','30 Min','10 Min','1 Min');
  TimeTextChinese   :Array [0..5,0..5] of WideChar =
                     ((#$0031,#$0020,#$5929,#$0000,#$0000,#$0000),
                      (#$0036,#$0020,#$5C0F,#$65F6,#$0000,#$0000),
                      (#$0031,#$0020,#$5C0F,#$65F6,#$0000,#$0000),
                      (#$0033,#$0030,#$0020,#$5206,#$949F,#$0000),
                      (#$0031,#$0030,#$0020,#$5206,#$949F,#$0000),
                      (#$0031,#$0020,#$5206,#$949F,#$0000,#$0000));


procedure xxxxxX.DrawTheText(lang:Integer; ArrCount:Integer; Canvas: TCanvas; ARect: TRect);
var
  Text:WideString;
begin
  case lang of
    ENGLISH:Text:=TimeTextEnglish[ArrCount];
    Spain:Text:=TimeTextSpain[ArrCount];
    Frensh:Text:=TimeTextFrensh[ArrCount];
    CHINESE:Text:=TimeTextChinese[ArrCount];
  end;
  with Canvas do begin
    Font.Name:='Arial UniCode MS';
    Font.Size:=10;
    Brush.Style:=bsClear;
    Font.Color := clMenuText;
    DrawTextW(Handle,PWideChar(Text),Length(Text),ARect,DT_LEFT OR DT_VCENTER);
  end;
end;






Please rate this article!
Skill level:
BeginnerExpert

Useful:
No!Very!

Overall rating:
PoorExcellent



Comments to this article
Write a new comment
Multilingual applications
    Nenad Fidanovski (Jun 18 2002 11:17AM)

There is an easier way to do it. Well I will need a time to explain how
i have done it. Even more user can translate it at run time
ok. You have to create a database (table) like languages with
as follows
Objectname A 15
Language1   A 15
Font             I
...
Langauage 10
Font
So on right click you will show form with all translating captions
also on your right click you should enable using language 1 ... 10
So before you activate the form you show the object using the language.db depending on the language set and the fonts

Everything else I would leave for yr imanigation
Imade abv to work for Labels.

Regards,
Nenad

Respond

...smal mistake
    manfred süsens (Apr 30 2002 6:16PM)

Sorry, smal mistake in: procedure TwLangConv.bLoadAndConvertClick. I changed this today. The StrHexToInt is no more necessary.
Respond

Resourcestrings
    Magnus Flysjö (Apr 29 2002 10:19AM)

....or you can do it the normal way, by using ResourceStrings and use Borlands Translation Manager.
Respond

RE: Resourcestrings
Manfred Süsens (Apr 29 2002 5:31PM)

Yes, but not every buddy owns the enterprise version. This solution works also in standard!!!
Respond

and a very good tip for german speaking people
    manfred süsens (Apr 28 2002 7:17PM)

you will find dictionaries for a lot of languages here:
   http://www.freedict.de/wbuch/Download.html
If you copy the translate display you will have the ANSI code directly.

Respond














 
Sign up to consume product discounts for Bronze memberships !

read more


   


  Community Ad of
I. Siticov
 
   














 







     
  Copyright © 2000 - 2007 delphi3000.com - All rights reserved. Terms of use. || Privacy
delphi3000.com is a service by bluestep.com IT-Services GmbH (Vienna)