true ) ), array( 'attachment' ) ) ); } function qtrad_missing_filter_language() { // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- A read-only list filter, like the core filters beside it. $lang = isset( $_GET['qtrad_missing'] ) && is_string( $_GET['qtrad_missing'] ) ? sanitize_key( wp_unslash( $_GET['qtrad_missing'] ) ) : ''; return qtrad_is_enabled( $lang ) ? $lang : ''; } function qtrad_missing_filter_select( $post_type ) { if ( ! in_array( $post_type, qtrad_overview_post_types(), true ) ) { return; } $current = qtrad_missing_filter_language(); echo ''; echo ''; } function qtrad_missing_filter_query( $query ) { if ( ! $query instanceof WP_Query || ! $query->is_main_query() || ! function_exists( 'get_current_screen' ) ) { return; } $screen = get_current_screen(); $lang = qtrad_missing_filter_language(); if ( '' !== $lang && $screen && 'edit' === $screen->base ) { $query->set( 'qtrad_missing_language', $lang ); } } /** Posts without content in the language, by the same rule that decides where a post is shown. */ function qtrad_missing_filter_where( $where, $query ) { $lang = $query instanceof WP_Query ? $query->get( 'qtrad_missing_language' ) : ''; if ( ! is_string( $lang ) || ! qtrad_is_enabled( $lang ) ) { return $where; } return $where . ' AND NOT (' . preg_replace( '/^\s*AND\s*/', '', qtrad_available_language_where( $lang ) ) . ')'; } function qtrad_dashboard_setup() { if ( current_user_can( 'edit_posts' ) && count( qtrad_enabled_languages() ) > 1 ) { wp_add_dashboard_widget( 'qtrad_translations', esc_html__( 'Translations', 'qtrad' ), 'qtrad_dashboard_widget' ); wp_enqueue_style( 'qtrad-admin', plugins_url( 'assets/css/admin.css', QTRAD_FILE ), array(), QTRAD_VERSION ); } } /** * Published items of a type and how many of them are missing each language. * Cached until posts change. * * @return array{total:int,missing:array} */ function qtrad_overview_counts( $post_type ) { $key = 'counts:' . $post_type . ':' . md5( implode( ',', qtrad_enabled_languages() ) ) . ':' . qtrad_cache_last_changed( 'posts' ); $counts = wp_cache_get( $key, 'qtrad_overview' ); if ( is_array( $counts ) ) { return $counts; } $hooked = has_filter( 'posts_where', 'qtrad_missing_filter_where' ); if ( false === $hooked ) { add_filter( 'posts_where', 'qtrad_missing_filter_where', 10, 2 ); } $base = array( 'post_type' => $post_type, 'post_status' => 'publish', 'posts_per_page' => 1, 'fields' => 'ids', 'ignore_sticky_posts' => true, 'suppress_filters' => false ); $total = new WP_Query( $base ); $counts = array( 'total' => (int) $total->found_posts, 'missing' => array() ); foreach ( qtrad_enabled_languages() as $lang ) { $query = new WP_Query( array_merge( $base, array( 'qtrad_missing_language' => $lang ) ) ); $counts['missing'][ $lang ] = (int) $query->found_posts; } if ( false === $hooked ) { remove_filter( 'posts_where', 'qtrad_missing_filter_where', 10 ); } wp_cache_set( $key, $counts, 'qtrad_overview' ); return $counts; } function qtrad_dashboard_widget() { $rows = array(); foreach ( qtrad_overview_post_types() as $post_type ) { $object = get_post_type_object( $post_type ); if ( ! $object || ! current_user_can( $object->cap->edit_posts ) ) { continue; } $counts = qtrad_overview_counts( $post_type ); if ( $counts['total'] > 0 ) { $rows[ $post_type ] = array( 'label' => $object->labels->name, 'counts' => $counts ); } } if ( ! $rows ) { echo '

' . esc_html__( 'There is no published content yet.', 'qtrad' ) . '

'; return; } $languages = qtrad_enabled_languages(); echo ''; foreach ( $languages as $lang ) { echo ''; } echo ''; foreach ( $rows as $post_type => $row ) { /* translators: %d: number of published items */ echo ''; foreach ( $languages as $lang ) { $missing = $row['counts']['missing'][ $lang ]; if ( 0 === $missing ) { echo ''; continue; } $url = add_query_arg( array( 'post_type' => $post_type, 'post_status' => 'publish', 'qtrad_missing' => $lang ), admin_url( 'edit.php' ) ); /* translators: %d: number of items without a translation */ echo ''; } echo ''; } echo '
' . esc_html__( 'Published content missing a translation, by language', 'qtrad' ) . '
' . esc_html__( 'Content', 'qtrad' ) . '' . esc_html( qtrad_language_name( $lang ) ) . '
' . esc_html( $row['label'] ) . ' ' . esc_html( sprintf( _n( '(%d published)', '(%d published)', $row['counts']['total'], 'qtrad' ), $row['counts']['total'] ) ) . '' . esc_html__( 'Complete', 'qtrad' ) . '' . esc_html( sprintf( _n( '%d missing', '%d missing', $missing, 'qtrad' ), $missing ) ) . '
'; echo '

' . esc_html__( 'Missing means the title or content has no text in that language, so visitors see the fallback.', 'qtrad' ) . '

'; }