Skip to content

Made for translators

Unpack an Unreal Engine game into a table you can translate.

UEExtractor turns .pak and .utoc containers into a locres CSV, and back again. Built for translators, not for engine veterans.

One pass, whole localization folder
UEExtractor.exe "C:\Games\Wuthering Waves" D:\out --path=Wuthering_Waves/Content/Localization
engineUE4_27, read from Client.exe
keynot required
scan3 of 1 284 assets under Localization

Game_Items.locres, 2 rows
Game_Quest.locres, 1 284 rows
Game_TextStringTable, 612 rows

wroteD:\out\Wuthering Waves_locres.csv
rows1 898, none merged from a previous run

Detected automatically from the folder or executable name

Neverness To Everness Ash Echoes Wuthering Waves inZOI Marvel Rivals Dead by Daylight FragPunk Infinity Nikki Snowbreak: Containment Zone

One archive in, one table out

Strings are collected from every asset in the archive, and from the .locres when the game ships one, into the same three column file. Games with no .locres at all, or with a stale one, are the normal case rather than the exception.

Strings are gathered from every asset

The archive is walked asset by asset and the localization is pulled out of whatever holds it. A compiled .locres is one place it can live, and by no means the only one.

  • DataTablerows and rich text
  • StringTablenamespaced keys
  • TextPropertyblueprint strings
  • .locreswhen the game ships one

Nothing to configure

Engine version and game type are read from the main executable. Nine titles have their own pak layout, index offset and encryption already wired in.

Updates keep your work

Run the extraction again after a patch. New rows are added, the Translation column you filled in is written back, and rows the game dropped stay put.

Translation is built in

Hand the CSV to OpenRouter, or to any local server that speaks the OpenAI chat API. Batched, resumable, and parallelisable when the model can keep up.

From folder to locres

Four moves, and the last one hands you back a file the game will actually load.

Drop the folder

Drag the game directory onto UEExtractor.exe, or pass the path as the first argument. A folder in, a CSV out.

Let it read the container

aes.txt and the .usmap are picked up from the game folder. The engine version comes from the executable.

Fill the Translation column

Translate in whatever editor you like, or let the built-in pass do it through OpenRouter or a model on your own machine.

Send it back

Point the tool at the CSV and it writes the .locres. Patch mode keeps the original key order, so every lookup still lands.

Command shapes

Four ways you normally drive the tool. Everything else is a flag sitting on top of one of them.

Extract to a single CSV
UEExtractor.exe "C:\Games\Wuthering Waves" "D:\out" --path=Wuthering_Waves/Content/Localization
engine UE4_27 read from Client.exe
key not required
scan 3 assets matched the path filter Game_Items 2 rows
Game_Quest 1 284 rows
Game_Text 612 rows wrote D:\out\Wuthering Waves_locres.csv 1 898 rows

Two files you may need to place

Most games need nothing at all. These are the two cases where you do, and both are a file drop.

UE4 before ZenLoader

Maybe an AES key

  • aes.txt
  • --aes=0x...
  • Check whether the game even needs a key. Plenty of them ship archives in the clear, and then this whole step disappears.
  • If it does, aes.txt goes in the main game folder holding one line: a 32 character hex key.
  • The key starts with 0x when you pass it as an argument instead.
  • No key to hand? --aes:auto looks through the game resources and writes aes.txt for you.
UE4 and UE5 with ZenLoader

And the .usmap on top

  • *.usmap
  • ZenLoader titles need a mapping file before the archives will open at all. Do the AES step first if it applies.
  • The file is usually already published. Nexus Mods and the modding forums are the places people look.
  • No luck? Dump it yourself with UE4SS-Experimental, or point straight at a path with --mapping.
  • Drop it in the game folder or any subfolder. The tool finds it without being told where.
Still nothing?
A container that opened is not the problem here. UEExtractor mounts .pak and .utoc on its own, so if the text is missing from the CSV, it either sits somewhere the asset scan does not reach, or it was dropped on purpose.
Blueprint text
Strings compiled into Blueprint bytecode are stored differently, and UEExtractor does not read Kismet script constants. KissE does, and writes them back into the asset. If the words you are chasing only ever show up inside a Blueprint graph, that is where to look.
Duplicate keys
When one key carries two different source strings, those lines are left out of the CSV, because there is no honest way to pick a winner. KissE edits the asset in place, so the choice stays yours.

Games with custom handling

Detection matches on the folder name and the main .exe, ignoring spaces, hyphens and underscores, so the version flag is not needed.

Neverness To EvernessCustom pak index offset, encrypted .locres
Ash EchoesCustom file provider
Wuthering WavesPartially encrypted pak format
inZOICustom FPakInfo offset
Marvel RivalsCustom encryption, handled in CUE4Parse
Dead by DaylightCustom encryption, handled in CUE4Parse
FragPunkCustom global IoStore handling
Infinity NikkiCustom encryption, handled in CUE4Parse
Snowbreak: Containment ZoneDefault handling

The file you actually work in

Three columns. The middle one is the game text, the last one is yours. The first one is a hash the game looks the string up by.

Example extraction output
Example contents of an extracted locres CSV
keysourceTranslation
4A6FDB1549E45F6C5D8D739129686E2F Default По умолчанию
B1C74E0A93D6428FA5C1E07D2B98F3A6 Press any button to continue Нажмите любую кнопку, чтобы продолжить
E7D05C38F1BA4E6290C3D745B8A26F1 Inventory full Инвентарь заполнен
3F92A6D5C0844B1E97AD6205FB3C84E Quest_Ch03_Retrieve

Two columns, changed source

The previous source becomes the Translation. This is the path you hit when you export straight from UE4LocalizationsTool.

Two columns, same source

The Translation column already in the file is written back untouched, so repeated runs change nothing.

A row the game dropped

It stays in the file. If the game ever rolls back, the string and your translation are both still there.

--hash
Adds the combined source hash as an extra column, key,hash,string.
--url
Adds the path of the file each string came from, url,key,string. Useful when a line has several possible sources.
.locreshashes
A sidecar written next to the CSV that preserves the game-original CityHash64 keys. Titles with non-standard hash truncation, such as Neverness To Everness, need it or the lookup fails in game.

Every argument

Thirty two of them. Filter by flag name, or by what you are trying to get done.

Version and access5

--version=<ver>-v

Engine version or game name, detected automatically when omitted. Use -v=UE5_6 to force one, or -v=GAME_NevernessToEverness to enable NTE encrypted locres output.

--aes=<key>-a

A 32 character hex AES key. It must start with 0x.

--aes:auto-a:auto

Finds the AES key in the game resources and writes aes.txt to the root of the folder. Directories only.

--mapping-map

Loads the .usmap from a path you give directly, instead of hunting for it in the game folder.

--update

Checks GitHub for a newer build and updates in place when there is one.

Scope7

--path=<virtual_path>-p

Restricts processing to assets under one internal path, for example --path=HT/Content/Localization. Case insensitive substring match. Far faster than a full scan when you already know where the strings live.

--all-all

Processes every folder in the archive, including effects, meshes and sounds. Slower, and it finds strings you would otherwise miss.

--read-locres-rl

Also reads the .locres binaries themselves, for games that keep their localization in pre-compiled .locres files instead of inside assets, NTE among them. Keys and hashes come from the game, so a v3 round trip stays valid.

--skip-uexp-s:xp

Skips .uexp files during processing.

--skip-uasset-s:et

Skips .uasset files during processing.

--picky

Strict mode. More detail per file, useful when a game will not cooperate.

--verbose-vb

Per file processing detail and diagnostics, in place of the progress bar.

Output format9

--locres=<1|2|3>-l

Writes a .locres of that version after processing. Version 1 is Compact and works from UE4.0, version 2 carries the DataTables inside the file and needs UE4.20, version 3 needs UE5.8. Default is 1. Hand an engine older than 4.20 a version 2 file and it reads that file at offsets that mean nothing to it: the game loads invisibly in the background and keeps allocating until the machine runs out of memory, taking the whole system with it. The only way out is force-killing the process, or rebooting. Newer engines just refuse the file, which is what makes the mistake easy to miss.

--extract-locres

Dumps the raw .locres binaries from the archive into the output directory. Handy for inspection, and required as a template for patch mode.

--table-format-tf

Swaps the standard comma separator for a pipe.

--headmark-m

Writes a header row and a footer into the .csv.

--hash-h

Adds the combined source hash to the output: key,hash,string.

--url-url

Adds the path of the file each string came from: url,key,string.

--qmarks-q

Forces quotation marks around text strings no matter what the source looks like.

--table:only:key=<name>-t:o:k

Keeps only the entries whose key or name matches the given value.

--search-s

At the end of the run, reports every occurrence of the string you searched for.

Filtering and translation11

--no-underscore-n:un

Drops lines built out of identifiers, such as ex_string.

--no-uppercase-n:up

Drops lines that are entirely uppercase, such as EXAMPLE. It clears the constants and debug leftovers out of the table.

--lang:from=[code]-l:f

Source language for the translation pass, for example en.

--lang:to=[code]-l:t

Target language for the translation pass, for example ru.

--api:key=<key>-a:key

API key for OpenRouter, or for any server that requires authentication. Not needed for local models.

--api:url=<url>-a:url

Custom OpenAI compatible base URL for a local model, for example http://localhost:11434/v1/ for Ollama. Leave it out to go through OpenRouter.

--api:model=<model>-a:model

Model to translate with. An OpenRouter slug such as tngtech/deepseek-r1t2-chimera:free, or llama3 on Ollama.

--batch-size=<n>-bs

Segments per translation request, 150 by default. Lower it for models with a small context window.

--parallel=<n>-par

Concurrent translation requests, one by default. Raise it for a local model on decent hardware.

--translate-only-t:o

Skips extraction entirely and translates the CSV files from a previous run, picking up where the last pass stopped.

--help

Prints the built in reference.

Nothing matches that. Clear the field to see all of them again.

Made by translators, for translators

The idea was to get text out of an Unreal Engine game so somebody could translate it. Everything running today was written from scratch in C#, mostly by the maintainer, with the rest coming from people who had a localisation to finish.

  • DenisSolicen Author and maintainer. Wrote the tool from scratch in C#, working only from that one idea. Ships every release from the first 1.0.0 onward, and handles the pull requests and the issues on his own.
  • Ambi Came up with the idea of digging inside these archives, and passed it on.
  • Saipan Working out how a .locres file is put back together.
  • FabianFG The CUE4Parse library everything is built on, plus the FModel example that started it off.
  • Nuked88 A long list of fixes and improvements out of a fork, pulled in wholesale.

Take the strings out.

Download a release, drop a game folder on the executable, and the CSV is yours to fill in.

Runtime
.NET 10
Engine
UE 4.0 to 6.0
Licence
MIT
Stars
64 on GitHubна GitHub
DownloadСкачать Source codeИсходный код Latest releaseПоследний релиз v.1.0.8.7