OnPlayerLanguageChanged
OnPlayerLanguageChanged is called when a connected client changes its game language, after Oxide has already applied the new language to that player's message lookups.
void OnPlayerLanguageChanged(BasePlayer player, string val)Parameters
| Parameter | Type | Description |
|---|---|---|
player | BasePlayer | The connected player whose client language setting changed. Use it to read UserIDString, displayName or to send localized feedback. |
val | string | The new language identifier the client sent, for example `en`, `de` or `ru`. Pass it to `lang.SetLanguage` or store it for later lookups. |
Return value
The return value is ignored. The original method returns void and the call is not gated on your result, so returning null or any value has no effect on the language update.
How it works
The hook fires inside Oxide's own client-info handler (RustCore.OnPlayerSetInfo) when a connection sends the global.language key. That happens when the player picks a different language in the game options or when a fresh client first reports its language setting. Before the hook runs, Oxide calls lang.SetLanguage(val, userid) so all subsequent lang.GetMessage calls for that player return text in the new language.
player is the BasePlayer resolved from the connection; the hook only fires when that cast succeeds, so you never receive a null player here. val is the raw language string exactly as the client sent it, not normalized to a specific format. Use it to persist per-player language preferences, refresh custom CUI text, or log language shifts for support.
Oxide also fires a second variant with an IPlayer parameter right after the BasePlayer one. Declaring BasePlayer in your method means only the first call is dispatched to you; declaring IPlayer catches the second. Declaring neither match means your method is skipped entirely. Because this hook fires only on a settings change, it is cheap enough to do real work in.
Other signatures
Oxide also calls OnPlayerLanguageChanged from other places in the game with these parameter types. Declare the overload you need, or several:
void OnPlayerLanguageChanged(IPlayer iplayer, string val)
Example plugin: Store per-player language and reply in it
using System;
using System.Collections.Generic;
using Newtonsoft.Json;
using Oxide.Core;
using UnityEngine;
namespace Oxide.Plugins
{
[Info("Player Language Tracker", "RustPluginGenerator", "1.0.0")]
[Description("Stores the language each player selects and answers a chat command in it")]
public class PlayerLanguageTracker : RustPlugin
{
private const string PermAdmin = "playerlanguagetracker.admin";
private PluginConfig config;
private Dictionary<string, string> languages = new Dictionary<string, string>();
private class PluginConfig
{
[JsonProperty("Announce language change in chat")]
public bool AnnounceChange { get; set; } = true;
}
protected override void LoadDefaultConfig() => config = new PluginConfig();
protected override void LoadConfig()
{
base.LoadConfig();
try
{
config = Config.ReadObject<PluginConfig>();
if (config == null) throw new Exception();
}
catch
{
PrintWarning("Configuration file is invalid, using defaults");
LoadDefaultConfig();
}
SaveConfig();
}
protected override void SaveConfig() => Config.WriteObject(config);
protected override void LoadDefaultMessages()
{
lang.RegisterMessages(new Dictionary<string, string>
{
["LanguageSaved"] = "Your language is now set to {0}.",
["LanguageQuery"] = "The server has your language on record as {0}.",
["ListHeader"] = "Recorded player languages:",
["NoData"] = "No language data has been recorded yet."
}, this);
}
private string Msg(string key, string userId = null, params object[] args) =>
string.Format(lang.GetMessage(key, this, userId), args);
private void Init()
{
permission.RegisterPermission(PermAdmin, this);
LoadData();
}
private void LoadData()
{
languages = Interface.Oxide.DataFileSystem.ReadObject<Dictionary<string, string>>(Name)
?? new Dictionary<string, string>();
}
private void OnPlayerLanguageChanged(BasePlayer player, string val)
{
if (player == null || string.IsNullOrEmpty(val)) return;
languages[player.UserIDString] = val;
if (config.AnnounceChange)
{
player.ChatMessage(Msg("LanguageSaved", player.UserIDString, val));
}
}
[ChatCommand("lang")]
private void CmdLang(BasePlayer player, string command, string[] args)
{
if (player == null) return;
string stored;
string current = languages.TryGetValue(player.UserIDString, out stored) ? stored : "unknown";
player.ChatMessage(Msg("LanguageQuery", player.UserIDString, current));
}
[ChatCommand("langlist")]
private void CmdLangList(BasePlayer player, string command, string[] args)
{
if (player == null || !permission.UserHasPermission(player.UserIDString, PermAdmin)) return;
if (languages.Count == 0)
{
player.ChatMessage(Msg("NoData", player.UserIDString));
return;
}
player.ChatMessage(Msg("ListHeader", player.UserIDString));
foreach (KeyValuePair<string, string> entry in languages)
{
string name = BasePlayer.FindByID(ulong.Parse(entry.Key))?.displayName ?? entry.Key;
player.ChatMessage(name + " - " + entry.Value);
}
}
private void Unload()
{
Interface.Oxide.DataFileSystem.WriteObject(Name, languages);
}
}
}The plugin keeps a dictionary of user IDs to the last language string each player reported and saves it to oxide/data. When OnPlayerLanguageChanged fires it updates the entry and sends a chat confirmation through the language system, so the player sees it in the language they just selected. A chat command lang lets players ask which language the server currently has on record for them, read from the saved data. Unload writes the data file so nothing is lost on reload. All player-facing strings go through the message table.
Common use cases
Persist language preferences
Record each player's chosen language in a data file so other systems (welcome messages, ticket replies) can format text in the right language without asking again.
Refresh custom UI
Rebuild any open CUI overlay after the change so labels that were cached as plain strings switch to the new language immediately.
Report language distribution
Increase a counter per language and print a breakdown on server restart to see which locales your population actually uses.
Pitfalls
Fires for the initial report too
The client sends
global.languageon connect as well as on an explicit change, so do not treat every call as a deliberate user action when counting changes.Two dispatch variants exist
Oxide calls the hook once with
BasePlayerand once withIPlayer. Declare exactly one parameter type; a method typed for the other variant will not be called.Language strings are not validated
valis passed through as the client sent it. Do not assume two-letter codes; normalize or whitelist before storing if you depend on a fixed set.
Build a plugin with OnPlayerLanguageChanged
Describe what should happen and get a compiled plugin that uses this hook.
Edit the prompt on the next screen. 2 free plugins a month, no card.
Last updated Oct 26, 2026. Found a mistake? Email support@rustplugingenerator.com.
Build a plugin with OnPlayerLanguageChanged
Generate