checkPlayTree function
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;
}