Utilidades de Números Telefónicos con libphonenumber en PHP: Detección de País, Idioma y Zona Horaria
Introducción
Al desarrollar aplicaciones globales, los números telefónicos son frecuentemente los datos de usuario más confiables disponibles. A diferencia de direcciones de correo electrónico o nombres de usuario, los números telefónicos contienen información geográfica inherente que puede ayudar a personalizar experiencias. Desde seleccionar automáticamente el idioma correcto para notificaciones hasta programar mensajes en horarios locales apropiados, extraer metadatos de números telefónicos es invaluable.
Esta guía demuestra cómo construir una clase PhoneUtil usando la biblioteca libphonenumber de Google (a través de la versión PHP) para extraer códigos de país, inferir idiomas preferidos y detectar zonas horarias de números telefónicos internacionales.
Prerrequisitos
- PHP 8.1 o superior
- Gestor de paquetes Composer
- Conocimiento básico de clases PHP y manejo de excepciones
Instalando libphonenumber-for-php
Instala la biblioteca vía Composer:
composer require giggsey/libphonenumber-for-phpVersión Lite Disponible
Si solo necesitas parsing y formateo básico sin geocodificación o información de operador, puedes usar giggsey/libphonenumber-for-php-lite para una instalación más ligera.
Construyendo la Clase PhoneUtil
Vamos a crear una clase utilitaria que proporciona tres métodos principales: detección de código de país, inferencia de idioma y resolución de zona horaria.
Estructura Básica
<?php
namespace App\Utils;
use libphonenumber\PhoneNumberToTimeZonesMapper;
use libphonenumber\PhoneNumberUtil;
use Throwable;
class PhoneUtil
{
// Los métodos irán aquí
}Obteniendo el Código de País
El primer método extrae el código de país ISO 3166-1 alfa-2 (ej: "US", "BR", "MX") de un número telefónico:
public static function getCountryCode(string $phone_number): ?string
{
try {
$phoneUtil = PhoneNumberUtil::getInstance();
$phoneObj = $phoneUtil->parse("+{$phone_number}");
return $phoneUtil->getRegionCodeForNumber($phoneObj);
} catch (Throwable $e) {
// Registra o reporta el error
return null;
}
}Formato del Número Telefónico
Esta implementación espera números telefónicos sin el signo + inicial. El método lo agrega durante el parsing. Ajusta según cómo tu aplicación almacena los números.
Infiriendo el Idioma del Usuario
Basándose en el código de país, podemos hacer suposiciones educadas sobre el idioma preferido del usuario. Esto es particularmente útil para enviar notificaciones localizadas:
public static function getCountryLanguage(string $phone_number): ?string
{
try {
$country_code = self::getCountryCode($phone_number);
if (! $country_code) {
return null;
}
return match ($country_code) {
// Países hispanohablantes
'ES', 'MX', 'AR', 'CO', 'PE', 'VE', 'CL', 'EC',
'GT', 'CU', 'BO', 'DO', 'HN', 'PY', 'SV', 'NI',
'CR', 'PA', 'UY' => 'es',
// Portugués
'BR' => 'pt_BR',
// Chino
'CN', 'TW', 'HK', 'SG' => 'zh_CN',
// Hindi
'IN' => 'hi',
// Por defecto inglés
default => 'en',
};
} catch (Throwable $e) {
return null;
}
}Expandiendo Soporte de Idiomas
El mapeo anterior cubre los idiomas principales. Expándelo según los locales soportados por tu aplicación. Considera países como Francia (FR => fr), Alemania (DE => de) o Japón (JP => ja) según sea necesario.
Detectando Zona Horaria
La detección de zona horaria es crucial para programar comunicaciones con usuarios en horarios apropiados. La biblioteca libphonenumber proporciona la clase PhoneNumberToTimeZonesMapper para este propósito:
public static function getTimezone(string $phone_number): ?string
{
try {
$country_code = self::getCountryCode($phone_number);
$phoneUtil = PhoneNumberUtil::getInstance();
$phoneObj = $phoneUtil->parse("+{$phone_number}", $country_code);
$timezoneMapper = PhoneNumberToTimeZonesMapper::getInstance();
$timezones = $timezoneMapper->getTimeZonesForNumber($phoneObj);
$timezone = $timezones[0] ?? null;
if (! $timezone) {
return null;
}
// Maneja fallbacks de zona horaria desconocida
if ($timezone === 'Etc/Unknown') {
return match ($country_code) {
'MX' => 'America/Mexico_City',
'BR' => 'America/Sao_Paulo',
'IN' => 'Asia/Kolkata',
default => null,
};
}
return $timezone;
} catch (Throwable $e) {
return null;
}
}Manejando Zonas Horarias Obsoletas
Algunos números telefónicos pueden retornar identificadores de zona horaria obsoletos. Es buena práctica mapearlos a sus equivalentes modernos:
public static function getTimezone(string $phone_number): ?string
{
try {
// ... código anterior ...
// Mapea zonas horarias obsoletas a equivalentes modernos
$deprecatedTimezones = [
'America/Buenos_Aires' => 'America/Argentina/Buenos_Aires',
'America/Catamarca' => 'America/Argentina/Catamarca',
'America/Cordoba' => 'America/Argentina/Cordoba',
'America/Jujuy' => 'America/Argentina/Jujuy',
'America/Mendoza' => 'America/Argentina/Mendoza',
];
if (isset($deprecatedTimezones[$timezone])) {
return $deprecatedTimezones[$timezone];
}
return $timezone;
} catch (Throwable $e) {
return null;
}
}Implementación Completa
Aquí está la clase PhoneUtil completa combinando todos los métodos:
<?php
namespace App\Utils;
use libphonenumber\PhoneNumberToTimeZonesMapper;
use libphonenumber\PhoneNumberUtil;
use Throwable;
class PhoneUtil
{
public static function getCountryCode(string $phone_number): ?string
{
try {
$phoneUtil = PhoneNumberUtil::getInstance();
$phoneObj = $phoneUtil->parse("+{$phone_number}");
return $phoneUtil->getRegionCodeForNumber($phoneObj);
} catch (Throwable $e) {
// Considera registrar: Errors::reportThrowable($e);
return null;
}
}
public static function getCountryLanguage(string $phone_number): ?string
{
try {
$country_code = self::getCountryCode($phone_number);
if (! $country_code) {
return null;
}
return match ($country_code) {
'ES', 'MX', 'AR', 'CO', 'PE', 'VE', 'CL', 'EC',
'GT', 'CU', 'BO', 'DO', 'HN', 'PY', 'SV', 'NI',
'CR', 'PA', 'UY' => 'es',
'BR' => 'pt_BR',
'CN', 'TW', 'HK', 'SG' => 'zh_CN',
'IN' => 'hi',
default => 'en',
};
} catch (Throwable $e) {
return null;
}
}
public static function getTimezone(string $phone_number): ?string
{
try {
$country_code = self::getCountryCode($phone_number);
$phoneUtil = PhoneNumberUtil::getInstance();
$phoneObj = $phoneUtil->parse("+{$phone_number}", $country_code);
$timezoneMapper = PhoneNumberToTimeZonesMapper::getInstance();
$timezones = $timezoneMapper->getTimeZonesForNumber($phoneObj);
$timezone = $timezones[0] ?? null;
if (! $timezone) {
return null;
}
if ($timezone === 'Etc/Unknown') {
return match ($country_code) {
'MX' => 'America/Mexico_City',
'BR' => 'America/Sao_Paulo',
'IN' => 'Asia/Kolkata',
default => null,
};
}
$deprecatedTimezones = [
'America/Buenos_Aires' => 'America/Argentina/Buenos_Aires',
'America/Catamarca' => 'America/Argentina/Catamarca',
'America/Cordoba' => 'America/Argentina/Cordoba',
'America/Jujuy' => 'America/Argentina/Jujuy',
'America/Mendoza' => 'America/Argentina/Mendoza',
];
return $deprecatedTimezones[$timezone] ?? $timezone;
} catch (Throwable $e) {
return null;
}
}
}Ejemplos de Uso
Uso Básico
use App\Utils\PhoneUtil;
// Número telefónico brasileño
$phone = '5511999998888';
$country = PhoneUtil::getCountryCode($phone); // "BR"
$language = PhoneUtil::getCountryLanguage($phone); // "pt_BR"
$timezone = PhoneUtil::getTimezone($phone); // "America/Sao_Paulo"Aplicación Práctica: Notificaciones Localizadas
public function sendNotification(User $user, string $message): void
{
$language = PhoneUtil::getCountryLanguage($user->phone);
$timezone = PhoneUtil::getTimezone($user->phone);
// Establece el locale para traducción
app()->setLocale($language ?? 'en');
// Programa en el horario local apropiado
$sendAt = now($timezone)->setTime(10, 0);
Notification::send($user, new UserNotification(
message: __($message),
scheduledAt: $sendAt
));
}Depurando Números Telefónicos
El proyecto libphonenumber proporciona una demostración en línea para probar el parsing de números telefónicos:
https://libphonenumber.appspot.com
Usa esta herramienta para verificar el comportamiento esperado antes de reportar problemas.
Solución de Problemas
Retornos de Zona Horaria Desconocida
Para algunos números móviles, especialmente en países grandes como Brasil, México o India, la biblioteca puede retornar Etc/Unknown. Esto sucede cuando el número no proporciona suficiente especificidad geográfica. Maneja esto con fallbacks a nivel de país, como se muestra arriba.
Fallos de Parsing
Causas comunes de fallos de parsing:
- Formato inválido de número telefónico
- Prefijo de código de país ausente o incorrecto
- Caracteres no numéricos en la entrada
Siempre sanitiza los números telefónicos antes de procesar:
$phone = preg_replace('/[^0-9]/', '', $phone);Conclusión
Construir utilidades de números telefónicos con libphonenumber proporciona una base confiable para aplicaciones internacionalizadas. Al extraer códigos de país, inferir idiomas y detectar zonas horarias, puedes entregar experiencias personalizadas que respetan los contextos geográficos de los usuarios.
Los puntos clave son:
- Usa
PhoneNumberUtilpara parsing y detección de país - Mapea códigos de país a tus locales soportados para inferencia de idioma
- Maneja zonas horarias
Etc/Unknowncon defaults sensatos a nivel de país - Mapea identificadores de zona horaria obsoletos a sus equivalentes modernos
Para aplicaciones con requisitos estrictos, considera permitir que los usuarios sobrescriban los valores inferidos en sus preferencias, usando la detección basada en teléfono como valores predeterminados inteligentes.

