How would I? (Classes)

Monkey Forums/Monkey Programming/How would I? (Classes)

Supertino(Posted 2014) [#1]
I want my game to support several languages.

Thinking I would create different Classes (same field names) for each language;
Class StringsEN
	Field MainMenuPlayCMD:String = "play"
	Field MainMenuSettingsCMD:String = "settings"
	Field MainMenuAboutCMD:String = "about"
End Class

Class StringsFR
	Field MainMenuPlayCMD:String = "fr play"
	Field MainMenuSettingsCMD:String = "fr settings"
	Field MainMenuAboutCMD:String = "fr about"
End Class

Class StringsDE
	Field MainMenuPlayCMD:String = "de play"
	Field MainMenuSettingsCMD:String = "de settings"
	Field MainMenuAboutCMD:String = "de about"
End Class


... then some how link a Class or a Global (we'll call it Text) to one of the language Classes so all I need to do programmatic is call the below regardless of the language.

DrawText Text.MainMenuPlayCMD,0,0


I could ofc hard code it;

Global Text:StringsEN
Text = New StringsEN


But I want a user to choose on start-up.


Trez(Posted 2014) [#2]
You will over complicate your app and end up with lots of duplicate code if you use the method described above.

Only use a single field for each of the text strings. Then either create the embedded translations as some kind of array / list or use external files to contain the text.

On language selection simply read the file or array and assign the correct strings to the fields for the selected language.


Danilo(Posted 2014) [#3]
Class StringsBase
    Field MainMenuPlayCMD:String
    Field MainMenuSettingsCMD:String
    Field MainMenuAboutCMD:String
End Class


Class StringsEN Extends StringsBase
    Method New()
        MainMenuPlayCMD     = "play"
        MainMenuSettingsCMD = "settings"
        MainMenuAboutCMD    = "about"
    End
End

Class StringsFR Extends StringsBase
    Method New()
        MainMenuPlayCMD     = "fr play"
        MainMenuSettingsCMD = "fr settings"
        MainMenuAboutCMD    = "fr about"
    End
End

Class StringsDE Extends StringsBase
    Method New()
        MainMenuPlayCMD     = "de play"
        MainMenuSettingsCMD = "de settings"
        MainMenuAboutCMD    = "de about"
    End
End


Function Main:Int()
    Local Text:StringsBase
    
    Text = New StringsEN
    Print(Text.MainMenuPlayCMD)
    Text = New StringsFR
    Print(Text.MainMenuPlayCMD)
    Text = New StringsDE
    Print(Text.MainMenuPlayCMD)

    Return 0
End



therevills(Posted 2014) [#4]
Diddy has an international module, where you store your translations in a simple xml file:
<?xml version="1.0" ?>
<i18n>
         <language name="french">
                 <string>
                         <key>Hello World!</key>
                         <value>Bonjour, tout le monde!</value>
                 </string>
         </language>
 
        <language name="japanese">
                 <string>
                         <key>Hello World!</key>
                         <value>こんにちは、みなさん!</value>
                 </string>
         </language>
 </i18n>

http://code.google.com/p/diddy/source/browse/examples/I18N/testI18N.monkey


Trez(Posted 2014) [#5]
Good point... Diddy, Fantom and Ignition all provide ways to localize the language.


Danilo(Posted 2014) [#6]
Class Translator
    Field _map:StringMap<String>
    
    Method New()
        _map = New StringMap<String>
        Self.SetLanguage("EN") ' default
    End
    
    Method Tr:String(txt:String)
        If _map.Contains(txt) = True
            Return _map.Get(txt)
        Else
            Return txt
        endif
    End
    
    Method SetLanguage(lang:String)
        ' Load all strings from "language.%lang%.txt" into _map
    End
End Class

Function Main:Int()
    Local Text:Translator = New Translator()
    
    Print(Text.Tr("play"))
    Text.SetLanguage("DE")
    Print(Text.Tr("play"))
    Text.SetLanguage("FR")
    Print(Text.Tr("play"))

    Return 0
End


Procedural:
Global _map:StringMap<String> = New StringMap<String>

Function tr:String(txt:String)
    If _map.Contains(txt) = True
        Return _map.Get(txt)
    Else
        Return txt
    endif
End

Function SetLanguage(lang:String)
    ' Load all strings from "language.%lang%.txt" into _map
    ' foreach line in language.%lang%.txt
    '    _map.Set( word, translation )
    ' next
End

Function Main:Int()
    SetLanguage("EN") ' default

    
    Print( tr("play") )
    SetLanguage("DE")
    Print( tr("play") )
    SetLanguage("FR")
    Print( tr("play") )

    Return 0
End

' language.EN.txt contents:
' play = play

' language.DE.txt contents:
' play = spielen

' language.FR.txt contents:
' play = fr play

I like the procedural tr() way for translating strings. Quick to write.


Supertino(Posted 2014) [#7]
@Danilo -Thanks your first solution is perfect
@therevills - I should probably look at Diddy for my next project and XML in general
@Trez - I am only going to have 20-30 strings in the game so I should be good

Thanks all. =D


Shinkiro1(Posted 2014) [#8]
I have a system nearly equal to the diddy one and load the values from an xml.
The only difference is that I use 1 file per language (like en.xml, de.xml, fr.xml, ...) which are all in a folder.

This is the interface:
Class Localize
	Function SetLanguage:Void(language:String)
	Function GetValue:String(key:String)
	Function SetValue:Void(key:String, value:String)
	Function GetCurrentLanguage:String()
	Function SetLanguageDirectory:Void(path:String)
	Function GetLanguageDirectory:String()
	Function UnlocalizedWords:String() 'returns all words that could not be localized (for debugging)
End


E.g.
SetLanguage("de")
will load de.xml from the folder that has been specified with SetLanguageDirectory().
After that you will use your strings like this:
Local menuPlay:String = Localize.GetValue("menu_play")