PlanetSquires Forums

Please login or register.

Login with username, password and session length
Advanced search  

Author Topic: multi-language apps  (Read 566 times)

James Fuller

  • FireFly3 Registered User
  • Senior FireFly Member
  • *
  • Posts: 272
  • FF3 User
multi-language apps
« on: December 19, 2016, 06:56:50 AM »

Josť,
  How do you code for multi-language apps?

James
Logged

Josť Roca

  • Moderator
  • Master FireFly Member
  • *****
  • Posts: 2732
    • Josť Roca Software
Re: multi-language apps
« Reply #1 on: December 19, 2016, 08:55:22 AM »

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

Paul Squires

  • Administrator
  • Master FireFly Member
  • *****
  • Posts: 8090
  • Windows 10
    • PlanetSquires Software
Re: multi-language apps
« Reply #2 on: December 19, 2016, 01:49:21 PM »

I save the localized names in separate text files (*.lang). Here is a sample of the start of the english.lang file:
Code: [Select]
' 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:
Code: [Select]
' 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:

Code: [Select]
' ========================================================================================
' 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:
Code: [Select]
               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.


Logged
Paul Squires
PlanetSquires Software
FireFly Visual Designer, WinFBE Editor

James Fuller

  • FireFly3 Registered User
  • Senior FireFly Member
  • *
  • Posts: 272
  • FF3 User
Re: multi-language apps
« Reply #3 on: December 19, 2016, 02:12:59 PM »

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

James
Logged

Paul Squires

  • Administrator
  • Master FireFly Member
  • *****
  • Posts: 8090
  • Windows 10
    • PlanetSquires Software
Re: multi-language apps
« Reply #4 on: December 19, 2016, 05:54:20 PM »

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

Pierre Bellisle

  • FireFly3 User
  • Junior FireFly Member
  • *
  • Posts: 57
Re: multi-language apps
« Reply #5 on: December 19, 2016, 08:23:30 PM »

As a side note, LoadString may be used to get MessageBox message string
in the language of the logged user.

Pierre

Code: [Select]
#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
Logged