Skip to content

Latest commit

 

History

History
247 lines (192 loc) · 6.61 KB

9.lang-switcher.md

File metadata and controls

247 lines (192 loc) · 6.61 KB
title description
Lang Switcher
When @nuxtjs/i18n loads in your app, it adds your `locales` configuration to `nuxtApp.$i18n` (or `this.$i18n`), which makes it really easy to display a lang switcher anywhere in your app.

When @nuxtjs/i18n loads in your app, it adds your locales configuration to nuxtApp.$i18n (or this.$i18n), which makes it really easy to display a lang switcher anywhere in your app.

Here's an example lang switcher where a name key has been added to each locale object in order to display friendlier titles for each link:

<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>
export default defineNuxtConfig({
  // ...

  i18n: {
    locales: [
      {
        code: 'en',
        name: 'English'
      },
      {
        code: 'es',
        name: 'Español'
      },
      {
        code: 'fr',
        name: 'Français'
      }
    ]
  },

  // ...
})

::alert{type="info"}

When using detectBrowserLanguage and wanting to persist locale on a route change, you must call one of the functions that update the stored locale cookie. Call either setLocaleCookie(locale) to persist just the cookie locale or setLocale(locale) to both persist the cookie locale and switch the route to the specified locale. Otherwise, locale might switch back to the saved one during navigation.

::

The template code might look like this, for example:

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

Dynamic route parameters

Dealing with dynamic route parameters requires a bit more work because you need to provide parameters translations to @nuxtjs/i18n. For this purpose, @nuxtjs/i18n use params with configured by definePageMeta. That will be merged with route params when generating lang switch routes with switchLocalePath().

::alert{type="warning"}

you did not set dynamicRouteParams option to false in @nuxtjs/i18n's options.

::

An example (replace id with the appropriate route parameter):

<script setup>
definePageMeta({
  // ...
  nuxtI18n: {
    en: { id: 'my-post' },
    fr: { id: 'mon-article' }
  }
  // ...
})
</script>

<template>
  <!-- pages/post/[id].vue -->
</template>

Note that for the special case of the catch-all route named like [...pathMatch.vue], the key of the object needs to say pathMatch. For example:

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

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

Note that catch all is defined as the array. In this case, there is only one element, but if you want a sub-path, for example /not-found/post, define multiple elements as in ['not-found', 'post']. You will need to define more than one, e.g. ['not-found', 'post'].

::alert{type="info"}

@nuxtjs/i18n won't reset parameters translations for you, this means that if you use identical parameters for different routes, navigating between those routes might result in conflicting parameters. Make sure you always set params translations in such cases.

::

Wait for page transition

By default, the locale will be changed right away when navigating to a route with a different locale which means that if you have a page transition, it will fade out the page with the text already switched to the new language and fade back in with the same content.

To work around the issue, you can set the option skipSettingLocaleOnNavigate to true and handle setting the locale yourself from a onBeforeEnter transition hook defined in a plugin.

Global transition

If you would like to transition the entire Nuxt app, you can use the transition of NuxtPage to control it as follows:

export default defineNuxtConfig({
  i18n: {
    // ... your other options
    skipSettingLocaleOnNavigate: true
  }
}
<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>

Optional, wait for locale before scrolling for a smoother transition with Router Options:

import type { RouterConfig } from '@nuxt/schema'

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

    // make sure the route has changed.
    if (nuxtApp.$18n && to.name !== from.name) {
      // `$i18n` is injected in the `setup` of the nuxtjs/i18n module.
      // `scrollBehavior` is guarded against being called even when it is not completed
      await nuxtApp.$i18n.waitForPendingLocaleChange()
    }

    return savedPosition || { top: 0 }
  }
}

Per page component transition

If you have a specific transition defined in a page component with definePageMeta and need to add finalizePendingLocaleChange at onBeforeEnter hook for pageTransition.

Example:

<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 1.0s;
}
.page-enter,
.page-leave-active {
  opacity: 0;
}
</style>