6

Is there a way to get a localized description of a shortcut like Ctrl+Z so that I get "Ctrl+Z" if the app runs on an English system and "Strg+Z" on a German system?

The VCL function ShortCutToText isn't internationalized. The API function GetKeyNameText is a bit better but still not perfect: If one switches the regional settings of a German XP to English (US), it still produces German texts. Besides the results are in CAPITALS which is ugly.

Clarification: I know how I can replace ShortCutToText or the Smkc* resource strings with customized versions. But to use that I need the translated strings. And I would like to get these from the OS (or similar).

Update: It looks like Microsoft expects developers to do the translation on their own - see 2. in Associating a Menu Item with an Accelerator Key. Quote:

For example, to assign CTRL+O to the Open command on the File menu, you modify the menu item’s caption so that it looks like this:

Open\tCtrl+O

The menu item in the Menu editor is updated to reflect the new caption as you type it.

Note that the shortcut is manually appended to the caption.

Uli Gerhardt
  • 13,748
  • 1
  • 45
  • 83
  • 1
    I believe that this means that owner-drawn menus will be drawn incorrectly in Delphi systems with non-English keyboards since the code in Menus.pas uses `ShortCutToText`. That's pretty lame in my view. – David Heffernan Jan 05 '11 at 14:23
  • @David: That's the reason why I ask - our non-German customers complain about Strg+Z etc. in their menus. They want at least Ctrl+Z or - even better of course - the equivalent in their own language. And this happens even with non-owner-drawn menus (with D2007). – Uli Gerhardt Jan 05 '11 at 14:40
  • What's more, if they would just stop using owner draw menus (since Vista you don't need owner draw to get glyphs drawn) then the system would just sort it out. – David Heffernan Jan 05 '11 at 14:46
  • @Ulrich It's easy enough to come up with code that will replace `ShortCutToText` with a version of your own making that uses Ctrl+ instead of Strg+ but I'm guessing you know that already. – David Heffernan Jan 05 '11 at 14:48
  • @Ulrich By owner draw I mean drawn by the Embarcadero code in Menus.pas - I don't mean drawn by you. Non-owner drawn should be drawn by the system and so should be internationalised correctly. – David Heffernan Jan 05 '11 at 15:03
  • @Ulrich Sorry, my mistake. You are quite right about non owner-draw menus. In Menus.pas the relevant line says `Caption := Caption + #9 + ShortCutToText(FShortCut);` which tallies with your MSDN link (#9=\t) – David Heffernan Jan 05 '11 at 16:01
  • Please note that changing the regional settings from German to English, will NOT affect the language of the OS/UI. The "Format" tab only affects formatting dates, times, currency, etc. The "location" tab only tells many apps where you are so they can provide local information (e.g. wheather). Even the "keyboards and Languages" tabs mostly only affects how applications treat keyboard input. MS Word once ignored languages set in documents: "If you had a Dutch keyboard you supposedly only typed Dutch texts". – Marjan Venema Jan 05 '11 at 17:00
  • @Marjan: I can't check right now, but IIRC I changed two comboboxes to "English". After a reboot e.g. Explorer and MS Office appeared in English (FireFox, Delphi and others **not**) but GetKeyNameText still returned German strings. – Uli Gerhardt Jan 05 '11 at 17:30
  • @Ulrich: yes, that's what I would expect. Explorer and MS Office would react to that, (if the multi-ui-language support is installed) but they carry their own localizations to respond to the settings. However, I assume that your OS is still in German? And if so, so would be any texts that you request through API's... – Marjan Venema Jan 05 '11 at 17:37
  • @Ulrich GetKeyNameText would return English names if you connected an English keyboard to it and correctly specified that the keyboard used an English layout. At least that's how I interpret the MSDN documentation: http://msdn.microsoft.com/en-us/library/ms646300%28v=VS.85%29.aspx – David Heffernan Jan 05 '11 at 19:11
  • @David: thanks for the link. I agree with your interpretation of it: specifying and activating(!) an English keyboard (layout) through the regional settings should make GetKeyNameText return English names. – Marjan Venema Jan 05 '11 at 19:27
  • @David, I keep hitting on your remarks on the VCL menu implementation, its weaknesses and your workarounds. How about a nice comprehensive article about that? Complete with reusable source code? :-P – Uli Gerhardt Feb 08 '13 at 10:45
  • @UliGerhardt That sounds like a good idea. I don't currently have anywhere to put that. I also have difficulty in coming up with reusable source code to workaround. My personal fix involves modifying Menus.pas. It would take a good deal of effort to come up with something that I could share easily. – David Heffernan Feb 08 '13 at 10:55
  • @David: I was afraid so, hence the smiley. :-) – Uli Gerhardt Feb 08 '13 at 11:02
  • Comment to myself - here is some of @David's code: http://stackoverflow.com/a/23254467/1431618 – Uli Gerhardt Oct 07 '15 at 13:32

2 Answers2

3

ShortCutToText uses the MenuKeyCaps array. This cannot be modified directly (because it is in the implementation of the Menus unit), but the array is filled with resourcestrings which can be translated using various translation tools.

You need to translate the SmkcCtrl resourcestring constant, which is in consts.pas (depending on your Delphi version).

[edit]

Or you can download BigProcHook.pas, which I created too hook functions and replace them by your own. You could then write an override that calls the regular ShortCutToText function and replaces the text 'Ctrl' with 'Strg' (or vice versa) without the menu even knowing it. But I would use this as a last resort only, because I think it is better to just translate the resource. If you want to use the hook, download and include the unit, and add the following code in any unit (a separate, new unit if you like).

uses
  BigProcHook, Menus;

var
  FHook: TBigProcHook;

// The replacement function
function MyShortCutToText(ShortCut: TShortCut): string;
begin
  FHook.Hooked := False;
  try
    Result := ShortCutToText(ShortCut);
    Result := StringReplace(Result, 'Ctrl', 'Whatever', []);
  finally
    FHook.Hooked := True;
  end;
end;

initialization
  FHook := TBigProcHook.Create(@ShortCutToText, @MyShortCutToText);
finalization
  FHook.Hooked := False;
  FHook.Free;
end.

It will replace Ctrl in the shortcut text with whatever text you like, without having to change any other code.

kobik
  • 21,001
  • 4
  • 61
  • 121
GolezTrol
  • 114,394
  • 18
  • 182
  • 210
  • Thanks for the link nevertheless. :-) – Uli Gerhardt Jan 16 '11 at 16:20
  • After a negligible delay :-) I just started using the BigProcHook solution because we want to translate at runtime (HookResourceString), and that doesn't work with the VCL implementation because MenuKeyCaps is an `array of string`. Thanks again! – Uli Gerhardt Jun 17 '13 at 12:08
1

I'll answer my own question so that I have something to accept: It looks like Microsoft expects developers to do the translation on their own - see 2. in Associating a Menu Item with an Accelerator Key. Quote:

For example, to assign CTRL+O to the Open command on the File menu, you modify the menu item’s caption so that it looks like this:

Open\tCtrl+O

The menu item in the Menu editor is updated to reflect the new caption as you type it.

Note that the shortcut is manually appended to the caption.

Uli Gerhardt
  • 13,748
  • 1
  • 45
  • 83