Store form of address, pronouns and gender as language-neutral concepts #1019

Merged
tiagoagueda merged 2 commits from 921-addressing-concepts into main 2026-10-22 01:25:00 +00:00
Owner

Closes #921

What changed

Form of address, pronouns and gender are now stored as language-neutral concepts (mrs, doctor_f, they, non_binary ...) and drawn in the language of the page or the document. Switching a record from English to French shows "il" for he with nothing stored changed. "Other" is the one exception: free text, stored and shown as typed in every language.

  • accounts/addressing.py: concept keys, per-language word tables with sources, the subset each language offers, label(field, key, language) (the language's table, then the gettext catalogues for a language or concept the tables lack) and concept_of / stored_value (a word of any catalogued language, or a pre-#864 gender word, is the concept; anything else stays as typed).
  • Migration accounts/0047_addressing_concepts: existing values become concepts where they match one in any language, unmatched text is kept as typed; reversible (writes the word of the career record's language, English where none).
  • Form: options are (key, label); a bound form keeps what was sent; a word typed under Other that a list has is stored as its concept.
  • Documents (printing/rendering), the vCard PRONOUNS line and the CV form's help texts resolve the label in their own language.
  • API: ProfileOut returns {key, label} for each of the three (breaking shape change); ProfilePatch accepts a key, a word of a list, or free text.
  • Archive format 73 -> 74, candidate format 21 -> 22 (block shape unchanged, so its fingerprint is the same); the importer and candidate reader map older words to concepts. demonyms.is_feminine compares with female.
  • Help topics, THREAT-MODEL.md, CHANGELOG, wiki (CVs, Getting started, The capture API; separate wiki commit, not pushed).

Decisions

  • Maintainer, 2026-10-15: store concepts, "Other" free text, migration by match in any language, labels through the catalogues (empty slots, Weblate fills), no field ever sets another.
  • Gendered concepts with a neutral fallback (chosen for the English/Portuguese ambiguity): doctor/doctor_f, professor/professor_f, engineer/engineer_f. English and French print "Dr"/"Prof."/"Pr" for both; Portuguese prints Dr./Dr.ª, Prof./Prof.ª; a bare "Dr"/"Prof." migrates to the unmarked concept.
  • Matching ignores case and a trailing full stop ("Dr" and "Dr." are the same word).

Tests

New and updated: form of address, gender, language registry, demonyms, CV prints (EN/FR/PT), vCard, candidate file, security (personal details, profile API), browser (form of address, CV prints, 320px lists), plus a migration test (forward, backward, forward again) and an archive round trip from format 73.

Library check: none needed.

Model use: written with Claude Code, reviewed by the person who opens the PR.

🤖 Generated with Claude Code

Closes #921 ## What changed Form of address, pronouns and gender are now stored as language-neutral concepts (`mrs`, `doctor_f`, `they`, `non_binary` ...) and drawn in the language of the page or the document. Switching a record from English to French shows "il" for `he` with nothing stored changed. "Other" is the one exception: free text, stored and shown as typed in every language. - `accounts/addressing.py`: concept keys, per-language word tables with sources, the subset each language offers, `label(field, key, language)` (the language's table, then the gettext catalogues for a language or concept the tables lack) and `concept_of` / `stored_value` (a word of any catalogued language, or a pre-#864 gender word, is the concept; anything else stays as typed). - Migration `accounts/0047_addressing_concepts`: existing values become concepts where they match one in any language, unmatched text is kept as typed; reversible (writes the word of the career record's language, English where none). - Form: options are `(key, label)`; a bound form keeps what was sent; a word typed under Other that a list has is stored as its concept. - Documents (`printing`/`rendering`), the vCard `PRONOUNS` line and the CV form's help texts resolve the label in their own language. - API: `ProfileOut` returns `{key, label}` for each of the three (breaking shape change); `ProfilePatch` accepts a key, a word of a list, or free text. - Archive format 73 -> 74, candidate format 21 -> 22 (block shape unchanged, so its fingerprint is the same); the importer and candidate reader map older words to concepts. `demonyms.is_feminine` compares with `female`. - Help topics, THREAT-MODEL.md, CHANGELOG, wiki (CVs, Getting started, The capture API; separate wiki commit, not pushed). ## Decisions - Maintainer, 2026-10-15: store concepts, "Other" free text, migration by match in any language, labels through the catalogues (empty slots, Weblate fills), no field ever sets another. - Gendered concepts with a neutral fallback (chosen for the English/Portuguese ambiguity): `doctor`/`doctor_f`, `professor`/`professor_f`, `engineer`/`engineer_f`. English and French print "Dr"/"Prof."/"Pr" for both; Portuguese prints Dr./Dr.ª, Prof./Prof.ª; a bare "Dr"/"Prof." migrates to the unmarked concept. - Matching ignores case and a trailing full stop ("Dr" and "Dr." are the same word). ## Tests New and updated: form of address, gender, language registry, demonyms, CV prints (EN/FR/PT), vCard, candidate file, security (personal details, profile API), browser (form of address, CV prints, 320px lists), plus a migration test (forward, backward, forward again) and an archive round trip from format 73. Library check: none needed. Model use: written with Claude Code, reviewed by the person who opens the PR. 🤖 Generated with [Claude Code](https://claude.com/claude-code)
feat(accounts): store form of address, pronouns and gender as concepts
Some checks failed
CI / Unit tests and coverage (Python 3.14) (push) Successful in 11m22s
CI / Security audit (push) Successful in 36s
CI / Checks: lint, migrations, catalogues, settings, built files (push) Successful in 2m38s
CI / Database tests on PostgreSQL (push) Successful in 3m9s
Every Python / Unit tests (Python 3.12) (push) Successful in 11m32s
CI / Browser tests (Chromium) (push) Failing after 46m46s
Every Python / Unit tests (Python 3.13) (push) Successful in 11m17s
CI / Unit tests and coverage (Python 3.14) (pull_request) Successful in 12m4s
CI / Checks: lint, migrations, catalogues, settings, built files (pull_request) Successful in 2m36s
CI / Database tests on PostgreSQL (pull_request) Successful in 3m21s
CI / Security audit (pull_request) Successful in 38s
CI / Browser tests (Chromium) (pull_request) Failing after 29m6s
90504be79a
A profile held one language's words ("Mme", "iel", "Féminin"), so a record
switched from English to French kept showing the English word, and a CV in
another language printed the words of the language they were typed in. The
maintainer decided (2026-10-15) to store language-neutral concepts and draw
them in the language of the page or document, with "Other" the one exception:
free text stored and shown as typed.

- accounts/addressing.py: concept keys (mr, doctor, doctor_f, she, he, they,
  female, non_binary ...), per-language word tables with their sources, the
  subset each language offers, label(field, key, language) (table, then the
  catalogues for a language or concept the tables lack) and concept_of /
  stored_value (a word of any language, or a pre-#864 gender word, is the
  concept; anything else stays as typed).
- Gendered titles have a neutral fallback: English and French print Dr and
  Prof. for both doctor and doctor_f; Portuguese prints Dr./Dr.ª; a bare Dr or
  Prof. becomes the unmarked concept.
- Migration accounts 0047 turns existing values into concepts (frozen table,
  unmatched text kept), reversible to the word of the record's language.
- The form offers (key, label) options, keeps a posted value on a bound form,
  and stores a word typed under Other as its concept. Documents and the vCard
  PRONOUNS line resolve the label in their own language; the profile API
  returns {key, label} and accepts a key, a word or free text.
- Archive format 74 and candidate format 22; the importer and the candidate
  reader map older words to concepts.
- Gender never decides pronouns or form of address, or the reverse.

Closes #921

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Merge main into 921-addressing-concepts
Some checks failed
Every Python / Unit tests (Python 3.12) (push) Has been cancelled
Every Python / Unit tests (Python 3.13) (push) Has been cancelled
CI / Browser tests (Chromium) (push) Failing after 19s
CI / Unit tests and coverage (Python 3.14) (push) Has been cancelled
CI / Checks: lint, migrations, catalogues, settings, built files (push) Has been cancelled
CI / Browser tests (Chromium) (pull_request) Has been cancelled
CI / Unit tests and coverage (Python 3.14) (pull_request) Has been cancelled
CI / Checks: lint, migrations, catalogues, settings, built files (pull_request) Has been cancelled
CI / Database tests on PostgreSQL (pull_request) Has been cancelled
CI / Security audit (pull_request) Has been cancelled
CI / Database tests on PostgreSQL (push) Has been cancelled
CI / Security audit (push) Has been cancelled
4932b1a1a5
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
tiagoagueda deleted branch 921-addressing-concepts 2026-10-22 01:25:01 +00:00
Sign in to join this conversation.
No reviewers
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set

Reference
Postulo/postulo!1019
No description provided.