Aller au contenu

Java - Interface graphique et internationalisation


Corey8

Messages recommandés

Bonjour à tous,

Je suis en train (plutôt je commence) de développer une application en interface graphique (Java - Swing). Je rencontre tout d'abord quelques difficultés à bien construire mon code. Voilà un exemple de ce que j'ai fait :

import javax.swing.JFrame;

public class FenetrePrincipal{

public static void main(String args[]){
	JFrame frame = new JFrame("Fenêtre principal");
	new BarreMenu(frame);

	frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
	frame.setSize(800, 600);
	frame.setVisible(true);
}
}

import javax.swing.JFrame;
import javax.swing.JMenu;
import javax.swing.JMenuBar;
import javax.swing.JMenuItem;
import javax.swing.JSeparator;
import javax.swing.KeyStroke;

public class BarreMenu {

final JFrame frame;

public BarreMenu(JFrame frame){
	this.frame = frame;

	JMenuBar menuBar = new JMenuBar();
	ajouterMenuFichier(menuBar);

	frame.setJMenuBar(menuBar);
}

private void ajouterMenuFichier(JMenuBar menuBar){
	JMenu menuFichier = new JMenu("Fichier");

	JMenuItem menuItemDeconnexion = new JMenuItem("Changer d'utilisateur...");
	JMenuItem menuItemQuitter = new JMenuItem("Quitter");
	menuItemQuitter.addActionListener(new ActionListener(){
		public void actionPerformed(ActionEvent event){
			frame.dispose();
		}
	});

	menuItemDeconnexion.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_D, ActionEvent.CTRL_MASK));
	menuItemQuitter.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_Q, ActionEvent.CTRL_MASK));

	menuFichier.add(menuItemDeconnexion);
	menuFichier.add(menuItemQuitter);

	menuBar.add(menuFichier);
}
}

Donc en fait comme c'est une "belle" (assez grosse) interface je vais découper la frame principale en sous-classes afin que ce soit plus "propre". Comme dans mon exemple ci-dessus une classe pour le menu.

J'ai un peu de mal à bien structurer mon code parce que voilà ce que le prof (oui c'est un projet d'études) ne veut pas que l'on fasse dans notre code :

Java est un langage Objet, il est donc possible d'utiliser l'héritage.

Doit-on hériter par exemple de la classe JFrame ? NON !

ex à pas faire :

import javax.swing.*;

public class MorphSwing2 extends JFrame {

public MorphSwing2() {
	super("MorphSwing2");
	JButton button=new JButton("Ok");
	setContentPane(button);
	setSize(400,300);
	setVisible(true);
}

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

mais

import javax.swing.*;

public class MorphSwing1 {

public static void main(String[] args) {
	JButton button=new JButton("Ok");
	JFrame frame=new JFrame("MorphSwing1");
	frame.setContentPane(button);
	frame.setSize(400,300);
	frame.setVisible(true);
}
 }

On hérite d'une classe si on veut en changer les fonctionnalités (i.e. redéfinir une méthode)

Il est possible de faire des fonctions pour rendre le code plus clair

Et enfin,

Doit-on stocker les composants en tant qu'attributs de la classe ?

NON! Les composants sont déjà stockés dans leurs parents.

ex :

import javax.swing.*;

public class MorphSwing4 extends JFrame {

private final JButton button; // idiot

public MorphSwing4() {
	super("MorphSwing2");
	button=new JButton("Ok");
	setContentPane(button);
}

public static void main(String[] args) {
	JFrame frame=new MorphSwing4();
	frame.setSize(400,300);
	frame.setVisible(true);
}
}

Si des personnes pouvait me dire ce qu'il ne va pas dans mon code ou ce qu'il faudrait que j'améliore se serai vraiment sympa.

Sinon, je voudrais aussi intégrer la possibilité d'internationaliser mon appli donc j'ai trouvé ce code qu'il a l'aire pas mal :

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

public class LanguageChooser implements ActionListener {

// Nom du fichier properties (sans les codes ISO)
private static final String LANGUAGE_FILE = "language";

private static ResourceBundle resourceBundle;

// La fenetre
private MyFrame frame;

public LanguageChooser(MyFrame aFrame) {
	frame = aFrame;
	resourceBundle = ResourceBundle.getBundle(LANGUAGE_FILE, Locale.getDefault());
}

// Methode appelee lorsqu’on selectionne un menu
public void actionPerformed(ActionEvent arg0) {
	// On a selectionne le menu anglais
	if (arg0.getActionCommand().equals("menuEn")) {
	// On choisi le fichier anglais
	resourceBundle = ResourceBundle.getBundle(LANGUAGE_FILE, Locale.US);
	}
	// Sinon on choisi le francais
	else if (arg0.getActionCommand().equals("menuFr")) {
	// On choisi le francais
	resourceBundle = ResourceBundle.getBundle(LANGUAGE_FILE, Locale.FRANCE);
	}
	//On modifie les labels avec la nouvelle langue
	frame.paintLabelWithLanguage();
}

/*
* Methode statique qui permet de recuperer un String du fichier de langue
* en fonction de la cle passee en parametre
*/
public static String getAnInternationalizeString(String key) {
	// On retourne la valeur associee a la cle
	return resourceBundle.getString(key);
}
}

import javax.swing.*;

public class MyFrame extends JFrame {
// Déclaration des différents composants
private JLabel labelHello;
private JMenuBar menuBar;
private JMenu menuLanguage;
private JMenuItem menuFr;
private JMenuItem menuEn;
private LanguageChooser languageChooser;

// Point d’entrée de l’application
public static void main(String[] args) {
	MyFrame fenetre = new MyFrame();
}

public MyFrame() {
	// Initialisation standard
	super("Application Internationale");
	setSize(300, 300);
	setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

	// Création du LanguageChooser
	languageChooser = new LanguageChooser(this);

	// Création du label
	labelHello = new JLabel();
	getContentPane().add(labelHello);

	// Création de la barre de menu
	menuBar = new JMenuBar();
	menuLanguage = new JMenu();

	menuFr = new JMenuItem("Francais");
	menuFr.setActionCommand("menuFr");
	menuFr.addActionListener(languageChooser);
	menuLanguage.add(menuFr);

	menuEn = new JMenuItem("English");
	menuEn.setActionCommand("menuEn");
	menuEn.addActionListener(languageChooser);
	menuLanguage.add(menuEn);

	menuBar.add(menuLanguage);
	setJMenuBar(menuBar);

	// On initailise les valeurs des labels, menus
	paintLabelWithLanguage();

	// On affiche la fenêtre
	setVisible(true);
}

/*
* Méthode qui initialise le texte des différents labels et menus en
* fonction de la langue
*/
protected void paintLabelWithLanguage() {
	menuLanguage.setText(LanguageChooser.getAnInternationalizeString("menuLanguage"));
	labelHello.setText(LanguageChooser.getAnInternationalizeString("labelHello"));
}
}

Mais voilà j'ai du mal à l'intégrer dans mon code afin que les conditions du prof soient respectées et que je puisse l'utiliser sur différent de la fenêtre (menu, calendrier, panel central, pop-up, ...). Si vous avez des aidés elles sont les bienvenues.

Merci d'avance à tous

Lien vers le commentaire
Partager sur d’autres sites

A vue de nez, tu colles ton ResourceBundle en propriété statique de ta classe principale d'application (Application.java par exemple), et tu n'as plus qu'à y faire appel depuis tous tes composants.

Plus exactement, tu mets la propriété privée et statique, et tu définis un accesseur statique et public.

En aucun cas il ne faut référencer partout le LanguageChooser, qui est un élément de l'interface.

Lien vers le commentaire
Partager sur d’autres sites

A vue de nez, tu colles ton ResourceBundle en propriété statique de ta classe principale d'application (Application.java par exemple), et tu n'as plus qu'à y faire appel depuis tous tes composants.

Plus exactement, tu mets la propriété privée et statique, et tu définis un accesseur statique et public.

En aucun cas il ne faut référencer partout le LanguageChooser, qui est un élément de l'interface.

Désolé, mais je n'ai tout compris. Est-ce que tu pourrais un peu plus détailler t'as "proposition" (solution), s'il te plait.

Il ne faut pas plutôt que je crée une instance de LanguageChooser dans la classe principale de mon application (qui est pour le moment FenetrePrincipal(e).java) qu'un ResourceBundle ? Parce que le ResourceBundle est une variable privée de la classe LanguageChooser.

Lien vers le commentaire
Partager sur d’autres sites

C'est bien ce que je dis, c'est une erreur de mettre le ResourceBundle dans une classe spécifiquement liée à l'interface graphique.

Tu devrais d'abord découpler la logique de l'interface : avoir une classe Application qui effectue les traitements métier de ton programme, et une classe Interface (mais ce nom est mal choisi, disons plutôt Presentation) qui se charge de proposer des fenêtres à l'utilisateur. En définissant des interfaces contractuelles pour la communication entre les deux, bien sûr.

La gestion de l'internationalisation est plutôt une problématique métier : si tu changes d'interface (mettons, passage au mode web), il faut toujours gérer l'internationalisation. Donc je préconise de mettre le ResourceBundle au niveau de la classe Application, avec un accesseur statique sur le ResourceBundle.

Mieux, tu devrais même masquer le fait que c'est un ResourceBundle en offrant une méthode getInternationalizedMessage(String msgId, Locale locale) qui, du point de vue des autres classes, se débrouille pour renvoyer un message internationalisé.

Lien vers le commentaire
Partager sur d’autres sites

La gestion de l'internationalisation est plutôt une problématique métier : si tu changes d'interface (mettons, passage au mode web), il faut toujours gérer l'internationalisation.

Le probleme c'est que l'internationalisation, il y en a partout. Par exemple il faut internationaliser les boutons de l'interface graphique, boutons qu'on n'est pas sur de retrouver du tout sur une autre interface graphique.

En general moi j'ai plusieurs resource bundles.

- 1 pour l'interface graphique avec le nom des boutons, les tooltips...etc. Ca ne concerne que les machins d'interface. Celle-la elle reste avec les classes de l'interface graphique

- 1 pour internationaliser le contenu a afficher (venant du modele). Par exemple si l'interface graphique affiche une liste de noms de villes dans une table, ces noms de villes venant du modele...j'utilise cette deuxieme resource bundle pour traduire les noms de ville dans la langue utilisee.

Et cette bundle la reste avec le modele. Comme ca si on balance l'interface graphique pour en faire une nouvelle, on n'a pas perdu toute l'internationalisation.

En fait, c'est comme le reste, l'internationalisation ca se separe a peu pres bien en modele et vue. Mais il y a toujours un moment ou on se retrouve avec quelque-chose qui n'est ni purement modele ni purement vue :transpi:

Lien vers le commentaire
Partager sur d’autres sites

Archivé

Ce sujet est désormais archivé et ne peut plus recevoir de nouvelles réponses.

×
×
  • Créer...