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

// Copyright 2014 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 org.jni_zero.CalledByNative;
import org.jni_zero.JNINamespace;

import org.chromium.base.ApkAssets;
import org.chromium.base.LocaleUtils;
import org.chromium.base.Log;

import java.util.Arrays;

/**
 * This class provides the resource bundle related methods for the native
 * library.
 *
 * IMPORTANT: Clients that use {@link ResourceBundle} MUST call either
 * {@link ResourceBundle#setAvailablePakLocales(String[], String[])} or
 * {@link ResourceBundle#setNoAvailableLocalePaks()} before calling the getters in this class.
 */
@JNINamespace("ui")
public final class ResourceBundle {
    private static final String TAG = "ResourceBundle";
    private static String[] sAvailableLocales;

    private ResourceBundle() {}

    /** Called when there are no locale pak files available. */
    @CalledByNative
    public static void setNoAvailableLocalePaks() {
        assert sAvailableLocales == null;
        sAvailableLocales = new String[] {};
    }

    /**
     * Sets the available locale pak files.
     * @param uncompressed Locales that have pak files.
     */
    public static void setAvailablePakLocales(String[] locales) {
        assert sAvailableLocales == null;
        sAvailableLocales = locales;
    }

    public static void clearAvailablePakLocalesForTesting() {
        sAvailableLocales = null;
    }

    /**
     * Return the list of available locales.
     * @return The correct locale list for this build.
     */
    public static String[] getAvailableLocales() {
        assert sAvailableLocales != null;
        return sAvailableLocales;
    }

    /**
     * Return the location of a locale-specific .pak file asset.
     *
     * @param locale Chromium locale name.
     * @param inBundle If true, return the path of the uncompressed .pak file
     *                 containing Chromium UI strings within app bundles. If
     *                 false, return the path of the WebView UI strings instead.
     * @param logError Logs if the file is not found.
     * @return Asset path to .pak file, or null if the locale is not supported.
     */
    @CalledByNative
    private static String getLocalePakResourcePath(
            String locale, boolean inBundle, boolean logError) {
        if (sAvailableLocales == null) {
            // Locales may be null in unit tests.
            return null;
        }
        if (Arrays.binarySearch(sAvailableLocales, locale) < 0) {
            // This locale is not supported by Chromium.
            return null;
        }
        String pathPrefix = "assets/stored-locales/";
        if (inBundle) {
            if (locale.equals("en-US")) {
                pathPrefix = "assets/fallback-locales/";
            } else {
                String lang =
                        LocalizationUtils.getSplitLanguageForAndroid(
                                LocaleUtils.toBaseLanguage(locale));
                pathPrefix = "assets/locales#lang_" + lang + "/";
            }
        }
        String apkSubpath = pathPrefix + locale + ".pak";
        // The file may not exist if the language split for this locale has not been installed
        // yet, so make sure it exists before returning the asset path.
        if (ApkAssets.exists(apkSubpath)) {
            return apkSubpath;
        }
        // Fallback for apk targets.
        // TODO(crbug.com/40168285): Remove the need for this fallback logic.
        String fallbackPath = "assets/locales/" + locale + ".pak";
        if (ApkAssets.exists(fallbackPath)) {
            return fallbackPath;
        }
        if (logError) {
            Log.e(TAG, "Did not exist: %s", apkSubpath);
        }
        return null;
    }
}