mirror of
https://github.com/fluxerapp/fluxer
synced 2026-10-18 21:45:31 +09:00
fix(i18n): review translations and fix i18n library misuse (#2750)
This commit is contained in:
1 parent
78f783f0c4
commit
8cb097f954
575 files changed
+192671
-160540
No files matched your search
@@ -155,8 +155,8 @@ const KeybindEditorPopout: React.FC<KeybindEditorPopoutProps> = ({
|
||||
const [recording, setRecording] = useState(true);
|
||||
const [previewCombo, setPreviewCombo] = useState<KeyCombo | null>(null);
|
||||
const currentCombo = previewCombo ?? value;
|
||||
const displayValue = formatKeyCombo(currentCombo);
|
||||
const defaultDisplayValue = defaultValue ? formatKeyCombo(defaultValue) : null;
|
||||
const displayValue = formatKeyCombo(i18n, currentCombo);
|
||||
const defaultDisplayValue = defaultValue ? formatKeyCombo(i18n, defaultValue) : null;
|
||||
const currentHasValue = !!(
|
||||
currentCombo?.key ||
|
||||
currentCombo?.code ||
|
||||
@@ -490,7 +490,7 @@ export const KeybindRecorder: React.FC<KeybindRecorderProps> = ({
|
||||
const triggerRef = useRef<HTMLButtonElement | null>(null);
|
||||
const isEmpty = !value?.key && !value?.code && value?.mouseButton === undefined && value?.gamepadButton === undefined;
|
||||
const hasValue = !isEmpty;
|
||||
const displayValue = formatKeyCombo(value);
|
||||
const displayValue = formatKeyCombo(i18n, value);
|
||||
return (
|
||||
<div
|
||||
className={clsx(styles.field, labelPlacement === 'inline' && styles.fieldInline)}
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
import * as Modal from '@app/features/app/components/dialogs/Modal';
|
||||
import {PRODUCT_NAME} from '@app/features/app/config/I18nDisplayConstants';
|
||||
import styles from '@app/features/input/components/modals/KeyboardModeIntroModal.module.css';
|
||||
import {SHIFT_KEY_LABEL} from '@app/features/input/utils/KeyboardUtils';
|
||||
import {SHIFT_KEY_DESCRIPTOR} from '@app/features/input/utils/KeyboardUtils';
|
||||
import * as ModalCommands from '@app/features/ui/commands/ModalCommands';
|
||||
import KeyboardMode from '@app/features/ui/state/KeyboardMode';
|
||||
import {isNativeMacOS} from '@app/features/ui/utils/NativeUtils';
|
||||
@@ -69,7 +69,7 @@ export function KeyboardModeIntroModal() {
|
||||
{i18n._(OR_DESCRIPTOR)}
|
||||
</span>
|
||||
<kbd className={styles.kbd} data-flx="input.keyboard-mode-intro-modal.kbd--2">
|
||||
{SHIFT_KEY_LABEL}
|
||||
{i18n._(SHIFT_KEY_DESCRIPTOR)}
|
||||
</kbd>
|
||||
<span className={styles.separator} data-flx="input.keyboard-mode-intro-modal.separator--2">
|
||||
+
|
||||
|
||||
+10
-14
@@ -3,16 +3,17 @@
|
||||
import * as Modal from '@app/features/app/components/dialogs/Modal';
|
||||
import {useTextOverflow} from '@app/features/app/hooks/useTextOverflow';
|
||||
import styles from '@app/features/input/components/modals/KeyboardShortcutsCheatsheetModal.module.css';
|
||||
import Keybind, {type KeybindConfig, type KeybindSection, type KeyCombo} from '@app/features/input/state/InputKeybind';
|
||||
import {formatKeyCombo} from '@app/features/input/utils/KeybindUtils';
|
||||
import Keybind, {type KeybindConfig, type KeybindSection} from '@app/features/input/state/InputKeybind';
|
||||
import {formatKeyComboParts} from '@app/features/input/utils/KeybindUtils';
|
||||
import {Tooltip} from '@app/features/ui/tooltip/Tooltip';
|
||||
import {
|
||||
chipsForDefaultEntry,
|
||||
DEFAULT_KEYBIND_SECTIONS,
|
||||
sortBySectionDisplayOrder,
|
||||
} from '@app/features/user/components/modals/tabs/keybinds_tab/shared';
|
||||
import type {I18n} from '@lingui/core';
|
||||
import {msg} from '@lingui/core/macro';
|
||||
import {Trans, useLingui} from '@lingui/react/macro';
|
||||
import {useLingui} from '@lingui/react/macro';
|
||||
import {observer} from 'mobx-react-lite';
|
||||
import type React from 'react';
|
||||
import {useMemo, useRef} from 'react';
|
||||
@@ -56,18 +57,13 @@ interface CheatsheetShortcut {
|
||||
chipGroups: Array<Array<string>>;
|
||||
}
|
||||
|
||||
function chipsForCombo(combo: KeyCombo): Array<string> {
|
||||
const formatted = formatKeyCombo(combo);
|
||||
return formatted ? formatted.split(' + ') : [];
|
||||
}
|
||||
|
||||
function getShortcutChipGroups(entry: KeybindConfig, hasCustomBinding: boolean): Array<Array<string>> {
|
||||
function getShortcutChipGroups(i18n: I18n, entry: KeybindConfig, hasCustomBinding: boolean): Array<Array<string>> {
|
||||
if (!hasCustomBinding) {
|
||||
const chips = chipsForDefaultEntry(entry);
|
||||
const chips = chipsForDefaultEntry(i18n, entry);
|
||||
return chips.length > 0 ? [chips] : [];
|
||||
}
|
||||
return Keybind.getActiveCombosForAction(entry.action)
|
||||
.map(chipsForCombo)
|
||||
.map((combo) => formatKeyComboParts(i18n, combo))
|
||||
.filter((chips) => chips.length > 0);
|
||||
}
|
||||
|
||||
@@ -97,7 +93,7 @@ const ShortcutChipGroups: React.FC<{groups: Array<Array<string>>}> = ({groups})
|
||||
key={`${chipIndex}-${chip}`}
|
||||
data-flx="input.keyboard-shortcuts-cheatsheet-modal.chip"
|
||||
>
|
||||
{chip === 'ANY KEY' ? <Trans>Any key</Trans> : chip}
|
||||
{chip}
|
||||
</kbd>
|
||||
))}
|
||||
</div>
|
||||
@@ -138,7 +134,7 @@ export const KeyboardShortcutsCheatsheetModal = observer(() => {
|
||||
for (const sectionId of DEFAULT_KEYBIND_SECTIONS) {
|
||||
const entries = defaults.filter((entry) => entry.section === sectionId && !entry.hideFromDefaults);
|
||||
for (const entry of sortBySectionDisplayOrder(sectionId, entries)) {
|
||||
const chipGroups = getShortcutChipGroups(entry, customActions.has(entry.action));
|
||||
const chipGroups = getShortcutChipGroups(i18n, entry, customActions.has(entry.action));
|
||||
if (chipGroups.length === 0) continue;
|
||||
result[sectionId].push({
|
||||
action: entry.action,
|
||||
@@ -148,7 +144,7 @@ export const KeyboardShortcutsCheatsheetModal = observer(() => {
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}, [customActions, defaults]);
|
||||
}, [customActions, defaults, i18n, i18n.locale]);
|
||||
return (
|
||||
<Modal.Root size="xlarge" data-flx="input.keyboard-shortcuts-cheatsheet-modal.modal-root">
|
||||
<Modal.ScreenReaderLabel
|
||||
|
||||
@@ -1,7 +1,10 @@
|
||||
// SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
|
||||
import type {KeyCombo} from '@app/features/input/state/InputKeybind';
|
||||
import {SHIFT_KEY_LABEL} from '@app/features/input/utils/KeyboardUtils';
|
||||
import {SHIFT_KEY_DESCRIPTOR} from '@app/features/input/utils/KeyboardUtils';
|
||||
|
||||
import type {I18n, MessageDescriptor} from '@lingui/core';
|
||||
import {msg} from '@lingui/core/macro';
|
||||
|
||||
const isMac = () => /Mac|iPod|iPhone|iPad/.test(navigator.platform);
|
||||
const CONTROL_KEY_SYMBOL = '⌃';
|
||||
@@ -31,163 +34,196 @@ export function isPrimaryKeyAlreadyRepresentedByModifier(
|
||||
return false;
|
||||
}
|
||||
|
||||
export function formatMouseButton(button: number): string {
|
||||
switch (button) {
|
||||
case 0:
|
||||
return 'Mouse Left';
|
||||
case 1:
|
||||
return 'Mouse Middle';
|
||||
case 2:
|
||||
return 'Mouse Right';
|
||||
case 3:
|
||||
return 'Mouse Back';
|
||||
case 4:
|
||||
return 'Mouse Forward';
|
||||
default:
|
||||
return `Mouse ${button}`;
|
||||
}
|
||||
const CTRL_DESCRIPTOR = msg({message: 'Ctrl'});
|
||||
const WIN_DESCRIPTOR = msg({message: 'Win'});
|
||||
const ALT_DESCRIPTOR = msg({message: 'Alt'});
|
||||
const SPACE_DESCRIPTOR = msg({message: 'Space'});
|
||||
const ENTER_DESCRIPTOR = msg({message: 'Enter'});
|
||||
const ESC_DESCRIPTOR = msg({message: 'Esc'});
|
||||
const TAB_DESCRIPTOR = msg({message: 'Tab'});
|
||||
const MOUSE_BUTTON_DESCRIPTOR = msg({message: 'Mouse button {button}', context: 'mouse-button-name'});
|
||||
const GAMEPAD_BUTTON_DESCRIPTOR = msg({message: 'Gamepad button {button}', context: 'gamepad-button-name'});
|
||||
const LEFT_KEY_DESCRIPTOR = msg({message: 'Left {keyName}', context: 'keyboard-key-name'});
|
||||
const RIGHT_KEY_DESCRIPTOR = msg({message: 'Right {keyName}', context: 'keyboard-key-name'});
|
||||
const NUMPAD_KEY_DESCRIPTOR = msg({message: 'Numpad {keyName}', context: 'keyboard-key-name'});
|
||||
const LAUNCH_APP_DESCRIPTOR = msg({message: 'Launch app {appNumber}', context: 'keyboard-key-name'});
|
||||
const MOUSE_BUTTON_DESCRIPTORS: Partial<Record<number, MessageDescriptor>> = {
|
||||
0: msg({message: 'Left mouse button', context: 'mouse-button-name'}),
|
||||
1: msg({message: 'Middle mouse button', context: 'mouse-button-name'}),
|
||||
2: msg({message: 'Right mouse button', context: 'mouse-button-name'}),
|
||||
3: msg({message: 'Mouse back button', context: 'mouse-button-name'}),
|
||||
4: msg({message: 'Mouse forward button', context: 'mouse-button-name'}),
|
||||
};
|
||||
const GAMEPAD_BUTTON_DESCRIPTORS: Partial<Record<number, MessageDescriptor>> = {
|
||||
0: msg({message: 'Gamepad A / Cross', context: 'gamepad-button-name'}),
|
||||
1: msg({message: 'Gamepad B / Circle', context: 'gamepad-button-name'}),
|
||||
2: msg({message: 'Gamepad X / Square', context: 'gamepad-button-name'}),
|
||||
3: msg({message: 'Gamepad Y / Triangle', context: 'gamepad-button-name'}),
|
||||
4: msg({message: 'Gamepad left bumper', context: 'gamepad-button-name'}),
|
||||
5: msg({message: 'Gamepad right bumper', context: 'gamepad-button-name'}),
|
||||
6: msg({message: 'Gamepad left trigger', context: 'gamepad-button-name'}),
|
||||
7: msg({message: 'Gamepad right trigger', context: 'gamepad-button-name'}),
|
||||
8: msg({message: 'Gamepad Back / Select', context: 'gamepad-button-name'}),
|
||||
9: msg({message: 'Gamepad Start / Options', context: 'gamepad-button-name'}),
|
||||
10: msg({message: 'Gamepad left stick click', context: 'gamepad-button-name'}),
|
||||
11: msg({message: 'Gamepad right stick click', context: 'gamepad-button-name'}),
|
||||
12: msg({message: 'Gamepad D-pad up', context: 'gamepad-button-name'}),
|
||||
13: msg({message: 'Gamepad D-pad down', context: 'gamepad-button-name'}),
|
||||
14: msg({message: 'Gamepad D-pad left', context: 'gamepad-button-name'}),
|
||||
15: msg({message: 'Gamepad D-pad right', context: 'gamepad-button-name'}),
|
||||
16: msg({message: 'Gamepad Guide', context: 'gamepad-button-name'}),
|
||||
};
|
||||
const KEY_DESCRIPTORS: Partial<Record<string, MessageDescriptor>> = {
|
||||
PageUp: msg({message: 'Page up', context: 'keyboard-key-name'}),
|
||||
PageDown: msg({message: 'Page down', context: 'keyboard-key-name'}),
|
||||
PrintScreen: msg({message: 'Print screen', context: 'keyboard-key-name'}),
|
||||
ScrollLock: msg({message: 'Scroll lock', context: 'keyboard-key-name'}),
|
||||
Pause: msg({message: 'Pause / Break', context: 'keyboard-key-name'}),
|
||||
Break: msg({message: 'Pause / Break', context: 'keyboard-key-name'}),
|
||||
NumLock: msg({message: 'Num lock', context: 'keyboard-key-name'}),
|
||||
ContextMenu: msg({message: 'Menu', context: 'keyboard-key-name'}),
|
||||
Home: msg({message: 'Home', context: 'keyboard-key-name'}),
|
||||
End: msg({message: 'End', context: 'keyboard-key-name'}),
|
||||
Insert: msg({message: 'Insert', context: 'keyboard-key-name'}),
|
||||
Delete: msg({message: 'Delete', context: 'keyboard-key-name'}),
|
||||
AudioVolumeMute: msg({message: 'Volume mute', context: 'keyboard-key-name'}),
|
||||
AudioVolumeDown: msg({message: 'Volume down', context: 'keyboard-key-name'}),
|
||||
AudioVolumeUp: msg({message: 'Volume up', context: 'keyboard-key-name'}),
|
||||
MediaTrackNext: msg({message: 'Media next', context: 'keyboard-key-name'}),
|
||||
MediaTrackPrevious: msg({message: 'Media previous', context: 'keyboard-key-name'}),
|
||||
MediaStop: msg({message: 'Media stop', context: 'keyboard-key-name'}),
|
||||
MediaPlayPause: msg({message: 'Media play/pause', context: 'keyboard-key-name'}),
|
||||
BrowserBack: msg({message: 'Browser back', context: 'keyboard-key-name'}),
|
||||
BrowserForward: msg({message: 'Browser forward', context: 'keyboard-key-name'}),
|
||||
BrowserRefresh: msg({message: 'Browser refresh', context: 'keyboard-key-name'}),
|
||||
BrowserStop: msg({message: 'Browser stop', context: 'keyboard-key-name'}),
|
||||
BrowserSearch: msg({message: 'Browser search', context: 'keyboard-key-name'}),
|
||||
BrowserFavorites: msg({message: 'Browser favorites', context: 'keyboard-key-name'}),
|
||||
BrowserHome: msg({message: 'Browser home', context: 'keyboard-key-name'}),
|
||||
LaunchMail: msg({message: 'Launch mail', context: 'keyboard-key-name'}),
|
||||
LaunchMediaPlayer: msg({message: 'Launch media', context: 'keyboard-key-name'}),
|
||||
Power: msg({message: 'Power', context: 'keyboard-key-name'}),
|
||||
Sleep: msg({message: 'Sleep', context: 'keyboard-key-name'}),
|
||||
WakeUp: msg({message: 'Wake up', context: 'keyboard-key-name'}),
|
||||
Convert: msg({message: 'Convert', context: 'keyboard-key-name'}),
|
||||
NonConvert: msg({message: 'Non-convert', context: 'keyboard-key-name'}),
|
||||
KanaMode: msg({message: 'Kana', context: 'keyboard-key-name'}),
|
||||
};
|
||||
|
||||
export function formatMouseButton(i18n: I18n, button: number): string {
|
||||
const descriptor = MOUSE_BUTTON_DESCRIPTORS[button];
|
||||
return descriptor ? i18n._(descriptor) : i18n._(MOUSE_BUTTON_DESCRIPTOR, {button});
|
||||
}
|
||||
|
||||
export function formatGamepadButton(button: number): string {
|
||||
const map: Record<number, string> = {
|
||||
0: 'A / Cross',
|
||||
1: 'B / Circle',
|
||||
2: 'X / Square',
|
||||
3: 'Y / Triangle',
|
||||
4: 'Left Bumper',
|
||||
5: 'Right Bumper',
|
||||
6: 'Left Trigger',
|
||||
7: 'Right Trigger',
|
||||
8: 'Back / Select',
|
||||
9: 'Start / Options',
|
||||
10: 'Left Stick Click',
|
||||
11: 'Right Stick Click',
|
||||
12: 'D-Pad Up',
|
||||
13: 'D-Pad Down',
|
||||
14: 'D-Pad Left',
|
||||
15: 'D-Pad Right',
|
||||
16: 'Guide',
|
||||
};
|
||||
const label = map[button];
|
||||
return `Gamepad ${label ?? `Button ${button}`}`;
|
||||
export function formatGamepadButton(i18n: I18n, button: number): string {
|
||||
const descriptor = GAMEPAD_BUTTON_DESCRIPTORS[button];
|
||||
return descriptor ? i18n._(descriptor) : i18n._(GAMEPAD_BUTTON_DESCRIPTOR, {button});
|
||||
}
|
||||
|
||||
function formatBothSidesModifier(combo: KeyCombo): string | null {
|
||||
let label: string | null = null;
|
||||
if (combo.shift) label = SHIFT_KEY_LABEL;
|
||||
else if (combo.ctrl) label = isMac() ? CONTROL_KEY_SYMBOL : 'Ctrl';
|
||||
else if (combo.ctrlOrMeta) label = isMac() ? '⌘' : 'Ctrl';
|
||||
else if (combo.alt) label = isMac() ? '⌥' : 'Alt';
|
||||
else if (combo.meta) label = isMac() ? '⌘' : 'Win';
|
||||
if (!label) return null;
|
||||
return `Left ${label} + Right ${label}`;
|
||||
function formatBothSidesModifier(i18n: I18n, combo: KeyCombo): Array<string> | null {
|
||||
let keyName: string | null = null;
|
||||
if (combo.shift) keyName = i18n._(SHIFT_KEY_DESCRIPTOR);
|
||||
else if (combo.ctrl) keyName = isMac() ? CONTROL_KEY_SYMBOL : i18n._(CTRL_DESCRIPTOR);
|
||||
else if (combo.ctrlOrMeta) keyName = isMac() ? '⌘' : i18n._(CTRL_DESCRIPTOR);
|
||||
else if (combo.alt) keyName = isMac() ? '⌥' : i18n._(ALT_DESCRIPTOR);
|
||||
else if (combo.meta) keyName = isMac() ? '⌘' : i18n._(WIN_DESCRIPTOR);
|
||||
if (!keyName) return null;
|
||||
return [i18n._(LEFT_KEY_DESCRIPTOR, {keyName}), i18n._(RIGHT_KEY_DESCRIPTOR, {keyName})];
|
||||
}
|
||||
|
||||
export function formatKeyCombo(combo: KeyCombo): string {
|
||||
export function formatKeyCombo(i18n: I18n, combo: KeyCombo): string {
|
||||
return formatKeyComboParts(i18n, combo).join(' + ');
|
||||
}
|
||||
|
||||
export function formatKeyComboParts(i18n: I18n, combo: KeyCombo): Array<string> {
|
||||
const parts: Array<string> = [];
|
||||
if (combo.modifierOnly && combo.bothSides) {
|
||||
const formatted = formatBothSidesModifier(combo);
|
||||
const formatted = formatBothSidesModifier(i18n, combo);
|
||||
if (formatted) return formatted;
|
||||
}
|
||||
if (combo.ctrl) {
|
||||
parts.push(isMac() ? CONTROL_KEY_SYMBOL : 'Ctrl');
|
||||
parts.push(isMac() ? CONTROL_KEY_SYMBOL : i18n._(CTRL_DESCRIPTOR));
|
||||
} else if (combo.ctrlOrMeta) {
|
||||
parts.push(isMac() ? '⌘' : 'Ctrl');
|
||||
parts.push(isMac() ? '⌘' : i18n._(CTRL_DESCRIPTOR));
|
||||
}
|
||||
if (combo.meta && !combo.ctrlOrMeta) {
|
||||
parts.push(isMac() ? '⌘' : 'Win');
|
||||
parts.push(isMac() ? '⌘' : i18n._(WIN_DESCRIPTOR));
|
||||
}
|
||||
if (combo.shift) {
|
||||
parts.push(SHIFT_KEY_LABEL);
|
||||
parts.push(i18n._(SHIFT_KEY_DESCRIPTOR));
|
||||
}
|
||||
if (combo.alt) parts.push(isMac() ? '⌥' : 'Alt');
|
||||
if (combo.alt) parts.push(isMac() ? '⌥' : i18n._(ALT_DESCRIPTOR));
|
||||
if (combo.mouseButton !== undefined && combo.mouseButton !== null) {
|
||||
parts.push(formatMouseButton(combo.mouseButton));
|
||||
return parts.join(' + ');
|
||||
parts.push(formatMouseButton(i18n, combo.mouseButton));
|
||||
return parts;
|
||||
}
|
||||
if (combo.gamepadButton !== undefined && combo.gamepadButton !== null) {
|
||||
parts.push(formatGamepadButton(combo.gamepadButton));
|
||||
return parts.join(' + ');
|
||||
parts.push(formatGamepadButton(i18n, combo.gamepadButton));
|
||||
return parts;
|
||||
}
|
||||
const rawKey = getPrimaryKeyForComboDisplay(combo);
|
||||
if (
|
||||
isPrimaryKeyAlreadyRepresentedByModifier(combo) ||
|
||||
(combo.modifierOnly && isModifierKeyName(rawKey) && parts.length > 0)
|
||||
) {
|
||||
return parts.join(' + ');
|
||||
return parts;
|
||||
}
|
||||
const primarySymbol = formatPrimaryKeySymbol(rawKey);
|
||||
parts.push(primarySymbol);
|
||||
return parts.join(' + ');
|
||||
const primarySymbol = formatPrimaryKeySymbol(i18n, rawKey);
|
||||
if (primarySymbol) parts.push(primarySymbol);
|
||||
return parts;
|
||||
}
|
||||
|
||||
function formatPrimaryKeySymbol(rawKey: string): string {
|
||||
if (rawKey === ' ' || rawKey === 'Spacebar') return 'Spacebar';
|
||||
if (isShiftKeyName(rawKey)) return SHIFT_KEY_LABEL;
|
||||
function formatNumpadKey(i18n: I18n, rawKey: string): string | null {
|
||||
let keyName: string;
|
||||
if (/^Numpad[0-9]$/.test(rawKey)) {
|
||||
keyName = rawKey.slice(6);
|
||||
} else if (rawKey === 'NumpadEnter') {
|
||||
keyName = isMac() ? '⏎' : i18n._(ENTER_DESCRIPTOR);
|
||||
} else {
|
||||
const symbols: Partial<Record<string, string>> = {
|
||||
NumpadDecimal: '.',
|
||||
NumpadAdd: '+',
|
||||
NumpadSubtract: '-',
|
||||
NumpadMultiply: '*',
|
||||
NumpadDivide: '/',
|
||||
NumpadEqual: '=',
|
||||
NumpadComma: ',',
|
||||
};
|
||||
const symbol = Object.hasOwn(symbols, rawKey) ? symbols[rawKey] : undefined;
|
||||
if (!symbol) return null;
|
||||
keyName = symbol;
|
||||
}
|
||||
return i18n._(NUMPAD_KEY_DESCRIPTOR, {keyName});
|
||||
}
|
||||
|
||||
export function formatPrimaryKeySymbol(i18n: I18n, rawKey: string): string {
|
||||
if (rawKey === ' ' || rawKey === 'Spacebar' || rawKey === 'Space') return i18n._(SPACE_DESCRIPTOR);
|
||||
if (isShiftKeyName(rawKey)) return i18n._(SHIFT_KEY_DESCRIPTOR);
|
||||
if (isControlKeyName(rawKey)) return isMac() ? CONTROL_KEY_SYMBOL : i18n._(CTRL_DESCRIPTOR);
|
||||
if (isAltKeyName(rawKey)) return isMac() ? '⌥' : i18n._(ALT_DESCRIPTOR);
|
||||
if (isMetaKeyName(rawKey)) return isMac() ? '⌘' : i18n._(WIN_DESCRIPTOR);
|
||||
if (rawKey === 'ArrowUp') return '▲';
|
||||
if (rawKey === 'ArrowDown') return '▼';
|
||||
if (rawKey === 'ArrowLeft') return '◀';
|
||||
if (rawKey === 'ArrowRight') return '▶';
|
||||
if (rawKey === 'PageUp') return 'Page Up';
|
||||
if (rawKey === 'PageDown') return 'Page Down';
|
||||
if (rawKey === 'Escape') return 'Esc';
|
||||
if (rawKey === 'PrintScreen') return 'Print Screen';
|
||||
if (rawKey === 'ScrollLock') return 'Scroll Lock';
|
||||
if (rawKey === 'Pause' || rawKey === 'Break') return 'Break';
|
||||
if (rawKey === 'NumLock') return 'Num Lock';
|
||||
if (rawKey === 'ContextMenu') return 'Menu';
|
||||
if (rawKey === 'Escape') return i18n._(ESC_DESCRIPTOR);
|
||||
if (rawKey === 'Backspace') return '⌫';
|
||||
if (rawKey === 'Enter' || rawKey === 'Return') return isMac() ? '⏎' : 'Enter';
|
||||
if (rawKey === 'Tab') return 'Tab';
|
||||
if (rawKey === 'Enter' || rawKey === 'Return') return isMac() ? '⏎' : i18n._(ENTER_DESCRIPTOR);
|
||||
if (rawKey === 'Tab') return i18n._(TAB_DESCRIPTOR);
|
||||
if (rawKey === 'Backquote' || rawKey === '`') return '`';
|
||||
if (rawKey === '[' || rawKey === ']') return rawKey;
|
||||
if (/^Key[A-Z]$/.test(rawKey)) {
|
||||
return rawKey.slice(3);
|
||||
if (/^Key[A-Z]$/.test(rawKey)) return rawKey.slice(3);
|
||||
if (/^Digit[0-9]$/.test(rawKey)) return rawKey.slice(5);
|
||||
const numpadKey = formatNumpadKey(i18n, rawKey);
|
||||
if (numpadKey) return numpadKey;
|
||||
if (rawKey === 'LaunchApp1' || rawKey === 'LaunchApp2') {
|
||||
return i18n._(LAUNCH_APP_DESCRIPTOR, {appNumber: rawKey.slice(-1)});
|
||||
}
|
||||
if (/^Digit[0-9]$/.test(rawKey)) {
|
||||
return rawKey.slice(5);
|
||||
}
|
||||
if (/^Numpad[0-9]$/.test(rawKey)) {
|
||||
return `Numpad ${rawKey.slice(6)}`;
|
||||
}
|
||||
if (rawKey === 'NumpadDecimal') return 'Numpad .';
|
||||
if (rawKey === 'NumpadAdd') return 'Numpad +';
|
||||
if (rawKey === 'NumpadSubtract') return 'Numpad -';
|
||||
if (rawKey === 'NumpadMultiply') return 'Numpad *';
|
||||
if (rawKey === 'NumpadDivide') return 'Numpad /';
|
||||
if (rawKey === 'NumpadEnter') return isMac() ? 'Numpad ⏎' : 'Numpad Enter';
|
||||
if (rawKey === 'NumpadEqual') return 'Numpad =';
|
||||
if (rawKey === 'NumpadComma') return 'Numpad ,';
|
||||
if (rawKey === 'AudioVolumeMute') return 'Volume Mute';
|
||||
if (rawKey === 'AudioVolumeDown') return 'Volume Down';
|
||||
if (rawKey === 'AudioVolumeUp') return 'Volume Up';
|
||||
if (rawKey === 'MediaTrackNext') return 'Media Next';
|
||||
if (rawKey === 'MediaTrackPrevious') return 'Media Previous';
|
||||
if (rawKey === 'MediaStop') return 'Media Stop';
|
||||
if (rawKey === 'MediaPlayPause') return 'Media Play/Pause';
|
||||
if (rawKey === 'BrowserBack') return 'Browser Back';
|
||||
if (rawKey === 'BrowserForward') return 'Browser Forward';
|
||||
if (rawKey === 'BrowserRefresh') return 'Browser Refresh';
|
||||
if (rawKey === 'BrowserStop') return 'Browser Stop';
|
||||
if (rawKey === 'BrowserSearch') return 'Browser Search';
|
||||
if (rawKey === 'BrowserFavorites') return 'Browser Favorites';
|
||||
if (rawKey === 'BrowserHome') return 'Browser Home';
|
||||
if (rawKey === 'LaunchMail') return 'Launch Mail';
|
||||
if (rawKey === 'LaunchMediaPlayer') return 'Launch Media';
|
||||
if (rawKey === 'LaunchApp1') return 'Launch App 1';
|
||||
if (rawKey === 'LaunchApp2') return 'Launch App 2';
|
||||
if (rawKey === 'Power') return 'Power';
|
||||
if (rawKey === 'Sleep') return 'Sleep';
|
||||
if (rawKey === 'WakeUp') return 'Wake Up';
|
||||
if (rawKey === 'Convert') return 'Convert';
|
||||
if (rawKey === 'NonConvert') return 'NonConvert';
|
||||
if (rawKey === 'KanaMode') return 'Kana';
|
||||
if (/^Lang[1-5]$/.test(rawKey)) return rawKey;
|
||||
const descriptor = Object.hasOwn(KEY_DESCRIPTORS, rawKey) ? KEY_DESCRIPTORS[rawKey] : undefined;
|
||||
if (descriptor) return i18n._(descriptor);
|
||||
if (rawKey === 'IntlBackslash') return 'Intl \\';
|
||||
if (rawKey === 'IntlRo') return 'Intl Ro';
|
||||
if (rawKey === 'IntlYen') return 'Intl Yen';
|
||||
if (rawKey.length === 1) {
|
||||
return rawKey.toUpperCase();
|
||||
}
|
||||
if (rawKey.length === 1) return rawKey.toUpperCase();
|
||||
return rawKey;
|
||||
}
|
||||
@@ -1,7 +1,9 @@
|
||||
// SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
|
||||
import {isKeyboardActivationKey} from '@app/features/input/utils/KeyboardUtils';
|
||||
import {describe, expect, it} from 'vitest';
|
||||
import {describe, expect, it, vi} from 'vitest';
|
||||
|
||||
vi.mock('@lingui/core/macro', () => ({msg: (descriptor: unknown) => descriptor}));
|
||||
|
||||
describe('KeyboardUtils', () => {
|
||||
it('recognizes common keyboard activation key values', () => {
|
||||
|
||||
@@ -1,8 +1,9 @@
|
||||
// SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
|
||||
import {msg} from '@lingui/core/macro';
|
||||
import type React from 'react';
|
||||
|
||||
export const SHIFT_KEY_LABEL = 'Shift';
|
||||
export const SHIFT_KEY_DESCRIPTOR = msg({message: 'Shift'});
|
||||
|
||||
export function isKeyboardActivationKey(key: string): boolean {
|
||||
return key === 'Enter' || key === ' ' || key === 'Space' || key === 'Spacebar';
|
||||
|
||||
Reference in new issue
Block a user