diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 54486423f..4b3070379 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -13,7 +13,6 @@ before_script: test: script: - cd app - - ./tools/langs-list-check.py - ./tools/check-string-format.py - cd .. - ./gradlew assemble -PdisablePreDex diff --git a/app/src/main/java/org/fdroid/fdroid/FDroidApp.java b/app/src/main/java/org/fdroid/fdroid/FDroidApp.java index 4d78f0af9..7009f4ebb 100644 --- a/app/src/main/java/org/fdroid/fdroid/FDroidApp.java +++ b/app/src/main/java/org/fdroid/fdroid/FDroidApp.java @@ -227,6 +227,7 @@ public class FDroidApp extends Application { Preferences.setup(this); curTheme = Preferences.get().getTheme(); Preferences.get().configureProxy(); + Languages.setup(getClass(), R.string.pref_language_default); InstalledAppProviderService.compareToPackageManager(this); AppUpdateStatusService.scanDownloadedApks(this); diff --git a/app/src/main/java/org/fdroid/fdroid/Languages.java b/app/src/main/java/org/fdroid/fdroid/Languages.java new file mode 100644 index 000000000..546605b06 --- /dev/null +++ b/app/src/main/java/org/fdroid/fdroid/Languages.java @@ -0,0 +1,277 @@ +package org.fdroid.fdroid; + +import android.annotation.TargetApi; +import android.app.Activity; +import android.content.ContextWrapper; +import android.content.Intent; +import android.content.res.AssetManager; +import android.content.res.Configuration; +import android.content.res.Resources; +import android.os.Build; +import android.text.TextUtils; +import android.util.DisplayMetrics; + +import java.util.Collections; +import java.util.LinkedHashSet; +import java.util.Locale; +import java.util.Map; +import java.util.Set; +import java.util.TreeMap; + +public final class Languages { + public static final String TAG = "Languages"; + + public static final String USE_SYSTEM_DEFAULT = ""; + + private static final Locale DEFAULT_LOCALE; + private static final Locale TIBETAN = new Locale("bo"); + private static final String DEFAULT_STRING = "System Default"; + + private static Locale locale; + private static Languages singleton; + private static Class clazz; + private static int resId; + private static Map tmpMap = new TreeMap<>(); + private static Map nameMap; + + static { + DEFAULT_LOCALE = Locale.getDefault(); + } + + private Languages(Activity activity) { + AssetManager assets = activity.getAssets(); + Configuration config = activity.getResources().getConfiguration(); + // Resources() requires DisplayMetrics, but they are only needed for drawables + DisplayMetrics ignored = new DisplayMetrics(); + activity.getWindowManager().getDefaultDisplay().getMetrics(ignored); + Resources resources; + Set localeSet = new LinkedHashSet<>(); + for (Locale locale : LOCALES_TO_TEST) { + config.locale = locale; + resources = new Resources(assets, ignored, config); + if (!TextUtils.equals(DEFAULT_STRING, resources.getString(resId)) + || locale.equals(Locale.ENGLISH)) { + localeSet.add(locale); + } + } + for (Locale locale : localeSet) { + if (locale.equals(TIBETAN)) { + // include English name for devices without Tibetan font support + tmpMap.put(TIBETAN.getLanguage(), "Tibetan བོད་སྐད།"); // Tibetan + } else if (locale.equals(Locale.SIMPLIFIED_CHINESE)) { + tmpMap.put(Locale.SIMPLIFIED_CHINESE.toString(), "中文 (中国)"); // Chinese (China) + } else if (locale.equals(Locale.TRADITIONAL_CHINESE)) { + tmpMap.put(Locale.TRADITIONAL_CHINESE.toString(), "中文 (台灣)"); // Chinese (Taiwan) + } else { + tmpMap.put(locale.getLanguage(), locale.getDisplayLanguage(locale)); + } + } + + /* SYSTEM_DEFAULT is a fake one for displaying in a chooser menu. */ + localeSet.add(null); + tmpMap.put(USE_SYSTEM_DEFAULT, activity.getString(resId)); + nameMap = Collections.unmodifiableMap(tmpMap); + } + + /** + * Get the instance of {@link Languages} to work with, providing the + * {@link Activity} that is will be working as part of, as well as the + * {@code resId} that has the exact string "Use System Default", + * i.e. {@code R.string.use_system_default}. + *

+ * That string resource {@code resId} is also used to find the supported + * translations: if an included translation has a translated string that + * matches that {@code resId}, then that language will be included as a + * supported language. + * + * @param clazz the {@link Class} of the default {@code Activity}, + * usually the main {@code Activity} from where the + * Settings is launched from. + * @param resId the string resource ID to for the string "System Default", + * e.g. {@code R.string.pref_language_default} + */ + public static void setup(Class clazz, int resId) { + if (Languages.clazz == null) { + Languages.clazz = clazz; + Languages.resId = resId; + } else { + throw new RuntimeException("Languages singleton was already initialized, duplicate call to Languages.setup()!"); + } + } + + /** + * @param activity the {@link Activity} this is working as part of + * @return the singleton to work with + */ + public static Languages get(Activity activity) { + if (singleton == null) { + singleton = new Languages(activity); + } + return singleton; + } + + @TargetApi(17) + public static void setLanguage(final ContextWrapper contextWrapper, String language, boolean refresh) { + if (locale != null && TextUtils.equals(locale.getLanguage(), language) && (!refresh)) { + return; // already configured + } else if (language == null || language.equals(USE_SYSTEM_DEFAULT)) { + locale = DEFAULT_LOCALE; + } else { + /* handle locales with the country in it, i.e. zh_CN, zh_TW, etc */ + String[] localeSplit = language.split("_"); + if (localeSplit.length > 1) { + locale = new Locale(localeSplit[0], localeSplit[1]); + } else { + locale = new Locale(language); + } + } + + final Resources resources = contextWrapper.getBaseContext().getResources(); + Configuration config = resources.getConfiguration(); + if (Build.VERSION.SDK_INT >= 17) { + config.setLocale(locale); + } else { + config.locale = locale; + } + resources.updateConfiguration(config, resources.getDisplayMetrics()); + Locale.setDefault(locale); + + } + + /** + * Force reload the {@link Activity to make language changes take effect.} + * + * @param activity the {@code Activity} to force reload + */ + public static void forceChangeLanguage(Activity activity) { + Intent intent = activity.getIntent(); + if (intent == null) { // when launched as LAUNCHER + return; + } + intent.addFlags(Intent.FLAG_ACTIVITY_NO_ANIMATION); + activity.finish(); + activity.overridePendingTransition(0, 0); + activity.startActivity(intent); + 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()}. + */ + public String[] getAllNames() { + 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. + */ + public String[] getSupportedLocales() { + Set keys = nameMap.keySet(); + return keys.toArray(new String[keys.size()]); + } + + private static final Locale[] LOCALES_TO_TEST = { + Locale.ENGLISH, + Locale.FRENCH, + Locale.GERMAN, + Locale.ITALIAN, + Locale.JAPANESE, + Locale.KOREAN, + Locale.SIMPLIFIED_CHINESE, + Locale.TRADITIONAL_CHINESE, + TIBETAN, + new Locale("af"), + new Locale("am"), + new Locale("ar"), + new Locale("az"), + new Locale("be"), + new Locale("bg"), + new Locale("bn"), + new Locale("ca"), + new Locale("cs"), + new Locale("da"), + new Locale("el"), + new Locale("es"), + new Locale("et"), + new Locale("eu"), + new Locale("fa"), + new Locale("fi"), + new Locale("gl"), + new Locale("hi"), + new Locale("hr"), + new Locale("hu"), + new Locale("hy"), + new Locale("in"), + new Locale("hy"), + new Locale("in"), + new Locale("is"), + new Locale("it"), + new Locale("iw"), + new Locale("ka"), + new Locale("kk"), + new Locale("km"), + new Locale("kn"), + new Locale("ky"), + new Locale("lo"), + new Locale("lt"), + new Locale("lv"), + new Locale("mk"), + new Locale("ml"), + new Locale("mn"), + new Locale("mr"), + new Locale("ms"), + new Locale("my"), + new Locale("nb"), + new Locale("ne"), + new Locale("nl"), + new Locale("pl"), + new Locale("pt"), + new Locale("rm"), + new Locale("ro"), + new Locale("ru"), + new Locale("si"), + new Locale("sk"), + new Locale("sl"), + new Locale("sn"), + new Locale("sr"), + new Locale("sv"), + new Locale("sw"), + new Locale("ta"), + new Locale("te"), + new Locale("th"), + new Locale("tl"), + new Locale("tr"), + new Locale("uk"), + new Locale("ur"), + new Locale("uz"), + new Locale("vi"), + new Locale("zu"), + }; + +} 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 4574cb177..f8c918e47 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 @@ -12,19 +12,19 @@ import android.preference.Preference; import android.preference.PreferenceCategory; import android.support.v4.preference.PreferenceFragment; import android.text.TextUtils; - +import com.geecko.QuickLyric.view.AppCompatListPreference; +import info.guardianproject.netcipher.NetCipher; +import info.guardianproject.netcipher.proxy.OrbotHelper; import org.fdroid.fdroid.AppDetails2; import org.fdroid.fdroid.CleanCacheService; import org.fdroid.fdroid.FDroidApp; +import org.fdroid.fdroid.Languages; import org.fdroid.fdroid.Preferences; import org.fdroid.fdroid.R; import org.fdroid.fdroid.UpdateService; import org.fdroid.fdroid.installer.InstallHistoryService; import org.fdroid.fdroid.installer.PrivilegedInstaller; -import info.guardianproject.netcipher.NetCipher; -import info.guardianproject.netcipher.proxy.OrbotHelper; - public class PreferencesFragment extends PreferenceFragment implements SharedPreferences.OnSharedPreferenceChangeListener { @@ -62,6 +62,12 @@ public class PreferencesFragment extends PreferenceFragment enableProxyCheckPref = (CheckBoxPreference) findPreference(Preferences.PREF_ENABLE_PROXY); updateAutoDownloadPref = findPreference(Preferences.PREF_AUTO_DOWNLOAD_INSTALL_UPDATES); 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()); } private void checkSummary(String key, int resId) { diff --git a/app/src/main/res/values/donottranslate.xml b/app/src/main/res/values/donottranslate.xml index 505cefb68..1de33474f 100644 --- a/app/src/main/res/values/donottranslate.xml +++ b/app/src/main/res/values/donottranslate.xml @@ -44,126 +44,4 @@ night - - - en - af - ar - ast - be - bg - ca - cs - da - de - el - eo - es - et - eu - fa - fi - fr - gl - he - hi - hr - hu - hy - id - is - it - ja - ko - lt - lv - mk - my - nb - nl - pl - pt-rBR - pt-rPT - ro - ru - sc - sk - sl - sn - sq - sr - sv - ta - th - tr - ug - uk - ur - vi - zh-rCN - zh-rHK - zh-rTW - - - - @string/pref_language_default - English - Afrikaans - ﺎﻠﻋﺮﺒﻳﺓ - Asturian - белорусский - Български - Català - Čeština - Dansk - Deutsch - Ελληνικά - Esperanto - Español - Eesti - Euskara - ﻑﺍﺮﺳی - Suomi - Français - Galego - עברית - हिन्दी - Hrvatski - Magyar - հայերեն - Bahasa Indonesia - Íslenska - Italiano - 日本語 - 한국어 - Lietuvių - Latviešu - македонски - မြန်မာစာ - Norsk bokmål - Nederlands - Polski - Português (Brasil) - Português (Portugal) - Română - Русский - Sardinian - Slovenčina - Slovenščina - ChiSona - Shqip - Српски - Svenska - தமிழ் - ไทย - Türkçe - ﺉۇﻲﻏۇﺭچە - Українська - اردو - Tiếng Việt - 中文 (中国) - 中文 (香港) - 中文 (台湾) - - diff --git a/app/src/main/res/xml/preferences.xml b/app/src/main/res/xml/preferences.xml index 6850f2e76..8f560d234 100644 --- a/app/src/main/res/xml/preferences.xml +++ b/app/src/main/res/xml/preferences.xml @@ -43,10 +43,7 @@ + android:key="language"/>