From 46edaf01ed580702442d8a682c487d5c16de4bce Mon Sep 17 00:00:00 2001 From: Divyansh013 Date: Tue, 10 Jun 2025 01:03:11 +0530 Subject: [PATCH 01/18] Test: Add documentation line to circle.mdx for translation tracker testing --- src/content/reference/en/p5/circle.mdx | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/content/reference/en/p5/circle.mdx b/src/content/reference/en/p5/circle.mdx index 5d60fbb3cc..3501fcc5a5 100644 --- a/src/content/reference/en/p5/circle.mdx +++ b/src/content/reference/en/p5/circle.mdx @@ -6,6 +6,8 @@ file: src/core/shape/2d_primitives.js description: >

Draws a circle.

+

This is a test modification for the translation tracker.

+

A circle is a round shape defined by the x, y, and d parameters. From ba9b4ee119e225f6b6a3b99d2e636a0f6e6f54fd Mon Sep 17 00:00:00 2001 From: Divyansh013 Date: Tue, 10 Jun 2025 01:16:13 +0530 Subject: [PATCH 02/18] Add Week 1 translation tracker implementation --- .github/actions/translation-tracker/index.js | 236 ++++++++++++++++++ .../actions/translation-tracker/package.json | 26 ++ .../actions/translation-tracker/test-local.js | 39 +++ .github/workflows/translation-sync.yml | 34 +++ 4 files changed, 335 insertions(+) create mode 100644 .github/actions/translation-tracker/index.js create mode 100644 .github/actions/translation-tracker/package.json create mode 100755 .github/actions/translation-tracker/test-local.js create mode 100644 .github/workflows/translation-sync.yml diff --git a/.github/actions/translation-tracker/index.js b/.github/actions/translation-tracker/index.js new file mode 100644 index 0000000000..f897582e90 --- /dev/null +++ b/.github/actions/translation-tracker/index.js @@ -0,0 +1,236 @@ +const { execSync } = require('child_process'); +const fs = require('fs'); +const path = require('path'); + + +const SUPPORTED_LANGUAGES = ['es', 'hi', 'ko', 'zh-Hans']; +const REFERENCE_PATH = 'src/content/reference'; +const ENGLISH_PATH = path.join(REFERENCE_PATH, 'en'); + + +function getChangedFiles() { + try { + console.log('๐Ÿ” Detecting changed files...'); + + + const gitCommand = process.env.GITHUB_EVENT_NAME === 'pull_request' + ? 'git diff --name-only HEAD~1 HEAD' + : 'git diff --name-only HEAD~1 HEAD'; + + const changedFilesOutput = execSync(gitCommand, { encoding: 'utf8' }); + const allChangedFiles = changedFilesOutput.trim().split('\n').filter(file => file.length > 0); + + + const changedEnglishFiles = allChangedFiles.filter(file => + file.startsWith(ENGLISH_PATH) && file.endsWith('.mdx') + ); + + console.log(`๐Ÿ“ Total changed files: ${allChangedFiles.length}`); + console.log(`๐Ÿ‡บ๐Ÿ‡ธ Changed English reference files: ${changedEnglishFiles.length}`); + + if (changedEnglishFiles.length > 0) { + console.log('๐Ÿ“„ Changed English files:'); + changedEnglishFiles.forEach(file => console.log(` - ${file}`)); + } + + return changedEnglishFiles; + } catch (error) { + console.error('โŒ Error getting changed files:', error.message); + return []; + } +} + + +function getTranslationPath(englishFilePath, language) { + return englishFilePath.replace('/en/', `/${language}/`); +} + +/** + * Check if a file exists + */ +function fileExists(filePath) { + try { + return fs.existsSync(filePath); + } catch (error) { + return false; + } +} + + +function getFileModTime(filePath) { + try { + return fs.statSync(filePath).mtime; + } catch (error) { + return null; + } +} + + +function checkTranslationStatus(changedEnglishFiles) { + console.log('\n๐ŸŒ Checking translation status...'); + + const translationStatus = { + needsUpdate: [], + missing: [], + upToDate: [] + }; + + changedEnglishFiles.forEach(englishFile => { + console.log(`\n๐Ÿ“– Analyzing: ${englishFile}`); + + const englishModTime = getFileModTime(englishFile); + if (!englishModTime) { + console.log(` โš ๏ธ Could not get modification time for English file`); + return; + } + + SUPPORTED_LANGUAGES.forEach(language => { + const translationPath = getTranslationPath(englishFile, language); + const exists = fileExists(translationPath); + + if (!exists) { + console.log(` โŒ ${language}: Missing translation`); + translationStatus.missing.push({ + englishFile, + language, + translationPath, + status: 'missing' + }); + } else { + const translationModTime = getFileModTime(translationPath); + const isOutdated = translationModTime < englishModTime; + + if (isOutdated) { + console.log(` ๐Ÿ”„ ${language}: Needs update (English: ${englishModTime.toISOString()}, Translation: ${translationModTime.toISOString()})`); + translationStatus.needsUpdate.push({ + englishFile, + language, + translationPath, + status: 'outdated', + englishModTime, + translationModTime + }); + } else { + console.log(` โœ… ${language}: Up to date`); + translationStatus.upToDate.push({ + englishFile, + language, + translationPath, + status: 'up-to-date' + }); + } + } + }); + }); + + return translationStatus; +} + + +function displaySummary(translationStatus) { + console.log('\n๐Ÿ“Š TRANSLATION STATUS SUMMARY'); + console.log('โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•'); + + console.log(`๐Ÿ†• Missing translations: ${translationStatus.missing.length}`); + if (translationStatus.missing.length > 0) { + translationStatus.missing.forEach(item => { + console.log(` - ${item.language}: ${item.englishFile}`); + }); + } + + console.log(`๐Ÿ”„ Outdated translations: ${translationStatus.needsUpdate.length}`); + if (translationStatus.needsUpdate.length > 0) { + translationStatus.needsUpdate.forEach(item => { + console.log(` - ${item.language}: ${item.englishFile}`); + }); + } + + console.log(`โœ… Up-to-date translations: ${translationStatus.upToDate.length}`); + + console.log('\n๐Ÿ’ก Next steps for Week 2+:'); + if (translationStatus.missing.length > 0) { + console.log(' - Create GitHub issues for missing translations'); + } + if (translationStatus.needsUpdate.length > 0) { + console.log(' - Create GitHub issues for outdated translations'); + } + console.log(' - Implement automated issue creation'); + console.log(' - Set up notification system for translation teams'); +} + +function exploreRepoStructure() { + console.log('\n๐Ÿ” REPOSITORY STRUCTURE ANALYSIS'); + console.log('โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•'); + + try { + const referencePath = REFERENCE_PATH; + console.log(`๐Ÿ“ Reference path: ${referencePath}`); + + if (fs.existsSync(referencePath)) { + const languages = fs.readdirSync(referencePath) + .filter(item => fs.statSync(path.join(referencePath, item)).isDirectory()) + .filter(item => !item.startsWith('.')); + + console.log(`๐ŸŒ Available languages: ${languages.join(', ')}`); + + // Count files in each language + languages.forEach(lang => { + const langPath = path.join(referencePath, lang); + try { + const subdirs = fs.readdirSync(langPath) + .filter(item => fs.statSync(path.join(langPath, item)).isDirectory()); + + let totalFiles = 0; + subdirs.forEach(subdir => { + const subdirPath = path.join(langPath, subdir); + const files = fs.readdirSync(subdirPath) + .filter(file => file.endsWith('.mdx')); + totalFiles += files.length; + }); + + console.log(` ${lang}: ${totalFiles} files across ${subdirs.length} categories`); + } catch (error) { + console.log(` ${lang}: Error reading directory - ${error.message}`); + } + }); + } else { + console.log(` Reference path does not exist: ${referencePath}`); + } + } catch (error) { + console.error(' Error exploring repository structure:', error.message); + } +} + + +function main() { + console.log('p5.js Translation Sync Tracker - Week 1 Prototype'); + console.log('โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•'); + console.log(`๐Ÿ“… Event: ${process.env.GITHUB_EVENT_NAME || 'local'}`); + console.log(`๐Ÿ  Working directory: ${process.cwd()}`); + console.log(`๐ŸŽฏ Tracking languages: ${SUPPORTED_LANGUAGES.join(', ')}`); + + + exploreRepoStructure(); + + + const changedEnglishFiles = getChangedFiles(); + + if (changedEnglishFiles.length === 0) { + console.log(' No changes detected in English reference files.'); + console.log(' Nothing to track for translations in this commit!'); + return; + } + + + const translationStatus = checkTranslationStatus(changedEnglishFiles); + + + displaySummary(translationStatus); + + +} + +// Run the main function +if (require.main === module) { + main(); +} \ No newline at end of file diff --git a/.github/actions/translation-tracker/package.json b/.github/actions/translation-tracker/package.json new file mode 100644 index 0000000000..b0630b623e --- /dev/null +++ b/.github/actions/translation-tracker/package.json @@ -0,0 +1,26 @@ +{ + "name": "p5js-translation-tracker", + "version": "1.0.0", + "description": "GitHub Action to track translation status for p5.js reference documentation", + "main": "index.js", + "scripts": { + "start": "node index.js", + "test": "node index.js" + }, + "keywords": [ + "p5.js", + "translation", + "documentation", + "github-actions", + "automation" + ], + "author": "Divyansh Srivastava", + "license": "LGPL-2.1", + "engines": { + "node": ">=18.0.0" + }, + "dependencies": { + "@actions/core": "^1.10.0", + "@actions/github": "^5.1.1" + } +} \ No newline at end of file diff --git a/.github/actions/translation-tracker/test-local.js b/.github/actions/translation-tracker/test-local.js new file mode 100755 index 0000000000..c605389af7 --- /dev/null +++ b/.github/actions/translation-tracker/test-local.js @@ -0,0 +1,39 @@ +#!/usr/bin/env node + +/** + * Local test script for the translation tracker + * This simulates the GitHub Action environment for testing + */ + +const { execSync } = require('child_process'); +const path = require('path'); + +process.env.GITHUB_EVENT_NAME = 'push'; +process.env.GITHUB_WORKSPACE = process.cwd(); + +console.log('๐Ÿงช LOCAL TEST: Translation Tracker'); +console.log('โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•'); +console.log(`๐Ÿ“ Working directory: ${process.cwd()}`); + +// Navigate to repository root +const repoRoot = path.resolve(__dirname, '../../..'); +process.chdir(repoRoot); + +console.log(`๐Ÿ“ Changed to repository root: ${process.cwd()}`); + +try { + const gitLogOutput = execSync('git log --oneline -5', { encoding: 'utf8' }); + console.log(' Recent commits:'); + console.log(gitLogOutput); +} catch (error) { + console.log(' No git history available or not in a git repository'); +} + +console.log(' Running translation tracker...\n'); + +try { + require('./index.js'); +} catch (error) { + console.error('โŒ Error running translation tracker:', error.message); + console.error(error.stack); +} \ No newline at end of file diff --git a/.github/workflows/translation-sync.yml b/.github/workflows/translation-sync.yml new file mode 100644 index 0000000000..b3bbe43440 --- /dev/null +++ b/.github/workflows/translation-sync.yml @@ -0,0 +1,34 @@ +name: Translation Sync Tracker + +on: + push: + branches: [main] + paths: + - 'src/content/reference/en/**' + pull_request: + branches: [main] + paths: + - 'src/content/reference/en/**' + +jobs: + track-translation-changes: + runs-on: ubuntu-latest + + steps: + - name: Checkout repository + uses: actions/checkout@v4 + with: + fetch-depth: 2 # Fetch previous commit to compare changes + + - name: Setup Node.js + uses: actions/setup-node@v4 + with: + node-version: '18' + + - name: Install dependencies + run: npm ci + + - name: Run translation tracker + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + run: node .github/actions/translation-tracker/index.js \ No newline at end of file From cd94f245555d86ae4cc33b4f2bd815cc7a2eba30 Mon Sep 17 00:00:00 2001 From: Divyansh013 Date: Tue, 10 Jun 2025 01:16:47 +0530 Subject: [PATCH 03/18] Test: Modify triangle.mdx for translation testing --- src/content/reference/en/p5/triangle.mdx | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/content/reference/en/p5/triangle.mdx b/src/content/reference/en/p5/triangle.mdx index 02740d2d1b..7534675583 100644 --- a/src/content/reference/en/p5/triangle.mdx +++ b/src/content/reference/en/p5/triangle.mdx @@ -6,6 +6,8 @@ file: src/core/shape/2d_primitives.js description: >

Draws a triangle.

+

Test modification for translation tracker testing.

+

A triangle is a three-sided shape defined by three points. The first two parameters specify the triangle's first point (x1, y1). From ddebbf3a65c9812a857c8a915a13e75a0ec7ef75 Mon Sep 17 00:00:00 2001 From: Divyansh013 Date: Tue, 10 Jun 2025 01:17:33 +0530 Subject: [PATCH 04/18] Test: Modify accelerationX.mdx to test missing translation detection --- src/content/reference/en/p5/accelerationX.mdx | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/content/reference/en/p5/accelerationX.mdx b/src/content/reference/en/p5/accelerationX.mdx index 14edb024b5..839003bd00 100644 --- a/src/content/reference/en/p5/accelerationX.mdx +++ b/src/content/reference/en/p5/accelerationX.mdx @@ -8,6 +8,8 @@ description: > device along the x axis. Value is represented as meters per second squared.

+ +

Test modification to demonstrate missing translation detection.

line: 23 isConstructor: false itemtype: property From 1e132870fe3788fb3d0ee9489e1af68af5f22b1b Mon Sep 17 00:00:00 2001 From: Divyansh013 Date: Tue, 10 Jun 2025 01:22:25 +0530 Subject: [PATCH 05/18] removed testing logs --- .github/actions/translation-tracker/index.js | 24 +++++++------------- 1 file changed, 8 insertions(+), 16 deletions(-) diff --git a/.github/actions/translation-tracker/index.js b/.github/actions/translation-tracker/index.js index f897582e90..c22a9f4408 100644 --- a/.github/actions/translation-tracker/index.js +++ b/.github/actions/translation-tracker/index.js @@ -10,7 +10,7 @@ const ENGLISH_PATH = path.join(REFERENCE_PATH, 'en'); function getChangedFiles() { try { - console.log('๐Ÿ” Detecting changed files...'); + const gitCommand = process.env.GITHUB_EVENT_NAME === 'pull_request' @@ -25,8 +25,8 @@ function getChangedFiles() { file.startsWith(ENGLISH_PATH) && file.endsWith('.mdx') ); - console.log(`๐Ÿ“ Total changed files: ${allChangedFiles.length}`); - console.log(`๐Ÿ‡บ๐Ÿ‡ธ Changed English reference files: ${changedEnglishFiles.length}`); + console.log(` Total changed files: ${allChangedFiles.length}`); + console.log(` Changed English reference files: ${changedEnglishFiles.length}`); if (changedEnglishFiles.length > 0) { console.log('๐Ÿ“„ Changed English files:'); @@ -35,7 +35,7 @@ function getChangedFiles() { return changedEnglishFiles; } catch (error) { - console.error('โŒ Error getting changed files:', error.message); + console.error(' Error getting changed files:', error.message); return []; } } @@ -67,7 +67,7 @@ function getFileModTime(filePath) { function checkTranslationStatus(changedEnglishFiles) { - console.log('\n๐ŸŒ Checking translation status...'); + const translationStatus = { needsUpdate: [], @@ -76,11 +76,11 @@ function checkTranslationStatus(changedEnglishFiles) { }; changedEnglishFiles.forEach(englishFile => { - console.log(`\n๐Ÿ“– Analyzing: ${englishFile}`); + const englishModTime = getFileModTime(englishFile); if (!englishModTime) { - console.log(` โš ๏ธ Could not get modification time for English file`); + console.log(`Could not get modification time for English file`); return; } @@ -148,15 +148,7 @@ function displaySummary(translationStatus) { console.log(`โœ… Up-to-date translations: ${translationStatus.upToDate.length}`); console.log('\n๐Ÿ’ก Next steps for Week 2+:'); - if (translationStatus.missing.length > 0) { - console.log(' - Create GitHub issues for missing translations'); - } - if (translationStatus.needsUpdate.length > 0) { - console.log(' - Create GitHub issues for outdated translations'); - } - console.log(' - Implement automated issue creation'); - console.log(' - Set up notification system for translation teams'); -} + function exploreRepoStructure() { console.log('\n๐Ÿ” REPOSITORY STRUCTURE ANALYSIS'); From 875a77cd3db85f6baa6a705f5ca63338efb42955 Mon Sep 17 00:00:00 2001 From: Divyansh013 Date: Thu, 12 Jun 2025 23:19:54 +0530 Subject: [PATCH 06/18] small fix --- .github/actions/translation-tracker/index.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/actions/translation-tracker/index.js b/.github/actions/translation-tracker/index.js index c22a9f4408..d4f3c04160 100644 --- a/.github/actions/translation-tracker/index.js +++ b/.github/actions/translation-tracker/index.js @@ -149,7 +149,7 @@ function displaySummary(translationStatus) { console.log('\n๐Ÿ’ก Next steps for Week 2+:'); - +} function exploreRepoStructure() { console.log('\n๐Ÿ” REPOSITORY STRUCTURE ANALYSIS'); console.log('โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•'); @@ -225,4 +225,4 @@ function main() { // Run the main function if (require.main === module) { main(); -} \ No newline at end of file +} From 5170eb676a71c2b467e97d372de64b9b1ea4978c Mon Sep 17 00:00:00 2001 From: Divyansh013 Date: Fri, 13 Jun 2025 15:49:36 +0530 Subject: [PATCH 07/18] post review changes --- .github/actions/translation-tracker/index.js | 145 ++++++++++-------- .../actions/translation-tracker/package.json | 4 +- .../actions/translation-tracker/test-local.js | 47 ++---- .../00_Shape_Primitives/description.mdx | 2 +- src/content/reference/en/p5/accelerationX.mdx | 2 - src/content/reference/en/p5/circle.mdx | 2 - src/content/reference/en/p5/triangle.mdx | 2 - 7 files changed, 95 insertions(+), 109 deletions(-) diff --git a/.github/actions/translation-tracker/index.js b/.github/actions/translation-tracker/index.js index d4f3c04160..fe1ba04807 100644 --- a/.github/actions/translation-tracker/index.js +++ b/.github/actions/translation-tracker/index.js @@ -2,17 +2,19 @@ const { execSync } = require('child_process'); const fs = require('fs'); const path = require('path'); - const SUPPORTED_LANGUAGES = ['es', 'hi', 'ko', 'zh-Hans']; -const REFERENCE_PATH = 'src/content/reference'; -const ENGLISH_PATH = path.join(REFERENCE_PATH, 'en'); -function getChangedFiles() { +function getChangedFiles(testFiles = null) { + // Allow passing test files for local development + if (testFiles) { + console.log('๐Ÿงช Using provided test files for local testing'); + return testFiles.filter(file => + file.startsWith('src/content/examples/en') && file.endsWith('.mdx') + ); + } + try { - - - const gitCommand = process.env.GITHUB_EVENT_NAME === 'pull_request' ? 'git diff --name-only HEAD~1 HEAD' : 'git diff --name-only HEAD~1 HEAD'; @@ -20,31 +22,25 @@ function getChangedFiles() { const changedFilesOutput = execSync(gitCommand, { encoding: 'utf8' }); const allChangedFiles = changedFilesOutput.trim().split('\n').filter(file => file.length > 0); - - const changedEnglishFiles = allChangedFiles.filter(file => - file.startsWith(ENGLISH_PATH) && file.endsWith('.mdx') + const changedExampleFiles = allChangedFiles.filter(file => + file.startsWith('src/content/examples/en') && file.endsWith('.mdx') ); - console.log(` Total changed files: ${allChangedFiles.length}`); - console.log(` Changed English reference files: ${changedEnglishFiles.length}`); + console.log(`๐Ÿ“Š Total changed files: ${allChangedFiles.length}`); + console.log(`๐Ÿ“– Changed English example files: ${changedExampleFiles.length}`); - if (changedEnglishFiles.length > 0) { - console.log('๐Ÿ“„ Changed English files:'); - changedEnglishFiles.forEach(file => console.log(` - ${file}`)); + if (changedExampleFiles.length > 0) { + console.log('๐Ÿ“„ Changed English example files:'); + changedExampleFiles.forEach(file => console.log(` - ${file}`)); } - return changedEnglishFiles; + return changedExampleFiles; } catch (error) { - console.error(' Error getting changed files:', error.message); + console.error('โŒ Error getting changed files:', error.message); return []; } } - -function getTranslationPath(englishFilePath, language) { - return englishFilePath.replace('/en/', `/${language}/`); -} - /** * Check if a file exists */ @@ -66,26 +62,24 @@ function getFileModTime(filePath) { } -function checkTranslationStatus(changedEnglishFiles) { - - +function checkTranslationStatus(changedExampleFiles) { const translationStatus = { needsUpdate: [], missing: [], upToDate: [] }; - changedEnglishFiles.forEach(englishFile => { - + changedExampleFiles.forEach(englishFile => { + console.log(`\n๐Ÿ“ Checking translations for: ${englishFile}`); const englishModTime = getFileModTime(englishFile); if (!englishModTime) { - console.log(`Could not get modification time for English file`); + console.log(`โš ๏ธ Could not get modification time for English file`); return; } SUPPORTED_LANGUAGES.forEach(language => { - const translationPath = getTranslationPath(englishFile, language); + const translationPath = englishFile.replace('/en/', `/${language}/`); const exists = fileExists(translationPath); if (!exists) { @@ -147,82 +141,99 @@ function displaySummary(translationStatus) { console.log(`โœ… Up-to-date translations: ${translationStatus.upToDate.length}`); - console.log('\n๐Ÿ’ก Next steps for Week 2+:'); - + } + +/** + * Explore repository structure (focusing on examples as requested) + */ function exploreRepoStructure() { console.log('\n๐Ÿ” REPOSITORY STRUCTURE ANALYSIS'); console.log('โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•'); try { - const referencePath = REFERENCE_PATH; - console.log(`๐Ÿ“ Reference path: ${referencePath}`); + const examplesPath = 'src/content/examples'; + console.log(`๐Ÿ“ Examples path: ${examplesPath}`); - if (fs.existsSync(referencePath)) { - const languages = fs.readdirSync(referencePath) - .filter(item => fs.statSync(path.join(referencePath, item)).isDirectory()) - .filter(item => !item.startsWith('.')); + if (fs.existsSync(examplesPath)) { + const languages = fs.readdirSync(examplesPath) + .filter(item => fs.statSync(path.join(examplesPath, item)).isDirectory()) + .filter(item => !item.startsWith('.') && item !== 'images'); console.log(`๐ŸŒ Available languages: ${languages.join(', ')}`); - // Count files in each language + // Count example files in each language languages.forEach(lang => { - const langPath = path.join(referencePath, lang); + const langPath = path.join(examplesPath, lang); try { - const subdirs = fs.readdirSync(langPath) + let totalFiles = 0; + const categories = fs.readdirSync(langPath) .filter(item => fs.statSync(path.join(langPath, item)).isDirectory()); - let totalFiles = 0; - subdirs.forEach(subdir => { - const subdirPath = path.join(langPath, subdir); - const files = fs.readdirSync(subdirPath) - .filter(file => file.endsWith('.mdx')); - totalFiles += files.length; + categories.forEach(category => { + const categoryPath = path.join(langPath, category); + const countFilesRecursively = (dir) => { + const items = fs.readdirSync(dir); + let count = 0; + items.forEach(item => { + const itemPath = path.join(dir, item); + if (fs.statSync(itemPath).isDirectory()) { + count += countFilesRecursively(itemPath); + } else if (item.endsWith('.mdx')) { + count++; + } + }); + return count; + }; + totalFiles += countFilesRecursively(categoryPath); }); - console.log(` ${lang}: ${totalFiles} files across ${subdirs.length} categories`); + console.log(` ${lang}: ${totalFiles} example files across ${categories.length} categories`); } catch (error) { console.log(` ${lang}: Error reading directory - ${error.message}`); } }); } else { - console.log(` Reference path does not exist: ${referencePath}`); + console.log(`โŒ Examples path does not exist: ${examplesPath}`); } } catch (error) { - console.error(' Error exploring repository structure:', error.message); + console.error('โŒ Error exploring repository structure:', error.message); } } -function main() { - console.log('p5.js Translation Sync Tracker - Week 1 Prototype'); - console.log('โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•'); +function main(testFiles = null) { + console.log('๐ŸŽฏ p5.js Example Translation Tracker - Week 1 Prototype'); + console.log('โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•'); console.log(`๐Ÿ“… Event: ${process.env.GITHUB_EVENT_NAME || 'local'}`); console.log(`๐Ÿ  Working directory: ${process.cwd()}`); - console.log(`๐ŸŽฏ Tracking languages: ${SUPPORTED_LANGUAGES.join(', ')}`); - - + console.log(`๐ŸŒ Tracking languages: ${SUPPORTED_LANGUAGES.join(', ')}`); + exploreRepoStructure(); + // Get changed files (supports both git and test files) + const changedExampleFiles = getChangedFiles(testFiles); - const changedEnglishFiles = getChangedFiles(); - - if (changedEnglishFiles.length === 0) { - console.log(' No changes detected in English reference files.'); - console.log(' Nothing to track for translations in this commit!'); + if (changedExampleFiles.length === 0) { + console.log('\nโœจ No changes detected in English example files.'); + console.log('๐Ÿ“ Nothing to track for translations in this commit!'); return; } - - const translationStatus = checkTranslationStatus(changedEnglishFiles); - - + const translationStatus = checkTranslationStatus(changedExampleFiles); + displaySummary(translationStatus); - - } -// Run the main function +// Export for testing +module.exports = { + main, + getChangedFiles, + checkTranslationStatus, + exploreRepoStructure +}; + + if (require.main === module) { main(); } diff --git a/.github/actions/translation-tracker/package.json b/.github/actions/translation-tracker/package.json index b0630b623e..7284cba02a 100644 --- a/.github/actions/translation-tracker/package.json +++ b/.github/actions/translation-tracker/package.json @@ -1,7 +1,7 @@ { "name": "p5js-translation-tracker", - "version": "1.0.0", - "description": "GitHub Action to track translation status for p5.js reference documentation", + "version": "0.1.1", + "description": "GitHub Action to track translation status for p5.js examples and documentation", "main": "index.js", "scripts": { "start": "node index.js", diff --git a/.github/actions/translation-tracker/test-local.js b/.github/actions/translation-tracker/test-local.js index c605389af7..79cd71a5e0 100755 --- a/.github/actions/translation-tracker/test-local.js +++ b/.github/actions/translation-tracker/test-local.js @@ -1,39 +1,20 @@ -#!/usr/bin/env node -/** - * Local test script for the translation tracker - * This simulates the GitHub Action environment for testing - */ -const { execSync } = require('child_process'); -const path = require('path'); +const { main } = require('./index.js'); -process.env.GITHUB_EVENT_NAME = 'push'; -process.env.GITHUB_WORKSPACE = process.cwd(); +// Test scenarios using actual example files that exist +const testFiles = [ + 'src/content/examples/en/01_Shapes_And_Color/00_Shape_Primitives/description.mdx', + 'src/content/examples/en/02_Animation_And_Variables/00_Drawing_Lines/description.mdx', + 'src/content/examples/en/03_Imported_Media/00_Words/description.mdx' +]; -console.log('๐Ÿงช LOCAL TEST: Translation Tracker'); -console.log('โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•'); -console.log(`๐Ÿ“ Working directory: ${process.cwd()}`); +console.log('๐Ÿงช Testing Example Translation Tracker Locally'); +console.log('===============================================\n'); -// Navigate to repository root -const repoRoot = path.resolve(__dirname, '../../..'); -process.chdir(repoRoot); +// Run the main function with test files +main(testFiles); -console.log(`๐Ÿ“ Changed to repository root: ${process.cwd()}`); - -try { - const gitLogOutput = execSync('git log --oneline -5', { encoding: 'utf8' }); - console.log(' Recent commits:'); - console.log(gitLogOutput); -} catch (error) { - console.log(' No git history available or not in a git repository'); -} - -console.log(' Running translation tracker...\n'); - -try { - require('./index.js'); -} catch (error) { - console.error('โŒ Error running translation tracker:', error.message); - console.error(error.stack); -} \ No newline at end of file +console.log('\n๐Ÿ’ก This demonstrates local testing capability as requested by mentor'); +console.log('๐Ÿ”ง The git logic is now separated and modular for easier testing'); +console.log('๐Ÿ“– Now tracking examples instead of tutorials as requested'); \ No newline at end of file diff --git a/src/content/examples/en/01_Shapes_And_Color/00_Shape_Primitives/description.mdx b/src/content/examples/en/01_Shapes_And_Color/00_Shape_Primitives/description.mdx index 428f348709..6d2b99d0e7 100644 --- a/src/content/examples/en/01_Shapes_And_Color/00_Shape_Primitives/description.mdx +++ b/src/content/examples/en/01_Shapes_And_Color/00_Shape_Primitives/description.mdx @@ -9,7 +9,7 @@ relatedReference: --- This program demonstrates the use of the basic shape -primitive functions +primitive functions (sample change for testing) square(), rect(), ellipse(), diff --git a/src/content/reference/en/p5/accelerationX.mdx b/src/content/reference/en/p5/accelerationX.mdx index 839003bd00..14edb024b5 100644 --- a/src/content/reference/en/p5/accelerationX.mdx +++ b/src/content/reference/en/p5/accelerationX.mdx @@ -8,8 +8,6 @@ description: > device along the x axis. Value is represented as meters per second squared.

- -

Test modification to demonstrate missing translation detection.

line: 23 isConstructor: false itemtype: property diff --git a/src/content/reference/en/p5/circle.mdx b/src/content/reference/en/p5/circle.mdx index 3501fcc5a5..5d60fbb3cc 100644 --- a/src/content/reference/en/p5/circle.mdx +++ b/src/content/reference/en/p5/circle.mdx @@ -6,8 +6,6 @@ file: src/core/shape/2d_primitives.js description: >

Draws a circle.

-

This is a test modification for the translation tracker.

-

A circle is a round shape defined by the x, y, and d parameters. diff --git a/src/content/reference/en/p5/triangle.mdx b/src/content/reference/en/p5/triangle.mdx index 7534675583..02740d2d1b 100644 --- a/src/content/reference/en/p5/triangle.mdx +++ b/src/content/reference/en/p5/triangle.mdx @@ -6,8 +6,6 @@ file: src/core/shape/2d_primitives.js description: >

Draws a triangle.

-

Test modification for translation tracker testing.

-

A triangle is a three-sided shape defined by three points. The first two parameters specify the triangle's first point (x1, y1). From 56abeec8affcde4440b254d7eb81dfb6add10d91 Mon Sep 17 00:00:00 2001 From: Divyansh013 Date: Sun, 15 Jun 2025 22:08:53 +0530 Subject: [PATCH 08/18] Week 2: Translation tracker with GitHub API and Hindi focus --- .../community-feedback-message.md | 97 ++++ .github/actions/translation-tracker/index.js | 283 ++++++++--- .../translation-tracker/package-lock.json | 457 ++++++++++++++++++ .../actions/translation-tracker/package.json | 6 +- .../translation-tracker/test-simulation.js | 91 ++++ .../actions/translation-tracker/test-week2.js | 36 ++ 6 files changed, 909 insertions(+), 61 deletions(-) create mode 100644 .github/actions/translation-tracker/community-feedback-message.md create mode 100644 .github/actions/translation-tracker/package-lock.json create mode 100644 .github/actions/translation-tracker/test-simulation.js create mode 100644 .github/actions/translation-tracker/test-week2.js diff --git a/.github/actions/translation-tracker/community-feedback-message.md b/.github/actions/translation-tracker/community-feedback-message.md new file mode 100644 index 0000000000..473e4049e7 --- /dev/null +++ b/.github/actions/translation-tracker/community-feedback-message.md @@ -0,0 +1,97 @@ +# ๐ŸŒ Community Feedback Needed: Translation Tracker Issue Template + +Hi p5.js community! ๐Ÿ‘‹ + +I'm working on a **GSoC 2024 project** to create an automated translation tracker for the p5.js website. As part of **Week 2**, I'm implementing a system that automatically creates GitHub issues when translations become outdated. + +## ๐ŸŽฏ What I'm Building + +The translation tracker will: +1. **Monitor changes** to English example/tutorial files +2. **Detect outdated translations** using GitHub API commit comparison +3. **Automatically create issues** to alert translators about needed updates +4. **Focus on Hindi first**, then expand to other languages + +## ๐Ÿ“‹ Issue Template Draft + +Here's the current issue template format that would be automatically generated: + +--- + +### ๐ŸŒ Update HI translation for description.mdx + +## ๐ŸŒ Translation Update Needed + +**File**: `src/content/examples/en/01_Shapes_And_Color/00_Shape_Primitives/description.mdx` +**Language**: Hindi (เคนเคฟเคจเฅเคฆเฅ€) +**Translation file**: `src/content/examples/hi/01_Shapes_And_Color/00_Shape_Primitives/description.mdx` + +### ๐Ÿ“… Timeline +- **English last updated**: 6/10/2024 by p5js-contributor +- **Translation last updated**: 6/1/2024 by hindi-translator + +### ๐Ÿ”— Quick Links +- [๐Ÿ“„ Current English file](https://github.com/processing/p5.js-website/blob/main/src/content/examples/en/01_Shapes_And_Color/00_Shape_Primitives/description.mdx) +- [๐Ÿ“ Translation file](https://github.com/processing/p5.js-website/blob/main/src/content/examples/hi/01_Shapes_And_Color/00_Shape_Primitives/description.mdx) +- [๐Ÿ” Compare changes](https://github.com/processing/p5.js-website/compare/def0987...abc1234) + +### ๐Ÿ“‹ What to do +1. Review the English changes in the file +2. Update the Hindi (เคนเคฟเคจเฅเคฆเฅ€) translation accordingly +3. Maintain the same structure and formatting +4. Test the translation for accuracy and cultural appropriateness + +### ๐Ÿ“ Recent English Changes +**Last commit**: [Update shape primitives documentation with better examples](https://github.com/processing/p5.js-website/commit/abc1234567890) + +--- +*This issue was automatically created by the p5.js Translation Tracker ๐Ÿค–* +*Need help? Check our [translation guidelines](https://github.com/processing/p5.js-website/blob/main/contributor_docs/translation.md)* + +--- + +## ๐Ÿค” Questions for the Community + +1. **Is this issue format helpful and clear?** What would you add/remove? + +2. **Should the issue title be bilingual?** + - Current: "๐ŸŒ Update HI translation for description.mdx" + - Alternative: "๐ŸŒ เคนเคฟเคจเฅเคฆเฅ€ เค…เคจเฅเคตเคพเคฆ เค…เคชเคกเฅ‡เคŸ / Update HI translation for description.mdx" + +3. **What labels should be automatically applied?** + - Current: `translation`, `lang-hi`, `help wanted` + - Suggestions for others? + +4. **Should we include diff information?** + - Show specific lines that changed (if easily obtainable)? + - Or is the "Compare changes" link sufficient? + +5. **Frequency concerns:** + - Should there be a cooldown period to avoid spamming issues? + - Batch multiple files into one issue? + +6. **Translation workflow preferences:** + - Should we assign issues to known translators automatically? + - Create issues in specific projects/milestones? + +## ๐Ÿš€ Timeline + +- **Week 1**: โœ… Basic detection logic +- **Week 2**: ๐Ÿ”„ Issue creation (current) +- **Week 3**: Enhanced issue management +- **Week 4**: Multi-language support expansion + +## ๐Ÿ™ How to Help + +Please share your thoughts on: +- Issue template format and content +- Translation workflow improvements +- Any missing information that would help translators +- Concerns about automation frequency + +Your feedback will directly shape how this tool works for the p5.js translation community! + +Thanks! ๐ŸŽจ + +--- +*@Divyansh013 | GSoC 2024 - p5.js Website Translation Automation* \ No newline at end of file diff --git a/.github/actions/translation-tracker/index.js b/.github/actions/translation-tracker/index.js index fe1ba04807..ad5db46ca5 100644 --- a/.github/actions/translation-tracker/index.js +++ b/.github/actions/translation-tracker/index.js @@ -1,12 +1,120 @@ const { execSync } = require('child_process'); const fs = require('fs'); const path = require('path'); +const core = require('@actions/core'); +const github = require('@actions/github'); +const { Octokit } = require('@octokit/rest'); -const SUPPORTED_LANGUAGES = ['es', 'hi', 'ko', 'zh-Hans']; +const SUPPORTED_LANGUAGES = ['hi']; // Start with Hindi only + +/** + * Week 2: GitHub API integration for commit tracking + */ +class GitHubCommitTracker { + constructor(token, owner, repo) { + this.octokit = new Octokit({ auth: token }); + this.owner = owner; + this.repo = repo; + } + + + async getLastCommit(filePath) { + try { + const { data } = await this.octokit.rest.repos.listCommits({ + owner: this.owner, + repo: this.repo, + path: filePath, + per_page: 1 + }); + + if (data.length > 0) { + return { + sha: data[0].sha, + date: new Date(data[0].commit.committer.date), + message: data[0].commit.message, + author: data[0].commit.author.name, + url: data[0].html_url + }; + } + return null; + } catch (error) { + console.error(`โŒ Error fetching commit for ${filePath}:`, error.message); + return null; + } + } + + + async createTranslationIssue(englishFile, language, commitInfo) { + const issueTitle = `๐ŸŒ Update ${language.toUpperCase()} translation for ${path.basename(englishFile)}`; + + const issueBody = this.formatIssueBody(englishFile, language, commitInfo); + + try { + const { data } = await this.octokit.rest.issues.create({ + owner: this.owner, + repo: this.repo, + title: issueTitle, + body: issueBody, + labels: ['translation', `lang-${language}`, 'help wanted'] + }); + + console.log(`โœ… Created issue #${data.number}: ${issueTitle}`); + return data; + } catch (error) { + console.error(`โŒ Error creating issue:`, error.message); + return null; + } + } + + + formatIssueBody(englishFile, language, commitInfo) { + const translationPath = englishFile.replace('/en/', `/${language}/`); + const englishCommit = commitInfo.english; + const translationCommit = commitInfo.translation; + + return `## ๐ŸŒ Translation Update Needed + +**File**: \`${englishFile}\` +**Language**: ${this.getLanguageDisplayName(language)} +**Translation file**: \`${translationPath}\` + +### ๐Ÿ“… Timeline +- **English last updated**: ${englishCommit.date.toLocaleDateString()} by ${englishCommit.author} +- **Translation last updated**: ${translationCommit ? translationCommit.date.toLocaleDateString() + ' by ' + translationCommit.author : 'Never translated'} + +### ๐Ÿ”— Quick Links +- [๐Ÿ“„ Current English file](https://github.com/${this.owner}/${this.repo}/blob/main/${englishFile}) +- [๐Ÿ“ Translation file](https://github.com/${this.owner}/${this.repo}/blob/main/${translationPath}) +- [๐Ÿ” Compare changes](https://github.com/${this.owner}/${this.repo}/compare/${translationCommit ? translationCommit.sha : 'HEAD'}...${englishCommit.sha}) + +### ๐Ÿ“‹ What to do +1. Review the English changes in the file +2. Update the ${this.getLanguageDisplayName(language)} translation accordingly +3. Maintain the same structure and formatting +4. Test the translation for accuracy and cultural appropriateness + +### ๐Ÿ“ Recent English Changes +**Last commit**: [${englishCommit.message}](${englishCommit.url}) + +--- +*This issue was automatically created by the p5.js Translation Tracker ๐Ÿค–* +*Need help? Check our [translation guidelines](https://github.com/processing/p5.js-website/blob/main/contributor_docs/translation.md)*`; + } + + + getLanguageDisplayName(langCode) { + const languages = { + 'es': 'Spanish (Espaรฑol)', + 'hi': 'Hindi (เคนเคฟเคจเฅเคฆเฅ€)', + 'ko': 'Korean (ํ•œ๊ตญ์–ด)', + 'zh-Hans': 'Chinese Simplified (็ฎ€ไฝ“ไธญๆ–‡)' + }; + return languages[langCode] || langCode; + } +} function getChangedFiles(testFiles = null) { - // Allow passing test files for local development if (testFiles) { console.log('๐Ÿงช Using provided test files for local testing'); return testFiles.filter(file => @@ -53,32 +161,30 @@ function fileExists(filePath) { } -function getFileModTime(filePath) { - try { - return fs.statSync(filePath).mtime; - } catch (error) { - return null; - } -} - - -function checkTranslationStatus(changedExampleFiles) { +async function checkTranslationStatus(changedExampleFiles, githubTracker, createIssues = false) { const translationStatus = { needsUpdate: [], missing: [], - upToDate: [] + upToDate: [], + issuesCreated: [] }; - changedExampleFiles.forEach(englishFile => { + for (const englishFile of changedExampleFiles) { console.log(`\n๐Ÿ“ Checking translations for: ${englishFile}`); - const englishModTime = getFileModTime(englishFile); - if (!englishModTime) { - console.log(`โš ๏ธ Could not get modification time for English file`); - return; + if (!githubTracker) { + console.log(`โš ๏ธ No GitHub tracker available - skipping commit analysis`); + console.log(`โ„น๏ธ To enable full Week 2 features, provide GITHUB_TOKEN`); + continue; } - SUPPORTED_LANGUAGES.forEach(language => { + const englishCommit = await githubTracker.getLastCommit(englishFile); + if (!englishCommit) { + console.log(`โš ๏ธ Could not get commit info for English file`); + continue; + } + + for (const language of SUPPORTED_LANGUAGES) { const translationPath = englishFile.replace('/en/', `/${language}/`); const exists = fileExists(translationPath); @@ -90,40 +196,70 @@ function checkTranslationStatus(changedExampleFiles) { translationPath, status: 'missing' }); - } else { - const translationModTime = getFileModTime(translationPath); - const isOutdated = translationModTime < englishModTime; + continue; + } + + const translationCommit = await githubTracker.getLastCommit(translationPath); + + if (!translationCommit) { + console.log(` โš ๏ธ ${language}: Could not get commit info for translation`); + continue; + } + + const isOutdated = translationCommit.date < englishCommit.date; + + if (isOutdated) { + console.log(` ๐Ÿ”„ ${language}: Needs update`); + console.log(` - English: ${englishCommit.date.toISOString()} (${englishCommit.sha.substring(0, 7)})`); + console.log(` - Translation: ${translationCommit.date.toISOString()} (${translationCommit.sha.substring(0, 7)})`); - if (isOutdated) { - console.log(` ๐Ÿ”„ ${language}: Needs update (English: ${englishModTime.toISOString()}, Translation: ${translationModTime.toISOString()})`); - translationStatus.needsUpdate.push({ - englishFile, - language, - translationPath, - status: 'outdated', - englishModTime, - translationModTime - }); - } else { - console.log(` โœ… ${language}: Up to date`); - translationStatus.upToDate.push({ - englishFile, - language, - translationPath, - status: 'up-to-date' - }); + const statusItem = { + englishFile, + language, + translationPath, + status: 'outdated', + commitInfo: { + english: englishCommit, + translation: translationCommit + } + }; + + translationStatus.needsUpdate.push(statusItem); + + // Week 2: Create issue if requested + if (createIssues && githubTracker) { + const issue = await githubTracker.createTranslationIssue( + englishFile, + language, + statusItem.commitInfo + ); + if (issue) { + translationStatus.issuesCreated.push({ + ...statusItem, + issueNumber: issue.number, + issueUrl: issue.html_url + }); + } } + } else { + console.log(` โœ… ${language}: Up to date`); + translationStatus.upToDate.push({ + englishFile, + language, + translationPath, + status: 'up-to-date' + }); } - }); - }); + } + } return translationStatus; } function displaySummary(translationStatus) { - console.log('\n๐Ÿ“Š TRANSLATION STATUS SUMMARY'); - console.log('โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•'); + console.log('\n๐Ÿ“Š WEEK 2 TRANSLATION STATUS SUMMARY'); + console.log('โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•'); console.log(`๐Ÿ†• Missing translations: ${translationStatus.missing.length}`); if (translationStatus.missing.length > 0) { @@ -141,15 +277,25 @@ function displaySummary(translationStatus) { console.log(`โœ… Up-to-date translations: ${translationStatus.upToDate.length}`); - + if (translationStatus.issuesCreated && translationStatus.issuesCreated.length > 0) { + console.log(`๐ŸŽซ Issues created: ${translationStatus.issuesCreated.length}`); + translationStatus.issuesCreated.forEach(item => { + console.log(` - Issue #${item.issueNumber}: ${item.language} - ${item.englishFile}`); + console.log(` ${item.issueUrl}`); + }); + } + + console.log('\n๐Ÿ’ก Week 2 Progress:'); + console.log('โœ… GitHub API integration for commit tracking'); + console.log('โœ… Automated issue creation for outdated translations'); + console.log('โœ… Enhanced issue formatting with helpful links'); + console.log('โœ… Focus on Hindi language (as planned)'); } -/** - * Explore repository structure (focusing on examples as requested) - */ + function exploreRepoStructure() { - console.log('\n๐Ÿ” REPOSITORY STRUCTURE ANALYSIS'); - console.log('โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•'); + console.log('\n๐Ÿ” WEEK 2 REPOSITORY STRUCTURE ANALYSIS'); + console.log('โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•'); try { const examplesPath = 'src/content/examples'; @@ -161,6 +307,7 @@ function exploreRepoStructure() { .filter(item => !item.startsWith('.') && item !== 'images'); console.log(`๐ŸŒ Available languages: ${languages.join(', ')}`); + console.log(`๐ŸŽฏ Week 2 focus: ${SUPPORTED_LANGUAGES.join(', ')} (Hindi only)`); // Count example files in each language languages.forEach(lang => { @@ -188,7 +335,8 @@ function exploreRepoStructure() { totalFiles += countFilesRecursively(categoryPath); }); - console.log(` ${lang}: ${totalFiles} example files across ${categories.length} categories`); + const indicator = SUPPORTED_LANGUAGES.includes(lang) ? '๐ŸŽฏ' : '๐Ÿ“‹'; + console.log(` ${indicator} ${lang}: ${totalFiles} example files across ${categories.length} categories`); } catch (error) { console.log(` ${lang}: Error reading directory - ${error.message}`); } @@ -202,16 +350,26 @@ function exploreRepoStructure() { } -function main(testFiles = null) { - console.log('๐ŸŽฏ p5.js Example Translation Tracker - Week 1 Prototype'); - console.log('โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•'); +async function main(testFiles = null, options = {}) { + console.log('๐ŸŽฏ p5.js Translation Tracker - Week 2 Prototype'); + console.log('โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•'); console.log(`๐Ÿ“… Event: ${process.env.GITHUB_EVENT_NAME || 'local'}`); console.log(`๐Ÿ  Working directory: ${process.cwd()}`); console.log(`๐ŸŒ Tracking languages: ${SUPPORTED_LANGUAGES.join(', ')}`); - + + let githubTracker = null; + const token = process.env.GITHUB_TOKEN || options.githubToken; + const [owner, repo] = (process.env.GITHUB_REPOSITORY || 'processing/p5.js-website').split('/'); + + if (token) { + githubTracker = new GitHubCommitTracker(token, owner, repo); + console.log(`๐Ÿ”— GitHub API initialized for ${owner}/${repo}`); + } else { + console.log('โš ๏ธ No GitHub token provided - running in basic mode'); + } + exploreRepoStructure(); - // Get changed files (supports both git and test files) const changedExampleFiles = getChangedFiles(testFiles); if (changedExampleFiles.length === 0) { @@ -220,8 +378,14 @@ function main(testFiles = null) { return; } - const translationStatus = checkTranslationStatus(changedExampleFiles); - + const createIssues = options.createIssues !== false; // Default to true + const translationStatus = await checkTranslationStatus( + changedExampleFiles, + githubTracker, + createIssues + ); + + // Display summary displaySummary(translationStatus); } @@ -230,7 +394,8 @@ module.exports = { main, getChangedFiles, checkTranslationStatus, - exploreRepoStructure + exploreRepoStructure, + GitHubCommitTracker }; diff --git a/.github/actions/translation-tracker/package-lock.json b/.github/actions/translation-tracker/package-lock.json new file mode 100644 index 0000000000..8c8d568011 --- /dev/null +++ b/.github/actions/translation-tracker/package-lock.json @@ -0,0 +1,457 @@ +{ + "name": "p5js-translation-tracker", + "version": "0.2.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "p5js-translation-tracker", + "version": "0.2.0", + "license": "LGPL-2.1", + "dependencies": { + "@actions/core": "^1.10.0", + "@actions/github": "^5.1.1", + "@octokit/rest": "^20.0.2", + "simple-git": "^3.24.0" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@actions/core": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@actions/core/-/core-1.11.1.tgz", + "integrity": "sha512-hXJCSrkwfA46Vd9Z3q4cpEpHB1rL5NG04+/rbqW9d3+CSvtB1tYe8UTpAlixa1vj0m/ULglfEK2UKxMGxCxv5A==", + "dependencies": { + "@actions/exec": "^1.1.1", + "@actions/http-client": "^2.0.1" + } + }, + "node_modules/@actions/exec": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@actions/exec/-/exec-1.1.1.tgz", + "integrity": "sha512-+sCcHHbVdk93a0XT19ECtO/gIXoxvdsgQLzb2fE2/5sIZmWQuluYyjPQtrtTHdU1YzTZ7bAPN4sITq2xi1679w==", + "dependencies": { + "@actions/io": "^1.0.1" + } + }, + "node_modules/@actions/github": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/@actions/github/-/github-5.1.1.tgz", + "integrity": "sha512-Nk59rMDoJaV+mHCOJPXuvB1zIbomlKS0dmSIqPGxd0enAXBnOfn4VWF+CGtRCwXZG9Epa54tZA7VIRlJDS8A6g==", + "dependencies": { + "@actions/http-client": "^2.0.1", + "@octokit/core": "^3.6.0", + "@octokit/plugin-paginate-rest": "^2.17.0", + "@octokit/plugin-rest-endpoint-methods": "^5.13.0" + } + }, + "node_modules/@actions/http-client": { + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/@actions/http-client/-/http-client-2.2.3.tgz", + "integrity": "sha512-mx8hyJi/hjFvbPokCg4uRd4ZX78t+YyRPtnKWwIl+RzNaVuFpQHfmlGVfsKEJN8LwTCvL+DfVgAM04XaHkm6bA==", + "dependencies": { + "tunnel": "^0.0.6", + "undici": "^5.25.4" + } + }, + "node_modules/@actions/io": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/@actions/io/-/io-1.1.3.tgz", + "integrity": "sha512-wi9JjgKLYS7U/z8PPbco+PvTb/nRWjeoFlJ1Qer83k/3C5PHQi28hiVdeE2kHXmIL99mQFawx8qt/JPjZilJ8Q==" + }, + "node_modules/@fastify/busboy": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/@fastify/busboy/-/busboy-2.1.1.tgz", + "integrity": "sha512-vBZP4NlzfOlerQTnba4aqZoMhE/a9HY7HRqoOPaETQcSQuWEIyZMHGfVu6w9wGtGK5fED5qRs2DteVCjOH60sA==", + "engines": { + "node": ">=14" + } + }, + "node_modules/@kwsites/file-exists": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@kwsites/file-exists/-/file-exists-1.1.1.tgz", + "integrity": "sha512-m9/5YGR18lIwxSFDwfE3oA7bWuq9kdau6ugN4H2rJeyhFQZcG9AgSHkQtSD15a8WvTgfz9aikZMrKPHvbpqFiw==", + "dependencies": { + "debug": "^4.1.1" + } + }, + "node_modules/@kwsites/promise-deferred": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@kwsites/promise-deferred/-/promise-deferred-1.1.1.tgz", + "integrity": "sha512-GaHYm+c0O9MjZRu0ongGBRbinu8gVAMd2UZjji6jVmqKtZluZnptXGWhz1E8j8D2HJ3f/yMxKAUC0b+57wncIw==" + }, + "node_modules/@octokit/auth-token": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/@octokit/auth-token/-/auth-token-2.5.0.tgz", + "integrity": "sha512-r5FVUJCOLl19AxiuZD2VRZ/ORjp/4IN98Of6YJoJOkY75CIBuYfmiNHGrDwXr+aLGG55igl9QrxX3hbiXlLb+g==", + "dependencies": { + "@octokit/types": "^6.0.3" + } + }, + "node_modules/@octokit/core": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/@octokit/core/-/core-3.6.0.tgz", + "integrity": "sha512-7RKRKuA4xTjMhY+eG3jthb3hlZCsOwg3rztWh75Xc+ShDWOfDDATWbeZpAHBNRpm4Tv9WgBMOy1zEJYXG6NJ7Q==", + "dependencies": { + "@octokit/auth-token": "^2.4.4", + "@octokit/graphql": "^4.5.8", + "@octokit/request": "^5.6.3", + "@octokit/request-error": "^2.0.5", + "@octokit/types": "^6.0.3", + "before-after-hook": "^2.2.0", + "universal-user-agent": "^6.0.0" + } + }, + "node_modules/@octokit/endpoint": { + "version": "6.0.12", + "resolved": "https://registry.npmjs.org/@octokit/endpoint/-/endpoint-6.0.12.tgz", + "integrity": "sha512-lF3puPwkQWGfkMClXb4k/eUT/nZKQfxinRWJrdZaJO85Dqwo/G0yOC434Jr2ojwafWJMYqFGFa5ms4jJUgujdA==", + "dependencies": { + "@octokit/types": "^6.0.3", + "is-plain-object": "^5.0.0", + "universal-user-agent": "^6.0.0" + } + }, + "node_modules/@octokit/graphql": { + "version": "4.8.0", + "resolved": "https://registry.npmjs.org/@octokit/graphql/-/graphql-4.8.0.tgz", + "integrity": "sha512-0gv+qLSBLKF0z8TKaSKTsS39scVKF9dbMxJpj3U0vC7wjNWFuIpL/z76Qe2fiuCbDRcJSavkXsVtMS6/dtQQsg==", + "dependencies": { + "@octokit/request": "^5.6.0", + "@octokit/types": "^6.0.3", + "universal-user-agent": "^6.0.0" + } + }, + "node_modules/@octokit/openapi-types": { + "version": "12.11.0", + "resolved": "https://registry.npmjs.org/@octokit/openapi-types/-/openapi-types-12.11.0.tgz", + "integrity": "sha512-VsXyi8peyRq9PqIz/tpqiL2w3w80OgVMwBHltTml3LmVvXiphgeqmY9mvBw9Wu7e0QWk/fqD37ux8yP5uVekyQ==" + }, + "node_modules/@octokit/plugin-paginate-rest": { + "version": "2.21.3", + "resolved": "https://registry.npmjs.org/@octokit/plugin-paginate-rest/-/plugin-paginate-rest-2.21.3.tgz", + "integrity": "sha512-aCZTEf0y2h3OLbrgKkrfFdjRL6eSOo8komneVQJnYecAxIej7Bafor2xhuDJOIFau4pk0i/P28/XgtbyPF0ZHw==", + "dependencies": { + "@octokit/types": "^6.40.0" + }, + "peerDependencies": { + "@octokit/core": ">=2" + } + }, + "node_modules/@octokit/plugin-rest-endpoint-methods": { + "version": "5.16.2", + "resolved": "https://registry.npmjs.org/@octokit/plugin-rest-endpoint-methods/-/plugin-rest-endpoint-methods-5.16.2.tgz", + "integrity": "sha512-8QFz29Fg5jDuTPXVtey05BLm7OB+M8fnvE64RNegzX7U+5NUXcOcnpTIK0YfSHBg8gYd0oxIq3IZTe9SfPZiRw==", + "dependencies": { + "@octokit/types": "^6.39.0", + "deprecation": "^2.3.1" + }, + "peerDependencies": { + "@octokit/core": ">=3" + } + }, + "node_modules/@octokit/request": { + "version": "5.6.3", + "resolved": "https://registry.npmjs.org/@octokit/request/-/request-5.6.3.tgz", + "integrity": "sha512-bFJl0I1KVc9jYTe9tdGGpAMPy32dLBXXo1dS/YwSCTL/2nd9XeHsY616RE3HPXDVk+a+dBuzyz5YdlXwcDTr2A==", + "dependencies": { + "@octokit/endpoint": "^6.0.1", + "@octokit/request-error": "^2.1.0", + "@octokit/types": "^6.16.1", + "is-plain-object": "^5.0.0", + "node-fetch": "^2.6.7", + "universal-user-agent": "^6.0.0" + } + }, + "node_modules/@octokit/request-error": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/@octokit/request-error/-/request-error-2.1.0.tgz", + "integrity": "sha512-1VIvgXxs9WHSjicsRwq8PlR2LR2x6DwsJAaFgzdi0JfJoGSO8mYI/cHJQ+9FbN21aa+DrgNLnwObmyeSC8Rmpg==", + "dependencies": { + "@octokit/types": "^6.0.3", + "deprecation": "^2.0.0", + "once": "^1.4.0" + } + }, + "node_modules/@octokit/rest": { + "version": "20.1.2", + "resolved": "https://registry.npmjs.org/@octokit/rest/-/rest-20.1.2.tgz", + "integrity": "sha512-GmYiltypkHHtihFwPRxlaorG5R9VAHuk/vbszVoRTGXnAsY60wYLkh/E2XiFmdZmqrisw+9FaazS1i5SbdWYgA==", + "dependencies": { + "@octokit/core": "^5.0.2", + "@octokit/plugin-paginate-rest": "11.4.4-cjs.2", + "@octokit/plugin-request-log": "^4.0.0", + "@octokit/plugin-rest-endpoint-methods": "13.3.2-cjs.1" + }, + "engines": { + "node": ">= 18" + } + }, + "node_modules/@octokit/rest/node_modules/@octokit/auth-token": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/@octokit/auth-token/-/auth-token-4.0.0.tgz", + "integrity": "sha512-tY/msAuJo6ARbK6SPIxZrPBms3xPbfwBrulZe0Wtr/DIY9lje2HeV1uoebShn6mx7SjCHif6EjMvoREj+gZ+SA==", + "engines": { + "node": ">= 18" + } + }, + "node_modules/@octokit/rest/node_modules/@octokit/core": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/@octokit/core/-/core-5.2.1.tgz", + "integrity": "sha512-dKYCMuPO1bmrpuogcjQ8z7ICCH3FP6WmxpwC03yjzGfZhj9fTJg6+bS1+UAplekbN2C+M61UNllGOOoAfGCrdQ==", + "dependencies": { + "@octokit/auth-token": "^4.0.0", + "@octokit/graphql": "^7.1.0", + "@octokit/request": "^8.4.1", + "@octokit/request-error": "^5.1.1", + "@octokit/types": "^13.0.0", + "before-after-hook": "^2.2.0", + "universal-user-agent": "^6.0.0" + }, + "engines": { + "node": ">= 18" + } + }, + "node_modules/@octokit/rest/node_modules/@octokit/endpoint": { + "version": "9.0.6", + "resolved": "https://registry.npmjs.org/@octokit/endpoint/-/endpoint-9.0.6.tgz", + "integrity": "sha512-H1fNTMA57HbkFESSt3Y9+FBICv+0jFceJFPWDePYlR/iMGrwM5ph+Dd4XRQs+8X+PUFURLQgX9ChPfhJ/1uNQw==", + "dependencies": { + "@octokit/types": "^13.1.0", + "universal-user-agent": "^6.0.0" + }, + "engines": { + "node": ">= 18" + } + }, + "node_modules/@octokit/rest/node_modules/@octokit/graphql": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/@octokit/graphql/-/graphql-7.1.1.tgz", + "integrity": "sha512-3mkDltSfcDUoa176nlGoA32RGjeWjl3K7F/BwHwRMJUW/IteSa4bnSV8p2ThNkcIcZU2umkZWxwETSSCJf2Q7g==", + "dependencies": { + "@octokit/request": "^8.4.1", + "@octokit/types": "^13.0.0", + "universal-user-agent": "^6.0.0" + }, + "engines": { + "node": ">= 18" + } + }, + "node_modules/@octokit/rest/node_modules/@octokit/openapi-types": { + "version": "24.2.0", + "resolved": "https://registry.npmjs.org/@octokit/openapi-types/-/openapi-types-24.2.0.tgz", + "integrity": "sha512-9sIH3nSUttelJSXUrmGzl7QUBFul0/mB8HRYl3fOlgHbIWG+WnYDXU3v/2zMtAvuzZ/ed00Ei6on975FhBfzrg==" + }, + "node_modules/@octokit/rest/node_modules/@octokit/plugin-paginate-rest": { + "version": "11.4.4-cjs.2", + "resolved": "https://registry.npmjs.org/@octokit/plugin-paginate-rest/-/plugin-paginate-rest-11.4.4-cjs.2.tgz", + "integrity": "sha512-2dK6z8fhs8lla5PaOTgqfCGBxgAv/le+EhPs27KklPhm1bKObpu6lXzwfUEQ16ajXzqNrKMujsFyo9K2eaoISw==", + "dependencies": { + "@octokit/types": "^13.7.0" + }, + "engines": { + "node": ">= 18" + }, + "peerDependencies": { + "@octokit/core": "5" + } + }, + "node_modules/@octokit/rest/node_modules/@octokit/plugin-request-log": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/@octokit/plugin-request-log/-/plugin-request-log-4.0.1.tgz", + "integrity": "sha512-GihNqNpGHorUrO7Qa9JbAl0dbLnqJVrV8OXe2Zm5/Y4wFkZQDfTreBzVmiRfJVfE4mClXdihHnbpyyO9FSX4HA==", + "engines": { + "node": ">= 18" + }, + "peerDependencies": { + "@octokit/core": "5" + } + }, + "node_modules/@octokit/rest/node_modules/@octokit/plugin-rest-endpoint-methods": { + "version": "13.3.2-cjs.1", + "resolved": "https://registry.npmjs.org/@octokit/plugin-rest-endpoint-methods/-/plugin-rest-endpoint-methods-13.3.2-cjs.1.tgz", + "integrity": "sha512-VUjIjOOvF2oELQmiFpWA1aOPdawpyaCUqcEBc/UOUnj3Xp6DJGrJ1+bjUIIDzdHjnFNO6q57ODMfdEZnoBkCwQ==", + "dependencies": { + "@octokit/types": "^13.8.0" + }, + "engines": { + "node": ">= 18" + }, + "peerDependencies": { + "@octokit/core": "^5" + } + }, + "node_modules/@octokit/rest/node_modules/@octokit/request": { + "version": "8.4.1", + "resolved": "https://registry.npmjs.org/@octokit/request/-/request-8.4.1.tgz", + "integrity": "sha512-qnB2+SY3hkCmBxZsR/MPCybNmbJe4KAlfWErXq+rBKkQJlbjdJeS85VI9r8UqeLYLvnAenU8Q1okM/0MBsAGXw==", + "dependencies": { + "@octokit/endpoint": "^9.0.6", + "@octokit/request-error": "^5.1.1", + "@octokit/types": "^13.1.0", + "universal-user-agent": "^6.0.0" + }, + "engines": { + "node": ">= 18" + } + }, + "node_modules/@octokit/rest/node_modules/@octokit/request-error": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/@octokit/request-error/-/request-error-5.1.1.tgz", + "integrity": "sha512-v9iyEQJH6ZntoENr9/yXxjuezh4My67CBSu9r6Ve/05Iu5gNgnisNWOsoJHTP6k0Rr0+HQIpnH+kyammu90q/g==", + "dependencies": { + "@octokit/types": "^13.1.0", + "deprecation": "^2.0.0", + "once": "^1.4.0" + }, + "engines": { + "node": ">= 18" + } + }, + "node_modules/@octokit/rest/node_modules/@octokit/types": { + "version": "13.10.0", + "resolved": "https://registry.npmjs.org/@octokit/types/-/types-13.10.0.tgz", + "integrity": "sha512-ifLaO34EbbPj0Xgro4G5lP5asESjwHracYJvVaPIyXMuiuXLlhic3S47cBdTb+jfODkTE5YtGCLt3Ay3+J97sA==", + "dependencies": { + "@octokit/openapi-types": "^24.2.0" + } + }, + "node_modules/@octokit/types": { + "version": "6.41.0", + "resolved": "https://registry.npmjs.org/@octokit/types/-/types-6.41.0.tgz", + "integrity": "sha512-eJ2jbzjdijiL3B4PrSQaSjuF2sPEQPVCPzBvTHJD9Nz+9dw2SGH4K4xeQJ77YfTq5bRQ+bD8wT11JbeDPmxmGg==", + "dependencies": { + "@octokit/openapi-types": "^12.11.0" + } + }, + "node_modules/before-after-hook": { + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/before-after-hook/-/before-after-hook-2.2.3.tgz", + "integrity": "sha512-NzUnlZexiaH/46WDhANlyR2bXRopNg4F/zuSA3OpZnllCUgRaOF2znDioDWrmbNVsuZk6l9pMquQB38cfBZwkQ==" + }, + "node_modules/debug": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.1.tgz", + "integrity": "sha512-KcKCqiftBJcZr++7ykoDIEwSa3XWowTfNPo92BYxjXiyYEVrUQh2aLyhxBCwww+heortUFxEJYcRzosstTEBYQ==", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/deprecation": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/deprecation/-/deprecation-2.3.1.tgz", + "integrity": "sha512-xmHIy4F3scKVwMsQ4WnVaS8bHOx0DmVwRywosKhaILI0ywMDWPtBSku2HNxRvF7jtwDRsoEwYQSfbxj8b7RlJQ==" + }, + "node_modules/is-plain-object": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-5.0.0.tgz", + "integrity": "sha512-VRSzKkbMm5jMDoKLbltAkFQ5Qr7VDiTFGXxYFXXowVj387GeGNOCsOH6Msy00SGZ3Fp84b1Naa1psqgcCIEP5Q==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==" + }, + "node_modules/node-fetch": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.7.0.tgz", + "integrity": "sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==", + "dependencies": { + "whatwg-url": "^5.0.0" + }, + "engines": { + "node": "4.x || >=6.0.0" + }, + "peerDependencies": { + "encoding": "^0.1.0" + }, + "peerDependenciesMeta": { + "encoding": { + "optional": true + } + } + }, + "node_modules/once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", + "dependencies": { + "wrappy": "1" + } + }, + "node_modules/simple-git": { + "version": "3.28.0", + "resolved": "https://registry.npmjs.org/simple-git/-/simple-git-3.28.0.tgz", + "integrity": "sha512-Rs/vQRwsn1ILH1oBUy8NucJlXmnnLeLCfcvbSehkPzbv3wwoFWIdtfd6Ndo6ZPhlPsCZ60CPI4rxurnwAa+a2w==", + "dependencies": { + "@kwsites/file-exists": "^1.1.1", + "@kwsites/promise-deferred": "^1.1.1", + "debug": "^4.4.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/steveukx/git-js?sponsor=1" + } + }, + "node_modules/tr46": { + "version": "0.0.3", + "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", + "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==" + }, + "node_modules/tunnel": { + "version": "0.0.6", + "resolved": "https://registry.npmjs.org/tunnel/-/tunnel-0.0.6.tgz", + "integrity": "sha512-1h/Lnq9yajKY2PEbBadPXj3VxsDDu844OnaAo52UVmIzIvwwtBPIuNvkjuzBlTWpfJyUbG3ez0KSBibQkj4ojg==", + "engines": { + "node": ">=0.6.11 <=0.7.0 || >=0.7.3" + } + }, + "node_modules/undici": { + "version": "5.29.0", + "resolved": "https://registry.npmjs.org/undici/-/undici-5.29.0.tgz", + "integrity": "sha512-raqeBD6NQK4SkWhQzeYKd1KmIG6dllBOTt55Rmkt4HtI9mwdWtJljnrXjAFUBLTSN67HWrOIZ3EPF4kjUw80Bg==", + "dependencies": { + "@fastify/busboy": "^2.0.0" + }, + "engines": { + "node": ">=14.0" + } + }, + "node_modules/universal-user-agent": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/universal-user-agent/-/universal-user-agent-6.0.1.tgz", + "integrity": "sha512-yCzhz6FN2wU1NiiQRogkTQszlQSlpWaw8SvVegAc+bDxbzHgh1vX8uIe8OYyMH6DwH+sdTJsgMl36+mSMdRJIQ==" + }, + "node_modules/webidl-conversions": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz", + "integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==" + }, + "node_modules/whatwg-url": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz", + "integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==", + "dependencies": { + "tr46": "~0.0.3", + "webidl-conversions": "^3.0.0" + } + }, + "node_modules/wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==" + } + } +} diff --git a/.github/actions/translation-tracker/package.json b/.github/actions/translation-tracker/package.json index 7284cba02a..fb554f92a2 100644 --- a/.github/actions/translation-tracker/package.json +++ b/.github/actions/translation-tracker/package.json @@ -1,6 +1,6 @@ { "name": "p5js-translation-tracker", - "version": "0.1.1", + "version": "0.2.0", "description": "GitHub Action to track translation status for p5.js examples and documentation", "main": "index.js", "scripts": { @@ -21,6 +21,8 @@ }, "dependencies": { "@actions/core": "^1.10.0", - "@actions/github": "^5.1.1" + "@actions/github": "^5.1.1", + "@octokit/rest": "^20.0.2", + "simple-git": "^3.24.0" } } \ No newline at end of file diff --git a/.github/actions/translation-tracker/test-simulation.js b/.github/actions/translation-tracker/test-simulation.js new file mode 100644 index 0000000000..444e043cbb --- /dev/null +++ b/.github/actions/translation-tracker/test-simulation.js @@ -0,0 +1,91 @@ + + +const { GitHubCommitTracker } = require('./index.js'); + + +class MockGitHubTracker extends GitHubCommitTracker { + constructor() { + super(null, 'processing', 'p5.js-website'); // Call super with mock values + } + + async getLastCommit(filePath) { + + const mockCommits = { + 'src/content/examples/en/01_Shapes_And_Color/00_Shape_Primitives/description.mdx': { + sha: 'abc1234567890', + date: new Date('2024-06-10T10:00:00Z'), + message: 'Update shape primitives documentation with better examples', + author: 'p5js-contributor', + url: 'https://github.com/processing/p5.js-website/commit/abc1234567890' + }, + 'src/content/examples/hi/01_Shapes_And_Color/00_Shape_Primitives/description.mdx': { + sha: 'def0987654321', + date: new Date('2024-06-01T15:30:00Z'), + message: 'เค†เค•เคพเคฐ เค†เคฆเคฟเคฎ เคฆเคธเฅเคคเคพเคตเฅ‡เคœเคผเฅ€เค•เคฐเคฃ เค•เคพ เค…เคจเฅเคตเคพเคฆ', + author: 'hindi-translator', + url: 'https://github.com/processing/p5.js-website/commit/def0987654321' + } + }; + + return mockCommits[filePath] || null; + } + + async createTranslationIssue(englishFile, language, commitInfo) { + + const issueTitle = `๐ŸŒ Update ${language.toUpperCase()} translation for ${require('path').basename(englishFile)}`; + const issueBody = this.formatIssueBody(englishFile, language, commitInfo); + + console.log('\n๐ŸŽซ SIMULATED ISSUE CREATION:'); + console.log('โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•'); + console.log(`Title: ${issueTitle}`); + console.log('Body Preview:'); + console.log(issueBody.substring(0, 300) + '...'); + + + return { + number: 42, + html_url: 'https://github.com/processing/p5.js-website/issues/42', + title: issueTitle + }; + } +} + +async function runSimulation() { + console.log('๐ŸŽญ Week 2 Translation Tracker Simulation'); + console.log('=========================================\n'); + + const mockTracker = new MockGitHubTracker(); + const englishFile = 'src/content/examples/en/01_Shapes_And_Color/00_Shape_Primitives/description.mdx'; + const language = 'hi'; + + console.log('๐Ÿ“‹ Simulation Scenario:'); + console.log(` - English file updated: June 10, 2024`); + console.log(` - Hindi translation: June 1, 2024 (9 days outdated)`); + console.log(` - Expected result: Create update issue\n`); + + + const englishCommit = await mockTracker.getLastCommit(englishFile); + const translationPath = englishFile.replace('/en/', `/${language}/`); + const translationCommit = await mockTracker.getLastCommit(translationPath); + + console.log('๐Ÿ“Š Commit Analysis:'); + console.log(` English: ${englishCommit.date.toISOString()} (${englishCommit.sha.substring(0, 7)})`); + console.log(` Hindi: ${translationCommit.date.toISOString()} (${translationCommit.sha.substring(0, 7)})`); + console.log(` Outdated: ${translationCommit.date < englishCommit.date ? 'YES' : 'NO'}\n`); + + if (translationCommit.date < englishCommit.date) { + await mockTracker.createTranslationIssue(englishFile, language, { + english: englishCommit, + translation: translationCommit + }); + } + + console.log('\n๐ŸŽฏ Week 2 Simulation Results:'); + console.log('โœ… GitHub API commit tracking simulation'); + console.log('โœ… Outdated detection logic verification'); + console.log('โœ… Issue creation format testing'); + console.log('โœ… Ready for real-world testing!'); +} + + +runSimulation().catch(console.error); \ No newline at end of file diff --git a/.github/actions/translation-tracker/test-week2.js b/.github/actions/translation-tracker/test-week2.js new file mode 100644 index 0000000000..59c5a4fceb --- /dev/null +++ b/.github/actions/translation-tracker/test-week2.js @@ -0,0 +1,36 @@ +/** + * Week 2 Local Testing Script + * Tests GitHub API integration and issue creation (without actually creating issues) + */ + +const { main } = require('./index.js'); + +// Test scenarios using actual example files +const testFiles = [ + 'src/content/examples/en/01_Shapes_And_Color/00_Shape_Primitives/description.mdx', + 'src/content/examples/en/02_Animation_And_Variables/00_Drawing_Lines/description.mdx' +]; + +console.log('๐Ÿงช Testing Week 2 Translation Tracker with GitHub API'); +console.log('=====================================================\n'); + +// Test options +const options = { + createIssues: false, // Set to false for local testing to avoid creating real issues + githubToken: process.env.GITHUB_TOKEN || null // Will use token if provided via environment +}; + +// Run the main function with test files and options +main(testFiles, options).then(() => { + console.log('\n๐Ÿ’ก Week 2 Testing Complete!'); + console.log('๐Ÿ”ง To test with real GitHub API:'); + console.log(' export GITHUB_TOKEN=your_token_here'); + console.log(' node test-week2.js'); + console.log('\n๐Ÿ“‹ Week 2 Features Tested:'); + console.log('โœ… GitHub API commit tracking'); + console.log('โœ… Enhanced outdated detection'); + console.log('โœ… Issue creation logic (dry run)'); + console.log('โœ… Hindi-only focus'); +}).catch(error => { + console.error('โŒ Test failed:', error.message); +}); \ No newline at end of file From d9c98c6fb3bab0ce6209674de031c67798b84947 Mon Sep 17 00:00:00 2001 From: Divyansh013 Date: Sun, 15 Jun 2025 22:09:26 +0530 Subject: [PATCH 09/18] Test: Update English example for translation tracker testing --- .../en/01_Shapes_And_Color/00_Shape_Primitives/description.mdx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/content/examples/en/01_Shapes_And_Color/00_Shape_Primitives/description.mdx b/src/content/examples/en/01_Shapes_And_Color/00_Shape_Primitives/description.mdx index 6d2b99d0e7..f590bcb858 100644 --- a/src/content/examples/en/01_Shapes_And_Color/00_Shape_Primitives/description.mdx +++ b/src/content/examples/en/01_Shapes_And_Color/00_Shape_Primitives/description.mdx @@ -9,7 +9,7 @@ relatedReference: --- This program demonstrates the use of the basic shape -primitive functions (sample change for testing) +primitive functions. Week 2 testing: improved documentation for better clarity. square(), rect(), ellipse(), From 1976d9dcd4482fc8aa81434ee98e155344f332f7 Mon Sep 17 00:00:00 2001 From: Divyansh013 Date: Sun, 15 Jun 2025 22:26:13 +0530 Subject: [PATCH 10/18] Fix: Add automatic branch detection to translation tracker --- .../actions/translation-tracker/debug-test.js | 99 +++++++++++++++++++ .../translation-tracker/final-test-summary.js | 92 +++++++++++++++++ .github/actions/translation-tracker/index.js | 96 +++++++++++++++++- .../translation-tracker/test-all-files.js | 51 ++++++++++ .../test-branch-specific.js | 99 +++++++++++++++++++ .../translation-tracker/test-create-issue.js | 61 ++++++++++++ .../test-multiple-files.js | 98 ++++++++++++++++++ .../translation-tracker/test-real-api.js | 59 +++++++++++ 8 files changed, 650 insertions(+), 5 deletions(-) create mode 100644 .github/actions/translation-tracker/debug-test.js create mode 100644 .github/actions/translation-tracker/final-test-summary.js create mode 100644 .github/actions/translation-tracker/test-all-files.js create mode 100644 .github/actions/translation-tracker/test-branch-specific.js create mode 100644 .github/actions/translation-tracker/test-create-issue.js create mode 100644 .github/actions/translation-tracker/test-multiple-files.js create mode 100644 .github/actions/translation-tracker/test-real-api.js diff --git a/.github/actions/translation-tracker/debug-test.js b/.github/actions/translation-tracker/debug-test.js new file mode 100644 index 0000000000..cb15b3acd8 --- /dev/null +++ b/.github/actions/translation-tracker/debug-test.js @@ -0,0 +1,99 @@ +/** + * Debug Test for GitHub API + * Shows detailed commit information to debug why outdated files aren't detected + */ + +const { Octokit } = require('@octokit/rest'); +const simpleGit = require('simple-git'); + +console.log('๐Ÿ” Debug Test - Analyzing GitHub Commit Data'); +console.log('=============================================\n'); + +async function debugCommitData() { + const token = process.env.GITHUB_TOKEN; + if (!token) { + console.log('โŒ No GITHUB_TOKEN provided'); + return; + } + + const octokit = new Octokit({ auth: token }); + const git = simpleGit(); + + // Test files + const englishFile = 'src/content/examples/en/01_Shapes_And_Color/00_Shape_Primitives/description.mdx'; + const hindiFile = 'src/content/examples/hi/01_Shapes_And_Color/00_Shape_Primitives/description.mdx'; + + console.log('๐Ÿ“‹ Files to analyze:'); + console.log(` English: ${englishFile}`); + console.log(` Hindi: ${hindiFile}\n`); + + try { + // Get local commit data for comparison + console.log('๐Ÿ  LOCAL Git Analysis:'); + const englishLog = await git.log({ file: englishFile, n: 1 }); + const hindiLog = await git.log({ file: hindiFile, n: 1 }); + + console.log(` English latest: ${englishLog.latest.hash} (${englishLog.latest.date})`); + console.log(` Hindi latest: ${hindiLog.latest.hash} (${hindiLog.latest.date})\n`); + + // Get GitHub API data + console.log('๐Ÿ”— GITHUB API Analysis:'); + + // For English file + console.log(' Fetching English file commits...'); + const englishCommits = await octokit.rest.repos.listCommits({ + owner: 'Divyansh013', + repo: 'p5.js-website-new', + path: englishFile, + per_page: 5 + }); + + console.log(` English commits found: ${englishCommits.data.length}`); + if (englishCommits.data.length > 0) { + const latest = englishCommits.data[0]; + console.log(` Latest English commit: ${latest.sha.substring(0, 8)} (${latest.commit.committer.date})`); + console.log(` Message: ${latest.commit.message}`); + } + + // For Hindi file + console.log('\n Fetching Hindi file commits...'); + const hindiCommits = await octokit.rest.repos.listCommits({ + owner: 'Divyansh013', + repo: 'p5.js-website-new', + path: hindiFile, + per_page: 5 + }); + + console.log(` Hindi commits found: ${hindiCommits.data.length}`); + if (hindiCommits.data.length > 0) { + const latest = hindiCommits.data[0]; + console.log(` Latest Hindi commit: ${latest.sha.substring(0, 8)} (${latest.commit.committer.date})`); + console.log(` Message: ${latest.commit.message}`); + } + + // Compare dates + if (englishCommits.data.length > 0 && hindiCommits.data.length > 0) { + const englishDate = new Date(englishCommits.data[0].commit.committer.date); + const hindiDate = new Date(hindiCommits.data[0].commit.committer.date); + + console.log('\n๐Ÿ“Š DATE COMPARISON:'); + console.log(` English: ${englishDate.toISOString()}`); + console.log(` Hindi: ${hindiDate.toISOString()}`); + console.log(` Difference: ${Math.floor((englishDate - hindiDate) / (1000 * 60 * 60 * 24))} days`); + + if (englishDate > hindiDate) { + console.log(' โœ… DETECTED: Hindi translation is OUTDATED!'); + } else { + console.log(' โŒ NOT DETECTED: System thinks Hindi is up to date'); + } + } + + } catch (error) { + console.error('โŒ Error:', error.message); + if (error.status === 404) { + console.error(' Repository or file not found'); + } + } +} + +debugCommitData(); \ No newline at end of file diff --git a/.github/actions/translation-tracker/final-test-summary.js b/.github/actions/translation-tracker/final-test-summary.js new file mode 100644 index 0000000000..c53c1507e1 --- /dev/null +++ b/.github/actions/translation-tracker/final-test-summary.js @@ -0,0 +1,92 @@ +/** + * Final Week 2 Test Summary + * Comprehensive demonstration of all fixed features + */ + +const { main } = require('./index.js'); + +console.log('๐Ÿ† Week 2 Translation Tracker - FINAL TEST'); +console.log('=========================================='); +console.log('๐Ÿ“‹ Testing all fixed features with branch detection\n'); + +async function runFinalTests() { + const token = process.env.GITHUB_TOKEN; + + if (!token) { + console.log('โŒ No GitHub token - limited testing available'); + return; + } + + // Override to use your fork + process.env.GITHUB_REPOSITORY = 'Divyansh013/p5.js-website-new'; + + console.log('๐Ÿ”ง Final Test Configuration:'); + console.log(` Repository: Divyansh013/p5.js-website-new`); + console.log(` Branch Detection: โœ… ENABLED (auto-detects week2)`); + console.log(` GitHub API: โœ… REAL API calls`); + console.log(` Issue Creation: ๐Ÿ”ด DISABLED (repo settings)`); + console.log(' Commit Analysis: โœ… REAL commit tracking\n'); + + // Test 1: File we know is outdated + console.log('๐Ÿ“‹ TEST 1: Known Outdated Translation'); + console.log('====================================='); + + const outdatedTest = [ + 'src/content/examples/en/01_Shapes_And_Color/00_Shape_Primitives/description.mdx' + ]; + + const options = { + createIssues: false, // Disable due to repo settings + githubToken: token + }; + + try { + await main(outdatedTest, options); + console.log('โœ… Test 1 PASSED: Correctly detected outdated Hindi translation'); + } catch (error) { + console.error('โŒ Test 1 FAILED:', error.message); + } + + console.log('\n๐Ÿ“‹ TEST 2: Multiple Files Analysis'); + console.log('=================================='); + + // Test with multiple files to see different scenarios + const multipleTest = [ + 'src/content/examples/en/01_Shapes_And_Color/00_Shape_Primitives/description.mdx', + 'src/content/examples/en/02_Animation_And_Variables/00_Drawing_Lines/description.mdx' + ]; + + try { + await main(multipleTest, options); + console.log('โœ… Test 2 PASSED: Multiple file analysis completed'); + } catch (error) { + console.error('โŒ Test 2 FAILED:', error.message); + } + + console.log('\n๐ŸŽ‰ WEEK 2 FEATURES VALIDATED:'); + console.log('=============================='); + console.log('โœ… Branch Detection: Auto-detects current branch (week2)'); + console.log('โœ… GitHub API Integration: Real commit tracking from your fork'); + console.log('โœ… Outdated Detection: Correctly identifies 135+ day difference'); + console.log('โœ… Enhanced Logging: Detailed commit information and timestamps'); + console.log('โœ… Issue Format: Ready for creation (disabled due to repo settings)'); + console.log('โœ… Hindi Focus: Targeted language support as planned'); + console.log('โœ… Error Handling: Graceful fallback and clear error messages'); + + console.log('\n๐Ÿ’ก PRODUCTION READINESS:'); + console.log('========================'); + console.log('๐ŸŸข Ready for real p5.js repository use'); + console.log('๐ŸŸข Handles different branch scenarios automatically'); + console.log('๐ŸŸข Robust error handling and fallbacks'); + console.log('๐ŸŸข Comprehensive commit-based tracking'); + console.log('๐ŸŸข Automated issue creation (when enabled)'); + + console.log('\n๐Ÿš€ NEXT STEPS FOR DEPLOYMENT:'); + console.log('============================='); + console.log('1. Deploy to processing/p5.js-website repository'); + console.log('2. Test with actual translation workflows'); + console.log('3. Enable issue creation for real tracking'); + console.log('4. Monitor and gather community feedback'); +} + +runFinalTests(); \ No newline at end of file diff --git a/.github/actions/translation-tracker/index.js b/.github/actions/translation-tracker/index.js index ad5db46ca5..1d808f9067 100644 --- a/.github/actions/translation-tracker/index.js +++ b/.github/actions/translation-tracker/index.js @@ -8,37 +8,122 @@ const { Octokit } = require('@octokit/rest'); const SUPPORTED_LANGUAGES = ['hi']; // Start with Hindi only /** - * Week 2: GitHub API integration for commit tracking + * Week 2: GitHub API integration for commit tracking with branch detection */ class GitHubCommitTracker { constructor(token, owner, repo) { this.octokit = new Octokit({ auth: token }); this.owner = owner; this.repo = repo; + this.currentBranch = this.detectCurrentBranch(); + console.log(`๐ŸŒฟ Using branch: ${this.currentBranch}`); + } + + /** + * Detect the current git branch + */ + detectCurrentBranch() { + try { + // Try different methods to get current branch + + // Method 1: GitHub Actions environment + if (process.env.GITHUB_HEAD_REF) { + // For pull requests + return process.env.GITHUB_HEAD_REF; + } + + if (process.env.GITHUB_REF_NAME) { + // For push events + return process.env.GITHUB_REF_NAME; + } + + // Method 2: Git command + try { + const branch = execSync('git rev-parse --abbrev-ref HEAD', { encoding: 'utf8' }).trim(); + if (branch && branch !== 'HEAD') { + return branch; + } + } catch (gitError) { + console.log(`โš ๏ธ Git command failed: ${gitError.message}`); + } + + // Method 3: Git symbolic-ref (alternative) + try { + const ref = execSync('git symbolic-ref HEAD', { encoding: 'utf8' }).trim(); + return ref.replace('refs/heads/', ''); + } catch (refError) { + console.log(`โš ๏ธ Git symbolic-ref failed: ${refError.message}`); + } + + // Fallback to main + console.log('โš ๏ธ Could not detect branch, defaulting to main'); + return 'main'; + + } catch (error) { + console.log(`โš ๏ธ Branch detection error: ${error.message}, using main`); + return 'main'; + } } async getLastCommit(filePath) { try { + console.log(` ๐Ÿ” Querying commits for ${filePath} on branch ${this.currentBranch}`); + const { data } = await this.octokit.rest.repos.listCommits({ owner: this.owner, repo: this.repo, + sha: this.currentBranch, // Use detected branch path: filePath, per_page: 1 }); if (data.length > 0) { - return { + const commit = { sha: data[0].sha, date: new Date(data[0].commit.committer.date), message: data[0].commit.message, author: data[0].commit.author.name, url: data[0].html_url }; + + console.log(` โœ… Found commit: ${commit.sha.substring(0, 7)} (${commit.date.toISOString()})`); + return commit; } + + console.log(` โš ๏ธ No commits found for ${filePath} on branch ${this.currentBranch}`); return null; } catch (error) { - console.error(`โŒ Error fetching commit for ${filePath}:`, error.message); + console.error(` โŒ Error fetching commit for ${filePath}:`, error.message); + + // If branch-specific query fails, try main branch as fallback + if (this.currentBranch !== 'main') { + console.log(` ๐Ÿ”„ Retrying with main branch...`); + try { + const { data } = await this.octokit.rest.repos.listCommits({ + owner: this.owner, + repo: this.repo, + sha: 'main', + path: filePath, + per_page: 1 + }); + + if (data.length > 0) { + const commit = { + sha: data[0].sha, + date: new Date(data[0].commit.committer.date), + message: data[0].commit.message, + author: data[0].commit.author.name, + url: data[0].html_url + }; + console.log(` โœ… Found commit on main: ${commit.sha.substring(0, 7)} (${commit.date.toISOString()})`); + return commit; + } + } catch (fallbackError) { + console.error(` โŒ Fallback to main also failed:`, fallbackError.message); + } + } + return null; } } @@ -77,14 +162,15 @@ class GitHubCommitTracker { **File**: \`${englishFile}\` **Language**: ${this.getLanguageDisplayName(language)} **Translation file**: \`${translationPath}\` +**Branch**: \`${this.currentBranch}\` ### ๐Ÿ“… Timeline - **English last updated**: ${englishCommit.date.toLocaleDateString()} by ${englishCommit.author} - **Translation last updated**: ${translationCommit ? translationCommit.date.toLocaleDateString() + ' by ' + translationCommit.author : 'Never translated'} ### ๐Ÿ”— Quick Links -- [๐Ÿ“„ Current English file](https://github.com/${this.owner}/${this.repo}/blob/main/${englishFile}) -- [๐Ÿ“ Translation file](https://github.com/${this.owner}/${this.repo}/blob/main/${translationPath}) +- [๐Ÿ“„ Current English file](https://github.com/${this.owner}/${this.repo}/blob/${this.currentBranch}/${englishFile}) +- [๐Ÿ“ Translation file](https://github.com/${this.owner}/${this.repo}/blob/${this.currentBranch}/${translationPath}) - [๐Ÿ” Compare changes](https://github.com/${this.owner}/${this.repo}/compare/${translationCommit ? translationCommit.sha : 'HEAD'}...${englishCommit.sha}) ### ๐Ÿ“‹ What to do diff --git a/.github/actions/translation-tracker/test-all-files.js b/.github/actions/translation-tracker/test-all-files.js new file mode 100644 index 0000000000..d602381aa1 --- /dev/null +++ b/.github/actions/translation-tracker/test-all-files.js @@ -0,0 +1,51 @@ +/** + * Test ALL Example Files + * This will check every English example file for translation status + */ + +const { main } = require('./index.js'); + +console.log('๐ŸŒ Testing ALL Example Files Translation Status'); +console.log('==============================================='); +console.log('๐Ÿ“‹ This will check EVERY English example file\n'); + +async function testAllFiles() { + const token = process.env.GITHUB_TOKEN; + + if (!token) { + console.log('โŒ No GitHub token - set it first:'); + console.log(' export GITHUB_TOKEN=your_token_here'); + return; + } + + // Override to use your fork + process.env.GITHUB_REPOSITORY = 'Divyansh013/p5.js-website-new'; + + console.log('๐Ÿ”ง Configuration:'); + console.log(` Repository: Divyansh013/p5.js-website-new`); + console.log(` Mode: CHECK ALL EXAMPLE FILES`); + console.log(` GitHub Token: โœ… Provided`); + console.log(` Issue Creation: ๐Ÿ”ด DISABLED (dry run)\n`); + + const options = { + createIssues: false, // Don't spam with issues + githubToken: token + }; + + try { + console.log('๐Ÿš€ Running tracker on ALL example files...\n'); + + // Pass null to testFiles - this will make it check ALL changed files + // But since we're in test mode, let's simulate checking multiple files + await main(null, options); + + console.log('\n๐ŸŽ‰ ALL FILES CHECK COMPLETE!'); + console.log('๐Ÿ“Š This shows what would happen in a real scenario'); + console.log('๐Ÿ’ก In production, it only checks files that changed in a commit'); + + } catch (error) { + console.error('\nโŒ Test failed:', error.message); + } +} + +testAllFiles(); \ No newline at end of file diff --git a/.github/actions/translation-tracker/test-branch-specific.js b/.github/actions/translation-tracker/test-branch-specific.js new file mode 100644 index 0000000000..ad237b689b --- /dev/null +++ b/.github/actions/translation-tracker/test-branch-specific.js @@ -0,0 +1,99 @@ +/** + * Branch-specific test for Week 2 commits + * Tests with our actual week2 branch where recent changes exist + */ + +const { Octokit } = require('@octokit/rest'); + +console.log('๐ŸŽฏ Branch-Specific GitHub API Test'); +console.log('==================================='); +console.log('๐ŸŒฟ Testing week2 branch specifically\n'); + +async function testWeek2Branch() { + const token = process.env.GITHUB_TOKEN; + if (!token) { + console.log('โŒ No GITHUB_TOKEN provided'); + return; + } + + const octokit = new Octokit({ auth: token }); + + // Test files + const englishFile = 'src/content/examples/en/01_Shapes_And_Color/00_Shape_Primitives/description.mdx'; + const hindiFile = 'src/content/examples/hi/01_Shapes_And_Color/00_Shape_Primitives/description.mdx'; + + console.log('๐Ÿ“‹ Files to analyze on week2 branch:'); + console.log(` English: ${englishFile}`); + console.log(` Hindi: ${hindiFile}\n`); + + try { + // Get commits from week2 branch specifically + console.log('๐Ÿ”— GITHUB API Analysis (week2 branch):'); + + // For English file on week2 branch + console.log(' Fetching English file commits from week2...'); + const englishCommits = await octokit.rest.repos.listCommits({ + owner: 'Divyansh013', + repo: 'p5.js-website-new', + sha: 'week2', // Specify the branch! + path: englishFile, + per_page: 5 + }); + + console.log(` English commits found: ${englishCommits.data.length}`); + if (englishCommits.data.length > 0) { + const latest = englishCommits.data[0]; + console.log(` โœ… Latest English commit: ${latest.sha.substring(0, 8)} (${latest.commit.committer.date})`); + console.log(` Message: "${latest.commit.message}"`); + } + + // For Hindi file on week2 branch + console.log('\n Fetching Hindi file commits from week2...'); + const hindiCommits = await octokit.rest.repos.listCommits({ + owner: 'Divyansh013', + repo: 'p5.js-website-new', + sha: 'week2', // Specify the branch! + path: hindiFile, + per_page: 5 + }); + + console.log(` Hindi commits found: ${hindiCommits.data.length}`); + if (hindiCommits.data.length > 0) { + const latest = hindiCommits.data[0]; + console.log(` โœ… Latest Hindi commit: ${latest.sha.substring(0, 8)} (${latest.commit.committer.date})`); + console.log(` Message: "${latest.commit.message}"`); + } + + // Compare dates + if (englishCommits.data.length > 0 && hindiCommits.data.length > 0) { + const englishDate = new Date(englishCommits.data[0].commit.committer.date); + const hindiDate = new Date(hindiCommits.data[0].commit.committer.date); + + console.log('\n๐ŸŽฏ WEEK2 BRANCH DATE COMPARISON:'); + console.log(` English: ${englishDate.toISOString()}`); + console.log(` Hindi: ${hindiDate.toISOString()}`); + + const daysDiff = Math.floor((englishDate - hindiDate) / (1000 * 60 * 60 * 24)); + console.log(` Difference: ${daysDiff} days`); + + if (englishDate > hindiDate) { + console.log(' ๐Ÿšจ CORRECTLY DETECTED: Hindi translation is OUTDATED!'); + console.log(' ๐Ÿ“ An issue should be created for this outdated translation.'); + } else { + console.log(' โœ… Hindi translation is up to date'); + } + } + + console.log('\n๐Ÿ’ก Key Learning:'); + console.log(' The tracker needs to specify the correct branch when querying GitHub API!'); + console.log(' Currently it defaults to main branch, missing recent week2 changes.'); + + } catch (error) { + console.error('โŒ Error:', error.message); + if (error.status === 404) { + console.error(' Repository, branch, or file not found'); + } + } +} + +testWeek2Branch(); \ No newline at end of file diff --git a/.github/actions/translation-tracker/test-create-issue.js b/.github/actions/translation-tracker/test-create-issue.js new file mode 100644 index 0000000000..6dd0af7349 --- /dev/null +++ b/.github/actions/translation-tracker/test-create-issue.js @@ -0,0 +1,61 @@ +/** + * Test Real GitHub Issue Creation + * This will create an actual issue on your repository! + */ + +const { main } = require('./index.js'); + +console.log('๐ŸŽซ Testing REAL GitHub Issue Creation'); +console.log('====================================='); +console.log('โš ๏ธ WARNING: This will create an actual GitHub issue!'); +console.log('๐ŸŽฏ Target: Your fork with outdated Hindi translation\n'); + +// Test with the file we know is outdated +const testFiles = [ + 'src/content/examples/en/01_Shapes_And_Color/00_Shape_Primitives/description.mdx' +]; + +// Test options - ENABLE issue creation +const options = { + createIssues: true, // This will create real issues! + githubToken: process.env.GITHUB_TOKEN, + repository: 'Divyansh013/p5.js-website-new' +}; + +async function testIssueCreation() { + // Override environment for your fork + process.env.GITHUB_REPOSITORY = 'Divyansh013/p5.js-website-new'; + + console.log('โš™๏ธ Configuration:'); + console.log(` Repository: ${process.env.GITHUB_REPOSITORY}`); + console.log(` Create Issues: ${options.createIssues ? '๐ŸŸข ENABLED' : '๐Ÿ”ด DISABLED'}`); + console.log(` GitHub Token: ${process.env.GITHUB_TOKEN ? 'โœ… Provided' : 'โŒ Missing'}`); + + if (!process.env.GITHUB_TOKEN) { + console.log('\nโŒ No GitHub token - cannot create issues'); + return; + } + + try { + console.log('\n๐Ÿš€ Running tracker with issue creation enabled...\n'); + + await main(testFiles, options); + + console.log('\n๐ŸŽ‰ ISSUE CREATION TEST COMPLETE!'); + console.log('โœ… Check your GitHub repository for new issues'); + console.log('๐Ÿ“‹ Issues should be labeled with: translation, lang-hi, help wanted'); + console.log('๐Ÿ”— Visit: https://github.com/Divyansh013/p5.js-website-new/issues'); + + } catch (error) { + console.error('\nโŒ Issue creation failed:', error.message); + console.error('๐Ÿ” Check:'); + console.error(' - GitHub token has issues:write permission'); + console.error(' - Repository exists and is accessible'); + console.error(' - Network connectivity'); + } +} + +console.log('โณ Starting in 3 seconds...'); +setTimeout(() => { + testIssueCreation(); +}, 3000); \ No newline at end of file diff --git a/.github/actions/translation-tracker/test-multiple-files.js b/.github/actions/translation-tracker/test-multiple-files.js new file mode 100644 index 0000000000..e2c1c502fe --- /dev/null +++ b/.github/actions/translation-tracker/test-multiple-files.js @@ -0,0 +1,98 @@ +/** + * Test Multiple Example Files + * Manually select several files to show different translation statuses + */ + +const { main } = require('./index.js'); +const fs = require('fs'); +const path = require('path'); + +console.log('๐Ÿ“š Testing Multiple Example Files'); +console.log('================================='); +console.log('๐ŸŽฏ Checking several files to show different scenarios\n'); + +function findExampleFiles(limit = 5) { + const examplesPath = 'src/content/examples/en'; + const files = []; + + function scanDirectory(dir, currentFiles) { + if (currentFiles.length >= limit) return currentFiles; + + try { + const items = fs.readdirSync(dir); + + for (const item of items) { + if (currentFiles.length >= limit) break; + + const itemPath = path.join(dir, item); + const stat = fs.statSync(itemPath); + + if (stat.isDirectory()) { + scanDirectory(itemPath, currentFiles); + } else if (item === 'description.mdx') { + currentFiles.push(itemPath); + } + } + } catch (error) { + console.log(`โš ๏ธ Error reading ${dir}: ${error.message}`); + } + + return currentFiles; + } + + return scanDirectory(examplesPath, files); +} + +async function testMultipleFiles() { + const token = process.env.GITHUB_TOKEN; + + if (!token) { + console.log('โŒ No GitHub token - set it first:'); + console.log(' GITHUB_TOKEN=your_token node test-multiple-files.js'); + return; + } + + // Override to use your fork + process.env.GITHUB_REPOSITORY = 'Divyansh013/p5.js-website-new'; + + // Find several example files to test + const testFiles = findExampleFiles(5); + + console.log('๐Ÿ”ง Configuration:'); + console.log(` Repository: Divyansh013/p5.js-website-new`); + console.log(` Files to check: ${testFiles.length}`); + console.log(` GitHub Token: โœ… Provided`); + console.log(` Issue Creation: ๐Ÿ”ด DISABLED (dry run)\n`); + + console.log('๐Ÿ“‹ Files being checked:'); + testFiles.forEach((file, index) => { + console.log(` ${index + 1}. ${file}`); + }); + console.log(''); + + const options = { + createIssues: false, // Don't create issues + githubToken: token + }; + + try { + console.log('๐Ÿš€ Running tracker on multiple files...\n'); + + await main(testFiles, options); + + console.log('\n๐ŸŽ‰ MULTIPLE FILES CHECK COMPLETE!'); + console.log('๐Ÿ“Š This shows different translation statuses:'); + console.log(' โ€ข Some files may be outdated (Hindi older than English)'); + console.log(' โ€ข Some files may be up-to-date (Hindi newer than English)'); + console.log(' โ€ข Some files may have missing translations'); + console.log('\n๐Ÿ’ก In real usage:'); + console.log(' โ€ข Only files changed in a commit are checked'); + console.log(' โ€ข Issues are created automatically for outdated translations'); + console.log(' โ€ข The system focuses on one language at a time (Hindi)'); + + } catch (error) { + console.error('\nโŒ Test failed:', error.message); + } +} + +testMultipleFiles(); \ No newline at end of file diff --git a/.github/actions/translation-tracker/test-real-api.js b/.github/actions/translation-tracker/test-real-api.js new file mode 100644 index 0000000000..624d4f2798 --- /dev/null +++ b/.github/actions/translation-tracker/test-real-api.js @@ -0,0 +1,59 @@ +/** + * Real GitHub API Test for User's Fork + * Tests with actual commit data from Divyansh013/p5.js-website-new + */ + +const { main } = require('./index.js'); + +console.log('๐Ÿ”ฅ Testing Translation Tracker with REAL GitHub API'); +console.log('==================================================='); +console.log('๐ŸŽฏ Target: Divyansh013/p5.js-website-new (YOUR FORK)'); +console.log('๐Ÿ“… Testing recent commits on week2 branch\n'); + +// Test with the file we just modified +const testFiles = [ + 'src/content/examples/en/01_Shapes_And_Color/00_Shape_Primitives/description.mdx' +]; + +// Test options for your fork +const options = { + createIssues: false, // Set to true if you want to actually create issues + githubToken: process.env.GITHUB_TOKEN, + // Override the repository to use your fork + repository: 'Divyansh013/p5.js-website-new' +}; + +// Custom main function that uses your fork +async function testWithFork() { + // Override environment variable for this test + process.env.GITHUB_REPOSITORY = 'Divyansh013/p5.js-website-new'; + + console.log('๐Ÿ”ง Configuration:'); + console.log(` Repository: ${process.env.GITHUB_REPOSITORY}`); + console.log(` Branch: week2`); + console.log(` GitHub Token: ${process.env.GITHUB_TOKEN ? 'โœ… Provided' : 'โŒ Missing'}`); + console.log(` Create Issues: ${options.createIssues ? 'YES' : 'NO (dry run)'}\n`); + + try { + await main(testFiles, options); + + console.log('\n๐ŸŽ‰ Real API Test Results:'); + console.log('โœ… GitHub API successfully connected to your fork'); + console.log('โœ… Commit data retrieved from your repository'); + console.log('โœ… Translation status analysis completed'); + console.log('\n๐Ÿ’ก Next Steps:'); + console.log('โ€ข To create real issues, set createIssues: true'); + console.log('โ€ข Check GitHub API rate limits if needed'); + console.log('โ€ข Test with different file changes'); + + } catch (error) { + console.error('โŒ Test failed:', error.message); + console.error('๐Ÿ” Check:'); + console.error(' - GitHub token permissions'); + console.error(' - Repository access'); + console.error(' - Network connectivity'); + } +} + +// Run the test +testWithFork(); \ No newline at end of file From 71e70493aeea35e75ea0b30960c44fd304198f23 Mon Sep 17 00:00:00 2001 From: Divyansh013 Date: Mon, 23 Jun 2025 00:30:20 +0530 Subject: [PATCH 11/18] week 2 complete --- .github/actions/translation-tracker/README.md | 48 +++ .../community-feedback-message.md | 97 ------ .../actions/translation-tracker/debug-test.js | 99 ------ .../translation-tracker/final-test-summary.js | 92 ------ .github/actions/translation-tracker/index.js | 312 ++++++++++-------- .../actions/translation-tracker/package.json | 5 +- .../translation-tracker/test-all-files.js | 51 --- .../test-branch-specific.js | 99 ------ .../translation-tracker/test-create-issue.js | 61 ---- .../actions/translation-tracker/test-local.js | 0 .../test-multiple-files.js | 98 ------ .../translation-tracker/test-real-api.js | 59 ---- .../translation-tracker/test-simulation.js | 91 ----- .../actions/translation-tracker/test-week2.js | 36 -- 14 files changed, 223 insertions(+), 925 deletions(-) create mode 100644 .github/actions/translation-tracker/README.md delete mode 100644 .github/actions/translation-tracker/community-feedback-message.md delete mode 100644 .github/actions/translation-tracker/debug-test.js delete mode 100644 .github/actions/translation-tracker/final-test-summary.js delete mode 100644 .github/actions/translation-tracker/test-all-files.js delete mode 100644 .github/actions/translation-tracker/test-branch-specific.js delete mode 100644 .github/actions/translation-tracker/test-create-issue.js mode change 100755 => 100644 .github/actions/translation-tracker/test-local.js delete mode 100644 .github/actions/translation-tracker/test-multiple-files.js delete mode 100644 .github/actions/translation-tracker/test-real-api.js delete mode 100644 .github/actions/translation-tracker/test-simulation.js delete mode 100644 .github/actions/translation-tracker/test-week2.js diff --git a/.github/actions/translation-tracker/README.md b/.github/actions/translation-tracker/README.md new file mode 100644 index 0000000000..1fbcc5f01e --- /dev/null +++ b/.github/actions/translation-tracker/README.md @@ -0,0 +1,48 @@ +# p5.js Translation Tracker + +A GitHub Action to track translation status for p5.js examples and documentation. + +## Files Structure + +- `index.js` - Main implementation (Week 1 + Week 2 functionality) +- `package.json` - Dependencies and metadata +- `test-local.js` - Local testing script +- `node_modules/` - Dependencies (auto-generated) +- `package-lock.json` - Dependency lock file (auto-generated) + +## Features + +### Week 1 (File-based tracking) +- Track changed English example files using git +- Compare file modification times +- Support local testing with test files +- Comprehensive repository structure analysis + +### Week 2 (GitHub API integration) +- Real commit-based comparison using GitHub API +- Automated GitHub issue creation for outdated translations +- Enhanced issue templates with helpful links +- Backward compatibility with Week 1 + +## Usage + +### Local Testing (Week 1 mode) +```bash +node test-local.js +``` + +### Production with GitHub API (Week 2 mode) +```bash +GITHUB_TOKEN=your_token GITHUB_REPOSITORY=owner/repo node index.js +``` + +## Supported Languages +- Spanish (es) +- Hindi (hi) +- Korean (ko) +- Chinese Simplified (zh-Hans) + +## Dependencies +- `@actions/core` - GitHub Actions toolkit +- `@actions/github` - GitHub Actions GitHub integration +- `@octokit/rest` - GitHub API client for Week 2 features \ No newline at end of file diff --git a/.github/actions/translation-tracker/community-feedback-message.md b/.github/actions/translation-tracker/community-feedback-message.md deleted file mode 100644 index 473e4049e7..0000000000 --- a/.github/actions/translation-tracker/community-feedback-message.md +++ /dev/null @@ -1,97 +0,0 @@ -# ๐ŸŒ Community Feedback Needed: Translation Tracker Issue Template - -Hi p5.js community! ๐Ÿ‘‹ - -I'm working on a **GSoC 2024 project** to create an automated translation tracker for the p5.js website. As part of **Week 2**, I'm implementing a system that automatically creates GitHub issues when translations become outdated. - -## ๐ŸŽฏ What I'm Building - -The translation tracker will: -1. **Monitor changes** to English example/tutorial files -2. **Detect outdated translations** using GitHub API commit comparison -3. **Automatically create issues** to alert translators about needed updates -4. **Focus on Hindi first**, then expand to other languages - -## ๐Ÿ“‹ Issue Template Draft - -Here's the current issue template format that would be automatically generated: - ---- - -### ๐ŸŒ Update HI translation for description.mdx - -## ๐ŸŒ Translation Update Needed - -**File**: `src/content/examples/en/01_Shapes_And_Color/00_Shape_Primitives/description.mdx` -**Language**: Hindi (เคนเคฟเคจเฅเคฆเฅ€) -**Translation file**: `src/content/examples/hi/01_Shapes_And_Color/00_Shape_Primitives/description.mdx` - -### ๐Ÿ“… Timeline -- **English last updated**: 6/10/2024 by p5js-contributor -- **Translation last updated**: 6/1/2024 by hindi-translator - -### ๐Ÿ”— Quick Links -- [๐Ÿ“„ Current English file](https://github.com/processing/p5.js-website/blob/main/src/content/examples/en/01_Shapes_And_Color/00_Shape_Primitives/description.mdx) -- [๐Ÿ“ Translation file](https://github.com/processing/p5.js-website/blob/main/src/content/examples/hi/01_Shapes_And_Color/00_Shape_Primitives/description.mdx) -- [๐Ÿ” Compare changes](https://github.com/processing/p5.js-website/compare/def0987...abc1234) - -### ๐Ÿ“‹ What to do -1. Review the English changes in the file -2. Update the Hindi (เคนเคฟเคจเฅเคฆเฅ€) translation accordingly -3. Maintain the same structure and formatting -4. Test the translation for accuracy and cultural appropriateness - -### ๐Ÿ“ Recent English Changes -**Last commit**: [Update shape primitives documentation with better examples](https://github.com/processing/p5.js-website/commit/abc1234567890) - ---- -*This issue was automatically created by the p5.js Translation Tracker ๐Ÿค–* -*Need help? Check our [translation guidelines](https://github.com/processing/p5.js-website/blob/main/contributor_docs/translation.md)* - ---- - -## ๐Ÿค” Questions for the Community - -1. **Is this issue format helpful and clear?** What would you add/remove? - -2. **Should the issue title be bilingual?** - - Current: "๐ŸŒ Update HI translation for description.mdx" - - Alternative: "๐ŸŒ เคนเคฟเคจเฅเคฆเฅ€ เค…เคจเฅเคตเคพเคฆ เค…เคชเคกเฅ‡เคŸ / Update HI translation for description.mdx" - -3. **What labels should be automatically applied?** - - Current: `translation`, `lang-hi`, `help wanted` - - Suggestions for others? - -4. **Should we include diff information?** - - Show specific lines that changed (if easily obtainable)? - - Or is the "Compare changes" link sufficient? - -5. **Frequency concerns:** - - Should there be a cooldown period to avoid spamming issues? - - Batch multiple files into one issue? - -6. **Translation workflow preferences:** - - Should we assign issues to known translators automatically? - - Create issues in specific projects/milestones? - -## ๐Ÿš€ Timeline - -- **Week 1**: โœ… Basic detection logic -- **Week 2**: ๐Ÿ”„ Issue creation (current) -- **Week 3**: Enhanced issue management -- **Week 4**: Multi-language support expansion - -## ๐Ÿ™ How to Help - -Please share your thoughts on: -- Issue template format and content -- Translation workflow improvements -- Any missing information that would help translators -- Concerns about automation frequency - -Your feedback will directly shape how this tool works for the p5.js translation community! - -Thanks! ๐ŸŽจ - ---- -*@Divyansh013 | GSoC 2024 - p5.js Website Translation Automation* \ No newline at end of file diff --git a/.github/actions/translation-tracker/debug-test.js b/.github/actions/translation-tracker/debug-test.js deleted file mode 100644 index cb15b3acd8..0000000000 --- a/.github/actions/translation-tracker/debug-test.js +++ /dev/null @@ -1,99 +0,0 @@ -/** - * Debug Test for GitHub API - * Shows detailed commit information to debug why outdated files aren't detected - */ - -const { Octokit } = require('@octokit/rest'); -const simpleGit = require('simple-git'); - -console.log('๐Ÿ” Debug Test - Analyzing GitHub Commit Data'); -console.log('=============================================\n'); - -async function debugCommitData() { - const token = process.env.GITHUB_TOKEN; - if (!token) { - console.log('โŒ No GITHUB_TOKEN provided'); - return; - } - - const octokit = new Octokit({ auth: token }); - const git = simpleGit(); - - // Test files - const englishFile = 'src/content/examples/en/01_Shapes_And_Color/00_Shape_Primitives/description.mdx'; - const hindiFile = 'src/content/examples/hi/01_Shapes_And_Color/00_Shape_Primitives/description.mdx'; - - console.log('๐Ÿ“‹ Files to analyze:'); - console.log(` English: ${englishFile}`); - console.log(` Hindi: ${hindiFile}\n`); - - try { - // Get local commit data for comparison - console.log('๐Ÿ  LOCAL Git Analysis:'); - const englishLog = await git.log({ file: englishFile, n: 1 }); - const hindiLog = await git.log({ file: hindiFile, n: 1 }); - - console.log(` English latest: ${englishLog.latest.hash} (${englishLog.latest.date})`); - console.log(` Hindi latest: ${hindiLog.latest.hash} (${hindiLog.latest.date})\n`); - - // Get GitHub API data - console.log('๐Ÿ”— GITHUB API Analysis:'); - - // For English file - console.log(' Fetching English file commits...'); - const englishCommits = await octokit.rest.repos.listCommits({ - owner: 'Divyansh013', - repo: 'p5.js-website-new', - path: englishFile, - per_page: 5 - }); - - console.log(` English commits found: ${englishCommits.data.length}`); - if (englishCommits.data.length > 0) { - const latest = englishCommits.data[0]; - console.log(` Latest English commit: ${latest.sha.substring(0, 8)} (${latest.commit.committer.date})`); - console.log(` Message: ${latest.commit.message}`); - } - - // For Hindi file - console.log('\n Fetching Hindi file commits...'); - const hindiCommits = await octokit.rest.repos.listCommits({ - owner: 'Divyansh013', - repo: 'p5.js-website-new', - path: hindiFile, - per_page: 5 - }); - - console.log(` Hindi commits found: ${hindiCommits.data.length}`); - if (hindiCommits.data.length > 0) { - const latest = hindiCommits.data[0]; - console.log(` Latest Hindi commit: ${latest.sha.substring(0, 8)} (${latest.commit.committer.date})`); - console.log(` Message: ${latest.commit.message}`); - } - - // Compare dates - if (englishCommits.data.length > 0 && hindiCommits.data.length > 0) { - const englishDate = new Date(englishCommits.data[0].commit.committer.date); - const hindiDate = new Date(hindiCommits.data[0].commit.committer.date); - - console.log('\n๐Ÿ“Š DATE COMPARISON:'); - console.log(` English: ${englishDate.toISOString()}`); - console.log(` Hindi: ${hindiDate.toISOString()}`); - console.log(` Difference: ${Math.floor((englishDate - hindiDate) / (1000 * 60 * 60 * 24))} days`); - - if (englishDate > hindiDate) { - console.log(' โœ… DETECTED: Hindi translation is OUTDATED!'); - } else { - console.log(' โŒ NOT DETECTED: System thinks Hindi is up to date'); - } - } - - } catch (error) { - console.error('โŒ Error:', error.message); - if (error.status === 404) { - console.error(' Repository or file not found'); - } - } -} - -debugCommitData(); \ No newline at end of file diff --git a/.github/actions/translation-tracker/final-test-summary.js b/.github/actions/translation-tracker/final-test-summary.js deleted file mode 100644 index c53c1507e1..0000000000 --- a/.github/actions/translation-tracker/final-test-summary.js +++ /dev/null @@ -1,92 +0,0 @@ -/** - * Final Week 2 Test Summary - * Comprehensive demonstration of all fixed features - */ - -const { main } = require('./index.js'); - -console.log('๐Ÿ† Week 2 Translation Tracker - FINAL TEST'); -console.log('=========================================='); -console.log('๐Ÿ“‹ Testing all fixed features with branch detection\n'); - -async function runFinalTests() { - const token = process.env.GITHUB_TOKEN; - - if (!token) { - console.log('โŒ No GitHub token - limited testing available'); - return; - } - - // Override to use your fork - process.env.GITHUB_REPOSITORY = 'Divyansh013/p5.js-website-new'; - - console.log('๐Ÿ”ง Final Test Configuration:'); - console.log(` Repository: Divyansh013/p5.js-website-new`); - console.log(` Branch Detection: โœ… ENABLED (auto-detects week2)`); - console.log(` GitHub API: โœ… REAL API calls`); - console.log(` Issue Creation: ๐Ÿ”ด DISABLED (repo settings)`); - console.log(' Commit Analysis: โœ… REAL commit tracking\n'); - - // Test 1: File we know is outdated - console.log('๐Ÿ“‹ TEST 1: Known Outdated Translation'); - console.log('====================================='); - - const outdatedTest = [ - 'src/content/examples/en/01_Shapes_And_Color/00_Shape_Primitives/description.mdx' - ]; - - const options = { - createIssues: false, // Disable due to repo settings - githubToken: token - }; - - try { - await main(outdatedTest, options); - console.log('โœ… Test 1 PASSED: Correctly detected outdated Hindi translation'); - } catch (error) { - console.error('โŒ Test 1 FAILED:', error.message); - } - - console.log('\n๐Ÿ“‹ TEST 2: Multiple Files Analysis'); - console.log('=================================='); - - // Test with multiple files to see different scenarios - const multipleTest = [ - 'src/content/examples/en/01_Shapes_And_Color/00_Shape_Primitives/description.mdx', - 'src/content/examples/en/02_Animation_And_Variables/00_Drawing_Lines/description.mdx' - ]; - - try { - await main(multipleTest, options); - console.log('โœ… Test 2 PASSED: Multiple file analysis completed'); - } catch (error) { - console.error('โŒ Test 2 FAILED:', error.message); - } - - console.log('\n๐ŸŽ‰ WEEK 2 FEATURES VALIDATED:'); - console.log('=============================='); - console.log('โœ… Branch Detection: Auto-detects current branch (week2)'); - console.log('โœ… GitHub API Integration: Real commit tracking from your fork'); - console.log('โœ… Outdated Detection: Correctly identifies 135+ day difference'); - console.log('โœ… Enhanced Logging: Detailed commit information and timestamps'); - console.log('โœ… Issue Format: Ready for creation (disabled due to repo settings)'); - console.log('โœ… Hindi Focus: Targeted language support as planned'); - console.log('โœ… Error Handling: Graceful fallback and clear error messages'); - - console.log('\n๐Ÿ’ก PRODUCTION READINESS:'); - console.log('========================'); - console.log('๐ŸŸข Ready for real p5.js repository use'); - console.log('๐ŸŸข Handles different branch scenarios automatically'); - console.log('๐ŸŸข Robust error handling and fallbacks'); - console.log('๐ŸŸข Comprehensive commit-based tracking'); - console.log('๐ŸŸข Automated issue creation (when enabled)'); - - console.log('\n๐Ÿš€ NEXT STEPS FOR DEPLOYMENT:'); - console.log('============================='); - console.log('1. Deploy to processing/p5.js-website repository'); - console.log('2. Test with actual translation workflows'); - console.log('3. Enable issue creation for real tracking'); - console.log('4. Monitor and gather community feedback'); -} - -runFinalTests(); \ No newline at end of file diff --git a/.github/actions/translation-tracker/index.js b/.github/actions/translation-tracker/index.js index 1d808f9067..1f1eb49db0 100644 --- a/.github/actions/translation-tracker/index.js +++ b/.github/actions/translation-tracker/index.js @@ -5,18 +5,16 @@ const core = require('@actions/core'); const github = require('@actions/github'); const { Octokit } = require('@octokit/rest'); -const SUPPORTED_LANGUAGES = ['hi']; // Start with Hindi only -/** - * Week 2: GitHub API integration for commit tracking with branch detection - */ +const SUPPORTED_LANGUAGES = ['es', 'hi', 'ko', 'zh-Hans']; + + class GitHubCommitTracker { constructor(token, owner, repo) { this.octokit = new Octokit({ auth: token }); this.owner = owner; this.repo = repo; this.currentBranch = this.detectCurrentBranch(); - console.log(`๐ŸŒฟ Using branch: ${this.currentBranch}`); } /** @@ -24,81 +22,60 @@ class GitHubCommitTracker { */ detectCurrentBranch() { try { - // Try different methods to get current branch - - // Method 1: GitHub Actions environment + // GitHub Actions environment if (process.env.GITHUB_HEAD_REF) { - // For pull requests - return process.env.GITHUB_HEAD_REF; + return process.env.GITHUB_HEAD_REF; // For pull requests } if (process.env.GITHUB_REF_NAME) { - // For push events - return process.env.GITHUB_REF_NAME; + return process.env.GITHUB_REF_NAME; // For push events } - // Method 2: Git command + // Git command fallback try { const branch = execSync('git rev-parse --abbrev-ref HEAD', { encoding: 'utf8' }).trim(); if (branch && branch !== 'HEAD') { return branch; } } catch (gitError) { - console.log(`โš ๏ธ Git command failed: ${gitError.message}`); + // Silent fallback } - // Method 3: Git symbolic-ref (alternative) - try { - const ref = execSync('git symbolic-ref HEAD', { encoding: 'utf8' }).trim(); - return ref.replace('refs/heads/', ''); - } catch (refError) { - console.log(`โš ๏ธ Git symbolic-ref failed: ${refError.message}`); - } - - // Fallback to main - console.log('โš ๏ธ Could not detect branch, defaulting to main'); + // Default fallback return 'main'; } catch (error) { - console.log(`โš ๏ธ Branch detection error: ${error.message}, using main`); return 'main'; } } - + /** + * Get the last commit for a specific file using GitHub API + */ async getLastCommit(filePath) { try { - console.log(` ๐Ÿ” Querying commits for ${filePath} on branch ${this.currentBranch}`); - const { data } = await this.octokit.rest.repos.listCommits({ owner: this.owner, repo: this.repo, - sha: this.currentBranch, // Use detected branch + sha: this.currentBranch, path: filePath, per_page: 1 }); if (data.length > 0) { - const commit = { + return { sha: data[0].sha, date: new Date(data[0].commit.committer.date), message: data[0].commit.message, author: data[0].commit.author.name, url: data[0].html_url }; - - console.log(` โœ… Found commit: ${commit.sha.substring(0, 7)} (${commit.date.toISOString()})`); - return commit; } - console.log(` โš ๏ธ No commits found for ${filePath} on branch ${this.currentBranch}`); return null; } catch (error) { - console.error(` โŒ Error fetching commit for ${filePath}:`, error.message); - - // If branch-specific query fails, try main branch as fallback + // Fallback to main branch if current branch fails if (this.currentBranch !== 'main') { - console.log(` ๐Ÿ”„ Retrying with main branch...`); try { const { data } = await this.octokit.rest.repos.listCommits({ owner: this.owner, @@ -109,18 +86,16 @@ class GitHubCommitTracker { }); if (data.length > 0) { - const commit = { + return { sha: data[0].sha, date: new Date(data[0].commit.committer.date), message: data[0].commit.message, author: data[0].commit.author.name, url: data[0].html_url }; - console.log(` โœ… Found commit on main: ${commit.sha.substring(0, 7)} (${commit.date.toISOString()})`); - return commit; } } catch (fallbackError) { - console.error(` โŒ Fallback to main also failed:`, fallbackError.message); + // Silent fallback } } @@ -128,10 +103,11 @@ class GitHubCommitTracker { } } - + /** + * Create a GitHub issue for outdated translation + */ async createTranslationIssue(englishFile, language, commitInfo) { const issueTitle = `๐ŸŒ Update ${language.toUpperCase()} translation for ${path.basename(englishFile)}`; - const issueBody = this.formatIssueBody(englishFile, language, commitInfo); try { @@ -143,7 +119,6 @@ class GitHubCommitTracker { labels: ['translation', `lang-${language}`, 'help wanted'] }); - console.log(`โœ… Created issue #${data.number}: ${issueTitle}`); return data; } catch (error) { console.error(`โŒ Error creating issue:`, error.message); @@ -151,7 +126,9 @@ class GitHubCommitTracker { } } - + /** + * Format the issue body with helpful information + */ formatIssueBody(englishFile, language, commitInfo) { const translationPath = englishFile.replace('/en/', `/${language}/`); const englishCommit = commitInfo.english; @@ -187,7 +164,9 @@ class GitHubCommitTracker { *Need help? Check our [translation guidelines](https://github.com/processing/p5.js-website/blob/main/contributor_docs/translation.md)*`; } - + /** + * Get display name for language code + */ getLanguageDisplayName(langCode) { const languages = { 'es': 'Spanish (Espaรฑol)', @@ -199,8 +178,12 @@ class GitHubCommitTracker { } } - +/** + * Week 1: Get changed files from git or test files + * This is the core Week 1 functionality that remains unchanged + */ function getChangedFiles(testFiles = null) { + // Allow passing test files for local development (Week 1 feature) if (testFiles) { console.log('๐Ÿงช Using provided test files for local testing'); return testFiles.filter(file => @@ -235,9 +218,7 @@ function getChangedFiles(testFiles = null) { } } -/** - * Check if a file exists - */ + function fileExists(filePath) { try { return fs.existsSync(filePath); @@ -247,7 +228,16 @@ function fileExists(filePath) { } -async function checkTranslationStatus(changedExampleFiles, githubTracker, createIssues = false) { +function getFileModTime(filePath) { + try { + return fs.statSync(filePath).mtime; + } catch (error) { + return null; + } +} + + +async function checkTranslationStatus(changedExampleFiles, githubTracker = null, createIssues = false) { const translationStatus = { needsUpdate: [], missing: [], @@ -258,18 +248,6 @@ async function checkTranslationStatus(changedExampleFiles, githubTracker, create for (const englishFile of changedExampleFiles) { console.log(`\n๐Ÿ“ Checking translations for: ${englishFile}`); - if (!githubTracker) { - console.log(`โš ๏ธ No GitHub tracker available - skipping commit analysis`); - console.log(`โ„น๏ธ To enable full Week 2 features, provide GITHUB_TOKEN`); - continue; - } - - const englishCommit = await githubTracker.getLastCommit(englishFile); - if (!englishCommit) { - console.log(`โš ๏ธ Could not get commit info for English file`); - continue; - } - for (const language of SUPPORTED_LANGUAGES) { const translationPath = englishFile.replace('/en/', `/${language}/`); const exists = fileExists(translationPath); @@ -285,56 +263,103 @@ async function checkTranslationStatus(changedExampleFiles, githubTracker, create continue; } - const translationCommit = await githubTracker.getLastCommit(translationPath); - if (!translationCommit) { - console.log(` โš ๏ธ ${language}: Could not get commit info for translation`); - continue; - } + if (githubTracker) { + const englishCommit = await githubTracker.getLastCommit(englishFile); + const translationCommit = await githubTracker.getLastCommit(translationPath); - const isOutdated = translationCommit.date < englishCommit.date; - - if (isOutdated) { - console.log(` ๐Ÿ”„ ${language}: Needs update`); - console.log(` - English: ${englishCommit.date.toISOString()} (${englishCommit.sha.substring(0, 7)})`); - console.log(` - Translation: ${translationCommit.date.toISOString()} (${translationCommit.sha.substring(0, 7)})`); + if (!englishCommit) { + console.log(` โš ๏ธ ${language}: Could not get English commit info`); + continue; + } + + if (!translationCommit) { + console.log(` ๐Ÿ†• ${language}: Missing translation (no commits)`); + translationStatus.missing.push({ + englishFile, + language, + translationPath, + status: 'missing' + }); + continue; + } + + const isOutdated = englishCommit.date > translationCommit.date; - const statusItem = { - englishFile, - language, - translationPath, - status: 'outdated', - commitInfo: { - english: englishCommit, - translation: translationCommit + if (isOutdated) { + console.log(` ๐Ÿ”„ ${language}: Needs update`); + console.log(` - English: ${englishCommit.date.toISOString()} (${englishCommit.sha.substring(0, 7)})`); + console.log(` - Translation: ${translationCommit.date.toISOString()} (${translationCommit.sha.substring(0, 7)})`); + + const statusItem = { + englishFile, + language, + translationPath, + status: 'outdated', + commitInfo: { + english: englishCommit, + translation: translationCommit + } + }; + + translationStatus.needsUpdate.push(statusItem); + + + if (createIssues) { + console.log(` ๐Ÿ“ Creating GitHub issue for ${language} translation...`); + const issue = await githubTracker.createTranslationIssue( + englishFile, + language, + statusItem.commitInfo + ); + if (issue) { + console.log(` โœ… Created issue #${issue.number}: ${issue.title}`); + translationStatus.issuesCreated.push({ + ...statusItem, + issueNumber: issue.number, + issueUrl: issue.html_url + }); + } } - }; + } else { + console.log(` โœ… ${language}: Up to date`); + translationStatus.upToDate.push({ + englishFile, + language, + translationPath, + status: 'up-to-date' + }); + } + } else { + // Week 1: Fallback to file modification time comparison + const englishModTime = getFileModTime(englishFile); + if (!englishModTime) { + console.log(` โš ๏ธ Could not get modification time for English file`); + continue; + } - translationStatus.needsUpdate.push(statusItem); + const translationModTime = getFileModTime(translationPath); + const isOutdated = translationModTime < englishModTime; - // Week 2: Create issue if requested - if (createIssues && githubTracker) { - const issue = await githubTracker.createTranslationIssue( - englishFile, - language, - statusItem.commitInfo - ); - if (issue) { - translationStatus.issuesCreated.push({ - ...statusItem, - issueNumber: issue.number, - issueUrl: issue.html_url - }); - } + if (isOutdated) { + console.log(` ๐Ÿ”„ ${language}: Needs update (English: ${englishModTime.toISOString()}, Translation: ${translationModTime.toISOString()})`); + translationStatus.needsUpdate.push({ + englishFile, + language, + translationPath, + status: 'outdated', + englishModTime, + translationModTime + }); + } else { + console.log(` โœ… ${language}: Up to date`); + translationStatus.upToDate.push({ + englishFile, + language, + translationPath, + status: 'up-to-date' + }); } - } else { - console.log(` โœ… ${language}: Up to date`); - translationStatus.upToDate.push({ - englishFile, - language, - translationPath, - status: 'up-to-date' - }); } } } @@ -343,9 +368,9 @@ async function checkTranslationStatus(changedExampleFiles, githubTracker, create } -function displaySummary(translationStatus) { - console.log('\n๐Ÿ“Š WEEK 2 TRANSLATION STATUS SUMMARY'); - console.log('โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•'); +function displaySummary(translationStatus, isWeek2 = false) { + console.log(`\n๐Ÿ“Š TRANSLATION STATUS SUMMARY ${isWeek2 ? '(Week 2)' : '(Week 1)'}`); + console.log('โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•'); console.log(`๐Ÿ†• Missing translations: ${translationStatus.missing.length}`); if (translationStatus.missing.length > 0) { @@ -371,17 +396,19 @@ function displaySummary(translationStatus) { }); } - console.log('\n๐Ÿ’ก Week 2 Progress:'); - console.log('โœ… GitHub API integration for commit tracking'); - console.log('โœ… Automated issue creation for outdated translations'); - console.log('โœ… Enhanced issue formatting with helpful links'); - console.log('โœ… Focus on Hindi language (as planned)'); + if (isWeek2) { + console.log('\n๐Ÿ’ก Week 2 Features:'); + console.log('โœ… GitHub API integration for accurate commit tracking'); + console.log('โœ… Automated issue creation for outdated translations'); + console.log('โœ… Enhanced issue templates with helpful links'); + console.log('โœ… Backward compatibility with Week 1 functionality'); + } } function exploreRepoStructure() { - console.log('\n๐Ÿ” WEEK 2 REPOSITORY STRUCTURE ANALYSIS'); - console.log('โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•'); + console.log('\n๐Ÿ” REPOSITORY STRUCTURE ANALYSIS'); + console.log('โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•'); try { const examplesPath = 'src/content/examples'; @@ -393,7 +420,6 @@ function exploreRepoStructure() { .filter(item => !item.startsWith('.') && item !== 'images'); console.log(`๐ŸŒ Available languages: ${languages.join(', ')}`); - console.log(`๐ŸŽฏ Week 2 focus: ${SUPPORTED_LANGUAGES.join(', ')} (Hindi only)`); // Count example files in each language languages.forEach(lang => { @@ -421,8 +447,7 @@ function exploreRepoStructure() { totalFiles += countFilesRecursively(categoryPath); }); - const indicator = SUPPORTED_LANGUAGES.includes(lang) ? '๐ŸŽฏ' : '๐Ÿ“‹'; - console.log(` ${indicator} ${lang}: ${totalFiles} example files across ${categories.length} categories`); + console.log(` ${lang}: ${totalFiles} example files across ${categories.length} categories`); } catch (error) { console.log(` ${lang}: Error reading directory - ${error.message}`); } @@ -437,25 +462,34 @@ function exploreRepoStructure() { async function main(testFiles = null, options = {}) { - console.log('๐ŸŽฏ p5.js Translation Tracker - Week 2 Prototype'); - console.log('โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•'); + const isWeek2 = !!options.enableWeek2 || !!process.env.GITHUB_TOKEN; + + console.log(`๐ŸŽฏ p5.js Translation Tracker - ${isWeek2 ? 'Week 2' : 'Week 1'} Mode`); + console.log('โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•'); console.log(`๐Ÿ“… Event: ${process.env.GITHUB_EVENT_NAME || 'local'}`); console.log(`๐Ÿ  Working directory: ${process.cwd()}`); console.log(`๐ŸŒ Tracking languages: ${SUPPORTED_LANGUAGES.join(', ')}`); - + + // Week 2: Initialize GitHub tracker if token is available let githubTracker = null; - const token = process.env.GITHUB_TOKEN || options.githubToken; - const [owner, repo] = (process.env.GITHUB_REPOSITORY || 'processing/p5.js-website').split('/'); - - if (token) { - githubTracker = new GitHubCommitTracker(token, owner, repo); - console.log(`๐Ÿ”— GitHub API initialized for ${owner}/${repo}`); - } else { - console.log('โš ๏ธ No GitHub token provided - running in basic mode'); + if (isWeek2) { + const token = process.env.GITHUB_TOKEN || options.githubToken; + if (token) { + try { + const [owner, repo] = (process.env.GITHUB_REPOSITORY || 'processing/p5.js-website').split('/'); + githubTracker = new GitHubCommitTracker(token, owner, repo); + console.log(`๐Ÿ”— GitHub API initialized for ${owner}/${repo} (branch: ${githubTracker.currentBranch})`); + } catch (error) { + console.error('โš ๏ธ GitHub API initialization failed:', error.message); + console.log('๐Ÿ“ Falling back to Week 1 mode...'); + } + } else { + console.log('โš ๏ธ No GitHub token provided - running in Week 1 mode'); + } } - + exploreRepoStructure(); - + const changedExampleFiles = getChangedFiles(testFiles); if (changedExampleFiles.length === 0) { @@ -463,19 +497,19 @@ async function main(testFiles = null, options = {}) { console.log('๐Ÿ“ Nothing to track for translations in this commit!'); return; } - - const createIssues = options.createIssues !== false; // Default to true + + const createIssues = options.createIssues !== false && githubTracker !== null; const translationStatus = await checkTranslationStatus( changedExampleFiles, githubTracker, createIssues ); - // Display summary - displaySummary(translationStatus); + + displaySummary(translationStatus, isWeek2); } -// Export for testing +// Export for testing (Week 1 + Week 2) module.exports = { main, getChangedFiles, @@ -484,7 +518,7 @@ module.exports = { GitHubCommitTracker }; - +// Run if called directly if (require.main === module) { main(); } diff --git a/.github/actions/translation-tracker/package.json b/.github/actions/translation-tracker/package.json index fb554f92a2..a6ea6d8bf3 100644 --- a/.github/actions/translation-tracker/package.json +++ b/.github/actions/translation-tracker/package.json @@ -5,7 +5,7 @@ "main": "index.js", "scripts": { "start": "node index.js", - "test": "node index.js" + "test": "node test-local.js" }, "keywords": [ "p5.js", @@ -22,7 +22,6 @@ "dependencies": { "@actions/core": "^1.10.0", "@actions/github": "^5.1.1", - "@octokit/rest": "^20.0.2", - "simple-git": "^3.24.0" + "@octokit/rest": "^19.0.5" } } \ No newline at end of file diff --git a/.github/actions/translation-tracker/test-all-files.js b/.github/actions/translation-tracker/test-all-files.js deleted file mode 100644 index d602381aa1..0000000000 --- a/.github/actions/translation-tracker/test-all-files.js +++ /dev/null @@ -1,51 +0,0 @@ -/** - * Test ALL Example Files - * This will check every English example file for translation status - */ - -const { main } = require('./index.js'); - -console.log('๐ŸŒ Testing ALL Example Files Translation Status'); -console.log('==============================================='); -console.log('๐Ÿ“‹ This will check EVERY English example file\n'); - -async function testAllFiles() { - const token = process.env.GITHUB_TOKEN; - - if (!token) { - console.log('โŒ No GitHub token - set it first:'); - console.log(' export GITHUB_TOKEN=your_token_here'); - return; - } - - // Override to use your fork - process.env.GITHUB_REPOSITORY = 'Divyansh013/p5.js-website-new'; - - console.log('๐Ÿ”ง Configuration:'); - console.log(` Repository: Divyansh013/p5.js-website-new`); - console.log(` Mode: CHECK ALL EXAMPLE FILES`); - console.log(` GitHub Token: โœ… Provided`); - console.log(` Issue Creation: ๐Ÿ”ด DISABLED (dry run)\n`); - - const options = { - createIssues: false, // Don't spam with issues - githubToken: token - }; - - try { - console.log('๐Ÿš€ Running tracker on ALL example files...\n'); - - // Pass null to testFiles - this will make it check ALL changed files - // But since we're in test mode, let's simulate checking multiple files - await main(null, options); - - console.log('\n๐ŸŽ‰ ALL FILES CHECK COMPLETE!'); - console.log('๐Ÿ“Š This shows what would happen in a real scenario'); - console.log('๐Ÿ’ก In production, it only checks files that changed in a commit'); - - } catch (error) { - console.error('\nโŒ Test failed:', error.message); - } -} - -testAllFiles(); \ No newline at end of file diff --git a/.github/actions/translation-tracker/test-branch-specific.js b/.github/actions/translation-tracker/test-branch-specific.js deleted file mode 100644 index ad237b689b..0000000000 --- a/.github/actions/translation-tracker/test-branch-specific.js +++ /dev/null @@ -1,99 +0,0 @@ -/** - * Branch-specific test for Week 2 commits - * Tests with our actual week2 branch where recent changes exist - */ - -const { Octokit } = require('@octokit/rest'); - -console.log('๐ŸŽฏ Branch-Specific GitHub API Test'); -console.log('==================================='); -console.log('๐ŸŒฟ Testing week2 branch specifically\n'); - -async function testWeek2Branch() { - const token = process.env.GITHUB_TOKEN; - if (!token) { - console.log('โŒ No GITHUB_TOKEN provided'); - return; - } - - const octokit = new Octokit({ auth: token }); - - // Test files - const englishFile = 'src/content/examples/en/01_Shapes_And_Color/00_Shape_Primitives/description.mdx'; - const hindiFile = 'src/content/examples/hi/01_Shapes_And_Color/00_Shape_Primitives/description.mdx'; - - console.log('๐Ÿ“‹ Files to analyze on week2 branch:'); - console.log(` English: ${englishFile}`); - console.log(` Hindi: ${hindiFile}\n`); - - try { - // Get commits from week2 branch specifically - console.log('๐Ÿ”— GITHUB API Analysis (week2 branch):'); - - // For English file on week2 branch - console.log(' Fetching English file commits from week2...'); - const englishCommits = await octokit.rest.repos.listCommits({ - owner: 'Divyansh013', - repo: 'p5.js-website-new', - sha: 'week2', // Specify the branch! - path: englishFile, - per_page: 5 - }); - - console.log(` English commits found: ${englishCommits.data.length}`); - if (englishCommits.data.length > 0) { - const latest = englishCommits.data[0]; - console.log(` โœ… Latest English commit: ${latest.sha.substring(0, 8)} (${latest.commit.committer.date})`); - console.log(` Message: "${latest.commit.message}"`); - } - - // For Hindi file on week2 branch - console.log('\n Fetching Hindi file commits from week2...'); - const hindiCommits = await octokit.rest.repos.listCommits({ - owner: 'Divyansh013', - repo: 'p5.js-website-new', - sha: 'week2', // Specify the branch! - path: hindiFile, - per_page: 5 - }); - - console.log(` Hindi commits found: ${hindiCommits.data.length}`); - if (hindiCommits.data.length > 0) { - const latest = hindiCommits.data[0]; - console.log(` โœ… Latest Hindi commit: ${latest.sha.substring(0, 8)} (${latest.commit.committer.date})`); - console.log(` Message: "${latest.commit.message}"`); - } - - // Compare dates - if (englishCommits.data.length > 0 && hindiCommits.data.length > 0) { - const englishDate = new Date(englishCommits.data[0].commit.committer.date); - const hindiDate = new Date(hindiCommits.data[0].commit.committer.date); - - console.log('\n๐ŸŽฏ WEEK2 BRANCH DATE COMPARISON:'); - console.log(` English: ${englishDate.toISOString()}`); - console.log(` Hindi: ${hindiDate.toISOString()}`); - - const daysDiff = Math.floor((englishDate - hindiDate) / (1000 * 60 * 60 * 24)); - console.log(` Difference: ${daysDiff} days`); - - if (englishDate > hindiDate) { - console.log(' ๐Ÿšจ CORRECTLY DETECTED: Hindi translation is OUTDATED!'); - console.log(' ๐Ÿ“ An issue should be created for this outdated translation.'); - } else { - console.log(' โœ… Hindi translation is up to date'); - } - } - - console.log('\n๐Ÿ’ก Key Learning:'); - console.log(' The tracker needs to specify the correct branch when querying GitHub API!'); - console.log(' Currently it defaults to main branch, missing recent week2 changes.'); - - } catch (error) { - console.error('โŒ Error:', error.message); - if (error.status === 404) { - console.error(' Repository, branch, or file not found'); - } - } -} - -testWeek2Branch(); \ No newline at end of file diff --git a/.github/actions/translation-tracker/test-create-issue.js b/.github/actions/translation-tracker/test-create-issue.js deleted file mode 100644 index 6dd0af7349..0000000000 --- a/.github/actions/translation-tracker/test-create-issue.js +++ /dev/null @@ -1,61 +0,0 @@ -/** - * Test Real GitHub Issue Creation - * This will create an actual issue on your repository! - */ - -const { main } = require('./index.js'); - -console.log('๐ŸŽซ Testing REAL GitHub Issue Creation'); -console.log('====================================='); -console.log('โš ๏ธ WARNING: This will create an actual GitHub issue!'); -console.log('๐ŸŽฏ Target: Your fork with outdated Hindi translation\n'); - -// Test with the file we know is outdated -const testFiles = [ - 'src/content/examples/en/01_Shapes_And_Color/00_Shape_Primitives/description.mdx' -]; - -// Test options - ENABLE issue creation -const options = { - createIssues: true, // This will create real issues! - githubToken: process.env.GITHUB_TOKEN, - repository: 'Divyansh013/p5.js-website-new' -}; - -async function testIssueCreation() { - // Override environment for your fork - process.env.GITHUB_REPOSITORY = 'Divyansh013/p5.js-website-new'; - - console.log('โš™๏ธ Configuration:'); - console.log(` Repository: ${process.env.GITHUB_REPOSITORY}`); - console.log(` Create Issues: ${options.createIssues ? '๐ŸŸข ENABLED' : '๐Ÿ”ด DISABLED'}`); - console.log(` GitHub Token: ${process.env.GITHUB_TOKEN ? 'โœ… Provided' : 'โŒ Missing'}`); - - if (!process.env.GITHUB_TOKEN) { - console.log('\nโŒ No GitHub token - cannot create issues'); - return; - } - - try { - console.log('\n๐Ÿš€ Running tracker with issue creation enabled...\n'); - - await main(testFiles, options); - - console.log('\n๐ŸŽ‰ ISSUE CREATION TEST COMPLETE!'); - console.log('โœ… Check your GitHub repository for new issues'); - console.log('๐Ÿ“‹ Issues should be labeled with: translation, lang-hi, help wanted'); - console.log('๐Ÿ”— Visit: https://github.com/Divyansh013/p5.js-website-new/issues'); - - } catch (error) { - console.error('\nโŒ Issue creation failed:', error.message); - console.error('๐Ÿ” Check:'); - console.error(' - GitHub token has issues:write permission'); - console.error(' - Repository exists and is accessible'); - console.error(' - Network connectivity'); - } -} - -console.log('โณ Starting in 3 seconds...'); -setTimeout(() => { - testIssueCreation(); -}, 3000); \ No newline at end of file diff --git a/.github/actions/translation-tracker/test-local.js b/.github/actions/translation-tracker/test-local.js old mode 100755 new mode 100644 diff --git a/.github/actions/translation-tracker/test-multiple-files.js b/.github/actions/translation-tracker/test-multiple-files.js deleted file mode 100644 index e2c1c502fe..0000000000 --- a/.github/actions/translation-tracker/test-multiple-files.js +++ /dev/null @@ -1,98 +0,0 @@ -/** - * Test Multiple Example Files - * Manually select several files to show different translation statuses - */ - -const { main } = require('./index.js'); -const fs = require('fs'); -const path = require('path'); - -console.log('๐Ÿ“š Testing Multiple Example Files'); -console.log('================================='); -console.log('๐ŸŽฏ Checking several files to show different scenarios\n'); - -function findExampleFiles(limit = 5) { - const examplesPath = 'src/content/examples/en'; - const files = []; - - function scanDirectory(dir, currentFiles) { - if (currentFiles.length >= limit) return currentFiles; - - try { - const items = fs.readdirSync(dir); - - for (const item of items) { - if (currentFiles.length >= limit) break; - - const itemPath = path.join(dir, item); - const stat = fs.statSync(itemPath); - - if (stat.isDirectory()) { - scanDirectory(itemPath, currentFiles); - } else if (item === 'description.mdx') { - currentFiles.push(itemPath); - } - } - } catch (error) { - console.log(`โš ๏ธ Error reading ${dir}: ${error.message}`); - } - - return currentFiles; - } - - return scanDirectory(examplesPath, files); -} - -async function testMultipleFiles() { - const token = process.env.GITHUB_TOKEN; - - if (!token) { - console.log('โŒ No GitHub token - set it first:'); - console.log(' GITHUB_TOKEN=your_token node test-multiple-files.js'); - return; - } - - // Override to use your fork - process.env.GITHUB_REPOSITORY = 'Divyansh013/p5.js-website-new'; - - // Find several example files to test - const testFiles = findExampleFiles(5); - - console.log('๐Ÿ”ง Configuration:'); - console.log(` Repository: Divyansh013/p5.js-website-new`); - console.log(` Files to check: ${testFiles.length}`); - console.log(` GitHub Token: โœ… Provided`); - console.log(` Issue Creation: ๐Ÿ”ด DISABLED (dry run)\n`); - - console.log('๐Ÿ“‹ Files being checked:'); - testFiles.forEach((file, index) => { - console.log(` ${index + 1}. ${file}`); - }); - console.log(''); - - const options = { - createIssues: false, // Don't create issues - githubToken: token - }; - - try { - console.log('๐Ÿš€ Running tracker on multiple files...\n'); - - await main(testFiles, options); - - console.log('\n๐ŸŽ‰ MULTIPLE FILES CHECK COMPLETE!'); - console.log('๐Ÿ“Š This shows different translation statuses:'); - console.log(' โ€ข Some files may be outdated (Hindi older than English)'); - console.log(' โ€ข Some files may be up-to-date (Hindi newer than English)'); - console.log(' โ€ข Some files may have missing translations'); - console.log('\n๐Ÿ’ก In real usage:'); - console.log(' โ€ข Only files changed in a commit are checked'); - console.log(' โ€ข Issues are created automatically for outdated translations'); - console.log(' โ€ข The system focuses on one language at a time (Hindi)'); - - } catch (error) { - console.error('\nโŒ Test failed:', error.message); - } -} - -testMultipleFiles(); \ No newline at end of file diff --git a/.github/actions/translation-tracker/test-real-api.js b/.github/actions/translation-tracker/test-real-api.js deleted file mode 100644 index 624d4f2798..0000000000 --- a/.github/actions/translation-tracker/test-real-api.js +++ /dev/null @@ -1,59 +0,0 @@ -/** - * Real GitHub API Test for User's Fork - * Tests with actual commit data from Divyansh013/p5.js-website-new - */ - -const { main } = require('./index.js'); - -console.log('๐Ÿ”ฅ Testing Translation Tracker with REAL GitHub API'); -console.log('==================================================='); -console.log('๐ŸŽฏ Target: Divyansh013/p5.js-website-new (YOUR FORK)'); -console.log('๐Ÿ“… Testing recent commits on week2 branch\n'); - -// Test with the file we just modified -const testFiles = [ - 'src/content/examples/en/01_Shapes_And_Color/00_Shape_Primitives/description.mdx' -]; - -// Test options for your fork -const options = { - createIssues: false, // Set to true if you want to actually create issues - githubToken: process.env.GITHUB_TOKEN, - // Override the repository to use your fork - repository: 'Divyansh013/p5.js-website-new' -}; - -// Custom main function that uses your fork -async function testWithFork() { - // Override environment variable for this test - process.env.GITHUB_REPOSITORY = 'Divyansh013/p5.js-website-new'; - - console.log('๐Ÿ”ง Configuration:'); - console.log(` Repository: ${process.env.GITHUB_REPOSITORY}`); - console.log(` Branch: week2`); - console.log(` GitHub Token: ${process.env.GITHUB_TOKEN ? 'โœ… Provided' : 'โŒ Missing'}`); - console.log(` Create Issues: ${options.createIssues ? 'YES' : 'NO (dry run)'}\n`); - - try { - await main(testFiles, options); - - console.log('\n๐ŸŽ‰ Real API Test Results:'); - console.log('โœ… GitHub API successfully connected to your fork'); - console.log('โœ… Commit data retrieved from your repository'); - console.log('โœ… Translation status analysis completed'); - console.log('\n๐Ÿ’ก Next Steps:'); - console.log('โ€ข To create real issues, set createIssues: true'); - console.log('โ€ข Check GitHub API rate limits if needed'); - console.log('โ€ข Test with different file changes'); - - } catch (error) { - console.error('โŒ Test failed:', error.message); - console.error('๐Ÿ” Check:'); - console.error(' - GitHub token permissions'); - console.error(' - Repository access'); - console.error(' - Network connectivity'); - } -} - -// Run the test -testWithFork(); \ No newline at end of file diff --git a/.github/actions/translation-tracker/test-simulation.js b/.github/actions/translation-tracker/test-simulation.js deleted file mode 100644 index 444e043cbb..0000000000 --- a/.github/actions/translation-tracker/test-simulation.js +++ /dev/null @@ -1,91 +0,0 @@ - - -const { GitHubCommitTracker } = require('./index.js'); - - -class MockGitHubTracker extends GitHubCommitTracker { - constructor() { - super(null, 'processing', 'p5.js-website'); // Call super with mock values - } - - async getLastCommit(filePath) { - - const mockCommits = { - 'src/content/examples/en/01_Shapes_And_Color/00_Shape_Primitives/description.mdx': { - sha: 'abc1234567890', - date: new Date('2024-06-10T10:00:00Z'), - message: 'Update shape primitives documentation with better examples', - author: 'p5js-contributor', - url: 'https://github.com/processing/p5.js-website/commit/abc1234567890' - }, - 'src/content/examples/hi/01_Shapes_And_Color/00_Shape_Primitives/description.mdx': { - sha: 'def0987654321', - date: new Date('2024-06-01T15:30:00Z'), - message: 'เค†เค•เคพเคฐ เค†เคฆเคฟเคฎ เคฆเคธเฅเคคเคพเคตเฅ‡เคœเคผเฅ€เค•เคฐเคฃ เค•เคพ เค…เคจเฅเคตเคพเคฆ', - author: 'hindi-translator', - url: 'https://github.com/processing/p5.js-website/commit/def0987654321' - } - }; - - return mockCommits[filePath] || null; - } - - async createTranslationIssue(englishFile, language, commitInfo) { - - const issueTitle = `๐ŸŒ Update ${language.toUpperCase()} translation for ${require('path').basename(englishFile)}`; - const issueBody = this.formatIssueBody(englishFile, language, commitInfo); - - console.log('\n๐ŸŽซ SIMULATED ISSUE CREATION:'); - console.log('โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•'); - console.log(`Title: ${issueTitle}`); - console.log('Body Preview:'); - console.log(issueBody.substring(0, 300) + '...'); - - - return { - number: 42, - html_url: 'https://github.com/processing/p5.js-website/issues/42', - title: issueTitle - }; - } -} - -async function runSimulation() { - console.log('๐ŸŽญ Week 2 Translation Tracker Simulation'); - console.log('=========================================\n'); - - const mockTracker = new MockGitHubTracker(); - const englishFile = 'src/content/examples/en/01_Shapes_And_Color/00_Shape_Primitives/description.mdx'; - const language = 'hi'; - - console.log('๐Ÿ“‹ Simulation Scenario:'); - console.log(` - English file updated: June 10, 2024`); - console.log(` - Hindi translation: June 1, 2024 (9 days outdated)`); - console.log(` - Expected result: Create update issue\n`); - - - const englishCommit = await mockTracker.getLastCommit(englishFile); - const translationPath = englishFile.replace('/en/', `/${language}/`); - const translationCommit = await mockTracker.getLastCommit(translationPath); - - console.log('๐Ÿ“Š Commit Analysis:'); - console.log(` English: ${englishCommit.date.toISOString()} (${englishCommit.sha.substring(0, 7)})`); - console.log(` Hindi: ${translationCommit.date.toISOString()} (${translationCommit.sha.substring(0, 7)})`); - console.log(` Outdated: ${translationCommit.date < englishCommit.date ? 'YES' : 'NO'}\n`); - - if (translationCommit.date < englishCommit.date) { - await mockTracker.createTranslationIssue(englishFile, language, { - english: englishCommit, - translation: translationCommit - }); - } - - console.log('\n๐ŸŽฏ Week 2 Simulation Results:'); - console.log('โœ… GitHub API commit tracking simulation'); - console.log('โœ… Outdated detection logic verification'); - console.log('โœ… Issue creation format testing'); - console.log('โœ… Ready for real-world testing!'); -} - - -runSimulation().catch(console.error); \ No newline at end of file diff --git a/.github/actions/translation-tracker/test-week2.js b/.github/actions/translation-tracker/test-week2.js deleted file mode 100644 index 59c5a4fceb..0000000000 --- a/.github/actions/translation-tracker/test-week2.js +++ /dev/null @@ -1,36 +0,0 @@ -/** - * Week 2 Local Testing Script - * Tests GitHub API integration and issue creation (without actually creating issues) - */ - -const { main } = require('./index.js'); - -// Test scenarios using actual example files -const testFiles = [ - 'src/content/examples/en/01_Shapes_And_Color/00_Shape_Primitives/description.mdx', - 'src/content/examples/en/02_Animation_And_Variables/00_Drawing_Lines/description.mdx' -]; - -console.log('๐Ÿงช Testing Week 2 Translation Tracker with GitHub API'); -console.log('=====================================================\n'); - -// Test options -const options = { - createIssues: false, // Set to false for local testing to avoid creating real issues - githubToken: process.env.GITHUB_TOKEN || null // Will use token if provided via environment -}; - -// Run the main function with test files and options -main(testFiles, options).then(() => { - console.log('\n๐Ÿ’ก Week 2 Testing Complete!'); - console.log('๐Ÿ”ง To test with real GitHub API:'); - console.log(' export GITHUB_TOKEN=your_token_here'); - console.log(' node test-week2.js'); - console.log('\n๐Ÿ“‹ Week 2 Features Tested:'); - console.log('โœ… GitHub API commit tracking'); - console.log('โœ… Enhanced outdated detection'); - console.log('โœ… Issue creation logic (dry run)'); - console.log('โœ… Hindi-only focus'); -}).catch(error => { - console.error('โŒ Test failed:', error.message); -}); \ No newline at end of file From 94159c8e1828541b0aeb6a803774f8dbd322d786 Mon Sep 17 00:00:00 2001 From: Divyansh013 Date: Sun, 6 Jul 2025 21:33:47 +0530 Subject: [PATCH 12/18] Week 3: Test automated translation tracking - Updated translation-sync workflow for examples instead of reference - Added Week 3 test change to Shape Primitives description - Should trigger automated issue creation for outdated translations --- .github/actions/translation-tracker/README.md | 276 ++++++++++++++++-- .github/actions/translation-tracker/index.js | 269 ++++++++++++++--- .../actions/translation-tracker/test-local.js | 128 +++++++- .github/workflows/translation-sync.yml | 11 +- .../00_Shape_Primitives/description.mdx | 2 +- 5 files changed, 598 insertions(+), 88 deletions(-) diff --git a/.github/actions/translation-tracker/README.md b/.github/actions/translation-tracker/README.md index 1fbcc5f01e..48c5e00f8a 100644 --- a/.github/actions/translation-tracker/README.md +++ b/.github/actions/translation-tracker/README.md @@ -1,48 +1,266 @@ # p5.js Translation Tracker -A GitHub Action to track translation status for p5.js examples and documentation. +A comprehensive GitHub Action and command-line tool to track translation status across multiple languages in the p5.js website examples. -## Files Structure +## ๐Ÿš€ Overview -- `index.js` - Main implementation (Week 1 + Week 2 functionality) -- `package.json` - Dependencies and metadata -- `test-local.js` - Local testing script -- `node_modules/` - Dependencies (auto-generated) -- `package-lock.json` - Dependency lock file (auto-generated) +This tool helps maintain translation consistency by: +- **Week 1**: Basic file-based change detection using Git +- **Week 2**: GitHub API integration for accurate commit tracking and automated issue creation +- **Week 3**: Multi-language support with single issues per file and manual scanning capabilities -## Features +## ๐Ÿ“‹ Features -### Week 1 (File-based tracking) -- Track changed English example files using git -- Compare file modification times -- Support local testing with test files -- Comprehensive repository structure analysis +### โœ… Week 1 Features (File-based tracking) +- Git-based change detection for English example files +- File modification time comparison +- Support for all languages: Spanish (es), Hindi (hi), Korean (ko), Chinese Simplified (zh-Hans) +- Local testing capabilities -### Week 2 (GitHub API integration) +### โœ… Week 2 Features (GitHub API integration) - Real commit-based comparison using GitHub API - Automated GitHub issue creation for outdated translations -- Enhanced issue templates with helpful links +- Enhanced issue templates with helpful links and timelines +- Branch detection (auto-detects current branch) - Backward compatibility with Week 1 -## Usage +### โœ… Week 3 Features (Multi-language & refinement) +- **Single issue per file**: Creates one issue covering all affected languages instead of separate issues per language +- **Enhanced labeling**: Uses "needs translation" base label + specific language labels (e.g., "lang-es", "lang-ko") +- **Manual scanning**: Can scan all English example files to find outdated/missing translations +- **Comprehensive issue format**: Detailed status for each language with links and action checklists +- **Batched language updates**: Groups all translation issues by file for better organization + +## ๐ŸŽฏ Supported Languages + +- **es**: Spanish (Espaรฑol) +- **hi**: Hindi (เคนเคฟเคจเฅเคฆเฅ€) +- **ko**: Korean (ํ•œ๊ตญ์–ด) +- **zh-Hans**: Chinese Simplified (็ฎ€ไฝ“ไธญๆ–‡) + +## ๐Ÿ“ File Structure + +``` +.github/actions/translation-tracker/ +โ”œโ”€โ”€ index.js # Main implementation (Week 1 + 2 + 3) +โ”œโ”€โ”€ package.json # Dependencies +โ”œโ”€โ”€ test-local.js # Testing script with examples +โ””โ”€โ”€ README.md # This documentation +``` + +## ๐Ÿ›  Usage + +### Basic Usage (Week 1 Mode) -### Local Testing (Week 1 mode) ```bash -node test-local.js +# Run locally with file-based tracking +node .github/actions/translation-tracker/index.js ``` -### Production with GitHub API (Week 2 mode) +### GitHub API Mode (Week 2 Mode) + +```bash +# Run with GitHub token for commit-based tracking +GITHUB_TOKEN=your_token node .github/actions/translation-tracker/index.js +``` + +### Manual Scanning (Week 3 Feature) + +```bash +# Scan all English example files +GITHUB_TOKEN=token node -e " +const { main } = require('./.github/actions/translation-tracker/index.js'); +main(null, { scanAll: true, createIssues: false }); +" +``` + +### Create Issues (Week 2 + 3 Feature) + ```bash -GITHUB_TOKEN=your_token GITHUB_REPOSITORY=owner/repo node index.js +# Create GitHub issues for outdated translations +GITHUB_TOKEN=token node -e " +const { main } = require('./.github/actions/translation-tracker/index.js'); +main(null, { scanAll: true, createIssues: true }); +" ``` -## Supported Languages -- Spanish (es) -- Hindi (hi) -- Korean (ko) -- Chinese Simplified (zh-Hans) +## ๐Ÿงช Testing + +The project includes a comprehensive test suite: + +```bash +# Run all tests +node .github/actions/translation-tracker/test-local.js + +# Manual scan test +node .github/actions/translation-tracker/test-local.js manual + +# Week 2 features with GitHub API +GITHUB_TOKEN=token node .github/actions/translation-tracker/test-local.js week2 + +# Create test issues +GITHUB_TOKEN=token node .github/actions/translation-tracker/test-local.js issues +``` + +## ๐Ÿ“ Issue Format (Week 3) + +The tool creates comprehensive GitHub issues with: + +### ๐Ÿ”– Labels +- `needs translation` (base label) +- `help wanted` +- Language-specific labels: `lang-es`, `lang-hi`, `lang-ko`, `lang-zh-Hans` + +### ๐Ÿ“„ Issue Content +- **Timeline**: Shows when English and translation files were last updated +- **Outdated Translations**: Lists languages that need updates with commit comparison links +- **Missing Translations**: Lists languages where translation files don't exist +- **Action Checklist**: Step-by-step guide for translators +- **Quick Links**: Direct links to files and comparison views + +### Example Issue Structure: +```markdown +## ๐ŸŒ Translation Update Needed + +**File**: `src/content/examples/en/01_Shapes_And_Color/00_Shape_Primitives/description.mdx` +**Branch**: `week2` + +### ๐Ÿ“… Timeline +- **Latest English update**: 6/22/2025 by p5js-contributor + +### ๐Ÿ”„ Outdated Translations + +- **Spanish (Espaรฑol)**: Last updated 6/9/2025 by spanish-translator + - [๐Ÿ“ View file](https://github.com/owner/repo/blob/week2/src/content/examples/es/...) + - [๐Ÿ” Compare changes](https://github.com/owner/repo/compare/abc123...def456) + +### โœ… Action Checklist + +**For translators / contributors:** + +- [ ] Review the recent English file changes and the current translations +- [ ] Confirm if translation already reflects the update โ€” close the issue if so +- [ ] Update the translation files accordingly +- [ ] Maintain structure, code blocks, and formatting +- [ ] Ensure translation is accurate and culturally appropriate +``` + +## ๐Ÿ”ง Configuration + +### Environment Variables + +- `GITHUB_TOKEN`: Required for Week 2+ features (API access and issue creation) +- `GITHUB_REPOSITORY`: Auto-detected in GitHub Actions (format: `owner/repo`) +- `GITHUB_EVENT_NAME`: Auto-detected in GitHub Actions +- `GITHUB_REF_NAME`: Auto-detected branch name + +### Options Object + +```javascript +{ + enableWeek2: boolean, // Force Week 2 mode + githubToken: string, // GitHub token for API access + createIssues: boolean, // Whether to create GitHub issues + scanAll: boolean // Scan all files instead of just changed files +} +``` + +## ๐Ÿ“Š Output Example + +``` +๐ŸŽฏ p5.js Translation Tracker - Week 2 Mode +โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ• +๐Ÿ“… Event: local +๐Ÿ  Working directory: /Users/user/p5.js-website-new +๐ŸŒ Tracking languages: es, hi, ko, zh-Hans +๐Ÿ” Scan mode: All files + +๐Ÿ” REPOSITORY STRUCTURE ANALYSIS +โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ• +๐Ÿ“ Examples path: src/content/examples +๐ŸŒ Available languages: en, es, hi, ko, zh-Hans + en: 61 example files across 15 categories + es: 61 example files across 15 categories + hi: 61 example files across 15 categories + ko: 61 example files across 15 categories + zh-Hans: 61 example files across 15 categories + +๐Ÿ“ Checking translations for: src/content/examples/en/01_Shapes_And_Color/00_Shape_Primitives/description.mdx + ๐Ÿ”„ es: Needs update + โœ… hi: Up to date + ๐Ÿ”„ ko: Needs update + ๐Ÿ”„ zh-Hans: Needs update + +๐Ÿ“ Creating GitHub issue for src/content/examples/en/01_Shapes_And_Color/00_Shape_Primitives/description.mdx... + โœ… Created issue #123: Update translations for description.mdx + +๐Ÿ“Š TRANSLATION STATUS SUMMARY (Week 2) +โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ• +๐Ÿ†• Missing translations: 0 +๐Ÿ”„ Outdated translations: 3 +โœ… Up-to-date translations: 1 +๐ŸŽซ Issues created: 1 + - Issue #123: description.mdx (Affected: es, ko, zh-Hans) +``` + +## ๐Ÿš€ GitHub Actions Integration + +Create `.github/workflows/translation-tracker.yml`: + +```yaml +name: Translation Tracker + +on: + push: + paths: + - 'src/content/examples/en/**/*.mdx' + schedule: + - cron: '0 0 * * 1' # Weekly scan + +jobs: + track-translations: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + - uses: actions/setup-node@v3 + with: + node-version: '18' + + - name: Install dependencies + run: cd .github/actions/translation-tracker && npm install + + - name: Track translation status + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + run: node .github/actions/translation-tracker/index.js +``` + +## ๐Ÿ”„ Migration from Previous Versions + +- **Week 1 โ†’ Week 2**: Fully backward compatible, automatically detects GitHub token +- **Week 2 โ†’ Week 3**: Automatically creates single issues instead of multiple issues per language +- **Legacy Mode**: Will continue working in Week 1 mode if no GitHub token is provided + +## ๐Ÿ’ก Key Improvements in Week 3 + +1. **Single Issue Per File**: Instead of creating separate issues for each language, creates one issue that covers all affected languages for a specific file +2. **Better Organization**: Issues are grouped by file rather than scattered by language +3. **Enhanced Labels**: Uses "needs translation" + specific language labels for better filtering +4. **Manual Scanning**: Ability to scan all files on demand rather than just changed files +5. **Improved Issue Format**: More detailed and actionable issue templates + +## ๐Ÿค Contributing + +The translation tracker is modular and extensible: + +- Add new languages by updating `SUPPORTED_LANGUAGES` array +- Modify issue templates in `formatMultiLanguageIssueBody()` method +- Extend file scanning logic in `getAllEnglishExampleFiles()` function +- Add new detection modes by extending the `main()` function options + +## ๐Ÿ“š Dependencies -## Dependencies -- `@actions/core` - GitHub Actions toolkit -- `@actions/github` - GitHub Actions GitHub integration -- `@octokit/rest` - GitHub API client for Week 2 features \ No newline at end of file +- `@actions/core`: GitHub Actions integration +- `@actions/github`: GitHub API wrapper +- `@octokit/rest`: GitHub REST API client +- Node.js built-in modules: `fs`, `path`, `child_process` \ No newline at end of file diff --git a/.github/actions/translation-tracker/index.js b/.github/actions/translation-tracker/index.js index 1f1eb49db0..3eb37cebf7 100644 --- a/.github/actions/translation-tracker/index.js +++ b/.github/actions/translation-tracker/index.js @@ -126,6 +126,43 @@ class GitHubCommitTracker { } } + /** + * Create a single GitHub issue for a file covering multiple languages + */ + async createMultiLanguageTranslationIssue(fileTranslations) { + const englishFile = fileTranslations.englishFile; + const issueTitle = `๐ŸŒ Update translations for ${path.basename(englishFile)}`; + const issueBody = this.formatMultiLanguageIssueBody(fileTranslations); + + // Create labels: "needs translation" + specific language labels + const labels = ['needs translation', 'help wanted']; + const affectedLanguages = [ + ...fileTranslations.outdatedLanguages.map(l => l.language), + ...fileTranslations.missingLanguages.map(l => l.language) + ]; + + // Add specific language labels (remove duplicates) + const uniqueLanguages = [...new Set(affectedLanguages)]; + uniqueLanguages.forEach(lang => { + labels.push(`lang-${lang}`); + }); + + try { + const { data } = await this.octokit.rest.issues.create({ + owner: this.owner, + repo: this.repo, + title: issueTitle, + body: issueBody, + labels: labels + }); + + return data; + } catch (error) { + console.error(`โŒ Error creating multi-language issue:`, error.message); + return null; + } + } + /** * Format the issue body with helpful information */ @@ -164,6 +201,70 @@ class GitHubCommitTracker { *Need help? Check our [translation guidelines](https://github.com/processing/p5.js-website/blob/main/contributor_docs/translation.md)*`; } + /** + * Format the issue body for multi-language updates + */ + formatMultiLanguageIssueBody(fileTranslations) { + const englishFile = fileTranslations.englishFile; + const outdatedLanguages = fileTranslations.outdatedLanguages; + const missingLanguages = fileTranslations.missingLanguages; + + let body = `## ๐ŸŒ Translation Update Needed + +**File**: \`${englishFile}\` +**Branch**: \`${this.currentBranch}\` + +### ๐Ÿ“… Timeline +- **Latest English update**: ${fileTranslations.englishCommit.date.toLocaleDateString()} by ${fileTranslations.englishCommit.author} + +`; + + // Outdated translations section + if (outdatedLanguages.length > 0) { + body += `### ๐Ÿ”„ Outdated Translations\n\n`; + outdatedLanguages.forEach(lang => { + const translationPath = lang.translationPath; + body += `- **${this.getLanguageDisplayName(lang.language)}**: Last updated ${lang.commitInfo.translation.date.toLocaleDateString()} by ${lang.commitInfo.translation.author}\n`; + body += ` - [๐Ÿ“ View file](https://github.com/${this.owner}/${this.repo}/blob/${this.currentBranch}/${translationPath})\n`; + body += ` - [๐Ÿ” Compare changes](https://github.com/${this.owner}/${this.repo}/compare/${lang.commitInfo.translation.sha}...${lang.commitInfo.english.sha})\n\n`; + }); + } + + // Missing translations section + if (missingLanguages.length > 0) { + body += `### โŒ Missing Translations\n\n`; + missingLanguages.forEach(lang => { + const translationPath = lang.translationPath; + body += `- **${this.getLanguageDisplayName(lang.language)}**: Translation file does not exist\n`; + body += ` - Expected location: \`${translationPath}\`\n\n`; + }); + } + + body += `### ๐Ÿ”— Quick Links +- [๐Ÿ“„ Current English file](https://github.com/${this.owner}/${this.repo}/blob/${this.currentBranch}/${englishFile}) + +### โœ… Action Checklist + +**For translators / contributors:** + +- [ ] Review the recent English file changes and the current translations +- [ ] Confirm if translation already reflects the update โ€” close the issue if so +- [ ] Update the translation files accordingly +- [ ] Maintain structure, code blocks, and formatting +- [ ] Ensure translation is accurate and culturally appropriate + +### ๐Ÿ“ Summary of English File Changes +**Last commit**: [${fileTranslations.englishCommit.message}](${fileTranslations.englishCommit.url}) + +${outdatedLanguages.length > 0 || missingLanguages.length > 0 ? `**Change Type**: English file was updated. ${outdatedLanguages.length > 0 ? `${outdatedLanguages.map(l => this.getLanguageDisplayName(l.language)).join(', ')} translation${outdatedLanguages.length > 1 ? 's' : ''} may be outdated.` : ''} ${missingLanguages.length > 0 ? `${missingLanguages.map(l => this.getLanguageDisplayName(l.language)).join(', ')} translation${missingLanguages.length > 1 ? 's are' : ' is'} missing.` : ''}` : ''} + +--- +โ„น๏ธ **Need help?** See our [Translation Guidelines](https://github.com/processing/p5.js-website/blob/main/contributor_docs/translation.md) + +๐Ÿค– *This issue was auto-generated by the p5.js Translation Tracker*`; + return body; + } + /** * Get display name for language code */ @@ -218,6 +319,40 @@ function getChangedFiles(testFiles = null) { } } +/** + * Scan all English example files (for manual scanning) + */ +function getAllEnglishExampleFiles() { + const examplesPath = 'src/content/examples/en'; + const allFiles = []; + + try { + if (!fs.existsSync(examplesPath)) { + console.log(`โŒ Examples path does not exist: ${examplesPath}`); + return []; + } + + const scanDirectory = (dir) => { + const items = fs.readdirSync(dir); + items.forEach(item => { + const itemPath = path.join(dir, item); + if (fs.statSync(itemPath).isDirectory()) { + scanDirectory(itemPath); + } else if (item.endsWith('.mdx')) { + allFiles.push(itemPath); + } + }); + }; + + scanDirectory(examplesPath); + console.log(`๐Ÿ“Š Found ${allFiles.length} English example files to check`); + return allFiles; + } catch (error) { + console.error('โŒ Error scanning English example files:', error.message); + return []; + } +} + function fileExists(filePath) { try { @@ -245,27 +380,44 @@ async function checkTranslationStatus(changedExampleFiles, githubTracker = null, issuesCreated: [] }; + // Group translation issues by file to create single issues per file + const fileTranslationMap = new Map(); + for (const englishFile of changedExampleFiles) { console.log(`\n๐Ÿ“ Checking translations for: ${englishFile}`); + const fileTranslations = { + englishFile, + outdatedLanguages: [], + missingLanguages: [], + upToDateLanguages: [], + englishCommit: null + }; + for (const language of SUPPORTED_LANGUAGES) { const translationPath = englishFile.replace('/en/', `/${language}/`); const exists = fileExists(translationPath); if (!exists) { console.log(` โŒ ${language}: Missing translation`); - translationStatus.missing.push({ + const missingItem = { englishFile, language, translationPath, status: 'missing' - }); + }; + translationStatus.missing.push(missingItem); + fileTranslations.missingLanguages.push(missingItem); continue; } if (githubTracker) { - const englishCommit = await githubTracker.getLastCommit(englishFile); + // Get English commit only once per file + if (!fileTranslations.englishCommit) { + fileTranslations.englishCommit = await githubTracker.getLastCommit(englishFile); + } + const englishCommit = fileTranslations.englishCommit; const translationCommit = await githubTracker.getLastCommit(translationPath); if (!englishCommit) { @@ -275,12 +427,14 @@ async function checkTranslationStatus(changedExampleFiles, githubTracker = null, if (!translationCommit) { console.log(` ๐Ÿ†• ${language}: Missing translation (no commits)`); - translationStatus.missing.push({ + const missingItem = { englishFile, language, translationPath, status: 'missing' - }); + }; + translationStatus.missing.push(missingItem); + fileTranslations.missingLanguages.push(missingItem); continue; } @@ -303,32 +457,17 @@ async function checkTranslationStatus(changedExampleFiles, githubTracker = null, }; translationStatus.needsUpdate.push(statusItem); - - - if (createIssues) { - console.log(` ๐Ÿ“ Creating GitHub issue for ${language} translation...`); - const issue = await githubTracker.createTranslationIssue( - englishFile, - language, - statusItem.commitInfo - ); - if (issue) { - console.log(` โœ… Created issue #${issue.number}: ${issue.title}`); - translationStatus.issuesCreated.push({ - ...statusItem, - issueNumber: issue.number, - issueUrl: issue.html_url - }); - } - } + fileTranslations.outdatedLanguages.push(statusItem); } else { console.log(` โœ… ${language}: Up to date`); - translationStatus.upToDate.push({ + const upToDateItem = { englishFile, language, translationPath, status: 'up-to-date' - }); + }; + translationStatus.upToDate.push(upToDateItem); + fileTranslations.upToDateLanguages.push(upToDateItem); } } else { // Week 1: Fallback to file modification time comparison @@ -343,25 +482,55 @@ async function checkTranslationStatus(changedExampleFiles, githubTracker = null, if (isOutdated) { console.log(` ๐Ÿ”„ ${language}: Needs update (English: ${englishModTime.toISOString()}, Translation: ${translationModTime.toISOString()})`); - translationStatus.needsUpdate.push({ + const statusItem = { englishFile, language, translationPath, status: 'outdated', englishModTime, translationModTime - }); + }; + translationStatus.needsUpdate.push(statusItem); + fileTranslations.outdatedLanguages.push(statusItem); } else { console.log(` โœ… ${language}: Up to date`); - translationStatus.upToDate.push({ + const upToDateItem = { englishFile, language, translationPath, status: 'up-to-date' - }); + }; + translationStatus.upToDate.push(upToDateItem); + fileTranslations.upToDateLanguages.push(upToDateItem); } } } + + // Store file translations for potential issue creation + if (fileTranslations.outdatedLanguages.length > 0 || fileTranslations.missingLanguages.length > 0) { + fileTranslationMap.set(englishFile, fileTranslations); + } + } + + // Create single issues per file (covering all affected languages) + if (createIssues && githubTracker) { + for (const [englishFile, fileTranslations] of fileTranslationMap) { + console.log(`\n๐Ÿ“ Creating GitHub issue for ${englishFile}...`); + const issue = await githubTracker.createMultiLanguageTranslationIssue(fileTranslations); + if (issue) { + console.log(` โœ… Created issue #${issue.number}: ${issue.title}`); + const issueItem = { + englishFile, + affectedLanguages: [ + ...fileTranslations.outdatedLanguages.map(l => l.language), + ...fileTranslations.missingLanguages.map(l => l.language) + ], + issueNumber: issue.number, + issueUrl: issue.html_url + }; + translationStatus.issuesCreated.push(issueItem); + } + } } return translationStatus; @@ -391,7 +560,7 @@ function displaySummary(translationStatus, isWeek2 = false) { if (translationStatus.issuesCreated && translationStatus.issuesCreated.length > 0) { console.log(`๐ŸŽซ Issues created: ${translationStatus.issuesCreated.length}`); translationStatus.issuesCreated.forEach(item => { - console.log(` - Issue #${item.issueNumber}: ${item.language} - ${item.englishFile}`); + console.log(` - Issue #${item.issueNumber}: ${item.englishFile} (Affected: ${item.affectedLanguages.join(', ')})`); console.log(` ${item.issueUrl}`); }); } @@ -463,12 +632,14 @@ function exploreRepoStructure() { async function main(testFiles = null, options = {}) { const isWeek2 = !!options.enableWeek2 || !!process.env.GITHUB_TOKEN; + const scanAllFiles = options.scanAll || false; console.log(`๐ŸŽฏ p5.js Translation Tracker - ${isWeek2 ? 'Week 2' : 'Week 1'} Mode`); console.log('โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•'); console.log(`๐Ÿ“… Event: ${process.env.GITHUB_EVENT_NAME || 'local'}`); console.log(`๐Ÿ  Working directory: ${process.cwd()}`); console.log(`๐ŸŒ Tracking languages: ${SUPPORTED_LANGUAGES.join(', ')}`); + console.log(`๐Ÿ” Scan mode: ${scanAllFiles ? 'All files' : 'Changed files only'}`); // Week 2: Initialize GitHub tracker if token is available let githubTracker = null; @@ -487,35 +658,53 @@ async function main(testFiles = null, options = {}) { console.log('โš ๏ธ No GitHub token provided - running in Week 1 mode'); } } - - exploreRepoStructure(); - const changedExampleFiles = getChangedFiles(testFiles); + exploreRepoStructure(); + + // Get files to check + let filesToCheck; + if (scanAllFiles) { + console.log('\n๐Ÿ” MANUAL SCAN MODE: Checking all English example files'); + filesToCheck = getAllEnglishExampleFiles(); + } else { + filesToCheck = getChangedFiles(testFiles); + } - if (changedExampleFiles.length === 0) { - console.log('\nโœจ No changes detected in English example files.'); - console.log('๐Ÿ“ Nothing to track for translations in this commit!'); + if (filesToCheck.length === 0) { + if (scanAllFiles) { + console.log('\nโœจ No English example files found to check.'); + } else { + console.log('\nโœจ No changes detected in English example files.'); + console.log('๐Ÿ“ Nothing to track for translations in this commit!'); + } return; } - + const createIssues = options.createIssues !== false && githubTracker !== null; const translationStatus = await checkTranslationStatus( - changedExampleFiles, + filesToCheck, githubTracker, createIssues ); displaySummary(translationStatus, isWeek2); + + if (scanAllFiles) { + console.log('\n๐Ÿ’ก Manual scan completed!'); + console.log('๐Ÿ“ To create issues, run with GitHub token and createIssues=true'); + } } -// Export for testing (Week 1 + Week 2) +// Export for testing (Week 1 + Week 2 + Week 3) module.exports = { main, getChangedFiles, + getAllEnglishExampleFiles, checkTranslationStatus, exploreRepoStructure, - GitHubCommitTracker + GitHubCommitTracker, + SUPPORTED_LANGUAGES }; // Run if called directly diff --git a/.github/actions/translation-tracker/test-local.js b/.github/actions/translation-tracker/test-local.js index 79cd71a5e0..3bd53798ee 100644 --- a/.github/actions/translation-tracker/test-local.js +++ b/.github/actions/translation-tracker/test-local.js @@ -1,20 +1,120 @@ +const { main, getAllEnglishExampleFiles, SUPPORTED_LANGUAGES } = require('./index.js'); -const { main } = require('./index.js'); +async function testWeek3Functionality() { + console.log('๐Ÿงช Testing Week 3 Multi-Language Translation Tracker'); + console.log('====================================================\n'); -// Test scenarios using actual example files that exist -const testFiles = [ - 'src/content/examples/en/01_Shapes_And_Color/00_Shape_Primitives/description.mdx', - 'src/content/examples/en/02_Animation_And_Variables/00_Drawing_Lines/description.mdx', - 'src/content/examples/en/03_Imported_Media/00_Words/description.mdx' -]; + // Test 1: Single issue per file with multiple languages + console.log('๐Ÿ“ TEST 1: Multi-language issue creation'); + console.log('=========================================='); + const testFiles = [ + 'src/content/examples/en/01_Shapes_And_Color/00_Shape_Primitives/description.mdx', + 'src/content/examples/en/02_Animation_And_Variables/00_Drawing_Lines/description.mdx', + 'src/content/examples/en/03_Imported_Media/00_Words/description.mdx' + ]; -console.log('๐Ÿงช Testing Example Translation Tracker Locally'); -console.log('===============================================\n'); + await main(testFiles, { + createIssues: false // Don't create real issues in test mode + }); -// Run the main function with test files -main(testFiles); + console.log('\n\n'); -console.log('\n๐Ÿ’ก This demonstrates local testing capability as requested by mentor'); -console.log('๐Ÿ”ง The git logic is now separated and modular for easier testing'); -console.log('๐Ÿ“– Now tracking examples instead of tutorials as requested'); \ No newline at end of file + // Test 2: Manual scan mode (scan all files) + console.log('๐Ÿ“ TEST 2: Manual scan mode (first 5 files only for demo)'); + console.log('=========================================================='); + + const allFiles = getAllEnglishExampleFiles(); + const firstFiveFiles = allFiles.slice(0, 5); // Limit for demo + + if (firstFiveFiles.length > 0) { + await main(firstFiveFiles, { + scanAll: false, // Set to false since we're passing specific files + createIssues: false + }); + } else { + console.log('No files found for manual scan test'); + } + + console.log('\n\n'); + + // Test 3: Week 2 mode with GitHub token (if available) + console.log('๐Ÿ“ TEST 3: Week 2 mode with GitHub API (if token available)'); + console.log('=============================================================='); + + if (process.env.GITHUB_TOKEN) { + console.log('โœ… GitHub token detected - running in Week 2 mode'); + await main(testFiles, { + githubToken: process.env.GITHUB_TOKEN, + createIssues: false // Change to true to actually create issues + }); + } else { + console.log('โš ๏ธ No GitHub token - run with GITHUB_TOKEN=your_token for Week 2 features'); + console.log('๐Ÿ“ Running in Week 1 mode instead...'); + await main(testFiles, { createIssues: false }); + } + + console.log('\n\n'); + + // Test 4: Full manual scan (commented out to avoid spam) + console.log('๐Ÿ“ TEST 4: Full manual scan mode (disabled by default)'); + console.log('======================================================'); + console.log('๐Ÿ’ก To run full scan: main(null, { scanAll: true })'); + console.log(`๐Ÿ“Š Found ${allFiles.length} total English example files`); + console.log(`๐ŸŒ Supported languages: ${SUPPORTED_LANGUAGES.join(', ')}`); + + console.log('\nโœ… Week 3 Testing Complete!'); + console.log('\n๐Ÿ“‹ Key Features Demonstrated:'); + console.log(' โœ… Single issue per file covering all affected languages'); + console.log(' โœ… Proper labeling: "needs translation" + language-specific labels'); + console.log(' โœ… Manual scan capability for all files'); + console.log(' โœ… Week 1 & Week 2 backward compatibility'); + console.log(' โœ… Enhanced issue format with detailed language status'); +} + +// Allow running specific modes +async function runMode() { + const mode = process.argv[2]; + + switch(mode) { + case 'manual': + console.log('๐Ÿ” Running manual scan of all files...'); + await main(null, { scanAll: true, createIssues: false }); + break; + case 'week2': + if (process.env.GITHUB_TOKEN) { + console.log('๐Ÿš€ Running Week 2 mode with issue creation...'); + await main(null, { + scanAll: false, + createIssues: true, + githubToken: process.env.GITHUB_TOKEN + }); + } else { + console.log('โŒ GITHUB_TOKEN required for Week 2 mode'); + console.log('Usage: GITHUB_TOKEN=token node test-local.js week2'); + } + break; + case 'issues': + console.log('๐ŸŽซ Creating issues for test files...'); + const testFiles = [ + 'src/content/examples/en/01_Shapes_And_Color/00_Shape_Primitives/description.mdx' + ]; + await main(testFiles, { + createIssues: true, + githubToken: process.env.GITHUB_TOKEN + }); + break; + default: + await testWeek3Functionality(); + } +} + +// Usage examples +console.log('๐Ÿ’ก Usage Examples:'); +console.log(' node test-local.js # Run all tests'); +console.log(' node test-local.js manual # Manual scan all files'); +console.log(' GITHUB_TOKEN=token node test-local.js week2 # Week 2 with issues'); +console.log(' GITHUB_TOKEN=token node test-local.js issues # Create test issues'); +console.log(''); + +runMode().catch(console.error); \ No newline at end of file diff --git a/.github/workflows/translation-sync.yml b/.github/workflows/translation-sync.yml index b3bbe43440..d0bd6c60a3 100644 --- a/.github/workflows/translation-sync.yml +++ b/.github/workflows/translation-sync.yml @@ -2,13 +2,13 @@ name: Translation Sync Tracker on: push: - branches: [main] + branches: [main, week2] paths: - - 'src/content/reference/en/**' + - 'src/content/examples/en/**' pull_request: - branches: [main] + branches: [main, week2] paths: - - 'src/content/reference/en/**' + - 'src/content/examples/en/**' jobs: track-translation-changes: @@ -28,6 +28,9 @@ jobs: - name: Install dependencies run: npm ci + - name: Install translation tracker dependencies + run: cd .github/actions/translation-tracker && npm install + - name: Run translation tracker env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} diff --git a/src/content/examples/en/01_Shapes_And_Color/00_Shape_Primitives/description.mdx b/src/content/examples/en/01_Shapes_And_Color/00_Shape_Primitives/description.mdx index f590bcb858..0a28e2fcc2 100644 --- a/src/content/examples/en/01_Shapes_And_Color/00_Shape_Primitives/description.mdx +++ b/src/content/examples/en/01_Shapes_And_Color/00_Shape_Primitives/description.mdx @@ -9,7 +9,7 @@ relatedReference: --- This program demonstrates the use of the basic shape -primitive functions. Week 2 testing: improved documentation for better clarity. +primitive functions. Week 3 testing: automated translation tracking now active. square(), rect(), ellipse(), From f03b60d63bf73ec2e9a19174ed09baecf26b97a9 Mon Sep 17 00:00:00 2001 From: Divyansh013 Date: Sun, 6 Jul 2025 21:38:34 +0530 Subject: [PATCH 13/18] Add manual trigger support to translation tracker workflow - Added workflow_dispatch with scan_all and create_issues options - Can now manually trigger action from GitHub UI - Supports both normal and full-scan modes --- .github/workflows/translation-sync.yml | 30 +++++++++++++++++++++++++- 1 file changed, 29 insertions(+), 1 deletion(-) diff --git a/.github/workflows/translation-sync.yml b/.github/workflows/translation-sync.yml index d0bd6c60a3..8f4c04dba3 100644 --- a/.github/workflows/translation-sync.yml +++ b/.github/workflows/translation-sync.yml @@ -9,6 +9,18 @@ on: branches: [main, week2] paths: - 'src/content/examples/en/**' + workflow_dispatch: + inputs: + scan_all: + description: 'Scan all files instead of just changed files' + required: false + default: false + type: boolean + create_issues: + description: 'Create GitHub issues for outdated translations' + required: false + default: true + type: boolean jobs: track-translation-changes: @@ -34,4 +46,20 @@ jobs: - name: Run translation tracker env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - run: node .github/actions/translation-tracker/index.js \ No newline at end of file + SCAN_ALL: ${{ inputs.scan_all || 'false' }} + CREATE_ISSUES: ${{ inputs.create_issues }} + run: | + if [ "$SCAN_ALL" = "true" ]; then + echo "๐Ÿ” Running manual scan of ALL files..." + node -e " + const { main } = require('./.github/actions/translation-tracker/index.js'); + main(null, { + scanAll: true, + createIssues: process.env.CREATE_ISSUES === 'true', + githubToken: process.env.GITHUB_TOKEN + }); + " + else + echo "๐Ÿ“ Running normal change detection..." + node .github/actions/translation-tracker/index.js + fi \ No newline at end of file From dea5e7cca99ea02b9c6c4d7da60bfb94d0b14468 Mon Sep 17 00:00:00 2001 From: Divyansh013 Date: Sun, 6 Jul 2025 21:44:50 +0530 Subject: [PATCH 14/18] Test: Manual trigger detection test - Modified description.mdx to test translation tracker - Should now detect 1 English example file change --- .../en/01_Shapes_And_Color/00_Shape_Primitives/description.mdx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/content/examples/en/01_Shapes_And_Color/00_Shape_Primitives/description.mdx b/src/content/examples/en/01_Shapes_And_Color/00_Shape_Primitives/description.mdx index 0a28e2fcc2..6ff16675b2 100644 --- a/src/content/examples/en/01_Shapes_And_Color/00_Shape_Primitives/description.mdx +++ b/src/content/examples/en/01_Shapes_And_Color/00_Shape_Primitives/description.mdx @@ -9,7 +9,7 @@ relatedReference: --- This program demonstrates the use of the basic shape -primitive functions. Week 3 testing: automated translation tracking now active. +primitive functions. Manual trigger testing: this should be detected by git. square(), rect(), ellipse(), From 486adedb3d463eeabcadb02097cbb4b039526500 Mon Sep 17 00:00:00 2001 From: Divyansh013 Date: Sun, 6 Jul 2025 23:36:18 +0530 Subject: [PATCH 15/18] Simplify translation tracker - clean output - test-local.js: Only shows changed files from predefined test files - index.js: Production mode with minimal console output - Removed verbose logging and repository exploration - Clear distinction: test mode vs production mode with GitHub token --- .github/actions/translation-tracker/index.js | 205 ++++-------------- .../actions/translation-tracker/test-local.js | 126 +---------- 2 files changed, 53 insertions(+), 278 deletions(-) diff --git a/.github/actions/translation-tracker/index.js b/.github/actions/translation-tracker/index.js index 3eb37cebf7..ffddfb984b 100644 --- a/.github/actions/translation-tracker/index.js +++ b/.github/actions/translation-tracker/index.js @@ -300,17 +300,9 @@ function getChangedFiles(testFiles = null) { const changedFilesOutput = execSync(gitCommand, { encoding: 'utf8' }); const allChangedFiles = changedFilesOutput.trim().split('\n').filter(file => file.length > 0); - const changedExampleFiles = allChangedFiles.filter(file => - file.startsWith('src/content/examples/en') && file.endsWith('.mdx') - ); - - console.log(`๐Ÿ“Š Total changed files: ${allChangedFiles.length}`); - console.log(`๐Ÿ“– Changed English example files: ${changedExampleFiles.length}`); - - if (changedExampleFiles.length > 0) { - console.log('๐Ÿ“„ Changed English example files:'); - changedExampleFiles.forEach(file => console.log(` - ${file}`)); - } + const changedExampleFiles = allChangedFiles.filter(file => + file.startsWith('src/content/examples/en') && file.endsWith('.mdx') + ); return changedExampleFiles; } catch (error) { @@ -384,7 +376,7 @@ async function checkTranslationStatus(changedExampleFiles, githubTracker = null, const fileTranslationMap = new Map(); for (const englishFile of changedExampleFiles) { - console.log(`\n๐Ÿ“ Checking translations for: ${englishFile}`); + const fileName = englishFile.split('/').pop(); const fileTranslations = { englishFile, @@ -399,7 +391,6 @@ async function checkTranslationStatus(changedExampleFiles, githubTracker = null, const exists = fileExists(translationPath); if (!exists) { - console.log(` โŒ ${language}: Missing translation`); const missingItem = { englishFile, language, @@ -421,12 +412,10 @@ async function checkTranslationStatus(changedExampleFiles, githubTracker = null, const translationCommit = await githubTracker.getLastCommit(translationPath); if (!englishCommit) { - console.log(` โš ๏ธ ${language}: Could not get English commit info`); continue; } if (!translationCommit) { - console.log(` ๐Ÿ†• ${language}: Missing translation (no commits)`); const missingItem = { englishFile, language, @@ -441,10 +430,6 @@ async function checkTranslationStatus(changedExampleFiles, githubTracker = null, const isOutdated = englishCommit.date > translationCommit.date; if (isOutdated) { - console.log(` ๐Ÿ”„ ${language}: Needs update`); - console.log(` - English: ${englishCommit.date.toISOString()} (${englishCommit.sha.substring(0, 7)})`); - console.log(` - Translation: ${translationCommit.date.toISOString()} (${translationCommit.sha.substring(0, 7)})`); - const statusItem = { englishFile, language, @@ -459,7 +444,6 @@ async function checkTranslationStatus(changedExampleFiles, githubTracker = null, translationStatus.needsUpdate.push(statusItem); fileTranslations.outdatedLanguages.push(statusItem); } else { - console.log(` โœ… ${language}: Up to date`); const upToDateItem = { englishFile, language, @@ -481,7 +465,6 @@ async function checkTranslationStatus(changedExampleFiles, githubTracker = null, const isOutdated = translationModTime < englishModTime; if (isOutdated) { - console.log(` ๐Ÿ”„ ${language}: Needs update (English: ${englishModTime.toISOString()}, Translation: ${translationModTime.toISOString()})`); const statusItem = { englishFile, language, @@ -493,7 +476,6 @@ async function checkTranslationStatus(changedExampleFiles, githubTracker = null, translationStatus.needsUpdate.push(statusItem); fileTranslations.outdatedLanguages.push(statusItem); } else { - console.log(` โœ… ${language}: Up to date`); const upToDateItem = { englishFile, language, @@ -515,10 +497,8 @@ async function checkTranslationStatus(changedExampleFiles, githubTracker = null, // Create single issues per file (covering all affected languages) if (createIssues && githubTracker) { for (const [englishFile, fileTranslations] of fileTranslationMap) { - console.log(`\n๐Ÿ“ Creating GitHub issue for ${englishFile}...`); const issue = await githubTracker.createMultiLanguageTranslationIssue(fileTranslations); if (issue) { - console.log(` โœ… Created issue #${issue.number}: ${issue.title}`); const issueItem = { englishFile, affectedLanguages: [ @@ -537,172 +517,73 @@ async function checkTranslationStatus(changedExampleFiles, githubTracker = null, } -function displaySummary(translationStatus, isWeek2 = false) { - console.log(`\n๐Ÿ“Š TRANSLATION STATUS SUMMARY ${isWeek2 ? '(Week 2)' : '(Week 1)'}`); - console.log('โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•'); - - console.log(`๐Ÿ†• Missing translations: ${translationStatus.missing.length}`); - if (translationStatus.missing.length > 0) { - translationStatus.missing.forEach(item => { - console.log(` - ${item.language}: ${item.englishFile}`); - }); - } - - console.log(`๐Ÿ”„ Outdated translations: ${translationStatus.needsUpdate.length}`); - if (translationStatus.needsUpdate.length > 0) { - translationStatus.needsUpdate.forEach(item => { - console.log(` - ${item.language}: ${item.englishFile}`); - }); - } - - console.log(`โœ… Up-to-date translations: ${translationStatus.upToDate.length}`); - - if (translationStatus.issuesCreated && translationStatus.issuesCreated.length > 0) { - console.log(`๐ŸŽซ Issues created: ${translationStatus.issuesCreated.length}`); - translationStatus.issuesCreated.forEach(item => { - console.log(` - Issue #${item.issueNumber}: ${item.englishFile} (Affected: ${item.affectedLanguages.join(', ')})`); - console.log(` ${item.issueUrl}`); - }); - } - - if (isWeek2) { - console.log('\n๐Ÿ’ก Week 2 Features:'); - console.log('โœ… GitHub API integration for accurate commit tracking'); - console.log('โœ… Automated issue creation for outdated translations'); - console.log('โœ… Enhanced issue templates with helpful links'); - console.log('โœ… Backward compatibility with Week 1 functionality'); - } -} +// Removed verbose summary function -function exploreRepoStructure() { - console.log('\n๐Ÿ” REPOSITORY STRUCTURE ANALYSIS'); - console.log('โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•'); - - try { - const examplesPath = 'src/content/examples'; - console.log(`๐Ÿ“ Examples path: ${examplesPath}`); - - if (fs.existsSync(examplesPath)) { - const languages = fs.readdirSync(examplesPath) - .filter(item => fs.statSync(path.join(examplesPath, item)).isDirectory()) - .filter(item => !item.startsWith('.') && item !== 'images'); - - console.log(`๐ŸŒ Available languages: ${languages.join(', ')}`); - - // Count example files in each language - languages.forEach(lang => { - const langPath = path.join(examplesPath, lang); - try { - let totalFiles = 0; - const categories = fs.readdirSync(langPath) - .filter(item => fs.statSync(path.join(langPath, item)).isDirectory()); - - categories.forEach(category => { - const categoryPath = path.join(langPath, category); - const countFilesRecursively = (dir) => { - const items = fs.readdirSync(dir); - let count = 0; - items.forEach(item => { - const itemPath = path.join(dir, item); - if (fs.statSync(itemPath).isDirectory()) { - count += countFilesRecursively(itemPath); - } else if (item.endsWith('.mdx')) { - count++; - } - }); - return count; - }; - totalFiles += countFilesRecursively(categoryPath); - }); - - console.log(` ${lang}: ${totalFiles} example files across ${categories.length} categories`); - } catch (error) { - console.log(` ${lang}: Error reading directory - ${error.message}`); - } - }); - } else { - console.log(`โŒ Examples path does not exist: ${examplesPath}`); - } - } catch (error) { - console.error('โŒ Error exploring repository structure:', error.message); - } -} +// Remove verbose repository exploration async function main(testFiles = null, options = {}) { - const isWeek2 = !!options.enableWeek2 || !!process.env.GITHUB_TOKEN; - const scanAllFiles = options.scanAll || false; + const hasToken = !!process.env.GITHUB_TOKEN; + const isProduction = hasToken && !testFiles; // Production if has token and no test files - console.log(`๐ŸŽฏ p5.js Translation Tracker - ${isWeek2 ? 'Week 2' : 'Week 1'} Mode`); - console.log('โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•'); - console.log(`๐Ÿ“… Event: ${process.env.GITHUB_EVENT_NAME || 'local'}`); - console.log(`๐Ÿ  Working directory: ${process.cwd()}`); - console.log(`๐ŸŒ Tracking languages: ${SUPPORTED_LANGUAGES.join(', ')}`); - console.log(`๐Ÿ” Scan mode: ${scanAllFiles ? 'All files' : 'Changed files only'}`); - - // Week 2: Initialize GitHub tracker if token is available + if (testFiles) { + console.log(`๐Ÿงช Test mode: Checking ${testFiles.length} predefined files`); + } else { + console.log(`๐ŸŽฏ Production mode: ${hasToken ? 'Creating real issues' : 'File-based tracking only'}`); + } + + // Initialize GitHub tracker if token is available let githubTracker = null; - if (isWeek2) { - const token = process.env.GITHUB_TOKEN || options.githubToken; - if (token) { - try { - const [owner, repo] = (process.env.GITHUB_REPOSITORY || 'processing/p5.js-website').split('/'); - githubTracker = new GitHubCommitTracker(token, owner, repo); - console.log(`๐Ÿ”— GitHub API initialized for ${owner}/${repo} (branch: ${githubTracker.currentBranch})`); - } catch (error) { - console.error('โš ๏ธ GitHub API initialization failed:', error.message); - console.log('๐Ÿ“ Falling back to Week 1 mode...'); - } - } else { - console.log('โš ๏ธ No GitHub token provided - running in Week 1 mode'); + if (hasToken) { + try { + const [owner, repo] = (process.env.GITHUB_REPOSITORY || 'processing/p5.js-website').split('/'); + githubTracker = new GitHubCommitTracker(process.env.GITHUB_TOKEN, owner, repo); + console.log(`๐Ÿ“ก Connected to ${owner}/${repo}`); + } catch (error) { + console.error('โŒ GitHub API failed, using file-based tracking'); } } - - exploreRepoStructure(); - + // Get files to check - let filesToCheck; - if (scanAllFiles) { - console.log('\n๐Ÿ” MANUAL SCAN MODE: Checking all English example files'); - filesToCheck = getAllEnglishExampleFiles(); - } else { - filesToCheck = getChangedFiles(testFiles); - } + const filesToCheck = getChangedFiles(testFiles); if (filesToCheck.length === 0) { - if (scanAllFiles) { - console.log('\nโœจ No English example files found to check.'); - } else { - console.log('\nโœจ No changes detected in English example files.'); - console.log('๐Ÿ“ Nothing to track for translations in this commit!'); - } + console.log('โœ… No English example files changed'); return; } - - const createIssues = options.createIssues !== false && githubTracker !== null; + + console.log(`๐Ÿ“ Checking ${filesToCheck.length} English example file(s):`); + filesToCheck.forEach(file => console.log(` - ${file.split('/').pop()}`)); + + const createIssues = isProduction && githubTracker !== null; const translationStatus = await checkTranslationStatus( filesToCheck, githubTracker, createIssues ); - - displaySummary(translationStatus, isWeek2); - - if (scanAllFiles) { - console.log('\n๐Ÿ’ก Manual scan completed!'); - console.log('๐Ÿ“ To create issues, run with GitHub token and createIssues=true'); + // Simple summary + const { needsUpdate, missing, issuesCreated } = translationStatus; + if (needsUpdate.length > 0 || missing.length > 0) { + console.log(`\n๐Ÿ“Š Translation issues found: ${needsUpdate.length + missing.length}`); + if (issuesCreated.length > 0) { + console.log(`๐ŸŽซ GitHub issues created: ${issuesCreated.length}`); + issuesCreated.forEach(issue => console.log(` - Issue #${issue.issueNumber}: ${issue.englishFile.split('/').pop()}`)); + } else if (!hasToken) { + console.log(`๐Ÿ’ก Run with GITHUB_TOKEN to create issues`); + } + } else { + console.log('\nโœ… All translations are up to date'); } } -// Export for testing (Week 1 + Week 2 + Week 3) +// Export for testing (simplified) module.exports = { main, getChangedFiles, getAllEnglishExampleFiles, checkTranslationStatus, - exploreRepoStructure, GitHubCommitTracker, SUPPORTED_LANGUAGES }; diff --git a/.github/actions/translation-tracker/test-local.js b/.github/actions/translation-tracker/test-local.js index 3bd53798ee..50a7a2c993 100644 --- a/.github/actions/translation-tracker/test-local.js +++ b/.github/actions/translation-tracker/test-local.js @@ -1,120 +1,14 @@ -const { main, getAllEnglishExampleFiles, SUPPORTED_LANGUAGES } = require('./index.js'); +const { main } = require('./index.js'); -async function testWeek3Functionality() { - console.log('๐Ÿงช Testing Week 3 Multi-Language Translation Tracker'); - console.log('====================================================\n'); +// Simple test with predefined files +const testFiles = [ + 'src/content/examples/en/01_Shapes_And_Color/00_Shape_Primitives/description.mdx', + 'src/content/examples/en/02_Animation_And_Variables/00_Drawing_Lines/description.mdx', + 'src/content/examples/en/03_Imported_Media/00_Words/description.mdx' +]; - // Test 1: Single issue per file with multiple languages - console.log('๐Ÿ“ TEST 1: Multi-language issue creation'); - console.log('=========================================='); - const testFiles = [ - 'src/content/examples/en/01_Shapes_And_Color/00_Shape_Primitives/description.mdx', - 'src/content/examples/en/02_Animation_And_Variables/00_Drawing_Lines/description.mdx', - 'src/content/examples/en/03_Imported_Media/00_Words/description.mdx' - ]; +console.log('๐Ÿงช Testing Translation Tracker with predefined files'); +console.log('===================================================='); - await main(testFiles, { - createIssues: false // Don't create real issues in test mode - }); - - console.log('\n\n'); - - // Test 2: Manual scan mode (scan all files) - console.log('๐Ÿ“ TEST 2: Manual scan mode (first 5 files only for demo)'); - console.log('=========================================================='); - - const allFiles = getAllEnglishExampleFiles(); - const firstFiveFiles = allFiles.slice(0, 5); // Limit for demo - - if (firstFiveFiles.length > 0) { - await main(firstFiveFiles, { - scanAll: false, // Set to false since we're passing specific files - createIssues: false - }); - } else { - console.log('No files found for manual scan test'); - } - - console.log('\n\n'); - - // Test 3: Week 2 mode with GitHub token (if available) - console.log('๐Ÿ“ TEST 3: Week 2 mode with GitHub API (if token available)'); - console.log('=============================================================='); - - if (process.env.GITHUB_TOKEN) { - console.log('โœ… GitHub token detected - running in Week 2 mode'); - await main(testFiles, { - githubToken: process.env.GITHUB_TOKEN, - createIssues: false // Change to true to actually create issues - }); - } else { - console.log('โš ๏ธ No GitHub token - run with GITHUB_TOKEN=your_token for Week 2 features'); - console.log('๐Ÿ“ Running in Week 1 mode instead...'); - await main(testFiles, { createIssues: false }); - } - - console.log('\n\n'); - - // Test 4: Full manual scan (commented out to avoid spam) - console.log('๐Ÿ“ TEST 4: Full manual scan mode (disabled by default)'); - console.log('======================================================'); - console.log('๐Ÿ’ก To run full scan: main(null, { scanAll: true })'); - console.log(`๐Ÿ“Š Found ${allFiles.length} total English example files`); - console.log(`๐ŸŒ Supported languages: ${SUPPORTED_LANGUAGES.join(', ')}`); - - console.log('\nโœ… Week 3 Testing Complete!'); - console.log('\n๐Ÿ“‹ Key Features Demonstrated:'); - console.log(' โœ… Single issue per file covering all affected languages'); - console.log(' โœ… Proper labeling: "needs translation" + language-specific labels'); - console.log(' โœ… Manual scan capability for all files'); - console.log(' โœ… Week 1 & Week 2 backward compatibility'); - console.log(' โœ… Enhanced issue format with detailed language status'); -} - -// Allow running specific modes -async function runMode() { - const mode = process.argv[2]; - - switch(mode) { - case 'manual': - console.log('๐Ÿ” Running manual scan of all files...'); - await main(null, { scanAll: true, createIssues: false }); - break; - case 'week2': - if (process.env.GITHUB_TOKEN) { - console.log('๐Ÿš€ Running Week 2 mode with issue creation...'); - await main(null, { - scanAll: false, - createIssues: true, - githubToken: process.env.GITHUB_TOKEN - }); - } else { - console.log('โŒ GITHUB_TOKEN required for Week 2 mode'); - console.log('Usage: GITHUB_TOKEN=token node test-local.js week2'); - } - break; - case 'issues': - console.log('๐ŸŽซ Creating issues for test files...'); - const testFiles = [ - 'src/content/examples/en/01_Shapes_And_Color/00_Shape_Primitives/description.mdx' - ]; - await main(testFiles, { - createIssues: true, - githubToken: process.env.GITHUB_TOKEN - }); - break; - default: - await testWeek3Functionality(); - } -} - -// Usage examples -console.log('๐Ÿ’ก Usage Examples:'); -console.log(' node test-local.js # Run all tests'); -console.log(' node test-local.js manual # Manual scan all files'); -console.log(' GITHUB_TOKEN=token node test-local.js week2 # Week 2 with issues'); -console.log(' GITHUB_TOKEN=token node test-local.js issues # Create test issues'); -console.log(''); - -runMode().catch(console.error); \ No newline at end of file +main(testFiles, { createIssues: false }); \ No newline at end of file From 63753de07d18e5fc26570591cb71bec200da70d1 Mon Sep 17 00:00:00 2001 From: Divyansh013 Date: Sun, 6 Jul 2025 23:40:04 +0530 Subject: [PATCH 16/18] Add scan all files option to index.js - SCAN_ALL=true environment variable to check all 61 example files - Simplified GitHub Actions workflow - Default: checks only changed files - Manual trigger: option to scan all files --- .github/actions/translation-tracker/index.js | 11 ++++++++- .github/workflows/translation-sync.yml | 24 ++------------------ 2 files changed, 12 insertions(+), 23 deletions(-) diff --git a/.github/actions/translation-tracker/index.js b/.github/actions/translation-tracker/index.js index ffddfb984b..c48b4bab08 100644 --- a/.github/actions/translation-tracker/index.js +++ b/.github/actions/translation-tracker/index.js @@ -525,10 +525,13 @@ async function checkTranslationStatus(changedExampleFiles, githubTracker = null, async function main(testFiles = null, options = {}) { const hasToken = !!process.env.GITHUB_TOKEN; + const scanAll = process.env.SCAN_ALL === 'true' || options.scanAll; const isProduction = hasToken && !testFiles; // Production if has token and no test files if (testFiles) { console.log(`๐Ÿงช Test mode: Checking ${testFiles.length} predefined files`); + } else if (scanAll) { + console.log(`๐Ÿ” Scan ALL mode: ${hasToken ? 'Creating real issues' : 'File-based tracking only'}`); } else { console.log(`๐ŸŽฏ Production mode: ${hasToken ? 'Creating real issues' : 'File-based tracking only'}`); } @@ -546,7 +549,13 @@ async function main(testFiles = null, options = {}) { } // Get files to check - const filesToCheck = getChangedFiles(testFiles); + let filesToCheck; + if (scanAll && !testFiles) { + console.log('๐Ÿ“Š Scanning all English example files...'); + filesToCheck = getAllEnglishExampleFiles(); + } else { + filesToCheck = getChangedFiles(testFiles); + } if (filesToCheck.length === 0) { console.log('โœ… No English example files changed'); diff --git a/.github/workflows/translation-sync.yml b/.github/workflows/translation-sync.yml index 8f4c04dba3..c309e60012 100644 --- a/.github/workflows/translation-sync.yml +++ b/.github/workflows/translation-sync.yml @@ -16,11 +16,6 @@ on: required: false default: false type: boolean - create_issues: - description: 'Create GitHub issues for outdated translations' - required: false - default: true - type: boolean jobs: track-translation-changes: @@ -46,20 +41,5 @@ jobs: - name: Run translation tracker env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - SCAN_ALL: ${{ inputs.scan_all || 'false' }} - CREATE_ISSUES: ${{ inputs.create_issues }} - run: | - if [ "$SCAN_ALL" = "true" ]; then - echo "๐Ÿ” Running manual scan of ALL files..." - node -e " - const { main } = require('./.github/actions/translation-tracker/index.js'); - main(null, { - scanAll: true, - createIssues: process.env.CREATE_ISSUES === 'true', - githubToken: process.env.GITHUB_TOKEN - }); - " - else - echo "๐Ÿ“ Running normal change detection..." - node .github/actions/translation-tracker/index.js - fi \ No newline at end of file + SCAN_ALL: ${{ inputs.scan_all || false }} + run: node .github/actions/translation-tracker/index.js \ No newline at end of file From 9f63c9df79c103ee6f1328bdd62ae8bdfd1c1dbd Mon Sep 17 00:00:00 2001 From: Divyansh013 Date: Mon, 7 Jul 2025 00:17:31 +0530 Subject: [PATCH 17/18] final fix --- .github/actions/translation-tracker/index.js | 85 +++++++++++++++----- 1 file changed, 64 insertions(+), 21 deletions(-) diff --git a/.github/actions/translation-tracker/index.js b/.github/actions/translation-tracker/index.js index c48b4bab08..a3af4aafae 100644 --- a/.github/actions/translation-tracker/index.js +++ b/.github/actions/translation-tracker/index.js @@ -1,8 +1,6 @@ const { execSync } = require('child_process'); const fs = require('fs'); const path = require('path'); -const core = require('@actions/core'); -const github = require('@actions/github'); const { Octokit } = require('@octokit/rest'); @@ -525,15 +523,18 @@ async function checkTranslationStatus(changedExampleFiles, githubTracker = null, async function main(testFiles = null, options = {}) { const hasToken = !!process.env.GITHUB_TOKEN; - const scanAll = process.env.SCAN_ALL === 'true' || options.scanAll; - const isProduction = hasToken && !testFiles; // Production if has token and no test files + const isGitHubAction = !!process.env.GITHUB_ACTIONS; // Detect if running in GitHub Actions + + // Default behavior: scan all files UNLESS running in GitHub Actions or test mode + const scanAll = isGitHubAction ? false : (process.env.SCAN_ALL !== 'false'); + const isProduction = hasToken && !testFiles; if (testFiles) { console.log(`๐Ÿงช Test mode: Checking ${testFiles.length} predefined files`); - } else if (scanAll) { - console.log(`๐Ÿ” Scan ALL mode: ${hasToken ? 'Creating real issues' : 'File-based tracking only'}`); + } else if (isGitHubAction) { + console.log(`๐Ÿš€ GitHub Actions: Checking changed files only`); } else { - console.log(`๐ŸŽฏ Production mode: ${hasToken ? 'Creating real issues' : 'File-based tracking only'}`); + console.log(`๐Ÿ” Manual run: Scanning all ${scanAll ? 'files' : 'changed files'}`); } // Initialize GitHub tracker if token is available @@ -550,7 +551,7 @@ async function main(testFiles = null, options = {}) { // Get files to check let filesToCheck; - if (scanAll && !testFiles) { + if (scanAll && !testFiles && !isGitHubAction) { console.log('๐Ÿ“Š Scanning all English example files...'); filesToCheck = getAllEnglishExampleFiles(); } else { @@ -558,12 +559,16 @@ async function main(testFiles = null, options = {}) { } if (filesToCheck.length === 0) { - console.log('โœ… No English example files changed'); + if (isGitHubAction) { + console.log('โœ… No English example files changed in this push'); + } else { + console.log('โœ… No files to check'); + } return; } console.log(`๐Ÿ“ Checking ${filesToCheck.length} English example file(s):`); - filesToCheck.forEach(file => console.log(` - ${file.split('/').pop()}`)); + filesToCheck.forEach(file => console.log(` - ${file}`)); const createIssues = isProduction && githubTracker !== null; const translationStatus = await checkTranslationStatus( @@ -572,18 +577,56 @@ async function main(testFiles = null, options = {}) { createIssues ); - // Simple summary - const { needsUpdate, missing, issuesCreated } = translationStatus; - if (needsUpdate.length > 0 || missing.length > 0) { - console.log(`\n๐Ÿ“Š Translation issues found: ${needsUpdate.length + missing.length}`); - if (issuesCreated.length > 0) { - console.log(`๐ŸŽซ GitHub issues created: ${issuesCreated.length}`); - issuesCreated.forEach(issue => console.log(` - Issue #${issue.issueNumber}: ${issue.englishFile.split('/').pop()}`)); - } else if (!hasToken) { - console.log(`๐Ÿ’ก Run with GITHUB_TOKEN to create issues`); + // Detailed results + const { needsUpdate, missing, upToDate, issuesCreated } = translationStatus; + + console.log('\n๐Ÿ“Š Translation Status Summary:'); + console.log(` ๐Ÿ”„ Outdated: ${needsUpdate.length}`); + console.log(` โŒ Missing: ${missing.length}`); + console.log(` โœ… Up-to-date: ${upToDate.length}`); + + if (needsUpdate.length > 0) { + console.log('\n๐Ÿ”„ Files needing translation updates:'); + needsUpdate.forEach(item => { + const langName = githubTracker ? githubTracker.getLanguageDisplayName(item.language) : item.language; + if (githubTracker && item.commitInfo) { + console.log(` - ${item.englishFile} โ†’ ${langName}`); + console.log(` English: ${item.commitInfo.english.date.toLocaleDateString()} by ${item.commitInfo.english.author}`); + console.log(` Translation: ${item.commitInfo.translation.date.toLocaleDateString()} by ${item.commitInfo.translation.author}`); + } else { + console.log(` - ${item.englishFile} โ†’ ${langName}`); + if (item.englishModTime && item.translationModTime) { + console.log(` English: ${item.englishModTime.toLocaleDateString()}`); + console.log(` Translation: ${item.translationModTime.toLocaleDateString()}`); + } + } + }); + } + + if (missing.length > 0) { + console.log('\nโŒ Missing translation files:'); + missing.forEach(item => { + const langName = githubTracker ? githubTracker.getLanguageDisplayName(item.language) : item.language; + console.log(` - ${item.englishFile} โ†’ ${langName}`); + console.log(` Expected: ${item.translationPath}`); + }); + } + + if (issuesCreated.length > 0) { + console.log(`\n๐ŸŽซ GitHub issues created: ${issuesCreated.length}`); + issuesCreated.forEach(issue => { + console.log(` - Issue #${issue.issueNumber}: ${issue.englishFile}`); + console.log(` Languages: ${issue.affectedLanguages.map(lang => githubTracker.getLanguageDisplayName(lang)).join(', ')}`); + console.log(` URL: ${issue.issueUrl}`); + }); + } else if (needsUpdate.length > 0 || missing.length > 0) { + if (!hasToken) { + console.log(`\n๐Ÿ’ก Run with GITHUB_TOKEN to create GitHub issues`); } - } else { - console.log('\nโœ… All translations are up to date'); + } + + if (needsUpdate.length === 0 && missing.length === 0) { + console.log('\nโœ… All translations are up to date!'); } } From 8de0a674b77729b310f959e97d948a5de7331275 Mon Sep 17 00:00:00 2001 From: Divyansh013 Date: Mon, 11 Aug 2025 21:35:20 +0530 Subject: [PATCH 18/18] temp commit --- .github/actions/translation-tracker/index.js | 145 +++++++++++++++++- .../OutdatedTranslationBanner/index.astro | 51 ++++++ src/layouts/ExampleLayout.astro | 4 + 3 files changed, 193 insertions(+), 7 deletions(-) create mode 100644 src/components/OutdatedTranslationBanner/index.astro diff --git a/.github/actions/translation-tracker/index.js b/.github/actions/translation-tracker/index.js index a3af4aafae..2fef3e9d41 100644 --- a/.github/actions/translation-tracker/index.js +++ b/.github/actions/translation-tracker/index.js @@ -4,6 +4,28 @@ const path = require('path'); const { Octokit } = require('@octokit/rest'); +function getTranslationPath(englishFilePath, language) { + // Ensure we have a valid English path + if (!englishFilePath.includes('/en/')) { + throw new Error(`Invalid English file path: ${englishFilePath}. Must contain '/en/'`); + } + + // Split path into parts and replace 'en' directory with target language + const pathParts = englishFilePath.split('/'); + const enIndex = pathParts.findIndex(part => part === 'en'); + + if (enIndex === -1) { + throw new Error(`Could not find 'en' directory in path: ${englishFilePath}`); + } + + // Create new path with language replacement + const translationParts = [...pathParts]; + translationParts[enIndex] = language; + + return translationParts.join('/'); +} + + const SUPPORTED_LANGUAGES = ['es', 'hi', 'ko', 'zh-Hans']; @@ -72,6 +94,8 @@ class GitHubCommitTracker { return null; } catch (error) { + console.log(`โš ๏ธ Primary commit lookup failed for ${filePath} on branch '${this.currentBranch}': ${error.message}`); + // Fallback to main branch if current branch fails if (this.currentBranch !== 'main') { try { @@ -93,14 +117,104 @@ class GitHubCommitTracker { }; } } catch (fallbackError) { - // Silent fallback + console.log(`โš ๏ธ Fallback to main branch also failed for ${filePath}: ${fallbackError.message}`); } } + console.log(`โŒ Could not get commit info for ${filePath} from any branch`); return null; } } + /** + * Get a recent diff for a file (head vs previous commit) and return a short patch snippet + */ + async getRecentDiffForFile(filePath) { + try { + // Get latest two commits for this file on current branch + const { data: commits } = await this.octokit.rest.repos.listCommits({ + owner: this.owner, + repo: this.repo, + sha: this.currentBranch, + path: filePath, + per_page: 2, + }); + + if (!commits || commits.length === 0) { + return null; + } + + const headSha = commits[0].sha; + let baseSha = commits.length > 1 ? commits[1].sha : null; + + // If only one commit is found for the file (new file), use the parent of head + if (!baseSha) { + try { + const { data: headCommit } = await this.octokit.rest.repos.getCommit({ + owner: this.owner, + repo: this.repo, + ref: headSha, + }); + baseSha = headCommit.parents && headCommit.parents.length > 0 ? headCommit.parents[0].sha : null; + } catch (parentErr) { + console.log(`โš ๏ธ Could not resolve base commit for diff of ${filePath}: ${parentErr.message}`); + } + } + + if (!baseSha) { + return { + baseSha: null, + headSha, + compareUrl: `https://github.com/${this.owner}/${this.repo}/commit/${headSha}`, + patchSnippet: null, + isTruncated: false, + }; + } + + // Compare the two commits and extract the file patch + const { data: compare } = await this.octokit.rest.repos.compareCommits({ + owner: this.owner, + repo: this.repo, + base: baseSha, + head: headSha, + }); + + const changedFile = (compare.files || []).find((f) => f.filename === filePath); + const patch = changedFile && changedFile.patch ? changedFile.patch : null; + + let patchSnippet = null; + let isTruncated = false; + if (patch) { + const lines = patch.split('\n'); + const maxLines = 80; + if (lines.length > maxLines) { + patchSnippet = lines.slice(0, maxLines).join('\n'); + isTruncated = true; + } else { + patchSnippet = patch; + } + } + + return { + baseSha, + headSha, + compareUrl: `https://github.com/${this.owner}/${this.repo}/compare/${baseSha}...${headSha}`, + patchSnippet, + isTruncated, + }; + } catch (error) { + console.log(`โš ๏ธ Failed to compute diff for ${filePath} on branch '${this.currentBranch}': ${error.message}`); + // Fallback to at least provide a compare link to branch head + return { + baseSha: null, + headSha: null, + compareUrl: `https://github.com/${this.owner}/${this.repo}/blob/${this.currentBranch}/${filePath}`, + patchSnippet: null, + isTruncated: false, + }; + } + } + /** * Create a GitHub issue for outdated translation */ @@ -130,7 +244,9 @@ class GitHubCommitTracker { async createMultiLanguageTranslationIssue(fileTranslations) { const englishFile = fileTranslations.englishFile; const issueTitle = `๐ŸŒ Update translations for ${path.basename(englishFile)}`; - const issueBody = this.formatMultiLanguageIssueBody(fileTranslations); + // Fetch recent English diff (best-effort) + const englishDiff = await this.getRecentDiffForFile(englishFile); + const issueBody = this.formatMultiLanguageIssueBody(fileTranslations, englishDiff); // Create labels: "needs translation" + specific language labels const labels = ['needs translation', 'help wanted']; @@ -165,7 +281,7 @@ class GitHubCommitTracker { * Format the issue body with helpful information */ formatIssueBody(englishFile, language, commitInfo) { - const translationPath = englishFile.replace('/en/', `/${language}/`); + const translationPath = getTranslationPath(englishFile, language); const englishCommit = commitInfo.english; const translationCommit = commitInfo.translation; @@ -202,7 +318,7 @@ class GitHubCommitTracker { /** * Format the issue body for multi-language updates */ - formatMultiLanguageIssueBody(fileTranslations) { + formatMultiLanguageIssueBody(fileTranslations, englishDiff) { const englishFile = fileTranslations.englishFile; const outdatedLanguages = fileTranslations.outdatedLanguages; const missingLanguages = fileTranslations.missingLanguages; @@ -238,6 +354,19 @@ class GitHubCommitTracker { }); } + // Include an English diff snippet if available + if (englishDiff) { + body += `### ๐Ÿงพ English Changes (Recent)\n\n`; + body += `- [๐Ÿ” View full diff](${englishDiff.compareUrl})\n`; + if (englishDiff.patchSnippet) { + body += `\n\n\u0060\u0060\u0060diff\n${englishDiff.patchSnippet}\n\u0060\u0060\u0060\n`; + if (englishDiff.isTruncated) { + body += `\n_(diff truncated โ€” open the full diff link above for all changes)_\n`; + } + } + body += `\n`; + } + body += `### ๐Ÿ”— Quick Links - [๐Ÿ“„ Current English file](https://github.com/${this.owner}/${this.repo}/blob/${this.currentBranch}/${englishFile}) @@ -291,9 +420,10 @@ function getChangedFiles(testFiles = null) { } try { + // Different git commands for different event types const gitCommand = process.env.GITHUB_EVENT_NAME === 'pull_request' - ? 'git diff --name-only HEAD~1 HEAD' - : 'git diff --name-only HEAD~1 HEAD'; + ? 'git diff --name-only origin/main...HEAD' // Compare with base branch for PRs + : 'git diff --name-only HEAD~1 HEAD'; // Compare with previous commit for pushes const changedFilesOutput = execSync(gitCommand, { encoding: 'utf8' }); const allChangedFiles = changedFilesOutput.trim().split('\n').filter(file => file.length > 0); @@ -357,6 +487,7 @@ function getFileModTime(filePath) { try { return fs.statSync(filePath).mtime; } catch (error) { + console.log(`โš ๏ธ Could not get file timestamp for ${filePath}: ${error.message}`); return null; } } @@ -385,7 +516,7 @@ async function checkTranslationStatus(changedExampleFiles, githubTracker = null, }; for (const language of SUPPORTED_LANGUAGES) { - const translationPath = englishFile.replace('/en/', `/${language}/`); + const translationPath = getTranslationPath(englishFile, language); const exists = fileExists(translationPath); if (!exists) { diff --git a/src/components/OutdatedTranslationBanner/index.astro b/src/components/OutdatedTranslationBanner/index.astro new file mode 100644 index 0000000000..6e1c552dbe --- /dev/null +++ b/src/components/OutdatedTranslationBanner/index.astro @@ -0,0 +1,51 @@ +--- +interface Props { + englishUrl?: string; + contributeUrl?: string; + title?: string; + message?: string; +} + +const { + englishUrl = '/en', + contributeUrl = 'https://github.com/processing/p5.js-website', + title = 'เคฏเคน เค…เคจเฅเคตเคพเคฆ เคชเฅเคฐเคพเคจเคพ เคนเฅ‹ เคธเค•เคคเคพ เคนเฅˆ', + message = 'เคฏเคน เคชเฅƒเคทเฅเค  เค…เค‚เค—เฅเคฐเฅ‡เคœเคผเฅ€ เคธเค‚เคธเฅเค•เคฐเคฃ เค•เฅ€ เคคเฅเคฒเคจเคพ เคฎเฅ‡เค‚ เค…เคฆเฅเคฏเคคเคจ เคจเคนเฅ€เค‚ เคนเฅˆเฅค เค•เฅƒเคชเคฏเคพ เค…เค‚เค—เฅเคฐเฅ‡เคœเคผเฅ€ เคชเฅƒเคทเฅเค  เคฆเฅ‡เค–เฅ‡เค‚ เคฏเคพ เค…เคจเฅเคตเคพเคฆ เคธเฅเคงเคพเคฐเคจเฅ‡ เคฎเฅ‡เค‚ เคฎเคฆเคฆ เค•เคฐเฅ‡เค‚เฅค', +} = Astro.props as Props; +--- + +

+ + + + +
+ + diff --git a/src/layouts/ExampleLayout.astro b/src/layouts/ExampleLayout.astro index cc31a5d2bf..48fe9ed151 100644 --- a/src/layouts/ExampleLayout.astro +++ b/src/layouts/ExampleLayout.astro @@ -10,6 +10,7 @@ import { import BaseLayout from "./BaseLayout.astro"; import EditableSketch from "@components/EditableSketch/index.astro"; import RelatedItems from "@components/RelatedItems/index.astro"; +import OutdatedTranslationBanner from "@components/OutdatedTranslationBanner/index.astro"; interface Props { example: CollectionEntry<"examples">; @@ -59,6 +60,9 @@ const { Content } = await example.render(); topic="examples" className="example" > + {currentLocale === 'hi' ? ( + + ) : null}