JavaFX Mehrsprachigkeit

Java20134

Bekanntes Mitglied
Guten Tag,

Ich habe mir als Aufgabe gestellt, dass meine Programme die Möglichkeit haben in mehreren Sprachen zu erscheinen. Deshalb habe ich mir einmal die Klasse ResourceBundle angeschaut. Dadurch weiß ich nun, wie ich eine Properties Datei einlesen, aber mir ist noch keine geniale Idee eingefallen, wie ich diese Daten dann in meinem ganzem Projekt verteilen könnte - Klasse mit static Methode oder Singelton Design Pattern. Was würdet ihr machen oder habt ihr eine bessere Idee?

Viele Dank!
 

Sugan

Mitglied
Hallo,

ich kenne mich zwar nicht mit Properties Dateien aus, aber du fragtest ja, wie wir das machen würden, also: ich würde an jeder Stelle in deinem Programm, die von der Mehrsprachigkeit betroffen ist (z.B. Label, Buttennamen oder Titel...) eine if-Bedingung machen. Du speicherst die Sprache als String (der Benutzer kann diesen dann z.b. ändern) und die if-Bedingung fragt überall ab, welche Sprache ausgewählt ist und deine Parameter werden entsprechend gesetzt.

Ca. so:

Java:
public class test {
   
    static JButton spracheWaehlen;
   
    public static void main(String [] args)
    {
        String sprache;
        sprache = "deutsch";
       
        if (sprache.equals("deutsch")){
           spracheWaehlen = new JButton("Sprache wählen");
        }
        else if (sprache.equals("english")){
            spracheWaehlen = new JButton("Choose language");
        }
       
    }
}

Das einzige, wo du dann noch drüber nachdenken musst, ist, dass all diese Initialisierungen neu aufgerufen werden müssen, wenn die Sprache geändert wird.


LG

Sugan
 

Thallius

Top Contributor
Das ist sicher eine der schlechtesten Methode. Wie soll denn dein Code aussehen wenn du alle Weltsprachen unterstützen willst?

Ich würde eine Klasse Messages machen. Darin die statische Methode getString(String Source).
beim Programmstart wird ein,al die Methode LoadLanguageFile() aufgerufen, welche die richtige Properties Datei lädt in ein Array liest.

Ich weis nicht welche IDE du benutzt aber in Eclipse gibt es z.B einen Menüpunkt "Externalize Strings" welche das alles komplett automatisch anlegt. Da brauchst du gar nichts selber programmieren.

Gruß

Claus
 

dayaftereh

Top Contributor
Glaube besser ist die ResourceBundle hinter einem Interface zu verstecken.
Java:
public interface Language {
   String getText(String key);
   String getText(String key, Locale locale);
}

Natürlich musst du die Language am Anfang einlesen und dann ihrgend wie an alle Klassen verteilen, die sie benötigen. Von Singleton würde ich abraten, da sie schwer zu Testen sind. Dependency injection wäre meine Lösung.Ich finde für solche Probleme die Frameworks Guice oder Spring gut, da sie eine elegante Lösung für Dependency injection haben.

In den Frameworks kannst du einfach Singleton definieren. Schau sie dir mal an.
 

Meniskusschaden

Top Contributor
Wenn man ResourceBundle nutzt, muß man sich im Code doch eigentlich überhaupt nicht mehr um die Sprache kümmern. Man pflegt pro Sprache eine Property-Datei mit dem passenden Namen, liest die Strings über getString() ein und ResourceBundle sucht sich automatisch die Property-Datei, die zur konfigurierten Sprache passt bzw. nutzt die Default-Datei, falls die passende Property-Datei oder einzelne Strings fehlen.
 

Java20134

Bekanntes Mitglied
Die Idee wie Thallius hatte ich auch und die Umsetzung habe ich bis jetzt wie Meniskusschade und Flown noch einmal sehr deutlich gezeigt haben. Und das Tutorial kann ich nur empfehlen, da es sehr leicht verständlich ist, auch wenn man kein Englisch sehr gut versteht!

Da wäre ich wohl die Variante mit der static Methode übernehmen. Danke für die hilfreiche Antwort Thallius!
 

dzim

Top Contributor
Ich hab mir dazu mal eine Hilfsklasse erstellt. In all seiner Singleton'schen Hässlichkeit...

Java:
package application;

import java.util.Formatter;
import java.util.Locale;
import java.util.MissingResourceException;
import java.util.ResourceBundle;

/**
* Helper class to load string resources based on the current active locale.
*
* @author dzimmermann
*/
public class StringResource {
   
    /**
     * The path, where the property file - containing translated strings - can be found. Be aware, that in the moment that means: inside the
     * {@link ClassLoader}s hierarchy. Be also aware, that the {@link ResourceBundle} mechanism doesn't need neither the underscore-language part nor
     * the dot-properties file ending to be attached.
     */
    private static final String BUNDLE_NAME = String.format("%s.%s", StringResource.class.getPackage().getName(), "strings"); //$NON-NLS-1$
   
    /**
     * A static reference to the current active language bundle. Per default the bundle for the English locale will be loaded.
     */
    private static ResourceBundle RESOURCE_BUNDLE = ResourceBundle.getBundle(BUNDLE_NAME, Locale.ENGLISH);
   
    /**
     * prevent instances
     */
    private StringResource() {}
   
    /**
     * Helper method to switch the language by the language string, for example &qout;en&qout;, &qout;de&qout;, ...
     *
     * @param language
     *          the language to set
     * @return <code>true</code>, if the language was changed (means: there is a bundle for the specified language and the current language is not the
     *         new one)
     */
    public static synchronized boolean setLanguage(String language) {
        synchronized(RESOURCE_BUNDLE) {
            Locale current = Locale.getDefault();
            Locale next = new Locale(language.toLowerCase());
            if (current == null || !current.equals(next)) {
                RESOURCE_BUNDLE = ResourceBundle.getBundle(BUNDLE_NAME, next);
                return true;
            }
            return false;
        }
    }
   
    /**
     * getter for the current {@link ResourceBundle}, if we ever need it.
     *
     * @return the current {@link ResourceBundle} object
     */
    public static synchronized ResourceBundle getResourceBundle() {
        synchronized(RESOURCE_BUNDLE) {
            return RESOURCE_BUNDLE;
        }
    }
   
    /**
     * Use this method, if you need only the plain string for a specific key.
     *
     * @param key
     *          the key for the string resource
     * @return the string resource
     */
    public static synchronized String getString(String key) {
        synchronized(RESOURCE_BUNDLE) {
            try {
                return RESOURCE_BUNDLE.getString(key);
            } catch (MissingResourceException e) {
                return '!' + key + '!';
            }
        }
    }
   
    /**
     * Use this method, if the key you want to fetch contains formatting options (see {@link Formatter}).
     *
     * @param key
     *          the key for the string resource
     * @param args
     *          the formatting arguments for the string resource
     * @return the formatted string resource
     */
    public static synchronized String getString(String key, Object... args) {
        synchronized(RESOURCE_BUNDLE) {
            String value = "!empty!";
            try {
                value = RESOURCE_BUNDLE.getString(key);
            } catch (MissingResourceException e) {
                value = '!' + key + '!';
            }
            if (value.startsWith("!") && value.endsWith("!"))
                return value;
            return String.format(value, args);
        }
    }
}

Wenn ich es mal wieder brauche, werde ich es in ein Interface umwandeln (siehe dem Post von @dayaftereh ) und dann per Dependency Injection reinbringen (Guice oder Spring Boot sind da meine Favoriten).

Hier in dem Singleton'schen Beispiel wird vorausgesetzt, dass im selben Package wie diese Klasse ("application") Property-Dateien mit dem Namen "strings[language-descriptor].properties", wobei language-descriptor dem Format folgt, wie es etwa hier im RFC 3066 beschrieben wird.

deutsch=de
Deutschland=de_DE
Schweiz=de_CH

english=en
UK=en_UK
US=en_US
[...]

Damit kannst du auf sprachliche Besonderheiten einer Sprache in verschiedenen Ländern eingehen. Die Schweiz z.B. hat kein "ß". In UK heist es "colour", in US "color", u.s.w.u.s.f.
 

AndyJ

Bekanntes Mitglied
Ich persoenlich benutze immer enums. Das folgende Beispiel zeigt wie. Man kann das Beispiel auch erweitern und z.B. die Files neu laden, wenn sich die Sprache aendert. Enums sind per Definition immer Singletons:
Code:
package info.junius.test;

import java.util.Locale;
import java.util.MissingResourceException;
import java.util.ResourceBundle;

public enum Txt {
  TITLE;

  // points to your properties file
  private static final String MESSAGEBUNDLE = "info/junius/test/myBundle";

  /** resource bundle */
  private static ResourceBundle MESSAGES = ResourceBundle.getBundle(MESSAGEBUNDLE, Locale.getDefault());

  /**
  * Returns message associated with this enum.
  *
  * @return message or its key if there is no entry in the properties file
  */
  public String get () {
  String result = "";
  try {
  result = MESSAGES.getString(this.toString().toLowerCase());
  } catch (NullPointerException | MissingResourceException | ClassCastException e) {
  // no way to recover from a missing resource, so we display just the key.
  result = this.toString();
  }
  return result;
  }
}

Benutzen ist dann denkbar einfach:
Code:
package info.junius.test;

public class UseProperties {

  public static void main(String[] args) {
  System.out.println(Txt.TITLE.get());
  }
}
Cheers,
Andy
 

Ähnliche Java Themen


Oben