Why Gemfury? Push, build, and install  RubyGems npm packages Python packages Maven artifacts PHP packages Go Modules Debian packages RPM packages NuGet packages

Repository URL to install this package:

Details    
@doodle/i18n / src / onesky / translations.js
Size: Mime:
import deepmerge from 'deepmerge';

import { extractDefaultMessages } from './extract';
import { getProjectMessagesByLanguage, getProjectLanguages, uploadProjectTranslation } from './api';
import { flatten, unflatten } from './transform';
import { readJsonFile, writeJsonFile } from './utils';
import { getAddedKeys, getChangedKeys, getRenamedKeys, getDeletedKeys, updateMessages } from './diff';

/**
 * Manage translations for OneSky
 *
 * @module @doodle/i18n/disty/onesky/translations
 */

/**
 * Download translations from a OneSky project for all defined languages in the project
 *
 * @param {string} projectId
 * @param {OneSkyCredentials} credentials
 * @returns {Promise<object>}
 * @async
 */
export const getProjectTranslations = async (projectId, credentials) => {
  const projectLanguages = await getProjectLanguages(projectId, credentials);
  const languageCodes = projectLanguages.map(({ code }) => code);

  const projectMessages = await Promise.all(
    languageCodes.map(language => getProjectMessagesByLanguage({ projectId, language }, credentials))
  );

  return projectMessages.reduce((result, messages, index) => {
    result[languageCodes[index]] = unflatten(messages);
    return result;
  }, {});
};

/**
 * Download translations from multiple OneSky projects for all defined languages
 * in the respective projects and combine the message translations
 *
 * @param {string[]} projectIds
 * @param {OneSkyCredentials} credentials
 * @returns {Promise<object>}
 * @async
 */
export const getMergedProjectTranslations = async (projectIds, credentials) => {
  const errors = {};

  const projectsTranslations = await Promise.all(
    projectIds.map(projectId =>
      getProjectTranslations(projectId, credentials).catch(error => {
        errors[projectId] = error;
      })
    )
  );

  if (Object.keys(errors).length) {
    throw new Error(JSON.stringify(errors));
  }

  return deepmerge.all(projectsTranslations);
};

/**
 * Download all translations from multiple OneSky projects and write them to JSON files by language
 *
 * @param {string[]} projectIds
 * @param {string} targetPath
 * @param {OneSkyCredentials} credentials
 * @returns {Promise<object>}
 * @async
 */
export const downloadMergedTranslationFiles = async (projectIds, targetPath, credentials) => {
  const combinedTranslations = await getMergedProjectTranslations(projectIds, credentials);

  await Promise.all(
    Object.keys(combinedTranslations).map(language =>
      writeJsonFile(`${targetPath}/${language}.json`, combinedTranslations[language])
    )
  );

  return combinedTranslations;
};

/**
 * Update a translation file containing hierarchical messages with changes from source messages
 *
 * @param {string} file
 * @param {object} sourceMessages
 * @param {bool} includeValues
 * @returns {Promise}
 * @async
 */
export const updateTranslationFile = async (file, sourceMessages, includeValues = false) => {
  const translation = await readJsonFile(file);
  const messages = flatten(translation);

  const addedKeys = includeValues ? getAddedKeys(sourceMessages, messages) : [];
  const changedKeys = includeValues ? getChangedKeys(sourceMessages, messages) : [];
  const renamed = getRenamedKeys(sourceMessages, messages);
  const deleted = getDeletedKeys(sourceMessages, messages);
  const sourceMessageEntries = Object.entries(sourceMessages);

  const updatedMessages = updateMessages(messages, {
    added: sourceMessageEntries.filter(([k]) => addedKeys.indexOf(k) >= 0),
    changed: sourceMessageEntries.filter(([k]) => changedKeys.indexOf(k) >= 0),
    renamed,
    deleted,
  });

  await writeJsonFile(file, updatedMessages);
};

/**
 * Update local translation files with changes from the source for all languages defined by the OneSky project
 *
 * @param {string} projectId
 * @param {OneSkyCredentials} credentials
 * @param {string} sourcePattern
 * @param {string} targetPath
 * @param {string} includeValues
 * @returns {Promise}
 * @async
 */
export const updateTranslationFiles = async (
  projectId,
  credentials,
  sourcePattern,
  targetPath,
  includeValues = false
) => {
  const projectLanguages = await getProjectLanguages(projectId, credentials);
  const languageCodes = projectLanguages.map(({ code }) => code);
  const baseLanguage = projectLanguages.filter(({ is_base_language: isBaseLanguage }) => isBaseLanguage)[0].code;

  const sourceMessages = await extractDefaultMessages(`${sourcePattern}`);
  const errors = {};

  await Promise.all(
    languageCodes.map(language =>
      updateTranslationFile(
        `${targetPath}/onesky/${projectId}/${language}.json`,
        sourceMessages,
        includeValues ? true : language === baseLanguage
      ).catch(error => {
        errors[language] = error;
      })
    )
  );

  if (Object.keys(errors).length) {
    console.error(errors);
  }
};

/**
 * Upload translation files for all languages of the project, not only the base language!
 *
 * @param {string} path path to folder containing the hierarchical `${language}.json` translation files
 * @param {OneSkyTranslationOptions} translationOptions
 * @param {OneSkyCredentials} credentials
 * @param {OneSkySyncOptions} syncOptions
 * @returns {Promise}
 * @async
 */
export const uploadTranslationFiles = async (
  path,
  { projectId, fileName },
  credentials,
  { keepStrings, format } = {}
) => {
  const projectLanguages = await getProjectLanguages(projectId, credentials);
  const languageCodes = projectLanguages.map(({ code }) => code);

  const doUpload = async language => {
    const translations = await readJsonFile(`${path}/onesky/${projectId}/${language}.json`);

    await uploadProjectTranslation({ language, projectId, fileName }, flatten(translations), credentials, {
      keepStrings,
      format,
    });
  };

  await Promise.all(languageCodes.map(doUpload));
};