As a compassionate human, the fact that full-width alphanumeric characters (i.e. 'abc' or '123') produced by Japanese IME/keyboards don't work in virtually any software form fields (even in software created in Japan!) has always bothered the hell out of me. Being a software user is a miserable enough existence before you start piling on unintended consequences of the Western hegemony of consumer technology on top.
Since HTML 5 is new-ish and technologists are starting to come around to the idea
that users exist outside the English-speaking Bay Area, I figured they'd have considered full-width numeric
characters for the 'number' input type
(e.g. <input type="number"/>
). If you're not familiar, the type is super handy for providing client-side
validation "for free" by the browser. If someone types something else in there, the browser will bark at them.
Well, in my new app I'm encouraging users to practice Japanese with Japanese keyboards, and users started complaining that my number fields were broken.
I started digging into this in this little Tootstorm, and was disappointed (but not surprised) to find that the WHATWG specifies that only valid floating point numbers are accepted as valid by this field type. What I'd prefer to have happened is that this semantic form type would have specified that full-width numerals would be magically converted to parseable strings of numbers when provided as form data or to JavaScript code. Instead, the browser does the stupid thing and will report to the user that '123' not a number and Japanese users will be forced to retype it.
Making matters worse, layer after layer of well-intended protections to withhold invalid values via JavaScript made fixing my user's issue unnecessarily difficult.
I wound up having to give up on the number type field, falling back on text, and then writing my own custom validation logic and conversion from full-width numbers to parseable ascii (half-width) ones.
Here's the preact component that shook out:
const japanFriendlyNumberInput = (name, defaultValue) => {
return h('input', {id: name, name: name,
value: defaultValue,
type: 'text',
pattern: '[0-9]*',
required: true,
autocomplete: 'off',
onChange (e) {
const input = e.target
input.value = input.value.replace(/[\uFF10-\uFF19]/g, c =>
String.fromCharCode(c.charCodeAt(0) - 0xfee0)
)
input.setCustomValidity(/^\d+$/.test(input.value) ? '' :
'Please input a number')
}
})
}
Neat.