jeudi 3 janvier 2013

[Java] Resources et internationalisation simple avec ResourceBundle


Un moyen simple d'internationaliser son code Java est d'utiliser la class ResourceBundle avec des fichiers properties.

Voici une IHM simple suivi de son code :


public final class SimpleFrame extends JFrame {
    
    private JLabel label;
    private JButton closeButton;

    public SimpleFrame() {
        super("Simple Frame");
        initComponents();
        setDefaultCloseOperation(DISPOSE_ON_CLOSE);
        setPreferredSize(new Dimension(280, 70));
        pack();
        setLocationRelativeTo(null);
    }
    
    private void initComponents() {
        label = new JLabel("Hello world!");
        closeButton = new JButton("Close");
        closeButton.addActionListener(new ActionListener() {

            @Override
            public void actionPerformed(ActionEvent e) {
                dispose();
            }
        });
        setLayout(new FlowLayout(FlowLayout.CENTER));
        add(label);
        add(closeButton);
    }
}


Vous noterez les trois chaînes de caractères écrites en dur dans le code.

La classe ResourceBundle permet d'externaliser ces ressources dans un fichier properties, le tout pouvant être internationalisé de surcroît.

Voici le code d'une IHM similaire à la précédente :
public final class I18nFrame extends JFrame {
    
    private static final ResourceBundle RESOURCES = 
                               ResourceBundle.getBundle(I18nFrame.class.getName());
    private JLabel label;
    private JButton closeButton;

    public I18nFrame() {
        super(RESOURCES.getString("title"));
        initComponents();
        setDefaultCloseOperation(DISPOSE_ON_CLOSE);
        setPreferredSize(new Dimension(280, 70));
        pack();
        setLocationRelativeTo(null);
    }
    
    private void initComponents() {
        label = new JLabel(RESOURCES.getString("label.text"));
        closeButton = new JButton(RESOURCES.getString("closeButton.text"));
        closeButton.addActionListener(new ActionListener() {

            @Override
            public void actionPerformed(ActionEvent e) {
                dispose();
            }
        });
        setLayout(new FlowLayout(FlowLayout.CENTER));
        add(label);
        add(closeButton);
    }
}

Les chaînes de caractères sont stockées dans un fichier properties portant le même nom que la classe (ici I18nFrame.properties) et situé dans le même package.
label.text = Hello world!
closeButton.text = Close
title=Internationalized Frame

On obtient le résultat suivant


Pour ajouter une nouvelle langue il suffit de créer un fichier properties pareil au premier en y ajoutant un suffixe à son nom. Le suffixe indique la locale concernée, ainsi un fichier I18nFrame_fr.properties spécifie les ressources pour la langue française (cf la javadoc de  ResourceBundle pour plus de détails).
label.text = Bonjour tout le monde !
closeButton.text = Fermer
title=Fenêtre internationalisée

Ce qui donne :




Ce mécanisme est assez simple et très facile à mettre en oeuvre, très utile pour internationaliser son code. Un dernier exemple pour la représentation textuelle d'une enum.
public enum Country {
    FRANCE,
    GERMANY,
    USA;
    
    private static final ResourceBundle RESOURCES = 
                               ResourceBundle.getBundle(Country.class.getName());

    @Override
    public String toString() {
        return RESOURCES.getString(name());
    }
}

avec le fichier Country.properties
FRANCE = France
GERMANY = Germany
USA = United State of America
et Country_fr.properties
FRANCE = France
GERMANY = Allemagne
USA = Etats Unis d'Amérique


Vous n'avez plus d'excuse pour hardcoder vos chaînes de caractère ;-)

@++