BeMyWords with Flutter
Integration guide for Flutter apps using the intl package with ARB (.arb) message files.
Shape of the integration:
- At build time, fetch translations from BeMyWords and convert to
lib/l10n/app_<lang>.arb. - Run
flutter gen-l10nto generate typed Dart accessors. - Use
AppLocalizations.of(context)in widgets.
Prerequisites
- A Flutter project with localization enabled (
intl+flutter_localizationsinpubspec.yaml, al10n.yamlconfig, andgenerate: trueunderflutter:). - A BeMyWords workspace, project, namespace, API key.
- Node.js available in your build environment.
1. pubspec.yaml
dependencies:
flutter:
sdk: flutter
flutter_localizations:
sdk: flutter
intl: any
flutter:
generate: true
2. l10n.yaml
arb-dir: lib/l10n
template-arb-file: app_en.arb
output-localization-file: app_localizations.dart
3. Environment variables
.env at project root (gitignored):
BEMYWORDS_BASE_URL=https://www.bemywords.no
BEMYWORDS_PROJECT_ID=<your-project-uuid>
BEMYWORDS_API_TOKEN=<your-api-token>
BEMYWORDS_NAMESPACE=flutter
4. Key naming convention
ARB keys become Dart method names and must be valid Dart identifiers (start with letter or underscore, no dots). Use camelCase in BeMyWords (homeHeroTitle) or underscore home_hero_title — pick one and stick to it.
5. Fetch + convert script
scripts/fetch-translations.mjs:
import { writeFileSync, mkdirSync } from "node:fs";
import { join, dirname } from "node:path";
import { fileURLToPath } from "node:url";
const __dirname = dirname(fileURLToPath(import.meta.url));
const L10N_DIR = join(__dirname, "..", "lib", "l10n");
const BASE_URL = process.env.BEMYWORDS_BASE_URL || "https://www.bemywords.no";
const PROJECT_ID = process.env.BEMYWORDS_PROJECT_ID;
const API_TOKEN = process.env.BEMYWORDS_API_TOKEN;
const NAMESPACE = process.env.BEMYWORDS_NAMESPACE || "flutter";
const LANGUAGES = ["en", "nb"];
async function fetchLanguage(lang) {
const url = `${BASE_URL}/api/${PROJECT_ID}/latest/${lang}/${NAMESPACE}`;
const res = await fetch(url, { headers: { Authorization: `Token token=${API_TOKEN}` } });
if (!res.ok) throw new Error(`${lang}: HTTP ${res.status}`);
return res.json();
}
async function main() {
mkdirSync(L10N_DIR, { recursive: true });
if (!PROJECT_ID || !API_TOKEN) {
console.warn("BEMYWORDS_* env vars not set.");
return;
}
for (const lang of LANGUAGES) {
const data = await fetchLanguage(lang);
// ARB format is just JSON with a leading "@@locale" meta key.
const arb = { "@@locale": lang, ...data };
writeFileSync(
join(L10N_DIR, `app_${lang}.arb`),
JSON.stringify(arb, null, 2) + "\n"
);
console.log(` [${lang}] ${Object.keys(data).length} keys → app_${lang}.arb`);
}
}
main().catch((e) => { console.error(e); process.exit(1); });
Run the script, then flutter gen-l10n produces lib/l10n/app_localizations.dart.
6. Use in Dart
import 'package:flutter_gen/gen_l10n/app_localizations.dart';
final localizations = AppLocalizations.of(context)!;
Text(localizations.homeHeroTitle);
7. Hook into build
In your CI or Makefile:
node scripts/fetch-translations.mjs
flutter gen-l10n
flutter build <target>
No native flutter pre-build hook for arbitrary scripts — you'll drive this from your build runner.
ICU message format
ARB supports ICU directly for plurals, gender, and interpolation:
{
"apples": "{count, plural, =0 {no apples} one {1 apple} other {{count} apples}}",
"greeting": "Hello, {name}!"
}
Store ICU strings verbatim in BeMyWords and enable ICU placeholder validation in project settings.
For more complex ARB features (placeholder types, descriptions via @ meta keys), those would need to be handled by the fetch script — either store the meta as BeMyWords key suffixes (apples.@) or as separate project settings data not covered by this basic flow.
API reference
Status
This guide is a best-effort draft — not yet verified in a production Flutter deployment. The ARB @ metadata (placeholder types, descriptions) handling is sketched but needs real-world validation. Report corrections.