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 value | Type | Description |
|---|---|---|
t | (key, vars?) => string | Translate a key synchronously. Returns the key name if labels haven't loaded yet. |
ready | boolean | true once the label file has been applied. |
locale | string | null | Current 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);Ruby on Rails
ShipEasyI18n integration for Ruby on Rails — i18n_t() view helper, layout script tag, Hotwire/Turbo compatibility, and i18n.config.json for ERB scanning.
Remix
ShipEasyI18n integration for Remix — server-side label loading in loader(), useShipEasyI18n() hook in components, and inline data via root loader.