Rekey 1.3.0

dotnet add package Rekey --version 1.3.0
                    
NuGet\Install-Package Rekey -Version 1.3.0
                    
This command is intended to be used within the Package Manager Console in Visual Studio, as it uses the NuGet module's version of Install-Package.
<PackageReference Include="Rekey" Version="1.3.0" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="Rekey" Version="1.3.0" />
                    
Directory.Packages.props
<PackageReference Include="Rekey" />
                    
Project file
For projects that support Central Package Management (CPM), copy this XML node into the solution Directory.Packages.props file to version the package.
paket add Rekey --version 1.3.0
                    
#r "nuget: Rekey, 1.3.0"
                    
#r directive can be used in F# Interactive and Polyglot Notebooks. Copy this into the interactive tool or source code of the script to reference the package.
#:package Rekey@1.3.0
                    
#:package directive can be used in C# file-based apps starting in .NET 10 preview 4. Copy this into a .cs file before any lines of code to reference the package.
#addin nuget:?package=Rekey&version=1.3.0
                    
Install as a Cake Addin
#tool nuget:?package=Rekey&version=1.3.0
                    
Install as a Cake Tool

Rekey

CI NuGet Downloads License

Your users type ghbdsn when they mean привіт — and your search returns nothing.

Rekey detects text typed in the wrong keyboard layout and rekeys it into the intended word: a server-side Punto Switcher for .NET. One line of code, no configuration, no external services.

var rekey = new Rekey();

rekey.Correct("ghbdsn");     // → "привіт"   (Ukrainian typed with an English layout)
rekey.Correct("xfcnbwf");    // → "частица"  (Russian typed with an English layout)
rekey.Correct("руддщ");      // → "hello"    (English typed with a Cyrillic layout)
rekey.Correct("beautiful");  // → "beautiful" (valid text passes through untouched)

▶ Try it live in your browser — the library running as WebAssembly.

Why you want this

Anyone who types in two layouts does it every day: they forget to switch, type rdbnrb instead of квитки into your search box, get zero results, and leave. If your audience uses Ukrainian or Russian alongside English, a real share of your searches, filters, and autocompletes silently fail.

Rekey fixes that on the server, per request, with no UI changes:

  • Fast — ~880,000 words/sec on a single thread; ~15 ms one-time load
  • 🪶 Self-contained — no dependencies, no network calls, all dictionaries embedded
  • 🧵 Thread-safe and stateless — register one singleton and forget it
  • 🌍 English ↔ Ukrainian, Russian, and Belarusian (BE is opt-in), both directions, mixed text, digits and case preserved
  • 🛡️ Safe by defaultCorrect() returns the input unchanged unless the switched variant is actually a plausible word, and URLs, e-mails, camelCase identifiers, and mixed-script tokens are never touched — so you can run it on every query
  • 🎚️ Confidence score — decide when to fix silently and when to show "did you mean …?"
  • ⚖️ Clean licensing — Apache-2.0 code; n-gram data generated from CC0/CC BY corpora (see THIRD-PARTY-NOTICES.md) — safe for commercial use

Install

dotnet add package Rekey

Works everywhere: .NET Framework 4.6.2+, .NET Core 2.0+, .NET 5+ (via netstandard2.0), with optimized builds for .NET 8 and .NET 10.

Usage

using RekeyNet;

// DI (recommended) — stateless, loads dictionaries once:
builder.Services.AddSingleton<Rekey>();

// Or without DI:
string s = Rekey.Default.Correct("ghbdsn");   // "привіт"

When you need details, Analyze returns everything:

RekeyResult result = rekey.Analyze("ghbdsn");
result.Text;          // "привіт"  — best text, never null
result.WasCorrected;  // true      — a wrong layout was detected
result.Corrected;     // "привіт"  — null when no switch was needed
result.Original;      // "ghbdsn"
result.Words;         // ["привіт"]
result.Confidence;    // 0.9 — heuristic tiers: 1.0 untouched · 0.95 curated exception ·
                      // 0.9 known word · 0.8 plausible switch · 0.55 ambiguous tie

RekeyResult converts implicitly to string (yields Text).

Don't need one of the languages? Configure the set — and the priority — with options:

// Ukrainian-only product: Russian corrections never appear
var rekey = new Rekey(new RekeyOptions { Languages = [Lang.En, Lang.Uk] });

// Or keep all languages but prefer Ukrainian on ambiguity:
var rekey = new Rekey(new RekeyOptions { Languages = [Lang.En, Lang.Uk, Lang.Ru] });

// Belarusian is available as an opt-in (experimental):
var rekey = new Rekey(new RekeyOptions { Languages = [Lang.En, Lang.Be] });

Recipe: search that survives the wrong layout

Don't replace the user's query — OR the original and corrected variants, so you match either. With PostgreSQL full-text search:

var result = rekey.Analyze(q);
var query = string.Join(" & ", terms.Select(t => $"{t}:*"));

if (result.WasCorrected)
{
    var corrected = Regex.Split(result.Corrected!, @"\W+")
        .Where(t => !string.IsNullOrWhiteSpace(t))
        .Select(t => $"{t.ToLowerInvariant()}:*");
    query = $"({query}) | ({string.Join(" & ", corrected)})";
}
// "(ghbdsn:*) | (привіт:*)" — matches either spelling

The same pattern works for Elasticsearch, SQL LIKE, or any autocomplete backend.

How it works

Rekey carries, per language, a blacklist of letter combinations ("n-grams") that never occur in real words — e.g. no Ukrainian word contains certain consonant clusters. A token is a plausible word in language L if it has a vowel and none of its n-grams are blacklisted. For each token Rekey "retypes" the keystrokes into the other layout and keeps the variant that looks like a real word.

One subtlety: the Russian and Ukrainian layouts differ only on four keys (s ] ' ` → ы/ъ/э/ё vs і/ї/є/ґ), so a wrong-layout token often produces a plausible word in both languages (ghbdsnпривыт/привіт). For those ties Rekey embeds two compact lists of known words containing the layout-specific letters and picks the real word.

The Ukrainian dictionaries are generated from ~378M tokens of real-world text (ParaCrawl, CC0 + UA-GEC, CC BY 4.0) by a fully reproducible tool in tools/Rekey.DictGen — measured false-positive rate on real Ukrainian words is 1.3%.

Current limitations

  • Languages: EN/RU/UK by default plus opt-in Belarusian. The n-gram approach ports cleanly to other non-Latin-script languages (Bulgarian, Greek, Hebrew, …) — open an issue if you need one.
  • Very short tokens (1–2 letters) are inherently ambiguous; use new Rekey(minWordLength) to leave them untouched.
  • Smart filtering recognizes URLs, e-mails, camelCase, and mixed-script tokens, but not high-entropy strings like passwords or SKUs; disable it with new RekeyOptions { SmartFiltering = false } if it gets in your way.

Build & test

dotnet build -c Release
dotnet test

Credits & license

A C# port of the Java library blizznets/langchecker (Apache-2.0), extended with Ukrainian support, RU/UK disambiguation, and regenerated dictionaries. Data sources: ParaCrawl (CC0 1.0), UA-GEC (CC BY 4.0) — details in THIRD-PARTY-NOTICES.md.

Licensed under Apache-2.0.

Product Compatible and additional computed target framework versions.
.NET net5.0 was computed.  net5.0-windows was computed.  net6.0 was computed.  net6.0-android was computed.  net6.0-ios was computed.  net6.0-maccatalyst was computed.  net6.0-macos was computed.  net6.0-tvos was computed.  net6.0-windows was computed.  net7.0 was computed.  net7.0-android was computed.  net7.0-ios was computed.  net7.0-maccatalyst was computed.  net7.0-macos was computed.  net7.0-tvos was computed.  net7.0-windows was computed.  net8.0 is compatible.  net8.0-android was computed.  net8.0-browser was computed.  net8.0-ios was computed.  net8.0-maccatalyst was computed.  net8.0-macos was computed.  net8.0-tvos was computed.  net8.0-windows was computed.  net9.0 was computed.  net9.0-android was computed.  net9.0-browser was computed.  net9.0-ios was computed.  net9.0-maccatalyst was computed.  net9.0-macos was computed.  net9.0-tvos was computed.  net9.0-windows was computed.  net10.0 is compatible.  net10.0-android was computed.  net10.0-browser was computed.  net10.0-ios was computed.  net10.0-maccatalyst was computed.  net10.0-macos was computed.  net10.0-tvos was computed.  net10.0-windows was computed. 
.NET Core netcoreapp2.0 was computed.  netcoreapp2.1 was computed.  netcoreapp2.2 was computed.  netcoreapp3.0 was computed.  netcoreapp3.1 was computed. 
.NET Standard netstandard2.0 is compatible.  netstandard2.1 was computed. 
.NET Framework net461 was computed.  net462 was computed.  net463 was computed.  net47 was computed.  net471 was computed.  net472 was computed.  net48 was computed.  net481 was computed. 
MonoAndroid monoandroid was computed. 
MonoMac monomac was computed. 
MonoTouch monotouch was computed. 
Tizen tizen40 was computed.  tizen60 was computed. 
Xamarin.iOS xamarinios was computed. 
Xamarin.Mac xamarinmac was computed. 
Xamarin.TVOS xamarintvos was computed. 
Xamarin.WatchOS xamarinwatchos was computed. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.
  • .NETStandard 2.0

    • No dependencies.
  • net10.0

    • No dependencies.
  • net8.0

    • No dependencies.

NuGet packages

This package is not used by any NuGet packages.

GitHub repositories

This package is not used by any popular GitHub repositories.

Version Downloads Last Updated
1.3.0 94 7/12/2026
1.2.0 82 7/12/2026
1.1.0 81 7/12/2026
1.0.1 94 7/11/2026
1.0.0 98 7/11/2026