All files / utils/i18n-merger i18n-merger.util.js

100% Statements 135/135
100% Branches 19/19
90% Functions 9/10
100% Lines 135/135

Press n or j to go to the next uncovered block, b, p or k for the previous block.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 1361x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 39x 39x 39x 39x 39x 39x 39x 39x 39x 39x 39x 39x 39x 39x 39x 39x 39x 39x 1x 1x 1x 1x 1x 1x 1x 15x 15x 15x 15x 1x 1x 1x 14x 15x 5x 5x 5x 9x 9x 9x 9x 9x 9x 1x 1x 1x 1x 1x 10x 10x 10x 10x 10x 39x 39x 39x 39x 39x 10x 10x 10x 1x 1x 1x 1x 1x 1x 1x 17x 10x 10x 10x 10x 17x 17x 1x 1x 1x 1x 1x 1x 17x 17x 17x 17x 17x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x  
import { normalizeI18nDefinition, normalizeI18nDefinitionMap } from '../i18n-normalizer/i18n-normalizer.js'
/** @import {I18nDefinitionMap, Translations, I18nDefinition, NormalizedI18nDefinition, NormalizedI18nDefinitionMap} from '../i18n-normalizer/i18n-normalizer.js'  */
 
/**
 * Reducer for {@link merge} function, works on data prepared in  {@link mergeReducer}
 * @param {I18nMergeIntermediaryResult} acc - accumulator
 * @param {NormalizedI18nDefinition} i18nDefinition - i18n definition
 * @param {string} language - i18n definition locale
 * @param {string | URL} locationBase - i18n definition url location
 * @returns {I18nMergeIntermediaryResult} updated accumulator
 */
function mergeLang (acc, i18nDefinition, language, locationBase) {
  const { translations, import: ext } = i18nDefinition
  const strLang = language.toString()
  const definition = acc[strLang] || { import: new Set(), translations: {} }
  const definitionExtSet = definition.import
  ext.forEach((e) => {
    const importLocation = new URL(e, locationBase).href
    definitionExtSet.add(importLocation)
  })
 
  definition.translations = {
    ...definition.translations,
    ...translations,
  }
 
  acc[strLang] = definition
  return acc
}
 
/**
 * Reducer for {@link merge} function, prepares i18n merge before applying {@link mergeLang}
 * @param {I18nMergeIntermediaryResult} acc - accumulator
 * @param {I18nLangMergeData} mergeData - merge data
 * @returns {I18nMergeIntermediaryResult} updated accumulator
 */
function mergeReducer (acc, mergeData) {
  const { location, kind } = mergeData
  const locationStr = typeof location === 'string' ? location : location.href
  if (kind === 'definition') {
    const i18nDefinition = normalizeI18nDefinition(mergeData.data).result
    return mergeLang(acc, i18nDefinition, mergeData.language.toString(), location)
  }
 
  if (kind === 'translations') {
    const i18nDefinition = normalizeI18nDefinition(locationStr).result
    return mergeLang(acc, i18nDefinition, mergeData.language.toString(), location)
  }
 
  const i18nDefinitionMap = normalizeI18nDefinitionMap(mergeData.data).result
  return Object.entries(i18nDefinitionMap).reduce(
    (acc, [lang, def]) => mergeLang(acc, def, lang, location),
    acc)
}
 
/**
 * @param  {...I18nLangMergeData} data - data to merge
 * @returns {NormalizedI18nDefinitionMap} merged data
 */
function merge (...data) {
  const result = data.reduce(mergeReducer, {})
 
  return Object.fromEntries(
    Object.entries(result).map(([lang, { import: ext, translations }]) => [
      lang,
      {
        import: [...ext],
        translations,
      },
    ])
  )
}
 
/**
 * {@link merge} call thunk
 * @param {I18nLangMergeData[]} data - {@link merge} input
 * @returns {() => NormalizedI18nDefinitionMap} thunk that calls merge with `data` and memoizes it
 */
const mergeThunk = (data) => {
  let buildResult = () => {
    const result = merge(...data)
    buildResult = () => result
    return result
  }
  return () => buildResult()
}
 
/**
 * @param {I18nLangMergeData[]} data - current data to merge
 * @returns {Readonly<MergerInstance>} immutable merger instance
 */
const mergeInstance = (data) => Object.freeze({
  addMap: (i18nInfo, location) => mergeInstance([...data, { kind: 'map', data: i18nInfo, location }]),
  addDefinitionOnLanguage: (i18nDef, language, location) => mergeInstance([...data, { kind: 'definition', language, data: i18nDef, location }]),
  addTranslations: (location, language) => mergeInstance([...data, { kind: 'translations', language, location }]),
  build: mergeThunk(data),
})
 
export const builder = mergeInstance([])
 
/** @typedef {{[language: string]: {"import": Set<string>,translations: Translations}}} I18nMergeIntermediaryResult */
 
/** @typedef {I18nMapMergeData | I18nDefinitionMergeData | I18nTranslationMergeData } I18nLangMergeData */
 
/**
 * @typedef {object} I18nMapMergeData
 * I18n definition map merge data
 * @property {'map'} kind - merge data type
 * @property {I18nDefinitionMap} data - i18n definition map data
 * @property {URL | string} location - i18n definition map location
 */
 
/**
 * @typedef {object} I18nDefinitionMergeData
 * @property {'definition'} kind - merge data type
 * @property {I18nDefinition} data - i18n definition data
 * @property {URL | string} location - i18n definition location
 * @property {Intl.Locale | string} language - i18n definition locale
 */
 
/**
 * @typedef {object} I18nTranslationMergeData
 * @property {'translations'} kind - merge data type
 * @property {URL | string} location - translations map location
 * @property {Intl.Locale | string} language - translations map locale
 */
 
/**
 * @typedef {object} MergerInstance
 * Contains api to add data to merge or merge with the current data
 * @property {(i18nInfo: I18nDefinitionMap, location: URL | string) => MergerInstance} addMap - add map of locale to i18n definition to merge
 * @property {(i18nDef: I18nDefinition, language: Intl.Locale | string, location: URL | string) => MergerInstance} addDefinitionOnLanguage - add i18n definition map in a specific locale to merge
 * @property {(location: URL | string, language: Intl.Locale | string) => MergerInstance} addTranslations - add url location of a translation map in a specific locale to merge
 * @property {() => NormalizedI18nDefinitionMap} build - merge current data
 */