How to implement a multiple language interface? I don't want to make string arrays in asm files. I want to add new languages to resources and switch them when the program is running. Is there any easy way?
Posted on 2004-04-15 02:23:29 by Vaxon
The traditional approach is to use a string table resource, and LoadString at runtime. This is somewhat cumbersome in my opinion; it requires you to reserve buffers for each and every string you use, and do LoadString calls to fill them - not to mention that changing languages will involve either updating the main exe (foo!), or supplying/updating language resource DLLs (less foo, but still a bit icky).

For an engineering app I worked on with a friend, we chose to do our own language handling. The basic idea wasn't too different from the resource way, we had a list of DWORD string id's and associated stringdata (we used OEM charset, but could as well have used UNICODE). We used our own binary language file format, though.

The idea of it is to structure the file as a header with number of strings, then a (sorted by string-id) list of {string-id, offset} pairs. At runtime, the entire file can be loaded into memory, and you add the memory block address to the offsets. Presto, you have very fast lookup of strings from stringids (since the ID's are sorted, a very fast and efficient binary search can be applied).

Also, we could directly return string offsets rather than having to fill in buffers. In C code, this means we could MessageBox(hwnd, string_get(SID_HELLOWORLD), string_get(SID_INFO), MB_OK). In assembly it's still a bit more work than working with normal static strings, but it's less troublesome than the LoadString approach.

Globally used strings could of course be loaded into string pointers, so that you don't have to call string_get all the time.

The code complexity of using the strings isn't *that* different from the LoadString approach - but with use of some masm macros, you can have easy syntax like in a HLL, and things become a bit simpler than LoadString. The main advantage, however, is still that it's easier working with your own string format than stirng resources, you can do a "string builder" application and send to translators, so they don't have to mess around with resource files or whatever. Also, distribution of language files is a bit easier, and they can be smaller (you can even compress+encrypt the binaries if you want).

And no, there isn't any possible memory-overwrite problems even though pointers are returned directly... the string block buffer is VirtualAlloc'ed, and after it's written, VirtualProtect'ed to readonly.
Posted on 2004-04-15 03:45:29 by f0dder
Good idea. Thank you. I also thought that messing up with resources was a headache.
Posted on 2004-04-15 04:18:43 by Vaxon
My solution is different.
I place resources in single localized dll. These dll are reource-only, no code.
When I load the program I load this dll, with LoadLibrary.
I use load string only for few items, i.e listview column name, localized licence in the about window.

Then I have many localized dialog boxes and the localized menu.

My solution is easier to create and use than f0dder's solution, but is harder to mantain (I think).
Microsoft has a free program (rlman) to syncronize diffrerent localized resources, but it is not very good.

I also use a message table for information and errors. I use FormatMessage to load strings from the message table



HandleError proc hWnd:HWND, bKind:DWORD, ErrorNum:DWORD
LOCAL buffer[1024] : BYTE

mov eax, bKind
.if al == FROM_SYSTEM
invoke GetLastError
mov ecx, eax
invoke FormatMessage, FORMAT_MESSAGE_FROM_SYSTEM, 0, ecx, 0, addr buffer, sizeof buffer, 0
.elseif al == FROM_HMODULE
invoke FormatMessage, FORMAT_MESSAGE_FROM_HMODULE, hLang, ErrorNum, 0, addr buffer, sizeof buffer, 0
.endif

.if eax == 0 ;Error string not found
invoke GetLastError
invoke wsprintf, addr buffer, addr ErrorNumber, eax
invoke MessageBox, hWnd, addr buffer, addr AppName, MB_OK + MB_ICONERROR

.else
;Extract severity from the error number
mov eax, ErrorNum
rol eax, 2
and al, 3

.if al == 0 ;Success
mov eax, 0
.elseif al == 1 ;Informational
mov eax, MB_ICONASTERISK
.elseif al == 2 ;Warning
mov eax, MB_ICONWARNING
.else ;Error
mov eax, MB_ICONERROR
.endif
invoke MessageBox, hWnd, addr buffer, addr AppName, eax
.endif
ret
HandleError endp


If you want to download the full code of my program
http://greenant.altervista.org
Look for Gremaf
Posted on 2004-04-15 14:17:09 by greenant
Good idea also, but changing language at runtime involves recreation of all dialogs and menus.
Posted on 2004-04-16 07:40:06 by Vaxon
There is no proble if you don't have active dialog boxes during language switch.
The only thing to do is to recreate the menu and recreate listview header.

In my program I don't allow switching at runtime. You have to reload the application
Posted on 2004-04-16 14:25:24 by greenant