Auto-Translation
Lexis Pro includes integrated auto-translation support using industry-leading translation services. Translate your strings directly within the Unity Editor without leaving your workflow.
| Service | API | Best For |
|---|---|---|
| DeepL | DeepL API | High-quality European language translation |
| OpenAI GPT | OpenAI API | Context-aware translations, creative content |
| Google Translate | Cloud Translation API | Wide language coverage, fast processing |
DeepL Setup
- Go to DeepL API
- Sign up for a DeepL API account (Free or Pro)
- Navigate to your account settings → API Keys
- Copy your API key
- Free vs Pro: DeepL auto-detects based on your key
- Free keys end with
:fx - Free tier: 500,000 characters/month
- Pro tier: Pay-as-you-go pricing
- Free keys end with
Supported Languages: EN, DE, FR, ES, IT, NL, PL, PT, RU, JA, ZH, and more.
OpenAI Setup
- Go to OpenAI Platform
- Create an account or sign in
- Navigate to API Keys section
- Click Create new secret key
- Copy the key (starts with
sk-) - Model Selection:
gpt-4o-mini(default): Fast, cost-effectivegpt-4o: Higher quality, more expensive
Note: OpenAI charges per token. Translation costs depend on text length and model.
Google Cloud Translation Setup
- Go to Google Cloud Console
- Create a new project or select existing
- Enable the Cloud Translation API:
- Go to APIs & Services → Library
- Search for "Cloud Translation API"
- Click Enable
- Create an API key:
- Go to APIs & Services → Credentials
- Click Create Credentials → API Key
- (Recommended) Restrict the key to Cloud Translation API only
- Copy the API key
Pricing: Google charges per character translated. See Cloud Translation pricing.
Lexis supports secure credential storage through environment variables (recommended) or EditorPrefs.
Environment Variables (Recommended)
Set environment variables before launching Unity:
| Service | Environment Variable |
|---|---|
| DeepL | DEEPL_API_KEY |
| OpenAI | OPENAI_API_KEY |
GOOGLE_TRANSLATE_API_KEY |
macOS/Linux:
export DEEPL_API_KEY="your-deepl-api-key"
export OPENAI_API_KEY="sk-your-openai-api-key"
export GOOGLE_TRANSLATE_API_KEY="your-google-api-key"Windows (PowerShell):
$env:DEEPL_API_KEY = "your-deepl-api-key"
$env:OPENAI_API_KEY = "sk-your-openai-api-key"
$env:GOOGLE_TRANSLATE_API_KEY = "your-google-api-key"EditorPrefs Storage
For convenience during development, you can store credentials in EditorPrefs via the Translation Window:
- Open Tools → KitStack → Lexis → Translation Window
- Go to the Settings tab
- Enter your API keys in the credential fields
- Keys are stored in EditorPrefs (per-machine, not in version control)
Security Warning EditorPrefs storage is convenient but less secure than environment variables. Never commit API keys to version control.
CI/CD Configuration
For automated pipelines and CI/CD environments, configure credentials via environment variables before running Unity.
GitHub Actions:
jobs:
build:
runs-on: ubuntu-latest
env:
DEEPL_API_KEY: ${{ secrets.DEEPL_API_KEY }}
OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }}
GOOGLE_TRANSLATE_API_KEY: ${{ secrets.GOOGLE_TRANSLATE_API_KEY }}
steps:
- uses: actions/checkout@v4
- name: Build Unity Project
uses: game-ci/unity-builder@v4
with:
projectPath: .
targetPlatform: StandaloneWindows64GitLab CI:
build:
variables:
DEEPL_API_KEY: $DEEPL_API_KEY
OPENAI_API_KEY: $OPENAI_API_KEY
GOOGLE_TRANSLATE_API_KEY: $GOOGLE_TRANSLATE_API_KEY
script:
- unity-editor -batchmode -projectPath . -buildTarget Win64Jenkins:
pipeline {
environment {
DEEPL_API_KEY = credentials('deepl-api-key')
OPENAI_API_KEY = credentials('openai-api-key')
GOOGLE_TRANSLATE_API_KEY = credentials('google-translate-api-key')
}
stages {
stage('Build') {
steps {
sh 'unity-editor -batchmode -projectPath . -buildTarget Win64'
}
}
}
}Docker:
FROM unityci/editor:ubuntu-2022.3.0f1-base
ENV DEEPL_API_KEY=""
ENV OPENAI_API_KEY=""
ENV GOOGLE_TRANSLATE_API_KEY=""
# Pass at runtime:
# docker run -e DEEPL_API_KEY=xxx -e OPENAI_API_KEY=xxx ...Credential Lookup Order:
- Environment variables (checked first)
- EditorPrefs (fallback for local development)
Menu: Tools → KitStack → Lexis → Translation Window
The Translation Window provides a unified interface for managing auto-translation.
Tabs
| Tab | Purpose |
|---|---|
| Translate | Translate strings with preview |
| Settings | Configure services and credentials |
| Memory | View and manage translation cache |
Translate Tab
- Service Selection: Choose DeepL, OpenAI, or Google Translate
- Language Selection: Source and target language dropdowns
- Text Input: Enter text to translate
- Preview: See translation result before applying
- Batch Translation: Translate multiple entries at once
Settings Tab
Service Configuration:
- DeepL: API key (Free or Pro), auto-detects endpoint
- OpenAI: API key, model selection (GPT-4o, GPT-4o-mini)
- Google: Cloud Translation API key
Translation Context: Select or create a TranslationContext asset
Quality Settings:
- Enable/disable source quality checking
- Configure AI-powered spell/grammar checking (requires OpenAI key)
Memory Tab
- Statistics: View cache size, entries, hit rate
- Search: Find cached translations
- Clear: Remove cached entries by language or clear all
- Export: Export cache for backup
TranslationContext is a ScriptableObject that provides contextual information to improve translation quality. It's especially powerful with AI-based services like OpenAI, which use the context to generate more accurate, consistent, and tonally appropriate translations.
Create: Right-click → Create → KitStack → Lexis → Translation Context
Why Use TranslationContext?
Without context, translation services treat each string in isolation. This leads to:
- Inconsistent terminology (translating "level" as "stage" sometimes and "tier" other times)
- Wrong tone (formal language in a casual mobile game)
- Incorrect domain assumptions (translating "tank" as a military vehicle instead of a game role)
With a well-configured TranslationContext, AI services understand your project and produce translations that:
- Match your game's tone and style
- Use consistent terminology throughout
- Respect domain-specific meanings
- Preserve brand names and untranslatable terms
Provider Support
| Feature | OpenAI GPT | DeepL | Google Translate |
|---|---|---|---|
| Project Description | ✓ Full support | — | — |
| Genre/Audience | ✓ Full support | — | — |
| Formality | ✓ Full support | ✓ Partial* | — |
| DoNotTranslate | ✓ Full support | — | — |
| Glossary | ✓ Full support | ✓ Via API** | — |
*DeepL supports formality natively for some languages (DE, FR, IT, ES, NL, PL, PT, RU, JA). **DeepL glossaries require separate API setup; Lexis passes glossary terms via context for OpenAI.
Recommendation: For best translation quality, use OpenAI GPT with a well-configured TranslationContext. For cost-effective translations where context is less critical, use DeepL or Google Translate.
Settings
| Property | Description |
|---|---|
ProjectDescription | Brief description of your project (see below for tips) |
Genre | Content genre (Game, App, Website, Documentation) |
TargetAudience | Target audience (Children, Teens, Adults, All Ages) |
Formality | Formality level (Default, Informal, Formal) |
DoNotTranslate | List of terms to preserve untranslated (brand names, etc.) |
Glossary | Term-to-translation mappings for consistent terminology |
Writing a Good Project Description
The ProjectDescription field is the most important setting for AI translation quality. Write 2-4 sentences that describe:
- What your project is - Game, app, website, etc.
- Genre and setting - Fantasy RPG, sci-fi shooter, casual puzzle, business app
- Tone and style - Serious, humorous, formal, casual, epic, lighthearted
- Target audience - Age group, player type
Good Examples:
"A dark fantasy action RPG set in a medieval world overrun by demons.
The tone is serious and dramatic, with occasional dark humor.
Target audience is mature gamers (18+).""A cheerful puzzle game for children ages 6-12 featuring colorful animals.
The tone is friendly, encouraging, and educational.
All text should be simple and easy to understand.""A competitive multiplayer FPS with a near-future military setting.
Communication is tactical and professional.
Target audience is teens and young adults who enjoy esports."Poor Examples:
"My game" // Too vague, provides no useful context"A game where you do stuff" // No genre, tone, or audience informationGlossary Entries
Define consistent translations for domain-specific terms. The Glossary is edited directly in the Unity Inspector:
How to Access:
- Create a TranslationContext asset: Right-click → Create → KitStack → Lexis → Translation Context
- Select the TranslationContext asset in the Project window
- In the Inspector, expand the Glossary section
- Click + to add entries, configure each with:
- Source Term: The English term to match
- Target Term: The desired translation
- Target Language: Language code (e.g., "de") or leave empty for all languages
Example Configuration (Inspector):
| Source Term | Target Term | Target Language |
|---|---|---|
| Health Points | Lebenspunkte | de |
| Health Points | Points de vie | fr |
| Mana | Mana | (empty - keep for all) |
| The Dark Lord | Der Dunkle Fürst | de |
You can also configure the Glossary via code:
var context = ScriptableObject.CreateInstance<TranslationContext>();
// Access glossary (read-only property, configure via Inspector or serialization)
foreach (var entry in context.Glossary)
{
Debug.Log($"{entry.SourceTerm} → {entry.TargetTerm} ({entry.TargetLanguage})");
}When to use Glossary:
- Character names that should be translated (not just transliterated)
- Game-specific terms with established translations
- Technical terms with specific meanings in your domain
- Terms that might be ambiguous without context
When to use DoNotTranslate:
- Brand names (your game title, company name)
- Character names that should stay in English
- Technical identifiers shown to users
- Proper nouns that shouldn't change
Before translation, Lexis can analyze source text for common issues that may affect translation quality.
Detected Issues
| Issue Type | Severity | Example |
|---|---|---|
| Unbalanced placeholders | Error | Hello {name (missing }) |
| Empty placeholders | Error | Hello {}! |
| Encoding issues | Error | Replacement character \uFFFD |
| Multiple spaces | Warning | Hello world |
| Leading/trailing whitespace | Warning | Hello |
| Hardcoded large numbers | Warning | You scored 10000 points |
| URLs in text | Info | Visit https://example.com |
Quality Levels
| Level | Meaning |
|---|---|
| Good | No issues detected |
| Fair | Warnings only |
| Poor | Contains errors |
Usage
using Lexis.Editor.Translation;
var checker = new SourceQualityChecker(() => openAiApiKey);
var result = checker.CheckBasicQuality("Hello {name}!");
if (result.HasIssues)
{
foreach (var issue in result.Issues)
{
Debug.LogWarning($"{issue.Type}: {issue.Description}");
}
}Translation Memory caches translations to reduce API calls and costs. Identical source strings return cached results instantly.
Features
- Automatic caching: All successful translations are cached
- Language-pair aware: Caches are specific to source/target language pairs
- Persistent: Cache survives Unity restarts (stored in Library folder)
- LRU eviction: Oldest entries removed when cache is full (10,000 max entries)
Statistics
using Lexis.Editor.Translation;
var tm = TranslationMemoryManager.Instance;
var stats = tm.GetStats();
Debug.Log($"Entries: {stats.TotalEntries}");
Debug.Log($"Characters: {stats.TotalCharacters}");
Debug.Log($"Language pairs: {stats.LanguagePairs}");
Debug.Log($"Services: {string.Join(", ", stats.Services)}");Cache Management
// Check for cached translation
if (tm.TryGetCachedTranslation("Hello", "en", "de", out string cached))
{
Debug.Log($"Cached: {cached}");
}
// Store a translation
tm.StoreTranslation("Hello", "Hallo", "en", "de", "deepl");
// Clear specific language
int removed = tm.ClearForLanguage("de");
// Clear all
tm.ClearAll();Single Translation
using Lexis.Editor.Translation;
using Lexis.Translation;
// Using the default configured service
var result = await TranslationIntegration.TranslateAsync(
"Hello, world!",
"en",
"de");
if (result.Success)
{
Debug.Log($"Translated: {result.TranslatedText}");
Debug.Log($"Characters: {result.CharacterCount}");
}
else
{
Debug.LogError($"Failed: {result.GetUserFriendlyMessage()}");
}Batch Translation
using Lexis.Editor.Translation;
using Lexis.Translation;
string[] texts = { "Hello", "World", "Welcome" };
var results = await TranslationIntegration.TranslateBatchAsync(
texts,
"en",
"de",
context: myTranslationContext,
progress: new Progress<TranslationProgress>(p =>
{
Debug.Log($"Progress: {p.Completed}/{p.Total}");
}));
for (int i = 0; i < results.Length; i++)
{
if (results[i].Success)
{
Debug.Log($"{texts[i]} → {results[i].TranslatedText}");
}
}Using Specific Services
using Lexis.Translation.Services;
// DeepL
var deepl = new DeepLService(() => Environment.GetEnvironmentVariable("DEEPL_API_KEY"));
var result = await deepl.TranslateAsync("Hello", "en", "de");
// OpenAI with custom model
var openai = new OpenAIService(() => Environment.GetEnvironmentVariable("OPENAI_API_KEY"));
openai.Model = "gpt-4o"; // or "gpt-4o-mini" (default)
var result = await openai.TranslateAsync("Hello", "en", "de", myContext);
// Google Translate
var google = new GoogleTranslateService(() => Environment.GetEnvironmentVariable("GOOGLE_TRANSLATE_API_KEY"));
var result = await google.TranslateAsync("Hello", "en", "de");| Feature | DeepL | OpenAI GPT | Google Translate |
|---|---|---|---|
| Languages | 30+ | All major | 100+ |
| Context-aware | Limited | Excellent | Limited |
| Formality control | Yes | Via prompt | No |
| Batch limit | 50 texts | 1 at a time | 128 texts |
| Cost | Per character | Per token | Per character |
| Best for | European languages | Creative content | Wide coverage |
- Use TranslationContext for AI services to improve quality
- Review translations before committing - machine translation isn't perfect
- Use glossaries for consistent terminology (character names, game terms)
- Check source quality before translating to avoid propagating errors
- Leverage caching - the Translation Memory reduces API costs significantly
- Use batch translation for multiple strings to reduce API overhead
- Set DoNotTranslate for brand names, product names, and untranslatable terms
| Error | Cause | Solution |
|---|---|---|
| "Invalid API key" | Key is incorrect or expired | Verify key in service dashboard |
| "Rate limited" | Too many requests | Wait and retry, or upgrade API plan |
| "Quota exceeded" | Monthly limit reached | Check usage in service dashboard |
| "Unsupported language" | Service doesn't support language | Try a different service |
| "Network error" | Connection failed | Check internet connection |
