diff --git a/.github/workflows/auto-tag.yml b/.github/workflows/auto-tag.yml new file mode 100644 index 00000000..a11cb133 --- /dev/null +++ b/.github/workflows/auto-tag.yml @@ -0,0 +1,45 @@ +name: Auto Tag on Version Change + +on: + push: + branches: + - main + paths: + - 'package.json' + +jobs: + tag: + runs-on: ubuntu-latest + steps: + - name: Checkout code + uses: actions/checkout@v4 + with: + fetch-depth: 0 + + - name: Get version from package.json + id: version + run: | + VERSION=$(jq -r '.version' package.json) + echo "version=$VERSION" >> $GITHUB_OUTPUT + echo "tag=v$VERSION" >> $GITHUB_OUTPUT + + - name: Check if tag exists + id: check_tag + run: | + if git rev-parse "v${{ steps.version.outputs.version }}" >/dev/null 2>&1; then + echo "exists=true" >> $GITHUB_OUTPUT + else + echo "exists=false" >> $GITHUB_OUTPUT + fi + + - name: Create and push tag + if: steps.check_tag.outputs.exists == 'false' + run: | + git config user.name "github-actions[bot]" + git config user.email "github-actions[bot]@users.noreply.github.com" + git tag -a "${{ steps.version.outputs.tag }}" -m "Release ${{ steps.version.outputs.tag }}" + git push origin "${{ steps.version.outputs.tag }}" + + - name: Tag already exists + if: steps.check_tag.outputs.exists == 'true' + run: echo "Tag ${{ steps.version.outputs.tag }} already exists, skipping" diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml index 2397f9fc..a44f3707 100644 --- a/.github/workflows/publish.yml +++ b/.github/workflows/publish.yml @@ -183,3 +183,21 @@ jobs: echo "- Chrome: ${{ steps.chrome-check.outputs.needs_publish == 'true' && '✓ Published' || '⊘ Skipped (up to date)' }}" >> $GITHUB_STEP_SUMMARY echo "- Firefox: ${{ steps.firefox-check.outputs.needs_publish == 'true' && '✓ Published' || '⊘ Skipped (up to date)' }}" >> $GITHUB_STEP_SUMMARY echo "- Edge: ${{ steps.edge-check.outputs.needs_publish == 'true' && '✓ Published' || '⊘ Skipped (up to date)' }}" >> $GITHUB_STEP_SUMMARY + + # Create GitHub Release with built packages + - name: Get version + id: get_version + if: startsWith(github.ref, 'refs/tags/') + run: echo "version=${GITHUB_REF#refs/tags/v}" >> $GITHUB_OUTPUT + + - name: Create GitHub Release + if: startsWith(github.ref, 'refs/tags/') + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + run: | + gh release create "v${{ steps.get_version.outputs.version }}" \ + --title "v${{ steps.get_version.outputs.version }}" \ + --notes "See [CHANGELOG](https://github.com/${{ github.repository }}/blob/main/description.md#version-history) for details." \ + chrome-extension.zip#"Chrome Extension" \ + firefox-extension.zip#"Firefox Extension" \ + edge-extension.zip#"Edge Extension" diff --git a/.gitignore b/.gitignore index 111f4143..308bc318 100644 --- a/.gitignore +++ b/.gitignore @@ -4,3 +4,4 @@ node_modules/ dist/ *.zip coverage/ +.env diff --git a/CLAUDE.md b/CLAUDE.md new file mode 100644 index 00000000..a7ba0ad6 --- /dev/null +++ b/CLAUDE.md @@ -0,0 +1,31 @@ +# Claude Code Reminders + +## Pre-Commit Checklist + +**ALWAYS run before committing:** + +1. **Format files**: `pnpm run format` +2. **Run tests**: `pnpm run test` +3. **Check linting**: `pnpm run lint` + +## Git Workflow + +- Always format before committing +- Ensure all tests pass +- Verify no linting errors + +## Common Commands + +```bash +# Format all files +pnpm run format + +# Run tests +pnpm run test + +# Run linter +pnpm run lint + +# Build extensions +pnpm run build +``` diff --git a/_locales/en/messages.json b/_locales/en/messages.json new file mode 100644 index 00000000..559c2aea --- /dev/null +++ b/_locales/en/messages.json @@ -0,0 +1,18 @@ +{ + "extensionName": { + "message": "Markdown Printer", + "description": "Name of the extension" + }, + "extensionDescription": { + "message": "Save web pages as Markdown files to your Downloads folder. No setup required!", + "description": "Description of the extension" + }, + "savePageButton": { + "message": "Save Page as Markdown", + "description": "Button text in popup" + }, + "contextMenuTitle": { + "message": "Save as Markdown", + "description": "Context menu item title" + } +} diff --git a/_locales/fr/messages.json b/_locales/fr/messages.json new file mode 100644 index 00000000..34372648 --- /dev/null +++ b/_locales/fr/messages.json @@ -0,0 +1,18 @@ +{ + "extensionName": { + "message": "Markdown Printer", + "description": "Name of the extension" + }, + "extensionDescription": { + "message": "Enregistrez des pages web en tant que fichiers Markdown dans votre dossier Téléchargements. Aucune configuration requise !", + "description": "Description of the extension" + }, + "savePageButton": { + "message": "Enregistrer la page en Markdown", + "description": "Button text in popup" + }, + "contextMenuTitle": { + "message": "Enregistrer en Markdown", + "description": "Context menu item title" + } +} diff --git a/_locales/he/messages.json b/_locales/he/messages.json new file mode 100644 index 00000000..02634f20 --- /dev/null +++ b/_locales/he/messages.json @@ -0,0 +1,18 @@ +{ + "extensionName": { + "message": "Markdown Printer", + "description": "Name of the extension" + }, + "extensionDescription": { + "message": "שמור דפי אינטרנט כקבצי Markdown לתיקיית ההורדות. ללא הגדרות נדרשות!", + "description": "Description of the extension" + }, + "savePageButton": { + "message": "שמור דף כ-Markdown", + "description": "Button text in popup" + }, + "contextMenuTitle": { + "message": "שמור כ-Markdown", + "description": "Context menu item title" + } +} diff --git a/_locales/hi/messages.json b/_locales/hi/messages.json new file mode 100644 index 00000000..21a679fd --- /dev/null +++ b/_locales/hi/messages.json @@ -0,0 +1,18 @@ +{ + "extensionName": { + "message": "Markdown Printer", + "description": "Name of the extension" + }, + "extensionDescription": { + "message": "वेब पेजों को Markdown फ़ाइलों के रूप में अपने डाउनलोड फ़ोल्डर में सहेजें। किसी सेटअप की आवश्यकता नहीं!", + "description": "Description of the extension" + }, + "savePageButton": { + "message": "पेज को Markdown के रूप में सहेजें", + "description": "Button text in popup" + }, + "contextMenuTitle": { + "message": "Markdown के रूप में सहेजें", + "description": "Context menu item title" + } +} diff --git a/build.js b/build.js index 89bc5ee4..114c2c07 100755 --- a/build.js +++ b/build.js @@ -26,12 +26,16 @@ const commonFiles = [ 'icon128.png', ]; +// Common directories to copy +const commonDirs = ['_locales']; + // Manifest templates const chromeManifest = { manifest_version: 3, - name: 'Markdown Printer', + name: '__MSG_extensionName__', version: version, - description: 'Save web pages as Markdown files to your Downloads folder. No setup required!', + description: '__MSG_extensionDescription__', + default_locale: 'en', author: 'Lev Gelfenbuim', homepage_url: 'https://github.com/levz0r/markdown-printer', permissions: ['activeTab', 'contextMenus', 'downloads', 'scripting'], @@ -91,6 +95,31 @@ function copyFiles(sourceDir, destDir, files) { }); } +// Function to copy directories recursively +function copyDir(src, dest) { + if (!fs.existsSync(src)) { + console.warn(` ⚠ Warning: Directory ${src} not found`); + return; + } + + ensureDir(dest); + + const entries = fs.readdirSync(src, { withFileTypes: true }); + + for (const entry of entries) { + const srcPath = path.join(src, entry.name); + const destPath = path.join(dest, entry.name); + + if (entry.isDirectory()) { + copyDir(srcPath, destPath); + } else { + fs.copyFileSync(srcPath, destPath); + } + } + + console.log(` ✓ Copied directory ${path.basename(src)}/`); +} + // Function to create zip package function createZip(sourceDir, outputName) { try { @@ -114,6 +143,11 @@ if (buildChrome) { copyFiles(sourceDir, chromeDir, commonFiles); } + // Copy common directories (like _locales) + commonDirs.forEach(dir => { + copyDir(dir, path.join(chromeDir, dir)); + }); + // Write manifest fs.writeFileSync(path.join(chromeDir, 'manifest.json'), JSON.stringify(chromeManifest, null, 2)); console.log(' ✓ Updated manifest.json'); @@ -137,6 +171,11 @@ if (buildFirefox) { copyFiles(sourceDir, firefoxDir, commonFiles); } + // Copy common directories (like _locales) + commonDirs.forEach(dir => { + copyDir(dir, path.join(firefoxDir, dir)); + }); + // Write manifest fs.writeFileSync( path.join(firefoxDir, 'manifest.json'), diff --git a/description-fr.md b/description-fr.md new file mode 100644 index 00000000..f4ffa9fc --- /dev/null +++ b/description-fr.md @@ -0,0 +1,92 @@ +# Markdown Printer - Français + +Markdown Printer vous permet de sauvegarder n'importe quelle page web en tant que fichier Markdown en un seul clic. Parfait pour le développement IA, la documentation et la prise de notes. + +## ✨ FONCTIONNALITÉS + +• Aucune configuration requise - fonctionne immédiatement après l'installation +• Préserve le formatage (titres, liens, blocs de code, listes, tableaux) +• Enregistrez n'importe où avec la boîte de dialogue familière "Enregistrer sous" +• Conversion rapide côté client +• Menu contextuel clic droit + popup dans la barre d'outils +• Ajoute des métadonnées (URL source, date d'enregistrement) +• Sortie propre - supprime les scripts, styles et éléments indésirables + +## 🎯 COMMENT ÇA MARCHE + +1. Accédez à n'importe quelle page web +2. Faites un clic droit et sélectionnez "Enregistrer en Markdown" OU cliquez sur l'icône de l'extension +3. Choisissez où enregistrer dans la boîte de dialogue +4. C'est fait ! Votre page est maintenant un fichier .md + +## 📝 PARFAIT POUR + +• Développement IA/LLM - Alimentez Claude, ChatGPT ou Cursor avec une efficacité de tokens 2-3x meilleure que HTML +• Codage rapide - Sauvegardez rapidement les docs API, tutoriels et références pour votre assistant de codage IA +• Construction de bases de connaissances - Archivez les docs techniques dans un format optimisé pour le contexte IA +• Revue de code - Sauvegardez les PRs et discussions de code dans un format facilement analysable par les LLMs +• Développeurs sauvegardant la documentation +• Chercheurs archivant des articles +• Étudiants prenant des notes +• Créateurs de contenu sauvegardant des posts +• Tous les amateurs de Markdown ! + +## 🤖 POURQUOI MARKDOWN POUR L'IA ? + +Markdown est le format optimal pour alimenter les outils IA en contexte : +• 2-3x plus de contenu dans les limites de tokens par rapport à HTML +• Format propre et structuré que les LLMs comprennent parfaitement +• Préserve les blocs de code, titres et liens sans le bruit HTML +• Fonctionne parfaitement avec Cursor, Continue et autres outils de codage IA + +## 🔒 CONFIDENTIALITÉ + +Cette extension ne collecte, ne transmet ni ne partage aucune donnée utilisateur. Toute la conversion se fait localement dans votre navigateur. Aucune donnée ne quitte votre ordinateur. + +## 🆓 GRATUIT ET OPEN SOURCE + +Voir le code source sur GitHub : https://github.com/levz0r/markdown-printer + +## ⭐ SUPPORT + +Trouvé un bug ou une suggestion ? Signalez-le sur GitHub ou envoyez un email à hi@lev.engineer + +--- + +## Historique des versions + +### v1.1.0 (2025-10-10) + +• Ajout de la prise en charge de l'internationalisation pour l'hébreu, l'hindi et le français +• Ajout de la prise en charge des langues RTL (droite à gauche) pour l'hébreu et autres langues RTL +• Ajout de l'affichage du numéro de version dans la popup +• Amélioration du système de build pour inclure les fichiers de localisation + +### v1.0.3 (2025-10-06) + +• Correction de l'extraction de contenu pour fonctionner de manière fiable sur toutes les mises en page de site +• Amélioration de la capture des sites de documentation complexes (Microsoft Learn, etc.) +• Simplification de la sélection de contenu pour une meilleure compatibilité + +### v1.0.2 (2025-10-06) + +• Ajout de la capture de page complète avec défilement automatique +• Charge automatiquement le contenu lazy-loaded avant la conversion +• Fait défiler toute la page pour déclencher le contenu dynamique +• Performance de défilement 3x plus rapide + +### v1.0.1 (2025-10-05) + +• Ajout de la prise en charge de Firefox avec compatibilité multi-navigateurs +• Amélioration de la sortie Markdown en supprimant les éléments indésirables (scripts, styles, iframes, SVGs) +• Ajout d'un système de build automatisé avec gestion des versions +• Meilleure gestion des erreurs pour les pages protégées + +### v1.0.0 (2025-10-01) + +• Version initiale +• Conversion Markdown en un clic +• Prise en charge du menu contextuel clic droit +• Interface popup dans la barre d'outils +• Insertion de métadonnées (URL source, date de sauvegarde) +• Intégration de la boîte de dialogue Enregistrer sous diff --git a/description-he.md b/description-he.md new file mode 100644 index 00000000..182456bd --- /dev/null +++ b/description-he.md @@ -0,0 +1,92 @@ +# Markdown Printer - עברית + +Markdown Printer מאפשר לך לשמור כל דף אינטרנט כקובץ Markdown בלחיצה אחת. מושלם לפיתוח AI, תיעוד ורישום הערות. + +## ✨ תכונות + +• אין צורך בהתקנה - עובד מיד לאחר ההתקנה +• שומר על עיצוב (כותרות, קישורים, בלוקי קוד, רשימות, טבלאות) +• שמירה בכל מקום עם דיאלוג "שמור בשם" מוכר +• המרה מהירה בצד הלקוח +• תפריט קליק ימני + חלון קופץ בסרגל הכלים +• הוספת מטא-דאטה (כתובת URL מקורית, תאריך שמירה) +• פלט נקי - מסיר סקריפטים, סגנונות ואלמנטים לא רצויים + +## 🎯 איך זה עובד + +1. נווט לכל דף אינטרנט +2. לחץ קליק ימני ובחר "שמור כ-Markdown" או לחץ על אייקון ההרחבה +3. בחר היכן לשמור בדיאלוג +4. זהו! הדף שלך עכשיו קובץ .md + +## 📝 מושלם עבור + +• פיתוח AI/LLM - העבר תיעוד ל-Claude, ChatGPT או Cursor עם יעילות טוקנים טובה פי 2-3 מ-HTML +• קידוד חכם - שמור מהר מסמכי API, מדריכים והפניות לעוזר הקידוד AI שלך +• בניית מאגרי ידע - ארכב מסמכים טכניים בפורמט מותאם להקשר AI +• סקירת קוד - שמור PRs ודיונים על קוד בפורמט שמודלי שפה יכולים לנתח בקלות +• מפתחים שומרים תיעוד +• חוקרים מארכבים מאמרים +• סטודנטים רושמים הערות +• יוצרי תוכן מגבים פוסטים +• כל מי שאוהב Markdown! + +## 🤖 למה Markdown ל-AI? + +Markdown הוא הפורמט האופטימלי להזנת הקשר לכלי AI: +• פי 2-3 יותר תוכן בתוך מגבלות טוקנים לעומת HTML +• פורמט נקי ומובנה שמודלי שפה מבינים בצורה מושלמת +• שומר בלוקי קוד, כותרות וקישורים ללא רעש HTML +• עובד בצורה חלקה עם Cursor, Continue וכלי קידוד AI אחרים + +## 🔒 פרטיות + +הרחבה זו לא אוספת, משדרת או משתפת כל נתוני משתמש. כל ההמרה מתרחשת מקומית בדפדפן שלך. שום מידע לא עוזב את המחשב שלך. + +## 🆓 חינמי וקוד פתוח + +צפה בקוד המקור ב-GitHub: https://github.com/levz0r/markdown-printer + +## ⭐ תמיכה + +מצאת באג או יש לך הצעה? דווח על זה ב-GitHub או שלח מייל ל-hi@lev.engineer + +--- + +## היסטוריית גרסאות + +### v1.1.0 (2025-10-10) + +• נוספה תמיכה בינלאומית לעברית, הינדי וצרפתית +• נוספה תמיכה בשפות RTL (מימין לשמאל) לעברית ושפות RTL אחרות +• נוספה הצגת מספר גרסה בחלון קופץ +• שיפור מערכת הבנייה לכלול קבצי לוקליזציה + +### v1.0.3 (2025-10-06) + +• תוקנה חילוץ תוכן לעבוד בצורה אמינה על כל פריסות האתרים +• שיפור לכידת אתרי תיעוד מורכבים (Microsoft Learn, וכו') +• פישוט בחירת תוכן לתאימות טובה יותר + +### v1.0.2 (2025-10-06) + +• נוספה לכידת דף מלא עם גלילה אוטומטית +• טעינה אוטומטית של תוכן lazy-loaded לפני המרה +• גלילה דרך כל הדף להפעלת תוכן דינמי +• ביצועי גלילה מהירים פי 3 + +### v1.0.1 (2025-10-05) + +• נוספה תמיכה ב-Firefox עם תאימות בין-דפדפנים +• שיפור פלט Markdown על ידי הסרת אלמנטים לא רצויים (סקריפטים, סגנונות, iframes, SVGs) +• נוספה מערכת בנייה אוטומטית עם ניהול גרסאות +• טיפול משופר בשגיאות עבור דפים מוגנים + +### v1.0.0 (2025-10-01) + +• שחרור ראשוני +• המרת Markdown בלחיצה אחת +• תמיכה בתפריט הקשר קליק ימני +• ממשק חלון קופץ בסרגל כלים +• הכנסת מטא-דאטה (URL מקור, תאריך שמירה) +• שילוב דיאלוג שמור בשם diff --git a/description-hi.md b/description-hi.md new file mode 100644 index 00000000..86378ae4 --- /dev/null +++ b/description-hi.md @@ -0,0 +1,92 @@ +# Markdown Printer - हिन्दी + +Markdown Printer आपको किसी भी वेब पेज को सिर्फ एक क्लिक में Markdown फ़ाइल के रूप में सहेजने की सुविधा देता है। AI विकास, डॉक्यूमेंटेशन और नोट लेने के लिए बिल्कुल सही। + +## ✨ विशेषताएं + +• कोई सेटअप की आवश्यकता नहीं - इंस्टॉल करने के तुरंत बाद काम करता है +• फॉर्मेटिंग को सुरक्षित रखता है (हेडर, लिंक, कोड ब्लॉक, सूचियाँ, टेबल) +• परिचित "इस रूप में सहेजें" डायलॉग के साथ कहीं भी सहेजें +• तेज़ क्लाइंट-साइड रूपांतरण +• राइट-क्लिक कॉन्टेक्स्ट मेनू + टूलबार पॉपअप +• मेटाडेटा जोड़ता है (स्रोत URL, सहेजने की तारीख) +• साफ आउटपुट - स्क्रिप्ट, स्टाइल और अनचाही एलिमेंट हटाता है + +## 🎯 यह कैसे काम करता है + +1. किसी भी वेबपेज पर जाएं +2. राइट-क्लिक करें और "Markdown के रूप में सहेजें" चुनें या एक्सटेंशन आइकन पर क्लिक करें +3. डायलॉग में कहां सहेजना है चुनें +4. हो गया! आपका पेज अब एक .md फ़ाइल है + +## 📝 इसके लिए बिल्कुल सही + +• AI/LLM विकास - Claude, ChatGPT या Cursor को डॉक्यूमेंटेशन फीड करें HTML से 2-3x बेहतर टोकन दक्षता के साथ +• वाइब कोडिंग - अपने AI कोडिंग असिस्टेंट के लिए API डॉक्स, ट्यूटोरियल और संदर्भ जल्दी से सहेजें +• नॉलेज बेस बनाना - AI संदर्भ के लिए अनुकूलित प्रारूप में तकनीकी डॉक्स संग्रहीत करें +• कोड समीक्षा - PRs और कोड चर्चाओं को ऐसे प्रारूप में सहेजें जिसे LLMs आसानी से पार्स कर सकें +• डॉक्यूमेंटेशन सहेजने वाले डेवलपर +• लेख संग्रहीत करने वाले शोधकर्ता +• नोट्स लेने वाले छात्र +• पोस्ट बैकअप करने वाले कंटेंट क्रिएटर +• Markdown पसंद करने वाला कोई भी! + +## 🤖 AI के लिए Markdown क्यों? + +Markdown AI टूल्स को संदर्भ फीड करने के लिए सबसे अच्छा प्रारूप है: +• HTML की तुलना में टोकन सीमा के भीतर 2-3x अधिक सामग्री +• साफ, संरचित प्रारूप जिसे LLMs पूरी तरह से समझते हैं +• HTML शोर के बिना कोड ब्लॉक, हेडर और लिंक को सुरक्षित रखता है +• Cursor, Continue और अन्य AI कोडिंग टूल्स के साथ सहजता से काम करता है + +## 🔒 गोपनीयता + +यह एक्सटेंशन कोई उपयोगकर्ता डेटा एकत्र, संचारित या साझा नहीं करता है। सभी रूपांतरण आपके ब्राउज़र में स्थानीय रूप से होता है। कोई डेटा आपके कंप्यूटर से नहीं निकलता। + +## 🆓 मुफ्त और ओपन सोर्स + +GitHub पर स्रोत कोड देखें: https://github.com/levz0r/markdown-printer + +## ⭐ समर्थन + +कोई बग मिला या सुझाव है? इसे GitHub पर रिपोर्ट करें या hi@lev.engineer पर ईमेल करें + +--- + +## संस्करण इतिहास + +### v1.1.0 (2025-10-10) + +• हिब्रू, हिंदी और फ्रेंच के लिए अंतर्राष्ट्रीयकरण समर्थन जोड़ा गया +• हिब्रू और अन्य RTL भाषाओं के लिए RTL (दाएं-से-बाएं) भाषा समर्थन जोड़ा गया +• पॉपअप में संस्करण नंबर प्रदर्शन जोड़ा गया +• स्थानीयकरण फ़ाइलों को शामिल करने के लिए बिल्ड सिस्टम में सुधार किया गया + +### v1.0.3 (2025-10-06) + +• सभी साइट लेआउट पर विश्वसनीय रूप से काम करने के लिए कंटेंट एक्सट्रैक्शन को ठीक किया गया +• जटिल डॉक्यूमेंटेशन साइट्स (Microsoft Learn, आदि) के कैप्चर में सुधार +• बेहतर संगतता के लिए सामग्री चयन को सरल बनाया गया + +### v1.0.2 (2025-10-06) + +• स्वचालित स्क्रॉलिंग के साथ पूर्ण-पेज कैप्चर जोड़ा गया +• रूपांतरण से पहले lazy-loaded सामग्री को स्वचालित रूप से लोड करता है +• गतिशील सामग्री को ट्रिगर करने के लिए पूरे पेज को स्क्रॉल करता है +• 3x तेज़ स्क्रॉलिंग प्रदर्शन + +### v1.0.1 (2025-10-05) + +• क्रॉस-ब्राउज़र संगतता के साथ Firefox समर्थन जोड़ा गया +• अवांछित तत्वों (स्क्रिप्ट, स्टाइल, iframes, SVGs) को हटाकर Markdown आउटपुट में सुधार +• संस्करण प्रबंधन के साथ स्वचालित बिल्ड सिस्टम जोड़ा गया +• सुरक्षित पेजों के लिए बेहतर त्रुटि हैंडलिंग + +### v1.0.0 (2025-10-01) + +• प्रारंभिक रिलीज़ +• एक-क्लिक Markdown रूपांतरण +• राइट-क्लिक कॉन्टेक्स्ट मेनू समर्थन +• टूलबार पॉपअप इंटरफ़ेस +• मेटाडेटा सम्मिलन (स्रोत URL, सहेजने की तारीख) +• इस रूप में सहेजें डायलॉग एकीकरण diff --git a/description.md b/description.md index 738f2a0b..64b91094 100644 --- a/description.md +++ b/description.md @@ -47,6 +47,13 @@ Found a bug or have a suggestion? Report it on GitHub or email hi@lev.engineer ## Version History +### v1.1.0 (2025-10-10) + +• Added internationalization support for Hebrew, Hindi, and French +• Added RTL (right-to-left) language support for Hebrew and other RTL languages +• Added version number display in popup +• Improved build system to include localization files + ### v1.0.3 (2025-10-06) • Fixed content extraction to work reliably across all site layouts diff --git a/extension-chrome/_locales/en/messages.json b/extension-chrome/_locales/en/messages.json new file mode 100644 index 00000000..559c2aea --- /dev/null +++ b/extension-chrome/_locales/en/messages.json @@ -0,0 +1,18 @@ +{ + "extensionName": { + "message": "Markdown Printer", + "description": "Name of the extension" + }, + "extensionDescription": { + "message": "Save web pages as Markdown files to your Downloads folder. No setup required!", + "description": "Description of the extension" + }, + "savePageButton": { + "message": "Save Page as Markdown", + "description": "Button text in popup" + }, + "contextMenuTitle": { + "message": "Save as Markdown", + "description": "Context menu item title" + } +} diff --git a/extension-chrome/_locales/fr/messages.json b/extension-chrome/_locales/fr/messages.json new file mode 100644 index 00000000..34372648 --- /dev/null +++ b/extension-chrome/_locales/fr/messages.json @@ -0,0 +1,18 @@ +{ + "extensionName": { + "message": "Markdown Printer", + "description": "Name of the extension" + }, + "extensionDescription": { + "message": "Enregistrez des pages web en tant que fichiers Markdown dans votre dossier Téléchargements. Aucune configuration requise !", + "description": "Description of the extension" + }, + "savePageButton": { + "message": "Enregistrer la page en Markdown", + "description": "Button text in popup" + }, + "contextMenuTitle": { + "message": "Enregistrer en Markdown", + "description": "Context menu item title" + } +} diff --git a/extension-chrome/_locales/he/messages.json b/extension-chrome/_locales/he/messages.json new file mode 100644 index 00000000..02634f20 --- /dev/null +++ b/extension-chrome/_locales/he/messages.json @@ -0,0 +1,18 @@ +{ + "extensionName": { + "message": "Markdown Printer", + "description": "Name of the extension" + }, + "extensionDescription": { + "message": "שמור דפי אינטרנט כקבצי Markdown לתיקיית ההורדות. ללא הגדרות נדרשות!", + "description": "Description of the extension" + }, + "savePageButton": { + "message": "שמור דף כ-Markdown", + "description": "Button text in popup" + }, + "contextMenuTitle": { + "message": "שמור כ-Markdown", + "description": "Context menu item title" + } +} diff --git a/extension-chrome/_locales/hi/messages.json b/extension-chrome/_locales/hi/messages.json new file mode 100644 index 00000000..21a679fd --- /dev/null +++ b/extension-chrome/_locales/hi/messages.json @@ -0,0 +1,18 @@ +{ + "extensionName": { + "message": "Markdown Printer", + "description": "Name of the extension" + }, + "extensionDescription": { + "message": "वेब पेजों को Markdown फ़ाइलों के रूप में अपने डाउनलोड फ़ोल्डर में सहेजें। किसी सेटअप की आवश्यकता नहीं!", + "description": "Description of the extension" + }, + "savePageButton": { + "message": "पेज को Markdown के रूप में सहेजें", + "description": "Button text in popup" + }, + "contextMenuTitle": { + "message": "Markdown के रूप में सहेजें", + "description": "Context menu item title" + } +} diff --git a/extension-chrome/background.js b/extension-chrome/background.js index 27730246..97964738 100644 --- a/extension-chrome/background.js +++ b/extension-chrome/background.js @@ -5,7 +5,7 @@ const browserAPI = typeof browser !== 'undefined' ? browser : chrome; browserAPI.runtime.onInstalled.addListener(() => { browserAPI.contextMenus.create({ id: 'saveAsMarkdown', - title: 'Save as Markdown', + title: browserAPI.i18n.getMessage('contextMenuTitle'), contexts: ['page'], }); }); diff --git a/extension-chrome/manifest.json b/extension-chrome/manifest.json index d79e3390..89007a3c 100644 --- a/extension-chrome/manifest.json +++ b/extension-chrome/manifest.json @@ -1,8 +1,9 @@ { "manifest_version": 3, - "name": "Markdown Printer", - "version": "1.0.3", - "description": "Save web pages as Markdown files to your Downloads folder. No setup required!", + "name": "__MSG_extensionName__", + "version": "1.1.0", + "description": "__MSG_extensionDescription__", + "default_locale": "en", "author": "Lev Gelfenbuim", "homepage_url": "https://github.com/levz0r/markdown-printer", "permissions": ["activeTab", "contextMenus", "downloads", "scripting"], diff --git a/extension-chrome/popup.html b/extension-chrome/popup.html index f1ac09b8..7514bc89 100644 --- a/extension-chrome/popup.html +++ b/extension-chrome/popup.html @@ -20,6 +20,12 @@ width: 32px; height: 32px; } + .version { + font-size: 10px; + color: #999; + font-weight: normal; + margin-inline-start: auto; + } .copyright { margin-top: 15px; font-size: 10px; @@ -75,9 +81,10 @@

- Markdown Printer + +

- + diff --git a/extension-chrome/popup.js b/extension-chrome/popup.js index 61693606..512fcec9 100644 --- a/extension-chrome/popup.js +++ b/extension-chrome/popup.js @@ -1,20 +1,44 @@ +// Cross-browser compatibility +const browserAPI = typeof browser !== 'undefined' ? browser : chrome; + +// Set localized text on load +document.addEventListener('DOMContentLoaded', () => { + // Set text direction for RTL languages + const uiLanguage = browserAPI.i18n.getUILanguage(); + const rtlLanguages = ['he', 'ar', 'fa', 'ur']; + const isRTL = rtlLanguages.some(lang => uiLanguage.startsWith(lang)); + + if (isRTL) { + document.body.dir = 'rtl'; + } + + document.getElementById('extensionName').textContent = + browserAPI.i18n.getMessage('extensionName'); + document.getElementById('saveBtn').textContent = browserAPI.i18n.getMessage('savePageButton'); + + // Display version number + const manifest = browserAPI.runtime.getManifest(); + document.getElementById('version').textContent = `v${manifest.version}`; +}); + document.getElementById('saveBtn').addEventListener('click', async () => { const button = document.getElementById('saveBtn'); + const originalText = browserAPI.i18n.getMessage('savePageButton'); button.disabled = true; button.textContent = 'Saving...'; try { - await chrome.runtime.sendMessage({ action: 'saveAsMarkdown' }); + await browserAPI.runtime.sendMessage({ action: 'saveAsMarkdown' }); button.textContent = 'Saved!'; setTimeout(() => { - button.textContent = 'Save Page as Markdown'; + button.textContent = originalText; button.disabled = false; }, 1500); } catch (_error) { button.textContent = 'Error - Try again'; button.disabled = false; setTimeout(() => { - button.textContent = 'Save Page as Markdown'; + button.textContent = originalText; }, 2000); } }); diff --git a/extension-firefox/_locales/en/messages.json b/extension-firefox/_locales/en/messages.json new file mode 100644 index 00000000..559c2aea --- /dev/null +++ b/extension-firefox/_locales/en/messages.json @@ -0,0 +1,18 @@ +{ + "extensionName": { + "message": "Markdown Printer", + "description": "Name of the extension" + }, + "extensionDescription": { + "message": "Save web pages as Markdown files to your Downloads folder. No setup required!", + "description": "Description of the extension" + }, + "savePageButton": { + "message": "Save Page as Markdown", + "description": "Button text in popup" + }, + "contextMenuTitle": { + "message": "Save as Markdown", + "description": "Context menu item title" + } +} diff --git a/extension-firefox/_locales/fr/messages.json b/extension-firefox/_locales/fr/messages.json new file mode 100644 index 00000000..34372648 --- /dev/null +++ b/extension-firefox/_locales/fr/messages.json @@ -0,0 +1,18 @@ +{ + "extensionName": { + "message": "Markdown Printer", + "description": "Name of the extension" + }, + "extensionDescription": { + "message": "Enregistrez des pages web en tant que fichiers Markdown dans votre dossier Téléchargements. Aucune configuration requise !", + "description": "Description of the extension" + }, + "savePageButton": { + "message": "Enregistrer la page en Markdown", + "description": "Button text in popup" + }, + "contextMenuTitle": { + "message": "Enregistrer en Markdown", + "description": "Context menu item title" + } +} diff --git a/extension-firefox/_locales/he/messages.json b/extension-firefox/_locales/he/messages.json new file mode 100644 index 00000000..02634f20 --- /dev/null +++ b/extension-firefox/_locales/he/messages.json @@ -0,0 +1,18 @@ +{ + "extensionName": { + "message": "Markdown Printer", + "description": "Name of the extension" + }, + "extensionDescription": { + "message": "שמור דפי אינטרנט כקבצי Markdown לתיקיית ההורדות. ללא הגדרות נדרשות!", + "description": "Description of the extension" + }, + "savePageButton": { + "message": "שמור דף כ-Markdown", + "description": "Button text in popup" + }, + "contextMenuTitle": { + "message": "שמור כ-Markdown", + "description": "Context menu item title" + } +} diff --git a/extension-firefox/_locales/hi/messages.json b/extension-firefox/_locales/hi/messages.json new file mode 100644 index 00000000..21a679fd --- /dev/null +++ b/extension-firefox/_locales/hi/messages.json @@ -0,0 +1,18 @@ +{ + "extensionName": { + "message": "Markdown Printer", + "description": "Name of the extension" + }, + "extensionDescription": { + "message": "वेब पेजों को Markdown फ़ाइलों के रूप में अपने डाउनलोड फ़ोल्डर में सहेजें। किसी सेटअप की आवश्यकता नहीं!", + "description": "Description of the extension" + }, + "savePageButton": { + "message": "पेज को Markdown के रूप में सहेजें", + "description": "Button text in popup" + }, + "contextMenuTitle": { + "message": "Markdown के रूप में सहेजें", + "description": "Context menu item title" + } +} diff --git a/extension-firefox/background.js b/extension-firefox/background.js index 27730246..97964738 100644 --- a/extension-firefox/background.js +++ b/extension-firefox/background.js @@ -5,7 +5,7 @@ const browserAPI = typeof browser !== 'undefined' ? browser : chrome; browserAPI.runtime.onInstalled.addListener(() => { browserAPI.contextMenus.create({ id: 'saveAsMarkdown', - title: 'Save as Markdown', + title: browserAPI.i18n.getMessage('contextMenuTitle'), contexts: ['page'], }); }); diff --git a/extension-firefox/manifest.json b/extension-firefox/manifest.json index 866a44c5..70f095d7 100644 --- a/extension-firefox/manifest.json +++ b/extension-firefox/manifest.json @@ -1,8 +1,9 @@ { "manifest_version": 3, - "name": "Markdown Printer", - "version": "1.0.3", - "description": "Save web pages as Markdown files to your Downloads folder. No setup required!", + "name": "__MSG_extensionName__", + "version": "1.1.0", + "description": "__MSG_extensionDescription__", + "default_locale": "en", "author": "Lev Gelfenbuim", "homepage_url": "https://github.com/levz0r/markdown-printer", "permissions": ["activeTab", "contextMenus", "downloads", "scripting"], diff --git a/extension-firefox/popup.html b/extension-firefox/popup.html index f1ac09b8..7514bc89 100644 --- a/extension-firefox/popup.html +++ b/extension-firefox/popup.html @@ -20,6 +20,12 @@ width: 32px; height: 32px; } + .version { + font-size: 10px; + color: #999; + font-weight: normal; + margin-inline-start: auto; + } .copyright { margin-top: 15px; font-size: 10px; @@ -75,9 +81,10 @@

- Markdown Printer + +

- + diff --git a/extension-firefox/popup.js b/extension-firefox/popup.js index 61693606..512fcec9 100644 --- a/extension-firefox/popup.js +++ b/extension-firefox/popup.js @@ -1,20 +1,44 @@ +// Cross-browser compatibility +const browserAPI = typeof browser !== 'undefined' ? browser : chrome; + +// Set localized text on load +document.addEventListener('DOMContentLoaded', () => { + // Set text direction for RTL languages + const uiLanguage = browserAPI.i18n.getUILanguage(); + const rtlLanguages = ['he', 'ar', 'fa', 'ur']; + const isRTL = rtlLanguages.some(lang => uiLanguage.startsWith(lang)); + + if (isRTL) { + document.body.dir = 'rtl'; + } + + document.getElementById('extensionName').textContent = + browserAPI.i18n.getMessage('extensionName'); + document.getElementById('saveBtn').textContent = browserAPI.i18n.getMessage('savePageButton'); + + // Display version number + const manifest = browserAPI.runtime.getManifest(); + document.getElementById('version').textContent = `v${manifest.version}`; +}); + document.getElementById('saveBtn').addEventListener('click', async () => { const button = document.getElementById('saveBtn'); + const originalText = browserAPI.i18n.getMessage('savePageButton'); button.disabled = true; button.textContent = 'Saving...'; try { - await chrome.runtime.sendMessage({ action: 'saveAsMarkdown' }); + await browserAPI.runtime.sendMessage({ action: 'saveAsMarkdown' }); button.textContent = 'Saved!'; setTimeout(() => { - button.textContent = 'Save Page as Markdown'; + button.textContent = originalText; button.disabled = false; }, 1500); } catch (_error) { button.textContent = 'Error - Try again'; button.disabled = false; setTimeout(() => { - button.textContent = 'Save Page as Markdown'; + button.textContent = originalText; }, 2000); } }); diff --git a/package.json b/package.json index 17932818..2b939fa0 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "markdown-printer", - "version": "1.0.3", + "version": "1.1.0", "description": "Save web pages as Markdown files with preserved formatting", "scripts": { "build": "node build.js", diff --git a/test/manifest.test.js b/test/manifest.test.js index e205ab3b..4c1e050f 100644 --- a/test/manifest.test.js +++ b/test/manifest.test.js @@ -34,11 +34,17 @@ describe('Manifest Validation', () => { describe('Common Fields', () => { test('both manifests have same name', () => { expect(chromeManifest.name).toBe(firefoxManifest.name); - expect(chromeManifest.name).toBe('Markdown Printer'); + expect(chromeManifest.name).toBe('__MSG_extensionName__'); }); test('both manifests have same description', () => { expect(chromeManifest.description).toBe(firefoxManifest.description); + expect(chromeManifest.description).toBe('__MSG_extensionDescription__'); + }); + + test('both manifests have default_locale set to en', () => { + expect(chromeManifest.default_locale).toBe('en'); + expect(firefoxManifest.default_locale).toBe('en'); }); test('both manifests use Manifest V3', () => { @@ -169,4 +175,53 @@ describe('Manifest Validation', () => { }); }); }); + + describe('Internationalization', () => { + test('_locales directory exists', () => { + const localesDir = path.join(__dirname, '../_locales'); + expect(fs.existsSync(localesDir)).toBe(true); + }); + + test('has required locale directories', () => { + const localesDir = path.join(__dirname, '../_locales'); + const requiredLocales = ['en', 'he', 'hi', 'fr']; + + requiredLocales.forEach(locale => { + const localeDir = path.join(localesDir, locale); + expect(fs.existsSync(localeDir)).toBe(true); + }); + }); + + test('each locale has messages.json', () => { + const localesDir = path.join(__dirname, '../_locales'); + const requiredLocales = ['en', 'he', 'hi', 'fr']; + + requiredLocales.forEach(locale => { + const messagesPath = path.join(localesDir, locale, 'messages.json'); + expect(fs.existsSync(messagesPath)).toBe(true); + }); + }); + + test('messages.json files have required keys', () => { + const localesDir = path.join(__dirname, '../_locales'); + const requiredLocales = ['en', 'he', 'hi', 'fr']; + const requiredKeys = [ + 'extensionName', + 'extensionDescription', + 'savePageButton', + 'contextMenuTitle', + ]; + + requiredLocales.forEach(locale => { + const messagesPath = path.join(localesDir, locale, 'messages.json'); + const messages = JSON.parse(fs.readFileSync(messagesPath, 'utf8')); + + requiredKeys.forEach(key => { + expect(messages).toHaveProperty(key); + expect(messages[key]).toHaveProperty('message'); + expect(messages[key].message).toBeTruthy(); + }); + }); + }); + }); });