From 8930e97a8f42d576947cca57e2e02ff10ebfd46d Mon Sep 17 00:00:00 2001 From: Oleh Kobeliatskyi <50925524+poputu@users.noreply.github.com> Date: Tue, 20 Oct 2026 10:12:34 +0300 Subject: [PATCH 1/2] Quoted cells can now span lines, so the bank-position row no longer stops at Slovenia. Added bank_identifier to SK, SM, SO, ST, SV, TL, TN, TR, UA, VA, VG, XK. --- script/builder.rb | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/script/builder.rb b/script/builder.rb index b6d4ba7f..43b4ec9b 100644 --- a/script/builder.rb +++ b/script/builder.rb @@ -1,5 +1,7 @@ # frozen_string_literal: true +require 'csv' + # Download: https://www.swift.com/swift-resource/11971/download # or whatever the file path is on this page: # https://www.swift.com/standards/data-standards/iban-international-bank-account-number @@ -42,8 +44,9 @@ def create_specification(data) end def extract - data = File.readlines('./script/registry.txt').each_with_object([]) do |line, result| - data = line.force_encoding("iso-8859-1").split("\t").map { |e| e.strip } + rows = CSV.read('./script/registry.txt', col_sep: "\t", encoding: 'iso-8859-1') + data = rows.each_with_object([]) do |row, result| + data = row.map { |e| e.to_s.strip } data[1..].each_with_index { |element, index| result[index] = { country_name: element } } if data.first == 'Name of country' data[1..].each_with_index { |element, index| result[index][:code] = element } if data.first == 'IBAN prefix country code (ISO 3166)' data[1..].each_with_index { |element, index| result[index][:sepa] = element == 'Yes' } if data.first == 'SEPA country' @@ -76,10 +79,10 @@ def fix_ranges(ranges) def type_to_range(type) return '[A-Z0-9]' if type == 'c' return '[0-9]' if type == 'n' - + '[A-Z]' end - + def build_reqexp(format) data = format.scan(/([0-9]+)!(.)/) '^' + data.map { |(amount, type)| "#{type_to_range(type)}{#{amount}}" }.join + '$' From d6bd461030d3a5c407d48d50d2c4ff1e36f23a89 Mon Sep 17 00:00:00 2001 From: Oleh Kobeliatskyi <50925524+poputu@users.noreply.github.com> Date: Tue, 20 Oct 2026 10:12:34 +0300 Subject: [PATCH 2/2] Added bank_identifier to SK, SM, SO, ST, SV, TL, TN, TR, UA, VA, VG, XK. --- ChangeLog | 4 ++++ script/iban_spec.js | 17 ++++++++++++++--- src/ibantools.ts | 12 ++++++++++++ test/ibantools_test.js | 40 ++++++++++++++++++++++++++++++++++++++++ 4 files changed, 70 insertions(+), 3 deletions(-) diff --git a/ChangeLog b/ChangeLog index 65b4efed..dd523fee 100755 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,7 @@ +2026-10-20 Oleh Kobeliatskyi <@poputu> + * Fixed bank identifier extraction for SK, SM, SO, ST, SV, TL, TN, TR, UA, VA, VG and XK + * Fixed script/builder.rb skipping registry values after a cell that spans two lines + 2026-04-10 Saša Jovanić * Version 4.5.4 diff --git a/script/iban_spec.js b/script/iban_spec.js index 4126c6f5..f7aed86d 100644 --- a/script/iban_spec.js +++ b/script/iban_spec.js @@ -598,15 +598,15 @@ export const autogeneratedCountrySpecs = { bban_regexp: '^[0-9]{5}[0-9]{8}[0-9]{2}$', IBANRegistry: true, SEPA: true, - branch_indentifier: '2-4', - bank_identifier: '0-1', - account_indentifier: '9-16', + bank_identifier: '0-4', + account_indentifier: '4-19', }, SK: { chars: 24, bban_regexp: '^[0-9]{4}[0-9]{6}[0-9]{10}$', IBANRegistry: true, SEPA: true, + bank_identifier: '0-3', }, SM: { chars: 27, @@ -614,6 +614,7 @@ export const autogeneratedCountrySpecs = { IBANRegistry: true, SEPA: true, branch_indentifier: '6-10', + bank_identifier: '1-5', }, SO: { chars: 23, @@ -621,6 +622,7 @@ export const autogeneratedCountrySpecs = { IBANRegistry: true, SEPA: false, branch_indentifier: '4-6', + bank_identifier: '0-3', account_indentifier: '11-23', }, ST: { @@ -629,12 +631,14 @@ export const autogeneratedCountrySpecs = { IBANRegistry: true, SEPA: false, branch_indentifier: '4-7', + bank_identifier: '0-3', }, SV: { chars: 28, bban_regexp: '^[A-Z]{4}[0-9]{20}$', IBANRegistry: true, SEPA: false, + bank_identifier: '0-3', account_indentifier: '8-28', }, TL: { @@ -642,6 +646,7 @@ export const autogeneratedCountrySpecs = { bban_regexp: '^[0-9]{3}[0-9]{14}[0-9]{2}$', IBANRegistry: true, SEPA: false, + bank_identifier: '0-2', account_indentifier: '4-23', }, TN: { @@ -650,6 +655,7 @@ export const autogeneratedCountrySpecs = { IBANRegistry: true, SEPA: false, branch_indentifier: '2-4', + bank_identifier: '0-1', account_indentifier: '4-24', }, TR: { @@ -657,6 +663,7 @@ export const autogeneratedCountrySpecs = { bban_regexp: '^[0-9]{5}[0-9]{1}[A-Z0-9]{16}$', IBANRegistry: true, SEPA: false, + bank_identifier: '0-4', account_indentifier: '0-0', }, UA: { @@ -664,6 +671,7 @@ export const autogeneratedCountrySpecs = { bban_regexp: '^[0-9]{6}[A-Z0-9]{19}$', IBANRegistry: true, SEPA: false, + bank_identifier: '0-5', account_indentifier: '15-29', }, VA: { @@ -671,6 +679,7 @@ export const autogeneratedCountrySpecs = { bban_regexp: '^[0-9]{3}[0-9]{15}$', IBANRegistry: true, SEPA: true, + bank_identifier: '0-2', account_indentifier: '7-22', }, VG: { @@ -678,6 +687,7 @@ export const autogeneratedCountrySpecs = { bban_regexp: '^[A-Z]{4}[0-9]{16}$', IBANRegistry: true, SEPA: false, + bank_identifier: '0-3', account_indentifier: '8-24', }, XK: { @@ -686,6 +696,7 @@ export const autogeneratedCountrySpecs = { IBANRegistry: true, SEPA: false, branch_indentifier: '2-3', + bank_identifier: '0-1', account_indentifier: '4-20', }, }; diff --git a/src/ibantools.ts b/src/ibantools.ts index f565bdd3..101e07aa 100755 --- a/src/ibantools.ts +++ b/src/ibantools.ts @@ -1706,6 +1706,7 @@ export const countrySpecs: CountryMapInternal = { bban_validation_func: checkCzechAndSlovakBBAN, IBANRegistry: true, SEPA: true, + bank_identifier: '0-3', }, SL: {}, SM: { @@ -1714,6 +1715,7 @@ export const countrySpecs: CountryMapInternal = { IBANRegistry: true, SEPA: true, branch_indentifier: '6-10', + bank_identifier: '1-5', }, SN: { chars: 28, @@ -1724,6 +1726,7 @@ export const countrySpecs: CountryMapInternal = { bban_regexp: '^[0-9]{19}$', IBANRegistry: true, branch_indentifier: '4-6', + bank_identifier: '0-3', account_indentifier: '11-23', }, SR: {}, @@ -1733,11 +1736,13 @@ export const countrySpecs: CountryMapInternal = { bban_regexp: '^[0-9]{21}$', IBANRegistry: true, branch_indentifier: '4-7', + bank_identifier: '0-3', }, SV: { chars: 28, bban_regexp: '^[A-Z]{4}[0-9]{20}$', IBANRegistry: true, + bank_identifier: '0-3', account_indentifier: '8-28', }, SX: {}, @@ -1764,6 +1769,7 @@ export const countrySpecs: CountryMapInternal = { chars: 23, bban_regexp: '^[0-9]{19}$', IBANRegistry: true, + bank_identifier: '0-2', account_indentifier: '4-23', }, TM: {}, @@ -1772,6 +1778,7 @@ export const countrySpecs: CountryMapInternal = { bban_regexp: '^[0-9]{20}$', IBANRegistry: true, branch_indentifier: '2-4', + bank_identifier: '0-1', account_indentifier: '4-24', }, TO: {}, @@ -1779,6 +1786,7 @@ export const countrySpecs: CountryMapInternal = { chars: 26, bban_regexp: '^[0-9]{5}[A-Z0-9]{17}$', IBANRegistry: true, + bank_identifier: '0-4', }, TT: {}, TV: {}, @@ -1788,6 +1796,7 @@ export const countrySpecs: CountryMapInternal = { chars: 29, bban_regexp: '^[0-9]{6}[A-Z0-9]{19}$', IBANRegistry: true, + bank_identifier: '0-5', account_indentifier: '15-29', }, UG: {}, @@ -1800,6 +1809,7 @@ export const countrySpecs: CountryMapInternal = { bban_regexp: '^[0-9]{18}', IBANRegistry: true, SEPA: true, + bank_identifier: '0-2', account_indentifier: '7-22', }, VC: {}, @@ -1808,6 +1818,7 @@ export const countrySpecs: CountryMapInternal = { chars: 24, bban_regexp: '^[A-Z0-9]{4}[0-9]{16}$', IBANRegistry: true, + bank_identifier: '0-3', account_indentifier: '8-24', }, VI: {}, @@ -1824,6 +1835,7 @@ export const countrySpecs: CountryMapInternal = { bban_regexp: '^[0-9]{16}$', IBANRegistry: true, branch_indentifier: '2-3', + bank_identifier: '0-1', account_indentifier: '4-20', }, YE: { diff --git a/test/ibantools_test.js b/test/ibantools_test.js index 037a9952..76fe1501 100644 --- a/test/ibantools_test.js +++ b/test/ibantools_test.js @@ -775,6 +775,46 @@ describe('IBANTools', function() { }); }); + describe('When calling extractIBAN() with valid Ukrainian IBAN', function() { + var ext = iban.extractIBAN('UA213223130000026007233566001'); + it('valid should be true', function() { + return expect(ext.valid).to.be.true; + }); + it('BBAN should be 3223130000026007233566001', function() { + return expect(ext.bban).to.equal('3223130000026007233566001'); + }); + it('countryCode should be UA', function() { + return expect(ext.countryCode).to.equal('UA'); + }); + it('accountNumber should be 26007233566001', function() { + return expect(ext.accountNumber).to.equal('26007233566001'); + }); + it('bankIdentifier should be 322313', function() { + return expect(ext.bankIdentifier).to.equal('322313'); + }); + }); + + describe('When calling extractIBAN() with valid IBANs from SK, SM, SO, ST, SV, TL, TN, TR, UA, VA, VG and XK', function() { + [ + ['SK3112000000198742637541', '1200'], + ['SM86U0322509800000000270100', '03225'], + ['SO211000001001000100141', '1000'], + ['ST68000100010051845310112', '0001'], + ['SV62CENR00000000000000700025', 'CENR'], + ['TL380080012345678910157', '008'], + ['TN5910006035183598478831', '10'], + ['TR330006100519786457841326', '00061'], + ['UA213223130000026007233566001', '322313'], + ['VA59001123000012345678', '001'], + ['VG96VPVG0000012345678901', 'VPVG'], + ['XK051212012345678906', '12'], + ].forEach(function(example) { + it('bankIdentifier of ' + example[0] + ' should be ' + example[1], function() { + return expect(iban.extractIBAN(example[0]).bankIdentifier).to.equal(example[1]); + }); + }); + }); + describe('When calling extractIBAN() with invalid IBAN', function() { var ext = iban.extractIBAN('BR970036030510009795493P1'); it('valid should be false', function() {