Skip to content

feat: add Turkish Tax ID (TR_TAX_ID / VKN) recognizer - #2250

Open
gkhnelbstn wants to merge 1 commit into
data-privacy-stack:mainfrom
gkhnelbstn:tr-tax-id-vkn-recognizer
Open

gkhnelbstn wants to merge 1 commit into
data-privacy-stack:mainfrom
gkhnelbstn:tr-tax-id-vkn-recognizer

Conversation

@gkhnelbstn

Copy link
Copy Markdown

Summary

Adds TrTaxIdRecognizer (TR_TAX_ID) for the Turkish Tax Identification
Number (Vergi Kimlik Numarası / VKN) — a 10-digit identifier issued by the
Revenue Administration (GİB) to legal entities and to individuals not
eligible for the 11-digit National ID. It follows the same shape as
TrNationalIdRecognizer (#1995): pattern match, context words, and its own
checksum.

Related to #1973, which scoped the original three Turkey recognizers
(national ID, license plate, phone number) — VKN wasn't in that list, but
it's the natural next one: a tax_id column in a Turkish system genuinely
mixes VKN (companies) and TCKN (sole traders), both digit strings of
adjacent length, so a checksum rather than a length check is what tells
them apart.

Changes

  • presidio-analyzer/presidio_analyzer/predefined_recognizers/country_specific/turkey/tr_tax_id_recognizer.py — the recognizer, with the GİB checksum algorithm and a docstring explaining it (the algorithm itself is undocumented by GİB but is the one their own e-Fatura/e-Devlet integrations implement, and it's widely reproduced in Turkish accounting/ERP software)
  • presidio-analyzer/tests/test_tr_tax_id_recognizer.py — 29 cases: valid/invalid checksums, wrong length, non-digits, the leading-zero case (VKN allows it, unlike TCKN), context words, entity/language/country_code
  • country_specific/turkey/__init__.py, predefined_recognizers/__init__.py — export
  • conf/default_recognizers.yaml — registered, enabled: false (country-specific default), country_code: tr set from the start (no follow-up migration needed the way feat(analyzer): add optional country filter to load_predefined_recogn… #2000 added it retroactively to TrNationalIdRecognizer)
  • docs/supported_entities.md, CHANGELOG.md

Testing

pytest tests/test_tr_tax_id_recognizer.py -v
# 29 passed

pytest tests/test_tr_national_id_recognizer.py tests/test_tr_license_plate_recognizer.py tests/test_recognizer_registry.py -q
# 103 passed -- no regression on the sibling Turkey recognizers or the country-filter registry

ruff check <touched files>
# All checks passed!

ruff format --check on the new test file reports it would reformat the
parametrize tuples onto multiple lines each — I left it matching
test_tr_national_id_recognizer.py's existing (also unformatted-by-this-rule)
style rather than diverge from the sibling file; happy to reformat either
file if that's preferred.

Adds TrTaxIdRecognizer, the 10-digit counterpart to TrNationalIdRecognizer
(data-privacy-stack#1995): the Vergi Kimlik Numarası issued by the Revenue Administration
(GIB) to legal entities and to individuals not eligible for the 11-digit
TCKN. Same shape as the TCKN recognizer -- pattern match, context words,
and its own checksum, distinct from the TCKN's.

Disabled by default, following the existing convention for country-specific
recognizers. COUNTRY_CODE = "tr" and the matching default_recognizers.yaml
country_code entry are set from the start, so this needs no follow-up
migration the way TrNationalIdRecognizer did in data-privacy-stack#2000.

A tax_id column in a Turkish system genuinely mixes VKN (companies) and
TCKN (sole traders) -- both are digit strings of adjacent length, so a
checksum rather than a length check is what tells them apart.

Related to data-privacy-stack#1973.

Tests: 29 new cases in test_tr_tax_id_recognizer.py, covering valid/invalid
checksums, wrong length, non-digits, the leading-zero case that TCKN
excludes but VKN allows, context words, and the standard
entity/language/country_code assertions. Verified locally:
- pytest tests/test_tr_tax_id_recognizer.py: 29 passed
- pytest tests/test_tr_national_id_recognizer.py tests/test_tr_license_plate_recognizer.py tests/test_recognizer_registry.py: 103 passed (no regression)
- ruff check on all touched files: clean
Copilot AI lite review requested due to automatic review settings September 12, 2026 17:52

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot was unable to review this pull request because the user who requested the review has reached their quota limit.

gkhnelbstn added a commit to gkhnelbstn/lightweight-data-platform that referenced this pull request Sep 12, 2026
…itch

"Presidio has no TCKN or VKN recognizer" went stale the moment issue #5 was
actually worked: TrNationalIdRecognizer (TCKN) turned out to already be
upstream, unrelated to us -- data-privacy-stack/presidio#1995 -- and TrTaxIdRecognizer
(VKN) is now offered, data-privacy-stack/presidio#2250, tracked in issue #5.

Records what the fork this needed actually is: contribution plumbing on
GitHub, nothing this repository builds or runs, unlike a category-4 carried
patch in 0015 -- and safe to delete once the PR resolves either way.

Also records the alternative raised and rejected: swap Presidio for a
Turkish-specific PII library instead of carrying two recognizers until
upstream catches up. A TR-only library would still need Presidio (or
something like it) beside it for IBANs, credit cards, emails, phone numbers
and IP addresses, so it replaces forty checksum-validated lines with a
second system to maintain.

classify.py's docstring updated to match.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants