Guide

语言切换器

如何更改网站当前的语言。

Nuxt i18n 模块 加载到您的应用中时,它会将您的 locales 配置添加到 nuxtApp.$i18n(或 this.$i18n),这使得在您的应用中任何地方显示一个语言切换器变得非常简单。

以下是一个语言切换器的示例,在每个语言对象中添加了 name 键,以便为每个链接显示更友好的标题:

<script setup>
const { locale, locales } = useI18n()
const switchLocalePath = useSwitchLocalePath()

const availableLocales = computed(() => {
  return locales.value.filter(i => i.code !== locale.value)
})
</script>

<template>
  <NuxtLink v-for="locale in availableLocales" :key="locale.code" :to="switchLocalePath(locale.code)">
    {{ locale.name }}
  </NuxtLink>
</template>
nuxt.config.ts
export default defineNuxtConfig({
  i18n: {
    locales: [
      {
        code: 'en',
        name: 'English'
      },
      {
        code: 'es',
        name: 'Español'
      },
      {
        code: 'fr',
        name: 'Français'
      }
    ]
  }
})
为了在使用 detectBrowserLanguage 时在路由更改时保持语言不变,您必须显式更新存储的语言 cookie。这可以通过 setLocaleCookie(locale)setLocale(locale) 来完成,它会设置 cookie 并切换到指定语言的路由。不这样做可能会导致根据在导航过程中设置的语言 cookie 进行重定向。

模板代码可能看起来像这样,例如:

<script setup>
const { locale, locales, setLocale } = useI18n()

const availableLocales = computed(() => {
  return locales.value.filter(i => i.code !== locale.value)
})
</script>

<template>
  ...
  <a href="#" v-for="locale in availableLocales" :key="locale.code" @click.prevent.stop="setLocale(locale.code)">
    {{ locale.name }}
  </a>
  ...
</template>

动态路由参数

处理动态路由参数需要更多的工作,因为您需要为 Nuxt i18n 模块 提供参数翻译。可组合的 useSetI18nParams 可以用于设置路由参数的翻译,这被用于设置 SEO 标签,以及更改 switchLocalePath 返回的路由。

在 SSR(服务器端渲染)期间,设置 i18n 参数的时机和位置非常重要,因为在 SSR 过程中是没有反应性的。

已经渲染的组件不会受到后续页面和组件引入的更改的影响。相反,这些链接在客户端的水合过程中更新,在大多数情况下这不会导致问题。

对于 SEO 标签并非如此,它们在 SSR 期间会正确更新,而不管 i18n 参数是在何时和何地设置的。

查看实验性的 switchLocalePathLinkSSR 功能,该功能与 <SwitchLocalePathLink> 组件结合使用,能够在 SSR 期间正确呈现链接,而不管它被在哪里和何时使用。

例如(将 slug 替换为适用的路由参数):

<script setup>
// 从API获取产品...(红色杯子)

const setI18nParams = useSetI18nParams()
setI18nParams({
  en: { slug: data.slugs.en }, // slug: 'red-mug'
  nl: { slug: data.slugs.nl } // slug: 'rode-mok'
})

const switchLocalePath = useSwitchLocalePath()
switchLocalePath('en') // /products/red-mug
switchLocalePath('nl') // /nl/products/rode-mok
</script>

<template>
  <!-- pages/products/[slug].vue -->
</template>

请注意,对于名为 [...pathMatch.vue] 的特殊情况,对象的键需要设置为 pathMatch。例如:

<script>
const setI18nParams = useSetI18nParams()
setI18nParams({
  en: { pathMatch: ['not-found-my-post'] },
  fr: { pathMatch: ['not-found-mon-article'] }
})
</script>

<template>
  <!-- pages/[...pathMatch].vue -->
</template>

请注意,捕获所有路由是作为 数组 定义的。在此案例中,只有一个元素,但如果您想使用子路径,例如 /not-found/post,则需要定义多个元素,如 ['not-found', 'post']。您需要定义多个,例如 ['not-found', 'post']

Nuxt i18n 模块 不会为您重置参数翻译,这意味着如果您为不同的路由使用相同的参数,在这些路由之间导航可能会导致参数冲突。确保在这些情况下始终设置参数翻译。

等待页面过渡

默认情况下,当导航到具有不同语言的路由时,语言将立即更改,这意味着如果您有页面过渡,它将淡出页面,并且文本已经切换为新语言,然后再淡入相同的内容。

要解决此问题,您可以将选项 skipSettingLocaleOnNavigate 设置为 true,并从插件中定义的 onBeforeEnter 过渡钩子手动处理设置语言。

全局过渡

如果您想要过渡整个 Nuxt 应用程序,您可以使用 NuxtPagetransition 来控制它,如下所示:

nuxt.config.ts
export default defineNuxtConfig({
  i18n: {
    // ... 其他选项
    skipSettingLocaleOnNavigate: true
  }
}
pages/app.vue
<script setup lang="ts">
const { finalizePendingLocaleChange } = useI18n()

const onBeforeEnter = async () => {
  await finalizePendingLocaleChange()
}
</script>

<template>
  <NuxtLayout>
    <NuxtPage
      :transition="{
        name: 'my',
        mode: 'out-in',
        onBeforeEnter
      }"
    />
  </NuxtLayout>
</template>

<style>
.my-enter-active,
.my-leave-active {
  transition: opacity 0.3s;
}
.my-enter,
.my-leave-active {
  opacity: 0;
}
</style>

可选,在滚动之前等待语言切换以获得更平滑的过渡,使用 Router Options

app/router.options.ts
import type { RouterConfig } from '@nuxt/schema'

export default <RouterConfig>{
  async scrollBehavior(to, from, savedPosition) {
    const nuxtApp = useNuxtApp()

    // 确保路由已经更改。
    if (nuxtApp.$i18n && to.name !== from.name) {
      // `$i18n` 被注入到 nuxtjs/i18n 模块的 `setup` 中。
      // `scrollBehavior` 在未完成之前被保护不被调用。
      await nuxtApp.$i18n.waitForPendingLocaleChange()
    }

    return savedPosition || { top: 0 }
  }
}

每个页面组件过渡

如果您在页面组件中使用 definePageMeta 定义了特定的过渡,并需要在 pageTransitiononBeforeEnter 钩子中添加 finalizePendingLocaleChange

示例:

pages/about.vue
<script setup lang="ts">
const route = useRoute()
const { finalizePendingLocaleChange } = useI18n()

definePageMeta({
  pageTransition: {
    name: 'page',
    mode: 'out-in'
  }
})

route.meta.pageTransition.onBeforeEnter = async () => {
  await finalizePendingLocaleChange()
}
</script>

<style scoped>
.page-enter-active,
.page-leave-active {
  transition: opacity 1s;
}
.page-enter,
.page-leave-active {
  opacity: 0;
}
</style>

使用 definePageMeta 的动态路由参数(已弃用)

使用 nuxtI18ndefinePageMeta 中的动态路由参数已被弃用,并将在 v9 中移除,请改用可组合的 useSetI18nParams

动态参数可以使用 definePageMeta 配置。当使用 switchLocalePath() 生成语言切换路由时,这些参数将与路由参数合并。

您必须在 Nuxt i18n 模块 的选项中将 dynamicRouteParams 设置为 true,以启用动态路由参数。

示例(将 id 替换为适用的路由参数):

<script setup>
definePageMeta({
  // ...
  nuxtI18n: {
    en: { id: 'my-post' },
    fr: { id: 'mon-article' }
  }
  // ...
})
</script>
<template>
  <!-- pages/post/[id].vue -->
</template>

请注意,对于名为 [...pathMatch.vue] 的特殊情况,对象的键需要设置为 pathMatch。例如:

<script>
definePageMeta({
  // ...
  nuxtI18n: {
    en: { pathMatch: ['not-found-my-post'] },
    fr: { pathMatch: ['not-found-mon-article'] }
  }
  // ...
})
</script>
<template>
  <!-- pages/[...pathMatch].vue -->
</template>

Vue i18n 注意事项

与 Vue i18n 相比,您不应直接设置 locale,应使用 setLocale 切换语言,或导航到由 switchLocalePath 返回的路由。这将加载翻译、触发钩子并更新语言 cookie(如果使用)。