Wordpress - Change the Author Slug from Username to Nickname

I see two ways to solve this problem: changing the data that forms the author URL, or changing the author URL. You probably should handle redirects too, so old URLs to user archives keep working when a user changes their nickname.

Changing the author URL

There are two parts to this question: handle incoming links with the author nickname instead of the author slug, and generate author post urls with the nickname instead of the standard slug.

The first part is solved by hooking into the request filter, checking whether it is an author request, and looking up the author by nickname instead of slug. If we find an author, we change the query parameters to use the author ID.

add_filter( 'request', 'wpse5742_request' );
function wpse5742_request( $query_vars )
{
    if ( array_key_exists( 'author_name', $query_vars ) ) {
        global $wpdb;
        $author_id = $wpdb->get_var( $wpdb->prepare( "SELECT user_id FROM {$wpdb->usermeta} WHERE meta_key='nickname' AND meta_value = %s", $query_vars['author_name'] ) );
        if ( $author_id ) {
            $query_vars['author'] = $author_id;
            unset( $query_vars['author_name'] );    
        }
    }
    return $query_vars;
}

The second part is done by hooking into the author_link filter and replacing the standard author part (indicated by $author_nicename) with the nickname.

add_filter( 'author_link', 'wpse5742_author_link', 10, 3 );
function wpse5742_author_link( $link, $author_id, $author_nicename )
{
    $author_nickname = get_user_meta( $author_id, 'nickname', true );
    if ( $author_nickname ) {
        $link = str_replace( $author_nicename, $author_nickname, $link );
    }
    return $link;
}

Changing the data that forms the author URL

A maybe easier way would be to update the otherwise unused user_nicename field in the database. I think it is generated from the user login and never changed after that. But I'm not an expert in user management, so use it at your own risk.

add_action( 'user_profile_update_errors', 'wpse5742_set_user_nicename_to_nickname', 10, 3 );
function wpse5742_set_user_nicename_to_nickname( &$errors, $update, &$user )
{
    if ( ! empty( $user->nickname ) ) {
        $user->user_nicename = sanitize_title( $user->nickname, $user->display_name );
    }
}