This article is for localization engineers and developers who prepare Gettext PO files that contain UE Rich Text markup. It explains how to enable the directive, what translators see, which markup Smartling converts, and the limitations to plan for.
Unreal Engine (UE) Rich Text closes every style tag with the nameless </>. Smartling's default tag detection expects a name after </, so it treats the opening tag as a tag but leaves </> as plain text in the string. The unreal_richtext_enabled directive makes Smartling recognize UE tags as tags. Translators work with each tag as a single unit, and your downloaded file keeps the exact original markup.
The unreal_richtext_enabled directive is available for Gettext (.po / .pot) files only.
UE Rich Text markup in a PO file
UE Rich Text uses two kinds of markup. For full details, see Epic's UMG Rich Text Blocks documentation.
-
Style tag:
<RowName>text</>.RowNameis a row of a Rich Text Style Row Data Table. Row names are case-insensitive. -
Image decorator:
<img id="RowName"/>. This tag is self-closing and is backed by a Rich Image Row.
The closing tag </> closes any style tag, whatever its name. That is why Smartling needs the directive: without it, the opening tag is recognized as a tag, but </> stays literal text in the string. In the CAT tool, translators see it as ordinary text next to the tags.
In a PO file, UE markup sits inside msgid. Double quotes inside the string are escaped with a backslash, as in any PO file:
msgid "This <{string}>is</> the <text size=\"12\">Display Name</>"
msgstr ""Enable UE Rich Text support
Add the following directives as comments in your PO file, after the header entry and before your first string, or pass them as API parameters when you upload the file:
# smartling.unreal_richtext_enabled = on
# smartling.string_format = noneFor example:
msgid ""
msgstr ""
"Language: en\n"
"Content-Type: text/plain; charset=UTF-8\n"
# smartling.unreal_richtext_enabled = on
# smartling.string_format = none
msgid "This <{string}>is</> the <text size=\"12\">Display Name</>"
msgstr ""Use plain # comments, not extracted comments #. for directives. Smartling ingests extracted comments #. as string instructions for translators.
- unreal_richtext_enabled = on converts UE tags to tags that translators handle as single units, and restores the original markup on download.
- string_format = none switches off nested string parsing, so the standard Gettext handling applies. See Why string_format must be none.
To use API parameters instead of inline directives, pass the same names and values with the file upload request, in the format smartling.[directive_name] = [value]. For all Gettext directives, see Gettext PO/POT.
Why string_format must be none
Set string_format to none even if an earlier directive or an API parameter set it to html. The value none turns off nested string parsing explicitly.
-
html starts a nested HTML parser built for genuinely nested markup that contains entities. UE tags are always flat and never nested, so the parser adds complexity with no benefit. It also has known gaps with UE markup. A string that is one whole tag pair (
<B>whole</>) is stored as plainwholewith no tags, and a string made only of tags (</>or<A></>) is dropped. -
none keeps the standard Gettext handling, which detects flat inline tags such as spans and literal
<br>correctly.
What translators see
When the directive is on, Smartling converts each UE tag to a tag in the string. In the CAT tool, each opening and closing tag appears to translators as a single highlighted tag, such as <span i="{0}"> and </span>. When you download the translated file, Smartling restores the exact original UE markup.
| In your PO file | This <{string}>is</> the <text size="12">Display Name</> |
| Ingested string in Smartling | This <span i="{0}">is</span> the <span i="{1}">Display Name</span> |
| In the downloaded file | The original markup, restored exactly |
Placeholders inside tag content stay separate from the tags. For example, in <text color="FF5045EE" size="18">{ItemName}</> out of energy, the translator sees the tag pair as tags and {ItemName} as a separate placeholder.
Example string in the CAT Tool showing tags around the words "is" and "Display Name":
Guidance for translators
Translators must not nest or edit tags. Nested tags produce nested UE markup, which the engine renders literally. To remind translators, you can add an extracted comment (a line beginning with #.) above affected entries. Smartling shows it to translators as an instruction above the string. See File and String Instructions.
What is converted and what is not
| Source | Result |
<Tag attrs>text</> |
A tag pair around text
|
<Tag attrs></> (a pair with no content) |
The tag pair is kept, so the translator can place text inside it |
<{placeholder}>text</> (a tag named by a runtime placeholder) |
A tag pair around text
|
A </> with no opening tag |
A single tag |
Self-closing tags, such as <img id="x"/> or <PC/>
|
Not changed by the directive. Smartling already handles them correctly. |
An opening tag with no </>, such as <gasp> or <{0}>
|
Not changed by the directive |
<img id="x" width="22"></> (a pair that happens to be named img) |
Treated as a tag pair like any other. Only the self-closing form is exempt. |
<strong>text</> (a UE tag whose name matches an HTML tag) |
Treated as one UE tag pair, not as an HTML tag with a dangling </>
|
Regular HTML next to UE tags, such as <strong>text</strong>, <span>, or <br>
|
Handled as ordinary HTML. Only the UE tags are converted. Your own spans are not affected, even if they use an i attribute. |
UE tags inside a plural entry (msgid_plural) |
Converted the same way as in any other entry |
Text that is not UE grammar, such as <war cry> or < bot noises >
|
Not changed by the directive |
Smartling recognizes UE tags that follow these rules:
- Tag names can include
.and-, for exampleFont.Emph. - Attributes must be written as
name="value", with a leading space and double quotes. Attributes with single quotes, no quotes, or no value are not recognized. - Tag names are matched case-insensitively. Smartling restores each tag with its own original casing on download, for example
<InputAction>and<INPUTACTION>in the same string.
Keep literal angle brackets intact on download
Some strings mix prose in angle brackets with tag-shaped content, for example <war cry> and < bot noises >. Neither part is real markup, but Smartling's HTML detection treats them differently:
-
<war cry>matches the loose HTML tag pattern (a word after<, a bare word, then>), so Smartling treats it as a tag and leaves it alone. -
< bot noises >has a space after<, so Smartling treats it as plain text. On download, it is entity-escaped to< bot noises >.
In the CAT tool, translators see <war cry> as a tag and < bot noises > as ordinary text.
To restore the literal brackets, set smartling.entity_escaping to false. This unescapes entities on download.
# smartling.entity_escaping = false
msgid "<war cry> and < bot noises >"
msgstr ""
# smartling.entity_escaping = autoIf you set this directive at the top of the file, it unescapes entities for every string in the file, not only the affected ones. Use it inline if you want to limit the effect.
Where you place the directive controls how far it reaches:
-
Inline in the file: The directive applies from that comment to the end of the file, until you change it. Place it right before the affected entries, and add
# smartling.entity_escaping = autoafter them to restore the default. - At the top of the file: The directive applies to every string.
- Inside a plural entry: The directive takes effect after the entry ends.
- With an API parameter: An inline value overrides an API parameter value from that point on.
The directive affects download only. It does not change ingestion, so translators still see the misclassified text, and it does not change which strings are created. For more on this directive, see Gettext PO/POT.
Limits and considerations
- Tag limit: Smartling converts up to 1000 tags per PO entry. Further tags stay plain text and round-trip unchanged, but translators see the raw tag text.
-
A closing tag that closes nothing: If a string contains regular HTML tags and a
</>that does not close a UE tag, Smartling pairs the</>with the nearest preceding opening tag, even if that tag is already closed. For example,<b>bold</b> text </>is ingested as<span i="{0}">bold</b> text </span>: the<b>becomes the opening tag of the pair, and the</b>stays in the middle of the string. The downloaded file is still identical to the source, but in the CAT tool translators see a span pair around the text with an unpaired</b>inside it. To avoid this, make sure every</>closes a UE opening tag. -
One unclosed tag at a time: Smartling tracks one open UE tag at a time. If an opening tag has no
</>and another UE tag opens after it, Smartling leaves the unclosed tag unchanged, and the next</>pairs with the newer tag. The downloaded file is still identical to the source. - Importing existing translations: Smartling matches tags by position in the string. If a translation reorders or deletes a tag, the wrong UE tag can be restored in that position. See Import existing translations.
- Enabling the directive on an existing project: If you re-upload a source file with the directive on, Smartling ingests the affected strings as new strings with new hashcodes, because the parsing has changed. The new strings need to be translated. Existing translations are not applied automatically. You may see high fuzzy matches, or some SmartMatch matches, depending on your SmartMatch rules and how the new parsing changed each string.
-
Not covered: Prose in angle brackets that resembles a tag, such as
<war cry>,<VERY LARGE BONUS>, and<DynamicText:Add Friend>, is still treated as a tag and is not editable by translators. Theunreal_richtext_enableddirective does not change that.