Multi-language Support

To enable support for multiple languages in your SØAD application, you can use the built-in internationalization (i18n) features. These features allow you to deliver localized content tailored to the user's language preferences.

Here’s an example of how to update the messages.properties file to include translations for various languages (using Transaction):

lang.py
from utils import render

from java.io import File
from java.nio.charset import StandardCharsets
from java.util import ResourceBundle
from com.google.common.io import Files

class Lang(object):
    def view(self, ctx):
        pass

    def get_content(self, ctx):
        request = ctx.getRequest()
        lang = request.getParameter("lang")
        if not lang:
            lang = "en"
        file_name = None
        if lang == "en":
            file_name = "messages.properties"
        elif lang == "ms":
            file_name = "messages_ms.properties"
        elif lang == "zh":
            file_name = "messages_zh.properties"
        if file_name:
            file_path = request.getServletContext().getRealPath("/WEB-INF/classes/%s" % file_name)
            file = File(file_path)
            if not file.exists():
                file.createNewFile()
            if file_path:
                content = Files.asCharSource(file, StandardCharsets.UTF_8).read()
                ctx.go_to = render.as_string(ctx, content)
                return
        ctx.go_to = render.as_string(ctx, "")

    def save(self, ctx):
        request = ctx.getRequest()
        lang = request.getParameter("lang")
        content = request.getParameter("content")
        file = None
        if lang == "en":
            file = "messages.properties"
        elif lang == "ms":
            file = "messages_ms.properties"
        elif lang == "zh":
            file = "messages_zh.properties"
        if file and content:
            pfile = request.getServletContext().getRealPath("/WEB-INF/classes/%s" % file)
            if pfile:
                Files.asCharSink(File(pfile), StandardCharsets.UTF_8).write(content)
                ctx.go_to = render.as_json(ctx, {"success": True})
                ResourceBundle.clearCache()
                return
        ctx.go_to = render.as_json(ctx, {"success": False})
lang.html
<!doctype html>
<html lang="en">

<head>
    <meta charset="utf-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1, viewport-fit=cover" />
    <meta http-equiv="X-UA-Compatible" content="ie=edge" />
    <title>Manage Language</title>
    <link href="https://cdn.jsdelivr.net/npm/[email protected]/dist/css/bootstrap.min.css" rel="stylesheet">
    <style>
    #my-editor {
        height: 750px;
        width: 100%;
    }
    </style>
</head>

<body>
    <div id="s0-crud-body">
    <div class="row">
        <div class="col">
        <div class="card">
            <div class="card-header">
            <div class="d-flex justify-content-between">
                <h5 class="heading mb-0">Manage Language</h5>
                <div>
                    <a class="btn btn-default btn-sm b_sel" id="b_sel_en" data-val="en" href="#">
                        English
                    </a>
                    <a class="btn btn-default btn-sm b_sel" data-val="ms" href="#">
                        Bahasa Melayu
                    </a>
                    <a class="btn btn-default btn-sm b_sel" data-val="zh" href="#">
                        Mandarin
                    </a>
                </div>
            </div>
            </div>
            <div class="card-body">
            <div id="my-editor"></div>
            </div>
        </div>
        </div>
    </div>
    <div class="toast-container position-fixed bottom-0 end-0 p-3">
        <div id="liveToast" class="toast" role="alert" aria-live="assertive" aria-atomic="true" data-bs-delay="5000" data-bs-autohide="true">
        <div class="toast-body">
            <center>Save Success</center>
        </div>
        </div>
    </div>
    </div>
    <style>
    </style>
    <!-- JS -->
    <script src="https://code.jquery.com/jquery-3.6.0.min.js"></script>
    <script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/js/bootstrap.bundle.min.js"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/ace/1.36.2/ace.min.js"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/ace/1.36.2/mode-properties.min.js"></script>
    <script>
    var currentLang = "en";

    $(function() {
        $(".b_sel").click(function() {
        if (!window.editorCode.session.getUndoManager().isClean()) {
            alert("Unsaved Changes");
            return;
        }
        doLoad($(this).attr("data-val"));
        $(".b_sel").each(function(i, elem) {
            $(elem).removeClass("btn-primary");
        });
        $(this).addClass("btn-primary");
        });
        $("#b_sel_en").click();
        window.myToast = new bootstrap.Toast(document.getElementById("liveToast"), {})
    });

    function doLoad(lang) {
        $.get("{{ctxPath}}/t/example/lang/get_content", {"lang":lang}, function(data) {
        currentLang = lang;
        window.editorCode.session.setValue(data);
        window.editorCode.session.getUndoManager().reset();
        }, "text");  
    }

    function doSave() {
        var opaque = window.editorCode.getValue();
        $.post("{{ctxPath}}/t/example/lang/save", {"lang":currentLang, "content":opaque}, function(data) {
        if (data.success) {
            window.myToast.show();
            window.editorCode.session.getUndoManager().reset();
        }
        });  
    }

    window.editorCode = ace.edit("my-editor");
    window.editorCode.session.setMode("ace/mode/properties");
    window.editorCode.setAutoScrollEditorIntoView(true);
    window.editorCode.setShowPrintMargin(false);
    window.editorCode.session.getUndoManager().reset();
    window.editorCode.commands.addCommand({
        name: 'save',
        bindKey: {win: "Ctrl-S", "mac": "Cmd-S"},
        exec: function(editor) {
        doSave(false);
        }
    });
    </script>
</body>

</html>

To support multiple languages, you need to create separate properties files for each language. Here’s how you can structure your properties files:

messages.properties
# Messages in English
greeting=Hello
farewell=Goodbye
messages_ms.properties
# Messages in Malay
greeting=Selamat datang
farewell=Selamat tinggal
messages_zh.properties
# Messages in Chinese
greeting=你好
farewell=再见

Tips

If you're using a containerized environment, consider mounting the properties files to the /app/conf/ directory. This directory is included in the classpath.