IBAN Validator — Ruby source
Validate International Bank Account Numbers (IBAN) with the mod-97 checksum, verify the country-specific length, and format the result. 100% client-side, no network.
This is the Ruby implementation — the same logic the interactive tool runs, in a shareable, citable form.
# iban-validator — pure IBAN validation logic (ISO 13616 mod-97 checksum).
#
# Language: Ruby (Ruby 3.2, standard library only)
# Source: CosmoDev polyglot showcase port of the iban-validator tool,
# ported from src/lib/iban.ts (the canonical TypeScript
# implementation).
# License: display source — part of CosmoDev's polyglot tool pages
#
# Normalizes the input (uppercase, strip whitespace/dashes), checks the
# structural regex (country code + 2 check digits + 1-30 BBAN chars),
# verifies the per-country length, and runs the ISO 13616 mod-97
# checksum: move the first 4 chars to the end, map A=10..Z=35, and
# confirm the resulting integer is congruent to 1 mod 97. Ruby ints are
# arbitrary precision, but the remainder is folded one digit at a time
# (it stays under 97) to mirror the reference algorithm exactly.
# validate_iban never raises — it reports failures through `error`.
# Per-country IBAN lengths (ISO 13616) — a representative subset.
IBAN_LENGTHS = {
'AL' => 28, 'AD' => 24, 'AT' => 20, 'AZ' => 28, 'BH' => 22, 'BY' => 28,
'BE' => 16, 'BA' => 20, 'BR' => 29, 'BG' => 22, 'CR' => 22, 'HR' => 21,
'CY' => 28, 'CZ' => 24, 'DK' => 18, 'DO' => 28, 'EE' => 20, 'FO' => 18,
'FI' => 18, 'FR' => 27, 'GE' => 22, 'DE' => 22, 'GI' => 23, 'GR' => 27,
'GL' => 18, 'GT' => 28, 'HU' => 28, 'IS' => 26, 'IE' => 22, 'IL' => 23,
'IT' => 27, 'JO' => 30, 'KZ' => 20, 'XK' => 20, 'KW' => 30, 'LV' => 21,
'LB' => 28, 'LI' => 21, 'LT' => 20, 'LU' => 20, 'MK' => 19, 'MT' => 31,
'MR' => 27, 'MU' => 30, 'MC' => 27, 'MD' => 24, 'ME' => 22, 'NL' => 18,
'NO' => 15, 'PK' => 24, 'PS' => 29, 'PL' => 28, 'PT' => 25, 'QA' => 29,
'RO' => 24, 'LC' => 32, 'SM' => 27, 'ST' => 25, 'SA' => 24, 'RS' => 22,
'SC' => 31, 'SK' => 24, 'SI' => 19, 'SG' => 19, 'ES' => 24, 'SE' => 24,
'CH' => 21, 'TL' => 23, 'TN' => 24, 'TR' => 26, 'UA' => 29, 'AE' => 23,
'GB' => 22, 'VG' => 24,
}.freeze
STRUCTURE = /\A[A-Z]{2}[0-9]{2}[A-Z0-9]{1,30}\z/.freeze
CC_HEAD = /\A[A-Z]{2}/.freeze
# Result of validation. nil fields mirror the TS nullable fields.
IbanInfo = Struct.new(
:input, :cleaned, :country_code, :valid, :checksum_ok, :length_ok,
:expected_length, :formatted, :error,
keyword_init: true
)
# ISO 13616 mod-97 checksum over a CLEANED iban (uppercase, no spaces).
def mod97_check(cleaned)
return false if cleaned.length < 4
# Move the first 4 chars (country + check) to the end.
rearranged = cleaned[4..] + cleaned[...4]
numeric = +''
rearranged.each_char do |ch|
case ch
when '0'..'9' then numeric << ch
when 'A'..'Z' then numeric << (ch.ord - 55).to_s # A=10 .. Z=35
else return false # invalid character
end
end
rem = 0
numeric.each_char { |d| rem = (rem * 10 + d.ord - 48) % 97 }
rem == 1
end
# Validate an IBAN. Always returns IbanInfo; never raises.
def validate_iban(input)
src = input.to_s
cleaned = src.upcase.gsub(/[\s-]/, '')
cc = cleaned[CC_HEAD]
expected = cc && IBAN_LENGTHS[cc]
info = IbanInfo.new(
input: src,
cleaned: cleaned,
country_code: cc,
expected_length: expected,
# Insert a space every 4 chars (last group may be short).
formatted: cleaned.gsub(/(.{4})(?=.)/, '\1 ').strip
)
unless cleaned =~ STRUCTURE
info.error = 'Invalid IBAN format.'
return info
end
info.checksum_ok = mod97_check(cleaned)
info.length_ok = expected.nil? || cleaned.length == expected
if info.length_ok && info.checksum_ok
info.valid = true
return info
end
unless info.length_ok
info.error = "Length should be #{expected} for #{cc}."
return info
end
info.error = 'Checksum failed.'
info
end
if __FILE__ == $PROGRAM_NAME
# GB82 WEST 1234 5698 7654 32 — a known-good reference IBAN.
r = validate_iban('GB82WEST12345698765432')
puts "valid=#{r.valid} error=#{r.error || '(none)'}"
end
Also available in 13 other languages
Every CosmoDev tool ships its pure logic in TypeScript (web) and Go (CLI), with authored implementations in a dozen-plus languages — the same contract, ported. Compare all languages side by side →