-
+
+
+
+
+
+ kcAliasDefRegExp.test(line)
+ )) {
+ const [fullMatch, intlAlias, macroExpansion] =
+ kcAliasDefRegExp.exec(aliasDefinition);
+ const usAlias = translateToUS(macroExpansion, intl2us);
+ intl2us.set(intlAlias, usAlias);
+ }
+ return intl2us;
+}
+
+function parseComment(comment) {
+ // String.prototype.trim is not acceptable because it trims ALL whitespace characters;
+ // it doesn't limit itself to U+20 SPACE.
+ const trimSpaces = (str) => /^ *(.*?) *$/.exec(str)[1];
+ const clarificationIndex =
+ comment.indexOf('(') === -1 ? comment.length : comment.indexOf('(');
+ let [keysym, clarification] = [
+ comment.slice(0, clarificationIndex),
+ comment.slice(clarificationIndex)
+ ].map(trimSpaces);
+ if (keysym.length === 0 && clarification.length > 0) {
+ // There can't be a clarification without a keysym.
+ // Notably, this is what catches the case where the comment is just ")".
+ // Meaning that the keysym of this keycode is the opening parenthesis.
+ [keysym, clarification] = [clarification, keysym];
+ }
+ return [keysym, clarification];
+}
+
+function computeKcInfo(lines, intl2us) {
+ let kcInfo = new Map();
+ for (const aliasDefinition of lines.filter((line) =>
+ kcAliasDefRegExp.test(line)
+ )) {
+ const [fullMatch, intlAlias, macroExpansion, comment] =
+ kcAliasDefRegExp.exec(aliasDefinition);
+ const [keysym, clarification] = parseComment(comment);
+ let readableKeysym = keysym;
+ // This obscure-looking regular expression is used to detect unicode characters
+ // pertaining to the [Marking, non-spacing](https://www.fileformat.info/info/unicode/category/Mn/list.htm) general category.
+ // This category includes characters such as COMBINING HORN which can, surprisingly enough, combine with the (double) quote
+ // character used to start a string in the generated output, and thus ruin the entire syntax.
+ //
+ // For an example of a keymap_extras header containing those characters, check out keymap_bepo.h
+ const markingNonSpacingRegExp = /(\p{gc=Mn})/gu;
+ if (markingNonSpacingRegExp.test(keysym)) {
+ const dottedCircle = '\u25cc';
+ readableKeysym = keysym.replaceAll(
+ markingNonSpacingRegExp,
+ dottedCircle + '$1'
+ );
+ } else if (keysym.length === 1 && invisibleChar2readableName.has(keysym)) {
+ readableKeysym = invisibleChar2readableName.get(keysym);
+ }
+ if (keysym.length > 3) {
+ console.error(
+ `Warning: parsing the line below associated a keysym of length greater than 3 (${keysym}) to ${intlAlias} aka ${macroExpansion}. This may cause the key legend text to overflow.`
+ );
+ console.error(fullMatch);
+ }
+ // aka the hover tip
+ const title = (intlAlias + ' ' + clarification).trimEnd();
+ const usMacroExpansion = translateToUS(macroExpansion, intl2us);
+ kcInfo.set(usMacroExpansion, {
+ intlAlias,
+ clarification,
+ title,
+ keysym: readableKeysym,
+ name: readableKeysym
+ });
+ const shiftedKcRegExp = /^(?:S|LSFT)\(\w+\)$/;
+ if (shiftedKcRegExp.test(macroExpansion)) {
+ if (shiftedKc2shiftedAlias.has(usMacroExpansion)) {
+ kcInfo.set(
+ shiftedKc2shiftedAlias.get(usMacroExpansion),
+ kcInfo.get(usMacroExpansion)
+ );
+ }
+ const basicKc = extractBasicKc(macroExpansion);
+ const basicUSKc = translateToUS(basicKc, intl2us);
+ if (kcInfo.has(basicUSKc)) {
+ const basicKcInfo = kcInfo.get(basicUSKc);
+ basicKcInfo.name = readableKeysym + '\n' + basicKcInfo.name;
+ } else if (!basicKc.startsWith('KC_')) {
+ console.error('State of kcInfo:');
+ console.error(kcInfo);
+ throw new Error(
+ 'Encountered the definition of a shifted keycode before having read the definition of the corresponding basic keycode first, when reading the following line.\n' +
+ fullMatch
+ );
+ }
+ }
+ }
+ return kcInfo;
+}
+
+function stringify(usAlias, kcInfoObject) {
+ // Important to surround the usAlias with quotes because it may contain parentheses.
+ const stringified = `'${usAlias}': ${JSON.stringify(kcInfoObject, [
+ 'name',
+ 'title'
+ ])},`;
+ if (stringified.includes('undefined')) {
+ throw new Error(
+ `Failed to stringify the kcInfoObject associated to ${usAlias}:\n${stringified}`
+ );
+ }
+ return stringified;
+}
+
+function convertLine(line, kcInfo, intl2us) {
+ if (line.includes('clang-format off')) {
+ return 'export default {';
+ }
+
+ const copyrightRegExp = /(^.*)Copyright .*$/;
+ if (copyrightRegExp.test(line)) {
+ let [fullMatch, beforeCopyrightWord] = copyrightRegExp.exec(line);
+ return (
+ beforeCopyrightWord +
+ 'Copyright ' +
+ new Date().getFullYear() +
+ ' - Generated by convert_keymap_extras_header.js'
+ );
+ }
+
+ const nonDefineDirectiveRegExp = /^#\s*(?!define)/;
+ if (nonDefineDirectiveRegExp.test(line)) {
+ return '';
+ }
+
+ if (kcAliasDefRegExp.test(line)) {
+ let keycodeObjectLine = '';
+ let [fullMatch, intlAlias, macroExpansion, comment] =
+ kcAliasDefRegExp.exec(line);
+ const usMacroExpansion = translateToUS(macroExpansion, intl2us);
+ let kcInfoLine = stringify(usMacroExpansion, kcInfo.get(usMacroExpansion));
+ if (shiftedKc2shiftedAlias.has(usMacroExpansion)) {
+ const shiftedAlias = shiftedKc2shiftedAlias.get(usMacroExpansion);
+ kcInfoLine +=
+ '\n' + stringify(shiftedAlias, kcInfo.get(usMacroExpansion));
+ }
+ if (kcInfoLine.includes('undefined')) {
+ throw new Error(
+ 'Parsing error occured for the following line:\n' + fullMatch
+ );
+ }
+ return kcInfoLine;
+ }
+
+ return line;
+}
+
+function generateMissingANSISOkeys(kcInfo) {
+ const fallbackKc = kcInfo.has('KC_BSLS') ? 'KC_BSLS' : 'KC_NUHS';
+ const missingKcs = ['KC_BSLS', 'KC_NUHS', 'KC_NUBS'].filter(
+ (kc) => !kcInfo.has(kc)
+ );
+ let missingKcInfoLines = [];
+ for (const missingKc of missingKcs) {
+ baseKcInfo = kcInfo.get(fallbackKc);
+ shiftedKcInfo = kcInfo.get(`S(${fallbackKc})`) || baseKcInfo;
+ if (baseKcInfo === undefined || shiftedKcInfo === undefined) {
+ throw new Error(
+ 'The input file is missing a mapping to both KC_BSLS and KC_NUHS. At least one of them must be mapped to a locale alias!'
+ );
+ }
+ // Copy the KC_NUHS kc info object but replace the associated intl alias with KC_BSLS/KC_PIPE.
+ // Since it is pointless to repeat the US kc alias in the title, we only keep the clarification.
+ const missingKcInfo = {
+ ...baseKcInfo,
+ title: baseKcInfo.clarification
+ };
+ const missingShiftedKcInfo = {
+ ...shiftedKcInfo,
+ title: shiftedKcInfo.clarification
+ };
+ kcInfo.set(missingKc, missingKcInfo);
+ kcInfo.set(`S(${missingKc})`, missingShiftedKcInfo);
+ missingKcInfoLines.push(stringify(missingKc, missingKcInfo));
+ missingKcInfoLines.push(stringify(`S(${missingKc})`, missingShiftedKcInfo));
+ }
+ return missingKcInfoLines.join('\n');
+}
+
+function generateMissingShiftedAliasKcInfo(kcInfo) {
+ let ret = '';
+ for (const [shiftedKc, shiftedAlias] of shiftedKc2shiftedAlias.entries()) {
+ if (kcInfo.has(shiftedAlias)) {
+ continue;
+ }
+ const shiftedAliasKcInfo = {
+ ...(kcInfo.get(shiftedKc) || kcInfo.get(extractBasicKc(shiftedKc)))
+ };
+ // In virtue of `generateMissingANSISOkeys`, the intlAlias of the kc info object associated
+ // to S(KC_BSLS)/KC_PIPE is already a shifted keycode to begin with, no need to surround it
+ // with `S()`.
+ if (shiftedAlias === 'KC_PIPE') {
+ shiftedAliasKcInfo.title = shiftedAliasKcInfo.intlAlias;
+ } else {
+ shiftedAliasKcInfo.title = `S(${shiftedAliasKcInfo.intlAlias})`;
+ }
+ const letterRegExp = /^\p{L}$/u;
+ if (letterRegExp.test(shiftedAliasKcInfo.keysym)) {
+ shiftedAliasKcInfo.title += ` (capital ${shiftedAliasKcInfo.keysym})`;
+ }
+ ret += stringify(shiftedAlias, shiftedAliasKcInfo) + '\n';
+ }
+ return ret;
+}
+
+function generateSpaceCadetKcInfo(kc, kcInfo) {
+ const spaceCadetKeycodeRegExp = /([LR])([GASC])P([OC])/;
+ let [fullMatch, handedness, modifier, variant] =
+ spaceCadetKeycodeRegExp.exec(kc);
+ const table = new Map([
+ ['L', 'Left'],
+ ['R', 'Right'],
+ ['G', 'GUI'],
+ ['A', 'Alt'],
+ ['C', 'Control'],
+ ['S', 'Shift']
+ ]);
+ const tapKc = variant === 'C' ? 'S(KC_0)' : 'S(KC_9)';
+ const keysym = (kcInfo.get(tapKc) || kcInfo.get(extractBasicKc(tapKc)))
+ .keysym;
+ const spaceCadetInfo = {
+ name: `${handedness}${modifier} / ${keysym}`,
+ title: `${table.get(handedness)} ${table.get(
+ modifier
+ )} when held, ${keysym} when tapped`
+ };
+ return stringify(kc, spaceCadetInfo);
+}
+
+if (process.argv.length <= 1 || process.argv.at(-1).endsWith('.js')) {
+ throw new Error(
+ 'No input file given as argument! Make sure to specify the path to the keymap_extras header file you wish to convert when calling this script.'
+ );
+}
+
+fs.readFile(process.argv.at(-1), 'utf8', function (err, data) {
+ if (err) {
+ console.error(err.stack);
+ } else {
+ const fileLines = data.replaceAll('(backslash)', '\\').split(/\r\n|\r|\n/);
+ let intl2us = computeIntl2US(fileLines);
+ const kcInfo = computeKcInfo(fileLines, intl2us);
+ if (kcInfo.size === 0) {
+ throw new Error(
+ 'No keycode mappings found! Make sure that the input file contains lines in the format of `#define // `.'
+ );
+ }
+ let convertedLines = fileLines.map((line) =>
+ convertLine(line, kcInfo, intl2us)
+ );
+ console.log(convertedLines.join('\n'));
+ console.log('/* Other keys */');
+ console.log(generateMissingANSISOkeys(kcInfo));
+ console.log(generateMissingShiftedAliasKcInfo(kcInfo));
+ spaceCadetKeycodes = [
+ 'KC_LSPO',
+ 'KC_RSPC',
+ 'KC_LCPO',
+ 'KC_RCPC',
+ 'KC_LAPO',
+ 'KC_RAPC'
+ ];
+ for (const spaceCadetKc of spaceCadetKeycodes) {
+ console.log(generateSpaceCadetKcInfo(spaceCadetKc, kcInfo));
+ }
+ console.log('');
+ const grvKeysym = kcInfo.get('KC_GRV').keysym;
+ const tildeKeysym = kcInfo.has('S(KC_GRV)')
+ ? kcInfo.get('S(KC_GRV)').keysym
+ : grvKeysym;
+ if (tildeKeysym !== grvKeysym) {
+ console.log(
+ stringify('QK_GESC', {
+ name: `${grvKeysym}/${tildeKeysym}\nEsc`,
+ title: `Esc normally, but ${grvKeysym} when GUI is active or ${tildeKeysym} when Shift is active`
+ })
+ );
+ } else {
+ console.log(
+ stringify('QK_GESC', {
+ name: `${grvKeysym}\nEsc`,
+ title: `Esc normally, but ${grvKeysym} when Shift or GUI is active`
+ })
+ );
+ }
+ console.log('}');
+ }
+});
diff --git a/src/i18n/keymap_extras/index.js b/src/i18n/keymap_extras/index.js
new file mode 100644
index 0000000000..bffb702c3d
--- /dev/null
+++ b/src/i18n/keymap_extras/index.js
@@ -0,0 +1,60 @@
+import us from './keymap_us';
+import uk from './keymap_uk';
+import german from './keymap_german';
+import russian from './keymap_russian';
+
+/* ==Template==
+ * keymap_: {
+ *
+ * prefix: (string) 2-letter code,
+ *
+ * sendstring: (string) The name of the associated sendstring header file if it exists.
+ * This is important for the "host_language" field of keymap.json in case text macros are used.
+ *
+ * isANSI: (boolean) True if the layout is ANSI, false if not.
+ *
+ * locales: (array[string]) List of locales, as defined in RFC 5646, closely tied to this OS keyboard layout.
+ * This can be useful to guess the OS keyboard layout in use based on the user's preferred locale.
+ *
+ * keycodeLUT: (Object) An object mapping US keycode aliases like "KC_A" or "KC_DLR" or even
+ * "QK_GESC" to a keycode object containing properties such as "name" (aka the key legends to display) and
+ * "title" (aka the extra information shown when hovering over the key).
+ * }
+ */
+
+export default {
+ keymap_us: {
+ prefix: 'KC',
+ // Special scenario where sendstring is an empty string not because there is no associated
+ // sendstring header file but because this is the default.
+ sendstring: '',
+ isANSI: true,
+ locales: ['en-US', 'en'],
+ keycodeLUT: us
+ },
+ keymap_uk: {
+ prefix: 'UK',
+ sendstring: 'uk',
+ isANSI: false,
+ locales: ['en-GB', 'en'],
+ keycodeLUT: uk
+ },
+ keymap_german: {
+ // Note: qmk_firmware also contains a separate `keymap_german_mac_iso.h` header file,
+ // but its only difference with the `keymap_german.h` header is the AltGr/Option layer.
+ // The QMK Configurator doesn't show the AltGr legends anyways so there is no point in
+ // including the Mac ISO version.
+ prefix: 'DE',
+ sendstring: 'german',
+ isANSI: false,
+ locales: ['de-GE', 'de-AU', 'de'],
+ keycodeLUT: german
+ },
+ keymap_russian: {
+ prefix: 'RU',
+ sendstring: '',
+ isANSI: true,
+ locales: ['ru-RU', 'ru'],
+ keycodeLUT: russian
+ }
+};
diff --git a/src/i18n/keymap_extras/keymap_german.js b/src/i18n/keymap_extras/keymap_german.js
new file mode 100644
index 0000000000..b4dd9909c1
--- /dev/null
+++ b/src/i18n/keymap_extras/keymap_german.js
@@ -0,0 +1,189 @@
+/* Copyright 2022 - Generated by convert_keymap_extras_header.js
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+export default {
+ /*
+ * ┌───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───────┐
+ * │ ^ │ 1 │ 2 │ 3 │ 4 │ 5 │ 6 │ 7 │ 8 │ 9 │ 0 │ ß │ ´ │ │
+ * ├───┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─────┤
+ * │ │ Q │ W │ E │ R │ T │ Z │ U │ I │ O │ P │ Ü │ + │ │
+ * ├─────┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┐ │
+ * │ │ A │ S │ D │ F │ G │ H │ J │ K │ L │ Ö │ Ä │ # │ │
+ * ├────┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴───┴────┤
+ * │ │ < │ Y │ X │ C │ V │ B │ N │ M │ , │ . │ - │ │
+ * ├────┼───┴┬──┴─┬─┴───┴───┴───┴───┴───┴──┬┴───┼───┴┬────┬────┤
+ * │ │ │ │ │ │ │ │ │
+ * └────┴────┴────┴────────────────────────┴────┴────┴────┴────┘
+ */
+ // Row 1
+ KC_GRV: { name: '°\n^', title: 'DE_CIRC (dead)' },
+ KC_1: { name: '!\n1', title: 'DE_1' },
+ KC_2: { name: '"\n2', title: 'DE_2' },
+ KC_3: { name: '§\n3', title: 'DE_3' },
+ KC_4: { name: '$\n4', title: 'DE_4' },
+ KC_5: { name: '%\n5', title: 'DE_5' },
+ KC_6: { name: '&\n6', title: 'DE_6' },
+ KC_7: { name: '/\n7', title: 'DE_7' },
+ KC_8: { name: '(\n8', title: 'DE_8' },
+ KC_9: { name: ')\n9', title: 'DE_9' },
+ KC_0: { name: '=\n0', title: 'DE_0' },
+ KC_MINS: { name: '?\nß', title: 'DE_SS' },
+ KC_EQL: { name: '`\n´', title: 'DE_ACUT (dead)' },
+ // Row 2
+ KC_Q: { name: 'Q', title: 'DE_Q' },
+ KC_W: { name: 'W', title: 'DE_W' },
+ KC_E: { name: 'E', title: 'DE_E' },
+ KC_R: { name: 'R', title: 'DE_R' },
+ KC_T: { name: 'T', title: 'DE_T' },
+ KC_Y: { name: 'Z', title: 'DE_Z' },
+ KC_U: { name: 'U', title: 'DE_U' },
+ KC_I: { name: 'I', title: 'DE_I' },
+ KC_O: { name: 'O', title: 'DE_O' },
+ KC_P: { name: 'P', title: 'DE_P' },
+ KC_LBRC: { name: 'Ü', title: 'DE_UDIA' },
+ KC_RBRC: { name: '*\n+', title: 'DE_PLUS' },
+ // Row 3
+ KC_A: { name: 'A', title: 'DE_A' },
+ KC_S: { name: 'S', title: 'DE_S' },
+ KC_D: { name: 'D', title: 'DE_D' },
+ KC_F: { name: 'F', title: 'DE_F' },
+ KC_G: { name: 'G', title: 'DE_G' },
+ KC_H: { name: 'H', title: 'DE_H' },
+ KC_J: { name: 'J', title: 'DE_J' },
+ KC_K: { name: 'K', title: 'DE_K' },
+ KC_L: { name: 'L', title: 'DE_L' },
+ KC_SCLN: { name: 'Ö', title: 'DE_ODIA' },
+ KC_QUOT: { name: 'Ä', title: 'DE_ADIA' },
+ KC_NUHS: { name: "'\n#", title: 'DE_HASH' },
+ // Row 4
+ KC_NUBS: { name: '>\n<', title: 'DE_LABK' },
+ KC_Z: { name: 'Y', title: 'DE_Y' },
+ KC_X: { name: 'X', title: 'DE_X' },
+ KC_C: { name: 'C', title: 'DE_C' },
+ KC_V: { name: 'V', title: 'DE_V' },
+ KC_B: { name: 'B', title: 'DE_B' },
+ KC_N: { name: 'N', title: 'DE_N' },
+ KC_M: { name: 'M', title: 'DE_M' },
+ KC_COMM: { name: ';\n,', title: 'DE_COMM' },
+ KC_DOT: { name: ':\n.', title: 'DE_DOT' },
+ KC_SLSH: { name: '_\n-', title: 'DE_MINS' },
+
+ /* Shifted symbols
+ * ┌───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───────┐
+ * │ ° │ ! │ " │ § │ $ │ % │ & │ / │ ( │ ) │ = │ ? │ ` │ │
+ * ├───┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─────┤
+ * │ │ │ │ │ │ │ │ │ │ │ │ │ * │ │
+ * ├─────┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┐ │
+ * │ │ │ │ │ │ │ │ │ │ │ │ │ ' │ │
+ * ├────┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴───┴────┤
+ * │ │ > │ │ │ │ │ │ │ │ ; │ : │ _ │ │
+ * ├────┼───┴┬──┴─┬─┴───┴───┴───┴───┴───┴──┬┴───┼───┴┬────┬────┤
+ * │ │ │ │ │ │ │ │ │
+ * └────┴────┴────┴────────────────────────┴────┴────┴────┴────┘
+ */
+ // Row 1
+ 'S(KC_GRV)': { name: '°', title: 'DE_DEG' },
+ KC_TILD: { name: '°', title: 'DE_DEG' },
+ 'S(KC_1)': { name: '!', title: 'DE_EXLM' },
+ KC_EXLM: { name: '!', title: 'DE_EXLM' },
+ 'S(KC_2)': { name: '"', title: 'DE_DQUO' },
+ KC_AT: { name: '"', title: 'DE_DQUO' },
+ 'S(KC_3)': { name: '§', title: 'DE_SECT' },
+ KC_HASH: { name: '§', title: 'DE_SECT' },
+ 'S(KC_4)': { name: '$', title: 'DE_DLR' },
+ KC_DLR: { name: '$', title: 'DE_DLR' },
+ 'S(KC_5)': { name: '%', title: 'DE_PERC' },
+ KC_PERC: { name: '%', title: 'DE_PERC' },
+ 'S(KC_6)': { name: '&', title: 'DE_AMPR' },
+ KC_CIRC: { name: '&', title: 'DE_AMPR' },
+ 'S(KC_7)': { name: '/', title: 'DE_SLSH' },
+ KC_AMPR: { name: '/', title: 'DE_SLSH' },
+ 'S(KC_8)': { name: '(', title: 'DE_LPRN' },
+ KC_ASTR: { name: '(', title: 'DE_LPRN' },
+ 'S(KC_9)': { name: ')', title: 'DE_RPRN' },
+ KC_LPRN: { name: ')', title: 'DE_RPRN' },
+ 'S(KC_0)': { name: '=', title: 'DE_EQL' },
+ KC_RPRN: { name: '=', title: 'DE_EQL' },
+ 'S(KC_MINS)': { name: '?', title: 'DE_QUES' },
+ KC_UNDS: { name: '?', title: 'DE_QUES' },
+ 'S(KC_EQL)': { name: '`', title: 'DE_GRV (dead)' },
+ KC_PLUS: { name: '`', title: 'DE_GRV (dead)' },
+ // Row 2
+ 'S(KC_RBRC)': { name: '*', title: 'DE_ASTR' },
+ KC_RCBR: { name: '*', title: 'DE_ASTR' },
+ // Row 3
+ 'S(KC_NUHS)': { name: "'", title: 'DE_QUOT' },
+ // Row 4
+ 'S(KC_NUBS)': { name: '>', title: 'DE_RABK' },
+ 'S(KC_COMM)': { name: ';', title: 'DE_SCLN' },
+ KC_LT: { name: ';', title: 'DE_SCLN' },
+ 'S(KC_DOT)': { name: ':', title: 'DE_COLN' },
+ KC_GT: { name: ':', title: 'DE_COLN' },
+ 'S(KC_SLSH)': { name: '_', title: 'DE_UNDS' },
+ KC_QUES: { name: '_', title: 'DE_UNDS' },
+
+ /* AltGr symbols
+ * ┌───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───────┐
+ * │ │ │ ² │ ³ │ │ │ │ { │ [ │ ] │ } │ \ │ │ │
+ * ├───┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─────┤
+ * │ │ @ │ │ € │ │ │ │ │ │ │ │ │ ~ │ │
+ * ├─────┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┐ │
+ * │ │ │ │ │ │ │ │ │ │ │ │ │ │ │
+ * ├────┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴───┴────┤
+ * │ │ | │ │ │ │ │ │ │ µ │ │ │ │ │
+ * ├────┼───┴┬──┴─┬─┴───┴───┴───┴───┴───┴──┬┴───┼───┴┬────┬────┤
+ * │ │ │ │ │ │ │ │ │
+ * └────┴────┴────┴────────────────────────┴────┴────┴────┴────┘
+ */
+ // Row 1
+ 'ALGR(KC_2)': { name: '²', title: 'DE_SUP2' },
+ 'ALGR(KC_3)': { name: '³', title: 'DE_SUP3' },
+ 'ALGR(KC_7)': { name: '{', title: 'DE_LCBR' },
+ 'ALGR(KC_8)': { name: '[', title: 'DE_LBRC' },
+ 'ALGR(KC_9)': { name: ']', title: 'DE_RBRC' },
+ 'ALGR(KC_0)': { name: '}', title: 'DE_RCBR' },
+ 'ALGR(KC_MINS)': { name: '\\', title: 'DE_BSLS' },
+ // Row 2
+ 'ALGR(KC_Q)': { name: '@', title: 'DE_AT' },
+ 'ALGR(KC_E)': { name: '€', title: 'DE_EURO' },
+ 'ALGR(KC_RBRC)': { name: '~', title: 'DE_TILD' },
+ // Row 4
+ 'ALGR(KC_NUBS)': { name: '|', title: 'DE_PIPE' },
+ 'ALGR(KC_M)': { name: 'µ', title: 'DE_MICR' },
+
+ /* Other keys */
+ KC_BSLS: { name: "'\n#", title: '' },
+ 'S(KC_BSLS)': { name: "'", title: '' },
+ KC_LCBR: { name: 'Ü', title: 'S(DE_UDIA) (capital Ü)' },
+ KC_COLN: { name: 'Ö', title: 'S(DE_ODIA) (capital Ö)' },
+ KC_PIPE: { name: "'", title: 'DE_QUOT' },
+ KC_DQUO: { name: 'Ä', title: 'S(DE_ADIA) (capital Ä)' },
+
+ KC_LSPO: { name: 'LS / )', title: 'Left Shift when held, ) when tapped' },
+ KC_RSPC: { name: 'RS / =', title: 'Right Shift when held, = when tapped' },
+ KC_LCPO: { name: 'LC / )', title: 'Left Control when held, ) when tapped' },
+ KC_RCPC: { name: 'RC / =', title: 'Right Control when held, = when tapped' },
+ KC_LAPO: { name: 'LA / )', title: 'Left Alt when held, ) when tapped' },
+ KC_RAPC: { name: 'RA / =', title: 'Right Alt when held, = when tapped' },
+
+ QK_GESC: {
+ name: '^/°\nEsc',
+ title: 'Esc normally, but ^ when GUI is active or ° when Shift is active'
+ },
+
+ KC_LCTL: { name: 'Left Strg' },
+ KC_RCTL: { name: 'Right Strg' }
+};
diff --git a/src/i18n/keymap_extras/keymap_russian.js b/src/i18n/keymap_extras/keymap_russian.js
new file mode 100644
index 0000000000..6a77666a3f
--- /dev/null
+++ b/src/i18n/keymap_extras/keymap_russian.js
@@ -0,0 +1,168 @@
+/* Copyright 2022 - Generated by convert_keymap_extras_header.js
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+export default {
+ /*
+ * ┌───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───────┐
+ * │ Ё │ 1 │ 2 │ 3 │ 4 │ 5 │ 6 │ 7 │ 8 │ 9 │ 0 │ - │ = │ │
+ * ├───┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─────┤
+ * │ │ Й │ Ц │ У │ К │ Е │ Н │ Г │ Ш │ Щ │ З │ Х │ Ъ │ \ │
+ * ├─────┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴─────┤
+ * │ │ Ф │ Ы │ В │ А │ П │ Р │ О │ Л │ Д │ Ж │ Э │ │
+ * ├──────┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴────────┤
+ * │ │ Я │ Ч │ С │ М │ И │ Т │ Ь │ Б │ Ю │ . │ │
+ * ├────┬───┴┬──┴─┬─┴───┴───┴───┴───┴───┴──┬┴───┼───┴┬────┬────┤
+ * │ │ │ │ │ │ │ │ │
+ * └────┴────┴────┴────────────────────────┴────┴────┴────┴────┘
+ */
+ // Row 1
+ KC_GRV: { name: 'Ё', title: 'RU_YO' },
+ KC_1: { name: '!\n1', title: 'RU_1' },
+ KC_2: { name: '"\n2', title: 'RU_2' },
+ KC_3: { name: '№\n3', title: 'RU_3' },
+ KC_4: { name: ';\n4', title: 'RU_4' },
+ KC_5: { name: '%\n5', title: 'RU_5' },
+ KC_6: { name: ':\n6', title: 'RU_6' },
+ KC_7: { name: '?\n7', title: 'RU_7' },
+ KC_8: { name: '*\n8', title: 'RU_8' },
+ KC_9: { name: '(\n9', title: 'RU_9' },
+ KC_0: { name: ')\n0', title: 'RU_0' },
+ KC_MINS: { name: '_\n-', title: 'RU_MINS' },
+ KC_EQL: { name: '+\n=', title: 'RU_EQL' },
+ // Row 2
+ KC_Q: { name: 'Й', title: 'RU_SHTI' },
+ KC_W: { name: 'Ц', title: 'RU_TSE' },
+ KC_E: { name: 'У', title: 'RU_U' },
+ KC_R: { name: 'К', title: 'RU_KA' },
+ KC_T: { name: 'Е', title: 'RU_IE' },
+ KC_Y: { name: 'Н', title: 'RU_EN' },
+ KC_U: { name: 'Г', title: 'RU_GHE' },
+ KC_I: { name: 'Ш', title: 'RU_SHA' },
+ KC_O: { name: 'Щ', title: 'RU_SHCH' },
+ KC_P: { name: 'З', title: 'RU_ZE' },
+ KC_LBRC: { name: 'Х', title: 'RU_HA' },
+ KC_RBRC: { name: 'Ъ', title: 'RU_HARD' },
+ KC_BSLS: { name: '/\n\\', title: 'RU_BSLS' },
+ // Row 3
+ KC_A: { name: 'Ф', title: 'RU_EF' },
+ KC_S: { name: 'Ы', title: 'RU_YERU' },
+ KC_D: { name: 'В', title: 'RU_VE' },
+ KC_F: { name: 'А', title: 'RU_A' },
+ KC_G: { name: 'П', title: 'RU_PE' },
+ KC_H: { name: 'Р', title: 'RU_ER' },
+ KC_J: { name: 'О', title: 'RU_O' },
+ KC_K: { name: 'Л', title: 'RU_EL' },
+ KC_L: { name: 'Д', title: 'RU_DE' },
+ KC_SCLN: { name: 'Ж', title: 'RU_ZHE' },
+ KC_QUOT: { name: 'Э', title: 'RU_E' },
+ // Row 4
+ KC_Z: { name: 'Я', title: 'RU_YA' },
+ KC_X: { name: 'Ч', title: 'RU_CHE' },
+ KC_C: { name: 'С', title: 'RU_ES' },
+ KC_V: { name: 'М', title: 'RU_EM' },
+ KC_B: { name: 'И', title: 'RU_I' },
+ KC_N: { name: 'Т', title: 'RU_TE' },
+ KC_M: { name: 'Ь', title: 'RU_SOFT' },
+ KC_COMM: { name: 'Б', title: 'RU_BE' },
+ KC_DOT: { name: 'Ю', title: 'RU_YU' },
+ KC_SLSH: { name: ',\n.', title: 'RU_DOT' },
+
+ /* Shifted symbols
+ * ┌───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───────┐
+ * │ │ ! │ " │ № │ ; │ % │ : │ ? │ * │ ( │ ) │ _ │ + │ │
+ * ├───┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─────┤
+ * │ │ │ │ │ │ │ │ │ │ │ │ │ │ / │
+ * ├─────┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴─────┤
+ * │ │ │ │ │ │ │ │ │ │ │ │ │ │
+ * ├──────┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴────────┤
+ * │ │ │ │ │ │ │ │ │ │ │ , │ │
+ * ├────┬───┴┬──┴─┬─┴───┴───┴───┴───┴───┴──┬┴───┼───┴┬────┬────┤
+ * │ │ │ │ │ │ │ │ │
+ * └────┴────┴────┴────────────────────────┴────┴────┴────┴────┘
+ */
+ // Row 1
+ 'S(KC_1)': { name: '!', title: 'RU_EXLM' },
+ KC_EXLM: { name: '!', title: 'RU_EXLM' },
+ 'S(KC_2)': { name: '"', title: 'RU_DQUO' },
+ KC_AT: { name: '"', title: 'RU_DQUO' },
+ 'S(KC_3)': { name: '№', title: 'RU_NUM' },
+ KC_HASH: { name: '№', title: 'RU_NUM' },
+ 'S(KC_4)': { name: ';', title: 'RU_SCLN' },
+ KC_DLR: { name: ';', title: 'RU_SCLN' },
+ 'S(KC_5)': { name: '%', title: 'RU_PERC' },
+ KC_PERC: { name: '%', title: 'RU_PERC' },
+ 'S(KC_6)': { name: ':', title: 'RU_COLN' },
+ KC_CIRC: { name: ':', title: 'RU_COLN' },
+ 'S(KC_7)': { name: '?', title: 'RU_QUES' },
+ KC_AMPR: { name: '?', title: 'RU_QUES' },
+ 'S(KC_8)': { name: '*', title: 'RU_ASTR' },
+ KC_ASTR: { name: '*', title: 'RU_ASTR' },
+ 'S(KC_9)': { name: '(', title: 'RU_LPRN' },
+ KC_LPRN: { name: '(', title: 'RU_LPRN' },
+ 'S(KC_0)': { name: ')', title: 'RU_RPRN' },
+ KC_RPRN: { name: ')', title: 'RU_RPRN' },
+ 'S(KC_MINS)': { name: '_', title: 'RU_UNDS' },
+ KC_UNDS: { name: '_', title: 'RU_UNDS' },
+ 'S(KC_EQL)': { name: '+', title: 'RU_PLUS' },
+ KC_PLUS: { name: '+', title: 'RU_PLUS' },
+ // Row 2
+ 'S(KC_BSLS)': { name: '/', title: 'RU_SLSH' },
+ KC_PIPE: { name: '/', title: 'RU_SLSH' },
+ // Row 4
+ 'S(KC_SLSH)': { name: ',', title: 'RU_COMM' },
+ KC_QUES: { name: ',', title: 'RU_COMM' },
+
+ /* AltGr symbols
+ * ┌───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───────┐
+ * │ │ │ │ │ │ │ │ │ ₽ │ │ │ │ │ │
+ * ├───┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─────┤
+ * │ │ │ │ │ │ │ │ │ │ │ │ │ │ │
+ * ├─────┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴─────┤
+ * │ │ │ │ │ │ │ │ │ │ │ │ │ │
+ * ├──────┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴────────┤
+ * │ │ │ │ │ │ │ │ │ │ │ │ │
+ * ├────┬───┴┬──┴─┬─┴───┴───┴───┴───┴───┴──┬┴───┼───┴┬────┬────┤
+ * │ │ │ │ │ │ │ │ │
+ * └────┴────┴────┴────────────────────────┴────┴────┴────┴────┘
+ */
+ // Row 1
+ 'ALGR(KC_8)': { name: '₽', title: 'RU_RUBL' },
+
+ /* Other keys */
+ KC_NUHS: { name: '/\n\\', title: '' },
+ 'S(KC_NUHS)': { name: '/', title: '' },
+ KC_NUBS: { name: '/\n\\', title: '' },
+ 'S(KC_NUBS)': { name: '/', title: '' },
+ KC_TILD: { name: 'Ё', title: 'S(RU_YO) (capital Ё)' },
+ KC_LCBR: { name: 'Х', title: 'S(RU_HA) (capital Х)' },
+ KC_RCBR: { name: 'Ъ', title: 'S(RU_HARD) (capital Ъ)' },
+ KC_LT: { name: 'Б', title: 'S(RU_BE) (capital Б)' },
+ KC_GT: { name: 'Ю', title: 'S(RU_YU) (capital Ю)' },
+ KC_COLN: { name: 'Ж', title: 'S(RU_ZHE) (capital Ж)' },
+ KC_DQUO: { name: 'Э', title: 'S(RU_E) (capital Э)' },
+
+ KC_LSPO: { name: 'LS / (', title: 'Left Shift when held, ( when tapped' },
+ KC_RSPC: { name: 'RS / )', title: 'Right Shift when held, ) when tapped' },
+ KC_LCPO: { name: 'LC / (', title: 'Left Control when held, ( when tapped' },
+ KC_RCPC: { name: 'RC / )', title: 'Right Control when held, ) when tapped' },
+ KC_LAPO: { name: 'LA / (', title: 'Left Alt when held, ( when tapped' },
+ KC_RAPC: { name: 'RA / )', title: 'Right Alt when held, ) when tapped' },
+
+ QK_GESC: {
+ name: 'Ё\nEsc',
+ title: 'Esc normally, but Ё when Shift or GUI is active'
+ }
+};
diff --git a/src/i18n/keymap_extras/keymap_uk.js b/src/i18n/keymap_extras/keymap_uk.js
new file mode 100644
index 0000000000..5466f7966c
--- /dev/null
+++ b/src/i18n/keymap_extras/keymap_uk.js
@@ -0,0 +1,184 @@
+/* Copyright 2022 - Generated by convert_keymap_extras_header.js
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+export default {
+ /*
+ * ┌───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───────┐
+ * │ ` │ 1 │ 2 │ 3 │ 4 │ 5 │ 6 │ 7 │ 8 │ 9 │ 0 │ - │ = │ │
+ * ├───┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─────┤
+ * │ │ Q │ W │ E │ R │ T │ Y │ U │ I │ O │ P │ [ │ ] │ │
+ * ├─────┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┐ │
+ * │ │ A │ S │ D │ F │ G │ H │ J │ K │ L │ ; │ ' │ # │ │
+ * ├────┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴───┴────┤
+ * │ │ \ │ Z │ X │ C │ V │ B │ N │ M │ , │ . │ / │ │
+ * ├────┼───┴┬──┴─┬─┴───┴───┴───┴───┴───┴──┬┴───┼───┴┬────┬────┤
+ * │ │ │ │ │ │ │ │ │
+ * └────┴────┴────┴────────────────────────┴────┴────┴────┴────┘
+ */
+ // Row 1
+ KC_GRV: { name: '¬\n`', title: 'UK_GRV' },
+ KC_1: { name: '!\n1', title: 'UK_1' },
+ KC_2: { name: '"\n2', title: 'UK_2' },
+ KC_3: { name: '£\n3', title: 'UK_3' },
+ KC_4: { name: '$\n4', title: 'UK_4' },
+ KC_5: { name: '%\n5', title: 'UK_5' },
+ KC_6: { name: '^\n6', title: 'UK_6' },
+ KC_7: { name: '&\n7', title: 'UK_7' },
+ KC_8: { name: '*\n8', title: 'UK_8' },
+ KC_9: { name: '(\n9', title: 'UK_9' },
+ KC_0: { name: ')\n0', title: 'UK_0' },
+ KC_MINS: { name: '_\n-', title: 'UK_MINS' },
+ KC_EQL: { name: '+\n=', title: 'UK_EQL' },
+ // Row 2
+ KC_Q: { name: 'Q', title: 'UK_Q' },
+ KC_W: { name: 'W', title: 'UK_W' },
+ KC_E: { name: 'E', title: 'UK_E' },
+ KC_R: { name: 'R', title: 'UK_R' },
+ KC_T: { name: 'T', title: 'UK_T' },
+ KC_Y: { name: 'Y', title: 'UK_Y' },
+ KC_U: { name: 'U', title: 'UK_U' },
+ KC_I: { name: 'I', title: 'UK_I' },
+ KC_O: { name: 'O', title: 'UK_O' },
+ KC_P: { name: 'P', title: 'UK_P' },
+ KC_LBRC: { name: '{\n[', title: 'UK_LBRC' },
+ KC_RBRC: { name: '}\n]', title: 'UK_RBRC' },
+ // Row 3
+ KC_A: { name: 'A', title: 'UK_A' },
+ KC_S: { name: 'S', title: 'UK_S' },
+ KC_D: { name: 'D', title: 'UK_D' },
+ KC_F: { name: 'F', title: 'UK_F' },
+ KC_G: { name: 'G', title: 'UK_G' },
+ KC_H: { name: 'H', title: 'UK_H' },
+ KC_J: { name: 'J', title: 'UK_J' },
+ KC_K: { name: 'K', title: 'UK_K' },
+ KC_L: { name: 'L', title: 'UK_L' },
+ KC_SCLN: { name: ':\n;', title: 'UK_SCLN' },
+ KC_QUOT: { name: "@\n'", title: 'UK_QUOT' },
+ KC_NUHS: { name: '~\n#', title: 'UK_HASH' },
+ // Row 4
+ KC_NUBS: { name: '|\n\\', title: 'UK_BSLS' },
+ KC_Z: { name: 'Z', title: 'UK_Z' },
+ KC_X: { name: 'X', title: 'UK_X' },
+ KC_C: { name: 'C', title: 'UK_C' },
+ KC_V: { name: 'V', title: 'UK_V' },
+ KC_B: { name: 'B', title: 'UK_B' },
+ KC_N: { name: 'N', title: 'UK_N' },
+ KC_M: { name: 'M', title: 'UK_M' },
+ KC_COMM: { name: '<\n,', title: 'UK_COMM' },
+ KC_DOT: { name: '>\n.', title: 'UK_DOT' },
+ KC_SLSH: { name: '?\n/', title: 'UK_SLSH' },
+
+ /* Shifted symbols
+ * ┌───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───────┐
+ * │ ¬ │ ! │ " │ £ │ $ │ % │ ^ │ & │ * │ ( │ ) │ _ │ + │ │
+ * ├───┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─────┤
+ * │ │ │ │ │ │ │ │ │ │ │ │ { │ } │ │
+ * ├─────┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┐ │
+ * │ │ │ │ │ │ │ │ │ │ │ : │ @ │ ~ │ │
+ * ├────┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴───┴────┤
+ * │ │ | │ │ │ │ │ │ │ │ < │ > │ ? │ │
+ * ├────┼───┴┬──┴─┬─┴───┴───┴───┴───┴───┴──┬┴───┼───┴┬────┬────┤
+ * │ │ │ │ │ │ │ │ │
+ * └────┴────┴────┴────────────────────────┴────┴────┴────┴────┘
+ */
+ // Row 1
+ 'S(KC_GRV)': { name: '¬', title: 'UK_NOT' },
+ KC_TILD: { name: '¬', title: 'UK_NOT' },
+ 'S(KC_1)': { name: '!', title: 'UK_EXLM' },
+ KC_EXLM: { name: '!', title: 'UK_EXLM' },
+ 'S(KC_2)': { name: '"', title: 'UK_DQUO' },
+ KC_AT: { name: '"', title: 'UK_DQUO' },
+ 'S(KC_3)': { name: '£', title: 'UK_PND' },
+ KC_HASH: { name: '£', title: 'UK_PND' },
+ 'S(KC_4)': { name: '$', title: 'UK_DLR' },
+ KC_DLR: { name: '$', title: 'UK_DLR' },
+ 'S(KC_5)': { name: '%', title: 'UK_PERC' },
+ KC_PERC: { name: '%', title: 'UK_PERC' },
+ 'S(KC_6)': { name: '^', title: 'UK_CIRC' },
+ KC_CIRC: { name: '^', title: 'UK_CIRC' },
+ 'S(KC_7)': { name: '&', title: 'UK_AMPR' },
+ KC_AMPR: { name: '&', title: 'UK_AMPR' },
+ 'S(KC_8)': { name: '*', title: 'UK_ASTR' },
+ KC_ASTR: { name: '*', title: 'UK_ASTR' },
+ 'S(KC_9)': { name: '(', title: 'UK_LPRN' },
+ KC_LPRN: { name: '(', title: 'UK_LPRN' },
+ 'S(KC_0)': { name: ')', title: 'UK_RPRN' },
+ KC_RPRN: { name: ')', title: 'UK_RPRN' },
+ 'S(KC_MINS)': { name: '_', title: 'UK_UNDS' },
+ KC_UNDS: { name: '_', title: 'UK_UNDS' },
+ 'S(KC_EQL)': { name: '+', title: 'UK_PLUS' },
+ KC_PLUS: { name: '+', title: 'UK_PLUS' },
+ // Row 2
+ 'S(KC_LBRC)': { name: '{', title: 'UK_LCBR' },
+ KC_LCBR: { name: '{', title: 'UK_LCBR' },
+ 'S(KC_RBRC)': { name: '}', title: 'UK_RCBR' },
+ KC_RCBR: { name: '}', title: 'UK_RCBR' },
+ // Row 3
+ 'S(KC_SCLN)': { name: ':', title: 'UK_COLN' },
+ KC_COLN: { name: ':', title: 'UK_COLN' },
+ 'S(KC_QUOT)': { name: '@', title: 'UK_AT' },
+ KC_DQUO: { name: '@', title: 'UK_AT' },
+ 'S(KC_NUHS)': { name: '~', title: 'UK_TILD' },
+ // Row 4
+ 'S(KC_NUBS)': { name: '|', title: 'UK_PIPE' },
+ 'S(KC_COMM)': { name: '<', title: 'UK_LABK' },
+ KC_LT: { name: '<', title: 'UK_LABK' },
+ 'S(KC_DOT)': { name: '>', title: 'UK_RABK' },
+ KC_GT: { name: '>', title: 'UK_RABK' },
+ 'S(KC_SLSH)': { name: '?', title: 'UK_QUES' },
+ KC_QUES: { name: '?', title: 'UK_QUES' },
+
+ /* AltGr symbols
+ * ┌───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───────┐
+ * │ ¦ │ │ │ │ € │ │ │ │ │ │ │ │ │ │
+ * ├───┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─────┤
+ * │ │ │ │ É │ │ │ │ Ú │ Í │ Ó │ │ │ │ │
+ * ├─────┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┐ │
+ * │ │ Á │ │ │ │ │ │ │ │ │ │ │ │ │
+ * ├────┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴───┴────┤
+ * │ │ │ │ │ │ │ │ │ │ │ │ │ │
+ * ├────┼───┴┬──┴─┬─┴───┴───┴───┴───┴───┴──┬┴───┼───┴┬────┬────┤
+ * │ │ │ │ │ │ │ │ │
+ * └────┴────┴────┴────────────────────────┴────┴────┴────┴────┘
+ */
+ // Row 1
+ 'ALGR(KC_GRV)': { name: '¦', title: 'UK_BRKP' },
+ 'ALGR(KC_4)': { name: '€', title: 'UK_EURO' },
+ // Row 2
+ 'ALGR(KC_E)': { name: 'É', title: 'UK_EACU' },
+ 'ALGR(KC_U)': { name: 'Ú', title: 'UK_UACU' },
+ 'ALGR(KC_I)': { name: 'Í', title: 'UK_IACU' },
+ 'ALGR(KC_O)': { name: 'Ó', title: 'UK_OACU' },
+ // Row 3
+ 'ALGR(KC_A)': { name: 'Á', title: 'UK_AACU' },
+
+ /* Other keys */
+ KC_BSLS: { name: '~\n#', title: '' },
+ 'S(KC_BSLS)': { name: '~', title: '' },
+ KC_PIPE: { name: '~', title: 'UK_TILD' },
+
+ KC_LSPO: { name: 'LS / (', title: 'Left Shift when held, ( when tapped' },
+ KC_RSPC: { name: 'RS / )', title: 'Right Shift when held, ) when tapped' },
+ KC_LCPO: { name: 'LC / (', title: 'Left Control when held, ( when tapped' },
+ KC_RCPC: { name: 'RC / )', title: 'Right Control when held, ) when tapped' },
+ KC_LAPO: { name: 'LA / (', title: 'Left Alt when held, ( when tapped' },
+ KC_RAPC: { name: 'RA / )', title: 'Right Alt when held, ) when tapped' },
+
+ QK_GESC: {
+ name: '`/¬\nEsc',
+ title: 'Esc normally, but ` when GUI is active or ¬ when Shift is active'
+ }
+};
diff --git a/src/i18n/keymap_extras/keymap_us.js b/src/i18n/keymap_extras/keymap_us.js
new file mode 100644
index 0000000000..83178338b6
--- /dev/null
+++ b/src/i18n/keymap_extras/keymap_us.js
@@ -0,0 +1,160 @@
+/* Copyright 2022 - Generated by convert_keymap_extras_header.js
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+export default {
+ /*
+ * ┌───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───────┐
+ * │ ` │ 1 │ 2 │ 3 │ 4 │ 5 │ 6 │ 7 │ 8 │ 9 │ 0 │ - │ = │ │
+ * ├───┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─────┤
+ * │ │ Q │ W │ E │ R │ T │ Y │ U │ I │ O │ P │ [ │ ] │ \ │
+ * ├─────┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴─────┤
+ * │ │ A │ S │ D │ F │ G │ H │ J │ K │ L │ ; │ ' │ │
+ * ├──────┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴────────┤
+ * │ │ Z │ X │ C │ V │ B │ N │ M │ , │ . │ / │ │
+ * ├────┬───┴┬──┴─┬─┴───┴───┴───┴───┴───┴──┬┴───┼───┴┬────┬────┤
+ * │ │ │ │ │ │ │ │ │
+ * └────┴────┴────┴────────────────────────┴────┴────┴────┴────┘
+ */
+ // Row 1
+ KC_GRV: { name: '~\n`', title: '' },
+ KC_1: { name: '!\n1', title: '' },
+ KC_2: { name: '@\n2', title: '' },
+ KC_3: { name: '#\n3', title: '' },
+ KC_4: { name: '$\n4', title: '' },
+ KC_5: { name: '%\n5', title: '' },
+ KC_6: { name: '^\n6', title: '' },
+ KC_7: { name: '&\n7', title: '' },
+ KC_8: { name: '*\n8', title: '' },
+ KC_9: { name: '(\n9', title: '' },
+ KC_0: { name: ')\n0', title: '' },
+ KC_MINS: { name: '_\n-', title: '' },
+ KC_EQL: { name: '+\n=', title: '' },
+ // Row 2
+ KC_Q: { name: 'Q', title: '' },
+ KC_W: { name: 'W', title: '' },
+ KC_E: { name: 'E', title: '' },
+ KC_R: { name: 'R', title: '' },
+ KC_T: { name: 'T', title: '' },
+ KC_Y: { name: 'Y', title: '' },
+ KC_U: { name: 'U', title: '' },
+ KC_I: { name: 'I', title: '' },
+ KC_O: { name: 'O', title: '' },
+ KC_P: { name: 'P', title: '' },
+ KC_LBRC: { name: '{\n[', title: '' },
+ KC_RBRC: { name: '}\n]', title: '' },
+ KC_BSLS: { name: '|\n\\', title: '' },
+ // Row 3
+ KC_A: { name: 'A', title: '' },
+ KC_S: { name: 'S', title: '' },
+ KC_D: { name: 'D', title: '' },
+ KC_F: { name: 'F', title: '' },
+ KC_G: { name: 'G', title: '' },
+ KC_H: { name: 'H', title: '' },
+ KC_J: { name: 'J', title: '' },
+ KC_K: { name: 'K', title: '' },
+ KC_L: { name: 'L', title: '' },
+ KC_SCLN: { name: ':\n;', title: '' },
+ KC_QUOT: { name: '"\n\'', title: '' },
+ // Row 4
+ KC_Z: { name: 'Z', title: '' },
+ KC_X: { name: 'X', title: '' },
+ KC_C: { name: 'C', title: '' },
+ KC_V: { name: 'V', title: '' },
+ KC_B: { name: 'B', title: '' },
+ KC_N: { name: 'N', title: '' },
+ KC_M: { name: 'M', title: '' },
+ KC_COMM: { name: '<\n,', title: '' },
+ KC_DOT: { name: '>\n.', title: '' },
+ KC_SLSH: { name: '?\n/', title: '' },
+
+ /* Shifted symbols
+ * ┌───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───────┐
+ * │ ~ │ ! │ @ │ # │ $ │ % │ ^ │ & │ * │ ( │ ) │ _ │ + │ │
+ * ├───┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─────┤
+ * │ │ │ │ │ │ │ │ │ │ │ │ { │ } │ | │
+ * ├─────┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴─────┤
+ * │ │ │ │ │ │ │ │ │ │ │ : │ " │ │
+ * ├──────┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴────────┤
+ * │ │ │ │ │ │ │ │ │ < │ > │ ? │ │
+ * ├────┬───┴┬──┴─┬─┴───┴───┴───┴───┴───┴──┬┴───┼───┴┬────┬────┤
+ * │ │ │ │ │ │ │ │ │
+ * └────┴────┴────┴────────────────────────┴────┴────┴────┴────┘
+ */
+ // Row 1
+ 'S(KC_GRV)': { name: '~', title: '' },
+ KC_TILD: { name: '~', title: '' },
+ 'S(KC_1)': { name: '!', title: '' },
+ KC_EXLM: { name: '!', title: '' },
+ 'S(KC_2)': { name: '@', title: '' },
+ KC_AT: { name: '@', title: '' },
+ 'S(KC_3)': { name: '#', title: '' },
+ KC_HASH: { name: '#', title: '' },
+ 'S(KC_4)': { name: '$', title: '' },
+ KC_DLR: { name: '$', title: '' },
+ 'S(KC_5)': { name: '%', title: '' },
+ KC_PERC: { name: '%', title: '' },
+ 'S(KC_6)': { name: '^', title: '' },
+ KC_CIRC: { name: '^', title: '' },
+ 'S(KC_7)': { name: '&', title: '' },
+ KC_AMPR: { name: '&', title: '' },
+ 'S(KC_8)': { name: '*', title: '' },
+ KC_ASTR: { name: '*', title: '' },
+ 'S(KC_9)': { name: '(', title: '' },
+ KC_LPRN: { name: '(', title: '' },
+ 'S(KC_0)': { name: ')', title: '' },
+ KC_RPRN: { name: ')', title: '' },
+ 'S(KC_MINS)': { name: '_', title: '' },
+ KC_UNDS: { name: '_', title: '' },
+ 'S(KC_EQL)': { name: '+', title: '' },
+ KC_PLUS: { name: '+', title: '' },
+ // Row 2
+ 'S(KC_LBRC)': { name: '{', title: '' },
+ KC_LCBR: { name: '{', title: '' },
+ 'S(KC_RBRC)': { name: '}', title: '' },
+ KC_RCBR: { name: '}', title: '' },
+ 'S(KC_BSLS)': { name: '|', title: '' },
+ KC_PIPE: { name: '|', title: '' },
+ // Row 3
+ 'S(KC_SCLN)': { name: ':', title: '' },
+ KC_COLN: { name: ':', title: '' },
+ 'S(KC_QUOT)': { name: '"', title: '' },
+ KC_DQUO: { name: '"', title: '' },
+ // Row 4
+ 'S(KC_COMM)': { name: '<', title: '' },
+ KC_LT: { name: '<', title: '' },
+ 'S(KC_DOT)': { name: '>', title: '' },
+ KC_GT: { name: '>', title: '' },
+ 'S(KC_SLSH)': { name: '?', title: '' },
+ KC_QUES: { name: '?', title: '' },
+
+ /* Other keys */
+ KC_NUHS: { name: '|\n\\', title: '' },
+ 'S(KC_NUHS)': { name: '|', title: '' },
+ KC_NUBS: { name: '|\n\\', title: '' },
+ 'S(KC_NUBS)': { name: '|', title: '' },
+
+ KC_LSPO: { name: 'LS / (', title: 'Left Shift when held, ( when tapped' },
+ KC_RSPC: { name: 'RS / )', title: 'Right Shift when held, ) when tapped' },
+ KC_LCPO: { name: 'LC / (', title: 'Left Control when held, ( when tapped' },
+ KC_RCPC: { name: 'RC / )', title: 'Right Control when held, ) when tapped' },
+ KC_LAPO: { name: 'LA / (', title: 'Left Alt when held, ( when tapped' },
+ KC_RAPC: { name: 'RA / )', title: 'Right Alt when held, ) when tapped' },
+
+ QK_GESC: {
+ name: '`/~\nEsc',
+ title: 'Esc normally, but ` when GUI is active or ~ when Shift is active'
+ }
+};
diff --git a/src/i18n/ms.csv b/src/i18n/ms.csv
index fd7360d627..b271d83265 100644
--- a/src/i18n/ms.csv
+++ b/src/i18n/ms.csv
@@ -111,14 +111,18 @@ settingsPanel:displaySizes:title,ctrl + alt + u
settingsPanel:fastInput:help,Masukkan kekunci melalui papan kekunci tanpa klik setiap posisi.
settingsPanel:fastInput:label,Input cepat
settingsPanel:fastInput:title,ctrl + alt + f
-settingsPanel:iso:help,Paparkan lagenda ISO secara lalai
-settingsPanel:iso:label,Gunakan lagenda kunci ISO
settingsPanel:kcno:label,KC_NO
settingsPanel:kctrns:label,KC_TRNS
settingsPanel:language:help,
settingsPanel:language:title,Bahasa
settingsPanel:off:label,Tutup
settingsPanel:on:label,Buka
+settingsPanel:osKeyboardLayout:help,
+settingsPanel:osKeyboardLayout:label:keymap_german,
+settingsPanel:osKeyboardLayout:label:keymap_russian,
+settingsPanel:osKeyboardLayout:label:keymap_uk,
+settingsPanel:osKeyboardLayout:label:keymap_us,
+settingsPanel:osKeyboardLayout:title,
settingsPanel:snowflakes:label,
settingsPanel:snowflakes:title,
settingsPanel:title,Konfigur seting
diff --git a/src/i18n/pl-PL.csv b/src/i18n/pl-PL.csv
index 1c38d04365..4528666c83 100644
--- a/src/i18n/pl-PL.csv
+++ b/src/i18n/pl-PL.csv
@@ -111,14 +111,18 @@ settingsPanel:displaySizes:title,Ctrl + Alt + U
settingsPanel:fastInput:help,Autmatyczne podświetlanie kolejnego klawisza po zdefiniowaniu funkcji poprzedniego.
settingsPanel:fastInput:label,Szybkie wprowadzanie
settingsPanel:fastInput:title,Ctrl + Alt + F
-settingsPanel:iso:help,Domyślnie wyświetlaj legendy ISO
-settingsPanel:iso:label,Użyj legendy kluczy ISO
settingsPanel:kcno:label,KC_NO
settingsPanel:kctrns:label,KC_TRNS
settingsPanel:language:help,
settingsPanel:language:title,Język
settingsPanel:off:label,Wył
settingsPanel:on:label,Wł
+settingsPanel:osKeyboardLayout:help,
+settingsPanel:osKeyboardLayout:label:keymap_german,Niemiecki (Niemcy i Austria)
+settingsPanel:osKeyboardLayout:label:keymap_russian,Rosyjski
+settingsPanel:osKeyboardLayout:label:keymap_uk,Angielski (UK)
+settingsPanel:osKeyboardLayout:label:keymap_us,Angielski (US)
+settingsPanel:osKeyboardLayout:title,
settingsPanel:snowflakes:label,
settingsPanel:snowflakes:title,
settingsPanel:title,Ustawienia konfiguratora
diff --git a/src/i18n/pt-BR.csv b/src/i18n/pt-BR.csv
index c1a8790b48..652142660c 100644
--- a/src/i18n/pt-BR.csv
+++ b/src/i18n/pt-BR.csv
@@ -111,14 +111,18 @@ settingsPanel:displaySizes:title,ctrl + alt + u
settingsPanel:fastInput:help,Entrada de comandos a partir do teclado sem clicar em outra posição.
settingsPanel:fastInput:label,Entrada rápida
settingsPanel:fastInput:title,ctrl + alt + f
-settingsPanel:iso:help,Exibir legendas ISO por padrão
-settingsPanel:iso:label,Use as legendas da chave ISO
settingsPanel:kcno:label,KC_NO
settingsPanel:kctrns:label,KC_TRNS
settingsPanel:language:help,
settingsPanel:language:title,Linguagem
settingsPanel:off:label,Off
settingsPanel:on:label,On
+settingsPanel:osKeyboardLayout:help,
+settingsPanel:osKeyboardLayout:label:keymap_german,Alemão (Alemanha e Áustria)
+settingsPanel:osKeyboardLayout:label:keymap_russian,Russo
+settingsPanel:osKeyboardLayout:label:keymap_uk,Inglês (Reino Unido)
+settingsPanel:osKeyboardLayout:label:keymap_us,Inglês (EUA)
+settingsPanel:osKeyboardLayout:title,
settingsPanel:snowflakes:label,
settingsPanel:snowflakes:title,
settingsPanel:title,Configurações
diff --git a/src/i18n/ru.csv b/src/i18n/ru.csv
index 97616c9fbf..9c8d5f8ccd 100644
--- a/src/i18n/ru.csv
+++ b/src/i18n/ru.csv
@@ -111,14 +111,18 @@ settingsPanel:displaySizes:title,ctrl + alt + u
settingsPanel:fastInput:help,Ввод клавиш через клавиатуру без необходимости нажимать на каждую позицию
settingsPanel:fastInput:label,Быстрый ввод
settingsPanel:fastInput:title,ctrl + alt + f
-settingsPanel:iso:help,Отображать легенды ISO по умолчанию
-settingsPanel:iso:label,Используйте ключевые легенды ISO
settingsPanel:kcno:label,KC_NO
settingsPanel:kctrns:label,KC_TRNS
settingsPanel:language:help,
settingsPanel:language:title,Язык
settingsPanel:off:label,Выкл
settingsPanel:on:label,Вкл
+settingsPanel:osKeyboardLayout:help,Изменяет легенды клавиш в соответствии с раскладкой клавиатуры операционной системы.
+settingsPanel:osKeyboardLayout:label:keymap_german,Немецкий (Германия и Австрия)
+settingsPanel:osKeyboardLayout:label:keymap_russian,Русский
+settingsPanel:osKeyboardLayout:label:keymap_uk,Английский (Великобритания)
+settingsPanel:osKeyboardLayout:label:keymap_us,Английский (США)
+settingsPanel:osKeyboardLayout:title,Раскладка клавиатуры операционной системы
settingsPanel:snowflakes:label,
settingsPanel:snowflakes:title,
settingsPanel:title,Настройки конфигуратора
diff --git a/src/i18n/zh-CN.csv b/src/i18n/zh-CN.csv
index 056b56bdad..fd00d6d3c4 100644
--- a/src/i18n/zh-CN.csv
+++ b/src/i18n/zh-CN.csv
@@ -111,14 +111,18 @@ settingsPanel:displaySizes:title,ctrl + alt + u
settingsPanel:fastInput:help,直接通过键盘输入键位
settingsPanel:fastInput:label,快速输入
settingsPanel:fastInput:title,ctrl + alt + f
-settingsPanel:iso:help,默认显示 ISO 图例
-settingsPanel:iso:label,使用 ISO 密钥图例
settingsPanel:kcno:label,
settingsPanel:kctrns:label,
settingsPanel:language:help,更改用户界面语言
settingsPanel:language:title,语言
settingsPanel:off:label,关闭
settingsPanel:on:label,开启
+settingsPanel:osKeyboardLayout:help,
+settingsPanel:osKeyboardLayout:label:keymap_german,德语(德国和奥地利)
+settingsPanel:osKeyboardLayout:label:keymap_russian,俄语
+settingsPanel:osKeyboardLayout:label:keymap_uk,英语(英国)
+settingsPanel:osKeyboardLayout:label:keymap_us,英语(美国)
+settingsPanel:osKeyboardLayout:title,
settingsPanel:snowflakes:label,开启 Snowflakes
settingsPanel:snowflakes:title,Snowflakes
settingsPanel:title,配置选项
diff --git a/src/i18n/zh.csv b/src/i18n/zh.csv
index bbfede5992..36f964aec8 100644
--- a/src/i18n/zh.csv
+++ b/src/i18n/zh.csv
@@ -109,14 +109,18 @@ settingsPanel:displaySizes:title,
settingsPanel:fastInput:help,
settingsPanel:fastInput:label,
settingsPanel:fastInput:title,
-settingsPanel:iso:help,
-settingsPanel:iso:label,
settingsPanel:kcno:label,KC_NO
settingsPanel:kctrns:label,KC_TRNS
settingsPanel:language:help,
settingsPanel:language:title,
settingsPanel:off:label,
settingsPanel:on:label,
+settingsPanel:osKeyboardLayout:help,
+settingsPanel:osKeyboardLayout:label:keymap_german,
+settingsPanel:osKeyboardLayout:label:keymap_russian,
+settingsPanel:osKeyboardLayout:label:keymap_uk,
+settingsPanel:osKeyboardLayout:label:keymap_us,
+settingsPanel:osKeyboardLayout:title,
settingsPanel:snowflakes:label,
settingsPanel:snowflakes:title,
settingsPanel:title,
diff --git a/src/store/modules/app/actions.js b/src/store/modules/app/actions.js
index c6d5e65142..3ee0739588 100644
--- a/src/store/modules/app/actions.js
+++ b/src/store/modules/app/actions.js
@@ -136,6 +136,22 @@ const actions = {
commit('setCurrentLanguage', lang);
await dispatch('saveConfiguratorSettings');
},
+ async changeOSKeyboardLayout({ dispatch, state, commit }, osLayout) {
+ commit('setOSKeyboardLayout', osLayout);
+ // Important to call keycodes/updateKeycodeNames *before* keymap/updateKeycodeNames.
+ this.commit('keycodes/updateKeycodeNames');
+ this.commit('keymap/updateKeycodeNames');
+ this.commit(
+ 'keycodes/changeActive',
+ state.configuratorSettings.iso ? 'ISO/JIS' : 'ANSI'
+ );
+ this.commit(
+ 'tester/setLayout',
+ state.configuratorSettings.iso ? 'ISO' : 'ANSI'
+ );
+ await this.dispatch('tester/init');
+ await dispatch('saveConfiguratorSettings');
+ },
// if init state we just load and not toggling
async toggleDarkMode({ commit, state, dispatch }, init) {
let darkStatus = state.configuratorSettings.darkmodeEnabled;
@@ -156,8 +172,6 @@ const actions = {
iso = !iso;
}
commit('setIso', iso);
- const keyboardLayout = iso ? 'enableIso' : 'disableIso';
- this.commit(`keycodes/${keyboardLayout}`);
this.commit('keymap/updateKeycodeNames');
this.commit('tester/setLayout', iso ? 'ISO' : 'ANSI');
await this.dispatch('tester/init');
diff --git a/src/store/modules/app/getters.js b/src/store/modules/app/getters.js
index 2f82f09b08..aefcbf277b 100644
--- a/src/store/modules/app/getters.js
+++ b/src/store/modules/app/getters.js
@@ -35,6 +35,9 @@ const getters = {
return state.layouts[state.layout].length;
}
return 0;
+ },
+ osKeyboardLayout: (state) => {
+ return state.configuratorSettings.osKeyboardLayout;
}
};
diff --git a/src/store/modules/app/mutations.js b/src/store/modules/app/mutations.js
index c120cbfe43..6cd62214f3 100644
--- a/src/store/modules/app/mutations.js
+++ b/src/store/modules/app/mutations.js
@@ -181,6 +181,9 @@ const mutations = {
setDarkmode(state, value) {
state.configuratorSettings.darkmodeEnabled = value;
},
+ setOSKeyboardLayout(state, value) {
+ state.configuratorSettings.osKeyboardLayout = value;
+ },
setIso(state, value) {
state.configuratorSettings.iso = value;
},
diff --git a/src/store/modules/app/state.js b/src/store/modules/app/state.js
index e799b0fd97..d1c5dd3479 100644
--- a/src/store/modules/app/state.js
+++ b/src/store/modules/app/state.js
@@ -4,7 +4,7 @@ import {
CONSTS
} from '@/store/localStorage';
-function setDefaultConfiguratorSettings() {
+function getDefaultConfiguratorSettings() {
// detect if OS supports dark mode and set as default
const osDarkMode =
window.matchMedia &&
@@ -15,21 +15,24 @@ function setDefaultConfiguratorSettings() {
favoriteKeyboard: '',
favoriteColor: '',
clearLayerDefault: false,
- iso: false
+ iso: false,
+ osKeyboardLayout: 'keymap_us'
};
- localStorageSet(CONSTS.configuratorSettings, JSON.stringify(initialConfig));
return initialConfig;
}
function loadSettings() {
try {
- let conf = JSON.parse(localStorageLoad(CONSTS.configuratorSettings));
- if (!conf) {
- return setDefaultConfiguratorSettings();
- }
+ // Locally stored configuratorSettings can miss properties that were added in a new update,
+ // so it is important to merge them with the default configurator settings to ensure that all
+ // settings are set.
+ let conf = {
+ ...getDefaultConfiguratorSettings(),
+ ...JSON.parse(localStorageLoad(CONSTS.configuratorSettings))
+ };
return conf;
} catch {
- return setDefaultConfiguratorSettings();
+ return getDefaultConfiguratorSettings();
}
}
@@ -75,6 +78,12 @@ const state = {
{ value: 'ja', label: '日本語' },
{ value: 'zh-CN', label: '简体中文' }
],
+ osKeyboardLayouts: [
+ 'keymap_german',
+ 'keymap_russian',
+ 'keymap_uk',
+ 'keymap_us'
+ ],
snowflakes: false
};
diff --git a/src/store/modules/keycodes/index.js b/src/store/modules/keycodes/index.js
index 6f3c213f1f..af4ff4dd87 100644
--- a/src/store/modules/keycodes/index.js
+++ b/src/store/modules/keycodes/index.js
@@ -6,15 +6,19 @@ import settings from './kb-settings';
import media from './app-media-mouse';
import steno from './steno';
import store from '@/store';
+import keymapExtras from '@/i18n/keymap_extras';
-const keycodeLayout = {
- ANSI: [...ansi, ...iso_jis],
- ISO: [...iso_jis, ...ansi],
- normal: [...quantum, ...settings, ...media]
+const keycodePickerTabLayout = {
+ ANSI_ISO: [...ansi, ...iso_jis],
+ ISO_ANSI: [...iso_jis, ...ansi],
+ special: [...quantum, ...settings, ...media]
};
const state = {
- keycodes: [...keycodeLayout.ANSI, ...keycodeLayout.normal],
+ keycodes: [
+ ...keycodePickerTabLayout.ANSI_ISO,
+ ...keycodePickerTabLayout.special
+ ],
searchFilter: '',
searchCounters: {
ANSI: 0,
@@ -27,17 +31,57 @@ const state = {
active: 'ANSI'
};
-function isISO() {
- return store.state.app.configuratorSettings.iso;
+function getOSKeyboardLayout() {
+ let osKeyboardLayout = store.getters['app/osKeyboardLayout'];
+ if (
+ isUndefined(osKeyboardLayout) ||
+ !Object.keys(keymapExtras).includes(osKeyboardLayout)
+ ) {
+ const fallbackOSKeyboardLayout = 'keymap_us';
+ console.log(
+ `The stored OS keyboard layout value (${osKeyboardLayout}) is not a valid value! Falling back to '${fallbackOSKeyboardLayout}'.`
+ );
+ store.commit('app/setOSKeyboardLayout', fallbackOSKeyboardLayout);
+ osKeyboardLayout = fallbackOSKeyboardLayout;
+ }
+ return osKeyboardLayout;
+}
+
+function isANSI() {
+ return keymapExtras[getOSKeyboardLayout()].isANSI;
+}
+
+function toLocaleKeycode(keycodeLUT, keycodeObject) {
+ console.assert(!isUndefined(keycodeLUT));
+ if (
+ !Object.keys(keycodeObject).includes('name') ||
+ !Object.keys(keycodeObject).includes('code')
+ ) {
+ // Not an object describing a keyboard key; return as is
+ return keycodeObject;
+ }
+ if (keycodeLUT[keycodeObject.code]) {
+ // Clone in a shallow manner the original keycodeObject object and
+ // override the name, title, and possibly other fields
+ return { ...keycodeObject, ...keycodeLUT[keycodeObject.code] };
+ } else {
+ return keycodeObject;
+ }
}
-function generateKeycodes(isIso, isSteno) {
+function generateKeycodes(osKeyboardLayout, isSteno) {
+ store.commit('app/setIso', !isANSI());
const keycodes = [
- ...(isIso ? keycodeLayout.ISO : keycodeLayout.ANSI),
- ...keycodeLayout.normal,
+ ...(isANSI()
+ ? keycodePickerTabLayout.ANSI_ISO
+ : keycodePickerTabLayout.ISO_ANSI),
+ ...keycodePickerTabLayout.special,
...(isSteno ? steno : [])
];
- return keycodes;
+ const { keycodeLUT } = keymapExtras[getOSKeyboardLayout()];
+ return keycodes.map((keycodeObject) =>
+ toLocaleKeycode(keycodeLUT, keycodeObject)
+ );
}
const getters = {
@@ -76,23 +120,14 @@ const mutations = {
},
enableSteno(state) {
state.steno = true;
- state.keycodes = generateKeycodes(isISO(), state.steno);
+ state.keycodes = generateKeycodes(getOSKeyboardLayout(), state.steno);
},
disableSteno(state) {
state.steno = false;
- state.keycodes = generateKeycodes(isISO(), state.steno);
+ state.keycodes = generateKeycodes(getOSKeyboardLayout(), state.steno);
},
- enableIso(state) {
- state.keycodes = generateKeycodes(isISO(), state.steno);
- if (state.active === 'ANSI') {
- state.active = 'ISO/JIS';
- }
- },
- disableIso(state) {
- state.keycodes = generateKeycodes(isISO(), state.steno);
- if (state.active === 'ISO/JIS') {
- state.active = 'ANSI';
- }
+ updateKeycodeNames(state) {
+ state.keycodes = generateKeycodes(getOSKeyboardLayout(), state.steno);
},
setSearchFilter(state, newVal) {
state.searchFilter = newVal;
diff --git a/src/store/modules/keycodes/quantum.js b/src/store/modules/keycodes/quantum.js
index fc81500f49..90c5921582 100644
--- a/src/store/modules/keycodes/quantum.js
+++ b/src/store/modules/keycodes/quantum.js
@@ -392,37 +392,37 @@ export default [
{ label: 'Special action keys', width: 'label' },
{
- name: 'Esc/~',
+ name: '` / ~\nEsc',
code: 'QK_GESC',
- title: 'Esc normally, but ~ when Shift or GUI is pressed'
+ title: 'Esc normally, but ` when GUI is active or ~ when Shift is active'
},
{
- name: 'LS/(',
+ name: 'LS / (',
code: 'KC_LSPO',
title: 'Left Shift when held, ( when tapped'
},
{
- name: 'RS/)',
+ name: 'RS / )',
code: 'KC_RSPC',
title: 'Right Shift when held, ) when tapped'
},
{
- name: 'LC/(',
+ name: 'LC / (',
code: 'KC_LCPO',
title: 'Left Control when held, ( when tapped'
},
{
- name: 'RC/)',
+ name: 'RC / )',
code: 'KC_RCPC',
title: 'Right Control when held, ) when tapped'
},
{
- name: 'LA/(',
+ name: 'LA / (',
code: 'KC_LAPO',
title: 'Left Alt when held, ( when tapped'
},
{
- name: 'RA/)',
+ name: 'RA / )',
code: 'KC_RAPC',
title: 'Right Alt when held, ) when tapped'
},
diff --git a/tests/integration/tester.spec.js b/tests/integration/tester.spec.js
index 57cf247298..b357728da1 100644
--- a/tests/integration/tester.spec.js
+++ b/tests/integration/tester.spec.js
@@ -23,7 +23,7 @@ describe('Tester feature', function () {
it('Should change layout', function () {
// ISO has 105 keys
cy.get('.bes-controls').click();
- cy.get('#setting-toggle-iso').click();
+ cy.get('#setting-panel-os-keyboard-layout').select('keymap_uk');
cy.get('.visual-tester-keymap').find('div').should('have.length', 105);
});
it('Handle typing', function () {