Локализация "слагов" темы (пользовательские типы записей, таксономии)
В моей теме я хочу определить несколько пользовательских типов записей и пользовательских таксономий, каждая из которых имеет свой настроенный слаг; базовый язык моей темы - английский, поэтому слаги будут на английском языке
например, при определении слага для аргументов пользовательского типа записи "product":
'rewrite' => array( 'slug' => 'product' ),
есть ли способ перевести "слаг" через po/mo файлы? могу ли я написать это как:
'rewrite' => array( 'slug' => __('product', 'mytextdomain') )
или это не будет работать? какая текущая практика локализации слагов?
Я бы не рекомендовал локализовать ваши слаги. Вместо этого, почему бы не дать пользователям возможность изменять их, добавив дополнительное поле на страницу настроек постоянных ссылок?
Подключитесь к хуку load-options-permalink.php
и настройте обработку данных $_POST
для сохранения вашего слага. Также добавьте поле настроек на страницу.
<?php
add_action( 'load-options-permalink.php', 'wpse30021_load_permalinks' );
function wpse30021_load_permalinks()
{
if( isset( $_POST['wpse30021_cpt_base'] ) )
{
update_option( 'wpse30021_cpt_base', sanitize_title_with_dashes( $_POST['wpse30021_cpt_base'] ) );
}
// Добавляем поле настроек на страницу постоянных ссылок
add_settings_field( 'wpse30021_cpt_base', __( 'База CPT' ), 'wpse30021_field_callback', 'permalink', 'optional' );
}
Затем callback-функция для поля настроек:
<?php
function wpse30021_field_callback()
{
$value = get_option( 'wpse30021_cpt_base' );
echo '<input type="text" value="' . esc_attr( $value ) . '" name="wpse30021_cpt_base" id="wpse30021_cpt_base" class="regular-text" />';
}
При регистрации типа записи получайте слаг через get_option
. Если его нет, используйте значение по умолчанию.
<?php
add_action( 'init', 'wpse30021_register_post_type' );
function wpse30021_register_post_type()
{
$slug = get_option( 'wpse30021_cpt_base' );
if( ! $slug ) $slug = 'ваш-слаг-по-умолчанию';
// Регистрируем тип записи, используя $slug для rewrite
$args['rewrite'] = array( 'slug' => $slug );
// Очевидно, вам понадобится больше параметров, чем один...
register_post_type( 'wpse30021_pt', $args );
}
Вот часть с полем настроек в виде плагина: https://gist.github.com/1275867
РЕДАКТИРОВАНИЕ: Другой вариант
Вы также можете изменять слаг на основе константы WPLANG
.
Просто создайте функцию с данными...
<?php
function wpse30021_get_slug()
{
// Возвращаем слаг по умолчанию
if( ! defined( 'WPLANG' ) || ! WPLANG || 'en_US' == WPLANG ) return 'press';
// Массив соответствий языков и слагов
$slugs = array(
'fr_FR' => 'presse',
'es_ES' => 'prensa'
// и т.д.
);
return $slugs[WPLANG];
}
Затем получайте слаг при регистрации типа записи.
<?php
add_action( 'init', 'wpse30021_register_post_type' );
function wpse30021_register_post_type()
{
$slug = wpse30021_get_slug();
// Регистрируем тип записи, используя $slug для rewrite
$args['rewrite'] = array( 'slug' => $slug );
// Очевидно, вам понадобится больше параметров, чем один...
register_post_type( 'wpse30021_pt', $args );
}
Лучший вариант, на мой взгляд, - это предоставить пользователю выбор и установить разумные значения по умолчанию:
<?php
add_action( 'init', 'wpse30021_register_post_type' );
function wpse30021_register_post_type()
{
$slug = get_option( 'wpse30021_cpt_base' );
// Если пользователь не установил опцию, получаем значение по умолчанию
if( ! $slug ) $slug = wpse30021_get_slug();
// Регистрируем тип записи, используя $slug для rewrite
$args['rewrite'] = array( 'slug' => $slug );
// Очевидно, вам понадобится больше параметров, чем один...
register_post_type( 'wpse30021_pt', $args );
}

+1 за плагин на gist и хорошо документированный код. Однако в моем случае это противоречит цели, которая заключается в том, чтобы не давать пользователю возможность изменять, а создавать SEO-дружественные URL с учетом локализации для пользовательских типов записей

Не совсем понимаю, зачем вам нужно лишать пользователя этой опции. Более того, пропуск слага через фильтр перевода дает им ту же возможность: изменить слаг. Просто без красивого поля формы для заполнения.

Похоже, что эта опция предназначена для локализации на основе WPLANG. Но что делать, если вы работаете с многоязычным сайтом? (например, с плагином WPML). Вопрос скорее о том, как отображать разный slug в зависимости от локализации клиента, а не о возможности задать slug произвольного типа записи из настроек сервера.

wpse = WordPress stack exchange. 30021 — это номер из URL. Удачи в ваших поисках; я дал свой ответ. Дополнительная сложность, которую вы добавляете, и кажущееся полное изменение первоначального вопроса — изначально о slug'ах CPT, только подтверждает аргумент в пользу разрешения конечному пользователю выбирать собственный slug.

Вопрос спрашивает, можно ли использовать функцию __()
со slug'ами, и "какова практика локализации slug'ов". Это можно интерпретировать как перевод slug'а, подобно переводу строки при просмотре темы на определенном языке, независимо от его использования в CPT. Именно поэтому изначально была установлена награда.

Учитывая, что начиная с WordPress 4.7 пользователи могут установить предпочитаемый язык для своего профиля, использование опции из базы данных фактически является предпочтительным решением. Если пользователь выбрал язык, отличный от языка сайта, и инициирует сброс постоянных ссылок, например, посетив настройки постоянных ссылок, то перевод слага записи изменится на язык пользователя, чего вам не хотелось бы.

Я реализую именно такой подход в теме, которую мы разрабатываем. Она поддерживает 5 различных языков, и для каждого языка есть переведённый набор категорий. Первый компонент URL в теме анализируется для определения используемого языка в формате страна-язык:
/uk-en
/fr-fr
/it-it
А затем переведённые категории обрабатываются как последующие компоненты URL.
URL анализируется на фазе parse_request
:
function my_parse_request( $wp ) {
$path = parse_url( $_SERVER['REQUEST_URI'], PHP_URL_PATH );
$components = preg_split('|/|', $path, null, PREG_SPLIT_NO_EMPTY );
// Определяем язык из $components[0]
$language = array_shift( $components );
...
// Загружаем файл перевода...
$mofile = get_stylesheet_directory()."/$language.mo";
load_textdomain( 'mydomain', $mofile );
...
// Определяем категорию из $components[0]
if( __( 'some-category', 'mydomain' ) == $components[0] )
$wp->query_vars['category'] = 'some-category';
...
}
add_action( 'parse_request', 'my_parse_request' );
В этом примере опущены необходимые проверки, он предназначен только для демонстрации.
Конечно, у такого подхода есть недостатки, но он позволяет использовать естественные URL на всех языках. Основные недостатки, которые я вижу:
1) Он не использует механизм постоянных ссылок. Это можно расширить, чтобы генерировались правильные правила постоянных ссылок для всех языков и parse_request
не понадобится, но для всех языков это потребует загрузки одного MO-файла за другим в цикле, и я не знаю, насколько хорошо это поддерживается.
2) Если переводчик изменит ярлык (slug), то ссылки становятся недействительными.

Вы добавили правильный текстовый домен? <?php load_theme_textdomain(my_text_domain);?> ?

Будьте осторожны! Начиная с WordPress 4.7, пользователи могут установить предпочитаемый язык для своего профиля. Если у пользователя язык отличается от языка сайта и он инициирует сброс постоянных ссылок (например, посетив настройки постоянных ссылок), то перевод слага записи изменится на язык пользователя! Вот почему принятый ответ, который использует опцию, загружаемую из базы данных, на самом деле является гораздо более надежным и безопасным решением.

У меня действительно была такая проблема, вот мое решение, которое работает, когда вы заранее знаете языки своего сайта. Допустим, у вас есть пользовательский тип записи (CPT) под названием "movie", и ваш сайт поддерживает 3 языка. Вам нужно переписать постоянные ссылки для этого CPT для каждого языка. Это также можно сделать динамически.
add_action("init", "rewrite_cpt", 10, 0);
function rewrite_cpt(){
add_rewrite_rule('^movie/([^/]*)/?', 'index.php?post_type=movie&name=$matches[1]', 'top'); // Английский
add_rewrite_rule('^film/([^/]*)/?', 'index.php?post_type=movie&name=$matches[1]', 'top'); // Итальянский
add_rewrite_rule('^фильм/([^/]*)/?', 'index.php?post_type=movie&name=$matches[1]', 'top'); // Русский
}
Теперь вам просто нужно перестроить постоянные ссылки: перейдите в Настройки > Постоянные ссылки и просто нажмите "Сохранить"
Следующая часть — это перезапись URL для вашего CPT. Если у вас есть переведенный CPT, вам нужно получить язык записи. В моем случае я использую Polylang. Важно кодировать иностранные символы в URL.
add_filter("post_type_link", "x_tours_postlink", 10, 2);
function x_tours_postlink($post_link, $post){
$urls = array(
"en" => "movie",
"it" => "film",
"ru" => "фильм"
);
if(get_post_type($post) == "movie")
$post_link = str_replace("movie", urlencode($urls[pll_get_post_language($post->ID)]), $post_link);
return $post_link;
}

Вы можете попробовать это в вашем functions.php
<?php
add_filter('rewrite_slugs', function($translated_slugs) {
// возможные переводы для вашего слага 'product'
$translated_slugs = array(
'product' => array(
'pt' => array(
'has_archive' => true,'rewrite' => array('slug' => 'produto'),
),
'es' => array(
'has_archive' => true,'rewrite' => array('slug' => 'producto'),
),
),
);
return $translated_slugs;
});
?>
как показано здесь

Я рекомендую не делать слаги переводимыми.
Перевод предназначен для контента сайта, который видят пользователи. Слаги используются внутренне и лишь частично "публичны" через перезапись URL — а URL также не должны переводиться.
Итак: оставьте свои слаги такими, как вы их определили. Переводите только те строки, которые предназначены для публичного использования.

переведенные ЧПУ (человеко-понятные URL) с точки зрения SEO и пользовательского опыта имеют огромное значение...

Я не согласен, что ЧПУ как-то влияют на пользовательский опыт. Если ЧПУ используется как часть ссылки, анкорный текст будет переведен, поэтому пользователь не заметит разницы. А когда начинают разбрасываться терминами вроде "SEO", у меня обычно возникает мысль "разводилово". Я не эксперт по SEO, но в влияние переведенных ЧПУ на SEO я не верю.

Я не согласен, основываясь на опыте. У нас в компании есть иностранные контент-менеджеры, которые прямо утверждают, что ЧПУ должны быть локализованы. Это вопрос создания полностью локального опыта для иностранного пользователя. Для некоторых стран, например Японии, это буквально необходимо для установления подлинного доверия и демонстрации серьезности намерений вести там бизнес.

URL-адреса должны быть понятными. Поэтому если ярлык (slug) представляет собой (как часто бывает) название сущности или таксономии, перезапись должна учитывать как множественное число, так и переводы. Это не просто опция, а необходимость как для SEO, так и просто хорошая практика по отношению к конечным пользователям.
