An easy to use solution for multilanguage programs

Share your advanced PureBasic knowledge/code with the community.
c4s
Addict
Addict
Posts: 1981
Joined: Thu Nov 01, 2007 5:37 pm
Location: Germany

Re: An easy to use solution for multilanguage programs

Post by c4s »

Here the passages I found:
Wikipedia wrote:In the case of languages which used 8-bit character sets with non-Latin alphabets encoded in the upper half, letters in UTF-8 will be double the size. For some languages such as Hindi's Devanagari and Thai, letters will be triple the size.
Wikipedia wrote:Characters U+0800 through U+FFFF use three bytes in UTF-8, but only two in UTF-16. As a result, text in (for example) Chinese, Japanese or Hindi could take more space in UTF-8 if there are more of these characters than there are ASCII characters.
I don't now what this actually means for you but maybe it helps.
If any of you native English speakers have any suggestions for the above text, please let me know (via PM). Thanks!
srod
PureBasic Expert
PureBasic Expert
Posts: 10589
Joined: Wed Oct 29, 2003 4:35 pm
Location: Beyond the pale...

Re: An easy to use solution for multilanguage programs

Post by srod »

Not all fonts support the full range of Unicode characters. You need to check the font you are using supports the character set that you are after.
I may look like a mule, but I'm not a complete ass.
IdeasVacuum
Always Here
Always Here
Posts: 6425
Joined: Fri Oct 23, 2009 2:33 am
Location: Wales, UK
Contact:

Re: An easy to use solution for multilanguage programs

Post by IdeasVacuum »

c4s wrote:Here the passages I found:
Wikipedia wrote:In the case of languages which used 8-bit character sets with non-Latin alphabets encoded in the upper half, letters in UTF-8 will be double the size. For some languages such as Hindi's Devanagari and Thai, letters will be triple the size.
Wikipedia wrote:Characters U+0800 through U+FFFF use three bytes in UTF-8, but only two in UTF-16. As a result, text in (for example) Chinese, Japanese or Hindi could take more space in UTF-8 if there are more of these characters than there are ASCII characters.
I don't now what this actually means for you but maybe it helps.
Thanks for your help c4s. Thai, Chinese and Japanese are fine, displayed very nicely by PB.
IdeasVacuum
If it sounds simple, you have not grasped the complexity.
IdeasVacuum
Always Here
Always Here
Posts: 6425
Joined: Fri Oct 23, 2009 2:33 am
Location: Wales, UK
Contact:

Re: An easy to use solution for multilanguage programs

Post by IdeasVacuum »

srod wrote:Not all fonts support the full range of Unicode characters. You need to check the font you are using supports the character set that you are after.
Thanks srod, I think that possibly is the issue and I'm looking into it now.
IdeasVacuum
If it sounds simple, you have not grasped the complexity.
IdeasVacuum
Always Here
Always Here
Posts: 6425
Joined: Fri Oct 23, 2009 2:33 am
Location: Wales, UK
Contact:

Re: An easy to use solution for multilanguage programs

Post by IdeasVacuum »

It was indeed the fonts. I thought that since the most common fonts had proven to support all the Asian multi-byte languages I have previously used, they must support Hindi as well - but they don't.

That is only part of the issue as well - there are many dedicated Hindi fonts out there but not all support every byte. I have found one that worked immediately and is nicely formed: "MANGAL.TTF".

Thanks again chaps.
IdeasVacuum
If it sounds simple, you have not grasped the complexity.
troy
User
User
Posts: 51
Joined: Sat May 31, 2003 2:59 pm

Re: An easy to use solution for multilanguage programs

Post by troy »

Any updates for 4.40 pls?
--
troy
User avatar
ts-soft
Always Here
Always Here
Posts: 5756
Joined: Thu Jun 24, 2004 2:44 pm
Location: Berlin - Germany

Re: An easy to use solution for multilanguage programs

Post by ts-soft »

troy wrote:Any updates for 4.40 pls?
Simple change the 4 Read to Read.s in "Procedure LoadLanguage" :wink:
User avatar
blueznl
PureBasic Expert
PureBasic Expert
Posts: 6161
Joined: Sat May 17, 2003 11:31 am
Contact:

Re: An easy to use solution for multilanguage programs

Post by blueznl »

UTF8 defines how characters are encoded, and how they SHOULD be shown and handled, it does NOT mean that the right character for each codepoint is available on the system that supports UTF8.

It's the same with chinese. For example, if you did not install the characterset, a page in chinese in UTF8 will not show properly.
( PB6.00 LTS Win11 x64 Asrock AB350 Pro4 Ryzen 5 3600 32GB GTX1060 6GB)
( The path to enlightenment and the PureBasic Survival Guide right here... )
User avatar
Guimauve
Enthusiast
Enthusiast
Posts: 742
Joined: Wed Oct 22, 2003 2:51 am
Location: Canada

Re: An easy to use solution for multilanguage programs

Post by Guimauve »

Hello everyone,

Sorry to re-open a three year old topic but I have revamped the original code.

Best regards
Guimauve

Code: Select all

; <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
; Project name : Basic Language Management System
; File Name : Basic Language Management System.pb
; File version: 1.0.0
; Programming : OK
; Programmed by : Guimauve
; Date : 09-12-2012
; Last Update : 09-12-2012
; PureBasic code : V5.00
; Platform : Windows, Linux, MacOS X
; <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<

; <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
; Programming notes
;
; This is a little remake of Freak's code posted in English 
; forum. It also include an extra SaveLangauge procedure 
; added by Thyphoon.
;
; <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<

; <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
; <<<<< Déclaration de la Structure <<<<<

Structure LanguageGroup
  
  Name.s
  GroupStart.l
  GroupEnd.l
  IndexTable.l[256]
  
EndStructure

; <<<<<<<<<<<<<<<<<<<<<<<<<<<<
; <<<<< Les observateurs <<<<<

Macro GetLanguageGroupName(LanguageGroupA)
  
  LanguageGroupA\Name
  
EndMacro

Macro GetLanguageGroupGroupStart(LanguageGroupA)
  
  LanguageGroupA\GroupStart
  
EndMacro

Macro GetLanguageGroupGroupEnd(LanguageGroupA)
  
  LanguageGroupA\GroupEnd
  
EndMacro

Macro GetLanguageGroupIndexTable(LanguageGroupA, IndexTableID)
  
  LanguageGroupA\IndexTable[IndexTableID]
  
EndMacro

; <<<<<<<<<<<<<<<<<<<<<<<<<
; <<<<< Les mutateurs <<<<<

Macro SetLanguageGroupName(LanguageGroupA, P_Name)
  
  GetLanguageGroupName(LanguageGroupA) = P_Name
  
EndMacro

Macro SetLanguageGroupGroupStart(LanguageGroupA, P_GroupStart)
  
  GetLanguageGroupGroupStart(LanguageGroupA) = P_GroupStart
  
EndMacro

Macro SetLanguageGroupGroupEnd(LanguageGroupA, P_GroupEnd)
  
  GetLanguageGroupGroupEnd(LanguageGroupA) = P_GroupEnd
  
EndMacro

Macro SetLanguageGroupIndexTable(LanguageGroupA, IndexTableID, P_IndexTable)
  
  GetLanguageGroupIndexTable(LanguageGroupA, IndexTableID) = P_IndexTable
  
EndMacro

; <<<<<<<<<<<<<<<<<<<<<<<<<<<<<
; <<<<< L'opérateur Reset <<<<<

Macro ResetLanguageGroup(LanguageGroupA)
  
  SetLanguageGroupName(LanguageGroupA, "")
  SetLanguageGroupGroupStart(LanguageGroupA, 0)
  SetLanguageGroupGroupEnd(LanguageGroupA, 0)
  
  For IndexTableID = 0 To 255
    SetLanguageGroupIndexTable(LanguageGroupA, IndexTableID, 0)
  Next
  
EndMacro

; <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
; <<<<< Code généré en : 00.004 secondes (25250.00 lignes/seconde) <<<<<
; <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<

; <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
; <<<<< Déclaration de la Structure <<<<<

Structure Language
  
  GroupCount.l ; Special : Increment
  StringCount.l ; Special : Increment
  Array Groups.LanguageGroup(0)
  Array Strings.s(0)
  Array Names.s(0)
  
EndStructure

; <<<<<<<<<<<<<<<<<<<<<<<<<<<<
; <<<<< Les observateurs <<<<<

Macro GetLanguageGroupCount(LanguageA)
  
  LanguageA\GroupCount
  
EndMacro

Macro GetLanguageStringCount(LanguageA)
  
  LanguageA\StringCount
  
EndMacro

Macro GetLanguageGroups(LanguageA)
  
  LanguageA\Groups()
  
EndMacro

Macro GetLanguageStrings(LanguageA)
  
  LanguageA\Strings()
  
EndMacro

Macro GetLanguageNames(LanguageA)
  
  LanguageA\Names()
  
EndMacro

; <<<<<<<<<<<<<<<<<<<<<<<<<
; <<<<< Les mutateurs <<<<<

Macro SetLanguageGroupCount(LanguageA, P_GroupCount)
  
  GetLanguageGroupCount(LanguageA) = P_GroupCount
  
EndMacro

Macro SetLanguageStringCount(LanguageA, P_StringCount)
  
  GetLanguageStringCount(LanguageA) = P_StringCount
  
EndMacro

Macro SetLanguageGroups(LanguageA, P_Groups)
  
  CopyLanguageGroups(P_Groups, GetLanguageGroups(LanguageA))
  
EndMacro

Macro SetLanguageStrings(LanguageA, P_Strings)
  
  GetLanguageStrings(LanguageA) = P_Strings
  
EndMacro

Macro SetLanguageNames(LanguageA, P_Names)
  
  GetLanguageNames(LanguageA) = P_Names
  
EndMacro

; <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
; <<<<< Les macros complémentaires pour les Tableaux dynamiques <<<<<

Macro GetLanguageGroupsElement(LanguageA, Groups_ID_1)
  
  LanguageA\Groups(Groups_ID_1)
  
EndMacro

Macro SetLanguageGroupsElement(LanguageA, Groups_ID_1, P_Element)
  
  GetLanguageGroupsElement(LanguageA, Groups_ID_1) = P_Element
  
EndMacro

Macro ReDimLanguageGroups(LanguageA, Groups_Max_1D)
  
  ReDim GetLanguageGroupsElement(LanguageA, Groups_Max_1D)
  
EndMacro

; <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
; <<<<< Les macros complémentaires pour les Tableaux dynamiques <<<<<

Macro GetLanguageStringsElement(LanguageA, Strings_ID_1)
  
  LanguageA\Strings(Strings_ID_1)
  
EndMacro

Macro SetLanguageStringsElement(LanguageA, Strings_ID_1, P_Element)
  
  GetLanguageStringsElement(LanguageA, Strings_ID_1) = P_Element
  
EndMacro

Macro ReDimLanguageStrings(LanguageA, Strings_Max_1D)
  
  ReDim GetLanguageStringsElement(LanguageA, Strings_Max_1D)
  
EndMacro

; <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
; <<<<< Les macros complémentaires pour les Tableaux dynamiques <<<<<

Macro GetLanguageNamesElement(LanguageA, Names_ID_1)
  
  LanguageA\Names(Names_ID_1)
  
EndMacro

Macro SetLanguageNamesElement(LanguageA, Names_ID_1, P_Element)
  
  GetLanguageNamesElement(LanguageA, Names_ID_1) = P_Element
  
EndMacro

Macro ReDimLanguageNames(LanguageA, Names_Max_1D)
  
  ReDim GetLanguageNamesElement(LanguageA, Names_Max_1D)
  
EndMacro

; <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
; <<<<< Les opérateurs spéciaux <<<<<

Macro IncrementLanguageGroupCount(LanguageA, P_Increment = 1)
  
  SetLanguageGroupCount(LanguageA, GetLanguageGroupCount(LanguageA) + P_Increment)
  
EndMacro

Macro IncrementLanguageStringCount(LanguageA, P_Increment = 1)
  
  SetLanguageStringCount(LanguageA, GetLanguageStringCount(LanguageA) + P_Increment)
  
EndMacro

; <<<<<<<<<<<<<<<<<<<<<<<<<<<<<
; <<<<< L'opérateur Reset <<<<<

Macro ResetLanguage(LanguageA)
  
  SetLanguageGroupCount(LanguageA, 0)
  SetLanguageStringCount(LanguageA, 0)
  
  For Groups_ID_1 = 0 To ArraySize(GetLanguageGroups(LanguageA), 1)
    ResetLanguageGroup(GetLanguageGroupsElement(LanguageA, Groups_ID_1))
  Next
  
  For Strings_ID_1 = 0 To ArraySize(GetLanguageStrings(LanguageA), 1)
    SetLanguageStringsElement(LanguageA, Strings_ID_1, "")
    SetLanguageNamesElement(LanguageA, Strings_ID_1, "")
  Next
  
  ClearStructure(LanguageA, Language)
  InitializeStructure(LanguageA, Language)
  
EndMacro

; <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
; <<<<< Code généré en : 00.009 secondes (25000.00 lignes/seconde) <<<<<
; <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<

; <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
; <<<<< The LoadLanguage operator <<<<<

Procedure LoadLanguage(*LanguageA.Language, P_FileName.s = "")
  
  Protected GroupID, StringID.l, CharID.a, FirstChar.a
  
  ResetLanguage(*LanguageA)
  
  ; do a quick count in the datasection first:
  
	Restore Language
	Repeat

		Read.s GroupName.s
		Read.s String.s

		GroupName = UCase(GroupName)

		If GroupName = "_GROUP_"
			IncrementLanguageGroupCount(*LanguageA)
		ElseIf GroupName = "_END_"
			Break
		Else
			IncrementLanguageStringCount(*LanguageA)
		EndIf
		
	ForEver
	
	ReDimLanguageGroups(*LanguageA, GetLanguageGroupCount(*LanguageA))
	ReDimLanguageStrings(*LanguageA, GetLanguageStringCount(*LanguageA))
	ReDimLanguageNames(*LanguageA, GetLanguageStringCount(*LanguageA))

	; Now load the standard language:
 
	GroupID = 0
	StringID = 0  

	Restore Language
	Repeat

		Read.s GroupName.s
		Read.s String.s

		GroupName = UCase(GroupName)

		If GroupName = "_GROUP_"
		  SetLanguageGroupGroupEnd(GetLanguageGroupsElement(*LanguageA, GroupID), StringID)
			GroupID + 1
			SetLanguageGroupName(GetLanguageGroupsElement(*LanguageA, GroupID), UCase(String))
			SetLanguageGroupGroupStart(GetLanguageGroupsElement(*LanguageA, GroupID), StringID + 1)

			For IndexTableID = 0 To 255
			  SetLanguageGroupIndexTable(GetLanguageGroupsElement(*LanguageA, GroupID), IndexTableID, 0)
			Next	

		ElseIf GroupName = "_END_"
			Break

		Else
		  StringID + 1
		  SetLanguageNamesElement(*LanguageA, StringID, GroupName + Chr(1) + String ); keep name and string together for easier sorting

		EndIf
		
	ForEver

	SetLanguageGroupGroupEnd(GetLanguageGroupsElement(*LanguageA, GroupID), StringID) ; set end for the last group!
	
	; Now do the sorting and the indexing for each group
	
	For GroupID = 1 To GetLanguageGroupCount(*LanguageA)
	  
		If GetLanguageGroupGroupStart(GetLanguageGroupsElement(*LanguageA, GroupID)) <= GetLanguageGroupGroupEnd(GetLanguageGroupsElement(*LanguageA, GroupID))  ; sanity check.. check for empty groups
			
			SortArray(GetLanguageNames(*LanguageA), 0, GetLanguageGroupGroupStart(GetLanguageGroupsElement(*LanguageA, GroupID)), GetLanguageGroupGroupEnd(GetLanguageGroupsElement(*LanguageA, GroupID)))
	
			CharID = 0
			
			For StringID = GetLanguageGroupGroupStart(GetLanguageGroupsElement(*LanguageA, GroupID)) To GetLanguageGroupGroupEnd(GetLanguageGroupsElement(*LanguageA, GroupID))

			  SetLanguageStringsElement(*LanguageA, StringID, StringField(GetLanguageNamesElement(*LanguageA, StringID), 2, Chr(1)))
			  SetLanguageNamesElement(*LanguageA, StringID, StringField(GetLanguageNamesElement(*LanguageA, StringID), 1, Chr(1)))
			  FirstChar = Asc(Left(GetLanguageNamesElement(*LanguageA, StringID), 1))
			  
				If FirstChar <> CharID
				  CharID = FirstChar
				  SetLanguageGroupIndexTable(GetLanguageGroupsElement(*LanguageA, GroupID), CharID, StringID)
				EndIf
				
			Next
			
		EndIf
		
	Next

	; Now try to load an external language file
    
	If P_FileName <> ""
			
	  If OpenPreferences(P_FileName)
	    
	    For GroupID = 1 To GetLanguageGroupCount(*LanguageA)
	      
				If GetLanguageGroupGroupStart(GetLanguageGroupsElement(*LanguageA, GroupID)) <= GetLanguageGroupGroupEnd(GetLanguageGroupsElement(*LanguageA, GroupID))  ; sanity check.. check for empty groups
				  
				  PreferenceGroup(GetLanguageGroupName(GetLanguageGroupsElement(*LanguageA, GroupID)))
					
					For StringID = GetLanguageGroupGroupStart(GetLanguageGroupsElement(*LanguageA, GroupID)) To GetLanguageGroupGroupEnd(GetLanguageGroupsElement(*LanguageA, GroupID))
					  SetLanguageStringsElement(*LanguageA, StringID, ReadPreferenceString(GetLanguageNamesElement(*LanguageA, StringID), GetLanguageStringsElement(*LanguageA, StringID)))
					Next
					
				EndIf
				
			Next 
			
			ClosePreferences()   
			
			ProcedureReturn #True
		EndIf    

	EndIf
	
	ProcedureReturn #True
EndProcedure

; <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
; <<<<< The SaveLanguage operator <<<<<

Procedure SaveLanguage(*LanguageA.Language, P_FileName.s)
  
  Protected GroupID, StringID.l
  
  If CreatePreferences(P_FileName)
    
    For GroupID = 1 To GetLanguageGroupCount(*LanguageA)
      
      PreferenceGroup(GetLanguageGroupName(GetLanguageGroupsElement(*LanguageA, GroupID)))
      
      For StringID = GetLanguageGroupGroupStart(GetLanguageGroupsElement(*LanguageA, GroupID)) To GetLanguageGroupGroupEnd(GetLanguageGroupsElement(*LanguageA, GroupID))
        WritePreferenceString(GetLanguageNamesElement(*LanguageA, StringID), GetLanguageStringsElement(*LanguageA, StringID))
      Next
      
    Next
    
    ClosePreferences()
    
  EndIf
  
EndProcedure

; <<<<<<<<<<<<<<<<<<<<<<<<<<<<<
; <<<<< Get Language text <<<<<

Procedure.s Language(*LanguageA.Language, P_Group.s, P_Name.s)
  
  Static GroupID.l  ; for quicker access when using the same group more than once
  Protected String.s, StringID, Result
  
  P_Group  = UCase(P_Group)
  P_Name   = UCase(P_Name)
  String = "##### String not found! #####"  ; to help find bugs
  
  If UCase(GetLanguageGroupName(GetLanguageGroupsElement(*LanguageA, GroupID))) <> P_Group  ; check if it is the same group as last time
    
    For GroupID = 1 To GetLanguageGroupCount(*LanguageA)
      If P_Group = GetLanguageGroupName(GetLanguageGroupsElement(*LanguageA, GroupID))
        Break
      EndIf
    Next 
    
    If GroupID > GetLanguageGroupCount(*LanguageA)  ; check if group was found
      GroupID = 0
    EndIf
    
  EndIf
  
  If GroupID <> 0
    
    StringID = GetLanguageGroupIndexTable(GetLanguageGroupsElement(*LanguageA, GroupID), Asc(Left(P_Name, 1)))

    If StringID <> 0
      
      Repeat
        
        Result = CompareMemoryString(@P_Name, @GetLanguageNamesElement(*LanguageA, StringID))
        
        If Result = 0
          String = GetLanguageStringsElement(*LanguageA, StringID)
          Break
        ElseIf Result = -1 ; string not found!
          Break
        EndIf
        
        StringID + 1
        
      Until StringID > GetLanguageGroupGroupEnd(GetLanguageGroupsElement(*LanguageA, GroupID))
      
    EndIf
    
  EndIf
  
  ProcedureReturn String
EndProcedure

; <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
; <<<<< DataSection pour les textes par défaut <<<<<

DataSection
  
  ; Here the default language is specified. It is a list of Group, Name pairs,
  ; with some special keywords for the Group:
  ;
  ; "_GROUP_" will indicate a new group in the datasection, the second value is the group name
  ; "_END_" will indicate the end of the language list (as there is no fixed number)
  ;
  ; Note: The identifier strings are case insensitive to make live easier :)
  
  Language:
  
  ; ===================================================
  Data.s "_GROUP_",            "MenuTitle"
  ; ===================================================
  
  Data.s "File",             "File"
  Data.s "Edit",             "Edit"
  
  ; ===================================================
  Data.s "_GROUP_",            "MenuItem"
  ; ===================================================
  
  Data.s "New",              "New"
  Data.s "Open",             "Open..."
  Data.s "Save",             "Save"
  
  ; ===================================================
  Data.s "_END_",              ""
  ; ===================================================
  
EndDataSection

; <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< 
; <<<<< !!! WARNING - YOU ARE NOW IN A TESTING ZONE - WARNING !!! <<<<< 
; <<<<< !!! WARNING - THIS CODE SHOULD BE COMMENTED - WARNING !!! <<<<< 
; <<<<< !!! WARNING - BEFORE THE FINAL COMPILATION. - WARNING !!! <<<<< 
; <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< 

Global Language.Language

LoadLanguage(Language)                ; load default language
;LoadLanguage(Language.Language, "german.prefs") ; uncomment this to load the german file

; get some language strings
Debug Language(Language, "MenuTitle", "Edit")
Debug Language(Language, "MenuItem", "Open")

ResetLanguage(Language)

; <<<<<<<<<<<<<<<<<<<<<<<
; <<<<< END OF FILE <<<<<
; <<<<<<<<<<<<<<<<<<<<<<<
Dear Optimist, Pessimist,
and Realist,

While you guys were
busy arguing about the
glass of water, I DRANK IT !

Sincerely,
the Opportunist
User avatar
Keya
Addict
Addict
Posts: 1891
Joined: Thu Jun 04, 2015 7:10 am

Re: An easy to use solution for multilanguage programs

Post by Keya »

Does anyone have a good way to use this alongside the PB Visual Designer?

The problem is that the VD generates code with a fixed string, such as quotes-encapsulated "Cancel" for a button, and then to use the languages like this demo we then call SetGadgetText for each gadget to re-set its text, which in the case of the base language such as English means setting the same string twice for no apparent reason. Even if you detect that the correct language is set you still need the extra code to re-set the text for other languages, rather than simply setting the correct language text when the control is initialized.

I was thinking it'd be good if there was a way where the VD didn't encapsulate strings with "" quotes automatically, so that for example we could specify for example "Cancel" like usual, but also for example LoadLanguage("Main","Cancel"), then the correct language is loaded straight away without any need for showing base language first and then quickly switching.

Or, perhaps a CaptionAsCode parameter to be used instead of the Caption parameter if used, which isn't ""-encapsulated like the regular Caption one is
GPI
PureBasic Expert
PureBasic Expert
Posts: 1394
Joined: Fri Apr 25, 2003 6:41 pm

Re: An easy to use solution for multilanguage programs

Post by GPI »

Keya wrote:Does anyone have a good way to use this alongside the PB Visual Designer?

The problem is that the VD generates code with a fixed string, such as quotes-encapsulated "Cancel" for a button,
Simple check the "Vaption is a variable?" option and type in caption < Language(Language,"MenuTitle","Edit") > )
User avatar
Keya
Addict
Addict
Posts: 1891
Joined: Thu Jun 04, 2015 7:10 am

Re: An easy to use solution for multilanguage programs

Post by Keya »

Great, thankyou! I was wondering what that "Caption as variable" was

An issue with that however is that when you assign it as a variable you can no longer press F5 to view the real version of it, as it complains about it not being a string. :(

incidentally while searching for the helpfile for more info about it (there's only a single brief mention of its existence) i also came across the USERGUIDES that exist in the helpfile, but seemingly can't be accessed by either the Contents or Index, only Search. Looks like some good reading too
GPI
PureBasic Expert
PureBasic Expert
Posts: 1394
Joined: Fri Apr 25, 2003 6:41 pm

Re: An easy to use solution for multilanguage programs

Post by GPI »

I created an other version of the code with Map and Module:

Code: Select all

DeclareModule Language
  EnableExplicit
  Structure Group
    Map Word.s()
  EndStructure
  
  Global NewMap Group.Group()
  
  Macro Get(sGroup,sWord)
    Language::Group(UCase(sGroup))\Word(UCase(sWord))
  EndMacro
  
  Declare Load(*DefaultLanguage,Filenname.s="")
  Declare Save(Filename.s)
EndDeclareModule


Module Language
  Procedure Load(*Default,File.s="")
    Define Group.s="COMMON"
    Define Option.s
    Define Value.s
    Define Len.i
    ClearMap(Group())
    
    Repeat
      ;because read without restore will not work
      Len=MemoryStringLength(*Default)+1
      Option.s=PeekS(*Default)
      *Default+Len*SizeOf(Character)
      
      len=MemoryStringLength(*Default)+1
      Value.s=PeekS(*Default)
      *Default+Len*SizeOf(Character)
      
      Option=UCase(Option)
      Select option
        Case "", "_END_"
          Break
        Case "_GROUP_"
          Group=UCase(Value)
        Default
          Group(Group)\Word(Option)=Value
          ;Debug Group+"\"+Option+"="+Value
      EndSelect
    ForEver
    
    If file
      If OpenPreferences(File)       
        ForEach Group()
          PreferenceGroup(MapKey(Group()))
          ForEach Group()\Word()
            Group()\Word()=ReadPreferenceString(MapKey(Group()\Word()),Group()\Word())
          Next
        Next
        ClosePreferences()
      Else
        ProcedureReturn #False
      EndIf
    EndIf
    ProcedureReturn #True    
  EndProcedure
  
  Procedure Save(File.s)
    If CreatePreferences(File,#PB_Preference_GroupSeparator)
      PreferenceComment("Language File")
      PreferenceGroup("info");just in case we need this information sometimes
      WritePreferenceString("Version","1.00")
      WritePreferenceString("Programm",GetFilePart(ProgramFilename()))
      ForEach Group()       
        PreferenceGroup(MapKey(Group()))
        ForEach Group()\Word()
          WritePreferenceString(MapKey(Group()\Word()),Group()\Word())
        Next
      Next
      ClosePreferences()
      ProcedureReturn #True
    EndIf
    ProcedureReturn #False
  EndProcedure
EndModule


CompilerIf #PB_Compiler_IsMainFile
  
  Language::Load(?English)
  ;Language::Load(?English,"german.pref")
  
  
  Debug Language::Get("MenuTitle","File")
  Debug Language::Get("MenuItem","Open")
  
  ;Language::Save("german.pref")
  
  
  
  
  DataSection
    
    ; Here the default language is specified. It is a list of Group, Name pairs,
    ; with some special keywords for the Group:
    ;
    ; "_GROUP_" will indicate a new group in the datasection, the second value is the group name
    ; "_END_" will indicate the end of the language list (as there is no fixed number)
    ;
    ; Note: The identifier strings are case insensitive to make live easier :)
    
    English:
    
    ; ===================================================
    Data.s "_GROUP_",            "MenuTitle"
    ; ===================================================
    
    Data.s "File",             "File"
    Data.s "Edit",             "Edit"
    
    ; ===================================================
    Data.s "_GROUP_",            "MenuItem"
    ; ===================================================
    
    Data.s "New",              "New"
    Data.s "Open",             "Open..."
    Data.s "Save",             "Save"
    
    ; ===================================================
    Data.s "_END_",              ""
    ; ===================================================
    
  EndDataSection
CompilerEndIf
Shorter and imo easier to understand.
User avatar
Fangbeast
PureBasic Protozoa
PureBasic Protozoa
Posts: 4749
Joined: Fri Apr 25, 2003 3:08 pm
Location: Not Sydney!!! (Bad water, no goats)

Re: An easy to use solution for multilanguage programs

Post by Fangbeast »

/me throws more of his old, dead, smelly, decayed fish at Freak and smiles evilly. This is still useful years later Timo!
Amateur Radio, D-STAR/VK3HAF
User avatar
Fangbeast
PureBasic Protozoa
PureBasic Protozoa
Posts: 4749
Joined: Fri Apr 25, 2003 3:08 pm
Location: Not Sydney!!! (Bad water, no goats)

Re: An easy to use solution for multilanguage programs

Post by Fangbeast »

GPI (if you are around somewhere), I wanted to use your solution but I ran into a huge snag.

I have two lots of strings for a menu entry (and indeed, all strings in the program as menu entries are not the only ones that need to be translated.

At the moment, I use a structure to keep track of short text and longform explanations for each item, object, tooltip etc and pull out the ones that I need.

In the following menu item example, I use the short text string for the menu item:

MenuItem(#MenuBar_Mystuff_Backupmystuff, FormatMenu(ShortKey\Mystuff_Backupmystuff, GetShortcutText(#Keys_Mystuff_Backupmystuff), 45), ImageID(#MenuBarIcon_Mystuff_Backupmystuff))

ShortKey\Mystuff_Backupmystuff would equal "Backup my stuff"

I have a backup imagebutton for which a tooltip uses the short form for a title and a long form for the explanation.

LongKey\Mystuff_Backupmystuff would equal "Backup all of my stuff to the hard disk as an XML file"

As far as I know, MapKey is only 2 elements right? How would I use Mapkey (and the data section) to do this below?


Data.s "_GROUP_", "MenuTitle"
; ===================================================

Data.s "Backup", "Backup my stuff", "Backup all of my stuff to the hard disk as an XML file"
Amateur Radio, D-STAR/VK3HAF
Post Reply