Skip to content
better-i18n.com

Better i18n reads four file formats on the way in, and always writes JSON on the way out. Those are two different lists, and knowing which one you are asking about saves a lot of confusion.

Formats you can upload #

Uploading a translation file directly — in the dashboard or through the API — accepts:

FormatExtensions
JSON.json
YAML.yaml, .yml
XML / XLIFF.xml, .xliff
Java properties.properties

The format is detected from the file extension, so name the file normally and upload it. Once the keys are in, the original format stops mattering — everything after that point works the same regardless of what you uploaded.

GitHub sync reads JSON only #

If you connect a repository instead of uploading, the importer looks for .json files. YAML, XLIFF and .properties are upload-only today.

So a repo with locales/en.yaml will not import over GitHub sync. Two options: upload the YAML once to get your keys in, or convert the files in your repo to JSON.

What Better i18n writes back #

Output is always JSON, in both destinations:

DestinationShape
CDN{org}/{project}/{lang}/{namespace}.json
GitHub pull requests{yourTranslationPath}/{lang}.json, or {lang}/{namespace}.json when you use namespaced folders

This is deliberate — every SDK we ship (Next.js, Remix, Vite, Expo, Flutter, plain @better-i18n/core) consumes JSON, and the CDN serves it with a manifest so a string change reaches production without a rebuild. If you need a different shape in your repo, transform it in your build step after pulling.

Two axes that shape the output #

Independently of format, two project settings decide the file layout:

SettingOptionsMeaning
File structuresingle file / namespaced foldersOne file per language, or one folder per language with a file per namespace
Key formatflat / nested"auth.login.title": "Sign in" versus nested objects

Set these to match what your app already expects, so the files Better i18n writes drop in without a migration.

Locale codes in filenames #

Filenames carry the locale, and regional codes are fine: en.json, en-US.json, pt-BR.json, zh-Hans.json all resolve correctly. You do not have to flatten en-US.json down to en.json before importing.

What is not a translation file #

Product documents — PDF, DOCX, slide decks, spreadsheets — are not translation files and are not supported as input. Better i18n translates the strings your application ships, not documents. If you uploaded a PDF and nothing happened, that is why.