platforms icon indicating copy to clipboard operation
platforms copied to clipboard

Support for internationalization

Open gmreburn opened this issue 7 months ago • 3 comments

[domain] conflicts with Next.js's routing guide on internationalization that recommends a [locale] directory. How can this work with next-intl/middleware or something similar?

gmreburn avatar Jan 25 '24 19:01 gmreburn

The problem with using something like next-intl/middleware is that their middleware returns a NextResponse and so is the rewrites of this repo and you can't return two responses. I have tried chaining multiple middleware functions but I think you can't do that. You can try using something like https://github.com/vercel/next.js/tree/canary/examples/app-dir-i18n-routing but then you will have to change the rewrites to take the locale into consideration.

alabdulaal avatar Jan 31 '24 16:01 alabdulaal

@gmreburn were you able to solve this?

ddeisadze avatar Mar 26 '24 21:03 ddeisadze

Yes, we solved this problem in a less elegant way. It works, but I'd love a solution that provides both the domain and locale as a dynamic route. I think the structure could work like /[domain]/[locale]/ with some middleware magic.

For our solution, we use [locale] as a dynamic route and use a helper function within components to access the domain via server or client components. We use a load balancer to accept traffic from multiple domains and route to the same Next.js process. I think this solution could be adapted to work with middleware, but we didn't go to that length. Please share if you take the time to do so.

/**
 * Get domain from env variables for local,
 * from request if it was provided (for server side),
 * or from location object (for client side)
 *
 * @param headers
 */
export function getDomain(headers?: ReadonlyHeaders): string {
  let domain = null

  if (process.env.NEXT_PUBLIC_CLIENT_DOMAIN) {
    // if a domain is configured in env variables, return from env
    domain = process.env.NEXT_PUBLIC_CLIENT_DOMAIN) 
  } else if (headers) {
    // try to parse from request
    domain = getDomainFromHeaders(headers)
  } else if (typeof window !== 'undefined') {
    // if no request specified and env variable is not set
    // try to get from location global object
    domain = window.location?.host
  }

  if (!domain) {
    throw new Error('Could not parse the domain')
  }

  return domain
}

export function getDomainFromHeaders(headers: ReadonlyHeaders): string | null {
  return headers.get('X-Forwarded-Host') ?? headers.get('Host')
}
`

You can use it in a client or SSR component:

```ts
import { headers } from 'next/headers'
import { getDomain } from '@/helpers/global'

export default async function MyServerComponent() {
  const headersList = headers()
  const domain = getDomain(headersList)

  ...

Or with a client component:

'use client'

import { getDomain } from '@/helpers/global'

export default function MyClientComponent() {
  const domain = getDomain()

  ...

For local development we set NEXT_PUBLIC_CLIENT_DOMAIN in .env.local to a domain name we desire for testing purposes.

gmreburn avatar Apr 01 '24 00:04 gmreburn