diff --git a/.github/scripts/translate.js b/.github/scripts/translate.js index 1a8bd4ca..0463b15d 100644 --- a/.github/scripts/translate.js +++ b/.github/scripts/translate.js @@ -7,7 +7,7 @@ const path = require('path'); // ================================================================================================== // Path to your original language files (e.g., English source content) -const ORIGIN_LANGUAGE_DIR = path.resolve('.'); +const ORIGIN_LANGUAGE_DIR = path.resolve('.'); // Path where translated files will be saved (Base directory) const TRANSLATION_OUTPUT_DIR = path.resolve('.'); @@ -20,15 +20,13 @@ const EXCLUDED_FILES = ['package.json', 'package-lock.json', 'node_modules', 'do // ============================================================ (async () => { - // Dynamically import the ESM-only module const sdkModule = await import('@frenglish/sdk'); const FrenglishSDK = sdkModule.FrenglishSDK; if (!FrenglishSDK) throw new Error('FrenglishSDK not found in module exports.'); - // Retrieve the API key from environment variable const FRENGLISH_API_KEY = process.env.FRENGLISH_API_KEY; if (!FRENGLISH_API_KEY) { - console.error('Error: FRENGLISH_API_KEY environment variable not set.'); + console.error('❌ FRENGLISH_API_KEY environment variable not set. Aborting action.'); process.exit(1); } const frenglish = FrenglishSDK(FRENGLISH_API_KEY); @@ -44,53 +42,46 @@ const EXCLUDED_FILES = ['package.json', 'package-lock.json', 'node_modules', 'do const data = await response.json(); return data.default_branch; } catch (error) { - console.log('Error getting default branch:', error.message); + console.error(`❌ Failed to retrieve default branch: ${error.message}`); return 'main'; } } async function isSupportedFile(filePath) { try { - // Ensure the file is within the intended source directory structure for relative path logic const relativeToOrigin = path.relative(ORIGIN_LANGUAGE_DIR, path.resolve(filePath)); if (relativeToOrigin.startsWith('..') || relativeToOrigin === '') { - return false; + return false; } - // Check against general excluded files/paths if (EXCLUDED_FILES.some(excluded => filePath.includes(excluded))) { - console.log(`File ${filePath} is explicitly excluded.`); + console.log(`⏭️ Skipping (excluded): ${filePath}`); return false; } - // Get the configuration to check origin language const config = await frenglish.getDefaultConfiguration(); const languageCodes = await frenglish.getSupportedLanguages(); const originLanguage = config.originLanguage.toLowerCase(); - // Check if the file is in a translated language directory const pathParts = filePath.split(path.sep); - const languageDirIndex = pathParts.findIndex(part => - part.toLowerCase() === originLanguage || + const languageDirIndex = pathParts.findIndex(part => + part.toLowerCase() === originLanguage || languageCodes.some(lang => lang.toLowerCase() === part.toLowerCase()) ); - // If the file is in a language directory and it's not the origin language, exclude it if (languageDirIndex !== -1 && pathParts[languageDirIndex].toLowerCase() !== originLanguage) { - console.log(`File ${filePath} is in a translated language directory. Excluding.`); + console.log(`⏭️ Skipping (translated dir): ${filePath}`); return false; } - // Check extension against Frenglish supported types const supportedFileTypes = await frenglish.getSupportedFileTypes(); const validFileTypes = supportedFileTypes.filter(type => type && type.length > 0); const ext = path.extname(filePath).toLowerCase().replace('.', ''); const isSupported = ext && validFileTypes.includes(ext); return isSupported; - } catch (error) { - console.error(`Error checking file support for ${filePath}:`, error.message); + console.error(`❌ Error checking file support for ${filePath}: ${error.message}`); return false; } } @@ -99,47 +90,39 @@ const EXCLUDED_FILES = ['package.json', 'package-lock.json', 'node_modules', 'do async function getChangedFiles() { try { const isPR = !!process.env.GITHUB_BASE_REF; - const currentBranch = process.env.GITHUB_HEAD_REF // PR - || process.env.GITHUB_REF.replace('refs/heads/', ''); // Push to default branch + const currentBranch = process.env.GITHUB_HEAD_REF || process.env.GITHUB_REF.replace('refs/heads/', ''); const defaultBranch = await getDefaultBranch(); - - // Skip non‑PR pushes that are NOT on default branch + if (!isPR && currentBranch !== defaultBranch) { - return []; + return []; } - + // Figure out what we’re diffing against const baseBranch = isPR ? process.env.GITHUB_BASE_REF : defaultBranch; let baseSha; if (isPR) { - // bring in the base branch locally execSync(`git fetch --depth=1 origin ${baseBranch}:${baseBranch}`); - - // HEAD is already at the PR tip baseSha = execSync(`git merge-base ${baseBranch} HEAD`).toString().trim(); } else { - // push on default branch: compare to the previous commit on that branch baseSha = process.env.GITHUB_EVENT_BEFORE || execSync('git rev-parse HEAD^').toString().trim(); } - - console.log(`Diff base: ${baseBranch} @ ${baseSha}`); - console.log(`Head : ${currentBranch} @ HEAD`); - - const output = execSync( - `git diff --diff-filter=ACM --name-only ${baseSha} HEAD` - ).toString().trim(); - - const changedFiles = output ? output.split('\n') : []; + + console.log(`🔀 Diff base: ${baseBranch} @ ${baseSha}`); + console.log(`🔝 Head : ${currentBranch} @ HEAD`); + + const output = execSync(`git diff --diff-filter=ACM --name-only ${baseSha} HEAD`).toString().trim(); + const changedFiles = output ? output.split('\n') : []; const supportedFiles = []; - + for (const file of changedFiles) { if (await isSupportedFile(file)) supportedFiles.push(file); } - console.log('Files sent for translation:', supportedFiles); + + console.log(`📦 Files queued for translation (${supportedFiles.length}): ${supportedFiles.join(', ') || 'None'}`); return supportedFiles; } catch (error) { - console.error('Error getting changed files:', error.message); + console.error(`❌ Error getting changed files: ${error.message}`); return []; } } @@ -150,12 +133,11 @@ const EXCLUDED_FILES = ['package.json', 'package-lock.json', 'node_modules', 'do const originLanguage = config.originLanguage.toLowerCase(); const filesToTranslate = await getChangedFiles(); - if (!filesToTranslate || filesToTranslate.length === 0) { - console.log('No supported files found in the diff within the origin language directory to translate.'); - return; // Exit gracefully if no files need translation + if (!filesToTranslate.length) { + console.log('ℹ️ No eligible files found for translation. Exiting.'); + return; } - // Prepare content for translation const fileContents = await Promise.all(filesToTranslate.map(async (file) => { try { const content = await fs.readFile(file, 'utf-8'); @@ -163,31 +145,29 @@ const EXCLUDED_FILES = ['package.json', 'package-lock.json', 'node_modules', 'do const fileId = path.relative(ORIGIN_LANGUAGE_DIR, file); return { fileId: fileId, content: content }; } catch (readError) { - console.error(`Error reading file ${file}:`, readError.message); + console.error(`❌ Error reading file ${file}:`, readError.message); return null; } })); - // Filter out any nulls from read errors const validFileContents = fileContents.filter(fc => fc !== null); if (validFileContents.length === 0) { - console.log('No valid file contents could be read for translation.'); + console.log('⚠️ No readable file contents detected. Exiting.'); return; } const filenames = validFileContents.map(file => file.fileId); const contents = validFileContents.map(file => file.content); - console.log(`Initiating translation for ${filenames.length} files:`, filenames); + console.log(`🚀 Initiating translation for ${filenames.length} file(s).`); const translation = await frenglish.translate(contents, false, filenames); - console.log(`Translation requested with ID: ${translation.translationId}`); + console.log(`📤 Translation request submitted. ID: ${translation.translationId}`); - // Process translated content for (const languageData of translation.content) { const language = languageData.language; // Skip writing files for the origin language if they are returned if (language === originLanguage) { - console.log(`Skipping write for origin language: ${language}`); + console.log(`⏩ Skipping origin language (${language}).`); continue; } @@ -195,47 +175,43 @@ const EXCLUDED_FILES = ['package.json', 'package-lock.json', 'node_modules', 'do try { await fs.mkdir(languageOutputDir, { recursive: true }); } catch (mkdirError) { - console.error(`Error creating directory ${languageOutputDir}:`, mkdirError.message); - continue; // Skip this language if directory creation fails + console.error(`❌ Unable to create directory ${languageOutputDir}: ${mkdirError.message}`); + continue; } for (const translatedFile of languageData.files) { - // Construct the full path for the translated file const translatedFilePath = path.join(languageOutputDir, translatedFile.fileId); - // Ensure the subdirectory for the file exists (e.g., for nested structures) try { await fs.mkdir(path.dirname(translatedFilePath), { recursive: true }); } catch (mkdirError) { - console.error(`Error creating subdirectory ${path.dirname(translatedFilePath)}:`, mkdirError.message); - continue; // Skip this file if subdirectory creation fails + console.error(`❌ Unable to create subdirectory ${path.dirname(translatedFilePath)}: ${mkdirError.message}`); + continue; } - + // Write the file content if not empty if (translatedFile.content && translatedFile.content.length > 0) { try { await fs.writeFile(translatedFilePath, translatedFile.content, 'utf8'); - console.log(`Translated file written: ${translatedFilePath}`); + console.log(`✅ Written: ${translatedFilePath}`); } catch (writeError) { - console.error(`Error writing translated file ${translatedFilePath}:`, writeError.message); + console.error(`❌ Error writing ${translatedFilePath}: ${writeError.message}`); } } else { - console.warn(`Empty content for translated file: ${translatedFile.fileId} in language ${language}. Skipping write.`); + console.warn(`⚠️ Empty content for ${translatedFile.fileId} (${language}). Skipping.`); } } } - console.log("Translation file writing complete. Git operations will be handled by the workflow."); - + console.log('🏁 Translation workflow complete. Git operations will be handled by the Action.'); } catch (error) { - console.error('Error during translation and file writing process:', error); - if (error.response && error.response.data) { - console.error('Frenglish API Error Details:', error.response.data); + console.error('❌ Translation process failed:', error); + if (error.response?.data) { + console.error('🔍 Frenglish API details:', error.response.data); } process.exit(1); } } - // Run the modified translation process translateAndWriteFiles(); })(); \ No newline at end of file