How to format message with argument names instead of numbers?

JavaString Formatting

Java Problem Overview


I have something like:

String text = "The user {0} has email address {1}."
// params = { "Robert", "[email protected]" }
String msg = MessageFormat.format(text, params);

This isn't great for me, because sometimes my translators are not sure what goes in the {0} and {1}, also it would be nice to be able to reword the messages without worrying about the order of the args.

I'd like to replace the arguments with readable names instead of numbers. Something like this:

String text = "The user {USERNAME} has email address {EMAILADDRESS}."
// Map map = new HashMap( ... [USERNAME="Robert", EMAILADDRESS="[email protected]"]
String msg = MessageFormat.format(text, map);

Is there an easy way to do this?

Thanks! rob

Java Solutions


Solution 1 - Java

You can use MapFormat for this. Find out the details here:

http://www.java2s.com/Code/Java/I18N/AtextformatsimilartoMessageFormatbutusingstringratherthannumerickeys.htm

String text = "The user {name} has email address {email}.";
Map map = new HashMap();
map.put("name", "Robert");
map.put("email", "[email protected]");
			
System.out.println("1st : " + MapFormat.format(text, map));

OUTPUT:

> 1st : The user Robert has email address [email protected].

Solution 2 - Java

See StrSubstitutor from org.apache.commons.lang3:

Map valuesMap = HashMap();
valuesMap.put("animal", "quick brown fox");
valuesMap.put("target", "lazy dog");
String templateString = "The ${animal} jumped over the ${target}.";
StrSubstitutor sub = new StrSubstitutor(valuesMap);
String resolvedString = sub.replace(templateString);
 
// resolvedString: "The quick brown fox jumped over the lazy dog."

Solution 3 - Java

Easy to make one yourself. This is what I use (the main() function is just for test code):

import java.util.HashMap;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

public class StringTemplate {
	final private String template;
	final private Matcher m;
	static final private Pattern keyPattern = 
		Pattern.compile("\\$\\{([a-zA-Z][a-zA-Z0-9_]*(\\.[a-zA-Z][a-zA-Z0-9_]*)*)\\}");
	private boolean blanknull=false;
	
	public StringTemplate(String template) { 
		this.template=template;
		this.m = keyPattern.matcher(template);
	}
	
	/**
	 * @param map substitution map
	 * @return substituted string
	 */
	public String substitute(Map<String, ? extends Object> map)
	{
		this.m.reset();
		StringBuffer sb = new StringBuffer();
		while (this.m.find())
		{
			String k0 = this.m.group();
			String k = this.m.group(1);
			Object vobj = map.get(k);
			String v = (vobj == null) 
				? (this.blanknull ? "" : k0)
				: vobj.toString();
			this.m.appendReplacement(sb, Matcher.quoteReplacement(v));
		}
		this.m.appendTail(sb);
		return sb.toString();		
	}
	
	public StringTemplate setBlankNull()
	{
		this.blanknull=true;
		return this;
	}
	
	static public void main(String[] args)
	{
		StringTemplate t1 = new StringTemplate("${this} is a ${test} of the ${foo} bar=${bar} ${emergency.broadcasting.system}");
		t1.setBlankNull();
		Map<String, String> m = new HashMap<String, String>();
		m.put("this", "*This*");
		m.put("test", "*TEST*");
		m.put("foo", "$$$aaa\\\\111");
		m.put("emergency.broadcasting.system", "EBS");
		System.out.println(t1.substitute(m));
	}
}

Solution 4 - Java

Your question is closely related to: How to replace a set of tokens in a Java String You could use velocity or another template library. But there will be some pain because Java does not have any kind of Map literals.

Solution 5 - Java

I know my answer comes a little late, but if you still need this functionality, without the need to download a full-fledged template engine you can take a look at aleph-formatter (I am one of the authors):

Student student = new Student("Andrei", 30, "Male");

String studStr = template("#{id}\tName: #{st.getName}, Age: #{st.getAge}, Gender: #{st.getGender}")
                    .arg("id", 10)
                    .arg("st", student)
                    .format();
System.out.println(studStr);

Or you can chain the arguments:

String result = template("#{x} + #{y} = #{z}")
                    .args("x", 5, "y", 10, "z", 15)
                    .format();
System.out.println(result);

// Output: "5 + 10 = 15"

Internally it works using a StringBuilder creating the result by "parsing" the expression, no string concatenation, regex/replace is performed.

Solution 6 - Java

static final Pattern REPLACE_PATTERN = Pattern.compile("\\x24\\x7B([a-zA-Z][\\w\\x2E].*?)\\x7D");

/**
 * Check for unresolved environment
 *
 * @param str
 * @return origin if all substitutions resolved
 */
public static String checkReplacement(String str) {
    Matcher matcher = REPLACE_PATTERN.matcher(str);
    if (matcher.find()) {
        throw LOG.getIllegalArgumentException("Environment variable '" + matcher.group(1) + "' is not defined");
    }
    return str;
}

// replace in str ${key} to value
public static String resolveReplacement(String str, Map<String, String> replacements) {
    Matcher matcher = REPLACE_PATTERN.matcher(str);
    while (matcher.find()) {
        String value = replacements.get(matcher.group(1));
        if (value != null) {
            str = matcher.replaceFirst(replaceWindowsSlash(value));
        }
    }
    return str;
}

But you loose all format options (like ##.#)

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
Questionuser550738View Question on Stackoverflow
Solution 1 - JavaGuruKulkiView Answer on Stackoverflow
Solution 2 - JavaAlikElzin-kilakaView Answer on Stackoverflow
Solution 3 - JavaJason SView Answer on Stackoverflow
Solution 4 - Javakevin clineView Answer on Stackoverflow
Solution 5 - JavaAndrei CiobanuView Answer on Stackoverflow
Solution 6 - JavaGrigory KislinView Answer on Stackoverflow