ShipEasy Docs

Qwik / Qwik City

ShipEasyI18n integration for Qwik and Qwik City — useStore integration, Qwik City routeLoader$, and resumable hydration with zero client-side fetch.

Package: @i18n/qwik

Install

npm install @i18n/qwik

Script tag

Add the loader to src/root.tsx:

// src/root.tsx
export default component$(() => {
  return (
    <QwikCityProvider>
      <head>
        <script
          src="https://cdn.i18n.shipeasy.ai/v1/loader.js"
          data-key="i18n_pk_your_key_here"
          data-profile="en:prod"
        />
      </head>
      <body>
        <RouterOutlet />
      </body>
    </QwikCityProvider>
  );
});

useShipEasyI18nProvider and useStore integration

Initialise the ShipEasyI18n store at the root component level so all child components can access labels without re-fetching:

// src/root.tsx
import { component$, useStore } from '@builder.io/qwik';
import { useShipEasyI18nProvider } from '@i18n/qwik';

export default component$(() => {
  const i18n = useShipEasyI18nProvider({ profile: 'en:prod' });

  return (
    <QwikCityProvider>
      <RouterOutlet />
    </QwikCityProvider>
  );
});

useShipEasyI18n() in components

// src/components/NavBar.tsx
import { component$ } from '@builder.io/qwik';
import { useShipEasyI18n } from '@i18n/qwik';

export const NavBar = component$(() => {
  const { t, ready } = useShipEasyI18n();

  return (
    <nav>
      <a href="/" data-label="nav.home">{t('nav.home')}</a>
      <a href="/account" data-label="nav.account">{t('nav.account')}</a>
    </nav>
  );
});

Qwik City routeLoader$

Use routeLoader$ to fetch labels on the server and serialize them into the resumable state — the client never makes a separate labels request:

// src/routes/layout.tsx
import { routeLoader$ } from '@builder.io/qwik-city';
import { fetchLabels } from '@i18n/qwik/server';

export const useShipEasyI18nData = routeLoader$(async ({ cookie }) => {
  const profile = cookie.get('i18n_profile')?.value ?? 'en:prod';

  return fetchLabels({
    i18nKey: process.env.ShipEasyI18n_KEY!,
    profile,
    chunk: 'index',
  });
});

Pass the loader result to ShipEasyI18nProvider:

// src/routes/layout.tsx
import { component$ } from '@builder.io/qwik';
import { ShipEasyI18nProvider } from '@i18n/qwik';

export default component$(() => {
  const i18nData = useShipEasyI18nData();

  return (
    <ShipEasyI18nProvider initialData={i18nData.value}>
      <Slot />
    </ShipEasyI18nProvider>
  );
});

Because Qwik serializes component state into the HTML, label data loaded in routeLoader$ is available instantly on resumption — no hydration fetch needed.

For the full implementation spec including package source code and edge cases, see plans/frameworks/qwik.md in the repository.