DEV Community

Explorer
Explorer

Posted on

🌐Locale Message Updates in Joget Using Beanshell and REST API

🧩 Overview

Managing multiple language packs in Joget can become tedious when you frequently update UI text or labels.
This blog demonstrates how to automate locale message updates by fetching localized entries from a database table and submitting them to the Joget message API — all within a Beanshell script.

Goal: Automatically sync custom message entries (key–value–locale) into Joget’s system locale messages.
⚙️ Technology Used: Beanshell, Joget REST API, CSRF-secured POST request.


⚙️ How It Works

  1. 🗄️ Fetch data from a custom table (app_fd_locale_msg_update) containing message keys, values, and locales.
  2. 🧠 Construct a JSON payload dynamically for each record.
  3. 🔐 Retrieve CSRF token securely from the Joget runtime.
  4. 🌍 POST the data to Joget’s internal message submission API endpoint.
  5. 🧹 Clean up by deleting the processed record from the table.

This makes localization updates dynamic, safe, and fully automated — without manual form submissions.


💻 Full Code

import java.net.URL;
import java.net.HttpURLConnection;
import java.io.OutputStream;
import java.io.InputStreamReader;
import java.io.BufferedReader;
import java.net.URLEncoder;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import javax.sql.DataSource;
import org.joget.apps.app.service.AppUtil;
import org.joget.commons.util.LogUtil;
import org.joget.commons.util.SecurityUtil;

public static String escapeJson(String value) {
    if (value == null) return "";
    return value.replace("\\", "\\\\")
                .replace("\"", "\\\"")
                .replace("\n", "\\n")
                .replace("\r", "\\r")
                .replace("\t", "\\t");
}

try {
    // 1️⃣ Fetch current record ID
    String currentId = "#form.locale_msg_update.id#";  

    // 2️⃣ Prepare database connection
    String appId = "", locale = "", messageKey = "", messageValue = "", app_version = "";
    DataSource ds = (DataSource) AppUtil.getApplicationContext().getBean("setupDataSource");
    Connection con = ds.getConnection();

    try {
        String sql = "SELECT * FROM app_fd_locale_msg_update WHERE id = ?";
        PreparedStatement ps = con.prepareStatement(sql);
        ps.setString(1, currentId);
        ResultSet rs = ps.executeQuery();

        if (rs.next()) {
            appId = rs.getString("c_app_id");
            locale = rs.getString("c_locale");
            messageKey = rs.getString("c_messageKey");
            messageValue = rs.getString("c_message");
            app_version = rs.getString("c_app_version");
        }

        rs.close();
        ps.close();
    } finally {
        if (con != null) con.close();
    }

    LogUtil.info("DB Values", "appId=" + appId + ", locale=" + locale + ", key=" + messageKey);

    // 3️⃣ Build API request
    String appVersion = "#appDef.version#";
    String apiUrl = "#request.baseURL#/web/json/console/app/" + appId + "/" + app_version + "/message/submit";

    String messageId = messageKey + "_" + locale;
    String jsonPayload = "[{\"id\":\"" + escapeJson(messageId) + 
                        "\",\"key\":\"" + escapeJson(messageKey) + 
                        "\",\"value\":\"" + escapeJson(messageValue) + "\"}]";

    // 4️⃣ Include CSRF token
    String csrfParam = SecurityUtil.getCsrfTokenName();
    String csrfValue = SecurityUtil.getCsrfTokenValue(request);

    String postData = "appId=" + URLEncoder.encode(appId, "UTF-8")
                    + "&appVersion=" + URLEncoder.encode(appVersion, "UTF-8")
                    + "&locale=" + URLEncoder.encode(locale, "UTF-8")
                    + "&data=" + URLEncoder.encode(jsonPayload, "UTF-8")
                    + "&" + csrfParam + "=" + URLEncoder.encode(csrfValue, "UTF-8");

    // 5️⃣ Send POST request
    HttpURLConnection conn = null;
    BufferedReader reader = null;

    try {
        URL url = new URL(apiUrl);
        conn = (HttpURLConnection) url.openConnection();
        conn.setRequestMethod("POST");
        conn.setDoOutput(true);
        conn.setRequestProperty("Content-Type", "application/x-www-form-urlencoded");
        conn.setRequestProperty("Cookie", "JSESSIONID=" + request.getSession().getId());
        conn.setRequestProperty(csrfParam, csrfValue);
        conn.setRequestProperty("X-Requested-With", "XMLHttpRequest");

        OutputStream os = conn.getOutputStream();
        os.write(postData.getBytes("UTF-8"));
        os.flush();
        os.close();

        InputStreamReader isr = conn.getResponseCode() >= 400
            ? new InputStreamReader(conn.getErrorStream(), "UTF-8")
            : new InputStreamReader(conn.getInputStream(), "UTF-8");

        reader = new BufferedReader(isr);
        String line;
        StringBuilder response = new StringBuilder();
        while ((line = reader.readLine()) != null) response.append(line);

        LogUtil.info("LocaleUpdater", "API Response: " + response.toString());

        // 6️⃣ Delete processed record
        try {
            con = ds.getConnection();
            String deleteSql = "DELETE FROM app_fd_locale_msg_update WHERE id = ?";
            PreparedStatement deletePs = con.prepareStatement(deleteSql);
            deletePs.setString(1, currentId);
            int deletedRows = deletePs.executeUpdate();
            LogUtil.info("LocaleUpdater", "Deleted rows: " + deletedRows);
            deletePs.close();
        } finally {
            if (con != null) con.close();
        }

    } finally {
        try { if (reader != null) reader.close(); } catch(Exception e) {}
        if (conn != null) conn.disconnect();
    }

} catch(Exception e) {
    LogUtil.error("LocaleUpdater", e, "Error in locale message update script");
}
Enter fullscreen mode Exit fullscreen mode

💡 Example Use Cases

  • 🌍 Multilingual UI Management: Automatically add or modify translated strings without opening the message manager.
  • ⚙️ Bulk Localization Sync: Use this script in a scheduled process or post-form submission trigger.
  • 🧾 Translation Review Workflow: Integrate it with a form where translators submit updates that get pushed into Joget once approved.

🧰 Customization Tips

✅ Modify the source table name if your schema differs.
✅ Add WHERE clauses or filters to handle batch updates.
✅ If used with multiple locales, consider looping through a list of pending records.
✅ You can return a JSON status to display on form submission (optional).


🚀 Key Benefits

  • ⚙️ Automation: Removes manual locale message maintenance.
  • 🧠 Accuracy: Ensures translation entries stay consistent across versions.
  • 🔄 Integration: Works with internal Joget CSRF-secured APIs.
  • 📦 Reusability: Plug this into any app that maintains localized text data.

🔒 Security Note

  • Always use SecurityUtil.getCsrfTokenValue(request) to include the valid CSRF token.
  • Avoid exposing internal API URLs publicly.
  • Validate messageKey and locale before submission to prevent injection attacks.

🏁 Final Thoughts

This automation provides a robust way to manage dynamic language updates directly from your form data into the Joget platform — no manual clicks required.
By coupling Beanshell with Joget’s secure internal API, you gain a powerful extension point for localization and continuous deployment of translated UI assets.

Top comments (0)