I18nController.java

/**
 * @file I18nController.java
 * @brief Controller that provides translations for different locales using message resource files.
 */

package com.deusto.deuspotify.controllers;

import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.MessageSource;
import org.springframework.core.io.Resource;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.bind.annotation.RequestParam;

import java.io.IOException;
import java.util.*;
import java.util.stream.Collectors;
import java.io.InputStreamReader;
import java.io.Reader;

/**
 * @class I18nController
 * @brief REST controller for retrieving internationalized message strings based on locale.
 */
@RestController
public class I18nController {

    private final MessageSource messageSource;

    @Value("classpath:messages.properties")
    private Resource messagesProperties;

    @Value("classpath:messages_es.properties")
    private Resource messagesEsProperties;

    @Value("classpath:messages_en.properties")
    private Resource messagesEnProperties;

    @Value("classpath:messages_eu.properties")
    private Resource messagesEuProperties;

    /**
     * @brief Constructor for I18nController.
     * @param messageSource Spring's message source for resolving messages.
     */
    public I18nController(MessageSource messageSource) {
        this.messageSource = messageSource;
    }

    /**
     * @brief Returns translated key-value pairs based on the provided language.
     * @param lang Language code ("es", "en", "eu"). Defaults to "es".
     * @return A map of message keys and their localized translations.
     * @throws IOException If there's an error reading the property files.
     */
    @GetMapping("/api/i18n")
    public Map<String, String> getTranslations(@RequestParam(value = "lang", required = false) String lang) throws IOException {
        Locale locale = switch (lang != null ? lang : "es") {
            case "en" -> Locale.ENGLISH;
            case "eu" -> new Locale("eu");
            default -> new Locale("es");
        };

        Map<String, String> allKeys = loadAllPropertiesKeys();

        return allKeys.entrySet().stream()
                .collect(Collectors.toMap(
                        Map.Entry::getKey,
                        entry -> messageSource.getMessage(entry.getKey(), null, locale)
                ));
    }

    /**
     * @brief Loads all keys from all localized message property files.
     * @return A map containing keys from all message files.
     * @throws IOException If any file fails to load.
     */
    private Map<String, String> loadAllPropertiesKeys() throws IOException {
        Map<String, String> keys = new HashMap<>();
        keys.putAll(loadPropertiesKeys(messagesProperties));
        keys.putAll(loadPropertiesKeys(messagesEsProperties));
        keys.putAll(loadPropertiesKeys(messagesEnProperties));
        keys.putAll(loadPropertiesKeys(messagesEuProperties));
        return keys;
    }

    /**
     * @brief Loads keys and values from a single message property file.
     * @param resource The resource pointing to a properties file.
     * @return A map of the keys and values in the file.
     * @throws IOException If the file cannot be read.
     */
    private Map<String, String> loadPropertiesKeys(Resource resource) throws IOException {
        Map<String, String> keys = new HashMap<>();
        try (Reader reader = new InputStreamReader(resource.getInputStream())) {
            Properties properties = new Properties();
            properties.load(reader);
            for (String key : properties.stringPropertyNames()) {
                keys.put(key, properties.getProperty(key));
            }
        }
        return keys;
    }
}