|
<?php |
|
|
|
/** |
|
* Translate post-type-archive-slug when different from post-type-slug. |
|
* |
|
* You can have your archive slug set to, for example /books and the singles on /book/title by setting |
|
* $args['rewrite'] => [ 'slug' => 'book', ... ]; |
|
* $args['has_archive'] => 'books'; |
|
* when registering your post_type |
|
* |
|
* WPML supports translating the single-slug, but not the archive-slug. |
|
* |
|
* Following code allows that. It's a 3-part solution, all 3 parts are needed. |
|
* |
|
* After implementation: |
|
* |
|
* 1. save permalinks (will register the strings with WPML) |
|
* 2. use WPML string translation to translate the strings, found in text-domain "post-type-archive-slug" |
|
* You may need to set the correct source-language for the text-domain using the WPML String Translation tools. |
|
* 3. save permalinks (will update rewrite rules for tanslations) |
|
* |
|
* If you don't want to use WPML String Translation, change the text-domain post-type-archive-slug to that of your theme |
|
* or plugin and register the strings by putting a line like this: |
|
* $not_used = __('archive-slug', 'text-domain'); |
|
* in your theme or plugin and scan the files with your favorite POMO-editor. |
|
* |
|
* Make sure that all this is done in WP backend when on the default (primary) language, see [this comment](https://gist.github.com/senlin/1adf65ae297c32142e765628fce194ad?permalink_comment_id=5351512#gistcomment-5351512) |
|
* |
|
* For more information: |
|
* @see https://developer.wordpress.org/apis/handbook/internationalization/localization/#translating-themes-and-plugins |
|
* |
|
* |
|
* Part 1: filter the link generation. This works on the page. |
|
*/ |
|
|
|
add_filter( 'post_type_archive_link', function ( $permalink, $post_type ) { |
|
$post_type_object = get_post_type_object( $post_type ); |
|
|
|
// dont do anything if |
|
// - not a valid post_type |
|
// - rewrite not enabled |
|
// - slug not defined |
|
// - archive-slug not defined |
|
// - archive-slug not different from post-slug |
|
if ( ! $post_type_object || ! is_array( $post_type_object->rewrite ) || ! isset( $post_type_object->rewrite['slug'] ) || ! is_a( $post_type_object, WP_Post_Type::class ) || ! is_string( $post_type_object->has_archive ) || $post_type_object->rewrite['slug'] == $post_type_object->has_archive ) { |
|
return $permalink; |
|
} |
|
|
|
// use a cached version written after permalink resave, so we don't have to do grunt work every time. |
|
$match = get_option( '__wpml_post_type_archive_slug_match', [] ); |
|
$match = $match && isset( $match[ $post_type ] ) ? $match[ $post_type ] : false; |
|
if ( ! $match ) { |
|
// sorry, need to save permalinks first. |
|
// silently fail. |
|
return $permalink; |
|
} |
|
|
|
/** |
|
* @action wpml_register_single_string |
|
* @var string $context the text-domain |
|
* This should be $text_domain, not $context |
|
* @var string $name a description of the string. |
|
* because this is the actual Context for the string |
|
* @var string $value the text to translate |
|
* @var bool $allow_empty_value , default = false |
|
* @var string $source_lang_code , default to system-default-language. |
|
*/ |
|
do_action( 'wpml_register_single_string', 'post-type-archive-slug', '', $post_type_object->has_archive ); |
|
$permalink = preg_replace( '@/' . implode('|', $match) . '/@', '/' . __( $post_type_object->has_archive, 'post-type-archive-slug' ) . '/', $permalink ); |
|
/** |
|
* Why? sometimes url's come with dual slashes after the website domain. |
|
* replace all // with / except when it is :// |
|
*/ |
|
$permalink = preg_replace('@([^:]/)/@', '\1', $permalink); |
|
|
|
return $permalink; |
|
}, PHP_INT_MAX, 2 ); |
|
|
|
// above works for "current language" but fails for the language switcher. hence the following hook |
|
|
|
/** |
|
* Part 2: filter the link generation. This works in the language switch. |
|
* Warning: this is done multiple times during a page generation, but only once for the actual language-switcher |
|
* This is detected by the code by checking the post_Type being defined. ... ugly, but it works. |
|
*/ |
|
|
|
add_filter( 'icl_ls_languages', function ( $languages ) { |
|
$patch = get_option( '__wpml_post_type_archive_slug_match', [] ); |
|
if ( is_admin() || ! $patch || ! is_array( $patch ) ) { |
|
return $languages; |
|
} |
|
|
|
foreach ( $patch as $post_type => $_patches ) { |
|
$post_type_object = get_post_type_object( $post_type ); |
|
if ($post_type_object && is_a($post_type_object, 'WP_Post_Type')) { |
|
foreach ( $languages as $language_code => &$language ) { |
|
$language['url'] = preg_replace( '@/(' . implode('|', $_patches) . ')/@', '/' . ($_patches[$language_code] ?: $post_type_object->has_archive) . '/', $language['url'] ); |
|
/** |
|
* Why? sometimes url's come with dual slashes after the website domain. |
|
* replace all // with / except when it is :// |
|
*/ |
|
$language['url'] = preg_replace('@([^:]/)/@', '\1', $language['url']); |
|
} |
|
} |
|
} |
|
|
|
return $languages; |
|
} ); |
|
|
|
/** |
|
* Part 3: make the url's work. |
|
* This step also caches the list of post-types and their possible slugs |
|
* Effort is mate to only do work when there is actually an archive-slug that is different from the post-type-slug and rewrite is enabled. |
|
*/ |
|
|
|
add_filter( 'rewrite_rules_array', function ( $rules ) { |
|
|
|
global $sitepress; |
|
|
|
$current_language = $sitepress->get_current_language(); |
|
|
|
$post_types = get_post_types(); |
|
$post_types = array_filter( $post_types, function ( $post_type ) { |
|
$post_type_object = get_post_type_object( $post_type ); |
|
|
|
return ! ( ! $post_type_object || ! is_array( $post_type_object->rewrite ) || ! isset( $post_type_object->rewrite['slug'] ) || ! is_a( $post_type_object, WP_Post_Type::class ) || ! is_string( $post_type_object->has_archive ) || $post_type_object->rewrite['slug'] == $post_type_object->has_archive ); |
|
} ); |
|
if ( ! $post_types ) { |
|
return $rules; |
|
} |
|
|
|
$languages = apply_filters( 'wpml_active_languages', array(), 'skip_missing=0' ); |
|
|
|
$match = array_map( function ( $post_type ) use ( $languages, $sitepress ) { |
|
$post_type_object = get_post_type_object( $post_type ); |
|
|
|
return $post_type_object->has_archive; |
|
}, $post_types ); |
|
|
|
$patch = array_map( function ( $post_type ) use ( $languages, $sitepress ) { |
|
$post_type_object = get_post_type_object( $post_type ); |
|
$strings = []; |
|
foreach ( $languages as $language_code => $language ) { |
|
$sitepress->switch_lang( $language_code ); |
|
$strings[ $language_code ] = __( $post_type_object->has_archive, 'post-type-archive-slug' ); |
|
} |
|
|
|
return $strings; |
|
}, $post_types ); |
|
|
|
$sitepress->switch_lang( $current_language ); |
|
|
|
update_option( '__wpml_post_type_archive_slug_match', $patch ); |
|
|
|
$patch = array_map(function($strings){ |
|
return implode('|', $strings); |
|
}, $patch); |
|
$match = '@^(' . implode( '|', $match ) . ')/@'; |
|
$patch = '(?:' . implode( '|', $patch ) . ')'; |
|
|
|
|
|
$keys = array_keys( $rules ); |
|
$values = array_values( $rules ); |
|
|
|
foreach ( $keys as &$key ) { |
|
$key = preg_replace( $match, $patch, $key ); |
|
} |
|
|
|
$rules = array_combine( $keys, $values ); |
|
|
|
return $rules; |
|
} ); |
This is brilliant @senlin
thanks for sharing!
you should mention in the comments that when saving permalinks user language (the admin language in user profile) should be the primary language of the website. Otherwise the translation will be applied even to the main language archive slug.