Implementing Database Backed Internationalization (i18n) for Java Web Applications

In today’s globalized world, creating applications that cater to diverse audiences is crucial. One of the essential aspects of accommodating different languages and cultures in web applications is internationalization (i18n). This blog post will address a common challenge developers face when trying to implement i18n in Java web applications—specifically how to utilize a database to store i18n key/value pairs and modify or reload this data at runtime.

The Challenge

A developer inquired about using a database-driven solution to manage their i18n data effectively. The goal was to allow dynamic updates to internationalization strings without needing to redeploy the application. They wanted to integrate this solution with JSTL (JavaServer Pages Standard Tag Library) tags like <fmt:setlocale>, <fmt:bundle>, and <fmt:message>, but encountered difficulties, particularly around extending the ResourceBundle.

If you’re facing similar challenges, fear not! Below, we will discuss a practical approach to implement database-backed i18n in your Java web application.

Proposed Solution for Database-backed i18n

Step 1: Database Setup

First, you’ll need to create a simple database table to store your i18n data. The suggested structure consists of three columns:

  • language: The language code (e.g., en, fr, es).
  • key: A unique identifier for each string, typically in English (e.g., login.error.password.dup).
  • value: The translated string corresponding to the key.

Example Table Creation

CREATE TABLE translations (
    language VARCHAR(5),
    key VARCHAR(100),
    value TEXT,
    PRIMARY KEY (language, key)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;

Step 2: Populate the Database

Populate your translations table with key/value pairs. Here’s a short example:

INSERT INTO translations (language, key, value) VALUES
('en', 'login.error.password.dup', 'Password is already in use.'),
('fr', 'login.error.password.dup', 'Le mot de passe est déjà utilisé.');

Step 3: Adjusting ResourceBundle

To integrate your database with JSTL tags, you’ll need to extend the ResourceBundle class. This extended version can fetch strings directly from your database.

Sample Code

public class DatabaseResourceBundle extends ResourceBundle {
    private String language;

    public DatabaseResourceBundle(String language) {
        this.language = language;
    }

    @Override
    protected Object handleGetObject(String key) {
        // Logic to fetch value from database using language and key
        return getValueFromDatabase(language, key);
    }

    @Override
    public Enumeration<String> getKeys() {
        // Logic to return an Enumeration of keys
    }
}

Step 4: Integrating with JSTL

Once you have the custom ResourceBundle, you’ll need to integrate it with your JSTL tags. You can set up the localization context in your Servlet or JSP.

Sample Implementation

HttpSession session = request.getSession();
String language = "en"; // Logic to determine the language
ResourceBundle bundle = new DatabaseResourceBundle(language);
Locale locale = new Locale(language);
javax.servlet.jsp.jstl.core.Config.set(session, Config.FMT_LOCALIZATION_CONTEXT, new LocalizationContext(bundle, locale));

Additional Considerations

  • UTF-8 Support: Remember to ensure that your database supports UTF-8 characters. Both MySQL and Oracle enable you to set this up during database or table creation.
  • Caching: To enhance performance, consider implementing caching mechanisms for the database resource bundle.
  • UI for Translators: For a more advanced approach, build a user interface that allows translators to edit entries, including audit trails for changes.

Conclusion

Implementing database-backed internationalization in Java web applications can greatly improve flexibility, enabling real-time updates to your application’s language strings. By following the steps outlined above, you can create a robust system that integrates seamlessly with JSTL tags.

Feel free to experiment and adapt these solutions based on your application’s specific requirements. Happy coding!