mirror of
https://github.com/bitburner-official/bitburner-src.git
synced 2026-04-16 06:18:42 +02:00
BUGFIX: Wrong plural form in modal of coding contract (#1939)
This commit is contained in:
@@ -7,6 +7,7 @@ import { Achievement, PlayerAchievement } from "./Achievements";
|
||||
import { Settings } from "../Settings/Settings";
|
||||
import { getFiltersFromHex } from "../ThirdParty/colorUtils";
|
||||
import { CorruptableText } from "../ui/React/CorruptableText";
|
||||
import { pluralize } from "../utils/I18nUtils";
|
||||
|
||||
interface IProps {
|
||||
achievements: Achievement[];
|
||||
@@ -101,7 +102,8 @@ export function AchievementList({ achievements, playerAchievements }: IProps): J
|
||||
</AccordionSummary>
|
||||
<AccordionDetails>
|
||||
<Typography sx={{ mt: 1 }}>
|
||||
{unavailable.length} additional achievements hidden behind content you don't have access to.
|
||||
{pluralize(unavailable.length, "additional achievement")} hidden behind content you don't have access
|
||||
to.
|
||||
</Typography>
|
||||
</AccordionDetails>
|
||||
</Accordion>
|
||||
|
||||
@@ -54,6 +54,7 @@ import { shuffleArray } from "../Infiltration/ui/BribeGame";
|
||||
import { assertObject } from "../utils/TypeAssertion";
|
||||
import { throwIfReachable } from "../utils/helpers/throwIfReachable";
|
||||
import { loadActionIdentifier } from "./utils/loadActionIdentifier";
|
||||
import { pluralize } from "../utils/I18nUtils";
|
||||
|
||||
export const BladeburnerPromise: PromisePair<number> = { promise: null, resolve: null };
|
||||
|
||||
@@ -167,9 +168,7 @@ export class Bladeburner implements OperationTeam {
|
||||
this.setSkillLevel(skillName, currentSkillLevel + availability.actualCount);
|
||||
return {
|
||||
success: true,
|
||||
message: `Upgraded skill ${skillName} by ${availability.actualCount} level${
|
||||
availability.actualCount > 1 ? "s" : ""
|
||||
}`,
|
||||
message: `Upgraded skill ${skillName} by ${pluralize(availability.actualCount, "level")}`,
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -6,6 +6,7 @@ import Typography from "@mui/material/Typography";
|
||||
import Button from "@mui/material/Button";
|
||||
import Box from "@mui/material/Box";
|
||||
import { RecruitmentResult } from "../Gang";
|
||||
import { pluralize } from "../../utils/I18nUtils";
|
||||
|
||||
interface IProps {
|
||||
onRecruit: () => void;
|
||||
@@ -35,9 +36,7 @@ export function RecruitButton(props: IProps): React.ReactElement {
|
||||
<>
|
||||
<Box display="flex" alignItems="center" sx={{ mx: 1 }}>
|
||||
<Button onClick={() => setOpen(true)}>Recruit Gang Member</Button>
|
||||
<Typography sx={{ ml: 1 }}>
|
||||
Can recruit {recruitsAvailable} more gang member{recruitsAvailable === 1 ? "" : "s"}
|
||||
</Typography>
|
||||
<Typography sx={{ ml: 1 }}>Can recruit {pluralize(recruitsAvailable, "more gang member")}</Typography>
|
||||
</Box>
|
||||
<RecruitModal open={open} onClose={() => setOpen(false)} onRecruit={props.onRecruit} />
|
||||
</>
|
||||
|
||||
@@ -5,6 +5,7 @@ import { ContentFile, ContentFilePath, allContentFiles } from "../../Paths/Conte
|
||||
import { Settings } from "../../Settings/Settings";
|
||||
import { help } from "../commands/help";
|
||||
import { Output } from "../OutputTypes";
|
||||
import { pluralize } from "../../utils/I18nUtils";
|
||||
|
||||
type LineParser = (options: Options, filename: string, line: string, i: number) => ParsedLine;
|
||||
|
||||
@@ -290,12 +291,9 @@ class Results {
|
||||
|
||||
getVerboseInfo(files: ContentFile[], pattern: string | RegExp, options: Options): string {
|
||||
if (!options.isVerbose) return "";
|
||||
const suffix = (pre: string, num: number) => pre + (num === 1 ? "" : "s");
|
||||
const totalLines = this.results.length;
|
||||
const matchCount = Math.abs((options.isInvertMatch ? totalLines : 0) - this.numMatches);
|
||||
const inputStr = options.isPipeIn
|
||||
? "piped from terminal "
|
||||
: `in ${files.length} ${suffix("file", files.length)}:\n`;
|
||||
const inputStr = options.isPipeIn ? "piped from terminal " : `in ${pluralize(files.length, "file")}:\n`;
|
||||
const filesStr = files
|
||||
.map((file, i) => `${i % 2 ? WHITE : ""}${file.filename}(${file.content.split("\n").length}loc)${DEFAULT}`)
|
||||
.join(", ");
|
||||
@@ -304,9 +302,9 @@ class Results {
|
||||
`\n${
|
||||
(this.params.maxMatches ? this.params.maxMatches : matchCount) + (options.isInvertMatch ? " INVERTED" : "")
|
||||
} `,
|
||||
suffix("line", matchCount) + " matched ",
|
||||
pluralize(matchCount, "line", undefined, true) + " matched ",
|
||||
`against PATTERN "${pattern.toString()}" `,
|
||||
`in ${totalLines} ${suffix("line", totalLines)}, `,
|
||||
`in ${pluralize(totalLines, "line")}, `,
|
||||
inputStr,
|
||||
`${filesStr}`,
|
||||
].join("");
|
||||
|
||||
@@ -11,6 +11,7 @@ import { ScriptFilePath, isLegacyScript } from "../../Paths/ScriptFilePath";
|
||||
import { sendDeprecationNotice } from "./common/deprecation";
|
||||
import { roundToTwo } from "../../utils/helpers/roundToTwo";
|
||||
import { RamCostConstants } from "../../Netscript/RamCostGenerator";
|
||||
import { pluralize } from "../../utils/I18nUtils";
|
||||
|
||||
export function runScript(path: ScriptFilePath, commandArgs: (string | number | boolean)[], server: BaseServer): void {
|
||||
// This takes in the absolute filepath, see "run.ts"
|
||||
@@ -77,7 +78,9 @@ export function runScript(path: ScriptFilePath, commandArgs: (string | number |
|
||||
sendDeprecationNotice();
|
||||
}
|
||||
Terminal.print(
|
||||
`Running script with ${numThreads} thread(s), pid ${runningScript.pid} and args: ${JSON.stringify(args)}.`,
|
||||
`Running script with ${pluralize(numThreads, "thread")}, pid ${runningScript.pid} and args: ${JSON.stringify(
|
||||
args,
|
||||
)}.`,
|
||||
);
|
||||
if (tailFlag) {
|
||||
LogBoxEvents.emit(runningScript);
|
||||
|
||||
@@ -9,6 +9,7 @@ import { EventEmitter } from "../../utils/EventEmitter";
|
||||
import Typography from "@mui/material/Typography";
|
||||
import TextField from "@mui/material/TextField";
|
||||
import Button from "@mui/material/Button";
|
||||
import { pluralize } from "../../utils/I18nUtils";
|
||||
|
||||
interface CodingContractProps {
|
||||
c: CodingContract;
|
||||
@@ -63,8 +64,9 @@ export function CodingContractModal(): React.ReactElement {
|
||||
<Modal open={contract !== null} onClose={close}>
|
||||
<CopyableText variant="h4" value={contract.c.type} />
|
||||
<Typography>
|
||||
You are attempting to solve a Coding Contract. You have {contract.c.getMaxNumTries() - contract.c.tries} tries
|
||||
remaining, after which the contract will self-destruct.
|
||||
You are attempting to solve a Coding Contract. You have{" "}
|
||||
{pluralize(contract.c.getMaxNumTries() - contract.c.tries, "try", "tries")} remaining, after which the contract
|
||||
will self-destruct.
|
||||
</Typography>
|
||||
<br />
|
||||
<Typography>{description}</Typography>
|
||||
|
||||
@@ -7,6 +7,7 @@ import { GetAllServers } from "../../Server/AllServers";
|
||||
import { resolveTextFilePath } from "../../Paths/TextFilePath";
|
||||
import { dialogBoxCreate as dialogBoxCreateOriginal } from "../../ui/React/DialogBox";
|
||||
import { Terminal } from "../../Terminal";
|
||||
import { pluralize } from "../I18nUtils";
|
||||
|
||||
// Temporary until fixing alerts manager to store alerts outside of react scope
|
||||
const dialogBoxCreate = (text: string) => setTimeout(() => dialogBoxCreateOriginal(text), 2000);
|
||||
@@ -64,7 +65,9 @@ export function showAPIBreaks(version: string, ...breakInfos: APIBreakInfo[]) {
|
||||
[...scriptImpactMap]
|
||||
.map(
|
||||
([filename, lineNumbers]) =>
|
||||
`${filename}: (Line number${lineNumbers.length > 1 ? "s" : ""}: ${lineNumbers.join(", ")})`,
|
||||
`${filename}: (${pluralize(lineNumbers.length, "Line number", undefined, true)}: ${lineNumbers.join(
|
||||
", ",
|
||||
)})`,
|
||||
)
|
||||
.join("\n"),
|
||||
)
|
||||
|
||||
10
src/utils/I18nUtils.ts
Normal file
10
src/utils/I18nUtils.ts
Normal file
@@ -0,0 +1,10 @@
|
||||
const pluralRules = new Intl.PluralRules("en-US");
|
||||
|
||||
export function pluralize(count: number, singular: string, plural?: string, skipCountInReturnedValue = false): string {
|
||||
const countText = !skipCountInReturnedValue ? `${count} ` : "";
|
||||
const pluralRule = pluralRules.select(count);
|
||||
if (pluralRule === "one") {
|
||||
return countText + singular;
|
||||
}
|
||||
return countText + (plural !== undefined ? plural : `${singular}s`);
|
||||
}
|
||||
@@ -1,5 +1,6 @@
|
||||
import { Settings } from "../Settings/Settings";
|
||||
import { CONSTANTS } from "../Constants";
|
||||
import { pluralize } from "./I18nUtils";
|
||||
|
||||
/*
|
||||
Converts a date representing time in milliseconds to a string with the format H hours M minutes and S seconds
|
||||
@@ -38,13 +39,13 @@ export function convertTimeMsToTimeElapsedString(time: number, showMilli = false
|
||||
|
||||
let res = "";
|
||||
if (days > 0) {
|
||||
res += `${days} day${days === 1 ? "" : "s"} `;
|
||||
res += `${pluralize(days, "day")} `;
|
||||
}
|
||||
if (hours > 0 || (Settings.ShowMiddleNullTimeUnit && res != "")) {
|
||||
res += `${hours} hour${hours === 1 ? "" : "s"} `;
|
||||
res += `${pluralize(hours, "hour")} `;
|
||||
}
|
||||
if (minutes > 0 || (Settings.ShowMiddleNullTimeUnit && res != "")) {
|
||||
res += `${minutes} minute${minutes === 1 ? "" : "s"} `;
|
||||
res += `${pluralize(minutes, "minute")} `;
|
||||
}
|
||||
res += `${seconds} second${!showMilli && secTruncMinutes === 1 ? "" : "s"}`;
|
||||
|
||||
|
||||
Reference in New Issue
Block a user