2

I'm trying to implement localization in Qwik framework with Qwik Speak but i'm getting the following error when trying to resolve locale:

Internal server error: locale is not a function

I'm following the instructions in the Qwik Speak docs.

  1. Created the src/speak-config.ts file
  2. Added QwikSpeakProvider to the src/root.tsx
  3. Added the onRequest function to the src/routes/layout.tsx
  4. Created the json files: public/i18n/en-US/app.json and public/i18n/it-IT/app.json

But still getting the error on the locale(lang || config.defaultLocale.lang); call in the src/routes/layout.tsx.

My src/speak-config.ts:

export const config: SpeakConfig = {
  defaultLocale: { lang: 'en-US', currency: 'USD', timeZone: 'America/Los_Angeles' },
  supportedLocales: [
    { lang: 'it-IT', currency: 'EUR', timeZone: 'Europe/Rome' },
    { lang: 'en-US', currency: 'USD', timeZone: 'America/Los_Angeles' }
  ],
  assets: [
    'app' // Translations shared by the pages
  ]
};

export const loadTranslation$: LoadTranslationFn = $(async (lang: string, asset: string, origin?: string) => {
  let url = '';
  // Absolute urls on server
  if (isServer && origin) {
    url = origin;
  }
  url += `/i18n/${lang}/${asset}.json`;
  const response = await fetch(url);

  if (response.ok) {
    return response.json();
  }
  else if (response.status === 404) {
    console.warn(`loadTranslation$: ${url} not found`);
  }
});

export const translationFn: TranslationFn = {
  loadTranslation$: loadTranslation$
};

My src/root.tsx:

export default component$(() => {
  useStyles$(globalStyles);

  return (
    <QwikSpeakProvider config={config} translationFn={translationFn}>
      <QwikCityProvider>
        <head>
          <meta charSet="utf-8" />
          <link rel="manifest" href="/manifest.json" />
          <QwikPartytown forward={["dataLayer.push"]} />
          <RouterHead />
        </head>
        <body lang="en">
          <RouterOutlet />
          <ServiceWorkerRegister />
        </body>
      </QwikCityProvider>
    </QwikSpeakProvider>
  );
});

My src/routes/layout.tsx:

export default component$(() => {
  return (
    <>
      <Header />
      <main>
        <section>
          <Slot />
        </section>
      </main>
      <Footer />
    </>
  );
});

export const onRequest: RequestHandler = ({ request, locale }) => {
  const cookie = request.headers?.get("cookie");
  const acceptLanguage = request.headers?.get("accept-language");

  let lang: string | null = null;
  // Try whether the language is stored in a cookie
  if (cookie) {
    const result = new RegExp(
      "(?:^|; )" + encodeURIComponent("locale") + "=([^;]*)"
    ).exec(cookie);
    if (result) {
      lang = JSON.parse(result[1])["lang"];
    }
  }
  // Try to use user language
  if (!lang) {
    if (acceptLanguage) {
      lang = acceptLanguage.split(";")[0]?.split(",")[0];
    }
  }

  // Set Qwik locale
  locale(lang || config.defaultLocale.lang);
};
Kristóf Göncző
  • 382
  • 1
  • 5
  • 16

2 Answers2

0

Move this piece of code to a new file src/routes/plugin.ts

export const onRequest: RequestHandler = ({ request, locale }) => {
  const cookie = request.headers?.get("cookie");
  const acceptLanguage = request.headers?.get("accept-language");

  let lang: string | null = null;
  // Try whether the language is stored in a cookie
  if (cookie) {
    const result = new RegExp(
      "(?:^|; )" + encodeURIComponent("locale") + "=([^;]*)"
    ).exec(cookie);
    if (result) {
      lang = JSON.parse(result[1])["lang"];
    }
  }
  // Try to use user language
  if (!lang) {
    if (acceptLanguage) {
      lang = acceptLanguage.split(";")[0]?.split(",")[0];
    }
  }

  // Set Qwik locale
  locale(lang || config.defaultLocale.lang);
};
0

From what I read it seems you are missing the step to create the function.

/**
 * Translation files are lazy-loaded via dynamic import and will be split into separate chunks during build.
 * Keys must be valid variable names
 */
const translationData = import.meta.glob<Translation>('/i18n/**/*.json');

/**
 * Using server$, translation data is always accessed on the server
 */
const loadTranslation$: LoadTranslationFn = server$(async (lang: string, asset: string) =>
  await translationData[`/i18n/${lang}/${asset}.json`]?.()
);

export const translationFn: TranslationFn = {
  loadTranslation$: loadTranslation$
};

You can follow the step by step guide here: https://github.com/robisim74/qwik-speak/blob/main/docs/tutorial-routing.md

Aurélien B.
  • 498
  • 2
  • 9