17

in my theme I want to define a series of custom post types and custom taxonomies, each one having its own customized slug; the base language of my theme is english, therefore the slugs will be in English language

for example while defining the slug of custom post type "product" args:

'rewrite' => array( 'slug' => 'product' ),

is there any way to translate the "slug" through po/mo files? can I put it as:

'rewrite' => array( 'slug' => __('product', 'mytextdomain') )

or it won't work? what's the current practice to localize slugs?

4
  • I don't know if we are dealing with the same problem but it seems like it. To better illustrate it here is a link to an original index page for a custom post type called prensa with a slug set to prensa. Using WPML the translated's page slug is press as it can't be prensa again: /en/press/ which doesn't display anything (note that now clicking the ES link doesn't bring you back to /prensa/). BUT, if you visit /en/prensa/ it does work... Commented Oct 10, 2011 at 16:45
  • I decided redirect the pages from /en/press to /en/prensa so the link will probably not work as mentioned any more. Too bad I couldn't use the localized slug but working-on-time is better thant url-localization-friendly Commented Oct 10, 2011 at 17:00
  • See my answer Naoise, I think it will give you a working solution. Commented Oct 10, 2011 at 17:31
  • I had this problem for hours. I finally found a hack : github.com/stouch/wp-plugin-polylang-localized-taxonomy-slug/… Regards. Commented Jul 14, 2019 at 12:12

6 Answers 6

21
+25

I wouldn't try to localize your slugs. Instead, why not give your users the option to change them by adding another field to the permalink settings page?

Hook into load-options-permalink.php and set up some things to catch the $_POST data to save your slug. Also add a settings field to the page.

<?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 a settings field to the permalink page
    add_settings_field( 'wpse30021_cpt_base', __( 'CPT Base' ), 'wpse30021_field_callback', 'permalink', 'optional' );
}

Then the call back function for the settings field:

<?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" />';
}

Then when you register your post type, grab the slug with get_option. If it's not there, use your default.

<?php
add_action( 'init', 'wpse30021_register_post_type' );
function wpse30021_register_post_type()
{
    $slug = get_option( 'wpse30021_cpt_base' );
    if( ! $slug ) $slug = 'your-default-slug';

    // register your post type, reference $slug for the rewrite
    $args['rewrite'] = array( 'slug' => $slug );

    // Obviously you probably need more $args than one....
    register_post_type( 'wpse30021_pt', $args );
}

Here's the settings field portion as a plugin https://gist.github.com/1275867

EDIT: Another Option

You could also change the slug based on what's defined in the WPLANG constant.

Just write a quick function that holds data...

<?php
function wpse30021_get_slug()
{
    // return a default slug
    if( ! defined( 'WPLANG' ) || ! WPLANG || 'en_US' == WPLANG ) return 'press';

    // array of slug data
    $slugs = array( 
        'fr_FR' => 'presse',
        'es_ES' => 'prensa'
        // etc.
    );

    return $slugs[WPLANG];
}

Then get the slug where you register your custom post type.

<?php
add_action( 'init', 'wpse30021_register_post_type' );
function wpse30021_register_post_type()
{
    $slug = wpse30021_get_slug();

    // register your post type, reference $slug for the rewrite
    $args['rewrite'] = array( 'slug' => $slug );

    // Obviously you probably need more $args than one....
    register_post_type( 'wpse30021_pt', $args );
}

The best option, IMO, would be to both give the user an option and provide solid defaults:

<?php
add_action( 'init', 'wpse30021_register_post_type' );
function wpse30021_register_post_type()
{
    $slug = get_option( 'wpse30021_cpt_base' );
    // They didn't set up an option, get the default
    if( ! $slug ) $slug = wpse30021_get_slug();

    // register your post type, reference $slug for the rewrite
    $args['rewrite'] = array( 'slug' => $slug );

    // Obviously you probably need more $args than one....
    register_post_type( 'wpse30021_pt', $args );
}
7
  • 2
    +1 for the plugin on gist and the well documented code. In my case, though, it defeats the purpose, which is to not give power to the user but to make localization-aware (seo friendly) urls for custom post types Commented Oct 11, 2011 at 17:01
  • 1
    I'm not sure I understand why you would want to remove an option from your user. More over, running a slug through a translation filter gives them the same option: to change the slug. Just not with a pretty form field to fill out. Commented Oct 12, 2011 at 16:05
  • 1
    just for curiosity, why wpse30021? Commented Oct 17, 2011 at 9:29
  • It seems as if this option is for a WPLANG-based localization. But what if you are working with a multi-language site? (for example WPML plugin). The question is more about displaying a different slug depending on the client's localization than being able to set a custom post type slug from the server's options. Commented Oct 17, 2011 at 9:33
  • wpse = WordPress stack exchange. 30021 is the number from the URL. Good luck with your quest; I've given my answer. The additional complexity you're adding, and apparent complete change of the original question -- originally about CPT slugs, only makes the case for allowing the end user to choose their own slug. Commented Oct 17, 2011 at 13:27
2

I am doing exactly that in a theme we are developing. It is available in 5 distinct languages, and each language has a translated set of categories. The first component of the URL in the theme is parsed to determine which language is used, in country-language format:

/uk-en
/fr-fr
/it-it

And then translated categories are parsed as further components of the URL.

The URL is parsed in the parse_request phase:

function my_parse_request( $wp ) {
    $path = parse_url( $_SERVER['REQUEST_URI'], PHP_URL_PATH );

    $components = preg_split('|/|', $path, null, PREG_SPLIT_NO_EMPTY );

    // Determine language from $components[0]
    $language = array_shift( $components );
    ...

    // Load translations file...
    $mofile = get_stylesheet_directory()."/$language.mo";

    load_textdomain( 'mydomain', $mofile );

    ...

    // Determine category from $components[0]
    if( __( 'some-category', 'mydomain' ) == $components[0] )
      $wp->query_vars['category'] = 'some-category';

    ...
}
add_action( 'parse_request', 'my_parse_request' );

This example is devoid of requisite checks, but is meant only as an example.

There are drawbacks to this approach, of course, but it allows natural URLs in all languages. The main drawbacks I see are:

1) It doesn't make use of the permalink mechanism. This could likely be extended so that the proper permalink rules for all languages are generated and parse_request won't be necessary, but to do it for all of the languages would involve loading one MO file after another in a loop, and I don't know how well supported that is.

2) If a translator changes a slug, then the links get invalidated.

1

If that doest not work Why not you just simple do:

$post_slug=  __('product', 'mytextdomain');
'rewrite' => array( 'slug' => $post_slug );
5
  • this didn't work for me Commented Oct 10, 2011 at 16:41
  • 1
    it is basically the same code in another style Commented Oct 10, 2011 at 16:47
  • Have you added the proper text domain ? <?php load_theme_textdomain(my_text_domain);?> ?
    – chifliiiii
    Commented Oct 10, 2011 at 18:37
  • This is by far, the best solution.
    – Al Rosado
    Commented Apr 20, 2017 at 6:44
  • Careful with this! Since WordPress 4.7, users can set a preferred for their profile. If a user has a different language than the site’s language and triggers a permalink flush, e.g. by visiting the permalink settings, then the post slug translation will change to the one of the user! That’s why the accepted answer, which uses an option that loads from the database is actually a much better and fail-safe solution.
    – Gchtr
    Commented Jul 30, 2020 at 15:53
1

I actually had this problem, here's my solution, which works when you know the languages of your website beforehand. Let's say you have a CPT called "movie", and your website has 3 languages. You need to rewrite your CPT permalink, for each language. You could do this dynamically too.

add_action("init", "rewrite_cpt", 10, 0);
function rewrite_cpt(){    
    add_rewrite_rule('^movie/([^/]*)/?', 'index.php?post_type=movie&name=$matches[1]', 'top'); // English
    add_rewrite_rule('^film/([^/]*)/?', 'index.php?post_type=movie&name=$matches[1]', 'top');  // Italian
    add_rewrite_rule('^фильм/([^/]*)/?', 'index.php?post_type=movie&name=$matches[1]', 'top'); // Russian
}

Now you just need to rebuild your permalinks: go to Settings > Permalinks and just press "Save"

The next part is rewriting URLs for your CPT, if you have a translated CPT you need to get the language of the post, in my case I use Polylang. It's important to encode foreign characters in URLs

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;
}
0

You could try this in your functions.php

<?php
add_filter('rewrite_slugs', function($translated_slugs) {
    // the possible translations for your slug '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;
});
?>

as seen here

1
  • 1
    filter rewrite_slugs does exist in WP itself, link you've referenced is for wp polylang plugin - so that may help poly lang people
    – jave.web
    Commented Feb 16, 2020 at 13:47
-1

I would recommend not making slugs translatable.

Translation is for user-facing site content. Slugs are used internally, and are only marginally "public-facing" via URL rewrites - and URLs should not be translatable, either.

So: leave your slugs alone, as you define them. Only make translatable strings that are intended for public consumption.

4
  • 13
    translated slugs, both from a seo and a user experience perspective, make a lot of sense... Commented Oct 17, 2011 at 9:20
  • I disagree that slugs impact user experience in anyway whatsoever. If a slug is used as part of a link, the link anchor text will be translated, so the user won't know the difference. And when people start tossing around "SEO", I generally think, "snake oil". I'm not an SEO expert, but I'm not buying SEO impact with respect to translated slugs. Commented Oct 17, 2011 at 11:28
  • 3
    I disagree based on experience. We have foreign content managers in-house who are explicit that URL slugs should be localized. It's a matter of creating a complete/ly local experience for the foreign user. For some countries, like Japan, it's literally essential to establish an authentic kind of trust and indicate you are really serious about doing business there. Commented Feb 14, 2013 at 23:13
  • urls must speak. So if the slug is (as often) the name of the entity or the taxonomy, the rewriting must take into account plurals as well as translations. That's not an option, both for SEO and simply good practice towards the end users. Commented Dec 21, 2015 at 14:11

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.