CodeGym /Courses /ChatGPT Apps /Localization of tools and descriptions: impact on GPT and...

Localization of tools and descriptions: impact on GPT and behavior experiments

ChatGPT Apps
Level 9 , Lesson 3
Available

1. How the model “sees” your tools

Let’s start with this: for the model, a tool is not “your beautiful TypeScript function”, but a structural description like:

  • name: a technical name, for example "search_gifts";
  • description: human‑readable text that explains when and why to use this tool;
  • inputSchema: a JSON Schema with fields, each of which can also have a description, type, and constraints.

Very roughly, the model does something like this (pseudo‑code inside GPT’s head):


1. Read the user request (in any language).
2. Read the list of tools: name + description + argument descriptions.
3. For each tool, estimate whether it fits the task.
4. If a tool is needed — generate JSON with arguments according to the schema.
5. Otherwise — respond with text.

There are two important takeaways.

First, the description field of a tool is not a developer comment, it is the interface between the model and your backend. If the tool’s description is vague, incomplete, or in a language that doesn’t match the user’s language, the model will miss more often: wrong tool, wrong arguments, a response without tools where they were needed.

Second, the description for fields in a JSON Schema is just as important as the description of the instrument itself. The model really reads the description of each property and uses it to decide which field should contain “age”, which one “budget”, and where there should be an id at all.

Mini example for GiftGenius

Let’s take our search_gifts tool. In the original, “EN‑only” version it could look like this:

// server/tools/searchGifts.ts
export const searchGiftsTool = {
  name: "search_gifts",
  description: "Search for gift ideas based on user preferences.",
  inputSchema: {
    type: "object",
    properties: {
      recipient_age: {
        type: "integer",
        description: "Age of the recipient in years.",
      },
      budget: {
        type: "number",
        description: "Maximum budget in user's currency.",
      },
    },
    required: ["budget"],
  },
};

If the user writes: “I need a gift for my mom, she is 60 years old, budget up to 3000 rubles,” the model should:

  1. Understand that search_gifts is the right tool.
  2. Understand that “60 years” should go into recipient_age, and “3000 rubles” should go into budget.

While the descriptions are only in English, GPT will still cope, but that already forces it to do extra internal “translation.” Across multiple languages and on weaker models, this hurts accuracy.

2. The “English descriptions” problem in a multilingual app

In module 9 we briefly covered the overall “localization map”: UI widget, catalogs, errors, commerce texts. Now we’ll narrow the focus and see what happens when tool descriptions are written only in English, while the user communicates in, say, Spanish — that’s where mild chaos starts.

A typical scenario:

  1. User: “Pick a gift for a developer friend, budget up to 50 euros.”
  2. The model looks at the list of tools and sees descriptions only in EN.
  3. If the request is also in EN — everything is fine.
  4. If the request is in another language, it has to:
    • first understand the request,
    • mentally align it with the English descriptions,
    • pick a tool,
    • and then extract arguments into JSON.

On strong models this still kind of works, but:

  • the share of answers without calling tools tends to increase — the model “thinks” it can handle it itself;
  • the chance of argument errors grows (especially when currency, units, regional constraints matter);
  • routing logic among multiple tools becomes less reliable (the tool is “off target”).

A simple example of an error: the budget field expects “in the user’s currency,” but the description doesn’t say that at all. The model decides it’s USD by default and sends 50 dollars to the backend where the user clearly wanted 50 euros.

And this is where localization of descriptions comes into play.

3. Approaches to localization: separate tools vs. multilingual descriptions

There are two basic architectural approaches, and both are valid.

Separate tools for each language

In this option, you create several tools with different names, each with descriptions in its “own” language.

For GiftGenius it might look like this:

export const searchGiftsEn = {
  name: "search_gifts_en",
  description: "Search for gift ideas based on user preferences.",
  // ...
};

export const searchGiftsRu = {
  name: "search_gifts_ru",
  description: "Podbor podarkov po predpochteniyam pol’zovatelya.",
  // ...
};

For the ChatGPT App, it’s important that the list of available tools depends on the locale. If locale = "ru-RU", then your MCP server should return only search_gifts_ru. If locale = "en-US", — only search_gifts_en.

Pros of this approach: the descriptions are maximally “clean” and monolingual. You can even think of the app as several single‑language versions, each with its own prompts and descriptions. This is comfortable when there are few languages and the markets differ significantly.

Cons — duplicated logic and analytics complexity. In backend code you’ll likely still have the same handler, but at the MCP/manifest level — two different tools. You need to remember to update both descriptions with every change.

Insight (data as of 2025-12-01)

Experimentally, no noticeable advantages were observed for a description matching the user’s language/locale. Tools were selected with almost the same frequency regardless of the description language. If there were two tools with similar descriptions (in different languages), they confused ChatGPT.

In addition, your application will need to pass a review when registering in the Store. So I recommend simply writing all tool descriptions and argument descriptions in English.

However, if in the future there are thousands of apps in ChatGPT and competition for “tool selection” increases, descriptions in the user’s locale may gain an advantage. We’re waiting for Tool Search Optimization.

One tool with multilingual descriptions

In the second option you keep a single name (for example, search_gifts), but make its description and JSON Schema field descriptions multilingual.

There are different styles:

  1. A short bilingual form:
    description: "Search gifts for a recipient. / Poisk podarkov po predpochteniyam poluchatelya.",
  2. Labeled blocks by language:
    description: "[EN] Search for gifts based on user preferences. [RU] Podbor podarkov po predpochteniyam poluchatelya.",
  3. Separate fields concatenated into a string (less convenient):
    description: `EN: ${enDescription} RU: ${ruDescription}`,

Pros — one tool, single source of truth; it’s easier to deploy an architecture with an MCP Gateway: you always expose the same interface to ChatGPT regardless of the user’s language.

Cons: descriptions become longer. If you “mix” languages carelessly, the model can get a bit confused — especially when English and the local text are interleaved without explicit [EN], [RU] markers.

For a learning project like GiftGenius we recommend a hybrid approach: keep descriptions primarily in English, but add a short clarification in the local language, and route the real “semantics” (which language to use, how to address the user) via arguments (locale) and the system prompt.

4. Localizing the JSON Schema: field descriptions

Now let’s go deeper: to the tool’s arguments themselves.

In a JSON Schema, you can (and should) specify a description for each field. The model reads this string when generating the JSON for the tool call.

For GiftGenius you can do this:

export const searchGiftsTool = {
  name: "search_gifts",
  description:
    "Search gifts based on user preferences (RU: podbor podarkov po predpochteniyam poluchatelya).",
  inputSchema: {
    type: "object",
    properties: {
      recipient_age: {
        type: "integer",
        description:
          "Recipient age in years. RU: vozrast poluchatelya (tseloe chislo).",
      },
      budget: {
        type: "number",
        description:
          "Maximum budget in user's currency. RU: maksimal’nyy byudzhet v valyute pol’zovatelya.",
      },
      locale: {
        type: "string",
        description:
          "User locale (e.g. 'en-US', 'ru-RU'). RU: yazyk interfeysa i otvetov.",
      },
    },
    required: ["budget", "locale"],
  },
};

A few practical observations.

First, field names (recipient_age, budget, locale) are usually kept in English. What gets translated is the description. This is important so that the JSON format doesn’t vary by language and you don’t have to maintain two different contracts.

Second, in description it helps to explicitly state currency, units, and key constraints. This dramatically reduces the number of “crooked” arguments.

Third, if you already use an MCP Gateway, you can agree that it automatically forwards locale into the tool’s arguments, so the model doesn’t have to insert it itself. Even then, keeping a description for locale is useful: the model still better understands what the parameter is and why it’s needed.

5. Choosing the description language: strategies for a real app

Now the key practical question: which language should be the primary one for descriptions, and when should you fully localize them?

Recommendations and real‑world experience show that GPT models still work best in an English context, and many developers leave descriptions in EN only. But for a multilingual app this can be a compromise.

Let’s go through a few strategies.

EN‑only descriptions

The simplest option — everything in English.

Pros: one codebase, one language to maintain, easier to write good, precise phrasing. The model is happy when everything around it is in English.

Cons: for users who write in other languages, the quality of tool selection and arguments may be lower, especially for “lazy” models or complex tools with many parameters.

EN + short local tail

A compromise: the main description in EN, and at the end — a short block for the local language that helps the model align user words with arguments.

Example:

description:
  "Search for gifts based on user preferences. RU: instrument podbiraet podarki po opisaniyu poluchatelya, ego vozrastu i byudzhetu.",

For JSON Schema:

description:
  "Age of the recipient in years. RU: vozrast poluchatelya (v godakh).",

Pros: the model is still in an “English” world but has a hint in the user’s language.

Cons: descriptions get longer, but this is usually fine.

Full description localization by locale

The most thorough approach: tool and field descriptions change depending on the locale you get from ChatGPT. For en-US you send purely English descriptions, for ru-RU — purely Russian, for de-DE — German.

This is no longer “one JSON Schema forever,” but a set of schemas that the MCP/Gateway chooses on the fly.

At the MCP level it looks like this:

function getSearchGiftsToolDescription(locale: string) {
  if (locale.startsWith("ru")) {
    return {
      name: "search_gifts",
      description: "Podbor podarkov po predpochteniyam poluchatelya.",
      // ru‑schema...
    };
  }
  return {
    name: "search_gifts",
    description: "Search for gifts based on user preferences.",
    // en‑schema...
  };
}

Pros: the model sees an interface in the same language the user writes in. Maximum convenience.

Cons: maintenance and testing get more complex. You need a process that guarantees all localized description variants remain semantically synchronized and you don’t forget to update, say, the German schema when adding a new field to the English one.

6. Implementation in our GiftGenius app

Let’s get specific. We’ll implement a hybrid in GiftGenius: one tool search_gifts, descriptions primarily in EN with Russian clarifications, plus a locale argument.

Assume you have a TypeScript MCP server that describes tools in the MCP SDK style.

// mcp/tools/searchGifts.ts
import { z } from "zod";

export const searchGiftsInputSchema = z.object({
  recipient_age: z
    .number()
    .int()
    .describe(
      "Age of the recipient in years. RU: vozrast poluchatelya (tseloe chislo)."
    ),
  budget: z
    .number()
    .describe(
      "Maximum budget in user's currency. RU: maksimal’nyy byudzhet v valyute pol’zovatelya."
    ),
  locale: z
    .string()
    .describe(
      "User locale (e.g. 'en-US', 'ru-RU'). RU: yazyk interfeysa i otvetov."
    ),
});

export const searchGiftsTool = {
  name: "search_gifts",
  description:
    "Search for gifts based on user preferences (RU: podbor podarkov po predpochteniyam poluchatelya).",
  inputSchema: searchGiftsInputSchema,
  // execute(...) ...
};

Important:

  • locale is required. If the widget knows it (and we do via _meta["openai/locale"]), it will either pass it itself in the callTool, or the MCP Gateway will insert it automatically on its side;
  • the descriptions already contain Russian keywords like “age”, “budget”, “interface language,” so it’s easier for the model to understand where to put what from the user’s request.

On the Apps SDK side you can, for example, have a function that calls this tool directly (if widgetAccessible is enabled), passing the locale from the widget.

// widget/hooks/useSearchGifts.ts
export async function searchGiftsFromWidget(params: {
  recipientAge: number;
  budget: number;
  locale: string;
}) {
  const openai = (window as any).openai;
  const result = await openai.callTool("search_gifts", {
    recipient_age: params.recipientAge,
    budget: params.budget,
    locale: params.locale,
  });
  return result;
}

This linkage strengthens the architecture: the locale came from ChatGPT → it reaches the tool → the tool picks the right catalog and price formats, which you then render nicely on the frontend.

7. Behavior experiments: how to measure localization impact

Now the fun part: how do you know localization of tools and descriptions actually improves the model’s behavior and that your translation effort was worth it?

You can run a small “scientific experiment” right in Dev Mode on GiftGenius.

Two app variants: base vs. localized

Prepare two configurations of your app:

  • base — tool and JSON Schema descriptions in EN only;
  • localized — descriptions with EN+RU (or full RU versions if you’re ready).

Keep the rest (catalogs, UI, prompts) the same to avoid confounding effects.

For simplicity you can:

  • keep only the localized version in Dev Mode (and especially in the Store);
  • run base locally in a separate branch and compare results on a predefined set of queries.

What to measure

There are three key metrics.

First — the frequency of correct tool selections. For a set of test queries in Russian (and/or another language), check how often the model:

  • decides to call a tool at all, when it should;
  • chooses search_gifts specifically instead of another tool.

Second — argument correctness. Check how often the JSON call meets expectations: fields aren’t mixed up, the budget is in the correct currency, age is explicitly an integer, locale isn’t lost.

Third — the number of odd or meaningless calls. For example, the model calls search_gifts for “what time is it right now?” or sets recipient_age to 3000 instead of the budget.

You can test both manually and via MCP/Agents logs — you’ll need logs later anyway, so it’s better to get used to this analytics early.

How to organize a manual test set

You can create a small “golden prompt set” for localization:

1. "Mne nuzhen nedorogoy podarok do 30 evro dlya devochki 10 let, ona lyubit risovat’."
2. "Podberi podarok dlya kollegi‑programmista, 35 let, byudzhet 100$."
3. "Nuzhen podarok babushke na yubiley, 70 let, byudzhet do 5000 rubley."

Run them through both app versions (base and localized), observing:

  • which tools the model selects;
  • which arguments it supplies;
  • how the text response changes if the tool wasn’t called.

A semi‑pro tip: you can make a simple script wrapper that runs these requests through the ChatGPT API, but within the course manual Dev Mode is enough. A special category of queries that are particularly useful to include in such a set are mixed‑language messages and odd locale combinations. We’ll devote a separate block to them.

If you’re developing a serious commercial application and millions of dollars are at stake, be sure to test these points specifically for your app. Module 20 is dedicated to professional work with a “golden prompt set” — make sure you dig into this topic.

8. Mixed languages and odd locale combinations

Nothing amuses an LLM developer quite like a user who writes in two languages at once. For example:

"Nuzhen podarok for my friend, on lyubit Star Wars, budget 100€"

We already know the model is multilingual and will usually cope. But with mixed languages and “English” descriptions, the error probability grows.

There are a few typical situations.

First — the user writes in RU, while the tool descriptions are in EN. The model can understand but sometimes stumbles, especially on specific terminology (category names, rare field labels).

Second — locale = "ru-RU", but the user writes in English for some reason. ChatGPT signals that the interface should be in Russian, but the actual message language is EN. You can:

  • still serve Russian descriptions, treating locale as primary truth;
  • or implement message language detection as an additional signal and adapt descriptions to the actual language.

Third — locale = "en", while the user occasionally inserts Russian words. In this case, English descriptions usually work just fine.

In practice, it’s enough to choose one clear policy. For example:

  • if locale starts with "ru" — you add Russian snippets to descriptions;
  • if not — descriptions stay purely English.

A clear rule is helpful because you can explicitly test each branch instead of guessing why descriptions ended up in a given language today.

9. Documentation, process, and the “canonical” language

Localization of descriptions is not a one‑time act; it’s a process. Users like new features to ship, and you like nothing old to break. So decide in advance:

  • which language is the “canonical” one for all translations;
  • where to store localized descriptions;
  • how to check consistency.

English is usually the canonical language. All new tools and fields are first described in EN, undergo review, and only then get localized into other languages. In the codebase, you could express it like this:

  • a tools.en.json file with the full description of names/descriptions/fields;
  • tools.ru.json, tools.de.json files as “derivatives” for specific languages;
  • a small generator that assembles final JSON Schemas for MCP from these dictionaries.

In a simple setup you can stick to strings in code for now but structure them so they’re easy to extract into separate dictionaries later.

Remember that descriptions are product text, too. They should be reviewed as rigorously as UI copy: check for clarity, absence of ambiguity, and fluff. Especially in multilingual form, we don’t want the Russian tail to contradict the English part.

10. Visual diagram: how language flows through the stack

To put it all together, let’s look at a simplified request flow considering tool localization.

flowchart TD
    U[User writes in RU] --> C[ChatGPT UI]
    C -->|"_meta.openai/locale = 'ru-RU'"| W[GiftGenius widget]
    W -->|"locale = 'ru-RU'"| T["Tool descriptions (EN+RU)"]
    T --> M[GPT model]
    M -->|callTool search_gifts| MCP[MCP / Gateway]
    MCP -->|"locale = 'ru-RU'"| B[Backend / RU catalogs]
    B --> MCP --> M2["GPT model (response)"]
    M2 --> C2[ChatGPT UI + RU widget]

Here the user’s language and the locale determine:

  • the language the widget uses for the UI;
  • which tool and field descriptions the model sees;
  • which catalogs and currencies the backend selects;
  • how responses are formatted (by both the model and the widget).

11. Common mistakes when localizing tools and descriptions

Mistake #1: treating the description field as “technical” and not localizing it at all.
This works while you only have English‑speaking users. As soon as other languages appear, the model starts responding without tools more often or sending malformed arguments per schemas. You translated the UI, but the app behaves “in English.”

Mistake #2: changing JSON field names by language.
It’s tempting to make agevozrast, budjet, etc. This leads to a backend nightmare: different schemas, different formats, complicated log analysis. It’s better to keep field names stable and localize only descriptions.

Mistake #3: mixing languages chaotically in descriptions.
Phrases like “Poisk gifts po predpochteniyam user” help neither the model nor the human. If you do multilingual descriptions, separate blocks explicitly: [EN] ... [RU] .... Then the model sees structure instead of a mess.

Mistake #4: not passing locale into tools.
Even if you localized descriptions but don’t pass locale to the tool (or the MCP Gateway doesn’t forward it), the backend doesn’t know which catalogs and formats to use. As a result, the model tries to be “multilingual,” while the server returns data for just one market.

Mistake #5: machine‑translating descriptions without review.
It seems you can run descriptions through an automatic translator and call it a day. In practice such translations are often inaccurate, especially regarding terms and arguments. As a result, the model can misinterpret the tool or a field’s meaning. It’s better to have one well‑crafted EN variant and carefully localized versions than twenty “automatic” languages.

Mistake #6: no tests/experiments for different locales.
If you don’t check app behavior on at least a basic set of queries for each locale, things can remain “broken” for months until your first real user with that language arrives. A small golden set of queries and manual Dev Mode tests greatly reduce this risk.

Mistake #7: desynchronization between canonical and localized descriptions.
You added a new occasion field (“gift occasion”) to the English schema but forgot to update the Russian one. As a result, in RU the model doesn’t know about this field at all and doesn’t fill it, while the backend already expects it. For example, the server tries to filter gifts by occasion, gets null, and shows a list that’s too broad — EN works fine, RU silently “breaks.” Therefore, any changes to tool descriptions should go through a simple but regular process: update EN → update locales → run a quick test.

1
Task
ChatGPT Apps, level 9, lesson 3
Locked
FormattingDemo — localization of numbers, currency, and dates via Intl
FormattingDemo — localization of numbers, currency, and dates via Intl
Comments
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION