Jump to content
Unity Insider Forum

Mehrsprachiges Menü erstellen


w0rks

Recommended Posts

Ich habe mein Menü Fertig & komplett auf Deutsch B) Jetzt will ich einen Button erstellen: Wenn man drauf drückt, verändert sich der Text in Englisch

(Er wird Kwasi ersetzt in der gleichen Scene). Aber wie mache ich das ? ! ? :wacko: Wüste jemand Rat

MFG       w0rks

Link zu diesem Kommentar
Auf anderen Seiten teilen

Da gibt's sehr einfache Lösungen - und gute.

Ein meiner Meinung nach vernünftiges Zwischending ist eine ScriptableObject-Klasse, die für jede vorgesehene Sprache ein Feld hat:

[CreateAssetMenu(menuName = "Language/Key")]
public class LanguageKey : ScriptableObject
{
  public string en;
  public string de;
  public string fr;
}

Davon erstellst du dann für jeden String, der in den Sprachen verfügbar sein soll, einen und schreibst halt rein, was da rein soll.

Dann kannstu so ein Ding einfach referenzieren und benutzen:

public LanguageKey languageKey;

public void SwitchLanguage()
{
  text.text = languageKey.en;
}

 

Link zu diesem Kommentar
Auf anderen Seiten teilen

Wenn du eine Komponente scriptest, dann kannst du diese ja mehrere Male auf beliebige GameObjects packen.

public class Test : MonoBehaviour
{
  public int zahl;
}

Jede Instanz dieser Klasse kann dann seinen Wert für jede der Felder (hier: "Zahl") haben.

image.png

Hier siehst du zwei Instanzen desselben Scripts auf einem GameObject, und jede hat ihren eigenen Wert für die Variable.

Wenn du nun nicht von MonoBehaviour erbst, sondern von "ScriptableObject", dann kannst du das Script nicht mehr auf GameObjects ziehen. Es verliert die Fähigkeit, eine Komponente zu sein. Was du dafür aber machen kannst, ist das CreateAssetMenuAttribute drüber zu packen, so wie ich das oben gemacht habe. Damit fügst du für dein ScriptableObject einen Menüpunkt im Asset/Create-Menü hinzu. Das ist das Menü, das erscheint, wenn du in deinen Assets rechtsklickst.

Durch

[CreateAssetMenu(menuName = "Language/Key")]

entsteht also:

image.png

Wenn du da jetzt draufklickst, entsteht ein neues Asset, eine Datei, die eine Instanz deiner Klasse darstellt. So wie bei Komponenten, nur halt nicht auf einem GameObject als Komponente angebracht, sondern einfach als Datei in den Assets. Sieht dann so aus:

image.png

Wenn du dieses Ding dann markierst, siehst du im Inspektor wieder alle Felder (in diesem Fall: "Zahl"), die die Klasse hat. Und wieder kann jede Instanz deiner Klasse einen eigenen Wert haben, der sich von den Werten der anderen Objekte derselben Sorte unterschiedet.

Wenn du jetzt also die ScriptableObject-Klasse, die ich oben gepostet habe, 1:1 in dein Projekt kopierst (Das obligatorische "using UnityEngine;" darüber aber nicht vergessen), dann kannst du lauter solche Objekte erzeugen. Statt einer Zahl hat aber jedes "LanguageKey"-Objekt, das du erstellst, drei Strings als Felder: "de", "en" und "fr". Das kannst du beliebig ändern oder erweitern.

Wenn du jetzt eines der LanguageKey-Objekte markierst, kannst du einen bestimmen String in allen benötigten Sprachen angeben:

image.png

(In den Screenshots heißt die Klasse MyLanguageKey, weil ich sonst die Klasse doppelt gehabt hätte)

Wenn du jetzt das, was du im zweiten Code-Ausschnitt siehst, in dein System einbaust, kannst du diese drei Strings abfragen, nachdem du das Objekt in das Feld gezogen hast:

image.png

Hier hast du noch eine ganze Klasse, die das ganze demonstriert:

using UnityEngine;

public class LanguageKeyTest : MonoBehaviour
{
    public LanguageKey languageKey;

    private void Start()
    {
        Debug.Log("Englisch: " + languageKey.en);
        Debug.Log("Deutsch: " + languageKey.de);
        Debug.Log("Französisch: " + languageKey.fr);
    }
}

 

Link zu diesem Kommentar
Auf anderen Seiten teilen

Bin zwar nicht der Thread Ersteller, aber Thema interessiert mich auch und den Einsatz finde ich sehr interessant. Das heißt also für Jedes Objekt was übersetzt werden muss, muss man eine Instanz der Klasse einlegen? Ist das nicht dann nciht zu viel Arbeit? Wenn man bedenkt wieviel verschiedene Buttons Namen man hat und jede einzelne Instanz muss man dann ja in anderen Script über public Variable einbinden.

Kann man das ganze irgendwie automatisieren, wenn man ein Knopf drückt, dass gesamte Menü übersetzt wird?

z.b sowas:

void LanguageChange(String language)
{	
	ok_Button.text = language.ok_ButtonName;
	exit_Button.text = language.exit_Buttonname;
	//...
	//usw.
}

So, dass je nachdem welchen Namen Language hat, die richtige Instanz der Übersetzungs Klasse gewählt wird und ohne ganze Prüferei wie:
 

void LanguageChange(String language)
{	
	if(language == "de") {
		ok_Button.text = languageDe.ok_ButtonName;
	}
	else if(language == "en") {
		ok_Button.text = languageEn.ok_ButtonName;
	}
	//...
}

 

Link zu diesem Kommentar
Auf anderen Seiten teilen

Mein Ansatz funktioniert in der Tat nicht sehr gut für größere Projekte. Habe ich auch weiter oben versucht, entsprechend anzusagen.

Deine Variante skaliert da vermutlich echt besser, hat aber, wie ich finde, auch Nachteile. Mein Ansatz ist aus einem DI-basierten Ideenumfeld entstanden. Wenn du irgendwo etwas anderes als zuvor stehen haben willst, ziehst du einfach ein anderes Objekt ins Feld. Bei dir muss man bei einer solchen Änderung gleich den Code aufmachen, abändern und neu kompilieren.

Aber nicht falsch verstehen: Ich finde deinen Ansatz nicht schlechter, nur halt mit anderen Vor- und Nachteilen.

Ich glaube, das richtig zu machen erfordert ein ganz schön komplexes System, in dem man zwar wie bei mir Zuweisungen von Schlüsseln ohne Änderungen im Code machen kann, dafür aber, wie du richtig angemerkt hast, nicht Unmengen an Objekten erstellen muss. Ich mache mir da auch seit ein, zwei Wochen Gedanken drüber.

Link zu diesem Kommentar
Auf anderen Seiten teilen

Hallo,

ich habe vor einiger Zeit mal eine Klasse geschrieben, welche Übersetzungen aus XML Dateien nimmt.

Ich pack das einfach mal hier rein, vielleicht könnt ihr was damit anfangen.

 

Die Klasse LanguageManager muss einfach in irgendwo in einem Objekt geladen werden und schon ist sie Einsatzfähig. Ich habe für eine Scene immer ein MAIN_GAME_STUFF Objekt, welches solche Sachen startet oder verwaltet.

using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Xml;
using Assets.Scripts.Debugging;
using UnityEngine;

/// <summary>
/// Übersetzungen können mit LanguageManager.Translation("TEXT_KEY"); angegeben werden.
/// </summary>
public class LanguageManager : MonoBehaviour
{
    private static Dictionary<string, string> _translations;
    private static readonly List<FileInfo> FileInfos = new List<FileInfo>();

    private void Awake()
    {
        InitializeLanguageFiles();
        SetLanguage(GameSettings.DefaultLanguage);
    }

    /// <summary>
    /// Ersetzt Placeholder in einen Text der ausgewählten Sprache.
    /// </summary>
    /// <param name="key">Placeholder</param>
    /// <returns></returns>
    public static string Translation(string key)
    {
        try
        {
            string value;
            if(_translations.TryGetValue(key, out value))
                return value;
            Debug.Log("No translation text found");
        }
        catch(Exception ex)
        {
            Debug.Log("No translation text found");
        }

        return key;
    }

    public static List<string> GetLanguageList()
    {
        var langList = new List<string>();
        foreach(var fi in FileInfos) langList.Add(fi.FileName);
        return langList;
    }

    public static bool SetLanguage(string lang)
    {
        var fileInfo = (from fi in FileInfos where fi.FileName == lang select new FileInfo(fi.FileName, fi.FilePath))
            .FirstOrDefault();

        if(fileInfo == null)
            return false;

        ReadLanguageFile(fileInfo.FilePath);
        return true;
    }

    private static void ReadLanguageFile(string filePath)
    {
        _translations = null;
        _translations = new Dictionary<string, string>();
        var xmlReader = XmlReader.Create(filePath);
        while(xmlReader.Read())
            if(xmlReader.Name == "translation")
                if(xmlReader.HasAttributes)
                    while(xmlReader.MoveToNextAttribute())
                        if(xmlReader.Name == "name")
                            _translations.Add(xmlReader.Value, xmlReader.ReadString());
    }

    public static void InitializeLanguageFiles()
    {
        if(IsLanguageDirAvailable())
        {
            var files = Directory.GetFiles(Application.streamingAssetsPath + @"\Language", "*.xml");
            foreach(var file in files)
            {
                var fi = new FileInfo(Path.GetFileNameWithoutExtension(file), file);
                FileInfos.Add(fi);
            }
        }
    }

    #region Helper methods

    private static bool IsLanguageDirAvailable()
    {
        var directories = Directory.GetDirectories(Application.streamingAssetsPath);
        if(directories.Length > 0)
            foreach(var dir in directories)
                if(dir == Application.streamingAssetsPath + @"\Language")
                    return true;
        return false;
    }

    #endregion

    public class FileInfo
    {
        public FileInfo() { }

        public FileInfo(string fileName, string filePath)
        {
            FileName = fileName;
            FilePath = filePath;
        }

        public string FileName { get; set; }
        public string FilePath { get; set; }
    }
}

 

Die XML Dateien sind in dem folgenden Verzeichnis: StreamingAssets/Language/deutsch.xml

So sieht der Aufbau einer XML Datei aus:

<?xml version="1.0" encoding="utf-8" standalone="yes"?>

<translations>
  <translation name="TEST_AUSGABE">Test-Ausgabe</translation>
  <translation name="TEST_AUSGABE2">Test-Ausgabe 2</translation>
  
  <translation name="START_GAME">Spiel starten</translation>
  <translation name="LOAD_GAME">Spiel laden</translation>
  <translation name="NEW_GAME">Neues Spiel</translation>
  <translation name="OPTIONS">Optionen</translation>
  <translation name="CLOSE_GAME">Spiel beenden</translation>
  <translation name="BACK">Zurück</translation>

  <translation name="ITEM_TITLE_1">Schwert</translation>
  <translation name="ITEM_DESCRIPTION_1">Sehr schwach und klein.</translation>
  <translation name="ITEM_TITLE_2">Schwert2</translation>
  <translation name="ITEM_DESCRIPTION_2">Auch sehr schwach und klein.</translation>
  <translation name="ITEM_TITLE_3">Schwert3</translation>
  <translation name="ITEM_DESCRIPTION_3">Auch sehr schwach und klein.</translation>

  <translation name="RARITY_NAME_0">Common</translation>
  <translation name="RARITY_NAME_1">Uncommon</translation>
  <translation name="RARITY_NAME_2">Rare</translation>
  <translation name="RARITY_NAME_3">Epic</translation>
  <translation name="RARITY_NAME_4">Legendary</translation>
  <translation name="RARITY_NAME_5">Mystic</translation>
  
</translations>

 

Damit kannst du dann den Text ausgeben:

LanguageManager.Translation(_title)

 

Sollte ich was vergessen haben, gern Bescheid geben.

 

Gruß,

Aaron

Link zu diesem Kommentar
Auf anderen Seiten teilen

Archiviert

Dieses Thema ist jetzt archiviert und für weitere Antworten gesperrt.

×
×
  • Neu erstellen...