Accessing contents of R.string using a variable to represent the resource name

I have a few strings which I need to translate and display. Those strings are in variables. I have the translation in the strings.xml file.

I want to display the "translated version" of the string. For example, inside an Activity:

String name = "Water";
TextView nameDisplay = new TextView(this).
nameDisplay.setText(name);

In the strings file I have the definition

<string name="Water">French word for Water</string>

If I used something like this:

nameDisplay.setText(R.string.KnownName);

it would work. But in my case, the name is stored in a variable so I do not know what to do in order for the setText method to function properly.

My current workaround is

String translation = ""

if(name == "Water") {
  translation = getString(R.string.Water);
}
else {
  ...
}

nameDisplay.setText(translation);

... but this does not scale very well.

Any suggestions?

Should I store the translated version in the variable?


Solution 1:

You can use the method to convert string into int identifier:

public static int getStringIdentifier(Context context, String name) {
    return context.getResources().getIdentifier(name, "string", context.getPackageName());
}

Pass in an activity as context parameter (or any other Context instance). Then you can use the identifier as usual with getString() method.

Note that conversion from string to identifier uses reflection and thus can be not that fast, so use carefully.

Solution 2:

 private String getStringResourceByName(String aString)
    {
      String packageName = context.getPackageName(); 
      int resId = getResources().getIdentifier(aString, "string", packageName);
      return getString(resId);
    }

Solution 3:

Another thing you can do is map the string to the resource. That way you can have the string in a variable (or build it programmatically) and then use the map to look up the resource id.

Example

strings.xml

<string name="water_english">water</string>
<string name="water_spanish">agua</string>
<string name="water_chinese">水</string>
<string name="water_mongolian">ᠤᠰᠥ</string>

Code

public class MainActivity extends AppCompatActivity {

    private Map<String, Integer> stringForResourceIdMap = createMap();

    private Map<String, Integer> createMap() {
        Map<String, Integer> result = new HashMap<>();

        result.put("water_english", R.string.water_english);
        result.put("water_spanish", R.string.water_spanish);
        result.put("water_chinese", R.string.water_chinese);
        result.put("water_mongolian", R.string.water_mongolian);

        return result;
    }

    private String getMyStringResource(String lookupString) {
        int resourceId = stringForResourceIdMap.get(lookupString); // R.string.xxx
        return getString(resourceId);
    }

    // ...
}

Note

If you are localizing your app with different translations, then you should be using different resource folders.

Solution 4:

As a Kotlin extension, ArK's answer could be written the following way. It also catches an exception when you give it an unknown key.

fun Context.getStringWithResKey(nameResKey: String): String {
    val resId = resources.getIdentifier(nameResKey, "string", packageName)
    return try {
        getString(resId)
    } catch (e: Exception) {
        Log.e(this.javaClass.simpleName, "Couldn't find string value for key '$nameResKey'", e)
        ""
    }
}