sergiodxa / remix-i18next

The easiest way to translate your Remix apps
https://sergiodxa.github.io/remix-i18next/
MIT License
584 stars 45 forks source link

Deployment to Vercel disables SSR #75

Closed nils-jansen closed 2 years ago

nils-jansen commented 2 years ago

Describe the bug

When running locally, everything works as expected and translations are rendered before they end up in the browser. However, when deployed to Vercel, the reponse only contains the i18next keys, which are then resolved and rendered on the client.

Your Example Website or App

https://locize-remix-i18next-example.vercel.app/

Steps to Reproduce the Bug or Issue

  1. Visit https://locize-remix-i18next-example.vercel.app/ (exact deployment of https://github.com/locize/locize-remix-i18next-example)
  2. Open devtools
  3. Check the raw response from server
  4. Compare to local behaviour

Expected behavior

Content including translations should be rendered on the server.

Screenshots or Videos

No response

Platform

Additional context

Not sure if this is an issue I can resolve within Vercel. If so, please point me in the right direction.

sergiodxa commented 2 years ago

The problem is that the server config is not able to find the translation files, this usually happens in serverless platforms because they change where your files and your public files are located on the file system after deploy, so your paths in local change, and in general reading from the file system in serverless platforms like Vercel and Netlify is not safe.

I recommend you to import your translations server-side instead of reading from the file system if you deploy to serverless.

nils-jansen commented 2 years ago

Ok, thanks for the explanation, this makes more sense now. How would I go about "importing translations server-side"?

sergiodxa commented 2 years ago

@nils-jansen you can import it as any other module

import translations from "~/path/to/translations/lng/namespace.json"

Then pass it to the configure of i18next as resources or calling addResourceBundle.

nils-jansen commented 2 years ago

Ok, thanks @sergiodxa. The frontend is still trying to load json files from the server as I am initializing it with the http backend. How can I make the frontend "dumb" while still keeping Remix happy during hydration?

sergiodxa commented 2 years ago

@nils-jansen remove the backend, you don't really need it if you setup the resources option or used addResourceBundle functionn.

michibertel commented 2 years ago

When using resources, SSR works totally fine as expected.

Although when removing the backend client side, SSR of course still works fine but useTranslation always replaces the correct value with the defaultValue on the client side which is initially set to the key itself. Is there a (clean) way to prevent useTranslation from using the defaultValue and just stay "dumb"?

akamfoad commented 1 year ago

@michibertel

Make sure you pass the imported file to the translation namespace, which is the default namespace, like:

 resources: {
    en: { translation: englishTranslations },
    ku: { translation: kurdishTranslations },
    ar: { translation: arabicTranslations },
  },

or if your files are managed separately for each name space per language, pass the imported namespace file to the corresponding namespace for each language, like:

 resources: {
    en: { common: commonEN, home: homeEN, about: aboutEN },
    ku: { common: commonKU, home: homeKU, about: aboutKU },
    ar: { common: commonAR, home: homeAR, about: aboutAR },
  },
designbyadrian commented 1 year ago

@nils-jansen remove the backend, you don't really need it if you setup the resources option or used addResourceBundle functionn.

How do you add a resource? The Remix-i18next instance doesn't have that property:

Property 'addResourceBundle' does not exist on type 'RemixI18Next'
vox91 commented 9 months ago

I also had the same issues when deploying on Cloudflare. Thanks @sergiodxa for your help (and this library).

@designbyadrian

Hope this can help you :

1) In i18next.server.ts

import en from "~/assets/locales/en/common.json"; //server side
import fr from "~/assets/locales/fr/common.json"; //server side

let i18next = new RemixI18Next({
    detection: {
        cookie: localeCookie,
        supportedLanguages: i18nextOptions.supportedLngs,
        fallbackLanguage: i18nextOptions.fallbackLng,
    },
    // This is the configuration for i18next used
    // when translating messages server-side only
    i18next: {
        ...i18nextOptions,
        resources: {
            "en": {
                "common": en,
            },
            "fr": {
                "common": fr,
            },
        },
    },
});`

The backend has been removed.

Then in entry.server.tsx

import en from "~/assets/locales/en/common.json"; //server side
import fr from "~/assets/locales/fr/common.json"; //server side

export default async function handleRequest(
  request: Request,
  responseStatusCode: number,
  responseHeaders: Headers,
  remixContext: EntryContext,
  // This is ignored so we can keep it in the template for visibility.  Feel
  // free to delete this parameter in your app if you're not using it!
  // eslint-disable-next-line @typescript-eslint/no-unused-vars
  loadContext: AppLoadContext
) {
  let instance = createInstance();
  let lng = await i18next.getLocale(request);

  // Check the user preferences
  let { locale } = await getLocale(request);
  if (locale) {
    lng = locale;
  }

  let ns = i18next.getRouteNamespaces(remixContext);

  await instance
    .use(initReactI18next) // Tell our instance to use react-i18next
    //.use(Backend) // Setup our backend
    .init({
      ...i18nextOptions, // spread the configuration
      lng, // The locale we detected above
      ns, // The namespaces the routes about to render wants to use
      // use resources from the server
      resources: {
        en: {
          common: en,
        },
        fr: {
          common: fr,
        },
      },
    });
   [...]
}

In entry.client.tsx, no changes : the locale files must be in the "public" folder and the backend can be used (i18next-http-backend)

nboliver-ventureweb commented 1 month ago

@vox91 Thanks for the example code, this helped me get translations rendering on the server using Vercel.