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
Related
I'm very new to Java so it makes it hard for me to explain what I'm trying to do.
I have an abstract class that invokes several object constants like this:
public abstract class Enchantment implements Keyed {
/**
* Provides protection against environmental damage
*/
public static final Enchantment PROTECTION_ENVIRONMENTAL = new EnchantmentWrapper("protection");
In a different file I can access this perfectly fine with Enchantment value = Enchantment.PROTECTION_ENVIRONMENTAL;
However, I'm trying to use a string variable for this instead. Something like this:
String str = "PROTECTION_ENVIRONMENTAL";
Enchantment value = Enchantment.str;
Obviously that won't work. So I did a bunch of research and learned I need to use reflection for this. Using this source code's docs I figured I was looking for field data. So I tried both:
Field fld = Enchantment.class.getField("PROTECTION_ENVIRONMENTAL");
Field fld = Enchantment.class.getDeclaredField("PROTECTION_ENVIRONMENTAL");
But these returned me a NoSuchFieldException. As I was on it, I've tried both getMethod() and getDeclaredMethod() just as well equally with no luck.
I'm now at the point that these are probably "object constants"? I'm not sure how to call them. But I'm definitely at a loss on how to get this to work now and after everything I've tried myself, I figured it was time to ask for some help here.
That one comment is spot on: you absolutely do not use reflection here.
There are only two valid reasons to use reflection:
you are creating a framework that has to deal with classes it doesn't know about
you have for some other reason to deal with classes you don't know about at compile time
But your code perfectly knows about that Enchantment class, its capabilities, and so on. Therefore reflection is the wrong approach. You figured it yourself: it is damn hard to get right, and damn right to get it wrong in some subtle ways. And when you get it wrong, it always blows up at runtime. Reflection code compiling means nothing. It always waits for you to run it to throw up in your face.
So to answer your question by not answering it: use a Map. Like:
Map<String, Enchantment> enchantmentsByConstantName = new HashMap<>();
enchantmentsByConstantName.put("PROTECTION_ENVIRONMENTAL", PROTECTION_ENVIRONMENTAL);
Alternatively, these constants could go into an enum, as outlined in the other answer, but in a sightly different way:
enum EnchantmentHolder {
PROTECTION_ENVIRONMENTAL(new EnchantmentWrapper("protection")),
ANOTHER_ENCHANTMENT(...)
A_THIRD_ENCHANTMENT(...)
...;
private Enchantment enchantment;
private EnchantmentHolder(Enchantment enchantment) {
this.entchantment = entchantment;
}
public Enchantment getEntchantment() { return entchantment; }
You may want to look into enumerations if you know they're going to be constant values;
public enum Enchantment {
PROTECTION_ENVIRONMENTAL {
public void cast() {
// do enum-specific stuff here
}
},
ANOTHER_ENCHANTMENT {
public void cast() {
// do enum-specific stuff here
}
},
A_THIRD_ENCHANTMENT{
public void cast() {
// do enum-specific stuff here
}
};
public abstract void cast();
}
enums can be treated like classes and have methods and properties. You can also convert to and from strings Enchantment.valueOf("PROTECTION_ENVIRONMENTAL") but that's generally if you are reading from a configuration file - in code you'd reference the value directly.
Once you have the Field, you need to call Field.get(Object) with an instance (in this case the class). Something like,
Class<?> cls = Enchantment.class;
try {
Field f = cls.getField("PROTECTION_ENVIRONMENTAL");
System.out.println(f.get(cls));
} catch (Exception e) {
e.printStackTrace();
}
Since you want the Enchantment, you could then test that the instance you get is assignable to Enchantment. Something like,
Class<? extends Enchantment> cls = Enchantment.class;
try {
Field f = cls.getField("PROTECTION_ENVIRONMENTAL");
Object obj = f.get(cls);
if (cls.isAssignableFrom(obj.getClass())) {
Enchantment e = cls.cast(obj);
System.out.println(e);
}
} catch (Exception e) {
e.printStackTrace();
}
But the enum approach is better.
Assume there is a simple class:
public class SingletonClass {
private static SingletonClass singObj;
private string variable1;
private string variable2;
.....
public static synchronized SingletonClass getInstance() {
if (singObj == null) {
singObj = new SingletonClass();
}
return singObj;
}
}
If there are lot of string variables and they need to be stored in multiple language, what's the standard method to manage this in Java?
Currently i use:
public class SingletonClass {
private static SingletonClass singObj_LANG1;
private static SingletonClass singObj_LANG2;
private static SingletonClass singObj_LANG3;
private string variable1;
private string variable2;
.....
public static synchronized SingletonClass getInstance(String lang) {
if (lang.equals("English")) {
if (singObj_LANG1 == null) {
singObj_LANG1 = new SingletonClass();
}
return singObj_LANG1;
}else if (lang.equals("Chinese")) {
if (singObj_LANG2 == null) {
singObj_LANG2 = new SingletonClass();
}
return singObj_LANG2;
}else{
if (singObj_LANG3 == null) {
singObj_LANG3 = new SingletonClass();
}
return singObj_LANG3;
}
}
}
which i think is a bad practice, any better implementation?
What you need is internationalization
Internationalization is the process of designing an application so
that it can be adapted to various languages and regions without
engineering changes. Sometimes the term internationalization is
abbreviated as i18n, because there are 18 letters between the first
"i" and the last "n."
Instead of a string variable for lang you need to use Locale.
You store the messages in a ResourceBundle.
Resource bundles contain locale-specific objects. When your program needs a
locale-specific resource, a String for example, your program can load
it from the resource bundle that is appropriate for the current user's
locale. In this way, you can write program code that is largely
independent of the user's locale isolating most, if not all, of the
locale-specific information in resource bundles. This allows you to
write programs that can:
be easily localized, or translated, into different languages handle
multiple locales at once be easily modified later to support even more
locales
The Java Platform provides two subclasses of ResourceBundle, ListResourceBundle and PropertyResourceBundle, that provide a fairly simple way to create resources. ListResourceBundle manages its resource as a list of key/value pairs. PropertyResourceBundle uses a properties file to manage its resources.
What i recommend is the PropertyResourceBundle because you should be keeping your translated values in a properties file.
A properties file is a simple text file. You can create and maintain a properties file with just about any text editor.
Read more backing a ResourceBundle with Properties Files here
You can read more about the concept here.
In the end you will end up getting the messing like this:
ResourceBundle messages = ResourceBundle.getBundle("MessagesBundle", currentLocale);
System.out.println(messages.getString("locale.language.key.example"));
The links i provided represent lessons in a wider course on internationalization. You can navigate and read more about it there and you will end up learning the best practices. Using a framework it becomes even easier.
Taking this approach you will be using a single class.
I wouldn't use the Singleton approach at all. Java Internationalization is what you need:
https://docs.oracle.com/javase/tutorial/i18n/intro/steps.html
I would use a Map as storage for your language-specific singletons
private static Map<String, TheClass> map = new HashMap<>();
public static SingletonClass getInstance(String lang) {
synchronized(map){
if(map.containsKey(lang)) return map.get(lang);
else{
SomeClass it = new SomeClass();
map.put(lang, it);
return it;
}
}
}
But the better solution for your problem is Internationalization (see other answers)
My question is how to use constant field values defined in predefined classes like I am practicing on the events program, and currently on action event, I have understand
the action listener part but when I go to action event part , I don't know how to use the static field constant, only I am able to use methods of the that classes, it will be more helpful if a simple example is given by you (simple not complex)
Elaboration:
I want to know how to use the ALT_MASK, ACTION_FIRST, ACTION_LAST constant
Also please show me how to create events of my own
Let's imagine you have class:
public Class ConstantsHere {
public static final int INTEGER_CONSTANT = 5;
}
Then, you want to use it in another class, and you write code like this:
//some code
if (myValue < ConstantsHere.INTEGER_CONSTANT) {
//do something
}
As mentioned in commens, Java Enum may be a good choice for this task:
public enum Action {
ALT_MASK, ACTION_FIRST, ACTION_LAST;
}
Usage:
//some code
if (myValue == Action.ACTION_LAST) {
//do something
}
To make things clear, Enum should be used in case when some variable may take limited number of values. For example, human gender can be only male or female (please do not take this as offensive for transsexuals, statement used only for explanation purposes), so it might be a good idea to use Enum for that instead of constants 0 and 1 (or M and F), just because we can put other number (or constant) there and break the logic.
Using enums example.
public enum UserStatus {
PENDING("P"), ACTIVE("A"), INACTIVE("I"), DELETED("D");
private String statusCode;
private UserStatus(String s) {
statusCode = s;
}
public String getStatusCode() {
return statusCode;
}
}
public void method(UserStatus status) {
System.out.println(status.getStatusCode());
}
}
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);
}
My variable is
private String category_code = null;
My getter and setter is generated as
public String getCategory_code() {
return category_code;
}
public void setCategory_code(String category_code) {
this.category_code = category_code;
}
Is it possible to generate
public String getCategorycode() {
return category_code;
}
public void setCategorycode(String categorycode) {
this.category_code = category_code;
}
I checked Properties-->Code Style-->Fields but that is only for prefix and suffix.
Or should I just rename my variables as m_categoryCode? and get my output as follows?
public String getCategoryCode() {
return m_categoryCode;
}
public void setCategoryCode(String categoryCode) {
m_categoryCode = categoryCode;
}
Which is better?
The names of the getter and setter methods are derived from the field name. If you use a prefix or suffix for fields (e.g. fValue, _value, val_m), you can specify the suffixes and prefixes in the Code Style preference page (Windows > Preferences > Java > Code Style).
reference at here
Java code tends to follow the camelCaseStyle, not the c_underscore_style. Following the existing standards will generally help you in a variety of ways (you will be able to better read others' code and others will be able to better read your code, where "others" are other developers in the same language). also, the tooling for the language tends to work better (case in point).