package org.wikipedia.util; import android.content.Context; import android.content.res.Configuration; import android.content.res.Resources; import android.os.Build; import android.support.annotation.StringRes; import android.text.format.DateUtils; import android.util.SparseArray; import android.view.View; import org.json.JSONException; import org.json.JSONObject; import org.wikipedia.R; import org.wikipedia.WikipediaApp; import org.wikipedia.bridge.CommunicationBridge; import org.wikipedia.language.LanguageUtil; import org.wikipedia.page.PageTitle; import java.util.Arrays; import java.util.Date; import java.util.Locale; /** * A collection of localization related methods. * * Note the distinction between Article language and device language. * Article language is the language of the current page content. * Device language is the current language setting in the device system settings. * Those can be different. */ public final class L10nUtil { /** * List of wiki language codes for which the content is primarily RTL. * * Ensure that this is always sorted alphabetically. */ private static final String[] RTL_LANGS = { "ar", "arc", "arz", "bcc", "bqi", "ckb", "dv", "fa", "glk", "he", "khw", "ks", "mzn", "pnb", "ps", "sd", "ug", "ur", "yi" }; /** * Returns true if the given wiki language is to be displayed RTL. * * @param lang Wiki code for the language to check for directionality * @return true if it is RTL, false if LTR */ public static boolean isLangRTL(String lang) { return Arrays.binarySearch(RTL_LANGS, lang, null) >= 0; } /** * Set up directionality for both UI and content elements in a webview. * * @param contentLang The Content language to use to set directionality. Wiki Language code. * @param uiLang The UI language to use to set directionality. Java language code. * @param bridge The CommunicationBridge to use to communicate with the WebView */ public static void setupDirectionality(String contentLang, String uiLang, CommunicationBridge bridge) { JSONObject payload = new JSONObject(); try { if (isLangRTL(contentLang)) { payload.put("contentDirection", "rtl"); } else { payload.put("contentDirection", "ltr"); } if (isLangRTL(LanguageUtil.languageCodeToWikiLanguageCode(uiLang))) { payload.put("uiDirection", "rtl"); } else { payload.put("uiDirection", "ltr"); } } catch (JSONException e) { throw new RuntimeException(e); } bridge.sendMessage("setDirectionality", payload); } /** * Sets text direction (RTL / LTR) for given view based on given lang. * * Doesn't do anything on pre Android 4.2, since their RTL support is terrible. * * @param view View to set text direction of * @param lang Wiki code for the language based on which to set direction */ public static void setConditionalTextDirection(View view, String lang) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) { view.setTextDirection(isLangRTL(lang) ? View.TEXT_DIRECTION_RTL : View.TEXT_DIRECTION_LTR); } } /** * Sets layout direction (RTL / LTR) for given view based on given lang. * * Doesn't do anything on pre Android 4.2, since their RTL support is terrible. * * @param view View to set layout direction of * @param lang Wiki code for the language based on which to set direction */ public static void setConditionalLayoutDirection(View view, String lang) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) { view.setLayoutDirection(isLangRTL(lang) ? View.LAYOUT_DIRECTION_RTL : View.LAYOUT_DIRECTION_LTR); } } /** * Returns true if the translated string for the stylized WP wordmark is equivalent to the * English one, so that the PNG image could be used instead. We'd like to avoid bloating up our * APK size with extra fonts just to show the logo in the correct font, which we only use * rarely (Initial onboarding and ShareAFact). * As a compromise we use the PNG image with the correct font for the mainly used * languages (and also for languages that haven't translated this value). For all other * languages we use a font already available in Android. * * @param context any valid Context will do (even ApplicationContext) * @return true if the translated stylized WP logo text is the same as in English. */ public static boolean canLangUseImageForWikipediaWordmark(Context context) { return "<big>W</big>IKIPEDI<big>A</big>".equals(context.getString(R.string.wp_stylized)); } /** * Returns true if the device languages is set to an RTL language. Note that this includes * RTL_Arabic (AL). * * @return true if RTL, false if not RTL */ public static boolean isDeviceRTL() { return isCharRTL(Locale.getDefault().getDisplayName().charAt(0)); } public static boolean isCharRTL(char c) { final int dir = Character.getDirectionality(c); return dir == Character.DIRECTIONALITY_RIGHT_TO_LEFT || dir == Character.DIRECTIONALITY_RIGHT_TO_LEFT_ARABIC; } public static String getStringForArticleLanguage(PageTitle title, int resId) { return getStringsForLocale(new Locale(title.getWikiSite().languageCode()), new int[]{resId}).get(resId); } public static SparseArray<String> getStringsForArticleLanguage(PageTitle title, int[] resId) { return getStringsForLocale(new Locale(title.getWikiSite().languageCode()), resId); } /** * Get a string resource associated with a specific target locale. This requires working around * Android's internal localization logic; as such, it isn't pretty. * * See http://stackoverflow.com/a/6380008 (submitted by WMF's own Anomie!). */ private static SparseArray<String> getStringsForLocale(Locale targetLocale, @StringRes int[] strings) { Configuration config = getCurrentConfiguration(); Locale systemLocale = ConfigurationCompat.getLocale(config); ConfigurationCompat.setLocale(config, targetLocale); SparseArray<String> localizedStrings = getTargetStrings(strings, config); ConfigurationCompat.setLocale(config, systemLocale); resetConfiguration(config); return localizedStrings; } private static Configuration getCurrentConfiguration() { return new Configuration(WikipediaApp.getInstance().getResources().getConfiguration()); } private static SparseArray<String> getTargetStrings(@StringRes int[] strings, Configuration altConfig) { SparseArray<String> localizedStrings = new SparseArray<>(); Resources targetResources = new Resources(WikipediaApp.getInstance().getResources().getAssets(), WikipediaApp.getInstance().getResources().getDisplayMetrics(), altConfig); for (int stringRes : strings) { localizedStrings.put(stringRes, targetResources.getString(stringRes)); } return localizedStrings; } /** * Reset the system resources by initializing a new Resources object with the original configuration. * @param defaultConfig The original system configuration */ private static void resetConfiguration(Configuration defaultConfig) { new Resources(WikipediaApp.getInstance().getResources().getAssets(), WikipediaApp.getInstance().getResources().getDisplayMetrics(), defaultConfig); } /** * Formats provided date relative to the current system time * @param date Date to format * @return String representing the relative time difference of the paramter from current time */ public static String formatDateRelative(Date date) { return DateUtils.getRelativeTimeSpanString(date.getTime(), System.currentTimeMillis(), DateUtils.SECOND_IN_MILLIS, 0).toString(); } private L10nUtil() { } }