This is a prototype for now I am just exploring idea the most efficient and future proof way to create translation system for wordpress.
function snn_get_languages() {
$languages = get_option( 'snn_languages', array() );
return $languages;
}
function snn_debug_mode() {
return get_option( 'snn_debug_mode', false );
}
function snn_register_language_post_types() {
$languages = snn_get_languages();
if ( empty( $languages ) ) {
return;
}
foreach ( $languages as $lang ) {
$lang = sanitize_key( $lang );
if ( empty( $lang ) || post_type_exists( $lang ) ) {
continue;
}
$labels = array(
'name' => sprintf( '%s', strtoupper( $lang ) ),
'singular_name' => sprintf( 'Translation (%s)', strtoupper( $lang ) ),
'add_new' => 'Add New',
'add_new_item' => sprintf( 'Add New Translation (%s)', strtoupper( $lang ) ),
'edit_item' => sprintf( 'Edit Translation (%s)', strtoupper( $lang ) ),
'new_item' => sprintf( 'New Translation (%s)', strtoupper( $lang ) ),
'view_item' => sprintf( 'View Translation (%s)', strtoupper( $lang ) ),
'search_items' => sprintf( 'Search Translations (%s)', strtoupper( $lang ) ),
'not_found' => 'No translations found',
'not_found_in_trash' => 'No translations found in Trash',
);
$args = array(
'labels' => $labels,
'public' => true,
'publicly_queryable' => true,
'show_ui' => true,
'show_in_menu' => true,
'has_archive' => false,
'rewrite' => array(
'slug' => $lang,
'with_front' => false,
),
'supports' => array( 'title', 'editor', 'excerpt', 'thumbnail', 'revisions' ),
'taxonomies' => array( 'category', 'post_tag' ),
);
register_post_type( $lang, $args );
}
}
add_action( 'init', 'snn_register_language_post_types', 15 );
function snn_hide_language_post_type_menu_items() {
if ( snn_debug_mode() ) {
return;
}
$languages = snn_get_languages();
if ( empty( $languages ) ) {
return;
}
echo '<style>';
foreach ( $languages as $lang ) {
$lang = sanitize_key( $lang );
echo '#menu-posts-' . esc_attr( $lang ) . ' { display: none !important; }';
}
echo '</style>';
}
add_action( 'admin_head', 'snn_hide_language_post_type_menu_items' );
function snn_add_admin_menu() {
add_menu_page(
'SNN Translation',
'SNN Translation',
'manage_options',
'snn-translation',
'snn_translation_admin_page',
'dashicons-translation',
80
);
}
add_action( 'admin_menu', 'snn_add_admin_menu' );
function snn_translation_admin_page() {
if ( isset( $_POST['snn_save'] ) && check_admin_referer( 'snn_save_options', 'snn_nonce' ) ) {
$langs = isset( $_POST['snn_languages'] ) ? sanitize_text_field( $_POST['snn_languages'] ) : '';
$lang_array = array_filter( array_map( 'trim', explode( ',', $langs ) ) );
update_option( 'snn_languages', $lang_array );
$debug = isset( $_POST['snn_debug_mode'] ) ? true : false;
update_option( 'snn_debug_mode', $debug );
echo '<div class="updated"><p>Settings saved.</p></div>';
}
$languages = snn_get_languages();
$languages_str = implode( ', ', $languages );
$debug = snn_debug_mode();
?>
<div class="wrap">
<h1>SNN Translation Settings</h1>
<form method="post">
<?php wp_nonce_field( 'snn_save_options', 'snn_nonce' ); ?>
<table class="form-table">
<tr>
<th scope="row"><label for="snn_languages">Languages</label></th>
<td>
<input type="text" name="snn_languages" id="snn_languages" value="<?php echo esc_attr( $languages_str ); ?>" class="regular-text" />
<p class="description">Enter language codes separated by commas. E.g., en, fr, de</p>
</td>
</tr>
<tr>
<th scope="row">Debug Mode</th>
<td>
<label for="snn_debug_mode">
<input type="checkbox" name="snn_debug_mode" id="snn_debug_mode" <?php checked( $debug ); ?> />
Enable debug mode (translations remain visible in admin menus)
</label>
</td>
</tr>
</table>
<?php submit_button( 'Save Settings', 'primary', 'snn_save' ); ?>
</form>
</div>
<?php
}
function snn_add_translation_flags_column( $columns ) {
$columns['snn_translation'] = 'Translations';
return $columns;
}
add_filter( 'manage_posts_columns', 'snn_add_translation_flags_column' );
add_filter( 'manage_pages_columns', 'snn_add_translation_flags_column' );
function snn_render_translation_flags_column( $column, $post_id ) {
if ( 'snn_translation' !== $column ) {
return;
}
if ( get_post_meta( $post_id, '_snn_original_post', true ) ) {
return;
}
$languages = snn_get_languages();
if ( empty( $languages ) ) {
return;
}
$output = '';
foreach ( $languages as $lang ) {
$translation = snn_get_translation_post( $post_id, $lang );
if ( $translation ) {
$edit_link = get_edit_post_link( $translation->ID );
$output .= '<a style="margin-right:6px;" href="' . esc_url( $edit_link ) . '" title="Edit ' . esc_attr( $lang ) . ' translation">' . strtoupper( esc_html( $lang ) ) . '</a>';
} else {
$url = add_query_arg( array(
'snn_translate' => 1,
'lang' => $lang,
'post_id' => $post_id,
), admin_url( 'admin-post.php' ) );
$output .= '<a style="margin-right:6px;" href="' . esc_url( $url ) . '" title="Translate to ' . esc_attr( $lang ) . '">' . strtoupper( esc_html( $lang ) ) . '</a>';
}
}
echo $output;
}
add_action( 'manage_posts_custom_column', 'snn_render_translation_flags_column', 10, 2 );
add_action( 'manage_pages_custom_column', 'snn_render_translation_flags_column', 10, 2 );
function snn_get_translation_post( $post_id, $lang ) {
$args = array(
'post_type' => $lang,
'meta_query' => array(
array(
'key' => '_snn_original_post',
'value' => $post_id,
'compare' => '='
),
array(
'key' => '_snn_translation_lang',
'value' => $lang,
'compare' => '='
)
),
'post_status' => 'any',
'numberposts' => 1,
);
$posts = get_posts( $args );
return ! empty( $posts ) ? $posts[0] : null;
}
function snn_clone_post( $post_id, $lang ) {
$original_post = get_post( $post_id );
if ( ! $original_post ) {
return new WP_Error( 'no_post', 'Original post not found.' );
}
$new_post = array(
'post_title' => $original_post->post_title,
'post_content' => $original_post->post_content,
'post_excerpt' => $original_post->post_excerpt,
'post_status' => 'draft',
'post_type' => $lang,
'post_author' => get_current_user_id(),
'menu_order' => $original_post->menu_order,
'post_parent' => $original_post->post_parent,
'comment_status' => $original_post->comment_status,
'ping_status' => $original_post->ping_status,
);
$new_post_id = wp_insert_post( $new_post );
if ( is_wp_error( $new_post_id ) ) {
return $new_post_id;
}
update_post_meta( $new_post_id, '_snn_original_post', $post_id );
update_post_meta( $new_post_id, '_snn_translation_lang', $lang );
update_post_meta( $new_post_id, '_snn_original_post_type', $original_post->post_type );
$meta = get_post_meta( $post_id );
foreach ( $meta as $meta_key => $meta_values ) {
if ( in_array( $meta_key, array( '_edit_lock', '_edit_last', '_snn_original_post', '_snn_translation_lang', '_snn_original_post_type' ), true ) ) {
continue;
}
foreach ( $meta_values as $meta_value ) {
add_post_meta( $new_post_id, $meta_key, maybe_unserialize( $meta_value ) );
}
}
$taxonomies = get_object_taxonomies( $original_post->post_type );
foreach ( $taxonomies as $taxonomy ) {
$terms = wp_get_object_terms( $post_id, $taxonomy, array( 'fields' => 'slugs' ) );
if ( ! empty( $terms ) && ! is_wp_error( $terms ) ) {
wp_set_object_terms( $new_post_id, $terms, $taxonomy, false );
}
}
$thumbnail_id = get_post_thumbnail_id( $post_id );
if ( $thumbnail_id ) {
set_post_thumbnail( $new_post_id, $thumbnail_id );
}
return $new_post_id;
}
function snn_handle_translation_action() {
if ( isset( $_GET['snn_translate'] ) && $_GET['snn_translate'] == 1 && isset( $_GET['lang'] ) && isset( $_GET['post_id'] ) ) {
$lang = sanitize_text_field( $_GET['lang'] );
$post_id = absint( $_GET['post_id'] );
if ( ! current_user_can( 'edit_post', $post_id ) ) {
wp_die( 'Permission denied.' );
}
$translation = snn_get_translation_post( $post_id, $lang );
if ( ! $translation ) {
$result = snn_clone_post( $post_id, $lang );
if ( is_wp_error( $result ) ) {
wp_die( 'Error cloning post: ' . $result->get_error_message() );
}
$translation = get_post( $result );
}
wp_redirect( get_edit_post_link( $translation->ID, '' ) );
exit;
}
}
add_action( 'admin_init', 'snn_handle_translation_action' );
function snn_disable_canonical_for_translations( $redirect_url, $requested_url ) {
if ( is_singular() ) {
$post = get_queried_object();
$languages = array_map( 'sanitize_key', snn_get_languages() );
if ( $post && in_array( $post->post_type, $languages, true ) ) {
return false;
}
}
return $redirect_url;
}
add_filter( 'redirect_canonical', 'snn_disable_canonical_for_translations', 10, 2 );