Interpolation
Interpolation is a process of inserting some computed/dynamic values into a message. It's also known as variable substitution or string formatting in other localization system. It's a powerful feature that makes any internationalization system complete!
Let's say, you want to display a message that says "Hello John!"
. But instead of hardcoding the name "John"
in the message, you want to make it dynamic so you can change the name later. That's where interpolation comes in handy!
// assume the system is initialized on global variable
normal_msg = i18n_get_messages("hello_normal"); // "Hello John!"
player_name = "John";
intp_msg = i18n_get_messages("hello_intp", [player_name]); // "Hello John!", {0} = "John" (player_name)
Message interpolation starts with a placeholder, which is a special string that will be replaced with the actual value. The placeholder is in the form of {index}
for indexed data, and {key}
for named data.
Indexed Data
Indexed data is the simplest and fastest form of interpolation. The data is in array form, and then you can use the index to insert the data into the message.
Format:
[data0, data1, data2, ...]
Placeholder:{0}
,{1}
,{2}
, etc.
Used In:i18n_get_messages()
,i18n_create_ref_message()
,i18n_draw_message()
// assume the system is initialized on global variable
// i18n_get_messages(key, [data], [locale], [i18n])
// i18n_create_ref_message(var_name, key, [data], [i18n])
// static messages
msg1 = i18n_get_messages("hello", ["John"]); // "Hello John!"
msg2 = i18n_get_messages("bye", ["Selamat tinggal"], "id"); // "Selamat tinggal Dunia!"
msg3 = i18n_get_messages("welcome", [ // "Welcome to GM-I18n, Bro!"
"GM-I18n", "Bro" // {0} = "GM-I18n", {1} = "Bro"
]);
intp_text = "John";
msg4 = [ // in array
i18n_get_messages("dialog.npc_4", [ // "Dia bilang, "Selamat datang di desa John, {1}! Dah gitu aja, sekarang selamatkan temen gue!!!""
intp_text // {0} = "John" (intp_text), {1} is not defined, so it won't be replaced
], "id")
];
nested = { // in struct
msg5 : i18n_get_messages("dialog.npc_1", [ // "I have 100 apples. Do you want to trade them with my 100 bamboos, John?"
100, intp_text, "apples" // {0} = 100, {1} = "John" (intp_text), {2} = "apple"
])
};
// dynamic messages, no difference with static messages
global.ref_msg1 = i18n_create_ref_message("global.ref_msg1", "hello", ["John"]);
global.ref_msg2 = i18n_create_ref_message("g.ref_msg2", "bye", ["Selamat tinggal"], "id");
ref_msg3 = i18n_create_ref_message("ref_msg3", "welcome", [
"GM-I18n", "Bro"
]);
ref_msg4 = [
i18n_create_ref_message("ref_msg4.0", "dialog.npc_4", [intp_text])
];
global.nested = {
ref_msg5 : i18n_create_ref_message("global.nested.ref_msg5", "dialog.npc_1", [
100, intp_text, "apples"
])
};
0
. You can use the same index for multiple placeholder. For example, you can use {0}
for multiple placeholder, and then pass only one data to the data
parameter.data
parameter on i18n_create_ref_message()
function is copied to the message reference and become static. So, if you change the value of the data after creating the message reference, it won't affect the data in the message. The indexed data is the fastest form of interpolation. But it's not flexible enough if you want to use advanced interpolation features, such as linked message, pluralization, and dictionary.
The next section will introduce you to the named data form of interpolation, which is more flexible and powerful.
{0}
, {1}
, {2}
, etc.) and don't skip any index for better readability and faster translation process.Named Data
Named data is the most flexible form of interpolation. The data is in struct form, and then you can use the key to insert the data into the message.
Format:
{key1: value1, key2: value2, key3: value3, ...}
Placeholder:{key1}
,{key2}
,{key3}
, etc.
Used In:i18n_get_messages()
,i18n_create_ref_message()
// assume the system is initialized on global variable
// static messages
msg1 = i18n_get_messages("hello", {name: "John"}); // "Hello John!"
msg2 = i18n_get_messages("bye", {text: "Selamat tinggal"}, "id"); // "Selamat tinggal Dunia!"
msg3 = i18n_get_messages("welcome", { // "Welcome to GM-I18n, Bro!"
title: "GM-I18n",
who: "Bro"
});
intp_text = "John";
msg4 = [
i18n_get_messages("dialog.npc_4", { // "Dia bilang, "Selamat datang di desa John, {1}! Dah gitu aja, sekarang selamatkan temen gue!!!""
name: intp_text // {name} = "John" (intp_text), {village} is not defined, so it won't be replaced
}, "id");
];
msg5 = i18n_get_messages("dialog.npc_1", { // "I have 100 apples. Do you want to trade them with your 100 bamboos, John?"
count: 100,
name: intp_text,
item: "apples"
});
// dynamic messages, no difference with static messages
global.ref_msg1 = i18n_create_ref_message("global.ref_msg1", "hello", {name: "John"});
global.ref_msg2 = i18n_create_ref_message("g.ref_msg2", "bye", {name: "Selamat tinggal"}, "id");
ref_msg3 = i18n_create_ref_message("ref_msg3", "welcome", {
title: "GM-I18n",
who: "Bro"
});
ref_msg4 = [
i18n_create_ref_message("ref_msg4.0", "dialog.npc_4", {
name: intp_text,
village: "John"
})
];
global.nested = {
ref_msg5 : i18n_create_ref_message("global.nested.ref_msg5", "dialog.npc_1", {
count: 100,
name: intp_text,
item: "apples"
})
};
{text}
for multiple placeholder, and then pass only one data to the data
parameter. The key is case-sensitive. So,
{text}
and {Text}
are different keys. You can set the key like you're setting a variable name. So, need to follow the variable naming convention. You can use alphanumeric character and underscore, but can't start with a number.
data
parameter on i18n_create_ref_message()
function is copied to the message reference and become static. So, if you change the value of the data after creating the message reference, it won't affect the data in the message. You can't mix the indexed data and named data in the same message. You need to choose one of them, because the structure of the data is different. If you try to do so, the placeholder (such as
{0}
) will be treated as a normal text . The named data is the most flexible form of interpolation, but can be too complex to manage if you have a lot of data or very deep nesting. You can see the example in the Nested Message Data section.
You can't pass a named data on
i18n_draw_message()
function, because the i18n_draw_message()
function only supports indexed data for the interpolation. You can use the i18n_get_messages()
or i18n_get_ref_message()
function to get the message first, and then pass the message to the i18n_draw_message()
or draw_text_*
function.plural
, plural_value
, child
, andchild_*
as the key for the named data. They are reserved for the system.Linked Message
Linked message is a special form of message that can link to another message. It's useful if you want to reuse a message in another message, so you don't have to duplicate the message.
Format:
[[key1]] [[key2]] [[key3]] ...
Placeholder:[[key1]]
,[[key2]]
,[[key3]]
, etc.
Used In:[locale].json
// assume the system is initialized on global variable
// i18n_get_messages(key, [data], [locale], [i18n])
// i18n_create_ref_message(var_name, key, [data], [i18n])
// static messages
msg1 = i18n_get_messages("dialog.npc_3"); // "Hope you enjoy your stay in [[village_1.name]]!", raw message, even there's a linked message
msg2 = i18n_get_messages("dialog.npc_3", {}); // "Hope you enjoy your stay in Sukamakan!", need to pass a named data (even empty) to activate the linked message
player_name = "John";
msg3 = i18n_get_messages("welcome", {who: "Bro"}); // "Welcome to Sukamakan, Bro!", [[village_1.name]] = "Sukamakan"
msg4 = i18n_get_messages("welcome", {who: player_name}, "id"); // "Selamat datang di Sukamakan, John!"
count = 10;
msg5 = [ // in an array
i18n_get_messages("dialog.npc_2", { // "I have 10 Swords. Do you want to trade them with your 100 Bamboos, John?"
count1 : count, // {count1} = 10 (count), {count2} = 100 (count * 10), {name} = "John" (player_name)
name: player_name, // [[items.sword]] = "Sword", [[items.bamboo]] = "Bamboo"
count2 : count * 10
})
];
msg_struct = { // in a struct
msg6 : i18n_get_messages("items.letter", { // a deeply nested message, but it's just a string replacement
who: player_name // {who} won't be replaced, even though you've passed the data
}) // do you know why?
};
/*
* "Announcement:"
* "Welcome to Sukamakan, {who}! I'm Budiman of Sukamakan. Hope you enjoy your stay in Sukamakan!"
*/
// dynamic messages, no difference with static messages
global.ref_msg1 = i18n_create_ref_message("global.ref_msg1", "dialog.npc_3");
global.ref_msg2 = i18n_create_ref_message("g.ref_msg2", "dialog.npc_3", {}, "id");
ref_msg3 = i18n_create_ref_message("ref_msg3", "welcome", {
who: "Bro"
});
ref_msg4 = [
"My string",
"Other string",
i18n_create_ref_message("ref_msg4.2", "dialog.npc_2", {
count1 : count,
name: player_name,
count2 : count * 10
})
];
global.nested = {
ref_msg5 : i18n_create_ref_message("global.nested.ref_msg5", "items.letter", {
who: player_name
})
};
[[welcome]]
) is the actual key of the message you want to link to. It's case-sensitive. So, [[key1]]
and [[Key1]]
are different keys. You can customize the placeholder format in the
i18n_create()
function, so that you can change the opening (default: [
) and closing (default: ]
) character, such as <<key1>>
or ::key1##
. See the API Reference for more information.You can't mix the indexed data and named data in the same message. You need to choose one of them, because the structure of the data is different. If you try to do so, the linked message (such as
[[key1]]
) will be treated as a normal text. The more deep the linked message is, the more painful it is to manage the data. It's not recommended to have a very deep linked message if you're also passing a lot of data (trust me on this one). You can see the example in the Nested Message Data section.
Summary
There are 3 forms of message data interpolation that you can use in GM-I18n:
- Indexed Data (
{0}
,{1}
,{2}
, etc.) - Named Data (
{key1}
,{key2}
,{key3}
, etc.) - Linked Message (
[[key1]]
,[[key2]]
,[[key3]]
, etc.)
You can use any of them in any form of message (static or dynamic). You can use any interpolation form on any level of the message, even in the nested/linked messages!
The indexed data is the simplest and fastest form of interpolation. But you can't use it with advanced interpolation features, such as linked message, pluralization, and dictionary.
The named data is more flexible than the indexed data. You can use it with linked message, pluralization, and dictionary. But it's a bit slower than the indexed data.
Use these form of interpolation based on your needs. If you want to use the linked message, pluralization, and dictionary features, you need to use the named data. If you don't, you can use the indexed data for better performance.