From df59a76da82798cf4bdb8614ab6f3e65661d3ca4 Mon Sep 17 00:00:00 2001 From: cemdrman Date: Sat, 28 Mar 2026 01:13:13 +0300 Subject: [PATCH] feat: add Turkish (TR) locale support --- .../LocaleSpecificProvidersFactory.java | 16 ++++++ .../tr/TrVATIdentificationNumberProvider.java | 47 +++++++++++++++++ .../jfairy/producer/person/Country.java | 3 +- .../producer/person/locale/tr/TrAddress.java | 20 ++++++++ .../person/locale/tr/TrAddressProvider.java | 18 +++++++ .../TrNationalIdentityCardNumberProvider.java | 51 +++++++++++++++++++ .../locale/tr/TrPassportNumberProvider.java | 18 +++++++ .../jfairy/producer/util/LanguageCode.java | 3 +- src/main/resources/jfairy_tr.yml | 28 ++++++++++ ...VATIdentificationNumberProviderSpec.groovy | 33 ++++++++++++ .../person/locale/tr/TrAddressSpec.groovy | 15 ++++++ ...ionalIdentityCardNumberProviderSpec.groovy | 50 ++++++++++++++++++ 12 files changed, 300 insertions(+), 2 deletions(-) create mode 100644 src/main/java/com/devskiller/jfairy/producer/company/locale/tr/TrVATIdentificationNumberProvider.java create mode 100644 src/main/java/com/devskiller/jfairy/producer/person/locale/tr/TrAddress.java create mode 100644 src/main/java/com/devskiller/jfairy/producer/person/locale/tr/TrAddressProvider.java create mode 100644 src/main/java/com/devskiller/jfairy/producer/person/locale/tr/TrNationalIdentityCardNumberProvider.java create mode 100644 src/main/java/com/devskiller/jfairy/producer/person/locale/tr/TrPassportNumberProvider.java create mode 100644 src/main/resources/jfairy_tr.yml create mode 100644 src/test/groovy/com/devskiller/jfairy/producer/company/locale/tr/TrVATIdentificationNumberProviderSpec.groovy create mode 100644 src/test/groovy/com/devskiller/jfairy/producer/person/locale/tr/TrAddressSpec.groovy create mode 100644 src/test/groovy/com/devskiller/jfairy/producer/person/locale/tr/TrNationalIdentityCardNumberProviderSpec.groovy diff --git a/src/main/java/com/devskiller/jfairy/LocaleSpecificProvidersFactory.java b/src/main/java/com/devskiller/jfairy/LocaleSpecificProvidersFactory.java index 2a6c0ba2..81474348 100644 --- a/src/main/java/com/devskiller/jfairy/LocaleSpecificProvidersFactory.java +++ b/src/main/java/com/devskiller/jfairy/LocaleSpecificProvidersFactory.java @@ -17,6 +17,7 @@ import com.devskiller.jfairy.producer.company.locale.pl.PlVATIdentificationNumberProvider; import com.devskiller.jfairy.producer.company.locale.sk.SkVATIdentificationNumberProvider; import com.devskiller.jfairy.producer.company.locale.sv.SvVATIdentificationNumberProvider; +import com.devskiller.jfairy.producer.company.locale.tr.TrVATIdentificationNumberProvider; import com.devskiller.jfairy.producer.company.locale.zh.ZhVATIdentificationNumberProvider; import com.devskiller.jfairy.producer.person.NationalIdentificationNumberFactory; import com.devskiller.jfairy.producer.person.locale.NoNationalIdentificationNumberFactory; @@ -50,6 +51,9 @@ import com.devskiller.jfairy.producer.person.locale.sv.SvNationalIdentificationNumberFactory; import com.devskiller.jfairy.producer.person.locale.sv.SvNationalIdentityCardNumberProvider; import com.devskiller.jfairy.producer.person.locale.sv.SvPassportNumberProvider; +import com.devskiller.jfairy.producer.person.locale.tr.TrAddressProvider; +import com.devskiller.jfairy.producer.person.locale.tr.TrNationalIdentityCardNumberProvider; +import com.devskiller.jfairy.producer.person.locale.tr.TrPassportNumberProvider; import com.devskiller.jfairy.producer.person.locale.zh.ZhAddressProvider; import com.devskiller.jfairy.producer.person.locale.zh.ZhNationalIdentityCardNumberProvider; import com.devskiller.jfairy.producer.person.locale.zh.ZhPassportNumberProvider; @@ -89,6 +93,7 @@ static LocaleSpecificProviders createProvidersForLocale(Locale locale, case DE -> createDeProviders(dataMaster, baseProducer); case KA -> createKaProviders(dataMaster, baseProducer); case BR -> createBrProviders(dataMaster, baseProducer); + case TR -> createTrProviders(dataMaster, baseProducer); }; } @@ -232,4 +237,15 @@ private static LocaleSpecificProviders createBrProviders(DataMaster dataMaster, new BrPassportNumberProvider() ); } + + private static LocaleSpecificProviders createTrProviders(DataMaster dataMaster, + BaseProducer baseProducer) { + return new LocaleSpecificProviders( + new NoNationalIdentificationNumberFactory(), + new TrNationalIdentityCardNumberProvider(baseProducer), + new TrVATIdentificationNumberProvider(baseProducer), + new TrAddressProvider(dataMaster, baseProducer), + new TrPassportNumberProvider(baseProducer) + ); + } } diff --git a/src/main/java/com/devskiller/jfairy/producer/company/locale/tr/TrVATIdentificationNumberProvider.java b/src/main/java/com/devskiller/jfairy/producer/company/locale/tr/TrVATIdentificationNumberProvider.java new file mode 100644 index 00000000..f540c95d --- /dev/null +++ b/src/main/java/com/devskiller/jfairy/producer/company/locale/tr/TrVATIdentificationNumberProvider.java @@ -0,0 +1,47 @@ +package com.devskiller.jfairy.producer.company.locale.tr; + +import com.devskiller.jfairy.producer.BaseProducer; +import com.devskiller.jfairy.producer.VATIdentificationNumberProvider; + +/** + * Turkish VKN (Vergi Kimlik Numarası) generator. + * 10 digits. + * Last digit is a check digit. + */ +public class TrVATIdentificationNumberProvider implements VATIdentificationNumberProvider { + + private final BaseProducer baseProducer; + + public TrVATIdentificationNumberProvider(BaseProducer baseProducer) { + this.baseProducer = baseProducer; + } + + @Override + public String get() { + int[] digits = new int[10]; + + for (int i = 0; i < 9; i++) { + digits[i] = baseProducer.randomBetween(0, 9); + } + + int sum = 0; + for (int i = 0; i < 9; i++) { + int p = (digits[i] + 10 - (i + 1)) % 10; + if (p == 9) { + sum += 9; + } else { + int q = (int) (p * Math.pow(2, 9 - i)) % 9; + sum += q; + } + } + + int c1 = (10 - (sum % 10)) % 10; + digits[9] = c1; + + StringBuilder sb = new StringBuilder(); + for (int digit : digits) { + sb.append(digit); + } + return sb.toString(); + } +} diff --git a/src/main/java/com/devskiller/jfairy/producer/person/Country.java b/src/main/java/com/devskiller/jfairy/producer/person/Country.java index 0d535fce..dd7016b2 100644 --- a/src/main/java/com/devskiller/jfairy/producer/person/Country.java +++ b/src/main/java/com/devskiller/jfairy/producer/person/Country.java @@ -21,7 +21,8 @@ public enum Country { Sweden("SE", LanguageCode.SV), China("CN", LanguageCode.ZH), Brazil("BR", LanguageCode.BR), - Slovakia("SK", LanguageCode.SK); + Slovakia("SK", LanguageCode.SK), + Turkey("TR", LanguageCode.TR); // ISO 3166 code private final String code; diff --git a/src/main/java/com/devskiller/jfairy/producer/person/locale/tr/TrAddress.java b/src/main/java/com/devskiller/jfairy/producer/person/locale/tr/TrAddress.java new file mode 100644 index 00000000..8280ab2a --- /dev/null +++ b/src/main/java/com/devskiller/jfairy/producer/person/locale/tr/TrAddress.java @@ -0,0 +1,20 @@ +package com.devskiller.jfairy.producer.person.locale.tr; + +import com.devskiller.jfairy.producer.person.locale.ContinentalAddress; + +public class TrAddress extends ContinentalAddress { + + public TrAddress(String streetNumber, String street, String apartmentNumber, String postalCode, String city) { + super(street, streetNumber, apartmentNumber, postalCode, city); + } + + @Override + protected String getStreetNumberSeparator() { + return " No: "; + } + + @Override + protected String getApartmentMark() { + return " Daire: "; + } +} diff --git a/src/main/java/com/devskiller/jfairy/producer/person/locale/tr/TrAddressProvider.java b/src/main/java/com/devskiller/jfairy/producer/person/locale/tr/TrAddressProvider.java new file mode 100644 index 00000000..4deac665 --- /dev/null +++ b/src/main/java/com/devskiller/jfairy/producer/person/locale/tr/TrAddressProvider.java @@ -0,0 +1,18 @@ +package com.devskiller.jfairy.producer.person.locale.tr; + +import com.devskiller.jfairy.data.DataMaster; +import com.devskiller.jfairy.producer.BaseProducer; +import com.devskiller.jfairy.producer.person.AbstractAddressProvider; + +public class TrAddressProvider extends AbstractAddressProvider { + + public TrAddressProvider(DataMaster dataMaster, BaseProducer baseProducer) { + super(dataMaster, baseProducer); + } + + @Override + public TrAddress get() { + return new TrAddress(getStreetNumber(), getStreet(), getApartmentNumber(), + getPostalCode(), getCity()); + } +} diff --git a/src/main/java/com/devskiller/jfairy/producer/person/locale/tr/TrNationalIdentityCardNumberProvider.java b/src/main/java/com/devskiller/jfairy/producer/person/locale/tr/TrNationalIdentityCardNumberProvider.java new file mode 100644 index 00000000..5601d2f4 --- /dev/null +++ b/src/main/java/com/devskiller/jfairy/producer/person/locale/tr/TrNationalIdentityCardNumberProvider.java @@ -0,0 +1,51 @@ +package com.devskiller.jfairy.producer.person.locale.tr; + +import com.devskiller.jfairy.producer.BaseProducer; +import com.devskiller.jfairy.producer.person.NationalIdentityCardNumberProvider; + +/** + * Turkish T.C. Kimlik No generator. + * 11 digits. + * 1st digit cannot be 0. + * 10th digit = ((sum(1,3,5,7,9) * 7) - sum(2,4,6,8)) % 10. + * 11th digit = (sum(1..10)) % 10. + */ +public class TrNationalIdentityCardNumberProvider implements NationalIdentityCardNumberProvider { + + private final BaseProducer baseProducer; + + public TrNationalIdentityCardNumberProvider(BaseProducer baseProducer) { + this.baseProducer = baseProducer; + } + + @Override + public String get() { + int[] digits = new int[11]; + + digits[0] = baseProducer.randomBetween(1, 9); + for (int i = 1; i < 9; i++) { + digits[i] = baseProducer.randomBetween(0, 9); + } + + int oddSum = digits[0] + digits[2] + digits[4] + digits[6] + digits[8]; + int evenSum = digits[1] + digits[3] + digits[5] + digits[7]; + + int d10 = ((oddSum * 7) - evenSum) % 10; + if (d10 < 0) { + d10 += 10; + } + digits[9] = d10; + + int totalSum = 0; + for (int i = 0; i < 10; i++) { + totalSum += digits[i]; + } + digits[10] = totalSum % 10; + + StringBuilder sb = new StringBuilder(); + for (int digit : digits) { + sb.append(digit); + } + return sb.toString(); + } +} diff --git a/src/main/java/com/devskiller/jfairy/producer/person/locale/tr/TrPassportNumberProvider.java b/src/main/java/com/devskiller/jfairy/producer/person/locale/tr/TrPassportNumberProvider.java new file mode 100644 index 00000000..8cdb036a --- /dev/null +++ b/src/main/java/com/devskiller/jfairy/producer/person/locale/tr/TrPassportNumberProvider.java @@ -0,0 +1,18 @@ +package com.devskiller.jfairy.producer.person.locale.tr; + +import com.devskiller.jfairy.producer.BaseProducer; +import com.devskiller.jfairy.producer.person.PassportNumberProvider; + +public class TrPassportNumberProvider implements PassportNumberProvider { + + private final BaseProducer baseProducer; + + public TrPassportNumberProvider(BaseProducer baseProducer) { + this.baseProducer = baseProducer; + } + + @Override + public String get() { + return "U" + String.format("%08d", baseProducer.randomBetween(1, 99999999)); + } +} diff --git a/src/main/java/com/devskiller/jfairy/producer/util/LanguageCode.java b/src/main/java/com/devskiller/jfairy/producer/util/LanguageCode.java index e81c818e..d271052d 100644 --- a/src/main/java/com/devskiller/jfairy/producer/util/LanguageCode.java +++ b/src/main/java/com/devskiller/jfairy/producer/util/LanguageCode.java @@ -20,5 +20,6 @@ public enum LanguageCode { SV, ZH, BR, - SK + SK, + TR } diff --git a/src/main/resources/jfairy_tr.yml b/src/main/resources/jfairy_tr.yml new file mode 100644 index 00000000..22f95bcf --- /dev/null +++ b/src/main/resources/jfairy_tr.yml @@ -0,0 +1,28 @@ +alphabet: abcçdefgğhıijklmnoöprsştuüvyz +domains: [ com.tr, org.tr, net.tr, edu.tr, gov.tr, k12.tr, av.tr, dr.tr, bel.tr, tsk.tr ] +firstNames: { + male: [ Ahmet, Mehmet, Mustafa, Ali, Hüseyin, Hasan, İbrahim, İsmail, Osman, Murat, Ömer, Ramazan, Halil, Süleyman, Abdullah, Mahmut, Recep, Salih, Fatih, Kadir, Emre, Gökhan, Hakan, Serkan, Metin, Burak, Volkan, Bülent, Kenan, Selçuk, Yavuz, Sinan, Can, Kerem, Deniz, Onur, Umut, Eren, Arda, Mert, Yiğit, Berk, Tolga, Kaan, Doruk, Batuhan, Görkem, Furkan, Enes, Yunus, Bilal ], + female: [ Fatma, Ayşe, Emine, Hatice, Zeynep, Özlem, Elif, Meryem, Gamze, Şerife, Hanife, Sultan, Zehra, Havva, Hayriye, Cemile, Güllü, Leyla, Saliha, Melek, Arzu, Yasemin, Esra, Dilek, Pınar, Tuğba, Seda, Ebru, Gizem, Büşra, Kübra, Feyza, Beyza, İrem, Merve, Nursel, Selin, Pelin, Damla, Yağmur, Ece, Duru, Defne, Azra, Derin, Bade, Nehir, Eylül, Melis ], +} +lastNames: { + male: [ Yılmaz, Kaya, Demir, Çelik, Şahin, Yıldız, Yıldırım, Öztürk, Aydın, Özdemir, Arslan, Doğan, Kılıç, Aslan, Çetin, Kara, Koç, Kurt, Özkan, Şimşek, Polat, Özcan, Korkmaz, Çakır, Erdoğan, Yavuz, Can, Aksoy, Köse, Güler, Aktaş, Avcı, Akın, Özer, Ünal, Kaplan, Bulut, Yiğit, Sarı, Taş, Kaymak, Önal, Güneş, Ay, Karaman, Çavuş, Yalçın, Uzun, Karakaş, Tekin ], + female: [ Yılmaz, Kaya, Demir, Çelik, Şahin, Yıldız, Yıldırım, Öztürk, Aydın, Özdemir, Arslan, Doğan, Kılıç, Aslan, Çetin, Kara, Koç, Kurt, Özkan, Şimşek, Polat, Özcan, Korkmaz, Çakır, Erdoğan, Yavuz, Can, Aksoy, Köse, Güler, Aktaş, Avcı, Akın, Özer, Ünal, Kaplan, Bulut, Yiğit, Sarı, Taş, Kaymak, Önal, Güneş, Ay, Karaman, Çavuş, Yalçın, Uzun, Karakaş, Tekin ] +} +personalEmails: [ gmail.com, hotmail.com, yahoo.com, outlook.com, icloud.com ] +companyNames: [ Holding, Şirket, Ticaret, Sanayi, Limited, Anonim, Bilişim, Otomotiv, Gıda, İnşaat, Tekstil, Lojistik, Turizm, Enerji, Medya, Sağlık, Eğitim, Banka, Sigorta, Mağaza, Market, Restoran, Kafe ] +companySuffixes: [ 'A.Ş.', 'Ltd. Şti.', 'Hizmetleri', 'Grubu', 'Dış Ticaret' ] +companyEmails: [ info, destek, satis, hello, merhaba ] + +text: Bu bir deneme metnidir. Türkçe dil desteği için oluşturulmuştur. Bu sistem sayesinde rastgele Türkçe veriler üretebilirsiniz. Adresler, isimler, telefon numaraları ve daha fazlası artık jfairy ile mümkün. Teknolojinin getirdiği bu kolaylıklar sayesinde test süreçleriniz daha hızlı ve verimli hale gelecek. + +telephone_number_formats: [ '+90 5## ### ## ##' ] + +postal_code: [ '#####' ] + +city: [ İstanbul, Ankara, İzmir, Bursa, Antalya, Adana, Konya, Gaziantep, Şanlıurfa, Mersin, Kocaeli, Diyarbakır, Hatay, Manisa, Kayseri, Samsun, Balıkesir, Kahramanmaraş, Van, Aydın ] + +street: [ İstiklal Caddesi, Bağdat Caddesi, Cumhuriyet Caddesi, Atatürk Bulvarı, Vatan Caddesi, Ordu Caddesi, Halaskargazi Caddesi, Abdi İpekçi Caddesi, Nispetiye Caddesi, Barbaros Bulvarı, Turgut Özal Bulvarı, Mevlana Caddesi, Fevzi Çakmak Caddesi, Ziya Gökalp Caddesi, Mithatpaşa Caddesi, Tunalı Hilmi Caddesi, Bahçelievler 7. Cadde, Kenan Evren Bulvarı, Gazipaşa Bulvarı, Talatpaşa Bulvarı ] + +country: [ 'Türkiye' ] + +language: 'TR' diff --git a/src/test/groovy/com/devskiller/jfairy/producer/company/locale/tr/TrVATIdentificationNumberProviderSpec.groovy b/src/test/groovy/com/devskiller/jfairy/producer/company/locale/tr/TrVATIdentificationNumberProviderSpec.groovy new file mode 100644 index 00000000..ef2e4bf8 --- /dev/null +++ b/src/test/groovy/com/devskiller/jfairy/producer/company/locale/tr/TrVATIdentificationNumberProviderSpec.groovy @@ -0,0 +1,33 @@ +package com.devskiller.jfairy.producer.company.locale.tr + +import com.devskiller.jfairy.producer.BaseProducer +import com.devskiller.jfairy.producer.RandomGenerator +import spock.lang.Specification + +class TrVATIdentificationNumberProviderSpec extends Specification { + + def "should generate valid VKN"() { + given: + BaseProducer baseProducer = new BaseProducer(new RandomGenerator()) + TrVATIdentificationNumberProvider provider = new TrVATIdentificationNumberProvider(baseProducer) + + expect: + (1..100).each { + String vkn = provider.get() + assert vkn.length() == 10 + int[] digits = vkn.chars().collect { Character.getNumericValue((char) it) } + int sum = 0 + for (int i = 0; i < 9; i++) { + int p = (digits[i] + 10 - (i + 1)) % 10 + if (p == 9) { + sum += 9 + } else { + int q = (int) (p * Math.pow(2, 9 - i)) % 9 + sum += q + } + } + int c1 = (10 - (sum % 10)) % 10 + assert digits[9] == c1 + } + } +} diff --git a/src/test/groovy/com/devskiller/jfairy/producer/person/locale/tr/TrAddressSpec.groovy b/src/test/groovy/com/devskiller/jfairy/producer/person/locale/tr/TrAddressSpec.groovy new file mode 100644 index 00000000..7808935c --- /dev/null +++ b/src/test/groovy/com/devskiller/jfairy/producer/person/locale/tr/TrAddressSpec.groovy @@ -0,0 +1,15 @@ +package com.devskiller.jfairy.producer.person.locale.tr + +import spock.lang.Specification + +class TrAddressSpec extends Specification { + + def "should format Turkish address correctly"() { + given: + TrAddress address = new TrAddress("10", "İstiklal Caddesi", "5", "34000", "İstanbul") + + expect: + address.getAddressLine1() == "İstiklal Caddesi No: 10 Daire: 5" + address.getAddressLine2() == "34000 İstanbul" + } +} diff --git a/src/test/groovy/com/devskiller/jfairy/producer/person/locale/tr/TrNationalIdentityCardNumberProviderSpec.groovy b/src/test/groovy/com/devskiller/jfairy/producer/person/locale/tr/TrNationalIdentityCardNumberProviderSpec.groovy new file mode 100644 index 00000000..f6b37471 --- /dev/null +++ b/src/test/groovy/com/devskiller/jfairy/producer/person/locale/tr/TrNationalIdentityCardNumberProviderSpec.groovy @@ -0,0 +1,50 @@ +package com.devskiller.jfairy.producer.person.locale.tr + +import com.devskiller.jfairy.producer.BaseProducer +import com.devskiller.jfairy.producer.RandomGenerator +import spock.lang.Specification + +class TrNationalIdentityCardNumberProviderSpec extends Specification { + + def "should generate valid T.C. Kimlik No"() { + given: + BaseProducer baseProducer = Mock() + TrNationalIdentityCardNumberProvider provider = new TrNationalIdentityCardNumberProvider(baseProducer) + + when: + baseProducer.randomBetween(1, 9) >> 1 + baseProducer.randomBetween(0, 9) >>> [2, 3, 4, 5, 6, 7, 8, 9] + String tckn = provider.get() + + then: + tckn.length() == 11 + tckn.startsWith("1") + // 1 2 3 4 5 6 7 8 9 + // oddSum = 1+3+5+7+9 = 25 + // evenSum = 2+4+6+8 = 20 + // d10 = (25 * 7 - 20) % 10 = (175 - 20) % 10 = 155 % 10 = 5 + // totalSum = 1+2+3+4+5+6+7+8+9+5 = 50 + // d11 = 50 % 10 = 0 + tckn == "12345678950" + } + + def "should generate multiple valid T.C. Kimlik Nos"() { + given: + BaseProducer baseProducer = new BaseProducer(new RandomGenerator()) + TrNationalIdentityCardNumberProvider provider = new TrNationalIdentityCardNumberProvider(baseProducer) + + expect: + (1..100).each { + String tckn = provider.get() + assert tckn.length() == 11 + assert !tckn.startsWith("0") + int[] digits = tckn.chars().collect { Character.getNumericValue((char) it) } + int oddSum = digits[0] + digits[2] + digits[4] + digits[6] + digits[8] + int evenSum = digits[1] + digits[3] + digits[5] + digits[7] + int d10 = ((oddSum * 7) - evenSum) % 10 + if (d10 < 0) d10 += 10 + assert digits[9] == d10 + assert digits[10] == (digits[0..9].sum() % 10) + } + } +}