shopify-app-i18n
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
ChineseInternationalization (i18n) for Shopify Apps
Shopify应用国际化(i18n)指南
Shopify merchants exist globally. Your app MUST support multiple languages to be featured or widely adopted.
Shopify商家遍布全球,你的应用必须支持多语言才能获得推荐或被广泛采用。
1. Stack
1. 技术栈
- Library: (Standard)
i18next - React:
react-i18next - Remix:
remix-i18next
- 依赖库:(行业标准方案)
i18next - React适配:
react-i18next - Remix适配:
remix-i18next
2. Setup
2. 环境搭建
Installation
安装
bash
npm install i18next react-i18next remix-i18next i18next-fs-backend i18next-http-backendbash
npm install i18next react-i18next remix-i18next i18next-fs-backend i18next-http-backendConfiguration (app/i18n.server.ts
)
app/i18n.server.ts配置(app/i18n.server.ts
)
app/i18n.server.tsCreate 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"),
},
},
});创建服务端实例用于检测语种。
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
)
app/root.tsx根加载器(app/root.tsx
)
app/root.tsxInject 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>
);
}将语种信息注入到文档中。
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
3. 翻译文件
Store JSON files in .
public/localespublic/
locales/
en/
common.json
fr/
common.json
vi/
common.jsonExample :
common.jsonjson
{
"welcome": "Welcome to my app",
"dashboard": {
"title": "Dashboard",
"stats": "Statistics"
}
}将JSON文件存储在目录下。
public/localespublic/
locales/
en/
common.json
fr/
common.json
vi/
common.jsoncommon.jsonjson
{
"welcome": "Welcome to my app",
"dashboard": {
"title": "Dashboard",
"stats": "Statistics"
}
}4. Usage in Components
4. 在组件中使用
typescript
import { useTranslation } from "react-i18next";
export function DashboardHeader() {
const { t } = useTranslation("common");
return (
<Page title={t("dashboard.title")}>
<p>{t("welcome")}</p>
</Page>
);
}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
5. 检测Shopify Admin语种
Shopify passes the locale in the URL query params () or you can fetch it from the GraphQL Admin API ( or similar, though strictly speaking the Admin UI language is preferred).
?locale=fr-FRShop.billingAddress.countryTypically, detection handles the request headers/query params automatically if configured correctly.
remix-i18nextShopify会将语种信息放在URL查询参数中(),你也可以从GraphQL Admin API获取该信息(可查询或类似字段,不过严格来说优先使用Admin UI的语种设置)。
?locale=fr-FRShop.billingAddress.country通常配置正确的情况下,的检测逻辑会自动处理请求头和查询参数中的语种信息。
remix-i18next6. Translating App Extensions
6. 应用扩展的翻译
For Theme App Extensions or Checkout UI Extensions, they have their own folder in their directory. They do NOT share the Remix app's locales.
locales- Theme Extension:
extensions/my-ext/locales/en.default.json - Checkout UI:
extensions/checkout-ui/locales/en.json
对于主题应用扩展或者结账UI扩展,它们的目录下有独立的文件夹,不会共用Remix应用的本地化文件。
locales- 主题扩展:
extensions/my-ext/locales/en.default.json - 结账UI扩展:
extensions/checkout-ui/locales/en.json