Aleš Sýkora / December 6, 2023 / 3 comments

How to Translate RSS Feeds with TranslatePress

Post summary: One of the challenges I found when using the TranslatePress plugin with Oxygen builder and Bricks builder is the translation of RSS feeds, which is not supported. Supported from TranslatePress – Multilingual (2.6.7+). EDIT: 06.12.2023 – The TranslatePress now support the RSS feeds, but only Titles are being translated, so this code works better. This…

One of the challenges I found when using the TranslatePress plugin with Oxygen builder and Bricks builder is the translation of RSS feeds, which is not supported. Supported from TranslatePress – Multilingual (2.6.7+).

This limitation becomes apparent when attempting to automate the distribution of multilingual content, such as sending newsletters in different languages through platforms like Mailchimp.

The Challenge with TranslatePress and RSS Feeds

TranslatePress simplifies the process of translating web content on WordPress sites (especially with automatic DeepL translation integration). However, it does not support the direct translation of RSS feeds. This poses a problem for website owners who wish to share their content in multiple languages through RSS, as the feeds generated by WordPress will only reflect the primary language of the site. As a result, the potential of reaching a broader, international audience through automated multilingual newsletters remains untapped.

Leveraging WordPress REST API V2 for Custom RSS Feeds

When trying many solutions I finally found the working one – utilizing the WordPress REST API V2. This approach involves creating a custom RSS feeds by fetching posts in different languages via the REST API and then formatting them into a valid RSS feed.

Create custom RSS feed from WP REST API V2 Explained

Here’s a breakdown of mine final PHP snippet used to create a custom RSS feed:

  1. REST API Route Registration: The code begins by registering a new REST API route with WordPress. This route is designed to respond to GET requests and generate an RSS feed.
  2. Fetching Posts: When the custom REST API endpoint is accessed, the code fetches posts in the specified language from the WordPress site. This is accomplished by constructing a URL that points to the WordPress REST API V2, appending the desired language parameter.
  3. Generating RSS Feed: The posts fetched from the REST API are then used to build an RSS feed. This is done using PHP’s SimpleXMLElement class, which allows for the creation of well-formed XML structures – in this case, an RSS 2.0 compliant feed.
  4. Adding atom:link for Feed Validation: To ensure broader compatibility with various feed readers and to adhere to RSS best practices, an atom:link element with a rel="self" attribute is added. This element points to the feed itself, a requirement for some validators and readers.
  5. Outputting the Feed: Finally, the script sets the correct Content-Type header for RSS (application/rss+xml) and outputs the XML, effectively serving a dynamically generated, multilingual RSS feed.

Feel free to use this code in your code snippets plugin. I use it in Advanced Scripts.

<?php
/*
Plugin Name: Custom RSS feeds for TranslatePress
Description: Create a custom RSS feed of posts accessible via REST API endpoint.
Version: 1.0
Author: Aleš Sýkora | Great-tit.com
*/

function generate_custom_rss_feed( WP_REST_Request $request ) {
    $language = $request['language'] ? $request['language'] : 'en';
    $feed_url = 'https://your-website.com/' . $language . '/wp-json/wp/v2/posts'; // Change to your site URL

    // URL for atom:link
    $selfLink = 'https://your-website.com/wp-json/custom-rss/v2/feed/' . $language; //Change to your site URL

    $response = wp_remote_get( $feed_url );
    if ( is_wp_error( $response ) ) {
        header('HTTP/1.1 404 Not Found');
        echo 'Unable to retrieve posts';
        exit;
    }

    $posts = json_decode( wp_remote_retrieve_body( $response ) );
    if ( empty( $posts ) ) {
        header('HTTP/1.1 404 Not Found');
        echo 'No posts found';
        exit;
    }

    $rss_feed = new SimpleXMLElement('<?xml version="1.0" encoding="UTF-8"?><rss version="2.0" xmlns_atom="http://www.w3.org/2005/Atom"></rss>');
    $channel = $rss_feed->addChild('channel');
    $channel->addChild('title', 'Feed Title'); // Change RSS feed title
    $channel->addChild('link', 'https://your-website.com'); // Change to your site URL
    $channel->addChild('description', 'Feed description'); // Change feed description

    // Add atom:link element
    $atom_link = $channel->addChild('atom:link', '', 'http://www.w3.org/2005/Atom');
    $atom_link->addAttribute('href', $selfLink);
    $atom_link->addAttribute('rel', 'self');
    $atom_link->addAttribute('type', 'application/rss+xml');

    foreach ($posts as $post) {
        $item = $channel->addChild('item');
        $item->addChild('title', esc_html($post->title->rendered));
        $item->addChild('link', esc_url($post->link));
        $item->addChild('pubDate', date(DATE_RSS, strtotime($post->date_gmt)));
        $item->addChild('guid', esc_url($post->link));
        $item->guid->addAttribute('isPermaLink', 'false');

        $description = $item->addChild('description');
        $node = dom_import_simplexml($description);
        $no = $node->ownerDocument;
        $node->appendChild($no->createCDATASection($post->excerpt->rendered));
    }

    header('Content-Type: application/rss+xml; charset=UTF-8');
    echo $rss_feed->asXML();
    exit;
}

function register_custom_rss_route() {
    register_rest_route('custom-rss/v2', '/feed/(?P<language>[a-zA-Z-]+)', array(
        'methods' => 'GET',
        'callback' => 'generate_custom_rss_feed',
        'args' => array(
            'language' => array(
                'validate_callback' => function( $param, $request, $key ) {
                    return is_string( $param );
                }
            ),
        ),
        'permission_callback' => '__return_true'
    ));
}

add_action('rest_api_init', 'register_custom_rss_route');

Change the number of posts generated in feed

If you want to change the number of posts manually, instead of number set up in WordPress Admin > Settings > Reading, you need to adjust this line:

$feed_url = 'https://your-website.com/' . $language . '/wp-json/wp/v2/posts?per_page=20'; // Change to your site URL and desired number of posts

the ?per_page=20 parameter is the key :-).

Conclusion

While TranslatePress effectively translates website content, it’s limitation in translating RSS feeds can be overcome through a custom solution using the WordPress REST API V2.

If you use this code, it has no warranty. If it works, please, buy me a beer to make more free snippets and plugins.

Fuel my passion for writing with a beer🍺

Your support not only makes me drunk but also greatly motivates me to continue creating content that helps. Cheers to more discoveries and shared success. 🍻

3 comments

  • Hi,

    Nice workaround for translating RSS Feeds with TranslatePress.

    There is support now for translating title, content, and excerpt in RSS Feeds. Just make sure to have the latest version of TranslatePress – Multilingual (2.6.7+).

    Cheers!

    • Gonzalo

      Hi!!
      I have a webpage using translate press and we are not able to translate it, also i cannot found any option for translating the wordpress feed, it seems that fields inside CDATA tags are not translated. Is thare any post/manual for translating feeds with translate press?

      • A

        Hello Gonzalo. If you use my code + TranslatePress automatic translation, then feeds are translated completely. Take a look here: bluedynamic en feed. This site is in Czech language and other language feeds are created with this snippet.

Share Your Thoughts