Difference between revisions of "Localization"

From Oldunreal-Wiki
Jump to navigation Jump to search
(Just began this.)
 
Line 13: Line 13:
In order to switch to a different language, open <tt>Unreal.ini</tt> or <tt>UnrealTournament.ini</tt> and replace the language code in the line <tt>Language=int</tt> by the code you wish to use. For example, in order to enable the Spanish language, this line must be <tt>Language=est</tt>. If the localization file for the <tt>Core.u</tt> package is available (for example, there's a file called <tt>Core.est</tt> in the System folder) and the Language value in the <tt>[Language]</tt> section is set, you can also select the language in UnrealEd by going to '''Advanced Options → Drivers → Language'''.
In order to switch to a different language, open <tt>Unreal.ini</tt> or <tt>UnrealTournament.ini</tt> and replace the language code in the line <tt>Language=int</tt> by the code you wish to use. For example, in order to enable the Spanish language, this line must be <tt>Language=est</tt>. If the localization file for the <tt>Core.u</tt> package is available (for example, there's a file called <tt>Core.est</tt> in the System folder) and the Language value in the <tt>[Language]</tt> section is set, you can also select the language in UnrealEd by going to '''Advanced Options → Drivers → Language'''.


== Authoring UnrealScript code for translatability ==
== Why is it important to localize the game? ==
Put all readable text into string instance variables, and use the <tt>localized</tt> keyword to designate them as localized, and use the <tt>localized</tt> keyword when declaring the class. For example, this code will be hard to translate because it will require messing with the code:
While English is considered the global language ''par excellence'', the truth is that a lot of other people doesn't speak it yet, or have difficulties understanding it. Localizing the game allows it to be properly played and enjoyed by many other people around the world. And more people playing the game means more people filling up servers and more people creating content for the game.
 
== Composition of an .int file ==
=== Public section ===
The <tt>[Public]</tt> section can take two different keys, <tt>Object</tt> and <tt>Preferences</tt>. These can be used as often as needed.
==== Object ===
<pre>Object=(Name=Package.ObjectName,Class=ObjectClass,MetaClass=Package.MetaClassName,Description="descriptive string")</pre>
 
* '''Name''' is an arbitrary name; generally, must refer to an existing class. (Though that's no engine requirement. If you work with GetNextIntDesc to retrieve Object keys yourself, the Name argument can be anything.)
* '''Class''' is the class of the object described by this line. In many cases, that's simply Class if the Object line refers to a class.
* '''MetaClass''' is the common superclass of all objects described in Object lines that belong together; for mutators, that'd be <tt>Engine.Mutator</tt>, for instance. (This class name is used as GetNextIntDesc's first parameter.)
* '''Description''' is an arbitrary description (optional), also retrieved by GetNextIntDesc.
 
==== Preferences ====
<pre>Preferences=(Caption="display name",Parent="display name of parent",Class=Package.ClassName,Category=variable group name,Immediate=True)</pre>
 
This is used to create the options available in the UnrealEd "Advanced Options" window. Either <tt>Class</tt>, <tt>Category</tt> and <tt>Immediate</tt> are left out or <tt>Class</tt> has to be a valid UnrealScript class and <tt>Category</tt> should be a variable group used in that class.
=== Localization Sections ===
Used to localize strings to different (natural) languages. To create a class variable with different default values for different languages, put all readable text into string instance variables, and use the <tt>localized</tt> keyword to designate them as localized, and use the <tt>localized</tt> keyword when declaring the class. For example, this code will be hard to translate because it will require messing with the code:


<pre>class OuchSayer expands Trigger;
<pre>class OuchSayer expands Trigger;
function Trigger( Pawn A, Actor B )
function Trigger(Pawn A, Actor B)
{
{
A.Message( "Ouch, you touched me!" );
A.Message("Ouch, you touched me!");
}</pre>
}</pre>


Line 25: Line 43:


<pre>class OuchSayer expands Trigger localized;
<pre>class OuchSayer expands Trigger localized;
var() localized string[64] Msg;
var localized string Msg;
function Trigger( Pawn A, Actor B )
function Trigger(Pawn A, Actor B)
{
{
A.Message( Msg );
A.Message(Msg);
}
}
defaultproperties
defaultproperties
Line 34: Line 52:
Msg="Ouch, you touched me!"
Msg="Ouch, you touched me!"
}</pre>
}</pre>
Thus a properly coded class in UnrealScript like this:
<pre>class aClassName extends aSuperClass localized;
var localized string Description;
var localized float SoundLength;
defaultproperties
{
    Description="An example class showing localization."
    SoundLength=2.750000
}</pre>
...could then be saved in Example.u, which, after being ran under DumpInt, would generate a file called Example.int with the following content:
<pre>[aClassName]
Description="An example class showing localization."
SoundLength="2.750000"</pre>
Suppose you'd want to translate this text to the [[German .det|German language]], well, all you'd need to do is to edit the file so it looks like this:
<pre>[aClassName]
Description="Eine Beispielklasse, die Localization veranschaulicht."
SoundLength="2.930000"</pre>
...and save it under the name Example.det. Since the name of the new file is Example.det the values in it automatically become the default values of aClassName when this language is selected.


The other advantage of this approach is that it makes it easy for a writer to go through and clean up the English text in a game. According to Sweeney, this has been beneficial in Unreal development, because Epic and DE had a lot of level designers and programmers writing game text which needed reworking due to stylistic inconsistencies.
The other advantage of this approach is that it makes it easy for a writer to go through and clean up the English text in a game. According to Sweeney, this has been beneficial in Unreal development, because Epic and DE had a lot of level designers and programmers writing game text which needed reworking due to stylistic inconsistencies.

Revision as of 04:31, 10 July 2020

The Unreal engine supports language localizations in a very straightforward and easy-to-use way. If you declare a string variable using the localized keyword, the engine will automatically look it up in the selected language's localization file, potentially falling back to the international (usually English) language localization file first and the value declared in the default properties at last.

Overview

According to Tim Sweeney's Unreal Localization Support document, the translatability for Unreal Engine games was made with some design goals in mind, namely making games easily translatable (where translations to UNICODE languages require no changes to source code or binary data files), build localization support into the engine at a fundamental level so that all the translation work can be done by a single person without taking programmer or level designer time, make translations modular so that translations can be installed after a game has been purchased, keep translations robust (so that when updates/patches to a game are released, they don't need to be retranslated; and if a particular piece of text hasn't been translated, be able to fall back gracefully on the international English text) and make the translation support portable (i.e. non-dependant on platform-specific stuff).

In order to achieve this, the Unreal Engine games use localized text in .ini style files which correspond to Unreal package files. Translation capability is available in UnrealScript and C++ in three key areas:

  • Native script support: default string properties of Actor (and Object) classes can be defined using the localized keyword in UnrealScript (or CPF_Localized in C++). These default properties contain the international English text as specified by the programmer or level designer. However, text in the current language's .ini files overrides text in the .u files, so translations can be performed purely by creating a new .ini file, without modifying any code.
  • In C++ and UnrealScript, there are Localize() functions which take an arbitrary key name and return its corresponding localized text. This is intended mainly for C++ code where there is no convenient UObject containing default properties in which to place the translatable string.
  • The translation .ini files contain a [LocalizedNames] section which enables mapping UnrealScript names (C++ FName's) to localized text. This is used in areas of the engine which are based on FName's internally but must use a localized representation of those FName's for display and user interaction purposes. For example, the Preferences windows are based on direct editing of named UnrealScript properties (like SoundVolume) which must be translated into other languages, and which also may benefit from being translated into more human-readable English, for example "Sound Volume Level".

In order to switch to a different language, open Unreal.ini or UnrealTournament.ini and replace the language code in the line Language=int by the code you wish to use. For example, in order to enable the Spanish language, this line must be Language=est. If the localization file for the Core.u package is available (for example, there's a file called Core.est in the System folder) and the Language value in the [Language] section is set, you can also select the language in UnrealEd by going to Advanced Options → Drivers → Language.

Why is it important to localize the game?

While English is considered the global language par excellence, the truth is that a lot of other people doesn't speak it yet, or have difficulties understanding it. Localizing the game allows it to be properly played and enjoyed by many other people around the world. And more people playing the game means more people filling up servers and more people creating content for the game.

Composition of an .int file

Public section

The [Public] section can take two different keys, Object and Preferences. These can be used as often as needed.

= Object

Object=(Name=Package.ObjectName,Class=ObjectClass,MetaClass=Package.MetaClassName,Description="descriptive string")
  • Name is an arbitrary name; generally, must refer to an existing class. (Though that's no engine requirement. If you work with GetNextIntDesc to retrieve Object keys yourself, the Name argument can be anything.)
  • Class is the class of the object described by this line. In many cases, that's simply Class if the Object line refers to a class.
  • MetaClass is the common superclass of all objects described in Object lines that belong together; for mutators, that'd be Engine.Mutator, for instance. (This class name is used as GetNextIntDesc's first parameter.)
  • Description is an arbitrary description (optional), also retrieved by GetNextIntDesc.

Preferences

Preferences=(Caption="display name",Parent="display name of parent",Class=Package.ClassName,Category=variable group name,Immediate=True)

This is used to create the options available in the UnrealEd "Advanced Options" window. Either Class, Category and Immediate are left out or Class has to be a valid UnrealScript class and Category should be a variable group used in that class.

Localization Sections

Used to localize strings to different (natural) languages. To create a class variable with different default values for different languages, put all readable text into string instance variables, and use the localized keyword to designate them as localized, and use the localized keyword when declaring the class. For example, this code will be hard to translate because it will require messing with the code:

class OuchSayer expands Trigger;
function Trigger(Pawn A, Actor B)
{
	A.Message("Ouch, you touched me!");
}

This code will be easy to translate because it can be done in an .ini file by a non-programmer:

class OuchSayer expands Trigger localized;
var localized string Msg;
function Trigger(Pawn A, Actor B)
{
	A.Message(Msg);
}
defaultproperties
{
	Msg="Ouch, you touched me!"
}

Thus a properly coded class in UnrealScript like this:

class aClassName extends aSuperClass localized;

var localized string Description;
var localized float SoundLength;

defaultproperties
{
     Description="An example class showing localization."
     SoundLength=2.750000
}

...could then be saved in Example.u, which, after being ran under DumpInt, would generate a file called Example.int with the following content:

[aClassName]
Description="An example class showing localization."
SoundLength="2.750000"

Suppose you'd want to translate this text to the German language, well, all you'd need to do is to edit the file so it looks like this:

[aClassName]
Description="Eine Beispielklasse, die Localization veranschaulicht."
SoundLength="2.930000"

...and save it under the name Example.det. Since the name of the new file is Example.det the values in it automatically become the default values of aClassName when this language is selected.

The other advantage of this approach is that it makes it easy for a writer to go through and clean up the English text in a game. According to Sweeney, this has been beneficial in Unreal development, because Epic and DE had a lot of level designers and programmers writing game text which needed reworking due to stylistic inconsistencies.

Character set

Unreal is designed to use the 16-bit UNICODE character set. The standard Unreal fonts include only the first page of UNICODE language characters, but the built-in TrueType font importer enables you to import (with optional anti-aliasing) any Unicode TrueType font, i.e. for piecing together Unreal fonts that support the roman languages, Japanese, and Korean.

Localized file extensions

Each language has a characteristic file extension, which Unreal uses to try to find the localized text for a given package file. For example, we define the following extensions:

When Unreal loads a package file (such as an .unr level file, or a .u class file), it searches for localized text in the following places. For example, when loading MyLevel.unr:

  • If using a language other than English, it looks in that language's localized file, such as MyLevel.frt for French.
  • If that file was not found, it looks in the international English version, MyLevel.int.
  • Otherwise, the text stored in the raw package file is used.

Preparing level text for translation

Lots of game text is stored within .unr level files, for example the text messages stored with Counters, Triggers, and TranslatorEvent actors. The intent is that level designers will write the first-pass version of this text in English, and then the text will be localized (or the English version cleaned up) by a translator or writer, who edits the .ini files and never modifies the .unr level file itself.

Not modifying binary data files (such as .unr level files, and .u class files) is an important concept, because many aspects of Unreal, including network play, rely on the exact binary contents of these data files. If translations were performed by having someone actually modify the .unr levels, or .uc scripts (thus affecting the .u binary files), it would be impossible for French or German people to play on American Unreal servers, etc.

UnrealEd supports a special console command analyze an Unreal binary file (such as an .unr or .u file) and generate an .ini style text file containing the international English version of the text. For example, in order to process UnrealI.u, type on the console dumpint UnrealI.u, and that generates the file UnrealI.int, the international text for the package. That file can then be given to translators, who rename it to their appropriate language's extension (such as UnrealI.frt or UnrealI.det) and translate all of the text within.

Starting with Unreal v227 and Unreal Tournament v469, UCC include a new Commandlet called DumpInt, which allows people on systems which don't support the Unreal Editor to create localization files from packages and work on them. It works with the following commandline: ucc dumpint packagename. Following the previous example, in order to generate UnrealI.int, a person would write in the command line, from the game's System directory: ucc dumpint UnrealI.u. This command generates a file called UnrealI.int in the same folder as UCC.

Hardcoded C++ translation support

CORE_API const char* Localize( const char* Section, const char* Key, const char* Package=GPackage );

Looks in the localization file corresponding to Package (i.e. Engine.int for the Engine package, using international English) and returns the localized string for Key, found in the Value section. If Package isn't specified, it defaults to the current package being compiled.

CORE_API const char* LocalizeError( const char* Key, const char* Package=GPackage );
CORE_API const char* LocalizeProgress( const char* Key, const char* Package=GPackage );
CORE_API const char* LocalizeQuery( const char* Key, const char* Package=GPackage );
CORE_API const char* LocalizeGeneral( const char* Key, const char* Package=GPackage );

These functions work like Localize but they look in a the section corresponding to their function name instead of taking Section as a string. For example, LocalizeError looks in the "Error" section.

Hardcoded UnrealScript translation support

intrinsic function string[192] Localize( name KeyName, name SectionName, name PackageName);

This puppy does the same thing as the C++ Localize function.

Debugging translations

When a C++ or UnrealScript localization function is called for which no corresponding localized .ini file entry is found, the returned string is displayed as <?Package.Section.Key?>, and an entry is written out to the log to record the missing translation. This is a non-critical error. Because this is easy to screw up, we recommend coverage testing all lines of code which lookup localized text.

Non-technical information for translators

You can edit .int files with a text editor like Notepad. A typical .int file will contain a lot of data that looks like this:

[Errors]
FileOpen=Error opening file
FileNotFound=Can't find file '%s'
FileClose=Error closing file

[Progress]
Saving=Saving file %s...
Closing=Closing

Text on the other side of an equals sign (=) is the text to be translated. For clarity, these .int files contain three kinds of data:

  • Section headings, such as [Errors] and [Progress]. These are used internally by Unreal. Do not translate these!
  • Key names such as FileOpen= and FileClose=. These are used internally by Unreal to identify the text. Do not translate these!
  • Translatable text such as Error opening file and Saving file %s... This text always immediately follows the "=" sign. Translate this.

So, for example, the result, translated file above might look like:

[Errors]
FileOpen=Fehleröffnung Datei
FileNotFound=Kann nicht Datei %s finden
FileClose=Schließende Datei des Fehlers

[Progress]
Saving=Einsparungdatei %s...
Closing=Schließen

There are several important points to keep in mind:

  • Be sure to test all translated text thoroughly in the game to make sure it's complete and it fits in the space available. Some areas that contain text, such as the menus, have a limited amount of space and the translated text will overflow if it's too large.
  • When you see symbols like %s or %i in the text, leave them and be sure not to move them around. These characters are used by Unreal to designate where numbers, and other text appear. For example, changing "Loading item %i from file %s" to "Loading file %s, item %i" will cause Unreal to crash when it displays the text. Unreal typically formats a message like "Loading item %i from file %s" into something like "Loading item 10 from file Unreal.unr".
  • The standard Unreal fonts should contain all of the ANSI characters necessary for translation into standard ANSI languages.

Limitations

Unreal's text printing support is currently limited to left-to-right. Support for other orientations, or bidirectional text (i.e. for Arabic or Hebrew) is not provided, but could be pieced together without too much pain.

External links and resources

See also