@procore/ai-translations
v0.8.0
Published
Library that provides a solution to use AI to translate text into a language
Downloads
389
Maintainers
Keywords
Readme
@procore/ai-translations
React package for AI-powered UI string translation in frontend apps. It provides:
AITranslationProviderto initialize translation contextAITranslateTextto render translated textuseAITranslationfor direct access to translation and progress state
Need deeper implementation details? See the internal guide: README.internal.md.
This package supports two runtime strategies:
frontend_translations: browser-based translation (Chrome-family AI APIs)backend_translations: server-side translation API
For Procore-specific operational details, see README.internal.md.
Installation
yarn add @procore/ai-translationsPeer dependencies:
react>= 16 < 19react-dom>= 16 < 19@procore/web-sdk-mfe-utils> 1.0.0 < 2
Quick Start
import React from 'react';
import {
AITranslationProvider,
AITranslateText,
} from '@procore/ai-translations';
export function AppRoot() {
return (
<AITranslationProvider
tool="timecard"
companyId={1}
projectId={10}
userId={42}
locale="es"
enableAIT={true}
companyLocale="de-DE"
projectLocale="fr-CA"
>
<AITranslateText text="Hello world" shouldTranslate={true} />
</AITranslationProvider>
);
}Public API
AITranslationProvider
Required props:
locale: string- target locale (BCP-47, for examplees,fr,de)tool: string- tool/MFE identifier used to scope translation eventscompanyId: numberprojectId: numberuserId: number
Optional props:
enableAIT?: boolean(defaulttrue) - whenfalse, returns source text without translationcompanyLocale?: string- company-level locale from environment metadata (e.g."de-DE"); passed through to context for downstream consumers such as AmplitudeprojectLocale?: string- project-level locale from environment metadata (e.g."fr-CA"); passed through to context for downstream consumers such as Amplitude
AITranslateText
Props:
text: stringshouldTranslate: booleanshowHighlight?: boolean(defaultfalse)translatedIconProps?: TranslatedIconProps
CustomizableAITranslateText
Translates only chosen substrings of a string. Use anywhere a value mixes translatable and non-translatable text — descriptions, status fields, breadcrumbs, tags, cards, forms, modals — not just data tables.
Supply a segmenter function that splits the full text into an ordered list of { text, translate } segments. Only segments with translate: true are sent through the AI pipeline; the rest are rendered verbatim.
Props:
text: string— the full text value, passed tosegmentershouldTranslate: boolean— master switch; whenfalseall segments render as plain textshowHighlight?: boolean(defaultfalse) — whether to show theTranslatedIconhighlightMode?: 'segment' | 'cell'(default'segment') — see belowsegmenter?: (text: string) => TranslatableSegment[]— split rule; when omitted the whole text is one translatable segmenttranslatedIconProps?: TranslatedIconProps
Segmenter examples
"{code} - {label}" — keep the code prefix, translate only the label:
import { CustomizableAITranslateText } from '@procore/ai-translations';
<CustomizableAITranslateText
text="INS-001 - Safety Inspection"
shouldTranslate={true}
segmenter={(text) => {
const idx = text.indexOf(' - ');
if (idx === -1) return [{ text, translate: true }];
return [
{ text: text.slice(0, idx + 3), translate: false }, // "INS-001 - "
{ text: text.slice(idx + 3), translate: true }, // "Safety Inspection"
];
}}
/>;"Key: Value" — keep the field name, translate only the value:
<CustomizableAITranslateText
text="Status: Awaiting Review"
shouldTranslate={true}
segmenter={(text) => {
const idx = text.indexOf(': ');
if (idx === -1) return [{ text, translate: true }];
return [
{ text: text.slice(0, idx + 2), translate: false }, // "Status: "
{ text: text.slice(idx + 2), translate: true }, // "Awaiting Review"
];
}}
/>"A | B" — translate both halves, keep the divider:
<CustomizableAITranslateText
text="Safety | Critical"
shouldTranslate={true}
segmenter={(text) => {
const idx = text.indexOf(' | ');
if (idx === -1) return [{ text, translate: true }];
return [
{ text: text.slice(0, idx), translate: true }, // "Safety"
{ text: ' | ', translate: false }, // " | "
{ text: text.slice(idx + 3), translate: true }, // "Critical"
];
}}
/>"A > B > C" breadcrumb — translate every crumb independently:
<CustomizableAITranslateText
text="Projects > Building A > Floor 3"
shouldTranslate={true}
segmenter={(text) => {
const parts = text.split(' > ');
return parts.flatMap((part, i) => [
{ text: part, translate: true },
...(i < parts.length - 1 ? [{ text: ' > ', translate: false }] : []),
]);
}}
/>highlightMode
Controls where TranslatedIcon appears when a translation actually changes the text:
| Mode | Behavior |
| --------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------- |
| 'segment' (default) | One icon next to each segment whose translated output differs from its source. Precisely marks which substrings are AI-generated. |
| 'cell' | A single icon at the very start of the value, only when at least one segment's output actually changed. Cleaner for values with many translated segments. |
showHighlight={false} suppresses all icons regardless of mode.
useAITranslation()
Returns:
ait(text: string): Promise<string>- translates text or returns cached valuelocale: stringcompanyLocale: string | undefined- company-level locale from environment metadataprojectLocale: string | undefined- project-level locale from environment metadatatool: stringtranslationProgress: { progress: number; current: number; total: number } | nullmodelDownloadProgress: ModelDownloadProgress | nullrenderVersion: number | undefined
Feature flag helpers
AI_TRANSLATION_FEATURE_FLAG_KEYgetAITranslationLDId(domain: string)
How It Works
flowchart LR
consumerApp[ConsumerApp] --> provider[AITranslationProvider]
provider --> aitCall["ait(text)"]
aitCall --> registryCheck[TranslationRegistryCheck]
registryCheck --> returnCached[ReturnCachedText]
registryCheck --> queueText[QueueText]
queueText --> manager[TranslationManager]
manager --> strategy[TranslatorStrategy]
strategy --> frontendClient[ChromeFrontendClient]
strategy --> backendClient[BackendApiClient]
frontendClient --> persist[PersistToIndexedDB]
backendClient --> persist
persist --> publish[PublishTranslationEvents]
publish --> rerender[AITranslateTextRerender]
rerender --> translatedUi[DisplayTranslatedText]At runtime, strings are registered through ait(), processed in batches, cached in memory and IndexedDB, and then rerendered via package events.
Configuration
The provider uses useConfig (React Query) to fetch translation config and refetch every 30 minutes.
Expected remote config shape:
type ToolConfig = {
strategy: 'frontend_translations' | 'backend_translations';
supportedLocales: string[];
translationBatchSize: number;
renderingBatchSize: number;
apiTimeout: number;
apiVersion: string;
};Operational Notes
AITranslateTextanduseAITranslationmust be used insideAITranslationProvider.- Frontend strategy depends on Chrome-family AI support; behavior differs by browser support.
- Backend/config endpoints are resolved from
window.location.originunder/rest/. - Translation cache uses IndexedDB; some restricted browser environments may limit persistence.
- For error handling behavior and failure modes, see
docs/error-handling.md.
Development
From packages/ai-translations:
yarn build
yarn test
yarn lint
yarn storybook
yarn cypress:run