I didn't reinvent the wheel, it's based on the excellent GPI's code version that I love!
With some small changes to make it work out of the box without any intervention.
Except for the datasection, of course for your default keys words and values.
At startup, it creates the language file with the Native name if it does not already exist,
otherwise it updates the language file (with possible keywords and values addition) without changing the current translated values.
Then it loads the default language and updates it with the translated values of the language file.
+#CRLF$+ and +#DQUOTE$+ without any space is accepted as an escape character for the values.
You only need to use: XIncludeFile "Multi-Language.pb" and then use the macros: GetInterfaceLang, GetTooltipLang,... You're done.
Here, with an example window for demo, to be removed.
Code: Select all
; Multi-Language.pb based on the excellent GPI's MultiLanguage code that I love!
EnableExplicit
Global LangFolder.s = "Lang"
Structure Lang
Map LangKey.s()
EndStructure
Global NewMap Lang.Lang()
;- ----- Public -----
Macro GetLang(Section, Keyname)
ReplaceCrlfQuote(Lang(UCase(Section))\LangKey(UCase(Keyname)))
EndMacro
Macro GetInterfaceLang(Keyname)
ReplaceCrlfQuote(Lang("INTERFACE")\LangKey(UCase(Keyname)))
EndMacro
Macro GetTooltipLang(Keyname)
ReplaceCrlfQuote(Lang("TOOLTIP")\LangKey(UCase(Keyname)))
EndMacro
Macro GetMenuLang(Keyname)
ReplaceCrlfQuote(Lang("MENU")\LangKey(UCase(Keyname)))
EndMacro
Macro GetToolBarLang(Keyname)
ReplaceCrlfQuote(Lang("TOOLBAR")\LangKey(UCase(Keyname)))
EndMacro
Macro GetStatusBarLang(Keyname)
ReplaceCrlfQuote(Lang("STATUSBAR")\LangKey(UCase(Keyname)))
EndMacro
Macro GetMessageLang(Keyname)
ReplaceCrlfQuote(Lang("MESSAGE")\LangKey(UCase(Keyname)))
EndMacro
Declare.s LocaleInfo(LCType.i = #LOCALE_SNATIVELANGNAME)
Declare.s ReplaceCrlfQuote(String.s)
Declare LangLoadDefault()
Declare LangLoad(Filename.s = "")
Declare LangSaveFile(Filename.s)
Declare LangSave(Filename.s)
;- ----- Private -----
Procedure.s LocaleInfo(LCType.i = #LOCALE_SNATIVELANGNAME)
Protected LCData.s, LCDataLen.i
; MS: The application should call GetLocaleInfoEx function in preference To GetLocaleInfo If designed To run only on Windows Vista And later. But it works
LCDataLen = GetLocaleInfo_(#LOCALE_USER_DEFAULT, LCType, @LCData, 0)
LCData = Space(LCDataLen)
If GetLocaleInfo_(#LOCALE_USER_DEFAULT, LCType, @LCData, LCDataLen)
ProcedureReturn LCData
EndIf
EndProcedure
Procedure.s ReplaceCrlfQuote(String.s)
Protected NewString.s, NewString2.s, I.i
For I = 1 To CountString(String, "+#CRLF$+") + 1
If I = 1
NewString2 + StringField(String, I, "+#CRLF$+")
Else
NewString2 + #CRLF$ + StringField(String, I, "+#CRLF$+")
EndIf
Next
For I = 1 To CountString(NewString2, "+#DQUOTE$+") + 1
If I = 1
NewString + StringField(NewString2, I, "+#DQUOTE$+")
Else
NewString + #DQUOTE$ + StringField(NewString2, I, "+#DQUOTE$+")
EndIf
Next
ProcedureReturn NewString
EndProcedure
Procedure LangLoadDefault()
Protected Section.s = "COMMON", Keyname.s, Value.s
ClearMap(Lang())
Restore DefaultLang:
Repeat
Read.s Keyname
Read.s Value
Keyname = UCase(Keyname)
Select Keyname
Case "", "_END_"
Break
Case "_SECTION_"
Section = UCase(Value)
Default
Lang(Section)\LangKey(Keyname) = Value
;Debug Section + "\" + Keyname + "=" + Value
EndSelect
ForEver
ProcedureReturn #True
EndProcedure
Procedure LangLoad(Filename.s = "")
If Filename
If OpenPreferences(LangFolder + "\" + Filename)
ForEach Lang()
PreferenceGroup(MapKey(Lang()))
ForEach Lang()\LangKey()
Lang()\LangKey() = ReadPreferenceString(MapKey(Lang()\LangKey()), Lang()\LangKey())
Next
Next
ClosePreferences()
Else
ProcedureReturn #False
EndIf
EndIf
ProcedureReturn #True
EndProcedure
Procedure LangSaveFile(Filename.s)
PreferenceGroup("Info") ;just in case we need this information sometimes
WritePreferenceString("Program", GetFilePart(ProgramFilename()))
WritePreferenceString("Version", "1.00")
ForEach Lang()
PreferenceGroup(MapKey(Lang()))
ForEach Lang()\LangKey()
If ReadPreferenceString(MapKey(Lang()\LangKey()), "") = ""
WritePreferenceString(MapKey(Lang()\LangKey()), Lang()\LangKey())
EndIf
Next
Next
EndProcedure
Procedure LangSave(Filename.s)
If Filename
If FileSize(LangFolder) = -1 : CreateDirectory(LangFolder) : EndIf
If FileSize(LangFolder) = -2
If MapSize(Lang()) = 0 : LangLoadDefault() : EndIf
If OpenPreferences(LangFolder + "\" + Filename, #PB_Preference_GroupSeparator)
LangSaveFile(Filename)
ElseIf CreatePreferences(LangFolder + "\" + Filename, #PB_Preference_GroupSeparator)
PreferenceComment("Language File")
LangSaveFile(Filename)
Else
ProcedureReturn #False
EndIf
ClosePreferences()
ProcedureReturn #True
EndIf
EndIf
ProcedureReturn #False
EndProcedure
;- ----- Main -----
; LocaleInfo() is for Windows only, see Keya's topic for Linux or MacOS: https://www.purebasic.fr/english/viewtopic.php?t=66552
Define NativeLangName.s = LocaleInfo() ; Or use LocaleInfo(#LOCALE_SENGLANGUAGE) For English Name
NativeLangName = UCase(Left(NativeLangName, 1)) + Mid(NativeLangName, 2)
If NativeLangName = "English"
LangLoadDefault()
Else
Define LangFileName.s = NativeLangName + ".lang" ; Or use LangFileName = "French.lang"
; Create the language file if it does not already exist otherwise update the language file (with possible addition) without changing the current values
LangSave(LangFileName)
; Load the default language then update with the language file translated values
LangLoad(LangFileName)
EndIf
; If you want to let the user choose his language, use:
;LangLoadDefault()
;LangSave(LangName + ".lang") ; If not English. LangName.s is your variable with the Language to load. By respecting the Windows Native name, if possible.
; Load the default values (English) before saving for a new template or to refresh the language file, if needed.
;LangLoadDefault()
;LangSave("German.lang")
;- Exemple
Enumeration Window
#MainWindow
EndEnumeration
Enumeration Gadgets
#Text_Welcome
#Check_Agree
#Button_Confirm
EndEnumeration
If OpenWindow(#MainWindow, 0, 0, 220, 150, "Test Multi-Language", #PB_Window_SystemMenu | #PB_Window_ScreenCentered)
TextGadget(#Text_Welcome, 20, 20, 180, 20, GetInterfaceLang("Text_Welcome"), #PB_Text_Center)
CheckBoxGadget(#Check_Agree, 20, 50, 180, 20, GetInterfaceLang("0001"))
GadgetToolTip(#Check_Agree, GetTooltipLang("0001"))
ButtonGadget(#Button_Confirm, 20, 80, 180, 50, GetInterfaceLang("ConfirmValidate"), #PB_Button_MultiLine)
GadgetToolTip(#Button_Confirm, GetTooltipLang("ConfirmValidate"))
Repeat
Select WaitWindowEvent()
Case #PB_Event_CloseWindow
Break
Case #PB_Event_Gadget
Select EventGadget()
Case #Button_Confirm
If GetGadgetState(#Check_Agree)
MessageRequester("terms and conditions", GetMessageLang("Msg001"))
Break
EndIf
EndSelect
EndSelect
ForEver
EndIf
DataSection
; Here the default language is specified (usually in English). It is a list of Section and of Key name with its Value,
;
; with some special keywords for the Section:
; "_SECTION_" will indicate a new Section in the datasection, the second value is the Section name
; "_END_" will indicate the end of the language list (as there is no fixed number)
;
; Note: The Section and Key name are case insensitive to make live easier :)
DefaultLang:
; =========================================================================
Data.s "_SECTION_", "Interface"
; =========================================================================
Data.s "Text_Welcome", "Hello World!"
Data.s "0001", "I Agree To Terms"
Data.s "ConfirmValidate", "Confirm+#CRLF$++#DQUOTE$+Validate+#DQUOTE$+"
; =========================================================================
Data.s "_SECTION_", "Tooltip"
; =========================================================================
Data.s "0001", "I agree to terms and conditions"
Data.s "ConfirmValidate", "Confirm and validate the information entered"
; =========================================================================
Data.s "_SECTION_", "Menu"
; =========================================================================
Data.s "File", "File"
Data.s "New", "New"
Data.s "Open", "Open..."
Data.s "Save", "Save"
; =========================================================================
Data.s "_SECTION_", "ToolBar"
; =========================================================================
; =========================================================================
Data.s "_SECTION_", "StatusBar"
; =========================================================================
; =========================================================================
Data.s "_SECTION_", "Message"
; =========================================================================
Data.s "Msg001", "Thank you for accepting the terms and conditions!"
; =========================================================================
Data.s "_END_", ""
; =========================================================================
EndDataSection
Edit: Do not create a language file for the default language, English
Edit2: Add +#CRLF$+ and +#DQUOTE$+ escape character