We could try to filter the <strike>`WPLANG` option</strike> *locale*  [(see e.g. this approach from the related list here on the right](http://wordpress.stackexchange.com/questions/53326/change-admin-language-based-on-user-in-single-site)  by @brasofilo, that's based on [this one](http://wordpress.stackexchange.com/a/52436/26350) by @toscho ):

    /**
     * Override locale settings for the current (non-admin) user
     */
    is_admin() && add_filter( 'locale', function( $locale )
    {
		// Modify locale for non-admins (we don't want to override this on the settings pages)
    	if( ! current_user_can( 'manage_options' ) )
		{
			// Get admin language for the current user
			$lang = get_user_meta( get_current_user_id(), 'wpse_lang', true );
			
			// Use 'en_US' as default
			$locale = ! empty( $lang ) ? sanitize_text_field( $lang ) : 'en_US';
		}	
    	return $locale;	
    } );	

where we check if the current user has the `wpse_lang` user meta key set, with values like `is_IS`, `da_DK`, ...

Then we could add the language selection for each user, on the user settings page. 

There we could use the [`wp_dropdown_languages()`][1] function, with the [`get_available_languages()`][2] function, to display the select-box for available languages.

Here's an [example](http://wordpress.stackexchange.com/a/6711/26350) by @sanchothefat on how we can add custom user settings.

We can display the user language selection with:

    /**
     * Display available language dropdown
     */
    function wpse_user_language( $user ) 
    {
        // Only display for non-admins, but allow admins to edit for other users
        if( current_user_can( 'manage_options' ) && $user->ID == get_current_user_id() )
            return;

        // Get the current 'wp_lang' settings
    	$lang = get_user_meta( $user->ID, 'wpse_lang', true ); 
        ?>
        <table class="form-table">
            <tr id="wpse-lang-selection">
                <th scope="row">
                   <label for="wpse_lang">
                       <?php _e( 'WPSE' ); ?> - <?php _e( 'Site Language' ); ?>
                   </label>
                </th>
                <td><?php wp_dropdown_languages( 
    					[
    						'id'                          => 'wpse_lang',
    						'name'                        => 'wpse_lang',
    						'languages'                   => get_available_languages(),
    						'translations'                => [],
    						'selected'                    => $lang,
    						'show_available_translations' => false,
    					] 
    				);
    			?></td>
            </tr>
        </table>
        <?php
    }
    add_action( 'show_user_profile', 'wpse_user_language' );
    add_action( 'edit_user_profile', 'wpse_user_language' );

Note that here we re-use the *Site Language* string, because it's translated.

The update part is:

    /**
     * Update the 'wp_lang' user settings
     */    
    function wpse_user_language_save( $user_id ) 
    {
    	if( current_user_can( 'edit_user', $user_id ) && isset( $_POST['wpse_lang'] ) )
    		return update_user_meta( $user_id, 'wpse_lang', $_POST['wpse_lang'] );
    	return false;
    }
    add_action( 'personal_options_update', 'wpse_user_language_save' );
    add_action( 'edit_user_profile_update', 'wpse_user_language_save' );

Here's an example output:

[![wpse_lang selection][3]][3]

Hopefully you can adjust this to your needs. It would be a good idea to wrap this in a class, where we could initialize it with a custom setup and re-use things for better performance. We might also consider removing the `is_admin()` check to apply this also on the front-end, but then we might need an extra `is_user_logged_in()` check.


  [1]: https://codex.wordpress.org/Function_Reference/wp_dropdown_languages
  [2]: https://developer.wordpress.org/reference/functions/get_available_languages/
  [3]: https://i.sstatic.net/Gczck.jpg