langcodes: IETF Language Tag Tools

repository·master·Indexed 43 days ago

https://github.com/rspeer/langcodes

A Python library for working with IETF language tags (BCP 47). It provides tools for standardizing, parsing, validating, and converting language codes, including support for ISO 639-2 alpha3 codes. Key features include the Language class for tag representation, natural language name conversion via .display_name() and langcodes.find(), and language distance calculation using tag_distance() to find the closest supported match.

Tokens
8.1K
Snippets
31
Records
37
Agent score
56%

What's inside langcodes

  1. Install the optional language data feature

    master

    To use methods related to language names in different languages or demographic data (like speaking/writing populations), you must install the optional language_data package.

    You can install it via pip:

    pip install langcodes[data]

    If you are managing dependencies in a setup.py file, use langcodes[data] as the requirement.

  2. Understand language tag subtag types

    master

    When using parse_tag(), the returned tuples use the following subtag type names to categorize components:

    • language: The primary language code.
    • extlang: Extended language codes (used for macrolanguages like Chinese or Arabic).
    • script: The writing system (e.g., Latn, Hant).
    • territory: The geographic region (e.g., US, 419).
    • variant: A specific variation of the language.
    • extension: BCP 47 extensions (e.g., u-co-backward).
    • private: Private-use subtags starting with x-.
    • grandfathered: Legacy tags from RFC 3066.
  3. Understand the TagTriple data structure

    master

    In the context of language distance calculation, a TagTriple is a specific type alias used to represent a fully qualified language specification. It is defined as a Tuple[str, str, str] consisting of:

    1. Language: The language code (e.g., "en", "es", "ar").
    2. Script: The script code (e.g., "Latn", "Cyrl", "Hant").
    3. Territory: The territory/region code (e.g., "US", "GB", "419").

    These triples are typically generated by 'maximizing' a standard language tag to ensure all three components are present for the distance algorithm to process.

  4. Use Language objects to parse and represent language codes

    master

    The Language class represents the parsed components of a language tag. You can create instances using Language.get(tag_string) or Language.make(...) with specific fields.

    Fields available in a Language object:

    • language: The code for the language itself.
    • script: The 4-letter writing system code.
    • territory: The 2-letter or 3-digit region/country code.
    • extlangs: List of specific language codes (deprecated).
    • variants: Specific usage variations.
    • extensions: System-specific information (e.g., Unicode collation).
    • private: Codes starting with x-.

    Key behaviors:

    • Normalization: By default, Language.get() replaces non-standard/overlong tags with their standardized versions. To get the literal codes from the string, use normalize=False.
    • Caching: Values are cached; calling get or make with the same inputs returns the same object.
    • String Conversion: Use str(language_object) to convert the object back to its standard BCP 47 string form.
    from langcodes import *
    
    # Parsing a tag (standardized by default)
    lang = Language.get('en-Latn-US')
    # Result: Language.make(language='en', script='Latn', territory='US')
    
    # Parsing without normalization
    lang_literal = Language.get('sgn-US', normalize=False)
    # Result: Language.make(language='sgn', territory='US')
    
    # Creating from fields
    lang_manual = Language.make(language='en', script='Latn', territory='US')
    
    # Converting back to string
    str(lang_manual) # 'en-Latn-US'
  5. Convert natural language names to codes using langcodes.find()

    master

    The langcodes.find() function performs the reverse of .display_name(), converting a natural language name (like "French") into a Language object (like fr).

    • Language Support: Works with hundreds of names used on Wiktionary across many languages.
    • Ambiguity: If a name is ambiguous (e.g., "Tonga" could refer to to or tog), langcodes.find() may raise a LookupError. To resolve this, provide the language= parameter to specify the language in which the name is written.
    • Limitation: It currently ignores parenthetical expressions (e.g., find('English (Canada)') returns the base en language).
    >>> import langcodes
    >>> langcodes.find('french')
    Language.make(language='fr')
    
    >>> # Resolving ambiguity
    >>> langcodes.find('tonga', language='id')
    Language.make(language='to')
  6. Calculate language distance with tag_distance()

    master

    The tag_distance function returns a numeric value from 0 to 134 representing the distance between a desired language and a supported language. This distance is based on CLDR data and indicates how similar or intelligible two languages are.

    Distance Value Reference

    ValueMeaning
    0Same language (after normalization)
    1-3Minor regional difference
    4-9Significant but unproblematic regional difference
    10-24Gray area; potential usability/understanding issues
    25-50Not similar, but some demographic intelligibility expected
    51-79Large barriers to understanding
    80-99Different languages in the same script
    100+No commonality

    See the tag_distance docstring for more detailed examples.

  7. Get ISO 639-2 alpha3 codes from a Language object

    master

    While BCP 47 prefers 2-letter codes, some applications require 3-letter (alpha3) codes. Use Language.to_alpha3() to retrieve them.

    • By default, it returns the 'terminology' code.
    • Passing variant='B' returns the 'bibliographic' code.
    • The method always returns a 3-letter string.
    • If a language code is unknown and has no alpha3 mapping, it raises a LookupError.
    from langcodes import *
    
    # Get default (terminology) alpha3 code
    Language.get('fr').to_alpha3()    # 'fra'
    Language.get('de').to_alpha3()    # 'deu'
    
    # Get bibliographic alpha3 code
    Language.get('fr').to_alpha3(variant='B') # 'fre'
    
    # Handling errors for unknown codes
    try:
        Language.get('un').to_alpha3()
    except LookupError:
        print("Unknown language")
  8. Find the best matching supported language

    master

    If you have a list of supported_languages and a desired_language from a user, you can use closest_supported_match to find the best available option.

    closest_supported_match(desired_language, supported_languages, max_distance=25)

    Returns the language tag of the best-supported language. If no language in the list is within the max_distance threshold, it returns None.

    closest_match(desired_language, supported_languages, max_distance=None)

    Returns a tuple containing (best_matching_tag, distance). If no match is found within the max_distance (or if no distance is provided and no match exists), it returns ('und', 1000).

    # Returns the best tag or None if too far
    closest_supported_match('af', ['en', 'nl', 'zu'], max_distance=10) # returns None
    
    # Returns (tag, distance)
    closest_match('id', ['zsm', 'mhp']) # returns ('zsm', 14)
    
    # Returns (tag, distance) with a custom cutoff
    closest_match('ja', ['ja-Latn-hepburn', 'en'], max_distance=60) # returns ('ja-Latn-hepburn', 50)
  9. Check if a language code is valid

    master

    A language code is considered valid if every subtag (language, script, territory, and variants) is assigned a meaning by IANA.

    • Use Language.get(tag).is_valid() to check a parsed object.
    • Use the top-level tag_is_valid(tag) function for a more convenient check that returns False even for tags that fail to parse.

    Note: und (undetermined) and private use codes (starting with x-) are considered valid. Non-ASCII characters in tags are considered invalid because they fail to parse.

    from langcodes import *
    
    # Checking validity of parsed objects
    Language.get('ja').is_valid() # True
    Language.get('jp').is_valid() # False
    
    # Using the top-level convenience function
    tag_is_valid('C')      # False
    tag_is_valid('en-000') # False
    tag_is_valid('und')    # True
    tag_is_valid('x-other') # True
    
    # Invalid due to non-ASCII characters
    tag_is_valid('zh-普通话') # False
  10. Get demographic data using .speaking_population() and .writing_population()

    master

    Using the optional language_data package, you can retrieve Unicode estimates for language demographics.

    • .speaking_population(): Estimates the number of speakers. You can limit this to a specific territory by providing a territory code (e.g., es-BR). Script codes are ignored as they don't affect speaking population.
    • .writing_population(): Estimates the number of people who write the language. This is often derived from literacy rates and may be an overestimate.

    Both methods support territory-specific filtering.

    >>> Language.get('es').speaking_population()
    493528077
    
    >>> Language.get('es-BR').speaking_population()
    76218
    
    >>> Language.get('zh-Hans').writing_population()
    36863340