Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support i18n helpers with Inertia #21

Closed
axelvaindal opened this issue Apr 28, 2024 · 6 comments
Closed

Support i18n helpers with Inertia #21

axelvaindal opened this issue Apr 28, 2024 · 6 comments

Comments

@axelvaindal
Copy link

Package version

1.0.0-25

Describe the bug

Hello,

I'm using Inertia with AdonisJS 6 and I need to create an internationalized version of the website.
Notably, most of the static content must be translated.

With AdonisJS + Edge, it's fairly simple as all the code is processed client side.

import { HttpContext } from '@adonisjs/core/http'

export default class PostsController {
  async store({ i18n, session }: HttpContext) {
    session.flash('success', {
      message: i18n.t('post.created')
    })
  }
}

// Inside edge template
<h1> {{ t('messages.heroTitle') }} </h1>

However though, when I create a wrapper around i18n to be used in my React component, it imports the server side code which is not good.

Is there any way to have a t() helper resolving the translations the same way we can do this in Edge ?

If not, can you pinpoint me in the right direction in creating such a thing ?

Thanks 🙏.

Reproduction repo

No response

@miguelmanteigueiro
Copy link

Hi @axelvaindal, have you found a solution? Looking forward!

@keil0
Copy link

keil0 commented May 29, 2024

@miguelmanteigueiro the best solution I've found is to pass the i18n instance via Inertia's shared props.

# config/inertia.ts

import { defineConfig } from '@adonisjs/inertia'

export default defineConfig({
  /**
   * Path to the Edge view that will be used as the root view for Inertia responses
   */
  rootView: 'inertia_layout',

  /**
   * Data that should be shared with all rendered pages
   */
  sharedData: {
    user: (ctx) => ctx.auth?.user?.serialize({ fields: ['id', 'email'] }),
    errors: (ctx) => ctx.session.flashMessages.get('errors'),
    notification: (ctx) => ctx.session.flashMessages.get('notification'),
    i18n: (ctx) => {
      return {
        ...ctx.i18n,
        locale: ctx.i18n.locale,
      }
    },
  },

  /**
   * Options for the server-side rendering
   */
  ssr: {
    enabled: true,
    entrypoint: 'inertia/app/ssr.tsx',
  },
})

I'll try to optimize that later.

@miguelmanteigueiro
Copy link

@miguelmanteigueiro the best solution I've found is to pass the i18n instance via Inertia's shared props.

# config/inertia.ts



import { defineConfig } from '@adonisjs/inertia'



export default defineConfig({

  /**

   * Path to the Edge view that will be used as the root view for Inertia responses

   */

  rootView: 'inertia_layout',



  /**

   * Data that should be shared with all rendered pages

   */

  sharedData: {

    user: (ctx) => ctx.auth?.user?.serialize({ fields: ['id', 'email'] }),

    errors: (ctx) => ctx.session.flashMessages.get('errors'),

    notification: (ctx) => ctx.session.flashMessages.get('notification'),

    i18n: (ctx) => {

      return {

        ...ctx.i18n,

        locale: ctx.i18n.locale,

      }

    },

  },



  /**

   * Options for the server-side rendering

   */

  ssr: {

    enabled: true,

    entrypoint: 'inertia/app/ssr.tsx',

  },

})

I'll try to optimize that later.

Thanks! And how would one change the i18n in a view (e.g., Index.svelte) and change the shared prop? I've tried finding that information in Inertia documentation and Adonis but no cigar.

@keil0
Copy link

keil0 commented May 29, 2024

@miguelmanteigueiro I use a combination of custom header and cookie in a middleware :

# app/middleware/detect_user_locale_middleware.ts

import { I18n } from '@adonisjs/i18n'
import i18nManager from '@adonisjs/i18n/services/main'
import type { NextFn } from '@adonisjs/core/types/http'
import { type HttpContext, RequestValidator } from '@adonisjs/core/http'

export default class DetectUserLocaleMiddleware {
  static {
    RequestValidator.messagesProvider = (ctx) => {
      return ctx.i18n.createMessagesProvider()
    }
  }

  protected getRequestLocale(ctx: HttpContext) {
    // Retrieve supported languages
    const supportedLocales = i18nManager.supportedLocales()

    // First, try to read the language from a custom header
    const customHeaderLocale = ctx.request.header('X-User-Language')

    // If the custom header exists and is a valid locale, use it
    if (customHeaderLocale && supportedLocales.includes(customHeaderLocale)) {
      return customHeaderLocale
    }

    // Then check the cookie
    const cookieLocale = ctx.request.cookie('user-locale')
    if (cookieLocale && supportedLocales.includes(cookieLocale)) {
      return cookieLocale
    }

    // Fallback to the Accept-Language header if no valid custom header or cookie
    const userLanguages = ctx.request.languages()
    return i18nManager.getSupportedLocaleFor(userLanguages)
  }

  async handle(ctx: HttpContext, next: NextFn) {
    const language = this.getRequestLocale(ctx)

    // Update the cookie if necessary
    if (!ctx.request.cookie('user-locale') || ctx.request.cookie('user-locale') !== language) {
      ctx.response.cookie('user-locale', language, {
        httpOnly: true,
        path: '/',
        maxAge: 60 * 60 * 24 * 30, // Example duration: 30 days
        sameSite: true, // Improve cookie security
      })
    }

    // Update the locale for this request
    ctx.i18n = i18nManager.locale(language || i18nManager.defaultLocale)
    ctx.containerResolver.bindValue(I18n, ctx.i18n)

    return next()
  }
}

/**
 * Notify TypeScript about i18n property
 */
declare module '@adonisjs/core/http' {
  export interface HttpContext {
    i18n: I18n
  }
}

@tom-brulin
Copy link
Contributor

Hi,

I got the same problem.

Then I created this package to solve the problem adonis-inertia-i18n, feel free to use it and give a feeback.

@Julien-R44
Copy link
Member

I think we can close the issue

To conclude, there are generally two solutions for sharing translation files :

  • The first is for your backend to send the translations to the frontend, as seen in the solutions suggested above, via inertia.share or other methods
  • The second solution: The i18n json files for Adonis use the ICU format. So you can just import this file directly into your frontend and let your frontend i18n library handle it. Most have ICU support. For example, https://react.i18next.com/misc/using-with-icu-format

Each has its own advantages and disadvantages. up to you to choose

@Julien-R44 Julien-R44 closed this as not planned Won't fix, can't repro, duplicate, stale Sep 30, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

5 participants