Lokalisierung .resx oder .resources - Komplexe Grundsatzfrage zur Lokalisierung

Macan

Mitglied
Guten Abend erstmal.

Also ich bin noch ein blutiger Anfänger auf .net und c++. Ich habe in meinem ersten Programm eine Klasse gebaut, die die CurrentCulture ausliesst, mit den von mir unterstützten Sprachen vergleicht und dementsprechend eben eine Standardsprache setzt oder eben die gewünschte Sprache verwendet. Später kann dann mittels Methode die Sprache einfach umgestellt werden.

Ich muss noch hinzufügen, dass ich mit der Visual Studio Express Edition arbeite und, da ich zuerst noch ohne Forms gearbeitet habe, ich eben keine .resx Dateien erstellt habe, sondern eben .txt mit resgen in .resource und diese dann zu Satelliten compiliert habe lassen.

Nun zu meinen Fragen:

1. Ich habe nun zwei .dll Dateien (en und de). en soll std sein und möglichst schon in der .exe enthalten sein. Aber ich weiss nicht, wie ich das bewerkstelligen soll, denn mit "einbetten" in den Projekt-Eigenschaften geht das anscheinend nicht.

2. Ich hole mir mit der GetString Methode die Strings aus der entsprechend eingestellten .dll und stelle sie dar. Problem: Bis die Lokalisierung im laufenden Programm initialisiert ist, bin ich eben ohne unterwegs und somit mit evtl. auftretenden Fehlern, Excepions, Meldungen in hardgecodeter Form - das ist unschön. Gibt es einen Weg - so wie bei den Forms mit den .resx Dateien, eben die Std-Sprache auch gleich simpel mit in die .exe zu bringen, aber dennoch nicht so hardgecoded - aber eben ohne dann GetString von Anfang an benutzen zu müssen? (Es ist eben einiges zu initialisieren und ich versuche ja schon, einen möglichst sicheren Weg der Initialisierung des Programms zu gehen).

3. Was ist sinnvoller - .resource oder .resx (in Anbetracht der Express Edition, die ja keine .resx Resourcen erstellen kann, mal von den Forms abgesehen)?

4. Ich habe Lokalisierung.h (und .cpp) und z.B. Ausnahmen.h (und .cpp), dann noch main und eben eine ApplicationContext.h (und .cpp). Die ganze Initialisierung läuft quasi im ApplCon Konstruktor ab, später würden dann dort auch noch mehrere Forms gestartet werden. Nun ist aber die instantiierte Klasse Sprache^ nicht in den anderen Headern verwendbar. Das ist ja wohl ein ziemlich grundsätzliches Problem (strukturell) und ich hoffe, hier kann mir jemand auch ganz grundsätzlich erklären, wie das in c++ bzw. .net sinnvoller struktuiert werden kann?

Ich weiss, das ist ein Haufen Text und ich hoffe, ich habe die Probleme verständlich darstellen können. Frage 4 wäre eigentlich ein eigenes Thema für sich (wohl auch eher c++ und nicht .net, aber zumindest ist es in .net noch etwas schwieriger, wenn man "extern" verwenden will - die managed Types mögen das ja nicht wirklich).

Ich hoffe, jemand kann helfen.

lg Macan
 
So, dann wollen wir mal. :D

  1. SprachResourcen mit in die Anwendung zu integrieren verursacht eher mehr Nach- als Vorteile.
    Damit meine ich die Pfleg- und Erweiterbarkeit der Anwendung.
    Simple Updates währen so ohne viel Aufwand nicht mehr möglich.
  2. Stell die Thread.CurrentThread.CurrentCulture auf die gewünschte Sprache ein,
    gleich nachdem die Anwendung gestartet wurde. Also in der Main-Methode
    und noch bevor Du eines deiner Forms öffnest. Somit werden die Resourcen aus den Resourcen geladen,
    die der aktuell eingestellten Sprachen entsprechen. Werden sie aber nur,
    wenn Du das BuildIn Localizing Feature im VS verwendest.
    Stell das Form.Localizable Property im Propertygrid des Designers auf true.
    Danach kannst die gewünschte Sprache im Form.Language Property einstellen.
    Die Resourcen werden dann vom VS selber werwaltet. Im Programmordner werden
    diese Resourcen darauf hin Ordnern abgelegt, deren name der ResoruceCulture entspricht.
  3. *.resource-Dateien werden darauf hin automatisch aus den vom Designer generierten *.resx-Dateien generiert
    und in die besagten Ordner kopiert.
  4. Wenn Du ein Objekt benötigst, welches nur einmal aber überall im Programm benötigt wird,
    kannst es viá des Singleton Patterns verfügbar machen.
    Im VS2005 ist das schon so implementiert. Erstell einfach eine neue Resource für dein Form.
    Verwende diese Resoucen aber bitte nur für Dialogtexte etc.. Die Beschriftungen der Controls,
    würd ich der Schnelligkeit wegen im Designer Pflegen. Du kannst Dir aber auch ein kleines Übersetzungstool schreiben, wenn Du verstehst was ich meine.
Ich muss noch hinzufügen, dass ich mit der Visual Studio Express Edition arbeite und, da ich zuerst noch ohne Forms gearbeitet habe, ich eben keine .resx Dateien erstellt habe, sondern eben .txt mit resgen in .resource und diese dann zu Satelliten compiliert habe lassen.
Text Dateien sind in dem Fall unpraktisch.
Wenn du ein XML (*.resx) verwendest, kannst das XML-DOM verwenden,
um an die Inhalte schnell und einfach herran zu kommen (mit dem ResourceReader oder dem ResourceManager zB).

Wenn Dir die besagten Features in der Express Edition nicht zur Verfügung stehen,
hast mehr Aufwand als Nutzen. Dafür ist ja eine IDE auch da. Ich würde meinem Arbeitgeber
dahingehend schon mal darauf Stoßen wenn er in einer gewissen Zeit überhaupt etwas verdienen will.
Für Schüler und Studenten schaut das anders aus. Auf der Microsoft-Academy bekommst in dem Fall was Du brauchst.
 
Zuletzt bearbeitet:
Wow - danke für die erschöpfende Antwort :)

zu 1. Ja, das verstehe ich. Wobei ich eben (noch ohne Forms) vergeblich versucht hatte, wenigstens eine Std-Fallback Sprache fest in die .exe zu integrieren. Darum war das eben nicht so einfach, eine sichere und stille Init zu schreiben, bis eben das erste Form erscheint. Dann muss ich wohl einfach noch anders strukturieren, denn ich hatte irgendwie das Gefühl, dass ich da einige Resourcen eben nicht von einem Form abhängig habe - also diese würden dann nur in einer MessageBox oder dergleichen dargestellt (oder in einem .log). Oder kann man eine einzelne MessageBox auch lokalisieren? Muss ich wohl dennoch eine Art Form dafür erzeugen.

zu 2. Ich hatte das bis jetzt mit CultureInfo::CurrentCulture->Name gemacht (ist da Thread.CurrentThread.CurrentCulture also doch vorzuziehen). Und wenn die Sprache nicht in der Applikation enthalten ist, dann wird also die Std Resource verwendet. Wie ich ein Form ansich lokalisiere, habe ich mir schon angesehen (und war kurz davor, das eben so zu verwenden - in Verbindung mit .resx - für die quasi unspezifischen Resourcen).

zu 4. Ah - das ist also das Singleton :) Schon oft gehört, aber ich wusste nicht wirklich, was das bringt :) Also ist es auch quasi "gute Praxis", das zu verwenden. Ich hatte da nur Angst, dass ich einfach ein strukturelles Problem in meinem Aufbau hatte (was natürlich immer noch sein kann ;)).

Und die .resx Dateien hätte ich mir dann mit diesem Resourcer Tool erstellt, wobei das so auch kein Spass werden würde.
Bis jetzt bin ich schon wirklich begeistert von VC++ 2005 und .net (und das, obwohl ich doch nur auf Linux programmieren wollte *g*) und werde mir dann schon eine Version leisten. Die freien Pro Versionen sind ja nun leider schon weg - kam ich zu spät (war in irgend einem Thread hier im Forum zu lesen).

Also nochmal vielen dank für die erschöpfende Auskunft :)

Ach und - so ein Singleton kann auch Methoden enthalten?
 
zu 1. Ja, das verstehe ich. Wobei ich eben (noch ohne Forms) vergeblich versucht hatte, wenigstens eine Std-Fallback Sprache fest in die .exe zu integrieren. Darum war das eben nicht so einfach, eine sichere und stille Init zu schreiben, bis eben das erste Form erscheint. Dann muss ich wohl einfach noch anders strukturieren, denn ich hatte irgendwie das Gefühl, dass ich da einige Resourcen eben nicht von einem Form abhängig habe - also diese würden dann nur in einer MessageBox oder dergleichen dargestellt (oder in einem .log). Oder kann man eine einzelne MessageBox auch lokalisieren? Muss ich wohl dennoch eine Art Form dafür erzeugen.
Nein das musst nicht. Das machst wenn Du eine MessageBox mit speziellen Funktionen benötigst.
Wie gesagt, im VS kannst einfach neue Resourcen anlegen. Darauf hin wird eine Klasse erzeugt,
die die Resourcen via Propertys zur Verfügung stellt.
Der dafür Benötigte ResourceManager wird viá Singleton verfügbar gemacht.
In diesen Resourcen legst die Dialogtexte ab und gibst sie mit einer Messagebox aus.

zu 2. Ich hatte das bis jetzt mit CultureInfo::CurrentCulture->Name gemacht (ist da Thread.CurrentThread.CurrentCulture also doch vorzuziehen). Und wenn die Sprache nicht in der Applikation enthalten ist, dann wird also die Std Resource verwendet. Wie ich ein Form ansich lokalisiere, habe ich mir schon angesehen (und war kurz davor, das eben so zu verwenden - in Verbindung mit .resx - für die quasi unspezifischen Resourcen).
Ich meinte das Du die Sprache direkt einstellen musst,
damit die entspechenden Resourcen vom ResourceManager in der InitializeComponent Methode in den Forms geladen werden.
Ich hab aber etwas vergessen gehabt. Der ResouceManager richtet sich nach der Thread.CurrentThread.CurrentUICulture.
Die Thread.CurrentThread.CurrentCulture beeinflusst die Formatierung von Strings hinsichtlich die aktuellen Region.
Sprich Datumsangaben und Zahlen etc..
Ein preudo Beispiel wie Du die Sprache einstellen könntest:
C#:
public static void SetCurrentCulture( string Culture ) {
	Thread.CurrentThread.CurrentUICulture = new CultureInfo( Culture );
	switch( Culture ){
		case "de":
			Thread.CurrentThread.CurrentCulture = new CultureInfo( Culture + "-" + Culture.ToUpper() );
			break;
		case "en":
			Thread.CurrentThread.CurrentCulture = new CultureInfo( Culture + "-GB" );
			break;
	}
}

Ach und - so ein Singleton kann auch Methoden enthalten?
Das hast jetzt vielleicht falsch verstanden. Singleton ist ein Zugriffs-Pattern für Objekte,
die nicht instanziert werden sollen, weil sie eben nur einmal und überall im Programm gebraucht werden. :D
Du bekommst das gesamte Objekt, ergo auch alle öffentlichen Propertys und Methoden.

Also nochmal vielen dank für die erschöpfende Auskunft :)
Wollte schon vor 3 tagen posten, nur habe ich derweil etwas getestet und einen BlueScreen erzeugt. :D
Der ganze Text war weg. :( Naja, zum Glück warte ich heute auf Zuarbeit und hab wieder mehr Zeit.

Btw. willst wissen die man die Beschriftungen eines Forms zur Laufzeit ändern kann
ohne das Form neu instanzieren zu müssen?
 
Nein das musst nicht. Das machst wenn Du eine MessageBox mit speziellen Funktionen benötigst.
Wie gesagt, im VS kannst einfach neue Resourcen anlegen. Darauf hin wird eine Klasse erzeugt. die die Resourcen via Propertys zur Verfügung stellt.
Ah jetzt verstehe ich :) Das ist ja genial :) Da habe ich mir ja sinnlosest Mehrarbeit gemacht. Und die Klasse hat dann den Namen der Resource? Na die werde ich im Object-Browser dann schon finden.

Der dafür Benötigte ResourceManager wird viá Singleton verfügbar gemacht.
In diesen Resourcen legst die Dialogtexte ab und gibst sie mit einer Messagebox aus.
Ja genau - so hatte ich mir das erhofft - wunderbar :)

Ich meinte das Du die Sprache direkt einstellen musst,
damit die entspechenden Resourcen vom ResourceManager in der InitializeComponent Methode in den Forms geladen werden.
Ah - mein Fehler. Ich hatte da die falsche Methode gepostet. Wobei ich dann eben, nach allen Checks, auch mittels CultureInfo die Sprache gesetzt hatte - äh... warum auch immer *g*

Ich hab aber etwas vergessen gehabt. Der ResouceManager richtet sich nach der Thread.CurrentThread.CurrentUICulture.
Die Thread.CurrentThread.CurrentCulture beeinflusst die Formatierung von Strings hinsichtlich die aktuellen Region.
Sprich Datumsangaben und Zahlen etc..
Ich dachte mir einfach, gehst auf Nummer sicher und nimmst einfach gleich CultureInfo - habe eben nicht verstanden, warum es da eine - für mich - zweite Variante in Thread gibt.

Ein preudo Beispiel wie Du die Sprache einstellen könntest:
Super - dann verwende ich diese Klasse. Ansonsten hatte ich die Sprachen in einem Jagged-Array und diese dann via Schleife überprüft/geswitched. Mir fehlt da aber immer noch der Königsweg, mit dem man die ganzen Sprachdaten (also welche Sprachen denn nun enthalten sind... de, de_DE, de_AT, etc.) aus dem Code herauszuziehen. Also wenn eine neue Sprache dazukommt, eben kein Statement mehr erweitern zu müssen... im Idealfall noch nicht einmal mehr das Array zu erweitern.

Das hast jetzt vielleicht falsch verstanden. Singleton ist ein Zugriffs-Pattern für Objekte,
die nicht instanziert werden sollen, weil sie eben nur einmal und überall im Programm gebraucht werden. :D
Du bekommst das gesamte Objekt, ergo auch alle öffentlichen Propertys und Methoden.
Darum auch das instance im Singleton also. Ja - das muss sich noch setzen :) Aber schön, dass es das so gibt (hätte ich das nur früher gewusst *g*).

Wollte schon vor 3 tagen posten, nur habe ich derweil etwas getestet und einen BlueScreen erzeugt. :D
Der ganze Text war weg. :( Naja, zum Glück warte ich heute auf Zuarbeit und hab wieder mehr Zeit.
Autsch *g* Mir genügt zur Zeit schon der nette Hinweis, dass VS irgendwie neustarten will... wenn ich dann gerade ein gutes Stück weiter gekommen bin *g*

Btw. willst wissen die man die Beschriftungen eines Forms zur Laufzeit ändern kann
ohne das Form neu instanzieren zu müssen?
Ich denke, das geht dann mit Update()?

Ich hätte ja nicht gedacht, das VS so diskret ist. Das mit den Resourcen hätte ich wohl nie gefunden - und leider konnte ich das auch nirgendwo lesen. Also ich dachte mir schon, keiner will antworten - aber nun ist die Freude umso grösser :) Ich danke Dir :)
 
Also ich hab's nun versucht, einfach mal eine Resource zu erstellen, aber das scheint so nicht zu gehen (zumindest in der Express Version nicht). Element hinzufügen listet sowieso keinen Typ Resource auf. Wenn ich nun eine .resx Datei als existierende Datei hinzufüge, dann passiert leider nichts - keine zusätzliche Klasse.

Wie sähe denn so eine Klasse aus, die VS erstellt? Könntest Du solch eine von VS erstellte Klasse hier posten? Dann könnte ich die wenigstens händisch verwenden :D
 
Ich dachte mir einfach, gehst auf Nummer sicher und nimmst einfach gleich CultureInfo - habe eben nicht verstanden, warum es da eine - für mich - zweite Variante in Thread gibt.
Thread.CurrentThread wohl gemerkt. Also Der Thread in dem Du dich gerade befindest.


Super - dann verwende ich diese Klasse. Ansonsten hatte ich die Sprachen in einem Jagged-Array und diese dann via Schleife überprüft/geswitched. Mir fehlt da aber immer noch der Königsweg, mit dem man die ganzen Sprachdaten (also welche Sprachen denn nun enthalten sind... de, de_DE, de_AT, etc.) aus dem Code herauszuziehen. Also wenn eine neue Sprache dazukommt, eben kein Statement mehr erweitern zu müssen... im Idealfall noch nicht einmal mehr das Array zu erweitern.
Bin mir grad nicht sicher ob das VS 2005 auch das Localizing für einfache Resourcen anbietet.

Ich mein jetzt nicht die Resourcen von Forms oder UserControls. Wenn nicht,
schau ich mir heut abend mal eine Lösung dafür an. Hab auf Arbeit nur das VS2003EA zur Verfügung.

Ich denke, das geht dann mit Update()?
Nein, so einfach ist das nicht. ;)
Mal ein Beispiel wie Du das rudimentär via Form.Controls und eine simplen Rekursion lösen kannst:
C#:
public static void SetTextPropertysForAllControls( Control control, ResourceManager resources )
{
	control.Text = resources.GetString( control.Name + ".Text" );
	
	foreach( Control _control in control.Controls )
		SetTextPropertysForAllControls( _control, resources );
}
Eine andere und elegante Möglichkeit währe, das Ganze viá Reflection abzufakeln.
Definier eine Liste der Typen (Type) die "resourced" werden sollen. Zusätzlich dazu deren Propertys (PropertyInfo) sowie deren Gültigkeitsbereich (BindingFlags.Public | BindingFlags.Instance).
Wander das Form ab und wenn ein Property was den kriterien entspricht gefunden wurde,
holst Dir die Resourcen aus dem aktuellen Form.

Das wird aber ein sehr aufwendiger Vorgang. Ich empfehle Dir das erst anzugehen,
wenn Du mit den Basics vertraut bist. Du wirst während der Implementaion auf Probleme stoßen,
die Du nur mit Erfahrung lösen kannst. Mein LanguageResourceManager beherrscht alle .NET Controls.
Auch UserControls und vor allem Menus. Weiterhin kann er alles andere Resourcen was in seiner Config steht. ;-)
Er ist für .NET 1.1 konzipiert. Ob es bereits sowas schon in .NET 2.0 gibt kann ich nicht sagen,
weil ich auch zZ meine ersten Schritte mit der neuen 2005'er IDE mache.

Das mit den Resourcen hätte ich wohl nie gefunden - und leider konnte ich das auch nirgendwo lesen. Also ich dachte mir schon, keiner will antworten - aber nun ist die Freude umso grösser :) Ich danke Dir :)
Jemand der sich so rein hängt einen leserlichen Beitrag zu verfassen hat es nur verdient.
Schau Dir bitte alle WebCasts und CodeClips auf der MSDN an, damit Du schneller vorran kommst. :)
 
Zuletzt bearbeitet:
Wie sähe denn so eine Klasse aus, die VS erstellt? Könntest Du solch eine von VS erstellte Klasse hier posten? Dann könnte ich die wenigstens händisch verwenden :D
Hier hast eine Derartige Klasse ind .NET 1.1 und in C#.
Das sollte aber denke ich mal kein Problem darstellen.
Bau es in Cpp nach und füge danach leglich die Sourcedatei zu deinem Projekt hinzu.
Die Resoucedateien LangResources.de.resx und LangResources.de.resx sollten vom VS automatisch als Resourcen der Klasse erkannt werden.
Somit erzeugt es automatisch *.resouce Dateien wenn Du deine Solution Kompilierst.
Kannst beliebig viele Sprachen hinzufügen.
 

Anhänge

  • 26898attachment.zip
    3,5 KB · Aufrufe: 121
Zurück