Page 3 of 3

Re: An easy to use solution for multilanguage programs

Posted: Mon May 26, 2025 9:20 am
by Jacobus
Hello,
A quick update is needed.
I'm using Freak's code (first post) to create a multilingual program, and everything works perfectly with PB 6.21B9 x86, but the program crashes on x64. I'm not given a reason, just the message:

Code: Select all

The debugger executable closes unexpectedly.
The problem only occurs when an external language file needs to be loaded, but not when loading the main language, which is defined in the Datasection. Same problem with PB 6.12 or PB 6.20.
I've tried looking for where the problem might be in the LoadLanguage(Filename$ = "") procedure, but I can't see or understand why the debugger closes unexpectedly.

Code: Select all

If FileName$ <> ""
                                   ;  Debug FileName$  ; ok
    If OpenPreferences(FileName$) ;: Debug "fichier ouvert" ; ok
      For Group = 1 To NbLanguageGroups
        If LanguageGroups(Group)\GroupStart <= LanguageGroups(Group)\GroupEnd  ; sanity check.. check for empty groups / vérification de cohérence... vérification des groupes vides
          PreferenceGroup(LanguageGroups(Group)\Name$)
         
          For StringIndex = LanguageGroups(Group)\GroupStart To LanguageGroups(Group)\GroupEnd
            LanguageStrings(StringIndex) = ReadPreferenceString(LanguageNames(StringIndex), LanguageStrings(StringIndex))
          Next StringIndex
          
        EndIf
      Next Group
      ClosePreferences()   
      
      ProcedureReturn #True
    EndIf    

  EndIf
Has anyone encountered this problem before? Do you have any ideas or solutions to fix it?

Re: An easy to use solution for multilanguage programs

Posted: Mon May 26, 2025 3:13 pm
by Axolotl
Just a cautious attempt to help:
I had a brief look at the original code.
Maybe you use some characters > 256 in your language files....
Because with these lines of code this could be problematic:

Code: Select all

        If Asc(Left(LanguageNames(StringIndex), 1)) <> char
          char = Asc(Left(LanguageNames(StringIndex), 1))
          LanguageGroups(Group)\IndexTable[char] = StringIndex
        EndIf

Re: An easy to use solution for multilanguage programs

Posted: Mon May 26, 2025 4:38 pm
by Jacobus
Hello Axolotl,
Thanks for the feedback. Unfortunately, I've already tried reducing the size of the strings (I have three that are actually longer than 256 characters, but the indexes are much shorter) and it doesn't change anything, especially since it works fine with PB x86.
The problem, which I was able to isolate, occurs when loading the language file with PB x64. The strangest thing is that it worked, only once!!. Now the debugger always crashes unexpectedly. Hence my incomprehension... :?

[Edit]
I just ran several tests in a row, with the debugger disabled, then enabled, and in both cases, I noticed that the compilation completed normally, then didn't... I'm lost for words...
Configuration: Windows 11 Professional x64, 24H2.

Re: An easy to use solution for multilanguage programs

Posted: Mon May 26, 2025 4:55 pm
by Axolotl
Hi Jacobus,
it is not the length. As you can see at my example it is the first character which is limited to 256, with unicode and stuff the value of a character can be greater.

Re: An easy to use solution for multilanguage programs

Posted: Mon May 26, 2025 5:16 pm
by Jacobus
Okay :| , I'm a bit on edge right now, but no, I don't use any non-alphabet characters for my indexes, and the language file is saved in UTF-8 format with BOM, as Freak pointed out.
Example of language file contents:

Code: Select all

[Hyperlinks]
Accueil              = Home
MesApplications      = Home - My applications
Bureau               = My Desktop
Recents              = Recent files
Telechargements      = My downloads
MonPC                = My PC
OutilsWindows        = Windows Tools
Aide                 = Need help?
Apropos              = About...
MinimiserFen         = Minimize window
Minimiser            = Minimize
Reduire              = Minimize window to taskbar
Quitter              = Quit
WindowsExplorer      = Windows File Explorer
GestionTaches        = Taskmanager
AnalyseurPerf        = Performance Analyzer
MoniteurRessources   = Resource Monitor
PanneauConfig        = Control Panel
InfoVersion          = Version info
ObservateurEvent     = Event Observer
CentreMaintenance    = Maintenance Center
InfoSysteme          = System Information
ProprietesSysteme    = System Properties
GestionServices      = Windows Services Manager
ParamCompteUser      = User account settings
Programmes           = Programs, repair and uninstall
GestionDisquesDurs   = Hard Disk and Partition Manager
NettoyerDisquesDurs  = Clean disks of unnecessary items
CreerArchivesAutoEx  = Creating self-extracting archives
CommandeExecuter     = Run Command
ConsoleLigneComm     = Command line console
EditeurRegistre      = Registry Editor
PanneauControleGen   = General Windows Tools Control Panel

Re: An easy to use solution for multilanguage programs

Posted: Mon May 26, 2025 5:48 pm
by HeX0R
Does it crash with that example file also?
Because I used it now, altered the data section to fit that example and nothing crashed.
Guess your problem might be somewhere else.

Re: An easy to use solution for multilanguage programs

Posted: Mon May 26, 2025 6:16 pm
by Jacobus
Hi HeXOR, thanks for your tests.
It's indeed part of one of my language files. For my application, the file contains a little over 300 lines of the same type.
The worst part is that I'm almost certain it's just a bug that's causing the whole thing to crash. This only happens in 64-bit mode. I can't be far from the truth... but so far, all my attempts have failed.

Re: An easy to use solution for multilanguage programs

Posted: Mon May 26, 2025 6:31 pm
by HeX0R
I don't think there is much secret in this part of your code (those are nothing but language phrases...), why not paste the code from freak with the altered datasection and the whole language file here, so others can easily test it?
Or in case it is too large, upload it somewhere.
I also tested it with both x64 compilers.

Re: An easy to use solution for multilanguage programs

Posted: Mon May 26, 2025 7:09 pm
by Jacobus
I'll create a minimalist test code to see if the problem is related to the translation of the texts, and if so, I'll post it. Otherwise, it's related to an interaction with another part of my code, and it will be more difficult to detect without the full code.
My last debugging attempt revealed that the file loads correctly, and it's once loaded that the problem occurs when applying the translation.

Re: An easy to use solution for multilanguage programs

Posted: Mon May 26, 2025 11:08 pm
by ChrisR
I've also tested it with your language file contents extended to 300 entries. No worries either, it works fine here too, as expected.
I guess the related error is probably elsewhere, in interaction with your full code.

Otherwise, have you tried the GPI module and the double map Group()\Word() ?
It works just as well, I personally find it much simpler with the maps well adapted to the need

Re: An easy to use solution for multilanguage programs

Posted: Tue May 27, 2025 5:43 pm
by Jacobus
Hello ChrisR,
Yes, I did try GPI's method and Guimauve's, but it also crashes.
In fact, sometimes it works (perfectly) and sometimes it doesn't (unexpected termination). I get different errors depending on the program's mood, which is a bit too playful for my taste.
Obviously, it must be an interaction with some part of my code, and I can only see one solution: disassemble everything point by point to detect which part of the code is generating these errors. I suspect a memory problem; we'll see.
Thanks for your feedback; I'll come back and update you when I find the error.
And sorry for the clutter in this thread.

Re: An easy to use solution for multilanguage programs

Posted: Thu May 29, 2025 4:46 pm
by Axolotl
With the debugger you can try to set breakpoints around the suspicious codes.
Or you can do some LoggingToFile stuff.

Re: An easy to use solution for multilanguage programs

Posted: Mon Jun 02, 2025 4:29 pm
by Jacobus
Axolotl wrote: Thu May 29, 2025 4:46 pm With the debugger you can try to set breakpoints around the suspicious codes.
Or you can do some LoggingToFile stuff.
In fact, after various tests, it turns out that the problem occurs when the debugger is enabled. It systematically crashes unexpectedly, either at startup or at exit if the program was able to launch. I haven't been able to discover the reason. I used the OnError() lib instead of the debugger, but no error was returned. I tested on several versions of PB, on Windows 10 and 11, and the result is the same. Difficult to debug with debugger that crashes without saying why... :?

To remedy this, I modified Freak's method to create a multilingual program, and I succeeded by including each language in the DataSection. Thus, with automatic detection of the user's language, the program loads the correct translation at startup. This way, I no longer have any crashes in 64-bit.

Method used:

Code: Select all

;-===========================
;-LANGAGE FREAK ;Edited by Jacobus
;-===========================

Global NbLanguageGroups, NbLanguageStrings
Structure LanguageGroup
  Name$
  GroupStart.l
  GroupEnd.l
  IndexTable.l[256]
EndStructure

;Modified procedure:
;we no longer load an external file, but we directly indicate which language should be used.
Procedure LoadLanguage(FileLang$)

  ; do a quick count in the datasection first:
  NbLanguageGroups = 0
  NbLanguageStrings = 0
  ;selection of language data in the DataSection:
  Select FileLang$
    Case "French"  :  Restore French
    Case "English" :  Restore English
    Case "German"  :  Restore German
  EndSelect

  Repeat

    Read.s Name$
    Read.s String$

    Name$ = UCase(Name$)

    If Name$ = "_GROUP_"
      NbLanguageGroups + 1
    ElseIf Name$ = "_END_"
      Break
    Else
      NbLanguageStrings + 1
    EndIf
   
  ForEver

  Global Dim LanguageGroups.LanguageGroup(NbLanguageGroups)  ; all one based here
  Global Dim LanguageStrings.s(NbLanguageStrings)
  Global Dim LanguageNames.s(NbLanguageStrings)

  ; Now load the standard language In the user language data part:
  Group.i = 0
  StringIndex.i = 0  
  
  Select FileLang$
    Case "French"  :  Restore French
    Case "English" :  Restore English
    Case "German"  :  Restore German
  EndSelect

  Repeat

    Read.s Name$
    Read.s String$

    Name$ = UCase(Name$)

    If Name$ = "_GROUP_"
      LanguageGroups(Group)\GroupEnd   = StringIndex
      Group + 1

      LanguageGroups(Group)\Name$      = UCase(String$)
      LanguageGroups(Group)\GroupStart = StringIndex + 1
      For i = 0 To 255
        LanguageGroups(Group)\IndexTable[i] = 0
      Next i
      
    ElseIf Name$ = "_END_"
      Break

    Else
      StringIndex + 1
      LanguageNames(StringIndex)  = Name$ + Chr(1) + String$  ; keep name and string together for easier sorting 

    EndIf
   
  ForEver

  LanguageGroups(Group)\GroupEnd   = StringIndex ; set end for the last group! 
  
  ; Now do the sorting and the indexing for each group
  For Group = 1 To NbLanguageGroups
    If LanguageGroups(Group)\GroupStart <= LanguageGroups(Group)\GroupEnd  ; sanity check.. check for empty groups 
      
      SortArray(LanguageNames(), 0, LanguageGroups(Group)\GroupStart, LanguageGroups(Group)\GroupEnd)
 
      char = 0
      For StringIndex = LanguageGroups(Group)\GroupStart To LanguageGroups(Group)\GroupEnd
        LanguageStrings(StringIndex) = StringField(LanguageNames(StringIndex), 2, Chr(1)) ; splitt the value from the name
        LanguageNames(StringIndex)   = StringField(LanguageNames(StringIndex), 1, Chr(1))

        If Asc(Left(LanguageNames(StringIndex), 1)) <> char
          char = Asc(Left(LanguageNames(StringIndex), 1))
          LanguageGroups(Group)\IndexTable[char] = StringIndex
        EndIf
      Next StringIndex
      
    EndIf
  Next Group
  
  ProcedureReturn #True
EndProcedure

; No change in the language application procedure
Procedure.s Language(Group$, Name$)
  Static Group.i  ; for quicker access when using the same group more than once / pour un accès plus rapide lorsque vous utilisez le même groupe plusieurs fois
  Protected String$, StringIndex.i, Result.i

  Group$  = UCase(Group$)
  Name$   = UCase(Name$)
  String$ = "##### String not found! #####"  ; to help find bugs / pour aider à trouver des bugs

  If LanguageGroups(Group)\Name$ <> Group$  ; check if it is the same group as last time  / vérifier s'il s'agit du même groupe que la dernière fois
    For Group = 1 To NbLanguageGroups
      If Group$ = LanguageGroups(Group)\Name$
        Break
      EndIf
    Next Group

    If Group > NbLanguageGroups  ; check if group was found / vérifier si le groupe a été trouvé
      Group = 0
    EndIf
  EndIf
  
  If Group <> 0
    StringIndex = LanguageGroups(Group)\IndexTable[ Asc(Left(Name$, 1)) ]
    If StringIndex <> 0

      Repeat
        Result = CompareMemoryString(@Name$, @LanguageNames(StringIndex));,#PB_String_CaseSensitive,-1,#PB_UTF8)

        If Result = 0
          String$ = LanguageStrings(StringIndex)
          Break

        ElseIf Result = -1 ; string not found! / chaîne non trouvée !
          Break

        EndIf

        StringIndex + 1
      Until StringIndex > LanguageGroups(Group)\GroupEnd

    EndIf

  EndIf
  ;Debug "StringIndex = "+StringIndex + " : "+String$
  ProcedureReturn String$
EndProcedure

;We define what the user's language is:
Procedure.s DefineUserLanguage()
  
  Define Lang.s, UserIntLang, *Lang
  
  If OpenLibrary(0, "kernel32.dll")   
    *Lang = GetFunction(0, "GetUserDefaultUILanguage")   
    If *Lang
      UserIntLang = CallFunctionFast(*Lang)
    EndIf   
    CloseLibrary(0)
  EndIf
  
  Select UserIntLang
    Case 1031 : Lang = "German"
    Case 2057 : Lang = "English"
    Case 1036 : Lang = "French"
    Case 1049 : Lang = "Russian"
    Case 3082 : Lang = "Spanish"
    Default   : Lang = "French"
  EndSelect
  
  ProcedureReturn Lang 
  
EndProcedure

;-LOAD LANGUAGE
UserLang.s = DefineUserLanguage()
If UserLang = "French"
  LoadLanguage("French") ; default language
ElseIf UserLang = "English"
  LoadLanguage("English") 
ElseIf UserLang = "German"
  LoadLanguage("German")
EndIf

DataSection ;{
  French:
  ; ===================================================
  Data$ "_GROUP_",          "MenuTitle"
  ; =================================================== 
  Data$ "Fichier",            "Fichier"
  Data$ "Editer",             "Editer"
  
  ; ===================================================
  Data$ "_GROUP_",          "MenuItem"
  ; =================================================== 
  Data$ "Nouveau",            "Nouveau"
  Data$ "Ouvrir",             "Ouvrir..."
  Data$ "Sauvegarder",        "Sauvegarder"
  
  ; ===================================================
  Data$ "_END_",              ""
  ; ===================================================
  
  English:
  ; ===================================================
  Data$ "_GROUP_",          "MenuTitle"
  ; ===================================================  
  Data$ "Fichier",            "File"
  Data$ "Editer",             "Edit"
  
  ; ===================================================
  Data$ "_GROUP_",          "MenuItem"
  ; ===================================================  
  Data$ "Nouveau",            "New"
  Data$ "Ouvrir",             "Open..."
  Data$ "Sauvegarder",        "Save"
  
  ; ===================================================
  Data$ "_END_",              ""
  ; ===================================================
  
  German:
  ; ===================================================
  Data$ "_GROUP_",          "MenuTitle"
  ; =================================================== 
  Data$ "Fichier",            "Datei"
  Data$ "Editer",             "Bearbeiten"
  
  ; ===================================================
  Data$ "_GROUP_",          "MenuItem"
  ; ===================================================  
  Data$ "Nouveau",            "Neu"
  Data$ "Ouvrir",             "Offen..."
  Data$ "Sauvegarder",        "Zum Schutz"
  
  ; ===================================================
  Data$ "_END_",              ""
  ; =================================================== 
  ;}
EndDataSection

Debug Language("MenuTitle", "Fichier")
Debug Language("MenuItem", "Sauvegarder")
Debug Language("MenuItem", "Ouvrir")
I haven't had time to test other programs yet, which I will do to determine if this is due to a specific program or not. To be continued...