From 0a59c5c6e59a5148ba8a9c647ed7f7b12bddab41 Mon Sep 17 00:00:00 2001 From: Hans-Christoph Steiner <hans@eds.org> Date: Fri, 28 Apr 2017 10:17:20 +0200 Subject: [PATCH] fully write up locale choosing for the 'localized' block This is how locales are handled when parsing the index from the server. --- .../main/java/org/fdroid/fdroid/data/App.java | 55 +++++++++++++++---- 1 file changed, 43 insertions(+), 12 deletions(-) diff --git a/app/src/main/java/org/fdroid/fdroid/data/App.java b/app/src/main/java/org/fdroid/fdroid/data/App.java index e8a61688e..f7d04b79a 100644 --- a/app/src/main/java/org/fdroid/fdroid/data/App.java +++ b/app/src/main/java/org/fdroid/fdroid/data/App.java @@ -362,15 +362,49 @@ public class App extends ValueObject implements Comparable<App>, Parcelable { /** * Parses the {@code localized} block in the incoming index metadata, * choosing the best match in terms of locale/language while filling as - * many fields as possible. The first English locale found is loaded, then - * {@code en-US} is loaded over that, since that's the most common English - * for software. Then the first language match, and then finally the - * current locale for this device, given it precedence over all the others. + * many fields as possible. It first sets up a locale list based on user + * preference and the locales available for this app, then picks the texts + * based on that list. One thing that makes this tricky is that any given + * locale block in the index might not have all the fields. So when filling + * out each value, it needs to go through the whole preference list each time, + * rather than just taking the whole block for a specific locale. This is to + * ensure that there is something to show, as often as possible. * <p> - * It is still possible that the fields will be loaded directly without any - * locale info. This comes from the old-style {@code .txt} app metadata + * It is still possible that the fields will be loaded directly by Jackson + * without any locale info. This comes from the old-style, inline app metadata * fields that do not have locale info. They should not be used if the - * {@code Localized} block is specified. + * {@code localized} block is included in the index. Also, null strings in + * the {@code localized} block should not overwrite Name/Summary/Description + * strings with empty/null if they were set directly by Jackson. + * <p> + * Choosing the locale to use follows two sets of rules, one for Android versions + * older than {@code android-24} and the other for {@code android-24} or newer. + * The system-wide language preference list was added in {@code android-24}. + * <ul> + * <li>{@code >= android-24}<ol> + * <li>the country variant {@code de-AT} from the user locale list + * <li>only the language {@code de} from the above locale + * <li>{@code en-US} since its the most common English for software + * <li>the first available {@code en} locale + * </ol></li> + * <li>{@code < android-24}<ol> + * <li>the country variant from the user locale: {@code de-AT} + * <li>only the language from the above locale: {@code de} + * <li>all available locales with the same language: {@code de-BE} + * <li>{@code en-US} since its the most common English for software + * <li>all available {@code en} locales + * </ol></li> + * </ul> + * On {@code >= android-24}, it is by design that this does not fallback to other + * country-specific locales, e.g. {@code fr-CH} does not fall back on {@code fr-FR}. + * If someone wants to fallback to {@code fr-FR}, they can add it to the system + * language list. There are many cases where it is inappropriate to fallback to a + * different country-specific locale, for example {@code de-DE --> de-CH} or + * {@code zh-CN --> zh-TW}. + * <p> + * On {@code < android-24}, the user can only set a single + * locale with a country as an option, so here it makes sense to try to fallback + * on other country-specific locales, rather than English. */ @JsonProperty("localized") private void setLocalized(Map<String, Map<String, Object>> localized) { // NOPMD @@ -419,14 +453,12 @@ public class App extends ValueObject implements Comparable<App>, Parcelable { break; } } - // if key starts with Upper case, its set by humans + + whatsNew = getLocalizedEntry(localized, localesToUse, "whatsNew"); String value = getLocalizedEntry(localized, localesToUse, "video"); if (!TextUtils.isEmpty(value)) { video = value.split("\n", 1)[0]; } - whatsNew = getLocalizedEntry(localized, localesToUse, "whatsNew"); - // Name, Summary, Description existed before localization so they shouldn't replace - // non-localized old data format with a null or blank string value = getLocalizedEntry(localized, localesToUse, "name"); if (!TextUtils.isEmpty(value)) { name = value; @@ -440,7 +472,6 @@ public class App extends ValueObject implements Comparable<App>, Parcelable { description = value; } - // if key starts with lower case, its generated based on finding the files featureGraphic = getLocalizedGraphicsEntry(localized, localesToUse, "featureGraphic"); promoGraphic = getLocalizedGraphicsEntry(localized, localesToUse, "promoGraphic"); tvBanner = getLocalizedGraphicsEntry(localized, localesToUse, "tvBanner");