diff --git a/CustomKeyboard/Model/KBKeyboardLayoutConfig.m b/CustomKeyboard/Model/KBKeyboardLayoutConfig.m index c8a2524..94a0c97 100644 --- a/CustomKeyboard/Model/KBKeyboardLayoutConfig.m +++ b/CustomKeyboard/Model/KBKeyboardLayoutConfig.m @@ -114,6 +114,38 @@ static NSString * const kKBKeyboardLayoutI18nFileName = @"kb_keyboard_layouts_i1 } } +- (NSArray *)kb_mergeRowsFromBase:(NSArray *)baseRows + override:(NSArray *)overrideRows { + if (baseRows.count == 0) { return overrideRows ?: @[]; } + if (overrideRows.count == 0) { return baseRows; } + + NSUInteger maxCount = MAX(baseRows.count, overrideRows.count); + NSMutableArray *merged = [NSMutableArray arrayWithCapacity:maxCount]; + for (NSUInteger i = 0; i < maxCount; i++) { + KBKeyboardRowConfig *baseRow = (i < baseRows.count) ? baseRows[i] : nil; + KBKeyboardRowConfig *overrideRow = (i < overrideRows.count) ? overrideRows[i] : nil; + if (!baseRow) { + if (overrideRow) { [merged addObject:overrideRow]; } + continue; + } + if (!overrideRow) { + [merged addObject:baseRow]; + continue; + } + KBKeyboardRowConfig *row = [KBKeyboardRowConfig new]; + row.height = baseRow.height ?: overrideRow.height; + row.insetLeft = baseRow.insetLeft ?: overrideRow.insetLeft; + row.insetRight = baseRow.insetRight ?: overrideRow.insetRight; + row.gap = baseRow.gap ?: overrideRow.gap; + row.align = baseRow.align.length > 0 ? baseRow.align : overrideRow.align; + BOOL hasOverrideItems = [overrideRow.items isKindOfClass:[NSArray class]] && ((NSArray *)overrideRow.items).count > 0; + row.items = hasOverrideItems ? overrideRow.items : baseRow.items; + row.segments = overrideRow.segments ?: baseRow.segments; + [merged addObject:row]; + } + return merged.copy; +} + - (void)kb_loadI18nConfig { NSString *path = [[NSBundle mainBundle] pathForResource:kKBKeyboardLayoutI18nFileName ofType:@"json"]; NSData *data = path.length ? [NSData dataWithContentsOfFile:path] : nil; @@ -143,9 +175,18 @@ static NSString * const kKBKeyboardLayoutI18nFileName = @"kb_keyboard_layouts_i1 return; } KBKeyboardLayout *layout = [KBKeyboardLayout mj_objectWithKeyValues:obj]; - if (layout) { + if (!layout) { return; } + + KBKeyboardLayout *baseLayout = mergedLayouts[key]; + if (!baseLayout) { mergedLayouts[key] = layout; + return; } + + KBKeyboardLayout *mergedLayout = [KBKeyboardLayout new]; + mergedLayout.rows = [self kb_mergeRowsFromBase:baseLayout.rows override:layout.rows]; + mergedLayout.shiftRows = [self kb_mergeRowsFromBase:baseLayout.shiftRows override:layout.shiftRows]; + mergedLayouts[key] = mergedLayout; }]; self.layouts = mergedLayouts.copy; diff --git a/CustomKeyboard/Resource/kb_keyboard_layout_config.json b/CustomKeyboard/Resource/kb_keyboard_layout_config.json index 54ee705..874eab5 100644 --- a/CustomKeyboard/Resource/kb_keyboard_layout_config.json +++ b/CustomKeyboard/Resource/kb_keyboard_layout_config.json @@ -246,6 +246,240 @@ } ] }, + "letters_es": { + "__comment": "西班牙语布局(QWERTY)", + "rows": [ + { + "__comment": "字母第一行 qwertyuiop", + "align": "left", + "__comment_align": "对齐方式:left/center", + "insetLeft": 4, + "__comment_insetLeft": "本行左边距(覆盖 metrics.edgeInset)", + "insetRight": 4, + "__comment_insetRight": "本行右边距(覆盖 metrics.edgeInset)", + "gap": 5, + "__comment_gap": "本行按键间距(覆盖 metrics.gap)", + "items": [ + "letter:q", "letter:w", "letter:e", "letter:r", "letter:t", + "letter:y", "letter:u", "letter:i", "letter:o", "letter:p" + ], + "__comment_items": "本行按键列表;letter:x/digit:x/sym:x 或 keyDefs 中的 id" + }, + { + "__comment": "字母第二行 asdfghjkl", + "align": "left", + "__comment_align": "对齐方式:left/center", + "insetLeft": 4, + "__comment_insetLeft": "本行左边距(覆盖 metrics.edgeInset)", + "insetRight": 4, + "__comment_insetRight": "本行右边距(覆盖 metrics.edgeInset)", + "gap": 5, + "__comment_gap": "本行按键间距(覆盖 metrics.gap)", + "items": [ + "letter:a", "letter:s", "letter:d", "letter:f", "letter:g", + "letter:h", "letter:j", "letter:k", "letter:l" + ], + "__comment_items": "本行按键列表;letter:x/digit:x/sym:x 或 keyDefs 中的 id" + }, + { + "__comment": "字母第三行:左 shift,中间字母,右 backspace", + "align": "left", + "__comment_align": "对齐方式:left/center", + "insetLeft": 4, + "__comment_insetLeft": "本行左边距(覆盖 metrics.edgeInset)", + "insetRight": 4, + "__comment_insetRight": "本行右边距(覆盖 metrics.edgeInset)", + "gap": 5, + "__comment_gap": "本行按键间距(覆盖 metrics.gap)", + "segments": { + "__comment": "分段布局:left/center/right", + "left": [ + { "id": "shift", "width": "controlWidth", "__comment_id": "引用 keyDefs 的 id", "__comment_width": "宽度引用 metrics.controlWidth" } + ], + "__comment_left": "左侧固定按钮", + "center": [ + "letter:z", "letter:x", "letter:c", "letter:v", "letter:b", "letter:n", "letter:m" + ], + "__comment_center": "中间字母键集合,整体居中", + "right": [ + { "id": "backspace", "width": "controlWidth", "__comment_id": "引用 keyDefs 的 id", "__comment_width": "宽度引用 metrics.controlWidth" } + ], + "__comment_right": "右侧固定按钮" + } + }, + { + "__comment": "字母第四行:123/emoji/space/send", + "align": "left", + "__comment_align": "对齐方式:left/center", + "insetLeft": 4, + "__comment_insetLeft": "本行左边距(覆盖 metrics.edgeInset)", + "insetRight": 4, + "__comment_insetRight": "本行右边距(覆盖 metrics.edgeInset)", + "gap": 5, + "__comment_gap": "本行按键间距(覆盖 metrics.gap)", + "items": [ + "mode_123", "emoji", "space", "send" + ], + "__comment_items": "本行按键列表;letter:x/digit:x/sym:x 或 keyDefs 中的 id" + } + ] + }, + "letters_id": { + "__comment": "印度尼西亚语布局(QWERTY)", + "rows": [ + { + "__comment": "字母第一行 qwertyuiop", + "align": "left", + "__comment_align": "对齐方式:left/center", + "insetLeft": 4, + "__comment_insetLeft": "本行左边距(覆盖 metrics.edgeInset)", + "insetRight": 4, + "__comment_insetRight": "本行右边距(覆盖 metrics.edgeInset)", + "gap": 5, + "__comment_gap": "本行按键间距(覆盖 metrics.gap)", + "items": [ + "letter:q", "letter:w", "letter:e", "letter:r", "letter:t", + "letter:y", "letter:u", "letter:i", "letter:o", "letter:p" + ], + "__comment_items": "本行按键列表;letter:x/digit:x/sym:x 或 keyDefs 中的 id" + }, + { + "__comment": "字母第二行 asdfghjkl", + "align": "left", + "__comment_align": "对齐方式:left/center", + "insetLeft": 23, + "__comment_insetLeft": "本行左边距(覆盖 metrics.edgeInset)", + "insetRight": 0, + "__comment_insetRight": "本行右边距(覆盖 metrics.edgeInset)", + "gap": 5, + "__comment_gap": "本行按键间距(覆盖 metrics.gap)", + "items": [ + "letter:a", "letter:s", "letter:d", "letter:f", "letter:g", + "letter:h", "letter:j", "letter:k", "letter:l" + ], + "__comment_items": "本行按键列表;letter:x/digit:x/sym:x 或 keyDefs 中的 id" + }, + { + "__comment": "字母第三行:左 shift,中间字母,右 backspace", + "align": "left", + "__comment_align": "对齐方式:left/center", + "insetLeft": 4, + "__comment_insetLeft": "本行左边距(覆盖 metrics.edgeInset)", + "insetRight": 4, + "__comment_insetRight": "本行右边距(覆盖 metrics.edgeInset)", + "gap": 5, + "__comment_gap": "本行按键间距(覆盖 metrics.gap)", + "segments": { + "__comment": "分段布局:left/center/right", + "left": [ + { "id": "shift", "width": "controlWidth", "__comment_id": "引用 keyDefs 的 id", "__comment_width": "宽度引用 metrics.controlWidth" } + ], + "__comment_left": "左侧固定按钮", + "center": [ + "letter:z", "letter:x", "letter:c", "letter:v", "letter:b", "letter:n", "letter:m" + ], + "__comment_center": "中间字母键集合,整体居中", + "right": [ + { "id": "backspace", "width": "controlWidth", "__comment_id": "引用 keyDefs 的 id", "__comment_width": "宽度引用 metrics.controlWidth" } + ], + "__comment_right": "右侧固定按钮" + } + }, + { + "__comment": "字母第四行:123/emoji/space/send", + "align": "left", + "__comment_align": "对齐方式:left/center", + "insetLeft": 4, + "__comment_insetLeft": "本行左边距(覆盖 metrics.edgeInset)", + "insetRight": 4, + "__comment_insetRight": "本行右边距(覆盖 metrics.edgeInset)", + "gap": 5, + "__comment_gap": "本行按键间距(覆盖 metrics.gap)", + "items": [ + "mode_123", "emoji", "space", "send" + ], + "__comment_items": "本行按键列表;letter:x/digit:x/sym:x 或 keyDefs 中的 id" + } + ] + }, + "letters_pt": { + "__comment": "葡萄牙语布局(QWERTY)", + "rows": [ + { + "__comment": "字母第一行 qwertyuiop", + "align": "left", + "__comment_align": "对齐方式:left/center", + "insetLeft": 4, + "__comment_insetLeft": "本行左边距(覆盖 metrics.edgeInset)", + "insetRight": 4, + "__comment_insetRight": "本行右边距(覆盖 metrics.edgeInset)", + "gap": 5, + "__comment_gap": "本行按键间距(覆盖 metrics.gap)", + "items": [ + "letter:q", "letter:w", "letter:e", "letter:r", "letter:t", + "letter:y", "letter:u", "letter:i", "letter:o", "letter:p" + ], + "__comment_items": "本行按键列表;letter:x/digit:x/sym:x 或 keyDefs 中的 id" + }, + { + "__comment": "字母第二行 asdfghjkl", + "align": "left", + "__comment_align": "对齐方式:left/center", + "insetLeft": 23, + "__comment_insetLeft": "本行左边距(覆盖 metrics.edgeInset)", + "insetRight": 0, + "__comment_insetRight": "本行右边距(覆盖 metrics.edgeInset)", + "gap": 5, + "__comment_gap": "本行按键间距(覆盖 metrics.gap)", + "items": [ + "letter:a", "letter:s", "letter:d", "letter:f", "letter:g", + "letter:h", "letter:j", "letter:k", "letter:l" + ], + "__comment_items": "本行按键列表;letter:x/digit:x/sym:x 或 keyDefs 中的 id" + }, + { + "__comment": "字母第三行:左 shift,中间字母,右 backspace", + "align": "left", + "__comment_align": "对齐方式:left/center", + "insetLeft": 4, + "__comment_insetLeft": "本行左边距(覆盖 metrics.edgeInset)", + "insetRight": 4, + "__comment_insetRight": "本行右边距(覆盖 metrics.edgeInset)", + "gap": 5, + "__comment_gap": "本行按键间距(覆盖 metrics.gap)", + "segments": { + "__comment": "分段布局:left/center/right", + "left": [ + { "id": "shift", "width": "controlWidth", "__comment_id": "引用 keyDefs 的 id", "__comment_width": "宽度引用 metrics.controlWidth" } + ], + "__comment_left": "左侧固定按钮", + "center": [ + "letter:z", "letter:x", "letter:c", "letter:v", "letter:b", "letter:n", "letter:m" + ], + "__comment_center": "中间字母键集合,整体居中", + "right": [ + { "id": "backspace", "width": "controlWidth", "__comment_id": "引用 keyDefs 的 id", "__comment_width": "宽度引用 metrics.controlWidth" } + ], + "__comment_right": "右侧固定按钮" + } + }, + { + "__comment": "字母第四行:123/emoji/space/send", + "align": "left", + "__comment_align": "对齐方式:left/center", + "insetLeft": 4, + "__comment_insetLeft": "本行左边距(覆盖 metrics.edgeInset)", + "insetRight": 4, + "__comment_insetRight": "本行右边距(覆盖 metrics.edgeInset)", + "gap": 5, + "__comment_gap": "本行按键间距(覆盖 metrics.gap)", + "items": [ + "mode_123", "emoji", "space", "send" + ], + "__comment_items": "本行按键列表;letter:x/digit:x/sym:x 或 keyDefs 中的 id" + } + ] + }, "numbers": { "__comment": "数字面板布局(123 页)", "rows": [ diff --git a/CustomKeyboard/Resource/kb_keyboard_layouts_i18n.json b/CustomKeyboard/Resource/kb_keyboard_layouts_i18n.json index 251bcde..a9a98e4 100644 --- a/CustomKeyboard/Resource/kb_keyboard_layouts_i18n.json +++ b/CustomKeyboard/Resource/kb_keyboard_layouts_i18n.json @@ -1651,4 +1651,4 @@ ] } } -} \ No newline at end of file +}