Read Frog

Prompt Variables

Every variable a Read Frog prompt can use, with the actual value each one is replaced by.

A variable is written {{name}}. Just before a request is sent, Read Frog replaces each known variable with a string. Nothing else is touched: an unknown name such as {{url}} is left in the prompt as the literal characters {{url}}.

The examples below all come from one page — an English article at https://devblog.example.com/posts/timestamps, being translated into Simplified Chinese.

Which variables exist where

VariablePage promptsSubtitle prompts
{{targetLanguage}}YesYes
{{input}}YesYes
{{webTitle}}YesYes
{{webDescription}}YesYes
{{webContent}}Yes
{{webSummary}}Yes
{{videoSummary}}Yes

A page prompt that uses {{videoSummary}}, or a subtitle prompt that uses {{webContent}}, keeps the literal text. The variable buttons under each editor field only offer the ones that list can fill.

{{targetLanguage}}

The English name of the language you are translating into — not a code, and not translated into your interface language.

Simplified Mandarin Chinese

Other examples: English, Traditional Mandarin Chinese, Japanese, Spanish. So a prompt written as Translate to {{targetLanguage}}: arrives as Translate to Simplified Mandarin Chinese:.

{{input}}

The text to translate. This is the one variable a prompt cannot do without.

One paragraph at a time — leading and trailing whitespace removed, invisible characters stripped:

Every timestamp you store without a zone is a bug waiting for a plane ticket.

Several paragraphs at once, which is what happens on an LLM provider with batch translation on. The paragraphs are joined with a line containing nothing but %%, with a blank line either side:

Every timestamp you store without a zone is a bug waiting for a plane ticket.

%%

The fix is boring: store UTC, render local, and never let the two meet in a database column.

%%

Ordering by a naive timestamp works until the clocks change, and then it works wrongly.

The model is expected to return the same number of paragraphs in the same order, separated the same way. Read Frog splits the reply back apart on those %% lines. If you rewrite the prompt in a way that changes the separator, or asks for the paragraphs numbered or merged, the split fails and the whole batch is retried and then re-sent one paragraph at a time.

With HTML, when the paragraph contains inline markup. Attributes that must survive are stripped out and the element is tagged with data-rf-attr, so the model sees a marker instead of a URL:

The fix is <a data-rf-attr="0">boring</a>: store <code data-rf-attr="1">UTC</code>, render local.

With a formula or inline symbol, which is replaced by a numbered placeholder and cloned back into the translation afterwards:

Converting is just {{0}}, which is why nobody gets it wrong twice.

You will not usually see these two shapes while writing a prompt, but they explain the extra rule blocks that appear in a request: Read Frog appends instructions about data-rf-attr and about numbered placeholders only when the text being sent actually contains one.

{{webTitle}}

The page's <title>, exactly as the browser tab shows it.

Why your timestamps are eight hours off — devblog

In a subtitle prompt this is the video's title instead.

{{webDescription}}

The page's own description, taken from the first of <meta name="description">, <meta property="og:description"> and <meta name="twitter:description"> that has content.

A short tour of the three places a naive timestamp can go wrong, and the one rule that prevents all three.

In a subtitle prompt this is the video's description.

{{webContent}}

The page's main body, extracted as Markdown and cut to the first 2000 characters. Navigation, sidebars and footers are dropped. No built-in prompt uses it — it is there for prompts that need more than a title to work with.

# Why your timestamps are eight hours off

Every timestamp you store without a zone is a bug waiting for a plane ticket.
It works on your laptop, it works in CI, and it fails the first time somebody
in another country opens the report.

## Three places it goes wrong

1. **At the boundary.** A form posts `2026-03-14 09:00` and the server decides
   what that means.
2. **In the column.** `timestamp without time zone` is not a moment in time; it
   is a picture of a wall clock.
3. **On the way out.** Formatting in UTC and labelling it local is the same bug
   wearing a hat.

The fix is boring: store UTC, render local, and never let the tw

The cut is a plain character count, not a sentence or paragraph boundary, so the value usually ends mid-word — as it does above. A prompt that tells the model the content is complete will be wrong on any page longer than a couple of screens.

{{webSummary}}

A summary of the page written by your LLM provider — a separate model call, cached per page, made once and reused for every paragraph.

A technical blog post arguing that timestamps should be stored in UTC and rendered in the reader's local zone. It identifies three failure points — form input, database column type, and output formatting — and recommends a single rule for all three. Written for backend developers; uses PostgreSQL column types as examples.

This requires AI Smart Context (Options → Translation). With it off, there is no summary to insert and the variable becomes the fallback string below. The default prompt uses {{webSummary}}, so leaving Smart Context off means the default system prompt always reads Webpage summary: No summary available.

{{videoSummary}}

The subtitle equivalent: a summary of the video, also behind AI Smart Context, also the fallback string when it is off.

A conference talk on time zone handling in web applications. The speaker walks through a production incident caused by naive timestamps, then demonstrates a UTC-in, local-out pattern. Names mentioned: PostgreSQL, Temporal, Luxon.

When a value is missing

A variable is never left empty and never disappears. If the value is missing, or blank, or whitespace only, Read Frog substitutes a fixed English sentence:

VariableSubstituted instead
{{webTitle}}No title available
{{webDescription}}No description available
{{webContent}}No content available
{{webSummary}}, {{videoSummary}}No summary available

So the default system prompt, on a page with no metadata and Smart Context off, arrives like this:

## Document Metadata for Context Awareness
Webpage title: No title available
Webpage summary: No summary available

This matters most if you write few-shot examples. A prompt that shows the model a worked example built from the page title will, on a page without one, show it an example about a page called "No title available" — and models will translate or comment on that sentence. If your prompt leans on a metadata variable, say in the prompt itself what to do when the value reads "No ... available".

There is no fallback for {{input}} or {{targetLanguage}}. {{targetLanguage}} always has a value, and {{input}} is the paragraph — a request is never sent for empty text.

What happens next

The substituted text is not the whole request: extra rule blocks and your glossary terms are appended afterwards, and the finished result becomes the translation cache key. See How a request is assembled.

On this page