Repository navigation
Home
The Dynamic Translation Framework is a plug-in that allows UI elements and in-game strings to be translated at runtime. Instead of hard-coded translations, the framework intercepts translation requests for keys that begin with $ and forwards them to your own code, native SKSE plug-ins, or Papyrus scripts.
When Skyrim asks Scaleform to translate a string, the framework inspects the translation key -- it removes the $ prefix and looks up a provider registered for that key. The provider (you) is (are) responsible for generating a translation string. After getting a translation from the provider, the mod replaces the original translation with the your value.
Providers are registered from JSON configuration files placed in a special folder (described below).
Alternatively, you do not need to do any of this and can just call the Papyrus API function that is shipped with this mod to change any translation any time you want:
Function DynamicTranslateV1(String a_key, String a_val)Give the key without $ prefix.
-
Location: Place your JSON files in:
Data\SKSE\Plugins\DynamicTranslationFramework
Each .json file in this folder is loaded when the plug-in starts.
-
Format: Each file can contain either a single JSON object or an array of objects. Each object defines a translation provider and must include at least the
stringsfield along with either asksefield or apapyrusfield (or both).
| Field | Required | Description |
|---|---|---|
strings |
Yes | A list of translation keys (without the leading $). When the game requests translation for $SomeKey, the framework will remove the $ and look up "SomeKey" in this list. Multiple keys can map to the same provider. |
skse |
Optional | Name of a native SKSE DLL (without file extension) that exports a function named OnDynamicTranslationRequest. When a key is mapped to this provider, the framework loads the DLL, resolves the function, and calls it to obtain a translation. |
papyrus |
Optional | Editor ID of a form that has a Papyrus script attached. The script must implement an OnDynamicTranslationRequest function. When a key is mapped to this provider, the framework will invoke the Papyrus function asynchronously to retrieve the translation. |
Only one of skse or papyrus is required. If both are present, the native SKSE function is attempted first; if it does not return a result, the Papyrus function will be called.
{
"strings": ["HELLO_WORLD", "HELLO_AGAIN"],
"skse": "MyTranslationPlugin"
}-
strings: This provider will handle$HELLO_WORLDand$HELLO_AGAIN. -
skse: The framework will loadMyTranslationPlugin.dllfrom your plug-ins folder and look for a function calledOnDynamicTranslationRequest.
{
"strings": ["FAREWELL"],
"papyrus": "MyTranslationScript"
}-
papyrus: The provider refers to a script namedMyTranslationScriptattached to a form. When$FAREWELLis requested, the framework will call the script’sOnDynamicTranslationRequestmethod.
- Create a DLL using the SKSE plug-in template.
- In your DLL, export a function named
OnDynamicTranslationRequestexactly. The framework resolves this symbol by name and expects it to use the following signature:
// SKSE native translation function
extern "C" __declspec(dllexport)
const wchar_t* OnDynamicTranslationRequest(std::string_view a_key);-
a_keyis the translation key (without$). Use it to look up the appropriate translation. - The function must return a null-terminated wide-character string (
const wchar_t*). The string must remain valid after the function returns. You can return a pointer to a static or globalstd::wstringor allocate memory on the heap; do not return a pointer to a stack buffer.
#include <string>
// Example translation table
static std::wstring Translate(const std::string_view key) {
if (key == "HELLO_WORLD") {
return L"Hallo Welt"; // German example
}
if (key == "DYNAMIC_GREETING") {
return L"Willkommen, Abenteurer!";
}
// return empty string if key not handled
return L"";
}
extern "C" __declspec(dllexport)
const wchar_t* OnDynamicTranslationRequest(std::string_view a_key) {
static std::wstring result;
result = Translate(a_key);
return result.c_str();
}Deploy the DLL into your SKSE plug-ins folder, for example:
Data\SKSE\Plugins\MyTranslationPlugin.dll
Then reference it in the JSON configuration with the skse field (without the .dll extension).
- Attach a Papyrus script to any form (e.g., a quest or an empty activator) in the Creation Kit. The script’s editor ID must match the value specified in the
papyrusfield of your JSON configuration. - Implement a function named
OnDynamicTranslationRequestthat returns a string. The function should match this signature:
ScriptName MyTranslationScript extends Quest
String Function OnDynamicTranslationRequest(String a_key)
If a_key == "FAREWELL"
return "Auf Wiedersehen"
EndIf
; return empty string if key is not handled
return ""
EndFunction-
a_keyis the translation key without$. - Return a non-empty string to translate the key; return an empty string to fall back to native translation or to the game’s default translation.
-
Register the script in JSON: Add the script’s editor ID in the
papyrusfield of your config file. The framework will use the editor ID to locate the script instance and call its function asynchronously.
- During game startup, the plug-in loads all JSON configuration files and builds a mapping from translation keys to providers.
- When Skyrim’s Scaleform translator receives a key beginning with
$, the plug-in’s hook removes the$and looks up a provider for the key. - The plug-in attempts to call the native SKSE provider first (if defined) and uses its return value as the translation.
- If the native function is not present or returns an empty string, it then calls the Papyrus provider.
- The resulting wide string is inserted into the translation result so that the UI element displays your dynamic text.
-
String Keys: Do not include the leading
$in thestringsarray; the framework removes it automatically. -
Error Handling: If neither
sksenorpapyrusis provided for a given entry, the configuration entry is ignored. - Multiple Keys: You can list multiple keys under a single provider; the provider will be invoked for any of the keys.
- Asynchronous Papyrus: Papyrus calls run asynchronously. The native SKSE provider will return immediately; Papyrus results may be slightly delayed but are cached by the framework.
By defining simple JSON configuration files and implementing a single function in either C++ or Papyrus, the Dynamic Translation Framework allows your mod to provide rich, runtime-generated translations for Skyrim.