diff --git a/docs/content/docs/5.v9/2.guide/18.breaking-changes-in-v9.md b/docs/content/docs/5.v9/2.guide/18.breaking-changes-in-v9.md index 25390b4b2..17d7ff1d5 100644 --- a/docs/content/docs/5.v9/2.guide/18.breaking-changes-in-v9.md +++ b/docs/content/docs/5.v9/2.guide/18.breaking-changes-in-v9.md @@ -46,4 +46,8 @@ nuxt.config.ts Reasons for change 1. Context - i18n files are used both server-side and client-side, using a dedicated `i18n/` folder in the root directory outside `app/` and `server/` makes more sense. - 2. Clean - less clutter/fragmentation of i18n files, and should make resolving and loading files easier for us. \ No newline at end of file + 2. Clean - less clutter/fragmentation of i18n files, and should make resolving and loading files easier for us. + +## Locale `iso` renamed to `language` + +The `iso` property on a locale object has been renamed to `language` to be consistent with the usage of Language Tags on the web (e.g. `navigator.language` and `Accept-Language`). The original `iso` property name referred to ISO standards which describe valid Language Tags, see the [related issue](https://github.com/nuxt-modules/i18n/issues/2449) for more details. \ No newline at end of file diff --git a/docs/content/docs/5.v9/2.guide/5.browser-language-detection.md b/docs/content/docs/5.v9/2.guide/5.browser-language-detection.md new file mode 100644 index 000000000..036d5e5c8 --- /dev/null +++ b/docs/content/docs/5.v9/2.guide/5.browser-language-detection.md @@ -0,0 +1,85 @@ +--- +title: Browser language detection +description: Detect user browser's language. +--- + +By default, Nuxt i18n module attempts to redirect users to their preferred language by detecting their browser's language. This is controlled by the `detectBrowserLanguage` option. + +```ts [nuxt.config.ts] +export default defineNuxtConfig({ + i18n: { + detectBrowserLanguage: { + useCookie: true, + cookieKey: 'i18n_redirected', + redirectOn: 'root' // recommended + } + } +}) +``` + +::callout{icon="i-heroicons-light-bulb"} +For better SEO, it's recommended to set `redirectOn` to `root` (which is the default value). When set, the language detection is only attempted when the user visits the root path (`/`) of the site. This allows crawlers to access the requested page rather than being redirected away based on detected locale. It also allows linking to pages in specific locales. +:: + +Browser language is detected either from `navigator` when running on client-side, or from the `accept-language` HTTP header. Configured `locales` (or locales `language` and/or `code` when locales are specified in object form) are matched against locales reported by the browser (for example `en-US,en;q=0.9,no;q=0.8`). If there is no exact match for the full locale, the language code (letters before `-`) are matched against configured locales. + +To prevent redirecting users every time they visit the app, **Nuxt i18n module** sets a cookie using the detected locale. You can change the cookie's name by setting `detectBrowserLanguage.cookieKey` option to whatever you'd like, the default is _i18n_redirected_. + +```ts [nuxt.config.ts] +i18n: { + detectBrowserLanguage: { + useCookie: true, + cookieKey: 'my_custom_cookie_name' + } +} +``` + +If you'd rather have users be redirected to their browser's language every time they visit the app, disable the cookie by setting `detectBrowserLanguage.useCookie` to `false`. + +```ts [nuxt.config.ts] +export default defineNuxtConfig({ + i18n: { + detectBrowserLanguage: { + useCookie: false + } + } +}) +``` + +To completely disable the browser's language detection feature, set `detectBrowserLanguage` to `false`. + +```ts [nuxt.config.ts] +export default defineNuxtConfig({ + i18n: { + // ... + detectBrowserLanguage: false + } +}) +``` + +To redirect the user every time they visit the app and keep their selected choice, enable `alwaysRedirect`: + +```ts [nuxt.config.ts] +export default defineNuxtConfig({ + i18n: { + // ... + detectBrowserLanguage: { + useCookie: true, + alwaysRedirect: true + } + } +}) +``` + +To use the cookie within a cross-origin environment (e.g. in an iFrame), you can set `cookieCrossOrigin: true`. This will change the cookie settings from `SameSite=Lax` to `SameSite=None; Secure`. + +```ts [nuxt.config.ts] +export default defineNuxtConfig({ + i18n: { + detectBrowserLanguage: { + useCookie: true, + cookieCrossOrigin: true + } + } +}) +``` diff --git a/docs/content/docs/5.v9/2.guide/6.seo.md b/docs/content/docs/5.v9/2.guide/6.seo.md new file mode 100644 index 000000000..b61543961 --- /dev/null +++ b/docs/content/docs/5.v9/2.guide/6.seo.md @@ -0,0 +1,262 @@ +--- +title: SEO +description: When the `useLocaleHead` is called, @nuxtjs/i18n attempts to add some metadata to improve your pages SEO. Here's what it does. +--- + +**Nuxt i18n module** provides the `useLocaleHead` composable function. Calling this composable function returns a function which you can use to generate SEO metadata to optimize locale-related aspects of the app for the search engines. + +Here are the specific optimizations and features that it enables: + +- `lang` attribute for the `` tag +- `hreflang` alternate link generation +- OpenGraph locale tag generation +- canonical link generation + +[Read more about those features below](#feature-details) + +## Requirements + +To leverage the SEO benefits, you must configure the `locales` option as an array of objects, where each object has an `language` option set to the locale language tags: + +```ts [nuxt.config.ts] +export default defineNuxtConfig({ + i18n: { + locales: [ + { + code: 'en', + language: 'en-US' + }, + { + code: 'es', + language: 'es-ES' + }, + { + code: 'fr', + language: 'fr-FR' + } + ] + } +}) +``` + +You must also set the `baseUrl` option to your production domain in order to make alternate URLs fully-qualified: + +```ts [nuxt.config.ts] +export default defineNuxtConfig({ + i18n: { + baseUrl: 'https://my-nuxt-app.com' + } +}) +``` + +(Note that `baseUrl` can also be set to a function. Check [`baseUrl` documentation](/docs/options/routing#baseurl).) + +## Setup + +The `useLocaleHead` is a composable function, Calling that composable function returns a function that returns metadata that is handled by [Head management](https://nuxt.com/docs/getting-started/seo-meta) that is integrated within Nuxt. That metadata can be specified the `setup` function in various places within Nuxt: + +- [`app.vue`](https://nuxt.com/docs/guide/directory-structure/app) +- Vue components of [`pages`](https://nuxt.com/docs/guide/directory-structure/pages) directory +- Vue components of [`layouts`](https://nuxt.com/docs/guide/directory-structure/layouts) directory + +To enable SEO metadata, declare a `setup` function in one of the places specified above and make it return the result of a `useLocaleHead` function call. + +To avoid duplicating the code, it's recommended to set globally with [Meta Components](https://nuxt.com/docs/getting-started/seo-meta#components) in [layout components](https://nuxt.com/docs/guide/directory-structure/layouts) and override some values per-page Vue component like [`definePageMeta`](https://nuxt.com/docs/guide/directory-structure/pages#page-metadata), if necessary. + +::code-group + +```vue [app.vue] + +``` + +```vue [layouts/default.vue] + + + +``` + +```vue [pages/index.vue] + + + +``` + +:: + +Check out the options you can pass to the `useLocaleHead` in the [API documentation](/docs/api#uselocalehead) + +That's it! + +If you also want to add your own metadata, you have to call `useHead`. When you call `useHead` with the additional metadata, `useHead` will merge it global metadata that has already defined. + +```vue [pages/about/index.vue] + + + +``` + +## Feature details + +- `lang` attribute for the `` tag + + Sets the correct `lang` attribute, equivalent to the current locale's `language` value, in the `` tag. + +- `hreflang` alternate link + + Generates `` tags for every configured locale. The locales' `language` value are used as `hreflang` values. + + A "catchall" locale hreflang link is provided for each locale group (e.g. `en-*`). By default, it is the first locale provided, but another locale can be selected by setting `isCatchallLocale` to `true` on that specific locale object in your **Nuxt i18n module** configuration. [More on hreflang](https://support.google.com/webmasters/answer/189077) + + An example without selected "catchall" locale: + + ```ts {}[nuxt.config.ts] + export default defineNuxtConfig({ + i18n: { + locales: [ + { + code: 'en', + language: 'en-US' // Will be used as "catchall" locale by default + }, + { + code: 'gb', + language: 'en-GB' + } + ] + } + }) + ``` + + Here is how you'd use `isCatchallLocale` to selected another locale: + + ```ts [nuxt.config.ts] + export default defineNuxtConfig({ + i18n: { + locales: [ + { + code: 'en', + language: 'en-US' + }, + { + code: 'gb', + language: 'en-GB', + isCatchallLocale: true // This one will be used as catchall locale + } + ] + } + }) + ``` + + In case you already have an `en` locale `language` set, it'll be used as the "catchall" without doing anything + + ```ts [nuxt.config.ts] + export default defineNuxtConfig({ + i18n: { + locales: [ + { + code: 'gb', + language: 'en-GB' + }, + { + code: 'en', + language: 'en' // will be used as "catchall" locale + } + ] + } + }) + ``` + +- OpenGraph Locale tag generation + + Generates `og:locale` and `og:locale:alternate` meta tags as defined in the [Open Graph protocol](http://ogp.me/#optional). + +- Canonical link + + Generates `rel="canonical"` link on all pages to specify the "main" version of the page that should be indexed by search engines. This is beneficial in various situations: + + - When using the `prefix_and_default` strategy there are technically two sets of pages generated for the default locale -- one prefixed and one unprefixed. The canonical link will be set to the unprefixed version of the page to avoid duplicate indexation. + - When the page contains query parameters, the canonical link will **not include** the query params by default. This is typically the right thing to do as various query params can be inserted by trackers and should not be part of the canonical link. This can be overridden by using the `canonicalQueries` option. For example: + + ```vue + + ``` + + [More on canonical](https://support.google.com/webmasters/answer/182192#dup-content) diff --git a/docs/content/docs/5.v9/3.options/2.routing.md b/docs/content/docs/5.v9/3.options/2.routing.md new file mode 100644 index 000000000..a2f1089ea --- /dev/null +++ b/docs/content/docs/5.v9/3.options/2.routing.md @@ -0,0 +1,163 @@ +--- +title: Routing +description: Routing and Strategies options. +--- + +## `baseUrl` + +- type: `string` or `function` +- default: `''` + +The fallback base URL to use as a prefix for alternate URLs in `hreflang` tags. By default VueRouter's base URL will be used and only if that is not available, fallback URL will be used. + +Can also be a function (will be passed a Nuxt Context as a parameter) that returns a string. Useful to make base URL dynamic based on request headers. + +This property can also be set using [`runtimeConfig`](/docs/options/runtime-config). + +::callout{icon="i-heroicons-light-bulb"} +It's especially important to set this option when using SEO features, in which case it's required that generated SEO tags use fully-qualified URLs. +:: + +## `locales` + +- type: `array` +- default: `[]` + +List of locales supported by your app. Can either be an array of codes (`['en', 'fr', 'es']`) or an array of objects for more complex configurations: + +```json +[ + { "code": "en", "language": "en-US", "file": "en.js", "dir": "ltr" }, + { "code": "ar", "language": "ar-EG", "file": "ar.js", "dir": "rtl" }, + { "code": "fr", "language": "fr-FR", "file": "fr.js" } +] +``` + +When using an object form, the properties can be: + +- `code` (**required**) - unique identifier of the locale +- `language` (required when using SEO features) - A language-range used for SEO features and for matching browser locales when using [`detectBrowserLanguage`](/docs/options/browser#detectbrowserlanguage) functionality. Should use the [language tag syntax](https://www.w3.org/International/articles/language-tags/) as defined by the IETF's [BCP47](https://www.rfc-editor.org/info/bcp47), for example: + - `'en'` (`language` subtag for English) + - `'fr-CA'` (`language+region` subtags for French as used in Canada) + - `'zh-Hans'` (`language+script` subtags for Chinese written with Simplified script) +- `file` - The name of the file. Will be resolved relative to `langDir` path when loading locale messages from file. +- `files` - The name of the file in which multiple locale messages are defined. Will be resolved relative to `langDir` path when loading locale messages from file. +- `dir` - The dir property specifies the direction of the elements and content, value could be `'rtl'`, `'ltr'` or `'auto'`. +- `domain` (required when using [`differentDomains`](/docs/options/domain#differentdomains)) - the domain name you'd like to use for that locale (including the port if used). This property can also be set using [`runtimeConfig`](/docs/options/runtime-config). +- `domainDefault` (required when using [`differentDomains`](/docs/options/domain#differentdomains) while one or more of the domains having multiple locales) - set `domainDefault` to `true` for each locale that should act as a default locale for the particular domain. +- `...` - any custom property set on the object will be exposed at runtime. This can be used, for example, to define the language name for the purpose of using it in a language selector on the page. + +You can access all the properties of the current locale through the `localeProperties` property. When using an array of codes, it will only include the `code` property. + +## `defaultDirection` + +- type: `string` +- default: `ltr` + +The app's default direction. Will only be used when `dir` is not specified. + +## `defaultLocale` + +- type: `string` or `null` +- default: `null` + +The app's default locale. Should match code of one of defined `locales`. + +When using `prefix_except_default` strategy, URLs for locale specified here won't have a prefix. **It's recommended to set this to some locale** regardless of chosen strategy, as it will be used as a fallback locale when navigating to a non-existent route. + +## `strategy` + +- type: `string` +- default: `'prefix_except_default'` + +Routes generation strategy. Can be set to one of the following: + +- `'no_prefix'`: routes won't have a locale prefix +- `'prefix_except_default'`: locale prefix added for every locale except default +- `'prefix'`: locale prefix added for every locale +- `'prefix_and_default'`: locale prefix added for every locale and default + +## `customRoutes` + +- type: `string` (`page` or `config`) | `undefined` +- default: `page` + +Whether [custom paths](/docs/guide/custom-paths) are extracted from page files + +## `pages` + +- type: `object` +- default: `{}` + +If `customRoutes` option is disabled with `config`, the module will look for custom routes in the `pages` option. Refer to the [Routing](/docs/guide) for usage. + +## `skipSettingLocaleOnNavigate` + +- type: `boolean` +- default: `false` + +If `true`, the locale will not be set when navigating to a new locale. This is useful if you want to wait for the page transition to end before setting the locale yourself using [`finalizePendingLocaleChange`](/docs/api/vue-i18n#finalizependinglocalechange). See more information in [Wait for page transition](/docs/guide/lang-switcher#wait-for-page-transition). + +## `defaultLocaleRouteNameSuffix` + +- type: `string` +- default: `'default'` + +Internal suffix added to generated route names for default locale, if strategy is `prefix_and_default`. You shouldn't need to change this. + +## `routesNameSeparator` + +- type: `string` +- default: `'___'` + +Internal separator used for generated route names for each locale. You shouldn't need to change this. + +## `rootRedirect` + +- type: `string` or `object` or `null` +- default: `null` + +Set to a path to which you want to redirect users accessing the root URL (`/`). Accepts either a string or an object with `statusCode` and `path` properties. E.g + +```json +{ + "statusCode": 301, + "path": "about-us" +} +``` + +## `dynamicRouteParams` + +- type: `boolean` +- default: `false` + +Whether to localize dynamic route parameters. + +If `true`, you can set the dynamic route parameter to `nuxtI18n` field of `definePageMeta` for each locale: + +```ts [nuxt.config.ts] +export default defineNuxtConfig({ + modules: ['@nuxtjs/i18n'], + + i18n: { + dynamicRouteParams: true + } +}) +``` + +```vue + + + +``` + +See more information in [Dynamic route parameters](/docs/guide/lang-switcher#dynamic-route-parameters) diff --git a/src/runtime/composables/index.ts b/src/runtime/composables/index.ts index f3c33b064..69e1a4a9b 100644 --- a/src/runtime/composables/index.ts +++ b/src/runtime/composables/index.ts @@ -72,7 +72,7 @@ export function useSetI18nParams(seoAttributes?: SeoAttributesOptions): SetI18nP }) const currentLocale = getNormalizedLocales(locales).find(l => l.code === locale) || { code: locale } - const currentLocaleIso = currentLocale.iso + const currentLocaleLanguage = currentLocale.language const setMeta = () => { const metaObject: HeadParam = { @@ -93,8 +93,8 @@ export function useSetI18nParams(seoAttributes?: SeoAttributesOptions): SetI18nP metaObject.meta.push( ...getOgUrl(common, idAttribute, seoAttributes), - ...getCurrentOgLocale(currentLocale, currentLocaleIso, idAttribute), - ...getAlternateOgLocales(locales, currentLocaleIso, idAttribute) + ...getCurrentOgLocale(currentLocale, currentLocaleLanguage, idAttribute), + ...getAlternateOgLocales(locales, currentLocaleLanguage, idAttribute) ) } diff --git a/src/runtime/routing/compatibles/head.ts b/src/runtime/routing/compatibles/head.ts index 69301d952..80603b1b1 100644 --- a/src/runtime/routing/compatibles/head.ts +++ b/src/runtime/routing/compatibles/head.ts @@ -44,7 +44,7 @@ export function localeHead( const currentLocale = getNormalizedLocales(locales).find(l => l.code === locale) || { code: locale } - const currentIso = currentLocale.iso + const currentLanguage = currentLocale.language const currentDir = currentLocale.dir || defaultDirection // Adding Direction Attribute @@ -54,8 +54,8 @@ export function localeHead( // Adding SEO Meta if (seoAttributes && locale && unref(i18n.locales)) { - if (currentIso) { - metaObject.htmlAttrs.lang = currentIso + if (currentLanguage) { + metaObject.htmlAttrs.lang = currentLanguage } metaObject.link.push( @@ -65,8 +65,8 @@ export function localeHead( metaObject.meta.push( ...getOgUrl(common, idAttribute, seoAttributes), - ...getCurrentOgLocale(currentLocale, currentIso, idAttribute), - ...getAlternateOgLocales(unref(locales) as LocaleObject[], currentIso, idAttribute) + ...getCurrentOgLocale(currentLocale, currentLanguage, idAttribute), + ...getAlternateOgLocales(unref(locales) as LocaleObject[], currentLanguage, idAttribute) ) } @@ -92,29 +92,29 @@ export function getHreflangLinks( const localeMap = new Map() for (const locale of locales) { - const localeIso = locale.iso + const localeLanguage = locale.language - if (!localeIso) { - console.warn('Locale ISO code is required to generate alternate link') + if (!localeLanguage) { + console.warn('Locale `language` ISO code is required to generate alternate link') continue } - const [language, region] = localeIso.split('-') + const [language, region] = localeLanguage.split('-') if (language && region && (locale.isCatchallLocale || !localeMap.has(language))) { localeMap.set(language, locale) } - localeMap.set(localeIso, locale) + localeMap.set(localeLanguage, locale) } - for (const [iso, mapLocale] of localeMap.entries()) { + for (const [language, mapLocale] of localeMap.entries()) { const localePath = switchLocalePath(common, mapLocale.code) if (localePath) { links.push({ - [idAttribute]: `i18n-alt-${iso}`, + [idAttribute]: `i18n-alt-${language}`, rel: 'alternate', href: toAbsoluteUrl(localePath, baseUrl), - hreflang: iso + hreflang: language }) } } @@ -198,26 +198,26 @@ export function getOgUrl( export function getCurrentOgLocale( currentLocale: LocaleObject, - currentIso: string | undefined, + currentLanguage: string | undefined, idAttribute: NonNullable ) { - if (!currentLocale || !currentIso) return [] + if (!currentLocale || !currentLanguage) return [] // Replace dash with underscore as defined in spec: language_TERRITORY - return [{ [idAttribute]: 'i18n-og', property: 'og:locale', content: hypenToUnderscore(currentIso) }] + return [{ [idAttribute]: 'i18n-og', property: 'og:locale', content: hypenToUnderscore(currentLanguage) }] } export function getAlternateOgLocales( locales: LocaleObject[], - currentIso: string | undefined, + currentLanguage: string | undefined, idAttribute: NonNullable ) { - const alternateLocales = locales.filter(locale => locale.iso && locale.iso !== currentIso) + const alternateLocales = locales.filter(locale => locale.language && locale.language !== currentLanguage) return alternateLocales.map(locale => ({ - [idAttribute]: `i18n-og-alt-${locale.iso}`, + [idAttribute]: `i18n-og-alt-${locale.language}`, property: 'og:locale:alternate', - content: hypenToUnderscore(locale.iso!) + content: hypenToUnderscore(locale.language!) })) } diff --git a/src/runtime/routing/utils.ts b/src/runtime/routing/utils.ts index 890e05dc4..cdcf3e9be 100644 --- a/src/runtime/routing/utils.ts +++ b/src/runtime/routing/utils.ts @@ -154,7 +154,7 @@ export interface BrowserLocale { * @remarks * This type is used by {@link BrowserLocaleMatcher} first argument */ -export type TargetLocale = Required> +export type TargetLocale = Required> /** * The browser locale matcher @@ -182,7 +182,7 @@ function matchBrowserLocale(locales: TargetLocale[], browserLocales: string[]): // first pass: match exact locale. for (const [index, browserCode] of browserLocales.entries()) { - const matchedLocale = locales.find(l => l.iso.toLowerCase() === browserCode.toLowerCase()) + const matchedLocale = locales.find(l => l.language.toLowerCase() === browserCode.toLowerCase()) if (matchedLocale) { matchedLocales.push({ code: matchedLocale.code, score: 1 - index / browserLocales.length }) break @@ -192,7 +192,7 @@ function matchBrowserLocale(locales: TargetLocale[], browserLocales: string[]): // second pass: match only locale code part of the browser locale (not including country). for (const [index, browserCode] of browserLocales.entries()) { const languageCode = browserCode.split('-')[0].toLowerCase() - const matchedLocale = locales.find(l => l.iso.split('-')[0].toLowerCase() === languageCode) + const matchedLocale = locales.find(l => l.language.split('-')[0].toLowerCase() === languageCode) if (matchedLocale) { // deduct a thousandth for being non-exact match. matchedLocales.push({ code: matchedLocale.code, score: 0.999 - index / browserLocales.length }) @@ -238,8 +238,8 @@ export function findBrowserLocale( const normalizedLocales = [] for (const l of locales) { const { code } = l - const iso = l.iso || code - normalizedLocales.push({ code, iso }) + const language = l.language || code + normalizedLocales.push({ code, language }) } // finding! diff --git a/src/types.ts b/src/types.ts index 0297290d8..db759923f 100644 --- a/src/types.ts +++ b/src/types.ts @@ -256,7 +256,11 @@ export interface LocaleObject extends Record { file?: string | LocaleFile files?: string[] | LocaleFile[] isCatchallLocale?: boolean + /** + * @deprecated in v9, use `language` instead + */ iso?: string + language?: string } /** diff --git a/src/utils.ts b/src/utils.ts index 81b0b8ad8..a4200f42b 100644 --- a/src/utils.ts +++ b/src/utils.ts @@ -45,7 +45,7 @@ export function getNormalizedLocales(locales: NuxtI18nOptions['locales']): Local const normalized: LocaleObject[] = [] for (const locale of locales) { if (isString(locale)) { - normalized.push({ code: locale, iso: locale }) + normalized.push({ code: locale, language: locale }) } else { normalized.push(locale) } @@ -508,13 +508,21 @@ export const mergeConfigLocales = (configs: LocaleConfig[], baseLocales: LocaleO // set normalized locale or to existing entry if (typeof locale === 'string') { - mergedLocales.set(code, merged ?? { iso: code, code }) + mergedLocales.set(code, merged ?? { language: code, code }) continue } const resolvedFiles = resolveRelativeLocales(locale, config) delete locale.file + if (locale.iso) { + console.warn( + `Locale ${locale.iso} uses deprecated \`iso\` property, this will be replaced with \`language\` in v9` + ) + locale.language = locale.iso + delete locale.iso + } + // merge locale and files with existing entry if (merged != null) { merged.files ??= [] as LocaleFile[] diff --git a/test/routing-utils.test.ts b/test/routing-utils.test.ts index 0ee14620e..38ba30f96 100644 --- a/test/routing-utils.test.ts +++ b/test/routing-utils.test.ts @@ -171,8 +171,8 @@ describe('findBrowserLocale', () => { test('matches ISO locale code', () => { const locales = [ - { code: 'cn', iso: 'zh-CN' }, - { code: 'en', iso: 'en-US' } + { code: 'cn', language: 'zh-CN' }, + { code: 'en', language: 'en-US' } ] const browserLocales = ['zh', 'zh-CN'] @@ -181,8 +181,8 @@ describe('findBrowserLocale', () => { test('matches full ISO code', () => { const locales = [ - { code: 'us', iso: 'en-US' }, - { code: 'gb', iso: 'en-GB' } + { code: 'us', language: 'en-US' }, + { code: 'gb', language: 'en-GB' } ] const browserLocales = ['en-GB', 'en'] @@ -203,7 +203,7 @@ describe('findBrowserLocale', () => { const matchedLocales = [] as utils.BrowserLocale[] for (const [index, browserCode] of browserLocales.entries()) { const languageCode = browserCode.split('-')[0].toLowerCase() - const matchedLocale = locales.find(l => l.iso.split('-')[0].toLowerCase() === languageCode) + const matchedLocale = locales.find(l => l.language.split('-')[0].toLowerCase() === languageCode) if (matchedLocale) { matchedLocales.push({ code: matchedLocale.code, score: 1 * index }) break