Warning: Constant ABSPATH already defined in /customers/e/7/0/florian-oeser.de/httpd.www/wordpress/wp-config.php on line 28 Development-Blog von Florian Oeser » Blog Archive » How-To: Textlokalisierung mit .NET

How-To: Textlokalisierung mit .NET

11. Juli 2008 – 14:16

Dieses Tutorial wurde ebenfalls auf myCSharp und xnamag.de veröffentlicht und kann auch dort diskutiert werden!

Einleitung

Hallo und Herzlich Willkommen zu einem weiterem How-To. Diesmal geht es um die Textlokalisierung. Ein effizientes und zugleich einfaches System zur Lokalisierung, zumindest von Texten, muss in jeder Anwendung, die mehrsprachig ausgeliefert werden soll, vorhanden sein.

Wichtig dabei ist das alle Texte in externen Dateien liegen, damit sie sich problemlos übersetzen lassen. Texte gehören keinesfalls in den Programmcode. Es ist dann auch eminent wichtig das diese Textdateien eine gute Struktur haben, damit sich diese komfortabel übersetzten lassen und in die Anwendung eingebunden werden können. Deshalb sollten alle Texte übersichtlich formatiert und gleich in der richtigen Reihenfolge angeordnet sein. Es kann auch von Vorteil sein an diversen Stellen Zusatzinformationen an den Übersetzer mitzugeben. Wie so eine Datei aussehen kann sehen wir gleich, nur vorweg, es ist eh nicht die optimale Lösung.

Ich möchte euch bevor wir zur Umsetzung mit .NET kommen noch zwei andere Möglichkeiten zeigen wie man Texte lokalisieren kann. Danach wird dann auch deutlich warum die Umsetzung mit .NET das Beste ist.

Umsetzung

Alle drei Umsetzungen möchte ich anhand von diesem kleinen Beispiel verdeutlichen:


MenuEntry optionsMenuEntry = new MenuEntry("Options"));

Generell wird alles mit C# umgesetzt!

Lokalisierung über Stringexternalisierung

Die Möglichkeit ist die einfachste aber zugleich auch die Schlechteste. Zum Einem liegt der Text direkt im Programmcode und zum Anderen muss die Sprache zur Kompilierung feststehen. Für eine kleine Anwendung mit wenig Text ist es aber trotzdem eine schönere Lösung als die Texte direkt zu schreiben.

Prinzipiell beruht das Verfahren auf einer statischen Klasse in der die Strings externalisiert werden.

using System;
using System.Collections.Generic;
using System.Text;

namespace MarioWorldWars.Text
{
sealed class GermanText
{
public static String OPTIONSMENU = "Optionen";
}
}

MenuEntry optionsMenuEntry = new MenuEntry(GermanText.OPTIONSMENU));

Lokalisierung über externe Dateien

Kommen wir zu der Möglichkeit die ich in der Einleitung erwähnt habe. Nun kommen alle Texte sauber in eine externe Datei. Die könnte zum Beispiel so ausehen:

MENU_OPTS                         „Einstellungen“
MENU_CONTROLLEROPTS    „Steuerungseinstellungen“ ; bitte auf Plural achten

Hinter dem Semikolon könnte jetzt für den Übersetzter noch eine Bemerkung angebracht werden.

Diese Datei wird jetzt einfach eingelesen und der entsprechende String kann zurückgeben werden. Der Wechsel zwischen den verschiedenen Dateien ist natürlich problemlos zur Laufzeit möglich. Ein kleines Beispiel zum Einlesen:

using System;
using System.Collections.Generic;
using System.Text;
using System.IO;

namespace ConsoleApplication1
{
class FileReader
{

private StreamReader sr;

public FileReader()
{
//read the file
sr = new StreamReader(@"test.txt", System.Text.Encoding.Default);

}

public String getString(String patter)
{
String line = string.Empty;
try
{
while ((line = sr.ReadLine()) != null)
{

if (line.Contains(patter)) //else read next line
{
int first = line.IndexOf(‚"‘);
int last = line.LastIndexOf(‚"‘);
//return the correct text related to the pattern
return line = line.Substring(++first, last – first);
}
else line = "Not found";
}
}
catch (Exception e)
{
Console.WriteLine("The file could not be read:");
Console.WriteLine(e.Message);
}

return line;
}
}
}

Ist natürlich so nicht zu gebrauchen, da die Funktion fehlt, um die Datei zu wechseln.

FileReader fr = new FileReader();
MenuEntry optionsMenuEntry = new MenuEntry(fr.getString("MENU_OPTS"));

Im Übrigen wurde dieses System in der /GameStar/Dev Ausgabe 01/08 vorgestellt in der es auch generell um die Lokalisierung von Spielen ging.

Lokalisierung mit .NET

Kommen wir nun zu der elegantesten Lösung. Die Textlokalisierung mit Ressourcendateien durch .NET!Das System ist sehr einfach und elegant. Auch wenn diese Dateien ja eigentlich zum Projekt gehören, lassen sie sich extern öffnen und so von Lokalisierungsagenturen bearbeiten. Somit gehören sie nicht direkt zum Programmcode und widersprechen deshalb auch nicht der wichtigen Regel zur Lokalisierung.

Hierzu legt man einfach für jede Sprache eine entsprechende Ressourcendatei an, in der dann die verschiedenen Texte in der jeweiligen Sprache verfasst werden. Auch da ist es direkt möglich ein entsprechenden Kommentar hinzuzufügen. Die Datei könnte dann wie folgt aussehen:

bsp_ressourcendatei

Der Name bleibt logischerweise in jeder Ressourcendatei der gleiche lediglich das Value ändert sich. Wichtig ist die Benennung der Datei. Das heißt das .NET Framework erkennt anhand des Suffixes um welche Sprachressource es sich handelt.

Das heißt beispielsweise das die Ressouce für den deutschen Text den Suffix *.de-DE am Ende des Dateinamens tragen muss. Es ist auch möglich eine Datei ohne Suffix anzulegen. Dann wird diese ausgewählt wenn ein Suffix, der übergeben wird, nicht explizit definiert wurde.

Alle Ressourcendateien müssen im Übrigen vor dem Suffix den gleichen Namen haben, damit diese von dem ResourceManager erkannt werden. Hier in dem Beispiel ResStrings.

ResStrings.resx

ResStrings.de-DE.resx

ResString.cs-CZ.resx

Alle Suffixe gibt es in der CultureInfo-Klasse nachzulesen.

Nun schreiben wir eine Klasse die zwei statische Methoden enthält um global auf diese zugreifen zu können. Zum Einen eine Methode die die jeweilige Textressource auswählt und zum Anderen eine die uns den entsprechenden String zurückgibt.

using System;
using System.Collections.Generic;
using System.Text;
using System.Globalization;
using System.Threading;
using System.Resources;
using System.Reflection;

namespace Application.Text
{
sealed class Localization
{

private static ResourceManager resMgr;

public static void UpdateLanguage(string langID)
{
try
{
//Set Language
Thread.CurrentThread.CurrentUICulture = new CultureInfo(langID);

// Init ResourceManager
resMgr = new ResourceManager("Application.Text.ResStrings", Assembly.GetExecutingAssembly());

}
catch (Exception ex)
{
}
}

public static string getString(String pattern)
{
return resMgr.GetString(pattern);
}

}
}

Nun noch ein Beispiel wie diese Klasse im Zusammenhang mit unserem Beispiel funktioniert:

Localization.UpdateLanguage("de-DE");

MenuEntry optionsMenuEntry = new MenuEntry(Localization.getString("MENU_MAINOPTS"));

Der Aufruf zumindest einmalige Aufruf der UpdateLangugage() Methode ist erforderlich, weil in ihr der ResourceManager initialisiert wird.

Im Übrigen ist es auch möglich sich die String so zurückgeben zu lassen:

MenuEntry optionsMenuEntry = new MenuEntry(ResStrings.MENU_MAINOPTS);

Man ist also nicht gezwungen sich eine Methode zu schreiben, die den String zurückgibt. Das funktioniert weil hinter der eigentlichen Ressourcendatei eine Klasse steht und man so auf deren statischen Felder zugreifen kann.

Wollen wir die Sprache wechseln genügt der Aufruf der UpdateLangugage() Methode:

Localization.UpdateLanguage("de-DE");

Würden wir jetzt zum Beispiel den String „en-GB“ übergeben, würde die Ressource ohne Suffix aufgerufen werden, da ja „en-GB“ von uns nicht explizit angeben wurde.

Ich möchte noch darauf hinweisen, das wenn ihr mit Systemen wie den WinForms arbeitet, nicht vergessen dürft etwaige Buttons, Labels usw. dann auch zu updaten. Dies könnte gleich in UpdateLangugage() erfolgen oder besser in einer separaten Methode die dann von UpdateLangugage() aufgerufen wird.

Meine Quelle für diese Umsetzung war das kompakte Tutorial von Martin H.!

Hilfreiche Tools

Hier möchte ich noch kurz zwei wunderbare Tools erwähnen, die die Arbeit mit den Ressourcendateien erheblich vereinfachen.

Es kann ganz schön nervig sein bei mehreren Ressourcen und viel Text immer zwischen den Reitern hin- und herzuschalten. Abhilfe schafft hier der Zeta Resource Editor von Uwe Keim. Mit diesem Tool lassen sich mehrere Ressourcen nebeneinander betrachten und bearbeitet. Sehr sehr praktisch. Die Dateien einfach speichern und im Visual Studio die externen Änderungen bestätigen.

Das zweite Tool, Resource Refactoring Tool, ist ebenfalls sehr praktisch und erlaubt es Strings, die im Code stehen, schnell in eine Ressourcendatei zu packen. Weiterhin werden dann gleich die Strings durch den Ressourcenaufruf ersetzt.

Conclusion

Das war’s dann auch schon. Ich denke ihr habt eine schicke und effiziente Methode mit der Umsetzung durch .NET gesehen. Sicherlich gibt es noch andere Wege aber dies dürfte eine gute Basis darstellen eigene Anwendungen erfolgreich zu lokalisieren.

Ich möchte noch erwähnen dass das .NET Framework einen sehr großen, hier nicht weiter erörterten Funktionsumfang zur Lokalisierung von Anwendungen bereitstellt. Darunter zählen zum Beispiel Zeitangaben, Satzbau, Formatierungen, weitere Kulturinformationen und vieles mehr. Informationen dazu findet ihr unter anderem in der MSDN hier und hier.

Bei Fragen und Anregungen könnt ihr mir gerne mailen oder ein Kommentar hinterlassen!

  1. 3 Responses to “How-To: Textlokalisierung mit .NET”

  2. Hallo!

    Heißt das, dass ich den Text vom Button auf „BUTTON_DOWNLOAD“ umbenenne und dann funktioniert es (Wenn in der Ressource-Datei unter „BUTTON_DOWNLOAD“ „Herunterladen“ steht. Sorry, bi noch ein C# Anfänger

    By Florian R. on Okt 31, 2012

  3. Hallo!

    Ganz so einfach ist es nicht. Zumal nennst du den Button-Text nicht um, du weißt ihm einen String zu. Also:

    Button btn = new Button();
    btn.Text = ResStrings.BUTTON_DOWNLOAD;

    oder

    // initialisiert den ResourceManager
    Localization.UpdateLanguage(„de-DE“);

    btn.Text = Localization.getString(„BUTTON_DOWNLOAD“));

    By admin on Nov 5, 2012

  1. 1 Trackback(s)

  2. Sep 15, 2008: [Tutorial]Textlokalisierung mit .NET | Rent A Pizza
  3. Warning: Undefined variable $thiscomment in /customers/e/7/0/florian-oeser.de/httpd.www/wordpress/wp-content/themes/silver-light-01/silver-light-01/comments.php on line 91

Post a Comment

Warning: Undefined variable $user_ID in /customers/e/7/0/florian-oeser.de/httpd.www/wordpress/wp-content/themes/silver-light-01/silver-light-01/comments.php on line 117