Opetushallitus/eperusteet-frontend-utils

View on GitHub
vue/src/stores/kieli.ts

Summary

Maintainability
B
4 hrs
Test Coverage
import { createLogger } from '../utils/logger';
import { Kieli } from '../tyypit';
import _ from 'lodash';
import VueI18n from 'vue-i18n';
import Vue, { VueConstructor } from 'vue';
import moment from 'moment';
import VueCompositionApi, { computed, reactive } from '@vue/composition-api';
import { updateRelativeTime } from '../plugins/aikaleima';
import { Computed } from '../utils/interfaces';

Vue.use(VueCompositionApi);

declare module 'vue/types/vue' {
  interface Vue {
    $locale: Computed<string>;
    $slang: Computed<string>;
    $t: typeof VueI18n.prototype.t;
    $tc: typeof VueI18n.prototype.tc;
    $te: typeof VueI18n.prototype.te;
  }
}

export const UiKielet = Object.freeze(['fi', 'sv', 'en']);
const logger = createLogger('Kieli');

const kfi = require('../translations/locale-fi.json');
const ksv = require('../translations/locale-sv.json');
const ken = require('../translations/locale-en.json');

export function getMessages() {
  const result: any = {};
  result.fi = kfi;
  result.sv = ksv;
  result.en = ken;
  return result;
}

export class KieliStore {
  private vi18n!: VueI18n;

  public get i18n() {
    return this.vi18n;
  }

  /**
   * Add language support to root vue instance.
   *
   */
  public install(v: VueConstructor, config = {} as Partial<VueI18n.I18nOptions>) {
    moment.locale(Kieli.fi);
    this.vi18n = new VueI18n({
      fallbackLocale: Kieli.fi,
      locale: Kieli.fi,
      ...config,
      messages: _.merge(getMessages(), config.messages),
    });
    v.prototype.$locale = this.uiKieli;
    v.prototype.$slang = this.sisaltoKieli;
    moment.updateLocale(Kieli.fi, updateRelativeTime());
  }

  /**
   * Load translation from other source
   *
   */
  public async load(kaannokset: Kaannokset) {
    logger.info('Initing locales');
    const results = await this.fetchLocaleMap(kaannokset);
    _.forEach(results, (locales, lang) => {
      this.i18n!.mergeLocaleMessage(lang, locales);
    });
  }

  private readonly state = reactive({
    sisaltoKieli: Kieli.fi,
  });

  public readonly getUiKieli = computed(() => this.i18n?.locale);
  public readonly getSisaltoKieli = computed(() => this.state.sisaltoKieli);
  public readonly sisaltoKieli = computed(() => this.state.sisaltoKieli);
  public readonly uiKieli = computed(() => this.i18n?.locale);

  public readonly getAikakaannokset = computed(() => {
    const kieli = this.state.sisaltoKieli;
    return {
      days: _.map(moment.weekdays(), (day: string) => _.toUpper(_.first(day))),
      months: moment.monthsShort(),
      placeholder: {
        date: this.i18n!.t('valitse-pvm'),
        dateRange: this.i18n!.t('valitse-pvm-jana'),
      },
    };
  });

  public setUiKieli(kieli: Kieli) {
    if (this.i18n!.locale !== kieli && _.includes(UiKielet, kieli)) {
      moment.locale(kieli);
      this.i18n!.locale = kieli;
    }
  }

  public setSisaltoKieli(kieli: Kieli) {
    if (this.state.sisaltoKieli !== kieli && _.includes(UiKielet, kieli)) {
      this.state.sisaltoKieli = kieli;
    }
  }

  public haeLokalisoituOlio(avain: string) {
    const result = {
      fi: this.i18n!.t(avain, 'fi'),
      sv: this.i18n!.t(avain, 'sv'),
      en: this.i18n!.t(avain, 'en'),
    };
    return result;
  }

  /**
   * Check if text query matches object
   *
   */
  public search(query: string, text: any, config = {}): boolean {
    if (text && query) {
      const target = _.isString(text) ? text : text[this.getSisaltoKieli.value];
      return _.includes(_.toLower(target), _.toLower(query));
    }
    else {
      return true;
    }
  }

  public filterBy(field: string, query: string, config = {}) {
    return (data: any) => this.search(query, data[field]);
  }

  public searchFn(query: string) {
    return (text: any) => this.search(query, text);
  }

  /**
   * i18n locale convert
   *
   */
  public t(value: string): string {
    if (this.vi18n) {
      return this.vi18n.t(value) as string || '<' + value + '>';
    }
    else {
      return value;
    }
  };

  /**
   * Convert field into sortable value
   *
   * @returns {string}
   */
  public sortValue(value: any): string {
    if (!value) {
      return '';
    }
    else if (_.isObject(value)) {
      const locale = this.getSisaltoKieli.value;
      return (value as any)[locale];
    }
    else {
      return value;
    }
  };

  public kaanna(value?: LokalisoituTeksti | undefined | null, emptyWhenNotFound = false, squareBrackets = true, forcedLang = null): string {
    if (!value) {
      return '';
    }
    else if (_.isObject(value)) {
      const locale = this.getSisaltoKieli.value;
      let kielet;
      let teksti: string;
      if (forcedLang) {
        kielet = [forcedLang];
        teksti = '' + (value[forcedLang] || '');
      }
      else {
        kielet = [locale, ..._.pull(['fi', 'sv', 'en', 'se', 'ru'], locale)];
        teksti = '' + (value[locale] || '');
      }

      if (teksti) {
        return teksti;
      }
      else if (emptyWhenNotFound) {
        return '';
      }
      else {
        const other = _.first(_.filter(_.map(kielet, kieli => value[kieli] as string)));
        if (other) {
          return squareBrackets ? '[' + other + ']' : other;
        }
      }
      return '';
    }
    else {
      logger.warn('"$kaanna" on tekstiolioiden kääntämiseen. Käytä vue-i18n vastaavaa funktiota. Esimerkiksi "$t()".', 'Käännös:', '"' + value + '"');
      return value;
    }
  };

  public kaannaOlioTaiTeksti(value: LokalisoituTeksti | string, emptyWhenNotFound = false, squareBrackets = true): string {
    if (_.isObject(value)) {
      return this.kaanna(value, emptyWhenNotFound, squareBrackets);
    }
    else if (_.isString(value)) {
      return this.t(value);
    }
    else {
      return value;
    }
  }

  private async fetchLocaleMap(kaannokset: Kaannokset) {
    try {
      const result: any = {};
      const localeObj = kaannokset;
      _.forEach(localeObj, (locales, lang) => {
        result[lang] = {};
        if (locales) {
          for (const locale of locales) {
            if (locale.key && locale.value) {
              result[lang][locale.key] = locale.value;
            }
          }
        }
      });
      return result;
    }
    catch (err: any) {
      logger.error('Käännösten haku epäonnistui', err.message);
      return {};
    }
  }
}

export const Kielet = new KieliStore();

export interface Kaannos {
  key: string;
  value: string;
}

export interface Kaannokset {
  fi?: Kaannos[];
  sv?: Kaannos[];
  en?: Kaannos[];
}

export interface LokalisoituTeksti {
  [locale: string]: string | null | undefined | number;
  _id?: number;
  _tunniste?: string;
}