Rename/fix: change Bots to Tribes (#3290)

## Description:

Resolves #3285. As discussed on Discord.

However, in at least one instance "Tribes" feels a bit off: in Humans vs
Nations, team "Tribes" feels as human too while they are just bots.

This PR changes Bots to Tribes outwardly by 
- Changing default EN translation.
- Changing (untranslated) alt text in PlayerPanel.
- To change "Team Bot" into "Team Tribes" too in PlayerInfoOverlay and
TeamStats (team leaderboard in-game), translate team names in there from
now on too.
- This way we also fix a bug where team names were not translated yet in
there. To add to that fix, also translate team names in LobbyPlayerView
in the same way. For this we re-use the existing
getTranslatedPlayerTeamLabel function from GameLeftSideBar by moving it
to Utils.
- No translation key was present yet for Humans and Nations teams, so
added those to now be used in PlayerInfoOverlay, LobbyPlayerView and
TeamStats for completeness.
- No internal code changes so nothing breaks.

**BEFORE (showing old team name Bot and also that team names weren't
translated yet in TeamStats)**
![No translation yet in
TeamStats](https://github.com/user-attachments/assets/38f465bc-ef82-4474-806c-015bb640d233)

![No translation yet in TeamStats
2](https://github.com/user-attachments/assets/a4387f1e-0e80-491d-b57d-e52b3c616e2b)


**AFTER** (translations in Dutch only shown as proof here, did not
include nl.json in the PR)
![AFTER translated in TeamStats for Humans vs Nations as an example in
NL
json](https://github.com/user-attachments/assets/1a7dcf4e-4263-4d6b-a992-58cb08a4fa7b)
![AFTER Tribe as player type in
PlayerInfoPanel](https://github.com/user-attachments/assets/6fd09686-320e-4fee-9c0d-397e581aa676)
![AFTER translated Team name PlayerInfoPanel as an
example](https://github.com/user-attachments/assets/1b4bc684-9ef4-47a9-b91c-4ed5cda65e9e)
![AFTER Tribes in EN now that it is translated in TeamStats so fetched
from EN
json](https://github.com/user-attachments/assets/5ea6528b-7e3c-4c6e-abeb-2769fb0aedee)
![AFTER Instructions example of changed text
](https://github.com/user-attachments/assets/6c7a7ab7-1dea-4f11-bacf-3e2edcdb074b)



## Please complete the following:

- [x] I have added screenshots for all UI updates
- [x] I process any text displayed to the user through translateText()
and I've added it to the en.json file
- [x] I have added relevant tests to the test directory
- [x] I confirm I have thoroughly tested these changes and take full
responsibility for any bugs introduced

## Please put your Discord username so you can be contacted if a bug or
regression is found:

tryout33
This commit is contained in:
VariableVince
2026-03-03 03:20:10 +01:00
committed by GitHub
parent 29e1ca2bda
commit 1d1b076672
7 changed files with 43 additions and 34 deletions
+9 -7
View File
@@ -115,7 +115,7 @@
"ui_options": "Options",
"ui_options_desc": "The following elements can be found inside:",
"ui_playeroverlay": "Player info overlay",
"ui_playeroverlay_desc": "When you hover over a country, the Player info overlay is displayed under Options. It shows the type of player: Human, Nation (smart bot), or Bot. A Nation's attitude towards you, ranging from Hostile to Friendly. And defending troops, gold, plus the number of Warships and various buildings the player has.",
"ui_playeroverlay_desc": "When you hover over a country, the Player info overlay is displayed under Options. It shows the type of player: Human, Nation, or Tribe. A Nation's attitude towards you, ranging from Hostile to Friendly. And defending troops, gold, plus the number of Warships and various buildings the player has.",
"ui_wilderness": "Wilderness",
"option_pause": "Pause/Unpause the game - Only available in single player mode.",
"option_timer": "Timer - Time passed since the start of the game.",
@@ -137,7 +137,7 @@
"info_trade": "Use \"Stop trading\" to stop giving the player gold and receiving their gold via trade ships. If you both click \"Start trading\" it will start again.",
"info_ally_panel": "Ally info panel",
"info_ally_desc": "When you ally with a player, the following new icons become available:",
"ally_betray": "Betray your ally, ending the alliance, halting trade, and weakening your defense. Trading between you is paused for 5 minutes (or until you become allies again) and others may stop trading too. And unless the other player was a traitor themselves, you'll be marked a traitor for 30 seconds. During this time an icon will be above your name and you will have a 50% defense debuff. Bots are less likely to ally with you and players will think twice before doing so.",
"ally_betray": "Betray your ally, ending the alliance, halting trade, and weakening your defense. Trading between you is paused for 5 minutes (or until you become allies again) and others may stop trading too. And unless the other player was a traitor themselves, you'll be marked a traitor for 30 seconds. During this time an icon will be above your name and you will have a 50% defense debuff. Tribes are less likely to ally with you and players will think twice before doing so.",
"ally_donate": "Donate some of your troops to your ally. Used when they're low on troops and are being attacked, or when they need that extra power to crush an enemy.",
"ally_donate_gold": "Donate some of your gold to your ally. Used when they're low on gold and need it for buildings, or when your team member is saving for that MIRV.",
"build_menu_title": "Build menu",
@@ -184,7 +184,7 @@
"toggle_achievements": "Toggle achievements",
"sign_in_for_achievements": "Sign in for achievements",
"options_title": "Options",
"bots": "Bots: ",
"bots": "Tribes: ",
"bots_disabled": "Disabled",
"disable_nations": "Disable Nations",
"instant_build": "Instant build",
@@ -264,7 +264,7 @@
"conquest_gold": "Conquered player gold",
"stolen_gold": "Stolen with warships",
"num_of_conquests_humans": "Player kills",
"num_of_conquests_bots": "Bot kills",
"num_of_conquests_bots": "Tribe kills",
"duration": "Duration",
"survival_time": "Survival time",
"war": "War",
@@ -399,7 +399,7 @@
"team_count": "Number of Teams",
"team_type": "Team Type",
"options_title": "Options",
"bots": "Bots: ",
"bots": "Tribes: ",
"bots_disabled": "Disabled",
"player_immunity_duration": "PVP immunity duration (minutes)",
"disable_nations": "Disable Nations",
@@ -440,7 +440,9 @@
"yellow": "Yellow",
"orange": "Orange",
"green": "Green",
"bot": "Bot"
"bot": "Tribes",
"humans": "Humans",
"nations": "Nations"
},
"game_starting_modal": {
"title": "Game is Starting...",
@@ -779,7 +781,7 @@
"player_type": {
"player": "Player",
"nation": "Nation",
"bot": "Bot"
"bot": "Tribe"
},
"relation": {
"hostile": "Hostile",
+8
View File
@@ -6,6 +6,7 @@ import {
MessageType,
PublicGameModifiers,
Quads,
Team,
Trios,
} from "../core/game/Game";
import { GameConfig } from "../core/Schemas";
@@ -408,6 +409,13 @@ export const translateText = (
}
};
export function getTranslatedPlayerTeamLabel(team: Team | null): string {
if (!team) return "";
const translationKey = `team_colors.${team.toLowerCase()}`;
const translated = translateText(translationKey);
return translated === translationKey ? team : translated;
}
/**
* Severity colors mapping for message types
*/
+4 -2
View File
@@ -18,7 +18,7 @@ import { assignTeamsLobbyPreview } from "../../core/game/TeamAssignment";
import { UserSettings } from "../../core/game/UserSettings";
import { ClientInfo, TeamCountConfig } from "../../core/Schemas";
import { createRandomName } from "../../core/Util";
import { translateText } from "../Utils";
import { getTranslatedPlayerTeamLabel, translateText } from "../Utils";
export interface TeamPreviewData {
team: Team;
@@ -193,6 +193,8 @@ export class LobbyTeamView extends LitElement {
? effectiveNationCount
: this.teamMaxSize;
const teamLabel = getTranslatedPlayerTeamLabel(preview.team);
return html`
<div class="bg-gray-800 border border-gray-700 rounded-xl flex flex-col">
<div
@@ -204,7 +206,7 @@ export class LobbyTeamView extends LitElement {
style="--bg:${this.teamHeaderColor(preview.team)};"
></span>`
: null}
<span class="truncate">${preview.team}</span>
<span class="truncate">${teamLabel}</span>
<span class="text-white/90">${displayCount}/${maxTeamSize}</span>
</div>
<div class="p-2 ${isEmpty ? "" : "flex flex-col gap-1.5"}">
+5 -11
View File
@@ -2,10 +2,10 @@ import { Colord } from "colord";
import { html, LitElement } from "lit";
import { customElement, state } from "lit/decorators.js";
import { EventBus } from "../../../core/EventBus";
import { GameMode } from "../../../core/game/Game";
import { GameMode, Team } from "../../../core/game/Game";
import { GameView } from "../../../core/game/GameView";
import { Platform } from "../../Platform";
import { translateText } from "../../Utils";
import { getTranslatedPlayerTeamLabel, translateText } from "../../Utils";
import { ImmunityBarVisibleEvent } from "./ImmunityTimer";
import { Layer } from "./Layer";
import { SpawnBarVisibleEvent } from "./SpawnTimer";
@@ -25,7 +25,7 @@ export class GameLeftSidebar extends LitElement implements Layer {
@state()
private isPlayerTeamLabelVisible = false;
@state()
private playerTeam: string | null = null;
private playerTeam: Team | null = null;
@state()
private spawnBarVisible = false;
@state()
@@ -99,13 +99,6 @@ export class GameLeftSidebar extends LitElement implements Layer {
return this.game?.config().gameConfig().gameMode === GameMode.Team;
}
private getTranslatedPlayerTeamLabel(): string {
if (!this.playerTeam) return "";
const translationKey = `team_colors.${this.playerTeam.toLowerCase()}`;
const translated = translateText(translationKey);
return translated === translationKey ? this.playerTeam : translated;
}
render() {
return html`
<aside
@@ -180,7 +173,8 @@ export class GameLeftSidebar extends LitElement implements Layer {
style="--color: ${this.playerColor.toRgbString()}"
class="text-(--color)"
>
&nbsp;${this.getTranslatedPlayerTeamLabel()} &#10687;
&nbsp;${getTranslatedPlayerTeamLabel(this.playerTeam)}
&#10687;
</span>
</div>
`
@@ -19,6 +19,7 @@ import {
TouchEvent,
} from "../../InputHandler";
import {
getTranslatedPlayerTeamLabel,
renderDuration,
renderNumber,
renderTroops,
@@ -314,6 +315,7 @@ export class PlayerInfoOverlay extends LitElement implements Layer {
playerType = translateText("player_type.player");
break;
}
const playerTeam = getTranslatedPlayerTeamLabel(player.team());
return html`
<div class="flex items-start gap-2 lg:gap-3 p-1.5 lg:p-2">
@@ -357,7 +359,7 @@ export class PlayerInfoOverlay extends LitElement implements Layer {
/>`
: html``}
<span>${player.name()}</span>
${player.team() !== null && player.type() !== PlayerType.Bot
${playerTeam !== "" && player.type() !== PlayerType.Bot
? html`<div class="flex flex-col leading-tight">
<span class="text-gray-400 text-xs font-normal"
>${playerType}</span
@@ -369,7 +371,7 @@ export class PlayerInfoOverlay extends LitElement implements Layer {
.theme()
.teamColor(player.team()!)
.toHex()}"
>${player.team()}</span
>${playerTeam}</span
>]</span
>
</div>`
+2 -5
View File
@@ -340,22 +340,19 @@ export class PlayerPanel extends LitElement implements Layer {
case PlayerType.Nation:
return {
labelKey: "player_type.nation",
aria: "Nation player",
classes: "border-indigo-400/25 bg-indigo-500/10 text-indigo-200",
icon: "🏛️",
};
case PlayerType.Bot:
return {
labelKey: "player_type.bot",
aria: "Bot",
classes: "border-purple-400/25 bg-purple-500/10 text-purple-200",
icon: "🤖",
icon: "⚔️",
};
case PlayerType.Human:
default:
return {
labelKey: "player_type.player",
aria: "Human player",
classes: "border-zinc-400/20 bg-zinc-500/5 text-zinc-300",
icon: "👤",
};
@@ -517,7 +514,7 @@ export class PlayerPanel extends LitElement implements Layer {
? html`<span
class=${`inline-flex items-center gap-1.5 rounded-full border px-2 py-0.5 text-xs font-semibold ${chip.classes}`}
role="status"
aria-label=${chip.aria}
aria-label=${translateText(chip.labelKey)}
title=${translateText(chip.labelKey)}
>
<span aria-hidden="true" class="leading-none">${chip.icon}</span>
+11 -7
View File
@@ -69,14 +69,18 @@ export class TeamStats extends LitElement implements Layer {
}
for (const player of players) {
const team = player.team();
if (team === null) continue;
grouped[team] ??= [];
grouped[team].push(player);
const rawTeam = player.team();
if (rawTeam === null) continue;
grouped[rawTeam] ??= [];
grouped[rawTeam].push(player);
}
this.teams = Object.entries(grouped)
.map(([teamStr, teamPlayers]) => {
.map(([rawTeam, teamPlayers]) => {
const key = `team_colors.${rawTeam.toLowerCase()}`;
const translated = translateText(key);
const teamName = translated !== key ? translated : rawTeam;
let totalGold = 0n;
let totalMaxTroops = 0;
let totalScoreSort = 0;
@@ -102,8 +106,8 @@ export class TeamStats extends LitElement implements Layer {
const totalScorePercent = totalScoreSort / numTilesWithoutFallout;
return {
teamName: teamStr,
isMyTeam: teamStr === this._myTeam,
teamName,
isMyTeam: rawTeam === this._myTeam,
totalScoreStr: formatPercentage(totalScorePercent),
totalScoreSort,
totalGold: renderNumber(totalGold),