CodeGym /Kursy /ChatGPT Apps /Skąd brać locale: openai/locale, _meta["openai/userLocati...

Skąd brać locale: openai/locale, _meta["openai/userLocation"]

ChatGPT Apps
Poziom 9 , Lekcja 1
Dostępny

1. Dlaczego w ogóle warto brać locale z platformy, a nie pytać użytkownika za każdym razem

Jeśli podejść do lokalizacji „po staremu”, logika bywa taka: pokazać modal „Wybierz język” i zapisać wynik w localStorage. W ChatGPT Apps podejście jest inne: platforma już dostarcza sprytne sygnały o języku i regionie. Trzeba nauczyć się z nich korzystać i nie męczyć użytkownika zbędnymi pytaniami.

ChatGPT w każdym żądaniu do twojej aplikacji dodaje do kontekstu:

  • preferowaną lokalizację użytkownika (język + region) — w polu openai/locale / _meta["openai/locale"];
  • geolokalizację/region użytkownika — w polu _meta["openai/userLocation"].

Po stronie widżetu (frontend) otrzymujesz locale przez window.openai lub hook SDK; po stronie MCP/backendu — przez _meta w żądaniu MCP.

W efekcie normalny scenariusz wygląda tak: użytkownik pisze „Dobierz prezent dla mamy do 50 euro”. ChatGPT już zna jego locale i userLocation, platforma przekazuje te sygnały twojej aplikacji, a ty:

  • pokazujesz UI w zrozumiałym języku,
  • ładujesz właściwy język katalogu,
  • formatujesz ceny we właściwej walucie i formacie.

Bez dodatkowego dialogu „A tak w ogóle, jaki masz język?”.

2. Sygnał nr 1: openai/locale — język i region użytkownika

Czym jest to pole i jak wygląda

openai/locale — to łańcuch w formacie BCP‑47, który zapewne znasz: "en", "en-US", "ru", "ru-RU", "uk-UA" itd.

Ważne, że platforma:

  • może przysłać sam język ("en", "ru"),
  • może przysłać język + region ("en-US", "en-GB", "fr-CA").

BCP‑47 — standard, z którym świetnie współpracują zarówno Intl-API w przeglądarce, jak i większość bibliotek i18n. To znaczy, że openai/locale można niemal bezpośrednio przekazywać do Intl.NumberFormat, silnika tłumaczeń oraz twoich tools.

Gdzie locale jest dostępny w widżecie

W customowym UI renderowanym wewnątrz ChatGPT, Apps SDK udostępnia globalny obiekt window.openai, gdzie jest locale.

Typowo wygląda to tak (TypeScript, Next.js 16, nasz widżet GiftGenius):

// src/app/widgets/gift-widget.tsx
declare global {
  interface Window {
    openai?: { locale?: string };
  }
}

function getOpenAiLocale(): string {
  if (typeof window === "undefined") return "en";
  return window.openai?.locale || "en";
}

W realnej aplikacji prościej zbudować hook, który zadziała zarówno w sandboxie ChatGPT, jak i w Storybooku:

// src/app/hooks/useOpenAiLocale.ts
import { useEffect, useState } from "react";

export function useOpenAiLocale(defaultLocale: string = "en") {
  const [locale, setLocale] = useState(defaultLocale);

  useEffect(() => {
    if (typeof window === "undefined") return;
    const next = window.openai?.locale || defaultLocale;
    setLocale(next);
  }, [defaultLocale]);

  return locale;
}

Teraz w dowolnym komponencie:

import { useOpenAiLocale } from "../hooks/useOpenAiLocale";

export function GiftHeader() {
  const locale = useOpenAiLocale();

  return (
    <h2>
      {/* później będzie tu t('titles.gift_search') */}
      {locale.startsWith("ru") ? "Wybór prezentu" : "Gift search"}
    </h2>
  );
}

Na wykładzie 4 przeniesiemy wszystkie ciągi do słowników, ale już teraz powiązaliśmy UI z realnym sygnałem z platformy, a nie z losowym navigator.language. Ten hook jest wyspecjalizowany; w realnym projekcie wygodnie zbudować go na bazie bardziej ogólnego mechanizmu dostępu do globali ChatGPT — wrócimy do tego w osobnym rozdziale poniżej.

Gdzie locale jest dostępny w MCP/back-endzie

Gdy ChatGPT wywołuje MCP‑narzędzie, SDK przekazuje _meta["openai/locale"] w żądaniu JSON‑RPC. Na serwerze TypeScript (nasz GiftGenius MCP) jest to zwykle dostępne w drugim argumencie handlera narzędzia.

Przykład:

// src/mcp/server.ts
import { McpServer } from "@openai/mcp-sdk";

const server = new McpServer();

server.registerTool(
  "suggest_gifts",
  {
    title: "Dobór prezentów",
    description: "Proponuje listę prezentów na podstawie preferencji",
    inputSchema: {
      type: "object",
      properties: {
        recipient: { type: "string" },
        budget: { type: "number" }
      },
      required: ["recipient", "budget"]
    }
  },
  async ({ input }, extra) => {
    const locale = extra?._meta?.["openai/locale"] || "en";
    // dalej można ładować właściwy katalog
    const gifts = await loadGiftCatalog(locale);
    // ...
    return {
      content: [
        {
          type: "text",
          text: `Found ${gifts.length} gifts for locale ${locale}`
        }
      ],
      structuredContent: { gifts }
    };
  }
);

W ten sposób locale „żyje” przez cały stos: ChatGPT → Apps SDK → twój serwer MCP.

Insight

Każdy mcp-tool na serwerze ma parametr extra, do którego mcp‑serwer odkłada wszystkie dane, które nie mieszczą się w inputSchema. Oto przykład takiego obiektu:

{
  sessionId: undefined,			// zawsze undefined, użyj `openai/subject` poniżej
  _meta: {
    'openai/userAgent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/143.0.0.0 Safari/537.36',
    'openai/locale': 'en-US',		// locale komputera użytkownika, może nie pokrywać się z językiem na czacie
    'openai/userLocation': {		// dość dokładna lokalizacja użytkownika
      city: 'London',
      region: 'London City',
      country: 'GB',
      timezone: 'Europe/London',
      latitude: '5.45466',
      longitude: '-0.52380'
    },
    timezone_offset_minutes: -240,	// przesunięcie strefy czasowej
    'openai/subject': 'v1/sEtRuS92UEOPNdwzEUZORfeOKf7XSk2KZoIUGfAsb68BzZ8h5FAOgrH'	// to jest sessionId
  },
  authInfo: undefined,
  requestId: 1,
  requestInfo: {
    headers: {
      accept: 'application/json, text/event-stream',
      'accept-encoding': 'gzip, deflate, br, zstd',
      'access-control-allow-headers': '*',
      'access-control-allow-methods': 'GET,POST,PUT,DELETE,OPTIONS',
      'access-control-allow-origin': '*',
      'content-length': '542',
      'content-type': 'application/json',
      host: 'test.ngrok.app',					// bazowa domena aplikacji
      'mcp-protocol-version': '2025-11-25',	
      traceparent: '00-69399d3a000000004fb8cc13dc3a2203-8748a8698107eb34-00',
      tracestate: 'dd=s:-1;p:01514e334c1ccef5;t.dm:-3',
      'user-agent': 'openai-mcp/1.0.0',
      'x-datadog-parent-id': '6089244476286233754',
      'x-datadog-sampling-priority': '-1',
      'x-datadog-tags': '_dd.p.tid=69399c3a00000000,_dd.p.dm=-3',
      'x-datadog-trace-id': '5744565710382309891',
      'x-forwarded-for': '199.210.139.232',
      'x-forwarded-host': 'test.ngrok.app',
      'x-forwarded-port': '3001',
      'x-forwarded-proto': 'https'
    }
  },
}

Być może część nagłówków wypełnił tu ngrok, ale ciekawych danych i tak jest sporo.

3. Sygnał nr 2: _meta["openai/userLocation"] — geografia użytkownika

Struktura i sens

_meta["openai/userLocation"] — to obiekt z informacjami geograficznymi: kraj, region, miasto, strefa czasowa, a nawet współrzędne. Mniej więcej tak:

{
  "city": "London",
  "region": "England",
  "country": "GB",
  "timezone": "Europe/London",
  "latitude": 51.5074,
  "longitude": -0.1278
}

Najważniejsze pola, z których realnie skorzystasz w GiftGenius:

  • country — dwuliterowy kod kraju ISO, kluczowy dla asortymentu i waluty;
  • timezone — przyda się do formatów dat/godzin i przypomnień.

Insight

Empirycznie sprawdzone – określanie userLocation działa bardzo dobrze. Dane przychodzą przy każdym wywołaniu narzędzia MCP przez parametr extra._meta["openai/userLocation"]. Możesz na nich polegać podczas tworzenia swoich aplikacji.

Jak używać userLocation w narzędziach MCP

Na serwerze MCP userLocation żyje w _meta["openai/userLocation"] obok _meta["openai/locale"].

Rozszerzmy przykład naszego narzędzia:

server.registerTool(
  "suggest_gifts",
  { /* schema jak wyżej */ },
  async ({ input }, extra) => {
    const meta = extra?._meta ?? {};
    const locale = (meta["openai/locale"] as string) || "en";
    const userLocation = meta["openai/userLocation"] as
      | { country?: string; city?: string }
      | undefined;

    const country = userLocation?.country || "US";

    const gifts = await loadGiftCatalog(locale, country);

    return {
      content: [
        {
          type: "text",
          text: `Found ${gifts.length} gifts for locale=${locale}, country=${country}`
        }
      ],
      structuredContent: { gifts }
    };
  }
);

Funkcja loadGiftCatalog(locale, country) może już:

  • wybrać właściwy plik JSON: gift_catalog.en-US.json, gift_catalog.ru-RU.json,
  • odfiltrować produkty, których nie da się dostarczyć do danego kraju,
  • wybrać walutę bazową.

Nieco później, w modułach commerce, będziesz na podstawie country wybierać zasady podatkowe i mapować właściwe SKU, ale z architektonicznego punktu widzenia dalej opierasz się na tym samym sygnale — country.

Jak userLocation uzupełnia locale

Klasyczny przykład:

locale = "en", userLocation.country = "DE".

Logika może być taka:

  • UI i podpowiedzi — po angielsku (szanuj locale);
  • format waluty i cen — euro, bo użytkownik jest fizycznie w Niemczech;
  • lista prezentów — tylko te, które można dostarczyć do DE.

W GiftGenius można to wyrazić krótką funkcją pomocniczą:

export function deriveCurrency(locale: string, country?: string): string {
  if (country === "DE") return "EUR";
  if (country === "JP") return "JPY";   
  if (locale === "zh_CN") return "CNY"; 
  return "USD";
}

I używać jej na back-endzie/front-endzie do formatowania cen:

const currency = deriveCurrency(locale, country);
const formatted = new Intl.NumberFormat(locale, {
  style: "currency",
  currency
}).format(price);

Na back-endzie nauczyliśmy się już używać locale i country do wyboru katalogu i waluty. Dalej ważne jest, by te same sygnały trafiły do UI w widżecie, aby użytkownik widział teksty i ceny w oczekiwanym formacie.

4. Jak pobierać locale i userLocation w widżecie GiftGenius

Już zobaczyliśmy, jak locale i userLocation działają po stronie MCP i wpływają na katalogi oraz walutę. Teraz omówmy, jak poprawnie pobrać locale w widżecie GiftGenius i użyć go bezpośrednio w React‑UI.

Ważne: w widżecie mamy bezpośredni dostęp tylko do locale (przez window.openai i hooki SDK). userLocation żyje w _meta i jest używany po stronie MCP/back‑endu — z nim pracowaliśmy wyżej.

W Apps SDK, poza „surowym” window.openai, są też narzędzia w postaci hooków React. Dokumentacja opisuje hooki w rodzaju useOpenAiGlobal("locale"), które wyciągają wartości globalnego kontekstu ChatGPT do komponentów React.

Zasymulujmy taki hook sami, żeby zrozumieć, co dzieje się pod spodem.

Podstawowy hook useOpenAiGlobal

Wcześniej zrobiliśmy wyspecjalizowany useOpenAiLocale. W praktyce wygodniej mieć jeden uniwersalny hook do dostępu do globali ChatGPT — na jego bazie łatwo zbudować i useOpenAiLocale, i inne wrappery. Załóżmy taki hook:

// src/app/hooks/useOpenAiGlobal.ts
import { useEffect, useState } from "react";

type OpenAiGlobals = {
  locale?: string;
  // tu później można dodać theme, userAgent itp.
};

export function useOpenAiGlobal<K extends keyof OpenAiGlobals>(
  key: K,
  fallback?: NonNullable<OpenAiGlobals[K]>
): NonNullable<OpenAiGlobals[K]> {
  const [value, setValue] = useState<NonNullable<OpenAiGlobals[K]>>(
    (fallback ?? "") as NonNullable<OpenAiGlobals[K]>
  );

  useEffect(() => {
    if (typeof window === "undefined") return;
    const globals = (window.openai || {}) as OpenAiGlobals;
    const next = globals[key] ?? fallback;
    if (next !== undefined) {
      setValue(next as NonNullable<OpenAiGlobals[K]>);
    }
  }, [key, fallback]);

  return value;
}

Teraz useOpenAiGlobal("locale", "en") daje nam aktualną wartość locale z domyślną wartością "en".

Zastosowanie w widżecie GiftGenius

Zróbmy mały komponent, który wyświetla zlokalizowane powitanie i bieżące locale do debugowania:

// src/app/widgets/GiftWelcome.tsx
"use client";

import React from "react";
import { useOpenAiGlobal } from "../hooks/useOpenAiGlobal";

export function GiftWelcome() {
  const locale = useOpenAiGlobal("locale", "en");

  const greeting =
    locale.startsWith("ru") || locale.startsWith("uk")
      ? "Cześć! Pomogę dobrać prezent."
      : "Hi! I’ll help you find a great gift.";

  return (
    <div>
      <p>{greeting}</p>
      <small style={{ opacity: 0.6 }}>Debug locale: {locale}</small>
    </div>
  );
}

Na razie bez słowników i bibliotek i18n — to będzie później. Teraz istotne jest, że umiemy już uczciwie pobrać język z ChatGPT, a nie z przypadkowych założeń.

5. Kiedy trzeba zapytać użytkownika o język wprost

Skoro openai/locale i userLocation są tak dobre, czy można już nigdy nie pytać użytkownika, w jakim języku chce pracować? Niestety, czasem trzeba.

Kiedy sygnałów brakuje

Jest kilka typowych sytuacji:

  • Konto ChatGPT jest anglojęzyczne (locale = "en"), ale użytkownik pisze po rosyjsku. Model odpowiada po rosyjsku, ale UI pokazujesz po angielsku.
  • Użytkownik jest w Niemczech (userLocation.country = "DE"), locale = "en", a ty masz gotowy interfejs po niemiecku i angielsku.
  • Aplikacja jest krytyczna dla języka komunikacji: psychoterapia, porady prawne, edukacja. Tam precyzja zrozumienia jest ważniejsza niż wygoda autodetekcji.

W takich przypadkach warto zadać krótkie i uprzejme pytanie raz na początku scenariusza, a potem zapamiętać wybór.

Jak zadać pytanie o język nienachalnie

Zwykle formułuje się to maksymalnie prosto i wizualnie, na przykład:

  • „W jakim języku jest ci wygodniej: English czy Rosyjski?”
  • „Wykryliśmy twój język jako English. Chcesz przełączyć na inny?”

W ChatGPT App można to zrobić na dwa sposoby:

  • Przez UI widżetu: narysować mały przełącznik języków u góry.
  • Przez wiadomość typu follow‑up w czacie w imieniu aplikacji: wysłać tekstowy follow‑up z pytaniem, a następnie obsłużyć odpowiedź.

Kod: prosty wybór języka w GiftGenius

Zróbmy komponent‑przełącznik, który:

  • bierze język startowy z locale,
  • pozwala użytkownikowi wybrać ru lub en,
  • przechowuje wybór w stanie widżetu (na razie w React‑state).
// src/app/widgets/LanguageSwitcher.tsx
"use client";

import React, { useState, useEffect } from "react";
import { useOpenAiGlobal } from "../hooks/useOpenAiGlobal";

type SupportedLocale = "en" | "ru";

export function LanguageSwitcher(props: {
  onChange?: (locale: SupportedLocale) => void;
}) {
  const initialLocale = useOpenAiGlobal("locale", "en");
  const [locale, setLocale] = useState<SupportedLocale>("en");

  useEffect(() => {
    const normalized: SupportedLocale = initialLocale.startsWith("ru")
      ? "ru"
      : "en";
    setLocale(normalized);
    props.onChange?.(normalized);
  }, [initialLocale, props]);

  const handleChange = (next: SupportedLocale) => {
    setLocale(next);
    props.onChange?.(next);
  };

  return (
    <div style={{ marginBottom: 8 }}>
      <span style={{ marginRight: 8 }}>
        {locale === "ru" ? "Język:" : "Language:"}
      </span>
      <button
        type="button"
        onClick={() => handleChange("en")}
        style={{ fontWeight: locale === "en" ? "bold" : "normal" }}
      >
        EN
      </button>
      <button
        type="button"
        onClick={() => handleChange("ru")}
        style={{ fontWeight: locale === "ru" ? "bold" : "normal", marginLeft: 4 }}
      >
        RU
      </button>
    </div>
  );
}

A w głównym widżecie GiftGenius możesz już wybierać teksty/słownik na podstawie selectedLocale, a nie „surowych” danych z ChatGPT.

W przyszłych wykładach zamienisz lokalny stan na trwalsze przechowywanie (np. przekażesz wybrany język do MCP / Gateway przez _meta["openai/subject"]), ale wzorzec pozostanie ten sam.

6. Jak przekazywać locale i userLocation do back-endu i je przechowywać

Sygnały od ChatGPT przychodzą „z góry”, ale na tym życie się nie kończy. Trzeba te dane donieść do twoich narzędzi i serwisów, nie zgubić po drodze i nie zmuszać modelu do odgadywania języka od nowa.

Jawne pole locale w argumentach tools

Najpewniejsza metoda – dodać locale (i opcjonalnie country) jako osobne pola w inputSchema narzędzia. Wtedy model dostaje jasny sygnał: „trzeba wypełnić to pole”.

server.registerTool(
  "suggest_gifts",
  {
    title: "Gift suggestions",
    description: "Suggest gifts based on recipient and budget",
    inputSchema: {
      type: "object",
      properties: {
        recipient: { type: "string" },
        budget: { type: "number" },
        locale: {
          type: "string",
          description: "Current user UI locale, BCP-47 (e.g. en-US, fr-FR)"
        },
        country: {
          type: "string",
          description: "ISO country code (e.g. US, DE)"
        }
      },
      required: ["recipient", "budget"]
    }
  },
  async ({ input }, extra) => {
    // Jeśli model nie wypełnił locale/country, zabezpieczymy się przez _meta:
    const meta = extra?._meta ?? {};
    const locale = input.locale || (meta["openai/locale"] as string) || "en";
    const country =
      input.country ||
      (meta["openai/userLocation"] as any)?.country ||
      "US";

    // ...
  }
);

To zmniejsza „magię” wewnątrz serwera: widzi on wyraźnie argumenty, których model zamierza użyć.

Przechowywanie locale na poziomie sesji/użytkownika

W architekturze z MCP Gateway (przyszłe moduły) przyjęło się przechowywać „stan klienta”: locale, currency, preferencje. Teraz ważne jest zrozumieć ideę: raz odczytaliśmy sygnały od ChatGPT — dalej używamy ich jako części stanu sesji, a nie liczymy na nowo za każdym razem.

Umowny pseudokod:

// gateway.ts
const sessionState = new Map<string, { locale: string; country?: string }>();

function onMcpRequest(request: any) {
  const subject = request._meta?.["openai/subject"]; // anonimowy identyfikator użytkownika
  const locale = request._meta?.["openai/locale"] || "en";
  const country = request._meta?.["openai/userLocation"]?.country;

  if (subject) {
    sessionState.set(subject, { locale, country });
  }

  // dalej przekazujemy locale/country do konkretnego serwera MCP
}

W ramach tego wykładu nie musisz implementować Gateway, wystarczy rozumieć, że locale i userLocation są kandydatami do takiego „stanu sesji”.

Insight

Dane eksperymentalne: request._meta?.["openai/locale"] pokazuje aktualnie ustawione locale użytkownika. Język komunikacji możesz przekazać jako parametr tool przez inputSchema.

Ustawiłem u siebie w komputerze locale EN, a rozmawiałem z ChatGPT po niemiecku (DE). W rezultacie:

  • request._meta?.["openai/locale"] było równe EN
  • locale otrzymane jako parametr tool przez inputSchema było równe DE

7. Locale vs automatyczne wykrywanie języka po tekście

Czasem kusi pomysł: „Po prostu będziemy wykrywać język z tekstu użytkownika, przecież LLM wszystko potrafi”. W praktyce to prawie zawsze gorsze niż poleganie na openai/locale.

Powody są dość przyziemne:

  • użytkownik może pisać mieszanką języków;
  • drobne różnice (uk-UA vs ru-RU) słabo wykrywają się po jednej wiadomości;
  • ChatGPT już wykonał tę pracę za ciebie i przysłał locale.

Automatyczne wykrywanie ma sens jako fallback, jeśli openai/locale przychodzi w jakiś nietypowy sposób albo brakuje go (co teraz zdarza się rzadko), ale nie powinno być podstawą logiki. Prosta zasada:

  • najpierw patrzymy na openai/locale jako na „prawdę”;
  • potem uwzględniamy userLocation (waluta, asortyment);
  • i tylko w zupełnie spornych przypadkach dodatkowo zerkamy na język ostatniej wiadomości.

8. Różne kombinacje locale i userLocation: tabela scenariuszy

Dla utrwalenia zobaczmy, jak GiftGenius powinien zachowywać się w różnych scenariuszach.

Scenariusz locale userLocation.country Język UI Waluta Katalog
1
en-US
US
EN
USD
US‑produkty
2
uk-UA
UA
UKR/RU
UAH
UA‑produkty
3
en
DE
EN
EUR
DE‑produkty
4
ru-RU
DE
RU
EUR
DE‑produkty
5
en
(brak danych) EN
USD
Global default

To spojrzenie przyda ci się później, gdy będziemy omawiać commerce, ale już teraz widać, jak łatwo zmieniać zachowanie, po prostu podsuwając różne locale i country.

9. Mały diagram przepływu sygnałów lokali

Aby ułożyć sobie temat, spójrzmy na uproszczony schemat:

flowchart TD
  U[Użytkownik<br/>pisze wiadomość] --> C[ChatGPT]
  C -->|określa| L[openai/locale<br/>+ userLocation]
  L -->|przekazuje| W["Widżet (Next.js)"]
  L -->|przekazuje przez _meta| S[MCP Server]

  W -->|locale| UI[GiftGenius UI<br/>teksty + format liczb]
  S -->|locale + country| DATA[Katalogi, ceny, filtry]

  style L fill:#e0f7ff,stroke:#00a
  style W fill:#f7fff0,stroke:#4b4
  style S fill:#fdf0ff,stroke:#b4

Ważne: na tym schemacie nigdzie nie ma modala „Wybierz język”. Jest potrzebny tylko jako dodatkowa warstwa, gdy sygnały są sprzeczne z oczekiwaniami użytkownika.

10. Praktyka: co możesz zrobić od razu w swojej aplikacji

Aby wykład nie został teorią, krótki praktyczny checklist dla GiftGenius:

  • W widżecie: dodać hook useOpenAiGlobal("locale") lub jego odpowiednik i przynajmniej w jednym miejscu zrobić rozwidlenie RU/EN dla tekstu.
  • W serwerze MCP: w jednym z istniejących narzędzi (suggest_gifts) odczytać _meta["openai/locale"] i _meta["openai/userLocation"], wypisać je do logu i użyć do wyboru katalogu.
  • Napisać prostą funkcję deriveCurrency(locale, country) i użyć jej w jednym miejscu przy formatowaniu ceny.

Nie trzeba od razu budować pełnego silnika i18n i 15 języków — naszym celem teraz jest nauczyć się rzetelnie korzystać z sygnałów platformy.

11. Typowe błędy przy pracy z locale i userLocation

Błąd nr 1: całkowicie ignorować openai/locale i polegać wyłącznie na navigator.language.
Tak robią ci, którzy przywykli do zwykłych aplikacji webowych. W ChatGPT użytkownik może w ogóle niczego nie otwierać w przeglądarce, a navigator.language po twojej stronie to język serwera tunelującego lub Vercela, a nie użytkownika. W efekcie UI jest „tajemniczo” po angielsku, choć ChatGPT konsekwentnie przysyła ci ru-RU.

Błąd nr 2: za każdym razem pytać użytkownika „w jakim języku wygodniej?”.
Jeśli w każdym czacie pierwsza kwestia widżetu to ankieta językowa, użytkownicy czują się jak na lotnisku, gdzie pięć razy z rzędu pytają ich, czy nie zapomnieli bagażu. Platforma już zna język i region — wystarczy respektować openai/locale i pytać tylko przy jawnym konflikcie (np. zapytanie po rosyjsku przy locale = "en").

Błąd nr 3: przechowywać wybrany język tylko w UI i nie przekazywać go do narzędzi MCP.
Widżet może być po polsku, a serwer nadal zwracać anglojęzyczny katalog, bo nie wie o zmianie języka. Zawsze myśl o ścieżce end‑to‑end: jeśli masz w UI przełącznik, wynik trzeba dostarczyć do back-endu — albo w argumentach narzędzia, albo przez sesję Gateway.

Błąd nr 4: próbować „zgadywać” język wyłącznie z tekstu wiadomości, ignorując openai/locale.
Autodetekcja po tekście może działać nieźle… dopóki użytkownik pisze czystym angielskim. Gdy tylko pojawią się mieszanki albo podobne frazy, rezultat zacznie pływać. openai/locale — to już gotowa, dość wiarygodna ocena, dostarczona przez platformę. Warto traktować ją jako główne źródło prawdy, a detekcję z tekstu — tylko jako sygnał dodatkowy.

Błąd nr 5: mieszać logikę biznesową i lokalizację w stylu if (locale === 'ru') { ... } w całym kodzie.
Na tym wykładzie jeszcze trochę tak robimy dla prostoty, ale warto z góry planować, że łańcuchy, formaty i katalogi powinny być oddzielone od logiki biznesowej. W przeciwnym razie za kilka miesięcy znajdziesz się w kodzie, gdzie każda funkcja zaczyna się od if (locale.startsWith("ru")), a dodanie kolejnego języka będzie bolesne. W wykładzie 44 będziemy leczyć właśnie ten problem, pamiętając, że źródło locale już mamy i umiemy z niego korzystać.

Komentarze
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION