TTranslator: (Übersetzung/internationalisation)

Übersicht BlitzMax, BlitzMax NG Codearchiv & Module

Neue Antwort erstellen

Xeres

Moderator

Betreff: TTranslator: (Übersetzung/internationalisation)

BeitragDi, Jan 29, 2013 0:45
Antworten mit Zitat
Benutzer-Profile anzeigen
Worum geht es hier?
TTranslator ist eine simple, kleine Klasse, mit der man ein Spiel oder eine Anwendung übersetzen bzw. internationalisieren kann (letzteres umfasst etwas mehr als Wörter allein, aber seien wir nicht so pingelig).
Im Prinzip handelt es sich um String Ersetzungen - keine Zauberei.

Code (mit Beispiel)
Bevor ihr neue Dateien anlegt, um auszuprobieren, ob und wie es läuft, nehmt dies kleine Archiv (107 kb) mit fertig kompilierten Beispielen und allen nun folgenden Dateien.

BlitzMax: [AUSKLAPPEN]
SuperStrict
Rem

TTranslator
i18n : i(nternationalisatio)n

Nötige Module:
Import brl.map
Import brl.filesystem
Import brl.linkedlist (debug)
endrem


Framework brl.standardio
Import brl.map
Import brl.linkedlist
Import brl.filesystem


'* Erstelle eine neue Instanz, die $<key>$ mit <value> ersetzt:
Global i18n:TTranslator = TTranslator.Create(["$", "$"])

'* Lade die deutschen keywords:
i18n.Add("i18n/de.txt", ["{", "}"])
Texte()

'* Vergiss die deutschen keywords
i18n.Clear()
'* Lade die englischen keywords:
i18n.Add("i18n/en.txt", ["{", "}"])
Texte()

?Debug
'* Was passiert, wenn man die deutsche Übersetzung darüber lädt?
i18n.Add("i18n/de.txt", ["{", "}"])
'* Beide files wurden geladen...
Print("~n".join(i18n.GetFilesLoaded()))
'* Aber die Übersetzungstabelle sieht so aus:
Print("~n".join(i18n.GetPairsLoaded()))
Print()
?

Input(i18n.Do("$key$"))
End

Function Texte()
'* Ersetze die in $ stehenden keywords und zeige das Ergebnis
Print(i18n.Do("Jeder Key"))
Print(i18n.Do("wie z.B. $Hello$"))
Print(i18n.Do("wird einfach ersetzt!"))
Print(i18n.Do("case sensitiv! => $HELLO$"))
Print(i18n.Do("Also: $Fun$"))
Print(i18n.Do("[$New$], [$End$]"))
Print()
End Function

Type TTranslator
Field Limit:String[]
Field Data:TMap
?Debug
Field files:TList
?

'* Erstellt eine neue Instanz:
Function Create:TTranslator(_limit:String[])
Local t:TTranslator = New TTranslator
t.Limit = _limit
t.Data = New TMap
?Debug
t.files = New TList
?
Return t
End Function

'* Löscht das Wörterbuch
Method Clear()
Self.Data.Clear()
?Debug
Self.files.Clear()
?
End Method

?Debug
'* Debugausgabe von geladenen Übersetzungs files
Method GetFilesLoaded:String[] ()
Local arr:String[]
For Local s:String = EachIn Self.files
arr:+[s]
Next
Return arr
End Method

'* Debugausgabe von geladenen Key Value paaren
Method GetPairsLoaded:String[] ()
Local arr:String[]
For Local s:String = EachIn Self.Data.Keys()
arr:+[s + " => " + String(Self.Data.ValueForKey(s))]
Next
Return arr
End Method
?

'* Füge ein neues file mit Übersetzungen hinzu, ein paar pro zeile, eingeschlossen in _limit
Method Add(_file:String, _limit:String[])
Local stream:TStream = OpenFile(_file)
If Not(stream) Then RuntimeError("TTranslator Add : can't open file ~q" + _file + "~q")
?Debug
Self.files.AddLast(_file)
?
While Not(stream.Eof())

Local pair:String[] = TTranslator.GetSubStrings(stream.ReadLine(), _limit[0], _limit[1])
If pair.Length <> 2 Then Continue

Self.Data.Insert(pair[0], pair[1])

Wend
End Method

'* Ersetze in _source alle Keys (und delimiter) mit ihren Values
Method Do:String(_source:String)
If _source.Find(Self.Limit[0]) <> - 1 Or _source.Find(Self.Limit[1]) <> - 1 Then
Local parts:String[] = TTranslator.GetSubStrings(_source, Self.Limit[0], Self.Limit[1])
For Local i:Int = 0 Until parts.Length
If Self.Data.Contains(parts[i]) Then
Local val:String = String(Self.Data.ValueForKey(parts[i]))
_source = _source.Replace(Self.Limit[0] + parts[i] + Self.Limit[1], val)
EndIf
Next
EndIf
Return _source
End Method

'* Hilfsfunktion um substrings aus delimitern heraus zu bekommen
Function GetSubStrings:String[] (_source:String, _start:String, _end:String = "")
If _end = "" Then _end = _start
Local out:String[], i:Int = 0

Repeat
Local pos1:Int = _source.Find(_start, i)
Local pos2:Int = _source.Find(_end, pos1 + _start.Length)
If pos1 = -1 Or pos2 = -1 Then Exit
i = pos2 + _end.Length
out:+[_source[pos1 + _start.Length..pos2]]
Until i > _source.Length

Return out
End Function

End Type


de.txt Code: [AUSKLAPPEN]
# Deutsche Version
{Hello} {Hallo Welt}
{Fun}   {Viel Spass!}
{New}   {Neues Spiel}
{End}   {Spiel Beenden}
{key}   {(Taste)}


en.txt Code: [AUSKLAPPEN]
# Englische Version
{Hello} {Hello world}
{Fun}   {Have fun!}
{New}   {New Game}
{End}   {End Game}
{key}   {(Press any key)}


Debug Ausgabe:
Code: [AUSKLAPPEN]
Jeder Key
wie z.B. Hallo Welt
wird einfach ersetzt!
case sensitiv! => $HELLO$
Also: Viel Spass!
[Neues Spiel], [Spiel Beenden]

Jeder Key
wie z.B. Hello world
wird einfach ersetzt!
case sensitiv! => $HELLO$
Also: Have fun!
[New Game], [End Game]

i18n/en.txt
i18n/de.txt
End => Spiel Beenden
Fun => Viel Spass!
Hello => Hallo Welt
New => Neues Spiel
key => (Taste)

(Taste)


Release Ausgabe:
Code: [AUSKLAPPEN]
Jeder Key
wie z.B. Hallo Welt
wird einfach ersetzt!
case sensitiv! => $HELLO$
Also: Viel Spass!
[Neues Spiel], [Spiel Beenden]

Jeder Key
wie z.B. Hello world
wird einfach ersetzt!
case sensitiv! => $HELLO$
Also: Have fun!
[New Game], [End Game]

(Press any key)


Dokumentation
Function Create:TTranslator(_limit:String[])
Erzeugt ein neues TTranslator Objekt, das keys zwischen _limit[0] und _limit[1] (zwei beliebige Strings an Anfang und Ende) ersetzt.
_limit sollte immer die Länge 2 haben, z.B. ["(*", "*)"]

Method Add(_file:String, _limit:String[])
Lädt neue Übersetzungsdefinitionen aus einer Datei.
_file ist der Dateipfad, _limit ist wiederum ein Array mit zwei Elementen, die als Begrenzung für die Wortpaare in der Datei dienen.
Es wird pro Zeile ein Wortpaar erwartet, andere Zeilen werden ignoriert.
Das erste Element ist der Key, nach dem gesucht wird, der zweite der Wert, mit dem ersetzt werden soll.
Bereits registrierte Keys werden einfach und ohne Warnung überschrieben.
Keys sind case sensitiv.

Method Do:String(_source:String)
Nimmt _source, ersetzt alle bekannten keys und liefert das Ergebnis zurück.

Method Clear()
Falls es nötig sein sollte, werden hiermit alle Wörter gelöscht.

Method GetFilesLoaded:String[]() debug only
Im Debugmodus schreibt die Klasse mit, welche Dateien eingeladen wurden. Hiermit bekommt ihr eine Liste zum loggen.

Method GetPairsLoaded:String[]() debug only
Gibt im Debugmodus alle Schlüssel mit den zugehörigen Werten zurück.

Sonstiges
Wie schnell ist das ganze?
Ich habe keine Ahnung. Stringverarbeitung ist vergleichsweise langsam, große, lange Texte würde ich also wenn möglich cachen, wenn man Performance Einbußen bemerkt.

Ich brauche aber andere Bilder und Sounds anstatt Strings! Was kann man da machen?
Man kann ganz einfach die Pfade anpassen: "/gfx/%LANG%/Bild.png" oder "/gfx/Bild_%LANG%.png" lassen sich zu "/gfx/de/Bild.png" oder "/gfx/Bild_de.png" Übersetzen.

Warum sollte ich mir die Mühe machen?
Es ist immer nett an die zu denken, die kein englisch oder deutsch sprechen. Wenn er mit euren spielen möglichst die meisten Spieler erreichen wollt, behaltet die Möglichkeit im Hinterkopf.

Warum hat vorher Niemand dran gedacht?
Ich glaube, Kernle 32DLL und Jolinah waren da einen Tick schneller.
Win10 Prof.(x64)/Ubuntu 16.04|CPU 4x3Ghz (Intel i5-4590S)|RAM 8 GB|GeForce GTX 960
Wie man Fragen richtig stellt || "Es geht nicht" || Video-Tutorial: Sinus & Cosinus
T
HERE IS NO FAIR. THERE IS NO JUSTICE. THERE IS JUST ME. (Death, Discworld)

M0rgenstern

BeitragDi, Jan 29, 2013 7:46
Antworten mit Zitat
Benutzer-Profile anzeigen
Hey Xeres,
Das sieht ganz ordentlich aus.
Leider muss man auch schon bei (einigen) kurzen Texten cachen.

Aus eigener Erfahrung kann ich dir nämlich sagen, dass es die Leistung extrem drosselt, wenn man zum Beispiel einen Text anzeigt, und dafür in jedem Frame eine TMap bemühen muss.
Aber die Texte die man benötigt zu cachen sollte wohl das geringste Problem sein.

Ansonsten:
Gute Arbeit.

Lg,
M0rgenstern

Neue Antwort erstellen


Übersicht BlitzMax, BlitzMax NG Codearchiv & Module

Gehe zu:

Powered by phpBB © 2001 - 2006, phpBB Group