This article covers how WPEstate Translate links the language versions of a post or term. It documents the metadata that holds the links, the relationship API that reads and writes them, and the legacy trid table that older sites may still use. For the plain-language version see How WPEstate Translate Links Translations Together.
The Family Model
A translation family has one canonical (source) object and up to one member per language. Every member is a normal post or term. The relationship module (includes/translation-relationships*.php) reads a family into this shape:
array(
'canonical_id' => 101,
'source_language' => 'en',
'members' => array( 'en' => 101, 'fr' => 202, 'es' => 303 ),
);
- The canonical object is always in
membersunder its source language. - All members share the post type, or the taxonomy for terms.
- A member belongs to one family and represents one language.
- The canonical object never changes. Linking writes the full family again under a MySQL
GET_LOCKon the source, so two concurrent Add requests cannot overwrite each other.
Post Metadata
| Key | Stored on | Value |
|---|---|---|
_wpr_language |
Every post | Language code. An empty value means the default language. Activation stamps an empty value on existing posts that lack the key. |
_wpr_translations |
The canonical post | Array of language => post_id for the family. |
wpr_translated_original_post_id |
Each translation | ID of the canonical post. |
wpr_translated_language |
Each translation | Language code, the same as _wpr_language. |
Writes happen in wpr_translate_relationship_write_post_family() (translation-relationships-writes.php). If any single update fails, all touched keys are restored from a snapshot and a WP_Error is returned. When a family is read, the keys are cross-checked. A mismatch, such as a member whose back-pointer names another source, returns post_metadata_malformed instead of a guessed family.
Term Metadata
| Key | Stored on | Value |
|---|---|---|
wpr_term_language |
Every translated term | Language code. |
wpr_translated_term_{lang} |
The canonical term | Term ID of the member in {lang}, one key per language. |
wpr_original_term_id |
Each translated term | Term ID of the canonical term. |
Relationship API
| Function | Purpose |
|---|---|
wpr_translate_relationship_get_post_family( $post_id ) |
Family from any member. Cached for the request and cleared on any post or term meta change. Returns array or WP_Error. |
wpr_translate_relationship_get_term_family( $term_id, $taxonomy ) |
The same for terms. |
wpr_translate_relationship_resolve_post( $post_id, $language, $strict, $availability ) |
Member ID for a language. With $strict = false it falls back to the canonical post. 'frontend' accepts only posts with a public status in a public language. 'admin' accepts anything not in the trash. |
wpr_translate_relationship_resolve_term( $term_id, $taxonomy, $language, $strict, $availability ) |
The same for terms. |
wpr_translate_relationship_link_post( $source_id, $source_lang, $member_id, $member_lang ) |
Adds a member. Both languages must be active and different, and the member must not belong to another family. |
wpr_translate_relationship_link_term( … , $taxonomy ) and wpr_translate_relationship_unlink_term( $term_id, $taxonomy ) |
Link or detach a term. The term edit screen calls them from the Source term field. |
Every failure is logged to debug.log with object IDs only, prefixed [wpr-translate relationships].
The Legacy trid Table
Versions before the metadata model linked objects in {$wpdb->prefix}wpestate_translation_translations, one row per object with a shared trid. The table is still created on activation (activator.php) and still read:
- When an object has no
_wpr_translationsmap and no back-pointer,wpr_translate_relationship_read_legacy_family()loads its family from the table (translation-relationships-legacy.php). - The next link write on that family saves it to metadata. Nothing writes new rows:
wpr_translation_insert_translation_record()in post-list-actions.php has no callers in 1.0.9. - Element types are
post_{post_type}andtax_{taxonomy}. Usewpr_translation_get_element_type()andwpr_translation_get_taxonomy_element_type(). - The
needs_update,translator_idandpost_statuscolumns are legacy. No screen displaysneeds_update, and the functions that set it have no callers.
Reset Translation Settings truncates this table. Families that exist only there lose their links. Uninstall does not drop it.
Compatibility Helpers
Older helper names still work and now delegate to the relationship module:
| Function | Now |
|---|---|
wpr_translate_get_sibling_map( $post_id ) |
Returns translations and source_language from the post family. |
wpr_translation_get_translation_group( $post_id, $element_type ) |
Reads post metadata first and falls back to the table. Terms go straight to the table. Returns trid, translations, source_language, and an always-empty needs_update for metadata families. |
wpr_translation_resolve_post_language_code( $post_id, $post_type ) |
The post’s language from its family. |
wpr_translate_resolve_translated_post_id( $post_id, $language ) |
Wrapper for wpr_translate_relationship_resolve_post( $post_id, $language, false, 'admin' ). |
wpr_translation_generate_trid() |
Still defined, no callers. |
Deletion Rules
translation-relationships-lifecycle.php keeps families consistent:
- Translated post deleted permanently: removed from the source
_wpr_translationsmap. - Canonical post with live members:
pre_delete_postreturnsfalse, which blocks the delete. The exception is when the source and all members are in the trash, for example Empty Trash: the members are deleted first, then the source. - Canonical term: deleting it deletes every member term.
- Translated term: its
wpr_translated_term_{lang}key is removed from the source. - Trashed member: it keeps its slot. Add for that language returns That translation is in the trash. Restore it or delete it permanently before adding a new one.
Reading and Observing Links from Your Code
// Every language version of a property.
$family = wpr_translate_relationship_get_post_family( $property_id );
if ( ! is_wp_error( $family ) ) {
foreach ( $family['members'] as $lang => $id ) {
// ...
}
}
// The published French version, or the original.
$fr_id = wpr_translate_relationship_resolve_post( $property_id, 'fr', false, 'frontend' );
1.0.9 fires no action when a translation is linked. The old wpr_translation_created action lives in the uncalled legacy writer. To react to new links, hook added_post_meta and updated_post_meta and check for _wpr_translations, or for wpr_translated_term_ keys on terms.
Related: Database Schema Reference, WP_Query Language Filtering, Meta Sync Across Language Variants.