Skip to content

Commit

Permalink
feat: set locale domains via runtimeConfig (nuxt-modules#2443) (nuxt-…
Browse files Browse the repository at this point in the history
…modules#2446)

* feat: set locale domains via runtimeConfig (nuxt-modules#2443)

* docs: add example of runtime domain configuration to the guide
  • Loading branch information
cjpearson authored Oct 1, 2023
1 parent a1ffd99 commit 8982b2c
Show file tree
Hide file tree
Showing 7 changed files with 104 additions and 7 deletions.
16 changes: 16 additions & 0 deletions docs/content/2.guide/10.different-domains.md
Original file line number Diff line number Diff line change
Expand Up @@ -109,3 +109,19 @@ export default defineNuxtConfig({
// ...
})
```

With the above config, a build would have to be run for staging and production with different .env files that specify `DOMAIN_UK` and `DOMAIN_FR`.

Alternatively, to avoid the need for multiple builds, the locale domains can be overridden via runtime environment variables. The variable name should follow the format `NUXT_PUBLIC_I18N_LOCALES_{locale code}_DOMAIN`

For example:

```env {}[production .env]
NUXT_PUBLIC_I18N_LOCALES_UK_DOMAIN=uk.example.test
NUXT_PUBLIC_I18N_LOCALES_FR_DOMAIN=fr.example.test
```

```env {}[staging .env]
NUXT_PUBLIC_I18N_LOCALES_UK_DOMAIN=uk.staging.example.test
NUXT_PUBLIC_I18N_LOCALES_FR_DOMAIN=fr.staging.example.test
```
2 changes: 1 addition & 1 deletion docs/content/3.options/2.routing.md
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ When using an object form, the properties can be:
- `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`](/options/domain#differentdomains)) - the domain name you'd like to use for that locale (including the port if used)
- `domain` (required when using [`differentDomains`](/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`](./runtime-config).
- `...` - 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.
Expand Down
7 changes: 7 additions & 0 deletions docs/content/3.options/9.runtime-config.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ export default defineNuxtConfig({
experimental: {
jsTsFormatResource: true,
},
locales: {},
// other options ...
}
}
Expand Down Expand Up @@ -66,3 +67,9 @@ Note that the `baseUrl` module option allows you to set the function, but the ru
* key: `NUXT_PUBLIC_I18N_EXPERIMENTAL_JS_TS_FORMAT_RESOURCE`

This runtime config option is the same as the [`experimental`](./misc#experimental) module option.

### `locales`
* property: `locales[code].domain`
* key: `NUXT_PUBLIC_I18N_LOCALES_{code}_DOMAIN`

This runtime config option allows overriding the domain set in the [`locales`](./routing#locales) module option.
60 changes: 60 additions & 0 deletions specs/different_domains.runtimeConfig.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
import { test, expect } from 'vitest'
import { fileURLToPath } from 'node:url'
import { setup, $fetch } from './utils'
import { getDom } from './helper'

await setup({
rootDir: fileURLToPath(new URL(`./fixtures/domain`, import.meta.url)),
// overrides
nuxtConfig: {
runtimeConfig: {
public: {
i18n: {
locales: {
en: {
domain: 'en.staging.nuxt-app.localhost'
},
fr: {
domain: 'fr.staging.nuxt-app.localhost'
}
}
}
}
},
i18n: {
locales: [
{
code: 'en',
iso: 'en',
name: 'English',
domain: 'en.nuxt-app.localhost'
},
{
code: 'fr',
iso: 'fr-FR',
name: 'Français',
domain: 'fr.nuxt-app.localhost'
}
],
differentDomains: true,
detectBrowserLanguage: {
useCookie: true
}
}
}
})

test('pass `<NuxtLink> to props using domains from runtimeConfig', async () => {
const html = await $fetch('/', {
headers: {
Host: 'fr.nuxt-app.localhost'
}
})
const dom = getDom(html)
expect(dom.querySelector('#switch-locale-path-usages .switch-to-en a').getAttribute('href')).toEqual(
`http://en.staging.nuxt-app.localhost`
)
expect(dom.querySelector('#switch-locale-path-usages .switch-to-fr a').getAttribute('href')).toEqual(
`http://fr.staging.nuxt-app.localhost`
)
})
13 changes: 12 additions & 1 deletion src/module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -135,7 +135,18 @@ export default defineNuxtModule<NuxtI18nOptions>({
// for public
nuxt.options.runtimeConfig.public.i18n = defu(nuxt.options.runtimeConfig.public.i18n, {
experimental: options.experimental,
baseUrl: options.baseUrl
baseUrl: options.baseUrl,
locales: options.locales.reduce(
(obj, locale) => {
if (typeof locale === 'string') {
obj[locale] = { domain: undefined }
} else {
obj[locale.code] = { domain: locale.domain }
}
return obj
},
{} as Record<string, { domain: string | undefined }>
)
// TODO: we should support more i18n module options. welcome PRs :-)
})

Expand Down
11 changes: 7 additions & 4 deletions src/runtime/internal.ts
Original file line number Diff line number Diff line change
Expand Up @@ -490,10 +490,13 @@ export function getLocaleDomain(locales: LocaleObject[]): string {

export function getDomainFromLocale(localeCode: Locale, locales: LocaleObject[], nuxt?: NuxtApp): string | undefined {
// lookup the `differentDomain` origin associated with given locale.
const config = nuxt?.$config.public.i18n as { locales?: Record<Locale, { domain?: string }> }
const lang = locales.find(locale => locale.code === localeCode)
if (lang && lang.domain) {
if (hasProtocol(lang.domain)) {
return lang.domain
const domain = config?.locales?.[localeCode]?.domain ?? lang?.domain

if (domain) {
if (hasProtocol(domain, { strict: true })) {
return domain
}
let protocol
if (process.server) {
Expand All @@ -504,7 +507,7 @@ export function getDomainFromLocale(localeCode: Locale, locales: LocaleObject[],
} else {
protocol = new URL(window.location.origin).protocol
}
return protocol + '//' + lang.domain
return protocol + '//' + domain
}

console.warn(formatMessage('Could not find domain name for locale ' + localeCode))
Expand Down
2 changes: 1 addition & 1 deletion src/runtime/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -464,7 +464,7 @@ export function extendPrefixable(differentDomains: boolean) {
export function extendSwitchLocalePathIntercepter(
differentDomains: boolean,
normalizedLocales: LocaleObject[],
nuxt?: NuxtApp
nuxt: NuxtApp
): SwitchLocalePathIntercepter {
return (path: string, locale: Locale): string => {
if (differentDomains) {
Expand Down

0 comments on commit 8982b2c

Please sign in to comment.