Standardization & validation is important, but good luck getting all you clients and your client's visitors on board with adhering to ISO standards. When it comes to phone numbers, we've embraced the E.164 international standard (ITU-T recommendation) in order to simplify integration with third-party SMS services (like Twilio), but that doesn't mean our clients like to see phone numbers in that format. When it comes to cosmetically formatting phone numbers, I personally prefer the (223) 456-7890
format, but some design-orientated clients demand that we use 123.456.7890
which I think more closely resembles an IP address.
In order to make our lives easier and quickly adapt when our clients change their minds (which tends to happen more than you'd think), we use dynamic server-side formatting for phone number data.
Apparently there are a lot of different ways to format phone numbers. The Gregg Reference Manual offers many possibilities (as referenced in this article.)
SalesForce has special logic formatting for the phone field to provide automatic formatting. I also found a blog with a decent article entitled "Why You Should Care About Phone Number Formatting In Your CRM (and How to Fix Them)" with some examples of phone number variations.
- 2124567890
- 212-456-7890
- (212)456-7890
- (212)-456-7890
- 212.456.7890
- 212 456 7890
- +12124567890
- +12124567890
- +1 212.456.7890
- +212-456-7890
- 1-212-456-7890
I've been using a CFLib phoneFormat library to reformat US phone numbers and had made some tweaks to it over the years. To provide even better value for mobile users, we use a CFML script to attempt to detect mobile browsers and then auto-generate clickable TEL
links on webpages using a makeTelLink UDF. (For more info regarding TEL
, check out MSDN or IETF.)
In cases where the phone number may not match a US pattern, the phoneFormat string is returned unchanged while makeTelLink returns a non-linked string.
UPDATE 2024-11-15: Added optional support in both UDFs for phone extensions (off by default).
Enjoy!
Source Code for phoneFormat & makeTelLink
https://gist.github.com/JamoCA/4342da7f2388a3a0b38fb6b55b8c9c35
<cfscript> | |
/* makeTelLink ColdFusion UDF - generates an HTML TEL link with optional phone formatting | |
2018-01-29 | |
Force TEL = MakeTelLink("831-393-1798", "", 1) | |
2023-07-21 Add "mask" option (pass boolean or string "(xxx) xxx-xxxx" mask) | |
2024-11-15 Add support for extension | |
Phone formatting requires phoneFormat UDF: https://gist.github.com/JamoCA/4342da7f2388a3a0b38fb6b55b8c9c35 | |
Author: James Moberg http://sunstarmedia.com @sunstarmedia | |
GIST: https://gist.github.com/JamoCA/4342da7f2388a3a0b38fb6b55b8c9c35 | |
Blog: https://dev.to/gamesover/phoneformat-maketellink-coldfusion-udfs-40fh | |
X/Twitter: https://x.com/gamesover/status/1857119411058201026 | |
LinkedIn: https://www.linkedin.com/posts/jamesmoberg_coldfusion-cfml-activity-7262886030020665345-MWwn | |
*/ | |
string function makeTelLink(string n="", string class="", boolean forceTel=false, any mask="", boolean allowExtension=false, string extPrefix="x") hint="Generates a TEL link" { | |
local.makeTel = (arguments.forceTel || (structkeyexists(request, "isMobileBrowser") && request.isMobileBrowser)); | |
// identify & standardize extension | |
local.ext = ""; | |
local.phone = trim(arguments.n); | |
if (arguments.allowExtension && listlen(local.phone, "x") eq 2 && len(trim(listlast(local.phone, "x")))){ | |
local.phone = local.phone.replaceAll("(?i)(ext\.?|extension)", "x"); | |
local.ext = listlast(local.phone, "x").replaceAll("\D", ""); | |
if (len(local.ext)){ | |
local.ext = ";" & local.ext; | |
local.phone = trim(listfirst(local.phone, "x")); | |
} | |
} | |
local.phone = local.phone.replaceAll("\D", ""); | |
if (len(local.phone) eq 10){local.phone = "1" & local.phone;} | |
if (len(local.phone) eq 11){local.phone = "+" & local.phone;} | |
if (!local.makeTel || len(local.phone) neq 12 || left(local.phone,2) neq "+1"){ | |
return arguments.n; | |
} | |
// mask the phone number using phoneNumber UDF (OPTIONAL) | |
if (issimplevalue(arguments.mask) && variables.keyexists("phoneFormat") && iscustomfunction(variables.phoneFormat)){ | |
if (isvalid("boolean", arguments.mask) && arguments.mask){ | |
arguments.n = phoneFormat(varInput=arguments.n, allowExtension=arguments.allowExtension, extPrefix=arguments.extPrefix); | |
} else if (find("x", arguments.mask)) { | |
arguments.n = phoneFormat(varInput=arguments.n, varMask=arguments.mask, allowExtension=arguments.allowExtension, extPrefix=arguments.extPrefix); | |
} | |
} | |
local.aHref = "<a href=""TEL:#local.phone##local.ext#"""; | |
if (len(trim(arguments.class))){ | |
local.aHref &= " class=""#trim(arguments.class)#"""; | |
} | |
return local.aHref & ">#arguments.n#</a>"; | |
} | |
</cfscript> |
<cfscript> | |
/* PhoneFormat ColdFusion UDF | |
7/22/2010 Original from http://cflib.org/udf/phoneFormat | |
Allows you to specify the mask you want added to your phone number. | |
v2 - code optimized by Ray Camden | |
v3.01 | |
v3.02 added code for single digit phone numbers from John Whish | |
v4 make a default format - by James Moberg | |
@param input Phone number to be formatted. (Required) | |
@param mask Mask to use for formatting. x represents a digit. (Required) | |
@author Derrick Rapley (adrapley@rapleyzone.com) | |
@version 3, May 9, 2009 | |
@version 4, February 11, 2011 | |
2014-01-14 Updated by James Moberg @ SunStarMedia.com (exit if alpha or not enough digits) | |
2024-11-15 Add support for extension | |
Author: James Moberg http://sunstarmedia.com @sunstarmedia | |
GIST: https://gist.github.com/JamoCA/4342da7f2388a3a0b38fb6b55b8c9c35 | |
Blog: https://dev.to/gamesover/phoneformat-maketellink-coldfusion-udfs-40fh | |
X/Twitter: https://x.com/gamesover/status/1857119411058201026 | |
LinkedIn: https://www.linkedin.com/posts/jamesmoberg_coldfusion-cfml-activity-7262886030020665345-MWwn | |
*/ | |
string function phoneFormat(string varInput="", string varMask="(xxx) xxx-xxxx", boolean allowExtension=false, string extPrefix="x") hint="Allows you to specify the mask you want added to your phone number." { | |
local.input = trim(arguments.varInput).replaceAll("^1","").replaceAll("^(\+1|1[\s\-\(\.])", ""); | |
local.mask = (len(trim(arguments.varMask)) && find("x", arguments.varMask)) ? arguments.varMask : "(xxx) xxx-xxxx"; | |
// identify & standardize extension | |
local.ext = ""; | |
if (arguments.allowExtension && listlen(local.input, "x") eq 2 && len(trim(listlast(local.input, "x")))){ | |
local.input = local.input.replaceAll("(?i)(ext\.?|extension)", "x"); | |
local.ext = listlast(local.input, "x").replaceAll("\D", ""); | |
if (len(local.ext)){ | |
local.ext = " " & arguments.extPrefix & local.ext; | |
local.input = trim(listfirst(local.input, "x")); | |
} | |
} | |
local.newFormat = javacast("string", local.input).replaceAll("\D", ""); | |
if (refind("\+[^1]", local.input)){ | |
return trim(arguments.varInput); // international (non-US) phone number | |
} | |
if (refind("[a-w]|y|z|[A-Z]", local.input)){ | |
return trim(arguments.varInput); // contains alpha characters; | |
} | |
if (len(local.newFormat) neq len(local.mask.replaceAll("(?i)[^x]", ""))){ | |
return trim(arguments.varInput); // Not enough digits | |
} | |
local.input = trim(local.input); | |
local.newFormat = " " & local.input.replaceAll("\D", ""); | |
local.newFormat = reverse(local.newFormat); | |
local.mask = reverse(local.mask); | |
for (local.i=1; local.i lte len(trim(local.mask)); local.i+=1) { | |
local.curPosition = mid(local.mask, local.i, 1); | |
if(local.curPosition neq "x") { | |
local.newFormat = insert(local.curPosition, local.newFormat, local.i-1) & " "; | |
} | |
} | |
return trim(reverse(local.newFormat)) & local.ext; | |
} | |
</cfscript> |
<cfscript> | |
/* Examples for phoneFormat & makeTelLink ColdFusion UDFs | |
Test phone numbers from https://blog.insycle.com/phone-number-formatting-crm | |
Author: James Moberg http://sunstarmedia.com @sunstarmedia | |
Ref: Salesforce Standard 'Phone' field length and formatting https://help.salesforce.com/s/articleView?id=000385963&type=1 | |
Ref: Business writing https://www.businesswritingblog.com/business_writing/2007/01/how_to_format_p.html | |
Ref: https://stackoverflow.com/questions/9482633/how-do-i-include-extensions-in-the-tel-uri | |
GIST: https://gist.github.com/JamoCA/4342da7f2388a3a0b38fb6b55b8c9c35 | |
Blog: https://dev.to/gamesover/phoneformat-maketellink-coldfusion-udfs-40fh | |
X/Twitter: https://x.com/gamesover/status/1857119411058201026 | |
LinkedIn: https://www.linkedin.com/posts/jamesmoberg_coldfusion-cfml-activity-7262886030020665345-MWwn | |
*/ | |
tests = [ | |
"+12124567890", // E.164 standard (no spaces, no dashes, no parenthesis, and no periods) | |
"2124567890", | |
"212-456-7890", | |
"(212)456-7890", | |
"(212)-456-7890", | |
"212.456.7890", | |
"212 456 7890", | |
"+1 212.456.7890", | |
"12124567890", | |
"1212-456-7890", | |
"1(212)456-7890", | |
"1(212)-456-7890", | |
"1.212.456.7890", | |
"1 212 456 7890", | |
"1 212 456 7890 x203", | |
"1 212 456 7890 x 203", | |
"1 212 456 7890 ext. 203", | |
"1 212 456 7890 extension 203", | |
"+212-456-7890", // If you do not want the parentheses-space-hyphen formatting (800) 555-1212 for a 10- or 11-digit number, enter a “+” before the number. | |
"21256789012", // extra digit | |
"678-9012" // local; not enough info | |
]; | |
for (phoneNumber in tests){ | |
writeoutput("<div>#phoneNumber# = #phoneFormat(varInput=phoneNumber, allowExtension=true)#."); | |
writeoutput(" Call us #makeTelLink(n=phoneNumber, forceTel=true, mask=true, allowExtension=true)#.</div>"); | |
} | |
</cfscript> |
Top comments (0)