In a previous article, we discussed how to enable multi-language support on a website using Polylang, and described how to automate multi-language translation of articles using n8n:
After spending some time using the plugin, I found that it fairly well met my needs. However, I discovered a significant limitation: the free version of the Polylang plugin does not support API usage, which severely impacted the user experience. For instance, the following scenarios could not be automated:
- It was impossible to retrieve the language status of articles. As a result, the webhook would create new articles by translating ones that were already in English, leading to an infinite loop.
- Automatic association of multi-language articles was not possible. Thus, there was no link between Chinese and English articles on the site, preventing automatic switching of the language for internal pages.
Considering that API support is a strong requirement for me and that the Pro version of Polylang does not offer a reasonable price-to-value ratio for my needs (it costs €99 for a single site), I decided to add the necessary functionality myself.
Understanding the Plugin
I am relatively familiar with PHP and WordPress, but I prefer not to customize and develop through complex code reading. So, I thought about leveraging AI assistance: I provide the ideas and AI helps me accomplish the requirements. Here are the relevant records:
Database Interaction
My first assumption was that the plugin must store relevant information in the database, which is related to the language type of the articles and their mapping relationships:
Based on that, I searched for relevant code in the Polylang plugin and ultimately found the pll_save_post
function and the related code:
I discovered that GPT-4o recognized this plugin and accurately indicated the purpose of the relevant function, so I directly asked about the data storage result:
After obtaining the answer, I validated it on the server. I found the keyword post_translations
, which serialized and saved the relevant mapping relationships between multi-language articles:
Identifying Key Functions
Based on the tips from GPT-4o:
I searched the source code for relevant keywords and found the variable names in the program:
After learning the variable names, I looked for corresponding method calls. After testing, I found that save_translation
was quite relevant in both name and function for practical scenarios:
After locating the function, I searched to see if there were any higher-level function calls, ultimately finding a call in the api.php
file. The function name pll_save_post_translations
fits our needs and has been verified to work:
Calling the Function
After identifying and verifying the function, the next step was to use it. Considering that the Polylang plugin may be updated in the future, I tried not to edit the original plugin. Therefore, I added the following code to the end of my theme’s functions.php
file to implement the functionality I needed.
In the code below, key functions worth mentioning are add_action
and add_filter
. These are built-in WordPress functions that allow us to edit the REST API, including adding endpoints or intercepting and editing request/response data (if needed, you can also seek AI assistance while writing).
// Adding custom API based on Polylang
add_action('rest_api_init', 'register_get_language');
function register_get_language() {
register_rest_route('pl', '/language', array(
'methods' => 'GET',
'callback' => 'get_language_by_post_id',
'args' => array(
'id' => array(
'required' => true,
'validate_callback' => function($param, $request, $key) {
return is_numeric($param);
}
),
),
'permission_callback' => '__return_true'
));
}
function get_language_by_post_id($request) {
// Get the 'id' parameter from the request
$post_id = $request->get_param('id');
// Validate the ID
if (empty($post_id) || !is_numeric($post_id)) {
return new WP_REST_Response(array(
'success' => false,
'message' => 'Invalid post ID.',
), 400); // Return 400 error
}
// Check if Polylang functions are available
if (!function_exists('pll_get_post_translations')) {
return new WP_REST_Response(array(
'success' => false,
'message' => 'Polylang plugin is not active or not installed.',
), 500); // Return 500 error
}
// Get the language information for the specified post
$languages = pll_get_post_translations($post_id);
// If no language information is found
if (empty($languages)) {
return new WP_REST_Response(array(
'success' => false,
'message' => 'No language information found for the given post ID.',
), 404); // Return 404 error
}
// Return the language information
return new WP_REST_Response(array(
'success' => true,
'language' => $languages,
), 200); // Return success with 200
}
// Set up language
add_filter('rest_pre_echo_response', 'save_post_translations', 10, 3);
function save_post_translations($response, $server, $request) {
// Check if the request is for /wp/v2/posts
if ($request->get_route() === '/wp/v2/posts') {
$from_id = $request->get_param('from_id');
if ($from_id) {
// Check if Polylang functions are available
if (!function_exists('pll_save_post_translations')) {
return new WP_REST_Response(array(
'success' => false,
'message' => 'Polylang plugin is not active or not installed.',
), 500); // Return 500 error
}
// Check the submitted post language format
$languages = pll_get_post_language($response['id']);
pll_save_post_translations(array(
'zh' => $from_id,
$languages => $response['id']
));
}
}
return $response;
}
Results
In the code above, we added two API endpoints to support our functionality, specifically /wp-json/pl/language?id={id}
and /wp-json/wp/v2/posts?from_id={id}
.
Get Article Language List /wp-json/pl/language?id={id}
When the article ID is provided, the server returns a list of multi-language article IDs associated with that ID, which can be used to determine whether translation is needed:
When all the multi-languages for an article are configured, the next action is not required:
Create Article and Associate Language Relationship /wp-json/wp/v2/posts?from_id={id}
When creating an article using the API, if the original article ID is included, it automatically binds the language relationship between the two:
Leave a Reply