DEV Community

Victor Laybats
Victor Laybats

Posted on Originally published at playwikicards.com

What broke when we generated daily trivia from Wikidata

I wanted daily quizzes whose answers anyone can check, so every question on our trivia pages is generated from a Wikidata fact, and every answer links back to its Wikidata item. Twenty questions a day, capitals, flags, rivers, literary works, films, events, championship winners.

The quiz part was easy. The data part was not. Here is what broke, measured on 23 October 2026, and what we changed.

1. Nearly one famous person in five had no English or French label

Since 2024 Wikidata stores many proper names once, under a label for all languages (mul), instead of repeating them in every language. A query that asks for lang(?label) = "en" silently drops those items.

On a sample of the 200 most-read people of our card catalogue, 36 had no English or no French label: George Washington, Barack Obama, William Shakespeare and Novak Djokovic among them. Juneau, the capital of Alaska, only had the mul label, so Alaska disappeared from our US states quiz.

The fix is a fallback chain:

OPTIONAL { ?x rdfs:label ?fr  FILTER(lang(?fr)  = "fr") }
OPTIONAL { ?x rdfs:label ?en  FILTER(lang(?en)  = "en") }
OPTIONAL { ?x rdfs:label ?mul FILTER(lang(?mul) = "mul") }
BIND(COALESCE(?fr, ?mul, ?en) AS ?labelFr)
BIND(COALESCE(?en, ?mul) AS ?labelEn)
Enter fullscreen mode Exit fullscreen mode

2. Scanning a whole class times out

The public SPARQL service stops a query after 60 seconds. "Every mountain with its elevation" or "every novel with its author" ended in an HTTP 504 or in a truncated JSON body, depending on the load of the service.

What works: start from a list of known items. We map our most-read Wikipedia articles to their Wikidata ids through the Wikipedia API (prop=pageprops&ppprop=wikibase_item, 50 titles per call), then query them with VALUES ?x { ... } in batches of 100 ids. We also write the output after each kind of fact, so one failure no longer loses the others.

3. The Nile was 2,850 km long

Many items carry several values for one property. The Nile has 6,650 km with a preferred rank and also 2,850 km with a normal rank. The Amazon has 6,400 km with a preferred rank and four other values, from 3,690 to 6,992 km. A median of all values makes "which river is the longest?" wrong.

?x p:P2043 ?st .
?st psn:P2043/wikibase:quantityAmount ?length ;
    wikibase:rank ?rank .
FILTER(?rank != wikibase:DeprecatedRank)
Enter fullscreen mode Exit fullscreen mode

We keep preferred-rank values when they exist, drop items whose remaining values disagree by more than 12 %, and only compare four rivers when the first is at least 15 % longer than the second. The Nile and the Amazon, about 4 % apart, are never the two candidates of the same question.

4. True in Wikidata, unfair in a quiz

  • Pop songs often record a single co-writer as composer: Wikidata gives RedOne for Poker Face, which he wrote with Lady Gaga. We removed music.
  • "Discoverer" also applies to places: Persepolis would be credited to a Spanish diplomat. Anything with coordinates is out of the discoveries.
  • Franchises keep their current name for past titles: Super Bowl XI (1977) is won by the Las Vegas Raiders, who were the Oakland Raiders then. American leagues only use editions from 2000.
  • Oceania exists twice (Oceania and Insular Oceania), which left Tuvalu without a continent. We merge them.
  • Capitals with an internationally disputed status are never asked, and are marked in the list of capitals.
  • A wrong answer is never a prefix of the right one, so "Rugby" never sits next to "Rugby union".

5. Commons refuses 320 px thumbnails

Flags and locator maps are Wikimedia Commons thumbnails. A width of 320 px returns HTTP 400 with "Use thumbnail sizes listed on https://w.wiki/GHai", while 120, 250, 330 and 500 px are served:

import hashlib, urllib.parse

def thumb(name, width=330):
    n = name.replace(" ", "_")
    h = hashlib.md5(n.encode()).hexdigest()
    q = urllib.parse.quote(n)
    ext = ".png" if n.lower().endswith(".svg") else ""
    return f"https://upload.wikimedia.org/wikipedia/commons/thumb/{h[0]}/{h[:2]}/{q}/{width}px-{q}{ext}"
Enter fullscreen mode Exit fullscreen mode

The result

Sixteen quiz pages, twenty questions a day each, every answer with its source. The cleaned fact set is open data (CC0) on GitHub, and the full write-up with the French version is on our site.

If you build on Wikidata, I would love to hear about the quirks I missed.

Top comments (0)