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!

Create Event
// 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)
en.json
{
    "hello_normal": "Hello John!",      // normal message
    "hello_intp": "Hello {0}!"          // message with interpolation
}
id.json
{
    "hello_normal": "Halo John!",       // normal message
    "hello_intp": "Halo {0}!"           // message with interpolation
}

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()

Create Event
// 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"
    ])
};
Key Pressed - Space
// change the locale
// i18n_set_locale(code, [update_refs], [i18n])
i18n_set_locale("id");                          // all message references will be updated automatically
en.json
{
    "hello": "Hello {0}!",
    "bye": "{1} World!",
    "welcome": "Welcome to {0}, {1}!",
    "dialog": {
        "npc_1": "I have {0} {2}. Do you want to trade them with my {0} bamboos, {1}?",
        "npc_4": "He said, \"Welcome to our {1} village, {0}! That's all, now save my friend!!!\""
    }
}
id.json
{
    "hello": "Halo {0}!",
    "bye": "{1} Dunia!",
    "welcome": "Selamat datang di {0}, {1}!",
    "dialog": {
        "npc_1": "Aku punya {0} {2}. Mau tukeran sama aku dengan {0} bambu, {1}?",
        "npc_4": "Dia bilang, \"Selamat datang di desa {1}, {0}! Dah gitu aja, sekarang selamatkan temen gue!!!\""
    }
}
The index starts from 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.
The data you passed to the 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.
It's a good practice to use the placeholder in ascending order ({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()

Create Event
// 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"
    })
};
en.json
{
    "hello": "Hello {name}!",
    "bye": "{text} World!",
    "welcome": "Welcome to {title}, {who}!",
    "dialog": {
        "npc_1": "I have {count} {item}. Do you want to trade them with your {count} bamboos, {name}?",
        "npc_4": "He said, \"Welcome to our {village} village, {name}! That's all, now save my friend!!!\""
    }
}
id.json
{
    "hello": "Halo {name}!",
    "bye": "{text} Dunia!",
    "welcome": "Selamat datang di {title}, {who}!",
    "dialog": {
        "npc_1": "Aku punya {count} {item}. Mau tukeran sama aku dengan {count} bambumu, {name}?",
        "npc_4": "Dia bilang, \"Selamat datang di desa {village}, {name}! Dah gitu aja, sekarang selamatkan temen gue!!!\""
    }
}
You can use the same key for multiple placeholder. For example, you can use {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.
The data you passed to the 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.
You can't useplural, 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

Create Event
// 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
    })
};
en.json
{
    "welcome": "Welcome to [[village_1.name]], {who}!",
    "items": {
        "apple": "Apple",
        "bamboo": "Bamboo",
        "sword": "Sword",
        "shield": "Shield",
        "potion": "Potion",
        "letter": "Announcement:\n\"[[dialog.npc_1]]\""
    },
    "dialog": {
        "npc_1": "[[welcome]] I'm [[village_1.chieftain]] of [[village_1.name]]. [[dialog.npc_3]]",
        "npc_2": "I have {count1} [[items.sword]]s. Do you want to trade them with your {count2} [[items.bamboo]]s, {name}?",
        "npc_3": "Hope you enjoy your stay in [[village_1.name]]!"
    },
    "village_1": {
        "name": "Sukamakan",
        "chieftain": "Budiman",
        "trait": {
            "num_1": "Lots of food",
            "num_2": "Love eating {item}",
            "num_3": "{adj} people"
        }
    }
}
id.json
{
    "welcome": "Selamat datang di [[village_1.name]], {who}!",
    "items": {
        "apple": "Apel",
        "bamboo": "Bambu",
        "sword": "Pedang",
        "shield": "Perisai",
        "potion": "Ramuan",
        "letter": "Pengumuman:\n\"[[dialog.npc_1]]\""
    },
    "dialog": {
        "npc_1": "[[welcome]] Aku adalah [[village_1.chieftain]] dari [[village_1.name]]. [[dialog.npc_3]]",
        "npc_2": "Aku punya {count1} [[items.sword]]. Mau tukeran sama aku dengan {count2} [[items.bamboo]]mu, {name}?",
        "npc_3": "Semoga betah di [[village_1.name]]!"
    },
    "village_1": {
        "name": "Sukamakan",
        "chieftain": "Budiman",
        "trait": {
            "num_1": "Banyak makanan",
            "num_2": "Suka makan {item}",
            "num_3": "Orang yang {adj}"
        }
    }
}
Linked message is regarded as named data. Take a note that the placeholder (such as [[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.
Same as other form of interpolation, the linked message is 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 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:

  1. Indexed Data ({0}, {1}, {2}, etc.)
  2. Named Data ({key1}, {key2}, {key3}, etc.)
  3. 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.

  If you've mastered the message interpolation, you've already mastered the most important part of GM-I18n system!