Translation of Shortcuts in Menus

All announcements, questions and issues related to the TsiLang Components Suite.
Post Reply
kai
Posts: 2
Joined: Wed Aug 06, 2003 4:40 pm

Translation of Shortcuts in Menus

Post by kai »

Hi,

I am using the TSILang and I am very satisfied with them. I have only on problem: I can't translate the Shortcut names of Menuitems. That means, that "Strg+N" is replaced with "Ctrl+N". Is there a solution for this?

Kai
isiticov
Site Admin
Posts: 2394
Joined: Thu Nov 21, 2002 3:17 pm

Post by isiticov »

Unfortunately, Borland didn't make these strings (shortcut names) used as resource strings when translating shortcut values to string representation in menus unit.
So this is possible only by altering code in standard Menus.Pas unit and translating resource strings responsible for shortcut names using resource string wizard from expert. If this is acceptable for you we can provide step by step instructions. Let us know about your decision.
stem
Posts: 3
Joined: Thu Aug 07, 2003 11:00 am

Post by stem »

Hi,

I have the same question as kai.

I've developed internationalization functions
for BCB 4 on my own which work perfectly,
even for Japanese. The language file are
simple text files which are loaded at
run-time.

The *only* thing left to translate are the
menu shortcuts.

If anybody as any ideas, let me know! :D


Thanx, Stefan
isiticov
Site Admin
Posts: 2394
Joined: Thu Nov 21, 2002 3:17 pm

Post by isiticov »

In SiComp.pas unit there is procedure called:
procedure OverwriteProcedure(OldProcedure, NewProcedure: Pointer);

Just copy it into your code and use the following example code which allows translation menu shortcuts:

Code: Select all

function SiShortCutToText(ShortCut: TShortCut): string;

implementation
{$R *.dfm}

function GetSpecialName(ShortCut: TShortCut): string;
var
  ScanCode: Integer;
  KeyName: array[0..255] of Char;
begin
  Result := '';
  ScanCode := MapVirtualKey(WordRec(ShortCut).Lo, 0) shl 16;
  if ScanCode <> 0 then
  begin
    GetKeyNameText(ScanCode, KeyName, SizeOf(KeyName));
    GetSpecialName := KeyName;
  end;
end;

function SiShortCutToText(ShortCut: TShortCut): string;
var
  Name: string;
begin
  case WordRec(ShortCut).Lo of
    $08: Name := Form1.siLang1.GetTextOrDefault('IDS_0' (* 'BkSp' *) );
    $09: Name := Form1.siLang1.GetTextOrDefault('IDS_1' (* 'Tab' *) );
    $0D: Name := Form1.siLang1.GetTextOrDefault('IDS_2' (* 'Enter' *) );
    $1B: Name := Form1.siLang1.GetTextOrDefault('IDS_3' (* 'Esc' *) );
    $20: Name := Form1.siLang1.GetTextOrDefault('IDS_4' (* 'Space' *) );
    $21: Name := Form1.siLang1.GetTextOrDefault('IDS_5' (* 'PgUp' *) );
    $22: Name := Form1.siLang1.GetTextOrDefault('IDS_6' (* 'PgDn' *) );
    $23: Name := Form1.siLang1.GetTextOrDefault('IDS_7' (* 'End' *) );
    $24: Name := Form1.siLang1.GetTextOrDefault('IDS_8' (* 'Home' *) );
    $25: Name := Form1.siLang1.GetTextOrDefault('IDS_9' (* 'Left' *) );
    $26: Name := Form1.siLang1.GetTextOrDefault('IDS_10' (* 'Up' *) );
    $27: Name := Form1.siLang1.GetTextOrDefault('IDS_11' (* 'Right' *) );
    $28: Name := Form1.siLang1.GetTextOrDefault('IDS_12' (* 'Down' *) );
    $2D: Name := Form1.siLang1.GetTextOrDefault('IDS_13' (* 'Ins' *) );
    $2E: Name := Form1.siLang1.GetTextOrDefault('IDS_14' (* 'Del' *) );
    $30..$39: Name := Chr(WordRec(ShortCut).Lo - $30 + Ord('0'));
    $41..$5A: Name := Chr(WordRec(ShortCut).Lo - $41 + Ord('A'));
    $60..$69: Name := Chr(WordRec(ShortCut).Lo - $60 + Ord('0'));
    $70..$87: Name := 'F' + IntToStr(WordRec(ShortCut).Lo - $6F);
  else
    Name := GetSpecialName(ShortCut);
  end;
  if Name <> '' then
  begin
    Result := '';
    if ShortCut and scShift <> 0 then Result := Result + Form1.siLang1.GetTextOrDefault('IDS_15' (* 'Shift+' *) );
    if ShortCut and scCtrl <> 0 then Result := Result + Form1.siLang1.GetTextOrDefault('IDS_16' (* 'Ctrl+' *) );
    if ShortCut and scAlt <> 0 then Result := Result + Form1.siLang1.GetTextOrDefault('IDS_17' (* 'Alt+' *) );
    Result := Result + Name;
  end
  else Result := '';
end;

type
    TAccessMenu = class(TMenu);

procedure TForm1.Language1Click(Sender: TObject);
begin
  siLang1.ActiveLanguage := TComponent(Sender).Tag;
  TAccessMenu(MainMenu1).UpdateItems;
end;

procedure TForm1.FormCreate(Sender: TObject);
begin
  TAccessMenu(MainMenu1).UpdateItems;
end;

initialization
  OverwriteProcedure(@ShortCutToText, @SiShortCutToText);
end.

Hope this helps.
stem
Posts: 3
Joined: Thu Aug 07, 2003 11:00 am

Post by stem »

Hi,

I've read your answer but still can't see how the
translation of menu shortcuts in general works
(especially without TsiLang components).

It would be great if you can describe the steps
I need to do a little bit more in detail.


Thanx alot and greetings from *hot* Germany

Stefan
isiticov
Site Admin
Posts: 2394
Joined: Thu Nov 21, 2002 3:17 pm

Post by isiticov »

The answer was how to translate the shortcuts using TsiLang Components Suite.
In order to translate them without using TsiLang Components Suite I can suggest the following way:
1. Delphi/C++Builder show shortcuts in menu after converting the shortcut value (word) to text prezentation.
2. This is done using ShortCutToText() function from Menus.pas unit.
3. Unfortunately, Borland didn't make the strings that are used in this conversation used as resource strings. You can see this by investigating original source code of ShortCutToText() function from Menus.pas unit.
4. So in order to translate this you can make a "hack" by overwriting the ShortcutToText() function in your executable by yours implementation. And in your implementation just use strings corresponding to shortcut modifiers as Shift, CTRL... and using your translate algorithm translate them.

OverwriteProcedure is implemented like this:

Code: Select all

procedure OverwriteProcedure(OldProcedure, NewProcedure: pointer);
var
  x: pchar;
  y: integer;
  ov2, ov: cardinal;
begin
  x := PChar(OldProcedure);
  if not VirtualProtect(Pointer(x), 5, PAGE_EXECUTE_READWRITE, @ov) then
    RaiseLastOSError;

  x[0] := char($E9);
  y := integer(NewProcedure) - integer(OldProcedure) - 5;
  x[1] := char(y and 255);
  x[2] := char((y shr 8) and 255);
  x[3] := char((y shr 16) and 255);
  x[4] := char((y shr 24) and 255);

  if not VirtualProtect(Pointer(x), 5, ov, @ov2) then
    RaiseLastOSError;
end;
stem
Posts: 3
Joined: Thu Aug 07, 2003 11:00 am

Post by stem »

Hi!

Thanx for your answer!

I've converted your code into C++. Does anybody find any
errors? At least it compiles without errors or warnings ... :D

Code: Select all

void OverwriteProcedure(Pointer OldProcedure, Pointer NewProcedure)
{
   PChar x;
   int y;
   unsigned long ov2, ov;

   x = PChar(OldProcedure);

   if (!VirtualProtect(Pointer(x), 5, PAGE_EXECUTE_READWRITE, &ov))
      RaiseLastWin32Error();

   x[0] = char('E9');

   y = int(NewProcedure) - int(OldProcedure) - 5;

   x[1] = char(y & 255);
   x[2] = char((y >> 8) & 255);
   x[3] = char((y >> 16) & 255);
   x[4] = char((y >> 24) & 255);

   if (!VirtualProtect(Pointer(x), 5, ov, &ov2))
      RaiseLastWin32Error();
}
Unfortunately, Borland C++ Builder 4 doesn't have a file
called "Menus.pas". It only has some headers or already
compiled files.

Could anyone, please, post the ShortcutToText() function
here?

By the way: Where should I call the overwrite function?
Before any forms are created?

Thanx

Stefan
isiticov
Site Admin
Posts: 2394
Joined: Thu Nov 21, 2002 3:17 pm

Post by isiticov »

Below is example code from C++ Builder to perform discussed task:

Code: Select all

AnsiString __fastcall SiShortCutToText(TShortCut ShortCut)
{
  AnsiString res = "AAA";
  return (res);
}

void OverwriteProcedure(Pointer OldProcedure, Pointer NewProcedure)
{
   PChar x;
   int y;
   unsigned long ov2, ov;

   x = PChar(OldProcedure);

   if (!VirtualProtect(Pointer(x), 5, PAGE_EXECUTE_READWRITE, &ov))
      RaiseLastWin32Error();

   x[0] = 0xE9;

   y = int(NewProcedure) - int(OldProcedure) - 5; 

   x[1] = char(y & 255); 
   x[2] = char((y >> 8) & 255); 
   x[3] = char((y >> 16) & 255); 
   x[4] = char((y >> 24) & 255); 

   if (!VirtualProtect(Pointer(x), 5, ov, &ov2)) 
      RaiseLastWin32Error(); 
} 


void __fastcall TForm1::FormCreate(TObject *Sender)
{
  OverwriteProcedure(&ShortCutToText, &SiShortCutToText);
  // to update menu and recreate shortcuts
  MainMenu1->Images = NULL;
}
Post Reply