Two ChatGPT-tagged visits from France exposed a missing French Thailand e-Visa photo page. We added that page, localized links in llms.txt, and a hydration-safe browser-language hint.
What did the French visits actually tell us?
On 2026-10-03, two visitors reached pixtidy's English Thailand e-Visa photo page with utm_source=chatgpt.com. Earlier that evening, a third French visitor had reached the same page.
At first, our analytics obscured this. PostHog's own GeoIP located our forwarding server in Ashburn, Virginia, producing a country breakdown that said "almost all US." That was the server's location, not a useful account of our audience.
The country from the request header on our own event endpoint placed these visitors in France.
That gave us a concrete gap to investigate: we had an English Thailand page and no French equivalent. Country helped expose the missing page; browser-language preferences would decide whether to offer its link.
What should you change when a localized page is missing?
- We added the missing French page.
We added the French Thailand e-Visa photo page at /fr/photo-evisa-thailande.
A writing tool drafted the copy, and a second model reviewed it. One required correction: the lead repeated the meta description word for word. We removed that repetition.
The page's reviewed title also became the text for the language hint. That kept the hint tied to the actual destination rather than introducing a separate, unreviewed translation.
- Connect the localized page to the document listing.
Each document line in our llms.txt now ends with an Also in: suffix listing localized pages.
For the Thailand line, the compact before-and-after is:
- Before: no French-page suffix.
- After: an
Also in:suffix linking to the French page.
That shows the added relationship without reproducing the rest of the document line. A unit test checks that the Thailand line links to the French page.
This test answers a narrow question: did we retain the link?
- Offer a link beneath the English page's H1.
English document pages now read navigator.languages. If the first non-English language ranks above English and the corresponding page exists, the hint appears.
In our browser checks, French showed the link and English showed nothing.
How do you wire the hint with useSyncExternalStore?
Our implementation uses useSyncExternalStore so the server-rendered hint stays empty, with no hydration mismatch.
Here is simplified wiring that includes the language selection, server snapshot, and destination check. The pages prop represents the current document's localized destinations, keyed by language, with each destination's href and reviewed title.
import { useSyncExternalStore } from "react";
const subscribe = () => () => {};
const getServerSnapshot = () => null;
function getSnapshot() {
const codes = navigator.languages.map((tag) =>
tag.toLowerCase().split("-")[0]
);
const other = codes.findIndex((code) => code !== "en");
const english = codes.indexOf("en");
return other !== -1 && (english === -1 || other < english)
? codes[other]
: null;
}
export function LanguageHint({ pages }) {
const language = useSyncExternalStore(
subscribe,
getSnapshot,
getServerSnapshot
);
const page = language ? pages[language] : null;
return page ? <a href={page.href}>{page.title}</a> : null;
}
Render this component beneath the English document's H1.
The important boundary is getServerSnapshot. The browser snapshot then supplies the preferred language candidate.
The snapshot returns a language string or null, rather than a newly created object. The destination lookup happens afterward, so language preference and page availability remain separate decisions.
The subscription in this simplified example is deliberately a no-op.
Which language-selection edge cases should you check?
English comes first. If French appears below English in navigator.languages, this rule shows no French hint. Being present in the list is not enough. The first non-English language must rank above English.
The preferred translation does not exist. The code selects the first non-English language, then checks for that page. It does not scan through less-preferred languages until it finds a translation. A missing destination means no hint.
English is absent. It still needs a matching localized page.
These cases define what the hint does.
Did the Thailand answer cite the French page afterward?
The 2026-10-04 weekly check did not. The Thailand question cited no site at all, as did the new UK ETA question.
Our weekly script asks ChatGPT 18 real questions with web search on. In that run, pixtidy appeared in 1 of 18 answers. The mention was for a Chinese visa photo question, citing pixtidy.com/china-visa-photo alongside two other tools.
For the next weekly comparison, the useful reporting questions are specific: how many of the 18 answers mention pixtidy, whether the Thailand answer cites the French page, and whether French-page arrivals carry the ChatGPT tag.
There was another problem after arrival. All three French visitors uploaded a photo, and all failed "Whole head fits in the frame." That was a separate cropping bug, fixed the next day and covered in another post.
Related: Why a ready-made ID photo failed our browser framing check ยท Schengen visa photos are 35 45 mm: turning millimetre rules into browser checks
Disclosure: I built pixtidy.
Top comments (0)