tw.i18n
Side: Shared (with client extension)
Internationalization module with locale management, translation keys, and dynamic locale switching.
ConVars
| ConVar | Default | Description |
|---|---|---|
twinded_libs:i18n:locale | "en" | Default locale |
twinded_libs:i18n:allowSwitchLocale | "true" | Allow players to change their locale |
twinded_libs:i18n:localeCommand | "setLocale" | Chat command name for switching locale |
Locale Files
Locale files are loaded from @{resourceName}.locales.{locale} using tw.file.load. Keys can be nested and are automatically flattened to dot notation.
my_script/
locales/
en.lua
fr.lua-- locales/en.lua
return {
greeting = "Hello %s!",
shop = {
buy = "Buy",
sell = "Sell",
not_enough = "Not enough money",
},
}Nested keys become: greeting, shop.buy, shop.sell, shop.not_enough.
Functions
tw.i18n.getLocale() -> string
Returns the current locale code.
Returns: string (e.g. "en", "fr")
local locale = tw.i18n.getLocale()tw.i18n.setLocale(locale)
Changes the current locale. Reloads all translations and fires the tw_i18n_locale_changed hook.
| Parameter | Type | Description |
|---|---|---|
locale | string | Locale code (e.g. "en", "fr") |
tw.i18n.setLocale("fr")tw.i18n.__(key) -> string
Translates a key using the current locale. Returns "#key" if the key is not found.
Also available as the global shorthand __.
| Parameter | Type | Description |
|---|---|---|
key | string | Translation key (dot notation for nested keys) |
Returns: string
local text = tw.i18n.__("shop.buy")
-- or using the global shorthand:
local text = __("shop.buy")tw.i18n.addEntry(key, value)
Adds a single translation entry to the current locale.
| Parameter | Type | Description |
|---|---|---|
key | string | Translation key |
value | string | Translation value |
tw.i18n.addEntry("custom.key", "Custom value")tw.i18n.addEntries(entries)
Adds multiple translation entries at once.
| Parameter | Type | Description |
|---|---|---|
entries | table | Key-value table of translations |
tw.i18n.addEntries({
["custom.hello"] = "Hello",
["custom.bye"] = "Goodbye",
})tw.i18n.getEntries() -> table
Returns all translation entries for the current locale.
Returns: table — Key-value table of all translations.
local entries = tw.i18n.getEntries()
for key, value in pairs(entries) do
print(key, value)
endtw.i18n.onLocaleChange(callback, priority?)
Registers a callback that fires when the locale changes.
| Parameter | Type | Default | Description |
|---|---|---|---|
callback | function | — | Called when the locale changes |
priority | integer? | — | Execution order (lower = earlier) |
tw.i18n.onLocaleChange(function()
print("Locale changed to", tw.i18n.getLocale())
end)tw.i18n.findMissingKeys(locale)
Debug tool that finds translation keys present in the default locale but missing in the specified locale.
| Parameter | Type | Description |
|---|---|---|
locale | string | Locale code to check |
tw.i18n.findMissingKeys("fr")
-- prints missing keys to consoleClient Features
On the client side, the player's locale preference is persisted via KVP (Key-Value Pair storage). The locale is restored automatically on reconnect.
Chat Command
Players can switch their locale using the chat command (default: /setLocale):
/setLocale frThe command name is configurable via the twinded_libs:i18n:localeCommand ConVar.
Examples
Basic translation with formatting
-- locales/en.lua
return {
collected = "You collected %dx %s",
}local msg = string.format(__("collected"), 5, "Apple")
-- "You collected 5x Apple"React to locale changes
tw.i18n.onLocaleChange(function()
-- refresh UI labels, update prompts, etc.
updatePromptLabels()
end)Notes
- The global
__function is a shorthand fortw.i18n.__and is available everywhere after the module loads. - Missing keys return
"#key"(e.g."#shop.missing") to make them easy to spot. - Nested keys in locale files are flattened to dot notation automatically.
setLocalereloads all translations from locale files, so changes take effect immediately.- Use
findMissingKeysduring development to ensure all locales are complete.

