Utilitários de Números de Telefone com libphonenumber em PHP: Detecção de País, Idioma e Fuso Horário
Introdução
Ao desenvolver aplicações globais, os números de telefone são frequentemente os dados de usuário mais confiáveis disponíveis. Diferente de endereços de e-mail ou nomes de usuário, números de telefone carregam informações geográficas inerentes que podem ajudar a personalizar experiências. Desde selecionar automaticamente o idioma correto para notificações até agendar mensagens em horários locais apropriados, extrair metadados de números de telefone é inestimável.
Este guia demonstra como construir uma classe PhoneUtil usando a biblioteca libphonenumber do Google (via a versão PHP) para extrair códigos de país, inferir idiomas preferidos e detectar fusos horários de números de telefone internacionais.
Pré-requisitos
- PHP 8.1 ou superior
- Gerenciador de pacotes Composer
- Conhecimento básico de classes PHP e tratamento de exceções
Instalando libphonenumber-for-php
Instale a biblioteca via Composer:
composer require giggsey/libphonenumber-for-phpVersão Lite Disponível
Se você precisa apenas de parsing e formatação básicos sem geocodificação ou informações de operadora, pode usar giggsey/libphonenumber-for-php-lite para uma instalação mais leve.
Construindo a Classe PhoneUtil
Vamos criar uma classe utilitária que fornece três métodos principais: detecção de código de país, inferência de idioma e resolução de fuso horário.
Estrutura Básica
<?php
namespace App\Utils;
use libphonenumber\PhoneNumberToTimeZonesMapper;
use libphonenumber\PhoneNumberUtil;
use Throwable;
class PhoneUtil
{
// Os métodos virão aqui
}Obtendo o Código do País
O primeiro método extrai o código de país ISO 3166-1 alfa-2 (ex: "US", "BR", "MX") de um número de telefone:
public static function getCountryCode(string $phone_number): ?string
{
try {
$phoneUtil = PhoneNumberUtil::getInstance();
$phoneObj = $phoneUtil->parse("+{$phone_number}");
return $phoneUtil->getRegionCodeForNumber($phoneObj);
} catch (Throwable $e) {
// Registre ou reporte o erro
return null;
}
}Formato do Número de Telefone
Esta implementação espera números de telefone sem o sinal + inicial. O método adiciona-o durante o parsing. Ajuste conforme a forma que sua aplicação armazena os números.
Inferindo o Idioma do Usuário
Com base no código do país, podemos fazer suposições educadas sobre o idioma preferido do usuário. Isso é particularmente útil para enviar notificações 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 de língua espanhola
'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',
// Chinês
'CN', 'TW', 'HK', 'SG' => 'zh_CN',
// Hindi
'IN' => 'hi',
// Padrão para inglês
default => 'en',
};
} catch (Throwable $e) {
return null;
}
}Expandindo Suporte a Idiomas
O mapeamento acima cobre os principais idiomas. Expanda-o com base nos locales suportados pela sua aplicação. Considere países como França (FR => fr), Alemanha (DE => de) ou Japão (JP => ja) conforme necessário.
Detectando Fuso Horário
A detecção de fuso horário é crucial para agendar comunicações com usuários em horários apropriados. A biblioteca libphonenumber fornece a classe PhoneNumberToTimeZonesMapper para esse 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;
}
// Trata fallbacks de fuso horário desconhecido
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;
}
}Tratando Fusos Horários Obsoletos
Alguns números de telefone podem retornar identificadores de fuso horário obsoletos. É uma boa prática mapeá-los para seus equivalentes modernos:
public static function getTimezone(string $phone_number): ?string
{
try {
// ... código anterior ...
// Mapeia fusos horários obsoletos para 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;
}
}Implementação Completa
Aqui está a classe PhoneUtil completa combinando todos os 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) {
// Considere 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;
}
}
}Exemplos de Uso
Uso Básico
use App\Utils\PhoneUtil;
// Número de telefone brasileiro
$phone = '5511999998888';
$country = PhoneUtil::getCountryCode($phone); // "BR"
$language = PhoneUtil::getCountryLanguage($phone); // "pt_BR"
$timezone = PhoneUtil::getTimezone($phone); // "America/Sao_Paulo"Aplicação Prática: Notificações Localizadas
public function sendNotification(User $user, string $message): void
{
$language = PhoneUtil::getCountryLanguage($user->phone);
$timezone = PhoneUtil::getTimezone($user->phone);
// Define o locale para tradução
app()->setLocale($language ?? 'en');
// Agenda no horário local apropriado
$sendAt = now($timezone)->setTime(10, 0);
Notification::send($user, new UserNotification(
message: __($message),
scheduledAt: $sendAt
));
}Depurando Números de Telefone
O projeto libphonenumber fornece uma demonstração online para testar o parsing de números de telefone:
https://libphonenumber.appspot.com
Use esta ferramenta para verificar o comportamento esperado antes de reportar problemas.
Solução de Problemas
Retornos de Fuso Horário Desconhecido
Para alguns números móveis, especialmente em países grandes como Brasil, México ou Índia, a biblioteca pode retornar Etc/Unknown. Isso acontece quando o número não fornece especificidade geográfica suficiente. Trate isso com fallbacks a nível de país, conforme mostrado acima.
Falhas de Parsing
Causas comuns de falhas de parsing:
- Formato inválido de número de telefone
- Prefixo de código de país ausente ou incorreto
- Caracteres não numéricos na entrada
Sempre sanitize os números de telefone antes de processar:
$phone = preg_replace('/[^0-9]/', '', $phone);Conclusão
Construir utilitários de números de telefone com libphonenumber fornece uma base confiável para aplicações internacionalizadas. Ao extrair códigos de país, inferir idiomas e detectar fusos horários, você pode entregar experiências personalizadas que respeitam os contextos geográficos dos usuários.
Os pontos principais são:
- Use
PhoneNumberUtilpara parsing e detecção de país - Mapeie códigos de país para seus locales suportados para inferência de idioma
- Trate fusos horários
Etc/Unknowncom defaults sensatos a nível de país - Mapeie identificadores de fuso horário obsoletos para seus equivalentes modernos
Para aplicações com requisitos rigorosos, considere permitir que os usuários sobrescrevam os valores inferidos em suas preferências, usando a detecção baseada em telefone como padrões inteligentes.

