| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420 |
- Strict
- Rem
- bbdoc: MaxGUI/Localization
- End Rem
- Module MaxGUI.Localization
- ModuleInfo "Version: 1.02"
- ModuleInfo "Author: Seb Hollington"
- ModuleInfo "License: zlib/libpng"
- Import BRL.System
- Import "language.bmx"
- Const LOCALIZATION_OFF:Int = 0
- Const LOCALIZATION_ON:Int = 1
- Rem
- bbdoc: Returns the localized version of a string.
- about: This function takes one parameter: @{localizationstring$}.
- A localization string is just like any other string except that any phrase enclosed in a double pair of
- curly-braces is identified as a localization token. For example, the following examples all use valid
- localization strings.
- {{
- LocalizeString("{{welcomemessage}}") 'Localization token(s): welcomemessage
- LocalizeString("{{apptitlelabel}}: {{AppTitle}}") 'Localization token(s): apptitlelabel, AppTitle
- LocalizeString("Current Time: {{CurrentTime}}") 'Localization token(s): CurrentTime
- }}
- Localization tokens are case insensitive, and may be made up of any combination of alphanumeric
- characters. Firstly, the token is tested to see if it is a reserved word. The following tokens
- are currently reserved (although more maybe added in the future):
- [ @{Localization Token} | @{Token Will Be Replaced With...}
- * AppDir | The value of the #AppDir global constant.
- * AppFile | The value of the #AppFile global constant.
- * AppTitle | The value of the #AppTitle global constant.
- * LaunchDir | The value of the #LaunchDir global constant.
- * GCMemAlloced | The value returned by the #GCMemAlloced function (at the moment the token is parsed).
- ]
- There are also some reserved date and time tokens which will display the current date and time (at the
- moment of parsing) using any formats defined in the current language. If there are no matching formats
- explicitly defined, the formats default to:
- [ @{Localization Token} | @{Default Format} | @{Sample Output}
- * ShortTime | "hh:mm pp" | 02:36 {{pm}}
- * LongTime | "hh:mm:ss" | 14:36:51
- * ShortDate | "dd/mm/yy" | 04/08/09
- * LongDate | "dddd oo mmmm yyyy" | {{Monday}} {{4th}} {{August}} 2009
- ]
- Notice how any text-based time and date information is wrapped in curly braces. These tokens will be
- localized, just like any other token, and so can be modified by adding a corresponding entry to the
- localization language.
- This also demonstrates the ability of the localization parser to handle nested tokens. To prevent lock-
- ups, the localization parser checks for cyclic token definitions, and if one is encountered the token will
- be simply replaced with '!ERROR!' and the the offending localization string will be identified in the warning
- message written to standard error.
- If and only if the localization token isn't reserved will the current localization language be queried. If
- no localization language is selected, or if there is no matching token defined in the current language, the
- token will simply be stripped of its curly braces in the returned string. Each language is required to have
- at least one token defined: {{LanguageID}}. This should represent the language name e.g. 'Fran�ais (French)'.
- %{NOTE: This function requires the LOCALIZATION_ON flag to be set (see #SetLocalizationMode) otherwise
- the function will simply return @{localizationstring$} exactly as it was passed (including any curly braces).}
- See Also: #SetLocalizationMode, #LocalizationMode, #SetLocalizationLanguage and #LocalizationLanguage.
- EndRem
- Function LocalizeString$( localizationstring$ )
- Return TMaxGUILocalizationEngine.LocalizeString( localizationstring )
- EndFunction
- Rem
- bbdoc: Enable or disable the localization engine, and set other localization modes.
- about: The mode can be set to:
- [ @{Constant} | @{Meaning}
- * LOCALIZATION_OFF | Any localized gadgets will display their localizedtext$ as their actual text.
- * LOCALIZATION_ON | Localized gadgets will use the current language to display their text.
- ]
- Either mode can be combined (btiwse OR'd) with LOCALIZATION_OVERRIDE, which will cause gadgets
- to become automatically 'localized' when they are created, with any @{text$} parameters supplied
- to the @{CreateGadget()} functions being interpreted as localization strings.
- If any window menus are localized, #UpdateWindowMenu may have to be called on all relevant windows for the text changes
- to be visible.
- See Also: #LocalizationMode, #SetLocalizationLanguage, #LocalizationLanguage and #LocalizeGadget.
- EndRem
- Function SetLocalizationMode( mode:Int = LOCALIZATION_ON )
- _SetLocalizationMode(mode:Int)
- EndFunction
- Global _SetLocalizationMode( mode:Int ) = TMaxGUILocalizationEngine.SetMode
- Rem
- bbdoc: Returns the value previously set using #SetLocalizationMode.
- about: The default value for a MaxGUI program is LOCALIZATION_OFF.
- See #SetLocalizationMode for valid modes, and their corresponding constants.
- EndRem
- Function LocalizationMode:Int()
- Return TMaxGUILocalizationEngine.GetMode()
- EndFunction
- Rem
- bbdoc: Set the language to be used by MaxGUI's localization system.
- about: Languages can be loaded from files/streams using #LoadLanguage and created from scratch using
- #CreateLanguage.
- This function will automatically update the text of any gadget marked as 'localized' using #LocalizeGadget.
- If any window menus are localized, #UpdateWindowMenu may have to be called on all relevant windows for the text changes
- to be visible.
- See Also: #LocalizationLanguage, #SetLocalizationMode, #LocalizationMode and #LocalizeString.
- EndRem
- Function SetLocalizationLanguage( language:TMaxGUILanguage )
- _SetLocalizationLanguage( language )
- EndFunction
- Global _SetLocalizationLanguage( language:TMaxGUILanguage ) = TMaxGUILocalizationEngine.SetLanguage
- Rem
- bbdoc: Returns the current language used by MaxGUI's localization system.
- about: Use the #DefineLanguageToken, #RemoveLanguageToken and #ClearLanguageTokens commands to add to and
- modify the returned language. #SetLanguageName and #LanguageName may also be useful.
- about: See Also: #SetLocalizationLanguage, #SetLocalizationMode, #LocalizationMode and #LocalizeGadget.
- EndRem
- Function LocalizationLanguage:TMaxGUILanguage()
- Return TMaxGUILocalizationEngine.GetLanguage()
- EndFunction
- Private
- Type TMaxGUILocalizationEngine
-
- Global intLocalizationMode:Int = LOCALIZATION_OFF
- Global _currentLanguage:TMaxGUILanguage
- Global _localizeStack:String[]
-
- Method New()
- Return Null
- EndMethod
-
- ' Indirection allows MaxGUI.MaxGUI's TMaxGUIDriver to intercept function calls
- ' and update gadgets if necessary (see bottom of maxgui.mod/driver.bmx).
- Global SetMode( mode:Int ) = TMaxGUILocalizationEngine._SetMode
- Global SetLanguage( language:TMaxGUILanguage ) = TMaxGUILocalizationEngine._SetLanguage
-
- Function _SetMode( mode:Int )
- intLocalizationMode = mode
- EndFunction
-
- Function GetMode:Int()
- Return intLocalizationMode
- EndFunction
-
- Function GetLanguage:TMaxGUILanguage()
- Return _currentLanguage
- EndFunction
-
- Function _SetLanguage( language:TMaxGUILanguage )
- _currentLanguage = language
- EndFunction
-
- Function LocalizeString:String( Text$ )
-
- ' Only localize the string if the localization engine is turned on.
- If (intLocalizationMode&LOCALIZATION_ON) Then
-
- ' Check for cyclic definitions by comparing localization string with those on the stack.
- For Local i:Int = _localizeStack.length-1 To 0 Step -1
- If _localizeStack[i] = Text Then
- WriteStderr "WARNING: Encountered cyclic localization string: ~q"+_localizeStack[i]+"~q.~n"
- Return "!ERROR!"
- EndIf
- Next
-
- ' Add localization string to the stack.
- _localizeStack:+[Text]
-
- Local tmpText:String
- Local i:Int, tmpPrevChar:Int
- Local tmpCount:Int, tmpOpenings:Int[Text.length/2] 'manages opening character index for nested tokens
-
- ' Start parsing for curly braces
- While i < Text.length
- Select Text[i]
- Case Asc("{")
-
- 'If previous char was also an opening "{" then we're entering into a token
- If tmpPrevChar = Text[i] Then
- tmpOpenings[tmpCount] = i+1
- tmpCount:+1
- tmpPrevChar = 0
-
- 'Otherwise update the value of the last char and move onto the next character.
- Else
- tmpPrevChar = Text[i]
- EndIf
-
- Case Asc("}")
-
- 'If previous char was also a closing "}" then we're leaving a token, so interpret it.
- If tmpPrevChar = Text[i] Then
-
- ' Retrieve the token text
- tmpCount:-1
- tmpText = Text[tmpOpenings[tmpCount]..i-1]
-
- ' Check for reserved words or run it through the dictionary.
- Select tmpText.ToLower()
-
- ' Keywords
- Case "appfile";tmpText = AppFile
- Case "appdir";tmpText = AppDir
- Case "apptitle";tmpText = AppTitle
- Case "launchdir";tmpText = LaunchDir
- Case "gcmemalloced";tmpText = GCMemAlloced()
-
- ' Time parsing
- Case "shorttime", "longtime"
- ' Check if the current language defines its own time format for the token
- If _currentLanguage Then tmpText = LocalizeString(_currentLanguage.LookupToken(tmpText))
- ' Either way, call LocalizeTime() and then localize the returned string (in case it contains tokens too).
- tmpText = LocalizeString(LocalizedTime(tmpText))
-
- ' Date parsing
- Case "shortdate", "longdate"
- ' Check if the current language defines its own date format for the token
- If _currentLanguage Then tmpText = LocalizeString(_currentLanguage.LookupToken(tmpText))
- ' Either way, call LocalizeDate() and then localize the returned string (in case it contains tokens too).
- tmpText = LocalizeString(LocalizedDate(tmpText))
-
- ' Language definition
- Default
- If _currentLanguage Then
- ' Lookup token using the current language, and localize the returned string (in case that contains tokens).
- tmpText = LocalizeString(_currentLanguage.LookupToken(tmpText))
- EndIf
-
- EndSelect
-
- ' Substitute the localized text into the string in-place, to enable nested parsing to work.
- Text = Text[..tmpOpenings[tmpCount]-2] + tmpText + Text[i+1..]
- i:+(tmpText.length-(i+3-tmpOpenings[tmpCount]))
- tmpPrevChar = 0
-
- 'Otherwise update the value of the last char and move onto the next character.
- Else
- tmpPrevChar = Text[i]
- EndIf
-
- Default
- tmpPrevChar = 0
- EndSelect
- i:+1
- Wend
-
- ' Remove localization string from stack as we're about to return
- _localizeStack = _localizeStack[.._localizeStack.length-1]
-
- EndIf
-
- ' Return the localized text.
- Return Text
-
- EndFunction
-
- Function LocalizedTime:String(pTimeFormat$)
- Local i:Int = 0, tmpTokenCount:Int = 0, tmpToken$, tmpTime:String[] = CurrentTime().Split(":")
- Select pTimeFormat.ToLower()
- Case "shorttime";pTimeFormat = "hh:mm "
- Case "longtime";pTimeFormat = "hh:mm:ss "
- Default;pTimeFormat:+" "
- EndSelect
- While i < pTimeFormat.length
-
- If tmpTokenCount And (pTimeFormat[i-1] <> pTimeFormat[i]) Then
- tmpToken = Null
- Select pTimeFormat[i-1]
- Case Asc("h")
- Select tmpTokenCount
- Case 1;tmpToken = Int(tmpTime[0]) Mod 12
- Case 2;tmpToken = Int(tmpTime[0])
- EndSelect
- Case Asc("m")
- Select tmpTokenCount
- Case 1;tmpToken = Int(tmpTime[1])
- Case 2;tmpToken = tmpTime[1]
- EndSelect
- Case Asc("s")
- Select tmpTokenCount
- Case 1;tmpToken = Int(tmpTime[2])
- Case 2;tmpToken = tmpTime[2]
- EndSelect
- Case Asc("p")
- If tmpTokenCount = 2 Then
- If tmpTime[0] < 12 Then tmpToken = "{{am}}" Else tmpToken = "{{pm}}"
- EndIf
- EndSelect
- If tmpToken
- pTimeFormat = pTimeFormat[..(i-tmpTokenCount)] + tmpToken + pTimeFormat[i..]
- i:+tmpToken.length-tmpTokenCount
- EndIf
- tmpTokenCount = 0
- EndIf
-
- Select pTimeFormat[i]
- Case "h"[0],"m"[0],"s"[0],"p"[0]
- tmpTokenCount:+1
- Default
- tmpTokenCount = 0
- EndSelect
-
- i:+1
-
- Wend
- Return pTimeFormat[..pTimeFormat.length-1]
- EndFunction
-
- Function LocalizedDate:String(pDateFormat$)
- Local i:Int = 0, tmpTokenCount:Int = 0, tmpToken$, tmpDate:String[] = CurrentDate().Split(" ")
- Select pDateFormat.ToLower()
- Case "shortdate";pDateFormat= "dd/mm/yy "
- Case "longdate";pDateFormat = "dddd oo mmmm yyyy "
- Default;pDateFormat:+" "
- EndSelect
- While i < pDateFormat.length
-
- If tmpTokenCount And (pDateFormat[i-1] <> pDateFormat[i]) Then
- tmpToken = Null
- Select pDateFormat[i-1]
- Case Asc("d")
- Select tmpTokenCount
- Case 1;tmpToken = Int(tmpDate[0])
- Case 2;tmpToken = tmpDate[0]
- Case 3,4
- Local tmpDayAsInt:Int = DayOfTheWeek(Int(tmpDate[0]),_MonthAsNumber(tmpDate[1]),Int(tmpDate[2]))
- If tmpTokenCount = 3 Then tmpToken = "{{"+_shortDays[tmpDayAsInt]+"}}" Else tmpToken = "{{"+_fullDays[tmpDayAsInt]+"}}"
- EndSelect
- Case Asc("m")
- Select tmpTokenCount
- Case 1,2
- Local tmpMonth:Int = _MonthAsNumber(tmpDate[1])
- If tmpTokenCount = 1 And tmpMonth < 10 Then tmpToken = " " + tmpMonth Else tmpToken = tmpMonth
- Case 3;tmpToken = "{{"+tmpDate[1]+"}}"
- Case 4;tmpToken = "{{" + _fullMonths[_MonthAsNumber(tmpDate[1])] + "}}"
- EndSelect
- Case Asc("y")
- Select tmpTokenCount
- Case 2;tmpToken = tmpDate[2][tmpDate.length-2..]
- Case 4;tmpToken = tmpDate[2]
- EndSelect
- Case Asc("o")
- If tmpTokenCount = 2 Then
- Select Int(tmpDate[0])
- Case 1,21,31;tmpToken = "{{" + Int(tmpDate[0]) + "st}}"
- Case 2,22;tmpToken = "{{" + Int(tmpDate[0]) + "nd}}"
- Case 3,23;tmpToken = "{{" + Int(tmpDate[0]) + "rd}}"
- Default;tmpToken = "{{" + Int(tmpDate[0]) + "th}}"
- EndSelect
- EndIf
- EndSelect
- If tmpToken
- pDateFormat = pDateFormat[..(i-tmpTokenCount)] + tmpToken + pDateFormat[i..]
- i:+tmpToken.length-tmpTokenCount
- EndIf
- tmpTokenCount = 0
- EndIf
-
- Select pDateFormat[i]
- Case Asc("d"),Asc("m"),Asc("y"),Asc("o")
- tmpTokenCount:+1
- Default
- tmpTokenCount = 0
- EndSelect
-
- i:+1
- Wend
- Return pDateFormat[..pDateFormat.length-1]
- EndFunction
-
- Global _fullMonths:String[] = ["", "January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"]
- Global _shortDays:String[] = ["Mon","Tue","Wed","Thu","Fri","Sat","Sun"]
- Global _fullDays:String[] = ["Monday","Tuesday","Wednesday","Thursday","Friday","Saturday","Sunday"]
-
- Function _MonthAsNumber:Int(month$)
- Select month.ToLower()
- Case "jan";Return 1
- Case "feb";Return 2
- Case "mar";Return 3
- Case "apr";Return 4
- Case "may";Return 5
- Case "jun";Return 6
- Case "jul";Return 7
- Case "aug";Return 8
- Case "sep";Return 9
- Case "oct";Return 10
- Case "nov";Return 11
- Case "dec";Return 12
- EndSelect
- RuntimeError "Unrecognised month: ~q" + month + "~q"
- EndFunction
-
- Function DayOfTheWeek:Int(Day:Int, Month:Int, Year:Int)
- Local Jt:Float = Float(367 * Year - ((7 * (Year + 5001 + ((Month - 9) / 7))) / 4) + ((275 * Month) / 9) + Day + 1729777)+1.5
- Return (jt Mod 7)
- End Function
- EndType
|