DEV Community

Explorer
Explorer

Posted on • Edited on

🌐 Update Joget Locale Messages with 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


πŸ’‘ 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.

Source 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");
}

// ====================
// 1. Prepare and Fetch Data
// ====================
try {
    // Get current record ID from form
    String currentId = "#form.locale_msg_update.id#";  

    // Fetch values from database
    String appId = "";
    String locale = "";
    String messageKey = "";
    String messageValue = "";
    String 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 + ", message=" + messageValue);

    // ====================
    // 2. Build API Data
    // ====================
    String appVersion = "#appDef.version#";   // App Version
    String apiUrl = "#request.baseURL#" + "/web/json/console/app/" + appId + "/"+app_version+"/message/submit";

    LogUtil.info("apiUrl =" , apiUrl);

    // Construct ID as messageKey + "_" + locale
    String messageId = messageKey + "_" + locale;

    // Construct JSON payload
    //String jsonPayload = "[{\"id\":\"" + messageId + "\",\"key\":\"" + messageKey + "\",\"value\":\"" + messageValue + "\"}]";


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



    LogUtil.info("jsonPayload", jsonPayload);

        // Get CSRF parameter name and value
    String csrfParam = SecurityUtil.getCsrfTokenName();             // e.g., "_csrf"
    String csrfValue = SecurityUtil.getCsrfTokenValue(request);     // actual token

    LogUtil.info("csrfParam", csrfParam);
    LogUtil.info("csrfValue", csrfValue);

    // URL-encode form parameters
    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");


    // ====================
    // 3. Send API Request
    // ====================
    HttpURLConnection conn = null;
    BufferedReader reader = null;

    try {
        URL url = new URL(apiUrl);
        conn = (HttpURLConnection) url.openConnection();
        conn.setRequestMethod("POST");
        conn.setDoOutput(true);

        // Set headers
        conn.setRequestProperty("Content-Type", "application/x-www-form-urlencoded");
        conn.setRequestProperty("Cookie", "JSESSIONID=" + request.getSession().getId());
        conn.setRequestProperty("Content-Length", String.valueOf(postData.getBytes("UTF-8").length));
        conn.setRequestProperty(csrfParam, csrfValue); // Also send CSRF in header
        conn.setRequestProperty("Referer", "#request.baseURL#/web/console/app/" + appId + "/" + app_version + "/form/builder/locale_msg_update");
        conn.setRequestProperty("Origin", "#request.baseURL#");
        conn.setRequestProperty("X-Requested-With", "XMLHttpRequest");

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

        // Read response
        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("BeanShellAPI", "API Response: " + response.toString());


        // =================== Delete the Record after Processing ======================
            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("BeanShellAPI", "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("BeanShellAPI", e, "Error in script");
}
Enter fullscreen mode Exit fullscreen mode

Top comments (0)