{"id":28373283,"url":"https://github.com/nigelhorne/text-names-abbreviate","last_synced_at":"2026-08-14T07:32:05.117Z","repository":{"id":296107485,"uuid":"992315965","full_name":"nigelhorne/Text-Names-Abbreviate","owner":"nigelhorne","description":"Create abbreviated name formats from full names","archived":false,"fork":false,"pushed_at":"2026-08-03T21:01:50.000Z","size":1020,"stargazers_count":0,"open_issues_count":1,"forks_count":0,"subscribers_count":0,"default_branch":"master","last_synced_at":"2026-08-07T08:27:05.840Z","etag":null,"topics":["cpan","cpan-module","perl","perl5"],"latest_commit_sha":null,"homepage":"https://metacpan.org/dist/Text-Names-Abbreviate","language":"Perl","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"gpl-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/nigelhorne.png","metadata":{"files":{"readme":"README.md","changelog":"Changes","contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2025-05-29T00:50:21.000Z","updated_at":"2026-07-11T01:05:14.000Z","dependencies_parsed_at":"2025-05-29T01:57:55.842Z","dependency_job_id":"578110ba-ff08-47ab-990e-9a42e9cb7fd8","html_url":"https://github.com/nigelhorne/Text-Names-Abbreviate","commit_stats":null,"previous_names":["nigelhorne/text-names-abbreviate"],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/nigelhorne/Text-Names-Abbreviate","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/nigelhorne%2FText-Names-Abbreviate","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/nigelhorne%2FText-Names-Abbreviate/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/nigelhorne%2FText-Names-Abbreviate/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/nigelhorne%2FText-Names-Abbreviate/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/nigelhorne","download_url":"https://codeload.github.com/nigelhorne/Text-Names-Abbreviate/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/nigelhorne%2FText-Names-Abbreviate/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":36636752,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-08-06T04:43:03.162Z","status":"online","status_checked_at":"2026-08-14T02:00:06.934Z","response_time":54,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["cpan","cpan-module","perl","perl5"],"created_at":"2025-05-29T19:08:42.848Z","updated_at":"2026-08-14T07:32:05.111Z","avatar_url":"https://github.com/nigelhorne.png","language":"Perl","funding_links":[],"categories":[],"sub_categories":[],"readme":"# NAME\n\nText::Names::Abbreviate - Create abbreviated name formats from full names\n\n## VERSION\n\nVersion 0.04\n\n# SYNOPSIS\n\n    use Text::Names::Abbreviate qw(abbreviate);\n\n    say abbreviate('John Quincy Adams');                            # J. Q. Adams\n    say abbreviate('Adams, John Quincy');                          # J. Q. Adams\n    say abbreviate('George R R Martin', { format =\u003e 'initials' }); # G.R.R.M.\n    say abbreviate('Ludwig van Beethoven');                         # L. van Beethoven\n    say abbreviate(\"R\\x{e9}mi Dupr\\x{e9}\");                       # R. Dupr\\x{e9}\n\n# DESCRIPTION\n\nThis module provides simple abbreviation logic for full personal names with\nmultiple formatting options and styles.  Input is expected to be a personal\nname consisting of one or more whitespace-separated components interpreted as:\n\n    First [Middle ...] Last\n\nNames consisting of a single component are returned unchanged.\n\n# SUBROUTINES/METHODS\n\n## abbreviate\n\nProduce an abbreviated form of a personal name.\n\n### Purpose\n\nAccept a full name in either `First Middle Last` or `Last, First Middle`\nform and return a formatted abbreviated string according to the requested\n`format`, `style`, `separator`, and `particles` options.  Input is\nNFC-normalised before processing, so strings differing only in Unicode\nnormalisation form produce identical output.  Surname particles (`van`,\n`de`, `von`, etc.) are absorbed into the last-name component by default.\n\n### Args\n\n- name (required)\n\n    Non-empty string.  Accepted in two forms:\n\n    - `First [Middle ...] Last`\n    - `Last, First [Middle ...]`\n\n    A leading comma (`\", John\"`) signals that no last name is present; only\n    initials are produced.\n\n- format (optional, default `default`)\n\n    One of `default`, `initials`, `compact`, `shortlast`.\n\n    - `default`   -- `J. Q. Adams`\n    - `initials`  -- `J.Q.A.`\n    - `compact`   -- `JQA`\n    - `shortlast` -- initials then full last name; honours `last_first` style\n    (e.g. `Adams, J. Q.`).\n\n- style (optional, default `first_last`)\n\n    One of `first_last`, `last_first`.  All formats honour this option.\n\n- separator (optional, default `.`)\n\n    String appended after each initial.  Empty string removes all punctuation.\n\n- particles (optional, default enabled)\n\n    Controls detection of surname particles (`van`, `de`, `von`, etc.) that\n    prefix the last name.  Tokens immediately before the last name that appear in\n    the particle list are absorbed into the last-name component.  Matching is\n    case-sensitive: only lowercase tokens are eligible.\n\n    - omitted or `1` - use the built-in particle list\n    - `0` - disable particle detection entirely\n    - arrayref of strings - use that list instead of the built-in one\n\n        abbreviate('Ludwig van Beethoven');                           # L. van Beethoven\n        abbreviate('Ludwig van Beethoven', { particles =\u003e 0 });      # L. v. Beethoven\n        abbreviate('Felipe de la Cruz', { particles =\u003e ['de','la'] }); # F. de la Cruz\n\n### Returns\n\nA plain string.  Returns `''` for inputs that normalise to nothing (e.g. a\nbare comma).\n\n### Side Effects\n\nNone.  The function is purely functional with no persistent state.\n\n### Usage\n\n    # Positional\n    my $abbrev = abbreviate('John Quincy Adams');\n\n    # Options hashref\n    my $abbrev = abbreviate('John Quincy Adams', {\n        format    =\u003e 'initials',\n        style     =\u003e 'last_first',\n        separator =\u003e '-',\n    });\n\n### API SPECIFICATION\n\n    INPUT\n    {\n      name      =\u003e { type =\u003e 'string', min =\u003e 1, optional =\u003e 0 },\n      format    =\u003e { type =\u003e 'string',\n                     memberof =\u003e [qw(default initials compact shortlast)],\n                     optional =\u003e 1 },\n      style     =\u003e { type =\u003e 'string',\n                     memberof =\u003e [qw(first_last last_first)],\n                     optional =\u003e 1 },\n      separator =\u003e { type =\u003e 'string', optional =\u003e 1 },\n      particles =\u003e { type =\u003e ['boolean', 'arrayref'], optional =\u003e 1 },\n    }\n\n    OUTPUT\n    { type =\u003e 'string' }    # croaks on argument error\n\n### MESSAGES\n\n    Error                                    Meaning / Resolution\n    ---------------------------------------  -----------------------------------------------\n    name parameter missing or undefined      Called without a name argument; supply one.\n    name must be a non-empty string          Passed '' or undef; supply a non-empty string.\n    format must be one of: ...               Invalid format constant; see API SPECIFICATION.\n    style must be one of: ...               Invalid style constant; see API SPECIFICATION.\n    particles: must be one of boolean,       Passed a string or hashref; pass 0/1 or an\n      arrayref                               arrayref of particle strings instead.\n\n### PSEUDOCODE\n\n    FUNCTION abbreviate(name, options):\n       Validate parameters via %PARAM_SCHEMA       (croak on violation)\n       Assign defaults: format=default, style=first_last, sep=\".\", particles=built-in list\n       _normalize_name(name):\n           - NFC-normalize to precomposed Unicode form\n           - collapse consecutive commas\n           - detect and reorder \"Last, First\" form\n           - track $had_leading_comma (input had no last-name component)\n           - collapse internal whitespace; trim\n       Return '' if normalized name is empty\n       _extract_parts(name, had_leading_comma, format, style, particles):\n           - tokenize on whitespace\n           - pop last token as $last_name (unless leading-comma form)\n           - if particles enabled: while last remaining token is a particle,\n             pop it and prepend to $last_name\n           - build @initials from remaining tokens (first char each)\n           - if style=last_first and format!=default: unshift last initial, clear $last_name\n           - filter empty initials\n       Format result:\n           compact   -\u003e join('', @initials, first($last_name))\n           initials  -\u003e join($sep, @all_letters) . $sep\n           shortlast -\u003e join(' ', map {\"$_$sep\"} @initials) . \" $last_name\"\n           default   -\u003e joined initials; prepend/append $last_name per $style\n\n# LIMITATIONS\n\n- Honorifics (`Dr.`, `Prof.`) and suffixes (`Jr.`, `III`) are not\ndetected or stripped; they are treated as name components.\n- Initials are taken verbatim from the first character of each token.\nNon-alphabetic leading characters (digits, punctuation) are included as-is.\n- Multiple consecutive commas collapse to a single comma before parsing.\nNames with two legitimate comma-separated clauses are not supported.\n- `compact` and `initials` formats are lossy: passing their output back into\n`abbreviate` does not reproduce the original result.\n- Particle detection is case-sensitive.  A token is only absorbed into the\nlast-name component when it exactly matches a particle string (all lowercase).\nCapitalised tokens such as `Van` or `De` are treated as ordinary name\ncomponents.\n- For `compact` and `initials` formats with `last_first` style, only the\nfirst character of the full particle-inclusive last name is used as the last\ninitial (e.g. `van Beethoven` contributes initial `v`).\n- Unicode input is NFC-normalised before processing.  Strings that differ only\nin normalisation form (e.g. precomposed `\\x{e9}` vs. combining `e\\x{301}`)\nproduce identical output.\n\n# AUTHOR\n\nNigel Horne, `\u003cnjh at nigelhorne.com\u003e`\n\n# BUGS\n\nPlease report bugs to `bug-text-names-abbreviate at rt.cpan.org` or via\n[http://rt.cpan.org/NoAuth/ReportBug.html?Queue=Text-Names-Abbreviate](http://rt.cpan.org/NoAuth/ReportBug.html?Queue=Text-Names-Abbreviate).\n\n# REPOSITORY\n\n[https://github.com/nigelhorne/Text-Names-Abbreviate](https://github.com/nigelhorne/Text-Names-Abbreviate)\n\n# SEE ALSO\n\n- [Test Dashboard](https://nigelhorne.github.io/Text-Names-Abbreviate/coverage/)\n\n# SUPPORT\n\nThis module is provided as-is without any warranty.\n\n    perldoc Text::Names::Abbreviate\n\n- MetaCPAN: [https://metacpan.org/dist/Text-Names-Abbreviate](https://metacpan.org/dist/Text-Names-Abbreviate)\n- RT tracker: [https://rt.cpan.org/NoAuth/Bugs.html?Dist=Text-Names-Abbreviate](https://rt.cpan.org/NoAuth/Bugs.html?Dist=Text-Names-Abbreviate)\n- CPAN Testers: [http://matrix.cpantesters.org/?dist=Text-Names-Abbreviate](http://matrix.cpantesters.org/?dist=Text-Names-Abbreviate)\n\n# FORMAL SPECIFICATION\n\n## abbreviate\n\n    Let Sigma* denote the set of all Unicode strings.\n    Let epsilon denote the empty string.\n\n    Sigma+ = Sigma* \\ {epsilon}\n    Format = {default, initials, compact, shortlast}\n    Style  = {first_last, last_first}\n\n    collapse(s) -- replace runs of whitespace with a single space, then trim\n\n    normalize : Sigma+ -\u003e Sigma* x Bool\n    normalize(n) =\n      let n0 = NFC(n)             -- Unicode NFC normalisation\n      let n1 = gsub(n0, \",+\", \",\")\n      if \",\" not-in n1 then (collapse(n1), false)\n      else\n        let (L, R) = split(n1, \",\", 2) each trimmed\n        case\n          L = epsilon ^ R != epsilon  -\u003e  (collapse(R), true)\n          L != epsilon ^ R != epsilon -\u003e  (collapse(R ++ \" \" ++ L), false)\n          L != epsilon ^ R = epsilon  -\u003e  (collapse(L), false)\n          L = epsilon ^ R = epsilon   -\u003e  (epsilon, false)\n        end\n\n    Particles = seq Sigma* | undef    -- arrayref of particle strings, or disabled\n\n    collect_particles : seq Sigma* x Particles -\u003e Sigma* x seq Sigma*\n    collect_particles(ps, P) =\n      if P = undef then (epsilon, ps)\n      else\n        let particle_set = { p | p \u003c- P }\n        iterate: while ps != [] ^ last(ps) in particle_set:\n          prepend last(ps) to accumulator; remove from ps\n        (join(\" \", accumulator), ps)\n\n    extract : Sigma* x Bool x Format x Style x Particles -\u003e (seq Sigma) x Sigma*\n    extract(n, leading, fmt, sty, P) =\n      let ps = tokenize(n)    -- split on whitespace\n      if ps = [] then ([], epsilon)\n      else if leading then\n        ([ first(p) | p \u003c- ps ], epsilon)\n      else\n        let base  = ps[#ps]\n            rest  = ps[1..#ps-1]\n        let (prefix, rest') = collect_particles(rest, P)\n        let last  = if prefix != epsilon then prefix ++ \" \" ++ base else base\n            inits = [ first(p) | p \u003c- rest' ]\n        if sty = last_first ^ fmt != default ^ fmt != shortlast ^ last != epsilon\n          then ([first(last)] ++ inits, epsilon)\n          else (inits, last)\n\n    abbreviate : Sigma+ x Format x Style x Sigma* x Particles -\u003e Sigma*\n    abbreviate = format_result . extract . normalize\n\n# LICENCE AND COPYRIGHT\n\nCopyright 2025-2026 Nigel Horne.\n\nUsage is subject to the terms of GPL2.\nIf you use it,\nplease let me know.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fnigelhorne%2Ftext-names-abbreviate","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fnigelhorne%2Ftext-names-abbreviate","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fnigelhorne%2Ftext-names-abbreviate/lists"}