ShipEasy Docs

React

ShipEasyI18n integration for React 18+ SPAs. ShipEasyI18nProvider, useShipEasyI18n hook, ShipEasyI18nString component. Works with Vite, CRA, and any React setup.

Package: @i18n/react

Install

npm install @i18n/react

Script tag

Add the loader to <head> before your React bundle. For Vite, this goes in index.html:

<!-- index.html -->
<head>
  <script
    src="https://cdn.i18n.shipeasy.ai/v1/loader.js"
    data-key="i18n_pk_your_key_here"
    data-profile="en:prod"
    async
  ></script>
</head>

Provider

Wrap your app with ShipEasyI18nProvider:

// main.tsx
import React from 'react';
import ReactDOM from 'react-dom/client';
import { ShipEasyI18nProvider } from '@i18n/react';
import App from './App';

ReactDOM.createRoot(document.getElementById('root')!).render(
  <ShipEasyI18nProvider>
    <App />
  </ShipEasyI18nProvider>
);

useShipEasyI18n hook

import { useShipEasyI18n } from '@i18n/react';

function Greeting({ name }: { name: string }) {
  const { t, ready } = useShipEasyI18n();

  return (
    <h1 data-label="user.greeting" data-variables={JSON.stringify({ name })}>
      {t('user.greeting', { name })}
    </h1>
  );
}
Return valueTypeDescription
t(key, vars?) => stringTranslate a key synchronously. Returns the key name if labels haven't loaded yet.
readybooleantrue once the label file has been applied.
localestring | nullCurrent profile, e.g. "en:prod".

ShipEasyI18nString component

Declarative wrapper — no hook needed:

import { ShipEasyI18nString } from '@i18n/react';

function NavBar() {
  return (
    <nav>
      <ShipEasyI18nString labelKey="nav.home" />
      <ShipEasyI18nString labelKey="nav.signIn" />
    </nav>
  );
}

ShipEasyI18nString renders a <span data-label="..."> with the translated value as its text content.

SSR / hydration

For server-rendered React (without Next.js), inject the inline label data in your server's HTML template:

// Express server
import { fetchLabels, serializeLabelsToScript } from '@i18n/react/server';

app.get('*', async (req, res) => {
  const labels = await fetchLabels({
    i18nKey: process.env.ShipEasyI18n_KEY,
    profile: 'en:prod',
    chunk: 'index',
  });

  const html = ReactDOMServer.renderToString(<App />);
  res.send(`
    <!DOCTYPE html>
    <html>
      <head>
        <script id="i18n-data" type="application/json">
          ${serializeLabelsToScript(labels)}
        </script>
        <script src="https://cdn.i18n.shipeasy.ai/v1/loader.js"
                data-key="${process.env.ShipEasyI18n_KEY}"
                data-profile="en:prod"></script>
      </head>
      <body>
        <div id="root">${html}</div>
      </body>
    </html>
  `);
});

For Next.js, use @i18n/next instead — it handles the inline data injection automatically as a Server Component.

HOC for class components

import { withShipEasyI18n, type ShipEasyI18nContextValue } from '@i18n/react';

class LegacyButton extends React.Component<{ i18n: ShipEasyI18nContextValue }> {
  render() {
    const { t } = this.props.i18n;
    return <button>{t('checkout.submit')}</button>;
  }
}

export default withShipEasyI18n(LegacyButton);