Skip to content

Dashboard guide

Import Translations

Bring existing translations into langctl by uploading the files you already have. You see every change before anything is imported.

Supported Import Formats

The dashboard imports:

  • JSON (flat or nested) and i18n JSON
  • Android XML (strings.xml)
  • iOS Strings (Localizable.strings)
  • Flutter ARB
  • CSV and Excel (.xlsx, .xls)

The format is detected automatically; you can also pick it yourself.

Importing a File

Open Import & Export in the sidebar (or Import on the Translations page). The import has three steps: Upload, Review, Done.

Step 1: Upload

  1. Drag and drop a file, or click Browse Files
  2. Check the detected Format
  3. Choose the Target Language - each import is for one language. If the file looks like a language the project doesn’t have yet, you can add it right there with Add language
  4. Optionally set a Module - it’s used for keys that don’t specify a module in the file

Step 2: Review

Every key in the file is listed with its Current value, the Incoming value, and the change:

  • New - The key, or this language’s translation, doesn’t exist yet
  • Update - The key already has a different translation
  • Unchanged - The translation is the same
  • Kept - The key already has a translation and overwrite is off, so it stays as it is

Use Overwrite existing translations to decide what happens to existing values:

  • Off (default): existing translations are kept; only new keys and missing translations are added
  • On: existing translations are replaced with the values from the file

Filter the list by change, and select or deselect individual keys - only selected keys are imported. Then click Import Selected Keys.

Note

New keys are imported as drafts. Review them and publish when they’re ready - drafts aren’t included in exports or langctl pull.

Step 3: Done

The summary shows how many keys were imported, updated and skipped, and the language’s coverage afterwards. From there you can import another file or open the translations to review them.

Warning

Keep the page open until the import finishes. Large files are uploaded in batches.

Format Details

JSON

Flat files use keys exactly as written. Nested files are flattened into dotted keys:

en.json (nested)
{
"home": {
  "welcome": "Welcome!"
},
"auth": {
  "login": { "title": "Sign In" }
}
}

becomes home.welcome and auth.login.title.

Android XML

Each <string name="…"> becomes a key with that name. Placeholders %1$s, %2$s become {{param1}}, {{param2}}, and %s becomes {{param}}. XML entities are unescaped.

iOS Strings

Each "key" = "value"; line becomes a key. %1$@, %2$@ become {{param1}}, {{param2}}, and %@ becomes {{param}}. Escaped characters (\", \n) are unescaped.

Flutter ARB

Message ids become keys, and descriptions from the @key metadata are imported. {name} placeholders become {{name}}.

CSV and Excel

One key per row, with the columns key, value, description and module (description and module are optional):

translations.csv
key,value,description,module
home.welcome,"Welcome to our app","Welcome message on home screen",home
auth.login,"Login","Login button text",auth

The value column is imported into the language you choose. Use Download Sample CSV or Download Sample Excel on the import page for a ready-made template.

Modules

When a key has no module in the file and you didn’t set one, keys with a dot get the first part of their name as module (auth.login.title → auth).

Renaming Generic Parameters

Android and iOS files don’t store placeholder names, so they’re imported as generic names:

output
Before: "Welcome, {{param1}}!"
After:  "Welcome, {{username}}!"

Rename them in the key editor (in every language) so they match the names your app passes. If you also have JSON or ARB versions of the same strings, import those instead - they keep the original names.

Importing from the CLI

The CLI imports JSON, Android XML, iOS Strings and ARB files (not CSV or Excel), and can upload every language at once:

terminal
langctl import my-app ./old-translations/es.json --language es --dry-run
langctl import my-app ./old-translations/es.json --language es

# with langctl.json set up, upload every language file at once
langctl push -l all

See Pull, Push & Import for details.

Troubleshooting

“Failed to parse file”:

  • Check for syntax errors (missing commas, brackets, unescaped quotes)
  • Make sure the file is UTF-8 encoded
  • Pick the format manually instead of auto-detect

“No translation keys were found in this file”:

  • Check that the file contains string values in the selected format
  • For CSV/Excel, make sure the first row has the key and value headers

Language not in the project:

  • Add it with Add language on the upload step, or from the Languages page

Existing translations didn’t change:

  • Turn on Overwrite existing translations - without it, existing values are kept

Next Steps