非拉丁字母键盘上的键盘快捷键/命令(JavaScript)



我想让键盘快捷键在尽可能多的键盘布局上工作,包括其他字母。

我知道,就拉丁键盘而言,最好使用KeyboardEvent.key属性——所以众所周知,用户按下了一个表示字母"的键;L";例如与键盘布局无关。这似乎与操作系统和其他应用程序的做法一致,因为我刚刚通过临时切换到Dvorak布局进行了测试。

我敢肯定,这种方法不会适用于非拉丁键盘,即西里尔字母。

我正在寻找一种处理其他字母的通用方法,而不必深入研究每种语言的本地化。

例如,如果我想对Ctrl+L(或Mac电脑的Cmd+L(进行操作,我希望它能在尽可能多的键盘布局上工作,即使这些布局没有字母L

我还想尊重最基本的操作系统命令:Ctrl+C、Ctrl+A、Ctrl+V、Ctrl+X-所以我很好奇操作系统是否以同样的方式执行,即在西里尔字母键盘上,粘贴操作是以Ctrl+(相当于西里尔字母中的V(的形式发生,还是取决于区域设置?

事实证明,大多数非拉丁键盘都打印有两个字母,拉丁字母和自己的字母,用户可以在布局之间切换。

因此,如果用户处于拉丁布局模式,KeyboardEvent.key应该开箱即用。问题是,当用户处于非拉丁模式时,键盘命令将不起作用,因为KeyboardEvent.key将是该字母表中的一个字母。

幸运的是,这些键盘大多遵循标准的Qwerty布局,因此KeyboardEvent.code可以作为参考拉丁字符的后备方案。

我创建了一个函数,如果检测到非拉丁字符,给定KeyboardEvent.keyKeyboardEvent.code应该回退到绝对的Qwerty代码值。

许可证:MIT

/**
* Gets key associated with a Keyboard event with a fallback to use absolute code value for
* non-Latin keyboard layouts.
*
* Most commonly non-Latin keyboards have 2 sets of alphabets printed and 2 modes to switch between
* them. The Latin mode usually follows the standard Qwerty layout so by falling back to use key
* codes, a keyboard command can work even though the layout is in non-Latin mode.
*
* Limitations:
* - some non-Latin layouts have a symbol on KeyQ which makes it impossible to distinguish it
* (without checking the entire layout) from Latin Dvorak layout, therefore KeyQ will not work for
* those
* - if the Latin layout mode is not Qwerty some of the mappings will not be correct
*
* @returns if `key` is a non-Latin letter (unicode >= 880) and `code` represents a letter or a
* digit on a Qwerty layout, it will return the corresponding letter (uppercase) or digit on a
* Qwerty layout. Otherwise it will return `key` (transformed to uppercase if it's a letter).
*
* License: MIT; Copyright 2021 Maciej Krawczyk
*/
function getLatinKey(key, code) {
if (key.length !== 1) {
return key;
}
const capitalHetaCode = 880;
const isNonLatin = key.charCodeAt(0) >= capitalHetaCode;
if (isNonLatin) {
if (code.indexOf('Key') === 0 && code.length === 4) { // i.e. 'KeyW'
return code.charAt(3);
}
if (code.indexOf('Digit') === 0 && code.length === 6) { // i.e. 'Digit7'
return code.charAt(5);
}
}
return key.toUpperCase();
}

示例用法:

document.addEventListener('keydown', (e) => {
if (e.ctrlKey && getLatinKey(e.key, e.code) === 'L') {
alert('Ctrl+L');
}
});

测试:

describe('getLatinKey', () => {
it('gets a Latin letter', () => {
expect(getLatinKey('A', 'irrelevant')).toBe('A');
});
it('gets a digit', () => {
expect(getLatinKey('0', 'irrelevant')).toBe('0');
});
it('transforms letters to uppercase', () => {
expect(getLatinKey('a', 'irrelevant')).toBe('A');
});
it('converts non-Latin letters to code values if available', () => {
expect(getLatinKey('β', 'KeyB')).toBe('B');
expect(getLatinKey('я', 'KeyZ')).toBe('Z');
// Not a real-world example, but it tests getting digits.
expect(getLatinKey('я', 'Digit1')).toBe('1');
});
it('does not convert non-Latin letters on special keys', () => {
expect(getLatinKey('ё', 'Backquote')).toBe('Ё');
});
it('does not convert Latin diacritics', () => {
expect(getLatinKey('ś', 'KeyS')).toBe('Ś');
expect(getLatinKey('ü', 'KeyU')).toBe('Ü');
expect(getLatinKey('ž', 'KeyZ')).toBe('Ž');
});
});

备选方案:

  1. 可以使用Keyboard.getLayoutMap()来确定布局是否是非拉丁的,这也可以使KeyQ工作。然而,它是一个实验性的API,不受Firefox和Safari的支持(可能永远不会,因为Firefox目前在隐私条款上拒绝了它——指纹(。这也是一个缺点,因为API不是同步的,如果需要的话,不可能在键盘事件上调用e.preventDefault()
  2. 在Electron应用程序中,keyboard-layoutnpm模块可以实现更高级的实现,而不会受到同样的限制

其他

至于复制、粘贴、撤消的操作系统快捷方式,它似乎是你所期望的(至少这是我在Mac操作系统上切换键盘布局后得到的印象(。

至于一些通用提示,请避免使用符号作为键盘快捷键。它们到处都是,甚至在拉丁语Qwerty布局中也是如此。

最新更新