Pricing comparison
Real pricing based on monthly character volume. All prices in USD.
| Volume | Google Translate | DeepL | Langbly | You Save |
|---|---|---|---|---|
| 500K chars/mo | $0 | Check live plan | $0 | Both included/evaluation |
| 1M chars/mo | $10 | Check live plan | $2.50 | 75% vs Google |
| 5M chars/mo | $90 | Check live plan | $22.50 | 75% vs Google |
| 25M chars/mo | $490 | Check live plan | $122.50 | 75% vs Google |
| 100M chars/mo | $1,990 | Above Growth limit | $497.50 | 75% vs Google |
Feature comparison
| Feature | Google Translate | DeepL | Langbly |
|---|---|---|---|
| Official .NET SDK | Google.Cloud.Translation.V2 | DeepL.net | Plain HTTP (no NuGet needed) |
| Works with HttpClient only | |||
| Auth method | Service account JSON | API key header | Bearer token header |
| Extra dependencies | Google.Apis stack | 1 package | None |
| System.Text.Json friendly | Wrapped in SDK types | Wrapped in SDK types | Flat JSON, maps to records |
| async/await support | |||
| IHttpClientFactory typed client | Not applicable | Not applicable | Standard pattern |
| Polly / resilience handler | SDK internal | SDK internal | Your policy, your rules |
| Retry-After honoured on 429 | |||
| Google v2 compatible | |||
| Languages | 100+ | 30+ | 100+ |
| Published usage rate | $20/1M after 500K | Check live Growth plan | $5/1M after 500K |
| Included/evaluation usage | 500K/mo | Developer: 1M total | 500K/mo |
| Translation quality | NMT | NMT + AI | Next-gen AI |
| Context-aware | Limited |
Translating text in C#: three APIs side by side
Most .NET projects do not need a vendor SDK to call a translation API. They need one HTTP call, a couple of records, and System.Text.Json. Here is what each option actually looks like in a modern C# project.
Google Translate (Google.Cloud.Translation.V2):
// dotnet add package Google.Cloud.Translation.V2
using Google.Cloud.Translation.V2;
// Requires a downloaded service account JSON file on disk
Environment.SetEnvironmentVariable("GOOGLE_APPLICATION_CREDENTIALS", "service-account.json");
TranslationClient client = await TranslationClient.CreateAsync();
TranslationResult result = await client.TranslateTextAsync("Hello world", "nl");
Console.WriteLine(result.TranslatedText);
DeepL (DeepL.net):
// dotnet add package DeepL.net
using DeepL;
var translator = new Translator(Environment.GetEnvironmentVariable("DEEPL_AUTH_KEY")!);
var result = await translator.TranslateTextAsync("Hello world", null, LanguageCode.Dutch);
Console.WriteLine(result.Text);
Langbly (HttpClient, no package):
using System.Net.Http.Headers;
using System.Net.Http.Json;
using System.Text.Json.Serialization;
public sealed record TranslateRequest(
[property: JsonPropertyName("q")] string Q,
[property: JsonPropertyName("target")] string Target,
[property: JsonPropertyName("source")] string? Source = null,
[property: JsonPropertyName("format")] string Format = "text");
public sealed record Translation(
[property: JsonPropertyName("translatedText")] string TranslatedText,
[property: JsonPropertyName("detectedSourceLanguage")] string? DetectedSourceLanguage);
public sealed record TranslateData(
[property: JsonPropertyName("translations")] IReadOnlyList<Translation> Translations);
public sealed record TranslateResponse(
[property: JsonPropertyName("data")] TranslateData Data);
using var http = new HttpClient { BaseAddress = new Uri("https://api.langbly.com/") };
http.DefaultRequestHeaders.Authorization =
new AuthenticationHeaderValue("Bearer", Environment.GetEnvironmentVariable("LANGBLY_API_KEY"));
using var response = await http.PostAsJsonAsync(
"language/translate/v2",
new TranslateRequest("Hello world", Target: "nl"));
response.EnsureSuccessStatusCode();
var payload = await response.Content.ReadFromJsonAsync<TranslateResponse>();
Console.WriteLine(payload!.Data.Translations[0].TranslatedText); // Hallo wereld
Console.WriteLine(payload.Data.Translations[0].DetectedSourceLanguage); // en
The request body is small on purpose: q and target are required, source and format are optional, and format accepts "text" or "html". Send "html" when you are translating Razor fragments or CMS content and you want tags left intact.
Note the response envelope. It is the Google Translate v2 shape, byte for byte, because Langbly is a drop-in replacement for that API. If you already have TranslationResponse DTOs from a Google integration, they deserialize against Langbly without a single edit. Swapping providers is a base address and a header.
There is no official Langbly NuGet package. Official SDKs cover Python, JavaScript/TypeScript, and PHP. In .NET that turns out to be a non-issue: HttpClient plus two records is less code than wiring up a vendor SDK, and you keep full control over serialization, timeouts, logging, and retries.
ASP.NET Core: a typed client with IHttpClientFactory
For anything beyond a console script, register a typed client. You get connection pooling, a single place for the base address and auth header, and a seam you can mock in tests.
The client:
public interface ITranslator
{
Task<string> TranslateAsync(string text, string target, CancellationToken ct = default);
}
public sealed class LangblyTranslator(HttpClient http, ILogger<LangblyTranslator> logger) : ITranslator
{
public async Task<string> TranslateAsync(string text, string target, CancellationToken ct = default)
{
using var response = await http.PostAsJsonAsync(
"language/translate/v2",
new TranslateRequest(text, target),
ct);
if (response.StatusCode is System.Net.HttpStatusCode.TooManyRequests)
{
var retryAfter = response.Headers.RetryAfter?.Delta;
logger.LogWarning("Rate limited, retry after {RetryAfter}", retryAfter);
}
response.EnsureSuccessStatusCode();
var payload = await response.Content.ReadFromJsonAsync<TranslateResponse>(ct);
return payload?.Data.Translations.FirstOrDefault()?.TranslatedText
?? throw new InvalidOperationException("Translation response contained no translations.");
}
}
Registration in Program.cs:
// dotnet user-secrets set "Langbly:ApiKey" "your-api-key"
// dotnet add package Microsoft.Extensions.Http.Resilience
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddHttpClient<ITranslator, LangblyTranslator>((sp, http) =>
{
var config = sp.GetRequiredService<IConfiguration>();
http.BaseAddress = new Uri("https://api.langbly.com/");
http.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue(
"Bearer",
config["Langbly:ApiKey"] ?? throw new InvalidOperationException("Langbly:ApiKey is not configured."));
http.Timeout = TimeSpan.FromSeconds(30);
})
.AddStandardResilienceHandler(); // Polly-backed retries, timeouts, circuit breaker
var app = builder.Build();
app.MapPost("/translate", async (TranslateBody body, ITranslator translator, CancellationToken ct) =>
Results.Ok(new { translated = await translator.TranslateAsync(body.Text, body.Target, ct) }));
app.Run();
record TranslateBody(string Text, string Target);
If you are on an older target framework without Microsoft.Extensions.Http.Resilience, a hand-rolled Polly policy does the same job:
.AddPolicyHandler(HttpPolicyExtensions
.HandleTransientHttpError()
.OrResult(r => (int)r.StatusCode == 429)
.WaitAndRetryAsync(3, attempt => TimeSpan.FromSeconds(Math.Pow(2, attempt))));
Two things worth doing while you are in here. Keep the key in user secrets locally and in your platform's secret store in production, never in appsettings.json. And turn on <Nullable>enable</Nullable> in the csproj so the compiler tells you where a missing translation can slip through as null.
Compare that to the Google SDK, which wants a service account JSON file mounted somewhere your app can read it, plus a GCP project with the Translation API enabled. It works, but it is a lot of ceremony for one POST request.
Practical .NET workflow: translating .resx resource files
Localization in .NET still runs on .resx files, and translating them by hand is nobody's favourite afternoon. Since .resx is just XML, a small utility can generate every satellite file from your neutral resources.
using System.Xml.Linq;
// Reads Strings.resx and writes Strings.nl.resx, Strings.de.resx, and so on.
static async Task TranslateResxAsync(
ITranslator translator,
string sourcePath,
string targetLanguage,
CancellationToken ct = default)
{
var doc = XDocument.Load(sourcePath);
var entries = doc.Root!
.Elements("data")
.Where(d => d.Attribute("type") is null && d.Attribute("mimetype") is null) // skip images and blobs
.ToList();
foreach (var entry in entries)
{
var value = entry.Element("value");
if (value is null || string.IsNullOrWhiteSpace(value.Value))
continue;
value.Value = await translator.TranslateAsync(value.Value, targetLanguage, ct);
}
var outputPath = Path.ChangeExtension(sourcePath, $"{targetLanguage}.resx");
doc.Save(outputPath);
Console.WriteLine($"{outputPath}: {entries.Count} strings translated");
}
// Usage
foreach (var lang in new[] { "nl", "de", "fr", "es", "ja" })
await TranslateResxAsync(translator, "Resources/Strings.resx", lang);
Two details that save you a bug report. First, filter out entries with a type or mimetype attribute, because those are icons and serialized objects, not user-facing text. Second, if your strings use composite format placeholders like {0}, add a check that the placeholder count matches before you save. A one-line assertion in the loop is cheaper than a FormatException in production.
For bigger resource sets, batch the calls with Parallel.ForEachAsync and a sensible MaxDegreeOfParallelism, and cache results by source string so repeated labels like "Save" are only paid for once. At $5 per million characters a full resource set for five languages usually lands inside the 500K characters/month free tier anyway.
The same pattern works for JSON localization files, Blazor IStringLocalizer resources, and XLIFF exports. Swap XDocument for whatever parser matches your format and keep the rest.
Frequently asked questions
What is the best translation API for C# and .NET?
It depends on what you optimise for. Google Translate has the widest language coverage but the heaviest setup, since it needs a GCP project and a service account JSON file. DeepL has a clean .NET package and strong European language quality. Langbly is the cheapest of the three at $5 per million characters, is context-aware, and needs no package at all because it works directly with HttpClient.
Is there an official Langbly SDK for .NET?
No. Official Langbly SDKs currently cover Python, JavaScript/TypeScript, and PHP. In C# you call the REST endpoint directly with HttpClient and System.Text.Json, which takes two record types and about ten lines. Register it as a typed client with IHttpClientFactory and you get pooling, resilience, and testability for free.
How do I translate text in C# without a third-party package?
POST to https://api.langbly.com/language/translate/v2 with an Authorization: Bearer header and a JSON body of {"q": "Hello world", "target": "nl"}. Use PostAsJsonAsync and ReadFromJsonAsync from System.Net.Http.Json, and map the response to a small record hierarchy. No NuGet package required beyond what ships with the framework.
Can I switch from the Google Translate API to Langbly in an existing .NET app?
Yes. Langbly is a drop-in replacement for the Google Translate v2 API and returns the identical response shape, so your existing DTOs keep working. If you were using the Google.Cloud.Translation.V2 package, replace it with a typed HttpClient, point the base address at api.langbly.com, and send your key as a Bearer token. There is no service account file to manage afterwards.
How much does a translation API cost for a .NET project?
Google Cloud Translation includes the first 500,000 characters each month, then charges $20 per million for standard NMT. Langbly includes the first 500,000 input characters and charges $5 per million additional input characters. At 5M total characters the current totals are $90 and $22.50. DeepL requires its live Growth subscription and overage for a fair comparison.