Skip to content

Conversation

@Innei
Copy link
Member

@Innei Innei commented Dec 24, 2025

Summary

  • Add keyStyle configuration option to control how dot-delimited keys are resolved when building diff entries
  • Supports three modes:
    • nested (default): treats dotted keys as nested paths
    • flat: flattens all nested paths to dot notation
    • auto: preserves the original key style from the entry file

Test plan

  • Added unit tests for flat and auto key styles in diffJson.test.ts
  • Verified behavior preserves backward compatibility (defaults to nested)
  • Updated documentation in README.md and README.zh-CN.md

🤖 Generated with Claude Code

Summary by Sourcery

Add configurable key style handling for dotted i18n keys and propagate it through diffing and translation workflows.

New Features:

  • Introduce a keyStyle configuration option to control how dot-delimited keys are represented in i18n diffs, supporting nested, flat, and auto modes.

Enhancements:

  • Update diffJson utility to resolve diff paths according to the configured key style and use it across translation and JSON-splitting flows.

Documentation:

  • Document the new keyStyle option in both English and Chinese README locale configuration tables.

Tests:

  • Add unit tests covering flat and auto keyStyle behaviors in diffJson.

Copilot AI review requested due to automatic review settings December 24, 2025 13:37
@sourcery-ai
Copy link

sourcery-ai bot commented Dec 24, 2025

Reviewer's Guide

Implements a configurable keyStyle option for diffing i18n JSON files, allowing control over whether dotted keys are treated as nested paths, flattened, or preserved, and wires this behavior through diff usage, config types, and documentation with accompanying tests.

Sequence diagram for diff usage with configurable keyStyle

sequenceDiagram
  participant TranslateLocale
  participant SplitJsonToChunks
  participant Diff
  participant JustDiff
  participant LodashSet as Lodash_set_unset

  TranslateLocale->>Diff: diff(entryObj, targetObj, config)
  Diff->>JustDiff: diff(targetObj, entryObj)
  JustDiff-->>Diff: diffResult
  loop for each remove item
    Diff->>Diff: resolveDiffPath(targetObj, item.path, config.keyStyle)
    Diff->>LodashSet: unset(cloneTarget, resolvedPath)
  end
  loop for each add item
    Diff->>Diff: resolveDiffPath(entryObj, item.path, config.keyStyle)
    Diff->>LodashSet: set(extra, resolvedPath, item.value)
  end
  Diff-->>TranslateLocale: { entry, target }

  SplitJsonToChunks->>Diff: diff(entry, target, config)
  Diff-->>SplitJsonToChunks: { entry: extraJSON, target }
  SplitJsonToChunks->>SplitJsonToChunks: splitJSONtoSmallChunks(extraJSON, splitToken)
Loading

Class diagram for updated i18n config and diff utilities

classDiagram
  class KeyStyle {
    <<type>>
    +auto
    +flat
    +nested
  }

  class I18nConfigLocale {
    +string entry
    +string entryLocale
    +string modelName
    +string output
    +string[] outputLocales
    +KeyStyle keyStyle
    +string reference
    +boolean saveImmediately
    +number splitToken
  }

  class DiffUtil {
    +diff(entry: LocaleObj, target: LocaleObj, config: I18nConfig) LocaleObj
    +resolveDiffPath(source: LocaleObj, path: DiffPath, keyStyle: KeyStyle) DiffPath
    +hasOwnKey(obj: LocaleObj, key: string) boolean
  }

  class LocaleObj {
    <<type>>
  }

  class DiffPath {
    <<type>>
    +string
    +Array~number|string~
  }

  class I18nConfig {
    +I18nConfigLocale localeConfig
  }

  I18nConfigLocale --> KeyStyle : uses
  I18nConfig --> I18nConfigLocale : contains
  DiffUtil --> LocaleObj : uses
  DiffUtil --> DiffPath : uses
  DiffUtil --> KeyStyle : uses
Loading

File-Level Changes

Change Details Files
Add configurable keyStyle handling to diff logic so dotted keys can be treated as nested, flat, or auto-resolved based on the entry JSON.
  • Introduce KeyStyle type and optional keyStyle field on I18nConfigLocale with default behavior documented as 'nested'.
  • Extend diff function signature to accept keyStyle via I18nConfig, defaulting to 'nested'.
  • Add resolveDiffPath helper to convert just-diff paths according to keyStyle, including preserving original dotted keys when present in auto mode.
  • Apply resolveDiffPath when unsetting removed keys from the cloned target object and when setting added keys into the extra object.
packages/lobe-i18n/src/types/config.ts
packages/lobe-i18n/src/utils/diffJson.ts
Propagate the new keyStyle option through translation and splitting workflows so they respect the configured key style when computing diffs.
  • Update TranslateLocale to pass the full config into diff when computing target objects for per-file and single-file locale modes.
  • Update splitJsonToChunks to pass config into diff when computing extraJSON prior to splitting.
packages/lobe-i18n/src/commands/TranslateLocale/index.tsx
packages/lobe-i18n/src/utils/splitJsonToChunks.ts
Add tests and documentation for keyStyle behavior and configuration.
  • Add unit tests covering flat and auto keyStyle behavior in diffJson tests, including flat flattening nested paths and auto preserving mixed key styles.
  • Document keyStyle in both English and Chinese READMEs, including description and default value, and adjust the locale config table formatting to accommodate the new option.
packages/lobe-i18n/src/utils/diffJson.test.ts
packages/lobe-i18n/README.md
packages/lobe-i18n/README.zh-CN.md

Tips and commands

Interacting with Sourcery

  • Trigger a new review: Comment @sourcery-ai review on the pull request.
  • Continue discussions: Reply directly to Sourcery's review comments.
  • Generate a GitHub issue from a review comment: Ask Sourcery to create an
    issue from a review comment by replying to it. You can also reply to a
    review comment with @sourcery-ai issue to create an issue from it.
  • Generate a pull request title: Write @sourcery-ai anywhere in the pull
    request title to generate a title at any time. You can also comment
    @sourcery-ai title on the pull request to (re-)generate the title at any time.
  • Generate a pull request summary: Write @sourcery-ai summary anywhere in
    the pull request body to generate a PR summary at any time exactly where you
    want it. You can also comment @sourcery-ai summary on the pull request to
    (re-)generate the summary at any time.
  • Generate reviewer's guide: Comment @sourcery-ai guide on the pull
    request to (re-)generate the reviewer's guide at any time.
  • Resolve all Sourcery comments: Comment @sourcery-ai resolve on the
    pull request to resolve all Sourcery comments. Useful if you've already
    addressed all the comments and don't want to see them anymore.
  • Dismiss all Sourcery reviews: Comment @sourcery-ai dismiss on the pull
    request to dismiss all existing Sourcery reviews. Especially useful if you
    want to start fresh with a new review - don't forget to comment
    @sourcery-ai review to trigger a new review!

Customizing Your Experience

Access your dashboard to:

  • Enable or disable review features such as the Sourcery-generated pull request
    summary, the reviewer's guide, and others.
  • Change the review language.
  • Add, remove or edit custom review instructions.
  • Adjust other review settings.

Getting Help

@lobehubbot
Copy link
Member

👍 @Innei

Thank you for raising your pull request and contributing to our Community
Please make sure you have followed our contributing guidelines. We will review it as soon as possible.
If you encounter any problems, please feel free to connect with us.
非常感谢您提出拉取请求并为我们的社区做出贡献,请确保您已经遵循了我们的贡献指南,我们会尽快审查它。
如果您遇到任何问题,请随时与我们联系。

Copy link

@sourcery-ai sourcery-ai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hey - I've reviewed your changes and they look great!


Sourcery is free for open source - if you like our reviews please consider sharing them ✨
Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.

Copy link

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR adds a keyStyle configuration option to control how dot-delimited keys are resolved when building translation diff entries. The feature supports three modes: nested (default, for backward compatibility), flat (converts all nested paths to dot notation), and auto (preserves the original key style from entry files).

Key Changes

  • Added KeyStyle type and keyStyle configuration property to handle different key resolution strategies
  • Updated diff() function to accept and apply keyStyle configuration when resolving paths
  • Added test coverage for flat and auto modes with nested object structures

Reviewed changes

Copilot reviewed 7 out of 7 changed files in this pull request and generated 3 comments.

Show a summary per file
File Description
packages/lobe-i18n/src/types/config.ts Defines KeyStyle type and adds keyStyle property to I18nConfigLocale interface
packages/lobe-i18n/src/utils/diffJson.ts Implements resolveDiffPath function and updates diff to support keyStyle modes
packages/lobe-i18n/src/utils/diffJson.test.ts Adds test cases for flat and auto keyStyle modes
packages/lobe-i18n/src/utils/splitJsonToChunks.ts Passes config to diff function to enable keyStyle support
packages/lobe-i18n/src/commands/TranslateLocale/index.tsx Updates diff calls to pass config parameter in both folder and flat query modes
packages/lobe-i18n/README.md Documents keyStyle configuration option in English
packages/lobe-i18n/README.zh-CN.md Documents keyStyle configuration option in Chinese

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +13 to +19
if (keyStyle === 'flat') {
return [path.join('.')];
}
if (keyStyle === 'auto') {
const joinedPath = path.join('.');
if (hasOwnKey(source, joinedPath)) return [joinedPath];
}
Copy link

Copilot AI Dec 24, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The flat mode converts array paths to dotted strings using join('.'), which may cause issues when the path includes array indices. For example, a path ['array', 3] (representing array[3]) would become ['array.3'], creating a literal object key "array.3" instead of preserving the array structure. Consider checking if any path elements are numbers (array indices) and handling them specially to preserve array semantics.

Suggested change
if (keyStyle === 'flat') {
return [path.join('.')];
}
if (keyStyle === 'auto') {
const joinedPath = path.join('.');
if (hasOwnKey(source, joinedPath)) return [joinedPath];
}
const hasNumericSegment = path.some((segment) => typeof segment === 'number');
if (keyStyle === 'flat') {
// In flat mode, only join segments when all are strings to avoid
// turning array indices into dotted string keys (e.g. ['array', 3] -> 'array.3').
if (!hasNumericSegment) {
return [path.join('.')];
}
return path;
}
if (keyStyle === 'auto') {
// In auto mode, prefer a flat key only when all path segments are strings
// and such a key actually exists on the source object.
if (!hasNumericSegment) {
const joinedPath = path.join('.');
if (hasOwnKey(source, joinedPath)) return [joinedPath];
}
}

Copilot uses AI. Check for mistakes.
Comment on lines +82 to +120
it('should flatten nested paths when keyStyle is flat', () => {
const entry = {
a: {
bc: {
b: 'value',
},
},
};
const target = {
a: {
bc: {},
},
};

const result = diff(entry, target, { keyStyle: 'flat' });

expect(result.entry).toEqual({
'a.bc.b': 'value',
});
});

it('should preserve mixed keys when keyStyle is auto', () => {
const entry = {
'a.bc.b': 'value',
'nested': {
key: 'value2',
},
};
const target = {};

const result = diff(entry, target, { keyStyle: 'auto' });

expect(result.entry).toEqual({
'a.bc.b': 'value',
'nested': {
key: 'value2',
},
});
});
Copy link

Copilot AI Dec 24, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The test coverage for the new keyStyle modes (flat and auto) should include test cases with arrays to ensure the behavior is correct when dealing with array indices. Currently, only the default nested mode has an array test case (line 260-276).

Copilot uses AI. Check for mistakes.
*/
entryLocale: string;
/**
* @description How to resolve dot-delimited keys when building diff entries
Copy link

Copilot AI Dec 24, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The JSDoc description for keyStyle could be more detailed to help developers understand what each mode does. Consider expanding it to explain: nested treats dots as path separators (a.b.c becomes {a: {b: {c: value}}}), flat preserves dotted keys as literal strings, and auto detects whether the entry file uses dotted keys or nested structure and preserves that format.

Suggested change
* @description How to resolve dot-delimited keys when building diff entries
* @description How to resolve dot-delimited keys when building diff entries.
* - `nested`: treats dots as path separators (e.g. `a.b.c` becomes `{ a: { b: { c: value } } }`).
* - `flat`: preserves dotted keys as literal strings (e.g. `a.b.c` stays as the single key `"a.b.c"`).
* - `auto`: detects whether the entry file uses dotted keys or nested objects and preserves that format.

Copilot uses AI. Check for mistakes.
@canisminor1990 canisminor1990 merged commit 49cab44 into master Dec 25, 2025
3 checks passed
@canisminor1990 canisminor1990 deleted the feat-i18n-key-style branch December 25, 2025 04:21
@lobehubbot
Copy link
Member

❤️ Great PR @Innei ❤️

The growth of project is inseparable from user feedback and contribution, thanks for your contribution!
项目的成长离不开用户反馈和贡献,感谢您的贡献!

github-actions bot pushed a commit that referenced this pull request Dec 25, 2025
## [Version&nbsp;1.26.0](https://github.com/lobehub/lobe-cli-toolbox/compare/@lobehub/[email protected]...@lobehub/[email protected])
<sup>Released on **2025-12-25**</sup>

#### ✨ Features

- **misc**: Update models info and add keyStyle for i18n dotted keys.

<br/>

<details>
<summary><kbd>Improvements and Fixes</kbd></summary>

#### What's improved

* **misc**: Update models info and add keyStyle for i18n dotted keys, closes [#160](#160) ([49cab44](49cab44))

</details>

<div align="right">

[![](https://img.shields.io/badge/-BACK_TO_TOP-151515?style=flat-square)](#readme-top)

</div>
@lobehubbot
Copy link
Member

🎉 This PR is included in version 1.26.0 🎉

The release is available on npm package (@latest dist-tag)

Your semantic-release bot 📦🚀

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants