Repository: joke2k/faker (python)

Issue:
ssn.it_IT.Provider generates SSNs shorter than 16 chars
* Faker version:  tested with 13.14.0,  15.3.3
* OS: Ubuntu 22.04

Generating an Italian SSN, when the last (or first) name returned by `person.it_IT.Provider` is 3 characters long and contains latin chars, the resulting string is shorter than expected (16 chars). In this scenario:

- string is not shorter than 3 chars, so it does not get padded
- latin char is neither a vowel nor a consonant, so it gets ignored

### Steps to reproduce

```
from unittest import mock 

from faker import Factory

generator = Factory.create("it_IT")
generator.last_name = mock.MagicMock(return_value="Foà")

assert len(generator.ssn()) == 16

Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AssertionError
```

### Expected behavior

The length of generated SSN should be 16 characters. Latin chars should be translated into equivalent ASCII chars (A for À, E for È and É, etc.)  

### Actual behavior

The length of generated SSN is 15 characters.


Pull request:
Fix IT SSN generation with latin chars in name or surname
### What does this change

Fix IT SSN generation when Person provider returns first or last name with latin chars

### What was wrong

Latin chars was considered neither vowel nor consonant. They just got ignored, and the resulting SSN was wrong, or even shorter than expected length (16 chars) in case of short names/surnames.

### How this fixes it

This change applies a transliteration for dyacritic chars, translating them into the equivalents ASCII chars, according to the Italian SSN generation rules.

Fixes #1757 


Patch:
diff --git a/faker/providers/ssn/it_IT/__init__.py b/faker/providers/ssn/it_IT/__init__.py
index 1b41b54c..98aedebb 100644
--- a/faker/providers/ssn/it_IT/__init__.py
+++ b/faker/providers/ssn/it_IT/__init__.py
@@ -1,5 +1,7 @@
 """it_IT ssn provider (yields italian fiscal codes)"""
 
+import unicodedata
+
 from string import ascii_uppercase, digits
 
 from .. import Provider as SsnProvider
@@ -8051,6 +8053,8 @@ class Provider(SsnProvider):
         else:
             name = self.generator.first_name_female().upper()
 
+        name = self._transliterate_name(name)
+
         if len(name) < 3:
             return self._pad_shorter(name)
 
@@ -8074,6 +8078,8 @@ class Provider(SsnProvider):
             str
         """
         surname = self.generator.last_name().upper()
+        surname = self._transliterate_name(surname)
+
         if len(surname) < 3:
             return self._pad_shorter(surname)
 
@@ -8086,6 +8092,11 @@ class Provider(SsnProvider):
             surname_part = "".join(surname_consonants)[:3]
         return surname_part
 
+    @staticmethod
+    def _transliterate_name(name: str) -> str:
+        nfkd_form: str = unicodedata.normalize("NFKD", name)
+        return "".join([c for c in nfkd_form if unicodedata.combining(c) == 0])
+
     @staticmethod
     def _get_vowels(sequence: str) -> list:
         """


Test patch:
diff --git a/tests/providers/test_ssn.py b/tests/providers/test_ssn.py
index 4bdbf3d0..996d9b2e 100644
--- a/tests/providers/test_ssn.py
+++ b/tests/providers/test_ssn.py
@@ -14,7 +14,7 @@ from validators.i18n.es import es_cif as is_cif
 from validators.i18n.es import es_nie as is_nie
 from validators.i18n.es import es_nif as is_nif
 
-from faker import Faker
+from faker import Factory, Faker
 from faker.providers.ssn.el_GR import tin_checksum as gr_tin_checksum
 from faker.providers.ssn.en_CA import checksum as ca_checksum
 from faker.providers.ssn.es_CL import rut_check_digit as cl_rut_checksum
@@ -904,6 +904,13 @@ class TestItIT(unittest.TestCase):
     def test_checksum(self) -> None:
         assert it_checksum("MDDMRA80L41H501") == "R"
 
+    def test_ssn_with_latin_chars(self):
+        generator = Factory.create("it_IT")
+        generator.last_name = mock.MagicMock(return_value="Foà")
+        ssn = generator.ssn()
+        self.assertEqual(len(ssn), 16)
+        self.assertEqual(ssn[:3], "FOA")
+
 
 class TestPtBR(unittest.TestCase):
     def setUp(self):