أحتاج إلى مساعدة، أنا مبتدئ نسبياً

قبل 3 أشهر لم يكن لدي أي معرفة بالبرمجة تطبيقات، وقد قضيت 8 أشهر في إسبانيا وجئت برؤية في رأسي، وبدعم جيميني إيه آي من أنتيجرافيتي، بدأت في تطبيق فكرتي في تطبيق ترجمة لغة للتواصل مع بعض الأصدقاء من ألمانيا، تدفقي يتكون من 4 عُقد (Webhook1 + HTTP REQUEST + JavaScript + Respond to Webhook)، عند النقر على تنفيذ سير العمل ينفذ في أقل من 3 ثوان لكن تطبيقي الذي لا يزال غير مكتمل وهو تطبيق ويب في الوقت الحالي الذي يتم تفعيله من ملف index مُستضاف محليًا على سطح مكتب جهاز الكمبيوتر الخاص بي، لا يتمكن من إجراء الترجمة إلى اللغة المستهدفة وينتهي به الحال إلى إرسال الرسالة الأصلية دائمًا؛ لا أعرف ماذا أفعل أكثر من ذلك

إعجاب واحد (1)

تشغيل سير العمل في أقل من 3 ثوانٍ يعني أن الأتمتة تعمل، لكن هذا لا يثبت أن مخرجات الترجمة يتم تمريرها بشكل صحيح. المشكلة ربما لا تكون “الترجمة” نفسها؛ بل من المحتمل أن تكون مشكلة في تعيين البيانات بين صفحة الويب الخاصة بك، والـ webhook، وطلب HTTP، وعقدة JavaScript، والاستجابة النهائية.

سأقوم بتصحيح الأخطاء عقدة تلو الأخرى:

  1. تحقق من مخرجات عقدة Webhook. تأكد من أنها تستقبل النص الأصلي واللغة المستهدفة.

  2. تحقق من مدخلات عقدة HTTP Request. تأكد من أنها ترسل النص واللغة المستهدفة الصحيحين إلى واجهة برمجة التطبيقات للترجمة.

  3. تحقق من مخرجات عقدة HTTP Request. تأكد من أن واجهة برمجة التطبيقات تُرجع فعلاً نصاً مترجماً.

  4. تحقق من عقدة JavaScript. قد تكون تستخرج حقل JSON خاطئاً أو تعود إلى الرسالة الأصلية.

  5. تحقق من عقدة Respond to Webhook. تأكد من أنها ترجع translatedText، وليس النص الأصلي.

  6. تحقق من JavaScript في الواجهة الأمامية. تأكد من أنها تعرض حقل الترجمة من استجابة webhook.

بما أن تطبيقك يُرجع دائماً النص الأصلي، أشك في وجود منطق احتياطي مثل translatedText || originalText، أو أن عقدة الاستجابة/الواجهة الأمامية تعرض متغير النص الأصلي. أزل الخيار الاحتياطي أثناء تصحيح الأخطاء حتى يفشل سير العمل بوضوح عندما لا يتم العثور على ترجمة.

يجب أن تكون الترجمة نفسها مباشرة وسهلة. لقد بنينا بالفعل خط أنابيب بأسلوب النسخ، والترجمة نصية عادة ما تكون مجرد خطوة معالجة إضافية. الجزء الأصعب هو التأكد من أن المتغير الصحيح يتحرك عبر السلسلة. إذا شاركت مخرجات Webhook، ومخرجات HTTP Request، وكود عقدة JavaScript، وكود fetch الخاص بالواجهة الأمامية، فيمكننا على الأرجح تحديد نقطة التعطل بالضبط.

إعجابَين (2)

من المثير للإعجاب حقاً أن تنتقل من عدم معرفة بالبرمجة على الإطلاق إلى pipeline webhook عملي، لذا لا تستسلم، أنت أقرب مما تعتقد.

الرد أعلاه يغطي جميع خطوات تصحيح الأخطاء جيداً، لكن هناك شيء يستحق الإضافة: السبب الأكثر شيوعاً لـ “إرجاع الرسالة الأصلية” في هذا الإعداد هو أن عقدة JavaScript تأخذ الحقل الخاطئ من استجابة API.

يجب أن تحاول إضافة console.log أو استخدام معاينة المخرجات المدمجة في n8n لترى بالضبط ما يعيده عقدة HTTP Request. حاول البحث بشكل خاص عن هيكل JSON. عادة ما يكون النص المترجم متداخلاً في مستوى أعمق من المتوقع، مثل response.data.translations[0].translatedText اعتماداً على API الذي تستخدمه.

إذا شاركت أي translation API تقوم بالوصول إليه والصقت كود JavaScript الذي تم استخدامه، يمكن لشخص ما على الأرجح أن يحدده في غضون دقيقتين.

إعجابَين (2)

"شكراً جزيلاً @pratham_gupta و @AnthonyAtXRay على تخصيصكما الوقت للرد! نقاط تصحيح الأخطاء الخاصة بكما ممتازة جداً.

لكي أزودكما بسياق إضافي: في البداية، عندما كنت أجري الاختبارات الأولية، بدا أن سير العمل بأكمله يستجيب بشكل جيد. غير أن المشاكل الحقيقية في المتصفح بدأت تظهر بشكل أقوى مؤخراً. بعد تحليل شامل، نحن نشك في أن المنطق الداخلي لـ n8n سليم، لكن الفشل يحدث في الاتصال النهائي: أنا أقوم بتشغيل تطبيق الويب محلياً بفتح ملف index.html مباشرة من جهازي الشخصي (file://). كل شيء يشير إلى أن Google Chrome أو سياسات الشبكة المحلية تقوم بحجب الاستجابة القادمة من عنوان IP الخاص بـ n8n بسبب CORS.

علاوة على ذلك، للإضافة إلى المشكلة، انتهت للتو رصيدي المدفوع مسبقاً من واجهة برمجة تطبيقات Google، مما أدى إلى إيقاف سير العمل.

للتحقق من ما إذا كان تعيين المتغيرات دقيقاً كما تقترحان، أشارك معكما أسفله هيكل العقدة الخاصة بي في JavaScript والمسار العام (بدون بيانات اعتماد). هل تعتقدان أن نظريتي بأن صيغة file:// و CORS هما المسؤولان عن الحجب في المتصفح منطقية، أم أنكما ترييان خطأ ما في منطق المتغيرات؟"

إعجابَين (2)

{
“nodes”: [
{
“parameters”: {
“respondWith”: “json”,
“responseBody”: “={{ $json }}”,
“options”: {
“responseCode”: 200,
“responseHeaders”: {
“entries”: [
{
“name”: “Content-Type”,
“value”: “application/json”
},
{
“name”: “Access-Control-Allow-Origin”,
“value”: “"
},
{
“name”: “Access-Control-Allow-Headers”,
“value”: "

}
]
}
}
},
“type”: “n8n-nodes-base.respondToWebhook”,
“typeVersion”: 1.5,
“position”: [
752,
112
],
“id”: “7eb0364c-e218-47a7-ba7e-f9d8fe8bb8f8”,
“name”: “الرد على Webhook”
},
{
“parameters”: {
“httpMethod”: “POST”,
“path”: “98bd0d2c-fcf4-49b7-b501-72c81c5d9d44”,
“responseMode”: “responseNode”,
“options”: {
“allowedOrigins”: “*”
}
},
“type”: “n8n-nodes-base.webhook”,
“typeVersion”: 2.1,
“position”: [
80,
112
],
“id”: “0af6f18b-0e1a-4d5c-bca6-7573f4a31cb9”,
“name”: “Webhook1”,
“webhookId”: “c1603781-563f-4655-9b6d-1170e847c6f0”
},
{
“parameters”: {
“method”: “POST”,
“url”: “https://generativelanguage.googleapis.com/v1/models/gemini-2.5-flash:generateContent?key=TU_API_KEY_AQUÍ”,
“sendBody”: true,
“specifyBody”: “json”,
“jsonBody”: “={{ { “contents”: [{ “parts”: [{ “text”: $json.body.text + " - Translate this to " + $json.body.target_lang }] }] } }}”,
“options”: {}
},
“id”: “06da87ea-d639-41fa-a859-50ae26c35224”,
“name”: “طلب HTTP”,
“type”: “n8n-nodes-base.httpRequest”,
“typeVersion”: 4.4,
“position”: [
304,
112
]
},
{
“parameters”: {
“jsCode”: “const geminiResponse = $input.first().json;\nconst translatedText = geminiResponse.candidates[0].content.parts[0].text.trim();\n\nreturn [\n {\n json: {\n status: "success",\n original_text: $(‘Webhook1’).item.json.body.text,\n translated_text: translatedText,\n audio_base64: ""\n }\n }\n];”
},
“id”: “576fa8a4-896f-4d33-ad1a-8cfe5d204ec8”,
“name”: “الكود بلغة جافاسكريبت”,
“type”: “n8n-nodes-base.code”,
“typeVersion”: 2,
“position”: [
528,
112
]
}
],
“connections”: {
“Webhook1”: {
“main”: [
[
{
“node”: “طلب HTTP”,
“type”: “main”,
“index”: 0
}
]
]
},
“طلب HTTP”: {
“main”: [
[
{
“node”: “الكود بلغة جافاسكريبت”,
“type”: “main”,
“index”: 0
}
]
]
},
“الكود بلغة جافاسكريبت”: {
“main”: [
[
{
“node”: “الرد على Webhook”,
“type”: “main”,
“index”: 0
}
]
]
}
},
“pinData”: {},
“meta”: {
“templateCredsSetupCompleted”: true,
“instanceId”: “ANONYMOUS_INSTANCE_ID”
}
}

إعجاب واحد (1)

فريدي، لقطات الشاشة ستساعد، لكن بناءً على ما وصفته، سأضيق هذا إلى سؤال محدد واحد:

في أي نقطة يختفي النص المترجم؟

قد يعمل سير عمل n8n بشكل صحيح، لكن تطبيق الويب قد يظل يقرأ الحقل الخاطئ.

سأختبره بهذا الترتيب:

  1. عقدة Webhook
    تأكد من أن البيانات الواردة تحتوي على شيء مثل:
{
  "text": "hello",
  "targetLanguage": "German"
}

  1. عقدة HTTP Request
    افتح مخرجات تنفيذ العقدة وتحقق مما إذا كان واجهة برمجة تطبيقات الترجمة تُرجع النص المترجم.
    على سبيل المثال، شيء مثل:
{
  "translatedText": "Hallo"
}

أو في بعض الأحيان قد يكون متداخلاً بشكل أعمق، مثل:

{
  "data": {
    "translation": "Hallo"
  }
}

  1. عقدة JavaScript
    هذه نقطة فشل شائعة. قد يكون الكود يقرأ الحقل الخاطئ.

للتصحيح، لا تُرجع الرسالة الأصلية كخيار احتياطي. أرجع خطأ واضح بدلاً من ذلك:

const output = $json.translatedText;

if (!output) {
  throw new Error("No translated text found. Check HTTP Request output field name.");
}

return [
  {
    json: {
      translatedText: output
    }
  }
];

إذا كان النص المترجم متداخلاً، فأنت بحاجة إلى تعديل مسار الحقل بناءً على مخرجات HTTP Request الحقيقية.

  1. عقدة Respond to Webhook
    تأكد من أنها تُرجع القيمة المترجمة وليس المدخل الأصلي.

مثال الاستجابة:

{
  "translatedText": "={{$json.translatedText}}"
}

  1. كود fetch في الواجهة الأمامية
    قد تفعل صفحة الويب المحلية شيئاً مثل هذا:
resultBox.innerText = data.text;

عندما يجب أن يكون:

resultBox.innerText = data.translatedText;

نظراً لأن تطبيقك دائماً يُرجع الرسالة الأصلية، فإن تخميني الأقوى هو أحد هذه:

  • عقدة JavaScript تعود إلى النص الأصلي

  • عقدة Respond to Webhook مربوطة بالحقل الأصلي

  • الواجهة الأمامية تعرض الحقل الأصلي بدلاً من الحقل المترجم

  • عقدة HTTP Request ترجع الترجمة في مسار JSON متداخل، لكن عقدة JS تقرأ المسار الخاطئ

أفضل خطوة تصحيح: أرجع مخرجات HTTP Request الكاملة مباشرة من Respond to Webhook مؤقتاً. ثم اختبر من تطبيق الويب وتحقق من الـ JSON الذي يستقبله متصفحك فعلياً. بمجرد رؤيتك للهيكل الحقيقي للـ JSON، يجب أن يكون ربط الحقل المترجم أمراً سهلاً.

إعجاب واحد (1)

نظريتك حول CORS صحيحة تماماً، وملف JSON الخاص بسير العمل يكشف المشكلة المحددة.

عقدة Webhook لديك تحتوي على “allowedOrigins” معيّنة على “asterisk” وهذا ما يجب أن تكون عليه. لكن عقدة Respond to Webhook لديك تحتوي على “Access-Control-Allow-Origin” معيّنة على سلسلة نصية فارغة. سيتعين تغيير هذا إلى “asterisk” أيضاً، وإلا فإن Chrome سيحظر الاستجابة حتى عندما تمر الطلب.

المشكلة التالية هي بروتوكول file:// نفسه. ينتهي الحال بـ Chrome إلى حظر طلبات fetch من صفحات file:// إلى عناوين URL خارجية بغض النظر عن رؤوس CORS. إنه إصلاح بسيط - بدلاً من فتح index.html مباشرة، تقدمه من خادم محلي.

إذا كان لديك Python مثبتاً: قم بتشغيل python -m http.server 8080 في المجلد الذي يحتوي على index.html، ثم افتح http://localhost:8080

بخصوص رصيد Google API، بمجرد أن تستنزف رصيدك، منطق JavaScript node نفسه يبدو صحيحاً. وتعيين المتغير إلى candidates[0].content.parts[0].text هو المسار الصحيح لاستجابات Gemini.