You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在Storybook中配置Nuxt3自定义i18n插件

问题

我是Nuxt.js和Storybook新手,在Nuxt3项目中配置Storybook时遇到问题。项目里有一个自定义i18n插件(代码如下),现在渲染使用$getLang()函数的组件Story时,报错:TypeError: _ctx.$getLang is not a function。

自定义i18n插件代码:

import { createI18n } from 'vue3-i18n'
import en from '@/locales/en.json'
import ja from '@/locales/ja.json'
import zh_TW from '@/locales/zh_TW.json'
import zh_CN from '@/locales/zh_CN.json'

interface I18nOptions {
  locale: string
  messages: Record<string, object>
}

const getBrowserLanguage = (): string => {
  if (process.client) {
    return window.navigator.language
  }
  return 'en'
}

const getLocaleFromCookie = (): string | null => {
  // Read from cookie
  const cookie = useCookie<string>('locale')
  if (cookie.value) {
    return cookie.value
  }
  return null
}

const LANGS = {
  EN: 'en',
  JP: 'ja',
  TW: 'zh-TW',
  CN: 'zh-CN',
}

const getLocale = (): string => {
  const browserLanguage = getBrowserLanguage()
  if (Object.values(LANGS).includes(browserLanguage)) {
    return browserLanguage
  }
  const cookieLocale = getLocaleFromCookie()
  if (cookieLocale && Object.values(LANGS).includes(cookieLocale)) {
    return cookieLocale
  }
  return 'en'
}

const messages: Record<string, object> = { en, ja, 'zh-tw': zh_TW, 'zh-cn': zh_CN }

const i18nOptions: I18nOptions = {
  locale: getLocale().toLowerCase(),
  messages,
}

const i18n = createI18n(i18nOptions)

export default defineNuxtPlugin((nuxt) => {
  nuxt.vueApp.use(i18n)

  return {
    provide: {
      t: () => null,
      getLang: (key: string, params?: Array<string>) => {
        if (!key) return ''
        return i18n.t(key, params)
      },
      setLocale: (value: string) => {
        i18n.setLocale(value)
      },
      getLocale: () => i18n.getLocale(),
    },
  }
})

项目devDependencies:

"devDependencies": {
  "@nuxtjs/tailwindcss": "^6.8.0",
  "@storybook/addon-essentials": "^7.0.27",
  "@storybook/addon-interactions": "^7.0.27",
  "@storybook/addon-links": "^7.0.27",
  "@storybook/blocks": "^7.0.27",
  "@storybook/testing-library": "^0.0.14-next.2",
  "@storybook/vue3": "^7.0.27",
  "@storybook/vue3-vite": "^7.0.27",
  "eslint": "^8.44.0",
  "eslint-plugin-storybook": "^0.6.12",
  "nuxt": "^3.5.0",
  "react": "^18.2.0",
  "react-dom": "^18.2.0",
  "storybook": "^7.0.27",
  "storybook-addon-nuxt": "^1.3.3",
  "typescript": "^5.1.6",
  "vue3-i18n": "^1.1.5"
}

当前.storybook/main.ts:

import type { StorybookConfig } from '@storybook/vue3-vite'

export const config: StorybookConfig = {
  stories: ['../src/**/*.mdx', '../src/**/*.stories.@(js|jsx|ts|tsx)'],
  addons: ['@storybook/addon-links', '@storybook/addon-essentials', '@storybook/addon-interactions'],
  framework: {
    name: '@storybook/vue3-vite',
    options: {},
  },
  docs: {
    autodocs: 'tag',
  },
}

当前.storybook/preview.ts:

import type { Preview } from '@storybook/vue3'

export const preview: Preview = {
  parameters: {
    actions: { argTypesRegex: '^on[A-Z].*' },
    controls: {
      matchers: {
        color: /(background|color)$/i,
        date: /Date$/,
      },
    },
  },
}

需要知道如何在.storybook/preview.ts中配置这个自定义插件。

解决方案

1. 适配i18n实例(处理Nuxt专属API)

原插件使用的useCookie和process.client是Nuxt专属API,Storybook环境无法直接识别。需要重新创建适配Storybook的i18n实例,保留原有语言配置和核心方法。

2. 修改.storybook/main.ts(配置路径别名)

Storybook默认不识别Nuxt的@别名,需在Vite配置中添加别名解析:

import type { StorybookConfig } from '@storybook/vue3-vite'
import { resolve } from 'path'

export const config: StorybookConfig = {
  stories: ['../src/**/*.mdx', '../src/**/*.stories.@(js|jsx|ts|tsx)'],
  addons: ['@storybook/addon-links', '@storybook/addon-essentials', '@storybook/addon-interactions'],
  framework: {
    name: '@storybook/vue3-vite',
    options: {},
  },
  docs: {
    autodocs: 'tag',
  },
  viteFinal: async (config) => {
    config.resolve!.alias = {
      ...config.resolve!.alias,
      '@': resolve(__dirname, '../src'),
    }
    return config
  },
}

3. 修改.storybook/preview.ts(挂载i18n和全局方法)

在preview中初始化i18n,并将$getLang、$setLocale、$getLocale挂载到Vue全局属性:

import type { Preview } from '@storybook/vue3'
import { createI18n } from 'vue3-i18n'
import en from '@/locales/en.json'
import ja from '@/locales/ja.json'
import zh_TW from '@/locales/zh_TW.json'
import zh_CN from '@/locales/zh_CN.json'

// 初始化适配Storybook的i18n实例
const messages = { en, ja, 'zh-tw': zh_TW, 'zh-cn': zh_CN }
const i18n = createI18n({
  locale: 'en', // Storybook默认语言,可按需调整
  messages,
})

export const preview: Preview = {
  parameters: {
    actions: { argTypesRegex: '^on[A-Z].*' },
    controls: {
      matchers: {
        color: /(background|color)$/i,
        date: /Date$/,
      },
    },
  },
  // 全局注入方法
  decorators: [
    (story) => ({
      components: { story },
      setup() {
        return {
          $getLang: (key: string, params?: Array<string>) => {
            if (!key) return ''
            return i18n.t(key, params)
          },
          $setLocale: (value: string) => {
            i18n.global.locale.value = value
          },
          $getLocale: () => i18n.global.locale.value,
        }
      },
      template: '<story />',
    }),
  ],
  // 全局应用i18n
  async setup(app) {
    app.use(i18n)
  },
}

4. 验证效果

重启Storybook后,组件中的$getLang即可正常调用。如需切换语言,可在Story的controls中添加自定义控件,或直接调用$setLocale方法。

内容的提问来源于stack exchange,提问作者yosuke.kom

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.15 23:04:52