chromium/ui/android/java/src/org/chromium/ui/base/LocalizationUtils.java

// Copyright 2012 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

package org.chromium.ui.base;

import android.view.View;

import org.jni_zero.CalledByNative;
import org.jni_zero.CalledByNativeForTesting;
import org.jni_zero.JNINamespace;
import org.jni_zero.NativeMethods;

import org.chromium.base.ContextUtils;
import org.chromium.base.LocaleUtils;
import org.chromium.base.ResettersForTesting;

import java.util.Locale;

/** This class provides the locale related methods for the native library. */
@JNINamespace("l10n_util")
public class LocalizationUtils {

    // This is mirrored from base/i18n/rtl.h. Please keep in sync.
    public static final int UNKNOWN_DIRECTION = 0;
    public static final int RIGHT_TO_LEFT = 1;
    public static final int LEFT_TO_RIGHT = 2;

    private static Boolean sIsLayoutRtlForTesting;

    private LocalizationUtils() {
        /* cannot be instantiated */
    }

    @CalledByNative
    private static Locale getJavaLocale(String language, String country, String variant) {
        return new Locale(language, country, variant);
    }

    @CalledByNative
    private static String getDisplayNameForLocale(Locale locale, Locale displayLocale) {
        return locale.getDisplayName(displayLocale);
    }

    /**
     * Returns whether the Android layout direction is RTL.
     *
     * Note that the locale direction can be different from layout direction. Two known cases:
     * - RTL languages on Android 4.1, due to the lack of RTL layout support on 4.1.
     * - When user turned on force RTL layout option under developer options.
     *
     * Therefore, only this function should be used to query RTL for layout purposes.
     */
    @CalledByNative
    public static boolean isLayoutRtl() {
        if (sIsLayoutRtlForTesting != null) return sIsLayoutRtlForTesting;

        return ContextUtils.getApplicationContext()
                        .getResources()
                        .getConfiguration()
                        .getLayoutDirection()
                == View.LAYOUT_DIRECTION_RTL;
    }

    @CalledByNativeForTesting
    public static void setRtlForTesting(boolean shouldBeRtl) {
        sIsLayoutRtlForTesting = shouldBeRtl;
        ResettersForTesting.register(() -> sIsLayoutRtlForTesting = null);
    }

    /** Returns whether navigation gestures should be mirrored due to the UI language. */
    @CalledByNative
    public static boolean shouldMirrorBackForwardGestures() {
        if (!UiAndroidFeatureMap.isEnabled(UiAndroidFeatures.MIRROR_BACK_FORWARD_GESTURES_IN_RTL)) {
            return false;
        }

        return LocalizationUtils.isLayoutRtl();
    }

    /**
     * Jni binding to base::i18n::GetFirstStrongCharacterDirection
     *
     * @param string String to decide the direction.
     * @return One of the UNKNOWN_DIRECTION, RIGHT_TO_LEFT, and LEFT_TO_RIGHT.
     */
    public static int getFirstStrongCharacterDirection(String string) {
        assert string != null;
        return LocalizationUtilsJni.get().getFirstStrongCharacterDirection(string);
    }

    public static String getNativeUiLocale() {
        return LocalizationUtilsJni.get().getNativeUiLocale();
    }

    public static String substituteLocalePlaceholder(String str) {
        return str.replace("$LOCALE", LocaleUtils.getDefaultLocaleString().replace('-', '_'));
    }

    /**
     * Return the asset split language associated with a given Chromium language.
     *
     * This matches the directory used to store language-based assets in bundle APK splits.
     * E.g. for Hebrew, known as 'he' by Chromium, this method should return 'iw' because
     * the .pak file will be stored as /assets/locales#lang_iw/he.pak within the split.
     *
     * @param language Chromium specific language name.
     * @return Matching Android specific language name.
     */
    public static String getSplitLanguageForAndroid(String language) {
        // IMPORTANT: Keep in sync with the mapping found in:
        // build/android/gyp/util/resource_utils.py
        switch (language) {
            case "he":
                return "iw"; // Hebrew
            case "yi":
                return "ji"; // Yiddish
            case "id":
                return "in"; // Indonesian
            case "fil":
                return "tl"; // Filipino
            default:
                return language;
        }
    }

    /**
     * Return true iff a locale string matches a specific language string.
     *
     * @param locale Chromium locale name (e.g. "fil", or "en-US").
     * @param lang Chromium language name (e.g. "fi", or "en").
     * @return true iff the locale name matches the languages. E.g. should
     *         be false for ("fil", "fi") (Filipino locale + Finish language)
     *         but true for ("en-US", "en") (USA locale + English language).
     */
    public static boolean chromiumLocaleMatchesLanguage(String locale, String lang) {
        return LocaleUtils.toBaseLanguage(locale).equals(lang);
    }

    @NativeMethods
    interface Natives {
        int getFirstStrongCharacterDirection(String string);

        String getNativeUiLocale();
    }
}