Localize Person Names

Copy Markdown View Source

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.Convertible protocol or the Localize.PersonName behaviour lets domain structs such as %User{} format directly.

  • MF2 messages — Localize.PersonName.MF2 provides a :personName function for Localize.Message templates.

  • Ecto storage — the optional Localize.PersonName.Ecto.Map.Type stores a name as jsonb with its parts intact.

  • String conversion — String.Chars and Localize.Chars implementations format a name with the current locale's defaults.

  • Formatting locale switching — the opt-in :locale_switching option 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"}
  ]
end

Quick 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 — :formal or :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 is Localize.get_locale().

Person name fields

FieldDescriptionExample
given_namePrimary given name (required)"José"
surnameFamily name"Valim"
titleHonorific"Mr.", "Dr."
other_given_namesMiddle name(s) or patronymic"Carlos"
informal_given_nameNickname or casual form"Zé"
surname_prefixTussenvoegsel / particle"von", "de"
other_surnamesSecondary/maternal surname"González"
generationGeneration marker"Jr.", "III"
credentialsAccreditation"Ph.D.", "MD"
preferred_orderExplicit ordering preference:given_first, :surname_first
localeLocale 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:

LocaleFailuresCause
my1Word-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:

  1. Empty field removal with grouping punctuation (cs, sk) — Fixed in this implementation by detecting parentheses/brackets during literal coalescing.

  2. 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.

  3. 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.

  4. 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

Full API documentation is on HexDocs.

License

Apache License 2.0. See LICENSE.md.