Skip to main content

humanjoin

Join a list into a natural-language phrase: "a, b, and c" with or without the Oxford comma, "a, b, and 3 more" when the list is long, and the list patterns of every Unicode CLDR locale for "a, b und c", "a、b和c" or "a, b o c". Typed, zero dependencies.

Install

pip install humanjoin

Usage

English with a chosen conjunction, as in 0.1:

from humanjoin import join, join_max

print(join(["apple", "pear", "plum"]))
print(join(["apple", "pear", "plum"], oxford=False))
print(join(["red", "green"], conj="or"))
print(join_max(["a", "b", "c", "d", "e"], 2))
print(join_max(["a", "b", "c", "d", "e"], 2, conj="or", oxford=False))
# apple, pear, and plum
# apple, pear and plum
# red or green
# a, b, and 3 more
# a, b or 3 more

Any language, with the vocabulary of Intl.ListFormat: type is conjunction, disjunction or unit, style is long, short or narrow.

from humanjoin import format_list

items = ["Ana", "Ben", "Cleo"]
print(format_list(items, "de"))
print(format_list(items, "en-GB"))
print(format_list(items, "fr", type="disjunction"))
print(format_list(items, "zh-TW"))
print(format_list(["5 ft", "2 in"], "en", type="unit", style="narrow"))
print(format_list(items, "en", style="short"))
# Ana, Ben und Cleo
# Ana, Ben and Cleo
# Ana, Ben ou Cleo
# Ana、Ben和Cleo
# 5 ft 2 in
# Ana, Ben, & Cleo

The rules ICU applies on top of the data are applied too: Spanish "y" becomes "e" before an "i" sound and "o" becomes "u" before an "o" sound.

from humanjoin import format_list

print(format_list(["padre", "hijo"], "es"))
print(format_list(["siete", "ocho"], "es", type="disjunction"))
# padre e hijo
# siete u ocho

Long lists fold into a tail. more is a template with {n} or a callable taking the hidden count, so plurals and other languages are up to you:

from humanjoin import format_list

names = ["Ana", "Ben", "Cleo", "Dan"]
print(format_list(names, "en", limit=2))
print(format_list(names, "de", limit=3, more="{n} weitere"))
print(format_list(names, "en", limit=3, more=lambda n: f"{n} other" + "s" * (n != 1)))
# Ana, Ben, and 2 more
# Ana, Ben, Cleo und 1 weitere
# Ana, Ben, Cleo, and 1 other

Locales and patterns:

from humanjoin import list_pattern, resolve_locale

print(resolve_locale("pt_BR.UTF-8"), resolve_locale("zh-TW"), resolve_locale("iw"))
pattern = list_pattern("en", "disjunction")
print(pattern.end)
print(pattern.format(["tea", "coffee", "water"]))
# pt zh-Hant he
# {0}, or {1}
# tea, coffee, or water

From the shell, items as arguments or one per line on stdin:

$ humanjoin tea coffee water --locale it
tea, coffee e water
$ printf 'tea\ncoffee\n' | humanjoin -l ja -t disjunction
teaまたはcoffee

API

Name What it does
join(items, *, conj="and", oxford=True) English-style phrase with any conjunction and an Oxford comma toggle.
join_max(items, limit, *, more="{n} more", conj="and", oxford=True) join, keeping at most limit items and folding the rest into a tail.
format_list(items, locale="en", *, type=, style=, limit=None, more=) The phrase locale writes, from the CLDR list patterns.
list_pattern(locale="en", type="conjunction", style="long") The ListPattern behind format_list.
ListPattern(two, start, middle, end, language=None) Four validated patterns and the format algorithm; build your own.
resolve_locale(locale) The CLDR locale whose data a tag uses.
supported_locales() Every CLDR locale in the table, sorted.
cldr_version() The CLDR release the table comes from.
ListType, ListStyle Literal types of the type and style values.

Items are converted with str. A wrong type or style, a negative limit or a malformed locale tag raise ValueError; a language CLDR has no data for raises LookupError.

How it compares with Intl.ListFormat

The table holds the list patterns of all 766 locales of CLDR 48, and the formatter follows the ICU list formatter. The test suite checks every locale, type and style against the CLDR files and against Intl.ListFormat of Node 24 (ICU 78.3, CLDR 48) for lists of zero to four items. The differences are deliberate:

  • ICU ships fewer locales than CLDR and falls back to the default locale of the machine when it has none; here every CLDR locale has its own data and an unknown language raises.
  • und and root name the CLDR root locale instead of the default locale.
  • POSIX spellings such as pt_BR.UTF-8 are accepted.
  • Items do not have to be strings.

Locale resolution replaces deprecated codes (iw is he, sh is sr-Latn, en-UK is en-GB), reads POSIX script modifiers (sr_RS@latin), adds the likely script where a language is written in several (zh-TW reads zh-Hant, zh-SG reads zh-Hans-SG, sr-ME reads sr-Latn-ME), keeps variant locales (ca-ES-valencia), then follows the CLDR parent locales (en-IN reads en-001, pt-FR reads pt-PT) and drops subtags until it finds data. Every name supported_locales() returns resolves to itself.

Why

Joining a list into readable prose is written again in every project, and the details are easy to get wrong: no comma for two items, the Oxford comma toggle, an "and N more" tail, and every other language. humanize.natural_list has no Oxford comma and no overflow limit. inflect.join has the Oxford comma but pulls in dependencies and is English only. Babel has format_list with CLDR data, but it is a large dependency and has no truncation. This is a small typed primitive for exactly those cases.

Development

python scripts/fetch_upstream.py   # CLDR, ICU, Unicode and ECMA-402 reference files, not committed
python scripts/build_patterns.py   # derive src/humanjoin/_cldr.json from the CLDR files
pwsh scripts/check.ps1             # format, lint, types, tests

License

MIT, see LICENSE. The pattern table is derived from Unicode CLDR data, used under the Unicode License v3, see LICENSE-UNICODE.

Metadata

Release files for humanjoin 0.2.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for humanjoin 0.2.0
File Size Uploaded Provenance
humanjoin-0.2.0.tar.gz 42.2 kB Without provenance Details

Built distribution (wheel)

Table of built distributions (wheels) for humanjoin 0.2.0
File Interpreter ABI Platform
humanjoin-0.2.0-py3-none-any.whl Python 3 none any Details

Total release size: 68.8 kB

Release files / humanjoin-0.2.0.tar.gz

Download URL humanjoin-0.2.0.tar.gz
Size 42.2 kB
Tags Source
SHA-256 checksum
How to use checksums
ca624711c2fce3a5f9bd5945cf805ad07bbd93a16721664c5ecbf089d181ec08
BLAKE2b-256 checksum
How to use checksums
3d4603487d71efef0ce440fc1d1520331aa4592dc1564efcb9e6e03fd4afc69c
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/7.0.0 CPython/3.13.2

Release files / humanjoin-0.2.0-py3-none-any.whl

Download URL humanjoin-0.2.0-py3-none-any.whl
Size 26.6 kB
Tags Python 3
SHA-256 checksum
How to use checksums
42131b9e6ee480526c36ff4cb84c25642a5914f8be144145ce8603c2d17eea1a
BLAKE2b-256 checksum
How to use checksums
deee36a3da509022b43f0e8d7f51a624500abf3514762828ba88a21b02c217c3
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/7.0.0 CPython/3.13.2

Security for humanjoin 0.2.0

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

No provenance found

None of the files in this release were uploaded with provenance metadata.

A note on package security

PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.

To learn more about protecting yourself from malicious actors, visit our package security documentation.

Release history Release notifications | RSS feed

This release

0.2.0 This release

2 release files No provenance

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page