Skip to content

Instantly share code, notes, and snippets.

@hirasso
Last active June 25, 2025 14:29
Show Gist options
  • Save hirasso/55ece097189fa24aca026323e462f692 to your computer and use it in GitHub Desktop.
Save hirasso/55ece097189fa24aca026323e462f692 to your computer and use it in GitHub Desktop.
Polylang UI Improvements
<?php
/*
* Copyright (c) Rasso Hilber
* https://rassohilber.com
*/
namespace Hirasso\WP;
/**
* Improvements of the Polylang admin UI, that helps my customers to better understand
* what they are doing. Can be required from a theme or plugin and then instanciated via
* new \Hirasso\WP\PolylangUI()
*/
final class PolylangUI
{
protected ?\WP_Post $post = null;
protected ?\PLL_Language $postLanguage = null;
public function __construct()
{
add_action('admin_head', [$this, 'on_admin_head']);
add_action('admin_bar_menu', [$this, 'add_post_language_switcher'], 999);
add_filter('acf/prepare_field', [$this, 'acf_prepare_field'], PHP_INT_MAX - 100);
}
/**
* Detect if we are currently on the post edit screen for a translatable post type
*/
public function on_admin_head()
{
if (get_current_screen()?->base !== 'post') {
return;
}
if (!$post = get_post()) {
return;
}
if (!pll_is_translated_post_type(get_post_type($post))) {
return;
}
if (!$postLanguage = pll_get_post_language($post->ID, \OBJECT)) {
return;
}
$this->post = $post;
$this->postLanguage = $postLanguage;
add_filter('pll_admin_languages_filter', fn () => []);
}
/**
* Add a post specific language switcher on admin post edit pages, instead of the default
* global Polylang language switcher
*/
public function add_post_language_switcher(\WP_Admin_Bar $wpAdminBar): void
{
$post = $this->post;
$postLanguage = $this->postLanguage;
if (!$post || !$postLanguage) {
return;
}
$parentNodeID = 'languages';
// Main node (parent item)
$wpAdminBar->add_node([
'id' => $parentNodeID,
'title' => sprintf(
'%s %s: %s',
$postLanguage->flag,
__('Post Language'),
$postLanguage->name
),
'meta' => ['class' => 'pll-filtered-languages']
]);
$translations = pll_get_post_translations($post->ID) ?: [];
if (count($translations) < 2) {
return;
}
foreach ($translations as $lang => $translatedPostID) {
if ($translatedPostID === $post->ID) {
continue;
}
$editLink = get_edit_post_link($translatedPostID);
$langObject = PLL()->model->get_language($lang);
$wpAdminBar->add_node([
'id' => 'post-language-' . $lang,
'title' => $langObject->flag . ' ' . $langObject->name,
'href' => $editLink,
'parent' => $parentNodeID,
]);
}
}
/**
* Move the polylang translation status in the admin area from a field's instructions
* to an icon in the field's label, with a (ACF-native) tooltip that
* displays the field's translation status.
*
* Currently depending on preg_replace until
* WP_Syntex\Polylang_Pro\Integrations\ACF\Translation_Instructions::get_field_instruction()
* becomes `public`.
*/
public function acf_prepare_field(?array $field): ?array
{
if (empty($field)) {
return null;
}
$pllIcon = '<span style="font-size: 1.2em; vertical-align: middle;" class="dashicons dashicons-translation"></span>';
if (!preg_match(
"#{$pllIcon}\s(?<translationStatus>.*)#",
$field['instructions'] ?? '',
$matches
)) {
return $field;
}
/** remove the instructions from the $field */
$field['instructions'] = str_replace($matches[0], '', $field['instructions']);
$translationStatus = preg_replace('/This field is (.*)\./', '$1', $matches['translationStatus']);
$field['label'] = $field['label'] . " <span title='$translationStatus' style='font-size: 1.2em; vertical-align: middle; opacity: 0.7;' class='dashicons dashicons-translation acf-js-tooltip '></span>";
return $field;
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment