sandywp / plugins / cyr2lat

Cyr-To-Lat

4.7 out of 5 stars. 4.7 98 reviews

Convert Non-Latin characters in post, page and term slugs to Latin characters.

Launch a sandbox with this plugin → No signup.
Ready in seconds.

v7.1.0 300,000+ installs WP 6.0+ PHP 7.4+

your-sandbox.sandywp.com/wp-admin
Cyr-To-Lat running inside a SandyWP sandbox

What you get inside

4 screens
  • Tables settings page

  • Converter settings page

  • Block editor with transliterated slug

  • WPML Certificate

About this plugin

Converts Cyrillic characters in post, page, and term slugs to Latin characters. Useful for creating human-readable URLs.

Features

  • The only plugin with a fully editable transliteration table. Allows adding/removing and editing pairs like ‘Я’ => ‘Ya’, or even ‘Пиво’ => ‘Beer’
  • Converts post, page, custom post type, and term slugs through explicit WordPress save and REST/Gutenberg paths
  • Converts any number of existing post, page, and term slugs in background processes or with WP-CLI
  • Saves existing post and page permalinks integrity
  • Performs transliteration of attachment file names
  • Supports WooCommerce product, product taxonomy, global attribute, local attribute, variation, and frontend cart slug flows without automatic migration of existing attributes
  • The plugin supports Russian, Belorussian, Ukrainian, Bulgarian, Macedonian, Serbian, Greek, Armenian, Georgian, Kazakh, Hebrew, and Chinese characters
  • Has many advantages over similar plugins
  • Officially compatible with WPML

Based on the original Rus-To-Lat plugin by Anton Skorobogatov.

Sponsored by Blackfire.

Plugin Support

Questions

How can I define my own substitutions?

Add this code to your theme’s functions.php file:

/**
 * Modify conversion table.
 *
 * @param array $table Conversion table.
 *
 * @return array
 */
function my_ctl_table( $table ) {
   $table['Ъ'] = 'U';
   $table['ъ'] = 'u';

   return $table;
}
add_filter( 'ctl_table', 'my_ctl_table' );

How can I redefine non-standard locale?

For instance, if your non-standard locale is uk_UA, you can redefine it to uk by adding the following code to your theme’s function.php file:

/**
 * Use non-standard locale.
 *
 * @param string $locale Current locale.
 *
 * @return string
 */
function my_ctl_locale( $locale ) {
    if ( 'uk_UA' === $locale ) {
        return 'uk';
    }

    return $locale;
}
add_filter( 'ctl_locale', 'my_ctl_locale' );

How can I define my own transliteration of titles?

Add similar code to your theme’s functions.php file:

/**
 * Filter title before sanitizing.
 *
 * @param string|false $result Sanitized title.
 * @param string       $title  Title.
 *
 * @return string|false
 */
function my_ctl_pre_sanitize_title( $result, $title ) {
    if ( 'пиво' === $title ) {
        return 'beer';
    }

    return $result;
}
add_filter( 'ctl_pre_sanitize_title', 10, 2 );

How can I control the legacy sanitize_title bridge?

Version 7.0 uses explicit slug handlers for posts, terms, WooCommerce attributes, and other known save paths. A legacy sanitize_title bridge remains as a compatibility fallback for broad calls that older integrations may still rely on.

You can disable the broad fallback with this code:

add_filter( 'ctl_enable_legacy_sanitize_title_bridge', '__return_false' );

The filter receives the current default value, $title, $raw_title, and $context. Explicit known contexts, such as WordPress save handling, continue to use the dedicated 7.0 slug paths.

For debugging unknown bridge calls, define CYR_TO_LAT_DEBUG_LEGACY_SANITIZE_TITLE_BRIDGE as true. This diagnostic log is disabled by default and is not enabled by WP_DEBUG.

How can I define my own transliteration of filenames?

Add similar code to your theme’s functions.php file:

/**
 * Filter filename before sanitizing.
 *
 * @param string|false $result   Sanitized filename.
 * @param string       $filename Title.
 *
 * @return string|false
 */
function my_ctl_pre_sanitize_filename( $result, $filename ) {
    if ( 'пиво' === $filename ) {
        return 'beer';
    }

    return $result;
}
add_filter( 'ctl_pre_sanitize_filename', 10, 2 );

How can I allow the plugin to work on the frontend?

Add the following code to your plugin’s (or mu-plugin’s) main file. This code won’t work being added to a theme’s functions.php file.

/**
 * Filter status allowed Cyr To Lat plugin to work.
 *
 * @param bool $allowed
 *
 * @return bool
 */
function my_ctl_allow( bool $allowed ): bool {
    $uri = isset( $_SERVER['REQUEST_URI'] ) ?
        sanitize_url( wp_unslash( $_SERVER['REQUEST_URI'] ) ) :
        '';

    if ( 0 === strpos( $uri, '/divi-comments' ) ) {
        return true;
    }

    return $allowed;
}

add_filter( 'ctl_allow', 'my_ctl_allow' );

How can I limit post types for background conversion?

Add similar code to your theme’s functions.php file:

/**
 * Filter post types allowed for background conversion.
 *
 * @param array $post_types Allowed post types.
 *
 * @return array
 */
function my_ctl_post_types( $post_types ) {
    return [
        'post'          => 'post',
        'page'          => 'page',
        'attachment'    => 'attachment',
        'product'       => 'product',
        'nav_menu_item' => 'nav_menu_item',
    ];
}
add_filter( 'ctl_post_types', 'my_ctl_post_types' );

How can I convert many posts/terms using wp-cli?

Use the following command in the console:

wp cyr2lat regenerate [--post_type=<post_type>] [--post_status=<post_status>]

Where
-post_type is a list of post types,
-post_status is a list of post statuses.

4.7

98 reviews

  • 5 ★ 85
  • 4 ★ 4
  • 3 ★ 3
  • 2 ★ 1
  • 1 ★ 5

Ratings come from WordPress.org. SandyWP does not collect its own reviews.

sandywp / also on the shelf

Try another plugin

Spin up a real WordPress site in seconds.

Test plugins, build a demo, hand a client a link — then squash it and start again. No local setup, no Docker.