Search code examples
javafxjavafx-8

How to create a numeric localized (decimal separator) TextField?


I want to create a numeric TextField which will be localized.

For example, in Excel, if you are in english, if you type a number with the KeyPad, the decimal separator key will enter a dot '.' But if you are in French, the decimal separator will be a comma ','

Therefore in Excel, the TextField is smart enough to detect in which Locale you're on, and to adapt the decimal separator you actually wanted to print in the TextField.

Right now in JavaFX, I haven't found a way to do that. I was thinking of listening to text modification and to replace any occurrence of dot with a comma if I detect I'm in French. But is this assumption true?


Solution

  • I would recommend subclassing TextField and overriding the insertText and replace* methods. This will handle text entry before the text is updated; listening to text modifications and fixing them will mean you temporarily have "invalid" text in your text field's text property, which could create problems in the rest of your application.

    Note that you can create a java.text.DecimalFormatSymbols object for the default (user) Locale (or a specified Locale, if you need), and call getDecimalSeparator() to find the correct decimal separator character. (Similarly, you can call getGroupingSeparator().)

    So, for proof of concept, this is nowhere near robust enough for production:

    public class NumericTextField extends TextField {
        @Override
        public void insertText(int index, String text) {
            String fixedText = fixText(text);
            StringBuilder newText = new StringBuilder(getText().substring(0, index));
            newText.append(fixedText);
            if (index < getText().length()) {
                newText.append(getText().substring(index));
            }
            if (isValid(newText)) {
                super.insertText(index, fixedText);
            }
        }
    
        @Override 
        public void replaceText(int start, int end, String text) {
            String fixedText = fixText(text);
            StringBuilder newText = new StringBuilder(getText().substring(0, start));
            newText.append(fixedText);
            newText.append(getText().substring(end));
            if (isValid(newText)) {
                super.insertText(start, end, fixedText);
            }
        }
    
        @Override
        public void replaceText(IndexRange range, String text) {
            replaceText(range.getStart(), range.getEnd(), text);
        }
    
    
        private String fixText(String text) {
            DecimalFormatSymbols symbols = DecimalFormatSymbols.getInstance();
            return text.replaceAll("\\.", Character.toString(symbols.getDecimalSeparator()));
        }
    
        private boolean isValid(String text) {
            // check that the text is a valid numeric representation (or partial representation)
        }
    }