Get Started

Export Formats

LangCTL exports translations to 6 different formats, each optimized for specific platforms and frameworks. This reference guide covers all supported formats in detail.

Supported Formats

FormatPlatformExtensionParameter Syntax
i18next JSONWeb (React, Vue, Angular).json{{param}}
Android XMLAndroid Native.xml%1$s, %2$d
iOS StringsiOS Native.strings%@, %1$@
Flutter ARBFlutter.arb{param}
Flat JSONGeneric.json{{param}}
Nested JSONGeneric.json{{param}}
πŸ’‘
Tip

LangCTL automatically converts parameter syntax between formats. Write {{username}} once, and it becomes %1$s for Android, %@ for iOS, and {username} for Flutter.

i18next JSON Format

The most popular format for web applications using React, Vue, Angular, or any framework with i18next.

Format Specification

  • File extension: .json
  • Encoding: UTF-8
  • Structure: Flat or nested object
  • Parameters: {{param}}, {{count}}, {{name}}
  • Plurals: _one, _other, _zero suffixes

Example Output

locales/en.json
{
"home": {
  "welcome": "Welcome, {{param}}!",
  "title": "Dashboard",
  "subtitle": "Get started with your projects"
},
"auth": {
  "login": "Log In",
  "signup": "Sign Up",
  "logout": "Log Out"
},
"notifications": {
  "message_one": "You have {{param}} new message",
  "message_other": "You have {{param}} new messages"
}
}
import { useTranslation } from 'react-i18next';

function WelcomeMessage() {
const { t } = useTranslation();
return (
  <div>
    <h1>{t('home.welcome', { username: 'John' })}</h1>
    <p>{t('home.subtitle')}</p>
  </div>
);
}
<template>
<div>
  <h1>{{ $t('home.welcome', { username: 'John' }) }}</h1>
  <p>{{param}}</p>
</div>
</template>
<div>
<h1>{{ 'home.welcome' | translate: {username: 'John'} }}</h1>
<p>{{param}}</p>
</div>
langctl export my-project -l en -f i18n-json -o ./locales/en.json
langctl export my-project -l es -f i18n-json -o ./locales/es.json

[IMAGE: i18next-export.png - i18next format in dashboard export]

Native Android string resources format for Kotlin and Java applications.

  • File extension: .xml
  • Encoding: UTF-8
  • Structure: XML with <resources> root
  • Parameters: Positional %1$s (string), %1$d (integer), %1$f (float)
  • Plurals: <plurals> element with quantity attribute
app/src/main/res/values/strings.xml
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="app_name">LangCTL Demo</string>
<string name="welcome_message">Welcome, %1$s!</string>
<string name="item_count">You have %1$d items</string>
</resources>

LangCTL automatically converts dot notation to underscore:

  • home.welcome -> home_welcome
  • auth.login.title -> auth_login_title
// Simple string
val title = getString(R.string.home_title)

// With parameter
val welcome = getString(R.string.home_welcome, username)

// With multiple parameters
val info = getString(R.string.profile_info, name, followerCount)

// Plurals
val message = resources.getQuantityString(
  R.plurals.notifications_message,
  count,
  count
)
// Simple string
String title = getString(R.string.home_title);

// With parameter
String welcome = getString(R.string.home_welcome, username);

// Plurals
String message = getResources().getQuantityString(
  R.plurals.notifications_message,
  count,
  count
);
app/src/main/res/
β”œβ”€β”€ values/             # Default (English)
β”‚   └── strings.xml
β”œβ”€β”€ values-es/          # Spanish
β”‚   └── strings.xml
β”œβ”€β”€ values-fr/          # French
β”‚   └── strings.xml
└── values-de/          # German
  └── strings.xml
langctl export my-project -l en -f android-xml -o ./app/src/main/res/values/strings.xml
langctl export my-project -l es -f android-xml -o ./app/src/main/res/values-es/strings.xml

Android XML requires escaping:

CharacterEscaped FormExample
' (apostrophe)\'It\'s working
" (quote)\"He said \"Hi\"
&lt;&lt;x &lt; 5
&gt;&gt;x &gt; 3
&&amp;Tom &amp; Jerry
\n\nMulti-line text
ℹ️
Note

LangCTL automatically handles XML escaping during export. You don’t need to escape characters in the dashboard or CLI.

[IMAGE: android-export.png - Android XML format export]

Native iOS and macOS localization format for Swift and Objective-C.

  • File extension: .strings
  • Encoding: UTF-8 or UTF-16
  • Structure: Key-value pairs with comments
  • Parameters: Positional %@ (object), %d (integer), %f (float)
  • Plurals: Handled via .stringsdict file
en.lproj/Localizable.strings
/* Home Screen */
"home.welcome" = "Welcome, %@!";
"home.title" = "Dashboard";
"home.subtitle" = "Get started with your projects";

/* Auth */
"auth.login" = "Log In";
"auth.signup" = "Sign Up";
"auth.logout" = "Log Out";

/* Multiple Parameters */
"profile.info" = "%@ has %d followers";

/* With numbered positions */
"order.details" = "Item: %1$@, Price: %2$@, Quantity: %3$d";
// Simple string
let title = NSLocalizedString("home.title", comment: "")

// With parameter
let welcome = String(format: NSLocalizedString("home.welcome", comment: ""), username)

// With multiple parameters
let info = String(format: NSLocalizedString("profile.info", comment: ""), name, followerCount)

// Using String interpolation (modern)
let welcome = String(localized: "home.welcome")
// Simple string
NSString *title = NSLocalizedString(@"home.title", @"");

// With parameter
NSString *welcome = [NSString stringWithFormat:
  NSLocalizedString(@"home.welcome", @""), username];
MyApp/
β”œβ”€β”€ en.lproj/
β”‚   └── Localizable.strings
β”œβ”€β”€ es.lproj/
β”‚   └── Localizable.strings
β”œβ”€β”€ fr.lproj/
β”‚   └── Localizable.strings
└── de.lproj/
  └── Localizable.strings
langctl export my-project -l en -f ios-strings -o ./en.lproj/Localizable.strings
langctl export my-project -l es -f ios-strings -o ./es.lproj/Localizable.strings

For complex plural rules, iOS uses .stringsdict:

en.lproj/Localizable.stringsdict
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>item.count</key>
<dict>
  <key>NSStringLocalizedFormatKey</key>
  <string>%#@items@</string>
  <key>items</key>
  <dict>
    <key>NSStringFormatSpecTypeKey</key>
    <string>NSStringPluralRuleType</string>
    <key>NSStringFormatValueTypeKey</key>
    <string>d</string>
    <key>zero</key>
    <string>No items</string>
    <key>one</key>
    <string>One item</string>
    <key>other</key>
    <string>%d items</string>
  </dict>
</dict>
</dict>
</plist>

[IMAGE: ios-export.png - iOS Strings format export]

Application Resource Bundle format for Flutter and Dart applications.

  • File extension: .arb
  • Encoding: UTF-8
  • Structure: JSON with metadata
  • Parameters: &#123;param&#125;, &#123;count&#125;, &#123;name&#125;
  • Plurals: ICU message format
lib/l10n/app_en.arb
{
"@@locale": "en",
"@@last_modified": "2024-01-15T10:30:00.000Z",

"homeWelcome": "Welcome, {username}!",
"@homeWelcome": {
  "description": "Welcome message on home screen",
  "placeholders": {
    "username": {
      "type": "String"
    }
  }
},

"homeTitle": "Dashboard",
"@homeTitle": {
  "description": "Home screen title"
},

"authLogin": "Log In",
"authSignup": "Sign Up",

"notificationsMessage": "{count, plural, =0{No messages} =1{You have {count} new message} other{You have {count} new messages}}",
"@notificationsMessage": {
  "description": "New message notification",
  "placeholders": {
    "count": {
      "type": "int"
    }
  }
}
}

LangCTL converts dot notation to camelCase:

  • home.welcome -> homeWelcome
  • auth.login.title -> authLoginTitle
import 'package:flutter/material.dart';
import 'package:flutter_gen/gen_l10n/app_localizations.dart';

class HomeScreen extends StatelessWidget {
@override
Widget build(BuildContext context) {
  final l10n = AppLocalizations.of(context)!;

  return Column(
    children: [
      Text(l10n.homeWelcome('John')),
      Text(l10n.homeTitle),
      Text(l10n.notificationsMessage(5)),
    ],
  );
}
}

Add to pubspec.yaml:

flutter:
generate: true

dependencies:
flutter_localizations:
  sdk: flutter
intl: ^0.18.0

Create l10n.yaml:

arb-dir: lib/l10n
template-arb-file: app_en.arb
output-localization-file: app_localizations.dart
nullable-getter: false
langctl export my-project -l en -f flutter-arb -o ./lib/l10n/app_en.arb
langctl export my-project -l es -f flutter-arb -o ./lib/l10n/app_es.arb

[IMAGE: flutter-export.png - Flutter ARB format export]

Simple key-value JSON format for generic use cases.

  • File extension: .json
  • Encoding: UTF-8
  • Structure: Flat object (no nesting)
  • Parameters: &#123;&#123;param&#125;&#125; (configurable)
locales/en.json
{
"home.welcome": "Welcome, {{param}}!",
"home.title": "Dashboard",
"home.subtitle": "Get started with your projects",
"auth.login": "Log In",
"auth.signup": "Sign Up",
"auth.logout": "Log Out",
"notifications.message": "You have {{param}} new messages"
}
import translations from './locales/en.json';

function translate(key, params = {}) {
let text = translations[key] || key;
Object.entries(params).forEach(([key, value]) => {
  text = text.replace(new RegExp(`{{\${key}}}`, 'g'), value);
});
return text;
}

// Usage
console.log(translate('home.welcome', { username: 'John' }));
// Output: "Welcome, John!"
langctl export my-project -l en -f flat-json -o ./locales/en.json

Hierarchical JSON format that preserves module structure.

  • File extension: .json
  • Encoding: UTF-8
  • Structure: Nested objects matching module structure
  • Parameters: &#123;&#123;param&#125;&#125; (configurable)
locales/en.json
{
"home": {
  "welcome": "Welcome, {{param}}!",
  "title": "Dashboard",
  "subtitle": "Get started with your projects"
},
"auth": {
  "login": "Log In",
  "signup": "Sign Up",
  "logout": "Log Out"
},
"notifications": {
  "message": "You have {{param}} new messages"
}
}
import translations from './locales/en.json';

function translate(key, params = {}) {
const keys = key.split('.');
let text = keys.reduce((obj, k) => obj?.[k], translations) || key;

Object.entries(params).forEach(([key, value]) => {
  text = text.replace(new RegExp(`{{\${key}}}`, 'g'), value);
});
return text;
}

// Usage
console.log(translate('home.welcome', { username: 'John' }));
// Output: "Welcome, John!"
langctl export my-project -l en -f nested-json -o ./locales/en.json

LangCTL automatically converts parameter syntax between formats:

"Welcome, {{param}}! You have {{param}} items."
FormatOutput
i18next JSONWelcome, &#123;&#123;username&#125;&#125;! You have &#123;&#123;count&#125;&#125; items.
Android XMLWelcome, %1$s! You have %2$d items.
iOS StringsWelcome, %@! You have %d items.
Flutter ARBWelcome, &#123;username&#125;! You have &#123;count&#125; items.
Flat JSONWelcome, &#123;&#123;username&#125;&#125;! You have &#123;&#123;count&#125;&#125; items.
πŸ’‘
Tip

Parameters are converted in order of appearance. The first parameter becomes %1$s/%@, the second becomes %2$s/%@, and so on.

Export only specific modules:

langctl export my-project -l en -f i18n-json --module auth -o ./locales/auth-en.json

langctl export my-project -l en -f i18n-json --modules auth,home -o ./locales/en.json
FormatBest ForProsCons
i18next JSONWeb appsMost popular, great toolingWeb-only
Android XMLNative AndroidNative integrationAndroid-only
iOS StringsNative iOSNative integrationiOS-only
Flutter ARBFlutter appsFull Flutter supportFlutter-only
Flat JSONCustom solutionsSimple, universalNo structure
Nested JSONCustom solutionsOrganized structureCustom parsing
- React/Vue/Angular -> i18next JSON
- Android Native -> Android XML
- iOS Native -> iOS Strings
- Flutter -> Flutter ARB

- Node.js backends
- Custom solutions
- Static site generators
"Welcome, {{param}}!"
"Hello, {{param}}!"
"{{param}}'s Profile"

"Welcome, {{param}}!"
"Hello, {{param}}!"
"{{param}}'s Profile"

Always test exported files in your application before deploying:

langctl export my-project -l en -f i18n-json -o /tmp/en.json

cat /tmp/en.json | jq

langctl export my-project -l en -f i18n-json -o ./public/locales/en.json

Commit exported files to version control:

public/locales/*.json
app/src/main/res/values*/strings.xml
ios/*/Localizable.strings

*.tmp
.langctl-cache/

Check parameter syntax matches your format:

t('message', { username: 'John' })

getString(R.string.message, username)

String(format: NSLocalizedString("message", comment: ""), username)

Ensure UTF-8 encoding:

file -I locales/en.json
iconv -f ISO-8859-1 -t UTF-8 input.json > output.json

Plurals Not Working

Different formats handle plurals differently:

  • i18next: Use _one, _other suffix or ICU format
  • Android: Use <plurals> element
  • iOS: Use .stringsdict file
  • Flutter: Use ICU message format in ARB

Next Steps