All documentation

Visibility

Languages and templates

Run a tracked prompt in a language, what the response-language instruction says and where it goes for each chat engine, how the two Google engines, ChatGPT (app) and Gemini (app) receive a language instead, choosing which engines run, the server-side slot and answer preview, and saving a country/audience/language/engine combination as a reusable template.

Variant templates settings page listing a saved combination of countries, audience, languages and engines

What a language variant does

Open Prompts and add or edit a tracked prompt. Alongside the countries and personas a prompt already tracks, it can now also track one or more languages. A language variant does not translate the prompt text: the exact words you typed are sent to the engine unchanged, with one addition, a plain-language instruction telling the engine to write its whole answer in that language (api/services/llm.py#render_language_block, #render_prompt_with_location). To track what a French-speaking buyer would actually type, write the prompt in French yourself and set its language to French; nothing here translates an English prompt into French for you.

"As written" is the value every target already carried before this feature, and it stays the default: no language block is added, and nothing about today's behaviour changes for a target you never touch (api/services/languages.py#is_supported_language).

The 48 supported languages

Language identity is one of 48 fixed ISO 639-1 two-letter codes, in this order, the same list every picker in the app reads from (api/services/languages.py#SUPPORTED_LANGUAGES, api/routers/languages.py#list_languages):

Code Language Code Language
en English ru Russian
es Spanish uk Ukrainian
fr French ar Arabic
de German he Hebrew
it Italian fa Persian
pt Portuguese hi Hindi
nl Dutch bn Bengali
sv Swedish ur Urdu
da Danish ta Tamil
no Norwegian te Telugu
fi Finnish mr Marathi
pl Polish gu Gujarati
cs Czech kn Kannada
sk Slovak ml Malayalam
hu Hungarian pa Punjabi
ro Romanian th Thai
bg Bulgarian vi Vietnamese
el Greek id Indonesian
hr Croatian ms Malay
sl Slovenian tl Tagalog
et Estonian zh Chinese
lv Latvian ja Japanese
lt Lithuanian ko Korean
tr Turkish sw Swahili

A code never carries a region: it is always the bare two-letter form (fr, not fr-CA), the same ISO 639-1 code the response-language instruction below names, and country already carries the region a prompt runs in (api/services/languages.py#SUPPORTED_LANGUAGES, api/services/llm.py#render_language_block).

Where the instruction goes

Every chat engine, Perplexity included, gets the response-language instruction the same way: it is appended to the rendered message the engine receives, after the audience block a persona variant adds and before the "Search context" location block (see How location reaches each engine). For a French variant the instruction reads exactly:

Response language:
- Write the whole answer in French (fr).

(api/services/llm.py#render_prompt_with_location). The saved execution record's prompt_text_sent stores the whole rendered message, this block included, exactly as the engine received it, the same way it already does for a persona's audience block.

The two Google engines

Google AI Overviews and Google AI Mode get no instruction block. Google receives the prompt as a search, and the language goes as a setting of the request instead: the variant's language, or for an As written target the project's default language, or else English (api/services/llm.py#dataforseo_language). Both Google engines support every language above except Chinese, where choosing Simplified or Traditional would be a guess. On AI Mode, Portuguese is sent as Brazilian Portuguese and Tagalog as Filipino (api/services/google_serp.py#AI_MODE_LANGUAGE_CODES, #AI_OVERVIEWS_LANGUAGE_CODES).

  • A Chinese variant does not run on either Google engine: it is left out rather than sent in English, and prompt detail lists it as "language not supported by AI Mode" or "AI Overviews" (api/services/collection.py#target_runs_on). It still runs on every chat engine the prompt runs on.
  • An As written target whose project default language is Chinese runs on the Google engines in English, and says so.
  • Every Google run shows the language actually sent in prompt detail's run history, as in "Language sent to Google: Portuguese (Brazil)" (api/services/google_serp_reads.py#serp_language_label).

As on every engine, the prompt text is not translated: Google searches for the words you wrote.

ChatGPT (app)

ChatGPT (app) gets no instruction block either. It receives the prompt as written, and the language goes as a setting of the request by the same rule: the variant's language, or for an As written target the project's default language, or else English (api/services/llm.py#dataforseo_language). It supports every language above except Chinese (api/services/chatgpt_app.py#LANGUAGE_CODES), so a Chinese variant does not run on it and prompt detail lists it as "language not supported by ChatGPT (app)"; an As written target whose project default language is Chinese runs on it in English. Each ChatGPT (app) run shows the language sent in prompt detail's run history, as "Language sent to ChatGPT (app)".

A brand study is the one exception: an objection, attribute or fact study in a language adds, after the question it sends ChatGPT (app), the same response-language instruction the chat engines get, because the language setting alone does not change the language ChatGPT (app) answers in (api/services/brand_study/question.py#study_prompt). Tracked prompts never get it.

Gemini (app)

Gemini (app) gets no instruction block either. It receives the prompt as written, and the language goes as a setting of the request by the same rule: the variant's language, or for an As written target the project's default language, or else English (api/services/llm.py#dataforseo_language). It supports every language above except Chinese; Portuguese is sent as Brazilian Portuguese and Tagalog as Filipino, as on AI Mode, because those are the codes Gemini (app) lists (api/services/gemini_app.py#LANGUAGE_CODES). A Chinese variant does not run on it and prompt detail lists it as "language not supported by Gemini (app)"; an As written target whose project default language is Chinese runs on it in English. Each Gemini (app) run shows the language sent in prompt detail's run history, as in "Language sent to Gemini (app): Portuguese (Brazil)".

Choosing which engines run

A prompt can also choose which engines run it. Leaving Engines on "All engines on your plan" means every engine on your plan, including one a later plan upgrade adds, with nothing to revisit (api/services/prompt_variants.py#plan_engine_names, #plan_providers). Choosing a subset restricts every variant of that prompt to those engines; it costs no prompt slots, only changes how many answers you get a month (api/routers/prompts.py#create_prompt, #update_prompt). One function decides whether a target actually runs on a given engine, and it is the only place that rule lives: an engine you did not choose creates no execution row at all. On the chat engines, neither a language nor a persona skips an engine, so every variant runs on every chat engine the prompt runs on. The two Google engines skip three kinds of variant: a persona variant, a Chinese variant, and any variant of a prompt over 700 characters once encoded for Google (see How the Google engines are collected). ChatGPT (app) skips four: a persona variant, a city variant, a Chinese variant, and any variant of a prompt over 2,000 characters once encoded (see What reaches ChatGPT). Gemini (app) skips three: a persona variant, a Chinese variant, and any variant of a prompt over 2,000 characters once encoded; it runs city variants (see What reaches Gemini (app)) (api/services/collection.py#target_runs_on).

If your plan changes and a chosen engine is no longer on it, the choice itself is not rewritten, so it resumes automatically the moment the engine is available again; nothing runs on the missing engine meanwhile. The prompt's own read exposes which of its chosen engines are currently off your plan, and saving the prompt again with that same engine still in the list is refused, naming it (api/routers/prompts.py#_stamp_engines_off_plan, api/services/prompt_variants.py#validate_engines).

Every plan; each variant uses a prompt slot

A location (a country, or a city), an audience and a language together now decide a prompt target, the same identity a country and an audience alone decided before (api/services/prompt_variants.py#target_key). Tracking one prompt in 2 countries as General, in As written plus French, produces 2 x 1 x 2 = 4 targets and reserves 4 slots (api/routers/prompts.py#create_prompt, api/services/prompt_variants.py#sync_targets). Languages, engine choice and templates draw on the same prompt-slot budget every country and persona already used, on every plan; none of the three needs a separate entitlement.

A prompt tops out at 100 active variants, locations (countries and cities) times audiences times languages, and every path that can add a target enforces it: creating or editing a prompt, bulk-applying a template, CSV/Excel import, and accepting a suggested or discovered prompt all refuse to push a prompt over the cap, naming the count and the limit (api/services/prompt_variants.py#MAX_VARIANTS_PER_PROMPT, #resolve_spec, #active_variant_count, #variant_cap_error; import turns this into a row error rather than a raised exception, api/services/prompt_import.py#_plan_one; accepting a suggestion or a discovered prompt raises it as a 422, api/routers/prompt_candidates.py#accept_candidate). Saving a template on its own is capped the same way, since a template's own grid can never be saved larger than 100 either. Editing a prompt, and bulk-applying a template to one, are the two cases that are not always a hard ceiling: either can resend or reapply a grid that is already over the cap unchanged without being blocked, only growing it further is refused, so a grid can be larger than 100 only if it was created before this release (api/services/prompt_variants.py#_per_prompt_over_cap). One spec can name up to 50 countries and up to 100 cities, at least one location between them, 1 to 10 languages, and, when you choose a subset, 1 to 10 engine names (api/schemas/prompt_variants.py#VariantSpec).

Default language, and where it applies

Set a project's default language on its project settings form; leaving it "As written" is the state every project starts in. Changing it never rewrites a target that already exists: only a target created afterward, with no language named explicitly, picks up the new default. Existing prompts are not changed.

A new prompt whose languages are left unset uses the project's current default for every target it creates, the same as naming that language explicitly. CSV/Excel import, and accepting a suggested, discovered, or persona-tagged prompt, are narrower: they manage one "base variant" per country, for the audience the row concerns (General, or the candidate's own persona). A country already tracked for that audience, meaning it has an ACTIVE target in any language, is left alone entirely: nothing is created or reactivated for it. A country that is not tracked for that audience, whether it has no target at all or only an inactive one, gets a target in the project's CURRENT default language: an inactive target with exactly that (country, audience, language) key is reactivated if one exists, otherwise a new one is created. So changing the default language, then re-importing the same file or re-accepting the same suggestion, never adds a second, new-language copy of a country a prompt already tracks: it can only ever add a country the prompt did not track before (api/services/prompt_import.py#apply_plan, #_plan_one, api/services/prompt_discovery.py#accept_candidate, api/routers/prompt_candidates.py#_count_new_or_reactivated_targets, all reading api/services/prompt_variants.py#base_language).

Onboarding is the one exception: a new project's onboarding prompts are created before a default language can be set on a project that does not exist yet, so those targets are always As written, on every project you create, not only your first (api/routers/projects.py#create_onboarding_project).

Editing a prompt's countries, cities, audience or languages still follows the "any one field, all five resync" rule: the grid is five fields (countries, cities, personas, General, languages), and when you actually change any one of them the whole grid recalculates together. Changing only a city resyncs the grid the same way a country does. But the Add/Edit prompt form only sends those five fields at all when you changed at least one of them from what the form opened showing, and then it sends all five, the cities with the rest, so a city you cleared is cleared rather than kept. A save that only edits the prompt text, its tags, or its classification sends none of the five, so it cannot resync the grid to a different shape than what is already tracked, even when that grid is "ragged" (say, a country an import only ever tracked for General, sitting alongside a country tracked for General and a persona) (frontend/src/lib/components/AddPromptModal.svelte, frontend/src/routes/(app)/prompts/[id]/+page.server.js, api/routers/prompts.py#update_prompt).

Preview before you save

Add prompt form with the United States and the city of London selected, As written and French both selected, all engines chosen, and the live server preview sentence counting 2 locations

The Add and Edit prompt forms, and bulk-applying a template (below), all call one server-side preview before anything is saved: the Add/Edit form 300ms after your last change, template bulk apply as soon as you pick a template. It reports the grid size, the new prompt slots it would use and how many are left, and a monthly estimate for before the change and after it (api/services/prompt_variants.py#_build_preview). Selecting one country, General, As written plus French, and every engine, on a plan with 8 engines and 373 slots left reads: "1 country × 1 audience × 2 languages = 2 variants. Uses 2 new slots (371 left). About 496 runs a month on 8 engines. Google does not show an AI answer on every run, so some runs have no answer." The estimate counts runs rather than answers whenever a Google engine runs a variant, because a Google run may show no AI answer; on chat engines alone it reads "answers" (frontend/src/lib/google-serp.js#estimateWording). The "(371 left)" figure is the slot count AFTER this change (373 minus the 2 new slots it uses), the same way the form always shows it: what you would have left if you saved right now, not what you have left today. A city counts as a location of its own: the form above, with the United States plus London, General, As written plus French and every engine, reads "2 locations (1 country, 1 city) × 1 audience × 2 languages = 4 variants. Uses 4 new slots (369 left). About 930 runs a month on 8 engines.", followed by the Google note and "ChatGPT (app): 2 city variants not run": the two London variants run on seven engines, not eight, because Gemini (app) runs city variants and ChatGPT (app) does not (frontend/src/lib/api/prompt-form.js#locationCountText). Choosing Chinese instead of French in the first example gives "About 372 runs a month", because the Chinese variant runs on the four chat engines only, and four more lines: "Google AI Overviews: 1 variant in an unsupported language not run" and the same for Google AI Mode, ChatGPT (app) and Gemini (app) (frontend/src/lib/google-serp.js#notRunPreviewLines). A persona audience adds "persona variants" lines the same way.

The answer estimate is 31, the pricing page's own days-in-a-month convention, times the number of (target, engine) pairs that would actually run under the rule above, summed over every active variant of every active prompt in the request (api/services/prompt_variants.py#monthly_answers, #DAYS_PER_MONTH_ESTIMATE). It is an upper bound: it does not subtract runs that fail or runs where Google shows no AI answer, and it keeps counting past the point where your slot budget would itself cut monitoring off partway through a month. For a new prompt the preview has not read the text yet, so a prompt over 700 characters is still counted for the Google engines; the form says so under the estimate.

Templates: save a combination once, reuse or bulk-apply it

Open Settings → Variant templates. A variant template is a named, project-scoped combination of countries, cities, audience, languages and engines, the exact shape the Add prompt form already fills in (api/services/variant_templates.py). A template saved before cities existed reads as one with no cities, so bulk-applying it to a prompt that has city targets shows those cities as removals in the preview before anything is written. Owners and editors create, edit and delete templates; every project member can read the list. A template fills the "Start from template" select on the Add/Edit prompt form, and drives bulk apply from the prompt list: select several tracked prompts, choose a template, preview what it would change across all of them, then apply.

A stale template, one naming an archived persona or a country the project no longer has enabled, behaves differently depending on how you apply it. "Start from template" on the Add/Edit prompt form fills the form from the template and drops any disabled country, or city of a disabled country, from the pickers (it simply is not one of the choices offered), while a flag under the template picker itself names how many countries, cities or personas were dropped (frontend/src/lib/components/AddPromptModal.svelte#templateFlags). Bulk apply is stricter: it sends the template's spec as saved, unfiltered, to the same validator prompt create and edit use, so a disabled country is refused with an error before anything is written for any of the selected prompts, though the error itself is the generic "Prompts can only target countries enabled for the project" message and does not name the country (a city in a country no longer enabled is refused the same way, and that error does name the city, as in "Lyon, FR"), and an archived persona is dropped from what gets activated rather than failing the request (api/services/prompt_variants.py#apply_variants, #resolve_spec). Reading a saved template resolves it against today's project first, so both surfaces can flag an archived persona, a disabled country, or an engine no longer on your plan before you apply it; the saved spec itself is never rewritten by that flag (api/services/variant_templates.py#_read). Deleting a template does not change any prompt already built from it: applying a template copies its combination onto the prompts you chose at that moment, it does not keep them linked to the template afterward.

A project can hold at most 20 saved templates. A name is 1 to 60 characters, trimmed, and cannot repeat another template's name in the same project regardless of case (api/services/variant_templates.py#TEMPLATE_LIMIT, #NAME_MAX_LEN, #UQ_TEMPLATE_NAME). There is no default template: the project's own default language above already covers what a template with no explicit choice would otherwise need to repeat.

Reporting by language

  • The filter bar: its language chip narrows to As written or one language on every screen that takes it, the prompt list included, where it lists the prompts with an active target in that language. It offers the languages your prompts' targets use, As written among them when some target runs untranslated, and is disabled with fewer than two choices (api/services/explorer/dimension_values.py#target_languages, api/services/answer_filters.py#available_filter_options); see The filter bar.
  • Prompts: each row carries a chip for every real language currently tracking it, plus a separate As written chip only when the same prompt also runs unmodified (frontend/src/lib/languages.js#languageChips). A prompt running on a subset of engines carries its own chip too.
  • Sentiment trends, Brand reasons and Compare vs. competitor follow the language chip like the persona chip (api/services/sentiment_trends.py#sentiment_trends_for_project). Sentiment trends and Brand reasons each pair the persona chip with a separate "By persona" breakdown view; neither has an equivalent "By language" view, and Compare vs. competitor has no breakdown view for either dimension, only the chips.
  • Explorer: language is one of its dimensions, so any of its metrics, citation rate and brand visibility included, can be broken down or filtered by language, and a language breakdown keeps every chat engine a prompt runs on; a Chinese row holds no Google AI Overviews or AI Mode answers (see Explorer) (api/services/explorer/dimensions.py#DIMENSIONS, api/services/collection.py#target_runs_on). Overview has no language card; its answer drawer tags an answer with the language it was collected in (api/services/overview_answer.py#build_answer).
  • Exports and API: the Answers and Brand mentions (entity_mentions) file exports, and Brand reasons, all gain a language column, empty for As written, never omitted; Citations, Cited URLs, Domains, Daily metrics and Prompts do not (api/services/exports/answers.py#COLUMNS, api/services/exports/entity_mentions.py#COLUMNS, api/services/exports/brand_reasons.py#COLUMNS). The customer API's answer and brand-reason objects, and the matching MCP tools, carry the same field, a code or null for As written (api/schemas/customer_api.py#CustomerAnswer, #CustomerBrandReason).

What this does not include

  • The prompt text itself is never translated; write it in the language you want measured and set that language on it, as above.
  • CSV and Excel import carries no language column: only a country new to the prompt for the audience a row concerns lands in the project's current default language, the same as typing a prompt by hand for that country. A country the prompt already tracks for that audience is left untouched, and a row whose countries are all already tracked is reported "Already tracked" and writes nothing.
  • Prompt discovery and research suggest prompt text the same way regardless of language; only the language an accepted suggestion's own target lands in follows the project default, not the text the research model was asked to write.
  • Nothing detects a project's default language automatically from its website; you set it yourself in project settings.
  • Engine choice is per prompt, not per variant: one prompt cannot run its French targets on one set of engines and its As-written targets on another.
  • A language code never carries a region: there is no separate variant for, say, Brazilian versus European Portuguese.
  • Answers from the chat engines are collected through their APIs, not the consumer apps, so a consumer AI app's own interface-language setting is not reproduced for them; this page's language is only what an engine is asked to answer in, or, for the four DataForSEO engines, the language setting sent with the request. This describes tracked prompts; brand studies are the exception above, since a study in a language also sends ChatGPT (app) the response-language instruction.
  • Answer analysis, mentions, sentiment and reasons, runs on a non-English answer with the same models used for an English one; how accurately it performs per language has not been separately measured.
  • Prompts: countries, audiences and now languages, together, and what running a prompt actually means
  • Personas: the audience dimension languages sit alongside
  • Sentiment trends: the language chip, alongside persona and prompt segment
  • Brand reasons: the same language chip, for strength and weakness reasons
  • Exports and activity: the language column on Answers and Brand mentions
  • Plans and limits: why languages, templates and engine choice need no separate entitlement

Last verified 2026-09-29

Start monitoring your AI visibility.

See how AI search engines talk about your brand.

Free to start. No credit card required.