sophone
v1.1.0
Published
πΈπ΄ Professional Somali phone number validation, formatting, operator detection & mobile wallet identification. Supports EVC, Sahal, ZAAD, eDahab, and Jeeb with CLI tools, TypeScript support, and beautiful error handling.
Maintainers
Readme
πΈπ΄ sophone
Professional Somali phone number validation, formatting, operator detection & mobile wallet identification
A comprehensive JavaScript/TypeScript library for working with Somali phone numbers. Validate, format, identify operators, and detect mobile money wallets (EVC, Sahal, ZAAD, eDahab, Jeeb) for Somalia (+252) phone numbers with beautiful error handling and TypeScript support.
β¨ Features
- π Validate Somali phone numbers in any format
- π¨ Format numbers to E.164, local, or international format
- π± Identify operators (Hormuud, Somtel, Telesom, etc.)
- π° Detect mobile wallets (EVC, Sahal, ZAAD, eDahab, Jeeb)
- π‘οΈ TypeScript support with complete type definitions
- π CLI tool for command-line usage
- π¦ Batch processing for multiple numbers
- π― Beautiful error handling with detailed messages
- β‘ Zero dependencies and lightweight
π¦ Installation
For Library Use
npm install sophoneFor CLI Use (Global)
npm install -g sophoneπ Quick Start
Basic Usage
import {
isValidSomaliMobile,
normalizeE164,
formatLocal,
getOperator,
getWallet,
getWalletInfo,
} from "sophone";
// Validate any Somali phone number format
isValidSomaliMobile("+252 61 123 4567"); // β
true
isValidSomaliMobile("0611234567"); // β
true
isValidSomaliMobile("611234567"); // β
true
isValidSomaliMobile("252611234567"); // β
true
isValidSomaliMobile("invalid"); // β false
// Convert to international E.164 format
normalizeE164("0611234567"); // "+252611234567"
// Format for local display
formatLocal("+252611234567"); // "0611 234 567"
// Identify the mobile operator
getOperator("0611234567"); // "Hormuud"
// Identify the mobile wallet
getWallet("0611234567"); // "EVC"
getWallet("0621234567"); // "Sahal"
getWallet("0631234567"); // "ZAAD"
// Get detailed wallet information
const walletInfo = getWalletInfo("0611234567");
console.log(walletInfo.name); // "EVC Plus"
console.log(walletInfo.ussd); // "*770#"π Comprehensive Usage Guide
1. Phone Number Validation
import { isValidSomaliMobile, validate } from "sophone";
// Simple validation (boolean result)
const phoneNumbers = [
"+252 61 123 4567", // Hormuud
"0621234567", // Somtel
"631234567", // Telesom
"invalid-number", // Invalid
"123", // Too short
];
phoneNumbers.forEach((number) => {
const isValid = isValidSomaliMobile(number);
console.log(`${number}: ${isValid ? "β
Valid" : "β Invalid"}`);
});
// Detailed validation with error information
const result = validate("0611234567");
if (result.ok) {
console.log("β
Valid number!");
console.log(`π± Original: ${result.value.input}`);
console.log(`π International: ${result.value.e164}`);
console.log(`π Local: ${result.value.local}`);
console.log(`π‘ Operator: ${result.value.operator}`);
console.log(`π° Wallet: ${result.value.wallet || 'None'}`);
} else {
console.log("β Invalid number!");
console.log(`π¨ Error: ${result.error.message}`);
console.log(`π Code: ${result.error.code}`);
}2. Number Formatting
import {
normalizeE164,
formatLocal,
formatInternational,
// Safe versions that return null instead of throwing
normalizeE164Safe,
formatLocalSafe,
formatInternationalSafe,
} from "sophone";
const phoneNumber = "0611234567";
// Different formatting options
console.log("π± Original:", phoneNumber);
console.log("π E.164:", normalizeE164(phoneNumber)); // "+252611234567"
console.log("π Local:", formatLocal(phoneNumber)); // "0611 234 567"
console.log("π International:", formatInternational(phoneNumber)); // "+252 61 123 4567"
// Safe formatting (won't throw errors)
const invalidNumber = "invalid";
console.log("Safe E.164:", normalizeE164Safe(invalidNumber)); // null
console.log("Safe Local:", formatLocalSafe(invalidNumber)); // null3. Operator Detection
import { getOperator, getOperatorInfo, getAllOperators } from "sophone";
// Get operator name
console.log(getOperator("0611234567")); // "Hormuud"
console.log(getOperator("0621234567")); // "Somtel"
console.log(getOperator("0631234567")); // "Telesom"
// Get detailed operator information
const operatorInfo = getOperatorInfo("0611234567");
console.log("π‘ Operator:", operatorInfo.name); // "Hormuud Telecom Somalia"
console.log("π Prefixes:", operatorInfo.prefixes); // ["61", "77"]
console.log("π Website:", operatorInfo.website); // "https://hormuud.com"
console.log("π± Type:", operatorInfo.type); // "GSM"
console.log("π° Primary Wallet:", operatorInfo.wallet); // "EVC"
// List all available operators
const allOperators = getAllOperators();
allOperators.forEach((op) => {
console.log(`π‘ ${op.name} (${op.prefixes.join(", ")}) - Wallet: ${op.wallet || 'None'}`);
});4. Mobile Wallet Detection
import {
getWallet,
getWalletInfo,
getAllWallets,
getWalletByName,
getWalletByOperator,
getSupportedWallets
} from "sophone";
// Get wallet name
console.log(getWallet("0611234567")); // "EVC"
console.log(getWallet("0621234567")); // "Sahal"
console.log(getWallet("0631234567")); // "ZAAD"
// Get detailed wallet information
const evcInfo = getWalletInfo("0611234567");
console.log("π° Wallet:", evcInfo.name); // "EVC Plus"
console.log("π Full Name:", evcInfo.fullName); // "Electronic Virtual Cash Plus"
console.log("π‘ Operator:", evcInfo.operator); // "Hormuud"
console.log("π USSD:", evcInfo.ussd); // "*770#"
console.log("π Website:", evcInfo.website); // "https://evcplus.so"
console.log("β‘ Features:", evcInfo.features); // ["Send Money", "Receive Money", ...]
// Get wallet by name
const zaadWallet = getWalletByName("ZAAD");
console.log("π° ZAAD USSD:", zaadWallet.ussd); // "*712#"
// Get wallet by operator
const hormuudWallet = getWalletByOperator("Hormuud");
console.log("π° Hormuud Wallet:", hormuudWallet.name); // "EVC Plus"
// List all supported wallets
const supportedWallets = getSupportedWallets();
console.log("π° Supported Wallets:", supportedWallets); // ["EVC", "Sahal", "ZAAD", "eDahab", "Jeeb"]
// Get all wallet details
const allWallets = getAllWallets();
allWallets.forEach((wallet) => {
console.log(`π° ${wallet.name} (${wallet.operator}) - USSD: ${wallet.ussd || 'N/A'}`);
});5. Error Handling
import { normalizeE164, SomaliPhoneError, ERROR_CODES } from "sophone";
// Throwing functions with proper error handling
function processPhoneNumber(input) {
try {
const e164 = normalizeE164(input);
console.log(`β
Processed: ${e164}`);
return e164;
} catch (error) {
if (error instanceof SomaliPhoneError) {
switch (error.code) {
case ERROR_CODES.INVALID_LENGTH:
console.log(`β Wrong length: ${error.message}`);
break;
case ERROR_CODES.INVALID_PREFIX:
console.log(`β Invalid prefix: ${error.message}`);
break;
case ERROR_CODES.INVALID_INPUT:
console.log(`β Invalid input: ${error.message}`);
break;
default:
console.log(`β Unknown error: ${error.message}`);
}
// Access additional error details
console.log("π Error details:", error.details);
}
return null;
}
}
// Test with different inputs
processPhoneNumber("0611234567"); // β
Valid
processPhoneNumber("123"); // β Too short
processPhoneNumber("0111234567"); // β Invalid prefix
processPhoneNumber("invalid"); // β Invalid input6. Batch Processing
import { validateBatch, normalizeBatch } from "sophone";
const phoneNumbers = [
"0611234567", // Valid Hormuud
"0621234567", // Valid Somtel
"invalid", // Invalid
"123", // Too short
"0771234567", // Valid Hormuud
];
// Validate multiple numbers at once
const validationResults = validateBatch(phoneNumbers);
validationResults.forEach((result) => {
if (result.ok) {
console.log(
`β
${result.input} β ${result.value.e164} (${result.value.operator})`
);
} else {
console.log(`β ${result.input} β ${result.error.message}`);
}
});
// Normalize multiple numbers (safe operation)
const normalizedResults = normalizeBatch(phoneNumbers);
normalizedResults.forEach((result) => {
console.log(`${result.input} β ${result.result || "Invalid"}`);
});7. Real-World Examples
User Registration Form Validation
import { isValidSomaliMobile, validate, formatLocal } from "sophone";
function validateUserPhone(phoneInput) {
// Quick validation
if (!isValidSomaliMobile(phoneInput)) {
return {
valid: false,
message: "Please enter a valid Somali phone number",
};
}
// Get detailed information
const result = validate(phoneInput);
if (result.ok) {
return {
valid: true,
formatted: result.value.local,
operator: result.value.operator,
wallet: result.value.wallet,
e164: result.value.e164,
};
}
return { valid: false, message: result.error.message };
}
// Usage in form validation
const userInput = "0611234567";
const validation = validateUserPhone(userInput);
if (validation.valid) {
console.log(`β
Phone: ${validation.formatted}`);
console.log(`π‘ Operator: ${validation.operator}`);
console.log(`π° Wallet: ${validation.wallet || 'No primary wallet'}`);
// Store validation.e164 in database
} else {
console.log(`β Error: ${validation.message}`);
}Contact List Formatter
import { formatLocal, getOperator, getWallet, isValidSomaliMobile } from "sophone";
const contacts = [
{ name: "Ahmed", phone: "+252611234567" },
{ name: "Fatima", phone: "0621234567" },
{ name: "Omar", phone: "631234567" },
{ name: "Amina", phone: "invalid-phone" },
];
const formattedContacts = contacts.map((contact) => {
if (isValidSomaliMobile(contact.phone)) {
return {
...contact,
formattedPhone: formatLocal(contact.phone),
operator: getOperator(contact.phone),
wallet: getWallet(contact.phone),
valid: true,
};
} else {
return {
...contact,
formattedPhone: contact.phone,
operator: null,
wallet: null,
valid: false,
};
}
});
console.log("π Contact List:");
formattedContacts.forEach((contact) => {
const status = contact.valid ? "β
" : "β";
const operator = contact.operator ? `(${contact.operator})` : "";
const wallet = contact.wallet ? ` | ${contact.wallet}` : "";
console.log(
`${status} ${contact.name}: ${contact.formattedPhone} ${operator}${wallet}`
);
});Throwing vs Safe Functions
The library provides both throwing and safe (non-throwing) versions of functions:
// Throwing functions (throw SomaliPhoneError on invalid input)
try {
normalizeE164("0611234567"); // "+252611234567"
formatLocal("0611234567"); // "0611 234 567"
getOperator("0611234567"); // "Hormuud"
} catch (error) {
if (error instanceof SomaliPhoneError) {
console.log(error.code); // ERROR_CODES.INVALID_PREFIX
console.log(error.message); // Descriptive message
}
}
// Safe functions (return null on invalid input, never throw)
normalizeE164Safe("0611234567"); // "+252611234567"
normalizeE164Safe("invalid"); // null
formatLocalSafe("0611234567"); // "0611 234 567"
formatLocalSafe("invalid"); // null
getOperatorSafe("0611234567"); // "Hormuud"
getOperatorSafe("invalid"); // nullError Handling
The library uses a custom SomaliPhoneError class with structured error codes:
try {
normalizeE164("invalid");
} catch (error) {
console.log(error instanceof SomaliPhoneError); // true
console.log(error.code); // "INVALID_INPUT"
console.log(error.message); // "invalid" contains no valid digits
console.log(error.details); // Additional context
}
// Available error codes
console.log(ERROR_CODES.INVALID_INPUT); // "INVALID_INPUT"
console.log(ERROR_CODES.INVALID_LENGTH); // "INVALID_LENGTH"
console.log(ERROR_CODES.INVALID_PREFIX); // "INVALID_PREFIX"
console.log(ERROR_CODES.UNKNOWN); // "UNKNOWN"π₯οΈ CLI Usage
The CLI tool provides a convenient way to work with Somali phone numbers from the command line.
Installation
npm install -g sophoneCommands
# Validate a phone number
sophone validate "+252 61 123 4567"
# Output: β valid
sophone validate "invalid-number"
# Output: β invalid
# "invalid-number" contains no valid digits
# Format to different formats
sophone format "0611234567" # 0611 234 567
sophone e164 "0611234567" # +252611234567
sophone international "0611234567" # +252 61 123 4567
# Get operator and wallet information
sophone operator "0611234567" # Hormuud
sophone wallet "0611234567" # EVC
sophone info "0611234567"
# Output: Operator: Hormuud Telecom Somalia
# Prefixes: 61, 77
# Type: GSM
# Website: https://hormuud.com
# Primary Wallet: EVC
sophone walletinfo "0611234567"
# Output: Wallet: EVC Plus
# Full Name: Electronic Virtual Cash Plus
# Operator: Hormuud
# Description: Hormuud's flagship mobile money service
# Features: Send Money, Receive Money, Pay Bills, Buy Airtime, Merchant Payments
# USSD Code: *770#
# Website: https://evcplus.so
# List all operators and wallets
sophone operators
# Output: Available Operators:
# Hormuud Telecom Somalia (61, 77)
# Website: https://hormuud.com
# Primary Wallet: EVC
# Somtel Network (62, 65, 66)
# Website: https://somtel.com
# Primary Wallet: Sahal
# ...
sophone wallets
# Output: Available Mobile Wallets:
# EVC Plus (Hormuud)
# USSD: *770#
# Website: https://evcplus.so
# Features: Send Money, Receive Money, Pay Bills, Buy Airtime, Merchant Payments
# Sahal (Somtel)
# USSD: *828#
# Website: https://somtel.com/sahal
# Features: Money Transfer, Bill Payment, Airtime Purchase, Merchant Services
# ...
# Process multiple numbers from a file
echo -e "0611234567\n0621234567\ninvalid\n123" > numbers.txt
sophone batch numbers.txt
# Output: Processing 4 numbers from numbers.txt:
# β 0611234567 β +252611234567 (Hormuud | EVC)
# β 0621234567 β +252621234567 (Somtel | Sahal)
# β invalid β "invalid" contains no valid digits
# β 123 β "123" is too short (3 digits)
# Summary: 2 valid, 2 invalid
# Get help
sophone helpCLI Examples in Scripts
#!/bin/bash
# Validate and format phone numbers in a script
phone="0611234567"
if sophone validate "$phone" > /dev/null 2>&1; then
echo "Phone number is valid!"
formatted=$(sophone format "$phone")
operator=$(sophone operator "$phone")
echo "Formatted: $formatted"
echo "Operator: $operator"
else
echo "Invalid phone number: $phone"
fiSupported Operators & Mobile Wallets
| Operator | Prefixes | Primary Wallet | USSD Code | |----------|----------|----------------|-----------| | Hormuud Telecom | 61, 77 | EVC Plus | *770# | | Somtel Network | 62, 65, 66 | Sahal | *828# | | Telesom | 63 | ZAAD | *712# | | SomLink | 64 | - | - | | SomNet | 68 | - | - | | NationLink | 69 | - | - | | Amtel | 71 | - | - |
Additional Mobile Wallets
- eDahab: Multi-operator service (*444#) - International remittance
- Jeeb: Multi-operator digital wallet - E-commerce focused
API
Core Functions
isValidSomaliMobile(input: string): boolean
Validates if the input is a valid Somali mobile number. Never throws errors.
validate(input: string): ValidationResult
Comprehensive validation that returns { ok: true, value: {...} } for valid numbers or { ok: false, error: {...} } for invalid ones.
Throwing Functions
These functions throw SomaliPhoneError for invalid input:
normalizeE164(input: string): string
Normalizes a Somali mobile number to E.164 format (+252XXXXXXXXX).
formatLocal(input: string): string
Formats a Somali mobile number to local format (0XXX XXX XXX).
getOperator(input: string): string | null
Gets the mobile operator for a Somali mobile number.
getWallet(input: string): string | null
Gets the primary mobile wallet for a Somali mobile number based on the operator.
getWalletInfo(input: string): WalletInfo | null
Gets detailed wallet information including name, USSD code, features, and website.
Safe Functions (Non-throwing)
These functions return null instead of throwing errors:
normalizeE164Safe(input: string): string | null
Safe version of normalizeE164.
formatLocalSafe(input: string): string | null
Safe version of formatLocal.
getOperatorSafe(input: string): string | null
Safe version of getOperator.
getWalletSafe(input: string): string | null
Safe version of getWallet.
getWalletInfoSafe(input: string): WalletInfo | null
Safe version of getWalletInfo.
Utility Functions
getAllWallets(): Array<WalletInfo>
Returns an array of all supported mobile wallets with detailed information.
getWalletByName(walletName: string): WalletInfo | null
Gets wallet information by wallet name (e.g., "EVC", "Sahal").
getWalletByOperator(operatorName: string): WalletInfo | null
Gets wallet information by operator name (e.g., "Hormuud", "Somtel").
getSupportedWallets(): Array<string>
Returns an array of supported wallet names.
Error Handling
SomaliPhoneError
Custom error class with code, message, and details properties.
ERROR_CODES
Constants for error codes:
INVALID_INPUT: Invalid or missing inputINVALID_LENGTH: Wrong number of digitsINVALID_PREFIX: Unrecognized mobile prefixUNKNOWN: General unknown error
Links
- GitHub Repository: https://github.com/omartood/sophone
- npm Package: https://www.npmjs.com/package/sophone
- Issues: https://github.com/omartood/sophone/issues
Contributing
Contributions are welcome! Please feel free to submit a Pull Request.
- Fork the repository
- Create your feature branch (
git checkout -b feature/amazing-feature) - Commit your changes (
git commit -m 'Add some amazing feature') - Push to the branch (
git push origin feature/amazing-feature) - Open a Pull Request
Development
# Clone the repository
git clone https://github.com/omartood/sophone.git
cd sophone
# Install dependencies
npm install
# Run tests
npm test
# Test CLI locally
node src/cli.js validate "0611234567"Changelog
See CHANGELOG.md for details.
License
MIT - see LICENSE file for details.
Created by Omar ToodπΈπ΄
