Skip to content

Conversation

kazupon
Copy link
Member

@kazupon kazupon commented Jun 16, 2025

Summary by CodeRabbit

  • New Features

    • Introduced comprehensive TypeScript API documentation and type declarations for Vue I18n, including core, composition, and general usage.
    • Added new markdown documentation placeholders for components, directives, and legacy API.
  • Bug Fixes

    • Added error handling to prevent multiple local useI18n calls within the same component, providing a clear error message.
  • Tests

    • Added a test case to verify that duplicate local useI18n calls within a component trigger the expected error.

Copy link

coderabbitai bot commented Jun 16, 2025

Important

Review skipped

Auto reviews are disabled on base/target branches other than the default branch.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Walkthrough

A new error code and message were added to prevent duplicate useI18n calls in the same component with local scope. The useI18n function now throws an error if called multiple times locally. Corresponding tests were introduced. Several new markdown files were added, with v11/composition.md and v11/general.md providing detailed TypeScript API documentation.

Changes

File(s) Change Summary
packages/vue-i18n-core/src/errors.ts Added error code/message for duplicate local useI18n calls; minor import order and formatting adjustments.
packages/vue-i18n-core/src/i18n.ts Added runtime check to throw error on duplicate local useI18n calls in the same component.
packages/vue-i18n-core/test/i18n.test.ts Added test to verify error is thrown for duplicate local useI18n calls.
v11/composition.md Added comprehensive TypeScript API documentation for the Vue 3 Composition API integration of vue-i18n.
v11/general.md Added detailed TypeScript API documentation and type declarations for Vue I18n core types and functions.
v11/component.md, v11/directive.md, v11/legacy.md Added new markdown files with only a heading or placeholder text.

Sequence Diagram(s)

sequenceDiagram
    participant Component
    participant useI18n
    participant Error

    Component->>useI18n: Call useI18n({ scope: 'local' })
    useI18n->>Component: Return Composer instance
    Component->>useI18n: Call useI18n({ scope: 'local' }) again
    useI18n-->>Error: Throw DUPLICATE_USE_I18N_CALLING
    Error-->>Component: Error message
Loading

Poem

In the warren of code, a rule now appears,
No double dipping in i18n burrows, my dears!
If you call useI18n twice in the same local space,
A new error will hop in and show its face.
Tests and docs sprout up, all shiny and new—
The garden of i18n grows, thanks to this crew!
🐇✨


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
🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Explain this complex logic.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query. Examples:
    • @coderabbitai explain this code block.
    • @coderabbitai modularize this function.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read src/utils.ts and explain its main purpose.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.
    • @coderabbitai help me debug CodeRabbit configuration file.

Support

Need help? Create a ticket on our support page for assistance with any issues or questions.

Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments.

CodeRabbit Commands (Invoked using PR comments)

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai full review to do a full review from scratch and review all the files again.
  • @coderabbitai summary to regenerate the summary of the PR.
  • @coderabbitai generate docstrings to generate docstrings for this PR.
  • @coderabbitai generate sequence diagram to generate a sequence diagram of the changes in this PR.
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @coderabbitai configuration to show the current CodeRabbit configuration for the repository.
  • @coderabbitai help to get help.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

CodeRabbit Configuration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Documentation and Community

  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

@kazupon kazupon added the Type: Improvement Includes backwards-compatible fixes label Jun 16, 2025
@kazupon
Copy link
Member Author

kazupon commented Jun 16, 2025

@coderabbitai review

Copy link

coderabbitai bot commented Jun 16, 2025

✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

Copy link

cloudflare-workers-and-pages bot commented Jun 16, 2025

Deploying vue-i18n-next with  Cloudflare Pages  Cloudflare Pages

Latest commit: 93f1aa9
Status: ✅  Deploy successful!
Preview URL: https://1a95494c.vue-i18n-next.pages.dev
Branch Preview URL: https://fix-error-on-duplicate-calli.vue-i18n-next.pages.dev

View logs

Copy link

pkg-pr-new bot commented Jun 16, 2025

Open in StackBlitz

@intlify/core

npm i https://pkg.pr.new/@intlify/core@2203

@intlify/core-base

npm i https://pkg.pr.new/@intlify/core-base@2203

@intlify/devtools-types

npm i https://pkg.pr.new/@intlify/devtools-types@2203

@intlify/message-compiler

npm i https://pkg.pr.new/@intlify/message-compiler@2203

petite-vue-i18n

npm i https://pkg.pr.new/petite-vue-i18n@2203

@intlify/shared

npm i https://pkg.pr.new/@intlify/shared@2203

vue-i18n

npm i https://pkg.pr.new/vue-i18n@2203

@intlify/vue-i18n-core

npm i https://pkg.pr.new/@intlify/vue-i18n-core@2203

commit: 93f1aa9

Copy link

@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: 2

🧹 Nitpick comments (15)
packages/vue-i18n-core/src/errors.ts (2)

29-31: Trailing comma helps future diffs

Consider adding a trailing comma after the new DUPLICATE_USE_I18N_CALLING entry. It’s syntactically optional but keeps future diffs cleaner when new codes are appended.

-  DUPLICATE_USE_I18N_CALLING: 35
+  DUPLICATE_USE_I18N_CALLING: 35,

62-64: Polish the error-message wording

Current wording is slightly awkward (“calling by local scope”). A small tweak improves clarity:

-    "Duplicate `useI18n` calling by local scope. Please don't call it on local scope"
+    "Duplicate `useI18n` call in local scope. Please don't call it in local scope"
v11/component.md (1)

1-1: Placeholder heading only – add TODO or flesh out content

This file currently contains only a heading. Either:

  1. Add at least a brief stub section (purpose, links), or
  2. Mark it clearly as “Work in progress” to avoid confusing readers.
v11/legacy.md (1)

1-1: Same placeholder concern as other docs

Consider adding minimal content or a TODO marker so that generated docs don’t look incomplete.

v11/directive.md (1)

1-1: Single-line placeholder

As with the other new docs, either flesh this out or mark it as pending.

packages/vue-i18n-core/test/i18n.test.ts (1)

650-661: Variable shadowing of i18n inside setup()

const i18n = useI18n({ … }) shadows the outer i18n instance created via createI18n.
While harmless at runtime, it obscures the distinction between the global instance and the component-local composer and can confuse future readers.

-const i18n = useI18n({
+const localComposer = useI18n({

Renaming improves readability without behaviour change.

v11/composition.md (1)

1608-1614: Fix typo in explanatory text

-You need to understand and use the structure of the locale messge returned by `tm`.
+You need to understand and use the structure of the locale message returned by `tm`.
v11/general.md (8)

40-44: Document all type parameters.

The createI18n signature includes additional generic parameters (Options, Messages, DateTimeFormats, NumberFormats, OptionLocale) that aren’t documented in the “Type Parameters” table. Please extend the table to cover these parameters for completeness.


54-54: Fix grammar in the parameters table.

Change the description from “An options, see …” to “Options, see …”.

-| options   | Options | An options, see the [I18nOptions](general#i18noptions) |
+| options   | Options | Options, see the [I18nOptions](general#i18noptions)    |

62-63: Use consistent code-block language.

The example is TypeScript, so update the fence from js to typescript for clarity:

-```js
+```typescript

213-213: Add missing article.

Revise to include “a” before “proxy-like property”:

-This property is proxy-like property for `Composer#availableLocales`.
+This property is a proxy-like property for `Composer#availableLocales`.

227-227: Add missing article.

Revise to include “a” before “proxy-like property”:

-This property is proxy-like property for `Composer#fallbackLocale`.
+This property is a proxy-like property for `Composer#fallbackLocale`.

299-299: Fix grammar in parameters table.

Update “An install options” to “Install options”:

-| options   | unknown[] | An install options        |
+| options   | unknown[] | Install options            |

379-379: Fix grammar in details.

Update “An options specified when installing…” to “Options specified when installing…”:

-**Details**
-An options specified when installing Vue I18n as Vue plugin with using `app.use`.
+**Details**
+Options specified when installing Vue I18n as a Vue plugin via `app.use`.

453-455: Correct infinitive usage.

Revise to use “to resolve” instead of “do resolve”:

-Whether do resolve on format keys when your language lacks a formatting for a key
+Whether to resolve format keys when your language lacks a format for a key
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 97d167f and 19a56de.

⛔ Files ignored due to path filters (1)
  • packages/vue-i18n-core/test/__snapshots__/i18n.test.ts.snap is excluded by !**/*.snap
📒 Files selected for processing (8)
  • packages/vue-i18n-core/src/errors.ts (3 hunks)
  • packages/vue-i18n-core/src/i18n.ts (1 hunks)
  • packages/vue-i18n-core/test/i18n.test.ts (1 hunks)
  • v11/component.md (1 hunks)
  • v11/composition.md (1 hunks)
  • v11/directive.md (1 hunks)
  • v11/general.md (1 hunks)
  • v11/legacy.md (1 hunks)
🧰 Additional context used
🧬 Code Graph Analysis (1)
packages/vue-i18n-core/src/i18n.ts (1)
packages/vue-i18n-core/src/errors.ts (2)
  • createI18nError (36-45)
  • I18nErrorCodes (10-32)
🪛 LanguageTool
v11/composition.md

[uncategorized] ~353-~353: Possible missing preposition found.
Context: ... Details Whether to allow the use locale messages of HTML formatting. If you se...

(AI_HYDRA_LEO_MISSING_OF)


[grammar] ~723-~723: It seems that a pronoun is missing.
Context: ...?: Locales): boolean; ``` Details whether do exist locale message on Composer ins...

(IF_VB)


[grammar] ~737-~737: “Value” is a singular noun. It appears that the verb form is incorrect.
Context: ...t false is returned even if the value present in the key is not translatable, yet if ...

(PCT_SINGULAR_NOUN_PLURAL_VERB_AGREEMENT)


[grammar] ~828-~828: Consider using either the past participle “extended” or the present participle “extending” here.
Context: ...tails** ComposerAdditionalOptions is extend for [ComposerOptions](composition#compo...

(BEEN_PART_AGREEMENT)


[style] ~895-~895: Three successive sentences begin with the same word. Consider rewording the sentence or use a thesaurus to find a synonym.
Context: ...ts than global scope datetime formats. If not, then it’s formatted with global sc...

(ENGLISH_WORD_REPEAT_BEGINNING_RULE)


[style] ~998-~998: Three successive sentences begin with the same word. Consider rewording the sentence or use a thesaurus to find a synonym.
Context: ...ts than global scope datetime formats. If not, then it’s formatted with global sc...

(ENGLISH_WORD_REPEAT_BEGINNING_RULE)


[uncategorized] ~1122-~1122: Possible missing comma found.
Context: ... If escapeParameter is configured as true then interpolation parameters are escap...

(AI_HYDRA_LEO_MISSING_COMMA)


[grammar] ~1122-~1122: Comparison is written ‘as true as’.
Context: ...escapeParameter is configured as true then interpolation parameters are escaped be...

(AS_ADJ_AS)


[grammar] ~1149-~1149: It seems that a pronoun is missing.
Context: ...backFormat?: boolean; ``` Details Whether do template interpolation on translatio...

(IF_VB)


[uncategorized] ~1174-~1174: Possible missing comma found.
Context: ...ocalization. For more complex fallback definitions see fallback. Default Value The d...

(AI_HYDRA_LEO_MISSING_COMMA)


[grammar] ~1194-~1194: The word “fallback” is a noun. The verb is spelled with a white space.
Context: ... the component localization, whether to fallback to root level (global scope) localizati...

(NOUN_VERB_CONFUSION)


[uncategorized] ~1256-~1256: Possible missing comma found.
Context: ...le?: boolean; ``` Details Whether inheritance the root level locale to the component ...

(AI_HYDRA_LEO_MISSING_COMMA)


[style] ~1387-~1387: You have already used this phrasing in nearby sentences. Consider replacing it to add variety to your writing.
Context: ...cale-2) is enabled, so the message will need to be resolved as well. The message resol...

(REP_NEED_TO_VB)


[style] ~1404-~1404: You have already used this phrasing in nearby sentences. Consider replacing it to add variety to your writing.
Context: ...) setting will be ignored. That is, you need to resolve the flat JSON by yourself. ::: ...

(REP_NEED_TO_VB)


[misspelling] ~1539-~1539: This word is normally spelled with a hyphen.
Context: ...eType>; ``` Details A handler for post processing of translation. The handler gets after...

(EN_COMPOUNDS_POST_PROCESSING)


[uncategorized] ~1543-~1543: Possible missing comma found.
Context: ...ful if you want to filter on translated text such as space trimming. **Default Valu...

(AI_HYDRA_LEO_MISSING_COMMA)


[uncategorized] ~1559-~1559: Possible missing preposition found.
Context: ... Details Whether to allow the use locale messages of HTML formatting. See the w...

(AI_HYDRA_LEO_MISSING_OF)


[style] ~1601-~1601: Three successive sentences begin with the same word. Consider rewording the sentence or use a thesaurus to find a synonym.
Context: ...ges than global scope locale messages. If not, then it’s translated with global s...

(ENGLISH_WORD_REPEAT_BEGINNING_RULE)


[grammar] ~1699-~1699: The plural noun “values” cannot be used with the article “A”. Did you mean “A value” or “values”?
Context: ... | A values of list interpolation. ...

(A_NNS)


[grammar] ~1739-~1739: The plural noun “values” cannot be used with the article “A”. Did you mean “A value” or “values”?
Context: ... | A values of named interpolation. ...

(A_NNS)


[style] ~1777-~1777: Three successive sentences begin with the same word. Consider rewording the sentence or use a thesaurus to find a synonym.
Context: ...ges than global scope locale messages. If not, then it’s translated with global s...

(ENGLISH_WORD_REPEAT_BEGINNING_RULE)


[uncategorized] ~1810-~1810: Possible missing comma found.
Context: ...press the warning, when the translation missing according to the options. See Also...

(AI_HYDRA_LEO_MISSING_COMMA)


[uncategorized] ~1908-~1908: Possible missing comma found.
Context: ...press the warning, when the translation missing according to the options. About detail...

(AI_HYDRA_LEO_MISSING_COMMA)


[uncategorized] ~1944-~1944: Possible missing comma found.
Context: ...press the warning, when the translation missing according to the options. See Also...

(AI_HYDRA_LEO_MISSING_COMMA)


[uncategorized] ~1977-~1977: Possible missing comma found.
Context: ...press the warning, when the translation missing according to the options. About detail...

(AI_HYDRA_LEO_MISSING_COMMA)


[uncategorized] ~2013-~2013: Possible missing comma found.
Context: ...press the warning, when the translation missing according to the options. #### Paramet...

(AI_HYDRA_LEO_MISSING_COMMA)


[uncategorized] ~2042-~2042: Possible missing comma found.
Context: ...press the warning, when the translation missing according to the options. About detail...

(AI_HYDRA_LEO_MISSING_COMMA)


[uncategorized] ~2074-~2074: Possible missing comma found.
Context: ...press the warning, when the translation missing according to the options. See Also...

(AI_HYDRA_LEO_MISSING_COMMA)


[uncategorized] ~2172-~2172: Possible missing comma found.
Context: ...press the warning, when the translation missing according to the options. About detail...

(AI_HYDRA_LEO_MISSING_COMMA)


[uncategorized] ~2243-~2243: Possible missing article found.
Context: ... by setup. If options are specified, Composer instance is created for each component ...

(AI_HYDRA_LEO_MISSING_A)


[uncategorized] ~2243-~2243: Use a comma before ‘and’ if it connects two independent clauses (unless they are closely connected and short).
Context: ...r instance is created for each component and you can be localized on the component. ...

(COMMA_COMPOUND_SENTENCE)


[grammar] ~2251-~2251: The plural noun “options” cannot be used with the article “An”. Did you mean “An option” or “options”?
Context: ...------------- | | options | Options | An options, see [UseI18nOptions](composition#usei1...

(A_NNS)

v11/general.md

[grammar] ~54-~54: The plural noun “options” cannot be used with the article “An”. Did you mean “An option” or “options”?
Context: ...------------- | | options | Options | An options, see the [I18nOptions](general#i18nopti...

(A_NNS)


[uncategorized] ~213-~213: Possible missing article found.
Context: ...e[]; ``` Details This property is proxy-like property for `Composer#availableLo...

(AI_HYDRA_LEO_MISSING_A)


[uncategorized] ~227-~227: Possible missing article found.
Context: ...cale ``` Details This property is proxy-like property for `Composer#fallbackLoc...

(AI_HYDRA_LEO_MISSING_A)


[grammar] ~299-~299: The word ‘install’ is not a noun.
Context: ...instance | | options | unknown[] | An install options | ## I18nAdditionalOpti...

(A_INSTALL)


[grammar] ~377-~377: The plural noun “options” cannot be used with the article “An”. Did you mean “An option” or “options”?
Context: ...ace I18nPluginOptions ``` Details An options specified when installing Vue I18n as V...

(A_NNS)


[grammar] ~452-~452: It seems that a pronoun is missing.
Context: ...llbackWarn?: boolean; ``` Details Whether do resolve on format keys when your lan...

(IF_VB)


[uncategorized] ~477-~477: Possible missing preposition found.
Context: ...rn?: boolean; ``` Details Whether suppress warnings outputted when localization fa...

(AI_HYDRA_LEO_MISSING_TO)


[uncategorized] ~798-~798: Possible missing article found.
Context: ...ues are the argument values passed from translation function, such as $t, t, or `transl...

(AI_HYDRA_LEO_MISSING_THE)


[grammar] ~1036-~1036: It seems that a pronoun is missing.
Context: ...llbackWarn?: boolean; ``` Details Whether do resolve on format keys when your lan...

(IF_VB)


[uncategorized] ~1061-~1061: Possible missing preposition found.
Context: ...rn?: boolean; ``` Details Whether suppress warnings outputted when localization fa...

(AI_HYDRA_LEO_MISSING_TO)


[grammar] ~1240-~1240: It seems that a pronoun is missing.
Context: ...eParameter?: boolean; ``` Details Whether do escape parameter for list or named i...

(IF_VB)


[grammar] ~1252-~1252: It seems that a pronoun is missing.
Context: ...llbackWarn?: boolean; ``` Details Whether do template interpolation on translatio...

(IF_VB)


[uncategorized] ~1277-~1277: Possible missing preposition found.
Context: ...rn?: boolean; ``` Details Whether suppress warnings outputted when localization fa...

(AI_HYDRA_LEO_MISSING_TO)

⏰ Context from checks skipped due to timeout of 90000ms (6)
  • GitHub Check: Build (windows-latest, 18)
  • GitHub Check: Build (ubuntu-latest, 18)
  • GitHub Check: Build (macos-latest, 18)
  • GitHub Check: release
  • GitHub Check: release
  • GitHub Check: Cloudflare Pages

Comment on lines 775 to 778
} else {
if (scope === 'local') {
throw createI18nError(I18nErrorCodes.DUPLICATE_USE_I18N_CALLING)
}
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

❓ Verification inconclusive

Throwing on second local call is breaking; document the change

The new block throws on a second useI18n with scope === 'local'.
This alters previous behaviour (which returned the existing composer) and will break code that legitimately re-invoked useI18n to get the same instance (a common pattern in composables).

  1. Confirm this behavioural change is intended and worth the break.
  2. If so, add a migration note / dev-only warning to guide users upgrading from v10.

Optionally, gate the throw behind __DEV__ to avoid production crashes while still warning during development.


Throwing on second local call is breaking; document the change

The new block throws on a second useI18n with scope === 'local'.
This alters previous behaviour (which returned the existing composer) and will break code that legitimately re-invoked useI18n to get the same instance (a common pattern in composables).

  1. Confirm this behavioural change is intended and worth the break.
  2. If so, add a migration note / dev-only warning to guide users upgrading from v10.

Optionally, gate the throw behind __DEV__ to avoid production crashes while still warning during development.

🤖 Prompt for AI Agents
In packages/vue-i18n-core/src/i18n.ts around lines 775 to 778, the new code
throws an error on a second call to useI18n with scope 'local', changing
previous behavior that returned the existing composer instance. Confirm if this
breaking change is intended; if yes, add a migration note or a development-only
warning to inform users upgrading from v10. Additionally, modify the throw to
only occur in development mode (__DEV__) to prevent production crashes while
still alerting developers.

Comment on lines 626 to 684
test(errorMessages[I18nErrorCodes.DUPLICATE_USE_I18N_CALLING], async () => {
const i18n = createI18n<false>({
legacy: false,
locale: 'en',
fallbackLocale: ['en'],
messages: {
en: { hello: 'hello!' }
}
})

const useMyComposable = () => {
const count = ref(0)
const { t } = useI18n({
messages: {
en: {
there: 'hi there! {count}'
}
}
})
return { message: t('there', { count: count.value }) }
}

let error = ''
const App = defineComponent({
setup() {
let message: string = ''
let t: any // eslint-disable-line @typescript-eslint/no-explicit-any
try {
const i18n = useI18n({
messages: {
en: {
hi: 'hi!'
}
}
})
t = i18n.t
const ret = useMyComposable()
message = ret.message
} catch (e: any) {
error = e.message
}
return { t, message, error }
},
template: `
<h1>Root</h1>
<form>
<select v-model="locale">
<option value="en">en</option>
<option value="ja">ja</option>
</select>
</form>
<p>{{ t('hi') }}</p>
<p>{{ message }}</p>
<p>{{ error }}</p>
`
})
const { html } = await mount(App, i18n as any) // eslint-disable-line @typescript-eslint/no-explicit-any
expect(html()).toMatchSnapshot()
})
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

❓ Verification inconclusive

Prefer explicit error assertion over generic snapshot

The goal of this test is to verify that a duplicate-scope useI18n() call surfaces DUPLICATE_USE_I18N_CALLING.
Relying on a full-HTML snapshot makes the test brittle (any innocuous template change or formatting tweak will break the snapshot while the functional contract is still satisfied).

A more focused assertion keeps the intent clear and reduces maintenance noise:

-const { html } = await mount(App, i18n as any) // eslint-disable-line @typescript-eslint/no-explicit-any
-expect(html()).toMatchSnapshot()
+await mount(App, i18n as any) // eslint-disable-line @typescript-eslint/no-explicit-any
+expect(error).toBe(errorMessages[I18nErrorCodes.DUPLICATE_USE_I18N_CALLING])

This directly validates the thrown error and avoids snapshot churn.


Prefer explicit error assertion over generic snapshot

The goal of this test is to verify that a duplicate-scope useI18n() call surfaces DUPLICATE_USE_I18N_CALLING.
Relying on a full-HTML snapshot makes the test brittle (any innocuous template change or formatting tweak will break the snapshot while the functional contract is still satisfied).

A more focused assertion keeps the intent clear and reduces maintenance noise:

-const { html } = await mount(App, i18n as any) // eslint-disable-line @typescript-eslint/no-explicit-any
-expect(html()).toMatchSnapshot()
+await mount(App, i18n as any) // eslint-disable-line @typescript-eslint/no-explicit-any
+expect(error).toBe(errorMessages[I18nErrorCodes.DUPLICATE_USE_I18N_CALLING])

This directly validates the thrown error and avoids snapshot churn.

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
test(errorMessages[I18nErrorCodes.DUPLICATE_USE_I18N_CALLING], async () => {
const i18n = createI18n<false>({
legacy: false,
locale: 'en',
fallbackLocale: ['en'],
messages: {
en: { hello: 'hello!' }
}
})
const useMyComposable = () => {
const count = ref(0)
const { t } = useI18n({
messages: {
en: {
there: 'hi there! {count}'
}
}
})
return { message: t('there', { count: count.value }) }
}
let error = ''
const App = defineComponent({
setup() {
let message: string = ''
let t: any // eslint-disable-line @typescript-eslint/no-explicit-any
try {
const i18n = useI18n({
messages: {
en: {
hi: 'hi!'
}
}
})
t = i18n.t
const ret = useMyComposable()
message = ret.message
} catch (e: any) {
error = e.message
}
return { t, message, error }
},
template: `
<h1>Root</h1>
<form>
<select v-model="locale">
<option value="en">en</option>
<option value="ja">ja</option>
</select>
</form>
<p>{{ t('hi') }}</p>
<p>{{ message }}</p>
<p>{{ error }}</p>
`
})
const { html } = await mount(App, i18n as any) // eslint-disable-line @typescript-eslint/no-explicit-any
expect(html()).toMatchSnapshot()
})
const App = defineComponent({
setup() {
let message: string = ''
let t: any // eslint-disable-line @typescript-eslint/no-explicit-any
try {
const i18n = useI18n({
messages: {
en: {
hi: 'hi!'
}
}
})
t = i18n.t
const ret = useMyComposable()
message = ret.message
} catch (e: any) {
error = e.message
}
return { t, message, error }
},
template: `
<h1>Root</h1>
<form>
<select v-model="locale">
<option value="en">en</option>
<option value="ja">ja</option>
</select>
</form>
<p>{{ t('hi') }}</p>
<p>{{ message }}</p>
<p>{{ error }}</p>
`
})
- const { html } = await mount(App, i18n as any) // eslint-disable-line @typescript-eslint/no-explicit-any
- expect(html()).toMatchSnapshot()
+ await mount(App, i18n as any) // eslint-disable-line @typescript-eslint/no-explicit-any
+ expect(error).toBe(errorMessages[I18nErrorCodes.DUPLICATE_USE_I18N_CALLING])
})
🤖 Prompt for AI Agents
In packages/vue-i18n-core/test/i18n.test.ts between lines 626 and 684, the test
currently uses a full HTML snapshot to verify the duplicate useI18n call error,
which is brittle and can break with unrelated template changes. Instead,
refactor the test to explicitly assert that the error message matches the
expected DUPLICATE_USE_I18N_CALLING error string. Remove the snapshot assertion
and replace it with a direct check on the error variable to confirm the correct
error is thrown, making the test more focused and maintainable.

@kazupon kazupon merged commit 08a81e3 into v11 Jun 17, 2025
27 checks passed
@kazupon kazupon deleted the fix/error-on-duplicate-calling-on-local-scope branch June 17, 2025 01:16
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Type: Improvement Includes backwards-compatible fixes
Projects
None yet
Development

Successfully merging this pull request may close these issues.

1 participant