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

de.frachtwerk.essencium.backend.service.translation.TranslationService Maven / Gradle / Ivy

/*
 * Copyright (C) 2024 Frachtwerk GmbH, Leopoldstraße 7C, 76133 Karlsruhe.
 *
 * This file is part of essencium-backend.
 *
 * essencium-backend 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.
 *
 * essencium-backend 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 Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with essencium-backend. If not, see .
 */

package de.frachtwerk.essencium.backend.service.translation;

import static de.frachtwerk.essencium.backend.service.translation.TranslationFileService.TRANSLATION_FILE_CACHE;

import de.frachtwerk.essencium.backend.model.Translation;
import de.frachtwerk.essencium.backend.repository.TranslationRepository;
import jakarta.validation.constraints.NotNull;
import java.util.Collection;
import java.util.LinkedList;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Optional;
import java.util.Set;
import java.util.SortedMap;
import java.util.TreeMap;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cache.annotation.CacheEvict;
import org.springframework.data.util.Pair;
import org.springframework.stereotype.Service;

@Service
public class TranslationService {

  private final TranslationRepository translationRepository;

  @Autowired
  public TranslationService(@NotNull final TranslationRepository translationRepository) {
    this.translationRepository = translationRepository;
  }

  public Collection getTranslations() {
    return translationRepository.findAll();
  }

  public Collection getTranslations(@NotNull final Locale locale) {
    return translationRepository.findAllByLocale(locale);
  }

  public Optional translate(String key, Locale locale) {
    return translateVariant(key, locale, 0);
  }

  // Inspired https://kazupon.github.io/vue-i18n/guide/pluralization.html
  public Optional translateVariant(String key, Locale locale, int variantIndex) {
    final var translation = translationRepository.findByKeyAndLocale(key, locale);
    return translation
        .map(Translation::getValue)
        .flatMap(s -> Stream.of(s.split("\\|")).skip(variantIndex).findFirst())
        .map(String::trim);
  }

  @CacheEvict(value = TRANSLATION_FILE_CACHE, allEntries = true)
  public Translation updateTranslation(@NotNull final Translation translation) {
    return translationRepository.save(translation);
  }

  @CacheEvict(value = TRANSLATION_FILE_CACHE, allEntries = true)
  public Collection updateTranslations(
      @NotNull final Collection translations) {
    return translationRepository.saveAll(translations);
  }

  @CacheEvict(value = TRANSLATION_FILE_CACHE, allEntries = true)
  public Collection updateTranslation(
      @NotNull final Locale locale, Map translationObjects) {
    final Collection> keyValueList = parseKeyValueList("", translationObjects);

    final Collection translations =
        keyValueList.stream()
            .map(
                translationPair ->
                    new Translation(
                        locale, translationPair.getFirst(), translationPair.getSecond()))
            .collect(Collectors.toCollection(LinkedList::new));
    return translationRepository.saveAll(translations);
  }

  public void deleteTranslation(String key) {
    translationRepository.deleteAll(translationRepository.findAllByKey(key));
  }

  public SortedMap> getTranslationsGroupedByKey() {
    return translationRepository.findAll().stream()
        .collect(Collectors.groupingBy(Translation::getKey, TreeMap::new, Collectors.toList()));
  }

  public Map> getTranslationMaps() {
    var translations = translationRepository.findAll();

    return translations.stream()
        .collect(
            Collectors.groupingBy(
                Translation::getKey,
                Collectors.groupingBy(
                    Translation::getLocale,
                    Collectors.mapping(
                        Translation::getValue, Collectors.reducing("", (o1, o2) -> o2)))));
  }

  public Collection getAvailableLocales() {
    return translationRepository.findDistinctLocale();
  }

  public Map> getTranslatedAvailableLocales() {
    Set availableLocales = translationRepository.findDistinctLocale();
    return availableLocales.stream()
        .flatMap(l1 -> availableLocales.stream().map(l2 -> Pair.of(l1, l2)))
        .collect(
            Collectors.groupingBy(
                Pair::getFirst,
                Collectors.toMap(
                    Pair::getSecond, p -> p.getFirst().getDisplayLanguage(p.getSecond()))));
  }

  @NotNull
  private Collection> parseKeyValueList(
      final String headKey, final Map translationObjects) {
    final var keyValueList = new LinkedList>();

    translationObjects.forEach(
        (key, object) -> {
          if (object instanceof final String value) {
            final String translationKey = headKey + key;
            keyValueList.add(Pair.of(translationKey, value));
          } else if (object instanceof Map) {
            final String newHeadKey = headKey + key + ".";
            //noinspection unchecked
            final Map translationMap = (Map) object;
            keyValueList.addAll(parseKeyValueList(newHeadKey, translationMap));
          } else {
            throw new IllegalArgumentException(
                "Unable to parse translation map. Only Strings are allowed");
          }
        });

    return keyValueList;
  }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy