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