LOINC is the international language of lab tests — and we're the bridge between it and your PDF.
Every lab test in the world has a LOINC code. "Glucose in serum, molar, mmol/L"
= 14749-6, whether it was measured in Warsaw, Chicago, or Tokyo.
LabCard's job is to map each of your results to the right code — because without
it, Apple Health doesn't know what to plot on the same trend line.
The rule everything hinges on
Discovered empirically after testing many variants: Apple Health merges results into a single trend line only when the PAIR matches:
(LOINC code) + (display name) — both fields identical
If glucose from 2023 has the name "Glucose [Moles/volume] in Serum or Plasma", and glucose from 2024 gets "Glucose in blood" (with the same code!) — Apple Health will create two separate entries, each with a single point, no shared history.
That's why the display name in our dictionary isn't just a "label" but a product half-key. Chosen once, locked for years. Any spelling edit = broken trend history for existing users.
Three dictionary layers
Layer 1 — the seed dictionary (what you see today)
About 55 most common tests (CBC, biochemistry, lipid panel, thyroid, electrolytes, hormones, coagulation, cardiology). Manually curated. Uses LOINC LONG_COMMON_NAME as the canonical English display — no intermediate translation needed for English-speaking users.
Unit choice drives the code choice. Example: glucose. Molar variant =
14749-6 (mmol/L), mass variant = 2345-7 (mg/dL).
Non-US labs often report mmol/L → we pick 14749-6. In the US, labs report
mg/dL → 2345-7 is the right pick. This is why the same seed dictionary needs
per-region tuning; today's seed is molar-oriented (Polish laboratory convention).
Layer 2 — build-time pipeline (PHP)
In the loinc-dictionary/ folder we have PHP scripts that take the
official LOINC table (~90,000 concepts) and our "target list" of expected tests
with OCR aliases, and produce a ready slownik_startowy.json. Run
once every six months when LOINC releases a new version (February and August).
Matching score weights the English component, material (serum/blood), quantity
type (molar/mass — SCnc picks the molar variant), and expected
UCUM unit. Entries below the threshold go to do_weryfikacji.csv
— resolved manually via search.loinc.org.
Layer 3 — canonical translations (per-region)
For English, the layer 3 is the LOINC LONG_COMMON_NAME field — no extra translation needed. For Polish, the Ministry of Health commissioned the Medical University of Łódź (with PTDL) to translate ~20,000 LOINC concepts. Completed July 2023, deposited in the official LOINC database — free (after registration), commercially usable. That's the target source for Polish display names.
A related project is SSIDL (Poland's Lab Diagnostics Standardization System, Medical University of Łódź) — will be open source with an HL7/FHIR API, providing direct local name → LOINC mappings. When it lands, integrating with it is our next big work.
OCR path — why only Diagnostyka for now
Polish lab PDFs share one technical quirk: they often use embedded
subset fonts without ToUnicode maps. That means a standard
text extractor (PDF.js, pdfplumber) gets (cid:53)(cid:24)…
instead of "Hemoglobin". Digits extract fine, names don't.
Our workaround: render the page to an image and OCR it with Tesseract, like a human would. Advantage: works regardless of the font the lab embedded. Cost: slower (tens of seconds per page; first run loads a ~15 MB language model).
Right now the OCR pipeline is tested only on clean PDFs exported from the Polish Diagnostyka patient portal. Other labs (ALAB, Synevo, LabCorp, Quest) and scans — a universal OCR is a planned future phase. In the meantime, for any other source, use the AI parser path — it's document-layout-agnostic.
What if a test isn't in the dictionary?
Today: we skip it (in OCR and AI parser paths) or don't offer it in the tree (manual path). We deliberately don't allow entering a custom LOINC code by hand — because a user typo in the code or name would silently split the trend history in Apple Health.
If you're missing a test — let us know, we'll add it in the next dictionary release. Long-term, we'll pull the full catalog from layer 3 sources automatically.