Frequently Asked Questions
Find answers to common questions
Getting Started
Do I need to rebuild my app after publishing translations?
No. Better i18n delivers translations via a global CDN at runtime. When you publish, translations are available to your app within ~60 seconds without any rebuild or redeploy. Your SDK fetches fresh translations automatically.
What happens if the CDN is down?
The CDN is designed to always return HTTP 200 — even during errors, your app receives {} or { "fallback": true } instead of a failure.
The SDK has a 5-layer fallback chain:
- In-memory cache (TtlCache, 60s TTL)
- CDN fetch (with timeout + retry)
- Persistent storage (if configured — e.g., AsyncStorage on mobile)
- Static fallback data (bundled translations you provide)
- Error (only if all layers fail)
In practice, users almost never see a failure because cached or static data is always available.
Is there a free plan?
Yes! Better i18n offers a free tier that includes:
- 1 project
- Unlimited translation keys
- AI translation credits
- CDN delivery
- CLI access
Paid plans add more projects, team members, advanced features, and higher usage limits. Check the pricing page for current details.
Does Better i18n support RTL languages?
Better i18n is a string delivery platform — it stores and delivers translation strings regardless of text direction. RTL languages like Arabic, Hebrew, and Persian are fully supported as translation targets.
However, RTL layout and CSS (e.g., direction: rtl, mirrored UI) is your app's responsibility. The SDK delivers the correct translated strings; your CSS framework handles the visual direction.
Managing Translations
How long does AI translation take?
AI translation speed depends on volume:
| Keys | Approximate time |
|---|---|
| 1–50 | A few seconds |
| 50–500 | Under 1 minute |
| 500–1000 | 1–3 minutes |
| 1000+ | 3–5 minutes |
Translation runs in parallel batches, so it scales well. You can continue working in the dashboard while translation is in progress.
Can I use human translators instead of AI?
Absolutely. You can:
- Invite translators with the Translator role — they get access to a focused translation interface
- Use the review queue — translators can review, edit, and approve translations
- Combine AI + human — use AI for the first pass, then have human translators review and refine
The Translator role only has access to translation editing, not project settings or key management.
What is a namespace?
A namespace is a logical grouping of translation keys. For example:
common— shared strings like "Save", "Cancel", "Loading"auth— login/signup related stringsdashboard— dashboard-specific strings
Namespaces enable lazy-loading — your app only fetches the translations it needs for the current page. They also help organize keys and reduce conflicts in large projects.
In code, you reference namespaces like: t('common:save_button') or useTranslations('common').
Can translations be rolled back?
Yes. Every publish creates a snapshot of your translations. If something goes wrong:
- Go to your project's Publish History
- Find the previous good publish
- Click Rollback
This restores the CDN to the selected snapshot. Your app will serve the rolled-back translations within ~60 seconds.
Developer Integration
Should I use a singleton or multiple SDK instances?
Always use a singleton — create one SDK instance at module scope and reuse it everywhere.
The SDK's TtlCache is a module-level global shared across all createI18nCore() instances in the same process. Creating multiple instances is wasteful but not harmful — they'll share the same cache.
// ✅ Good — module-level singleton
export const i18n = createI18n({ project: 'acme/dashboard', ... });
// ❌ Avoid — creating inside a component/handler
function handler() {
const i18n = createI18n({ ... }); // New instance per request
}
What is the org/project format?
The project identifier uses org/project format, like acme/dashboard. This maps directly to the CDN URL:
https://cdn.better-i18n.com/acme/dashboard/manifest.json
https://cdn.better-i18n.com/acme/dashboard/en/translations.json
- org — your organization slug (set during signup)
- project — the project slug (set when creating a project)
This format is used everywhere: SDK config, CLI config, API calls, and CDN URLs. It's case-sensitive.
Does the SDK work on edge runtimes?
Yes. The SDK uses only the standard Fetch API — no Node.js built-ins (fs, path, crypto, etc.) are required. This means it works on:
- Cloudflare Workers
- Vercel Edge Functions
- Deno Deploy
- Bun
- Any Web Standards-compatible runtime
The only requirement is a global fetch function. You can also pass a custom fetch via the fetch config option if needed.
What's the difference between the CLI and the SDK?
They serve different purposes:
CLI (@better-i18n/cli) |
SDK (@better-i18n/next, etc.) |
|
|---|---|---|
| When | Development time | Runtime |
| Purpose | Scan code, sync keys, check status | Fetch and display translations |
| Runs on | Your machine / CI | Your app (browser, server, mobile) |
| Auth | Secret key (sk_...) |
Public key or none |
| Install | Global or devDependency | Dependency |
The CLI manages your translation keys. The SDK delivers them to users.
Troubleshooting & Common Issues
How do I check if the CDN is healthy?
Fetch your translations directly to verify CDN health:
# Check manifest
curl -w '\nHTTP %{http_code} | Time: %{time_total}s\n' \
https://cdn.better-i18n.com/your-org/your-project/manifest.json
# Check translations
curl -w '\nHTTP %{http_code} | Time: %{time_total}s\n' \
https://cdn.better-i18n.com/your-org/your-project/en/translations.json
The CDN always returns HTTP 200. Check the response body:
- Valid JSON with your translations → CDN is healthy
{}or{ "fallback": true }→ translations not published yet- Response time > 1s → possible network issue (typical is <100ms)
Why does my app show the fallback locale instead of the user's language?
Four common causes:
- Locale not published — check if translations exist for that language in the dashboard and are published
- Locale normalization mismatch — the CDN uses lowercase BCP 47 (
pt-br), but your app might sendpt-BR. UsenormalizeLocale()from@better-i18n/core - Middleware not detecting locale — check your middleware configuration and ensure
Accept-Languageheader matching is set up - Missing locale in manifest — the CDN manifest lists available locales. If your locale isn't there, the SDK falls back to the default locale
Verify by checking the manifest:
curl https://cdn.better-i18n.com/your-org/your-project/manifest.json
The response should list all published locales.
AI & Automation
Is AI translation accurate enough for production?
For most content, yes. AI translation quality depends on several factors:
- Context — keys with context annotations translate significantly better
- Glossary — enforcing consistent terminology eliminates the most common errors
- Content type — UI strings, labels, and short descriptions translate very well. Marketing copy and creative content may need human review
The recommended approach: use AI for the first pass, then have native speakers review critical user-facing content. The review queue makes this workflow easy.
Which AI models are used for translation?
Better i18n uses a combination of large language models optimized for translation quality. The platform automatically selects the best model based on the content type and language pair.
You don't need to choose or configure models — the platform handles this for you. Model selection is continuously improved based on translation quality metrics.
Which AI coding agents work with the MCP server?
Any MCP-compatible agent works with the Better i18n MCP server, including:
- Claude Code (CLI) — full support
- Claude Desktop — full support
- Cursor — via MCP settings
- Windsurf — via MCP settings
- Continue — via MCP configuration
- Custom agents — anything built with the MCP SDK
The MCP server exposes tools for managing translation keys, translations, publishing, and content management. The agent calls these tools via natural language commands.
Content Management
What's the difference between translation keys and the Content SDK?
They serve different use cases:
| Translation keys | Content SDK | |
|---|---|---|
| For | Short UI strings | Structured rich content |
| Examples | Button labels, error messages, page titles | Blog posts, changelogs, help articles |
| Format | Key-value pairs | Content models with typed fields |
| Editing | Dashboard translation editor | Dashboard content editor (Markdown) |
| Delivery | CDN via SDK | Content API via @better-i18n/sdk |
Use translation keys for UI text. Use the Content SDK for structured content that has a title, body, metadata, and relations.
Are there rate limits on the Content SDK?
Yes, rate limits depend on your plan tier. The API returns standard rate-limit headers:
X-RateLimit-Limit— requests allowed per windowX-RateLimit-Remaining— requests remainingX-RateLimit-Reset— when the window resets (Unix timestamp)
If you exceed the limit, the API returns HTTP 429. The SDK does not auto-retry on rate limits — handle this in your application if you're making many requests.
For most applications, rate limits are generous enough that you won't hit them. Server-side caching (as used in the help center app) further reduces API calls.
Team & Account
How many projects can I have?
The number of projects depends on your plan tier:
- Free — 1 project
- Pro — multiple projects (see pricing page for current limit)
- Enterprise — unlimited projects
Each project is fully independent with its own keys, languages, team members, and publish history. Check the pricing page for current details.
Can translators see all projects in my organization?
No. Team members are assigned per project. A translator invited to "Project A" cannot see "Project B" unless explicitly added to it.
This lets you:
- Invite external translators to specific projects without exposing others
- Assign different translators to different projects
- Control access granularly across your organization