UNPKG

@crowdin/ota-client

Version:

JavaScript library for Crowdin OTA Content Delivery

288 lines (285 loc) 8.98 kB
// 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 };