Skip to content

feat: implement i18n support with ConfigProvider integration and locale-aware Thinking component#470

Open
IsDyh01 wants to merge 2 commits intoelement-plus-x:mainfrom
IsDyh01:feat/i18n
Open

feat: implement i18n support with ConfigProvider integration and locale-aware Thinking component#470
IsDyh01 wants to merge 2 commits intoelement-plus-x:mainfrom
IsDyh01:feat/i18n

Conversation

@IsDyh01
Copy link
Copy Markdown
Member

@IsDyh01 IsDyh01 commented Apr 6, 2026

Summary by CodeRabbit

Release Notes

  • New Features

    • Multi-language support with English and Chinese locales
    • ConfigProvider now accepts locale configuration for app-wide language settings
    • Plugin installation supports locale option parameter
    • Enhanced type exports for improved TypeScript support
  • Chores

    • Build configuration updates for locale file handling
    • Type declaration reorganization and improvements

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Apr 6, 2026

📝 Walkthrough

Walkthrough

This PR introduces a comprehensive internationalization system with new locale modules supporting Chinese and English, refactors the build configuration to auto-discover locale entry points, enhances ConfigProvider with hierarchical locale management, updates components to use locale utilities, and reorganizes exports to explicitly export all components and types.

Changes

Cohort / File(s) Summary
Build Configuration
packages/core/.build/build.ts, packages/core/.build/plugins/dts.ts, packages/core/.build/scripts/auto-export-all-components.ts
Updated glob patterns to exclude test/spec files; added discovery and mapping of src/locale/**/* entry points; switched from barrel components.ts export to direct component/type exports in index.ts; updated plugin install signature to accept ElementPlusXInstallOptions with locale parameter.
Locale System
packages/core/src/locale/types.ts, packages/core/src/locale/use-locale.ts, packages/core/src/locale/element-plus.ts, packages/core/src/locale/index.ts, packages/core/src/locale/lang/en.ts, packages/core/src/locale/lang/zh-cn.ts
New i18n infrastructure: added Language, LanguageConfig, and ThinkingLocale types; implemented translate(), buildTranslator(), and useLocale() utilities for reactive translation; created English and Chinese locale definitions; added resolveElementPlusLocale() mapper for Element Plus compatibility.
ConfigProvider Enhancement
packages/core/src/components/ConfigProvider/types.d.ts, packages/core/src/components/ConfigProvider/constants.ts, packages/core/src/components/ConfigProvider/hooks.ts, packages/core/src/components/ConfigProvider/index.vue
Added locale property to ConfigProvider props; strengthened APP_CONFIG_PROVIDE_KEY type as InjectionKey; introduced hierarchical config merging (mergeConfig, buildConfigProviderContext, provideGlobalConfig); updated component to provide Element Plus locale via ElConfigProvider.
Component Localization
packages/core/src/components/Thinking/index.vue, packages/core/src/components/Typewriter/index.vue
Integrated useLocale() hook; replaced hardcoded Chinese strings with translation keys for status labels and error messages in Thinking component; made Typewriter's markdown instance reactive via computed ref.
Type Re-exports
packages/core/src/components/XMarkdown/types.d.ts, packages/core/src/components/XMarkdownAsync/types.d.ts
New type declaration files re-exporting MarkdownProps and CodeXProps under component-scoped aliases (XMarkdownProps, XMarkdownAsyncProps).
Public API & Exports
packages/core/src/index.ts, packages/core/package.json
Replaced barrel export with explicit named exports of 17 components and 40+ types; added ElementPlusXInstallOptions interface; updated plugin install() to accept options and provision global locale config; added types/locale to package files allowlist.
Story Updates
packages/core/src/stories/Thinking/index.vue
Added locale preview demonstration wrapping Thinking component with ConfigProvider to showcase Chinese and English rendering.

Sequence Diagram

sequenceDiagram
    participant App as App.use(plugin)
    participant Install as install(app, options)
    participant GlobalConfig as provideGlobalConfig
    participant EPGlobal as Element Plus<br/>provideGlobalConfig
    participant Comp as Component
    participant UseLocale as useLocale()
    participant Translate as translate(path)

    App->>Install: options: { locale? }
    Install->>GlobalConfig: config from options.locale
    GlobalConfig->>GlobalConfig: buildConfigProviderContext<br/>(hierarchical merge)
    Install->>EPGlobal: resolveElementPlusLocale<br/>(options.locale)
    
    Comp->>UseLocale: hook initialization
    UseLocale->>UseLocale: compute lang from<br/>ConfigProvider.locale
    UseLocale->>Translate: bind t() to locale
    
    Comp->>Translate: t("elpx.thinking.start")
    Translate->>Translate: getLocaleValue<br/>(locale, path)
    Translate-->>Comp: translated string
    Comp->>Comp: render with locale
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~75 minutes

Poem

🐰 Hops with delight

With locales now in place, east and west speak true,
ConfigProvider weaves hierarchies bold and new,
Components translate with useLocale's grace,
A rabbit celebrates exports in their proper place! 🌍✨

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately describes the main changes: implementing i18n (internationalization) support with ConfigProvider integration and making the Thinking component locale-aware, which are the core objectives of this PR.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@IsDyh01 IsDyh01 requested a review from WengJianFei April 6, 2026 14:22
Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (2)
packages/core/src/stories/Thinking/index.vue (1)

18-19: ⚠️ Potential issue | 🟠 Major

Exposed API key in source code.

The hardcoded API key sk-vfjyscildobjnrijtcllnkhtcouidcxdgjxtldzqzeowrbga is visible in the source. Even if this is a free-tier or demo key, it could be scraped and abused. Consider using environment variables or removing it from committed code.

🔒 Suggested approach
-const API_KEY = 'sk-vfjyscildobjnrijtcllnkhtcouidcxdgjxtldzqzeowrbga';
+const API_KEY = import.meta.env.VITE_SILICONFLOW_API_KEY || '';

Then document that developers need to provide their own key via .env.local.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/core/src/stories/Thinking/index.vue` around lines 18 - 19, The file
exposes a hardcoded API key via the constant API_KEY; remove the literal and
read the key from an environment variable instead (e.g. replace API_KEY
assignment with code that uses import.meta.env.VITE_API_KEY or
process.env.VUE_APP_API_KEY depending on your build tool), add a runtime check
in the Thinking component (or the init function that uses API_KEY) to throw/log
a clear error if the env var is missing, and update README/.env.local docs to
instruct developers to supply their own key rather than committing secrets.
packages/core/src/components/Typewriter/index.vue (1)

26-38: ⚠️ Potential issue | 🟡 Minor

Multiple Typewriter components may register plugins redundantly on shared markdown-it instance.

When multiple Typewriter components share the same md instance (the default behavior when using BubbleList or multiple siblings without a custom ConfigProvider), each component independently calls initMarkdownPlugins() during setup. This causes the same plugins to be registered multiple times on the shared instance. Most markdown-it plugins lack guards against duplicate registration, potentially causing memory leaks or unexpected behavior.

Consider:

  1. Moving plugin registration to provideGlobalConfig (once per app/config scope) instead of per-component
  2. Tracking already-registered plugins to prevent duplicates, or
  3. Documenting that custom md instances should be used if registering plugins
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/core/src/components/Typewriter/index.vue` around lines 26 - 38, The
initMarkdownPlugins() function registers plugins on the shared md instance for
each Typewriter component, causing duplicate registrations; change
initMarkdownPlugins (or registration logic) to guard against repeats by tracking
already-registered plugins on the md instance itself (e.g., attach a unique
symbol/set like md.value[REGISTERED_PLUGINS] and before calling
md.value.use(plugin) check and add to that set), or alternatively move plugin
registration into provideGlobalConfig so plugins are applied once per app/config
scope; update checks for both configProvider.value.mdPlugins and props.mdPlugins
to consult this registry before calling md.value.use.
🧹 Nitpick comments (5)
packages/core/.build/scripts/auto-export-all-components.ts (1)

75-76: Sort the discovered component list before generating the barrel.

Sorting the directory names here will make the generated src/index.ts deterministic across filesystems and avoid noisy diffs.

♻️ Suggested change
-    const dirs = await fs.readdir(componentsDir);
+    const dirs = (await fs.readdir(componentsDir)).sort();
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/core/.build/scripts/auto-export-all-components.ts` around lines 75 -
76, The list of component directories read into the dirs variable from
componentsDir is not sorted, causing nondeterministic generated src/index.ts;
fix it by sorting the discovered names before generation (e.g., call .sort() or
.sort((a,b)=>a.localeCompare(b)) on the array returned by await
fs.readdir(componentsDir)) so the barrel creation in
auto-export-all-components.ts is deterministic and produces stable diffs.
packages/core/src/stories/Thinking/index.vue (1)

64-66: Fix style: brace placement.

ESLint reports that the closing curly brace should not appear on the same line as the subsequent block (} catch). This is a style enforcement from the project's linting rules.

🔧 Proposed fix for lines 64-66
-  } catch (err) {
+  }
+  catch (err) {
     console.error('解析数据时出错:', err);
   }
🔧 Proposed fix for lines 118-120
-  } catch (err) {
+  }
+  catch (err) {
     handleError(err);
   }

Also applies to: 118-120

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/core/src/stories/Thinking/index.vue` around lines 64 - 66, The catch
blocks violate the project's brace-style rule by keeping the closing curly on
the same line as the catch; change occurrences where you have "} catch (err) {"
to place the closing brace on its own line followed by the catch on the next
line (i.e. replace the "} catch (err) {" pattern around the
console.error('解析数据时出错:', err); block and the similar block at lines ~118-120 so
they become "}" then a new line "catch (err) {" to satisfy the linter.
packages/core/src/components/Typewriter/index.vue (1)

79-79: Fix style issues flagged by ESLint.

Multiple lines have style violations for if-newline and brace-style rules.

🔧 Proposed fixes

Line 79:

-  if (!props.content) return '';
+  if (!props.content)
+    return '';

Line 136:

-    } else {
+    }
+    else {

Line 149:

-  if (!props.typing || !contentCache.value) return;
+  if (!props.typing || !contentCache.value)
+    return;

Also applies to: 136-136, 149-149

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/core/src/components/Typewriter/index.vue` at line 79, The ESLint
errors come from single-line if statements like "if (!props.content) return '';"
which violate brace-style and if-newline rules; locate each occurrence in the
Typewriter component (checks against props.content in setup/render helpers or
any function such as setup() or format/formatContent methods) and replace the
single-line form with a braced block using a newline for the return, e.g. change
to "if (!props.content) { return ''; }" with the return on its own line inside
the braces so the statement complies with brace-style and if-newline rules for
all occurrences.
packages/core/src/locale/element-plus.ts (1)

16-16: Fix style: add newline after if statement.

ESLint reports that a newline is expected after the if condition per the antfu/if-newline rule.

🔧 Proposed fix
-  if (!localeName) return epZhCn;
+  if (!localeName)
+    return epZhCn;
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/core/src/locale/element-plus.ts` at line 16, The single-line if
statement "if (!localeName) return epZhCn;" violates the antfu/if-newline rule;
change it to a multi-line form so the consequent is on the next line (e.g.
transform the statement so the condition uses a newline before the return, or
expand to a block with braces) — update the occurrence referencing localeName
and epZhCn accordingly in the element-plus locale resolution code.
packages/core/src/index.ts (1)

38-53: Consider removing .d.ts extension from type import paths.

Importing directly from .d.ts files (e.g., './components/Attachments/types.d.ts') is valid but unconventional. Some bundlers or TypeScript configurations may not resolve these paths correctly. The standard pattern is to omit the extension or use .ts files that export types.

♻️ Suggested change
-export type {
-  AttachmentsEmits,
-  AttachmentsProps,
-  FileListProps
-} from './components/Attachments/types.d.ts';
+export type {
+  AttachmentsEmits,
+  AttachmentsProps,
+  FileListProps
+} from './components/Attachments/types';

Apply the same pattern to all type exports (lines 38-121).

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/core/src/index.ts` around lines 38 - 53, The exported type import
paths include explicit “.d.ts” extensions (e.g.,
'./components/Attachments/types.d.ts') which is non-standard and can cause
resolution issues; update each export statement that references a types.d.ts
file (such as the exports for AttachmentsEmits, AttachmentsProps, FileListProps,
BubbleEmits, BubbleProps, BubbleListEmits, BubbleListInstance,
BubbleListItemProps, BubbleListProps, ConfigProviderProps, MarkdownItPlugin,
etc.) to omit the “.d.ts” extension (e.g., './components/Attachments/types') so
the bundler/TS resolver will use normal module resolution for those type
modules.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@packages/core/.build/scripts/auto-export-all-components.ts`:
- Around line 80-81: The current inline arrow callback used to compute compName
(the dir.replace(/(^\w|-\w)/g, (m: string) => m.replace('-', '').toUpperCase()))
and similar occurrences at the other locations violate consistent-list-newline,
if-newline, and brace-style rules; change these inline single-expression arrow
callbacks to use a full arrow-body with braces and an explicit return (e.g., (m:
string) => { return m.replace('-', '').toUpperCase(); }) and ensure braces and
newlines follow the project's brace-style (opening brace on same line,
statements on new lines) and any multi-item arrays/objects are formatted to
satisfy consistent-list-newline; apply the same pattern to the other affected
symbols/occurrences at the noted locations so the script passes linting.

---

Outside diff comments:
In `@packages/core/src/components/Typewriter/index.vue`:
- Around line 26-38: The initMarkdownPlugins() function registers plugins on the
shared md instance for each Typewriter component, causing duplicate
registrations; change initMarkdownPlugins (or registration logic) to guard
against repeats by tracking already-registered plugins on the md instance itself
(e.g., attach a unique symbol/set like md.value[REGISTERED_PLUGINS] and before
calling md.value.use(plugin) check and add to that set), or alternatively move
plugin registration into provideGlobalConfig so plugins are applied once per
app/config scope; update checks for both configProvider.value.mdPlugins and
props.mdPlugins to consult this registry before calling md.value.use.

In `@packages/core/src/stories/Thinking/index.vue`:
- Around line 18-19: The file exposes a hardcoded API key via the constant
API_KEY; remove the literal and read the key from an environment variable
instead (e.g. replace API_KEY assignment with code that uses
import.meta.env.VITE_API_KEY or process.env.VUE_APP_API_KEY depending on your
build tool), add a runtime check in the Thinking component (or the init function
that uses API_KEY) to throw/log a clear error if the env var is missing, and
update README/.env.local docs to instruct developers to supply their own key
rather than committing secrets.

---

Nitpick comments:
In `@packages/core/.build/scripts/auto-export-all-components.ts`:
- Around line 75-76: The list of component directories read into the dirs
variable from componentsDir is not sorted, causing nondeterministic generated
src/index.ts; fix it by sorting the discovered names before generation (e.g.,
call .sort() or .sort((a,b)=>a.localeCompare(b)) on the array returned by await
fs.readdir(componentsDir)) so the barrel creation in
auto-export-all-components.ts is deterministic and produces stable diffs.

In `@packages/core/src/components/Typewriter/index.vue`:
- Line 79: The ESLint errors come from single-line if statements like "if
(!props.content) return '';" which violate brace-style and if-newline rules;
locate each occurrence in the Typewriter component (checks against props.content
in setup/render helpers or any function such as setup() or format/formatContent
methods) and replace the single-line form with a braced block using a newline
for the return, e.g. change to "if (!props.content) { return ''; }" with the
return on its own line inside the braces so the statement complies with
brace-style and if-newline rules for all occurrences.

In `@packages/core/src/index.ts`:
- Around line 38-53: The exported type import paths include explicit “.d.ts”
extensions (e.g., './components/Attachments/types.d.ts') which is non-standard
and can cause resolution issues; update each export statement that references a
types.d.ts file (such as the exports for AttachmentsEmits, AttachmentsProps,
FileListProps, BubbleEmits, BubbleProps, BubbleListEmits, BubbleListInstance,
BubbleListItemProps, BubbleListProps, ConfigProviderProps, MarkdownItPlugin,
etc.) to omit the “.d.ts” extension (e.g., './components/Attachments/types') so
the bundler/TS resolver will use normal module resolution for those type
modules.

In `@packages/core/src/locale/element-plus.ts`:
- Line 16: The single-line if statement "if (!localeName) return epZhCn;"
violates the antfu/if-newline rule; change it to a multi-line form so the
consequent is on the next line (e.g. transform the statement so the condition
uses a newline before the return, or expand to a block with braces) — update the
occurrence referencing localeName and epZhCn accordingly in the element-plus
locale resolution code.

In `@packages/core/src/stories/Thinking/index.vue`:
- Around line 64-66: The catch blocks violate the project's brace-style rule by
keeping the closing curly on the same line as the catch; change occurrences
where you have "} catch (err) {" to place the closing brace on its own line
followed by the catch on the next line (i.e. replace the "} catch (err) {"
pattern around the console.error('解析数据时出错:', err); block and the similar block
at lines ~118-120 so they become "}" then a new line "catch (err) {" to satisfy
the linter.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 0282b277-a312-473c-afd6-1ee5a4038514

📥 Commits

Reviewing files that changed from the base of the PR and between 6ffbf97 and fce1f83.

📒 Files selected for processing (20)
  • packages/core/.build/build.ts
  • packages/core/.build/plugins/dts.ts
  • packages/core/.build/scripts/auto-export-all-components.ts
  • packages/core/package.json
  • packages/core/src/components/ConfigProvider/constants.ts
  • packages/core/src/components/ConfigProvider/hooks.ts
  • packages/core/src/components/ConfigProvider/index.vue
  • packages/core/src/components/ConfigProvider/types.d.ts
  • packages/core/src/components/Thinking/index.vue
  • packages/core/src/components/Typewriter/index.vue
  • packages/core/src/components/XMarkdown/types.d.ts
  • packages/core/src/components/XMarkdownAsync/types.d.ts
  • packages/core/src/index.ts
  • packages/core/src/locale/element-plus.ts
  • packages/core/src/locale/index.ts
  • packages/core/src/locale/lang/en.ts
  • packages/core/src/locale/lang/zh-cn.ts
  • packages/core/src/locale/types.ts
  • packages/core/src/locale/use-locale.ts
  • packages/core/src/stories/Thinking/index.vue

Comment on lines 80 to +81
const compName = dir.replace(/(^\w|-\w)/g, (m: string) =>
m.replace('-', '').toUpperCase());

m.replace('-', '').toUpperCase()
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Fix the existing eslint violations in this script.

These lines already violate the configured consistent-list-newline, if-newline, and brace-style rules, so the helper itself stays red before it even formats the generated file.

🧹 Minimal lint-fix patch
-        const compName = dir.replace(/(^\w|-\w)/g, (m: string) =>
-          m.replace('-', '').toUpperCase()
-        );
+        const compName = dir.replace(/(^\w|-\w)/g, (m: string) => m.replace('-', '').toUpperCase());

-      if (!typeNames.length) return [];
+      if (!typeNames.length) {
+        return [];
+      }

-    } catch (error) {
+    }
+    catch (error) {
       console.warn('⚠️ Eslint formatting failed:', error);
     }
-  } catch (error) {
+  }
+  catch (error) {
     console.error('❌ Error generating auto-entry files:', error);
     exit(1);
   }

Also applies to: 101-101, 144-147

🧰 Tools
🪛 ESLint

[error] 80-81: Should not have line breaks between items, in node CallExpression

(antfu/consistent-list-newline)

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/core/.build/scripts/auto-export-all-components.ts` around lines 80 -
81, The current inline arrow callback used to compute compName (the
dir.replace(/(^\w|-\w)/g, (m: string) => m.replace('-', '').toUpperCase())) and
similar occurrences at the other locations violate consistent-list-newline,
if-newline, and brace-style rules; change these inline single-expression arrow
callbacks to use a full arrow-body with braces and an explicit return (e.g., (m:
string) => { return m.replace('-', '').toUpperCase(); }) and ensure braces and
newlines follow the project's brace-style (opening brace on same line,
statements on new lines) and any multi-item arrays/objects are formatted to
satisfy consistent-list-newline; apply the same pattern to the other affected
symbols/occurrences at the noted locations so the script passes linting.

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant