/**
* Copyright IBM Corp. 2020, 2022
*
* This source code is licensed under the Apache-2.0 license found in the
* LICENSE file in the root directory of this source tree.
*/
import axios from 'axios';
import { LocaleAPI } from '../Locale';
import root from 'window-or-global';
/**
* @constant {string | string} Host for the Translation API call
* @private
*/
const _host =
(process &&
(process.env.REACT_APP_TRANSLATION_HOST || process.env.TRANSLATION_HOST)) ||
'https://1.www.s81c.com';
/**
* Translation API default endpoint
*
* @type {string}
* @private
*/
const _c4dEndpointDefault =
'/common/carbon-for-ibm-dotcom/translations/masthead-footer/v2.1';
/**
* Translation API endpoint
*
* @type {string}
* @private
*/
const _c4dEndpoint =
(process &&
(process.env.REACT_APP_C4D_TRANSLATION_ENDPOINT ||
process.env.C4D_TRANSLATION_ENDPOINT)) ||
_c4dEndpointDefault;
/**
* Session Storage key for translation data
*
* @type {string}
* @private
*/
const _sessionTranslationKey = 'c4d-translation';
/**
* The cache for in-flight or resolved requests for the i18n data, keyed by the initiating locale.
*
* @type {object}
* @private
*/
const _requestsTranslation = {};
/**
* Sets the default location if nothing is returned
*
* @type {object}
* @private
*/
const _localeDefault = {
lc: 'en',
cc: 'us',
};
/**
* Two hours in milliseconds to compare session timestamp.
*
* @type {number}
* @private
*/
const _twoHours = 60 * 60 * 2000;
/**
* Translation API class with methods for fetching i18n data for ibm.com
*/
class TranslationAPI {
/**
* Clears the cache.
*
* @param {string} endpoint specified API non-default endpoint (optional)
*/
static clearCache(endpoint) {
const sessionKey = this.getSessionKey(endpoint);
if (typeof sessionStorage !== 'undefined') {
Object.keys(_requestsTranslation).forEach(
(key) => delete _requestsTranslation[key]
);
for (let i = 0; i < sessionStorage.length; ++i) {
const key = sessionStorage.key(i);
if (key.indexOf(sessionKey) === 0) {
sessionStorage.removeItem(key);
}
}
}
}
/**
* Returns translation i18n data
*
* @param {object} codes object containing lc and cc
* @param {string} endpoint endpoint to fetch data from (optional)
* @returns {Promise<any>} Translation data
* @example
* import { TranslationAPI } from '@carbon/ibmdotcom-services';
*
* async function getTranslation() {
* const response = await TranslationAPI.getTranslation({
* lc: 'en',
* cc: 'us',
* });
* return response;
* }
*/
static async getTranslation(codes, endpoint) {
let lang = 'en';
let country = 'us';
if (codes && codes.lc && codes.cc) {
lang = codes.lc;
country = codes.cc;
} else {
const locale = await LocaleAPI.getLocale();
lang = locale.lc;
country = locale.cc;
}
return new Promise((resolve, reject) => {
this.fetchTranslation(lang, country, endpoint, resolve, reject);
});
}
/**
* Fetches the translation data from sessionStorage or data fetch
*
* @param {string} lang Language code
* @param {string} country Country code
* @param {string} endpoint endpoint to fetch data (optional)
* @param {Function} resolve resolves the Promise
* @param {Function} reject rejects the promise
* @private
*/
static fetchTranslation(lang, country, endpoint, resolve, reject) {
const sessionKey = this.getSessionKey(endpoint);
const itemKey = `${sessionKey}-${country}-${lang}`;
const sessionTranslation = this.getSessionCache(itemKey);
if (sessionTranslation) {
resolve(sessionTranslation);
} else {
const key = country !== 'undefined' ? `${country}-${lang}` : `${lang}`;
if (!_requestsTranslation[key]) {
const regex = /((http(s?)):\/\/)/g;
// Check to see if the string from the endpoint variable contains https/http or not.
const urlEndpoint = endpoint || _c4dEndpoint;
const locationParam =
country !== 'undefined' ? `${country}${lang}` : `${lang}`;
const host = regex.test(endpoint) ? '' : _host;
const url = `${host}${urlEndpoint}/${locationParam}.json`;
_requestsTranslation[key] = axios
.get(url, {
headers: {
'Content-Type': 'text/plain',
origin: _host,
},
})
.then((response) => this.transformData(response.data))
.then((data) => {
data['timestamp'] = Date.now();
if (typeof sessionStorage !== 'undefined') {
sessionStorage.setItem(
`${sessionKey}-${key}`,
JSON.stringify(data)
);
}
return data;
});
}
_requestsTranslation[key].then(resolve, (error) => {
if (country === _localeDefault.cc && lang === _localeDefault.lc) {
reject(error);
} else {
this.fetchTranslation(
_localeDefault.lc,
_localeDefault.cc,
endpoint,
resolve,
reject
);
}
});
}
}
/**
* sets the Session key depending on API endpoint
*
* @param {string} endpoint specified endpoint passed as arg in getTranslation()
* @returns {string} session key
* @private
*/
static getSessionKey(endpoint) {
let sessionKey = _sessionTranslationKey;
// form session key from specified endpoint
if (_c4dEndpointDefault !== _c4dEndpoint || endpoint) {
const endpointSrc = endpoint || _c4dEndpoint;
sessionKey = endpointSrc.replace(
/[`~!@#$%^&*()_|+\-=?;:'",.<>{}[\]\\/]/gi,
''
);
}
return sessionKey;
}
/**
* Transforms translation data
*
* @param {object} data translation data to be transformed
* @returns {object} Translation data
* @private
*/
static transformData(data) {
const signedout = data.profileMenu?.signedout;
if (signedout) {
const strReplace = 'state=https%3A%2F%2Fwww.ibm.com';
const loginIdx = signedout.findIndex(
(elem) => elem.url?.indexOf(strReplace) !== -1
);
if (loginIdx !== -1 && root.location) {
const location = encodeURIComponent(root.location.href);
data.profileMenu.signedout[loginIdx].url = signedout[
loginIdx
].url.replace(strReplace, `state=${location}`);
}
}
data.footerMenu.push(data.socialFollow);
return data;
}
/**
* Retrieves session cache and checks if cache needs to be refreshed
*
* @param {string} key session storage key
* @private
*/
static getSessionCache(key) {
const session =
typeof sessionStorage === 'undefined'
? undefined
: JSON.parse(sessionStorage.getItem(key));
if (!session || !session.timestamp) {
return;
}
const currentTime = Date.now(),
timeDiff = currentTime - session.timestamp;
if (timeDiff > _twoHours) {
sessionStorage.removeItem(key);
return;
}
return session;
}
}
export default TranslationAPI;