Skip to content

CkIntl

Job: clean and check the values that cross borders — international phone numbers, IBANs and BIC/SWIFT codes — and mask the sensitive ones for display.

US numbers live in CkUS

CkIntl is for values that carry their country with them. US-only phone numbers without a country code — (212) 555-0123 — plus SSNs, ZIPs and routing numbers live in CkUS.

These three values arrive from forms and files in every shape people can type them: spaces, dashes, dots, lower case, a bracketed zero in the middle. CkIntl turns each one into its single official form, or rejects it. The standard signal applies: a value that cannot be used answers "" (the empty string) — non-empty in, empty out means rejected. The two …Mask methods are the exception: made for display, they never return empty.

Quick look

using SafeCoder.CommonKit;

CkIntl.Phone("+49 (0)151 1234567");            // -> "+491511234567"
CkIntl.Iban("de44 5001 0517 5407 3249 31");    // -> "DE44500105175407324931"
CkIntl.Bic("deutdeff");                        // -> "DEUTDEFF"
CkIntl.PhoneMask("+49 (0)151 1234567");        // -> "+********4567"
CkIntl.Phone("020 7946 0958", country: "GB");  // -> "+442079460958"  (national, with its country)
CkIntl.CallingCode("+41 44 668 18 00");        // -> "41"
CkIntl.Phone("555-867-5309");                  // -> ""  (not international, no country — rejected)
CkIntl.IbanMatchesBic("DE44500105175407324931", "DEUTDEFF");   // -> true
CkIntl.InCallingCodes("+49 151 1234567", "+49", "+43");       // -> true
CkIntl.Explain("(212) 555-0123");   // -> "no international prefix: it must start with +, 00 or 011"
CkIntl.PostalCode("k1a0b1", "CA");      // -> "K1A 0B1"
CkIntl.PostalCode("12 34", length: 4);   // -> "1234"  (no country: a length is the rule)

Methods

Phone

The international phone number.

string Phone(
    string? input = null,
    string? country = null,
    string? fallback = null,
    bool throwIfInvalid = CkSettings.DefaultThrowIfInvalid
)
Parameter Type Default What it does
input string? null The raw phone value.
country string? null The country a national number belongs to, in any spelling CkCountry accepts: "GB", "gb", "UK", "United Kingdom". Ignored when the value declares its own country code.
fallback string? null Backup value used when the result would be empty. It goes through the same cleaning and checks as input, with the same country; a fallback that is itself rejected answers "".
throwIfInvalid bool CkSettings.DefaultThrowIfInvalid (false) Pass true to throw a CkIntlException that says why, instead of answering "", when neither the value nor the fallback can be used.

Returns

When Default With throwIfInvalid: true
The value (or the fallback) is a valid international number The E.164 form: "+491511234567" The E.164 form: "+491511234567"
It is a national number and country names a country the library has a calling code for The E.164 form: "+442079460958" The same
Rejected: missing, oversized, national with no country, an unknown country, or structurally invalid Empty string Throws CkIntlException — carries Input, Expected and Reason

Structural only — it does not know each country's numbering plan.

Returns the number in E.164 form — the worldwide standard: a +, then the country code, then the rest, digits only, 7 to 15 in total. Spaces, dots, dashes and parentheses between the digits are fine; a bracketed trunk zero after the country code (the (0) in +49 (0)151 …) is dropped, and so is a trailing extension like x12.

The value may say for itself that it is international — a +, the 00 / 011 exit codes, or an RFC 3966 tel: URI, whose scheme and parameters are not part of the number:

CkIntl.Phone("+1 212 555 0123");           // -> "+12125550123"
CkIntl.Phone("0049 151 1234567");          // -> "+491511234567"  (00 exit code)
CkIntl.Phone("011 44 20 7946 0018");       // -> "+442079460018"  (US exit code)
CkIntl.Phone("+49 151 1234567 x12");       // -> "+491511234567"  (extension dropped)
CkIntl.Phone("tel:+41-44-668-18-00");      // -> "+41446681800"
CkIntl.Phone("TEL:+41446681800;ext=12");   // -> "+41446681800"   (the parameters go too)
CkIntl.Phone("+0 123 4567");               // -> ""  (a country code cannot start with 0)

A national number, with its country

A value with no country code of its own is rejected — unless you say which country it belongs to. Then the country's trunk prefix (the digit dialled at home and dropped abroad) comes off, and its calling code goes in front:

CkIntl.Phone("020 7946 0958", country: "GB");      // -> "+442079460958"  (the trunk 0 comes off)
CkIntl.Phone("(212) 555-0142", country: "US");     // -> "+12125550142"
CkIntl.Phone("1 212 555 0142", country: "US");     // -> "+12125550142"  (the leading 1 is the code)
CkIntl.Phone("06 12345678", country: "IT");        // -> "+390612345678"  (Italy keeps its zero)
CkIntl.Phone("8 495 123-45-67", country: "RU");    // -> "+74951234567"   (Russia's trunk is 8)
CkIntl.Phone("020 7946 0958");                     // -> ""  (national, and nothing says whose)

A declared code always wins

CkIntl.Phone("+44 20 7946 0958", country: "IT") is "+442079460958", not an Italian number. A value that carries its own country code is read as it stands and country is ignored — the same way a date that declares its own offset ignores assumeZone.

Three things about the trunk prefixes are worth knowing:

  • Italy keeps its leading zero. A Roman landline is 06 … at home and +39 06 … abroad, so nothing is stripped. Spain, Portugal, Denmark, Norway, Iceland, Greece, Poland, the Czech Republic, Latvia, Estonia, Malta, Cyprus, Monaco, Luxembourg, Hong Kong, Singapore and a dozen more have no trunk prefix either.
  • Russia and Kazakhstan dial 8, and Hungary dials 06. Belarus writes 8 017 … for +375 17 … — the 0 belongs to the prefix, so 80 comes off, or the 0 alone when the 8 was left out; Lithuania is moving from 8 to 0 and both are read.
  • The North American plan has no trunk prefix of its own, but a leading 1 is the country code — it is read as that and never doubled.

An unknown country is its own rejection, and so is a country the library has no calling code for — Antarctica, Bouvet Island, Heard Island and McDonald Islands and the French Southern Territories have none of their own.

CallingCode

The country code at the front of a number.

string CallingCode(
    string? input = null,
    string? country = null,
    int maxLength = CkSettings.MaxPhoneIntlInputLength,
    string? fallback = null,
    bool throwIfInvalid = CkSettings.DefaultThrowIfInvalid
)
Parameter Type Default What it does
input string? null The raw phone value.
country string? null The country a national number belongs to, as Phone takes it.
maxLength int CkSettings.MaxPhoneIntlInputLength (32) Longest raw value read, capped at the class limit; below 1 counts as 1.
fallback string? null A spare value, held to the same rules.
throwIfInvalid bool CkSettings.DefaultThrowIfInvalid (false) Pass true to throw a CkIntlException that says why, instead of answering "".

Returns

When Default With throwIfInvalid: true
Phone accepts the value and a known calling code opens it The code as one to three digits: "41", "1", "590" The code
Rejected: whatever Phone rejects, with its reason — or a prefix that belongs to no country Empty string Throws CkIntlException — carries Input, Expected and Reason

The code is the E.164 country code proper, never a longer area prefix.

CkIntl.CallingCode("+41 44 668 18 00");             // -> "41"
CkIntl.CallingCode("0049 151 1234567");             // -> "49"
CkIntl.CallingCode("+1 268 464 1234");              // -> "1"   (Antigua shares the plan; it is not "1268")
CkIntl.CallingCode("020 7946 0958", country: "GB"); // -> "44"
CkIntl.CallingCode("+800 1234 5678");               // -> ""    (freephone: no country owns it)

There is no PhoneCountry, on purpose

+1 is the United States, Canada and twenty-odd Caribbean islands; +7 is Russia and Kazakhstan; +44 also covers Guernsey, the Isle of Man and Jersey; +590 covers Guadeloupe, Saint Barthélemy and Saint Martin. Turning a number back into one country needs each country's numbering plan, which this library does not carry. Ask the caller which country a number belongs to — and pass it to Phone as country.

Iban

The international bank account number.

string Iban(
    string? input = null,
    string? fallback = null,
    bool throwIfInvalid = CkSettings.DefaultThrowIfInvalid
)
Parameter Type Default What it does
input string? null The raw value.
fallback string? null Backup value used when the result would be empty. It goes through the same cleaning and checks as input; a fallback that is itself rejected answers "".
throwIfInvalid bool CkSettings.DefaultThrowIfInvalid (false) Pass true to throw a CkIntlException that says why, instead of answering "", when neither the value nor the fallback can be used.

Returns

When Default With throwIfInvalid: true
The value (or the fallback) is a valid IBAN The compact upper-case IBAN: "DE44500105175407324931" The compact upper-case IBAN: "DE44500105175407324931"
Rejected: missing, oversized, malformed, the wrong length for its country, or the check digits fail Empty string Throws CkIntlException — carries Input, Expected and Reason

The check digits prove the number is internally consistent, not that the account exists.

An IBAN is the international bank account number — a two-letter country, two check digits, then the account, up to 34 characters. Iban drops spaces and dashes, upper-cases, checks the country against the official registry, holds the value to that country's exact length (a German IBAN is always 22 characters), and verifies the two check digits with the standard mod-97 test — an arithmetic check that catches nearly every typo and swapped pair of digits.

CkIntl.Iban("de44 5001 0517 5407 3249 31");     // -> "DE44500105175407324931"
CkIntl.Iban("GB82-WEST-1234-5698-7654-32");     // -> "GB82WEST12345698765432"
CkIntl.Iban("DE44500105175407324932");          // -> ""  (one digit off — checksum fails)
CkIntl.Iban("DE44 5001 0517 5407 3249");        // -> ""  (wrong length for DE)

An IBAN is account data — treat the result as sensitive and never log or show it unmasked. That is what IbanMask below is for.

Bic

The bank's identifier.

string Bic(
    string? input = null,
    string? fallback = null,
    bool throwIfInvalid = CkSettings.DefaultThrowIfInvalid
)
Parameter Type Default What it does
input string? null The raw value.
fallback string? null Backup value used when the result would be empty. It goes through the same cleaning and checks as input; a fallback that is itself rejected answers "".
throwIfInvalid bool CkSettings.DefaultThrowIfInvalid (false) Pass true to throw a CkIntlException that says why, instead of answering "", when neither the value nor the fallback can be used.

Returns

When Default With throwIfInvalid: true
The value (or the fallback) is a valid BIC The 8- or 11-character code, upper-cased: "DEUTDEFF" The 8- or 11-character code, upper-cased: "DEUTDEFF"
Rejected: missing, oversized or malformed Empty string Throws CkIntlException — carries Input, Expected and Reason

No check digit — this proves the shape, not that the bank exists.

A BIC (also called a SWIFT code) names the bank itself on an international transfer: four letters of bank code, a two-letter country, a two-character location, and an optional three-character branch — 8 or 11 characters in all, upper-cased.

CkIntl.Bic("deutdeff");      // -> "DEUTDEFF"
CkIntl.Bic("deutdeff500");   // -> "DEUTDEFF500"  (the branch form)
CkIntl.Bic("DEUT DE FF");    // -> "DEUTDEFF"     (spaces dropped)
CkIntl.Bic("DEUTDEFF5");     // -> ""             (not 8 or 11 characters)

A BIC carries no check digit, so this proves the shape only, not that the bank exists — and the two country letters are only held to being letters, not checked against a country list. Pair the BIC with the account's Iban, whose country is.

The two masks, PhoneMask and IbanMask, share one contract. Each mask validates its value first (with Phone or Iban), then hides every letter and digit but the last keepLast (default 4, capped at 4) behind maskChar (default '*'). Unlike everything else in this class, the masks never return empty: a missing or invalid value comes back as a fully masked placeholder, so a screen or a log line never shows a raw or blank value. The placeholders use a middling length on purpose — eleven digits for a phone, twenty characters for an IBAN — so they suggest no particular country. When you also need to know whether the value was valid, call Phone or Iban and check for "".

PostalCode

A postal code, written the way its country writes it.

string PostalCode(
    string? input = null,
    string? country = null,
    int length = 0,
    string? fallback = null,
    bool throwIfInvalid = CkSettings.DefaultThrowIfInvalid
)
Parameter Type Default What it does
input string? null The raw value.
country string? null The country, in any spelling CkCountry accepts: "CA", "Canada", "can". Null to judge by length alone.
length int 0 The exact number of letters and digits, for a country without a fixed shape or no country at all. A country's own rule always wins over it.
fallback string? null A spare value, held to the same rules.
throwIfInvalid bool CkSettings.DefaultThrowIfInvalid (false) Pass true to throw a CkIntlException that says why, instead of answering "", when neither the value nor the fallback can be used.

Returns

When Default With throwIfInvalid: true
The country has a fixed shape and the value fits it The code as that country writes it: "K1A 0B1", "SW1A 1AA", "01310-100", "10115" The code
The country has no fixed shape, or none was given, and the value is the right length The letters and digits alone, upper-cased: "11511", "AB12" The code
Rejected: missing, oversized, an unknown country, not the country's shape, the wrong length, characters beyond letters, digits, spaces and hyphens — or no country and no length "" Throws CkIntlException — carries Input, Expected and Reason

181 countries and territories have a fixed shape here — every one that has a postal code system — Canada's letter rules included (D, F, I, O, Q and U never appear, W and Z never lead) and every outward-code layout of the United Kingdom. The United States, its territories and the freely associated states hand off to CkUS.Zip. Any country outside the table takes three to ten letters and digits, or exactly length of them; with no country at all, length is the rule. Spaces, hyphens and a printed country prefix, "D-10115", "LV-1010", are understood. Shape only: a code of the right shape may still name no real area.

Where a country prints letters in front of the digits, the value reads with them or without, and the answer always carries them:

CkIntl.PostalCode("k1a0b1", "CA");                 // -> "K1A 0B1"
CkIntl.PostalCode("sw1a1aa", "United Kingdom");    // -> "SW1A 1AA"
CkIntl.PostalCode("D-10115", "DE");                // -> "10115"
CkIntl.PostalCode("500", "AD");                    // -> "AD500"    (Andorra prints "AD")
CkIntl.PostalCode("AZ 1000", "AZ");                // -> "AZ 1000"
CkIntl.PostalCode("2001", "MD");                   // -> "MD-2001"
CkIntl.PostalCode("ky11001", "KY");                // -> "KY1-1001"
CkIntl.PostalCode("tkca1zz", "TC");                // -> "TKCA 1ZZ"  (the one code the islands have)
CkIntl.PostalCode("D1A 0B1", "CA");                // -> ""  (D never appears in a Canadian code)
CkIntl.PostalCode("12 34", length: 4);             // -> "1234"  (no country: the length is the rule)
CkIntl.PostalCode("1234");                         // -> ""  (nothing to judge by)

PhoneMask

The phone number, safe for display.

string PhoneMask(
    string? input = null,
    string? country = null,
    int keepLast = CkSettings.DefaultMaskKeepLast,
    char maskChar = '*'
)
Parameter Type Default What it does
input string? null The raw phone value.
country string? null The country a national number belongs to, as Phone takes it — so a national number masks as the E.164 number it means.
keepLast int CkSettings.DefaultMaskKeepLast (4) How many trailing digits stay visible. Capped at CkSettings.MaxMaskKeepLast (4); negatives count as 0.
maskChar char '*' The replacement character. Whitespace and control characters fall back to '*'.

Returns

When You get
The value is valid The masked number: "+********4567"
Missing or invalid The fully masked placeholder: "+***********" — never empty, and it never throws

The display never leaks and never goes blank.

The phone's leading + is neither a letter nor a digit, which is why it stays visible. A valid number keeps its real length under the mask.

CkIntl.PhoneMask("+49 (0)151 1234567");   // -> "+********4567"
CkIntl.PhoneMask("nonsense");             // -> "+***********"  (the placeholder)

CkIntl.PhoneMask(
    input: "+491511234567",
    keepLast: 2
);   // -> "+**********67"

IbanMask

The account number, safe for display.

string IbanMask(
    string? input = null,
    int keepLast = CkSettings.DefaultMaskKeepLast,
    char maskChar = '*'
)
Parameter Type Default What it does
input string? null The raw value.
keepLast int CkSettings.DefaultMaskKeepLast (4) How many trailing characters stay visible. Capped at CkSettings.MaxMaskKeepLast (4); negatives count as 0.
maskChar char '*' The replacement character. Whitespace and control characters fall back to '*'.

Returns

When You get
The value is valid The masked value: "******************4931"
Missing or invalid The fully masked placeholder: "********************" — never empty, and it never throws

The display never leaks and never goes blank.

Works like PhoneMask above, with Iban doing the validating; here everything is hidden, the two country letters included.

CkIntl.IbanMask("DE44 5001 0517 5407 3249 31");   // -> "******************4931"
CkIntl.IbanMask(null);                            // -> "********************"

IsPhone

An explicitly international number?

bool IsPhone(
    string? input = null,
    string? country = null,
    bool throwIfInvalid = CkSettings.DefaultThrowIfInvalid
)
Parameter Type Default What it does
input string? null The raw phone value.
country string? null The country a national number belongs to, as Phone takes it.
throwIfInvalid bool CkSettings.DefaultThrowIfInvalid (false) Pass true to throw a CkIntlException that says why, instead of answering false, when the value is not an international number.

Returns

When Default With throwIfInvalid: true
+, 00, 011 or tel: in front, then a valid E.164 structure true true
A national number, and a country whose calling code the library knows true true
Rejected: a bare national number with no country, a country code starting with 0, too few or too many digits, letters false Throws CkIntlException — carries Input, Expected and Reason

The yes-or-no form of Phone, reading country the same way. US numbers can be judged here with country: "US", but CkUS also enforces the NANP's own rules and is the better reader where every number is American.

CkIntl.IsPhone("+49 (0)151 1234567");              // -> true
CkIntl.IsPhone("(212) 555-0123");                  // -> false  (not international, no country)
CkIntl.IsPhone("(212) 555-0123", country: "US");   // -> true

IsIban

A valid IBAN?

bool IsIban(
    string? input = null,
    bool throwIfInvalid = CkSettings.DefaultThrowIfInvalid
)
Parameter Type Default What it does
input string? null The raw value.
throwIfInvalid bool CkSettings.DefaultThrowIfInvalid (false) Pass true to throw a CkIntlException that says why, instead of answering false, when the value is not an IBAN.

Returns

When Default With throwIfInvalid: true
A known country, that country's exact length, and check digits that pass the mod-97 test true true
Rejected: an unknown country, the wrong length, failing check digits, junk false Throws CkIntlException — carries Input, Expected and Reason

The yes-or-no form of Iban; the check digits catch nearly every typo, as they do there.

CkIntl.IsIban("de44 5001 0517 5407 3249 31");   // -> true
CkIntl.IsIban("DE44500105175407324932");         // -> false  (one digit off)

IsBic

A BIC of the right shape?

bool IsBic(
    string? input = null,
    bool throwIfInvalid = CkSettings.DefaultThrowIfInvalid
)
Parameter Type Default What it does
input string? null The raw value.
throwIfInvalid bool CkSettings.DefaultThrowIfInvalid (false) Pass true to throw a CkIntlException that says why, instead of answering false, when the value is not a BIC.

Returns

When Default With throwIfInvalid: true
Four letters, a two-letter country, a two-character location, an optional three-character branch true true
Rejected: the wrong length, a digit where a letter belongs, junk false Throws CkIntlException — carries Input, Expected and Reason

The yes-or-no form of Bic. A BIC alone proves little — no check digit, no country list — so pair it with IbanMatchesBic.

CkIntl.IsBic("deutdeff500");   // -> true   (the branch form)
CkIntl.IsBic("DEUTDEF");       // -> false  (seven characters)

IsPostalCode

Is it a postal code of that country?

bool IsPostalCode(
    string? input = null,
    string? country = null,
    int length = 0,
    bool throwIfInvalid = CkSettings.DefaultThrowIfInvalid
)
Parameter Type Default What it does
input string? null The raw value.
country string? null The country, in any spelling; null to judge by length alone.
length int 0 The exact number of letters and digits when no country rule applies.
throwIfInvalid bool CkSettings.DefaultThrowIfInvalid (false) Pass true to throw a CkIntlException that says why, instead of answering false, when the value is not a postal code.

Returns

When Default With throwIfInvalid: true
PostalCode would answer a code true true
It would answer "" false Throws CkIntlException — carries Input, Expected and Reason

The yes-or-no form of PostalCode, under exactly its rules.

CkIntl.IsPostalCode("K1A 0B1", "CA");     // -> true
CkIntl.IsPostalCode("D1A 0B1", "CA");     // -> false
CkIntl.IsPostalCode("1234", length: 4);   // -> true

SamePhone

The same line, whatever the spelling?

bool SamePhone(
    string? a = null,
    string? b = null,
    string? country = null,
    bool throwIfInvalid = CkSettings.DefaultThrowIfInvalid
)
Parameter Type Default What it does
a string? null The first value.
b string? null The second value.
country string? null The country a national value belongs to, applied to both sides — so a national number and its international spelling are one line. Ignored for a value that declares its own code.
throwIfInvalid bool CkSettings.DefaultThrowIfInvalid (false) Pass true to throw a CkIntlException that says which value is not an international number and why, instead of answering false.

Returns

When Default With throwIfInvalid: true
Both are valid international numbers and the same one true true
Both are valid, and differ false false
Rejected: either is not an international number false Throws CkIntlException — carries Input, Expected and Reason

"+49 (0)151 1234567", "0049 151 1234567" and "+491511234567" are one line. Extensions are dropped by the reader, as Phone drops them, so two extensions on one line count as the same number. Two values that are not numbers are never the same.

CkIntl.SamePhone("+49 (0)151 1234567", "0049 151 1234567");          // -> true
CkIntl.SamePhone("+49 151 1234567", "0151 1234567");                  // -> false  (one is not international)
CkIntl.SamePhone("+49 151 1234567", "0151 1234567", country: "DE");   // -> true   (now it is)

SameIban

The same account, whatever the spelling?

bool SameIban(
    string? a = null,
    string? b = null,
    bool throwIfInvalid = CkSettings.DefaultThrowIfInvalid
)
Parameter Type Default What it does
a string? null The first value.
b string? null The second value.
throwIfInvalid bool CkSettings.DefaultThrowIfInvalid (false) Pass true to throw a CkIntlException that says which value is not an IBAN and why, instead of answering false.

Returns

When Default With throwIfInvalid: true
Both are valid IBANs and the same one true true
Both are valid, and differ false false
Rejected: either is not an IBAN false Throws CkIntlException — carries Input, Expected and Reason

"de44 5001 0517 5407 3249 31" and "DE44500105175407324931" are one account. For reconciling bank details between a form and a record. Two values that fail their check digits are two unknowns, never the same.

CkIntl.SameIban("de44 5001 0517 5407 3249 31", "DE44500105175407324931");   // -> true

IbanMatchesBic

Do the IBAN and the BIC agree on the country?

bool IbanMatchesBic(
    string? iban = null,
    string? bic = null,
    bool throwIfInvalid = CkSettings.DefaultThrowIfInvalid
)
Parameter Type Default What it does
iban string? null The raw IBAN.
bic string? null The raw BIC.
throwIfInvalid bool CkSettings.DefaultThrowIfInvalid (false) Pass true to throw a CkIntlException that says which value cannot be used and why, instead of answering false.

Returns

When Default With throwIfInvalid: true
Both are valid and the IBAN's first two letters equal the BIC's fifth and sixth true true
Both are valid, and the countries differ false false
Rejected: either cannot be used false Throws CkIntlException — carries Input, Expected and Reason

The consistency check a payment form should run: a BIC alone proves little, and a mismatched pair is the commonest bank-detail typo. Countries only — whether the BIC's bank code matches the bank inside the IBAN would need a registry the library does not carry.

CkIntl.IbanMatchesBic("DE44500105175407324931", "DEUTDEFF");   // -> true
CkIntl.IbanMatchesBic("DE44500105175407324931", "BNPAFRPP");   // -> false  (a French bank)

InCallingCodes

From one of these countries' numbering plans?

bool InCallingCodes(
    string? input = null,
    params IEnumerable<string?> codes
)
Parameter Type Default What it does
input string? null The raw phone value.
codes params IEnumerable<string?> — The calling codes to accept, with or without +. Entries with no digits, or starting with 0, are skipped.

Returns

When You get
The value is a valid international number whose digits start with a listed code true
It is not international, no code matches, or the list is empty false — an answer, not a failure; it never throws

"Which countries' numbers we accept", the SMS-delivery rule. Calling codes are prefix-free by design, so a prefix match is exact: "+49" matches every German number and nothing else. No numbering-plan table is involved: "+1" means the whole North American plan, and "+7" Russia and Kazakhstan alike, because they share it. A longer entry such as "+1212" narrows the match to that area. A params list must be last, so there is no throwIfInvalid; call Phone with it first when you need the exception.

CkIntl.InCallingCodes("+49 151 1234567", "+49", "43", "+41");   // -> true
CkIntl.InCallingCodes("+33 6 12 34 56 78", "+49", "43", "+41");  // -> false

IbanInCountries

A bank in one of these countries?

bool IbanInCountries(
    string? input = null,
    params IEnumerable<string?> countries
)
Parameter Type Default What it does
input string? null The raw IBAN.
countries params IEnumerable<string?> — The countries to accept, in any form CkCountry reads: codes, names, aliases. Entries that are not recognised are skipped.

Returns

When You get
The value is a valid IBAN and an entry names its country true
It is not a valid IBAN, no entry matches, or the list is empty false — an answer, not a failure; it never throws

The SEPA-zone and "banks we can pay" check, with your list: the IBAN's first two letters against each entry read the way CkCountry.Alpha2 reads a country, so "Germany", "de" and "DEU" all name the same one. A params list must be last, so there is no throwIfInvalid; call Iban with it first when you need the exception.

CkIntl.IbanInCountries("DE44500105175407324931", "DE", "France", "NLD");   // -> true
CkIntl.IbanInCountries("GB29 NWBK 6016 1331 9268 19", "DE", "France");     // -> false

Explain

Why not — in words, without an exception.

string Explain(
    string? input = null,
    string? what = nameof(Phone),
    string? country = null,
    int length = 0
)
Parameter Type Default What it does
input string? null The raw value.
what string? nameof(Phone) The reader to judge by: Phone, CallingCode, Iban, Bic or PostalCode, case-insensitive.
country string? null For Phone and CallingCode: the country a national number belongs to. For PostalCode: the country, in any spelling.
length int 0 For PostalCode: the exact number of letters and digits when no country rule applies.

Returns

When You get
The value passes the reader ""
It does not The reason, the same words the exception would carry — an answer, not a failure; it never throws

The form-friendly twin of throwIfInvalid: the message to show beside the field, without a try and catch. Every class with a reader has one, on the reader's own rules, so the reason is exactly what the reader would say. what names the reader to judge by.

CkIntl.Explain("(212) 555-0123");                      // -> "no international prefix: it must start with +, 00 or 011"
CkIntl.Explain("DEUTDEF", nameof(CkIntl.Bic));          // -> "not 8 or 11 characters"
CkIntl.Explain("(212) 555-0123", country: "US");        // -> ""  (a country makes it readable)

The fallback

Phone, CallingCode, Iban, Bic and PostalCode each take a fallback — a spare value used when the input is rejected. The fallback goes through the exact same cleaning and checking; a bad fallback answers "" too. The masks take no fallback — their placeholder is the fallback — and neither do the validators, whose answer is false.

CkIntl.Phone(
    input: "n/a",
    fallback: "+1 212 555 0123"
);   // -> "+12125550123"

Good to know

  • Non-empty in, empty out means rejected. That is the standard CommonKit signal — check for "" where it matters. Need null instead? Wrap the call in CkText.NullIfEmpty(...) — see How it works.
  • Phone never guesses a country. No +, 00, 011 or tel: at the front means rejection, unless you pass country and say whose national number it is. US numbers like (212) 555-0123 are also at home in CkUS's Phone, which enforces the NANP's own rules on top.
  • country is ignored when the value declares its own code. A declared code always wins, and there is no PhoneCountry to go the other way — see CallingCode.
  • The checks are structural, not real-world. Phone does not know each country's numbering plan, and an IBAN's check digits prove the number is internally consistent, not that the account exists.
  • Extensions are dropped, not returned. x12, ext. 12 or #12 at the end disappears from the result; keep it separately if you need it.
  • The masks never return empty — by design. An invalid value looks exactly like a hidden valid one; a display has nothing to leak either way.
  • A BIC alone proves little. No check digit, and its country letters are not checked against a country list — validate the matching IBAN too, and run IbanMatchesBic on the pair.
  • The validators answer false for junk too. IsPhone, IsIban, IsBic and the pair checks answer false both for "no" and for "not usable"; pass throwIfInvalid: true when the difference matters. InCallingCodes and IbanInCountries never take the flag.
  • InCallingCodes knows no numbering plan. It matches the codes you list as prefixes of the E.164 digits, which is exact because calling codes are prefix-free; it cannot tell Russia from Kazakhstan on +7, and neither can anyone without a table.

If you want errors instead

Pass throwIfInvalid: true to Phone, CallingCode, Iban, Bic, PostalCode or any validator but the two params ones, and a value that cannot be used raises a CkIntlException (from SafeCoder.CommonKit.Exceptions) instead of answering "" or false. The exception carries the Input, what was Expected and a short Reason, flattened into a one-line message. The masks take no throwIfInvalid — they always answer with a masked shape.

CkIntl.Iban(
    input: "DE44500105175407324932",
    throwIfInvalid: true
);   // throws CkIntlException — "the check digits do not match"