# Resourcebundle - Sprache zur Laufzeit aktualisieren?



## flashray (17. September 2005)

Hallo Freunde,

Habe eine kleine Applikation geschrieben, welche mit Hilfe von Resourcebundle und Locale zweisprachig ist. Das Programm funktioniert soweit.
Meine Frage ist:
Beinhaltet das aktuelle Java SDK eine Klasse, Methode mit welcher man die Sprache einer Applikation zur Laufzeit ändern kann?
Wenn es keine Javainterne Lösung zur Zeit vorhanden ist, gibt es denn vielleicht eine zu empfehlende externe API die dies löst?

Danke!

VG Erdal


----------



## schnuffie (20. September 2005)

Du könntest nach jeder Sprachwahl das verwendete ResourceBundle durch Übergeben des aktuellen Locale-Objekts überschreiben:


ResourceBundle* getBundle*(String baseName, Locale locale)


----------



## flashray (20. September 2005)

Hallo Schnuffie,

das wäre ja zu einfach um wahr zu sein.

Genau so etwas suche ich. Funktioniert aber leider nicht.

Die einzigste Möglichkeit die ich bisher kenne ist, nach der Sprachauswahl diese in eine Konfigurationsdatei schreiben. Programm beenden, neu starten. Jetzt kann die Applikation die Ressourcen der gewählten Sprache erhalten.

Die Lösung müsste irgendwie so was sein wie, wenn die neue Lokale gesetzt ist alle Komponenten neu initialisieren. (theoretisch)

Wie weiß ich nicht?

Trotzdem danke!

VG Erdal


----------



## schnuffie (22. September 2005)

Mit der angegebenen Methode bekommst Du genau das ResourceBundle-Objekt, das der übergebenen Locale entspricht. Somit wird nach Sprachwechsel eben die andere Properties-Datei verwendet und falls noch nicht geschehen - vorher eingelesen.

Du könntest Dir allerdings auch mit ListResourceBundle von Properties-Dateien unabhängige ResourceBundle erzeugen.


----------



## flashray (22. September 2005)

Hallo Schnuffie,

also ich tue die Lokale neu setzen. Allerdings sehe ich keine Veränderungen. Hab mal unten den relevanten Codeteil aus meiner Applikation rausgeschrieben. Kannst du mir sagen, ob da noch irgendetwas fehlt oder etwas falsch ist. Bei Programmstart tue ich die Lokale aus einer Propertiesdatei lesen. Ist das was ich in die Methode change geschrieben habe richtig, ausreichend zum wechseln der Sprache.

VG Erdal


```
public class Test {

   public static String land;
   public static String sprache;
   public static Locale currentLocale, de, tr, en;
   public static ResourceBundle m;

   public void change () {
      m.getBundle("Bundle", en);
   }

   public static void main (String[] args) {
      try {
         Properties prop = new Properties();
         FileInputStream in = new FileInputStream("config.txt");
         prop.load(in);
         in.close();
         land = prop.getProperty("land", "DE");
         sprache = (prop.getProperty("sprache", "de"));
      } catch (IOException e) {
      } catch (NumberFormatException e) {
      }

      currentLocale = new Locale(sprache, land);
      de = new Locale("de", "DE");
      tr = new Locale("tr", "TR");
      en = new Locale("en", "EN");

      m = ResourceBundle.getBundle("Bundle", currentLocale);

   }

}
```


----------



## schnuffie (23. September 2005)

Die derzeitige Sprache erhälst Du mit:


```
Locale currentLocale = Locale.getDefault();
```
 
Ich würde das event. so lösen:


```
private Locale currentLocale = Locale.getDefault();
private Locale en = Locale.ENGLISH;
private Locale de = Locale.GERMAN;
private Locale tr = new Locale("tr"); //der 2. Parameter ist nur für landesspezifische Sprachen (z.B. de und CH für deutschsprachige Schweiz) erforderlich
 
public ResourceBundle getGerman() {
  return ResourceBundle.getBundle("sprache", de);
}
 
public ResourceBundle getEnglish() {
  return ResourceBundle.getBundle("sprache", en);
}
 
public ResourceBundle getTurkish() {
  return ResourceBundle.getBundle("sprache", tr);
}
 
public ResourceBundle getDefault() {
  return ResourceBundle.getBundle("sprache", currentLocale);
}
```
 
Zur Verfügung mußt Du diese 3 Properties-Dateien stellen:
sprache_de.properties
sprache_en.properties
sprache_tr.properties


----------



## flashray (26. September 2005)

Hallo Schnuffie,

habe nach deinen Vorgaben ein kleines Beispiel implementiert. Funktioniert leider nicht.

Fehlt da noch irgendetwas, oder ist da was falsch?

Beste Grüsse
Erdal 


```
import java.awt.GridLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.Locale;
import java.util.ResourceBundle;
import javax.swing.JButton;
import javax.swing.JFrame;

public class Language {
	private ResourceBundle r;
	
	private Locale en = Locale.ENGLISH;
	private Locale de = Locale.GERMAN;
	 
	public ResourceBundle getGerman() {
	  return ResourceBundle.getBundle("sprache", de);
	}
	 
	public ResourceBundle getEnglish() {
	  return ResourceBundle.getBundle("sprache", en);
	}
	 	
	public void example() {
		r = getGerman();
		
		final JFrame f = new JFrame(r.getString("l_titel"));
		JButton b1 = new JButton(r.getString("l_eins"));
		JButton b2 = new JButton(r.getString("l_zwei"));
		
		ActionListener deutsch = new ActionListener() {
			public void actionPerformed(ActionEvent e) {
				r = getGerman();
			}
		};
		b1.addActionListener(deutsch);
		
		ActionListener english = new ActionListener() {
			public void actionPerformed(ActionEvent e) {
				r = getEnglish();
			}
		};
		b2.addActionListener(english);
		
		f.setAlwaysOnTop(true);
		f.setSize(200,150);
		f.setLayout(new GridLayout(2,1));
		f.add(b1);
		f.add(b2);
		f.setVisible(true);
	}
	
	public static void main(String[] args) {
		Language l = new Language();
		l.example();
	}

}
```

sprache_de.properties:
l_titel=Deutsch
l_eins=eins
l_zwei=zwei

sprache_en.properties:
l_titel=English
l_eins=one
l_zwei=two


----------



## schnuffie (26. September 2005)

Auf Anhieb kann ich keinen Fehler feststellen, nehme allerdings an, daß er Deine Dateien nicht findet. Habe von meinem (funktionierenden) Beispiel mal Screenshots erstellt (siehe Anlagen).

Es gibt bei mir nur eine spezielle deutsche Datei für "deutsche" PCs, sonst wird immer die allgemeine Datei geladen, die bei mir die englischen Begriffe enthält.


----------



## Badabum (25. Februar 2011)

Also ich weiß, dass es schon länger her ist,dass dieses Thema bearbeitet wurde aber vllt antwortet ja jemand. Ich habe ein Problem mit dem ResourceBundle, ich habe mit Eclipse mir eine propertie Datei erstellen lassen und auch noch weitere angelegt. Nun möchte ich, wenn ich in meiner Gui auf German klicke, dass er das Fenster in Deutsch anzeigt. Ich hab das auch wie hier beschrieben mit getGerman, getEnglish versucht aber die Buttons werden mir nicht aktualisiert.
Die Console gibt folgendes aus:

java.util.PropertyResourceBundle@62acc57

Wo liegt mein Fehler oder was muss ich ändern?


----------



## Akeshihiro (25. Februar 2011)

Dann wirst du wohl die Beschriftungen aller deiner Buttons, Labels, etc. neu setzen müssen. Das ResourceBundle besrogt ja lediglich die sprachenspezifische Einstellungen, welche in den meisten Fällen einfach nur Texte sind, hat aber nix mit der Verarbeitung dieser zu tun. Also das Aktualisieren muss man noch selbst implementieren.


----------



## Badabum (25. Februar 2011)

Ja genau das ist jetzt mein Problem, ich finde kein Ansatz wie er die Änderungen nun übernehmen soll. Ich habe angenommen, dass er nun jegliche Platzhalter, welche vor zB mit den Englischen besetzt waren, durch die Deutschen ersetzt. Hat jemand vllt einen Ansatz wie man das umsetzen kann?


----------



## Akeshihiro (25. Februar 2011)

Hier mal mein Ansatz, mit dem bin ich eigentlich immer ganz gut gefahren.

```
package de.tutorials.forum.hilfe.badabum.resourcebundle.update;

import java.awt.FlowLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.Locale;
import java.util.ResourceBundle;

import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;

public class ComponentsUpdateSample extends JFrame {
	private static final String	COMPONENTS_BUTTONS_DEBUTTON	= "componentsupdatesample.components.buttons.debutton";
	private static final String	COMPONENTS_BUTTONS_ENBUTTON	= "componentsupdatesample.components.buttons.enbutton";
	private static final String	COMPONENTS_LABELS_TESTLABEL	= "componentsupdatesample.components.labels.testlabel";
	private static final String	LANG_BUNDLE_NAME			= "lang";
	private static final long	serialVersionUID			= 8049149370558375561L;

	private JButton				deButton;
	private JButton				enButton;
	private ResourceBundle		langBundle;
	private JLabel				testLabel;

	public static void main(String[] args) {
		new ComponentsUpdateSample();
	}

	public ComponentsUpdateSample() {
		initSettings();
		initComponents();
		initGui();
	}

	private JButton getDeButton() {
		if(deButton == null) {
			deButton = new JButton(
					langBundle.getString(COMPONENTS_BUTTONS_DEBUTTON));
			deButton.addActionListener(new ActionListener() {
				@Override
				public void actionPerformed(ActionEvent e) {
					langBundle = getLangBundle(Locale.GERMAN);
					updateLangComponents();
				}
			});
		}

		return deButton;
	}

	private JButton getEnButton() {
		if(enButton == null) {
			enButton = new JButton(
					langBundle.getString(COMPONENTS_BUTTONS_ENBUTTON));
			enButton.addActionListener(new ActionListener() {
				@Override
				public void actionPerformed(ActionEvent e) {
					langBundle = getLangBundle(Locale.ENGLISH);
					updateLangComponents();
				}
			});
		}

		return enButton;
	}

	private ResourceBundle getLangBundle(Locale locale) {
		return ResourceBundle.getBundle(
				ResLoader.class.getPackage().getName() + "." + LANG_BUNDLE_NAME,
				locale);
	}

	private JLabel getTestLabel() {
		if(testLabel == null) {
			testLabel = new JLabel(
					langBundle.getString(COMPONENTS_LABELS_TESTLABEL));
		}

		return testLabel;
	}

	private void initComponents() {
		setLayout(new FlowLayout(FlowLayout.LEFT));
		add(getDeButton());
		add(getEnButton());
		add(getTestLabel());
	}

	private void initGui() {
		setSize(500, 500);
		setLocationRelativeTo(null);
		setVisible(true);
	}

	private void initSettings() {
		setTitle(getName());
		setDefaultCloseOperation(EXIT_ON_CLOSE);

		langBundle = getLangBundle(Locale.getDefault());
	}

	private void updateButtons() {
		getDeButton().setText(langBundle.getString(COMPONENTS_BUTTONS_DEBUTTON));
		getEnButton().setText(langBundle.getString(COMPONENTS_BUTTONS_ENBUTTON));
	}

	private void updateLabels() {
		getTestLabel().setText(langBundle.getString(COMPONENTS_LABELS_TESTLABEL));
	}

	private void updateLangComponents() {
		updateButtons();
		updateLabels();
	}
}
```
Ist an sich nix besonderes und leicht zu verstehen, daher auch keine Kommentare.
Auf ResLoader brauchst du nicht achten. Das ist nur eine Klasse, die ich immer in das resource-Package kloppe, um somit zentral auf die Ressourcen zuzugreifen, da sich sonst je nach Klasse ein anderer Pfad ergibt und das Nachkorrigieren ziemlich nervig sein kann. Kurz gesagt: nicht drauf achten, is nix besonderes.

Und hier meine Property-Files:
lang_de.properties


> componentsupdatesample.components.buttons.enbutton=Englisch
> componentsupdatesample.components.buttons.debutton=Deutsch
> componentsupdatesample.components.labels.testlabel=Das ist ein Test


lang_en.properties


> componentsupdatesample.components.buttons.enbutton=English
> componentsupdatesample.components.buttons.debutton=German
> componentsupdatesample.components.labels.testlabel=This is a test


----------



## SE (25. Februar 2011)

schnuffie hat gesagt.:


> ResourceBundle


 war bie dir vor 5 1/2 jahren das hier file:///D:/dokumentationen/java1.4.2/docs/api/java/util/ResourceBundle.html

hoffe du liest das hier und hast dir diesen fehler inzwischen abgewöhnt ... denn wir können nicht auf DEINE platten zureifen *zumindest nich ohne umwege*


----------



## isaias_b (25. Februar 2011)

Hi Leute,

ich hab da was für euch, mag sein das es da auch was von Ratiopha*** gibt, aber von der Heuristik ist das ein skalierbarer Ansatz den man beliebig nach eigenen Wünschen verfeinern kann.
Die Idee.
Man hat eine Zentrale an der man Komponenten anmeldet die sich auf Sprachänderung anpassen sollen. Beobachter (Observer) Muster quasi. Ändert man in der Zentrale die Sprache so werden alle angemeldeten Komponenten benachrichtigt.

```
import java.awt.GridLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Locale;
import java.util.Map;
import java.util.ResourceBundle;
import java.util.Set;

import javax.swing.JButton;
import javax.swing.JFrame;

/**
 * kleine hilfsklasse könnte man auch wegoptimieren
 * aaaaber pro ResourceBundle kann man verschiedene
 * LocalizableComponent anmelden das müsste man sonst
 * mit maps mit sets oder maps von listen lösen...
 * 
 * ##OBSERVER-PATTERN## - BeobachtbaresObjekt
 */
class HelpBundle {
	private ResourceBundle rBundle;

	private Set<LocalizableComponent> lComponents;

	public HelpBundle(ResourceBundle rBundle) {
		this.rBundle = rBundle;
		this.lComponents = new HashSet<LocalizableComponent>();
	}

	public void addLocalizableComponent(LocalizableComponent lComponent) {
		lComponents.add(lComponent);
	}

	ResourceBundle getResourceBundle() {
		return rBundle;
	}

	/**
	 * bescheid geben an alle angemeldeten beobachter
	 * ##OBSERVER-PATTERN## - Benachrichtigungen Abfeuern
	 */
	void notifyComponents(ResourceBundleHelper rbHelper) {
		for (LocalizableComponent component : lComponents)
			component.localeChanged(rbHelper, rBundle);
	}

	void setResourceBundle(ResourceBundle rBundle) {
		this.rBundle = rBundle;
	}
}

/**
 * Beobachter Interface
 * 
 * ##OBSERVER-PATTERN## - Beobachter
 */
interface LocalizableComponent {
	/**
	 * wird aufgerufen wenn sprache sich ändert
	 * ##OBSERVER-PATTERN## - Benachrichtigungs Methode
	 */
	public void localeChanged(ResourceBundleHelper rbHelper,
			ResourceBundle rBundle);
}

/**
 * kontroller klasse bei der mehrere ResourceBundles auf einmal geändert werden
 * können.
 * 
 * ##OBSERVER-PATTERN## - Beobachtbares Objekt
 * 
 */
class ResourceBundleHelper {

	private static Locale defaultLocale = Locale.getDefault();

	private Map<String, HelpBundle> bundles;
	private Locale locale;

	private static final ResourceBundleHelper instance;

	static {
		instance = new ResourceBundleHelper(defaultLocale);
	}

	public static ResourceBundleHelper getInstance() {
		return instance;
	}

	private ResourceBundleHelper(Locale locale) {
		bundles = new HashMap<String, HelpBundle>();
		this.locale = locale;
	}

	public void addLocalizableComponent(String bundleName,
			LocalizableComponent lComponent) {
		if (!bundles.containsKey(bundleName))
			addRes(bundleName, getLocale());
		HelpBundle hBundle = bundles.get(bundleName);
		hBundle.addLocalizableComponent(lComponent);
	}

	private void addRes(String baseName, Locale locale) {
		ResourceBundle rBundle = ResourceBundle.getBundle(baseName, locale);
		bundles.put(baseName, new HelpBundle(rBundle));
	}

	public Locale getLocale() {
		return locale;
	}

	public ResourceBundle getResourceBundle(String bundleName) {
		return bundles.get(bundleName).getResourceBundle();
	}

	private void notifyListeners() {
		for (String baseName : bundles.keySet()) {
			HelpBundle hBundle = bundles.get(baseName);
			hBundle.notifyComponents(this);
		}

	}

	public void setLocale(Locale locale) {
		if (!this.locale.equals(locale)) { // just a little performance tweak
			this.locale = locale;
			for (String bundleName : bundles.keySet()) {
				HelpBundle hBundle = bundles.get(bundleName);
				ResourceBundle rBundle = ResourceBundle.getBundle(bundleName,
						locale);
				hBundle.setResourceBundle(rBundle);
			}
			notifyListeners();
		}
	}
}
/**
 * TEST CLASS
 * 
 * ##OBSERVER-PATTERN## - Konkreter Beobachter
 *
 */
public class ResTest extends JFrame
		implements LocalizableComponent, ActionListener {
	private static final String LOCALIZED_FRAME_TITLE = "Frame.title";
	private static final String LOCALIZED_LANG_DE = "lang.de";
	private static final String LOCALIZED_LANG_EN = "lang.en";
	private static final String RESOURCE_BUNDLE_LANG = "lang";

	public static void main(String[] args) {
		new ResTest().setVisible(true);
	}

	private JButton deButton = new JButton();

	private JButton enButton = new JButton();

	public ResTest() {
		super();
		setLayout(new GridLayout());
		setDefaultCloseOperation(EXIT_ON_CLOSE);

		add(deButton);
		add(enButton);
		deButton.addActionListener(this);
		enButton.addActionListener(this);

		initLocalizedComponents();
	}

	public void actionPerformed(ActionEvent e) {
		ResourceBundleHelper rbh = ResourceBundleHelper.getInstance();
		Locale locale = rbh.getLocale();
		if (e.getSource().equals(enButton))
			locale = Locale.ENGLISH;
		if (e.getSource().equals(deButton))
			locale = Locale.GERMAN;
		rbh.setLocale(locale);
	}

	private void initLocalizedComponents() {
		ResourceBundleHelper rbh = ResourceBundleHelper.getInstance();
		/**
		 * ##OSERVER-PATTERN## - Konkreten Beobachter registrieren 
		 * in diesem Fall das Fenster nur einmal.
		 * Statt Fenster und je 2 Knöpfe.
		 */
		rbh.addLocalizableComponent(RESOURCE_BUNDLE_LANG, this);
		localeChanged(rbh, rbh.getResourceBundle(RESOURCE_BUNDLE_LANG));
	}

	/**
	 * ##OSERVER-PATTERN## - Konkrete Benachrichtigung
	 */
	public void localeChanged(ResourceBundleHelper rbHelper,
			ResourceBundle rBundle) {

		setTitle(rBundle.getString(LOCALIZED_FRAME_TITLE));
		deButton.setText(rBundle.getString(LOCALIZED_LANG_DE));
		enButton.setText(rBundle.getString(LOCALIZED_LANG_EN));
	}
}
```

Sollte lauffähig sein! Bei mir rennt das Moped zumindest.

Interessante Methoden sind ResTest::localeChanged() die vom ResourceBundleHelper automatisch aufgerufen wird wenn die Sprachen sich ändert & ResTest::initLocalizedComponents() mit der man sich am RescourceBundleHelper anmeldet.

Hoffe das hilft. 

Greez isaias


----------

