All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.nuiton.i18n.I18nDefinitionFile Maven / Gradle / Ivy

There is a newer version: 4.0-beta-27
Show newest version
package org.nuiton.i18n;

/*-
 * #%L
 * I18n :: Runtime
 * %%
 * Copyright (C) 2018 Code Lutin, Ultreia.io
 * %%
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License as
 * published by the Free Software Foundation, either version 3 of the
 * License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Lesser Public License for more details.
 *
 * You should have received a copy of the GNU General Lesser Public
 * License along with this program.  If not, see
 * .
 * #L%
 */

import com.google.common.base.Joiner;
import com.google.common.collect.ArrayListMultimap;
import com.google.common.collect.HashMultimap;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Multimap;
import io.ultreia.java4all.util.SortedProperties;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.io.IOUtils;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.nuiton.version.Version;
import org.nuiton.version.Versions;

import java.io.BufferedWriter;
import java.io.Closeable;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.Reader;
import java.net.MalformedURLException;
import java.net.URL;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.Date;
import java.util.Locale;
import java.util.Map;
import java.util.Objects;
import java.util.Optional;
import java.util.Properties;
import java.util.Set;
import java.util.TreeMap;
import java.util.stream.Collectors;

/**
 * Created by tchemit on 09/07/17.
 *
 * @author Tony Chemit - [email protected]
 * @since 3.7
 */
public class I18nDefinitionFile implements Closeable {

    /** Logger. */
    private static final Logger log = LogManager.getLogger(I18nDefinitionFile.class);

    private static final String ENTRY_VERSION = "version";
    private static final String ENTRY_ENCODING = "encoding";
    private static final String ENTRY_LOCALES = "locales";
    private static final String ENTRY_TEMPLATE_EXTENSION = "template.extension";
    private static final String ENTRY_TEMPLATE_LIST = "template.list";
    private static final String ENTRY_BUNDLE_PREFIX = "bundles.";
    private static final String ENTRY_KEY_CATEGORIES = "key.categories";
    private static final String SUFFIX_DEFINITION_PROPERTIES = "-definition.properties";
    private final String name;
    private final Version version;
    private final Set locales;
    private final String templateExtension;
    private final Set templateList;
    private final Map keyCategories;
    private final Multimap bundles;
    private final Map propertiesCache;
    private final Map contentCache;
    private String urlPrefix;
    private Charset encoding;
    private HashMultimap translationKeys;

    public I18nDefinitionFile(String name,
                              Charset encoding,
                              Version version,
                              Set locales,
                              String templateExtension,
                              Set templateList,
                              Map keyCategories,
                              Multimap bundles) {
        this.name = name;
        this.encoding = encoding;
        this.version = version;
        this.locales = locales;
        this.templateExtension = templateExtension;
        this.templateList = templateList;
        this.keyCategories = keyCategories;
        this.bundles = bundles;
        this.propertiesCache = new TreeMap<>();
        this.contentCache = new TreeMap<>();

    }

    public I18nDefinitionFile(URL url) throws IOException, NullPointerException {
        Objects.requireNonNull(url);
        propertiesCache = new TreeMap<>();
        contentCache = new TreeMap<>();
        InputStream inputStream = url.openStream();
        if (inputStream == null) {
            throw new IllegalArgumentException(String.format("Url [%s] does not exists.", url));
        }

        String i18nBundleUrlPrefix = url.toString().replace(SUFFIX_DEFINITION_PROPERTIES, "");
        name = i18nBundleUrlPrefix.substring(url.toString().lastIndexOf("/") + 1);
        urlPrefix = i18nBundleUrlPrefix.substring(0, url.toString().lastIndexOf("/") + 1);

        Properties i18nProperties = loadProperties(url);

        encoding = Charset.forName(i18nProperties.getProperty(ENTRY_ENCODING));
        version = Versions.valueOf(i18nProperties.getProperty(ENTRY_VERSION));
        templateExtension = i18nProperties.getProperty(ENTRY_TEMPLATE_EXTENSION);
        String templatesListStr = i18nProperties.getProperty(ENTRY_TEMPLATE_LIST);
        ImmutableSet.Builder templateNamesBuilder = ImmutableSet.builder();
        if (templatesListStr != null) {
            for (String templateName : templatesListStr.split(",")) {
                templateNamesBuilder.add(templateName.trim());
            }
        }
        templateList = templateNamesBuilder.build();
        String keyCategoriesStr = i18nProperties.getProperty(ENTRY_KEY_CATEGORIES);
        ImmutableMap.Builder keyCategoriesBuilder = ImmutableMap.builder();
        if (keyCategoriesStr != null) {
            for (String templateName : keyCategoriesStr.split(",")) {
                String[] parts = templateName.split("=");
                keyCategoriesBuilder.put(parts[0].trim(), parts[1].trim());
            }
        }
        keyCategories = keyCategoriesBuilder.build();

        ImmutableSet.Builder localesBuilder = ImmutableSet.builder();
        bundles = ArrayListMultimap.create();
        for (String localeStr : ((String) i18nProperties.get(ENTRY_LOCALES)).split(",")) {
            String[] split = localeStr.split("_");
            Locale locale = new Locale(split[0], split[1]);
            localesBuilder.add(locale);
            String bundleList = (String) i18nProperties.get(ENTRY_BUNDLE_PREFIX + locale.getLanguage() + "_" + locale.getCountry());
            for (String bundle : bundleList.split(",")) {
                bundles.put(locale, bundle.trim());
            }
        }
        locales = localesBuilder.build();

    }

    public URL getDefinitionFileURL() {
        return url(urlPrefix + name + SUFFIX_DEFINITION_PROPERTIES);
    }

    public URL getMainBundleURL(Locale locale) {
        return url(urlPrefix + String.format("%s_%s_%s.properties", name, locale.getLanguage(), locale.getCountry()));
    }

    public Map getKeyCategories() {
        return keyCategories;
    }

    public Optional getTemplateURL(String templateName, Locale locale) {
        URL url;
        if (templateName.startsWith("classpath:")) {
            String urlStart = String.format("/%s/%s_%s.%s", templateExtension, templateName.substring("classpath:".length()), locale.getLanguage(), templateExtension);
            url = getClass().getResource(urlStart);
            if (url == null && locale.equals(locales.iterator().next())) {
                urlStart = String.format("/%s/%s.%s", templateExtension, templateName.substring("classpath:".length()), templateExtension);
                url = Objects.requireNonNull(getClass().getResource(urlStart));
            }
        } else {
            url = url(urlPrefix + String.format("%s/%s_%s.%s", templateExtension, templateName, locale.getLanguage(), templateExtension));
        }


        try {
            InputStream inputStream = url.openStream();
            try {
                if (inputStream == null) {
                    url = null;
                }
            } finally {
                if (inputStream != null) {
                    inputStream.close();
                }
            }

        } catch (IOException e) {
            throw new IllegalStateException(e);
        }
        return Optional.ofNullable(url);
    }

    public URL getBundleURL(String bundleName) {
        return getClass().getClassLoader().getResource(bundleName);
    }

    private URL url(String suffix) {
        try {
            return new URL(suffix);
        } catch (MalformedURLException e) {
            throw new IllegalStateException(e);
        }
    }

    private Properties loadProperties(URL url) {
        Objects.requireNonNull(url);

        return propertiesCache.computeIfAbsent(url.toString(), u -> {
            log.info("Loading " + url);
            Properties p = new Properties();
            try (Reader inputStream = new InputStreamReader(url.openStream(), encoding == null ? StandardCharsets.UTF_8 : encoding)) {
                p.load(inputStream);
            } catch (IOException e) {
                throw new IllegalStateException("Can't load properties at: " + url, e);
            }
            return p;
        });
    }


    private String loadContent(URL url) {
        Objects.requireNonNull(url);

        return contentCache.computeIfAbsent(url.toString(), u -> {
            log.info("Loading " + url);
            try {
                return IOUtils.toString(url, encoding);
            } catch (IOException e) {
                throw new IllegalStateException("Can't load properties at: " + url, e);
            }
        });
    }

    public void store(File directory) throws IOException {

        Joiner joiner = Joiner.on(",");

        SortedProperties p = new SortedProperties();
        p.put(ENTRY_VERSION, version.toString());
        p.put(ENTRY_ENCODING, encoding.toString());
        p.put(ENTRY_LOCALES, joiner.join(locales));
        if (templateExtension != null) {
            p.put(ENTRY_TEMPLATE_EXTENSION, templateExtension);
        }
        if (CollectionUtils.isNotEmpty(templateList)) {
            p.put(ENTRY_TEMPLATE_LIST, joiner.join(templateList));
        }
        if (!keyCategories.isEmpty()) {
            p.put(ENTRY_KEY_CATEGORIES, joiner.join(keyCategories.entrySet().stream().map(e -> e.getKey() + "=" + e.getValue()).collect(Collectors.toList())));
        }
        for (Locale locale : locales) {
            p.put("bundles." + locale.getLanguage() + "_" + locale.getCountry(), joiner.join(bundles.get(locale)));
        }

        Path target = directory.toPath().resolve(String.format("%s%s", name, SUFFIX_DEFINITION_PROPERTIES));
        log.info("Store definition file to " + target);

        try (BufferedWriter writer = Files.newBufferedWriter(target)) {

            p.store(writer, "Generated by " + getClass().getName() + " at " + new Date());
        }
    }

    public String getName() {
        return name;
    }

    public Version getVersion() {
        return version;
    }

    public Set getLocales() {
        return locales;
    }

    public String getTemplateExtension() {
        return templateExtension;
    }

    public Set getTemplateList() {
        return templateList;
    }

    public Properties getMainBundle(Locale locale) {
        return loadProperties(getMainBundleURL(locale));
    }

    public HashMultimap getTranslationKeys() {
        if (translationKeys == null) {
            translationKeys = HashMultimap.create();
            for (Locale locale : locales) {
                Properties properties = loadProperties(getMainBundleURL(locale));
                properties.keySet().forEach(o -> {
                    translationKeys.put(null, (String) o);
                    translationKeys.put(locale, (String) o);
                });
            }
        }
        return translationKeys;
    }

    public Multimap getBundles() {
        return bundles;
    }

    public String getTemplateContent(String templateName, Locale locale) {
        return getTemplateURL(templateName, locale).map(this::loadContent).orElse(null);
    }

    @Override
    public void close() {
        contentCache.clear();
        propertiesCache.clear();
        translationKeys = null;
    }

    public Charset getEncoding() {
        return encoding;
    }

    public String getMainBundleFilename(Locale locale) {
        return String.format("%s_%s_%s.properties", getName(), locale.getLanguage(), locale.getCountry());
    }

    public String getTemplateFilename(Locale locale, String templateName) {
        return String.format("%s/%s_%s.%s", getTemplateExtension(), templateName, locale.getLanguage(), getTemplateExtension());
    }

    public Properties getBundle(String bundleName) {
        return loadProperties(getBundleURL(bundleName));
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy