Managing Dynamic Localisation Resources
Solution 1:
Scalebale Solution
If you want to keep this scaleble, you need to save your strings in a form which supports random access without loading everything into memory. So, a plain file (which strings.xml is essentially) won't do the job.
I recommend you check if you can accomplish what you want with an SQLite Database.
This would result in something like:
SELECT text FROM table WHERE locale = yourlocale ORDER BY RANDOM() LIMIT 1
(see Select random row from an sqlite table).
This solution requires quite a lot of work to create the needed database, so for known small situations, use the solution below.
Limited solution
If you know you won't have too many entries I would recommend to use plain textfiles (one per language). They are easiest to manage.
You can either save them as raw resource or in the assets folder. Both are relatively easy to read into a String. Then you just need to call String.split("\n")
and have an array from which you can select one at random.
Alternatively, you can put the strings in a string-array in each localized strings.xml and load the wanted array using resources like this:
ResourcesstandardResources= context.getResources();
AssetManagerassets= standardResources.getAssets();
DisplayMetricsmetrics= standardResources.getDisplayMetrics();
Configurationconfig=newConfiguration(standardResources.getConfiguration());
config.locale = yourLocale;
Resourcesresources=newResources(assets, metrics, config);
(see : Load language specific string from resource?)
As noted in the sources comments this seems to override the resources returned from context.getResources()
, maybe you have to reset to the previous locale afterwards.
Starting from Jellybean there is also context.createConfigurationContext
, which doesn't seem to have this problem.
In all cases it might be a good idea to cache the array if you need to select entries repeatedly.
Note: This solution doesn't scale well, because the whole array has to be loaded into memory just to select one entry. So large collections might exceed your heap or at least use a lot of memory.
Solution 2:
Taking reference from this answer and this answer, I came up with the following custom class solution:
package com.my.package.localisation;
import android.content.Context;
import android.content.res.AssetManager;
import android.content.res.Configuration;
import android.content.res.Resources;
import android.os.Build;
import android.support.annotation.NonNull;
import android.util.DisplayMetrics;
import java.util.Formatter;
import java.util.Locale;
/**
* Class to manage fetching {@link Resources} for a specific {@link Locale}. API levels less
* than {@link Build.VERSION_CODES#JELLY_BEAN_MR1} require an ugly implementation.
* <p/>
* Subclass extends {@link Resources} in case of further functionality requirements.
*/publicclassMyResources {
privatefinal Context mContext;
privatefinal AssetManager assetManager;
privatefinal DisplayMetrics metrics;
privatefinal Configuration configuration;
privatefinal Locale targetLocale;
privatefinal Locale defaultLocale;
publicMyResources(@NonNullfinal Context mContext, @NonNullfinal Locale defaultLocale,
@NonNullfinal Locale targetLocale) {
this.mContext = mContext;
finalResourcesresources=this.mContext.getResources();
this.assetManager = resources.getAssets();
this.metrics = resources.getDisplayMetrics();
this.configuration = newConfiguration(resources.getConfiguration());
this.targetLocale = targetLocale;
this.defaultLocale = defaultLocale;
}
public String[] getStringArray(finalint resourceId) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) {
configuration.setLocale(targetLocale);
return mContext.createConfigurationContext(configuration).getResources().getStringArray(resourceId);
} else {
configuration.locale = targetLocale;
final String[] resourceArray = newResourceManager(assetManager, metrics, configuration).getStringArray(resourceId);
configuration.locale = defaultLocale; // resetnewResourceManager(assetManager, metrics, configuration); // resetreturn resourceArray;
}
}
public String getString(finalint resourceId) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) {
configuration.setLocale(targetLocale);
return mContext.createConfigurationContext(configuration).getResources().getString(resourceId);
} else {
configuration.locale = targetLocale;
finalStringresource=newResourceManager(assetManager, metrics, configuration).getString(resourceId);
configuration.locale = defaultLocale; // resetnewResourceManager(assetManager, metrics, configuration); // resetreturn resource;
}
}
privatefinalclassResourceManagerextendsResources {
publicResourceManager(final AssetManager assets, final DisplayMetrics metrics, final Configuration config) {
super(assets, metrics, config);
}
/**
* Return the string array associated with a particular resource ID.
*
* @param id The desired resource identifier, as generated by the aapt
* tool. This integer encodes the package, type, and resource
* entry. The value 0 is an invalid identifier.
* @return The string array associated with the resource.
* @throws NotFoundException Throws NotFoundException if the given ID does not exist.
*/@Overridepublic String[] getStringArray(finalint id) throws NotFoundException {
returnsuper.getStringArray(id);
}
/**
* Return the string value associated with a particular resource ID,
* substituting the format arguments as defined in {@link Formatter}
* and {@link String#format}. It will be stripped of any styled text
* information.
* {@more}
*
* @param id The desired resource identifier, as generated by the aapt
* tool. This integer encodes the package, type, and resource
* entry. The value 0 is an invalid identifier.
* @param formatArgs The format arguments that will be used for substitution.
* @return String The string data associated with the resource,
* stripped of styled text information.
* @throws NotFoundException Throws NotFoundException if the given ID does not exist.
*/@NonNull@Overridepublic String getString(finalint id, final Object... formatArgs)throws NotFoundException {
returnsuper.getString(id, formatArgs);
}
/**
* Return the string value associated with a particular resource ID. It
* will be stripped of any styled text information.
* {@more}
*
* @param id The desired resource identifier, as generated by the aapt
* tool. This integer encodes the package, type, and resource
* entry. The value 0 is an invalid identifier.
* @return String The string data associated with the resource,
* stripped of styled text information.
* @throws NotFoundException Throws NotFoundException if the given ID does not exist.
*/@NonNull@Overridepublic String getString(finalint id)throws NotFoundException {
returnsuper.getString(id);
}
}
}
Post a Comment for "Managing Dynamic Localisation Resources"