HI I am working on a creating of a plugin where site is built using child of a classic theme In my site i am already using polylang (free) version now i tried to build a plugin
where I need to Ensure all French translations from the theme .po files are imported into the Polylang database automatically
Current status:I Manual conversion of _()/e() to pll()/pll_e() in the theme is done and working.
So Now i my requirement is to Create a one-time plugin to import existing .po translations into Polylang GUI/database.
The Purpose of it is to Avoid manual entry errors, reduce deployment downtime, and ensure translations work correctly in live/production.
Scope: Use existing .pot, .po, .mo files from the theme. No additional files are required. in my wp-contents/Language contain the theme and plugin folders where the all .po, .mo files are present
so on enabling this plugin all French translations must be verified and confirmed functional before task completion.
Also English strings may appear on French pages if the import is not done properly. The plugin is single-use, not continuous.
Requirement:
Create a one-time plugin that imports all French translations from theme .po files into Polylang’s database.
Site: child of a classic theme
Polylang (free) active
Manual conversion of _() / e() → pll() / pll_e() is already done
Goal: avoid manual errors, ensure translations work correctly in production
Plugin draft (current code):
<?php
/**
* Plugin Name: Polylang French Translation Importer - DEBUG
* Description: Debug version to import French translations from .po files into Polylang database
* Version: 1.3
* Author: Your Name
*/
if (!defined('ABSPATH')) exit;
class PolylangFrenchImporter {
private $import_path;
public function __construct() {
$this->import_path = WP_CONTENT_DIR . '/languages/themes/';
add_action('admin_menu', [$this, 'add_admin_page']);
}
public function add_admin_page() {
add_management_page(
'Polylang Import',
'Polylang Import',
'manage_options',
'polylang-import',
[$this, 'render_import_page']
);
}
public function render_import_page() {
if (isset($_POST['run_import'])) {
$this->run_import();
}
echo '<div class="wrap"><h1>Polylang PO Import Debug</h1>';
echo '<form method="post">';
submit_button('Run Import', 'primary', 'run_import');
echo '</form></div>';
}
private function run_import() {
if (!function_exists('pll_get_the_language')) {
echo '<p style="color:red;">Polylang not active!</p>';
return;
}
require_once ABSPATH . 'wp-admin/includes/translation-install.php';
$files = glob($this->import_path . '*.po');
if (!$files) {
echo '<p style="color:red;">No .po files found in: ' . esc_html($this->import_path) . '</p>';
return;
}
foreach ($files as $file) {
$locale = basename($file, '.po');
echo '<h3>Processing: ' . esc_html($locale) . '</h3>';
$translations = $this->parse_po_file($file);
if (!$translations) {
echo '<p style="color:red;">Failed to load PO file or no translations found.</p>';
continue;
}
global $wpdb;
$table = $wpdb->prefix . 'polylang_strings';
$count = 0;
foreach ($translations as $msgid => $msgstr) {
if (!$msgid || !$msgstr) continue;
$wpdb->insert($table, [
'string' => $msgid,
'context' => 'theme',
'name' => md5($msgid),
'value' => $msgstr,
'group' => 'po-import',
'multiline' => 0,
]);
$count++;
}
echo '<p style="color:green;">Imported ' . intval($count) . ' strings from ' . esc_html($file) . '</p>';
}
}
private function parse_po_file($file) {
if (!class_exists('PO')) {
require_once ABSPATH . 'wp-includes/pomo/po.php';
}
$po = new PO();
$loaded = $po->import_from_file($file);
if (!$loaded) {
error_log('Failed to load PO file: ' . $file);
return false;
}
$translations = [];
foreach ($po->entries as $entry) {
if (!empty($entry->translations[0])) {
$translations[$entry->singular] = $entry->translations[0];
}
}
error_log('Parsed ' . count($translations) . ' strings from ' . $file);
return $translations;
}
}
new PolylangFrenchImporter();
Error while testing:
PO files are detected (fr_CA.po, en_CA.po) ✅
But import_from_file fails → Failed to load PO file ❌
Results: Strings Found = 0, Strings Imported = 0
Debug log shows PO files exist but cannot be parsed