home / skills / toilahuongg / shopify-agents-kit / shopify-app-i18n

shopify-app-i18n skill

/.claude/skills/shopify-app-i18n

This skill guides adding multi-language support to Shopify Apps using i18next, including setup, translation files, and admin context for broad reach.

npx playbooks add skill toilahuongg/shopify-agents-kit --skill shopify-app-i18n

Review the files below or copy the command above to add this skill to your agents.

Files (1)
SKILL.md
3.1 KB
---
name: shopify-app-i18n
description: Guide for adding Multi-language support to Shopify Apps using i18next. Covers setup, localization files, and Admin context.
---

# Internationalization (i18n) for Shopify Apps

Shopify merchants exist globally. Your app MUST support multiple languages to be featured or widely adopted.

## 1. Stack
- **Library**: `i18next` (Standard)
- **React**: `react-i18next`
- **Remix**: `remix-i18next`

## 2. Setup

### Installation
```bash
npm install i18next react-i18next remix-i18next i18next-fs-backend i18next-http-backend
```

### Configuration (`app/i18n.server.ts`)
Create a server-side instance to detect language.

```typescript
import { RemixI18Next } from "remix-i18next/server";
import { createInstance } from "i18next";
import i18n from "~/i18n"; // client config
import { resolve } from "node:path";

export const i18nServer = new RemixI18Next({
  detection: {
    supportedLanguages: i18n.supportedLngs,
    fallbackLanguage: i18n.fallbackLng,
  },
  // This is the configuration for i18next
  i18next: {
    ...i18n,
    backend: {
      loadPath: resolve("./public/locales/{{lng}}/{{ns}}.json"),
    },
  },
});
```

### Root Loader (`app/root.tsx`)
Inject the locale into the document.

```typescript
export async function loader({ request }: LoaderFunctionArgs) {
  const locale = await i18nServer.getLocale(request);
  return json({ locale });
}

export const handle = {
  // In the handle export, we can add a reference to a translation namespace
  // This will load the translations for this namespace for this route
  i18n: "common",
};

export default function App() {
  const { locale } = useLoaderData<typeof loader>();
  useChangeLanguage(locale); // Syncs remix locale with i18next
  
  return (
    <html lang={locale} dir={i18n.dir(locale)}>
      {/* ... */}
    </html>
  );
}
```

## 3. Translation Files
Store JSON files in `public/locales`.

```
public/
  locales/
    en/
      common.json
    fr/
      common.json
    vi/
      common.json
```

**Example `common.json`**:
```json
{
  "welcome": "Welcome to my app",
  "dashboard": {
    "title": "Dashboard",
    "stats": "Statistics"
  }
}
```

## 4. Usage in Components

```typescript
import { useTranslation } from "react-i18next";

export function DashboardHeader() {
  const { t } = useTranslation("common");

  return (
    <Page title={t("dashboard.title")}>
      <p>{t("welcome")}</p>
    </Page>
  );
}
```

## 5. Detecting Shopify Admin Language
Shopify passes the locale in the URL query params (`?locale=fr-FR`) or you can fetch it from the GraphQL Admin API (`Shop.billingAddress.country` or similar, though strictly speaking the Admin UI language is preferred).

Typically, `remix-i18next` detection handles the request headers/query params automatically if configured correctly.

## 6. Translating App Extensions
For Theme App Extensions or Checkout UI Extensions, they have their *own* `locales` folder in their directory. They do NOT share the Remix app's locales.
- **Theme Extension**: `extensions/my-ext/locales/en.default.json`
- **Checkout UI**: `extensions/checkout-ui/locales/en.json`

Overview

This skill guides adding multi-language support to Shopify Apps using i18next, react-i18next, and remix-i18next. It focuses on practical setup, where to store localization files, and how to detect and apply the Shopify Admin language. The guidance covers both server and client integration and notes special handling for app extensions.

How this skill works

Set up i18next on both server and client, using remix-i18next to detect locale from requests and sync Remix with i18next. Store JSON translation files under public/locales with one folder per language and namespace files (for example common.json). Use react-i18next hooks in components to load translations and render localized content. Extensions (theme or checkout) require their own locales folder inside the extension directory.

When to use it

  • You want your Shopify App to support multiple merchant languages and locales.
  • You need server-side locale detection to render correct lang/dir attributes and initial content.
  • You are building a Remix-based app and need smooth language switching and SSR-friendly translations.
  • You must localize admin-facing UI and also separate extension translations for Theme/Checkout extensions.

Best practices

  • Keep translation keys small and namespaced (e.g., common.json -> dashboard.title) for reuse and clarity.
  • Store all translations in public/locales/{{lng}}/{{ns}}.json and load via i18next backend to support SSR.
  • Detect Shopify Admin locale from request query (?locale=fr-FR) or headers; prefer the Admin UI locale when available.
  • Sync server-detected locale with client using useChangeLanguage or equivalent to avoid flicker.
  • Manage extension translations separately in extensions/<ext>/locales to avoid conflicts with the main app.

Example use cases

  • Render localized dashboard titles and strings using useTranslation('common') in React components.
  • Detect merchant locale from incoming Remix requests and set <html lang> and dir attributes accordingly.
  • Provide fallbacks and supported languages via i18next config so untranslatable keys default safely.
  • Include separate locale bundles for a Theme App Extension under extensions/my-ext/locales/en.default.json.

FAQ

Where should translation files live?

Place them under public/locales/{{lng}}/{{ns}}.json for the Remix app; extensions must use their own locales folder inside the extension directory.

How do I detect the Shopify Admin language?

remix-i18next can detect locale from request headers or query params (Shopify sends ?locale=xx-XX); prefer the Admin UI locale or use the Admin API if needed.