@crowdin/ota-client
Version:
JavaScript library for Crowdin OTA Content Delivery
288 lines (285 loc) • 8.98 kB
JavaScript
// src/internal/http/fetch.ts
var FetchHttpClient = class {
async get(url) {
const res = await fetch(url);
if (this.isJson(res)) {
return res.json();
}
return res.text();
}
isJson(res) {
var _a, _b;
return ((_b = (_a = res.headers) == null ? void 0 : _a.get("Content-Type")) == null ? void 0 : _b.toLowerCase()) === "application/json";
}
};
// src/internal/util/strings.ts
function isJsonFile(file) {
const extension = (file != null ? file : "").split(".").pop();
return (extension == null ? void 0 : extension.toLocaleLowerCase()) === "json";
}
function isObject(value) {
return value && typeof value === "object" && !Array.isArray(value);
}
function mergeDeep(targetObj, sourceObj) {
const target = targetObj != null ? targetObj : {};
const source = sourceObj != null ? sourceObj : {};
Object.keys(source).forEach((key) => {
if (isObject(source[key])) {
if (!(key in target)) {
target[key] = source[key];
} else {
target[key] = mergeDeep(target[key], source[key]);
}
} else {
target[key] = source[key];
}
});
return target;
}
// src/index.ts
var _OtaClient = class _OtaClient {
/**
* @param distributionHash Hash of released Crowdin project distribution
* @param config Client config
*/
constructor(distributionHash, config) {
this.distributionHash = distributionHash;
this.disableManifestCache = false;
this.stringsCache = {};
this.disableStringsCache = false;
this.disableJsonDeepMerge = false;
this.httpClient = (config == null ? void 0 : config.httpClient) || new FetchHttpClient();
this.disableManifestCache = !!(config == null ? void 0 : config.disableManifestCache);
this.locale = config == null ? void 0 : config.languageCode;
this.disableStringsCache = !!(config == null ? void 0 : config.disableStringsCache);
this.disableJsonDeepMerge = !!(config == null ? void 0 : config.disableJsonDeepMerge);
}
/**
* Get the distribution hash.
*
* @category Helper Methods
*/
getHash() {
return this.distributionHash;
}
/**
* Define the global language for the client instance.
* Default language code to be used if language was not passed as an input argument of the method.
*
* @category Helper Methods
* @param languageCode {@link https://support.crowdin.com/developer/language-codes/ Language Code}
*/
setCurrentLocale(languageCode) {
this.locale = languageCode;
}
/**
* Get the current locale of the client instance.
*
* @category Helper Methods
*/
getCurrentLocale() {
return this.locale;
}
/**
* Get distribution's manifest timestamp.
*
* @category Helper Methods
*/
async getManifestTimestamp() {
return (await this.manifest).timestamp;
}
/**
* List distribution's files content.
*
* @category Content Management Methods
*
* @returns An object mapping {@link https://support.crowdin.com/developer/language-codes/ Language Code} to arrays of strings: `{[languageCode: string]: string[]}`
*/
async getContent() {
return (await this.manifest).content;
}
/**
* List distribution's files content for a specific language.
*
* @category Content Management Methods
*
* @param languageCode {@link https://support.crowdin.com/developer/language-codes/ Language Code}
*/
async getLanguageContent(languageCode) {
const language = this.getLanguageCode(languageCode);
const content = await this.getContent();
return content[language];
}
/**
* List Crowdin project {@link https://support.crowdin.com/developer/language-codes/ language codes}.
*
* @category Helper Methods
*/
async listLanguages() {
return Object.keys(await this.getContent());
}
/**
* Get all translations for all languages.
*
* @category Content Management Methods
*
* @returns All translations per each language code
*/
async getTranslations() {
const languages = await this.listLanguages();
const translations = {};
await Promise.all(
languages.map(async (language) => {
translations[language] = await this.getLanguageTranslations(language);
})
);
return translations;
}
/**
* Get translations for a specific language.
*
* @category Content Management Methods
*
* @param languageCode {@link https://support.crowdin.com/developer/language-codes/ Language Code}
* @returns Translations for each file in the distribution for a given language (content)
*/
async getLanguageTranslations(languageCode) {
const language = this.getLanguageCode(languageCode);
const content = await this.getContent();
const files = content[language] || [];
return Promise.all(
files.map(async (file) => {
const content2 = await this.getFileTranslations(file);
return { content: content2, file };
})
);
}
/**
* Get translations for a specific file.
*
* @category Content Management Methods
*
* @param file file content path
* @returns Translations for a specific file (content)
*/
async getFileTranslations(file) {
const content = await this.getContent();
const fileExists = Object.values(content).some((files) => files.includes(file));
if (!fileExists) {
throw new Error(`File ${file} does not exists in manifest content`);
}
const timestamp = await this.getManifestTimestamp();
const url = `${_OtaClient.BASE_URL}/${this.distributionHash}${file}?timestamp=${timestamp}`;
return this.httpClient.get(url).catch(() => null);
}
/**
* Get all translation strings for all languages.
*
* @category Strings Management Methods
*
* @returns Translation strings from json-based files for all languages
*/
async getStrings() {
const content = await this.getJsonFiles();
const res = {};
await Promise.all(
Object.entries(content).map(async ([lang, files]) => {
res[lang] = await this.getStringsByFilesAndLocale(files);
})
);
return res;
}
/**
* Get translation strings for a specific language.
*
* @category Strings Management Methods
*
* @param languageCode {@link https://support.crowdin.com/developer/language-codes/ Language Code}
* @returns Translation strings from json-based files for a given language
*/
async getStringsByLocale(languageCode) {
const language = this.getLanguageCode(languageCode);
const content = await this.getJsonFiles();
return this.getStringsByFilesAndLocale(content[language] || []);
}
/**
* Get translation string for a specific key.
*
* @category Strings Management Methods
*
* @param key path to the translation string in json file
* @param languageCode {@link https://support.crowdin.com/developer/language-codes/ Language Code}
* @returns Translation string for language for given key
*/
async getStringByKey(key, languageCode) {
const strings = await this.getStringsByLocale(languageCode);
const path = Array.isArray(key) ? key : [key];
const firstKey = path.shift();
if (!firstKey) {
return void 0;
}
let res = strings[firstKey];
for (const keyPart of path) {
res = res == null ? void 0 : res[keyPart];
}
return res;
}
/**
* Clear the translation strings cache.
*
* @category Helper Methods
*/
clearStringsCache() {
this.stringsCache = {};
}
async getStringsByFilesAndLocale(files) {
let strings = {};
for (const filePath of files) {
let content;
if (this.disableStringsCache) {
content = await this.getFileTranslations(filePath);
} else {
if (!this.stringsCache[filePath]) {
this.stringsCache[filePath] = this.getFileTranslations(filePath);
}
content = await this.stringsCache[filePath];
}
if (this.disableJsonDeepMerge) {
strings = { ...strings, ...content };
} else {
mergeDeep(strings, content);
}
}
return strings;
}
get manifest() {
if (this.manifestHolder && !this.disableManifestCache) {
return this.manifestHolder;
} else {
this.manifestHolder = this.httpClient.get(`${_OtaClient.BASE_URL}/${this.distributionHash}/manifest.json`);
return this.manifestHolder;
}
}
getLanguageCode(lang) {
const languageCode = lang || this.getCurrentLocale();
if (languageCode) {
return languageCode;
} else {
throw new Error(
'Language code should be either provided through input arguments or by "setCurrentLocale" method'
);
}
}
async getJsonFiles() {
const content = await this.getContent();
const res = {};
Object.entries(content).forEach(([lang, files]) => res[lang] = files.filter(isJsonFile));
return res;
}
};
/** @internal */
_OtaClient.BASE_URL = "https://distributions.crowdin.net";
var OtaClient = _OtaClient;
export {
OtaClient as default
};