Skip to content

Commit

Permalink
fix: detectBrowserLanguage option (#2276)
Browse files Browse the repository at this point in the history
resolve #2262
ref: #1888
  • Loading branch information
kazupon authored Jul 30, 2023
1 parent 080660b commit 26f7829
Show file tree
Hide file tree
Showing 20 changed files with 313 additions and 20 deletions.
9 changes: 0 additions & 9 deletions pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Original file line number Diff line number Diff line change
Expand Up @@ -23,13 +23,15 @@ await setup({
test('alwaysRedirect: no prefix', async () => {
const page = await createPage(undefined, { locale: 'en' }) // set browser locale
await page.goto(url('/about'))
const ctx = await page.context()

// detect locale from navigator language
expect(await getText(page, '#lang-switcher-current-locale code')).toEqual('en')

// click `fr` lang switch with nutlink
await page.locator('#set-locale-link-fr').click()
expect(await getText(page, '#lang-switcher-current-locale code')).toEqual('fr')
expect(await ctx.cookies()).toMatchObject([{ name: 'i18n_redirected', value: 'fr' }])

// go to `blog/article` page
await page.goto(url('/blog/article'))
Expand Down
4 changes: 2 additions & 2 deletions specs/fixtures/issues/1721/app.vue
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
<li
v-for="({ code, name }, i) in locales"
:key="code"
:class="{ 'selected': code === locale }"
:class="{ selected: code === locale }"
@click.prevent="setLocale(code)"
>
{{ name }}
Expand All @@ -16,7 +16,7 @@
</template>

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

<style>
Expand Down
55 changes: 55 additions & 0 deletions specs/fixtures/issues/1888/app.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
<template>
<div>
<h3>Switch language:</h3>

<button
v-for="locale in availableLocales"
:id="locale.code"
:key="locale.code"
@click="$i18n.setLocale(locale.code)"
>
{{ locale.name }}
</button>

<hr />

<h3>Current language:</h3>
<p>
<img id="flag" :src="`/flags/${currentLocale.flag}.svg`" :alt="currentLocale.flag" :key="currentLocale.flag" />

<img
v-if="isMounted"
id="flag-mounted"
:src="`/flags/${currentLocale.flag}.svg`"
:alt="currentLocale.flag"
:key="currentLocale.flag"
/>

<span>{{ currentLocale.name }}</span>
</p>

<hr />

<h3>Translation test</h3>
<p id="html-msg" v-html="$t('test')" :key="currentLocale" />
<p id="html-msg-mounted" v-html="$t('test')" v-if="isMounted" />
<p id="test-msg">{{ $t('test') }}</p>
</div>
</template>

<script lang="ts" setup>
const i18n = useI18n()
const availableLocales = computed(() => i18n.locales.value)
const currentLocale = computed(() => availableLocales.value.find(({ code }) => code === i18n.locale.value))
const isMounted = ref(false)
onMounted(() => {
isMounted.value = true
})
</script>

<style lang="css" scoped>
button,
img {
margin-right: 16px;
}
</style>
3 changes: 3 additions & 0 deletions specs/fixtures/issues/1888/locales/en.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
{
"test": "<strong>Translation</strong> example"
}
3 changes: 3 additions & 0 deletions specs/fixtures/issues/1888/locales/fr.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
{
"test": "Exemple de <strong>traduction</strong>"
}
3 changes: 3 additions & 0 deletions specs/fixtures/issues/1888/locales/pl.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
{
"test": "Przykład <strong>tłumaczenia</strong>"
}
36 changes: 36 additions & 0 deletions specs/fixtures/issues/1888/nuxt.config.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
// https://v3.nuxtjs.org/api/configuration/nuxt.config
export default defineNuxtConfig({
modules: ['@nuxtjs/i18n'],
i18n: {
locales: [
{
code: 'en',
flag: 'us',
iso: 'en-US',
file: 'en.json',
name: 'English'
},
{ code: 'pl', flag: 'pl', iso: 'pl-PL', file: 'pl.json', name: 'Polski' },
{
code: 'fr',
flag: 'fr',
iso: 'fr-FR',
file: 'fr.json',
name: 'Français'
}
],
defaultLocale: 'en',
strategy: 'prefix_except_default',
langDir: 'locales',
compilation: {
strictMessage: false
},
detectBrowserLanguage: {
useCookie: true,
// cookieKey: 'locale',
// fallbackLocale: 'en',
alwaysRedirect: true,
redirectOn: 'root'
}
}
})
14 changes: 14 additions & 0 deletions specs/fixtures/issues/1888/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
{
"name": "nuxt3-test-issues-1888",
"private": true,
"scripts": {
"build": "nuxt build",
"dev": "nuxt dev",
"generate": "nuxt generate",
"preview": "nuxt preview"
},
"devDependencies": {
"@nuxtjs/i18n": "latest",
"nuxt": "latest"
}
}
7 changes: 7 additions & 0 deletions specs/fixtures/issues/2262/app.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
<template>
<div>
<NuxtLayout>
<NuxtPage></NuxtPage>
</NuxtLayout>
</div>
</template>
12 changes: 12 additions & 0 deletions specs/fixtures/issues/2262/i18n.config.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
export default defineI18nConfig(() => ({
legacy: false,
locale: 'en',
messages: {
en: {
welcome: 'Welcome'
},
fr: {
welcome: 'Bienvenue'
}
}
}))
5 changes: 5 additions & 0 deletions specs/fixtures/issues/2262/layouts/default.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
<template>
<div>
<slot />
</div>
</template>
25 changes: 25 additions & 0 deletions specs/fixtures/issues/2262/nuxt.config.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
// https://nuxt.com/docs/api/configuration/nuxt-config
export default defineNuxtConfig({
devtools: { enabled: true },
modules: ['@nuxtjs/i18n'],
i18n: {
locales: [
{
code: 'en',
name: 'English'
},
{
code: 'fr',
name: 'Français'
}
],
strategy: 'prefix_except_default',
defaultLocale: 'en',
detectBrowserLanguage: {
useCookie: true,
cookieKey: 'i18n_redirected',
redirectOn: 'root', // recommended
alwaysRedirect: true
}
}
})
14 changes: 14 additions & 0 deletions specs/fixtures/issues/2262/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
{
"name": "nuxt3-test-issues-2262",
"private": true,
"scripts": {
"build": "nuxt build",
"dev": "nuxt dev",
"generate": "nuxt generate",
"preview": "nuxt preview"
},
"devDependencies": {
"@nuxtjs/i18n": "latest",
"nuxt": "latest"
}
}
21 changes: 21 additions & 0 deletions specs/fixtures/issues/2262/pages/index.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
<script setup>
const { locale, locales } = useI18n()
const switchLocalePath = useSwitchLocalePath()
const availableLocales = computed(() => {
return locales.value.filter(i => i.code !== locale.value)
})
</script>

<template>
<div>
<NuxtLink
:id="locale.code"
v-for="locale in availableLocales"
:key="locale.code"
:to="switchLocalePath(locale.code)"
>{{ locale.name }}</NuxtLink
>
</div>

<p id="msg">{{ $t('welcome') }}</p>
</template>
41 changes: 41 additions & 0 deletions specs/issues/1888.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
import { test, expect, describe } from 'vitest'
import { fileURLToPath } from 'node:url'
import { URL } from 'node:url'
import { setup, url, createPage } from '../utils'
import { getText } from '../helper'

describe('#1888', async () => {
await setup({
rootDir: fileURLToPath(new URL(`../fixtures/issues/1888`, import.meta.url))
})

test('should be worked', async () => {
const home = url('/')
const page = await createPage(undefined, { locale: 'pl' })
await page.goto(home)

expect(await getText(page, '#html-msg')).toEqual('Przykład tłumaczenia')
expect(await getText(page, '#html-msg-mounted')).toEqual('Przykład tłumaczenia')
expect(await getText(page, '#test-msg')).toEqual('Przykład <strong>tłumaczenia</strong>')
expect(await page.locator('#flag').getAttribute('alt')).toContain('pl')
expect(await page.locator('#flag-mounted').getAttribute('alt')).toContain('pl')

// change to `en` locale
await page.locator('#en').click()

expect(await getText(page, '#html-msg')).toEqual('Translation example')
expect(await getText(page, '#html-msg-mounted')).toEqual('Translation example')
expect(await getText(page, '#test-msg')).toEqual('<strong>Translation</strong> example')
expect(await page.locator('#flag').getAttribute('alt')).toContain('us')
expect(await page.locator('#flag-mounted').getAttribute('alt')).toContain('us')

// change to `fr` locale
await page.locator('#fr').click()

expect(await getText(page, '#html-msg')).toEqual('Exemple de traduction')
expect(await getText(page, '#html-msg-mounted')).toEqual('Exemple de traduction')
expect(await getText(page, '#test-msg')).toEqual('Exemple de <strong>traduction</strong>')
expect(await page.locator('#flag').getAttribute('alt')).toContain('fr')
expect(await page.locator('#flag-mounted').getAttribute('alt')).toContain('fr')
})
})
35 changes: 35 additions & 0 deletions specs/issues/2262.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
import { test, expect, describe } from 'vitest'
import { fileURLToPath } from 'node:url'
import { URL } from 'node:url'
import { setup, url, createPage } from '../utils'
import { getText } from '../helper'

describe('#2262', async () => {
await setup({
rootDir: fileURLToPath(new URL(`../fixtures/issues/2262`, import.meta.url))
})

test('redirect with browser cookie', async () => {
const home = url('/')
const page = await createPage()
await page.goto(home)
const ctx = await page.context()

expect(await getText(page, '#msg')).toEqual('Welcome')

// change to `fr`
await page.locator('#fr').click()
expect(await getText(page, '#msg')).toEqual('Bienvenue')
expect(await ctx.cookies()).toMatchObject([{ name: 'i18n_redirected', value: 'fr' }])

// direct access to root `/`
await page.goto(home)
expect(await getText(page, '#msg')).toEqual('Bienvenue')
expect(page.url().endsWith('/fr'))

// change to `en`
await page.locator('#en').click()
expect(await getText(page, '#msg')).toEqual('Welcome')
expect(await ctx.cookies()).toMatchObject([{ name: 'i18n_redirected', value: 'en' }])
})
})
17 changes: 14 additions & 3 deletions src/runtime/internal.ts
Original file line number Diff line number Diff line change
Expand Up @@ -308,6 +308,7 @@ export function setLocaleCookie(
export type DetectBrowserLanguageNotDetectReason =
| 'unknown'
| 'not_found_match'
| 'first_access_only'
| 'not_redirect_on_root'
| 'not_redirect_on_no_prefix'
| 'detect_ignore_on_ssg'
Expand All @@ -320,7 +321,11 @@ export type DetectBrowserLanguageFromResult = {
}
export type DetectLocaleForSSGStatus = 'ssg_ignore' | 'ssg_setup' | 'normal'
export type DetectLocaleCallType = 'setup' | 'routing'
export type DetectLocaleContext = { ssg: DetectLocaleForSSGStatus; callType: DetectLocaleCallType }
export type DetectLocaleContext = {
ssg: DetectLocaleForSSGStatus
callType: DetectLocaleCallType
firstAccess: boolean
}

export const DefaultDetectBrowserLanguageFromResult: DetectBrowserLanguageFromResult = {
locale: '',
Expand All @@ -339,14 +344,19 @@ export function detectBrowserLanguage<Context extends NuxtApp = NuxtApp>(
locale: Locale = ''
): DetectBrowserLanguageFromResult {
const { strategy } = nuxtI18nOptions
const { ssg, callType } = detectLocaleContext
__DEBUG__ && console.log('detectBrowserLanguage: (ssg, callType) - ', ssg, callType)
const { ssg, callType, firstAccess } = detectLocaleContext
__DEBUG__ && console.log('detectBrowserLanguage: (ssg, callType, firstAccess) - ', ssg, callType, firstAccess)

// browser detection is ignored if it's a nuxt generate.
if (isSSG && strategy === 'no_prefix' && (process.server || ssg === 'ssg_ignore')) {
return { locale: '', stat: true, reason: 'detect_ignore_on_ssg' }
}

// Locale detection from the browser is first access only
if (!firstAccess) {
return { locale: '', stat: false, reason: 'first_access_only' }
}

const { redirectOn, alwaysRedirect, useCookie, fallbackLocale } =
nuxtI18nOptions.detectBrowserLanguage as DetectBrowserLanguageOptions

Expand All @@ -360,6 +370,7 @@ export function detectBrowserLanguage<Context extends NuxtApp = NuxtApp>(
redirectOn,
locale
)

if (strategy !== 'no_prefix') {
if (redirectOn === 'root') {
if (path !== '/') {
Expand Down
Loading

0 comments on commit 26f7829

Please sign in to comment.