Jose,
How do you code for multi-language apps?
James
I haven't done it. Paul is using text files in his editor.
I save the localized names in separate text files (*.lang). Here is a sample of the start of the english.lang file:
' WinFBE localization file for ENGLISH language
'
' The first element to exist must be called "MAXIMUM" and needs to be set to
' the value of the last key in this file.
'
' This file should be created and saved using UTF-16 encoding (unicode).
'
' Each line represents a key/value pair describing the position to store the
' localized word/phrase into the localization array used by winFBE. Simple.
MAXIMUM:260
00000:&OK
00001:&Cancel
00002:&File
00003:&New
00004:&Open...
00005:&Close
00006:C&lose All
00007:&Save
00008:Save &As...
00009:Sa&ve All
00010:&Recent Files
00011:(Empty)
00012:Co&mmand Prompt...
00013:E&xit
00014:&Edit
00015:&Undo
00016:&Redo
00017:Cu&t
00018:&Copy
00019:&Paste
00020:&Delete Line
etc...
The language phrases are loaded into a shared array and accessed using a simple macro:
' Create a dynamic array that will hold all localization words/phrases. This
' array is resized and loaded using the LoadLocalizationFile function.
ReDim Shared LL(Any) As WString * MAX_PATH
' Define a macro that allows the user to specify the LL array subscript and
' also a descriptive label (that is ignored), and return the LL array value.
#Define L(e,s) LL(e)
Here is the code I use to load the file. The file to load is saved by the user through the Editor Settings:
' ========================================================================================
' Load a .lang localization file from disk and populate the localization array
' ========================================================================================
Function LoadLocalizationFile( ByVal pwszName As WString Ptr ) As BOOLEAN
' default that the file failed to load
Function = False
If pwszName = 0 Then Exit Function
If Dir(*pwszName) = "" Then Exit Function
Dim st As WString * MAX_PATH
Dim wKey As WString * MAX_PATH
Dim wData As WString * MAX_PATH
Dim nKey As Long
Dim nData As Long
Dim i As Long
Dim f As Long = Freefile
If Open( *pwszName For Input Encoding "utf16" As #f ) <> 0 Then Exit Function
Do Until Eof(f)
Line Input #f, st
If Len(st) = 0 Then Continue Do
If Left(st, 1) = "'" Then Continue Do
i = Instr(st, ":")
If i = 0 Then Continue Do
wKey = Left(st, i-1)
wData = Mid(st, i+1)
nKey = Val(wKey)
nData = Val(wData)
If Ucase(wKey) = "MAXIMUM" Then
' resize the global dynamic array
ReDim LL(nData) As WString * MAX_PATH
Else
' this should be a key/value pair line in the format:
' 00001:value
' Ensure that we add the value to the array within the valid
' boundaries of the array.
If (nKey >= LBound(LL)) AndAlso (nKey <= Ubound(LL)) Then
LL(nKey) = wData
End If
End If
Loop
Close #f
Function = True
End Function
Whenever I need to use one of the language phrases I simple use the "L" macro with the correct number specified:
Select Case ptttdi->hdr.idFrom
Case IDM_FILENEW : wszText = L(81,"New (Ctrl+N)")
Case IDM_FILEOPEN : wszText = L(82,"Open (Ctrl+O)")
Case IDM_FILESAVE : wszText = L(83,"Save (Ctrl+S)")
Case IDM_CUT : wszText = L(84,"Cut (Ctrl+X)")
Case IDM_COPY : wszText = L(85,"Copy (Ctrl+C)")
Case IDM_PASTE : wszText = L(86,"Paste (Ctrl+V)")
Case IDM_FIND : wszText = L(87,"Find (Ctrl+F)")
Case IDM_REPLACE : wszText = L(88,"Replace (Ctrl+R)")
Case IDM_UNDO : wszText = L(89,"Undo (Ctrl+Z)")
Case IDM_REDO : wszText = L(90,"Redo (Ctrl+E)")
Case IDM_INDENTBLOCK : wszText = L(91,"Indent (TAB)")
Case IDM_UNINDENTBLOCK : wszText = L(92,"UnIndent (Shift+TAB)")
Case IDM_COMMENTBLOCK : wszText = L(93,"Comment Block (Ctrl+B)")
Case IDM_UNCOMMENTBLOCK : wszText = L(94,"UnComment Block (Ctrl+Shift+B)")
Case IDM_FUNCTIONLIST : wszText = L(222,"Function List (F4)")
Case IDM_COMPILE : wszText = L(96,"Compile (Ctrl+F5)")
Case IDM_BUILDEXECUTE : wszText = L(97,"Compile and Execute (F5)")
Case IDM_RUNEXE : wszText = L(98,"Run Executable (Shift+F5)")
Case IDM_HELP : wszText = L(99,"Help (F1)")
End Select
I find this to be a really simple and easy way to do localization.
Paul,
Fine for menu items but what about widget text ;button/label..... where the text might be longer than the widget?
James
You need to make your buttons wide enough to handle the language with the longest text. I find that French seems to have long texts. I do not try to dynamically adjust the widths of GUI elements based on language text because it would be a nightmare.
As a side note, LoadString may be used to get MessageBox message string
in the language of the logged user.
Pierre
#Print Get MessageBox String in Windows user language
#Print Coded written under FreeBASIC 1.05.0
#Ifdef __FB_64BIT__
#Print 64bit compiler used
#Else
#Print 32bit compiler used
#EndIf
#lang "fb"
#Define unicode
#Include Once "windows.bi"
Const User32IdOK = 800
Const User32IdCancel = 801
Const User32IdAbort = 802
Const User32IdRetry = 803
Const User32IdIgnore = 804
Const User32IdYes = 805
Const User32IdNo = 806
Const User32IdClose = 807
Const User32IdHelp = 808
Const User32IdRepeat = 809
Const User32IdContinue = 810
Const TextLen = 4096
Dim zText As WString * TextLen
Dim sBuf As String
Dim hLib As HINSTANCE
Dim ByteCount As Long
hLib = LoadLibrary("user32.dll") '
ByteCount = LoadString(hLib, User32IdOK , @zText, TextLen) : Print "OK in local language is """ & zText & """ (Will be ""OK"" in french)"
ByteCount = LoadString(hLib, User32IdCancel, @zText, TextLen) : Print "Cancel in local language is """ & zText & """ (Will be ""Annuler"" in french)"
ByteCount = LoadString(hLib, User32IdAbort, @zText, TextLen) : Print "Abort in local language is """ & zText & """ (Will be ""&Abandonner"" in french)"
ByteCount = LoadString(hLib, User32IdRetry, @zText, TextLen) : Print "Retry in local language is """ & zText & """ (Will be ""&Recommencer"" in french)"
ByteCount = LoadString(hLib, User32IdIgnore, @zText, TextLen) : Print "Ignore in local language is """ & zText & """ (Will be ""&Ignorer"" in french)"
ByteCount = LoadString(hLib, User32IdYes, @zText, TextLen) : Print "Yes in local language is """ & zText & """ (Will be ""&Oui"" in french)"
ByteCount = LoadString(hLib, User32IdNo, @zText, TextLen) : Print "No in local language is """ & zText & """ (Will be ""&Non"" in french)"
ByteCount = LoadString(hLib, User32IdClose, @zText, TextLen) : Print "Close in local language is """ & zText & """ (Will be ""&Fermer"" in french)"
ByteCount = LoadString(hLib, User32IdHelp, @zText, TextLen) : Print "Help in local language is """ & zText & """ (Will be ""Aide"" in french)"
ByteCount = LoadString(hLib, User32IdRepeat, @zText, TextLen) : Print "Repeat in local language is """ & zText & """ (Will be ""&Recommencer"" in french)"
ByteCount = LoadString(hLib, User32IdContinue, @zText, TextLen) : Print "Continue in local language is """ & zText & """ (Will be ""&Continuer"" in french)"
Print : Print "Press a key or click to end"
Dim buttons As Long
Do
GetMouse(0, 0, 0, Buttons) : IF buttons Or Len(InKey) Then Exit Do : Sleep 200
Loop