UNPKG

asksuite-core

Version:
265 lines (220 loc) 6.75 kB
const { asyncMapValuesDeep } = require('./util'); const _ = require('lodash'); const md5 = require('md5'); const request = require('request-json'); const createRedisClient = require('../util/redis'); const AsksuiteUtil = require('../asksuite.util')(); const { URL } = require('url'); const languageMap = { pt_br: 'ptBR', pt: 'ptBR', 'pt-br': 'ptBR', en: 'enUS', es: 'es', }; const UGLIFY_TOKEN = '_'; const fullVariableRegex = /^[{]{2,3}\s*[\w\.]+\s*[}]{2,3}$/g; const innerNameVariableRegex = /\w+/; module.exports = configuration => { const redis = createRedisClient(configuration.redis); const getTranslation = async (text, languageFrom, languageTo, customId, options) => { if (languageTo === languageFrom) return text; const client = request.createClient(configuration.url); let path = `translate/${languageTo}/${languageFrom}`; if (customId) { path += `/${customId}`; } const optionsKeys = _.keys(options); if (!_.isEmpty(optionsKeys)) { const queryString = _.map(optionsKeys, key => `${key}=${_.get(options, key)}`).join('&'); path += `?${queryString}`; } const translateResponse = await client.post(path, [text]); return translateResponse.body[0]; }; const getCachedTranslation = async ( hash, languageFormatIsoFrom, languageFormatIsoTo, languageFormattedTo, customId, ) => { const path = `translations.${languageFormatIsoTo}`; const customTranslation = await redis.getValue(`${customId}:${hash}`); if ( customTranslation && _.get(customTranslation, path) != null && _.includes(customTranslation.chatTreeIds, customId) ) { return _.get(customTranslation, path); } const globalTranslationByLanguage = await redis.getValue(`${languageFormatIsoFrom}:${hash}`); if ( globalTranslationByLanguage && _.get(globalTranslationByLanguage, path) != null && _.includes(globalTranslationByLanguage.chatTreeIds, customId) ) { return _.get(globalTranslationByLanguage, path); } const globalTranslation = await redis.getValue(hash); if ( globalTranslation && _.get(globalTranslation, path) != null && _.includes(globalTranslation.chatTreeIds, customId) ) { return _.get(globalTranslation, path); } const languageProperty = _.get(languageMap, languageFormattedTo); return _.chain([customTranslation, globalTranslationByLanguage, globalTranslation]) .compact() .filter(v => v.translations == null) // old translations .filter(v => _.includes(v.chatTreeIds, customId)) // vinculateds .map(v => _.get(v, languageProperty)) .compact() .head() .value(); }; const normalizeLanguage = (lang, languages) => { if (languages) { const l = AsksuiteUtil.resolveLanguage(lang, languages, 'formatIso'); if (l) { return l; } } return lang .toLowerCase() .replace('-', '_') .split('_')[0]; }; const stringIsAValidUrl = s => { try { new URL(s); return true; } catch (err) { return false; } }; const notNeedsTranslate = ( text, attribute, removedAttributes, languageFrom, languageTo, languages, ) => { const regexRemovedAttributes = _.filter(removedAttributes, _.isRegExp); const notNeedsByAllRegexRemovedAttributes = _.some(regexRemovedAttributes, regex => regex.test(text), ); if (notNeedsByAllRegexRemovedAttributes) { return notNeedsByAllRegexRemovedAttributes; } const notNeeds = _.includes(removedAttributes, attribute) || !_.isString(text) || _.isEmpty(text); if (notNeeds) { return notNeeds; } if (stringIsAValidUrl(text)) { return true; } return normalizeLanguage(languageFrom, languages) === normalizeLanguage(languageTo, languages); }; const protectMustacheVariablesTranslation = text => { try { const variablesNames = extractVariables(text); for (const name of variablesNames) { text = text.replace(name, variableNameUglify(name)); } } catch (e) { console.log(e); } return text; }; const variableNameUglify = name => { if (name) { return `${UGLIFY_TOKEN}${name.split('').join(UGLIFY_TOKEN)}`; } return null; }; const unprotectMustacheVariablesTranslation = text => { try { const variablesNames = extractVariables(text); for (const name of variablesNames) { text = text.replace(name, variableNamePrettify(name)); } } catch (e) { console.log(e); } return text; }; const variableNamePrettify = name => { const auxToken = '####'; if (name) { name = name.replace(new RegExp(`[${UGLIFY_TOKEN}]{3}`, 'g'), auxToken); return name.replace(new RegExp(`${UGLIFY_TOKEN}`, 'g'), '').replace(auxToken, UGLIFY_TOKEN); } return null; }; const extractVariables = text => { const variables = text.match(fullVariableRegex); if (variables && variables.length) { return variables.map(item => item.match(innerNameVariableRegex)[0]); } return []; }; const translate = async ( text, attribute, removedAttributes, languageFrom, languageTo, customId, options, languages, ) => { if (notNeedsTranslate(text, attribute, removedAttributes, languageFrom, languageTo, languages)) return text; const hash = md5(text); const languageFormatIsoTo = normalizeLanguage(languageTo, languages); const languageFormatIsoFrom = normalizeLanguage(languageFrom, languages); const languageFormattedTo = normalizeLanguage(languageTo, null); const cachedTranslation = await getCachedTranslation( hash, languageFormatIsoFrom, languageFormatIsoTo, languageFormattedTo, customId, ); if (cachedTranslation != null) { return cachedTranslation || text; } text = protectMustacheVariablesTranslation(text); const translation = await getTranslation(text, languageFrom, languageTo, customId, options); text = unprotectMustacheVariablesTranslation(translation || text); return text; }; const translateObject = ( object, languageFrom, languageTo, removedAttributes, customId, options, languages, ignoreBranches, ) => { const contextTranslate = (text, attribute) => translate( text, attribute, removedAttributes, languageFrom, languageTo, customId, options, languages, ); return asyncMapValuesDeep(object, contextTranslate, ignoreBranches); }; return translateObject; };