Psali jste někdy lokalizované aplikace ve Swingu? A jak jste řešili klávesové zkratky (Mnemonics) pro různé jazyky? Já jsem se při programování JSignPdf rozhodl použít přístup, který znám z Delphi. V resource řetězcích vložím ampersand (&) před písmeno, které má být použito jako klávesová zkratka. Když chci zobrazit znak ampersand, tak napíšu dva za sebe.
A jak to funguje v kódu? Mám wrapper pro ResourceBundle, který nabízí mimo metod pro přístup k lokalizovaným stringům i metodu, která vrací MnemonicIndex (pozici znaku, který je použit jako klávesová zkratka).
/** * Returns message for given key from active ResourceBundle * @param aKey name of key in resource bundle * @return message for given key */ public String get(final String aKey) { String tmpMessage = bundle.getString(aKey); if (tmpMessage == null) { tmpMessage = aKey; } else { tmpMessage = tmpMessage.replaceAll("&([^&])", "$1"); } return tmpMessage; } /** * Returns index of character which should be used as a mnemonic. * It returns -1 if such an character doesn't exist. * @param aKey resource key * @return index (position) of character in translated message */ public int getMnemonicIndex(final String aKey) { String tmpMessage = bundle.getString(aKey); int tmpResult = -1; if (tmpMessage != null) { int searchFrom = 0; int tmpDoubles = 0; int tmpPos; final int tmpLen = tmpMessage.length(); do { tmpPos = tmpMessage.indexOf('&', searchFrom); if (tmpPos == tmpLen-1) tmpPos = -1; if (tmpPos>-1) { if (tmpMessage.charAt(tmpPos+1) != '&') { tmpResult = tmpPos - tmpDoubles; } else { searchFrom = tmpPos + 2; tmpDoubles++; } } } while (tmpPos!=-1 && tmpResult==-1 && searchFrom<tmpLen); } return tmpResult; } /** * Returns message for given key from active ResourceBundle and replaces * parameters with values given in array. * @param aKey key in resource bundle * @param anArgs array of parameters to replace in message * @return message for given key with given arguments */ public String get(String aKey, String anArgs[]) { String tmpMessage = get(aKey); if (aKey==tmpMessage || anArgs == null || anArgs.length == 0) { return tmpMessage; } final MessageFormat tmpFormat = new MessageFormat(tmpMessage); return tmpFormat.format(anArgs); }
použití uvedeného wrapperu potom vypadá následovně (res
je jméno instance wrapperu):
/** * Application translations. */ private void translateLabels() { setTitle(res.get("gui.title", new String[] {Constants.VERSION})); setLabelAndMnemonic(lblKeystoreType, "gui.keystoreType.label"); setLabelAndMnemonic(chkbAdvanced, "gui.advancedView.checkbox"); setLabelAndMnemonic(btnSignIt,"gui.signIt.button"); } /** * Sets translations and mnemonics for labels and different kind of buttons * @param aComponent component in which should be label set * @param aKey message key */ private void setLabelAndMnemonic(final JComponent aComponent, final String aKey) { final String tmpLabelText = res.get(aKey); final int tmpMnemIndex = res.getMnemonicIndex(aKey); if (aComponent instanceof JLabel) { final JLabel tmpLabel = (JLabel) aComponent; tmpLabel.setText(tmpLabelText); if (tmpMnemIndex>-1) { tmpLabel.setDisplayedMnemonic(tmpLabelText.toLowerCase().charAt(tmpMnemIndex)); tmpLabel.setDisplayedMnemonicIndex(tmpMnemIndex); } } else if (aComponent instanceof AbstractButton) { //handles Buttons, Checkboxes and Radiobuttons final AbstractButton tmpBtn = (AbstractButton) aComponent; tmpBtn.setText(tmpLabelText); if (tmpMnemIndex>-1) { tmpBtn.setMnemonic(tmpLabelText.toLowerCase().charAt(tmpMnemIndex)); } } else { throw new IllegalArgumentException(); } }
Resource fajl potom obsahuje takovéto záznamy:
gui.advancedView.checkbox=A&dvanced view gui.alias.label=Key &alias
Komentáře