UNPKG

scratch-l10n

Version:
143 lines (126 loc) 4.99 kB
import locales, { localeMap } from '../../src/supported-locales.mjs' import { poolMap } from './concurrent.mts' import { ProgressLogger } from './progress-logger.mts' import { TransifexStringsKeyValueJson } from './transifex-formats.mts' import { txPull, txResources } from './transifex.mts' import { filterInvalidTranslations, TransifexEditorString } from './validate.mts' const CONCURRENCY_LIMIT = 36 const SOURCE_LOCALE = 'en' // TODO: don't hardcode this /** * @param resources - A list of Transifex resource names * @param selectedLocales - A list of Scratch locale codes * @returns The list of all resource/locale combinations to pull. The source locale is always excluded. */ const expandResourceFiles = (resources: string[], selectedLocales: string[]) => { const files = [] for (const resource of resources) { for (const locale of selectedLocales) { if (locale === SOURCE_LOCALE) { continue } files.push({ resource: resource, locale: locale }) } } return files } /** * Pull and validate a single Transifex "file" (resource + locale). * @param o - The options for pulling and validating a single file. * @param o.allStrings - All pulled strings so far, used to get source strings for validation * @param o.project - The Transifex project to pull from * @param o.resource - The Transifex resource to pull from * @param o.locale - The locale to pull * @param o.mode - The mode to use when pulling (e.g., "reviewed") * @returns A list of messages about errors encountered during validation, if any. */ async function pullAndValidateFile({ allStrings, project, resource, locale, mode, }: { allStrings: Record<string, Record<string, TransifexStringsKeyValueJson>> project: string resource: string locale: string mode?: string }) { const messages: string[] = [] const txLocale = localeMap[locale] || locale const fileContent = await txPull<TransifexEditorString>(project, resource, txLocale, mode) // if fileContent has message & description, we only want the message const translations: TransifexStringsKeyValueJson = {} for (const key of Object.keys(fileContent)) { const tx = fileContent[key] if (typeof tx === 'string') { translations[key] = tx } else { translations[key] = tx.message } } if (!(resource in allStrings)) { allStrings[resource] = {} } allStrings[resource][locale] = translations // may or may not be the same as `translations` const sourceStrings = allStrings[resource][SOURCE_LOCALE] // some of the validation checks may still be relevant even if locale === SOURCE_LOCALE // console.log({ resource, locale, translations, sourceStrings }) messages.push(...filterInvalidTranslations(locale, translations, sourceStrings)) return messages } /** * Pull one or more resource(s) from a transifex project and validate the strings. * Return any error messages from the validation process along with all valid strings. * @param o - The options for pulling, validating, and saving translations. * @param o.project - The Transifex project to pull translations from. * @param o.mode - The mode to use when pulling translations (e.g., "reviewed"). * @param o.resources - The resources within the project to pull translations for. * @param o.selectedLocales - The locales to pull translations for. Defaults to all supported locales. * @returns Translation strings and a list of messages about errors encountered during validation, if any. */ export async function pullAndValidateProject({ project, resources, mode, selectedLocales, }: { project: string mode?: string resources?: string[] selectedLocales?: string | string[] }) { const selectedResources = resources ?? (await txResources(project)) selectedLocales = typeof selectedLocales === 'string' ? [selectedLocales] : (selectedLocales ?? Object.keys(locales)) const files = expandResourceFiles(selectedResources, selectedLocales) const allStrings: Record<string, Record<string, TransifexStringsKeyValueJson>> = {} const messages: string[] = [] const progress = new ProgressLogger(selectedResources.length + files.length) const handleFile = async (resource: string, locale: string) => { try { const fileMessages = await pullAndValidateFile({ allStrings, project, resource, locale, mode, }) for (const message of fileMessages) { // `message` already contains locale and/or string info if appropriate messages.push(`resource ${resource} / ${message}`) } } finally { progress.increment() } } // Ensure source locale is available for validation await poolMap(selectedResources, CONCURRENCY_LIMIT, async resource => handleFile(resource, SOURCE_LOCALE)) // Non-source locales await poolMap(files, CONCURRENCY_LIMIT, async ({ resource, locale }) => handleFile(resource, locale)) return { allStrings, messages, } }