Table of Contents

Translate and Localize Content

LM-Kit.NET provides built-in translation that detects the source language automatically and translates to any supported target language. Everything runs locally, so sensitive documents (legal contracts, medical records, internal communications) never leave your infrastructure. This tutorial builds a working translation tool with language detection, streaming output, and batch processing.


Why Local Translation Matters

Two enterprise problems that on-device translation solves:

  1. Sensitive document translation. Legal contracts, patent filings, medical records, and M&A documents require translation but cannot be sent to cloud services. Cloud translation providers may retain submitted text to improve their models. Local translation eliminates that data exposure entirely.
  2. Multilingual support without per-word costs. Cloud translation APIs charge per character. Translating product catalogs, help centers, and marketing content across 10+ languages adds up quickly. A local model handles unlimited volume at fixed hardware cost.

Prerequisites

Requirement Minimum
.NET SDK 8.0+
VRAM 4+ GB
Disk ~3 GB free for model download

Use a multilingual model. qwen3:4b and gemma3:4b both support 30+ languages.


Step 1: Create the Project

dotnet new console -n TranslationQuickstart
cd TranslationQuickstart
dotnet add package LM-Kit.NET

Step 2: Basic Translation with Language Detection

This program detects the source language and translates to a target language of your choice.

using System.Text;
using LMKit.Model;
using LMKit.TextGeneration;
using LMKit.TextGeneration.Chat;
using LMKit.Translation;

LMKit.Licensing.LicenseManager.SetLicenseKey("");

Console.InputEncoding = Encoding.UTF8;
Console.OutputEncoding = Encoding.UTF8;

// ──────────────────────────────────────
// 1. Load a multilingual model
// ──────────────────────────────────────
Console.WriteLine("Loading model...");
using LM model = LM.LoadFromModelID("qwen3:4b",
    downloadingProgress: (_, len, read) =>
    {
        if (len.HasValue) Console.Write($"\r  Downloading: {(double)read / len.Value * 100:F1}%   ");
        return true;
    },
    loadingProgress: p => { Console.Write($"\r  Loading: {p * 100:F0}%   "); return true; });
Console.WriteLine("\n");

// ──────────────────────────────────────
// 2. Create translator with streaming
// ──────────────────────────────────────
var translator = new TextTranslation(model);

translator.AfterTextCompletion += (_, e) =>
{
    if (e.SegmentType == TextSegmentType.UserVisible)
        Console.Write(e.Text);
};

// ──────────────────────────────────────
// 3. Translation loop
// ──────────────────────────────────────
Console.WriteLine("Enter text to translate (or 'quit' to exit).");
Console.WriteLine("Target language defaults to English. Type 'lang:French' to change.\n");

Language targetLanguage = Language.English;

while (true)
{
    Console.ForegroundColor = ConsoleColor.Green;
    Console.Write($"[→ {targetLanguage}] Text: ");
    Console.ResetColor();

    string? input = Console.ReadLine();
    if (string.IsNullOrWhiteSpace(input) || input.Equals("quit", StringComparison.OrdinalIgnoreCase))
        break;

    // Allow changing target language
    if (input.StartsWith("lang:", StringComparison.OrdinalIgnoreCase))
    {
        string langName = input.Substring(5).Trim();
        if (Enum.TryParse<Language>(langName, ignoreCase: true, out Language parsed))
        {
            targetLanguage = parsed;
            Console.WriteLine($"  Target language set to {targetLanguage}\n");
        }
        else
        {
            Console.WriteLine($"  Unknown language: {langName}\n");
        }
        continue;
    }

    // Detect source language
    Language detected = translator.DetectLanguage(input);
    Console.ForegroundColor = ConsoleColor.DarkGray;
    Console.WriteLine($"  Detected: {detected} ({translator.Confidence:P0})");
    Console.ResetColor();

    // Translate
    Console.ForegroundColor = ConsoleColor.Cyan;
    Console.Write("  Translation: ");
    Console.ResetColor();

    string result = translator.Translate(input, targetLanguage);
    Console.WriteLine("\n");
}

Step 3: Translating Between Specific Language Pairs

Language detection is automatic, but you can control both source detection and target:

using System.Text;
using LMKit.Model;
using LMKit.TextGeneration;
using LMKit.TextGeneration.Chat;
using LMKit.Translation;

LMKit.Licensing.LicenseManager.SetLicenseKey("");

Console.InputEncoding = Encoding.UTF8;
Console.OutputEncoding = Encoding.UTF8;

// ──────────────────────────────────────
// 1. Load a multilingual model
// ──────────────────────────────────────
Console.WriteLine("Loading model...");
using LM model = LM.LoadFromModelID("qwen3:4b",
    downloadingProgress: (_, len, read) =>
    {
        if (len.HasValue) Console.Write($"\r  Downloading: {(double)read / len.Value * 100:F1}%   ");
        return true;
    },
    loadingProgress: p => { Console.Write($"\r  Loading: {p * 100:F0}%   "); return true; });
Console.WriteLine("\n");

// ──────────────────────────────────────
// 2. Create translator with streaming
// ──────────────────────────────────────
var translator = new TextTranslation(model);

translator.AfterTextCompletion += (_, e) =>
{
    if (e.SegmentType == TextSegmentType.UserVisible)
        Console.Write(e.Text);
};

// ──────────────────────────────────────
// 3. Translation loop
// ──────────────────────────────────────
Console.WriteLine("Enter text to translate (or 'quit' to exit).");
Console.WriteLine("Target language defaults to English. Type 'lang:French' to change.\n");

Language targetLanguage = Language.English;

while (true)
{
    Console.ForegroundColor = ConsoleColor.Green;
    Console.Write($"[→ {targetLanguage}] Text: ");
    Console.ResetColor();

    string? input = Console.ReadLine();
    if (string.IsNullOrWhiteSpace(input) || input.Equals("quit", StringComparison.OrdinalIgnoreCase))
        break;

    // Allow changing target language
    if (input.StartsWith("lang:", StringComparison.OrdinalIgnoreCase))
    {
        string langName = input.Substring(5).Trim();
        if (Enum.TryParse<Language>(langName, ignoreCase: true, out Language parsed))
        {
            targetLanguage = parsed;
            Console.WriteLine($"  Target language set to {targetLanguage}\n");
        }
        else
        {
            Console.WriteLine($"  Unknown language: {langName}\n");
        }
        continue;
    }

    // Detect source language
    Language detected = translator.DetectLanguage(input);
    Console.ForegroundColor = ConsoleColor.DarkGray;
    Console.WriteLine($"  Detected: {detected} ({translator.Confidence:P0})");
    Console.ResetColor();

    // Translate
    Console.ForegroundColor = ConsoleColor.Cyan;
    Console.Write("  Translation: ");
    Console.ResetColor();

    string result = translator.Translate(input, targetLanguage);
    Console.WriteLine("\n");
}

// Detect among a restricted set of languages (faster, more accurate)
string text = "Bonjour le monde";
Language detected2 = translator.DetectLanguage(text,
    new[] { Language.English, Language.French, Language.Spanish, Language.German });

// Translate to a specific language
string french = translator.Translate(text, Language.French);
string japanese = translator.Translate(text, Language.Japanese);
string portuguese = translator.Translate(text, Language.Portuguese);

Step 4: Batch Translation

Translate multiple texts efficiently:

using System.Text;
using LMKit.Model;
using LMKit.TextGeneration;
using LMKit.TextGeneration.Chat;
using LMKit.Translation;

LMKit.Licensing.LicenseManager.SetLicenseKey("");

Console.InputEncoding = Encoding.UTF8;
Console.OutputEncoding = Encoding.UTF8;

// ──────────────────────────────────────
// 1. Load a multilingual model
// ──────────────────────────────────────
Console.WriteLine("Loading model...");
using LM model = LM.LoadFromModelID("qwen3:4b",
    downloadingProgress: (_, len, read) =>
    {
        if (len.HasValue) Console.Write($"\r  Downloading: {(double)read / len.Value * 100:F1}%   ");
        return true;
    },
    loadingProgress: p => { Console.Write($"\r  Loading: {p * 100:F0}%   "); return true; });
Console.WriteLine("\n");

// ──────────────────────────────────────
// 2. Create translator with streaming
// ──────────────────────────────────────
var translator = new TextTranslation(model);

translator.AfterTextCompletion += (_, e) =>
{
    if (e.SegmentType == TextSegmentType.UserVisible)
        Console.Write(e.Text);
};

// ──────────────────────────────────────
// 3. Translation loop
// ──────────────────────────────────────
Console.WriteLine("Enter text to translate (or 'quit' to exit).");
Console.WriteLine("Target language defaults to English. Type 'lang:French' to change.\n");

Language targetLanguage = Language.English;

while (true)
{
    Console.ForegroundColor = ConsoleColor.Green;
    Console.Write($"[→ {targetLanguage}] Text: ");
    Console.ResetColor();

    string? input = Console.ReadLine();
    if (string.IsNullOrWhiteSpace(input) || input.Equals("quit", StringComparison.OrdinalIgnoreCase))
        break;

    // Allow changing target language
    if (input.StartsWith("lang:", StringComparison.OrdinalIgnoreCase))
    {
        string langName = input.Substring(5).Trim();
        if (Enum.TryParse<Language>(langName, ignoreCase: true, out Language parsed))
        {
            targetLanguage = parsed;
            Console.WriteLine($"  Target language set to {targetLanguage}\n");
        }
        else
        {
            Console.WriteLine($"  Unknown language: {langName}\n");
        }
        continue;
    }

    // Detect source language
    Language detected = translator.DetectLanguage(input);
    Console.ForegroundColor = ConsoleColor.DarkGray;
    Console.WriteLine($"  Detected: {detected} ({translator.Confidence:P0})");
    Console.ResetColor();

    // Translate
    Console.ForegroundColor = ConsoleColor.Cyan;
    Console.Write("  Translation: ");
    Console.ResetColor();

    string result = translator.Translate(input, targetLanguage);
    Console.WriteLine("\n");
}

string[] sourceTexts =
{
    "Welcome to our product documentation.",
    "Click the Settings icon to configure your account.",
    "For support, contact our help desk at support@example.com."
};

Language[] targets = { Language.French, Language.Spanish, Language.German };

foreach (Language target in targets)
{
    Console.WriteLine($"\n=== {target} ===");

    foreach (string text in sourceTexts)
    {
        string translated = translator.Translate(text, target);
        Console.WriteLine($"  {translated}");
    }
}

Step 5: Language Detection for Documents

Detect the language of files (PDFs, images with text) using attachments:

using LMKit.Data;

// Detect language from a PDF
var doc = new Attachment("contract.pdf");
Language docLanguage = translator.DetectLanguage(doc);
Console.WriteLine($"Document language: {docLanguage} ({translator.Confidence:P0})");

Supported Languages

LM-Kit.NET supports these languages through the Language enum:

Language Enum Value Language Enum Value
Arabic Language.Arabic Japanese Language.Japanese
Chinese (Simplified) Language.ChineseSimplified Korean Language.Korean
Chinese (Traditional) Language.ChineseTraditional Portuguese Language.Portuguese
Dutch Language.Dutch Russian Language.Russian
English Language.English Spanish Language.Spanish
French Language.French Hindi Language.Hindi
German Language.German Italian Language.Italian

The actual language quality depends on the model. qwen3:4b excels at CJK languages. gemma3:4b works well across European languages.


Model Selection for Translation

Model ID VRAM Multilingual Strength Best For
qwen3:4b ~3.5 GB Excellent (CJK + European) General multilingual use (recommended)
gemma3:4b ~3.5 GB Very good (European) European language pairs
qwen3:8b ~6 GB Excellent Highest translation quality
gemma3:12b ~8 GB Excellent Complex, nuanced texts

For most translation tasks, qwen3:4b offers the best balance of quality, speed, and language coverage.


Common Issues

Problem Cause Fix
Wrong language detected Short text or ambiguous content Restrict detection to expected languages with the languages parameter
Translation is too literal Model preserving source structure Use a larger model (8B+) for more natural phrasing
Missing characters in output Console encoding wrong Ensure Console.OutputEncoding = Encoding.UTF8 is set
Slow translation for long texts Full document processed at once Split into paragraphs and translate individually
Mixed-language input confused Text contains multiple languages Translate sentence by sentence for mixed-language content

Next Steps

Share