Skip to content

docs(ui): use storybook-i18n addon for locale switching#2125

Open
cylewaitforit wants to merge 1 commit intonpmx-dev:mainfrom
cylewaitforit:sb-storybook-i18n
Open

docs(ui): use storybook-i18n addon for locale switching#2125
cylewaitforit wants to merge 1 commit intonpmx-dev:mainfrom
cylewaitforit:sb-storybook-i18n

Conversation

@cylewaitforit
Copy link
Contributor

@cylewaitforit cylewaitforit commented Mar 17, 2026

🔗 Linked issue

Related: #1964

🧭 Context

This adds the storybook-i18n for locale switching in the npmx storybook.

📚 Description

This allows stories that have been configured with a translation to be viewed with different translations.

@vercel
Copy link

vercel bot commented Mar 17, 2026

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
docs.npmx.dev Ready Ready Preview, Comment Mar 17, 2026 7:07pm
npmx.dev Ready Ready Preview, Comment Mar 17, 2026 7:07pm
1 Skipped Deployment
Project Deployment Actions Updated (UTC)
npmx-lunaria Ignored Ignored Mar 17, 2026 7:07pm

Request Review

@codecov
Copy link

codecov bot commented Mar 17, 2026

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ All tests successful. No failed tests found.

📢 Thoughts on this report? Let us know!

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Mar 17, 2026

📝 Walkthrough

Walkthrough

This pull request introduces internationalisation (i18n) support to the Storybook setup. The storybook-i18n addon is added to the configuration and registered as a development dependency. The preview configuration is extended to initialise locale globals set to en-US, add a runtime listener for locale changes, and manage the active i18n instance. Story files are updated to use render functions with translated content. The pnpm workspace configuration is modified to include the storybook-i18n package version.

Possibly related PRs

  • feat: add storybook #1270: Modifies the same Storybook configuration files (.storybook/main.ts and .storybook/preview.ts) with i18n/locale-related updates, directly implementing i18n addon integration and locale handling.

Suggested reviewers

  • danielroe
  • JReinhold
🚥 Pre-merge checks | ✅ 1
✅ Passed checks (1 passed)
Check name Status Explanation
Description check ✅ Passed The pull request description clearly relates to the changeset, describing the addition of storybook-i18n addon for locale switching functionality across multiple modified files.

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

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
📝 Coding Plan
  • Generate coding plan for human review comments

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.

Tip

You can customize the tone of the review comments and chat replies.

Configure the tone_instructions setting to customize the tone of the review comments and chat replies. For example, you can set the tone to Act like a strict teacher, Act like a pirate and more.

Copy link
Contributor

@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

🧹 Nitpick comments (1)
.storybook/preview.ts (1)

22-26: Avoid any for the i18n instance and narrow before calling setLocale.

Line 22 and Lines 92-96 currently bypass type checks; a narrow interface here keeps the locale bridge safe and explicit.

💡 Suggested refactor
-let currentI18nInstance: any = null
+type StorybookI18nInstance = {
+  setLocale: (locale: string) => void | Promise<void>
+}
+let currentI18nInstance: StorybookI18nInstance | null = null
@@
-          currentI18nInstance = this.$i18n
+          currentI18nInstance =
+            this.$i18n && typeof this.$i18n.setLocale === 'function'
+              ? (this.$i18n as StorybookI18nInstance)
+              : null
@@
-          if (locale && this.$i18n) {
-            this.$i18n.setLocale(locale)
+          if (locale && currentI18nInstance) {
+            void currentI18nInstance.setLocale(locale)
           }

As per coding guidelines, "Ensure you write strictly type-safe code, for example by ensuring you always check when accessing an array value by index".

Also applies to: 91-96


ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: a584aa74-dd82-4f31-b2eb-5c2635be10b0

📥 Commits

Reviewing files that changed from the base of the PR and between ad2b174 and 542b245.

⛔ Files ignored due to path filters (1)
  • pnpm-lock.yaml is excluded by !**/pnpm-lock.yaml
📒 Files selected for processing (5)
  • .storybook/main.ts
  • .storybook/preview.ts
  • app/components/Link/Link.stories.ts
  • package.json
  • pnpm-workspace.yaml

Comment on lines +21 to +27
// Subscribe to locale changes from storybook-i18n addon (once, outside decorator)
let currentI18nInstance: any = null
addons.getChannel().on('LOCALE_CHANGED', (newLocale: string) => {
if (currentI18nInstance) {
currentI18nInstance.setLocale(newLocale)
}
})
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🌐 Web query:

For Storybook 10 using storybook/preview-api, is channel.off(eventName, listener)the supported way to avoid duplicate listeners during HMR afterchannel.on(eventName, listener)?

💡 Result:

Yes. In Storybook 10, addons.getChannel() returns a Node.js EventEmitter-compatible channel, so removing a listener with channel.off(eventName, listener) is part of the supported API surface. [1]

Storybook’s own addon examples use the exact pattern to avoid duplicate subscriptions (subscribe with on, then unsubscribe with off in cleanup), e.g. channel.on(EVENTS.REQUEST, handleRequest) paired with channel.off(EVENTS.REQUEST, handleRequest). [2]

Also, Storybook’s @storybook/preview-api testing utilities model the channel with an explicit off(eventId, listener) method, reinforcing that off is the intended unsubscribe call. [3]

Important: you must pass the same function reference to off that you passed to on (i.e., don’t inline a new lambda each HMR pass). [2]

Sources: [1] (storybook.js.org) [2] (storybook.js.org) [3] (tessl.io)

Citations:


De-register the locale listener before re-registering it to avoid duplicate handlers during HMR.

Line 23 adds a module-scope LOCALE_CHANGED listener; on preview HMR reloads this can stack handlers and trigger repeated setLocale calls. Use channel.off() with the same function reference to clean up before re-subscribing.

💡 Suggested fix
-let currentI18nInstance: any = null
-addons.getChannel().on('LOCALE_CHANGED', (newLocale: string) => {
+let currentI18nInstance: any = null
+const channel = addons.getChannel()
+const onLocaleChanged = (newLocale: string) => {
   if (currentI18nInstance) {
     currentI18nInstance.setLocale(newLocale)
   }
-})
+}
+channel.off?.('LOCALE_CHANGED', onLocaleChanged)
+channel.on('LOCALE_CHANGED', onLocaleChanged)

Also consider typing currentI18nInstance to improve type safety.

📝 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
// Subscribe to locale changes from storybook-i18n addon (once, outside decorator)
let currentI18nInstance: any = null
addons.getChannel().on('LOCALE_CHANGED', (newLocale: string) => {
if (currentI18nInstance) {
currentI18nInstance.setLocale(newLocale)
}
})
// Subscribe to locale changes from storybook-i18n addon (once, outside decorator)
let currentI18nInstance: any = null
const channel = addons.getChannel()
const onLocaleChanged = (newLocale: string) => {
if (currentI18nInstance) {
currentI18nInstance.setLocale(newLocale)
}
}
channel.off?.('LOCALE_CHANGED', onLocaleChanged)
channel.on('LOCALE_CHANGED', onLocaleChanged)

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