Messages_en.properties implementation to database

Hello, is in Jmix some way to make for example messages_en.properties shown as variables in database table? I want to do something like ‘Translator’ table in database where each variables of each class in application will have its own row in table with columns like | trans_en | trans_de | trans_pl | and so on. This table would also have screen in application so it gives the administrator option to change translation of some variables if they would need it in their company, it often occurs in technical language.

So in short description: how to merge vriables descriptions from messages_$.properties to database and how to read it if its possible?

One way of doing this is to intercept loading of all messages for a specific language. It’s done by the Resources implementation used by JmixMessageSource bean created by the framework. So you can define your own JmixMessageSource bean and provide a custom Resources implementation to it.

For example:

@SpringBootApplication
public class AppApplication {
    // ...

    @Bean(name = "customMessageSource")
    @Primary
    MessageSource messageSource(JmixModules modules, ApplicationContext applicationContext) {
        CustomResources customResources = new CustomResources(applicationContext);
        return new JmixMessageSource(modules, customResources);
    }
}
package com.company.app;

import io.jmix.core.Resources;
import org.apache.commons.io.IOUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.core.io.InputStreamResource;
import org.springframework.core.io.Resource;

import javax.annotation.Nullable;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.nio.charset.StandardCharsets;

public class CustomResources implements Resources {

    private ApplicationContext applicationContext;
    private Resources delegate;

    @Autowired
    public CustomResources(ApplicationContext applicationContext) {
        this.applicationContext = applicationContext;
        this.delegate = applicationContext.getBean(Resources.class);
    }

    @Override
    public Resource getResource(String location) {
        byte[] data = loadResourceFromCustomLocation(location);
        if (data.length > 0) {
            return new InputStreamResource(new ByteArrayInputStream(data));
        } else {
            return delegate.getResource(location);
        }
    }

    private byte[] loadResourceFromCustomLocation(String location) {
        // load messages for a specific language from the database and 
        // optionally merge with a content loaded by delegate.getResource(location)
        return new byte[0];
    }

    @Override
    public ClassLoader getClassLoader() {
        return delegate.getClassLoader();
    }

    @Nullable
    @Override
    public InputStream getResourceAsStream(String location) {
        try {
            Resource resource = getResource(location);
            if (resource.exists())
                return resource.getInputStream();
            else
                return null;
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }

    @Nullable
    @Override
    public String getResourceAsString(String location) {
        try (InputStream stream = getResourceAsStream(location)) {
            if (stream == null)
                return null;
            return IOUtils.toString(stream, StandardCharsets.UTF_8);

        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }
}

Another option is to redefine the JmixMessageSource bean itself and override its methods loading individual messages. The new bean should be defined as follows:

@SpringBootApplication
public class AppApplication {
    // ...

    @Bean(name = "customMessageSource")
    @Primary
    MessageSource messageSource(JmixModules modules, Resources resources) {
        return new CustomMessageSource(modules, resources);
    }
}

Regards,
Konstantin