Skip to content

Repository files navigation

UassetLocalize logo

UassetLocalize

A localization tool for Unreal Engine assets (.uasset, .uexp, .umap) and localization resources (.locres).

UassetLocalize is a ground-up rewrite of UE4LocalizationsTool, which is no longer maintained. The important difference is under the hood: asset parsing is done by UAssetAPI, the library behind UAssetGUI, rather than by hand-written byte splicing. That is why this tool follows new engine versions instead of guessing.

There is a GUI and a command line tool. Both do the same things.

What it reads

Format Notes
.uasset, .umap Anything UAssetAPI parses — roughly UE 4.13 through 5.7
.locres All four versions, including the CityHash64 one used by recent games

Inside an asset it finds text in FText and FString properties (however deeply nested in arrays, structs, maps and sets), string table exports, data table rows, and — if you ask for it — FName properties and string literals compiled into blueprint bytecode.

No .usmap for the game? There's a raw text scan that reads assets anyway.

Getting it

git clone --recurse-submodules https://github.com/rm-NoobInCoding/UassetLocalize.git
cd UassetLocalize
dotnet build -c Release

You need the .NET 10 SDK. If you already cloned without --recurse-submodules, run git submodule update --init.

The GUI ends up at src/UassetLocalize.App/bin/Release/net10.0/UassetLocalize.exe and the CLI at src/UassetLocalize.Cli/bin/Release/net10.0/UassetLocalize.Cli.exe.

The icon lives in assets/ as an .svg, a .png and a multi-resolution .ico, all written by assets/make-icon.ps1 from one set of shapes so they can't drift apart. You only need to run it if you change the design; the .svg has the letters as outlines, so editing that needs no particular fonts installed.

Using the GUI

Open a file, or drag one onto the window. The grid lists every piece of text under a heading for the export it came from, so a row only has to say where it sits inside that export rather than repeating the whole path. The pane underneath is where you actually write, because it handles line breaks and long dialogue properly.

  • Kind says what a row is. Raw and Code are marked in amber, because a raw scan hit is a guess and a bytecode literal can't safely change length.
  • Edited rows are marked by a rail down the left. The full path is a hover away, and the selected row's localization key sits in the status bar.
  • Search matches translation, source, key and path at once — and takes the filter syntax when you want to be more specific. The ? in the box spells it out.
  • Edited only narrows the list to what you have changed. It's the same as edited:yes.
  • Replace works across whatever rows are currently shown.
  • Translation → Export / Import moves text in and out of CSV, PO or plain text.
  • Settings (Ctrl ,) holds the engine version, the mappings file and the file type associations. You shouldn't need to go looking in there to read a file: when one needs a .usmap or a version, the window asks for it.

When an asset turns out to need a .usmap you get a dialog offering to open one, with the engine version alongside it — already set to whatever the file's header looks like — and a second button to guess without mappings instead. Turn that down and the offer stays on a strip above the grid, so a half-read file never sits there unexplained.

Saving defaults to writing <name>_NEW.uasset next to the original rather than overwriting it, because the original usually came out of a game archive.

Opening files from Explorer

Settings → File types registers .uasset, .umap and .locres, each on its own. This adds UassetLocalize to the Open with menu for that extension, and makes it what a double click opens when nothing else has claimed the extension — an existing association is never taken over.

Everything is written under HKCU\Software\Classes, so it needs no administrator, affects only your account, and unticking the box removes exactly what ticking it added.

Using the CLI

# See what's in a file without changing anything
UassetLocalize.Cli info Game.locres

# Export for translation, then bring it back
UassetLocalize.Cli export DT_Dialogue.uasset lines.po --engine 5.3
UassetLocalize.Cli import DT_Dialogue.uasset lines.po --engine 5.3

# Everything under a folder, into one combined spreadsheet
UassetLocalize.Cli export-all Content strings.csv --single --engine 5.3
UassetLocalize.Cli import-all Content strings.csv --engine 5.3 --out Content_translated

Run it with no arguments for the full option list, and engines to list the engine version names --engine accepts (5.3 and VER_UE5_3 both work).

Exit codes: 0 success, 1 error, 2 finished but some rows matched nothing or some files were skipped.

Filtering

The same syntax works in the GUI's search box and in --filter on the command line. Plain words still just search, so there's nothing to learn until you want it.

# Only the English rows that nobody has translated yet
UassetLocalize.Cli export-all Content todo.csv --single --engine 5.4 \
  --filter "key~_en$ empty:yes"

# Everything except raw-scan guesses
UassetLocalize.Cli export DT_Table.uasset text.csv --raw --filter "-kind:raw"
Term Matches
new game rows containing new and game, anywhere
"new game" rows containing the phrase
key:Item_ key contains, ignoring case
key=Item_Tonic key equals, ignoring case
key~^Item_.*Name$ key matches a regular expression, case sensitive
-kind:raw everything that isn't a raw-scan hit
kind:text|locres either kind
key:a or key:b either term

Fields are key, path, source, text, kind, edited, warned and empty. The last four take yes or no, except kind, which takes text, string, name, stringtable, bytecode, raw or locres. Terms are ANDed unless you write or.

Reusable filters go in a file, one per line, any of which is enough to keep an entry:

UassetLocalize.Cli export-all Content ui.csv --single --filter-file ui.filter

A mistake — an unknown kind, a broken regular expression — is reported rather than quietly matching nothing.

How this differs from UE4LocalizationsTool's filter

The old tool had the same idea, with three limits this doesn't have: its switches (match case, regex, reverse) applied to the whole list rather than to each term, it could only match one column, and -filter on the command line read a FilterValues.txt that only the GUI could write — so a headless machine couldn't filter at all. It also swallowed invalid regular expressions and silently matched nothing.

Translation formats

Format Matched on import by Good for
CSV key, then path spreadsheets, bulk editing
PO key, then path Poedit, Weblate, Crowdin, most CAT tools
Plain text line position dumps from the old tool

Prefer CSV or PO. They carry each entry's identity, so a translation still lands in the right place after the game updates and the asset gains or loses strings. Plain text only knows about line numbers — insert one string upstream and everything after it shifts by one, silently. That was the old tool's only option; it is kept here so existing .txt translations still import, including the [~NAMES-INCLUDED~] form.

Because a .txt can be written either way, exporting one asks which you want: with names writes name=text and can be re-imported after the asset changes, without names writes the bare text one line per entry, which is what the old tool produced and what other tooling expecting its output will want. On the CLI that choice is --format txt or --format txt-keys.

Rows that match nothing are reported rather than guessed at.

Cooked UE5 games need a version and mappings

Assets cooked by most UE5 games don't record the engine version they were built with, and store their properties unversioned. To read those properly you need two things:

  1. The engine version--engine 5.3, or the picker in Parsing options.
  2. A .usmap mappings file for the game, which you generate with a dumper such as Dumper-7 — or find one someone else has already dumped. A lone .usmap in the asset's folder or up to five folders above it is picked up automatically, which covers keeping one at the root of an extracted game; otherwise point at one explicitly.

If either is missing, or the version you picked doesn't match the file, you get a message saying so rather than a corrupt read. In the GUI that message is the dialog described above: pick a .usmap, or guess without one. On the CLI, pass --mappings and --engine, or --raw.

Getting these right is worth the trouble. On one game's 18 MB localization table, a proper parse gives 139,294 text entries each with its real localization key, where the raw scan gives 417,010 hits that also include every row name and table name.

No .usmap? Use the raw text scan

Unversioned properties carry neither names nor types in the stream, so without mappings the layout genuinely cannot be reconstructed. Nothing can guess it. But an FString is self-describing enough to find: a length, that many bytes or UTF-16 units, and a null terminator. The raw text scan skips parsing entirely and hunts for that shape in the export bytes — the same trick UE4LocalizationsTool called "method 2", done carefully.

UassetLocalize.Cli info    DT_LocalizeTable.uasset --raw
UassetLocalize.Cli export  DT_LocalizeTable.uasset text.csv --raw
UassetLocalize.Cli import  DT_LocalizeTable.uasset text.csv --raw

In the GUI it's a checkbox under Parsing options, and you'll be offered it automatically when an asset turns out to need mappings you don't have.

The file header is still read normally — that's what gives you real export names and class names to orient by. It needs an engine version, so if you don't pass --engine the header layouts get tried newest-first and the first that reads is used; you'll get a warning saying which. Pass --engine yourself if the result looks wrong. On an 18 MB localization table this finds ~417,000 strings in under two seconds.

What to expect:

  • Some false positives. Nothing here knows what the surrounding bytes mean, so runs that merely look like strings get picked up. Row keys and table names show up next to the actual text. Use the search box.
  • Length changes shift things. Editing a string moves everything after it inside that export. That's fine for a table of strings and bad for an export that stores offsets into itself. Strings you don't touch are copied through byte for byte, so only what you edit can move — an unedited save is still byte-identical.
  • Entries are numbered, not addressed. A path looks like Export 0 (DT_Foo : DataTable) > #42 — the 42nd string in that export, not a byte offset, so a translation still applies to a file you already edited. The numbering only shifts if an edit makes a string too short to be found again (under two characters).

Get a .usmap when you can. This is the fallback, not the good path.

Limitations worth knowing

  • IoStore / Zen packages are not supported. UAssetAPI can only read legacy packages. If a .uasset came out of a .utoc/.ucas container, convert it to legacy format first — retoc and ZenTools both do this — and open the converted file. UassetLocalize tells you when it sees one.
  • Blueprint bytecode text is off by default. Kismet jump instructions store absolute code offsets and nothing recalculates them, so changing the length of a literal can break the function. Same-length replacements are safe; anything else needs testing in-game. Entries carry a warning saying as much.
  • FName properties are off by default. They are identifiers at least as often as they are text.
  • Text that lives in a string table is not editable from the asset that references it. Open the string table asset itself.

How a save works

Only entries you actually edited are written back, straight into the object UAssetAPI parsed. Everything else is re-serialized exactly as it was read — open a file, save it untouched, and you get back the same bytes. There is a test for that.

For .locres, each entry's source string hash is preserved rather than recomputed. Unreal uses it to spot translations that have gone stale against the game's native text, so recomputing it from the translated string makes the engine discard every entry.

Licence

CC BY-NC 4.0 — see LICENSE. Use it, change it, pass it on; credit where it came from; not for commercial purposes.

That last part is the point of choosing this licence, so to be plain about what it rules out: you may not sell localization mods you made with this tool, or anything else it produced. No price on the download, no paywall, no subscriber-only or early-access build, no key reselling, no bundling it into something people pay for, no paid localization work done with it.

UAssetAPI, in external/UAssetAPI, is a separate work by atenfyr under the MIT licence. Its terms are unchanged by any of the above, and its notice is reproduced in LICENSE.

About

Unreal Engine .ussets and .locres Localization tool.

Resources

Contributing

Stars

3 stars

Watchers

0 watching

Forks

Releases

Contributors

Languages