Skip to content

Translation audit: fr/ru/es/de/pt/chi for the 33 plugins with phrase files + typo fixes #5

Open

Description

@WareZ57

Hi! I went through every sm-plugin-* repo that ships a .phrases.txt (33 of them) and prepared one patch per repo:

  • every phrase now has en, fr, ru, es, de, pt, chi (existing extra languages such as zho, fi, ar… are kept, never removed)
  • spelling/grammar fixes in existing languages, including English, plus a few stale translations that no longer matched the English text
  • no code change: phrase names, #format lines, {N} parameters, color tags, %s/\n escapes, encoding, BOM and CRLF/LF are untouched (checked by a script against the original files: 0 errors on all 38 files; PropWatch gets new fr/es/de/pt/chi/ subfolder files like the existing ru/ one)

Patches (git am-ready, one per repo): https://gist.github.com/WareZ57/76680446b96d69fc8cf11aee20ce96e1

curl -sL https://gist.githubusercontent.com/WareZ57/76680446b96d69fc8cf11aee20ce96e1/raw/KnockbackRestrict.patch | git am

CustomChatColors is shipped base64-encoded (CustomChatColors.patch.b64) because allchat.phrases.txt contains raw \x01/\x03 color bytes that gists reject: base64 -d CustomChatColors.patch.b64 | git am.

Repos covered

BlockerPasses, BoostAlert, BossHUD, CustomChatColors, Extend, FixMemoryLeak, FunModes, GlowColors, HideTeammates, HitMarker, KnockbackRestrict, NameFilter, NoBlock, NoShake, Prop, PropWatch, RadioSpamBlock, RenderDistance, ResetScore, Shop_CreditsDistributor, Shop_MathCredits, Shop_Paintball, Shop_ZRSkins, ShowDamage, StopSound, TopDefenders, TopInfectors, TriggerWatcher, VIP_Test, VIP_Tracers, VIP_VIPsOnline, ZR_LaserMines, ZR_RepeatKillDetector

Notable fixes to existing text
  • HideTeammates (ru): 8 phrases were stale after the en rewording (still said "Включено", "Всей карте" for All Teammates, typo "Диапозон"); stray {green} removed
  • VIP_Test (fr): "Les fonctionnalités VIP sera disponible…" → wrong agreement and meaning; fi rewritten too
  • VIP_VIPsOnline: cze PANEL_CLOSE was still English ("Exit"), de said "Verlassen" instead of "Schließen"
  • FixMemoryLeak: en "has not be set"; zho/chi typos 以→已, 在→再, 一→已; ru Scheduled/Canceled turned into verbs since they're inserted mid-sentence
  • RadioSpamBlock (ru) "новго", Shop_Paintball (ru) "ууль", GlowColors (ru) "доступка", Shop_MathCredits (ru) missing half sentence
  • Prop / ResetScore (en): "reseted" → "reset"; TopDefenders (en): "have" → "has" (admin name is prepended)
  • HitMarker (zho): simplified 弹 in the traditional line
  • FunModes VIPMode_VIPDeathLaser (en): "Refusing Killing All humans!" → "Humans will not be killed!" (matches RemoveClientVIP(client, false))
  • French typography/accents in TriggerWatcher, BoostAlert, BossHUD, NoShake ("Le Map" → "La map")

Code issues found along the way (not in the patches)

  1. FunModes VIPMode.sp:300: RemoveClientVIP(client, false) is called without a translation, then RemoveClientVIP runs CPrintToChatAll("%s %t", tag, "", client) → Language phrase "" not found error on every laser death of a VIP.
  2. BossHUD BossHUD.sp:811: "Show health has been" is followed by raw English Enabled/Disabled, so the message is half-English in every language.
  3. KnockbackRestrict KnockbackRestrict.sp:1212-1220: the "Minutes"/"Hours" phrase names are passed as plain %s, so the time left in RestrictTimeLeft is always English.
  4. Prop: phrases "Enabled"/"Disabled" are unused.

Partially addresses srcdslab/sm-plugin-HideTeammates#1 ("Better translations").

Open question: ~55 other plugins still print hard-coded English (SprayManager, ExtraCommands, SelfMute…). Happy to help convert them to %t if you want that, one repo at a time.

Activity

  1. Rushaway commented on Nov 1, 2026

    @Rushaway
    Member

    Thanks a lot for this audit, it was really useful. Everything is now up as PRs, and you are co-author of every commit.

    Your 33 patches

    They are applied, reviewed and completed, one PR per repo:

    srcdslab/sm-plugin-BlockerPasses#16, srcdslab/sm-plugin-BoostAlert#37, srcdslab/sm-plugin-BossHUD#61, srcdslab/sm-plugin-CustomChatColors#107, srcdslab/sm-plugin-Extend#33, srcdslab/sm-plugin-FixMemoryLeak#43, srcdslab/sm-plugin-FunModes#55, srcdslab/sm-plugin-GlowColors#43, srcdslab/sm-plugin-HideTeammates#28, srcdslab/sm-plugin-HitMarker#35, srcdslab/sm-plugin-KnockbackRestrict#68, srcdslab/sm-plugin-NameFilter#34, srcdslab/sm-plugin-NoBlock#33, srcdslab/sm-plugin-NoShake#36, srcdslab/sm-plugin-Prop#4, srcdslab/sm-plugin-PropWatch#27, srcdslab/sm-plugin-RadioSpamBlock#29, srcdslab/sm-plugin-RenderDistance#29, srcdslab/sm-plugin-ResetScore#27, srcdslab/sm-plugin-Shop_CreditsDistributor#20, srcdslab/sm-plugin-Shop_MathCredits#20, srcdslab/sm-plugin-Shop_Paintball#29, srcdslab/sm-plugin-Shop_ZRSkins#33, srcdslab/sm-plugin-StopSound#37, srcdslab/sm-plugin-TopDefenders#65, srcdslab/sm-plugin-TopInfectors#48, srcdslab/sm-plugin-TriggerWatcher#38, srcdslab/sm-plugin-VIP_Test#37, srcdslab/sm-plugin-VIP_Tracers#30, srcdslab/sm-plugin-VIP_VIPsOnline#32, srcdslab/sm-plugin-ZR_LaserMines#29, srcdslab/sm-plugin-ZR_RepeatKillDetector#35, srcdslab/sm-plugin-ShowDamage#5

    Compared with the gist:

    • 6 patches were incomplete (HitMarker, Prop, ResetScore, Shop_MathCredits, TopDefenders, TopInfectors): about 320 strings were still missing. They are filled in.
    • CustomChatColors: the new allchat.phrases.txt lines had lost the raw \x01/\x03 bytes of en. They are restored byte for byte.
    • A second review pass fixed a few more lines in most repos (details in each PR).
    • All 33 pass smtranslate --languages en,fr,ru,es,de,pt,chi with 0 errors and 0 warnings, and CI is green.

    The code issues you listed

    1. FunModes: fixed in feat(translations): complete fr, ru, es, de, pt and chi translations sm-plugin-FunModes#55. It also fixes 8 VIPMode/HealBeacon announcements that were sent to everyone with %T, in one player's language.
    2. BossHUD: fixed in feat(translations): complete fr, ru, es, de, pt and chi translations sm-plugin-BossHUD#61, along with the hard-coded sm_subtracthp reply.
    3. KnockbackRestrict: fixed in feat(translations): complete fr, ru, es, de, pt and chi translations sm-plugin-KnockbackRestrict#68, with singular/plural units and the exact 1 hour / 1 day cases. The same PR fixes RestrictedPermaOffline, which declared {2:N} but is given a name string, so the permanent offline kban announcement errored.
    4. Prop: the unused Enabled/Disabled phrases are kept (harmless).

    The review found a few more translation bugs, fixed in the same PRs:

    • ZR_LaserMines used a phrase that does not exist.
    • The PropWatch HUD was always in the server language.
    • RenderDistance and NameFilter buffers cut translated text.
    • BlockerPasses printed the wrong phrase in one case.
    • FixMemoryLeak gave the wrong reply to sm_forcerestartcmds.
    • Prop printed @all target names untranslated.
    • The VIP_Tracers menu had no translation target.

    Repos with phrase files that the audit did not cover

    These had whole languages missing: srcdslab/sm-plugin-mapchooser-extended#130, srcdslab/sm-plugin-ZLeader#23, srcdslab/sm-plugin-entwatch-4#131, srcdslab/sm-plugin-MusicName#12, srcdslab/sm-plugin-autosilencer#26, srcdslab/sm-plugin-nativevotes#5, srcdslab/sm-plugin-QuakeSounds#47, srcdslab/sm-plugin-Shop-Core#17, srcdslab/sm-plugin-VIP-Core#36.

    entwatch-4 also gets the 2 phrases its code uses but the file lacked. Shop-Core's release package now ships its translations, which it never did before.

    Your open question: yes, and it's done

    The plugins that printed hard-coded English now have a phrase file with the 7 languages, which CI lints and packages:

    srcdslab/sm-plugin-ExtraCommands#40, srcdslab/sm-plugin-CommandBlocker#20, srcdslab/sm-plugin-ConVarEnforcer#4, srcdslab/sm-plugin-ConVarSuppression#26, srcdslab/sm-plugin-CommandHelper#25, srcdslab/sm-plugin-ConsoleChatManager#52, srcdslab/sm-plugin-FixHostTimeScale#23, srcdslab/sm-plugin-SMTextReplace#29, srcdslab/sm-plugin-DevZones#26, srcdslab/sm-plugin-SaveLevel#36, srcdslab/sm-plugin-Teleport#26, srcdslab/sm-plugin-stripper#20, srcdslab/sm-plugin-EntWatch-Maker#10, srcdslab/sm-plugin-FireCustomizer#4, srcdslab/sm-plugin-SelfMute#41, srcdslab/sm-plugin-SelfMuteRadio#29, srcdslab/sm-plugin-Spectate#51, srcdslab/sm-plugin-ThirdPerson#42, srcdslab/sm-plugin-TeamManager#47, srcdslab/sm-plugin-Parachute#14, srcdslab/sm-plugin-Laser#32, srcdslab/sm-plugin-ZR_PersonalSkins#43, srcdslab/sm-plugin-VScript-MakoReactor#32, srcdslab/sm-plugin-PotcVote#31, srcdslab/sm-plugin-TogsJumpStats#30, srcdslab/sm-plugin-AntiBhopCheat#42, srcdslab/sm-plugin-SelectiveBhop#28, srcdslab/sm-plugin-AdvancedTargeting#41, srcdslab/sm-plugin-LagCompensation#40, srcdslab/sm-plugin-Shop_Chat#30, srcdslab/sm-plugin-AFKManager#52, srcdslab/sm-plugin-ForceInputs#30, srcdslab/sm-plugin-ConnectAnnounce#68, srcdslab/sm-plugin-KnifeMode#53, srcdslab/sm-plugin-CallAdmin#56, srcdslab/sm-plugin-CancelKnife#27, srcdslab/sm-plugin-VIP_FreeVIPGiveaway#32, srcdslab/sm-plugin-CountryTags#40, srcdslab/sm-plugin-GrenadeManager#3, srcdslab/sm-plugin-ZR_Ammo#25, srcdslab/sm-plugin-AdvancedAdminList#43, srcdslab/sm-plugin-Shop_Nades#19, srcdslab/sm-plugin-Shop_Top10#27, srcdslab/sm-plugin-Shop_Trails#30, srcdslab/sm-plugin-Shop_Aura#23, srcdslab/sm-plugin-VIP_SourcemodFlags#32, srcdslab/sm-plugin-NoAloneInfectionNoArmor#4, srcdslab/sm-plugin-Shop_GrenadeTrails#19, srcdslab/sm-plugin-Shop_OnlineCredits#18, srcdslab/sm-plugin-Shop_Tracers#19, srcdslab/sm-plugin-Shop_WeaponColors#19, srcdslab/sm-plugin-SprayManager#68, srcdslab/sm-plugin-CIDR#34, srcdslab/sm-plugin-FamilyShareManager#38, srcdslab/sm-plugin-FixSprayExploit#39, srcdslab/sm-plugin-PlayerManager#55, srcdslab/sm-plugin-ProxyKiller#50, srcdslab/sm-plugin-ZEEliteDetector#52, srcdslab/sm-plugin-AutoRecorder#19

    Skipped for now:

    MultiColors' CShowActivity2 formatted the message once in the server language, so every translated admin-activity line reached all players in the server language. The fix is srcdslab/sm-plugin-MultiColors#5.

    Still left (not in these PRs)

    Hard-coded English remains in plugins that already have phrase files:

    • FunModes: about 600 lines across the modes.
    • CustomChatColors: about 160.
    • HitMarker: the menus.
    • Extend: the units.
    • TopDefenders: the round-end title, which is built once in the server language.
  2. WareZ57 commented on Nov 1, 2026

    @WareZ57
    Author

    Thanks for picking this up so fast! I checked all 81 translation PRs you opened against what I had prepared (CI-like build with spcomp 1.12 + a phrase linter that also flags phrases missing a language): all build, no phrase-file errors, and the typos / code issues listed above are fixed 👍

    What was still missing is now up for review, without duplicating your work:

    Follow-ups targeting your feat/translations branches (only the additions show — hard-coded messages that your PR, which completes the existing phrase file, didn't convert):

    Repo PR Builds on
    FunModes srcdslab/sm-plugin-FunModes#56 #55
    CustomChatColors srcdslab/sm-plugin-CustomChatColors#108 #107
    BlockerPasses srcdslab/sm-plugin-BlockerPasses#17 #16
    KnockbackRestrict srcdslab/sm-plugin-KnockbackRestrict#69 #68
    HitMarker srcdslab/sm-plugin-HitMarker#36 #35
    GlowColors srcdslab/sm-plugin-GlowColors#44 #43
    RadioSpamBlock srcdslab/sm-plugin-RadioSpamBlock#30 #29
    VIP_Tracers srcdslab/sm-plugin-VIP_Tracers#31 #30
    NameFilter srcdslab/sm-plugin-NameFilter#35 #34
    StopSound srcdslab/sm-plugin-StopSound#38 #37

    As patches (GitHub is rate-limiting fork creation on my account, so these 5 couldn't become PRs yet). Each is 2 commits (translation + version bump), builds with spcomp 1.12 and passes the phrase check:

    • ExtraCommands — on top of feat/translations (#40): the sm_uptime reply to the server console was still English (text + the "%d Days … Seconds" value; the existing "Uptime Value" phrase has colour tags that would show raw in the console, so a colour-free one is added).
    • AdminRoom, BossHP, BotManager, VIP_Neon — on top of master (no translation PR yet): hard-coded messages converted, new phrase file in en/fr/ru/es/de/pt/chi, CI packages translations. AdminRoom will conflict with the old #34 if that one is merged first.

    Apply with git am (save the block to a file first).

    ExtraCommands.patch
    From c8a2d3d80f4175da1a1aad8fa9252289baf92851 Mon Sep 17 00:00:00 2001
    From: WareZ57 <7504466+WareZ57@users.noreply.github.com>
    Date: Sun, 1 Nov 2026 01:19:54 +0100
    Subject: [PATCH 1/2] fix(translations): translate the remaining hard-coded
     message
    
    ---
     addons/sourcemod/scripting/ExtraCommands.sp           |  4 ++--
     .../sourcemod/translations/ExtraCommands.phrases.txt  | 11 +++++++++++
     2 files changed, 13 insertions(+), 2 deletions(-)
    
    diff --git a/addons/sourcemod/scripting/ExtraCommands.sp b/addons/sourcemod/scripting/ExtraCommands.sp
    index c2f5200..edc1966 100644
    --- a/addons/sourcemod/scripting/ExtraCommands.sp
    +++ b/addons/sourcemod/scripting/ExtraCommands.sp
    @@ -1893,8 +1893,8 @@ public Action Command_Uptime(int client, int args)
     
     	if (!client)
     	{
    -		FormatEx(UpTime, sizeof(UpTime), "%d Days %02d Hours %02d Minutes %02d Seconds", Days, Hours, Mins, Secs);
    -		ReplyToCommand(client, "[SM] Server uptime: %s", UpTime);
    +		FormatEx(UpTime, sizeof(UpTime), "%T", "Uptime Value Plain", LANG_SERVER, Days, Hours, Mins, Secs);
    +		ReplyToCommand(client, "[SM] %t", "Server Uptime", UpTime);
     	}
     	else
     	{
    diff --git a/addons/sourcemod/translations/ExtraCommands.phrases.txt b/addons/sourcemod/translations/ExtraCommands.phrases.txt
    index 4c2daf9..cec188e 100644
    --- a/addons/sourcemod/translations/ExtraCommands.phrases.txt
    +++ b/addons/sourcemod/translations/ExtraCommands.phrases.txt
    @@ -836,6 +836,17 @@
     		"pt"		"{green}{1} {default}dias {green}{2} {default}horas {green}{3} {default}minutos {green}{4} {default}segundos."
     		"chi"		"{green}{1} {default}天 {green}{2} {default}小时 {green}{3} {default}分钟 {green}{4} {default}秒。"
     	}
    +	"Uptime Value Plain"
    +	{
    +		"#format"	"{1:d},{2:02d},{3:02d},{4:02d}"
    +		"en"		"{1} Days {2} Hours {3} Minutes {4} Seconds"
    +		"fr"		"{1} jours {2} heures {3} minutes {4} secondes"
    +		"ru"		"{1} д. {2} ч. {3} мин. {4} сек."
    +		"es"		"{1} días {2} horas {3} minutos {4} segundos"
    +		"de"		"{1} Tage {2} Stunden {3} Minuten {4} Sekunden"
    +		"pt"		"{1} dias {2} horas {3} minutos {4} segundos"
    +		"chi"		"{1} 天 {2} 小时 {3} 分钟 {4} 秒"
    +	}
     	"Server Uptime"
     	{
     		"#format"	"{1:s}"
    -- 
    2.47.3
    
    
    From 298c6f2ab61639cced8ab18f3fb7aba6e98e5880 Mon Sep 17 00:00:00 2001
    From: WareZ57 <7504466+WareZ57@users.noreply.github.com>
    Date: Sun, 1 Nov 2026 01:19:54 +0100
    Subject: [PATCH 2/2] chore: bump version to 2.7.18
    
    ---
     addons/sourcemod/scripting/ExtraCommands.sp | 2 +-
     1 file changed, 1 insertion(+), 1 deletion(-)
    
    diff --git a/addons/sourcemod/scripting/ExtraCommands.sp b/addons/sourcemod/scripting/ExtraCommands.sp
    index edc1966..ee35803 100644
    --- a/addons/sourcemod/scripting/ExtraCommands.sp
    +++ b/addons/sourcemod/scripting/ExtraCommands.sp
    @@ -44,7 +44,7 @@ public Plugin myinfo =
     	name        = "Advanced Commands",
     	author      = "BotoX + Obus + maxime1907, .Rushaway",
     	description = "Adds extra commands for admins.",
    -	version     = "2.7.17",
    +	version     = "2.7.18",
     	url         = ""
     };
     
    -- 
    2.47.3
    
    AdminRoom.patch
    From 4f91de6c330148e7719d1368afeb6ce6188ca3e5 Mon Sep 17 00:00:00 2001
    From: WareZ57 <7504466+WareZ57@users.noreply.github.com>
    Date: Sat, 31 Oct 2026 23:42:53 +0100
    Subject: [PATCH 1/2] feat(translations): translate hard-coded messages
    
    ---
     .github/workflows/ci.yml                      |   2 +
     addons/sourcemod/scripting/AdminRoom.sp       |  42 ++--
     .../translations/AdminRoom.phrases.txt        | 205 ++++++++++++++++++
     3 files changed, 230 insertions(+), 19 deletions(-)
     create mode 100644 addons/sourcemod/translations/AdminRoom.phrases.txt
    
    diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
    index aecc23f..1637954 100644
    --- a/.github/workflows/ci.yml
    +++ b/.github/workflows/ci.yml
    @@ -40,6 +40,8 @@ jobs:
               set -euo pipefail
               mkdir -p /tmp/package/addons/sourcemod/plugins
               cp addons/sourcemod/plugins/*.smx /tmp/package/addons/sourcemod/plugins/
    +          mkdir -p /tmp/package/addons/sourcemod/translations
    +          cp -R addons/sourcemod/translations/* /tmp/package/addons/sourcemod/translations/
               cp -R addons/sourcemod/configs /tmp/package/addons/sourcemod/
     
           - name: Upload build archive for test runners
    diff --git a/addons/sourcemod/scripting/AdminRoom.sp b/addons/sourcemod/scripting/AdminRoom.sp
    index a0edc30..ca070c4 100644
    --- a/addons/sourcemod/scripting/AdminRoom.sp
    +++ b/addons/sourcemod/scripting/AdminRoom.sp
    @@ -48,6 +48,7 @@ public APLRes AskPluginLoad2(Handle myself, bool late, char[] error, int err_max
     public void OnPluginStart()
     {
     	LoadTranslations("common.phrases");
    +	LoadTranslations("AdminRoom.phrases");
     
     	RegAdminCmd("sm_adminroom_reloadcfg", Command_ReloadConfig, ADMFLAG_CONFIG, "Reload both map and keyword configs");
     	RegAdminCmd("sm_adminroom", Command_AdminRoom, ADMFLAG_BAN, "Teleport anyone to the admin room");
    @@ -93,7 +94,7 @@ public Action Command_ReloadConfig(int client, int argc)
     	g_bLateLoad = true;
     	OnMapStart();
     
    -	CReplyToCommand(client, "%s AdminRoom configs reloaded.", TAG_COLOR);
    +	CReplyToCommand(client, "%s %t", TAG_COLOR, "AR_Reloaded");
     	LogAction(client, -1, "[AdminRoom] %L Reloaded the configs files.", client);
     	return Plugin_Handled;
     }
    @@ -105,19 +106,19 @@ public Action Command_Stage(int client, int argc)
     
     	if (!g_AdminRoom.bEnabled)
     	{
    -		CReplyToCommand(client, "%s The current map is not supported.", TAG_COLOR);
    +		CReplyToCommand(client, "%s %t", TAG_COLOR, "AR_MapNotSupported");
     		return Plugin_Handled;
     	}
     
     	if (!cStages || cStages.Length <= 0)
     	{
    -		CReplyToCommand(client, "%s The current map either does not have stages or is incorrectly configured.", TAG_COLOR);
    +		CReplyToCommand(client, "%s %t", TAG_COLOR, "AR_NoStages");
     		return Plugin_Handled;
     	}
     
     	if (argc < 1)
     	{
    -		CReplyToCommand(client, "%s Available stages :", TAG_COLOR);
    +		CReplyToCommand(client, "%s %t", TAG_COLOR, "AR_AvailableStages");
     
     		for (int i = 0; i < cStages.Length; i++)
     		{
    @@ -212,10 +213,10 @@ public Action Command_Stage(int client, int argc)
     						{
     							// Handling client for plugins who change stages (to be able to debug it)
     							if (client > 0)
    -								CReplyToCommand(client, "%s Can not set \"{olive}%s{default}\". Button (#%s) is locked.", TAG_COLOR, sName, sIdentifier);
    +								CReplyToCommand(client, "%s %t", TAG_COLOR, "AR_ButtonLocked", sName, sIdentifier);
     							else
     							{
    -								CPrintToChatAll("%s Can not set \"{olive}%s{default}\". Button (#%s) is locked.", TAG_COLOR, sName, sIdentifier);
    +								CPrintToChatAll("%s %t", TAG_COLOR, "AR_ButtonLocked", sName, sIdentifier);
     								LogAction(client, -1, "\"%L\" tried to set \"%s\" but the button (#%s) is locked.", client, sName, sIdentifier);
     							}
     
    @@ -228,16 +229,16 @@ public Action Command_Stage(int client, int argc)
     		}
     
     		if (client > 0)
    -			CShowActivity2(client, "{green}[SM] {olive}", "{default}Changed the stage to {green}%s{default}.", sName);
    +			CShowActivity2(client, "{green}[SM] {olive}", "%t", "AR_StageChangedColor", sName);
     		else
    -			ShowActivity2(client, "[SM] ", "Changed the stage to %s.", sName);
    +			ShowActivity2(client, "[SM] ", "%t", "AR_StageChanged", sName);
     
     		LogAction(client, -1, "\"%L\" changed the stage to \"%s\".", client, sName);
     
     		return Plugin_Handled;
     	}
     
    -	CReplyToCommand(client, "%s Invalid stage %s", TAG_COLOR, sArg);
    +	CReplyToCommand(client, "%s %t", TAG_COLOR, "AR_InvalidStage", sArg);
     	return Plugin_Handled;
     }
     
    @@ -245,13 +246,13 @@ public Action Command_AdminRoom(int client, int argc)
     {
     	if (!client)
     	{
    -		CReplyToCommand(client, "%s Console cannot be teleported.", TAG_COLOR);
    +		CReplyToCommand(client, "%s %t", TAG_COLOR, "AR_ConsoleNoTeleport");
     		return Plugin_Handled;
     	}
     
     	if(argc > 1)
     	{
    -		ReplyToCommand(client, "[SM] Usage: sm_adminroom [#userid|name]");
    +		ReplyToCommand(client, "[SM] %t", "AR_Usage");
     		return Plugin_Handled;
     	}
     
    @@ -280,15 +281,15 @@ public Action Command_AdminRoom(int client, int argc)
     			if ((g_AdminRoom.GetAdminRoomLocations(cAdminRoomLocations) && cAdminRoomLocations.Length > 0) || g_cAdminRoomLocationsDetected.Length > 0)
     			{
     				Menu_AdminRoom(iTargets[i]);
    -				CReplyToCommand(client, "%s AdminRoom Menu has been sent to {olive}%N{default}.", TAG_COLOR, iTargets);
    +				CReplyToCommand(client, "%s %t", TAG_COLOR, "AR_MenuSent", iTargets);
     				LogAction(client, client, "\"%L\" printed the AdminRoom Menu to \"%L\".", client, iTargets);
     			}
     			else
    -				CReplyToCommand(client, "%s Unable to detect any admin room.", TAG_COLOR);
    +				CReplyToCommand(client, "%s %t", TAG_COLOR, "AR_NoAdminRoom");
     		}
     	}
     	else
    -		CReplyToCommand(client, "%s Only one target can be reached.", TAG_COLOR);
    +		CReplyToCommand(client, "%s %t", TAG_COLOR, "AR_OneTarget");
     
     	return Plugin_Handled;
     }
    @@ -299,8 +300,11 @@ void Menu_AdminRoom(int client)
     
     	menuHandle[client] = menu;
     
    -	menu.SetTitle("Admin Room");
    -	menu.AddItem("-1", "Get out (saved position)");
    +	menu.SetTitle("%T", "AR_MenuTitle", client);
    +
    +	char sGetOut[64];
    +	FormatEx(sGetOut, sizeof(sGetOut), "%T", "AR_MenuGetOut", client);
    +	menu.AddItem("-1", sGetOut);
     
     	ArrayList cAdminRoomLocations;
     	if (g_AdminRoom.GetAdminRoomLocations(cAdminRoomLocations) && cAdminRoomLocations.Length)
    @@ -877,7 +881,7 @@ stock void GoToEntity(int client, CAdminRoomLocation cAdminRoomLocation)
     
     	TeleportEntity(client, entityposition, NULL_VECTOR, NULL_VECTOR);
     
    -	CShowActivity2(client, "{green}[SM] {olive}", "{default}has been teleported to the adminroom.");
    +	CShowActivity2(client, "{green}[SM] {olive}", "%t", "AR_Teleported");
     
     	LogAction(client, -1, "\"%L\" teleported himself to the adminroom.", client);
     
    @@ -888,12 +892,12 @@ stock void GetOut(int client)
     {
     	if (g_fPlayerOrigin[client][0] == 0.0 && g_fPlayerOrigin[client][1] == 0.0 && g_fPlayerOrigin[client][2] == 0.0)
     	{
    -		CPrintToChat(client, "%s Could not teleport you because your position was not saved.", TAG_COLOR);
    +		CPrintToChat(client, "%s %t", TAG_COLOR, "AR_NoSavedPosition");
     		return;
     	}
     
     	TeleportEntity(client, g_fPlayerOrigin[client], NULL_VECTOR, NULL_VECTOR);
    -	CPrintToChat(client, "%s You have left the admin room.", TAG_COLOR);
    +	CPrintToChat(client, "%s %t", TAG_COLOR, "AR_LeftRoom");
     	LogAction(client, -1, "\"%L\" teleported himself OUT of the adminroom.", client);
     }
     
    diff --git a/addons/sourcemod/translations/AdminRoom.phrases.txt b/addons/sourcemod/translations/AdminRoom.phrases.txt
    new file mode 100644
    index 0000000..f25ec04
    --- /dev/null
    +++ b/addons/sourcemod/translations/AdminRoom.phrases.txt
    @@ -0,0 +1,205 @@
    +"Phrases"
    +{
    +	"AR_Reloaded"
    +	{
    +		"en"			"AdminRoom configs reloaded."
    +		"fr"			"Configs AdminRoom rechargées."
    +		"ru"			"Конфиги AdminRoom перезагружены."
    +		"es"			"Configuraciones de AdminRoom recargadas."
    +		"de"			"AdminRoom-Konfigurationen neu geladen."
    +		"pt"			"Configurações do AdminRoom recarregadas."
    +		"chi"			"AdminRoom 配置已重新加载。"
    +	}
    +
    +	"AR_MapNotSupported"
    +	{
    +		"en"			"The current map is not supported."
    +		"fr"			"La map actuelle n'est pas prise en charge."
    +		"ru"			"Текущая карта не поддерживается."
    +		"es"			"El mapa actual no es compatible."
    +		"de"			"Die aktuelle Map wird nicht unterstützt."
    +		"pt"			"O mapa atual não é suportado."
    +		"chi"			"当前地图不受支持。"
    +	}
    +
    +	"AR_NoStages"
    +	{
    +		"en"			"The current map either does not have stages or is incorrectly configured."
    +		"fr"			"La map actuelle n'a pas de stages ou est mal configurée."
    +		"ru"			"На текущей карте нет этапов или она неправильно настроена."
    +		"es"			"El mapa actual no tiene fases o está mal configurado."
    +		"de"			"Die aktuelle Map hat keine Stages oder ist falsch konfiguriert."
    +		"pt"			"O mapa atual não tem fases ou está configurado incorretamente."
    +		"chi"			"当前地图没有关卡或配置错误。"
    +	}
    +
    +	"AR_AvailableStages"
    +	{
    +		"en"			"Available stages :"
    +		"fr"			"Stages disponibles :"
    +		"ru"			"Доступные этапы:"
    +		"es"			"Fases disponibles:"
    +		"de"			"Verfügbare Stages:"
    +		"pt"			"Fases disponíveis:"
    +		"chi"			"可用关卡:"
    +	}
    +
    +	"AR_ButtonLocked"
    +	{
    +		"#format"		"{1:s},{2:s}"
    +		"en"			"Can not set \"{olive}{1}{default}\". Button (#{2}) is locked."
    +		"fr"			"Impossible de régler \"{olive}{1}{default}\". Le bouton (#{2}) est verrouillé."
    +		"ru"			"Невозможно установить \"{olive}{1}{default}\". Кнопка (#{2}) заблокирована."
    +		"es"			"No se puede establecer \"{olive}{1}{default}\". El botón (#{2}) está bloqueado."
    +		"de"			"\"{olive}{1}{default}\" kann nicht gesetzt werden. Knopf (#{2}) ist gesperrt."
    +		"pt"			"Não é possível definir \"{olive}{1}{default}\". O botão (#{2}) está travado."
    +		"chi"			"无法设置 \"{olive}{1}{default}\"。按钮 (#{2}) 已锁定。"
    +	}
    +
    +	"AR_StageChangedColor"
    +	{
    +		"#format"		"{1:s}"
    +		"en"			"{default}Changed the stage to {green}{1}{default}."
    +		"fr"			"{default}A changé le stage en {green}{1}{default}."
    +		"ru"			"{default}Сменил этап на {green}{1}{default}."
    +		"es"			"{default}Cambió la fase a {green}{1}{default}."
    +		"de"			"{default}Hat die Stage auf {green}{1}{default} geändert."
    +		"pt"			"{default}Mudou a fase para {green}{1}{default}."
    +		"chi"			"{default}将关卡切换为 {green}{1}{default}。"
    +	}
    +
    +	"AR_StageChanged"
    +	{
    +		"#format"		"{1:s}"
    +		"en"			"Changed the stage to {1}."
    +		"fr"			"A changé le stage en {1}."
    +		"ru"			"Сменил этап на {1}."
    +		"es"			"Cambió la fase a {1}."
    +		"de"			"Hat die Stage auf {1} geändert."
    +		"pt"			"Mudou a fase para {1}."
    +		"chi"			"将关卡切换为 {1}。"
    +	}
    +
    +	"AR_InvalidStage"
    +	{
    +		"#format"		"{1:s}"
    +		"en"			"Invalid stage {1}"
    +		"fr"			"Stage invalide {1}"
    +		"ru"			"Неверный этап {1}"
    +		"es"			"Fase no válida {1}"
    +		"de"			"Ungültige Stage {1}"
    +		"pt"			"Fase inválida {1}"
    +		"chi"			"无效关卡 {1}"
    +	}
    +
    +	"AR_ConsoleNoTeleport"
    +	{
    +		"en"			"Console cannot be teleported."
    +		"fr"			"La console ne peut pas être téléportée."
    +		"ru"			"Консоль нельзя телепортировать."
    +		"es"			"La consola no puede ser teletransportada."
    +		"de"			"Die Konsole kann nicht teleportiert werden."
    +		"pt"			"O console não pode ser teleportado."
    +		"chi"			"控制台无法被传送。"
    +	}
    +
    +	"AR_Usage"
    +	{
    +		"en"			"Usage: sm_adminroom [#userid|name]"
    +		"fr"			"Utilisation : sm_adminroom [#userid|nom]"
    +		"ru"			"Использование: sm_adminroom [#userid|ник]"
    +		"es"			"Uso: sm_adminroom [#userid|nombre]"
    +		"de"			"Verwendung: sm_adminroom [#userid|Name]"
    +		"pt"			"Uso: sm_adminroom [#userid|nome]"
    +		"chi"			"用法: sm_adminroom [#userid|名字]"
    +	}
    +
    +	"AR_MenuSent"
    +	{
    +		"#format"		"{1:N}"
    +		"en"			"AdminRoom Menu has been sent to {olive}{1}{default}."
    +		"fr"			"Le menu AdminRoom a été envoyé à {olive}{1}{default}."
    +		"ru"			"Меню AdminRoom отправлено игроку {olive}{1}{default}."
    +		"es"			"El menú AdminRoom se ha enviado a {olive}{1}{default}."
    +		"de"			"Das AdminRoom-Menü wurde an {olive}{1}{default} gesendet."
    +		"pt"			"O menu AdminRoom foi enviado para {olive}{1}{default}."
    +		"chi"			"AdminRoom 菜单已发送给 {olive}{1}{default}。"
    +	}
    +
    +	"AR_NoAdminRoom"
    +	{
    +		"en"			"Unable to detect any admin room."
    +		"fr"			"Impossible de détecter une admin room."
    +		"ru"			"Не удалось обнаружить админ-комнату."
    +		"es"			"No se pudo detectar ninguna sala de admin."
    +		"de"			"Es konnte kein Adminraum erkannt werden."
    +		"pt"			"Não foi possível detectar nenhuma sala de admin."
    +		"chi"			"未检测到任何管理员房间。"
    +	}
    +
    +	"AR_OneTarget"
    +	{
    +		"en"			"Only one target can be reached."
    +		"fr"			"Une seule cible peut être atteinte."
    +		"ru"			"Можно выбрать только одну цель."
    +		"es"			"Solo se puede alcanzar un objetivo."
    +		"de"			"Es kann nur ein Ziel erreicht werden."
    +		"pt"			"Apenas um alvo pode ser alcançado."
    +		"chi"			"只能选择一个目标。"
    +	}
    +
    +	"AR_MenuTitle"
    +	{
    +		"en"			"Admin Room"
    +		"fr"			"Admin Room"
    +		"ru"			"Админ-комната"
    +		"es"			"Sala de admin"
    +		"de"			"Adminraum"
    +		"pt"			"Sala de admin"
    +		"chi"			"管理员房间"
    +	}
    +
    +	"AR_MenuGetOut"
    +	{
    +		"en"			"Get out (saved position)"
    +		"fr"			"Sortir (position sauvegardée)"
    +		"ru"			"Выйти (сохранённая позиция)"
    +		"es"			"Salir (posición guardada)"
    +		"de"			"Raus (gespeicherte Position)"
    +		"pt"			"Sair (posição salva)"
    +		"chi"			"离开(已保存位置)"
    +	}
    +
    +	"AR_Teleported"
    +	{
    +		"en"			"{default}has been teleported to the adminroom."
    +		"fr"			"{default}a été téléporté dans l'admin room."
    +		"ru"			"{default}телепортирован в админ-комнату."
    +		"es"			"{default}ha sido teletransportado a la sala de admin."
    +		"de"			"{default}wurde in den Adminraum teleportiert."
    +		"pt"			"{default}foi teleportado para a sala de admin."
    +		"chi"			"{default}已被传送到管理员房间。"
    +	}
    +
    +	"AR_NoSavedPosition"
    +	{
    +		"en"			"Could not teleport you because your position was not saved."
    +		"fr"			"Impossible de vous téléporter car votre position n'a pas été sauvegardée."
    +		"ru"			"Не удалось телепортировать вас: ваша позиция не была сохранена."
    +		"es"			"No se pudo teletransportarte porque tu posición no se guardó."
    +		"de"			"Du konntest nicht teleportiert werden, da deine Position nicht gespeichert wurde."
    +		"pt"			"Não foi possível te teleportar porque sua posição não foi salva."
    +		"chi"			"无法传送你,因为你的位置未被保存。"
    +	}
    +
    +	"AR_LeftRoom"
    +	{
    +		"en"			"You have left the admin room."
    +		"fr"			"Vous avez quitté l'admin room."
    +		"ru"			"Вы покинули админ-комнату."
    +		"es"			"Has salido de la sala de admin."
    +		"de"			"Du hast den Adminraum verlassen."
    +		"pt"			"Você saiu da sala de admin."
    +		"chi"			"你已离开管理员房间。"
    +	}
    +}
    -- 
    2.47.3
    
    
    From 70ee402979f5f4f8a18ddb085dc25ee78e0d61df Mon Sep 17 00:00:00 2001
    From: WareZ57 <7504466+WareZ57@users.noreply.github.com>
    Date: Sun, 1 Nov 2026 00:07:10 +0100
    Subject: [PATCH 2/2] chore: bump version to 2.2.0
    
    ---
     addons/sourcemod/scripting/AdminRoom.sp | 2 +-
     1 file changed, 1 insertion(+), 1 deletion(-)
    
    diff --git a/addons/sourcemod/scripting/AdminRoom.sp b/addons/sourcemod/scripting/AdminRoom.sp
    index ca070c4..a748845 100644
    --- a/addons/sourcemod/scripting/AdminRoom.sp
    +++ b/addons/sourcemod/scripting/AdminRoom.sp
    @@ -35,7 +35,7 @@ public Plugin myinfo =
     	name = "Admin Room",
     	author = "IT-KILLER, BotoX, maxime1907, .Rushaway",
     	description = "Teleport to admin rooms and change stages.",
    -	version = "2.1.8",
    +	version = "2.2.0",
     	url = ""
     };
     
    -- 
    2.47.3
    
    BossHP.patch
    From b2dd62ccbe42a65e8c214e9bce26308015f35e0b Mon Sep 17 00:00:00 2001
    From: WareZ57 <7504466+WareZ57@users.noreply.github.com>
    Date: Sat, 31 Oct 2026 23:44:23 +0100
    Subject: [PATCH 1/2] feat(translations): translate hard-coded messages
    
    ---
     .github/workflows/ci.yml                      |  2 +
     addons/sourcemod/scripting/BossHP.sp          | 14 ++--
     .../sourcemod/translations/BossHP.phrases.txt | 69 +++++++++++++++++++
     3 files changed, 79 insertions(+), 6 deletions(-)
     create mode 100644 addons/sourcemod/translations/BossHP.phrases.txt
    
    diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
    index 66d2271..8dffeb8 100644
    --- a/.github/workflows/ci.yml
    +++ b/.github/workflows/ci.yml
    @@ -40,6 +40,8 @@ jobs:
               set -euo pipefail
               mkdir -p /tmp/package/addons/sourcemod/plugins
               cp addons/sourcemod/plugins/*.smx /tmp/package/addons/sourcemod/plugins/
    +          mkdir -p /tmp/package/addons/sourcemod/translations
    +          cp -R addons/sourcemod/translations/* /tmp/package/addons/sourcemod/translations/
               cp -R addons/sourcemod/configs /tmp/package/addons/sourcemod/
     
           - name: Upload build archive for test runners
    diff --git a/addons/sourcemod/scripting/BossHP.sp b/addons/sourcemod/scripting/BossHP.sp
    index 5b59382..d5d8e0f 100644
    --- a/addons/sourcemod/scripting/BossHP.sp
    +++ b/addons/sourcemod/scripting/BossHP.sp
    @@ -49,6 +49,8 @@ public APLRes AskPluginLoad2(Handle myself, bool late, char[] error, int err_max
     
     public void OnPluginStart()
     {
    +	LoadTranslations("BossHP.phrases");
    +
     	HookEvent("round_start", OnRoundStart, EventHookMode_PostNoCopy);
     	HookEvent("round_end", OnRoundEnd, EventHookMode_PostNoCopy);
     	HookEntityOutput("env_entity_maker", "OnEntitySpawned", OnEnvEntityMakerEntitySpawned);
    @@ -85,7 +87,7 @@ public void OnMapEnd()
     public void OnRoundStart(Event event, const char[] name, bool dontBroadcast)
     {
     	if (g_bConfigLoaded && g_cvVerboseLog.IntValue > 0)
    -		CPrintToChatAll("{lightgreen}[BossHP]{default} The current map is supported by this plugin.");
    +		CPrintToChatAll("{lightgreen}[BossHP]{default} %t", "BossHP_Map_Supported");
     }
     
     public void OnRoundEnd(Event event, const char[] name, bool dontBroadcast)
    @@ -168,16 +170,16 @@ public void OnGameFrame()
     public Action Command_IsConfigLoaded(int client, int args)
     {
     	if (!g_bConfigLoaded)
    -		CReplyToCommand(client, "{lightgreen}[BossHP]{default} Map config file is {red}not loaded.");
    +		CReplyToCommand(client, "{lightgreen}[BossHP]{default} %t", "BossHP_Config_NotLoaded");
     	else
     	{
     		if (!g_bConfigError)
    -			CReplyToCommand(client, "{lightgreen}[BossHP]{default} Map config file {green}is loaded.");
    +			CReplyToCommand(client, "{lightgreen}[BossHP]{default} %t", "BossHP_Config_Loaded");
     		else
    -			CReplyToCommand(client, "{lightgreen}[BossHP]{default} Map config file is {green}loaded {fullred}but has errors.");
    +			CReplyToCommand(client, "{lightgreen}[BossHP]{default} %t", "BossHP_Config_Errors");
     
     		if (CheckCommandAccess(client, "sm_bosshp", ADMFLAG_ROOT))
    -			CReplyToCommand(client, "{lightgreen}[BossHP]{default} Actual cfg: {olive}%s", g_sConfigLoaded);
    +			CReplyToCommand(client, "{lightgreen}[BossHP]{default} %t", "BossHP_Actual_Cfg", g_sConfigLoaded);
     	}
     
     	return Plugin_Handled;
    @@ -186,7 +188,7 @@ public Action Command_IsConfigLoaded(int client, int args)
     public Action Command_ReloadConfig(int client, int args)
     {
     	OnConfigsExecuted();
    -	ReplyToCommand(client, "[BossHP] Map config file has been reloaded.");
    +	ReplyToCommand(client, "[BossHP] %t", "BossHP_Config_Reloaded");
     	return Plugin_Handled;
     }
     
    diff --git a/addons/sourcemod/translations/BossHP.phrases.txt b/addons/sourcemod/translations/BossHP.phrases.txt
    new file mode 100644
    index 0000000..b1e7670
    --- /dev/null
    +++ b/addons/sourcemod/translations/BossHP.phrases.txt
    @@ -0,0 +1,69 @@
    +"Phrases"
    +{
    +	"BossHP_Map_Supported"
    +	{
    +		"en"		"The current map is supported by this plugin."
    +		"fr"		"La map actuelle est supportée par ce plugin."
    +		"ru"		"Текущая карта поддерживается этим плагином."
    +		"es"		"El mapa actual es compatible con este plugin."
    +		"de"		"Die aktuelle Map wird von diesem Plugin unterstützt."
    +		"pt"		"O mapa atual é suportado por este plugin."
    +		"chi"		"当前地图受此插件支持。"
    +	}
    +
    +	"BossHP_Config_NotLoaded"
    +	{
    +		"en"		"Map config file is {red}not loaded."
    +		"fr"		"Le fichier de config de la map {red}n'est pas chargé."
    +		"ru"		"Конфиг карты {red}не загружен."
    +		"es"		"El archivo de configuración del mapa {red}no está cargado."
    +		"de"		"Die Map-Konfigurationsdatei ist {red}nicht geladen."
    +		"pt"		"O arquivo de configuração do mapa {red}não está carregado."
    +		"chi"		"地图配置文件{red}未加载。"
    +	}
    +
    +	"BossHP_Config_Loaded"
    +	{
    +		"en"		"Map config file {green}is loaded."
    +		"fr"		"Le fichier de config de la map {green}est chargé."
    +		"ru"		"Конфиг карты {green}загружен."
    +		"es"		"El archivo de configuración del mapa {green}está cargado."
    +		"de"		"Die Map-Konfigurationsdatei {green}ist geladen."
    +		"pt"		"O arquivo de configuração do mapa {green}está carregado."
    +		"chi"		"地图配置文件{green}已加载。"
    +	}
    +
    +	"BossHP_Config_Errors"
    +	{
    +		"en"		"Map config file is {green}loaded {fullred}but has errors."
    +		"fr"		"Le fichier de config de la map est {green}chargé {fullred}mais contient des erreurs."
    +		"ru"		"Конфиг карты {green}загружен, {fullred}но содержит ошибки."
    +		"es"		"El archivo de configuración del mapa está {green}cargado {fullred}pero tiene errores."
    +		"de"		"Die Map-Konfigurationsdatei ist {green}geladen, {fullred}enthält aber Fehler."
    +		"pt"		"O arquivo de configuração do mapa está {green}carregado, {fullred}mas tem erros."
    +		"chi"		"地图配置文件{green}已加载,{fullred}但存在错误。"
    +	}
    +
    +	"BossHP_Actual_Cfg"
    +	{
    +		"#format"	"{1:s}"
    +		"en"		"Actual cfg: {olive}{1}"
    +		"fr"		"Config actuelle : {olive}{1}"
    +		"ru"		"Текущий конфиг: {olive}{1}"
    +		"es"		"Configuración actual: {olive}{1}"
    +		"de"		"Aktuelle Konfiguration: {olive}{1}"
    +		"pt"		"Configuração atual: {olive}{1}"
    +		"chi"		"当前配置:{olive}{1}"
    +	}
    +
    +	"BossHP_Config_Reloaded"
    +	{
    +		"en"		"Map config file has been reloaded."
    +		"fr"		"Le fichier de config de la map a été rechargé."
    +		"ru"		"Конфиг карты перезагружен."
    +		"es"		"El archivo de configuración del mapa se ha recargado."
    +		"de"		"Die Map-Konfigurationsdatei wurde neu geladen."
    +		"pt"		"O arquivo de configuração do mapa foi recarregado."
    +		"chi"		"地图配置文件已重新加载。"
    +	}
    +}
    -- 
    2.47.3
    
    
    From c9df7c0b0f06aec08b6a137edfc3ac28438d5eb3 Mon Sep 17 00:00:00 2001
    From: WareZ57 <7504466+WareZ57@users.noreply.github.com>
    Date: Sun, 1 Nov 2026 00:07:10 +0100
    Subject: [PATCH 2/2] chore: bump version to 1.8.0
    
    ---
     addons/sourcemod/scripting/include/BossHP.inc | 4 ++--
     1 file changed, 2 insertions(+), 2 deletions(-)
    
    diff --git a/addons/sourcemod/scripting/include/BossHP.inc b/addons/sourcemod/scripting/include/BossHP.inc
    index d69bd78..5597739 100644
    --- a/addons/sourcemod/scripting/include/BossHP.inc
    +++ b/addons/sourcemod/scripting/include/BossHP.inc
    @@ -10,8 +10,8 @@
     
     // Version information
     #define BossHP_V_MAJOR   "1"
    -#define BossHP_V_MINOR   "7"
    -#define BossHP_V_PATCH   "5"
    +#define BossHP_V_MINOR   "8"
    +#define BossHP_V_PATCH   "0"
     
     #define BossHP_VERSION   BossHP_V_MAJOR..."."...BossHP_V_MINOR..."."...BossHP_V_PATCH
     
    -- 
    2.47.3
    
    BotManager.patch
    From 86ba2a9891bf50a7cec81c014a0bf039e6965306 Mon Sep 17 00:00:00 2001
    From: WareZ57 <7504466+WareZ57@users.noreply.github.com>
    Date: Sat, 31 Oct 2026 23:46:41 +0100
    Subject: [PATCH 1/2] feat(translations): translate hard-coded messages
    
    ---
     addons/sourcemod/scripting/BotManager.sp          |  4 +++-
     .../sourcemod/translations/botmanager.phrases.txt | 15 +++++++++++++++
     2 files changed, 18 insertions(+), 1 deletion(-)
     create mode 100644 addons/sourcemod/translations/botmanager.phrases.txt
    
    diff --git a/addons/sourcemod/scripting/BotManager.sp b/addons/sourcemod/scripting/BotManager.sp
    index cd7d10e..a9a400e 100644
    --- a/addons/sourcemod/scripting/BotManager.sp
    +++ b/addons/sourcemod/scripting/BotManager.sp
    @@ -52,6 +52,8 @@ public Plugin myinfo =
     
     public void OnPluginStart()
     {
    +	LoadTranslations("botmanager.phrases");
    +
     	g_hPrefix = CreateConVar("sm_botmanager_prefix", "", "Prefix for bot names (include a trailing space, if needed!)", FCVAR_NOTIFY);
     	g_hRandom = CreateConVar("sm_botmanager_random", "1", "Randomize names used", FCVAR_NOTIFY);
     	g_hAnnounce = CreateConVar("sm_botmanager_announce", "1", "Announce bots when added", FCVAR_NOTIFY);
    @@ -138,7 +140,7 @@ public bool OnClientConnect(int client, char[] rejectmsg, int maxlen)
     		SetClientInfo(client, "name", newname);
     		if (g_hAnnounce.BoolValue)
     		{
    -			PrintToChatAll("[botnames] Bot %s created.", newname);
    +			PrintToChatAll("%t", "BM_Bot_Created", newname);
     			PrintToServer("[botnames] Bot %s created.", newname);
     		}
     	}
    diff --git a/addons/sourcemod/translations/botmanager.phrases.txt b/addons/sourcemod/translations/botmanager.phrases.txt
    new file mode 100644
    index 0000000..2a0cfaf
    --- /dev/null
    +++ b/addons/sourcemod/translations/botmanager.phrases.txt
    @@ -0,0 +1,15 @@
    +"Phrases"
    +{
    +
    +	"BM_Bot_Created"
    +	{
    +		"#format"	"{1:s}"
    +		"en"		"[botnames] Bot {1} created."
    +		"fr"		"[botnames] Bot {1} créé."
    +		"ru"		"[botnames] Бот {1} создан."
    +		"es"		"[botnames] Bot {1} creado."
    +		"de"		"[botnames] Bot {1} erstellt."
    +		"pt"		"[botnames] Bot {1} criado."
    +		"chi"		"[botnames] 已创建机器人 {1}。"
    +	}
    +}
    -- 
    2.47.3
    
    
    From 4ef6afdee48f31345b90d94b5b4a7da8f306fec1 Mon Sep 17 00:00:00 2001
    From: WareZ57 <7504466+WareZ57@users.noreply.github.com>
    Date: Sun, 1 Nov 2026 00:07:10 +0100
    Subject: [PATCH 2/2] chore: bump version to 1.3.0
    
    ---
     addons/sourcemod/scripting/BotManager.sp | 2 +-
     1 file changed, 1 insertion(+), 1 deletion(-)
    
    diff --git a/addons/sourcemod/scripting/BotManager.sp b/addons/sourcemod/scripting/BotManager.sp
    index a9a400e..56776f6 100644
    --- a/addons/sourcemod/scripting/BotManager.sp
    +++ b/addons/sourcemod/scripting/BotManager.sp
    @@ -46,7 +46,7 @@ public Plugin myinfo =
     	name = "BotManager",
     	author = "Rakeri, Knagg0, maxime1907",
     	description = "Manage bot's name, ping and weapon restictions",
    -	version = "1.2",
    +	version = "1.3.0",
     	url = ""
     }
     
    -- 
    2.47.3
    
    VIP_Neon.patch
    From f811607698782df11f0753637608b4b7adba4940 Mon Sep 17 00:00:00 2001
    From: WareZ57 <7504466+WareZ57@users.noreply.github.com>
    Date: Sat, 31 Oct 2026 23:46:40 +0100
    Subject: [PATCH 1/2] feat(translations): translate hard-coded messages
    
    ---
     .github/workflows/ci.yml                      |  2 ++
     addons/sourcemod/scripting/VIP_Neon.sp        |  6 +++--
     .../translations/vip_neon.phrases.txt         | 27 +++++++++++++++++++
     3 files changed, 33 insertions(+), 2 deletions(-)
     create mode 100644 addons/sourcemod/translations/vip_neon.phrases.txt
    
    diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
    index 810c382..e774051 100644
    --- a/.github/workflows/ci.yml
    +++ b/.github/workflows/ci.yml
    @@ -34,6 +34,8 @@ jobs:
               set -euo pipefail
               mkdir -p /tmp/package/addons/sourcemod/plugins
               cp addons/sourcemod/plugins/*.smx /tmp/package/addons/sourcemod/plugins/
    +          mkdir -p /tmp/package/addons/sourcemod/translations
    +          cp -R addons/sourcemod/translations/* /tmp/package/addons/sourcemod/translations/
               cp -R addons/sourcemod/data /tmp/package/addons/sourcemod/
     
           - name: Upload build archive for test runners
    diff --git a/addons/sourcemod/scripting/VIP_Neon.sp b/addons/sourcemod/scripting/VIP_Neon.sp
    index 2b8f7e3..4334a9d 100644
    --- a/addons/sourcemod/scripting/VIP_Neon.sp
    +++ b/addons/sourcemod/scripting/VIP_Neon.sp
    @@ -33,6 +33,8 @@ public void VIP_OnVIPLoaded()
     
     public void OnPluginStart()
     {
    +	LoadTranslations("vip_neon.phrases");
    +
     	g_hCookie = new Cookie("VIP_Neon", "VIP_Neon", CookieAccess_Public);
     	g_hHideNeonCookie = new Cookie("VIP_HideNeon", "VIP_HideNeon", CookieAccess_Public);
     	
    @@ -199,7 +201,7 @@ public void Handler_ColorsMenu(Menu hMenu, MenuAction action, int iClient, int I
     			g_iClientItem[iClient] = Item;
     			if(LoadClientColor(iClient, sColor))
     			{
    -				VIP_PrintToChatClient(iClient, " \x03Вы изменили цвет неона на \x04%s", sColor);
    +				VIP_PrintToChatClient(iClient, " \x03%t", "VIP_Neon_Color_Changed", "\x04", sColor);
     				g_hCookie.Set(iClient, sColor);
     				if(IsPlayerAlive(iClient))
     				{
    @@ -208,7 +210,7 @@ public void Handler_ColorsMenu(Menu hMenu, MenuAction action, int iClient, int I
     			}
     			else
     			{
    -				VIP_PrintToChatClient(iClient, "Ошибка использования \"%s\"!.", sColor);
    +				VIP_PrintToChatClient(iClient, "%t", "VIP_Neon_Failed_Use", sColor);
     			}
     
     			g_hColorsMenu.Display(iClient, MENU_TIME_FOREVER);
    diff --git a/addons/sourcemod/translations/vip_neon.phrases.txt b/addons/sourcemod/translations/vip_neon.phrases.txt
    new file mode 100644
    index 0000000..c653979
    --- /dev/null
    +++ b/addons/sourcemod/translations/vip_neon.phrases.txt
    @@ -0,0 +1,27 @@
    +"Phrases"
    +{
    +
    +	"VIP_Neon_Color_Changed"
    +	{
    +		"#format"	"{1:s},{2:s}"
    +		"en"		"You changed your neon color to {1}{2}"
    +		"fr"		"Vous avez changé la couleur de votre néon en {1}{2}"
    +		"ru"		"Вы изменили цвет неона на {1}{2}"
    +		"es"		"Has cambiado el color de tu neón a {1}{2}"
    +		"de"		"Du hast deine Neonfarbe geändert zu {1}{2}"
    +		"pt"		"Você mudou a cor do seu neon para {1}{2}"
    +		"chi"		"你已将霓虹颜色更改为 {1}{2}"
    +	}
    +
    +	"VIP_Neon_Failed_Use"
    +	{
    +		"#format"	"{1:s}"
    +		"en"		"Failed to use \"{1}\"!"
    +		"fr"		"Impossible d'utiliser \"{1}\" !"
    +		"ru"		"Ошибка использования \"{1}\"!."
    +		"es"		"¡No se pudo usar \"{1}\"!"
    +		"de"		"\"{1}\" konnte nicht verwendet werden!"
    +		"pt"		"Falha ao usar \"{1}\"!"
    +		"chi"		"无法使用 \"{1}\"!"
    +	}
    +}
    -- 
    2.47.3
    
    
    From a318e3f3d1d24eeb98e8b731f7e63f26ffc49922 Mon Sep 17 00:00:00 2001
    From: WareZ57 <7504466+WareZ57@users.noreply.github.com>
    Date: Sun, 1 Nov 2026 00:07:11 +0100
    Subject: [PATCH 2/2] chore: bump version to 1.4.0
    
    ---
     addons/sourcemod/scripting/VIP_Neon.sp | 2 +-
     1 file changed, 1 insertion(+), 1 deletion(-)
    
    diff --git a/addons/sourcemod/scripting/VIP_Neon.sp b/addons/sourcemod/scripting/VIP_Neon.sp
    index 4334a9d..e80d95e 100644
    --- a/addons/sourcemod/scripting/VIP_Neon.sp
    +++ b/addons/sourcemod/scripting/VIP_Neon.sp
    @@ -9,7 +9,7 @@
     public Plugin myinfo =
     {
     	name        = "[VIP] Neon",
    -	version     = "1.3",
    +	version     = "1.4.0",
         description	= "Colored neons",
     	author      = "R1KO & Pheonix (˙·٠●Феникс●٠·˙)",
         url			= "www.hlmod.ru",
    -- 
    2.47.3
    
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions