Say I have a drop-down menu in my java application. I used the below XML code to call the list of menus:
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<menutypes>
<Menutype>
<name>Menu A</name>
<type>2</type>
<param>0</param>
<diameter>0</diameter>
<autocollimatable>false</autocollimatable>
<autotrackable>false</autotrackable>
</Menutype>
<Menutype>
<name>Menu B</name>
<type>1</type>
<param>0</param>
<diameter>30</diameter>
<autocollimatable>true</autocollimatable>
<autotrackable>false</autotrackable>
</Menutype>
<Menutype>
<name>Menu C</name>
<type>0</type>
<param>-17</param>
<diameter>23</diameter>
<autocollimatable>true</autocollimatable>
<autotrackable>false</autotrackable>
</Menutype>
</menutypes>
Now I call the above menus in one of my java class like so; instance = JAXB.unmarshal(new FileInputStream("src/resource/menutypes.xml"), Menutypes.class);. Now I want to implement localization for each corresponding menus based on the user's locale settings like one for Japanese and one for Chinese and so on so that when they open the app, it will show the language based on their locale. Is there any effective way to implement this in Java?
One possibility would be to have a ResourceBundle that specifies that name of the XML file to use for each locale, and then get the filename from it. You would then have one XML file per locale.
MenuBundle_en_US.properties
filename=menutypes_en_US.xml
MenuBundle_jp_JP.properties
filename=menutypes_jp_JP.xml
MenuBundle_zh_CN.properties
filename=menutypes_zh_CN.xml
Menu.java
public Menutypes loadMenus(Locale locale) {
ResourceBundle bundle = ResourceBundle.getBundle("MenuBundle", locale);
String filename = bundle.getString("filename");
Menutypes instance = JAXB.unmarshal
new FileInputStream("src/resource/" + filename), Menutypes.class);
return instance;
}
Something like that, modulo error handling. I don't know if there's a better way to do it with XML.
Here is the sample.
public final class ResourceUtils {
private static final String MESSAGE_FILE_NAME = "messages";
public static String getMessage(String key) {
Locale local = SessionPreferences.getCurrentLocale();
ResourceBundle bundle = ResourceBundle.getBundle(MESSAGE_FILE_NAME, local);
return bundle.getString(key);
}
}
The file messages.properties
APP.title=HOME
APP.home.title=Salesman
Then you can modify the set/get:
class Menutypes {
String getName() {
return ResourceUtils.getMessage('APP.title');
}
}
Related
My Android app uses an enum type to define certain API endpoints.
public static enum API_ENDPOINT{
MISSION, FEATURED_MEDIA
}
The enum type seems an appropriate argument for methods that are dependent on the API call type, but I'm unable to translate enums to consistent Strings (i.e for mapping to API endpoint urls) across devices configured with different languages.
In Turkish API_ENDPOINT.values() returns: mıssıon, featured_medıa
In English API_ENDPOINT.values() returns: mission, featured_media
An obvious solution is an additional data structure that maps API_ENDPOINT to hard-coded string endpoints, but I'm curious as to whether this behavior of enum.values() is intended and/or avoidable.
Solved: Thanks everyone for the insight. It turns out deeper in the logic to convert API_ENDPOINT to a URL string I used String.toLowerCase() without specifying a Locale, which resulted in the undesirable behavior. This has been replaced with String.toLowerCase(Locale.US)
You can hard-code the strings as part of the enum, without any additional data structure:
public static enum API_ENDPOINT{
MISSION("mission"), FEATURED_MEDIA("featured_media");
private final String value;
API_ENDPOINT(String value) { this.value = value; }
public String value() { return value; }
}
but it would be nice if there were just a way to control the representation that's automatically generated.
The JLS enum section doesn't speak directly to language differences like this, but strongly suggests that the output would exactly match the enum identifiers; I'm surprised that you'd even get lower-case strings with upper-case identifiers.
After further testing, this isn't reproducible, something else must be going on in your code.
This minimal program displays the enum identifiers exactly as typed regardless of locale:
public class MainActivity extends Activity {
public enum ENUM {
MISSION, FEATURED_MEDIA
}
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
TextView textView = (TextView) findViewById(R.id.text);
String enums = "";
for (ENUM e : ENUM.values()) {
enums += e + " ";
}
textView.setText(enums);
}
}
You can define two property-files. One for English and one for Turkish.The Enum could then look like this:
public static enum API_ENDPOINT{
MISSION("path.to.property.mission"), FEATURED_MEDIA("path.to.property.featured_media");
private String propertyName;
API_ENDPOINT(String propertyName){
this.propertyName = propertyName;
}
// language could also be an enum which defines the language to be taken
// and should contain the path to the file.
public String getTranslatedText(Language language){
Properties prop = new Properties();
try {
//load a properties file from class path
prop.load(API_ENDPOINT.class.getClassLoader().getResourceAsStream(language.getPropertyFileName()));
//get the translated value and raturn it.
return prop.getProperty(propertyName);
} catch (IOException ex) {
ex.printStackTrace();
}
}
}
The Property-File will look like this (English):
path.to.property.mission=Mission
path.to.property.featured_media=Featured Media
Same goes for Turkish.
Hope that helps.
EDIT: Due to you are using Android, this might be the solution for your problem:
Is there a sensible way to refer to application resources (R.string...) in static initializers
Make Enum.toString() localized
I am currently making resources for an app that is using ResourceBundle. The thing is, with the current code to dispatch the resources I would need to create an instance of the resource bundle every time I need it and I can guess this is not a good idea, since I would end up loading the resources again and again.
The second solution would be to divide the bundle into many, But I would end up with bundles have only 2-3 strings and like 15 bundles.
My question is:
Is there a way to simple load all the resources in a single static class and access them from there.
I made this little piece of code that seems to work for me but I doubt its quality.
public class StaticBundle
{
private final static ResourceBundle resBundle =
ResourceBundle.getBundle("com.resources");
public final static String STRING_A = resBundle.getString("KEY_A");
public final static String STRING_B = resBundle.getString("KEY_B");
public final static String STRING_C = resBundle.getString("KEY_C");
}
With this I can call StaticBundle.STRING_A and get the value anywhere in the project but since the bundle is initialized at the same time as the class itself... It is highly possible that the program won't have the time to load the proper local from the preferences.
Is there a good way to do this or any other possible solution?
Thank you
If you intend to have only messages for the default locale then what you have is fine.
Alternatively you could let the caller specify which key it needs instead of having constants, like this:
public static String getMessage(String key) {
return resBundle.getString(key);
}
If you like to support multiple locales then the usual approach is to have a Map<Locale, ResourceBundle>Map<Locale, Map<String, String> where you load the resources only once for each locale. In that case your class would have a method where the caller can specify the locale:
public static String getMessage(String key, Locale locale) {
Map<String, String> bundle = bundles.get(locale); // this is the map with all bundles
if (bundle == null) {
// load the bundle for the locale specified
// here you would also need some logic to mark bundles that were not found so
// to avoid continously searching bundles that are not present
// you could even return the message for the default locale if desirable
}
return bundle.get(key);
}
Edit: As correctly pointed out by #JB Nizet (thanks) ResourceBundle already stores a Map. The custom solution I provided in the source example, was about a custom mechanism similar to ResourceBundle that used a Map of Maps to load translations of keys in a property=value format, not only from files but also a database. I have incorrectly thought that we had a Map of ResourceBundle in that solution. The source example is fixed now.
You can create a singleton class:
public class MyResouceBundle extends ResourceBundle {
private static MyResourceBundle instance = new MyResouceBundle();
// private constructor, no one can instantiate this class, only itself
private MyResourceBundle() {
}
public ResourceBundle getInstance() {
return instance;
}
}
Then, everyone will access the same instance of the class with (to get string for KEY_A, for example):
MyResourceBunde.getInstance().get("KEY_A");
I'm developing an Android application and I want to know if I can set Enum.toString() multilanguage.
I'm going to use this Enum on a Spinner and I want to use multi language texts.
public class Types
{
public enum Stature
{
tall (0, "tall"),
average(1, "average"),
small(2, "small");
private final int stature;
private final String statureString;
Stature(int anStature, String anStatureString) { stature = anStature; statureString = anStatureString; }
public int getValue() { return stature; }
#Override
public String toString() { return statureString; }
}
}
I don't know how to use Context.getString() inside an Enum, and I have hardcoded "tall", "average" and "small" to test it. I have defined that enum inside on a helper class.
This how I use the enum on a Spinner:
mSpinStature.setAdapter(new ArrayAdapter<Stature>(mActivity, android.R.layout.simple_dropdown_item_1line, Stature.values()));
Do you know how can I do it?
I created a simple library which is a part of my big project (Xdroid):
compile 'com.shamanland:xdroid-enum-format:0.2.4'
Now you can avoid the same monkey-job (declaring field, constructor, etc) for all enumetations by using annotations:
public enum State {
#EnumString(R.string.state_idle)
IDLE,
#EnumString(R.string.state_pending)
PENDING,
#EnumString(R.string.state_in_progress)
IN_PROGRESS,
#EnumString(R.string.state_cancelled)
CANCELLED,
#EnumString(R.string.state_done)
DONE;
}
And then use the common Java approach - use extensions of class java.text.Format:
public void onStateChanged(State state) {
EnumFormat enumFormat = EnumFormat.getInstance();
toast(enumFormat.format(state));
}
strings.xml
<string name="state_idle">Idle</string>
<string name="state_pending">Pending</string>
<string name="state_in_progress">In progress</string>
<string name="state_cancelled">Cancelled</string>
<string name="state_done">Done</string>
Look here how to show Toast simply.
You can also compile a demo app from github.
Assume this resource path
String resourceBundlePath = "my.package.bundles.messages"
In package my.package.bundles you may have messages.properties, messages_en_US.properties etc.
Then, using
ResourceBundle resourceBundle = ResourceBundle.getBundle(resourceBundlePath);
String messageKey = "myFirstMessage";
String message = resourceBundle.getMessage(messageKey);
message will contain the value of the messageKey property defined on messages.properties. If the current Locale is actually en_US you will get the value from messages_en_US.properties. If the current locale is something you do not have a properties file for the value will be from the default messages.properties
You can also call
ResourceBundle.getBundle(resourceBundlePath, myLocale);
but it is generally better to use the platform locale (have a look at jvm arguments -Duser.language, -Duser.country)
You can have a ResourceBundle for each enum you want to translate with keys the enum element names and use it in the toString() implementation of your enum:
#Override
public String toString() {
return resourceBudle.getString(super.toString());
}
I would leave enum as is and use the standard ResourceBundle approach http://docs.oracle.com/javase/tutorial/i18n/resbundle/concept.html using Enum.toString as the key
#Override
public String toString()
{
//java
return ResourceBundle.getBundle().getString(id);
//android?
App.getContext().getString(id);
}
I want to store file names, which keep on changing as the new files get added. I am looking for a minimum change in server code later when there is a need to support a new 'file' The thought I have is to store them either in properties file or as Java enum, but still thinking which is a better approach.
I am using REST and having 'file type' in the URL.
Example rest url:
hostname/file-content/TYPE
where value of TYPE could be any of these: standardFileNames1,standardFileNames2,randomFileName1,randomFileName2
I have used TYPE to group the files, so as to minimize the change in url when a new file is added. Dont want to have file names in the URL due to security issues.
my thought goes like this:
having as ENUM:
public enum FileType
{
standardFileNames1("Afile_en", "Afile_jp"),
standardFileNames2("Bfile_en","Bfile_jp"),
randomFileName1("xyz"),
randomFileName2("abc"),
...
...
}
having as properties file:
standardFileNames1=Afile_en,Afile_jp
standardFileNames2=Bfile_en,Bfile_jp
randomFileName1=xyz
randomFileName2=abc
I know having this in properties will save build efforts on every change, but still want to know your views to figure out best solution with all considerations.
Thanks!
Akhilesh
I often use property file + enum combination. Here is an example:
public enum Constants {
PROP1,
PROP2;
private static final String PATH = "/constants.properties";
private static final Logger logger = LoggerFactory.getLogger(Constants.class);
private static Properties properties;
private String value;
private void init() {
if (properties == null) {
properties = new Properties();
try {
properties.load(Constants.class.getResourceAsStream(PATH));
}
catch (Exception e) {
logger.error("Unable to load " + PATH + " file from classpath.", e);
System.exit(1);
}
}
value = (String) properties.get(this.toString());
}
public String getValue() {
if (value == null) {
init();
}
return value;
}
}
Now you also need a property file (I ofter place it in src, so it is packaged into JAR), with properties just as you used in enum. For example:
constants.properties:
#This is property file...
PROP1=some text
PROP2=some other text
Now I very often use static import in classes where I want to use my constants:
import static com.some.package.Constants.*;
And an example usage
System.out.println(PROP1);
Source:http://stackoverflow.com/questions/4908973/java-property-file-as-enum
My suggestion is to keep in properties or config file and write a generic code to get the file list and parse in java. So that whenever a new file comes, there will be no change on server side rather you will add an entry to the properties or config file.
We are using java.util.ResourceBundle to load property information. Our property file has grown so huge and we are thinking of splitting the master property file into several sub modules. Is it possible to achieve this?
master.properties
==>
master.properties
include moduleA.properties
include moduleB.properties
Let me know?
First of all, I wonder why you've chosen java.util.ResourceBundle over java.util.Properties. Given how your question is formulated, you don't seem to care about localization/internationalization nor about bundle file inheritance.
With Properties it's extraordinary easy since it implements Map which in turn offers a putAll() method to merge another map. Kickoff example:
Properties master = new Properties();
master.load(masterInput);
Properties moduleA = new Properties();
moduleA.load(moduleAinput);
master.putAll(moduleA);
Properties moduleB = new Properties();
moduleB.load(moduleBinput);
master.putAll(moduleB);
// Now `master` contains the properties of all files.
If you really insist in using ResourceBundle, your best bet is to create a custom ResourceBundle wherein you contol the loading by a custom Control.
Assuming that you've the following entry in master.properties which represents a commaseparated string with base names of the module properties files:
include=moduleA,moduleB
Then the following custom ResourceBundle example should work:
public class MultiResourceBundle extends ResourceBundle {
protected static final Control CONTROL = new MultiResourceBundleControl();
private Properties properties;
public MultiResourceBundle(String baseName) {
setParent(ResourceBundle.getBundle(baseName, CONTROL));
}
protected MultiResourceBundle(Properties properties) {
this.properties = properties;
}
#Override
protected Object handleGetObject(String key) {
return properties != null ? properties.get(key) : parent.getObject(key);
}
#Override
#SuppressWarnings("unchecked")
public Enumeration<String> getKeys() {
return properties != null ? (Enumeration<String>) properties.propertyNames() : parent.getKeys();
}
protected static class MultiResourceBundleControl extends Control {
#Override
public ResourceBundle newBundle(
String baseName, Locale locale, String format, ClassLoader loader, boolean reload)
throws IllegalAccessException, InstantiationException, IOException
{
Properties properties = load(baseName, loader);
String include = properties.getProperty("include");
if (include != null) {
for (String includeBaseName : include.split("\\s*,\\s*")) {
properties.putAll(load(includeBaseName, loader));
}
}
return new MultiResourceBundle(properties);
}
private Properties load(String baseName, ClassLoader loader) throws IOException {
Properties properties = new Properties();
properties.load(loader.getResourceAsStream(baseName + ".properties"));
return properties;
}
}
}
(trivial exception handling and localization handling is left aside, this is up to you)
This can be used as:
ResourceBundle bundle = new MultiResourceBundle("master");
You can programatically, however, construct ResourceBundle but as you are saying your file is huge then what if it is loaded into memory.
update
public class Resource extends java.util.ResourceBundle {
public Object handleGetObject(String key) {
//code
}
public Enumeration getKeys() {
//code
}
}
then for IN locale
import java.util.*;
public class Resource_en_IN extends Resource{
public Object handleGetObject(String key) {
//code
}
}
More Food for thought than a tested solution.
XML files support entities to inline text from other files during parsing. If seen complex xml files, where this technique has been used to modularise the files.
Properties now supports two file formats, the common .properties format with key/value pairs and an xml formats. Properties can load and store to/from xml files.
ResourceBundle has one direct subclass: PropertyResourceBundle. It looks like this class is actually limited to the older key/value pair format, but it could be used to implement another class, like XMLPropertyResourceBundle which is capable of reading properties from xml files where the entity trick could help to modularize those files.
If this works - transforming the existing property files to xml properties files should be easy, just use the Properties class, read from standard format and store to XML.