Files
filezzy-staging/backend/scripts/add-arabic-to-tools-json.ts
2026-02-04 14:16:04 +01:00

313 lines
12 KiB
TypeScript

/**
* Add Arabic (ar) to nameLocalized, descriptionLocalized, metaTitleLocalized, metaDescriptionLocalized
* in prisma/tools.json. Does not change slug, id, category, or any other non-localized fields.
*
* Run from backend: npx ts-node scripts/add-arabic-to-tools-json.ts
* Or: node scripts/add-arabic-to-tools-json.js (if compiled)
*
* Usage: npx ts-node scripts/add-arabic-to-tools-json.ts [path/to/tools.json]
* Default path: prisma/tools.json (relative to backend dir)
*/
import * as fs from 'fs';
import * as path from 'path';
const TOOLS_JSON = process.argv[2] || path.join(__dirname, '../prisma/tools.json');
/** English -> Arabic replacements for tool names and descriptions (order matters: longer first) */
const EN_AR: Record<string, string> = {
'Filezzy': 'Filezzy',
' | Filezzy': ' | Filezzy',
' - Filezzy': ' - Filezzy',
'Batch Add Page Numbers': 'إضافة أرقام الصفحات للمجموعات',
'Batch Add Password': 'إضافة كلمة مرور للمجموعات',
'Batch Add Stamp': 'إضافة ختم للمجموعات',
'Batch Add Watermark': 'إضافة علامة مائية للمجموعات',
'Batch Compress': 'ضغط مجموعة',
'Batch Convert to PDF': 'تحويل مجموعة إلى PDF',
'Batch Merge': 'دمج مجموعة',
'Batch Add Page Numbers to PDF': 'إضافة أرقام الصفحات لملفات PDF المجمعة',
'Add page numbers to multiple PDFs': 'إضافة أرقام الصفحات لعدة ملفات PDF',
'Password-protect multiple PDFs': 'حماية عدة ملفات PDF بكلمة مرور',
'Add the same stamp image to multiple PDFs': 'إضافة نفس صورة الختم لعدة ملفات PDF',
'Add the same watermark to multiple PDFs': 'إضافة نفس العلامة المائية لعدة ملفات PDF',
'Compress multiple PDFs': 'ضغط عدة ملفات PDF',
'Convert multiple files to PDF': 'تحويل عدة ملفات إلى PDF',
'Merge multiple PDFs': 'دمج عدة ملفات PDF',
'Batch': 'مجموعة',
'Add': 'إضافة',
'Compress': 'ضغط',
'Merge': 'دمج',
'Split': 'تقسيم',
'Convert': 'تحويل',
'PDF': 'PDF',
'Image': 'صورة',
'Word': 'Word',
'Excel': 'Excel',
'Watermark': 'علامة مائية',
'Stamp': 'ختم',
'Password': 'كلمة مرور',
'Page Numbers': 'أرقام الصفحات',
'Remove': 'إزالة',
'Extract': 'استخراج',
'Protect': 'حماية',
'Unlock': 'فتح',
'Rotate': 'تدوير',
'Crop': 'قص',
'Resize': 'تغيير الحجم',
'Format': 'تنسيق',
'Multiple': 'متعدد',
'files': 'ملفات',
'file': 'ملف',
'Free': 'مجاني',
'tool': 'أداة',
'tools': 'أدوات',
' at once': ' دفعة واحدة',
' at once.': ' دفعة واحدة.',
' once.': ' مرة واحدة.',
'One setting for all files.': 'إعداد واحد لجميع الملفات.',
'Same password for all.': 'نفس كلمة المرور للجميع.',
'Same format for all.': 'نفس التنسيق للجميع.',
'| Filezzy': ' | Filezzy',
' - Multiple Files': ' - ملفات متعددة',
'Multiple Files': 'ملفات متعددة',
' to PDF': ' إلى PDF',
' to Word': ' إلى Word',
' to Excel': ' إلى Excel',
' to Image': ' إلى صورة',
' to Images': ' إلى صور',
' to HTML': ' إلى HTML',
' to EPUB': ' إلى EPUB',
' from PDF': ' من PDF',
' from Image': ' من صورة',
' from Word': ' من Word',
' from Excel': ' من Excel',
' from HTML': ' من HTML',
' from Markdown': ' من Markdown',
' from JSON': ' من JSON',
' from CSV': ' من CSV',
' from XML': ' من XML',
' from Text': ' من نص',
' from URL': ' من رابط',
' from URL.': ' من رابط.',
' from Images': ' من صور',
' from PDFs': ' من ملفات PDF',
' from files': ' من ملفات',
' from file': ' من ملف',
' to JPG': ' إلى JPG',
' to PNG': ' إلى PNG',
' to WebP': ' إلى WebP',
' to GIF': ' إلى GIF',
' to SVG': ' إلى SVG',
' to TIFF': ' إلى TIFF',
' to BMP': ' إلى BMP',
' to HEIC': ' إلى HEIC',
' to AVIF': ' إلى AVIF',
' to PDF/A': ' إلى PDF/A',
' to PDF/X': ' إلى PDF/X',
' to DOCX': ' إلى DOCX',
' to DOC': ' إلى DOC',
' to ODT': ' إلى ODT',
' to RTF': ' إلى RTF',
' to TXT': ' إلى TXT',
' to Markdown': ' إلى Markdown',
' to CSV': ' إلى CSV',
' to JSON': ' إلى JSON',
' to XML': ' إلى XML',
' to PowerPoint': ' إلى PowerPoint',
' to PPTX': ' إلى PPTX',
' to Presentation': ' إلى عرض تقديمي',
' to Ebook': ' إلى كتاب إلكتروني',
' to EPUB or AZW3': ' إلى EPUB أو AZW3',
' to Archive': ' إلى أرشيف',
' to ZIP': ' إلى ZIP',
' to Searchable': ' إلى قابل للبحث',
' to Searchable PDF': ' إلى PDF قابل للبحث',
'Searchable PDF': 'PDF قابل للبحث',
'OCR': 'OCR',
'Compress PDF': 'ضغط PDF',
'Merge PDF': 'دمج PDF',
'Split PDF': 'تقسيم PDF',
'Add Watermark': 'إضافة علامة مائية',
'Add Stamp': 'إضافة ختم',
'Add Password': 'إضافة كلمة مرور',
'Remove Password': 'إزالة كلمة مرور',
'Unlock PDF': 'فتح PDF',
'Rotate PDF': 'تدوير PDF',
'Crop PDF': 'قص PDF',
'Compress Image': 'ضغط صورة',
'Resize Image': 'تغيير حجم الصورة',
'Convert Image': 'تحويل صورة',
'Remove Background': 'إزالة الخلفية',
'Crop Image': 'قص الصورة',
'Rotate Image': 'تدوير الصورة',
'Grayscale': 'تدرج رمادي',
'QR Code': 'رمز QR',
'Barcode': 'الباركود',
'Hash': 'تجزئة',
'Encode': 'ترميز',
'Decode': 'فك الترميز',
'Base64': 'Base64',
'JSON': 'JSON',
'XML': 'XML',
'CSV': 'CSV',
'Markdown': 'Markdown',
'HTML': 'HTML',
'Regex': 'التعبير النمطي',
'Password Generator': 'مولد كلمات المرور',
'Lorem Ipsum': 'نص تجريبي',
'Blank removal': 'إزالة الفراغات',
'Pipeline': 'سير العمل',
'Workflow': 'سير العمل',
'Scan to Searchable': 'المسح إلى PDF قابل للبحث',
'Invoice': 'فاتورة',
'Archive': 'أرشفة',
'E-sign': 'التوقيع الإلكتروني',
'Sign': 'توقيع',
'Digital Sign': 'توقيع رقمي',
'Verify': 'التحقق',
'Validate': 'التحقق من الصحة',
'Form': 'نموذج',
'Fill': 'ملء',
'Flatten': 'تسوية',
'Optimize': 'تحسين',
'Repair': 'إصلاح',
'Sanitize': 'تنظيف',
'Redact': 'إخفاء',
'Auto Redact': 'إخفاء تلقائي',
'Compare': 'مقارنة',
'Organize': 'تنظيم',
'Reorder': 'إعادة ترتيب',
'Extract Pages': 'استخراج الصفحات',
'Extract Text': 'استخراج النص',
'Extract Images': 'استخراج الصور',
'Get Info': 'الحصول على المعلومات',
'Info': 'معلومات',
'Metadata': 'البيانات الوصفية',
'Attachments': 'المرفقات',
'List Attachments': 'قائمة المرفقات',
'Embed': 'تضمين',
'Overlay': 'تراكب',
'Blank': 'فارغ',
'Blanks': 'فراغات',
'Remove Blanks': 'إزالة الصفحات الفارغة',
'Sections': 'أقسام',
'Split by Sections': 'تقسيم حسب الأقسام',
'Chapters': 'فصول',
'Split by Chapters': 'تقسيم حسب الفصول',
'Size': 'حجم',
'Split by Size': 'تقسيم حسب الحجم',
'Single Page': 'صفحة واحدة',
'Page': 'صفحة',
'Pages': 'صفحات',
'Presentation': 'عرض تقديمي',
'PowerPoint': 'PowerPoint',
'Ebook': 'كتاب إلكتروني',
'EPUB': 'EPUB',
'AZW3': 'AZW3',
'Table': 'جدول',
'Tables': 'جداول',
'Text': 'نص',
'Image to PDF': 'صورة إلى PDF',
'Images to PDF': 'صور إلى PDF',
'HTML to PDF': 'HTML إلى PDF',
'Markdown to PDF': 'Markdown إلى PDF',
'Word to PDF': 'Word إلى PDF',
'Excel to PDF': 'Excel إلى PDF',
'PPT to PDF': 'PPT إلى PDF',
'PDF to Word': 'PDF إلى Word',
'PDF to Excel': 'PDF إلى Excel',
'PDF to Images': 'PDF إلى صور',
'PDF to HTML': 'PDF إلى HTML',
'PDF to Text': 'PDF إلى نص',
'PDF to CSV': 'PDF إلى CSV',
'PDF to EPUB': 'PDF إلى EPUB',
'PDF to PowerPoint': 'PDF إلى PowerPoint',
'PDF/A': 'PDF/A',
'PDF/X': 'PDF/X',
'Archival': 'أرشفة',
'Long-term preservation': 'الحفظ طويل الأمد',
'Free batch': 'مجموعة مجانية',
'Free tool': 'أداة مجانية',
'Free online': 'مجاني على الإنترنت',
' at once!': ' دفعة واحدة!',
'protection': 'حماية',
' numbering': ' الترقيم',
' encryption': ' التشفير',
' stamping': ' الختم',
' protection': ' الحماية',
' protection to ': ' الحماية لـ ',
' numbering tool': ' أداة ترقيم',
' encryption tool': ' أداة تشفير',
' stamping tool': ' أداة ختم',
' protection.': ' الحماية.',
' to ': ' إلى ',
'!': '!',
'.': '.',
};
// Placeholder must not contain any substring that our EN_AR might replace (e.g. "file")
const FILEZZY_PLACEHOLDER = '\u200B\u200B\u200B'; // Unicode zero-width spaces (invisible, won't match dict)
function translateToArabic(text: string): string {
if (!text || typeof text !== 'string') return text;
// Protect "Filezzy" from partial replacement (e.g. "file" -> "ملف" breaking "Filezzy")
let out = text.replace(/\bFilezzy\b/gi, FILEZZY_PLACEHOLDER);
// Sort by key length descending so longer phrases are replaced first
const entries = Object.entries(EN_AR).sort((a, b) => b[0].length - a[0].length);
for (const [en, ar] of entries) {
if (en === 'Filezzy' || en === ' | Filezzy' || en === ' - Filezzy') continue; // already protected
const re = new RegExp(escapeRegex(en), 'gi');
out = out.replace(re, ar);
}
out = out.split(FILEZZY_PLACEHOLDER).join('Filezzy');
return out.trim() || text;
}
function escapeRegex(s: string): string {
return s.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
}
type Localized = Record<string, string> | null | undefined;
function ensureAr(obj: Localized, sourceKey: 'en' | 'fr', fallback: string): Record<string, string> {
const o = obj && typeof obj === 'object' ? { ...obj } : {};
const source = o[sourceKey] ?? o.en ?? o.fr ?? fallback;
o.ar = translateToArabic(String(source));
return o as Record<string, string>;
}
function main() {
const dataPath = path.resolve(process.cwd(), TOOLS_JSON);
if (!fs.existsSync(dataPath)) {
console.error('File not found:', dataPath);
process.exit(1);
}
const raw = fs.readFileSync(dataPath, 'utf-8');
const tools: Record<string, unknown>[] = JSON.parse(raw);
if (!Array.isArray(tools)) {
console.error('tools.json must be an array');
process.exit(1);
}
let updated = 0;
for (const tool of tools) {
const name = (tool.name as string) || '';
const desc = (tool.description as string) || '';
const metaTitle = (tool.metaTitle as string) || name;
const metaDesc = (tool.metaDescription as string) || desc;
tool.nameLocalized = ensureAr(tool.nameLocalized as Localized, 'en', name);
tool.descriptionLocalized = ensureAr(tool.descriptionLocalized as Localized, 'en', desc);
tool.metaTitleLocalized = ensureAr(tool.metaTitleLocalized as Localized, 'en', metaTitle);
tool.metaDescriptionLocalized = ensureAr(tool.metaDescriptionLocalized as Localized, 'en', metaDesc);
updated++;
}
fs.writeFileSync(dataPath, JSON.stringify(tools, null, 2), 'utf-8');
console.log(`Added Arabic (ar) to ${updated} tools in ${dataPath}`);
}
main();