Contents
Multilingual (WPML / Polylang) — Plugixa Testimonial
Plugixa Testimonial integrates with WPML and Polylang out of the box. If either is active, the plugin automatically detects it and makes your testimonials, categories, and custom fields translatable. No separate setup or switch is required.
How it works (in 30 seconds)
Plugixa Testimonial uses the Translation Group pattern. Every testimonial, category, and custom field has two extra columns:
language_code—en,fr,de, etc.translation_group— a UUID shared by all translations of the same item.
Items with the same translation_group UUID are translations of each other, regardless of language. This is the same model WPML uses internally, so it plays nicely with both WPML and Polylang.
┌─────────────────────────────────────────────┐
│ wp_plugixatestimonial_testimonials │
├──┬──────────────────┬──────────┬────────────┤
│id│ client_name │ lang │ group │
├──┼──────────────────┼──────────┼────────────┤
│ 1│ Jane Doe │ en │ abc-123… │ ← English
│ 2│ Jane Doe │ fr │ abc-123… │ ← French (same group)
│ 3│ Jane Doe │ de │ abc-123… │ ← German (same group)
│ 4│ Bob Smith │ en │ def-456… │ ← different item
└──┴──────────────────┴──────────┴────────────┘
Enable / disable
Multilingual is on by default when WPML or Polylang is detected. Turn it off globally from Plugixa Testimonial → Settings → Multilingual:
| Setting | Values | Default | Effect |
|---|---|---|---|
multilingual_enabled |
auto, off |
auto |
auto = enable when WPML/Polylang is present. off = ignore language, show everything. |
Which fields are translatable
| Entity | Translatable | Shared across languages |
|---|---|---|
| Testimonials | client_name, client_role, client_company, testimonial_text, website_url |
rating, client_photo_id, video_url, status, is_featured, display_order, source, categories |
| Categories | name, slug, description |
status, display_order |
| Custom Fields PRO | field_label, placeholder |
field_key, field_type, field_options, is_required, show_in_frontend, menu_order |
“Shared” fields are copied automatically when you create a translation, so you don’t have to set the rating or photo again in every language.
Creating translations
Admin UI
Every testimonial, category, and custom field form shows a TranslationStatus panel in the sidebar with flag icons for each site language:
- Colored flag — translation exists for that language. Click to jump.
- Grey flag — missing translation. Click to open the Create Translation dialog.
The Create Translation dialog:
- Shows a language dropdown with only the remaining untranslated languages.
- Clones the source item into the new language.
- Shares the
translation_groupUUID so the items are linked. - Copies shared fields automatically.
- Opens the new translation for editing.
Row-level flags in list tables
Each row in the Testimonials, Categories, and Custom Fields lists shows compact flag indicators for all site languages. This gives you a quick visual of translation coverage across the entire dataset.
Filtering by language
Each list page has a Language filter section inside its filter drawer:
[All] [🇺🇸 EN] [🇫🇷 FR] [🇩🇪 DE]
- All — show every item regardless of language.
- Specific language — show only items matching that language code.
The selected language is persisted in localStorage (via LanguageContext) and reflected in the filter badge count, active filter chips, and reset actions.
REST API
# Get available languages
GET /wp-json/plugixatestimonial/v1/multilingual/languages
# Multilingual status
GET /wp-json/plugixatestimonial/v1/multilingual/status
# Get translations for a testimonial/category/custom-field
GET /wp-json/plugixatestimonial/v1/multilingual/{entity}/{id}/translations
# Create a translation (clones the source)
POST /wp-json/plugixatestimonial/v1/multilingual/{entity}/{id}/translations
{ "language": "fr" }
# Link two existing items as translations
PUT /wp-json/plugixatestimonial/v1/multilingual/{entity}/link-translations
{ "source_id": 5, "target_id": 12 }
# Unlink a translation
DELETE /wp-json/plugixatestimonial/v1/multilingual/{entity}/{id}/translations/{lang}
Where {entity} is testimonials, categories, or custom-fields.
Frontend behavior
Automatic language detection
On the frontend, the shortcode detects the current language from:
- WPML — via WPML API hooks.
- Polylang —
pll_current_language(), with?lang=query parameter as an override. - Fallback — the site’s default language.
Only testimonials matching the current language are rendered.
Forcing a language in a shortcode
You can force a language regardless of WPML/Polylang state:
[plugixa_testimonials language="fr"]
Useful for:
- Building a language-specific landing page without setting up full page translation.
- Embedding testimonials in a single language from a language picker.
Frontend submission passthrough
When a visitor submits a testimonial via [plugixa_testimonial_form]:
- The form’s JS reads the current site language from the page.
- Sends it as
language_codein the submit payload. - The new testimonial is created with that language_code.
No UI change needed — the passthrough is automatic. See Submission Form.
Where language is stored
Database columns
Three tables have these columns:
language_code VARCHAR(10) -- e.g., 'en', 'fr'
translation_group VARCHAR(36) -- UUID v4
Indexed on both columns for fast filtering.
Backfill for existing installs
When you activate a multilingual-aware version:
- The activator adds
language_codeandtranslation_groupcolumns if missing. Activator::maybe_backfill_null_language_codes()backfills every existing row with:language_code= site’s default language (reads Polylangdefault_langdirectly fromwp_optionsfor reliable early-load detection).translation_group= a new UUID (unique per item).
- The backfill runs once on admin init.
If the backfill fails or you need to re-run it, deactivate and reactivate the plugin.
WPML configuration
The plugin ships with a wpml-config.xml file that registers shortcode attributes as translatable content. WPML String Translation can then translate inline shortcode strings (like custom button_text values) automatically.
Usage in custom code
Check if multilingual is active
if ( \PlugixaTestimonial\Multilingual::instance()->is_multilingual_active() ) {
// WPML or Polylang is installed AND the feature isn't disabled
}
Get the current language
$lang = \PlugixaTestimonial\Multilingual::instance()->get_current_language();
// → 'en', 'fr', etc.
Build a language-aware WHERE clause
$ml = \PlugixaTestimonial\Multilingual::instance();
$current = $ml->get_current_language();
$default = $ml->get_default_language();
[ $where_sql, $where_values ] = $ml->build_language_where(
't', // table alias
'plugixatestimonial_testimonials', // logical table
$current,
$default
);
$sql = "SELECT * FROM {$wpdb->prefix}plugixatestimonial_testimonials t WHERE 1=1 $where_sql";
$testimonials = $wpdb->get_results( $wpdb->prepare( $sql, ...$where_values ) );
Link two items as translations
\PlugixaTestimonial\Multilingual::instance()->link_translations(
'plugixatestimonial_testimonials',
$source_id,
$target_id
);
Hooks
| Hook | Type | Description |
|---|---|---|
plugixatestimonial_multilingual_active |
filter | Override auto-detection (return bool). |
plugixatestimonial_current_language |
filter | Override the detected current language. |
plugixatestimonial_available_languages |
filter | Modify the languages array. |
plugixatestimonial_language_where |
filter | Modify the WHERE clause fragment. |
plugixatestimonial_before_link_translations |
action | Before linking two items. |
plugixatestimonial_after_link_translations |
action | After linking. |
Troubleshooting
I see the same testimonial in every language
- Check that the testimonial has a non-empty
language_code. Empty = “belongs to every language” (legacy behavior).
Translation not showing up on the frontend
- Confirm the testimonial has
status = approvedin every language (each translation has its own status). - Confirm the
language_codematches the URL language (e.g.,frnotfr_FR). - Check whether the testimonial’s categories are also translated — a translated testimonial with only English categories may be filtered out by a translated category filter.
My existing testimonials have no language
Deactivate and reactivate the plugin. The activator re-runs maybe_backfill_null_language_codes() which sets every NULL-language row to the site default language.