Functions for retrieving and managing translation relationships between posts. These are the primary API for working with post translations from theme templates and custom plugins.
lf_get_post_language($post_id)
Returns the language code assigned to a specific post.
php
/**
* Get the language of a post.
*
* @param int $post_id WordPress post ID
* @return string Language code. Falls back to the default language
* if no language meta is set.
*/
$lang = lf_get_post_language(42);
// Returns: 'en'| Parameter | Type | Description |
|---|---|---|
$post_id | int | WordPress post ID |
| Return Type | Description |
|---|---|
string | Language code (e.g., 'en'). Returns the default language if no _lf_language meta exists. |
php
// In The Loop
$lang = lf_get_post_language(get_the_ID());
$languages = lf_get_languages();
$lang_name = $languages[$lang]['native_name'] ?? $lang;
$flag = $languages[$lang]['flag'] ?? $lang;
$flag_url = plugins_url('langforge/assets/flags/' . $flag . '.svg');
echo '<span class="lang-badge">';
echo '<img src="' . esc_url($flag_url) . '" alt="" width="16" height="12"> ';
echo esc_html($lang_name);
echo '</span>';php
// Build a language breakdown for a set of posts
$all_posts = get_posts(['posts_per_page' => -1, 'post_type' => 'post']);
$by_language = [];
foreach ($all_posts as $post) {
$lang = lf_get_post_language($post->ID);
if (!isset($by_language[$lang])) {
$by_language[$lang] = 0;
}
$by_language[$lang]++;
}
// $by_language = ['ru' => 45, 'en' => 38, 'de' => 12]php
// single.php - Show a translation notice if the post is not in the visitor's preferred language
$post_lang = lf_get_post_language(get_the_ID());
$current_lang = lf_get_current_language();
if ($post_lang !== $current_lang) {
$translations = lf_get_post_translations(get_the_ID());
if (isset($translations[$current_lang])) {
$translated_url = get_permalink($translations[$current_lang]);
echo '<div class="translation-notice">';
echo '<a href="' . esc_url($translated_url) . '">';
_e('This page is available in your language', 'theme');
echo '</a>';
echo '</div>';
}
}lf_get_post_translations($post_id)
Returns all translations linked to a post, including the post itself.
php
/**
* Get all translations for a post.
*
* Uses an in-memory cache: when you fetch translations for one post
* in a group, all posts in that group are cached for the rest of
* the request.
*
* @param int $post_id WordPress post ID
* @return array Associative array: language_code => post_id
*/
$translations = lf_get_post_translations(42);
// Returns: ['ru' => 42, 'en' => 99, 'de' => 155]| Parameter | Type | Description |
|---|---|---|
$post_id | int | WordPress post ID |
| Return Type | Description |
|---|---|
array | Associative array mapping language codes to post IDs. Always includes the source post. Returns empty array if no translation data exists. |
php
// single.php
$translations = lf_get_post_translations(get_the_ID());
$languages = lf_get_languages();
$current = lf_get_current_language();
if (count($translations) > 1) {
echo '<div class="also-available">';
echo '<span>' . esc_html__('Also available in:', 'theme') . ' </span>';
$links = [];
foreach ($translations as $lang_code => $trans_id) {
if ($lang_code === $current) continue;
$name = $languages[$lang_code]['native_name'] ?? $lang_code;
$links[] = '<a href="' . esc_url(get_permalink($trans_id)) . '">' . esc_html($name) . '</a>';
}
echo implode(', ', $links);
echo '</div>';
}php
// Show which languages are missing translations for this post
$translations = lf_get_post_translations(get_the_ID());
$all_languages = lf_get_languages();
$missing = array_diff_key($all_languages, $translations);
if (!empty($missing)) {
echo '<div class="missing-translations">';
echo '<strong>Missing translations:</strong> ';
$names = [];
foreach ($missing as $code => $lang) {
$names[] = esc_html($lang['name']);
}
echo implode(', ', $names);
echo '</div>';
}php
// Efficient pattern: preload translations before a loop
$query = new WP_Query(['post_type' => 'post', 'posts_per_page' => 20]);
$post_ids = wp_list_pluck($query->posts, 'ID');
// Warm the cache: first call per translation group triggers 1 query
foreach ($post_ids as $pid) {
lf_get_post_translations($pid);
}
// Now display posts with translation info - all cache hits
while ($query->have_posts()) {
$query->the_post();
$trans = lf_get_post_translations(get_the_ID()); // 0 queries
$trans_count = count($trans);
echo '<p>' . get_the_title() . ' (' . $trans_count . ' languages)</p>';
}
wp_reset_postdata();lf_get_translation($post_id, $language)
Returns the post ID of a specific translation.
php
/**
* Get a single translation of a post.
*
* @param int $post_id WordPress post ID
* @param string $language Target language code
* @return int|null Translated post ID, or null if not found
*/
$en_id = lf_get_translation(42, 'en');
// Returns: 99| Parameter | Type | Description |
|---|---|---|
$post_id | int | WordPress post ID |
$language | string | Target language code (e.g., 'en', 'de') |
| Return Type | Description | |
|---|---|---|
int | null | Translated post ID, or null if no translation exists for that language |
php
$en_version = lf_get_translation(get_the_ID(), 'en');
if ($en_version) {
echo '<a href="' . esc_url(get_permalink($en_version)) . '">';
echo '<img src="' . esc_url(plugins_url('langforge/assets/flags/gb.svg')) . '" alt="English" width="20"> ';
echo 'Read in English';
echo '</a>';
} else {
echo '<span class="no-translation">English version not available</span>';
}php
add_action('wp_head', function () {
if (!is_singular()) return;
$languages = lf_get_languages();
$post_id = get_the_ID();
foreach ($languages as $code => $lang) {
$trans_id = lf_get_translation($post_id, $code);
if ($trans_id) {
$url = get_permalink($trans_id);
$hreflang = str_replace('_', '-', $lang['locale']);
echo '<link rel="alternate" hreflang="' . esc_attr($hreflang) . '" href="' . esc_url($url) . '">' . "n";
}
}
});php
// Show the same post in other languages as "Read in other languages" widget
function theme_cross_language_widget($post_id) {
$languages = lf_get_languages();
$current = lf_get_current_language();
$has_translations = false;
echo '<div class="widget cross-language-widget">';
echo '<h3>' . esc_html__('Read in other languages', 'theme') . '</h3>';
echo '<ul>';
foreach ($languages as $code => $lang) {
if ($code === $current) continue;
$trans_id = lf_get_translation($post_id, $code);
if ($trans_id) {
$has_translations = true;
$title = get_the_title($trans_id);
echo '<li>';
echo '<a href="' . esc_url(get_permalink($trans_id)) . '">';
echo esc_html($lang['native_name']) . ': ' . esc_html($title);
echo '</a>';
echo '</li>';
}
}
echo '</ul>';
echo '</div>';
return $has_translations;
}—