π§© 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
- ποΈ Fetch data from a custom table (
app_fd_locale_msg_update) containing message keys, values, and locales. - π§ Construct a JSON payload dynamically for each record.
- π Retrieve CSRF token securely from the Joget runtime.
- π POST the data to Jogetβs internal message submission API endpoint.
- π§Ή 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
messageKeyandlocalebefore 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");
}
Top comments (0)