n8n结合polylang和deepseek翻译流程全记录

首先要去创建获取的路由(get)和写入的路由(post)

这个目前针对文章和产品文章分类翻译有效,页面没配置过,页面需要涉及到一个block的翻译的问题

class Polylang_REST_API_Extension
{

	private $namespace = 'polylang-api/v1';

	public function __construct()
	{
		add_action('rest_api_init', array($this, 'register_routes'));
	}

	/**
	 * Register all REST API routes
	 */
	public function register_routes()
	{
		// Get all translatable post types
		register_rest_route($this->namespace, '/post-types', array(
			'methods' => 'GET',
			'callback' => array($this, 'get_post_types'),
			'permission_callback' => array($this, 'check_permission'),
		));

		// Get untranslated posts
		register_rest_route($this->namespace, '/untranslated-posts', array(
			'methods' => 'GET',
			'callback' => array($this, 'get_untranslated_posts'),
			'permission_callback' => array($this, 'check_permission'),
			'args' => array(
				'post_type' => array(
					'default' => 'post',
					'sanitize_callback' => 'sanitize_text_field',
				),
				'source_lang' => array(
					'default' => 'en',
					'sanitize_callback' => 'sanitize_text_field',
				),
				'target_lang' => array(
					'required' => true,
					'sanitize_callback' => 'sanitize_text_field',
				),
			),
		));

		// Get post content for translation
		register_rest_route($this->namespace, '/post/(?P<id>\d+)', array(
			'methods' => 'GET',
			'callback' => array($this, 'get_post_content'),
			'permission_callback' => array($this, 'check_permission'),
			'args' => array(
				'id' => array(
					'validate_callback' => function ($param) {
						return is_numeric($param);
					}
				),
			),
		));

		// Create translated post
		register_rest_route($this->namespace, '/translate-post', array(
			'methods' => 'POST',
			'callback' => array($this, 'create_translated_post'),
			'permission_callback' => array($this, 'check_permission'),
		));

		// Get languages
		register_rest_route($this->namespace, '/languages', array(
			'methods' => 'GET',
			'callback' => array($this, 'get_languages'),
			'permission_callback' => array($this, 'check_permission'),
		));

		// Get translation status
		register_rest_route($this->namespace, '/translation-status/(?P<id>\d+)', array(
			'methods' => 'GET',
			'callback' => array($this, 'get_translation_status'),
			'permission_callback' => array($this, 'check_permission'),
		));
	}

	/**
	 * Permission callback
	 */
	public function check_permission()
	{
		return current_user_can('edit_posts');
	}

	/**
	 * Get all public post types that support translation
	 */
	public function get_post_types()
	{
		if (!function_exists('pll_languages_list')) {
			return new WP_Error('polylang_not_active', 'Polylang is not active', array('status' => 500));
		}

		$post_types = get_post_types(array(
			'public' => true,
		), 'objects');

		unset($post_types['attachment'], $post_types['video']);

		$translatable_types = array();

		foreach ($post_types as $post_type) {
			if (function_exists('pll_is_translated_post_type') && pll_is_translated_post_type($post_type->name)) {
				$translatable_types[] = array(
					'name' => $post_type->name,
					'label' => $post_type->label,
					'singular_name' => $post_type->labels->singular_name,
				);
			}
		}

		return rest_ensure_response(array(
			'success' => true,
			'post_types' => array_column($translatable_types, 'name'),
			'post_types_details' => $translatable_types,
		));
	}

	/**
	 * Get all untranslated posts for a specific language
	 */
	public function get_untranslated_posts($request)
	{
		if (!function_exists('pll_languages_list')) {
			return new WP_Error('polylang_not_active', 'Polylang is not active', array('status' => 500));
		}

		$post_type = $request->get_param('post_type');
		$source_lang = $request->get_param('source_lang');
		$target_lang = $request->get_param('target_lang');

		$args = array(
			'post_type' => $post_type,
			'posts_per_page' => -1,
			'lang' => $source_lang,
			'post_status' => 'publish',
		);

		$posts = get_posts($args);
		$untranslated = array();

		foreach ($posts as $post) {
			$translation_id = pll_get_post($post->ID, $target_lang);

			if (!$translation_id) {
				$untranslated[] = array(
					'id' => $post->ID,
					'title' => $post->post_title,
					'slug' => $post->post_name,
					'status' => $post->post_status,
					'date' => $post->post_date,
					'modified' => $post->post_modified,
					'post_type' => $post->post_type,
					'language' => $source_lang,
				);
			}
		}

		return rest_ensure_response(array(
			'success' => true,
			'count' => count($untranslated),
			'posts' => $untranslated,
		));
	}

	/**
	 * Get full post content including all meta fields
	 */
	public function get_post_content($request)
	{
		$post_id = $request->get_param('id');
		$post = get_post($post_id);

		if (!$post) {
			return new WP_Error('post_not_found', 'Post not found', array('status' => 404));
		}

		$post_meta = get_post_meta($post_id);

		$clean_meta = array();
		foreach ($post_meta as $key => $value) {
			if (substr($key, 0, 1) === '_')
				continue;

			$meta_value = is_array($value) ? $value[0] : $value;

			if (!is_string($meta_value) || $this->is_serialized($meta_value))
				continue;

			$clean_meta[$key] = $meta_value;
		}

		$categories = wp_get_post_categories($post_id, array('fields' => 'all'));
		$tags = wp_get_post_tags($post_id, array('fields' => 'all'));

		$custom_taxonomies = array();
		$taxonomies = get_object_taxonomies($post->post_type, 'objects');
		foreach ($taxonomies as $taxonomy) {
			if (!in_array($taxonomy->name, array('category', 'post_tag', 'language', 'post_translations'))) {
				$terms = wp_get_post_terms($post_id, $taxonomy->name);
				if (!is_wp_error($terms) && !empty($terms)) {
					$custom_taxonomies[$taxonomy->name] = $terms;
				}
			}
		}

		$thumbnail_id = get_post_thumbnail_id($post_id);
		$thumbnail_url = $thumbnail_id ? wp_get_attachment_url($thumbnail_id) : null;

		$acf_fields = array();
		if (function_exists('get_fields')) {
			$acf_fields = get_fields($post_id) ?: array();
		}

		$current_lang = pll_get_post_language($post_id);

		return rest_ensure_response(array(
			'success' => true,
			'post' => array(
				'id' => $post->ID,
				'title' => $post->post_title,
				'content' => $post->post_content,
				'excerpt' => $post->post_excerpt,
				'slug' => $post->post_name,
				'status' => $post->post_status,
				'type' => $post->post_type,
				'language' => $current_lang,
				'featured_image' => $thumbnail_url,
				'thumbnail_id' => $thumbnail_id,
				'categories' => $categories,
				'tags' => $tags,
				'custom_taxonomies' => $custom_taxonomies,
				'meta' => $clean_meta,
				'acf_fields' => $acf_fields,
			),
		));
	}

	/**
	 * Create or update translated post - FIXED VERSION
	 * Now properly preserves all translation relationships
	 */
	public function create_translated_post($request)
	{
		$params = $request->get_json_params();

		if (!isset($params['source_post_id']) || !isset($params['target_lang'])) {
			return new WP_Error('missing_params', 'Missing required parameters', array('status' => 400));
		}

		$source_post_id = intval($params['source_post_id']);
		$target_lang = sanitize_text_field($params['target_lang']);
		$translated_title = sanitize_text_field($params['title']);
		$translated_content = wp_kses_post($params['content']);
		$translated_excerpt = isset($params['excerpt']) ? sanitize_textarea_field($params['excerpt']) : '';

		$source_post = get_post($source_post_id);

		if (!$source_post) {
			return new WP_Error('post_not_found', 'Source post not found', array('status' => 404));
		}

		// Check if translation exists
		$existing_translation = pll_get_post($source_post_id, $target_lang);

		if ($existing_translation) {
			// Update existing translation
			$updated_post = array(
				'ID' => $existing_translation,
				'post_title' => $translated_title,
				'post_content' => $translated_content,
				'post_excerpt' => $translated_excerpt,
			);

			$result = wp_update_post($updated_post, true);

			if (is_wp_error($result)) {
				return $result;
			}

			$translated_post_id = $existing_translation;
			$action = 'updated';
		} else {
			// Create new translation
			$new_post = array(
				'post_title' => $translated_title,
				'post_content' => $translated_content,
				'post_excerpt' => $translated_excerpt,
				'post_status' => $source_post->post_status,
				'post_type' => $source_post->post_type,
				'post_author' => $source_post->post_author,
			);

			$translated_post_id = wp_insert_post($new_post, true);

			if (is_wp_error($translated_post_id)) {
				return $translated_post_id;
			}

			// Set language for new post
			pll_set_post_language($translated_post_id, $target_lang);

			// ⭐ CRITICAL FIX: Get ALL existing translations first
			$existing_translations = pll_get_post_translations($source_post_id);

			// Add the new translation to the existing group
			$existing_translations[$target_lang] = $translated_post_id;

			// Save the complete translation group
			pll_save_post_translations($existing_translations);

			$action = 'created';
		}

		// Copy or set featured image
		if (isset($params['thumbnail_id']) && $params['thumbnail_id']) {
			set_post_thumbnail($translated_post_id, intval($params['thumbnail_id']));
		} else {
			$thumbnail_id = get_post_thumbnail_id($source_post_id);
			if ($thumbnail_id) {
				set_post_thumbnail($translated_post_id, $thumbnail_id);
			}
		}

		// Handle categories
		if (isset($params['categories']) && is_array($params['categories'])) {
			$translated_category_ids = array();
			foreach ($params['categories'] as $category_data) {
				$cat_id = $this->translate_term($category_data, 'category', $target_lang);
				if ($cat_id) {
					$translated_category_ids[] = $cat_id;
				}
			}
			wp_set_post_categories($translated_post_id, $translated_category_ids);
		}

		// Handle tags
		if (isset($params['tags']) && is_array($params['tags'])) {
			$translated_tag_ids = array();
			foreach ($params['tags'] as $tag_data) {
				$tag_id = $this->translate_term($tag_data, 'post_tag', $target_lang);
				if ($tag_id) {
					$translated_tag_ids[] = $tag_id;
				}
			}
			wp_set_post_tags($translated_post_id, $translated_tag_ids);
		}

		// Handle ACF fields
		if (isset($params['acf_fields']) && function_exists('update_field')) {
			foreach ($params['acf_fields'] as $field_key => $field_value) {
				update_field($field_key, $field_value, $translated_post_id);
			}
		}

		// Handle custom meta fields
		if (isset($params['meta_fields']) && is_array($params['meta_fields'])) {
			foreach ($params['meta_fields'] as $meta_key => $meta_value) {
				if (substr($meta_key, 0, 1) === '_')
					continue;

				update_post_meta($translated_post_id, $meta_key, sanitize_text_field($meta_value));
			}
		}

		// Get final translation status for verification
		$final_translations = pll_get_post_translations($source_post_id);

		return rest_ensure_response(array(
			'success' => true,
			'action' => $action,
			'translated_post_id' => $translated_post_id,
			'source_post_id' => $source_post_id,
			'post_type' => $source_post->post_type,
			'target_language' => $target_lang,
			'url' => get_permalink($translated_post_id),
			'all_translations' => $final_translations, // Shows all connected translations
		));
	}

	/**
	 * Helper to translate taxonomy terms
	 */
	private function translate_term($term_data, $taxonomy, $target_lang)
	{
		$source_term_id = isset($term_data['term_id']) ? intval($term_data['term_id']) : 0;
		$translated_name = isset($term_data['translated_name']) ? sanitize_text_field($term_data['translated_name']) : '';

		if ($source_term_id && function_exists('pll_get_term')) {
			$existing_translation = pll_get_term($source_term_id, $target_lang);

			if ($existing_translation) {
				return $existing_translation;
			}
		}

		if ($translated_name) {
			$new_term = wp_insert_term($translated_name, $taxonomy);

			if (!is_wp_error($new_term)) {
				$new_term_id = $new_term['term_id'];

				if (function_exists('pll_set_term_language')) {
					pll_set_term_language($new_term_id, $target_lang);

					if ($source_term_id) {
						// ⭐ SAME FIX for terms: preserve existing translations
						$existing_term_translations = pll_get_term_translations($source_term_id);
						$existing_term_translations[$target_lang] = $new_term_id;
						pll_save_term_translations($existing_term_translations);
					}
				}

				return $new_term_id;
			}
		}

		return null;
	}

	/**
	 * Get all configured languages
	 */
	public function get_languages()
	{
		if (!function_exists('pll_languages_list')) {
			return new WP_Error('polylang_not_active', 'Polylang is not active', array('status' => 500));
		}

		$languages = pll_languages_list(array('fields' => 'all'));
		$lang_data = array();

		foreach ($languages as $lang) {
			$lang_data[] = array(
				'slug' => $lang->slug,
				'name' => $lang->name,
				'locale' => $lang->locale,
				'is_rtl' => $lang->is_rtl,
				'flag' => $lang->flag,
			);
		}

		return rest_ensure_response(array(
			'success' => true,
			'languages' => $lang_data,
		));
	}

	/**
	 * Get translation status for a specific post
	 */
	public function get_translation_status($request)
	{
		$post_id = $request->get_param('id');

		if (!function_exists('pll_get_post_translations')) {
			return new WP_Error('polylang_not_active', 'Polylang is not active', array('status' => 500));
		}

		$translations = pll_get_post_translations($post_id);
		$languages = pll_languages_list();

		$status = array();
		foreach ($languages as $lang) {
			$status[$lang] = isset($translations[$lang]) ? array(
				'exists' => true,
				'post_id' => $translations[$lang],
				'url' => get_permalink($translations[$lang]),
			) : array(
				'exists' => false,
				'post_id' => null,
				'url' => null,
			);
		}

		return rest_ensure_response(array(
			'success' => true,
			'post_id' => $post_id,
			'translations' => $status,
		));
	}

	/**
	 * Check if a string is serialized
	 */
	private function is_serialized($data)
	{
		if (!is_string($data)) {
			return false;
		}
		$data = trim($data);
		if ($data === 'N;') {
			return true;
		}
		if (strlen($data) < 4 || $data[1] !== ':') {
			return false;
		}
		return @unserialize($data) !== false;
	}
}

// Initialize the plugin
new Polylang_REST_API_Extension();

翻译文章时候需要在rest api创建文章时候同步更新一样的文章缩略图和字段方法

/**
 * Sync featured image and gallery meta when post is created/updated via REST API
 */
function sync_media_on_rest_insert($post, $request, $creating)
{
	// Check if Polylang is active
	if (!function_exists('pll_get_post_translations')) {
		return;
	}

	// Get the post ID
	$post_id = $post->ID;

	// Check if this post has translations
	$translations = pll_get_post_translations($post_id);

	if (empty($translations) || count($translations) <= 1) {
		return; // No translations to sync
	}

	// Sync Featured Image
	$thumbnail_id = get_post_thumbnail_id($post_id);
	if ($thumbnail_id) {
		foreach ($translations as $lang_code => $translated_post_id) {
			if ($translated_post_id == $post_id) {
				continue;
			}
			set_post_thumbnail($translated_post_id, $thumbnail_id);
		}
	}

	// Sync Gallery Meta Field (adjust meta key as needed)
	$gallery_meta_keys = array('product_gallery'); // Add your meta keys

	foreach ($gallery_meta_keys as $meta_key) {
		$gallery_value = get_post_meta($post_id, $meta_key, true);

		if (!empty($gallery_value)) {
			foreach ($translations as $lang_code => $translated_post_id) {
				if ($translated_post_id == $post_id) {
					continue;
				}
				update_post_meta($translated_post_id, $meta_key, $gallery_value);
			}
		}
	}
}

// Hook into REST API post insert/update for all post types
add_action('rest_after_insert_post', 'sync_media_on_rest_insert', 10, 3);
add_action('rest_after_insert_page', 'sync_media_on_rest_insert', 10, 3);
add_action('rest_after_insert_product', 'sync_media_on_rest_insert', 10, 3);

// For custom post types, add specific hooks
// add_action('rest_after_insert_YOUR_POST_TYPE', 'sync_media_on_rest_insert', 10, 3);

另外一种注册路由的方法

add_action('rest_api_init', function () {
    // Your existing endpoint
    register_rest_route('polylang-api/v1', '/translate-post', array(
        'methods' => 'POST',
        'callback' => 'create_polylang_translation',
        'permission_callback' => function() {
            return current_user_can('edit_posts');
        }
    ));
	
	register_rest_route('polylang-api/v1', '/check-translation/(?P<id>\d+)', array(
    'methods' => 'GET',
    'callback' => function($request) {
        $post_id = intval($request['id']);
        
        return array(
            'post_id' => $post_id,
            'language' => pll_get_post_language($post_id),
            'translations' => pll_get_post_translations($post_id),
            'all_languages' => pll_languages_list()
        );
    },
    'permission_callback' => '__return_true'
));
});

function create_polylang_translation($request) {
    $params = $request->get_json_params();
    
    $source_post_id = intval($params['source_post_id']);
    $target_lang = sanitize_text_field($params['target_lang']);
    $title = $params['title'];
    $content = $params['content'];
    $excerpt = $params['excerpt'] ?? '';
    
    // Validate source post exists
    $source_post = get_post($source_post_id);
    if (!$source_post) {
        return new WP_Error('invalid_post', 'Source post not found', array('status' => 404));
    }
    
    // Get source language
    $source_lang = pll_get_post_language($source_post_id);
    
    // Check if translation already exists
    $existing_translation_id = pll_get_post($source_post_id, $target_lang);
    
    $post_data = array(
        'post_title'    => $title,
        'post_content'  => $content,
        'post_excerpt'  => $excerpt,
        'post_status'   => $source_post->post_status, // Match source status
        'post_type'     => $source_post->post_type,
        'post_parent'   => 0, // Will link via Polylang
        'menu_order'    => $source_post->menu_order,
    );
    
    if ($existing_translation_id) {
        // Update existing translation
        $post_data['ID'] = $existing_translation_id;
        $translated_post_id = wp_update_post($post_data, true);
        
        if (is_wp_error($translated_post_id)) {
            return $translated_post_id;
        }
        
        $message = 'Translation updated';
    } else {
        // Create new translation
        $translated_post_id = wp_insert_post($post_data, true);
        
        if (is_wp_error($translated_post_id)) {
            return $translated_post_id;
        }
        
        // CRITICAL: Set the language for the new post
        pll_set_post_language($translated_post_id, $target_lang);
        
        // CRITICAL: Link the translation to the source post
        $translations = pll_get_post_translations($source_post_id);
        $translations[$target_lang] = $translated_post_id;
        pll_save_post_translations($translations);
        
        $message = 'Translation created and linked';
    }
    
    // Copy thumbnail
    if (!empty($params['thumbnail_id'])) {
        $thumbnail_id = intval($params['thumbnail_id']);
        set_post_thumbnail($translated_post_id, $thumbnail_id);
    }
    
    // Handle categories
    if (!empty($params['categories']) && is_array($params['categories'])) {
        $category_ids = array();
        foreach ($params['categories'] as $cat) {
            $term_id = intval($cat['term_id']);
            // Get translated category if exists
            $translated_cat = pll_get_term($term_id, $target_lang);
            if ($translated_cat) {
                $category_ids[] = $translated_cat;
            } else {
                // If no translation exists, create one or use original
                $category_ids[] = $term_id;
            }
        }
        wp_set_post_categories($translated_post_id, $category_ids);
    }
    
    // Handle tags
    if (!empty($params['tags']) && is_array($params['tags'])) {
        $tag_ids = array();
        foreach ($params['tags'] as $tag) {
            $term_id = intval($tag['term_id']);
            $translated_tag = pll_get_term($term_id, $target_lang);
            if ($translated_tag) {
                $tag_ids[] = $translated_tag;
            } else {
                $tag_ids[] = $term_id;
            }
        }
        wp_set_post_tags($translated_post_id, $tag_ids);
    }
    
    // Handle ACF fields
    if (!empty($params['acf_fields']) && is_array($params['acf_fields'])) {
        foreach ($params['acf_fields'] as $field_key => $field_value) {
            update_field($field_key, $field_value, $translated_post_id);
        }
    }
    
    // Handle meta fields
    if (!empty($params['meta_fields']) && is_array($params['meta_fields'])) {
        foreach ($params['meta_fields'] as $meta_key => $meta_value) {
            update_post_meta($translated_post_id, $meta_key, $meta_value);
        }
    }
    
    return array(
        'success' => true,
        'post_id' => $translated_post_id,
        'source_post_id' => $source_post_id,
        'target_lang' => $target_lang,
        'source_lang' => $source_lang,
        'message' => $message,
        'translations' => pll_get_post_translations($source_post_id) // Return all linked translations
    );
}

翻译的时候需要考虑,第一,要翻译全部文章,自定义文章类型,页面,第二,要翻译页面里的block, 第三,要翻译分类,自定义分类,并进行链接文章

{
  "name": "wp-auto-translate-all-post-types",
  "nodes": [
    {
      "parameters": {
        "rule": {
          "interval": [
            {
              "field": "weeks"
            }
          ]
        }
      },
      "name": "Schedule Trigger",
      "type": "n8n-nodes-base.scheduleTrigger",
      "typeVersion": 1,
      "position": [
        400,
        560
      ],
      "id": "59704616-352e-41de-a588-bf594674d6cb"
    },
    {
      "parameters": {
        "url": "=https://banwork.cn/wp-json/polylang-api/v1/post-types",
        "authentication": "genericCredentialType",
        "genericAuthType": "httpBasicAuth",
        "options": {}
      },
      "name": "Get All Post Types",
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4.1,
      "position": [
        592,
        560
      ],
      "id": "1f9c6d8a-eb18-4d43-bfff-d7b10704032c",
      "credentials": {
        "httpBasicAuth": {
          "id": "nRdVHRimMjQuSFHB",
          "name": "Unnamed credential"
        }
      }
    },
    {
      "parameters": {
        "jsCode": "// Get post types from previous node\nconst postTypesData = $input.first().json;\nconst postTypes = postTypesData.post_types || [];\n\n// Define language configuration\nconst config = {\n  post_types: postTypes,\n  target_languages: ['zh'],  // Add more languages as needed: ['zh', 'es', 'fr']\n  source_lang: 'en'\n};\n\nreturn {\n  json: config\n};"
      },
      "name": "Set Language Config",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        800,
        560
      ],
      "id": "c8cf8dab-f866-41d4-814b-0ffdd70b6f83"
    },
    {
      "parameters": {
        "fieldToSplitOut": "post_types",
        "options": {}
      },
      "name": "Split Post Types",
      "type": "n8n-nodes-base.splitOut",
      "typeVersion": 1,
      "position": [
        992,
        560
      ],
      "id": "5c047ae5-e3fc-4413-9f50-90ebf907e169"
    },
    {
      "parameters": {
        "fieldToSplitOut": "target_languages",
        "options": {}
      },
      "name": "Split Target Languages",
      "type": "n8n-nodes-base.splitOut",
      "typeVersion": 1,
      "position": [
        1200,
        560
      ],
      "id": "7529fb8e-b056-4b0f-98d2-831428deb1a4"
    },
    {
      "parameters": {
        "url": "=https://fuckthe.work/wp-json/polylang-api/v1/untranslated-posts",
        "authentication": "genericCredentialType",
        "genericAuthType": "httpBasicAuth",
        "sendQuery": true,
        "queryParameters": {
          "parameters": [
            {
              "name": "post_type",
              "value": "={{ $('Split Post Types').item.json.post_types }}"
            },
            {
              "name": "source_lang",
              "value": "={{ $('Set Language Config').item.json.source_lang }}"
            },
            {
              "name": "target_lang",
              "value": "={{ $json.target_languages }}"
            }
          ]
        },
        "options": {}
      },
      "name": "Get Untranslated Posts",
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4.1,
      "position": [
        1408,
        560
      ],
      "id": "46f1a4b8-b8de-4cca-bb40-95a563fd7b0a",
      "credentials": {
        "httpBasicAuth": {
          "id": "nRdVHRimMjQuSFHB",
          "name": "Unnamed credential"
        }
      }
    },
    {
      "parameters": {
        "conditions": {
          "options": {
            "caseSensitive": true,
            "leftValue": "",
            "typeValidation": "strict",
            "version": 1
          },
          "conditions": [
            {
              "id": "has-posts",
              "leftValue": "={{ $json.count }}",
              "rightValue": 0,
              "operator": {
                "type": "number",
                "operation": "gt"
              }
            }
          ],
          "combinator": "and"
        },
        "options": {}
      },
      "name": "Has Untranslated Posts?",
      "type": "n8n-nodes-base.if",
      "typeVersion": 2,
      "position": [
        1616,
        560
      ],
      "id": "fe7587ca-35dd-47ed-ace0-c554e90d6d24"
    },
    {
      "parameters": {
        "fieldToSplitOut": "posts",
        "options": {}
      },
      "name": "Split Posts",
      "type": "n8n-nodes-base.splitOut",
      "typeVersion": 1,
      "position": [
        1824,
        448
      ],
      "id": "8d2e78b5-ea1f-4e64-a8a5-dfb217797868"
    },
    {
      "parameters": {
        "url": "=https://fuckthe.work/wp-json/polylang-api/v1/post/{{ $json.id }}",
        "authentication": "genericCredentialType",
        "genericAuthType": "httpBasicAuth",
        "options": {}
      },
      "name": "Get Post Content",
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4.1,
      "position": [
        2032,
        448
      ],
      "id": "28e411ff-2159-4cb2-ba0d-ff523a0cd407",
      "credentials": {
        "httpBasicAuth": {
          "id": "nRdVHRimMjQuSFHB",
          "name": "Unnamed credential"
        }
      }
    },
    {
      "parameters": {
        "jsCode": "function stripHtml(html) {\n  if (!html) return '';\n  \n  let text = html\n    .replace(/<script\\b[^<]*(?:(?!<\\/script>)<[^<]*)*<\\/script>/gi, '')\n    .replace(/<style\\b[^<]*(?:(?!<\\/style>)<[^<]*)*<\\/style>/gi, '');\n  \n  text = text.replace(/<[^>]+>/g, ' ');\n  text = text.replace(/\\s+/g, ' ').trim();\n  \n  return text;\n}\n\n// Process translatable meta fields\nfunction extractTranslatableMeta(metaFields, postType) {\n  const translatable = {};\n  \n  if (!metaFields || typeof metaFields !== 'object') return translatable;\n  \n  // Extract text-based meta fields (skip internal WordPress fields)\n  for (const [key, value] of Object.entries(metaFields)) {\n    // Skip internal WP fields and serialized data\n    if (key.startsWith('_') || !value) continue;\n    \n    // Handle arrays (take first value)\n    const metaValue = Array.isArray(value) ? value[0] : value;\n    \n    // Only include string values\n    if (typeof metaValue === 'string' && metaValue.length > 0) {\n      translatable[key] = stripHtml(metaValue);\n    }\n  }\n  \n  return translatable;\n}\n\nconst items = $input.all();\n\nreturn items.map((item, index) => {\n  const postData = item.json.post || item.json;\n  \n  return {\n    json: {\n      id: item.json.id || postData.id,\n      post: {\n        id: postData.id,\n        post_type: postData.type,\n        title: stripHtml(postData.title),\n        content: stripHtml(postData.content),\n        excerpt: stripHtml(postData.excerpt || ''),\n        categories: postData.categories || [],\n        tags: postData.tags || [],\n        acf_fields: postData.acf_fields || {},\n        meta_fields: extractTranslatableMeta(postData.meta, postData.type),\n        thumbnail_id: postData.thumbnail_id || null\n      }\n    },\n    pairedItem: index\n  };\n});"
      },
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        2240,
        448
      ],
      "id": "61eeb731-e6d1-48b1-af60-1827f80f9e33",
      "name": "Process Post Data"
    },
    {
      "parameters": {
        "jsCode": "const items = $input.all();\nconst sourceLang = $('Set Language Config').item.json.source_lang;\nconst targetLang = $('Split Target Languages').item.json.target_languages;\n\nreturn items.map((item, index) => {\n  const postData = item.json.post;\n  \n  // Build content for translation including meta fields\n  let translationContent = `Title: ${postData.title}\\n\\nContent: ${postData.content}`;\n  \n  if (postData.excerpt) {\n    translationContent += `\\n\\nExcerpt: ${postData.excerpt}`;\n  }\n  \n  // Add meta fields to translation\n  const metaFieldsArray = [];\n  if (postData.meta_fields && Object.keys(postData.meta_fields).length > 0) {\n    translationContent += '\\n\\nMeta Fields:';\n    for (const [key, value] of Object.entries(postData.meta_fields)) {\n      translationContent += `\\n${key}: ${value}`;\n      metaFieldsArray.push({ key, value });\n    }\n  }\n  \n  // Build expected JSON structure\n  let expectedStructure = '\"title\": \"translated title\", \"content\": \"translated content\"';\n  if (postData.excerpt) {\n    expectedStructure += ', \"excerpt\": \"translated excerpt\"';\n  }\n  if (metaFieldsArray.length > 0) {\n    expectedStructure += ', \"meta_fields\": {\"field_key\": \"translated value\"}';\n  }\n  \n  const requestBody = {\n    model: \"deepseek-chat\",\n    messages: [\n      {\n        role: \"system\",\n        content: `You are a professional translator. Translate the following WordPress ${postData.post_type} content from ${sourceLang} to ${targetLang}. Return ONLY a valid JSON object with these exact keys: {${expectedStructure}}. For meta_fields, use the same keys but translate the values.`\n      },\n      {\n        role: \"user\",\n        content: translationContent\n      }\n    ],\n    temperature: 0.3,\n    response_format: { type: \"json_object\" }\n  };\n  \n  return {\n    json: {\n      requestBody: requestBody,\n      post: postData,\n      metaFieldsKeys: metaFieldsArray.map(m => m.key)\n    },\n    pairedItem: index\n  };\n});"
      },
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        2448,
        448
      ],
      "id": "8b218b25-ced3-465d-a9bd-1f7d00eb7247",
      "name": "Build DeepSeek Request"
    },
    {
      "parameters": {
        "method": "POST",
        "url": "https://api.deepseek.com/v1/chat/completions",
        "authentication": "predefinedCredentialType",
        "nodeCredentialType": "httpHeaderAuth",
        "sendHeaders": true,
        "headerParameters": {
          "parameters": [
            {
              "name": "Content-Type",
              "value": "application/json"
            }
          ]
        },
        "sendBody": true,
        "specifyBody": "json",
        "jsonBody": "={{ $json.requestBody }}",
        "options": {}
      },
      "name": "DeepSeek Translate",
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4.1,
      "position": [
        2656,
        448
      ],
      "id": "545d1efa-23f4-499b-a091-e04d79ae3037",
      "credentials": {
        "httpHeaderAuth": {
          "id": "5X9OcogB9LTTIp0J",
          "name": "Header Auth account"
        }
      }
    },
    {
      "parameters": {
        "jsCode": "const items = $input.all();\nconst postContentItems = $('Get Post Content').all();\nconst targetLang = $('Split Target Languages').item.json.target_languages;\n\nreturn items.map((item, index) => {\n  const response = item.json;\n  const translatedData = JSON.parse(response.choices[0].message.content);\n  \n  const originalPostItem = postContentItems[index].json;\n  const originalPost = originalPostItem.post || originalPostItem;\n  \n  const categories = (originalPost.categories || []).map(cat => ({\n    term_id: cat.term_id,\n    translated_name: cat.name\n  }));\n  \n  const tags = (originalPost.tags || []).map(tag => ({\n    term_id: tag.term_id,\n    translated_name: tag.name\n  }));\n  \n  let acfFields = {};\n  if (originalPost.acf_fields && typeof originalPost.acf_fields === 'object') {\n    acfFields = originalPost.acf_fields;\n  }\n  \n  // Extract translated meta fields\n  let translatedMeta = {};\n  if (translatedData.meta_fields && typeof translatedData.meta_fields === 'object') {\n    translatedMeta = translatedData.meta_fields;\n  }\n  \n  return {\n    json: {\n      source_post_id: originalPost.id,\n      post_type: originalPost.type,\n      target_lang: targetLang,\n      title: translatedData.title,\n      content: translatedData.content,\n      excerpt: translatedData.excerpt || '',\n      categories: categories,\n      tags: tags,\n      acf_fields: acfFields,\n      meta_fields: translatedMeta,\n      thumbnail_id: originalPost.thumbnail_id\n    },\n    pairedItem: index\n  };\n});"
      },
      "name": "Format Translation Data",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        2864,
        448
      ],
      "id": "192db91b-5c62-4400-a727-640bced0c904"
    },
    {
      "parameters": {
        "method": "POST",
        "url": "=https://fuckthe.work/wp-json/polylang-api/v1/translate-post",
        "authentication": "genericCredentialType",
        "genericAuthType": "httpBasicAuth",
        "sendBody": true,
        "specifyBody": "json",
        "jsonBody": "={\n  \"source_post_id\": {{ $json.source_post_id }},\n  \"target_lang\": \"{{ $json.target_lang }}\",\n  \"title\": {{ JSON.stringify($json.title) }},\n  \"content\": {{ JSON.stringify($json.content) }},\n  \"excerpt\": {{ JSON.stringify($json.excerpt) }},\n  \"categories\": {{ JSON.stringify($json.categories) }},\n  \"tags\": {{ JSON.stringify($json.tags) }},\n  \"acf_fields\": {{ JSON.stringify($json.acf_fields) }},\n  \"meta_fields\": {{ JSON.stringify($json.meta_fields) }},\n  \"thumbnail_id\": {{ $json.thumbnail_id }}\n}",
        "options": {}
      },
      "name": "Create Translation",
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4.1,
      "position": [
        3072,
        448
      ],
      "id": "88d88b3e-1255-4764-a1be-67c7ab3f4c02",
      "credentials": {
        "httpBasicAuth": {
          "id": "nRdVHRimMjQuSFHB",
          "name": "Unnamed credential"
        }
      }
    },
    {
      "parameters": {
        "amount": 2
      },
      "name": "Rate Limit Delay",
      "type": "n8n-nodes-base.wait",
      "typeVersion": 1.1,
      "position": [
        3280,
        448
      ],
      "id": "28000c82-342e-4cf5-b9c9-64756756bb40",
      "webhookId": "rate-limit-delay"
    },
    {
      "parameters": {
        "options": {}
      },
      "name": "Log Success",
      "type": "n8n-nodes-base.set",
      "typeVersion": 3.2,
      "position": [
        3472,
        448
      ],
      "id": "616ccc77-6d2f-4b44-af82-27b2778db74f"
    },
    {
      "parameters": {
        "options": {}
      },
      "name": "Workflow Complete",
      "type": "n8n-nodes-base.set",
      "typeVersion": 3.2,
      "position": [
        3680,
        560
      ],
      "id": "f46d3075-dea5-4036-8176-98fe6de463f4"
    }
  ],
  "pinData": {},
  "connections": {
    "Schedule Trigger": {
      "main": [
        [
          {
            "node": "Get All Post Types",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Get All Post Types": {
      "main": [
        [
          {
            "node": "Set Language Config",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Set Language Config": {
      "main": [
        [
          {
            "node": "Split Post Types",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Split Post Types": {
      "main": [
        [
          {
            "node": "Split Target Languages",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Split Target Languages": {
      "main": [
        [
          {
            "node": "Get Untranslated Posts",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Get Untranslated Posts": {
      "main": [
        [
          {
            "node": "Has Untranslated Posts?",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Has Untranslated Posts?": {
      "main": [
        [
          {
            "node": "Split Posts",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Workflow Complete",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Split Posts": {
      "main": [
        [
          {
            "node": "Get Post Content",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Get Post Content": {
      "main": [
        [
          {
            "node": "Process Post Data",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Process Post Data": {
      "main": [
        [
          {
            "node": "Build DeepSeek Request",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Build DeepSeek Request": {
      "main": [
        [
          {
            "node": "DeepSeek Translate",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "DeepSeek Translate": {
      "main": [
        [
          {
            "node": "Format Translation Data",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Format Translation Data": {
      "main": [
        [
          {
            "node": "Create Translation",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Create Translation": {
      "main": [
        [
          {
            "node": "Rate Limit Delay",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Rate Limit Delay": {
      "main": [
        [
          {
            "node": "Log Success",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Log Success": {
      "main": [
        [
          {
            "node": "Workflow Complete",
            "type": "main",
            "index": 0
          }
        ]
      ]
    }
  },
  "active": false,
  "settings": {
    "executionOrder": "v1",
    "availableInMCP": false
  },
  "versionId": "1c21db38-c2cd-4127-9419-9e9cdbca9b5b",
  "meta": {
    "instanceId": "e116f0cdc2af85ea765f95e160256c7af2464258f43bb537f78b1a25e7dab4f3"
  },
  "id": "PH4nhu4nqMun1_KCNC23O",
  "tags": []
}

{
  "name": "如果文章超长版这个适用但是多语言没测试过只测试翻译一种wp-auto-translate-all-post-types",
  "nodes": [
    {
      "parameters": {
        "rule": {
          "interval": [
            {
              "field": "hours",
              "hoursInterval": 6
            }
          ]
        }
      },
      "name": "Schedule Trigger1",
      "type": "n8n-nodes-base.scheduleTrigger",
      "typeVersion": 1,
      "position": [
        672,
        448
      ],
      "id": "0e1b246e-44f2-4513-ae03-fb6368c24496"
    },
    {
      "parameters": {
        "url": "=https://banwork.cn/wp-json/polylang-api/v1/post-types",
        "authentication": "genericCredentialType",
        "genericAuthType": "httpBasicAuth",
        "options": {}
      },
      "name": "Get All Post Types1",
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4.1,
      "position": [
        864,
        448
      ],
      "id": "27b8f772-e94a-4441-a3e8-dd33ed8a38b2",
      "credentials": {
        "httpBasicAuth": {
          "id": "nRdVHRimMjQuSFHB",
          "name": "Unnamed credential"
        }
      }
    },
    {
      "parameters": {
        "fieldToSplitOut": "post_types",
        "include": "selectedOtherFields",
        "fieldsToInclude": "target_languages",
        "options": {}
      },
      "name": "Split Post Types1",
      "type": "n8n-nodes-base.splitOut",
      "typeVersion": 1,
      "position": [
        1264,
        448
      ],
      "id": "5a162a60-5a37-4b66-be6f-8e64559bf429"
    },
    {
      "parameters": {
        "fieldToSplitOut": "target_languages",
        "options": {}
      },
      "name": "Split Target Languages1",
      "type": "n8n-nodes-base.splitOut",
      "typeVersion": 1,
      "position": [
        1472,
        448
      ],
      "id": "ce33536c-0199-417a-9a84-0e631c285fb1"
    },
    {
      "parameters": {
        "url": "=https://banwork.cn/wp-json/polylang-api/v1/untranslated-posts",
        "authentication": "genericCredentialType",
        "genericAuthType": "httpBasicAuth",
        "sendQuery": true,
        "queryParameters": {
          "parameters": [
            {
              "name": "post_type",
              "value": "={{ $('Split Post Types1').item.json.post_types }}"
            },
            {
              "name": "source_lang",
              "value": "={{ $('Set Language Config1').item.json.source_lang }}"
            },
            {
              "name": "target_lang",
              "value": "={{ $json.target_languages }}"
            }
          ]
        },
        "options": {}
      },
      "name": "Get Untranslated Posts1",
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4.1,
      "position": [
        1680,
        448
      ],
      "id": "91a76fe3-9915-40fd-9142-c655f5a7c7c7",
      "credentials": {
        "httpBasicAuth": {
          "id": "nRdVHRimMjQuSFHB",
          "name": "Unnamed credential"
        }
      }
    },
    {
      "parameters": {
        "conditions": {
          "options": {
            "caseSensitive": true,
            "leftValue": "",
            "typeValidation": "strict",
            "version": 1
          },
          "conditions": [
            {
              "id": "has-posts",
              "leftValue": "={{ $json.count }}",
              "rightValue": 0,
              "operator": {
                "type": "number",
                "operation": "gt"
              }
            }
          ],
          "combinator": "and"
        },
        "options": {}
      },
      "name": "Has Untranslated Posts?1",
      "type": "n8n-nodes-base.if",
      "typeVersion": 2,
      "position": [
        1872,
        464
      ],
      "id": "25ac9872-9f6b-4c97-9db9-66054b9d797b"
    },
    {
      "parameters": {
        "fieldToSplitOut": "posts",
        "options": {}
      },
      "name": "Split Posts1",
      "type": "n8n-nodes-base.splitOut",
      "typeVersion": 1,
      "position": [
        2096,
        336
      ],
      "id": "6e7ad1b8-1cd8-4cda-8d66-ad30f4a2a3a1"
    },
    {
      "parameters": {
        "url": "=https://banwork.cn/wp-json/polylang-api/v1/post/{{ $json.id }}",
        "authentication": "genericCredentialType",
        "genericAuthType": "httpBasicAuth",
        "options": {}
      },
      "name": "Get Post Content1",
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4.1,
      "position": [
        2304,
        336
      ],
      "id": "bb12daca-9142-4dfb-aa88-ea52f09fa093",
      "credentials": {
        "httpBasicAuth": {
          "id": "nRdVHRimMjQuSFHB",
          "name": "Unnamed credential"
        }
      }
    },
    {
      "parameters": {
        "jsCode": "function stripHtml(html) {\n  if (!html) return '';\n  \n  let text = html\n    .replace(/<script\\b[^<]*(?:(?!<\\/script>)<[^<]*)*<\\/script>/gi, '')\n    .replace(/<style\\b[^<]*(?:(?!<\\/style>)<[^<]*)*<\\/style>/gi, '');\n  \n  text = text.replace(/<[^>]+>/g, ' ');\n  text = text.replace(/\\s+/g, ' ').trim();\n  \n  return text;\n}\n\n// Process translatable meta fields\nfunction extractTranslatableMeta(metaFields, postType) {\n  const translatable = {};\n  \n  if (!metaFields || typeof metaFields !== 'object') return translatable;\n  \n  // Extract text-based meta fields (skip internal WordPress fields)\n  for (const [key, value] of Object.entries(metaFields)) {\n    // Skip internal WP fields and serialized data\n    if (key.startsWith('_') || !value) continue;\n    \n    // Handle arrays (take first value)\n    const metaValue = Array.isArray(value) ? value[0] : value;\n    \n    // Only include string values\n    if (typeof metaValue === 'string' && metaValue.length > 0) {\n      translatable[key] = stripHtml(metaValue);\n    }\n  }\n  \n  return translatable;\n}\n\nconst items = $input.all();\n\nreturn items.map((item, index) => {\n  const postData = item.json.post || item.json;\n  \n  return {\n    json: {\n      id: item.json.id || postData.id,\n      post: {\n        id: postData.id,\n        post_type: postData.type,\n        title: stripHtml(postData.title),\n        content: stripHtml(postData.content),\n        excerpt: stripHtml(postData.excerpt || ''),\n        categories: postData.categories || [],\n        tags: postData.tags || [],\n        acf_fields: postData.acf_fields || {},\n        meta_fields: extractTranslatableMeta(postData.meta, postData.type),\n        thumbnail_id: postData.thumbnail_id || null\n      }\n    },\n    pairedItem: index\n  };\n});\n\n"
      },
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        2512,
        336
      ],
      "id": "079ca47d-f7b4-4ba2-9878-78133e5053de",
      "name": "Process Post Data1"
    },
    {
      "parameters": {
        "jsCode": "const items = $input.all();\nconst sourceLang = $('Set Language Config1').item.json.source_lang;\nconst targetLang = $('Split Target Languages1').item.json.target_languages;\n\n// Helper function to truncate text\nfunction truncateText(text, maxLength = 8000) {\n  if (text.length <= maxLength) return text;\n  return text.substring(0, maxLength) + '... [truncated]';\n}\n\nreturn items.map((item, index) => {\n  const postData = item.json.post;\n  \n  // Truncate content if too long\n  const title = truncateText(postData.title, 500);\n  const content = truncateText(postData.content, 10000);\n  const excerpt = postData.excerpt ? truncateText(postData.excerpt, 1000) : '';\n  \n  // Build content for translation including meta fields\n  let translationContent = `Title: ${title}\\n\\nContent: ${content}`;\n  \n  if (excerpt) {\n    translationContent += `\\n\\nExcerpt: ${excerpt}`;\n  }\n  \n  // Add meta fields to translation\n  const metaFieldsArray = [];\n  if (postData.meta_fields && Object.keys(postData.meta_fields).length > 0) {\n    translationContent += '\\n\\nMeta Fields:';\n    for (const [key, value] of Object.entries(postData.meta_fields)) {\n      const truncatedValue = truncateText(String(value), 500);\n      translationContent += `\\n${key}: ${truncatedValue}`;\n      metaFieldsArray.push({ key, value: truncatedValue });\n    }\n  }\n  \n  // Build expected JSON structure\n  let expectedStructure = '\"title\": \"translated title\", \"content\": \"translated content\"';\n  if (postData.excerpt) {\n    expectedStructure += ', \"excerpt\": \"translated excerpt\"';\n  }\n  if (metaFieldsArray.length > 0) {\n    expectedStructure += ', \"meta_fields\": {\"field_key\": \"translated value\"}';\n  }\n  \n  const requestBody = {\n    model: \"deepseek-chat\",\n    messages: [\n      {\n        role: \"system\",\n        content: `You are a professional translator. Translate the following WordPress ${postData.post_type} content from ${sourceLang} to ${targetLang}. Return ONLY a valid JSON object with these exact keys: {${expectedStructure}}. For meta_fields, use the same keys but translate the values. IMPORTANT: Ensure all strings in the JSON are properly escaped and the response is valid JSON.`\n      },\n      {\n        role: \"user\",\n        content: translationContent\n      }\n    ],\n    temperature: 0.4,\n    max_tokens: 8192, // Add max_tokens limit\n    response_format: { type: \"json_object\" }\n  };\n  \n  return {\n    json: {\n      requestBody: requestBody,\n      post: postData,\n      metaFieldsKeys: metaFieldsArray.map(m => m.key)\n    },\n    pairedItem: index\n  };\n});"
      },
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        2720,
        336
      ],
      "id": "6e998ed2-f3e8-45a5-9df2-e127624bad03",
      "name": "Build DeepSeek Request1"
    },
    {
      "parameters": {
        "method": "POST",
        "url": "https://api.deepseek.com/v1/chat/completions",
        "authentication": "predefinedCredentialType",
        "nodeCredentialType": "httpHeaderAuth",
        "sendHeaders": true,
        "headerParameters": {
          "parameters": [
            {
              "name": "Content-Type",
              "value": "application/json"
            }
          ]
        },
        "sendBody": true,
        "specifyBody": "json",
        "jsonBody": "={{ $json.requestBody }}",
        "options": {}
      },
      "name": "DeepSeek Translate1",
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4.1,
      "position": [
        2928,
        336
      ],
      "id": "765f8a4e-5b45-4995-9c9c-170c62b0fc8e",
      "credentials": {
        "httpHeaderAuth": {
          "id": "5X9OcogB9LTTIp0J",
          "name": "Header Auth account"
        }
      }
    },
    {
      "parameters": {
        "jsCode": "const items = $input.all();\nconst postContentItems = $('Get Post Content1').all();\nconst targetLang = $('Split Target Languages1').item.json.target_languages;\n\nreturn items.map((item, index) => {\n  const response = item.json;\n  \n  let translatedData;\n  try {\n    // Get the raw content\n    const rawContent = response.choices[0].message.content;\n    \n    // Try to parse the JSON\n    translatedData = JSON.parse(rawContent);\n  } catch (parseError) {\n    // If JSON parsing fails, try to extract what we can\n    const rawContent = response.choices[0].message.content;\n    \n    // Log the error for debugging\n    console.error('Failed to parse translation response:', parseError.message);\n    console.error('Raw content:', rawContent.substring(0, 500)); // First 500 chars\n    \n    // Try to salvage partial data\n    translatedData = {\n      title: 'Translation failed - JSON parse error',\n      content: 'Error: Could not parse translation response',\n      excerpt: ''\n    };\n    \n    // Try to extract title if possible\n    const titleMatch = rawContent.match(/\"title\"\\s*:\\s*\"([^\"]*)\"/);\n    if (titleMatch) {\n      translatedData.title = titleMatch[1];\n    }\n  }\n  \n  const originalPostItem = postContentItems[index].json;\n  const originalPost = originalPostItem.post || originalPostItem;\n  \n  const categories = (originalPost.categories || []).map(cat => ({\n    term_id: cat.term_id,\n    translated_name: cat.name\n  }));\n  \n  const tags = (originalPost.tags || []).map(tag => ({\n    term_id: tag.term_id,\n    translated_name: tag.name\n  }));\n  \n  let acfFields = {};\n  if (originalPost.acf_fields && typeof originalPost.acf_fields === 'object') {\n    acfFields = originalPost.acf_fields;\n  }\n  \n  // Extract translated meta fields\n  let translatedMeta = {};\n  if (translatedData.meta_fields && typeof translatedData.meta_fields === 'object') {\n    translatedMeta = translatedData.meta_fields;\n  }\n  \n  return {\n    json: {\n      source_post_id: originalPost.id,\n      post_type: originalPost.type,\n      target_lang: targetLang,\n      title: translatedData.title || originalPost.title,\n      content: translatedData.content || originalPost.content,\n      excerpt: translatedData.excerpt || '',\n      categories: categories,\n      tags: tags,\n      acf_fields: acfFields,\n      meta_fields: translatedMeta,\n      thumbnail_id: originalPost.thumbnail_id\n    },\n    pairedItem: index\n  };\n});"
      },
      "name": "Format Translation Data1",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        3136,
        336
      ],
      "id": "7eb7a33c-060e-4e2f-9d68-7d8173070ab7"
    },
    {
      "parameters": {
        "method": "POST",
        "url": "=https://banwork.cn/wp-json/polylang-api/v1/translate-post",
        "authentication": "genericCredentialType",
        "genericAuthType": "httpBasicAuth",
        "sendBody": true,
        "specifyBody": "json",
        "jsonBody": "={\n  \"source_post_id\": {{ $json.source_post_id }},\n  \"target_lang\": \"{{ $json.target_lang }}\",\n  \"title\": {{ JSON.stringify($json.title) }},\n  \"content\": {{ JSON.stringify($json.content) }},\n  \"excerpt\": {{ JSON.stringify($json.excerpt) }},\n  \"categories\": {{ JSON.stringify($json.categories) }},\n  \"tags\": {{ JSON.stringify($json.tags) }},\n  \"acf_fields\": {{ JSON.stringify($json.acf_fields) }},\n  \"meta_fields\": {{ JSON.stringify($json.meta_fields) }},\n  \"thumbnail_id\": {{ $json.thumbnail_id }}\n}",
        "options": {}
      },
      "name": "Create Translation1",
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4.1,
      "position": [
        3344,
        336
      ],
      "id": "a4960f8d-d5ed-4a61-be0a-a2659dc9da79",
      "credentials": {
        "httpBasicAuth": {
          "id": "nRdVHRimMjQuSFHB",
          "name": "Unnamed credential"
        }
      }
    },
    {
      "parameters": {
        "amount": 2
      },
      "name": "Rate Limit Delay1",
      "type": "n8n-nodes-base.wait",
      "typeVersion": 1.1,
      "position": [
        3552,
        336
      ],
      "id": "cd0b2e9f-852d-4eb2-aafc-8473ef02f382",
      "webhookId": "rate-limit-delay"
    },
    {
      "parameters": {
        "options": {}
      },
      "name": "Log Success1",
      "type": "n8n-nodes-base.set",
      "typeVersion": 3.2,
      "position": [
        3744,
        336
      ],
      "id": "b9a47f7a-cbd4-4db4-87a0-decd1b8396ed"
    },
    {
      "parameters": {
        "options": {}
      },
      "name": "Workflow Complete1",
      "type": "n8n-nodes-base.set",
      "typeVersion": 3.2,
      "position": [
        3952,
        448
      ],
      "id": "33ab1d69-8a88-4a58-84fb-0c8b12d82b0a"
    },
    {
      "parameters": {
        "jsCode": "const inputData = $input.first().json;\n\nreturn [\n  {\n    json: {\n      post_types: inputData.post_types,\n      // Add all your target languages here\n      target_languages: [\"fr\"],\n      source_lang: \"en\"\n    }\n  }\n];"
      },
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        1072,
        448
      ],
      "id": "c45d4f72-852f-42a5-ab2b-7e52d8f10c3b",
      "name": "Set Language Config1"
    }
  ],
  "pinData": {},
  "connections": {
    "Schedule Trigger1": {
      "main": [
        [
          {
            "node": "Get All Post Types1",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Get All Post Types1": {
      "main": [
        [
          {
            "node": "Set Language Config1",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Split Post Types1": {
      "main": [
        [
          {
            "node": "Split Target Languages1",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Split Target Languages1": {
      "main": [
        [
          {
            "node": "Get Untranslated Posts1",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Get Untranslated Posts1": {
      "main": [
        [
          {
            "node": "Has Untranslated Posts?1",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Has Untranslated Posts?1": {
      "main": [
        [
          {
            "node": "Split Posts1",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Workflow Complete1",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Split Posts1": {
      "main": [
        [
          {
            "node": "Get Post Content1",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Get Post Content1": {
      "main": [
        [
          {
            "node": "Process Post Data1",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Process Post Data1": {
      "main": [
        [
          {
            "node": "Build DeepSeek Request1",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Build DeepSeek Request1": {
      "main": [
        [
          {
            "node": "DeepSeek Translate1",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "DeepSeek Translate1": {
      "main": [
        [
          {
            "node": "Format Translation Data1",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Format Translation Data1": {
      "main": [
        [
          {
            "node": "Create Translation1",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Create Translation1": {
      "main": [
        [
          {
            "node": "Rate Limit Delay1",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Rate Limit Delay1": {
      "main": [
        [
          {
            "node": "Log Success1",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Log Success1": {
      "main": [
        [
          {
            "node": "Workflow Complete1",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Set Language Config1": {
      "main": [
        [
          {
            "node": "Split Post Types1",
            "type": "main",
            "index": 0
          }
        ]
      ]
    }
  },
  "active": false,
  "settings": {
    "executionOrder": "v1",
    "availableInMCP": false
  },
  "versionId": "80268b84-3537-4eec-829f-a030c34d3e04",
  "meta": {
    "instanceId": "e116f0cdc2af85ea765f95e160256c7af2464258f43bb537f78b1a25e7dab4f3"
  },
  "id": "O0FMfybFZu--afOm6Pv9S",
  "tags": []
}

{
  "name": "这个版本保留Post conten所有html",
  "nodes": [
    {
      "parameters": {
        "rule": {
          "interval": [
            {
              "field": "hours",
              "hoursInterval": 6
            }
          ]
        }
      },
      "name": "Schedule Trigger1",
      "type": "n8n-nodes-base.scheduleTrigger",
      "typeVersion": 1,
      "position": [
        672,
        448
      ],
      "id": "1080d094-efb9-421f-9c19-efb00894b52f"
    },
    {
      "parameters": {
        "url": "=https://banwork.cn/wp-json/polylang-api/v1/post-types",
        "authentication": "genericCredentialType",
        "genericAuthType": "httpBasicAuth",
        "options": {}
      },
      "name": "Get All Post Types1",
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4.1,
      "position": [
        864,
        448
      ],
      "id": "3278e59f-712f-4b8d-912e-5365201a051e",
      "credentials": {
        "httpBasicAuth": {
          "id": "nRdVHRimMjQuSFHB",
          "name": "Unnamed credential"
        }
      }
    },
    {
      "parameters": {
        "fieldToSplitOut": "post_types",
        "include": "selectedOtherFields",
        "fieldsToInclude": "target_languages",
        "options": {}
      },
      "name": "Split Post Types1",
      "type": "n8n-nodes-base.splitOut",
      "typeVersion": 1,
      "position": [
        1264,
        448
      ],
      "id": "e9166d45-c3c2-4848-b8e0-b5c9067ab3fe"
    },
    {
      "parameters": {
        "fieldToSplitOut": "target_languages",
        "options": {}
      },
      "name": "Split Target Languages1",
      "type": "n8n-nodes-base.splitOut",
      "typeVersion": 1,
      "position": [
        1472,
        448
      ],
      "id": "7a05a4c4-89e9-40fa-829e-32bdfd568f3a"
    },
    {
      "parameters": {
        "url": "=https://banwork.cn/wp-json/polylang-api/v1/untranslated-posts",
        "authentication": "genericCredentialType",
        "genericAuthType": "httpBasicAuth",
        "sendQuery": true,
        "queryParameters": {
          "parameters": [
            {
              "name": "post_type",
              "value": "={{ $('Split Post Types1').item.json.post_types }}"
            },
            {
              "name": "source_lang",
              "value": "={{ $('Set Language Config1').item.json.source_lang }}"
            },
            {
              "name": "target_lang",
              "value": "={{ $json.target_languages }}"
            }
          ]
        },
        "options": {}
      },
      "name": "Get Untranslated Posts1",
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4.1,
      "position": [
        1680,
        448
      ],
      "id": "06185347-be6a-4d38-a1c1-0f261548ab5b",
      "credentials": {
        "httpBasicAuth": {
          "id": "nRdVHRimMjQuSFHB",
          "name": "Unnamed credential"
        }
      }
    },
    {
      "parameters": {
        "conditions": {
          "options": {
            "caseSensitive": true,
            "leftValue": "",
            "typeValidation": "strict",
            "version": 1
          },
          "conditions": [
            {
              "id": "has-posts",
              "leftValue": "={{ $json.count }}",
              "rightValue": 0,
              "operator": {
                "type": "number",
                "operation": "gt"
              }
            }
          ],
          "combinator": "and"
        },
        "options": {}
      },
      "name": "Has Untranslated Posts?1",
      "type": "n8n-nodes-base.if",
      "typeVersion": 2,
      "position": [
        1872,
        464
      ],
      "id": "2efcb82a-3a05-4f70-824f-7ac5ec9a0946"
    },
    {
      "parameters": {
        "fieldToSplitOut": "posts",
        "options": {}
      },
      "name": "Split Posts1",
      "type": "n8n-nodes-base.splitOut",
      "typeVersion": 1,
      "position": [
        2096,
        336
      ],
      "id": "412f4054-b3e4-4384-bba8-901b98925420"
    },
    {
      "parameters": {
        "url": "=https://banwork.cn/wp-json/polylang-api/v1/post/{{ $json.id }}",
        "authentication": "genericCredentialType",
        "genericAuthType": "httpBasicAuth",
        "options": {}
      },
      "name": "Get Post Content1",
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4.1,
      "position": [
        2304,
        336
      ],
      "id": "9673e104-fe30-4d27-9baa-ec25a24f9a27",
      "credentials": {
        "httpBasicAuth": {
          "id": "nRdVHRimMjQuSFHB",
          "name": "Unnamed credential"
        }
      }
    },
    {
      "parameters": {
        "jsCode": "function stripHtml(html) {\n  if (!html) return '';\n  \n  let text = html\n    .replace(/<script\\b[^<]*(?:(?!<\\/script>)<[^<]*)*<\\/script>/gi, '')\n    .replace(/<style\\b[^<]*(?:(?!<\\/style>)<[^<]*)*<\\/style>/gi, '');\n  \n  text = text.replace(/<[^>]+>/g, ' ');\n  text = text.replace(/\\s+/g, ' ').trim();\n  \n  return text;\n}\n\n// Process translatable meta fields\nfunction extractTranslatableMeta(metaFields, postType) {\n  const translatable = {};\n  \n  if (!metaFields || typeof metaFields !== 'object') return translatable;\n  \n  // Extract text-based meta fields (skip internal WordPress fields)\n  for (const [key, value] of Object.entries(metaFields)) {\n    // Skip internal WP fields and serialized data\n    if (key.startsWith('_') || !value) continue;\n    \n    // Handle arrays (take first value)\n    const metaValue = Array.isArray(value) ? value[0] : value;\n    \n    // Only include string values\n    if (typeof metaValue === 'string' && metaValue.length > 0) {\n      translatable[key] = stripHtml(metaValue);\n    }\n  }\n  \n  return translatable;\n}\n\nconst items = $input.all();\n\nreturn items.map((item, index) => {\n  const postData = item.json.post || item.json;\n  \n  return {\n    json: {\n      id: item.json.id || postData.id,\n      post: {\n        id: postData.id,\n        post_type: postData.type,\n        title: stripHtml(postData.title),\n        content: postData.content,\n        excerpt: stripHtml(postData.excerpt || ''),\n        categories: postData.categories || [],\n        tags: postData.tags || [],\n        acf_fields: postData.acf_fields || {},\n        meta_fields: extractTranslatableMeta(postData.meta, postData.type),\n        thumbnail_id: postData.thumbnail_id || null\n      }\n    },\n    pairedItem: index\n  };\n});\n\n"
      },
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        2512,
        336
      ],
      "id": "959d8a1e-3eca-4088-b723-e09bb38d068b",
      "name": "Process Post Data1"
    },
    {
      "parameters": {
        "jsCode": "const items = $input.all();\nconst sourceLang = $('Set Language Config1').item.json.source_lang;\nconst targetLang = $('Split Target Languages1').item.json.target_languages;\n\n// Helper function to truncate text\nfunction truncateText(text, maxLength = 8000) {\n  if (text.length <= maxLength) return text;\n  return text.substring(0, maxLength) + '... [truncated]';\n}\n\nreturn items.map((item, index) => {\n  const postData = item.json.post;\n  \n  // Truncate content if too long\n  const title = truncateText(postData.title, 500);\n  const content = truncateText(postData.content, 10000);\n  const excerpt = postData.excerpt ? truncateText(postData.excerpt, 1000) : '';\n  \n  // Build content for translation including meta fields\n  let translationContent = `Title: ${title}\\n\\nContent: ${content}`;\n  \n  if (excerpt) {\n    translationContent += `\\n\\nExcerpt: ${excerpt}`;\n  }\n  \n  // Add meta fields to translation\n  const metaFieldsArray = [];\n  if (postData.meta_fields && Object.keys(postData.meta_fields).length > 0) {\n    translationContent += '\\n\\nMeta Fields:';\n    for (const [key, value] of Object.entries(postData.meta_fields)) {\n      const truncatedValue = truncateText(String(value), 500);\n      translationContent += `\\n${key}: ${truncatedValue}`;\n      metaFieldsArray.push({ key, value: truncatedValue });\n    }\n  }\n  \n  // Build expected JSON structure\n  let expectedStructure = '\"title\": \"translated title\", \"content\": \"translated content\"';\n  if (postData.excerpt) {\n    expectedStructure += ', \"excerpt\": \"translated excerpt\"';\n  }\n  if (metaFieldsArray.length > 0) {\n    expectedStructure += ', \"meta_fields\": {\"field_key\": \"translated value\"}';\n  }\n  \n  const requestBody = {\n    model: \"deepseek-chat\",\n    messages: [\n      {\n        role: \"system\",\n        content: `You are a professional translator. Translate the following WordPress ${postData.post_type} content from ${sourceLang} to ${targetLang}. CRITICAL RULES:\n- The input content contains HTML.\n- DO NOT remove, rewrite, reorder, escape, or normalize any HTML tags.\n- DO NOT add or remove <p>, <br>, <strong>, <a>, <ul>, <li>, or any attributes.\n- ONLY translate the visible text between HTML tags.\n- Preserve whitespace and line breaks.\n- Return VALID JSON ONLY.\n- Keep HTML exactly as-is. Return ONLY a valid JSON object with these exact keys: {${expectedStructure}}. For meta_fields, use the same keys but translate the values. IMPORTANT: Ensure all strings in the JSON are properly escaped and the response is valid JSON.`\n      },\n      {\n        role: \"user\",\n        content: translationContent\n      }\n    ],\n    temperature: 0.4,\n    max_tokens: 8192, // Add max_tokens limit\n    response_format: { type: \"json_object\" }\n  };\n  \n  return {\n    json: {\n      requestBody: requestBody,\n      post: postData,\n      metaFieldsKeys: metaFieldsArray.map(m => m.key)\n    },\n    pairedItem: index\n  };\n});"
      },
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        2720,
        336
      ],
      "id": "61db9cd5-d503-49d0-893c-c3603e514a48",
      "name": "Build DeepSeek Request1"
    },
    {
      "parameters": {
        "method": "POST",
        "url": "https://api.deepseek.com/v1/chat/completions",
        "authentication": "predefinedCredentialType",
        "nodeCredentialType": "httpHeaderAuth",
        "sendHeaders": true,
        "headerParameters": {
          "parameters": [
            {
              "name": "Content-Type",
              "value": "application/json"
            }
          ]
        },
        "sendBody": true,
        "specifyBody": "json",
        "jsonBody": "={{ $json.requestBody }}",
        "options": {}
      },
      "name": "DeepSeek Translate1",
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4.1,
      "position": [
        2928,
        336
      ],
      "id": "438fbcb2-37a4-45a1-9531-61c1378cfdfb",
      "credentials": {
        "httpHeaderAuth": {
          "id": "5X9OcogB9LTTIp0J",
          "name": "Header Auth account"
        }
      }
    },
    {
      "parameters": {
        "jsCode": "const items = $input.all();\nconst postContentItems = $('Get Post Content1').all();\nconst targetLang = $('Split Target Languages1').item.json.target_languages;\n\nreturn items.map((item, index) => {\n  const response = item.json;\n  \n  let translatedData;\n  try {\n    // Get the raw content\n    const rawContent = response.choices[0].message.content;\n    \n    // Try to parse the JSON\n    translatedData = JSON.parse(rawContent);\n  } catch (parseError) {\n    // If JSON parsing fails, try to extract what we can\n    const rawContent = response.choices[0].message.content;\n    \n    // Log the error for debugging\n    console.error('Failed to parse translation response:', parseError.message);\n    console.error('Raw content:', rawContent.substring(0, 500)); // First 500 chars\n    \n    // Try to salvage partial data\n    translatedData = {\n      title: 'Translation failed - JSON parse error',\n      content: 'Error: Could not parse translation response',\n      excerpt: ''\n    };\n    \n    // Try to extract title if possible\n    const titleMatch = rawContent.match(/\"title\"\\s*:\\s*\"([^\"]*)\"/);\n    if (titleMatch) {\n      translatedData.title = titleMatch[1];\n    }\n  }\n  \n  const originalPostItem = postContentItems[index].json;\n  const originalPost = originalPostItem.post || originalPostItem;\n  \n  const categories = (originalPost.categories || []).map(cat => ({\n    term_id: cat.term_id,\n    translated_name: cat.name\n  }));\n  \n  const tags = (originalPost.tags || []).map(tag => ({\n    term_id: tag.term_id,\n    translated_name: tag.name\n  }));\n  \n  let acfFields = {};\n  if (originalPost.acf_fields && typeof originalPost.acf_fields === 'object') {\n    acfFields = originalPost.acf_fields;\n  }\n  \n  // Extract translated meta fields\n  let translatedMeta = {};\n  if (translatedData.meta_fields && typeof translatedData.meta_fields === 'object') {\n    translatedMeta = translatedData.meta_fields;\n  }\n  \n  return {\n    json: {\n      source_post_id: originalPost.id,\n      post_type: originalPost.type,\n      target_lang: targetLang,\n      title: translatedData.title || originalPost.title,\n      content: translatedData.content || originalPost.content,\n      excerpt: translatedData.excerpt || '',\n      categories: categories,\n      tags: tags,\n      acf_fields: acfFields,\n      meta_fields: translatedMeta,\n      thumbnail_id: originalPost.thumbnail_id\n    },\n    pairedItem: index\n  };\n});"
      },
      "name": "Format Translation Data1",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        3136,
        336
      ],
      "id": "80234c05-0a20-465f-afc4-a42cb52ae6db"
    },
    {
      "parameters": {
        "method": "POST",
        "url": "=https://banwork.cn/wp-json/polylang-api/v1/translate-post",
        "authentication": "genericCredentialType",
        "genericAuthType": "httpBasicAuth",
        "sendBody": true,
        "specifyBody": "json",
        "jsonBody": "={\n  \"source_post_id\": {{ $json.source_post_id }},\n  \"target_lang\": \"{{ $json.target_lang }}\",\n  \"title\": {{ JSON.stringify($json.title) }},\n  \"content\": {{ JSON.stringify($json.content) }},\n  \"excerpt\": {{ JSON.stringify($json.excerpt) }},\n  \"categories\": {{ JSON.stringify($json.categories) }},\n  \"tags\": {{ JSON.stringify($json.tags) }},\n  \"acf_fields\": {{ JSON.stringify($json.acf_fields) }},\n  \"meta_fields\": {{ JSON.stringify($json.meta_fields) }},\n  \"thumbnail_id\": {{ $json.thumbnail_id }}\n}",
        "options": {}
      },
      "name": "Create Translation1",
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4.1,
      "position": [
        3344,
        336
      ],
      "id": "9bcdb731-7348-4608-8469-f49d5b38082e",
      "credentials": {
        "httpBasicAuth": {
          "id": "nRdVHRimMjQuSFHB",
          "name": "Unnamed credential"
        }
      }
    },
    {
      "parameters": {
        "amount": 2
      },
      "name": "Rate Limit Delay1",
      "type": "n8n-nodes-base.wait",
      "typeVersion": 1.1,
      "position": [
        3552,
        336
      ],
      "id": "8d744c8a-f962-4d61-b6bc-47f9a311e21e",
      "webhookId": "0f33c2d9-789c-43d0-9186-25a2b0a33997"
    },
    {
      "parameters": {
        "options": {}
      },
      "name": "Log Success1",
      "type": "n8n-nodes-base.set",
      "typeVersion": 3.2,
      "position": [
        3744,
        336
      ],
      "id": "f22d9d72-d053-4da3-a6ce-552241e8cd2d"
    },
    {
      "parameters": {
        "options": {}
      },
      "name": "Workflow Complete1",
      "type": "n8n-nodes-base.set",
      "typeVersion": 3.2,
      "position": [
        3952,
        448
      ],
      "id": "02017e05-b91c-4617-a819-0b15794e21c9"
    },
    {
      "parameters": {
        "jsCode": "const inputData = $input.first().json;\n\nreturn [\n  {\n    json: {\n      post_types: inputData.post_types,\n      // Add all your target languages here\n      target_languages: [\"de\"],\n      source_lang: \"en\"\n    }\n  }\n];"
      },
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        1072,
        448
      ],
      "id": "95253ccb-037a-45b8-952d-2fbf22aaface",
      "name": "Set Language Config1"
    }
  ],
  "pinData": {},
  "connections": {
    "Schedule Trigger1": {
      "main": [
        [
          {
            "node": "Get All Post Types1",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Get All Post Types1": {
      "main": [
        [
          {
            "node": "Set Language Config1",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Split Post Types1": {
      "main": [
        [
          {
            "node": "Split Target Languages1",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Split Target Languages1": {
      "main": [
        [
          {
            "node": "Get Untranslated Posts1",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Get Untranslated Posts1": {
      "main": [
        [
          {
            "node": "Has Untranslated Posts?1",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Has Untranslated Posts?1": {
      "main": [
        [
          {
            "node": "Split Posts1",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Workflow Complete1",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Split Posts1": {
      "main": [
        [
          {
            "node": "Get Post Content1",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Get Post Content1": {
      "main": [
        [
          {
            "node": "Process Post Data1",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Process Post Data1": {
      "main": [
        [
          {
            "node": "Build DeepSeek Request1",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Build DeepSeek Request1": {
      "main": [
        [
          {
            "node": "DeepSeek Translate1",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "DeepSeek Translate1": {
      "main": [
        [
          {
            "node": "Format Translation Data1",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Format Translation Data1": {
      "main": [
        [
          {
            "node": "Create Translation1",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Create Translation1": {
      "main": [
        [
          {
            "node": "Rate Limit Delay1",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Rate Limit Delay1": {
      "main": [
        [
          {
            "node": "Log Success1",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Log Success1": {
      "main": [
        [
          {
            "node": "Workflow Complete1",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Set Language Config1": {
      "main": [
        [
          {
            "node": "Split Post Types1",
            "type": "main",
            "index": 0
          }
        ]
      ]
    }
  },
  "active": false,
  "settings": {
    "executionOrder": "v1",
    "availableInMCP": false
  },
  "versionId": "4726220f-8b5e-42ab-bff5-0a389cf970b9",
  "meta": {
    "instanceId": "e116f0cdc2af85ea765f95e160256c7af2464258f43bb537f78b1a25e7dab4f3"
  },
  "id": "FlCxo05rYNW5KAdv",
  "tags": []
}

{
  "name": "能用2-保留block翻译同时保留Post conten所有html copy",
  "nodes": [
    {
      "parameters": {
        "rule": {
          "interval": [
            {
              "field": "hours",
              "hoursInterval": 6
            }
          ]
        }
      },
      "name": "Schedule Trigger1",
      "type": "n8n-nodes-base.scheduleTrigger",
      "typeVersion": 1,
      "position": [
        672,
        448
      ],
      "id": "cb01752b-62c8-4da9-9c42-fb7269216fff"
    },
    {
      "parameters": {
        "url": "=https://banwork.cn/wp-json/polylang-api/v1/post-types",
        "authentication": "genericCredentialType",
        "genericAuthType": "httpBasicAuth",
        "options": {}
      },
      "name": "Get All Post Types1",
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4.1,
      "position": [
        864,
        448
      ],
      "id": "9ac5d1e3-0a47-4ba7-ac72-35cbf04b37ce",
      "credentials": {
        "httpBasicAuth": {
          "id": "nRdVHRimMjQuSFHB",
          "name": "Unnamed credential"
        }
      }
    },
    {
      "parameters": {
        "fieldToSplitOut": "post_types",
        "include": "selectedOtherFields",
        "fieldsToInclude": "target_languages",
        "options": {}
      },
      "name": "Split Post Types1",
      "type": "n8n-nodes-base.splitOut",
      "typeVersion": 1,
      "position": [
        1264,
        448
      ],
      "id": "75e5ea49-dc0d-48b2-8b98-168f20975e0e"
    },
    {
      "parameters": {
        "fieldToSplitOut": "target_languages",
        "options": {}
      },
      "name": "Split Target Languages1",
      "type": "n8n-nodes-base.splitOut",
      "typeVersion": 1,
      "position": [
        1472,
        448
      ],
      "id": "152f703c-60c4-4a1c-8fa8-60b57d613645"
    },
    {
      "parameters": {
        "url": "=https://banwork.cn/wp-json/polylang-api/v1/untranslated-posts",
        "authentication": "genericCredentialType",
        "genericAuthType": "httpBasicAuth",
        "sendQuery": true,
        "queryParameters": {
          "parameters": [
            {
              "name": "post_type",
              "value": "={{ $('Split Post Types1').item.json.post_types }}"
            },
            {
              "name": "source_lang",
              "value": "={{ $('Set Language Config1').item.json.source_lang }}"
            },
            {
              "name": "target_lang",
              "value": "={{ $json.target_languages }}"
            }
          ]
        },
        "options": {}
      },
      "name": "Get Untranslated Posts1",
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4.1,
      "position": [
        1680,
        448
      ],
      "id": "c8171ee1-9eb9-4220-ad26-d9e6c0c92b8a",
      "credentials": {
        "httpBasicAuth": {
          "id": "nRdVHRimMjQuSFHB",
          "name": "Unnamed credential"
        }
      }
    },
    {
      "parameters": {
        "conditions": {
          "options": {
            "caseSensitive": true,
            "leftValue": "",
            "typeValidation": "strict",
            "version": 1
          },
          "conditions": [
            {
              "id": "has-posts",
              "leftValue": "={{ $json.count }}",
              "rightValue": 0,
              "operator": {
                "type": "number",
                "operation": "gt"
              }
            }
          ],
          "combinator": "and"
        },
        "options": {}
      },
      "name": "Has Untranslated Posts?1",
      "type": "n8n-nodes-base.if",
      "typeVersion": 2,
      "position": [
        1872,
        464
      ],
      "id": "948b3989-9e4e-4a33-a7d3-a3119982bf4f"
    },
    {
      "parameters": {
        "fieldToSplitOut": "posts",
        "options": {}
      },
      "name": "Split Posts1",
      "type": "n8n-nodes-base.splitOut",
      "typeVersion": 1,
      "position": [
        2096,
        336
      ],
      "id": "de2dcb1c-3dd3-4e40-a727-6b509b59d2a3"
    },
    {
      "parameters": {
        "url": "=https://banwork.cn/wp-json/polylang-api/v1/post/{{ $json.id }}",
        "authentication": "genericCredentialType",
        "genericAuthType": "httpBasicAuth",
        "options": {}
      },
      "name": "Get Post Content1",
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4.1,
      "position": [
        2304,
        336
      ],
      "id": "f4b25d59-f0a3-4bb2-be25-42a6e04ec42a",
      "credentials": {
        "httpBasicAuth": {
          "id": "nRdVHRimMjQuSFHB",
          "name": "Unnamed credential"
        }
      }
    },
    {
      "parameters": {
        "jsCode": "function stripHtml(html) {\n  if (!html) return '';\n  \n  let text = html\n    .replace(/<script\\b[^<]*(?:(?!<\\/script>)<[^<]*)*<\\/script>/gi, '')\n    .replace(/<style\\b[^<]*(?:(?!<\\/style>)<[^<]*)*<\\/style>/gi, '');\n  \n  text = text.replace(/<[^>]+>/g, ' ');\n  text = text.replace(/\\s+/g, ' ').trim();\n  \n  return text;\n}\n\n// NEW: Parse WordPress blocks\nfunction parseBlocks(content) {\n  if (!content) return { blocks: [], plainContent: '' };\n  \n  const blockPattern = /<!--\\s*wp:([a-z0-9-]+\\/[a-z0-9-]+)\\s+({[^}]+})\\s*(\\/)?-->/gi;\n  const blocks = [];\n  let match;\n  \n  while ((match = blockPattern.exec(content)) !== null) {\n    try {\n      const blockName = match[1];\n      const attributes = JSON.parse(match[2]);\n      const isSelfClosing = match[3] === '/';\n      \n      blocks.push({\n        blockName,\n        attributes,\n        isSelfClosing,\n        originalMatch: match[0],\n        index: match.index\n      });\n    } catch (e) {\n      console.error('Failed to parse block:', match[0]);\n    }\n  }\n  \n  // Remove blocks from content for separate translation\n  const plainContent = content.replace(blockPattern, '');\n  \n  return { blocks, plainContent };\n}\n\nfunction extractTranslatableMeta(metaFields, postType) {\n  const translatable = {};\n  \n  if (!metaFields || typeof metaFields !== 'object') return translatable;\n  \n  for (const [key, value] of Object.entries(metaFields)) {\n    if (key.startsWith('_') || !value) continue;\n    \n    const metaValue = Array.isArray(value) ? value[0] : value;\n    \n    if (typeof metaValue === 'string' && metaValue.length > 0) {\n      translatable[key] = stripHtml(metaValue);\n    }\n  }\n  \n  return translatable;\n}\n\nconst items = $input.all();\n\nreturn items.map((item, index) => {\n  const postData = item.json.post || item.json;\n  \n  // Parse blocks from content\n  const { blocks, plainContent } = parseBlocks(postData.content);\n  \n  return {\n    json: {\n      id: item.json.id || postData.id,\n      post: {\n        id: postData.id,\n        post_type: postData.type,\n        title: stripHtml(postData.title),\n        content: plainContent, // Content without block comments\n        blocks: blocks, // Separate blocks for translation\n        excerpt: stripHtml(postData.excerpt || ''),\n        categories: postData.categories || [],\n        tags: postData.tags || [],\n        acf_fields: postData.acf_fields || {},\n        meta_fields: extractTranslatableMeta(postData.meta, postData.type),\n        thumbnail_id: postData.thumbnail_id || null\n      }\n    },\n    pairedItem: index\n  };\n});"
      },
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        2512,
        336
      ],
      "id": "7ce1aa13-7eb8-4a29-b779-38f3e972728f",
      "name": "Process Post Data1"
    },
    {
      "parameters": {
        "jsCode": "const items = $input.all();\nconst sourceLang = $('Set Language Config1').item.json.source_lang;\nconst targetLang = $('Split Target Languages1').item.json.target_languages;\n\nfunction truncateText(text, maxLength = 8000) {\n  if (text.length <= maxLength) return text;\n  return text.substring(0, maxLength) + '... [truncated]';\n}\n\n// Extract translatable content from blocks\nfunction extractBlockTranslatables(content) {\n  const blockPattern = /<!--\\s*wp:([^\\s]+)\\s+(\\{[^}]+\\})\\s*(?:\\/)?-->/g;\n  const translatables = [];\n  let match;\n  \n  while ((match = blockPattern.exec(content)) !== null) {\n    try {\n      const blockName = match[1];\n      const attrs = JSON.parse(match[2]);\n      \n      // Extract translatable attributes\n      const translatable = {};\n      for (const [key, value] of Object.entries(attrs)) {\n        if (typeof value === 'string' && !key.includes('Id') && !key.includes('Url')) {\n          translatable[key] = value;\n        } else if (Array.isArray(value)) {\n          // Handle arrays like stats\n          translatable[key] = value.map(item => {\n            if (typeof item === 'object') {\n              const trans = {};\n              for (const [k, v] of Object.entries(item)) {\n                if (typeof v === 'string' && !k.includes('Id')) {\n                  trans[k] = v;\n                }\n              }\n              return trans;\n            }\n            return item;\n          });\n        }\n      }\n      \n      if (Object.keys(translatable).length > 0) {\n        translatables.push({\n          blockName,\n          attributes: translatable\n        });\n      }\n    } catch (e) {\n      console.error('Block parse error:', e);\n    }\n  }\n  \n  return translatables;\n}\n\nreturn items.map((item, index) => {\n  const postData = item.json.post;\n  \n  const title = truncateText(postData.title, 500);\n  const excerpt = postData.excerpt ? truncateText(postData.excerpt, 1000) : '';\n  \n  // Extract block translatables\n  const blockTranslatables = extractBlockTranslatables(postData.content);\n  \n  let translationContent = `Title: ${title}`;\n  \n  if (excerpt) {\n    translationContent += `\\n\\nExcerpt: ${excerpt}`;\n  }\n  \n  if (blockTranslatables.length > 0) {\n    translationContent += `\\n\\nBlocks to translate:\\n${JSON.stringify(blockTranslatables, null, 2)}`;\n  }\n  \n  const requestBody = {\n    model: \"deepseek-chat\",\n    messages: [\n      {\n        role: \"system\",\n        content: `Translate from ${sourceLang} to ${targetLang}. Return JSON with:\n- \"title\": translated title\n- \"excerpt\": translated excerpt  \n- \"blocks\": array of {blockName, attributes} with ONLY text values translated\n\nDO NOT translate: IDs, URLs, numbers (unless they're labels).`\n      },\n      {\n        role: \"user\",\n        content: translationContent\n      }\n    ],\n    temperature: 0.4,\n    max_tokens: 8192,\n    response_format: { type: \"json_object\" }\n  };\n  \n  return {\n    json: {\n      requestBody,\n      post: postData\n    },\n    pairedItem: index\n  };\n});\n"
      },
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        2720,
        336
      ],
      "id": "1a3f9225-c35e-4848-b281-758a8402bd1f",
      "name": "Build DeepSeek Request1"
    },
    {
      "parameters": {
        "method": "POST",
        "url": "https://api.deepseek.com/v1/chat/completions",
        "authentication": "predefinedCredentialType",
        "nodeCredentialType": "httpHeaderAuth",
        "sendHeaders": true,
        "headerParameters": {
          "parameters": [
            {
              "name": "Content-Type",
              "value": "application/json"
            }
          ]
        },
        "sendBody": true,
        "specifyBody": "json",
        "jsonBody": "={{ $json.requestBody }}",
        "options": {}
      },
      "name": "DeepSeek Translate1",
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4.1,
      "position": [
        2928,
        336
      ],
      "id": "d02dc43f-ae3c-4025-bf12-ee0dbdd8961b",
      "credentials": {
        "httpHeaderAuth": {
          "id": "5X9OcogB9LTTIp0J",
          "name": "Header Auth account"
        }
      }
    },
    {
      "parameters": {
        "jsCode": "const items = $input.all();\nconst postContentItems = $('Get Post Content1').all();\nconst targetLang = $('Split Target Languages1').item.json.target_languages;\n\nreturn items.map((item, index) => {\n  const response = item.json;\n  \n  let translatedData;\n  try {\n    // Get the raw content\n    const rawContent = response.choices[0].message.content;\n    \n    // Try to parse the JSON\n    translatedData = JSON.parse(rawContent);\n  } catch (parseError) {\n    // If JSON parsing fails, try to extract what we can\n    const rawContent = response.choices[0].message.content;\n    \n    // Log the error for debugging\n    console.error('Failed to parse translation response:', parseError.message);\n    console.error('Raw content:', rawContent.substring(0, 500)); // First 500 chars\n    \n    // Try to salvage partial data\n    translatedData = {\n      title: 'Translation failed - JSON parse error',\n      content: 'Error: Could not parse translation response',\n      excerpt: ''\n    };\n    \n    // Try to extract title if possible\n    const titleMatch = rawContent.match(/\"title\"\\s*:\\s*\"([^\"]*)\"/);\n    if (titleMatch) {\n      translatedData.title = titleMatch[1];\n    }\n  }\n  \n  const originalPostItem = postContentItems[index].json;\n  const originalPost = originalPostItem.post || originalPostItem;\n  \n  const categories = (originalPost.categories || []).map(cat => ({\n    term_id: cat.term_id,\n    translated_name: cat.name\n  }));\n  \n  const tags = (originalPost.tags || []).map(tag => ({\n    term_id: tag.term_id,\n    translated_name: tag.name\n  }));\n  \n  let acfFields = {};\n  if (originalPost.acf_fields && typeof originalPost.acf_fields === 'object') {\n    acfFields = originalPost.acf_fields;\n  }\n  \n  // Extract translated meta fields\n  let translatedMeta = {};\n  if (translatedData.meta_fields && typeof translatedData.meta_fields === 'object') {\n    translatedMeta = translatedData.meta_fields;\n  }\n  \n  return {\n    json: {\n      source_post_id: originalPost.id,\n      post_type: originalPost.type,\n      target_lang: targetLang,\n      title: translatedData.title || originalPost.title,\n      content: translatedData.content || originalPost.content,\n      excerpt: translatedData.excerpt || '',\n      categories: categories,\n      tags: tags,\n      acf_fields: acfFields,\n      meta_fields: translatedMeta,\n      thumbnail_id: originalPost.thumbnail_id\n    },\n    pairedItem: index\n  };\n});"
      },
      "name": "Format Translation Data1",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        3136,
        336
      ],
      "id": "b9943686-75d1-4889-8628-8f51d7ed023d"
    },
    {
      "parameters": {
        "method": "POST",
        "url": "=https://banwork.cn/wp-json/polylang-api/v1/translate-post",
        "authentication": "genericCredentialType",
        "genericAuthType": "httpBasicAuth",
        "sendBody": true,
        "specifyBody": "json",
        "jsonBody": "={\n  \"source_post_id\": {{ $json.source_post_id }},\n  \"target_lang\": \"{{ $json.target_lang }}\",\n  \"title\": {{ JSON.stringify($json.title) }},\n  \"content\": {{ JSON.stringify($json.content) }},\n  \"excerpt\": {{ JSON.stringify($json.excerpt) }},\n  \"categories\": {{ JSON.stringify($json.categories) }},\n  \"tags\": {{ JSON.stringify($json.tags) }},\n  \"acf_fields\": {{ JSON.stringify($json.acf_fields) }},\n  \"meta_fields\": {{ JSON.stringify($json.meta_fields) }},\n  \"thumbnail_id\": {{ $json.thumbnail_id }}\n}",
        "options": {}
      },
      "name": "Create Translation1",
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4.1,
      "position": [
        3552,
        336
      ],
      "id": "da6a142d-3436-43ac-978c-07e1dd9c1274",
      "credentials": {
        "httpBasicAuth": {
          "id": "nRdVHRimMjQuSFHB",
          "name": "Unnamed credential"
        }
      }
    },
    {
      "parameters": {
        "amount": 2
      },
      "name": "Rate Limit Delay1",
      "type": "n8n-nodes-base.wait",
      "typeVersion": 1.1,
      "position": [
        3760,
        336
      ],
      "id": "6a04a6e8-9c84-4337-9176-4e96b5bbd11d",
      "webhookId": "58627df9-d46d-44dc-98ac-10f6b76a3d17"
    },
    {
      "parameters": {
        "options": {}
      },
      "name": "Log Success1",
      "type": "n8n-nodes-base.set",
      "typeVersion": 3.2,
      "position": [
        3952,
        336
      ],
      "id": "38301203-d5c0-4905-94de-a19dcc7d4209"
    },
    {
      "parameters": {
        "options": {}
      },
      "name": "Workflow Complete1",
      "type": "n8n-nodes-base.set",
      "typeVersion": 3.2,
      "position": [
        4160,
        448
      ],
      "id": "4e486234-0d01-4144-8357-1cacc32e03ae"
    },
    {
      "parameters": {
        "jsCode": "const inputData = $input.first().json;\n\nreturn [\n  {\n    json: {\n      post_types: inputData.post_types,\n      // Add all your target languages here\n      target_languages: [\"es\"],\n      source_lang: \"en\"\n    }\n  }\n];"
      },
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        1072,
        448
      ],
      "id": "ac5434f8-e518-456d-930f-d67770a7d2c0",
      "name": "Set Language Config1"
    },
    {
      "parameters": {
        "jsCode": "const items = $input.all();\nconst buildRequestItems = $('Build DeepSeek Request1').all();\n\nreturn items.map((item, index) => {\n  const translatedData = item.json;\n  const originalBlocks = buildRequestItems[index].json.originalBlocks;\n  \n  // Reconstruct blocks with translated attributes\n  let reconstructedContent = translatedData.content || '';\n  \n  if (translatedData.blocks && originalBlocks.length > 0) {\n    translatedData.blocks.forEach(translatedBlock => {\n      const originalBlock = originalBlocks[translatedBlock.blockIndex];\n      if (!originalBlock) return;\n      \n      // Merge translated attributes with original structure\n      const newAttributes = JSON.stringify({\n        ...originalBlock.attributes,\n        ...translatedBlock.attributes\n      });\n      \n      // Reconstruct block comment\n      const blockComment = originalBlock.isSelfClosing \n        ? `<!-- wp:${originalBlock.blockName} ${newAttributes} /-->`\n        : `<!-- wp:${originalBlock.blockName} ${newAttributes} --><!-- /wp:${originalBlock.blockName} -->`;\n      \n      reconstructedContent += '\\n' + blockComment;\n    });\n  }\n  \n  return {\n    json: {\n      ...translatedData,\n      content: reconstructedContent\n    },\n    pairedItem: index\n  };\n});"
      },
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        3344,
        336
      ],
      "id": "bfc5ac85-d614-4b15-a3c7-ed764a474d95",
      "name": "Reconstruct Blocks"
    }
  ],
  "pinData": {
    "Split Post Types1": [
      {
        "json": {
          "post_types": "page",
          "target_languages": [
            "es"
          ]
        }
      }
    ]
  },
  "connections": {
    "Schedule Trigger1": {
      "main": [
        [
          {
            "node": "Get All Post Types1",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Get All Post Types1": {
      "main": [
        [
          {
            "node": "Set Language Config1",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Split Post Types1": {
      "main": [
        [
          {
            "node": "Split Target Languages1",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Split Target Languages1": {
      "main": [
        [
          {
            "node": "Get Untranslated Posts1",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Get Untranslated Posts1": {
      "main": [
        [
          {
            "node": "Has Untranslated Posts?1",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Has Untranslated Posts?1": {
      "main": [
        [
          {
            "node": "Split Posts1",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Workflow Complete1",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Split Posts1": {
      "main": [
        [
          {
            "node": "Get Post Content1",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Get Post Content1": {
      "main": [
        [
          {
            "node": "Process Post Data1",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Process Post Data1": {
      "main": [
        [
          {
            "node": "Build DeepSeek Request1",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Build DeepSeek Request1": {
      "main": [
        [
          {
            "node": "DeepSeek Translate1",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "DeepSeek Translate1": {
      "main": [
        [
          {
            "node": "Format Translation Data1",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Format Translation Data1": {
      "main": [
        [
          {
            "node": "Reconstruct Blocks",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Create Translation1": {
      "main": [
        [
          {
            "node": "Rate Limit Delay1",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Rate Limit Delay1": {
      "main": [
        [
          {
            "node": "Log Success1",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Log Success1": {
      "main": [
        [
          {
            "node": "Workflow Complete1",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Set Language Config1": {
      "main": [
        [
          {
            "node": "Split Post Types1",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Reconstruct Blocks": {
      "main": [
        [
          {
            "node": "Create Translation1",
            "type": "main",
            "index": 0
          }
        ]
      ]
    }
  },
  "active": false,
  "settings": {
    "executionOrder": "v1",
    "availableInMCP": false
  },
  "versionId": "ca79bb5e-4a9b-44c8-a749-eed77d978773",
  "meta": {
    "instanceId": "e116f0cdc2af85ea765f95e160256c7af2464258f43bb537f78b1a25e7dab4f3"
  },
  "id": "eF1lHl9gFO6PkAx6",
  "tags": []
}

×
n8n结合polylang和deepseek翻译流程全记录

n8n结合polylang和deepseek翻译流程全记录

首先要去创建获取的路由(get)和写入的...

查看更多

AI agent 如何翻译网站(标签+动态内容)

先让AI把网站进行i18n化, …