Repository: joke2k/faker

Issue:
Separate male and female names for `hi_IN` provider
* Faker version: `<= 29.0.0`
* OS: all

The `hi_IN` locale doesn't have separate male and female names along with prefixes

### Expected behavior

There should be separate male and female names for `hi_IN` locale

### Actual behavior

Male and female names are mixed

Also mentioned in #2073 

Discussion:
wh0th3h3llam1: waiting for #2091 to be merged

Patch:
diff --git a/.bumpversion.cfg b/.bumpversion.cfg
index 65a0ebf5..e073e37a 100644
--- a/.bumpversion.cfg
+++ b/.bumpversion.cfg
@@ -1,5 +1,5 @@
 [bumpversion]
-current_version = 30.1.0
+current_version = 30.3.0
 files = VERSION faker/__init__.py docs/conf.py
 commit = True
 tag = True
diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md
index c53f7f76..af66a2ba 100644
--- a/.github/PULL_REQUEST_TEMPLATE.md
+++ b/.github/PULL_REQUEST_TEMPLATE.md
@@ -15,4 +15,5 @@ Fixes #...
 ### Checklist
 
 - [ ] I have read the documentation about [CONTRIBUTING](https://github.com/joke2k/faker/blob/master/CONTRIBUTING.rst)
+- [ ] I have read the documentation about [Coding style](https://github.com/joke2k/faker/blob/master/docs/coding_style.rst)
 - [ ] I have run `make lint`
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 5ceedb2f..e69f7edc 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,5 +1,13 @@
 ## Changelog
 
+### [v30.3.0 - 2024-10-07](https://github.com/joke2k/faker/compare/v30.2.0...v30.3.0)
+
+* Add decorator to optionally format dates as string. Thanks @browniebroke.
+
+### [v30.2.0 - 2024-10-07](https://github.com/joke2k/faker/compare/v30.1.0...v30.2.0)
+
+* Cache Factory._find_provider_class module look-ups. Thanks @huonw.
+
 ### [v30.1.0 - 2024-09-30](https://github.com/joke2k/faker/compare/v30.0.0...v30.1.0)
 
 * Add PIN Code range and union territories in `en_IN` address provider. Thanks @wh0th3h3llam1.
diff --git a/VERSION b/VERSION
index a75ef34c..96332141 100644
--- a/VERSION
+++ b/VERSION
@@ -1,1 +1,1 @@
-30.1.0
+30.3.0
diff --git a/docs/conf.py b/docs/conf.py
index 4ab9b066..08f8e7a1 100644
--- a/docs/conf.py
+++ b/docs/conf.py
@@ -51,9 +51,9 @@ copyright = f"2014-{datetime.now().year}, Daniele Faraglia"
 # built documents.
 #
 # The short X.Y version.
-version = "30.1.0"
+version = "30.3.0"
 # The full version, including alpha/beta/rc tags.
-release = "30.1.0"
+release = "30.3.0"
 
 # The language for content autogenerated by Sphinx. Refer to documentation
 # for a list of supported languages.
diff --git a/faker/__init__.py b/faker/__init__.py
index 9a466641..e866a61b 100644
--- a/faker/__init__.py
+++ b/faker/__init__.py
@@ -2,6 +2,6 @@ from faker.factory import Factory
 from faker.generator import Generator
 from faker.proxy import Faker
 
-VERSION = "30.1.0"
+VERSION = "30.3.0"
 
 __all__ = ("Factory", "Generator", "Faker")
diff --git a/faker/factory.py b/faker/factory.py
index a1e9c065..68495705 100644
--- a/faker/factory.py
+++ b/faker/factory.py
@@ -1,3 +1,4 @@
+import functools
 import locale as pylocale
 import logging
 import sys
@@ -64,6 +65,7 @@ class Factory:
         return faker
 
     @classmethod
+    @functools.lru_cache(maxsize=None)
     def _find_provider_class(
         cls,
         provider_path: str,
diff --git a/faker/providers/date_time/__init__.py b/faker/providers/date_time/__init__.py
index 0c67fb13..6dd8b3f7 100644
--- a/faker/providers/date_time/__init__.py
+++ b/faker/providers/date_time/__init__.py
@@ -1,3 +1,4 @@
+import functools
 import platform
 import re
 
@@ -33,6 +34,20 @@ def timestamp_to_datetime(timestamp: Union[int, float], tzinfo: Optional[TzInfo]
     return convert_timestamp_to_datetime(timestamp, tzinfo)
 
 
+def maybe_format_as_string(func):
+    """Format date as string if a pattern is provided."""
+
+    @functools.wraps(func)
+    def wrapper(*args, **kwargs):
+        result: dtdate = func(*args, **kwargs)
+        pattern = kwargs.get("pattern")
+        if pattern:
+            return result.strftime(pattern)
+        return result
+
+    return wrapper
+
+
 def change_year(current_date: dtdate, year_diff: int) -> dtdate:
     """
     Unless the current_date is February 29th, it is fine to just subtract years.
@@ -2057,15 +2072,22 @@ class Provider(BaseProvider):
         else:
             return (datetime(1970, 1, 1, tzinfo=tzutc()) + timedelta(seconds=ts)).astimezone(tzinfo)
 
-    def date_between(self, start_date: DateParseType = "-30y", end_date: DateParseType = "today") -> dtdate:
+    @maybe_format_as_string
+    def date_between(
+        self,
+        start_date: DateParseType = "-30y",
+        end_date: DateParseType = "today",
+        pattern: Optional[str] = None,
+    ) -> Union[dtdate, str]:
         """
         Get a Date object based on a random date between two given dates.
         Accepts date strings that can be recognized by strtotime().
 
         :param start_date: Defaults to 30 years ago
         :param end_date: Defaults to "today"
-        :example: Date('1999-02-02')
-        :return: Date
+        :param pattern: optional pattern to format the date as string
+        :example: Date('1999-02-02') or '1999-02-02'
+        :return: Date or string
         """
 
         start_date = self._parse_date(start_date)
@@ -2085,7 +2107,13 @@ class Provider(BaseProvider):
         """
         return self.date_time_between(start_date="+1s", end_date=end_date, tzinfo=tzinfo)
 
-    def future_date(self, end_date: DateParseType = "+30d", tzinfo: Optional[TzInfo] = None) -> dtdate:
+    @maybe_format_as_string
+    def future_date(
+        self,
+        end_date: DateParseType = "+30d",
+        tzinfo: Optional[TzInfo] = None,
+        pattern: Optional[str] = None,
+    ) -> Union[dtdate, str]:
         """
         Get a Date object based on a random date between 1 day from now and a
         given date.
@@ -2093,6 +2121,7 @@ class Provider(BaseProvider):
 
         :param end_date: Defaults to "+30d"
         :param tzinfo: timezone, instance of datetime.tzinfo subclass
+        :param pattern: optional pattern to format the date as string
         :example: dtdate('2030-01-01')
         :return: dtdate
         """
@@ -2111,7 +2140,13 @@ class Provider(BaseProvider):
         """
         return self.date_time_between(start_date=start_date, end_date="-1s", tzinfo=tzinfo)
 
-    def past_date(self, start_date: DateParseType = "-30d", tzinfo: Optional[TzInfo] = None) -> dtdate:
+    @maybe_format_as_string
+    def past_date(
+        self,
+        start_date: DateParseType = "-30d",
+        tzinfo: Optional[TzInfo] = None,
+        pattern: Optional[str] = None,
+    ) -> Union[dtdate, str]:
         """
         Get a Date object based on a random date between a given date and 1 day
         ago.
@@ -2119,8 +2154,9 @@ class Provider(BaseProvider):
 
         :param start_date: Defaults to "-30d"
         :param tzinfo: timezone, instance of datetime.tzinfo subclass
+        :param pattern: optional pattern to format the date as string
         :example: dtdate('1999-02-02')
-        :return: dtdate
+        :return: dtdate or string
         """
         return self.date_between(start_date=start_date, end_date="-1d")
 
@@ -2167,18 +2203,21 @@ class Provider(BaseProvider):
             )
         return pick
 
+    @maybe_format_as_string
     def date_between_dates(
         self,
         date_start: Optional[DateParseType] = None,
         date_end: Optional[DateParseType] = None,
-    ) -> dtdate:
+        pattern: Optional[str] = None,
+    ) -> Union[dtdate, str]:
         """
         Takes two Date objects and returns a random date between the two given dates.
         Accepts Date or datetime objects
 
         :param date_start: Date
         :param date_end: Date
-        :return: Date
+        :param pattern: optional pattern to format the date as string
+        :return: Date or string
         """
         return self.date_time_between_dates(date_start, date_end).date()
 
@@ -2294,14 +2333,21 @@ class Provider(BaseProvider):
         else:
             return now
 
-    def date_this_century(self, before_today: bool = True, after_today: bool = False) -> dtdate:
+    @maybe_format_as_string
+    def date_this_century(
+        self,
+        before_today: bool = True,
+        after_today: bool = False,
+        pattern: Optional[str] = None,
+    ) -> Union[dtdate, str]:
         """
         Gets a Date object for the current century.
 
         :param before_today: include days in current century before today
         :param after_today: include days in current century after today
+        :param pattern: optional pattern to format the date as string
         :example: Date('2012-04-04')
-        :return: Date
+        :return: Date or string
         """
         today = dtdate.today()
         this_century_start = dtdate(today.year - (today.year % 100), 1, 1)
@@ -2316,14 +2362,21 @@ class Provider(BaseProvider):
         else:
             return today
 
-    def date_this_decade(self, before_today: bool = True, after_today: bool = False) -> dtdate:
+    @maybe_format_as_string
+    def date_this_decade(
+        self,
+        before_today: bool = True,
+        after_today: bool = False,
+        pattern: Optional[str] = None,
+    ) -> Union[dtdate, str]:
         """
         Gets a Date object for the decade year.
 
         :param before_today: include days in current decade before today
         :param after_today: include days in current decade after today
+        :param pattern: optional pattern to format the date as string
         :example: Date('2012-04-04')
-        :return: Date
+        :return: Date or string
         """
         today = dtdate.today()
         this_decade_start = dtdate(today.year - (today.year % 10), 1, 1)
@@ -2338,12 +2391,19 @@ class Provider(BaseProvider):
         else:
             return today
 
-    def date_this_year(self, before_today: bool = True, after_today: bool = False) -> dtdate:
+    @maybe_format_as_string
+    def date_this_year(
+        self,
+        before_today: bool = True,
+        after_today: bool = False,
+        pattern: Optional[str] = None,
+    ) -> Union[dtdate, str]:
         """
         Gets a Date object for the current year.
 
         :param before_today: include days in current year before today
         :param after_today: include days in current year after today
+        :param pattern: optional pattern to format the date as string
         :example: Date('2012-04-04')
         :return: Date
         """
@@ -2360,14 +2420,21 @@ class Provider(BaseProvider):
         else:
             return today
 
-    def date_this_month(self, before_today: bool = True, after_today: bool = False) -> dtdate:
+    @maybe_format_as_string
+    def date_this_month(
+        self,
+        before_today: bool = True,
+        after_today: bool = False,
+        pattern: Optional[str] = None,
+    ) -> Union[dtdate, str]:
         """
         Gets a Date object for the current month.
 
         :param before_today: include days in current month before today
         :param after_today: include days in current month after today
+        :param pattern: optional pattern to format the date as string
         :example: dtdate('2012-04-04')
-        :return: dtdate
+        :return: dtdate or string
         """
         today = dtdate.today()
         this_month_start = today.replace(day=1)
@@ -2457,12 +2524,14 @@ class Provider(BaseProvider):
         """
         return gettz(self.timezone(*args, **kwargs))  # type: ignore
 
+    @maybe_format_as_string
     def date_of_birth(
         self,
         tzinfo: Optional[TzInfo] = None,
         minimum_age: int = 0,
         maximum_age: int = 115,
-    ) -> dtdate:
+        pattern: Optional[str] = None,
+    ) -> Union[dtdate, str]:
         """
         Generate a random date of birth represented as a Date object,
         constrained by optional miminimum_age and maximum_age
@@ -2471,6 +2540,7 @@ class Provider(BaseProvider):
         :param tzinfo: Defaults to None.
         :param minimum_age: Defaults to 0.
         :param maximum_age: Defaults to 115.
+        :param pattern: optional pattern to format the date as string
 
         :example: Date('1979-02-02')
         :return: Date
diff --git a/faker/providers/person/hi_IN/__init__.py b/faker/providers/person/hi_IN/__init__.py
index d5aebad4..9937fc82 100644
--- a/faker/providers/person/hi_IN/__init__.py
+++ b/faker/providers/person/hi_IN/__init__.py
@@ -2,239 +2,524 @@ from .. import Provider as PersonProvider
 
 
 class Provider(PersonProvider):
+
+    formats_male = (
+        "{{first_name_male}} {{last_name}}",
+        "{{prefix_male}} {{first_name_male}} {{last_name}}",
+        "{{first_name_male}} {{last_name}}{{suffix}}",
+        "{{prefix}} {{first_name_male}} {{last_name}}",
+    )
+    formats_female = (
+        "{{first_name_female}} {{last_name}}",
+        "{{prefix_female}} {{first_name_female}} {{last_name}}",
+        "{{first_name_female}} {{last_name}}{{suffix}}",
+        "{{prefix}} {{first_name_female}} {{last_name}}",
+    )
+
     formats = (
         "{{first_name}} {{last_name}}",
-        "{{first_name}} {{last_name}}",
-        "{{last_name}}, {{first_name}}",
+        "{{prefix}} {{first_name}} {{last_name}}",
+        "{{first_name}} {{last_name}}{{suffix}}",
     )
 
-    # First 20 names from here
-    # https://www.babycenter.in/l25020672/top-20-indian-boys-names-of-2016-photos
-    # Next 20 names from here
-    # https://www.babycenter.in/l25020674/top-20-indian-girls-names-of-2016-photos
-    first_names = (
-        "मुहम्मद",
-        "आरव",
-        "अर्जुन",
-        "रायन",
-        "आद्विक",
-        "अथर्व",
-        "रेयांश",
-        "अयान",
-        "विहान",
-        "साई",
-        "अद्वैत",
-        "शौर्य",
-        "विआन",
-        "आरुष",
-        "इशान",
-        "अयांश",
-        "पार्थ",
-        "देन्यल",
-        "किआन",
-        "विवान",
-        "आद्या",
-        "अनन्या",
-        "शनाया",
-        "फ़ातिमा",
-        "श्री",
-        "अनाया",
-        "अनिका",
-        "मायरा",
-        "इनाया",
-        "अमायरा",
-        "आन्वी",
-        "स्वरा",
-        "ज़ारा",
-        "मरियम",
-        "आराध्या",
-        "तन्वी",
-        "दीया",
-        "अद्विका",
-        "ईवा",
-        "आव्या",
+    # http://www.20000-names.com/male_hindi_names.htm
+    first_names_male = (
         "अभय",
         "आदित्य",
         "अजित",
+        "आकाङ्क्षा",
+        "अकबर",
         "अखिल",
         "अमर",
+        "अमित",
+        "अमृत",
         "आनन्द",
+        "अनन्‍त",
+        "अनिल",
+        "अनिरुद्ध",
+        "अनिश",
         "अंकुर",
+        "अनुज",
         "अनुपम",
+        "अरविन्द",
+        "अर्जुन",
+        "अरुण",
+        "अरुणा",
+        "असीम",
         "अशोक",
+        "बल",
+        "बलदेव",
+        "बलराम",
+        "भारत",
+        "ब्रह्मा",
+        "बृजेश",
+        "चण्ड",
         "चन्दना",
+        "चन्द्रकान्त",
+        "दामोदर",
+        "दर्शन",
+        "दयाराम",
+        "देवदान",
+        "दीपक",
+        "देवदान",
+        "देवदास",
+        "देवराज",
+        "धनञ्जय",
+        "धवल",
+        "दिलीप",
+        "दिनेश",
+        "दीपक",
+        "दिलीप",
         "गणेश",
         "गौतम",
+        "गोपाल",
+        "गोतम",
         "गोविंदा",
+        "गुलज़ार",
         "हनुमान्",
+        "हरेन्द्र",
+        "हरि",
+        "हरीश",
+        "हर्श",
+        "हर्शद",
+        "हर्शल",
+        "इला",
+        "इन्द्र",
         "इन्द्रजित",
         "ईश",
         "जगन्नाथ",
         "जगदीश",
+        "जगजीत",
         "जयदेव",
+        "ज़स्विन्देर्",
+        "जय",
+        "जयन्त",
+        "जयेन्द्र",
         "जितेन्द्र",
+        "जौहर",
+        "ज्योतिष",
         "कैलाश",
         "कालिदास",
+        "काम",
+        "कमल",
         "कम्बोज",
+        "कपिल",
+        "कर्ण",
+        "ख़ान",
         "किरण",
+        "कशोर",
+        "कृष्ण",
+        "कुमार",
+        "कुणाल",
+        "लक्ष्मण",
+        "लाल",
         "ललित",
+        "लोचन",
+        "माधव",
+        "मधुकर",
+        "महात्मा",
+        "महावीर",
+        "महेन्द्रा",
         "मानदीप",
+        "मनीश",
+        "मणि",
+        "मणीन्द्र",
+        "मनीश",
+        "मञ्जुनाथ",
         "मोहन",
         "मुकेश",
-        "नरेन्द्र",
+        "नंद",
         "नारायण",
+        "नरेन्द्र",
+        "नवीन",
         "निखिल",
+        "नीरव",
+        "िनशा",
+        "ओम",
+        "पद्म",
+        "पल्लव",
+        "पीताम्बर",
         "प्रभाकर",
+        "प्रभात",
+        "प्रभु",
         "प्रबोध",
         "प्रदीप",
+        "प्रकाश",
+        "प्रमोद",
         "प्रणव",
+        "प्रणय",
+        "प्रसाद",
+        "प्रसन्न",
+        "प्रताप",
         "प्रेम",
+        "पुरुषोत्तम",
+        "रघु",
+        "राहुल",
+        "राज",
+        "राजन",
+        "रजनीकांत",
         "राजीव",
+        "राजेन्द्र",
+        "राजेश",
+        "राजीव",
+        "राकेश",
+        "राम",
+        "रामचन्द्र",
+        "रामकृष्ण",
+        "रञ्जित",
         "रतन",
+        "रत्नम",
+        "रावण",
+        "रवि",
+        "ऋषि",
         "रोहन",
-        "विष्णु",
-        "विक्रम",
-        "विजया",
+        "सचिन",
+        "संदीप",
+        "शनि",
+        "संजय",
+        "संजित",
+        "संजीव",
+        "शंकर",
+        "सरल",
+        "सतीश",
+        "सवितृ",
+        "शेखर",
+        "सेठ",
+        "शनि",
+        "शंकर",
+        "शङ्कर",
+        "शंतनु",
+        "शर्म",
+        "शशि",
+        "शेखर",
+        "शेष",
+        "शिव",
+        "श्रेष्ठ",
+        "श्रीपति",
+        "श्याम",
+        "श्यामल",
+        "सिद्धार्थ",
+        "सिकन्दर",
+        "सोहेल",
+        "सुभाष",
+        "सुदर्शन",
+        "सुधीर",
+        "सुमन",
+        "सुमन्त्र",
+        "सुन्दर",
+        "सुनील",
+        "सुरज",
+        "सुरेन्द्र",
+        "सुरेश",
+        "सूर्य",
+        "सुशील",
+        "स्वपन",
+        "स्वप्निल",
+        "स्वर्ण",
+        "उत्तम",
+        "वसन्त",
+        "वासिष्ठ",
+        "भरत",
         "विजय",
+        "विजया",
+        "विक्रम",
+        "विमल",
+        "विनय",
+        "विपिन",
+        "विपुल",
+        "विशाल",
+        "विष्णु",
         "विवेक",
         "यश",
+    )
+
+    # http://www.20000-names.com/female_hindi_names.htm
+    first_names_female = (
+        "आभा",
         "अभिलाषा",
         "अदिती",
         "ऐश्वर्या",
+        "आकाङ्क्षा",
+        "अमला",
         "अमिता",
+        "अमृता",
+        "आनन्दा",
+        "अनिला",
+        "अणिमा",
         "अंकिता",
+        "अनुष्का",
+        "अनुजा",
+        "अर्चना",
+        "अरुंधती",
         "आशा",
         "अवनी",
+        "अवन्ती",
+        "बल",
         "भरत",
+        "चण्डा",
+        "चन्दना",
+        "चन्द्रकान्ता",
         "चेतना",
+        "दमयंती",
+        "दर्शना",
+        "दीपाली",
+        "दीप्ति",
+        "देवी",
+        "दीपाली",
+        "दीप्ति",
         "दिव्या",
+        "दुर्गा",
         "एषा",
+        "गौहर",
+        "गौरी",
+        "गीता",
+        "गोपीनाथ",
+        "गुलज़ार",
+        "इला",
+        "इन्दिरा",
+        "इन्द्रजित",
         "इन्दु",
+        "ज़स्विन्देर्",
         "जया",
         "जयन्ती",
         "ज्योत्सना",
+        "ज्योत्स्ना",
+        "कैलाश",
+        "कला",
+        "काली",
+        "कल्पना",
+        "कमला",
+        "कान्ता",
         "कान्ती",
+        "करिश्मा",
+        "काशी",
+        "कौशल्या",
+        "िकशोरी",
+        "क्षितिज",
         "कुमारी",
+        "कुंती",
+        "लक्ष्मी",
         "लता",
+        "लावण्या",
+        "लक्ष्मी",
+        "लीला",
+        "लीलावती",
+        "लीला",
         "लीला",
+        "लीलावती",
+        "लीना",
+        "माधवी",
+        "मधु",
+        "मधुर",
+        "माला",
         "मालती",
+        "मनीषा",
+        "मञ्जु",
+        "मञ्जुला",
+        "मञ्जूषा",
+        "माया",
+        "मीरा",
+        "मोहना",
         "मोहिनी",
+        "मुक्ता",
+        "नेहा",
+        "निखिला",
         "निशा",
+        "नित्य",
+        "पद्म",
+        "पद्मावती",
+        "पद्मिनी",
+        "पार्वती",
+        "परवीन",
+        "पूर्णिमा",
+        "प्रतिभा",
+        "प्रतिमा",
+        "प्रेमा",
+        "प्रिया",
         "पूर्णिमा",
         "पुष्पा",
         "रचना",
+        "राधा",
         "रजनी",
+        "राज्य",
+        "रानी",
         "रश्मी",
+        "रति",
+        "रत्न",
+        "रेशमी",
+        "रीतिका",
         "रिया",
+        "रोहना",
+        "रुक्मिणी",
+        "रुपिन्द्र",
+        "संजना",
         "सरला",
         "सरस्वती",
+        "सारिका",
+        "सती",
         "सावित्री",
+        "सीमा",
+        "सीता",
         "शक्ति",
+        "शकुन्तला",
         "शान्ता",
+        "शान्ती",
         "शर्मिला",
+        "शशी",
+        "शीला",
+        "शिवाली",
+        "शोभा",
         "श्यामा",
+        "श्यामला",
+        "सीमा",
+        "सीता",
+        "सितारा",
+        "सोनल",
+        "श्री",
+        "सुदर्शना",
         "सुलभा",
+        "सुमना",
+        "सुमती",
+        "सुनीता",
+        "सुनीती",
+        "सुशीला",
+        "स्वर्ण",
+        "तारा",
         "तृष्णा",
+        "उमा",
+        "उषा",
+        "वसन्ता",
         "विद्या",
-        "अली",
-        "हासन",
-        "हुसैन",
-        "ज़ाकिर",
-        "रिज़वान",
-        "फ़रहान",
-        "ज़ोया",
+        "विजया",
+        "विमला",
     )
 
+    first_names = first_names_male + first_names_female
+
+    # https://blogs.transparent.com/hindi/common-surnames-in-india/
     last_names = (
-        "पाटिल",
+        # Common Surnames in North India (Delhi, Haryana, Punjab,etc)
         "शर्मा",
-        "आचार्य",
-        "अग्रवाल",
+        "भट",
+        "वर्मा",
+        "कुमार",
+        "गुप्ता",
+        "मल्होत्रा",
+        "भटनागर",
+        "सक्सेना",
+        "कपूर",
         "सिंह",
-        "अहलुवालिया",
+        "महरा",
+        "चोपरा",
+        "सरीन",
+        "मालिक",
+        "सैनी",
+        "जैन",
+        "कौल",
+        "खत्री",
+        "गोयल",
+        "तिवारी",
+        "भरद्वाज",
+        "चोपरा",
+        "प्रसाद",
+        "आचार्य",
+        "अगरवाल",
+        "अहलूवालिया",
+        "टंडन",
         "आहूजा",
-        "पुष्कर",
-        "शिरोळे",
-        "गायकवाड",
-        "गावित",
-        "शिरोळे",
-        "बापट",
-        "अरोड़ा",
-        "बाबू",
-        "बादामी",
-        "जमानत",
-        "बजाज",
-        "बक्षी",
-        "बालकृष्णन",
-        "बालासुब्रमणियम",
-        "बसु",
-        "भंडारी",
-        "चौधरी",
-        "चौहान",
-        "छाबरा",
-        "दादा",
-        "डानी",
-        "डार",
-        "दारा",
-        "दत्ता",
-        "दवे",
-        "दयाल",
-        "धालीवाल",
-        "दीक्षित",
-        "दोषी",
-        "दुआ",
-        "दूबे",
-        "ढींगरा",
-        "वाल",
-        "साया",
-        "बना",
-        "ड़ाल",
-        "गर्ग",
-        "गणेश",
-        "गांगुली",
-        "गुप्ता",
-        "हेगडे",
-        "जोशी",
-        "काले",
-        "कृष्णा",
-        "कृष्णमूर्ति",
-        "कृष्णन",
-        "कुलकर्णी",
-        "कुमार",
-        "कुण्डा",
-        "नाम",
-        "रामलला",
-        "लता",
-        "लोदी",
-        "लोकनाट्यों",
-        "विकावि",
-        "लाल",
-        "लाला",
-        "वफादार",
-        "लूथरा",
-        "मदन",
-        "मगर",
-        "भारत",
-        "महावीर",
-        "महादेव",
-        "महाजन",
-        "महाराज",
+        "अरोरा",
+        # Common Surnames in East India: (Bengal, Orrisa, etc.)
+        "चटर्जी",
+        "चतुर्वेदी",
+        "सेन",
+        "बोस",
+        "सेनगुप्ता",
+        "दास",
+        "दासगुप्ता",
+        "मुख़र्जी",
+        "दुत्ता",
+        "बनर्जी",
+        "चक्रवर्ती",
+        "भट्टाचार्य",
+        "घोष",
+        "मित्रा",
+        "गुहा",
+        "सरकार",
+        "साहा",
+        "रॉय",
+        "चोधरी",
+        "रॉय चौधरी",
         "मजूमदार",
-        "मल्लिक",
-        "सेनाधीश",
-        "माने",
-        "मंगल",
-        "मंगत",
-        "रामशर्मा",
-        "मणि",
-        "मान",
-        "श्रीविमल",
-        "कुमार",
         "मंडल",
-        "अली",
-        "हासन",
-        "हुसैन",
+        "मैती",
+        "कलिता",
+        "हजारिका",
+        "नाथ",
+        "बुरुाह",
+        "थापा",
+        "गुरुंग",
+        "राय",
+        "प्रधान",
+        "तमांग",
+        "छेत्री",
+        # Common Surnames in South India (Karnataka, Tamil Nadu, Kerala, etc.)
+        "नायर",
+        "मेनन",
+        "पिल्लई",
+        "वेंकटएसन",
+        "बलासुब्रमानियम",
+        "राव",
+        "जयरामन",
+        "सुब्रमण्यम",
+        "रंगन",
+        "रंगराजन",
+        "नारायण",
+        "रेड्डी",
+        # Common Surnames in Central India (Bihar/ Uttar Pradesh, Madhya Pradesh, etc)
+        "सिंह",
+        "द्विवेदी",
+        "मिश्रा",
+        "त्रिवेदी",
+        "झा",
+        "शुक्ला",
+        "यादव",
+        "सिन्हा",
+        "पाण्डेय",
+        "झादव",
+        "जेटली",
+        "चौहान",
+        "जोशी",
+        "मिस्त्री",
         "खान",
-        "अब्बासी",
-        "नूरानी",
+        "श्रीवास्तव",
+        # Common Surnames in West India (Maharashtra, Gujarat, Goa etc)
+        "शाह",
+        "देशपांडे",
+        "गावडे",
+        "कदम",
+        "ताम्बे",
+        "मेहता",
+        "पटेल",
+        "पाटिल",
+        "पवार",
+        "चवन",
+        "डी’सोउज़ा",
+        "लोबो",
+        "रोद्रिगुएस",
+        "डी’कोस्टा",
     )
+
+    prefixes_male = ("श्री", "श्रीमान")
+
+    prefixes_female = ("श्री", "श्रीमती")
+
+    prefixes = (
+        "माननीय",
+        "आदरसूचक",
+        "सम्मानसूचक",
+        "संमानित",
+        "आदरवाचक",
+        "सम्मानात्मक",
+    )
+
+    suffixes = ("जी",)
diff --git a/faker/proxy.pyi b/faker/proxy.pyi
index 377c3ad2..56782d25 100644
--- a/faker/proxy.pyi
+++ b/faker/proxy.pyi
@@ -970,15 +970,17 @@ class Faker:
         self,
         start_date: Union[datetime.date, datetime.datetime, datetime.timedelta, str, int] = ...,
         end_date: Union[datetime.date, datetime.datetime, datetime.timedelta, str, int] = ...,
-    ) -> datetime.date:
+        pattern: Optional[str] = ...,
+    ) -> Union[datetime.date, str]:
         """
         Get a Date object based on a random date between two given dates.
         Accepts date strings that can be recognized by strtotime().
 
         :param start_date: Defaults to 30 years ago
         :param end_date: Defaults to "today"
-        :example: Date('1999-02-02')
-        :return: Date
+        :param pattern: optional pattern to format the date as string
+        :example: Date('1999-02-02') or '1999-02-02'
+        :return: Date or string
         """
         ...
 
@@ -986,14 +988,16 @@ class Faker:
         self,
         date_start: Union[datetime.date, datetime.datetime, datetime.timedelta, str, int, None] = ...,
         date_end: Union[datetime.date, datetime.datetime, datetime.timedelta, str, int, None] = ...,
-    ) -> datetime.date:
+        pattern: Optional[str] = ...,
+    ) -> Union[datetime.date, str]:
         """
         Takes two Date objects and returns a random date between the two given dates.
         Accepts Date or datetime objects
 
         :param date_start: Date
         :param date_end: Date
-        :return: Date
+        :param pattern: optional pattern to format the date as string
+        :return: Date or string
         """
         ...
 
@@ -1006,8 +1010,12 @@ class Faker:
         ...
 
     def date_of_birth(
-        self, tzinfo: Optional[datetime.tzinfo] = ..., minimum_age: int = ..., maximum_age: int = ...
-    ) -> datetime.date:
+        self,
+        tzinfo: Optional[datetime.tzinfo] = ...,
+        minimum_age: int = ...,
+        maximum_age: int = ...,
+        pattern: Optional[str] = ...,
+    ) -> Union[datetime.date, str]:
         """
         Generate a random date of birth represented as a Date object,
         constrained by optional miminimum_age and maximum_age
@@ -1016,51 +1024,64 @@ class Faker:
         :param tzinfo: Defaults to None.
         :param minimum_age: Defaults to 0.
         :param maximum_age: Defaults to 115.
+        :param pattern: optional pattern to format the date as string
 
         :example: Date('1979-02-02')
         :return: Date
         """
         ...
 
-    def date_this_century(self, before_today: bool = ..., after_today: bool = ...) -> datetime.date:
+    def date_this_century(
+        self, before_today: bool = ..., after_today: bool = ..., pattern: Optional[str] = ...
+    ) -> Union[datetime.date, str]:
         """
         Gets a Date object for the current century.
 
         :param before_today: include days in current century before today
         :param after_today: include days in current century after today
+        :param pattern: optional pattern to format the date as string
         :example: Date('2012-04-04')
-        :return: Date
+        :return: Date or string
         """
         ...
 
-    def date_this_decade(self, before_today: bool = ..., after_today: bool = ...) -> datetime.date:
+    def date_this_decade(
+        self, before_today: bool = ..., after_today: bool = ..., pattern: Optional[str] = ...
+    ) -> Union[datetime.date, str]:
         """
         Gets a Date object for the decade year.
 
         :param before_today: include days in current decade before today
         :param after_today: include days in current decade after today
+        :param pattern: optional pattern to format the date as string
         :example: Date('2012-04-04')
-        :return: Date
+        :return: Date or string
         """
         ...
 
-    def date_this_month(self, before_today: bool = ..., after_today: bool = ...) -> datetime.date:
+    def date_this_month(
+        self, before_today: bool = ..., after_today: bool = ..., pattern: Optional[str] = ...
+    ) -> Union[datetime.date, str]:
         """
         Gets a Date object for the current month.
 
         :param before_today: include days in current month before today
         :param after_today: include days in current month after today
+        :param pattern: optional pattern to format the date as string
         :example: dtdate('2012-04-04')
-        :return: dtdate
+        :return: dtdate or string
         """
         ...
 
-    def date_this_year(self, before_today: bool = ..., after_today: bool = ...) -> datetime.date:
+    def date_this_year(
+        self, before_today: bool = ..., after_today: bool = ..., pattern: Optional[str] = ...
+    ) -> Union[datetime.date, str]:
         """
         Gets a Date object for the current year.
 
         :param before_today: include days in current year before today
         :param after_today: include days in current year after today
+        :param pattern: optional pattern to format the date as string
         :example: Date('2012-04-04')
         :return: Date
         """
@@ -1194,7 +1215,8 @@ class Faker:
         self,
         end_date: Union[datetime.date, datetime.datetime, datetime.timedelta, str, int] = ...,
         tzinfo: Optional[datetime.tzinfo] = ...,
-    ) -> datetime.date:
+        pattern: Optional[str] = ...,
+    ) -> Union[datetime.date, str]:
         """
         Get a Date object based on a random date between 1 day from now and a
         given date.
@@ -1202,6 +1224,7 @@ class Faker:
 
         :param end_date: Defaults to "+30d"
         :param tzinfo: timezone, instance of datetime.tzinfo subclass
+        :param pattern: optional pattern to format the date as string
         :example: dtdate('2030-01-01')
         :return: dtdate
         """
@@ -1247,7 +1270,8 @@ class Faker:
         self,
         start_date: Union[datetime.date, datetime.datetime, datetime.timedelta, str, int] = ...,
         tzinfo: Optional[datetime.tzinfo] = ...,
-    ) -> datetime.date:
+        pattern: Optional[str] = ...,
+    ) -> Union[datetime.date, str]:
         """
         Get a Date object based on a random date between a given date and 1 day
         ago.
@@ -1255,8 +1279,9 @@ class Faker:
 
         :param start_date: Defaults to "-30d"
         :param tzinfo: timezone, instance of datetime.tzinfo subclass
+        :param pattern: optional pattern to format the date as string
         :example: dtdate('1999-02-02')
-        :return: dtdate
+        :return: dtdate or string
         """
         ...
 


Test patch:
diff --git a/tests/providers/test_date_time.py b/tests/providers/test_date_time.py
index d708bf84..9d221c6c 100644
--- a/tests/providers/test_date_time.py
+++ b/tests/providers/test_date_time.py
@@ -115,6 +115,14 @@ class TestDateTime(unittest.TestCase):
         past_date = self.fake.past_date()
         assert past_date < date.today()
 
+    def test_past_date_string(self):
+        date_format = "%Y-%m-%d"
+        date_string = self.fake.past_date(pattern=date_format)
+        assert isinstance(date_string, str)
+        past_date = datetime.strptime(date_string, date_format).date()
+        assert isinstance(past_date, date)
+        assert past_date < date.today()
+
     def test_future_datetime(self):
         future_datetime, now = self.fake.future_datetime(), datetime.now()
         assert future_datetime > now
@@ -123,6 +131,14 @@ class TestDateTime(unittest.TestCase):
         future_date = self.fake.future_date()
         assert future_date > date.today()
 
+    def test_future_date_string(self):
+        date_format = "%Y-%m-%d"
+        date_string = self.fake.future_date(pattern=date_format)
+        assert isinstance(date_string, str)
+        future_date = datetime.strptime(date_string, date_format).date()
+        assert isinstance(future_date, date)
+        assert future_date > date.today()
+
     def test_parse_date_time(self):
         timestamp = DatetimeProvider._parse_date_time("+30d")
         now = DatetimeProvider._parse_date_time("now")
@@ -272,6 +288,15 @@ class TestDateTime(unittest.TestCase):
         assert date_start <= random_date
         assert date_end >= random_date
 
+    def test_date_string_between_dates(self):
+        date_end = date.today()
+        date_start = date_end - timedelta(days=10)
+        date_format = "%Y-%m-%d"
+
+        date_string = self.fake.date_between_dates(date_start, date_end, pattern=date_format)
+        assert isinstance(date_string, str)
+        assert isinstance(datetime.strptime(date_string, date_format), datetime)
+
     def test_date_time_between_long_past_dates(self):
         random_date = self.fake.date_between("-100y", "-50y")
         assert random_date
@@ -411,6 +436,18 @@ class TestDateTime(unittest.TestCase):
         assert self.fake.date_this_month(before_today=False, after_today=True) >= date.today()
         assert self.fake.date_this_month(before_today=False, after_today=False) == date.today()
 
+    @unittest.skipUnless(is64bit(), "requires 64bit")
+    def test_date_string_this_period(self):
+        date_format = "%Y-%m-%d"
+        # test century
+        assert isinstance(self.fake.date_this_century(pattern=date_format), str)
+        # test decade
+        assert isinstance(self.fake.date_this_decade(pattern=date_format), str)
+        # test year
+        assert isinstance(self.fake.date_this_year(pattern=date_format), str)
+        # test month
+        assert isinstance(self.fake.date_this_month(pattern=date_format), str)
+
     def test_date_time_between(self):
         now = datetime.now()
         _30_years_ago = change_year(now, -30)
@@ -448,6 +485,19 @@ class TestDateTime(unittest.TestCase):
         assert isinstance(random_date, date)
         self.assertBetween(random_date, _9_months_ago, _2_months_ago)
 
+    def test_date_string_between(self):
+        today = date.today()
+        _30_years_ago = change_year(today, -30)
+        _20_years_ago = change_year(today, -20)
+        date_format = "%Y-%m-%d"
+
+        date_string = self.fake.date_between(start_date="-30y", end_date="-20y", pattern=date_format)
+
+        assert isinstance(date_string, str)
+        date_ = datetime.strptime(date_string, date_format).date()
+        assert isinstance(date_, date)
+        self.assertBetween(date_, _30_years_ago, _20_years_ago)
+
     def test_parse_timedelta(self):
         from faker.providers.date_time import Provider
 
@@ -677,6 +727,10 @@ class DatesOfBirth(unittest.TestCase):
         dob = self.fake.date_of_birth()
         assert isinstance(dob, date)
 
+    def test_date_of_birth_string(self):
+        dob_str = self.fake.date_of_birth(pattern="%Y-%m-%d")
+        assert isinstance(dob_str, str)
+
     @freezegun.freeze_time("2020-02-29")
     def test_date_of_birth_on_leap_day(self):
         """
diff --git a/tests/providers/test_person.py b/tests/providers/test_person.py
index 9018aff3..aedfa5bf 100644
--- a/tests/providers/test_person.py
+++ b/tests/providers/test_person.py
@@ -21,6 +21,7 @@ from faker.providers.person.fi_FI import Provider as FiProvider
 from faker.providers.person.fr_BE import Provider as FrBEProvider
 from faker.providers.person.ga_IE import Provider as GaIEProvider
 from faker.providers.person.he_IL import Provider as HeILProvider
+from faker.providers.person.hi_IN import Provider as HiINProvider
 from faker.providers.person.hy_AM import Provider as HyAmProvider
 from faker.providers.person.lv_LV import Provider as LvProvider
 from faker.providers.person.ne_NP import Provider as NeProvider
@@ -1719,6 +1720,584 @@ class TestZhTW(unittest.TestCase):
         assert last_romanized_name in ZhTWProvider.last_romanized_names
 
 
+class TestHyAM(unittest.TestCase):
+    """Tests person in the hy_AM locale"""
+
+    def setUp(self):
+        self.fake = Faker("hy_AM")
+        Faker.seed(0)
+
+    def test_name(self):
+        # General name
+        name = self.fake.name()
+        self.assertIsInstance(name, str)
+
+        # Female name
+        name = self.fake.name_female()
+        self.assertIsInstance(name, str)
+
+        # Male name
+        name = self.fake.name_male()
+        self.assertIsInstance(name, str)
+
+    def test_first_name(self):
+        # General first name
+        name = self.fake.first_name()
+        self.assertIsInstance(name, str)
+        assert name in HyAmProvider.first_names
+
+        # Female first name
+        name = self.fake.first_name_female()
+        self.assertIsInstance(name, str)
+        assert name in HyAmProvider.first_names
+        assert name in HyAmProvider.first_names_female
+
+        # Male first name
+        name = self.fake.first_name_male()
+        self.assertIsInstance(name, str)
+        assert name in HyAmProvider.first_names
+        assert name in HyAmProvider.first_names_male
+
+    def test_last_name(self):
+        # There's no gender-specific last name in Armenian.
+        assert not hasattr(HyAmProvider, "last_names_male")
+        assert not hasattr(HyAmProvider, "last_names_female")
+        # All last names apply for all genders.
+        assert hasattr(HyAmProvider, "last_names")
+
+        # General last name.
+        name = self.fake.last_name()
+        self.assertIsInstance(name, str)
+        assert name in HyAmProvider.last_names
+
+        # Females last name.
+        name = self.fake.last_name_female()
+        self.assertIsInstance(name, str)
+        assert name in HyAmProvider.last_names
+
+        # Male last name.
+        name = self.fake.last_name_male()
+        self.assertIsInstance(name, str)
+        assert name in HyAmProvider.last_names
+
+
+class TestTaIN(unittest.TestCase):
+    def setUp(self):
+        self.fake = Faker("ta_IN")
+        Faker.seed(0)
+
+    def test_gender_first_names(self):
+        """simple test to verify that we are pulling gender specific names"""
+        name = self.fake.first_name_female()
+        assert name in TaINProvider.first_names_female
+        name = self.fake.first_name_male()
+        assert name in TaINProvider.first_names_male
+        name = self.fake.first_name()
+        assert name in TaINProvider.first_names
+
+
+class TestRuRU(unittest.TestCase):
+    """Tests person in the ru_RU locale"""
+
+    def setUp(self):
+        self.fake = Faker("ru_RU")
+        Faker.seed(0)
+
+    def test_translit(self):
+        assert translit("Александр Сергеевич Пушкин") == "Aleksandr Sergeevich Pushkin"
+        assert translit("Анна Андреевна Ахматова") == "Anna Andreevna Akhmatova"
+        assert translit("Михаил") == "Mikhail"
+        assert translit("Фёдор") == "Fedor"
+        assert translit("Екатерина") == "Yekaterina"
+        assert translit("Анастасия") == "Anastasiya"
+        assert translit("Юрьевич") == "Yurevich"
+        assert translit("Никитична") == "Nikitichna"
+        assert translit("Щербакова") == "Shcherbakova"
+        assert translit("Маяковский") == "Mayakovskiy"
+        assert translit("Петров-Водкин") == "Petrov-Vodkin"
+        assert translit("Воронцова-Дашкова") == "Vorontsova-Dashkova"
+        assert translit("А.С.Пушкин") == "A.S.Pushkin"
+        assert translit("А. С. Пушкин") == "A. S. Pushkin"
+        assert translit("тов. И.И.Сидоров") == "tov. I.I.Sidorov"
+        assert translit("г-н А.Б.Петров") == "g-n A.B.Petrov"
+        assert translit("г-жа Ю.М.Петрова") == "g-zha Yu.M.Petrova"
+
+    def test_name_female(self):
+        first_name = self.fake.first_name_female()
+        assert first_name in RuProvider.first_names_female
+        middle_name = self.fake.middle_name_female()
+        assert middle_name in RuProvider.middle_names_female
+        last_name = self.fake.last_name_female()
+        assert last_name in RuProvider.last_names_female
+
+    def test_name_male(self):
+        first_name = self.fake.first_name_male()
+        assert first_name in RuProvider.first_names_male
+        middle_name = self.fake.middle_name_male()
+        assert middle_name in RuProvider.middle_names_male
+        last_name = self.fake.last_name_male()
+        assert last_name in RuProvider.last_names_male
+
+    def test_language_name(self):
+        language_name = self.fake.language_name()
+        assert language_name in RuProvider.language_names
+
+
+class TestEs(unittest.TestCase):
+    """Tests person in the es locale."""
+
+    def setUp(self):
+        self.fake = Faker("es")
+        Faker.seed(0)
+
+    def test_language_name(self):
+        language_name = self.fake.language_name()
+        assert language_name in EsProvider.language_names
+
+
+class TestEsCO(unittest.TestCase):
+    """Tests person in the es_CO locale"""
+
+    def setUp(self):
+        self.fake = Faker("es_CO")
+        Faker.seed(0)
+
+    def test_first_names(self):
+        # General first name
+        name = self.fake.first_name()
+        self.assertIsInstance(name, str)
+        assert name in EsCOProvider.first_names
+
+        # Female first name
+        name = self.fake.first_name_female()
+        self.assertIsInstance(name, str)
+        assert name in EsCOProvider.first_names
+        assert name in EsCOProvider.first_names_female
+
+        # Male first name
+        name = self.fake.first_name_male()
+        self.assertIsInstance(name, str)
+        assert name in EsCOProvider.first_names
+        assert name in EsCOProvider.first_names_male
+
+    def test_last_names(self):
+        # General last name
+        name = self.fake.last_name()
+        self.assertIsInstance(name, str)
+        assert name in EsCOProvider.last_names
+
+        # Female last name
+        name = self.fake.last_name_female()
+        self.assertIsInstance(name, str)
+        assert name in EsCOProvider.last_names
+
+        # Male last name
+        name = self.fake.last_name_male()
+        self.assertIsInstance(name, str)
+        assert name in EsCOProvider.last_names
+
+    def test_prefix(self):
+        # Female prefix
+        prefix = self.fake.prefix_female()
+        self.assertIsInstance(prefix, str)
+        assert prefix in EsCOProvider.prefixes_female
+
+        # Male prefix
+        prefix = self.fake.prefix_male()
+        self.assertIsInstance(prefix, str)
+        assert prefix in EsCOProvider.prefixes_male
+
+
+class TestHeIL(unittest.TestCase):
+    """Tests person in the he_IL locale."""
+
+    def setUp(self):
+        self.fake = Faker("he_IL")
+        Faker.seed(0)
+
+    def test_language_name(self):
+        language_name = self.fake.language_name()
+        assert language_name in HeILProvider.language_names
+
+    def test_male_first_name(self):
+        first_name_male = self.fake.first_name_male()
+        assert first_name_male in HeILProvider.first_names_male
+
+    def test_female_first_name(self):
+        first_name_female = self.fake.first_name_female()
+        assert first_name_female in HeILProvider.first_names_female
+
+    def test_last_name(self):
+        last_name = self.fake.last_name()
+        assert last_name in HeILProvider.last_names
+
+
+class TestPtPt(unittest.TestCase):
+    """Tests person in the pt_PT locale."""
+
+    def setUp(self):
+        self.fake = Faker("pt_PT")
+        Faker.seed(0)
+
+    def test_male_first_name(self):
+        first_name_male = self.fake.first_name_male()
+        assert first_name_male in PtPtProvider.first_names_male
+
+    def test_female_first_name(self):
+        first_name_female = self.fake.first_name_female()
+        assert first_name_female in PtPtProvider.first_names_female
+
+    def test_last_name(self):
+        last_name = self.fake.last_name()
+        assert last_name in PtPtProvider.last_names
+
+
+class TestEnGB(unittest.TestCase):
+    def setUp(self):
+        self.fake = Faker("en_GB")
+        Faker.seed(0)
+
+    def test_first_name_female(self):
+        name = self.fake.first_name_female()
+        assert name in EnGBProvider.first_names_female
+
+    def test_first_name_male(self):
+        name = self.fake.first_name_male()
+        assert name in EnGBProvider.first_names_male
+
+    def test_name_female(self):
+        full_name = self.fake.name_female()
+        first_name = self.get_first_name_from_full_name(full_name)
+        assert first_name in EnGBProvider.first_names_female
+
+    def test_name_male(self):
+        full_name = self.fake.name_male()
+        first_name = self.get_first_name_from_full_name(full_name)
+        assert first_name in EnGBProvider.first_names_male
+
+    def get_first_name_from_full_name(self, full_name):
+        names = full_name.split(" ")
+        if len(names) == 2:
+            return names[0]
+        return names[1]
+
+
+class TestUs(unittest.TestCase):
+    """Tests person in the en_US locale"""
+
+    def setUp(self):
+        self.fake = Faker("en_US")
+        Faker.seed(0)
+
+    def test_first_names(self):
+        # General first name
+        name = self.fake.first_name()
+        self.assertIsInstance(name, str)
+        assert name in EnUSProvider.first_names
+
+        # Female first name
+        name = self.fake.first_name_female()
+        self.assertIsInstance(name, str)
+        assert name in EnUSProvider.first_names
+        assert name in EnUSProvider.first_names_female
+
+        # Male first name
+        name = self.fake.first_name_male()
+        self.assertIsInstance(name, str)
+        assert name in EnUSProvider.first_names
+        assert name in EnUSProvider.first_names_male
+
+        # Nonbinary first name
+        name = self.fake.first_name_nonbinary()
+        self.assertIsInstance(name, str)
+        assert name in EnUSProvider.first_names
+        assert name in EnUSProvider.first_names_nonbinary
+
+    def test_last_names(self):
+        # General last name
+        name = self.fake.last_name()
+        self.assertIsInstance(name, str)
+        assert name in EnUSProvider.last_names
+
+        # Female last name
+        name = self.fake.last_name_female()
+        self.assertIsInstance(name, str)
+        assert name in EnUSProvider.last_names
+
+        # Male last name
+        name = self.fake.last_name_male()
+        self.assertIsInstance(name, str)
+        assert name in EnUSProvider.last_names
+
+        # Nonbinary last name
+        name = self.fake.last_name_nonbinary()
+        self.assertIsInstance(name, str)
+        assert name in EnUSProvider.last_names
+
+    def test_prefix(self):
+        # Nonbinary prefix
+        prefix = self.fake.prefix_nonbinary()
+        self.assertIsInstance(prefix, str)
+        assert prefix in EnUSProvider.prefixes_nonbinary
+
+    def test_suffix(self):
+        # Nonbinary suffix
+        suffix = self.fake.suffix_nonbinary()
+        self.assertIsInstance(suffix, str)
+        assert suffix in EnUSProvider.suffixes_nonbinary
+
+
+class TestEn(unittest.TestCase):
+    """Tests person in the en locale"""
+
+    def setUp(self):
+        self.fake = Faker("en")
+        Faker.seed(0)
+
+    def test_suffix(self):
+        # Traditional suffix -- provider does not offer a nonbinary suffix at this time
+        suffix = self.fake.suffix()
+        self.assertIsInstance(suffix, str)
+        assert suffix in EnProvider.suffixes_male or suffix in EnProvider.suffixes_female
+
+
+class TestOrIN(unittest.TestCase):
+    def setUp(self):
+        self.fake = Faker("or_IN")
+        Faker.seed(0)
+
+    def test_first_names(self):
+        """simple test to verify that we are pulling gender specific names"""
+        name = self.fake.first_name_female()
+        assert name in OrINProvider.first_names_female
+
+        name = self.fake.first_name_male()
+        assert name in OrINProvider.first_names_male
+
+        name = self.fake.first_name_unisex()
+        assert name in OrINProvider.first_names_unisex
+
+        name = self.fake.first_name()
+        assert name in OrINProvider.first_names
+
+    def test_middle_names(self):
+        """test the middle name"""
+        name = self.fake.middle_name()
+        assert name in OrINProvider.middle_names
+
+    def test_last_names(self):
+        """test the last name is generating from the provided tuple"""
+        last_name = self.fake.last_name()
+        assert last_name in OrINProvider.last_names
+
+
+class TestEnIN(unittest.TestCase):
+    """Tests person in the en_IN locale"""
+
+    def setUp(self):
+        self.fake = Faker("en_IN")
+        Faker.seed(0)
+
+    def test_first_name(self):
+        first_name = self.fake.first_name()
+        assert first_name in EnINProvider.first_names
+
+    def test_last_name(self):
+        last_name = self.fake.last_name()
+        assert last_name in EnINProvider.last_names
+
+
+class TestHiIN(unittest.TestCase):
+    """Tests person in the hi_IN locale"""
+
+    def setUp(self):
+        self.fake = Faker("hi_IN")
+        Faker.seed(0)
+
+    def test_first_name(self):
+        """Verify that gender specific names are set correctly"""
+
+        name = self.fake.first_name_male()
+        assert name in HiINProvider.first_names_male
+
+        name = self.fake.first_name_female()
+        assert name in HiINProvider.first_names_female
+
+        name = self.fake.first_name()
+        assert name in HiINProvider.first_names_male
+
+    def test_last_name(self):
+        last_name = self.fake.last_name()
+        assert last_name in HiINProvider.last_names
+
+    def test_name(self):
+        name = self.fake.name().split()
+
+        assert all(isinstance(n, str) for n in name)
+
+        prefixes = HiINProvider.prefixes_male + HiINProvider.prefixes_female + HiINProvider.prefixes
+
+        # name should always be 2-3 words. If 3, first word should be a prefix.
+        if len(name) == 3:
+            assert all(
+                [
+                    name[0] in prefixes,
+                    name[1] in HiINProvider.first_names,
+                    name[2] in HiINProvider.last_names,
+                ]
+            )
+        else:
+            assert name[0] in HiINProvider.first_names
+            if name[-1].endswith(HiINProvider.suffixes):
+                assert name[-1][:-1] in HiINProvider.last_names
+                assert name[-1][-1] in HiINProvider.suffixes
+            else:
+                assert name[-1] in HiINProvider.last_names
+
+
+class TestEnIE(unittest.TestCase):
+    """Tests person in the en-IE locale"""
+
+    def setUp(self):
+        self.fake = Faker("en-ie")
+        self.provider = EnIEProvider
+        Faker.seed(0)
+
+    def test_first_name(self):
+        # General first name
+        name = self.fake.first_name()
+        assert name
+        self.assertIsInstance(name, str)
+        assert name in self.provider.first_names
+
+        # Females first name
+        name = self.fake.first_name_female()
+        assert name
+        self.assertIsInstance(name, str)
+        assert name in self.provider.first_names
+        assert name in self.provider.first_names_female
+
+        # Male first name
+        name = self.fake.first_name_male()
+        assert name
+        self.assertIsInstance(name, str)
+        assert name in self.provider.first_names
+        assert name in self.provider.first_names_male
+
+    def test_last_name(self):
+        assert not hasattr(self.provider, "last_names_male")
+        assert not hasattr(self.provider, "last_names_female")
+        # All last names apply for all genders.
+        assert hasattr(self.provider, "last_names")
+
+        # General last name.
+        name = self.fake.last_name()
+        assert name
+        self.assertIsInstance(name, str)
+        assert name in self.provider.last_names
+
+        # Females last name.
+        name = self.fake.last_name_female()
+        assert name
+        self.assertIsInstance(name, str)
+        assert name in self.provider.last_names
+        assert name in self.provider.last_names
+
+        # Male last name.
+        name = self.fake.last_name_male()
+        assert name
+        self.assertIsInstance(name, str)
+        assert name in self.provider.last_names
+
+
+class TestGaIE(TestEnIE):
+    """Tests person in the ga-IE locale"""
+
+    def setUp(self):
+        self.fake = Faker("ga-ie")
+        self.provider = GaIEProvider
+        Faker.seed(0)
+
+
+class TestYoNG(unittest.TestCase):
+    def setUp(self):
+        self.fake = Faker("yo_NG")
+        Faker.seed(0)
+
+    def test_last_name(self):
+        """
+        Test the generation of Zulu last names.
+        """
+        # There's no gender-specific last name in Zulu.
+        self.assertTrue(hasattr(YoNGProvider, "last_names_male"))
+        self.assertTrue(hasattr(YoNGProvider, "last_names_female"))
+
+        # All last names apply to all genders.
+        self.assertTrue(hasattr(YoNGProvider, "last_names"))
+
+        # General last name.
+        name = self.fake.last_name()
+        self.assertIsInstance(name, str)
+        self.assertIn(name, YoNGProvider.last_names)
+
+        # Females last name.
+        name = self.fake.last_name_female()
+        self.assertIsInstance(name, str)
+        self.assertIn(name, YoNGProvider.last_names)
+
+        # Male last name.
+        name = self.fake.last_name_male()
+        self.assertIsInstance(name, str)
+        self.assertIn(name, YoNGProvider.last_names)
+
+    def test_first_name(self):
+        """
+        Test the generation of Zulu first names.
+        """
+        # General first name.
+        name = self.fake.first_name()
+        self.assertIsInstance(name, str)
+        self.assertIn(name, YoNGProvider.first_names)
+
+        # Female first name.
+        name = self.fake.first_name_female()
+        self.assertIsInstance(name, str)
+        self.assertIn(name, YoNGProvider.first_names)
+        self.assertIn(name, YoNGProvider.first_names_female)
+
+        # Male first name.
+        name = self.fake.first_name_male()
+        self.assertIsInstance(name, str)
+        self.assertIn(name, YoNGProvider.first_names)
+        self.assertIn(name, YoNGProvider.first_names_male)
+
+    def test_full_name(self):
+        """
+        Test the generation of full Zulu names.
+        """
+        # Full name.
+        name = self.fake.name()
+        self.assertIsInstance(name, str)
+
+        full_name_parts = name.split()
+
+        if len(full_name_parts) == 2:
+            first_name = full_name_parts[0]
+            last_name = full_name_parts[1]
+            self.assertIn(first_name, YoNGProvider.first_names)
+            self.assertIn(last_name, YoNGProvider.last_names)
+        elif len(full_name_parts) == 3:
+            prefix = full_name_parts[0]
+            first_name = full_name_parts[1]
+            last_name = full_name_parts[2]
+
+            self.assertIn(prefix, YoNGProvider.prefixes_female + YoNGProvider.prefixes_male)
+            self.assertIn(first_name, YoNGProvider.first_names)
+            self.assertIn(last_name, YoNGProvider.last_names)
+        else:
+            raise AssertionError("Invalid number of name parts. Expected 2 or 3.")
+
+
 class TestZuZa(unittest.TestCase):
     def setUp(self):
         self.fake = Faker("zu_ZA")
diff --git a/tests/test_factory.py b/tests/test_factory.py
index 48c9015e..4c226f41 100644
--- a/tests/test_factory.py
+++ b/tests/test_factory.py
@@ -151,6 +151,10 @@ class FactoryTestCase(unittest.TestCase):
                 def __init__(self, *args, **kwargs):
                     pass
 
+        # There's a cache based on the provider name, so when the provider changes behaviour we need
+        # a new name:
+        provider_path = f"test_lang_localized_provider_{with_default}"
+
         with patch.multiple(
             "faker.factory",
             import_module=MagicMock(return_value=DummyProviderModule()),
@@ -167,8 +171,8 @@ class FactoryTestCase(unittest.TestCase):
                 ("ar_EG", with_default),  # True if module defines a default locale
             ]
             for locale, expected_used in test_cases:
-                factory = Factory.create(providers=["dummy"], locale=locale)
-                assert factory.providers[0].__provider__ == "dummy"
+                factory = Factory.create(providers=[provider_path], locale=locale)
+                assert factory.providers[0].__provider__ == provider_path
                 from faker.config import DEFAULT_LOCALE
 
                 print(f"requested locale = {locale} , DEFAULT LOCALE {DEFAULT_LOCALE}")