Multilanguage Dialogs

Share your advanced PureBasic knowledge/code with the community.
User avatar
HeX0R
Addict
Addict
Posts: 992
Joined: Mon Sep 20, 2004 7:12 am
Location: Hell

Multilanguage Dialogs

Post by HeX0R »

I'm not sure if anyone is aware of the simple possibility to add multi language support to Dialogs.
My DialogDesign0R uses that since the beginning, I thought it's time to introduce that feature.

This module is very tiny, but there is not more to do.
When you extend your dialog, extend the JSON file(s), no need to change any code, all will be done in the JSON files from then on.
The example code will create an english.lan and german.lan file in your temp folder.
I thought this might be more straight forward for all those guys, hating to copy/paste code, create files, name them accordingly, ...
Bad side is, the example code got longer than the module itself :mrgreen:

Enough, here we go:

Code: Select all

;/------------------
;|
;| DialogMultiLAN.pbi
;|
;| (c) HeX0R 2021
;|
;| V 1.00
;|
;| very tiny Module to add multi language support to your dialogs
;|
;| all you have to do is creating language files, in fact those are json files
;| Load() it with this module, and call Set() to set all phrases automatically
;| Both, the dialog and the language phrases are completely separated
;| Create dialogs without even thinking about multilanguage support.
;| Than add *.lan files, and that Module will do anything for you.
;|
;| The example code will create an english.lan and a german.lan file in your temp folder
;| This is for the lazy guys, just open it and examine it, expand it, do whatever you like.
;| Those files have to be UTF8 encoded!
;|
;/-------------------

; ----------------------------------------------------------------------------
; "THE BEER-WARE LICENSE":
; <HeX0R@coderbu.de> wrote this file. as long as you retain this notice you
; can do whatever you want with this stuff. If we meet some day, and you think
; this stuff is worth it, you can buy me a beer in return
; (see address on https://hex0rs.coderbu.de/).
; Or just go out and drink a few on your own/with your friends ;)
;=============================================================================

DeclareModule LANG
		
	Enumeration
		#MODE_Names
		#MODE_IDs
	EndEnumeration
	
	Declare SetPathToLAN(Path.s)
	Declare.s Load(Language.s = "english")
	Declare Set(Mode = #MODE_IDs)
	
EndDeclareModule

Module LANG
	
	Structure _LAN_
		Text.s
		ToolTipp.s
	EndStructure

	Structure _LAN_DIAGS_
		Title.s
		Map Gadget._LAN_()
	EndStructure
	
	Global NewMap LAN._LAN_DIAGS_()
	Global PathToLAN.s

	
	Procedure SetPathToLAN(Path.s)
		If Right(Path, 1) <> #PS$
			Path + #PS$
		EndIf
		PathToLAN = Path
	EndProcedure
	
	Procedure.s Load(Language.s = "english")
		Protected JSON, Result.s

		If FileSize(PathToLAN + Language + ".lan") = -1
			Language = "english"
		EndIf
		If FileSize(PathToLAN + Language + ".lan") = -1
			ProcedureReturn "File not found, forgot to call SetPathToLAN() maybe?"
		EndIf
		
		JSON = LoadJSON(#PB_Any, PathToLAN + Language + ".lan")
		If JSON
			ExtractJSONMap(JSONValue(JSON), LAN())
			FreeJSON(JSON)
		Else
			Result = "Error while parsing '" + Language + ~".lan'!\r\n"
			Result + "Error: " + JSONErrorMessage() + #CRLF$
			Result + "Line: " + Str(JSONErrorLine()) + #CRLF$
			Result + "Pos: " + Str(JSONErrorPosition())
		EndIf
		
		ProcedureReturn Result
	EndProcedure
	
	Procedure Set(Mode = #MODE_IDs)
		Protected DialogID$, GadgetID$, Dialog, Gadget
	
		ForEach LAN()
			DialogID$ = MapKey(LAN())
			If IsRuntime(DialogID$)
				Dialog = GetRuntimeInteger(DialogID$)
				If IsDialog(Dialog) And DialogWindow(Dialog) <> -1
					If LAN()\Title
						SetWindowTitle(DialogWindow(Dialog), LAN()\Title)
					EndIf
					ForEach LAN()\Gadget()
						GadgetID$ = MapKey(LAN()\Gadget())
						Gadget    = -1
						If Mode = #MODE_IDs
							If IsRuntime(GadgetID$)
								Gadget = GetRuntimeInteger(GadgetID$)
							EndIf
						Else
							Gadget = DialogGadget(Dialog, GadgetID$)
						EndIf
						If Gadget <> -1 And IsGadget(Gadget)
							If LAN()\Gadget()\Text
								SetGadgetText(Gadget, LAN()\Gadget()\Text)
							EndIf
							If LAN()\Gadget()\ToolTipp
								GadgetToolTip(Gadget, LAN()\Gadget()\ToolTipp)
							EndIf
						EndIf
					Next
					RefreshDialog(Dialog)
				EndIf
			EndIf
		Next
	EndProcedure

EndModule

;---- Module End



CompilerIf #PB_Compiler_IsMainFile
	
	
	Runtime Enumeration ;<-- the only thing you have to be aware of is, our dialogs must be runtime constants!
		#Dialog_1
		#Dialog_2
	EndEnumeration
	
	Runtime Enumeration
		#check_2
		#string_2
		#button_2
	EndEnumeration
	
	#DIALOG_USES_Names = 1 ;<- Dialog_1 uses names as Gadget identifiers, Dialog_2 uses IDs, this is just to demonstrate both options are supported
	
	Global UsedLANG.s = "english"
	
	Procedure.s GetXMLString()
		Protected XML$

		XML$ + "<?xml version='1.0' encoding='UTF-16'?>"
		XML$ + ""
		XML$ + "<dialogs>"
		XML$ + "  <window name='win_1' flags='#PB_Window_SystemMenu | #PB_Window_SizeGadget | #PB_Window_ScreenCentered' minwidth='250'>"
		XML$ + "    <vbox>"
		XML$ + "      <checkbox name='check_1'/>"
		XML$ + "      <string name='string_1'/>"
		XML$ + "      <button onevent='ChangeLang()' name='button_1'/>"
		XML$ + "    </vbox>"
		XML$ + "  </window>"
		XML$ + "  <window name='win_2' flags='#PB_Window_SystemMenu | #PB_Window_SizeGadget | #PB_Window_ScreenCentered' minwidth='250'>"
		XML$ + "    <vbox>"
		XML$ + "      <checkbox id='#check_2'/>"
		XML$ + "      <string id='#string_2'/>"
		XML$ + "      <button onevent='ChangeLang()' id='#button_2'/>"
		XML$ + "    </vbox>"
		XML$ + "  </window>"
		XML$ + "</dialogs><!--DDesign0R Definition: PureBasic|1|1|1|_|example_with_declares-->"
		XML$ + ""

		ProcedureReturn XML$
	EndProcedure
	
	Runtime Procedure ChangeLang()
		If UsedLANG = "english"
			UsedLANG = "german"
		Else
			UsedLANG = "english"
		EndIf
		LANG::Load(UsedLANG)
		CompilerIf #DIALOG_USES_Names
			LANG::Set(LANG::#MODE_Names)
		CompilerElse
			LANG::Set()
		CompilerEndIf
		
	EndProcedure
	
	a$ = GetXMLString()
	If ParseXML(0, a$) And XMLStatus(0) = #PB_XML_Success
		CompilerIf #DIALOG_USES_Names
			CreateDialog(#Dialog_1)
			OpenXMLDialog(#Dialog_1, 0, "win_1") ;<- win_1 uses gadget names
		CompilerElse
			CreateDialog(#Dialog_2)
			OpenXMLDialog(#Dialog_2, 0, "win_2") ;<- win_2 used gadget ids
		CompilerEndIf
		
		;create example lan files
		Restore english
		CreateFile(0, GetTemporaryDirectory() + "english.lan", #PB_UTF8)
		Repeat
			Read.s a$
			If a$
				WriteString(0, a$)
			EndIf
		Until a$ = ""
		CloseFile(0)
		Restore german
		CreateFile(0, GetTemporaryDirectory() + "german.lan", #PB_UTF8)
		Repeat
			Read.s a$
			If a$
				WriteString(0, a$)
			EndIf
		Until a$ = ""
		CloseFile(0)
		
		Lang::SetPathToLAN(GetTemporaryDirectory())
		b$ = LANG::Load()
		If b$
			;JSON parsing error
			Debug b$
		Else
			CompilerIf #DIALOG_USES_Names
				LANG::Set(LANG::#MODE_Names)
			CompilerElse
				LANG::Set()
			CompilerEndIf
		EndIf

		
		Repeat : Until WaitWindowEvent() = #PB_Event_CloseWindow
		
	EndIf
		
	
	DataSection
		english:
		Data.s ~"{\r\n"
		Data.s ~"  \"#Dialog_1\"        : {\r\n"
		Data.s ~"      \"Title\" : \"Just a Window (Dialog 1)\",\r\n"
		Data.s ~"      \"Gadget\": {\r\n"
		Data.s ~"          \"check_1\"       : {\r\n"
		Data.s ~"              \"Text\"    : \"a stupid CheckBox\",\r\n"
		Data.s ~"              \"ToolTipp\": \"Checkbox ToolTipp\"\r\n"
		Data.s ~"            },\r\n"
		Data.s ~"          \"string_1\"       : {\r\n"
		Data.s ~"              \"Text\"    : \"a stupid StringGadget\",\r\n"
		Data.s ~"              \"ToolTipp\": \"String ToolTipp\"\r\n"
		Data.s ~"            },\r\n"
		Data.s ~"          \"button_1\"       : {\r\n"
		Data.s ~"              \"Text\"    : \"Change Language\",\r\n"
		Data.s ~"              \"ToolTipp\": \"Click to change language\"\r\n"
		Data.s ~"            }\r\n"
		Data.s ~"        }\r\n"
		Data.s ~"    },\r\n"
		Data.s ~"  \"#Dialog_2\"        : {\r\n"
		Data.s ~"      \"Title\" : \"Just a Window (Dialog 2)\",\r\n"
		Data.s ~"      \"Gadget\": {\r\n"
		Data.s ~"          \"#check_2\"       : {\r\n"
		Data.s ~"              \"Text\"    : \"a stupid CheckBox\",\r\n"
		Data.s ~"              \"ToolTipp\": \"Checkbox ToolTipp\"\r\n"
		Data.s ~"            },\r\n"
		Data.s ~"          \"#string_2\"       : {\r\n"
		Data.s ~"              \"Text\"    : \"a stupid StringGadget\",\r\n"
		Data.s ~"              \"ToolTipp\": \"String ToolTipp\"\r\n"
		Data.s ~"            },\r\n"
		Data.s ~"          \"#button_2\"       : {\r\n"
		Data.s ~"              \"Text\"    : \"Change Language\",\r\n"
		Data.s ~"              \"ToolTipp\": \"Click to change language\"\r\n"
		Data.s ~"            }\r\n"
		Data.s ~"        }\r\n"
		Data.s ~"    }\r\n"
		Data.s ~"}\r\n"
		Data.s ""
		german:
		Data.s ~"{\r\n"
		Data.s ~"  \"#Dialog_1\"        : {\r\n"
		Data.s ~"      \"Title\" : \"Nur ein doofes Window (Dialog 1)\",\r\n"
		Data.s ~"      \"Gadget\": {\r\n"
		Data.s ~"          \"check_1\"       : {\r\n"
		Data.s ~"              \"Text\"    : \"eine komische CheckBox\",\r\n"
		Data.s ~"              \"ToolTipp\": \"Checkbox ToolTipp auf deutsch\"\r\n"
		Data.s ~"            },\r\n"
		Data.s ~"          \"string_1\"       : {\r\n"
		Data.s ~"              \"Text\"    : \"ein komisches StringGadget\",\r\n"
		Data.s ~"              \"ToolTipp\": \"String ToolTipp auf deutsch\"\r\n"
		Data.s ~"            },\r\n"
		Data.s ~"          \"button_1\"       : {\r\n"
		Data.s ~"              \"Text\"    : \"Sprache ändern\",\r\n"
		Data.s ~"              \"ToolTipp\": \"Klicken um die Sprache zu ändern\"\r\n"
		Data.s ~"            }\r\n"
		Data.s ~"        }\r\n"
		Data.s ~"    },\r\n"
		Data.s ~"  \"#Dialog_2\"        : {\r\n"
		Data.s ~"      \"Title\" : \"Nur ein doofes Window (Dialog 1)\",\r\n"
		Data.s ~"      \"Gadget\": {\r\n"
		Data.s ~"          \"#check_2\"       : {\r\n"
		Data.s ~"              \"Text\"    : \"eine komische CheckBox\",\r\n"
		Data.s ~"              \"ToolTipp\": \"Checkbox ToolTipp auf deutsch\"\r\n"
		Data.s ~"            },\r\n"
		Data.s ~"          \"#string_2\"       : {\r\n"
		Data.s ~"              \"Text\"    : \"ein komisches StringGadget\",\r\n"
		Data.s ~"              \"ToolTipp\": \"String ToolTipp auf deutsch\"\r\n"
		Data.s ~"            },\r\n"
		Data.s ~"          \"#button_2\"       : {\r\n"
		Data.s ~"              \"Text\"    : \"Sprache ändern\",\r\n"
		Data.s ~"              \"ToolTipp\": \"Klicken um die Sprache zu ändern\"\r\n"
		Data.s ~"            }\r\n"
		Data.s ~"        }\r\n"
		Data.s ~"    }\r\n"
		Data.s ~"}\r\n"
		Data.s ""
	EndDataSection
		
CompilerEndIf
Mesa
Enthusiast
Enthusiast
Posts: 349
Joined: Fri Feb 24, 2012 10:19 am

Re: Multilanguage Dialogs

Post by Mesa »

:D
User avatar
OldSkoolGamer
Enthusiast
Enthusiast
Posts: 150
Joined: Mon Dec 15, 2008 11:15 pm
Location: Nashville, TN
Contact:

Re: Multilanguage Dialogs

Post by OldSkoolGamer »

HeX0R,

Thanks for posting this module. Very nice. I was looking for an easier way to translate my apps and this will definitely make it easier. Much appreciated. :D
User avatar
Kwai chang caine
Always Here
Always Here
Posts: 5353
Joined: Sun Nov 05, 2006 11:42 pm
Location: Lyon - France

Re: Multilanguage Dialogs

Post by Kwai chang caine »

Nice idea, works here :wink:
Thanks for sharing 8)
ImageThe happiness is a road...
Not a destination
Post Reply