Skip to content

Localisation, and the sample project

Three layers, all in filmopen_l10n (packages/filmopen_l10n/lib/l10n/ for the ARB files and the generated code, lib/src/ for the two hand-written files):

  1. Messages — app_en.arb (template) and app_es.arb, compiled by gen-l10n (l10n.yaml: nullable-getter: false) into app_localizations.dart and one file per language. Widgets use context.l10n; providers use localizationsProvider. Counts use ICU plurals (countScene(n) → “1 scene” / “3 scenes”).
  2. The format’s vocabulary — the JSON keys are normative and never translated. fieldLabel(l10n, key) in field_labels.dart maps 310 keys from Appendix A to messages (fieldName, fieldForkedFrom, …) and returns null for unknown keys, in which case the raw key is shown. Entity types (typeScene, typeScenePlural, countScene) and deliverables (deliverableClip) have their own messages, reached through the Labels extension in labels.dart (typeLabel, typePlural, typeCount, deliverableLabel).
  3. Diagnostics — warningText(ProjectWarning) and problemText(ResolutionProblem) in labels.dart turn model codes into sentences.
  4. Jobs — JobViewText.jobStateText(code, phase:) and jobActionText(code) accept primitive codes and phase names, keeping localisation independent of jobs. Known failures and actions have EN/ES sentences. Unknown state codes use the phase’s generic sentence; an unknown action says Continue. Raw codes or server words never become fallback job text.

The generated files are committed so editors and the analyzer see them; flutter gen-l10n inside the package regenerates them. English is the source of the keys and the one language maintained. Regeneration preserves existing values in the frozen language files, including the Spanish keysStoreUnavailable sentence, across both generated message getters and key lookup. A message is added to app_en.arb alone, appended at the end; every other language’s file is frozen and, while FilmOpen is in development, not shown: the app shows English alone (shownLocales, filmopen_state), Settings’ language control reads English, held, its tooltip saying that other languages return after development, and a preference or a system language naming Spanish resolves to English (resolveSupportedLocale), the preference kept as it was; a message a frozen file lacks reads in English, since gen-l10n writes the template’s message into a language’s class where that language’s file has none (a plug-in’s app:<key> lookup, §6.11, falls back to English the same way). A message whose English changes its meaning takes a new key, so that a frozen translation never says what English no longer says; a key nothing reads any more leaves every file, so that no language keeps a message nobody can reach. filmopen_l10n/test/arb_keys_test.dart holds every English key to a message name with its metadata, and fails where another language has a key, or metadata, that English lacks. To add a language: copy app_en.arb, translate, add the language name to settings_page.dart and the language to shownLocales once languages other than English are shown again — one file per language, so a translator and a colleague adding a language never touch the same file. To add a field label: add field<Key> to app_en.arb and one case to field_labels.dart. British spelling is used in English messages (colour, licence).

Every package’s words are the app’s, filmopen_support (§6.8) included: its sentences — the support dialog’s introduction where a recording can be sent, the rail microphone’s tooltips and the reasons it went off, the caption’s Listening…, Describe the problem and what becomes of a recording — are messages of app_en.arb read through AppLocalizations, and the app’s localizationsDelegates are AppLocalizations.localizationsDelegates alone. Spanish lacks them, so they read in English there, as every message the frozen file lacks does.

assets/sample/ holds The Cartographer from Appendix B of the Project Specification, extended so the tree has depth, laid out the way a real project is meant to be (§4.4, §14.5):

  • the-cartographer/ — the project: root filmopen-project.json (tag cartographer), 45 JSON documents directly in data/: two project versions (John’s, and Suda’s Thai remake forked from it) with an official pointer; the character main-hero at two epochs with three versions at 30yo and an official pointer to Maria’s fork; a second character; two outfits; two locations, one with an area; a prop, a style with an official pointer, a misc entry, a document; four folders — Season 1 (fo_s1, kind season) holding Episode 1 - High Water and Episode 2 - Low Tide (fo_e1, fo_e2, episode), the first holding Arrival (fo_arrival, sequence), their short names made by hand and none of them saying anything about what its scenes hold; two scenes, one of which has two versions and an official pointer; shots and cues including a J-cut and a music cue that spans the second episode and names it in folder; two commentary files; nine render records — eight renders, one record each, and one record of a sheet made by hand; and the root’s notes-from-the-read-through.txt, its one Other files entry.
  • the-cartographer/media/ — the flat media folder: the nine placeholder takes (four small PNGs, and .mp4 and .wav files named as takes, each the smallest honest file of its kind — an MP4 with a header and no tracks, a 44-byte silent WAVE with no frames — so a player says it cannot read them rather than the file failing to load; flutter run -d web-server answers 500 for a zero-length asset, which the older empty placeholders were), and voice-calm.wav, the one hand-supplied reference a character names (§9.0 of the Project Specification).
  • library/ — the seed of the shared library: a model, a platform and a plugin manifest.

Being under assets/ and in Git is a development convenience (the build copies them to build/flutter_assets/assets/sample/…); a real project’s data/ and media/ can be synced separately, and the real library is the folder the app installs beside its preferences.

The sample is what the app opens at start — as an editable session copy in either development mode (§9.5): extracted to a fresh folder of its own on a desktop or Android build, an in-memory overlay over the bundle on the web (§6), so edits and uploads last until the sample is opened again and the complete project can be kept with Copy to a folder… — and what most tests read. In-memory fixtures (filmopen_test_support’s fixtures.dart) cover what the sample cannot: ambiguity, missing epochs, multi-author project folders, bad field types, cycles.