Skip to content

Integrations

Cross-Platform Integration

langctl writes Flutter ARB files and the JSON that React Native apps load with i18next.

Flutter Integration

Pull ARB Files

terminal
# once per repo: writes langctl.json
langctl init --project my-app --format arb

# then, whenever translations change
langctl pull
# + lib/l10n/app_en.arb  created
# + lib/l10n/app_es.arb  created
# + lib/l10n/app_pt_BR.arb  created

The default path is lib/l10n/app_{lang_}.arb, so pt-BR becomes app_pt_BR.arb. Message ids become Dart identifiers (home.welcome → homeWelcome), {{name}} placeholders become {name} and are declared in the @homeWelcome metadata, and key descriptions are written as description.

Configure Flutter

yaml
# pubspec.yaml
flutter:
generate: true

dependencies:
flutter_localizations:
  sdk: flutter
intl: any

# l10n.yaml
arb-dir: lib/l10n
template-arb-file: app_en.arb
output-localization-file: app_localizations.dart
nullable-getter: false

Use Translations

dart
import 'package:flutter_gen/gen_l10n/app_localizations.dart';

class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
  return MaterialApp(
    localizationsDelegates: AppLocalizations.localizationsDelegates,
    supportedLocales: AppLocalizations.supportedLocales,
    home: HomePage(),
  );
}
}

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

  return Scaffold(
    appBar: AppBar(title: Text(l10n.homeWelcome)),
    body: Text(l10n.homeSubtitle),
  );
}
}

React Native Integration

React Native apps use the regular json format — there is no separate React Native format:

terminal
npm install i18next react-i18next

langctl init --project my-app --format json --output "src/locales/{lang}.json"
langctl pull
typescript
// i18n.js
import i18n from 'i18next';
import { initReactI18next } from 'react-i18next';
import en from './locales/en.json';
import es from './locales/es.json';

i18n
.use(initReactI18next)
.init({
  resources: {
    en: { translation: en },
    es: { translation: es }
  },
  lng: 'en',
  fallbackLng: 'en',
  compatibilityJSON: 'v3'
});

export default i18n;

// App.tsx
import { useTranslation } from 'react-i18next';
import './i18n';

export default function App() {
const { t, i18n } = useTranslation();

return (
  <View>
    <Text>{t('home.welcome', { username: 'John' })}</Text>
    <Button title="Español" onPress={() => i18n.changeLanguage('es')} />
  </View>
);
}

CI/CD

yaml
name: Flutter Build

on: [push]

jobs:
build:
  runs-on: ubuntu-latest
  steps:
    - uses: actions/checkout@v4
    - uses: actions/setup-node@v4
      with: { node-version: 22 }
    - uses: subosito/flutter-action@v2

    - name: Pull Translations
      env:
        LANGCTL_API_KEY: ${{ secrets.LANGCTL_API_KEY }}
      run: npx langctl@0 pull    # uses langctl.json

    - name: Build
      run: flutter build apk

Tip

Flutter automatically generates type-safe localization code from ARB files. Run `flutter gen-l10n` to regenerate after updating translations.

Next Steps