-
-
Notifications
You must be signed in to change notification settings - Fork 377
fix: error on duplicate useI18n
calling on local scope
#2203
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
Important Review skippedAuto reviews are disabled on base/target branches other than the default branch. Please check the settings in the CodeRabbit UI or the You can disable this status message by setting the WalkthroughA new error code and message were added to prevent duplicate Changes
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
Poem
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. 🪧 TipsChatThere are 3 ways to chat with CodeRabbit:
SupportNeed 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)
Other keywords and placeholders
CodeRabbit Configuration File (
|
@coderabbitai review |
✅ Actions performedReview triggered.
|
Deploying vue-i18n-next with
|
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 |
@intlify/core
@intlify/core-base
@intlify/devtools-types
@intlify/message-compiler
petite-vue-i18n
@intlify/shared
vue-i18n
@intlify/vue-i18n-core
commit: |
There was a problem hiding this 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 diffsConsider 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 wordingCurrent 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 contentThis file currently contains only a heading. Either:
- Add at least a brief stub section (purpose, links), or
- Mark it clearly as “Work in progress” to avoid confusing readers.
v11/legacy.md (1)
1-1
: Same placeholder concern as other docsConsider adding minimal content or a TODO marker so that generated docs don’t look incomplete.
v11/directive.md (1)
1-1
: Single-line placeholderAs 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 ofi18n
insidesetup()
const i18n = useI18n({ … })
shadows the outeri18n
instance created viacreateI18n
.
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
totypescript
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
⛔ 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
packages/vue-i18n-core/src/i18n.ts
Outdated
} else { | ||
if (scope === 'local') { | ||
throw createI18nError(I18nErrorCodes.DUPLICATE_USE_I18N_CALLING) | ||
} |
There was a problem hiding this comment.
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).
- Confirm this behavioural change is intended and worth the break.
- 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).
- Confirm this behavioural change is intended and worth the break.
- 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.
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() | ||
}) |
There was a problem hiding this comment.
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.
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.
Summary by CodeRabbit
New Features
Bug Fixes
useI18n
calls within the same component, providing a clear error message.Tests
useI18n
calls within a component trigger the expected error.