package es.icarto.gvsig.navtableforms; import java.awt.Component; import java.awt.Container; import javax.swing.JButton; import javax.swing.JLabel; import javax.swing.JCheckBox; import javax.swing.JTabbedPane; import com.jeta.forms.components.border.TitledBorderLabel; import es.icarto.gvsig.navtableforms.gui.i18n.I18nResourceManager; /** * This class provides base i18n support for NavTableForms. * * Default i18n support is provided for static texts in Abeille forms (or similar). * The widget name is used as the i18n key (see {@link #getTranslation()} for the specific syntax). * * @author Jorge L�pez Fern�ndez <jlopez@cartolab.es> * */ public class I18nHandler { private static final String i18nPrefix = "i18n"; private II18nForm i18nForm; private I18nResourceManager i18nManager; public I18nHandler(II18nForm i18nForm) { this.i18nForm = i18nForm; this.i18nManager = new I18nResourceManager(i18nForm.getI18nResources()); } public I18nResourceManager getResourceManager() { return i18nManager; } public void translateFormStaticTexts() { translateContainerStaticTexts(i18nForm.getFormPanel()); } /** * This method is used to apply internationalization onto the container static texts * (labels, buttons, border titles and tab names) using the * {@link es.icarto.gvsig.navtableforms.gui.i18n.resource.I18nResource} instances retrieved * from the form. */ public void translateContainerStaticTexts(Container c) { for (int i = 0, len = c.getComponentCount(); i < len; i++) { Component comp = c.getComponent(i); translateComponentText(comp); if (comp instanceof Container) { translateContainerStaticTexts((Container) comp); } } } private void translateComponentText(Component comp) { String name = comp.getName(); if (comp instanceof JLabel) { JLabel label = (JLabel) comp; label.setText(getTranslation(name, label.getText())); } else if (comp instanceof JButton) { JButton btn = (JButton) comp; btn.setText(getTranslation(name, btn.getText())); } else if (comp instanceof TitledBorderLabel) { TitledBorderLabel title = (TitledBorderLabel) comp; title.setText(getTranslation(name, title.getText())); } else if (comp instanceof JCheckBox) { JCheckBox chb = (JCheckBox) comp; // Checkboxes can't always have the i18n prefix because they must reference // a DB field name, so we add it manually in order to translate them when the // checkbox is already displaying a text if (!name.startsWith(i18nPrefix) && chb.getText() != null && !chb.getText().trim().isEmpty()) { name = i18nPrefix + "." + name; } chb.setText(getTranslation(name, chb.getText())); } else if (comp instanceof JTabbedPane) { JTabbedPane panel = (JTabbedPane) comp; for (int j = 0, leng = panel.getTabCount(); j < leng; j++) { panel.setTitleAt(j, getTranslation(getTabKey(name, j + 1), panel.getTitleAt(j))); } } } /** * The widget name must follow a syntax, which is * * i18n.<i18n_key>[.<nr>][.<format_code>]* * * The first section, 'i18n', is just a prefix for identifying widgets which require i18n * for their texts. * Then comes the i18n id key itself (the one which should be used as key in the * {@link es.icarto.gvsig.navtableforms.gui.i18n.resource.I18nResource} instances). * The optional section that follows should contain a plain integer, and is intended * to allow us to use the same i18n id key in multiple widgets without causing problems * by them having the same name (e.g. 'i18n.msg.1', 'i18n.msg.2', etc.). * Optional format codes provide simple formatting options (see {@link #formatValue()}). * We accept multiple formatting codes, and they are applied in the order in which they appear. * E.g.: if 'msg' i18n id returns 'test case', then 'i18n.msg.lower.dd.title' -> 'Test Case:' * Keep in mind that both the nr and the format codes are optional, but if we use both, then * the number should come before the codes. */ private String getTranslation(String key, String defaultValue) { if (key == null) { return defaultValue; } String[] splitName = key.split("\\."); if (!i18nPrefix.equals(splitName[0]) || !i18nManager.containsKey(splitName[1])) { return defaultValue; } String value = i18nManager.getString(splitName[1]); if (value.length() > 0 && splitName.length > 2) { int formatStartIdx; try { Integer.parseInt(splitName[2]); formatStartIdx = 3; } catch (NumberFormatException e) { formatStartIdx = 2; } for (int i = formatStartIdx, len = splitName.length; i < len; i++) { value = formatValue(value, splitName[i]); } } return value; } /** * Tab panels are a special case for i18n, because they can have multiple texts but * a sole widget name. * Because of this, we identify their tabs' texts by combining the tab panel name * with the tab number, starting by 1. * E.g.: if the tab panel name is 'i18n.tab', then we should use 'tab_1', 'tab_2'... * in the i18n resource. * Trailing numbers and formatting are still accepted for the tab panel's name, and * the formatting will be applied in all the tabs' names. */ private String getTabKey(String panelKey, int tabNr) { if (panelKey == null) { return null; } String[] splitName = panelKey.split("\\."); if (splitName.length < 2) { return null; } return panelKey.replaceFirst(splitName[0] + "\\." + splitName[1], splitName[0] + "\\." + splitName[1] + "_" + tabNr); } /** * We provide some formatting codes for making easier to display * i18n strings in different ways w/o having to store them multiple * times. * As of now we support the following codes: * * - dd: adds trailing double dots. * - upper: displays the text in uppercase. * - lower: displays the text in lowercase. * - title: displays the text in title case (capitalizing the first * letter of each word). * - html: wraps the current text in html tags. */ private static String formatValue(String value, String formatCode) { if ("dd".equals(formatCode)) { return value + ":"; } if ("upper".equals(formatCode)) { return value.toUpperCase(); } if ("lower".equals(formatCode)) { return value.toLowerCase(); } if ("title".equals(formatCode)) { return toTitleCase(value); } if ("html".equals(formatCode)) { return "<html>" + value + "</html>"; } return value; } private static String toTitleCase(String input) { StringBuilder titleCase = new StringBuilder(); boolean nextTitleCase = true; for (char c : input.toCharArray()) { if (Character.isSpaceChar(c)) { nextTitleCase = true; } else if (nextTitleCase) { c = Character.toTitleCase(c); nextTitleCase = false; } titleCase.append(c); } return titleCase.toString(); } }