If you've worked on schema markup for clients, you know the rhythm. Content writes the FAQs in a Google Doc. Somebody needs to convert them to schema-compliant JSON-LD. The conversion is mechanical but error-prone - one missed closing brace and Google's Rich Results Test rejects the whole block.
I got tired of doing it by hand. So I built a tiny desktop GUI that does it in three clicks.
What it does
The GUI is two text fields and a save button:
- A big text box for the raw FAQ content. Paste anything that follows a Q: / A: structure (or Q. / A., or Question: / Answer:, or any reasonable variation).
- A small text input for the optional source URL.
- A file-format radio (JSON or TXT) and a Save button.
Click Save, point it at a folder, and you get a valid FAQPage JSON-LD block ready to drop into a page's <head>.
The format-flexibility detail
The reason this GUI exists rather than a CLI script is that the input format is never the same twice. One writer uses "Q:" and "A:". Another uses "Question 1." and "Answer 1." Another uses just numbered lines. Hand-parsing each variant into the same JSON output gets old fast.
The regex catches all the common patterns:
re.match(r'^(Q:|Q\.|Question\.?|Question:)[\s\t]*', line, re.I)
re.match(r'^(A:|A\.|Answer\.?|Answer:)[\s\t]*', line, re.I)
Anything matching the question pattern starts a new entry. Anything matching the answer pattern closes the current entry and writes it to the mainEntity array. Empty lines and unrelated text are skipped.
The case-insensitive flag and the . vs : flexibility cover about 95% of the format variations writers use in real content. The remaining 5% I just fix by hand in the text box before hitting Save - faster than expanding the regex to handle every conceivable input style.
Why Tkinter instead of a web UI
Two reasons.
First, Tkinter ships with Python. No dependencies, no Node.js, no npm install, no localhost server. Double-click the .py file and it runs.
Second, this lives on my laptop and runs offline. Generating FAQ schema doesn't need a backend, doesn't need to talk to an API, and doesn't need a database. A Python script with a 50-line GUI is the right scale for the job. Anything bigger - a Flask app, a static site with JS, a web service - would be overengineering for what is fundamentally a one-person utility.
Tkinter is also fast enough. The GUI launches in under a second. Compared to a web app that needs to spin up a server, open a browser, and load a page, Tkinter is genuinely the right choice for a single-user desktop tool.
The output
For input like:
Q: Do you ship to Canada?
A: Yes, we ship to all Canadian provinces with delivery in 3-5 business days.
Q: What is your return policy?
A: 30-day full refund on unworn items. Contact [email protected] to start a return.
The script writes a clean JSON-LD block:
{
"@context": "https://schema.org",
"@type": "FAQPage",
"url": "https://example.com/faq",
"mainEntity": [
{
"@type": "Question",
"name": "Do you ship to Canada?",
"acceptedAnswer": {
"@type": "Answer",
"text": "Yes, we ship to all Canadian provinces with delivery in 3-5 business days."
}
},
{
"@type": "Question",
"name": "What is your return policy?",
"acceptedAnswer": {
"@type": "Answer",
"text": "30-day full refund on unworn items. Contact [email protected] to start a return."
}
}
]
}
That goes straight into the page's <head> wrapped in a <script type="application/ld+json"> tag and Google's Rich Results Test passes it without complaint.
What I would change
A few iterations worth doing on the next pass:
Live validation. Right now the script writes the JSON file and trusts that it's valid. A small jsonschema check against the FAQPage spec would catch malformed inputs before they hit the file system.
Direct paste-to-clipboard mode. Half the time the JSON is going straight into a CMS editor, not into a file. A "Copy to clipboard" button alongside Save would skip the disk round-trip.
HTML wrapper option. Toggle that wraps the JSON-LD in the <script type="application/ld+json">...</script> tags ready to paste. Today I do that wrapping manually because I forget half the time.
Multi-FAQ batch mode. Some pages have multiple FAQ sections. Right now I run the tool once per section. A "process multiple FAQs from the same input" option would handle pages with five separate FAQ blocks at once.
But for the actual job - "take some FAQ content from a writer and turn it into schema" - the GUI has shipped dozens of FAQPage blocks across client work without a single Rich Results Test rejection. The tool is small. The work it removes from my plate is not.
Source: github.com/schandler7171/portfolio-example-scripts/tree/main/FAQSchema-SEOOn-Page