4

I want to use locale specific error messages with my JAXP and Xerces2. By default only English messages are available.

First step is to retrieve the messages files and put them into the package "org/apache/xerces/impl/msg/" - done. By using Locale.setDefault (Locale.GERMANY) the German messages are displayed so this is working.

But I want the messages to be differently localized on a per-instance basis. So one parser should return English messages and another parser should return German messages.

The code I'm using to create SAX2 parsers is:

org.xml.sax.XMLReader ret = XMLReaderFactory.createXMLReader ();

for DOM I'm using the DocumentBuilder likes this (very simplified):

    final DocumentBuilderFactory aDocumentBuilderFactory = DocumentBuilderFactory.newInstance ();
    final DocumentBuilder aDocBuilder = aDocBuilderFactory.newDocumentBuilder ();
    final Document doc = aDocumentBuilder.parse (aInputSource);

I found something like the org.apache.xerces.impl.XMLErrorReporter class which has a setLocale(Locale) method but I didn't find a way to get/set it.

Switching to SAX1 is not an option btw.

Any help is appreciated!

Philip Helger
  • 1,814
  • 18
  • 28

3 Answers3

7

Not the maximum in portability but it works as the parser is the apache parser in 99% of all cases.

final DocumentBuilderFactory aDocBuilderFactory = DocumentBuilderFactory.newInstance();
aDocBuilderFactory.setAttribute("http://apache.org/xml/properties/locale", Locale.FRANCE);
final DocumentBuilder aDocBuilder = aDocBuilderFactory.newDocumentBuilder();
final Document doc = aDocBuilder.parse (aInputSource);

For a SAXParser saxParser simply call saxParser.setProperty("http://apache.org/xml/properties/locale", Locale.FRANCE);

Oh, forgot the official source: http://xerces.apache.org/xerces2-j/properties.html

Holger
  • 285,553
  • 42
  • 434
  • 765
  • 1
    Note: if you want to force english validation messages, you may need to input an empty Locale object. This is because the JRE does not include a XMLMessage_en.properties and thus tries the default locale set (in my case german or french) which resolves fine to the included bundles. With the use of an empty Locale you force the ResourceBundle mechanism to just try the basename (which in JRE contains the english properties). – beat Feb 20 '17 at 09:57
  • 3
    Small note because I stumbled upon a similar issue when using XSD validation: If you have a `Validator` instance you can use `validator.setProperty("http://apache.org/xml/properties/locale", Locale.FRANCE);` - but beware if you have a Locale with "en" language but a non-English default Locale - use `Locale.ROOT` to enforce English messages! – Philip Helger Jan 26 '18 at 18:55
  • @PhilipHelger Thanks for your note, it's very helpful. To enforce using English for non-English users, we must use Locale.ROOT. – h2o2 Apr 15 '21 at 15:12
4

Possibility set/get MessageFormatter:

Validator validator = schema.newValidator();      
XMLErrorReporter property = (XMLErrorReporter) validator.getProperty("http://apache.org/xml/properties/internal/error-reporter");
MessageFormatter messageFormatter = property.getMessageFormatter("http://www.w3.org/TR/xml-schema-1");
property.putMessageFormatter(MyMessageFormatter.SCHEMA_DOMAIN, new MyMessageFormatter());


public class MyMessageFormatter implements MessageFormatter {
    public static final String SCHEMA_DOMAIN = "http://www.w3.org/TR/xml-schema-1";
    //...
    public String formatMessage(Locale locale, String key, Object[] arguments)
            throws MissingResourceException {...}
    //...

}
upx1988
  • 41
  • 2
0

I think you should try using

com.sun.org.apache.xerces.internal.impl.msg.XMLMessageFormatter 

If you are writing a custom validation, try calling its formatMessage(...) method, where you could provide the locale name as parameter.

An example of the same is provided in the apache library itself. See it http://cr.openjdk.java.net/~coffeys/openJDK.7u21.sync/webrev/jaxp/src/com/sun/org/apache/xerces/internal/impl/msg/XMLMessageFormatter_zh_CN.java-.html

or

http://www.docjar.com/html/api/com/sun/org/apache/xerces/internal/impl/msg/XMLMessageFormatter.java.html

Another approach could be that you may override the formatMessage() method to implement it in your own way. See the below implemented code of this method:

 public String More ...formatMessage(Locale locale, String key, Object[] arguments)
         throws MissingResourceException {
          if (fResourceBundle == null || locale != fLocale) {
             if (locale != null) {
                 fResourceBundle = PropertyResourceBundle.getBundle("com.sun.org.apache.xerces.internal.impl.msg.XMLMessages", locale);
                 // memorize the most-recent locale
                 fLocale = locale;
             }
             if (fResourceBundle == null)
                 fResourceBundle = PropertyResourceBundle.getBundle("com.sun.org.apache.xerces.internal.impl.msg.XMLMessages");
         }

This indicate that, if a resource bundle file is declared according to locale, the control should be able to pick a different resource file having error message in different language.

Gyanendra Dwivedi
  • 5,511
  • 2
  • 27
  • 53
  • I was thinking about using XMLMessageFormatter but in the example provided, they are using some `XMLSchemaValidatorComponentManager` to get and update the XMLMessageFormatter (which I dont have). There must be a way to do it with standard DocumentBuilder setup.... – Philip Helger Sep 08 '13 at 15:03
  • Sorry, My bad.. I had put a wrong link.. Have updated the right one. Hope this helps. – Gyanendra Dwivedi Sep 08 '13 at 16:56
  • Thanks for updating the link. So it seems to me that I can't set a locale directly. So how can I set the XMLMessageFormatter? Which property to use? Even though this solution is not optimal because it is binding to Xerces..... – Philip Helger Sep 09 '13 at 12:20
  • I guess, you need to use formatMessage() method here. One thing I could suggest that if you wish to have a setter method, you should think to create a child class with setter method to locale and your own format method which would call the parent's super.formatMessage() passing the locale information set through setter method. – Gyanendra Dwivedi Sep 10 '13 at 05:31
  • But that totally interferes with the factory concept of JAXP - there must be an "easy" way to "simply" set a locale (or a message formatter) to a DocumentBuilder or a DocumentBuilderFactory – Philip Helger Sep 10 '13 at 06:03
  • The formatMessage() parameter do have an option to send Locale. It is only that how you want to pass it. If you want to get the locale from the system (like java.util.ResourceBundle.getLocale()) and send that as parameter to formatMessage(), you should be all set. – Gyanendra Dwivedi Sep 10 '13 at 06:13
  • OK, and how do I set the XMLMessageFormatter? – Philip Helger Sep 10 '13 at 08:56