Translations
SwiftlyS2 translation APIs let you localize plugin text by key and automatically resolve player-specific language.
Translation File Layout
Place translation files in your plugin under:
resources/translations/Use .jsonc files named by language code (for example en.jsonc, fr.jsonc, pt-BR.jsonc, es-419.jsonc).
Custom language codes are supported for direct lookups through Core.Localizer, but they are not resolved by Core.Translation.GetPlayerLocalizer(player).
Player localizers are limited to language codes provided by the player's game language.
Keep an en.jsonc baseline with all keys so unsupported or incomplete languages still have usable text.
Language Codes
Use the following language codes when naming your translation files:
| Language | Code | File Name |
|---|---|---|
| Arabic | ar | ar.jsonc |
| Bulgarian | bg | bg.jsonc |
| Chinese (CN & TW) | zh-CN / zh-TW | zh-CN.jsonc / zh-TW.jsonc |
| Czech | cs | cs.jsonc |
| Danish | da | da.jsonc |
| Dutch | nl | nl.jsonc |
| English | en | en.jsonc |
| Finnish | fi | fi.jsonc |
| French | fr | fr.jsonc |
| German | de | de.jsonc |
| Greek | el | el.jsonc |
| Hungarian | hu | hu.jsonc |
| Indonesian | id | id.jsonc |
| Italian | it | it.jsonc |
| Japanese | ja | ja.jsonc |
| Korean | ko | ko.jsonc |
| Norwegian | no | no.jsonc |
| Polish | pl | pl.jsonc |
| Portuguese | pt | pt.jsonc |
| Portuguese (Brazilian) | pt-BR | pt-BR.jsonc |
| Romanian | ro | ro.jsonc |
| Russian | ru | ru.jsonc |
| Spanish | es | es.jsonc |
| Spanish (Latin America) | es-419 | es-419.jsonc |
| Swedish | sv | sv.jsonc |
| Thai | th | th.jsonc |
| Turkish | tr | tr.jsonc |
| Ukrainian | uk | uk.jsonc |
| Vietnamese | vn | vn.jsonc |
Example Translation File
{
// General
"plugin.name": "My Plugin",
"plugin.ready": "Plugin is ready.",
// Command feedback
"command.heal.success": "You have been healed.",
"command.heal.other": "{0} healed {1}",
"command.heal.no_permission": "You do not have permission.",
// Errors
"error.player_not_found": "Player '{0}' was not found."
}Accessing Translation APIs
SwiftlyS2 exposes two useful entry points:
Core.Localizer(ILocalizer) for default/server-localized lookupsCore.Translation.GetPlayerLocalizer(IPlayer)for player-language lookups
public override void Load(bool hotReload)
{
var serverLocalizer = Core.Localizer;
}Server-Side Localization
Use Core.Localizer when the message is not player-specific.
string readyMessage = Core.Localizer["plugin.ready"];
string versionMessage = Core.Localizer["plugin.version", "1.2.0"];Player-Specific Localization
Use Core.Translation.GetPlayerLocalizer(player) when output should match the player's language.
public async Task GreetAsync(IPlayer player)
{
var localizer = Core.Translation.GetPlayerLocalizer(player);
string message = localizer["welcome.message", player.PlayerName];
await player.SendChatAsync(message);
}IPlayer.PlayerLanguage can be used for diagnostics or conditional flows:
Core.Logger.LogInformation("Player language: {Language}", player.PlayerLanguage.ToString());Placeholder Formatting
ILocalizer supports two indexer forms:
localizer["key"]localizer["key", arg0, arg1, ...]
Keep placeholder order stable across languages:
{
"round.win": "{0} won the round in {1} seconds"
}string text = localizer["round.win", "CT", 42];Key Design Best Practices
- Use stable dot-separated keys (for example
command.heal.success). - Keep the same key set across all language files.
- Add JSONC comments for translator context when placeholders are involved.
- Avoid string concatenation in code for translatable sentences.
Reference
See ITranslationService for translation service methods.
See ILocalizer for translation indexers.
See Language for supported language definitions.