Generating Multilingual HTML Reports with Attachment Download Links in Joget
Generating Multilingual HTML Reports with Attachment Download Links in Joget Creating customized executive report summaries in Joget DX often requires more than simple database lists. Real-world business reports frequently need to join multiple tables, translate status labels based on the user's active locale ( #platform.currentLocale# ), and generate secure file download links for form attachments. In this guide, we'll build a Java/BeanShell script that queries main records and history logs, resolves internationalization ( i18n ) message keys dynamically, and generates interactive HTML reports embedded with secure attachment links. Key Components Dynamic i18n Translation: Uses AppUtil.processHashVariable("#i18n.key#", null, null, null) to convert database status codes into localized text matching the user's language setting. File Attachment Links: Formats secure file download URLs ( /jw/web/client/app/{appId}/{version}/form/download/{tableName}/{recordId}/{fileName} ) so users can open uploaded documents directly from the report summary. Multi-Table SQL Join: Merges main request details, audit transaction history, and custom review tables into a clean HTML document layout. The BeanShell Script Place this code inside a BeanShell Form Bounding Box or an HTML Report Generator tool step: import java.sql.Connection ; import java.sql.PreparedStatement ; import java.sql.ResultSet ; import java.net.URLEncoder ; import javax.sql.DataSource ; import org.joget.apps.app.service.AppUtil ; import org.joget.apps.app.model.AppDefinition ; import org.joget.commons.util.LogUtil ; // Helper: Resolve i18n hash variables dynamically public String getLocalizedText ( String messageKey ) { if ( messageKey == null || messageKey . isEmpty ()) return "" ; String hashVariable = "#i18n." + messageKey + "#" ; return AppUtil . processHashVariable ( hashVariable , null , null , null ); } String recordId = "#requestParam.id#" ; if ( recordId == null || recordId . trim (). isEmpty ()) { return "<di