""" Language definitions that are based on the [ISO 639-3](https://en.wikipedia.org/wiki/ISO_639-3) & [ISO 639-5](https://en.wikipedia.org/wiki/ISO_639-5). """ from __future__ import annotations from dataclasses import dataclass from functools import lru_cache from typing import Any, Union from pydantic import GetCoreSchemaHandler, GetJsonSchemaHandler from pydantic_core import PydanticCustomError, core_schema try: import pycountry except ModuleNotFoundError: # pragma: no cover raise RuntimeError( 'The `language_code` module requires "pycountry" to be installed.' ' You can install it with "pip install pycountry".' ) @dataclass class LanguageInfo: """ LanguageInfo is a dataclass that contains the language information. Args: alpha2: The language code in the [ISO 639-1 alpha-2](https://en.wikipedia.org/wiki/ISO_639-1) format. alpha3: The language code in the [ISO 639-3 alpha-3](https://en.wikipedia.org/wiki/ISO_639-3) format. name: The language name. """ alpha2: Union[str, None] alpha3: str name: str @lru_cache def _languages() -> list[LanguageInfo]: """ Return a list of LanguageInfo objects containing the language information. Returns: A list of LanguageInfo objects containing the language information. """ return [ LanguageInfo( alpha2=getattr(language, 'alpha_2', None), alpha3=language.alpha_3, name=language.name, ) for language in pycountry.languages ] @lru_cache def _index_by_alpha2() -> dict[str, LanguageInfo]: """ Return a dictionary with the language code in the [ISO 639-1 alpha-2](https://en.wikipedia.org/wiki/ISO_639-1) format as the key and the LanguageInfo object as the value. """ return {language.alpha2: language for language in _languages() if language.alpha2 is not None} @lru_cache def _index_by_alpha3() -> dict[str, LanguageInfo]: """ Return a dictionary with the language code in the [ISO 639-3 alpha-3](https://en.wikipedia.org/wiki/ISO_639-3) format as the key and the LanguageInfo object as the value. """ return {language.alpha3: language for language in _languages()} @lru_cache def _index_by_name() -> dict[str, LanguageInfo]: """ Return a dictionary with the language name as the key and the LanguageInfo object as the value. """ return {language.name: language for language in _languages()} class LanguageAlpha2(str): """LanguageAlpha2 parses languages codes in the [ISO 639-1 alpha-2](https://en.wikipedia.org/wiki/ISO_639-1) format. ```py from pydantic import BaseModel from pydantic_extra_types.language_code import LanguageAlpha2 class Movie(BaseModel): audio_lang: LanguageAlpha2 subtitles_lang: LanguageAlpha2 movie = Movie(audio_lang='de', subtitles_lang='fr') print(movie) #> audio_lang='de' subtitles_lang='fr' ``` """ @classmethod def _validate(cls, __input_value: str, _: core_schema.ValidationInfo) -> LanguageAlpha2: """ Validate a language code in the ISO 639-1 alpha-2 format from the provided str value. Args: __input_value: The str value to be validated. _: The Pydantic ValidationInfo. Returns: The validated language code in the ISO 639-1 alpha-2 format. """ if __input_value not in _index_by_alpha2(): raise PydanticCustomError('language_alpha2', 'Invalid language alpha2 code') return cls(__input_value) @classmethod def __get_pydantic_core_schema__( cls, source: type[Any], handler: GetCoreSchemaHandler ) -> core_schema.AfterValidatorFunctionSchema: """ Return a Pydantic CoreSchema with the language code in the ISO 639-1 alpha-2 format validation. Args: source: The source type. handler: The handler to get the CoreSchema. Returns: A Pydantic CoreSchema with the language code in the ISO 639-1 alpha-2 format validation. """ return core_schema.with_info_after_validator_function( cls._validate, core_schema.str_schema(to_lower=True), ) @classmethod def __get_pydantic_json_schema__( cls, schema: core_schema.CoreSchema, handler: GetJsonSchemaHandler ) -> dict[str, Any]: """ Return a Pydantic JSON Schema with the language code in the ISO 639-1 alpha-2 format validation. Args: schema: The Pydantic CoreSchema. handler: The handler to get the JSON Schema. Returns: A Pydantic JSON Schema with the language code in the ISO 639-1 alpha-2 format validation. """ json_schema = handler(schema) json_schema.update({'pattern': r'^\w{2}$'}) return json_schema @property def alpha3(self) -> str: """The language code in the [ISO 639-3 alpha-3](https://en.wikipedia.org/wiki/ISO_639-3) format.""" return _index_by_alpha2()[self].alpha3 @property def name(self) -> str: """The language name.""" return _index_by_alpha2()[self].name class LanguageName(str): """LanguageName parses languages names listed in the [ISO 639-3 standard](https://en.wikipedia.org/wiki/ISO_639-3) format. ```py from pydantic import BaseModel from pydantic_extra_types.language_code import LanguageName class Movie(BaseModel): audio_lang: LanguageName subtitles_lang: LanguageName movie = Movie(audio_lang='Dutch', subtitles_lang='Mandarin Chinese') print(movie) #> audio_lang='Dutch' subtitles_lang='Mandarin Chinese' ``` """ @classmethod def _validate(cls, __input_value: str, _: core_schema.ValidationInfo) -> LanguageName: """ Validate a language name from the provided str value. Args: __input_value: The str value to be validated. _: The Pydantic ValidationInfo. Returns: The validated language name. """ if __input_value not in _index_by_name(): raise PydanticCustomError('language_name', 'Invalid language name') return cls(__input_value) @classmethod def __get_pydantic_core_schema__( cls, source: type[Any], handler: GetCoreSchemaHandler ) -> core_schema.AfterValidatorFunctionSchema: """ Return a Pydantic CoreSchema with the language name validation. Args: source: The source type. handler: The handler to get the CoreSchema. Returns: A Pydantic CoreSchema with the language name validation. """ return core_schema.with_info_after_validator_function( cls._validate, core_schema.str_schema(), serialization=core_schema.to_string_ser_schema(), ) @property def alpha2(self) -> Union[str, None]: """The language code in the [ISO 639-1 alpha-2](https://en.wikipedia.org/wiki/ISO_639-1) format. Does not exist for all languages.""" return _index_by_name()[self].alpha2 @property def alpha3(self) -> str: """The language code in the [ISO 639-3 alpha-3](https://en.wikipedia.org/wiki/ISO_639-3) format.""" return _index_by_name()[self].alpha3 class ISO639_3(str): """ISO639_3 parses Language in the [ISO 639-3 alpha-3](https://en.wikipedia.org/wiki/ISO_639-3_alpha-3) format. ```py from pydantic import BaseModel from pydantic_extra_types.language_code import ISO639_3 class Language(BaseModel): alpha_3: ISO639_3 lang = Language(alpha_3='ssr') print(lang) # > alpha_3='ssr' ``` """ allowed_values_list = [lang.alpha_3 for lang in pycountry.languages] allowed_values = set(allowed_values_list) @classmethod def _validate(cls, __input_value: str, _: core_schema.ValidationInfo) -> ISO639_3: """ Validate a ISO 639-3 language code from the provided str value. Args: __input_value: The str value to be validated. _: The Pydantic ValidationInfo. Returns: The validated ISO 639-3 language code. Raises: PydanticCustomError: If the ISO 639-3 language code is not valid. """ if __input_value not in cls.allowed_values: raise PydanticCustomError( 'ISO649_3', 'Invalid ISO 639-3 language code. See https://en.wikipedia.org/wiki/ISO_639-3' ) return cls(__input_value) @classmethod def __get_pydantic_core_schema__( cls, _: type[Any], __: GetCoreSchemaHandler ) -> core_schema.AfterValidatorFunctionSchema: """ Return a Pydantic CoreSchema with the ISO 639-3 language code validation. Args: _: The source type. __: The handler to get the CoreSchema. Returns: A Pydantic CoreSchema with the ISO 639-3 language code validation. """ return core_schema.with_info_after_validator_function( cls._validate, core_schema.str_schema(min_length=3, max_length=3), ) @classmethod def __get_pydantic_json_schema__( cls, schema: core_schema.CoreSchema, handler: GetJsonSchemaHandler ) -> dict[str, Any]: """ Return a Pydantic JSON Schema with the ISO 639-3 language code validation. Args: schema: The Pydantic CoreSchema. handler: The handler to get the JSON Schema. Returns: A Pydantic JSON Schema with the ISO 639-3 language code validation. """ json_schema = handler(schema) json_schema.update({'enum': cls.allowed_values_list}) return json_schema class ISO639_5(str): """ISO639_5 parses Language in the [ISO 639-5 alpha-3](https://en.wikipedia.org/wiki/ISO_639-5_alpha-3) format. ```py from pydantic import BaseModel from pydantic_extra_types.language_code import ISO639_5 class Language(BaseModel): alpha_3: ISO639_5 lang = Language(alpha_3='gem') print(lang) # > alpha_3='gem' ``` """ allowed_values_list = [lang.alpha_3 for lang in pycountry.language_families] allowed_values_list.sort() allowed_values = set(allowed_values_list) @classmethod def _validate(cls, __input_value: str, _: core_schema.ValidationInfo) -> ISO639_5: """ Validate a ISO 639-5 language code from the provided str value. Args: __input_value: The str value to be validated. _: The Pydantic ValidationInfo. Returns: The validated ISO 639-3 language code. Raises: PydanticCustomError: If the ISO 639-5 language code is not valid. """ if __input_value not in cls.allowed_values: raise PydanticCustomError( 'ISO649_5', 'Invalid ISO 639-5 language code. See https://en.wikipedia.org/wiki/ISO_639-5' ) return cls(__input_value) @classmethod def __get_pydantic_core_schema__( cls, _: type[Any], __: GetCoreSchemaHandler ) -> core_schema.AfterValidatorFunctionSchema: """ Return a Pydantic CoreSchema with the ISO 639-5 language code validation. Args: _: The source type. __: The handler to get the CoreSchema. Returns: A Pydantic CoreSchema with the ISO 639-5 language code validation. """ return core_schema.with_info_after_validator_function( cls._validate, core_schema.str_schema(min_length=3, max_length=3), ) @classmethod def __get_pydantic_json_schema__( cls, schema: core_schema.CoreSchema, handler: GetJsonSchemaHandler ) -> dict[str, Any]: """ Return a Pydantic JSON Schema with the ISO 639-5 language code validation. Args: schema: The Pydantic CoreSchema. handler: The handler to get the JSON Schema. Returns: A Pydantic JSON Schema with the ISO 639-5 language code validation. """ json_schema = handler(schema) json_schema.update({'enum': cls.allowed_values_list}) return json_schema