0

In an Android app, I need to select resources from a resources folder other than the currently selected one (the interface language is whatever the system selects, but the user may select a different language for TTS). Due to the way the application is structured, switching resources is not an option; I need both the default and the alternate set at the same time. I was able to use this code to, for example, select Spanish strings when the user interface was in English:

class SpanishStrings()
{
    private Resources mResources;

    public Foo(Context context)
    {
        final Resources oldResources = context.getResources();
        Configuration oldConfiguration = oldResources.getConfiguration();
        DisplayMetrics metrics = oldResources.getDisplayMetrics();
        Configuration configuration = new Configuration(oldConfiguration);
        configuration.locale = new Locale("es", "es");
        mResources = new Resources(oldResources.getAssets(), metrics, configuration);
    }

    String getString(int id)
    {
        return mResources.getString(id);
    }
}

But this also set the entire user interface to Spanish! Can Android only have one active Resources at a time?

This is running on Android 2.3.4, if that makes any difference.

Shakeeb Ayaz
  • 6,200
  • 6
  • 45
  • 64
Logan Pickup
  • 2,294
  • 2
  • 22
  • 29

2 Answers2

2

I found one way to get it to work. Given the class in the question, remove the constructor and change getString() like so:

String getString(int id)
    {
        final Resources oldResources = mContext.getResources();
        final Configuration oldConfiguration = oldResources.getConfiguration();
        final DisplayMetrics metrics = oldResources.getDisplayMetrics();
        final Configuration configuration = new Configuration(oldConfiguration);
        configuration.locale = new Locale("es", "es");
        Resources resources = new Resources(oldResources.getAssets(), metrics, configuration);
        final String result = resources.getString(id);
        oldResources.updateConfiguration(oldConfiguration, metrics);
        return result;
    }

It's the updateConfiguration that is important. I looked through the code for android.content.res.Resources, and it turns out that the Resources() constructor calls updateConfiguration(), and that updateConfiguration() calls mAssets.setConfiguration(). This means that any changes to configuration are applied to all users of the same AssetManager instance.

I didn't look any deeper than that, and I know the above answer could be optimised by keeping two sets of resources and only calling updateConfiguration() instead of constructing a whole new Resources object before and after retrieving the string, so if anyone knows how to create a whole new AssetManager() instance then please post a better answer than this!

EDIT: In my current code I've made it less painful to use by subclassing Resources, like so:

final oldResources = context.getResources();
final Configuration configuration = new Configuration(mDefaultResources.getConfiguration());
configuration.locale = new Locale("es", "es");
mResources = new Resources(mDefaultResources.getAssets(), mDefaultResources.getDisplayMetrics(), configuration)
    {
        @Override
        public String getString(int id) throws NotFoundException
        {
            updateConfiguration(getConfiguration(), getDisplayMetrics());
            String result = super.getString(id);
            oldResources.updateConfiguration(oldResources.getConfiguration(), oldResources.getDisplayMetrics());
            return result;
        }
    };
Logan Pickup
  • 2,294
  • 2
  • 22
  • 29
0

Try modifying your code like this (taken from https://stackoverflow.com/a/10879185/446963)

 cMK = getResources().getStringArray(R.array.cities);
 Configuration confTmp =new Configuration( getResources().getConfiguration());
 confTmp.locale = new Locale("en");
 DisplayMetrics metrics = new DisplayMetrics();
 getWindowManager().getDefaultDisplay().getMetrics(metrics);
 Resources resources = new Resources(getAssets(), metrics, confTmp);
 /* get localized string */
 cENG = getResources().getStringArray(R.array.cities);

In your code you are the metrics and assets from the original resources to the new resources. So I think any changes happening to those objects in the new resources and filtering back to the original resources.

Using the code above new objects are being created for all the needed resources parameters. Just a guess but see if it works...

Community
  • 1
  • 1
athor
  • 6,848
  • 2
  • 34
  • 37
  • The above code uses the the original Assets() too, and my code above already creates a new Configuration. The only thing different are the Metrics, but I'll give it a try. – Logan Pickup Jul 11 '13 at 22:59
  • Sorry, didn't work, but now I think I know why! Creating new resources updates the assets, and AssetManager doesn't provide a way to duplicate itself that I can see. – Logan Pickup Jul 11 '13 at 23:11