From 5febedebd6aa774b516aa977a15bd970e75c9a18 Mon Sep 17 00:00:00 2001 From: Hans-Christoph Steiner Date: Tue, 18 Apr 2017 22:55:59 +0200 Subject: [PATCH 01/10] fix mispelled Languages.getLanguages() --- app/src/main/java/org/fdroid/fdroid/FDroidApp.java | 4 ++-- app/src/main/java/org/fdroid/fdroid/Preferences.java | 2 +- .../fdroid/fdroid/views/fragments/PreferencesFragment.java | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/app/src/main/java/org/fdroid/fdroid/FDroidApp.java b/app/src/main/java/org/fdroid/fdroid/FDroidApp.java index 078bd7ce8..d3f0b58ce 100644 --- a/app/src/main/java/org/fdroid/fdroid/FDroidApp.java +++ b/app/src/main/java/org/fdroid/fdroid/FDroidApp.java @@ -194,7 +194,7 @@ public class FDroidApp extends Application { @Override public void onConfigurationChanged(Configuration newConfig) { super.onConfigurationChanged(newConfig); - Languages.setLanguage(this, Preferences.get().getLangauge(), false); + Languages.setLanguage(this, Preferences.get().getLanguage(), false); } @Override @@ -212,7 +212,7 @@ public class FDroidApp extends Application { } Preferences.setup(this); Languages.setup(getClass(), R.string.pref_language_default); - Languages.setLanguage(this, Preferences.get().getLangauge(), false); + Languages.setLanguage(this, Preferences.get().getLanguage(), false); ACRA.init(this); if (isAcraProcess()) { diff --git a/app/src/main/java/org/fdroid/fdroid/Preferences.java b/app/src/main/java/org/fdroid/fdroid/Preferences.java index 92977927e..85342f3ee 100644 --- a/app/src/main/java/org/fdroid/fdroid/Preferences.java +++ b/app/src/main/java/org/fdroid/fdroid/Preferences.java @@ -229,7 +229,7 @@ public final class Preferences implements SharedPreferences.OnSharedPreferenceCh .replaceAll(" ", "-"); } - public String getLangauge() { + public String getLanguage() { return preferences.getString(Preferences.PREF_LANGUAGE, ""); } diff --git a/app/src/main/java/org/fdroid/fdroid/views/fragments/PreferencesFragment.java b/app/src/main/java/org/fdroid/fdroid/views/fragments/PreferencesFragment.java index a4e6cdd3a..113dbfd04 100644 --- a/app/src/main/java/org/fdroid/fdroid/views/fragments/PreferencesFragment.java +++ b/app/src/main/java/org/fdroid/fdroid/views/fragments/PreferencesFragment.java @@ -150,7 +150,7 @@ public class PreferencesFragment extends PreferenceFragment entrySummary(key); if (changing) { Activity activity = getActivity(); - Languages.setLanguage(activity, Preferences.get().getLangauge(), false); + Languages.setLanguage(activity, Preferences.get().getLanguage(), false); Languages.forceChangeLanguage(activity); } break; From 2586e875240f39ec8c4f9968f2e65a623355eae8 Mon Sep 17 00:00:00 2001 From: Hans-Christoph Steiner Date: Tue, 18 Apr 2017 23:09:32 +0200 Subject: [PATCH 02/10] clear Languages pref when using System Default Keeps things clean if System Default clears out the pref entirely. --- app/src/main/java/org/fdroid/fdroid/Languages.java | 1 + app/src/main/java/org/fdroid/fdroid/Preferences.java | 6 +++++- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/app/src/main/java/org/fdroid/fdroid/Languages.java b/app/src/main/java/org/fdroid/fdroid/Languages.java index c30512c32..70ce33981 100644 --- a/app/src/main/java/org/fdroid/fdroid/Languages.java +++ b/app/src/main/java/org/fdroid/fdroid/Languages.java @@ -120,6 +120,7 @@ public final class Languages { if (locale != null && TextUtils.equals(locale.getLanguage(), language) && (!refresh)) { return; // already configured } else if (language == null || language.equals(USE_SYSTEM_DEFAULT)) { + Preferences.get().clearLanguage(); locale = DEFAULT_LOCALE; } else { /* handle locales with the country in it, i.e. zh_CN, zh_TW, etc */ diff --git a/app/src/main/java/org/fdroid/fdroid/Preferences.java b/app/src/main/java/org/fdroid/fdroid/Preferences.java index 85342f3ee..8609870a5 100644 --- a/app/src/main/java/org/fdroid/fdroid/Preferences.java +++ b/app/src/main/java/org/fdroid/fdroid/Preferences.java @@ -230,7 +230,11 @@ public final class Preferences implements SharedPreferences.OnSharedPreferenceCh } public String getLanguage() { - return preferences.getString(Preferences.PREF_LANGUAGE, ""); + return preferences.getString(Preferences.PREF_LANGUAGE, Languages.USE_SYSTEM_DEFAULT); + } + + public void clearLanguage() { + preferences.edit().remove(Preferences.PREF_LANGUAGE).apply(); } public String getLocalRepoName() { From 50982060aca8be819621dc0c9db2f2223dfa8fa7 Mon Sep 17 00:00:00 2001 From: Hans-Christoph Steiner Date: Tue, 18 Apr 2017 23:17:22 +0200 Subject: [PATCH 03/10] support index locales that do not include country (e.g. 'en') --- app/src/main/java/org/fdroid/fdroid/data/App.java | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) 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 e1d46ea26..bb4f84d0f 100644 --- a/app/src/main/java/org/fdroid/fdroid/data/App.java +++ b/app/src/main/java/org/fdroid/fdroid/data/App.java @@ -373,7 +373,14 @@ public class App extends ValueObject implements Comparable, Parcelable { private void setLocalized(Map> localized) { // NOPMD Locale defaultLocale = Locale.getDefault(); String languageTag = defaultLocale.getLanguage(); - String localeTag = languageTag + "-" + defaultLocale.getCountry(); + String countryTag = defaultLocale.getCountry(); + String localeTag; + if (TextUtils.isEmpty(countryTag)) { + localeTag = languageTag; + } else { + localeTag = languageTag + "-" + countryTag; + } + Set locales = localized.keySet(); Set localesToUse = new LinkedHashSet<>(); From add570ab4b02ccb753d87914e9ea5795a3f91f52 Mon Sep 17 00:00:00 2001 From: Hans-Christoph Steiner Date: Tue, 18 Apr 2017 23:36:37 +0200 Subject: [PATCH 04/10] hide Languages pref on >= android-24 Android 24 and later provides tons of languages, and a way to rank multiple languages instead of choosing one. The Languages pref is a big hack and can be problematic, so its better to disable it when its not needed. This will make it so it is no longer possible to set F-Droid to a language that the system does not support. #943 --- .../java/org/fdroid/fdroid/Languages.java | 34 +++++-------------- .../views/fragments/PreferencesFragment.java | 17 +++++++--- app/src/main/res/xml/preferences.xml | 3 +- 3 files changed, 23 insertions(+), 31 deletions(-) diff --git a/app/src/main/java/org/fdroid/fdroid/Languages.java b/app/src/main/java/org/fdroid/fdroid/Languages.java index 70ce33981..68d75ff83 100644 --- a/app/src/main/java/org/fdroid/fdroid/Languages.java +++ b/app/src/main/java/org/fdroid/fdroid/Languages.java @@ -117,6 +117,11 @@ public final class Languages { @TargetApi(17) public static void setLanguage(final ContextWrapper contextWrapper, String language, boolean refresh) { + if (Build.VERSION.SDK_INT >= 24) { + Utils.debugLog(TAG, "Languages.setLanguage() ignored on >= android-24"); + Preferences.get().clearLanguage(); + return; + } if (locale != null && TextUtils.equals(locale.getLanguage(), language) && (!refresh)) { return; // already configured } else if (language == null || language.equals(USE_SYSTEM_DEFAULT)) { @@ -148,6 +153,10 @@ public final class Languages { * @param activity the {@code Activity} to force reload */ public static void forceChangeLanguage(Activity activity) { + if (Build.VERSION.SDK_INT >= 24) { + Utils.debugLog(TAG, "Languages.forceChangeLanguage() ignored on >= android-24"); + return; + } Intent intent = activity.getIntent(); if (intent == null) { // when launched as LAUNCHER return; @@ -159,18 +168,6 @@ public final class Languages { activity.overridePendingTransition(0, 0); } - /** - * @return the name of the language based on the locale. - */ - public String getName(String locale) { - String ret = nameMap.get(locale); - // if no match, try to return a more general name (i.e. English for en_IN) - if (ret == null && locale.contains("_")) { - ret = nameMap.get(locale.split("_")[0]); - } - return ret; - } - /** * @return an array of the names of all the supported languages, sorted to * match what is returned by {@link Languages#getSupportedLocales()}. @@ -179,19 +176,6 @@ public final class Languages { return nameMap.values().toArray(new String[nameMap.size()]); } - public int getPosition(Locale locale) { - String localeName = locale.getLanguage(); - int i = 0; - for (String key : nameMap.keySet()) { - if (TextUtils.equals(key, localeName)) { - return i; - } else { - i++; - } - } - return -1; - } - /** * @return sorted list of supported locales. */ diff --git a/app/src/main/java/org/fdroid/fdroid/views/fragments/PreferencesFragment.java b/app/src/main/java/org/fdroid/fdroid/views/fragments/PreferencesFragment.java index 113dbfd04..f15843f50 100644 --- a/app/src/main/java/org/fdroid/fdroid/views/fragments/PreferencesFragment.java +++ b/app/src/main/java/org/fdroid/fdroid/views/fragments/PreferencesFragment.java @@ -65,10 +65,15 @@ public class PreferencesFragment extends PreferenceFragment updatePrivilegedExtensionPref = findPreference(Preferences.PREF_UNINSTALL_PRIVILEGED_APP); AppCompatListPreference languagePref = (AppCompatListPreference) findPreference(Preferences.PREF_LANGUAGE); - Languages languages = Languages.get(getActivity()); - languagePref.setDefaultValue(Languages.USE_SYSTEM_DEFAULT); - languagePref.setEntries(languages.getAllNames()); - languagePref.setEntryValues(languages.getSupportedLocales()); + if (Build.VERSION.SDK_INT >= 24) { + PreferenceCategory category = (PreferenceCategory) findPreference("pref_category_display"); + category.removePreference(languagePref); + } else { + Languages languages = Languages.get(getActivity()); + languagePref.setDefaultValue(Languages.USE_SYSTEM_DEFAULT); + languagePref.setEntries(languages.getAllNames()); + languagePref.setEntryValues(languages.getSupportedLocales()); + } } private void checkSummary(String key, int resId) { @@ -78,7 +83,9 @@ public class PreferencesFragment extends PreferenceFragment private void entrySummary(String key) { ListPreference pref = (ListPreference) findPreference(key); - pref.setSummary(pref.getEntry()); + if (pref != null) { + pref.setSummary(pref.getEntry()); + } } private void textSummary(String key, int resId) { diff --git a/app/src/main/res/xml/preferences.xml b/app/src/main/res/xml/preferences.xml index 8f560d234..1c878c2c7 100644 --- a/app/src/main/res/xml/preferences.xml +++ b/app/src/main/res/xml/preferences.xml @@ -41,7 +41,8 @@ android:defaultValue="true" android:key="updateNotify" /> - + Date: Wed, 19 Apr 2017 00:12:29 +0200 Subject: [PATCH 05/10] use android-24+ LocaleList when choosing localized index data When choosing localized data from the index, this includes the new LocaleList feature that represents the user's preferred locale ranking https://developer.android.com/reference/android/os/LocaleList.html --- .../main/java/org/fdroid/fdroid/data/App.java | 18 +++++++++++++----- 1 file changed, 13 insertions(+), 5 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 bb4f84d0f..1c0e71a80 100644 --- a/app/src/main/java/org/fdroid/fdroid/data/App.java +++ b/app/src/main/java/org/fdroid/fdroid/data/App.java @@ -7,9 +7,12 @@ import android.content.pm.FeatureInfo; import android.content.pm.PackageInfo; import android.content.pm.PackageManager; import android.content.res.AssetManager; +import android.content.res.Resources; import android.content.res.XmlResourceParser; import android.database.Cursor; +import android.os.Build; import android.os.Environment; +import android.os.LocaleList; import android.os.Parcel; import android.os.Parcelable; import android.support.annotation.Nullable; @@ -383,14 +386,19 @@ public class App extends ValueObject implements Comparable, Parcelable { Set locales = localized.keySet(); Set localesToUse = new LinkedHashSet<>(); - if (locales.contains(localeTag)) { localesToUse.add(localeTag); } - for (String l : locales) { - if (l.startsWith(languageTag)) { - localesToUse.add(l); - break; + if (Build.VERSION.SDK_INT >= 24) { + LocaleList localeList = Resources.getSystem().getConfiguration().getLocales(); + localesToUse.addAll(Arrays.asList(localeList.toLanguageTags().split(","))); + } + for (String toUse : localesToUse) { + for (String l : locales) { + if (l.startsWith(toUse.split("-")[0])) { + localesToUse.add(l); + break; + } } } if (locales.contains("en-US")) { From dc57fd712bc1dbbc50438e2bbcb6ef26fe0dbb20 Mon Sep 17 00:00:00 2001 From: Hans-Christoph Steiner Date: Wed, 19 Apr 2017 15:02:50 +0200 Subject: [PATCH 06/10] rename index-v1 'localized' key names to match fdroidserver This makes the key names standardized across fdroidclient, fdroidserver, and index-v1.json. fdroidserver!261 --- app/src/main/java/org/fdroid/fdroid/data/App.java | 10 +++++----- 1 file changed, 5 insertions(+), 5 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 1c0e71a80..4e8012817 100644 --- a/app/src/main/java/org/fdroid/fdroid/data/App.java +++ b/app/src/main/java/org/fdroid/fdroid/data/App.java @@ -411,22 +411,22 @@ public class App extends ValueObject implements Comparable, Parcelable { } } // if key starts with Upper case, its set by humans - String value = getLocalizedEntry(localized, localesToUse, "Video"); + String value = getLocalizedEntry(localized, localesToUse, "video"); if (!TextUtils.isEmpty(value)) { video = value.split("\n", 1)[0]; } - whatsNew = getLocalizedEntry(localized, localesToUse, "WhatsNew"); + 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"); + value = getLocalizedEntry(localized, localesToUse, "name"); if (!TextUtils.isEmpty(value)) { name = value; } - value = getLocalizedEntry(localized, localesToUse, "Summary"); + value = getLocalizedEntry(localized, localesToUse, "summary"); if (!TextUtils.isEmpty(value)) { summary = value; } - value = getLocalizedEntry(localized, localesToUse, "Description"); + value = getLocalizedEntry(localized, localesToUse, "description"); if (!TextUtils.isEmpty(value)) { description = value; } From 03168ff99e2fb225581d02dc0d915962181e05a4 Mon Sep 17 00:00:00 2001 From: Hans-Christoph Steiner Date: Thu, 27 Apr 2017 23:02:34 +0200 Subject: [PATCH 07/10] separate index locale preference handling on >= android-24 In android-24 and newer, the user can specify multiple languages in a priority list. Therefore, the locale chooser logic here does not need to work so hard to find a language match. For example, if the user wanted to see country-specific variants, they would add them to the preference list. With older versions of Android, the pref is only a single locale. So chances are that someone who specified de_AT would rather see de or de_DE than en_US. Same goes for es_AR, ar_EG, etc. This could annoy Chinese speakers, since someone who sets zh_TW could potentially see zh_CN, which are written pretty differently. --- .../main/java/org/fdroid/fdroid/data/App.java | 25 +++++++++++++------ 1 file changed, 17 insertions(+), 8 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 4e8012817..49ccee5be 100644 --- a/app/src/main/java/org/fdroid/fdroid/data/App.java +++ b/app/src/main/java/org/fdroid/fdroid/data/App.java @@ -386,18 +386,27 @@ public class App extends ValueObject implements Comparable, Parcelable { Set locales = localized.keySet(); Set localesToUse = new LinkedHashSet<>(); - if (locales.contains(localeTag)) { - localesToUse.add(localeTag); - } if (Build.VERSION.SDK_INT >= 24) { LocaleList localeList = Resources.getSystem().getConfiguration().getLocales(); - localesToUse.addAll(Arrays.asList(localeList.toLanguageTags().split(","))); - } - for (String toUse : localesToUse) { + for (String toUse : localeList.toLanguageTags().split(",")) { + localesToUse.add(toUse); + for (String l : locales) { + if (l.equals(toUse.split("-")[0])) { + localesToUse.add(l); + break; + } + } + } + } else { + if (locales.contains(localeTag)) { + localesToUse.add(localeTag); + } + if (locales.contains(languageTag)) { + localesToUse.add(languageTag); + } for (String l : locales) { - if (l.startsWith(toUse.split("-")[0])) { + if (l.startsWith(languageTag)) { localesToUse.add(l); - break; } } } From 4d785bd6bd7f5c7f989c588a001f152255467d47 Mon Sep 17 00:00:00 2001 From: Hans-Christoph Steiner Date: Thu, 27 Apr 2017 23:03:47 +0200 Subject: [PATCH 08/10] use better variable name for the set of available locales --- app/src/main/java/org/fdroid/fdroid/data/App.java | 14 +++++++------- 1 file changed, 7 insertions(+), 7 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 49ccee5be..e8a61688e 100644 --- a/app/src/main/java/org/fdroid/fdroid/data/App.java +++ b/app/src/main/java/org/fdroid/fdroid/data/App.java @@ -384,13 +384,13 @@ public class App extends ValueObject implements Comparable, Parcelable { localeTag = languageTag + "-" + countryTag; } - Set locales = localized.keySet(); + Set availableLocales = localized.keySet(); Set localesToUse = new LinkedHashSet<>(); if (Build.VERSION.SDK_INT >= 24) { LocaleList localeList = Resources.getSystem().getConfiguration().getLocales(); for (String toUse : localeList.toLanguageTags().split(",")) { localesToUse.add(toUse); - for (String l : locales) { + for (String l : availableLocales) { if (l.equals(toUse.split("-")[0])) { localesToUse.add(l); break; @@ -398,22 +398,22 @@ public class App extends ValueObject implements Comparable, Parcelable { } } } else { - if (locales.contains(localeTag)) { + if (availableLocales.contains(localeTag)) { localesToUse.add(localeTag); } - if (locales.contains(languageTag)) { + if (availableLocales.contains(languageTag)) { localesToUse.add(languageTag); } - for (String l : locales) { + for (String l : availableLocales) { if (l.startsWith(languageTag)) { localesToUse.add(l); } } } - if (locales.contains("en-US")) { + if (availableLocales.contains("en-US")) { localesToUse.add("en-US"); } - for (String l : locales) { + for (String l : availableLocales) { if (l.startsWith("en")) { localesToUse.add(l); break; From d9466785684e089ed6c85a85dedba2cf9888f7ee Mon Sep 17 00:00:00 2001 From: Hans-Christoph Steiner Date: Thu, 27 Apr 2017 22:24:26 +0200 Subject: [PATCH 09/10] remove redundant "About" header in Settings view --- app/src/main/res/xml/preferences.xml | 2 -- 1 file changed, 2 deletions(-) diff --git a/app/src/main/res/xml/preferences.xml b/app/src/main/res/xml/preferences.xml index 1c878c2c7..9ebaaf4c7 100644 --- a/app/src/main/res/xml/preferences.xml +++ b/app/src/main/res/xml/preferences.xml @@ -1,13 +1,11 @@ - - Date: Fri, 28 Apr 2017 10:17:20 +0200 Subject: [PATCH 10/10] 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, 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. *

- * 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. + *

+ * 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}. + *

    + *
  • {@code >= android-24}
      + *
    1. the country variant {@code de-AT} from the user locale list + *
    2. only the language {@code de} from the above locale + *
    3. {@code en-US} since its the most common English for software + *
    4. the first available {@code en} locale + *
  • + *
  • {@code < android-24}
      + *
    1. the country variant from the user locale: {@code de-AT} + *
    2. only the language from the above locale: {@code de} + *
    3. all available locales with the same language: {@code de-BE} + *
    4. {@code en-US} since its the most common English for software + *
    5. all available {@code en} locales + *
  • + *
+ * 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}. + *

+ * 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> localized) { // NOPMD @@ -419,14 +453,12 @@ public class App extends ValueObject implements Comparable, 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, 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");