Locale-aware person name formatting built on the Unicode CLDR Person Names specification and the Localize library.
Features
CLDR person-name formatting — order, length, usage and formality resolved from CLDR data for every Localize locale, and tested against CLDR's own conformance suite.
Script-aware initials — initials use UAX #29 extended grapheme clusters from
unicode_string, which Brahmic scripts need.Existing structs — the
Localize.PersonName.Convertibleprotocol or theLocalize.PersonNamebehaviour lets domain structs such as%User{}format directly.MF2 messages —
Localize.PersonName.MF2provides a:personNamefunction forLocalize.Messagetemplates.Ecto storage — the optional
Localize.PersonName.Ecto.Map.Typestores a name asjsonbwith its parts intact.String conversion —
String.CharsandLocalize.Charsimplementations format a name with the current locale's defaults.Formatting locale switching — the opt-in
:locale_switchingoption formats a name in its own script's conventions.
Supported Elixir and OTP versions
Requires Elixir 1.17 or later on OTP 26 or later.
Installation
def deps do
[
{:localize_person_names, "~> 1.1"}
]
endQuick start
# Create a person name
{:ok, name} = Localize.PersonName.new(title: "Mr.", given_name: "José", surname: "Valim", credentials: "Ph.D.", locale: "pt")
# Format with defaults (locale-driven length, formality, addressing usage)
Localize.PersonName.to_string(name)
#=> {:ok, "José"}
# Format with explicit options
Localize.PersonName.to_string(name, format: :long, formality: :formal, usage: :referring)
#=> {:ok, "Mr. José Valim Ph.D."}
# Format in a different locale. Japanese joins foreign name parts with "・"
# and places the title after the name; names are never transliterated.
Localize.PersonName.to_string(name, locale: :ja)
#=> {:ok, "José・ValimMr."}Options
:format—:short,:medium, or:long. Controls how many name parts appear. Default is derived from the formatting locale.:usage—:addressing(speaking to someone),:referring(speaking about someone), or:monogram(abbreviated, e.g., initials). Default is:addressing.:formality—:formalor:informal. Controls whether titles, credentials, and full names are used versus nicknames and abbreviated forms. Default is derived from the formatting locale.:order—:given_first,:surname_first, or:sorting. Default is derived from the person name's locale and the formatting locale.:locale— The formatting locale. Default isLocalize.get_locale().
Person name fields
| Field | Description | Example |
|---|---|---|
given_name | Primary given name (required) | "José" |
surname | Family name | "Valim" |
title | Honorific | "Mr.", "Dr." |
other_given_names | Middle name(s) or patronymic | "Carlos" |
informal_given_name | Nickname or casual form | "Zé" |
surname_prefix | Tussenvoegsel / particle | "von", "de" |
other_surnames | Secondary/maternal surname | "González" |
generation | Generation marker | "Jr.", "III" |
credentials | Accreditation | "Ph.D.", "MD" |
preferred_order | Explicit ordering preference | :given_first, :surname_first |
locale | Locale of the name data | "pt", Localize.LanguageTag.t() |
Integrating existing structs
Any struct can participate in person name formatting, either through the Localize.PersonName.Convertible protocol or through the Localize.PersonName behaviour. See Integrating existing name structs for the full comparison and recommendations.
MF2 message formatting
A :personName MF2 function is provided as Localize.PersonName.MF2 for use with Localize.Message. See Using Localize.PersonName with Localize.Message for formal and informal worked examples.
Conformance
Test coverage
36,960 tests across 110 CLDR locales, 0 failures. Test data is from the CLDR person name test suite, which provides format conformance tests for each locale covering all combinations of order, length, usage, and formality.
Locale coverage
The test data mirrors CLDR's common/testData/personNameTest directory exactly: 111 locale files as of CLDR 49. 110 pass all tests. The one excluded locale is documented in the CLDR Specification Conformance guide and summarised below:
| Locale | Failures | Cause |
|---|---|---|
| my | 1 | Word-break segmentation splits a transliterated Myanmar given name into six words where ICU finds two. |
Specification deviances
See CLDR Specification Conformance for detailed analysis of these issues:
Empty field removal with grouping punctuation (cs, sk) — Fixed in this implementation by detecting parentheses/brackets during literal coalescing.
Initial derivation requires UAX #29 grapheme clusters — Spec says "first grapheme cluster" without specifying UAX #29. This implementation uses UAX #29 extended grapheme clusters via
unicode_string, which is required for correct initials in Brahmic scripts.Malayalam sample names contain a digit homoglyph (ml) — CLDR 49 spells a sample name with U+0D6A digit four where the chillu U+0D7C is meant. Harmless here since initials follow the reference formatter's rule, but worth an upstream fix.
Format selection tiebreaker and yo_BJ test data mismatch — Historical. Both concerned regional test files CLDR retired in CLDR 45; the analysis is kept in the guide.
Known limitations
See TODO.md for tracked implementation gaps.
Documentation
Integrating existing name structs — two ways to wire existing domain structs (
%User{},%Customer{}, etc.) into the formatter: theLocalize.PersonName.Convertibleprotocol (recommended) and theLocalize.PersonNamebehaviour.Using Localize.PersonName with Localize.Message — integrating person name formatting into MF2 message templates via a custom function, with formal and informal worked examples.
Cultural naming conventions — a field guide to how naming differs across cultures, the risks of getting it wrong, and how this library addresses them.
CLDR Specification Conformance — where CLDR's test data and the specification text disagree, and how this implementation resolves each case.
Full API documentation is on HexDocs.
License
Apache License 2.0. See LICENSE.md.