Cybo Advanced Field EngineCyboweb Technologies · Documentation · v1.0.2

WPML (multilingual sites)

Status (0.8.0): built on WPML's documented public hooks. Tested with WPML 5.0.2 and WPML String Translation on a single site (Dutch default, English second language): posts, terms, attachments, all four translation modes, edit screens, Settings Pages per language, translated field labels and bulk loading. Not tested yet: WPML's Advanced Translation Editor, the WPML Media add-on, and WPML on multisite. Run the manual test on your own WPML site before relying on it in production.

CAFE works without WPML exactly as before. When WPML is active and set up, CAFE adds one thing: each top-level field gets a Translation mode that decides how it behaves in other languages. Your theme code does not change:

cafe_get_field( 'property_title', $post_id ); // the value in the right language
cafe_get_option( 'phone' );                   // the Settings Page value for the current language

There are no per-language fields (property_title_nl). A field is defined once; WPML's translated posts and terms carry the values.

Installation and detection

  1. Install and set up WPML (a default language must be chosen).
  2. Keep CAFE active. Nothing else is needed.

On every request CAFE checks whether WPML is loaded (ICL_SITEPRESS_VERSION, the wpml_current_language hook) and has a default language. If not, CAFE does nothing multilingual and loads no WPML code: no errors when WPML is deactivated, upgraded or not configured yet.

Where you see it in CAFE

  • CAFE → Settings → Compatibility: WPML with its status (Active, Needs setup or Not activated), its version, WPML String Translation's version, and a link to WPML's languages screen.
  • CAFE → Settings → Multilingual (WPML): shown when WPML is installed. It says whether WPML is active or still needs its setup wizard, the WPML version, whether String Translation is active (with a link to CAFE's field labels in WPML → String Translation), the site's languages with the default marked, and what the four translation modes do.
  • Field group builder: everything about languages is on each field's Translation tab: the translation mode (radio buttons: Do not translate, Copy, Copy once, Translate; sub-fields: Same as parent, Copy (locked)), then the label per language. Fields not set to Translate show a badge in the field list.
  • Edit screens of a translation: Copy fields are locked: read-only, with a lock icon after the label (and after the labels of their sub-fields); hover the field to see why. Translate and Copy once fields are unlocked. Do not translate fields are hidden. Settings Pages say which language you are editing.

Translation modes

Set per field in CAFE → Field Groups → field → Translation (the tab appears when WPML is active). Only top-level fields have a mode; sub-fields of a Field Set, Collection or Section Builder follow their top-level field.

Mode Value on a translation On the translation's edit screen
Translate (default) Its own value. Empty until a translator fills it in. Editable
Copy (locked) Always the original's value. Changing the original updates every language at once. Read-only, lock icon after the label; hover: "Copied from the original (English)"
Copy once The original's value until the translation saves its own; from then on independent. Editable, prefilled with the original's value
Do not translate Always the original's value. Hidden

Details:

  • The original is the post or term the translation was made from (WPML's original element in the translation group).
  • Copy / Do not translate values are stored once, on the original. They are read from there at request time; nothing is duplicated into translations. Saving such a field through a translation with cafe_update_field() writes the original's value. The edit screen never does this: read-only and hidden fields are not saved from a translation's form, even if their value is posted.
  • Copy once needs no WPML event: the translation simply shows the original's value until it has its own. Collections and Section Builders shown this way are saved as the translation's own new rows; the original's rows are never touched.
  • Missing translation value (Translate): empty, or the field's default value, like any empty field. There is no automatic fallback to the original; use Copy once for that behaviour.

Example setup for a property:

Field Mode
Title, Description Translate
Price, SKU Copy
Hero image, Gallery Translate or Copy (see Media)
Internal ID Do not translate
Layout structure (Section Builder) Copy, or Translate for fully localised pages

Sub-fields: same as parent, or locked

Inside a Collection, Section Builder or Field Set, each sub-field chooses on its Translation tab:

  • Same as parent (default): follows the top-level field. In a translated Section Builder the sub-field is translated too.
  • Copy (locked): on a translation the sub-field always shows the value of the same item on the original page (same position, same section kind), read-only with a lock icon. Typical use: images and galleries inside translated sections, where only the texts change per language.

An item that only exists in the translation (e.g. a third section when the original has two) reads a locked sub-field as empty. Locked sub-fields are not required on translations (the value comes from the original). Works for translated posts and terms; on Settings Pages sub-fields follow their parent.

Settings Pages

A Settings Page is one site-wide set of values, so languages are kept as scopes:

Mode Default language Other language (WPML admin bar language)
Translate Shared scope Its own scope (empty until saved)
Copy Shared scope Shared scope, read-only on the page
Copy once Shared scope The default language's value until saved in this language
Do not translate Shared scope Shared scope, hidden on the page

Switch the language in the admin bar to edit a Settings Page in that language; a notice on the page says which language is being edited. cafe_get_option( 'email' ) returns the value for the current request language.

Collections and Section Builders

The top-level mode applies to the whole structure (rows, sections and all sub-fields):

  • Translate: each language has its own items / sections.
  • Copy: every language shows the original's items; edit them on the original.
  • Copy once: a translation starts with the original's items and becomes independent on its first save.

Inside those items, a sub-field can be locked on its own (Translation tab: Copy (locked)): it then always shows the value of the same item of the original: same position, same section kind, at any depth. That is how a translated text and a shared image can live in one item. See Sub-fields: same as parent, or locked.

CAFE stores attachment IDs. When a value is shown, CAFE asks WPML (wpml_object_id) for the attachment's translation in the current language and uses it when it exists (WPML Media Translation), otherwise the stored attachment. This applies to Image, File, Video and every Gallery image, in every mode. The stored ID never changes and no media file is duplicated.

  • Translated image: translate the attachment in WPML Media; CAFE shows it automatically.
  • Copied gallery: set the Gallery to Copy; the order and choice of images stay the original's, each image shown in the current language when translated.
  • Missing translated media: the original attachment is shown.

Field labels

The main labels (and group title) are the default language's texts. The builder shows a box for every other language; every language shows the main labels until you type a translation. There are two ways to translate what editors see; field names and keys never change, so theme code stays the same.

In the CAFE builder (easiest). With WPML active:

  • Group title: Settings card → Presentation → Title per language (default Nederlands "Homepagina" → English: "Home Page").
  • Field, tab or collapsible label and instructions: open the field → Translation tab → Label per language.
  • Section names (Section Builder): open the section → Section name per language.

Empty boxes use the main text. Edit screens show the text of the language you are editing (the WPML language switcher in the admin bar).

The texts are stored in the group's and field's own settings (title_translations, label_translations), which CAFE already compiles and caches: no extra table, no extra query, and nothing is loaded on the front end. They are exported and imported with the field group.

In WPML String Translation. Group titles, labels and instructions are also registered there (context CAFE Field Groups) when a group is saved. A text typed in the CAFE builder wins over String Translation. Groups saved before WPML was activated are registered the next time you save them.

Language names in CAFE notices are WPML's native names ("Nederlands", "English"), so they read the same whichever language you are editing.

REST

CAFE has no public REST route for field values yet. WordPress REST collections run in WPML's request language, and values read in register_rest_field() callbacks follow the same rules as cafe_get_field().

Import and export

The translation mode is a normal field setting ("translation": "copy"): exported and imported with the field group, validated (unknown modes are rejected), and kept when WPML is not installed. Values are never exported.

Object types

Object Supported Reason
Posts, pages, custom post types Yes WPML translation groups
Taxonomy terms Yes WPML translation groups (by term_taxonomy_id)
Settings Pages Yes Language scopes
Users Unsupported WPML does not give users a language; values are shared
Comments Unsupported No WPML translation relationship for comments

Multisite

Every site has its own CAFE tables, field settings, Settings Page language scopes and caches, and its own WPML setup. Nothing is shared between sites.

Hooks

cafe_language_provider lets another plugin supply languages instead of WPML. The rules use the general CAFE hooks cafe_value_context, cafe_field_view, cafe_prime_object_ids and cafe_display_object_id. See Hooks.

Limitations

  • Tested with WPML 5.0.2 on a single site; see the status note for what was not tested.
  • No per-sub-field modes inside Collections / Section Builders (see above).
  • Translate mode does not fall back to the original when empty (use Copy once).
  • Users and comments are not translated.
  • WPML's own translation editor (ATE / translation jobs) is not wired to CAFE fields yet: translators edit CAFE fields on the translation's edit screen.

Manual test

On a site with WPML (English default, plus Dutch and German):

  1. Create a field group on a custom post type "property" with title (Translate), description (Translate), price (Copy), sku (Copy), hero_image (Translate), gallery (Copy), internal_id (Do not translate) and a Collection team_members (Copy once) with name and position.
  2. Create an English property and fill everything. Translate it to Dutch with the WordPress editor ("+" in the Languages box).
  3. On the Dutch edit screen check: price / sku / gallery read-only with the note, internal_id hidden, team_members prefilled. Fill title and description in Dutch and save.
  4. On the front end in each language check cafe_get_field() for every field. Change the English price, and check that it changed in Dutch too.
  5. Settings Page "Contact" with phone (Copy), email (Translate), address (Copy once): edit it in English, then switch the admin bar to Dutch and edit again. Check cafe_get_option() on the front end per language.
  6. Translate one image in WPML Media and check that the Dutch page shows it.
  7. Loop over 100 Dutch properties with WP_Query (Query Monitor installed): CAFE should run a constant number of queries, not one per property.
  8. Report anything unexpected with the steps to reproduce.

Troubleshooting

  • No translation choice in the builder: WPML is not active or has no default language; reload the builder after finishing WPML's setup wizard.
  • Translation shows the English value: the field is set to Copy, Copy once (and not translated yet) or Do not translate.
  • Dutch Settings Page is empty: Translate fields start empty per language; use Copy once to start from the default language.