Autor Beitrag
winx
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starontopic star
Beiträge: 249



BeitragVerfasst: Do 01.12.05 13:44 
Hallo,

wenn ich mit VS 2005 und C# eine Multilanguage App schreiben will, wie muß ich dann die Texte von Exceptions, Nachrichten, captions etc am besten verwalten um diese möglichst einfach (am besten in einer datei(xml,ini)) übersetzen zu können?

Gibts da irgendwo Beispiele?

danke,
winx
Christian S.
ontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic starofftopic star
Beiträge: 20451
Erhaltene Danke: 2264

Win 10
C# (VS 2019)
BeitragVerfasst: Sa 03.12.05 13:21 
Unter .NET 1.1 habe ichd as über die ApplicationSettings gemacht. Die gibt es in .NET 2.0 auch noch (man kann im Abschnitt "ApplicatonSettings" der Properties einer Komponente ihre Eigenschaften in den Settings ablegen), aber irgendwie scheitere ich unter .NET 2.0 am Auslesen.

Eigentlich sollte es (laut Doku) so gehen:
ausblenden C#-Quelltext
1:
ConfigurationManager.AppSettings["foobar"];					


Aber leider ist die AppSettings-Collection bei mir leer. Das automatische Auslesen durch das Programm funktioniert aber, die Eigenschaften werden korrekt aus der app.config übernommen. :nixweiss:

_________________
Zwei Worte werden Dir im Leben viele Türen öffnen - "ziehen" und "drücken".
Macoy
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starontopic star
Beiträge: 31



BeitragVerfasst: So 04.12.05 13:22 
Titel: Meine Lösung
Hallo,

Ich benutze eine einface CSV Datei, in dir ich alle Sprachen
eintrage.

ausblenden Quelltext
1:
2:
3:
4:
5:
6:
KEY;ENGLISH;GERMAN;
File;File;Datei;
FileOpen;Open;Öffnen;
.
.
.


Beim Start des Programms lese ich die erste Zeile aus und merke mir die Spaltenzahl der Sprache (ENGLISH =1, GERMAN = 2) - abhängig von der Sprache, die per Kommandozeilenparameter an das Programm übergeben wurde.

Dann die Datei zeilenweise auslesen und splitten. Die Werte der ersten Spalte nehme ich als Keys, die Werte in der oben festgestellten Spalte als Values und speichere die in einer Statischen Hashtable ab, auf die ich von überall her zugreifen kann.

Man kann es auch über Recourcen-Dateien machen, aber das war mir zu kompliziert und ausserdem unterstützt SharpDevelop nicht das erstellen von Satelite-Assemblies. Zudem ist eine CSV Datei einfacher zu editieren als ne Resourcendatei. Zb. kann der Enduser selbst eine neue Übersetzung hinzufügen.

Der Nachteil ist, das man die Interface sprache zur Laufzeit nicht ändern kann, aber das sollte kein grosser Nachteil sein.


Beispiel:
ausblenden volle Höhe C#-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
20:
21:
22:
23:
24:
25:
26:
27:
28:
29:
30:
31:
32:
33:
34:
35:
36:
37:
38:
39:
40:
41:
42:
43:
44:
45:
static Hashtable lang;
     
[STAThread]
public static void Main(string[] argv)
{
  LoadLangFile("lang.csv","GERMAN");
  Application.Run(new SDWindow());
}
     
public static void LoadLangFile(string file_name, string language)
{
  String line;
  StreamReader f = new StreamReader(file_name);
  bool first_line = true;
  int lang_index = -1;
       
  lang = new Hashtable();
       
  while( (line = f.ReadLine()) != null )
  {
    String[] tokens = line.Split(';');
     
    //suchen nach Spaltenindex der Sprache    
    if(first_line)
    {
   first_line = false;
   forint i = 0; i < tokens.Length ; i++)
   {
     if(tokens[i] == language)
     {
       lang_index = i;
       break;
     }
      }
    }
  //ab zeile 2 hinzufuegen der werte zur table
  lang.Add(tokens[0],tokens[lang_index]);
  }      
  f.Close();
  }
   
  public static string GetValue(string key)
  {
    return (string)lang[key];
  }
winx Threadstarter
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starontopic star
Beiträge: 249



BeitragVerfasst: Mo 05.12.05 09:54 
Hallo,

danke für eure Vorschläge. Also ich habe das vorübergehend so gelöst:

ausblenden volle Höhe C#-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
20:
21:
22:
23:
24:
25:
26:
27:
28:
29:
30:
31:
32:
33:
34:
35:
36:
37:
38:
39:
40:
41:
42:
43:
44:
45:
46:
47:
48:
49:
50:
51:
52:
53:
54:
55:
56:
57:
58:
59:
60:
61:
62:
63:
64:
65:
66:
67:
68:
69:
70:
71:
72:
73:
74:
75:
76:
77:
78:
79:
80:
81:
82:
83:
84:
85:
86:
87:
88:
89:
90:
using System;
using System.Collections.Generic;
using System.Text;
using System.Resources;
using System.Globalization;


namespace VSNWLMark.NET.Language
{
    public sealed class I18nManager
    {

        #region Statische Variablen
        
        private static I18nManager instance=null;
        static readonly object padlock = new object();

        
        #region Properties

        private CultureInfo m_culture;

        public CultureInfo Culture
        {
            get { return m_culture; }
            set { m_culture = value; }
        }

        private ResourceManager m_rm;

        public ResourceManager RM
        {
            get { return m_rm; }
            set { m_rm = value; }
        }
    
        
        #endregion
        
        #region Constructor

        public static I18nManager Instance
        {
            get 
            {
                lock (padlock)
                {
                    if (instance == null) { instance = new I18nManager(); }
                }
                return instance;
            }
            
        }

        private I18nManager()
        {
            m_culture = CultureInfo.CurrentCulture;
            
        }
        #endregion


        #region public functions

        public void changeLanguage(string newLanguage)
        {
            try
            {
                m_culture = CultureInfo.CreateSpecificCulture(newLanguage);
            }
            catch 
            {
                m_culture = CultureInfo.CurrentCulture;                 
            }

        }


        public string getText(string name,System.Reflection.Assembly ass)
        {
            
            m_rm = new ResourceManager("Language.Markres", ass);
            return m_rm.GetString(name, m_culture);
        }

        #endregion


    }
}


Die Sprachen hab ich in in zwei resx-Files gespeichert.

Nur hab ich jetzt noch folgendes Problem:

Wie ordne ich Formularen, Buttons, etc die Texte zu? Würdet ihr einfach in jeder Klasse eine Funktion adjustLanguage()
schreiben, die die Sprache zuordnet oder das ganze über eigene Resx-Dateien lösen?

Danke,
winx
Robert_G
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starofftopic star
Beiträge: 416


Delphi32 (D2005 PE); Chrome/C# (VS2003 E/A, VS2005)
BeitragVerfasst: Di 06.12.05 01:31 
Wie so vieles andere ist das mit 2.0 ziemlich easy. ;)
Du wählst dein Form im designer aus und setzt Localizable auf true.
Alles was du nun änderst, gilt für die eingestellte Sprache. Willst du also einen deutschen Button text haben, setzte language auf German.
Wenn du jetzt den Text des Button änderst, wird das VS eine neue resx anlegen (DeinForm.de.resx) und darin den geänderten Buttontext werfen.
Willst du für die Schwetzer einen besonderen haben: Einfach "German(Switzerland)" auswählen und spezielle Scheizer-dingsens anpassen.

Gleiches gilt für nomrale resources, die nicht mit einer designable class verknüpft sind.
Einfach eine Resource names DeineResource.de-CH anlegen und dort Bilder, trings,... mit gleichem Namen wie in der übergeordneten Resource anlegen. (.resx->.de.resx->.de-CH.resx)
Im Code kannst du ganz normal auf die autogenerierte Resource class zugreifen, das Framework sucht sich dann den passenden Wert aus.
winx Threadstarter
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starontopic star
Beiträge: 249



BeitragVerfasst: Di 06.12.05 10:01 
Danke Robert,

das mit den Design-Objekten (Formular) hab ich (fast) vestanden. Nur wie weise ich nun einem Formualr die Sprache dynamisch zur Laufzeit zu?

Nur könntest du mir für "nomrale resources, die nicht mit einer designable class verknüpft sind"ein Beispiel posten?
Wie greif ich dann besipielsweise bei der Ausgabe einer MessageBox zu?

Und wie muß ich die Hierarchie der resx Files genau aufbauen, damit das Framework das richtige File findet?

danke,
winx
Robert_G
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starofftopic star
Beiträge: 416


Delphi32 (D2005 PE); Chrome/C# (VS2003 E/A, VS2005)
BeitragVerfasst: Di 06.12.05 12:44 
Zitat:
das mit den Design-Objekten (Formular) hab ich (fast) vestanden. Nur wie weise ich nun einem Formualr die Sprache dynamisch zur Laufzeit zu?
Da hast du das System misverstanden.
Die App startet soll ja mit den lokalisierten einstellungen _starten_. Nachträgliches Ändern erfordert ein bisschen Gefriemel:
ausblenden C#-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
20:
21:
22:
23:
24:
25:
26:
27:
28:
void cultureInfoBindingSource_PositionChanged(object sender, EventArgs e)
{
  Thread.CurrentThread.CurrentUICulture = cultureInfoBindingSource.Current as CultureInfo;

  ApplyResources(this);
}

static void ApplyResources(Control owner)
{
  ComponentResourceManager resourceManager = new ComponentResourceManager(owner.GetType());

  resourceManager.ApplyResources(owner, "$this");

  foreach (Control control in owner.Controls)
  {
    ApplyTo(resourceManager, control);
  }
}

static void ApplyTo(ComponentResourceManager resourceManager, Control control)
{
  resourceManager.ApplyResources(control, control.Name);

  foreach (Control subControl in control.Controls)
  {
    ApplyTo(resourceManager, subControl);
  }
}


Zitat:
Nur könntest du mir für "nomrale resources, die nicht mit einer designable class verknüpft sind"ein Beispiel posten?

Schaue mal in die Properties deines Projektes. Dort findest du bereits eine Resources.resx.
Öffne sie mal und trage einen String namens Hello mit dem Wert Hello ein.
Jetzt kommen wir zu einer absolut widerlichen Macken im VS: :autsch:
Um deine Default resx vernünftig benutzen zu können, musst du sie aus denm Properties Ordner auf deinen Projektnamen ziehen. (Der Solution Explorer verhält sich einfach nur dämlich innerhalb der Properties)
Ctrl gedrückt halten und die Resource nochmal auf's Projekt ziehen. (Wie im richtigen Explorer kopiert das die Datei.
Bei dieser Kopie änderst du den Namen auf Resources.de.resx und darin den Wert für Hello auf Hallo.
Außerdem kannst im PropertyGrid das CustomTool entfernen. (Die Resource-Klasse wird bereits für die .resx angelegt und ist somit für die .de.resx witzlos. ;) )
Zitat:
Wie greif ich dann besipielsweise bei der Ausgabe einer MessageBox zu?
Unglaublich easy:
ausblenden C#-Quelltext
1:
MessageBox.Show(Resources.Hello);					

Jeder Eintrag in der resx wird als statische Property abgebildet.
Zitat:
Und wie muß ich die Hierarchie der resx Files genau aufbauen, damit das Framework das richtige File findet?
Schaue dir einfach mal die Ordnerstruktur in deinem Debug/Release ordner an. Durch diese Spielereien hat das nämlich das VS schon für dich gemacht. ;)