CRISP partners

Multilingual market displays

Add translated event, market and outcome labels without changing your trading integration.

CRISP owns the provider adapter, locale validation, display projection, batching contract, request coalescing and bounded caching. You own your application’s UI messages, language picker, language preference, number/date formatting and optional localized URLs. No new service, translation credentials or configuration for your organization is required. Localization uses your existing market-data access.

Start with the SDK

Use SDK 0.7.0 or later for locale-aware catalog reads. Reuse your server-only client and expose public display metadata through your backend. Never expose Partner API credentials in browser code.

const response = await crisp.listEvents({ active: true, closed: false, limit: 20, locale: 'zh-hant' });
// response.data.events remains canonical.
// response.data.localization contains matching display metadata in the SAME response.

The optional locale query is supported by GET /v1/events, /v1/events/:id, /v1/markets, and /v1/markets/:conditionId. Non-English responses include a localization companion with the same shape as the standalone catalog endpoint. Omitting locale or using en preserves the existing response. Unsupported locales return invalid_locale.

Render the companion using catalogDisplay(response.data.localization) when present, without modifying canonical objects. Resolve the selected language on the server (for example, a validated preference cookie), await the localized catalog, and serialize the display snapshot with initial page data. Hydrate from that identical snapshot. For navigation, filters and pagination, consume the response's display metadata before publishing new cards. Do not render English cards and initiate a second translation request from each card's mount effect. Include locale in every page/data cache key; never publicly cache cookie-dependent responses without an explicit locale key.

Apps with custom discovery feeds may continue using the standalone batching API below. Assemble canonical data and its localization on the server under one response/render boundary, rather than a browser waterfall. Discovery topic metadata can be loaded in parallel and included in the same page snapshot. Preserve a bounded provider-error fallback; ordinary cold reads should not be cut short by a separate rendering deadline.

const capabilities = await crisp.getLocalizationLocales();
// locales: en, zh (Simplified Chinese), zh-hant (Traditional Chinese), ja
// defaultLocale: en; maxBatchSize: 25; rulesPolicy: original_only

const translated = await crisp.getCatalogLocalization({
  locale: 'zh-hant',
  eventId: ['481717'],
});
// translated.data.events contains event title, nested market titles,
// groupItemTitle, outcome token IDs + labels, and tag IDs + labels.

The endpoint is GET /v1/localization/catalog. Repeat eventId or marketId query parameters for multiple records. eventId is the numeric catalog event ID. marketId is a condition ID (0x + 64 hex characters), not the numeric sourceMarketId. A mixed batch may contain at most 25 total IDs. Deduplicate and split larger pages into batches with limited concurrency. Fetch visible records, not the entire catalog on each page load. An event includes its market labels, so you usually do not need a second request for those markets.

Render without changing trading semantics

The browser-safe helper contains no credentials or transport:

import { catalogDisplay } from '@crisp-trade/partner-sdk/localization';

const display = catalogDisplay(translated.data);
const title = display.eventTitle(event.id, event.title);
const question = display.marketTitle(market.marketId, market.title);
const outcomeLabel = display.outcome(
  market.marketId, outcome.tokenId, outcome.label,
);
// Render these strings only. Submit the ORIGINAL marketId, tokenId, side,
// outcome index, price, size and amount. Do not translate or replace slugs.

The same market lookup works for portfolio positions, open orders and trade history: collect condition IDs from the canonical records, translate them once, and join each outcome by tokenId, never by array position or translated text. Monetary amounts, fees, balances, execution status and ledger entries are not localized by this module. Use Intl.NumberFormat / Intl.DateTimeFormat for display only; never parse localized money back into order or ledger calculations.

Recurring crypto series (SDK 0.6.1)

Rolling 5-minute, 15-minute and hourly event titles may remain English upstream, even when their parent series title is translated. Catalog events now include optional seriesTitle from that same provider response. Use the shared helper:

const compactTitle = display.eventSeriesTitle(
  event.id, event.seriesTitle || event.title,
);

Use this for recurring-market headings, and show the canonical start/end window alongside it using locale-aware date formatting with an explicit timezone. Keep the original event IDs, slugs, times and trading data untouched. Do not replace ordinary event titles with series text. Missing or ambiguous series metadata and older API responses fall back to your supplied text. Event and series labels share one cached event-ID request; no extra provider call is needed. Deploy the additive API field before clients depend on translated series titles.

Carousel rows and chart legends must also use display.marketGroup(conditionId, originalLabel) (or marketTitle when no group title exists). Reuse the event's nested market translations; do not request each market again. Match event tags by tag ID. Translating only the event heading leaves the rest of the carousel in English.

SDK 0.6.0 or later also provides:

const { data } = await crisp.getDiscoveryLocalization({ locale: 'zh-hant' });
const topicLabels = new Map(data.topics.map(t => [t.key, t.label]));
const tagLabels = new Map(data.tags.map(t => [t.slug, t.label]));
const topicLabel = topicLabels.get(topic.url) ?? topic.title;
// Use topicLabel only for visible text, NOT to build search queries or links.

GET /v1/localization/discovery returns topics: [{ key, label }] and tags: [{ slug, label }]. Topic keys are the original provider-relative URL; tag keys are canonical slugs. Keep canonical ranking, volume, search terms and navigation unchanged. Translate app-owned headings such as “Hot topics” separately.

CRISP reads the provider's public localized homepage metadata, safely projects only display fields, and shares a five-minute cache across organizations within each API process. Requests coalesce per locale, time out after four seconds, and cap response size. This is a best-effort page adapter: upstream markup changes may result in source: unavailable. Last-good labels can remain available for at most one hour; otherwise render original labels. Failures cache for ten seconds. No user-specific scraping, translation secrets or trading-path dependency is needed.

Fallback and caching

  • Existing event/market fields remain canonical and backward compatible. Localization is an optional display companion, never an execution prerequisite.
  • source: upstream means data originated from a successful provider read (possibly cached), not that every field is translated or freshly fetched. Providers can return original English for individual fields. Missing text is null; missing records are listed in missingEventIds / missingMarketIds.
  • source: unavailable can include partial results. Render available translated fields and fall back to canonical text for everything else. Network failures must use the same fallback. Do not show a trading-disabled error for a translation outage.
  • CRISP caches each locale/entity identity independently, so reordered or overlapping pages reuse existing translations. Successful records are fresh for five minutes, with stale-while-revalidate for up to one hour. Confirmed missing records cache for 30 seconds; provider failures for 10 seconds. Errors never extend the last-good expiry. Requests coalesce within each process; provider requests time out after three seconds. Saturation returns fallback metadata instead of creating an unbounded queue.
  • The existing Partner API database provides a best-effort shared display cache across instances/restarts. A dedicated two-connection pool has short timeouts and failure backoff; writes are asynchronous. Database failure falls back to the bounded in-process cache/provider. Popular catalog records are warmed in the background within the existing Partner API process. No new service, credentials or execution dependency is introduced.
  • Key your own cache by locale + stable IDs, never solely by URL path or current user. Abort or isolate old-language requests so a late response cannot overwrite a newly selected language.
  • Never await localization in order submission, signing, wallet prewarming, fee collection, or balance reservation. Await it only at catalog/page-read boundaries. If localization is unavailable, do not CDN-cache an incomplete non-English page as a successful translated response.

CRISP operates the localization service and its shared cache. Your team does not need to deploy the Partner API, create cache tables or configure database access.

Rules, resolution descriptions and additional context remain in their original language. Show an original-language notice. Do not present generated or untranslated rules as verified translated settlement terms. Search results may be localized by returned IDs; multilingual search matching is a separate capability and is not guaranteed by this module.

Integration checklist

  1. Discover supported locales and persist a validated language preference; default to English.
  2. Keep canonical records immutable and maintain a separate display map.
  3. Batch by event ID for discovery/detail pages and by condition ID for portfolio/history.
  4. Render escaped plain text, never provider HTML.
  5. Test English, both Chinese locales and Japanese, missing translations, provider timeout, rapid language switching, and more than 25 records.
  6. Verify identical order payloads before and after a language switch, including non-English Yes/No labels.

The provider adapter is internal to CRISP. Future provider implementations can reuse this display contract without requiring you to integrate provider-specific translation APIs.

CRISP Partner Documentation