pull
इस पेज पर
Download translations from the Better i18n CDN to local JSON files — the offline safety net for mobile apps, and the build-time message files for web frameworks like next-intl.
Why pull? #
Mobile (Expo, React Native, Swift, Flutter) depends on CDN translations at runtime. If the CDN is unreachable — airplane mode, slow network, or App Store Review environment — the app needs a fallback. pull downloads translations so you can bundle them as staticData.
CDN available → Fresh translations (best case)
CDN unavailable → Persistent cache (MMKV/AsyncStorage)
First launch, no cache, no network → staticData from pull ✅Web frameworks that read message files off disk at build time (next-intl, i18next, vue-i18n) use pull as the step that fetches them. No export or transform: the CDN already serves the shape those loaders expect. See Web Workflow.
Usage #
# Auto-detect project from i18n.config.ts or initBetterI18n() call
better-i18n pull
# Explicit project (no config file needed)
better-i18n pull -p acme/my-app
# Custom output directory
better-i18n pull -o ./src/locales
# Download specific locales only
better-i18n pull -l en,tr,deExample output #
✔ Project: acme/my-app
✔ Manifest: 3 languages (en, de, tr)
✔ Downloaded 3 locale(s) to locales/
✓ en 1867 keys 145.8 KB
✓ de 1742 keys 146.3 KB
✓ tr 1741 keys 131.0 KB
Use as offline fallback in your app:
staticData: { en: require('./locales/en.json'), de: require('./locales/de.json'), tr: require('./locales/tr.json') }Options #
| Flag | Description | Default |
|---|---|---|
-p, --project <org/name> | Project identifier | From i18n.config.ts |
-o, --output <path> | Output directory for JSON files | ./locales (or config pull.output) |
-l, --locales <codes> | Comma-separated locale codes | All languages from manifest |
-d, --dir <path> | Directory to scan for config | Current directory |
--verbose | Show per-locale download details | Off |
Configuration #
Add a pull section to your i18n.config.ts to set defaults:
export const i18nConfig = {
projectId: "acme/my-app",
defaultLocale: "en",
pull: { // [!code highlight]
output: "./locales", // [!code highlight]
locales: ["en", "tr", "de"], // [!code highlight]
}, // [!code highlight]
};CLI flags always override config values.
Mobile Workflow #
Expo / React Native #
After pulling, import the JSON files as staticData:
import en from './locales/en.json';
import tr from './locales/tr.json';
await initBetterI18n({
projectId: 'acme/my-app',
i18n,
storage: storageAdapter(new MMKV()),
staticData: { en, tr }, // [!code highlight]
});Web Workflow #
What the CDN serves is already the shape web i18n libraries expect: top-level namespaces, nested keys underneath, no dotted keys. For next-intl that means no export and no transform step — pull straight into the folder it already reads:
npx @better-i18n/cli@latest pull -p acme/my-app -o ./messagesmessages/
en.json
es.json{
"HomePage": { "title": "Hello world!" },
"Nav": { "pricing": "Pricing", "docs": "Docs" }
}That file works unchanged with useTranslations("HomePage"). The same holds for i18next (resources) and vue-i18n (messages) — point -o at whatever directory your loader reads.
CI/CD Integration #
Add pull to the build so bundled translations always match the CDN:
npx @better-i18n/cli@latest pull -p acme/my-app -o ./messagesWithout the CLI #
The CDN is public and needs no token, so a plain fetch works if you would rather not add a dependency:
curl -sf https://cdn.better-i18n.com/acme/my-app/en/translations.json -o messages/en.json
curl -sf https://cdn.better-i18n.com/acme/my-app/es/translations.json -o messages/es.jsonFiles are served with an ETag and a 60-second cache, so conditional requests are cheap. To avoid hardcoding the locale list, read the manifest — it carries every language plus per-locale keyCount and lastModified, which also makes a good cache key:
curl -s https://cdn.better-i18n.com/acme/my-app/manifest.jsonKeep the -f: without it curl writes an error page over your message file and the build carries on.
Failure Behaviour #
Since 0.6.2, pull fails a locale rather than writing an empty file when the manifest expects keys and the CDN returns none:
✗ en CDN returned no keys, but the manifest expects 140The reason prints without --verbose and the process exits non-zero, so a broken fetch breaks the build instead of shipping an app with no strings. Nothing is written for a failed locale — the file already on disk stays intact.
Project Detection #
The pull command finds your project in this order:
-pflag (highest priority)i18n.config.tsin the target directory- Source file scan for
initBetterI18n()orcreateI18n()calls
This means Expo projects work without a config file — the CLI finds projectId: 'acme/my-app' from your initBetterI18n() call automatically.
Better I18N