• Welcome to PlanetSquires Forums.
 

multi-language apps

Started by James Fuller, December 19, 2016, 06:56:50 AM

Previous topic - Next topic

James Fuller

Jose,
  How do you code for multi-language apps?

James

José Roca

I haven't done it. Paul is using text files in his editor.

Paul Squires

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 Squires
PlanetSquires Software
WinFBE Editor and Visual Designer

James Fuller

Paul,
  Fine for menu items but what about widget text ;button/label..... where the text might be longer than the widget?

James

Paul Squires

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.
Paul Squires
PlanetSquires Software
WinFBE Editor and Visual Designer

Pierre Bellisle

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