【问题标题】:Keyboard shortcuts / commands on non-Latin keyboards (JavaScript)非拉丁键盘上的键盘快捷键/命令 (JavaScript)
【发布时间】:2022-01-09 16:12:57
【问题描述】:

我想让键盘快捷键适用于尽可能多的键盘布局,包括非拉丁键盘布局。

我知道,就拉丁键盘而言,最好使用KeyboardEvent.key 属性 - 因此,无论键盘布局如何,用户都知道用户按下了代表字母“L”的键。这似乎与操作系统和其他应用程序的做法一致,因为我只是通过暂时切换到 Dvorak 布局对其进行了测试。

我很确定这种方法不适用于非拉丁键盘,即 - 西里尔文。

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

例如,如果我想为 Ctrl+L(或 Mac 的 Cmd+L)设置一个操作,我希望它可以在尽可能多的键盘布局上工作,即使这些布局没有字母 L。在该键盘布局上获取与 L 等效的字符。

我还想尊重最基本的操作系统命令:Ctrl+C、Ctrl+A、Ctrl+V、Ctrl+X - 所以我很好奇操作系统是否以相同的方式执行此操作,即在西里尔字母键盘上,粘贴操作是按 Ctrl +(相当于西里尔文中的 V)出现还是取决于语言环境?

【问题讨论】:

    标签: javascript localization keyboard accessibility keyboard-shortcuts


    【解决方案1】:

    原来most non-Latin keyboards have 2 alphabets printed,拉丁语和它自己的 - 用户可以在布局之间切换。

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

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

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

    许可证:麻省理工学院

    /**
     * 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-layout npm 模块可以实现更高级的实现,而不会受到同样的限制。

    其他事情

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

    至于一些一般提示,请避免在键盘快捷键中使用符号。它们无处不在,甚至在拉丁 Qwerty 布局中也是如此。

    【讨论】:

      猜你喜欢
      • 2011-06-06
      • 2013-09-25
      • 2017-07-07
      • 1970-01-01
      • 2019-08-01
      • 1970-01-01
      • 2014-05-29
      • 1970-01-01
      相关资源
      最近更新 更多