KeyboardGen (shortened kbdgen) is a tool for generating keyboard layouts from .kbdgen bundles and associated resources.

CLI

Core kbdgen functionality will be within the subcommand target, that then allows to select among the various linguistic targets: Windows, MacOS, .svg, etc.

Example usage:

cargo run — target --bundle-path C:\Projects\Divvun\keyboards\keyboard-sme\sme.kbdgen --output-path C:\KbdgenBuilds\sme_mac macos generate

The first argument is the location of the given .kbdgen bundle (see below for information on bundles) that the keyboard should be built from.

The second argument is the output path.

And the third argument is the desired target.

Bundle

\.kbdgen bundles are folders with the .kbdgen extension which describe keyboards for a language or language family. They’re expected to contain 3 pieces:

*project.yaml *layouts folder *targets folder *resources

You can see an example of a .kbdgen bundle here: https://github.com/giellalt/keyboard-sme

Layouts

A layout is a file that describes keyboard layouts, including associated keymaps, dead keys, and transforms, as well as any configuration information, for all supported targets.

With the new kbdgen, the layout format has been changed as described below.

Layouts are described in .yaml files, with one layout file per language tag (i.e., se-FI (Northern Sami (Finland)))

The layout may be described for multiple targets (windows, macOS, etc.), i.e., groupings of platforms.

A given target may support one or more platforms (iPad-9in, iPad-12in).

Within a platform are the supported keyboard layers, i.e., default, shift, caps (for caps lock), etc.

Layouts also have associated deadKeys information, which is for keys that do not themselves result in a key printed, but will print a key in combination with some other key. For deadKeys, there must be a corresponding transforms entry for the ' ' key, and referrs to deadKey usage with a space (i.e., a termination).

There’s also the space category.

Deadkeys

deadKeys can be nested:

'a':
  'b': ['c']

'a':
  'b':
    'c': ['d']

Or with a more authentic example:

transforms:
  '-':
    ' ': '-'
    a: ā
    e: ē
    '´':
      ' ': '-´'
      a: ā́
      e: ḗ

Layers

Layers are expected to be whitespace separated strings listing the keymap of characters in correspondence with appropriate format for the expected platform-layer combination.

Newlines are permitted for readability, but the keys must be in the correct order, and having more keys than expected will result in an error.

Special keys are supported, and must be contained within \s{}. Unrepresentable unicode characters must be contained within u{}.

Additional Info

displayNames, at the top of the layout file, are the name of the given keyboard in each of the relevant languages. This is often used in parts of UI or for naming.

Each target may have an associated config describing some target-specific settings.

Additionally, various keyboards may require these:

longpress - used for mobile keyboards, the set of keys that become available if a key is pressed for a long time.

transforms - used in combination with deadKeys, describes the final character that should be printed once a series of keys is pressed.

keyNames - descriptions of named keys (space and return) in the language in question.

Targets

Target files are .yaml files that are specific to the given target and hold additional per target configuration.

For a given build to succeed for a target, the target file must be present.

Target Specific information

Windows

Generating a Windows keyboard layout in the current implementation of kbdgen mainly concerns the generation of .klc files, and then subsequent building of those files on a Windows system.

KLC is Windows’s format for keyboard layouts, meaning Keyboard Layout Creator, which is also the tool that can be used to view and modify .klc files. The tool can be downloaded from Microsoft: https://www.microsoft.com/en-us/download/details.aspx?id=102134

Installing MSKLC requires enabling .NET Framework 3.5 in Windows Programs and Features.

\.klc is a format that has specific requirements for how a keyboard is described and must be encoded as UTF-16, with Windows style CRLF, or the file will be perceived as invalid.

For details on the .klc format, please see the developer documentation.

Target configuration

A Windows target file does not have to be specified. ??

Keyboard layouts

windows:
  config:
    locale: se-Latn-FI
  primary:
    layers:
      default: |
        § 1 2 3 4 5 6 7 8 9 0 + ´
          á š e r t y u i o p å ŋ
          a s d f g h j k l ö ä đ
        ž z č c v b n m , . -
      shift: |
        ½ ! " # ¤ % & / ( ) = ? `
          Á Š E R T Y U I O P Å Ŋ
          A S D F G H J K L Ö Ä Đ
        Ž Z Č C V B N M ; : _
      caps: |
        § 1 2 3 4 5 6 7 8 9 0 + ´
          Á Š E R T Y U I O P Å Ŋ
          A S D F G H J K L Ö Ä Đ
        Ž Z Č C V B N M , . -
      caps+shift: |
        ½ ! " # ¤ % & / ( ) = ? `
          á š e r t y u i o p å ŋ
          a s d f g h j k l ö ä đ
        ž z č c v b n m ; : _
      alt: |
        |     \u{0} @     £     $     €     \u{0} {     [     ]     }     \     \u{0}
              q     w     €     \u{0} ŧ     \u{0} \u{0} ï     õ     \u{0} ¨     ~
              â     \u{0} \u{0} \u{0} ǧ     ǥ     \u{0} ǩ     \u{0} ø     æ     '
        ǯ     ʒ     x     \u{0} \u{0} \u{0} \u{0} µ     <     >     \u{0}
      alt+shift: |
        \u{0} \u{0} \u{0} \u{0} \u{0} \u{0} \u{0} \u{0} \u{0} \u{0} \u{0} \u{0} \u{0}
              Q     W     \u{0} \u{0} Ŧ     \u{0} \u{0} Ï     Õ     \u{0} ^     ˇ
              Â     \u{0} \u{0} \u{0} Ǧ     Ǥ     \u{0} Ǩ     \u{0} Ø     Æ     *
        Ǯ     Ʒ     X     \u{0} \u{0} \u{0} \u{0} \u{0} \u{0} \u{0} \u{0}
      ctrl: |
        \u{0} \u{0} \u{0} \u{0} \u{0} \u{0} \u{0} \u{0} \u{0} \u{0} \u{0} \u{0} \u{0}
        \u{0} \u{0} \u{0} \u{0} \u{0} \u{0} \u{0} \u{0} \u{0} \u{0} \u{0} \u{0}
        \u{0} \u{0} \u{0} \u{0} \u{0} \u{0} \u{0} \u{0} \u{0} \u{0} \u{0} \u{0}
        \u{0} \u{0} \u{0} \u{0} \u{0} \u{0} \u{0} \u{0} \u{0} \u{0} \u{0}
  deadKeys:
    default: [´]
    shift: ['`']
    caps: [´]
    caps+shift: ['`']
    alt: ['~', ¨]
    alt+shift: [^, ˇ]

Config

The config section is used by Pahkat for spellchecking, the fields:

  • locale - defines the language locale

  • id - defines the id

Layers

Each layer is split by line, typically to 3 lines of the keyboard. Keys in the format of \u{} are written in Unicode to avoid display issues. In particular, \u{0} is an absent key. Note that KLC cannot support keys outside the Basic Multilingual Plane, even if Unicode supports them.

ChromeOS

ChromeOS generation mainly involves adding keyboard layout information to a JavaScript file.

Target configuration

A file for the desired target has to be specified, and in this case for ChromeOS, it could look like this:

appId: dnihbfekindancgddjehgonciaopmkbe
build: 11
version: 1.0.0

The fields define what follows:

  • appId - the id of the app

  • version - current app version

  • build - current app build

Keyboard layouts

The ChromeOS keyboard was modeled after the Windows one and so shares a lot of similarities.

chromeOS:
  config:
    locale: fi
  primary:
    layers:
      default: |
        § 1 2 3 4 5 6 7 8 9 0 + ´
          á š e r t y u i o p å ŋ
          a s d f g h j k l ö ä đ
        ž z č c v b n m , . -
      shift: |
        ½ ! " # ¤ % & / ( ) = ? `
          Á Š E R T Y U I O P Å Ŋ
          A S D F G H J K L Ö Ä Đ
        Ž Z Č C V B N M ; : _
      caps: |
        § 1 2 3 4 5 6 7 8 9 0 + ´
          Á Š E R T Y U I O P Å Ŋ
          A S D F G H J K L Ö Ä Đ
        Ž Z Č C V B N M , . -
      caps+shift: |
        ½ ! " # ¤ % & / ( ) = ? `
          á š e r t y u i o p å ŋ
          a s d f g h j k l ö ä đ
        ž z č c v b n m ; : _
      alt: |
        |     \u{0} @     £     $     €     \u{0} {     [     ]     }     \     \u{0}
              q     w     €     \u{0} ŧ     \u{0} \u{0} ï     õ     \u{0} ¨     ~
              â     \u{0} \u{0} \u{0} ǧ     ǥ     \u{0} ǩ     \u{0} ø     æ     '
        ǯ     ʒ     x     \u{0} \u{0} \u{0} \u{0} µ     <     >     \u{0}
      alt+shift: |
        \u{0} \u{0} \u{0} \u{0} \u{0} \u{0} \u{0} \u{0} \u{0} \u{0} \u{0} \u{0} \u{0}
              Q     W     \u{0} \u{0} Ŧ     \u{0} \u{0} Ï     Õ     \u{0} ^     ˇ
              Â     \u{0} \u{0} \u{0} Ǧ     Ǥ     \u{0} Ǩ     \u{0} Ø     Æ     *
        Ǯ     Ʒ     X     \u{0} \u{0} \u{0} \u{0} \u{0} \u{0} \u{0} \u{0}
      ctrl: |
        \u{0} \u{0} \u{0} \u{0} \u{0} \u{0} \u{0} \u{0} \u{0} \u{0} \u{0} \u{0} \u{0}
        \u{0} \u{0} \u{0} \u{0} \u{0} \u{0} \u{0} \u{0} \u{0} \u{0} \u{0} \u{0}
        \u{0} \u{0} \u{0} \u{0} \u{0} \u{0} \u{0} \u{0} \u{0} \u{0} \u{0} \u{0}
        \u{0} \u{0} \u{0} \u{0} \u{0} \u{0} \u{0} \u{0} \u{0} \u{0} \u{0}

Config

The config section is used by Pahkat for spellchecking, the fields:

  • locale - defines the language locale

  • xkbLayout - defines the layout ??

Layers

Each layer is split by line, typically to 3 lines of the keyboard. Keys in the format of \u{} are written in Unicode to avoid display issues.

iOS

Target configuration

A file for the desired target has to be specified, and in this case for iOS, it could look like this:

codeSignId: "Apple Distribution: The University of Tromso (2K5J2584NX)"
teamId: 2K5J2584NX
packageId: no.uit.giella.keyboards.Sami
bundleName: Divvun Keyboards
sentryDsn: https://94b33a64dc8e4471a7c8cb2f40ce37dc@sentry.io/1227106
aboutDir: ../aboutFiles
version: 3.2.1
build: 278

The fields define what follows:

  • codeSignId - the id for codesigning

  • teamId - team id

  • packageId - package id

  • bundleName - name of the bundle

  • sentryDsn - url to sentry

  • aboutDir - a path to files each containing a localized description about the app

  • version - current app version

  • build - current app build

Keyboard layouts

iOS is one of the few keyboard layouts that handle more than one platform. Additionally, it also requires the symbols layers.

iOS:
  config:
    spellerPackageKey: https://pahkat.uit.no/main/packages/speller-sme?platform=mobile
    spellerPath: se.bhfst
  primary:
    layers:
      default: |
        á š e r t y u i o p ŋ
        a s d f g h j k l đ ŧ
        \s{shift:1.25} \s{spacer:0.25} ž z č c v b n m \s{spacer:0.25} \s{backspace:1.25}
      shift: |
        Á Š E R T Y U I O P Ŋ
        A S D F G H J K L Đ Ŧ
        \s{shift:1.25} \s{spacer:0.25} Ž Z Č C V B N M \s{spacer:0.25} \s{backspace:1.25}
      symbols-1: |
        1 2 3 4 5 6 7 8 9 0
        - / : ; ( ) kr & @ "
        \s{shiftSymbols} \s{spacer:0.25} . , ? ! ' \s{spacer:0.25} \s{backspace}
      symbols-2: |
        [ ] { } # % ^ * + =
        _ \ | ~ < > € $ £ •
        \s{shiftSymbols} \s{spacer:0.25} . , ? ! ' \s{spacer:0.25} \s{backspace}

  iPad-9in:
    layers:
      default: |
        \s{"`":0.75}  á š e r t y u i o p ŋ \s{backspace:1.25}
        \s{shift}     a s d f g h j k l đ ŧ \s{shift}
          \s{spacer:0.5}  ž z č c v b n m , . \s{spacer:0.5} \s{return:2}
      shift: |
        \s{"@":0.75}  Á Š E R T Y U I O P Ŋ \s{backspace:1.25}
        \s{shift}     A S D F G H J K L Đ Ŧ \s{shift}
        \s{spacer:0.5}  Ž Z Č C V B N M ! ? \s{spacer:0.5} \s{return:2}
      alt: |
        \s{"@":0.75}  1 2 3  4 5 6 7 8 9 0 å \s{backspace:1.25}
        \s{shift}     % # kr & * ( ) ' " ø æ \s{shift}
        \s{spacer:0.5}  q w  x - = / ; : ! ? \s{spacer:0.5} \s{return:2}
      alt+shift: |
        \s{"@":0.75}  1 2 3  4 5 6 7 8 9 0 Å \s{backspace:1.25}
        \s{shift}     % # kr & * ( ) ' " Ø Æ \s{shift}
        \s{spacer:0.5}  Q W  X - = / ; : ! ? \s{spacer:0.5} \s{return:2}
      symbols-1: |
        \s{spacer:0.75} 1 2 3 4 5 6 7 8 9 0 ` \s{backspace:1.25}
        \s{shiftSymbols} @ # kr & * ( ) ' " + • \s{shiftSymbols}
        \s{spacer:1.5} % _ - = / ; : , . \s{spacer:0.5} \s{return:2}
      symbols-2: |
        \s{spacer:0.75}  1 2 3 4 5 6 7 8 9 0 ´ \s{backspace:1.25}
        \s{shiftSymbols} € $ £ ^ [ ] { } — ° … \s{shiftSymbols}
        \s{spacer:1.5}    § | ~ ≠ \ < > ! ?    \s{spacer:0.5} \s{return:2}

  iPad-12in:
    layers:
      ...

Longpress

A longpress section like this is also needed for alternative keys but isn’t specifically exclusive to iOS but rather defined independently in the layout file and shared between Android and iOS

  longpress:
    A: Å Æ Ä   À Â Ã Ā Ạ
    Á: Q
    E: Ë É È Ê Ẽ Ē Ẹ Ė Ǝ
    I: Ï Í Ì Î Ĩ Ī Ị I
    ...

Config

The config section is used by Pahkat for spellchecking, the fields:

  • spellerPackageKey - defines the url for the spellchecking files of the layout we want to generate

  • spellerPath - defines which spellchecker file to use.

  • space - name of space key

  • r#return - name of return key

Platforms

Three platforms are supported, all of which have to be defined, primary, iPad-9in, iPad-12in.

iPad-9in:
    layers:
      default: |
        ...

Layers

iOS supports some standard keyboard layers in addition to symbols-1 and symbols-2.

Each layer is split by line, typically to 3 lines of the keyboard.

Special keys need to be preceeded by \s, and is sometimes followed by a number after : representing the desired width of the key (keywidth is 1 by default, centered):

  • \s{spacer:0.5} - not a key, just a spacer

  • \s{shift} - Shift

  • \s{backspace:1.25} - Delete

  • \s{return:2} - Enter

  • \s{"@":0.75} - a given key, but with a custom width

  • \s{shiftSymbols} - switch between symbols-1 and symbols-2 layers

MacOS

Target configuration

A MacOS target file does not have to be specified. ??

Keyboard layouts

macOS:
  primary:
    layers:
      default: |
        ' 1 2 3 4 5 6 7 8 9 0 + ´
          á š e r t y u i o p å ŋ
          a s d f g h j k l ö ä đ
        ž z č c v b n m , . -
      shift: |
        § ! " # $ % & / ( ) = ? `
          Á Š E R T Y U I O P Å Ŋ
          A S D F G H J K L Ö Ä Đ
        Ž Z Č C V B N M ; : _
      caps: |
        ' 1 2 3 4 5 6 7 8 9 0 + ´
          Á Š E R T Y U I O P Å Ŋ
          A S D F G H J K L Ö Ä Đ
        Ž Z Č C V B N M , . -
      alt: |
        ' © ™ £ € ˆ § | [ ] ˝ ± \u{301}
          q w é ˇ ŧ þ ˀ ʼ œ ˙ ˚ ¨
          ¯ ß ð ƒ . ˛ ˘ ˜ - ø æ @
        < ÷ x ¸ ‹ › ‘ ’ ‚ … –
      alt+shift: |
        § ¡       ® ¥       ¢       \u{302} ¶       \       {       }       \u{30B} ¿       \u{300}
          Q       W É       \u{30C} Ŧ       Þ       \u{309} \u{31B} Œ       \u{307} \u{30A} \u{308}
          \u{304} № Ð       ʔ       \u{323} \u{328} \u{306} \u{303} \u{335} Ø       Æ       *
        > ⁄       X \u{327} «       »       “       ”       „       ·       —
      alt+caps: |
        ' © ™  £ € ˆ § | [ ] ˝ ± \u{301}
          Q W  É ˇ Ŧ Þ ˀ ʼ Œ ˙ ˚ ¨
          ¯ SS Ð ƒ . ˛ ˘ ˜ - Ø Æ @
        < ÷ X  ¸ ‹ › ‘ ’ ‚ … –
      ctrl: |
        0 1 2 3 4 5 6 7 8 9 0 \u{1F} =
          \u{11} \u{17} \u{5} \u{12} \u{14} \u{19} \u{15} \u{9} \u{F} \u{10} \u{1B} \u{1D}
          \u{1} \u{13} \u{4} \u{6} \u{7} \u{8} \u{A} \u{B} \u{C} ; ' \u{1C}
        ` \u{1A} \u{18} \u{3} \u{16} \u{2} \u{E} \u{D} , . /
      cmd: |
        § 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 , . -
      cmd+shift: |
        ° ! " # € % & / ( ) = ? `
          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 ; : _
      cmd+alt: |
        € © ™ £ € § \u{0} | [ ] ≈ ± `
          • , é \u{0} ŧ µ ü ı œ þ ˙ ˜
          ¯ ß ð ƒ \u{0} ħ ˝ ª ł ø æ '
        ≤ ÷ đ ¸ ‹ › ‘ ’ ‚ … –
  deadKeys:
    default: [´]
    shift: ['`']
    caps: [´]
    alt: ['-', ., ¨, ¯, ¸, ƒ, ʼ, ˀ, ˆ, ˇ, ˘, ˙, ˚, ˛, ˜, ˝]
    alt+shift: [ʔ, №]
    alt+caps: ['-', ., ¨, ¯, ¸, ƒ, ʼ, ˀ, ˆ, ˇ, ˘, ˙, ˚, ˛, ˜, ˝]
    cmd+alt: [',', ¯, ¸, ƒ, ˙, ˜, ˝]

  space:
    caps: '\u{A0}'
    alt: '\u{A0}'
    alt+shift: '\u{A0}'
    alt+caps: '\u{A0}'
    cmd+alt: '\u{A0}'

Layers

Each layer is split by line, typically to 3 lines of the keyboard. Keys in the format of \u{} are written in Unicode to avoid display issues. In particular, \u{0} is an absent key.

Dead keys

Dead keys

Space

Space

Android

Target configuration

A file for the desired target has to be specified, and in this case for Android, it could look like this:

packageId: no.uit.giella.keyboards.Sami
keyAlias: sami_keyboard
sentryDsn: https://8f857d788e764bd2a13aaafdf0018c92@sentry.io/1341912
version: 3.2.0
build: 113

The fields define what follows:

  • packageId - the id of the package

  • keyAlias - name

  • sentryDsn - url to sentry

  • version - current app version

  • build - current app build

  • key_store - store ??

  • key_alias - alias ??

  • play_store_account - play store email ??

  • play_store_p12 - play store ??

  • store_password - password ??

  • key_password - password ??

Keyboard layouts

Android layouts tend to be quite simple:

    android:
    config:
        spellerPackageKey: https://pahkat.uit.no/main/packages/speller-sme?platform=mobile
        spellerPath: se.bhfst
    primary:
        layers:
            default: |
                á š e r t y u i o p ŋ
                a s d f g h j k l đ ŧ
                \s{shift} ž z č c v b n m \s{backspace}
            shift: |
                Á Š E R T Y U I O P Ŋ
                A S D F G H J K L Đ Ŧ
                \s{shift} Ž Z Č C V B N M \s{backspace}

Longpress

A longpress section like this is also needed for alternative keys but isn’t specifically exclusive to Android but rather defined independently in the layout file and shared between Android and iOS

  longpress:
    A: Å Æ Ä   À Â Ã Ā Ạ
    Á: Q
    E: Ë É È Ê Ẽ Ē Ẹ Ė Ǝ
    I: Ï Í Ì Î Ĩ Ī Ị I
    ...

Config

The config section is used by Pahkat for spellchecking, the fields:

  • spellerPackageKey - defines the url for the spellchecking files of the layout we want to generate

  • spellerPath - defines which spellchecker file to use.

Layers

Just two layers are standard, default and shift. Each layer is split by line, typically to 3 lines of the keyboard. Special keys need to be preceeded by \s:

  • \s{shift} - Shift

  • \s{backspace} - Delete

  • \s{return} - Enter