checkPlayTree function

List<ReleaseProblem> checkPlayTree(
  1. String path, {
  2. Set<String> requireScreenshotTypes = const {},
  3. Set<String> requireLocales = const {},
})

Loads the Play tree at path and reports what Play would refuse.

requireScreenshotTypes is the app's own requirement — which types this listing must carry — and is the only thing a consumer names. The icon and feature graphic are Play's and are always required.

requireLocales behaves as on the App Store side: a declared locale missing from the tree is an error. A locale present in the tree that nobody declared is reported, not failed — adding a language should not be a two-commit operation, and the same asymmetry is settled the same way in checkAppStoreTree.

Implementation

List<ReleaseProblem> checkPlayTree(
  String path, {
  Set<String> requireScreenshotTypes = const {},
  Set<String> requireLocales = const {},
}) {
  final PlayMetadata metadata;
  try {
    metadata = loadPlayMetadata(path);
  } on MetadataException catch (e) {
    return [ReleaseProblem(path, e.message)];
  }

  final problems = <ReleaseProblem>[];

  if (metadata.locales.isEmpty) {
    return [ReleaseProblem(path, 'no locales — nothing would publish')];
  }

  final present = metadata.locales.map((l) => l.locale).toSet();

  for (final locale in requireLocales) {
    if (!present.contains(locale)) {
      problems.add(
        ReleaseProblem(path, 'no listing for required locale $locale'),
      );
    }
  }

  // Play has a distinguished locale and the App Store does not. A default
  // naming a locale the tree does not carry is a listing that cannot publish,
  // and it is invisible in a diff of either file alone.
  final defaultLanguage = metadata.defaultLanguage;
  if (defaultLanguage != null && !present.contains(defaultLanguage)) {
    problems.add(
      ReleaseProblem(
        '$path → details/default_language.txt',
        'names $defaultLanguage, which has no listings/$defaultLanguage/ '
            'directory — the default locale has to be one the tree carries',
      ),
    );
  }
  if (defaultLanguage != null &&
      requireLocales.isNotEmpty &&
      !requireLocales.contains(defaultLanguage)) {
    problems.add(
      ReleaseProblem(
        '$path → details/default_language.txt',
        'names $defaultLanguage, which is not among the declared locales '
            '(${(requireLocales.toList()..sort()).join(', ')})',
      ),
    );
  }

  // **A locale in the tree that nobody declared is not reported here.**
  //
  // An earlier draft did report it, with a comment claiming it was "reported,
  // not failed" — which was untrue twice over. `ReleaseProblem` carries no
  // severity, so every caller fails on it; and `checkAppStoreTree` settles the
  // same question the opposite way, by skipping such a locale silently. One
  // store failing where the other shrugs is not a policy, it is an accident.
  //
  // The decision on record is that an undeclared locale reports and does not
  // fail. Until there is a mechanism that can express that, the honest
  // implementation is the one that matches its sibling.

  // Which locale carries the images Play requires. Localized graphics are
  // optional per locale and fall back to the default language, so demanding an
  // icon in every locale directory fails a tree Play accepts — a text-only
  // `de-DE/` beside a fully illustrated `en-US/` is an ordinary, published
  // shape. Only the locale that has to stand alone is held to it.
  final fallbackLocale =
      metadata.defaultLanguage ??
      (metadata.locales.length == 1 ? metadata.locales.single.locale : null);

  for (final locale in metadata.locales) {
    problems.addAll(
      _checkLocale(
        path,
        locale,
        requireScreenshotTypes,
        // Same reasoning as the images: a locale without its own screenshots
        // shows the default language's.
        //
        // **When the fallback cannot be determined, nothing is treated as
        // one.** An earlier revision passed `true` for every locale here, on
        // the theory that checking more was the safe direction. It is not: a
        // two-locale tree with no `default_language.txt` then reported a
        // missing icon against the locale that happened not to carry one,
        // which is an innocent locale — someone acting on that adds an icon to
        // a translation that never needed it and still has the real problem.
        //
        // The real problem is reported once, below. This is the same rule the
        // App Store platform selection follows: when the answer cannot be
        // determined, say so rather than picking one and blaming what follows
        // from the pick.
        isFallback: fallbackLocale != null && locale.locale == fallbackLocale,
      ),
    );
  }

  if (fallbackLocale == null && metadata.locales.length > 1) {
    problems.add(
      ReleaseProblem(
        '$path → details/default_language.txt',
        'is missing, and the tree has ${metadata.locales.length} locales — so '
            'which one supplies the icon, the feature graphic and the '
            'screenshots the others fall back to cannot be told from the tree',
      ),
    );
  }

  return problems;
}