php preg_replace ucwords for spanish names with special characters

2020-05-27 php regex preg-replace special-characters capitalize

I need to get the same ucwords function result for names in spanish having accents and 'ñ', like " Ángela María Ñusté Fernández " or " José Édgar Ramírez Álvarez "

After dealing a lot I get the following algorithm:

/*------------------------------------------------------------------*/
function html2ucwords($texto)
/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
DESCRIPCION:
Pone la primera letra de cada palabra en mayúscula teniendo en cuenta
las tildes.
--------------------------------------------------------------------*/
{
    $retorno = htmlentities($texto);
    $retorno = strtolower($retorno);
    $retorno = ucwords($retorno);

    $retorno = preg_split("# #", $retorno);
    $tilinc = array( '#^á#'
                   , '#^é#'
                   , '#^í#'
                   , '#^ó#'
                   , '#^ú#'
                   , '#^ñ#'
                   );
    $mayusculas = array( 'Á'
                       , 'É'
                       , 'Í'
                       , 'Ó'
                       , 'Ú'
                       , 'Ñ'
                       );
    foreach ($retorno as $llave => $valor)
        $retorno[$llave] = preg_replace($tilinc, $mayusculas, $valor);

    $retorno = join(" ", $retorno);
    $retorno = html_entity_decode($retorno);

    return $retorno;
}

I'm wondering if there is a better way to solve this requirement.

Answers

Yes, by far: avoid entities and treat the Unicode as is - that way it also works for characters/letters you forget. Not to mention other languages and letter systems.

As per https://www.php.net/manual/en/function.mb-strtoupper.php#124253 you can easily combine mb_strlen(), mb_substr() and mb_strtoupper() to build your own function:

// First character in uppercase, others in lowercase
function mb_ucfirst( $sInput, $sEncoding= 'UTF-8' ) {
  $iLen=   mb_strlen    ( $sInput,              $sEncoding );
  $sFirst= mb_substr    ( $sInput, 0,        1, $sEncoding );
  $sRest=  mb_substr    ( $sInput, 1, $iLen- 1, $sEncoding );
  return   mb_strtoupper( $sFirst,              $sEncoding ). mb_strtolower( $sRest );
}

// Perform ucfirst() on every word 
function mb_ucwords( $sInput, $sEncoding= 'UTF-8' ) {
  $aWord= preg_split( '/ /u', $sInput );
  $sOut= '';
  foreach( $aWord as $iWord=> $sWord ) $aWord[$iWord]= mb_ucfirst( $sWord, $sEncoding );
  return join( ' ', $aWord );
}

// Now test it
header( 'Content-type: text/plain' );
$aTest= array
( 'áNgELa MARÌA ñUESé Álvarez áLVAREZ'  // i.e. Spanish
, 'änGY ösTRogEN'                       // i.e. German
, 'Мария шараПОВА'                      // Cyrillic
, 'βήτα θήΤΑ'                           // Greek
, 'CHLOË'                               // i.e. Afrikaans
, 'łaTYnKA'                             // i.e. Polish
, 
);
$i= 0;
foreach( $aTest as $sTest ) printf( "%d. %s \t >>> \t %s\n", ++$i, $sTest, mb_ucwords( $sTest ) );

Of course: save the whole PHP file in UTF-8 and likewise treat your input like that. If you're using a different encoding then you have to provide that as second parameter to the function.

Related