sjngm
sjngm

Reputation: 12861

"ResourceBundle" For Entire Files?

Obviously ResourceBundle requires a property file like syntax in the files it finds.

We have a situation where we want to use entire files (in our case HTML-files) as "values". This means that we don't have keys as such. Well, maybe the filename would work as the key.

Here's a directory tree:

src/
    main/
        resources/
            html/
                content.html
                content_de.html
                content_fr.html
                content_es.html
                order.html
                order_de.html
                order_fr.html
                order_es.html

Now we need logic to find the correct file based on the current locale. If the current locale is German and I'm looking for html/content.html file, it should find html/content_de.html. It doesn't necessarily need to load it right away. Is there some existing mechanism in Java? Do we need to do this manually?

Due to some restrictions, we are currently planning to not use any third-party libraries. So if there is something available in Java 6 SE, it would be our best choice; however, if you know of a third-party library, feel free to name it.

EDIT #1: An obvious solution would be to have a key in messages.properties to name that HTML-file. While that would work it may become a pain in the butt on the long run (and besides that I don't think this would solve all our issues with this).

EDIT #2: I forgot to say that this is a desktop application.

Upvotes: 5

Views: 973

Answers (3)

Akira
Akira

Reputation: 4071

I know this is an old question but I just came across the exact same problem and I found a more elegant solution. What you need is to delegate locale to file mapping to the standard Java API the same way the API solves translations for keys. So in your example, if the current locale is fr_FR you want to load the files called "content_fr.html" and "order_fr.html", right?

Then simply have a set of resource bundle files and designate a variable to translate the current locale to the closest existing locale:

File translation.properties:

localeCode = 

File translation_fr.properties:

localeCode = fr

File translation_en.properties:

localeCode = en

Then you just need to read the value of "localeCode" and concatenate it with "content" and ".html" or "order" and ".html".

Upvotes: 0

sjngm
sjngm

Reputation: 12861

To show that I wasn't doing nothing, here are two attempts using an "on our own"-approach:

The first attempt with locale-postfix build up and straight forward loading of resources:

  public void attempt1(String baseName, String extension) {
    List<String> locales = buildLocaleStrings(Locale.getDefault());
    String resourceFound = null;

    for (String locale : locales) {
      String resourceName = baseName + locale + "." + extension;
      URL resource = getClass().getClassLoader().getResource(resourceName);
      if (resource != null) {
        resourceFound = resourceName;
        break;
      }
    }
    System.out.println("found #1: " + resourceFound);
  }

  private List<String> buildLocaleStrings(Locale localeBase) {
    String locale = "_" + localeBase;
    List<String> locales = new ArrayList<String>();

    while (locale.length() > 0) {
      locales.add(locale);
      locale = locale.replaceFirst("_[^_]*?$", "");
    }
    locales.add("");

    return locales;
  }

The second attempt "abusing" ResourceBundle and its toString():

  public void attempt2(String baseName, final String extension) {
    ResourceBundle.Control control = new ResourceBundle.Control() {

      private String resourceFound = null;

      @Override
      public List<String> getFormats(String baseName) {
        return Arrays.asList(extension);
      }

      @Override
      public ResourceBundle newBundle(String baseName, Locale locale, String format, ClassLoader loader, boolean reload) throws IllegalAccessException, InstantiationException, IOException {
        String bundleName = toBundleName(baseName, locale);
        String resourceName = toResourceName(bundleName, format);

        if (loader.getResource(resourceName) != null) {
          resourceFound = resourceName;
          return new ResourceBundle() {

            @Override
            public Enumeration<String> getKeys() {
              return null;
            }

            @Override
            protected Object handleGetObject(String key) {
              return null;
            }

          };
        }
        return null;
      }

      @Override
      public String toString() {
        return resourceFound;
      }
    };

    ResourceBundle.getBundle(baseName, control);

    System.out.println("found #2: " + control.toString());
  }

Sample calls:

  public void proof() {
    attempt1("html/content", "html");
    attempt2("html/content", "html");
  }

Both find the same file.

To be honest, I don't like neither.

Upvotes: 1

Makoto
Makoto

Reputation: 106430

To make this more ideal, if your naming convention for your files remains consistent (i.e. for each locale, you use the two-letter prefix of the language - meaning 'en' for English, 'fr' for French, and 'es' for Spanish), then this process is extremely straightforward.

We will make use of the Properties class to read the properties in, then use MessageFormat to format the appropriate locale we want from the resultant property.

First, we make a change to the property file - we parameterize it such that we are able to pass in whatever we like.

content=content_{0}.html
order=order_{0}.html

The {0} represents the first parameter to the property.

Now, we only need to load the property in, and pass in the appropriate parameter.

Properties prop = new Properties();
try {
    MessageFormat messageFormat = new MessageFormat("");
    String property;
    // change to suit the locale you wish to serve
    String[] param = {"en"};

    prop.load(new FileReader(new File("/full/path/to/property/file.properties")));
    property = prop.getProperty("content");
    messageFormat.applyPattern(property);
    System.out.println(messageFormat.format(param));
} catch(IOException ioex) {
    System.out.println("no property file here");
}

This prints out:

content_en.html

Ensure that the HTML file you want to access exists before making this call, or turn this into a function which returns String, and ensure that the file exists before it's returned.

Upvotes: 1

Related Questions