Add react i18next to console

Signed-off-by: Jonathan <contact@grafikart.fr>
This commit is contained in:
Jonathan
2026-07-21 16:09:41 +02:00
committed by Bryan Frimin
parent 1b7b2594eb
commit a7ff5f07bc
420 changed files with 10251 additions and 6825 deletions

View File

@@ -28,20 +28,20 @@ export const auditStates = [
"OUTDATED",
] as const;
export function getAuditStateLabel(__: Translator, state: (typeof auditStates)[number]) {
export function getAuditStateLabel(t: Translator, state: (typeof auditStates)[number]) {
switch (state) {
case "NOT_STARTED":
return __("Not Started");
return t("helpers.auditState.notStarted");
case "IN_PROGRESS":
return __("In Progress");
return t("helpers.auditState.inProgress");
case "COMPLETED":
return __("Completed");
return t("helpers.auditState.completed");
case "REJECTED":
return __("Rejected");
return t("helpers.auditState.rejected");
case "OUTDATED":
return __("Outdated");
return t("helpers.auditState.outdated");
default:
return __("Unknown");
return t("helpers.common.unknown");
}
}

View File

@@ -36,19 +36,19 @@ export const certifications = {
} as const;
export const certificationCategoryLabel = (
__: Translator,
t: Translator,
category: keyof typeof certifications,
) => {
switch (category) {
case "securityStandards":
return __("Security Standards");
return t("helpers.certificationCategory.securityStandards");
case "regulatoryLegal":
return __("Regulatory & Legal");
return t("helpers.certificationCategory.regulatoryLegal");
case "industrySpecific":
return __("Industry-Specific");
return t("helpers.certificationCategory.industrySpecific");
case "internationalGov":
return __("International & Government");
return t("helpers.certificationCategory.internationalGovernment");
default:
return __("Custom certifications");
return t("helpers.certificationCategory.custom");
}
};

View File

@@ -32,16 +32,16 @@ export function getCompliancePortalDocumentAccessStatusBadgeVariant(status: Comp
}
}
export function getCompliancePortalDocumentAccessStatusLabel(status: CompliancePortalDocumentAccessStatus, __: (key: string) => string) {
export function getCompliancePortalDocumentAccessStatusLabel(status: CompliancePortalDocumentAccessStatus, t: (key: string) => string) {
switch (status) {
case "REQUESTED":
return __("requested");
return t("helpers.compliancePortalDocumentAccessStatus.requested");
case "GRANTED":
return __("granted");
return t("helpers.compliancePortalDocumentAccessStatus.granted");
case "REJECTED":
return __("rejected");
return t("helpers.compliancePortalDocumentAccessStatus.rejected");
case "REVOKED":
return __("revoked");
return t("helpers.compliancePortalDocumentAccessStatus.revoked");
}
}
@@ -68,4 +68,3 @@ export type CompliancePortalDocumentAccessInfo = ICompliancePortalDocumentAccess
type: "file",
}
)

View File

@@ -54,13 +54,13 @@ export const getCompliancePortalVisibilityLabel = (visibility: CompliancePortalV
}
};
export function getCompliancePortalVisibilityOptions(__: Translator) {
export function getCompliancePortalVisibilityOptions(t: Translator) {
return compliancePortalVisibilities.map((visibility) => ({
value: visibility,
label: __({
"NONE": "None",
"PRIVATE": "Private",
"PUBLIC": "Public",
label: t({
"NONE": "helpers.compliancePortalVisibility.none",
"PRIVATE": "helpers.compliancePortalVisibility.private",
"PUBLIC": "helpers.compliancePortalVisibility.public",
}[visibility]),
variant: getCompliancePortalVisibilityVariant(visibility),
}));

View File

@@ -31,19 +31,19 @@ export const controlMaturityLevels = [
export type ControlMaturityLevel = (typeof controlMaturityLevels)[number];
export function getControlMaturityLevelLabel(__: Translator, level: string) {
export function getControlMaturityLevelLabel(t: Translator, level: string) {
switch (level) {
case "NONE":
return __("0 - None");
return t("helpers.controlMaturityLevel.none");
case "INITIAL":
return __("1 - Initial");
return t("helpers.controlMaturityLevel.initial");
case "MANAGED":
return __("2 - Managed");
return t("helpers.controlMaturityLevel.managed");
case "DEFINED":
return __("3 - Defined");
return t("helpers.controlMaturityLevel.defined");
case "QUANTITATIVELY_MANAGED":
return __("4 - Quantitatively Managed");
return t("helpers.controlMaturityLevel.quantitativelyManaged");
case "OPTIMIZING":
return __("5 - Optimizing");
return t("helpers.controlMaturityLevel.optimizing");
}
}

View File

@@ -18,8 +18,6 @@
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
type Translator = (s: string) => string;
// ISO 3166-1 alpha-2 country codes and supported regions.
export const countries = [
"GLOBAL",
@@ -52,271 +50,17 @@ export const countries = [
export type CountryCode = typeof countries[number];
const countryNames: Record<CountryCode, (t: Translator) => string> = {
"GLOBAL": (__) => __("Global"),
"AD": (__) => __("Andorra"),
"AE": (__) => __("United Arab Emirates"),
"AF": (__) => __("Afghanistan"),
"AG": (__) => __("Antigua and Barbuda"),
"AI": (__) => __("Anguilla"),
"AL": (__) => __("Albania"),
"AM": (__) => __("Armenia"),
"AO": (__) => __("Angola"),
"AQ": (__) => __("Antarctica"),
"AR": (__) => __("Argentina"),
"AS": (__) => __("American Samoa"),
"AT": (__) => __("Austria"),
"AU": (__) => __("Australia"),
"AW": (__) => __("Aruba"),
"AX": (__) => __("Åland Islands"),
"AZ": (__) => __("Azerbaijan"),
"BA": (__) => __("Bosnia and Herzegovina"),
"BB": (__) => __("Barbados"),
"BD": (__) => __("Bangladesh"),
"BE": (__) => __("Belgium"),
"BF": (__) => __("Burkina Faso"),
"BG": (__) => __("Bulgaria"),
"BH": (__) => __("Bahrain"),
"BI": (__) => __("Burundi"),
"BJ": (__) => __("Benin"),
"BL": (__) => __("Saint Barthélemy"),
"BM": (__) => __("Bermuda"),
"BN": (__) => __("Brunei"),
"BO": (__) => __("Bolivia"),
"BQ": (__) => __("Bonaire, Sint Eustatius and Saba"),
"BR": (__) => __("Brazil"),
"BS": (__) => __("Bahamas"),
"BT": (__) => __("Bhutan"),
"BV": (__) => __("Bouvet Island"),
"BW": (__) => __("Botswana"),
"BY": (__) => __("Belarus"),
"BZ": (__) => __("Belize"),
"CA": (__) => __("Canada"),
"CC": (__) => __("Cocos (Keeling) Islands"),
"CD": (__) => __("Congo (DRC)"),
"CF": (__) => __("Central African Republic"),
"CG": (__) => __("Congo (Republic)"),
"CH": (__) => __("Switzerland"),
"CI": (__) => __("Côte d'Ivoire"),
"CK": (__) => __("Cook Islands"),
"CL": (__) => __("Chile"),
"CM": (__) => __("Cameroon"),
"CN": (__) => __("China"),
"CO": (__) => __("Colombia"),
"CR": (__) => __("Costa Rica"),
"CU": (__) => __("Cuba"),
"CV": (__) => __("Cape Verde"),
"CW": (__) => __("Curaçao"),
"CX": (__) => __("Christmas Island"),
"CY": (__) => __("Cyprus"),
"CZ": (__) => __("Czechia"),
"DE": (__) => __("Germany"),
"DJ": (__) => __("Djibouti"),
"DK": (__) => __("Denmark"),
"DM": (__) => __("Dominica"),
"DO": (__) => __("Dominican Republic"),
"DZ": (__) => __("Algeria"),
"EC": (__) => __("Ecuador"),
"EE": (__) => __("Estonia"),
"EG": (__) => __("Egypt"),
"EH": (__) => __("Western Sahara"),
"ER": (__) => __("Eritrea"),
"ES": (__) => __("Spain"),
"ET": (__) => __("Ethiopia"),
"EU": (__) => __("EU"),
"FI": (__) => __("Finland"),
"FJ": (__) => __("Fiji"),
"FK": (__) => __("Falkland Islands"),
"FM": (__) => __("Micronesia"),
"FO": (__) => __("Faroe Islands"),
"FR": (__) => __("France"),
"GA": (__) => __("Gabon"),
"GB": (__) => __("United Kingdom"),
"GD": (__) => __("Grenada"),
"GE": (__) => __("Georgia"),
"GF": (__) => __("French Guiana"),
"GG": (__) => __("Guernsey"),
"GH": (__) => __("Ghana"),
"GI": (__) => __("Gibraltar"),
"GL": (__) => __("Greenland"),
"GM": (__) => __("Gambia"),
"GN": (__) => __("Guinea"),
"GP": (__) => __("Guadeloupe"),
"GQ": (__) => __("Equatorial Guinea"),
"GR": (__) => __("Greece"),
"GT": (__) => __("Guatemala"),
"GU": (__) => __("Guam"),
"GW": (__) => __("Guinea-Bissau"),
"GY": (__) => __("Guyana"),
"HK": (__) => __("Hong Kong"),
"HM": (__) => __("Heard Island and McDonald Islands"),
"HN": (__) => __("Honduras"),
"HR": (__) => __("Croatia"),
"HT": (__) => __("Haiti"),
"HU": (__) => __("Hungary"),
"ID": (__) => __("Indonesia"),
"IE": (__) => __("Ireland"),
"IL": (__) => __("Israel"),
"IM": (__) => __("Isle of Man"),
"IN": (__) => __("India"),
"IO": (__) => __("British Indian Ocean Territory"),
"IQ": (__) => __("Iraq"),
"IR": (__) => __("Iran"),
"IS": (__) => __("Iceland"),
"IT": (__) => __("Italy"),
"JE": (__) => __("Jersey"),
"JM": (__) => __("Jamaica"),
"JO": (__) => __("Jordan"),
"JP": (__) => __("Japan"),
"KE": (__) => __("Kenya"),
"KG": (__) => __("Kyrgyzstan"),
"KH": (__) => __("Cambodia"),
"KI": (__) => __("Kiribati"),
"KM": (__) => __("Comoros"),
"KN": (__) => __("Saint Kitts and Nevis"),
"KP": (__) => __("North Korea"),
"KR": (__) => __("South Korea"),
"KW": (__) => __("Kuwait"),
"KY": (__) => __("Cayman Islands"),
"KZ": (__) => __("Kazakhstan"),
"LA": (__) => __("Laos"),
"LB": (__) => __("Lebanon"),
"LC": (__) => __("Saint Lucia"),
"LI": (__) => __("Liechtenstein"),
"LK": (__) => __("Sri Lanka"),
"LR": (__) => __("Liberia"),
"LS": (__) => __("Lesotho"),
"LT": (__) => __("Lithuania"),
"LU": (__) => __("Luxembourg"),
"LV": (__) => __("Latvia"),
"LY": (__) => __("Libya"),
"MA": (__) => __("Morocco"),
"MC": (__) => __("Monaco"),
"MD": (__) => __("Moldova"),
"ME": (__) => __("Montenegro"),
"MF": (__) => __("Saint Martin"),
"MG": (__) => __("Madagascar"),
"MH": (__) => __("Marshall Islands"),
"MK": (__) => __("North Macedonia"),
"ML": (__) => __("Mali"),
"MM": (__) => __("Myanmar"),
"MN": (__) => __("Mongolia"),
"MO": (__) => __("Macau"),
"MP": (__) => __("Northern Mariana Islands"),
"MQ": (__) => __("Martinique"),
"MR": (__) => __("Mauritania"),
"MS": (__) => __("Montserrat"),
"MT": (__) => __("Malta"),
"MU": (__) => __("Mauritius"),
"MV": (__) => __("Maldives"),
"MW": (__) => __("Malawi"),
"MX": (__) => __("Mexico"),
"MY": (__) => __("Malaysia"),
"MZ": (__) => __("Mozambique"),
"NA": (__) => __("Namibia"),
"NC": (__) => __("New Caledonia"),
"NE": (__) => __("Niger"),
"NF": (__) => __("Norfolk Island"),
"NG": (__) => __("Nigeria"),
"NI": (__) => __("Nicaragua"),
"NL": (__) => __("Netherlands"),
"NO": (__) => __("Norway"),
"NP": (__) => __("Nepal"),
"NR": (__) => __("Nauru"),
"NU": (__) => __("Niue"),
"NZ": (__) => __("New Zealand"),
"OM": (__) => __("Oman"),
"PA": (__) => __("Panama"),
"PE": (__) => __("Peru"),
"PF": (__) => __("French Polynesia"),
"PG": (__) => __("Papua New Guinea"),
"PH": (__) => __("Philippines"),
"PK": (__) => __("Pakistan"),
"PL": (__) => __("Poland"),
"PM": (__) => __("Saint Pierre and Miquelon"),
"PN": (__) => __("Pitcairn"),
"PR": (__) => __("Puerto Rico"),
"PS": (__) => __("Palestine"),
"PT": (__) => __("Portugal"),
"PW": (__) => __("Palau"),
"PY": (__) => __("Paraguay"),
"QA": (__) => __("Qatar"),
"RE": (__) => __("Réunion"),
"RO": (__) => __("Romania"),
"RS": (__) => __("Serbia"),
"RU": (__) => __("Russia"),
"RW": (__) => __("Rwanda"),
"SA": (__) => __("Saudi Arabia"),
"SB": (__) => __("Solomon Islands"),
"SC": (__) => __("Seychelles"),
"SD": (__) => __("Sudan"),
"SE": (__) => __("Sweden"),
"SG": (__) => __("Singapore"),
"SH": (__) => __("Saint Helena"),
"SI": (__) => __("Slovenia"),
"SJ": (__) => __("Svalbard and Jan Mayen"),
"SK": (__) => __("Slovakia"),
"SL": (__) => __("Sierra Leone"),
"SM": (__) => __("San Marino"),
"SN": (__) => __("Senegal"),
"SO": (__) => __("Somalia"),
"SR": (__) => __("Suriname"),
"SS": (__) => __("South Sudan"),
"ST": (__) => __("São Tomé and Príncipe"),
"SV": (__) => __("El Salvador"),
"SX": (__) => __("Sint Maarten"),
"SY": (__) => __("Syria"),
"SZ": (__) => __("Eswatini"),
"TC": (__) => __("Turks and Caicos Islands"),
"TD": (__) => __("Chad"),
"TF": (__) => __("French Southern Territories"),
"TG": (__) => __("Togo"),
"TH": (__) => __("Thailand"),
"TJ": (__) => __("Tajikistan"),
"TK": (__) => __("Tokelau"),
"TL": (__) => __("Timor-Leste"),
"TM": (__) => __("Turkmenistan"),
"TN": (__) => __("Tunisia"),
"TO": (__) => __("Tonga"),
"TR": (__) => __("Turkey"),
"TT": (__) => __("Trinidad and Tobago"),
"TV": (__) => __("Tuvalu"),
"TW": (__) => __("Taiwan"),
"TZ": (__) => __("Tanzania"),
"UA": (__) => __("Ukraine"),
"UG": (__) => __("Uganda"),
"UM": (__) => __("United States Minor Outlying Islands"),
"US": (__) => __("USA"),
"UY": (__) => __("Uruguay"),
"UZ": (__) => __("Uzbekistan"),
"VA": (__) => __("Vatican City"),
"VC": (__) => __("Saint Vincent and the Grenadines"),
"VE": (__) => __("Venezuela"),
"VG": (__) => __("Virgin Islands (British)"),
"VI": (__) => __("Virgin Islands (U.S.)"),
"VN": (__) => __("Vietnam"),
"VU": (__) => __("Vanuatu"),
"WF": (__) => __("Wallis and Futuna"),
"WS": (__) => __("Samoa"),
"YE": (__) => __("Yemen"),
"YT": (__) => __("Mayotte"),
"ZA": (__) => __("South Africa"),
"ZM": (__) => __("Zambia"),
"ZW": (__) => __("Zimbabwe"),
};
export function getCountryName(__: Translator, code: CountryCode): string {
const translator = countryNames[code];
return translator ? translator(__) : code;
export function getCountryName(language: string, code: CountryCode): string {
return new Intl.DisplayNames(language, { type: "region" }).of(code) ?? code;
}
export function getCountryOptions(__: Translator) {
export function getCountryOptions(lang: string) {
return countries.map(code => ({
value: code,
label: getCountryName(__, code),
label: getCountryName(lang, code),
}));
}
export function getCountryLabel(__: Translator, code: CountryCode): string {
return getCountryName(__, code);
export function getCountryLabel(lang: string, code: CountryCode): string {
return getCountryName(lang, code);
}

View File

@@ -44,42 +44,38 @@ export const getCustomDomainStatusBadgeVariant = (sslStatus: Status) => {
export const getCustomDomainStatusBadgeLabel = (
sslStatus: Status,
__: (key: string) => string,
t: (key: string) => string,
) => {
if (sslStatus === "ACTIVE") {
return __("Active");
return t("helpers.customDomain.status.active");
}
if (sslStatus === "PROVISIONING" || sslStatus === "RENEWING") {
return __("Provisioning");
return t("helpers.customDomain.status.provisioning");
}
if (sslStatus === "PENDING") {
return __("Pending");
return t("helpers.customDomain.status.pending");
}
if (sslStatus === "FAILED") {
return __("Failed");
return t("helpers.customDomain.status.failed");
}
if (sslStatus === "EXPIRED") {
return __("Expired");
return t("helpers.customDomain.status.expired");
}
return __("Unknown");
return t("helpers.common.unknown");
};
const provisioningErrorMessages: Record<string, string> = {
DNS_CNAME: "DNS is not configured correctly yet. Check the CNAME record.",
DNS_CAA: "DNS CAA records do not allow our certificate authority.",
ACME_RATE_LIMITED:
"Certificate authority is temporarily rate-limiting. We will retry automatically.",
ACME_INVALID_ORDER:
"Domain ownership could not be verified. We will retry automatically.",
ACME_TEMPORARY:
"Certificate provisioning hit a temporary error. We will retry automatically.",
ACME_FAILED:
"Certificate provisioning failed. Contact support if this persists.",
DNS_CNAME: "helpers.customDomain.provisioningError.dnsCname",
DNS_CAA: "helpers.customDomain.provisioningError.dnsCaa",
ACME_RATE_LIMITED: "helpers.customDomain.provisioningError.rateLimited",
ACME_INVALID_ORDER: "helpers.customDomain.provisioningError.invalidOrder",
ACME_TEMPORARY: "helpers.customDomain.provisioningError.temporary",
ACME_FAILED: "helpers.customDomain.provisioningError.failed",
};
export const getCertificateProvisioningErrorMessage = (
provisioningError: string | null | undefined,
__: (key: string) => string,
t: (key: string) => string,
) => {
if (!provisioningError) {
return null;
@@ -87,10 +83,8 @@ export const getCertificateProvisioningErrorMessage = (
const knownMessage = provisioningErrorMessages[provisioningError];
if (knownMessage) {
return __(knownMessage);
return t(knownMessage);
}
return __(
"Certificate provisioning failed. Contact support if this persists.",
);
return t("helpers.customDomain.provisioningError.failed");
};

View File

@@ -36,13 +36,6 @@ export function todayAsDateInput(): string {
return `${year}-${month}-${day}`;
}
export function formatDate(dateInput?: string | null): string {
if (!dateInput) return "";
const date = parseDate(dateInput);
return date.toLocaleDateString();
}
export function parseDate(dateString: string): Date {
if (dateString.includes("T")) {
return new Date(dateString);
@@ -54,36 +47,3 @@ export function parseDate(dateString: string): Date {
parts[2] ? parseInt(parts[2], 10) : 1,
);
}
export function formatDuration(duration?: string | null, __?: (s: string) => string): string | null {
if (!duration || !__) return null;
const timeMatch = duration.match(/PT(\d+)([MH])/);
if (timeMatch) {
const amount = parseInt(timeMatch[1], 10) || 0;
const unit = timeMatch[2];
if (unit === "M") {
return `${amount} ${amount === 1 ? __("Minute") : __("Minutes")}`;
} else if (unit === "H") {
return `${amount} ${amount === 1 ? __("Hour") : __("Hours")}`;
}
}
const dateMatch = duration.match(/P(\d+)([DW])/);
if (dateMatch) {
const amount = parseInt(dateMatch[1], 10) || 0;
const unit = dateMatch[2];
if (unit === "W") {
return `${amount} ${amount === 1 ? __("Week") : __("Weeks")}`;
} else if (unit === "D") {
const days = amount;
if (days % 7 === 0 && days > 0) {
const weeks = days / 7;
return `${weeks} ${weeks === 1 ? __("Week") : __("Weeks")}`;
}
return `${days} ${days === 1 ? __("Day") : __("Days")}`;
}
}
return null;
}

View File

@@ -22,39 +22,39 @@ type Translator = (s: string) => string;
export const documentTypes = ["OTHER", "GOVERNANCE", "POLICY", "PROCEDURE", "PLAN", "REGISTER", "RECORD", "REPORT", "TEMPLATE", "STATEMENT_OF_APPLICABILITY"] as const;
export function getDocumentTypeLabel(__: Translator, type: string) {
export function getDocumentTypeLabel(t: Translator, type: string) {
switch (type) {
case "OTHER":
return __("Other");
return t("helpers.documentType.other");
case "GOVERNANCE":
return __("Governance");
return t("helpers.documentType.governance");
case "POLICY":
return __("Policy");
return t("helpers.documentType.policy");
case "PROCEDURE":
return __("Procedure");
return t("helpers.documentType.procedure");
case "PLAN":
return __("Plan");
return t("helpers.documentType.plan");
case "REGISTER":
return __("Register");
return t("helpers.documentType.register");
case "RECORD":
return __("Record");
return t("helpers.documentType.record");
case "REPORT":
return __("Report");
return t("helpers.documentType.report");
case "TEMPLATE":
return __("Template");
return t("helpers.documentType.template");
case "STATEMENT_OF_APPLICABILITY":
return __("Statement of Applicability");
return t("helpers.documentType.statementOfApplicability");
}
}
export const documentWriteModes = ["AUTHORED", "GENERATED"] as const;
export function getDocumentWriteModeLabel(__: Translator, writeMode: string) {
export function getDocumentWriteModeLabel(t: Translator, writeMode: string) {
switch (writeMode) {
case "AUTHORED":
return __("Authored");
return t("helpers.documentWriteMode.authored");
case "GENERATED":
return __("Generated");
return t("helpers.documentWriteMode.generated");
}
}
@@ -65,15 +65,15 @@ export const documentClassifications = [
"SECRET",
] as const;
export function getDocumentClassificationLabel(__: Translator, classification: string) {
export function getDocumentClassificationLabel(t: Translator, classification: string) {
switch (classification) {
case "PUBLIC":
return __("Public");
return t("helpers.documentClassification.public");
case "INTERNAL":
return __("Internal");
return t("helpers.documentClassification.internal");
case "CONFIDENTIAL":
return __("Confidential");
return t("helpers.documentClassification.confidential");
case "SECRET":
return __("Secret");
return t("helpers.documentClassification.secret");
}
}

View File

@@ -28,49 +28,6 @@ export const DURATION_UNITS: { value: string; label: string; singular: string; s
{ value: "years", label: "years", singular: "year", seconds: 31536000, snap: 21 * 24 * 3600 },
] as const;
// Tracker types whose data persists until explicitly cleared. When such a
// tracker has no max-age, its lifetime is "persistent" rather than "session"
// (cookies and session storage are cleared when the session/tab ends).
const PERSISTENT_TRACKER_TYPES = new Set([
"LOCAL_STORAGE",
"INDEXED_DB",
"CACHE_STORAGE",
]);
export function humanizeSeconds(
seconds: number | null,
trackerType?: string | null,
): string {
if (seconds === null || seconds <= 0) {
return trackerType && PERSISTENT_TRACKER_TYPES.has(trackerType)
? "persistent"
: "session";
}
let remaining = seconds;
const parts: string[] = [];
for (const {label, singular, seconds: durationInSeconds, snap} of [...DURATION_UNITS].reverse()) {
if (remaining >= durationInSeconds - snap) {
let count = Math.floor(remaining / durationInSeconds);
const leftover = remaining - count * durationInSeconds;
if (leftover >= durationInSeconds - snap) {
count++;
remaining = 0;
} else if (leftover <= snap) {
remaining = 0;
} else {
remaining = leftover;
}
parts.push(`${count} ${count === 1 ? singular : label}`);
}
}
return parts.length > 0 ? parts.join(", ") : "session";
}
export function toMaxAgeSeconds(value: string, unit: string): number | null {
const trimmed = value.trim();
if (trimmed === "" || !/^\d+(\.\d+)?$/.test(trimmed)) return null;

View File

@@ -1,32 +0,0 @@
// Copyright (c) 2025-2026 Probo Inc <hello@probo.com>.
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
import { describe, it, expect } from "vitest";
import { fileSize } from "./file";
describe("file", () => {
it("should display a file size in a human readable format", () => {
const fakeTranslator = (s: string) => s;
expect(fileSize(fakeTranslator, 4911)).toMatchInlineSnapshot(
`"4.8 KB"`,
);
expect(fileSize(fakeTranslator, 20)).toMatchInlineSnapshot(`"20 B"`);
});
});

View File

@@ -1,54 +0,0 @@
// Copyright (c) 2025-2026 Probo Inc <hello@probo.com>.
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
/**
* Return the file size in a human readable format
*/
export function fileSize(__: (s: string) => string, size: number): string {
if (size < 0) return "";
if (size === 0) return `0 ${__("B")}`;
const units = [__("B"), __("KB"), __("MB"), __("GB"), __("TB")];
const i = Math.floor(Math.log(size) / Math.log(1024));
// Don't go beyond available units
const unitIndex = Math.min(i, units.length - 1);
// Convert to the appropriate unit with 2 decimal places
const convertedSize = size / Math.pow(1024, unitIndex);
const formattedSize = Math.round(convertedSize * 100) / 100;
return `${formattedSize} ${units[unitIndex]}`;
}
type FileInfo = {
type: string;
mimeType: string;
};
export function fileType(__: (s: string) => string, info: FileInfo): string {
if (
info.type !== "FILE" ||
(info.mimeType !== "text/uri-list" && info.mimeType !== "text/uri")
) {
return __("Document");
}
return __("Link");
}

View File

@@ -96,7 +96,6 @@ export {
type CompliancePortalVisibility as CompliancePageVisibility,
} from "./compliancePortalVisibility";
export { promisifyMutation } from "./relay";
export { fileType, fileSize } from "./file";
export {
acceptDocument,
acceptSpreadsheet,
@@ -112,11 +111,9 @@ export {
formatDate,
toDateInput,
todayAsDateInput,
formatDuration,
parseDate,
} from "./date";
export {
humanizeSeconds,
DURATION_UNITS,
toMaxAgeSeconds,
fromMaxAgeSeconds,

View File

@@ -29,21 +29,21 @@ export const measureStates = [
"NOT_IMPLEMENTED",
] as const;
export function getMeasureStateLabel(__: Translator, state: string) {
export function getMeasureStateLabel(t: Translator, state: string) {
switch (state) {
case "IMPLEMENTED":
return __("Implemented");
return t("helpers.measureState.implemented");
case "IN_PROGRESS":
return __("In Progress");
return t("helpers.measureState.inProgress");
case "NOT_APPLICABLE":
return __("Not Applicable");
return t("helpers.measureState.notApplicable");
case "NOT_STARTED":
return __("Not Started");
return t("helpers.measureState.notStarted");
case "UNKNOWN":
return __("Unknown");
return t("helpers.common.unknown");
case "NOT_IMPLEMENTED":
return __("Not Implemented");
return t("helpers.measureState.notImplemented");
default:
return __("Unknown");
return t("helpers.common.unknown");
}
}

View File

@@ -54,13 +54,13 @@ export const getObligationStatusLabel = (status: ObligationStatus) => {
}
};
export function getObligationStatusOptions(__: Translator) {
export function getObligationStatusOptions(t: Translator) {
return obligationStatuses.map((status) => ({
value: status,
label: __({
"NON_COMPLIANT": "Non-compliant",
"PARTIALLY_COMPLIANT": "Partially compliant",
"COMPLIANT": "Compliant",
label: t({
"NON_COMPLIANT": "helpers.obligationStatus.nonCompliant",
"PARTIALLY_COMPLIANT": "helpers.obligationStatus.partiallyCompliant",
"COMPLIANT": "helpers.obligationStatus.compliant",
}[status]),
}));
}

View File

@@ -38,12 +38,12 @@ export const getObligationTypeLabel = (type: ObligationType) => {
}
};
export function getObligationTypeOptions(__: Translator) {
export function getObligationTypeOptions(t: Translator) {
return obligationTypes.map((type) => ({
value: type,
label: __({
"LEGAL": "Legal",
"CONTRACTUAL": "Contractual",
label: t({
"LEGAL": "helpers.obligationType.legal",
"CONTRACTUAL": "helpers.obligationType.contractual",
}[type]),
}));
}

View File

@@ -26,32 +26,32 @@ export const peopleRoles = [
"SERVICE_ACCOUNT",
] as const;
export function getRoles(__: Translator) {
export function getRoles(t: Translator) {
return [
{
value: "EMPLOYEE",
label: __("Employee"),
label: t("helpers.peopleRole.employee"),
},
{
value: "CONTRACTOR",
label: __("Contractor"),
label: t("helpers.peopleRole.contractor"),
},
{
value: "SERVICE_ACCOUNT",
label: __("Service account"),
label: t("helpers.peopleRole.serviceAccount"),
},
];
}
export function getRole(__: Translator, role?: string): string {
export function getRole(t: Translator, role?: string): string {
switch (role) {
case "EMPLOYEE":
return __("Employee");
return t("helpers.peopleRole.employee");
case "CONTRACTOR":
return __("Contractor");
return t("helpers.peopleRole.contractor");
case "SERVICE_ACCOUNT":
return __("Service account");
return t("helpers.peopleRole.serviceAccount");
default:
return __("Unknown");
return t("helpers.common.unknown");
}
}

View File

@@ -69,16 +69,16 @@ export const getStatusLabel = (status: RegistryStatus) => {
}
};
export function getStatusOptions(__: Translator) {
export function getStatusOptions(t: Translator) {
return registryStatuses.map((status) => ({
value: status,
label: __({
"OPEN": "Open",
"IN_PROGRESS": "In Progress",
"CLOSED": "Closed",
"RISK_ACCEPTED": "Risk Accepted",
"MITIGATED": "Mitigated",
"FALSE_POSITIVE": "False Positive",
label: t({
"OPEN": "helpers.registryStatus.open",
"IN_PROGRESS": "helpers.registryStatus.inProgress",
"CLOSED": "helpers.registryStatus.closed",
"RISK_ACCEPTED": "helpers.registryStatus.riskAccepted",
"MITIGATED": "helpers.registryStatus.mitigated",
"FALSE_POSITIVE": "helpers.registryStatus.falsePositive",
}[status]),
}));
}

View File

@@ -46,23 +46,23 @@ export const rightsRequestStates = [
"REJECTED",
] as const;
const rightsRequestTypeLabels: Record<RightsRequestType, string> = {
"ACCESS": "Access",
"DELETION": "Deletion",
"RECTIFICATION": "Rectification",
"PORTABILITY": "Portability",
"OBJECTION": "Objection",
"COMPLAINT": "Complaint",
const rightsRequestTypeKeys: Record<RightsRequestType, string> = {
"ACCESS": "helpers.rightsRequestType.access",
"DELETION": "helpers.rightsRequestType.deletion",
"RECTIFICATION": "helpers.rightsRequestType.rectification",
"PORTABILITY": "helpers.rightsRequestType.portability",
"OBJECTION": "helpers.rightsRequestType.objection",
"COMPLAINT": "helpers.rightsRequestType.complaint",
};
export function getRightsRequestTypeLabel(__: Translator, type: RightsRequestType) {
return __(rightsRequestTypeLabels[type] ?? type);
export function getRightsRequestTypeLabel(t: Translator, type: RightsRequestType) {
return t(rightsRequestTypeKeys[type]);
}
export function getRightsRequestTypeOptions(__: Translator) {
export function getRightsRequestTypeOptions(t: Translator) {
return rightsRequestTypes.map((type) => ({
value: type,
label: __(rightsRequestTypeLabels[type]),
label: t(rightsRequestTypeKeys[type]),
}));
}
@@ -83,20 +83,20 @@ export const getRightsRequestStateVariant = (
}
};
const rightsRequestStateLabels: Record<RightsRequestState, string> = {
"TODO": "To Do",
"IN_PROGRESS": "In Progress",
"DONE": "Done",
"REJECTED": "Rejected",
const rightsRequestStateKeys: Record<RightsRequestState, string> = {
"TODO": "helpers.rightsRequestState.todo",
"IN_PROGRESS": "helpers.rightsRequestState.inProgress",
"DONE": "helpers.rightsRequestState.done",
"REJECTED": "helpers.rightsRequestState.rejected",
};
export function getRightsRequestStateLabel(__: Translator, state: RightsRequestState) {
return __(rightsRequestStateLabels[state] ?? state);
export function getRightsRequestStateLabel(t: Translator, state: RightsRequestState) {
return t(rightsRequestStateKeys[state]);
}
export function getRightsRequestStateOptions(__: Translator) {
export function getRightsRequestStateOptions(t: Translator) {
return rightsRequestStates.map((state) => ({
value: state,
label: __(rightsRequestStateLabels[state]),
label: t(rightsRequestStateKeys[state]),
}));
}

View File

@@ -20,97 +20,97 @@
type Translator = (s: string) => string;
export function getRiskImpacts(__: Translator) {
export function getRiskImpacts(t: Translator) {
return [
{
value: 1,
label: __("Negligible"),
label: t("helpers.riskImpact.negligible"),
},
{
value: 2,
label: __("Low"),
label: t("helpers.riskImpact.low"),
},
{
value: 3,
label: __("Moderate"),
label: t("helpers.riskImpact.moderate"),
},
{
value: 4,
label: __("Significant"),
label: t("helpers.riskImpact.significant"),
},
{
value: 5,
label: __("Catastrophic"),
label: t("helpers.riskImpact.catastrophic"),
},
];
}
export function getTreatment(__: Translator, treatment?: string): string {
export function getTreatment(t: Translator, treatment?: string): string {
switch (treatment) {
case "MITIGATED":
return __("Mitigate");
return t("helpers.riskTreatment.mitigate");
case "ACCEPTED":
return __("Accept");
return t("helpers.riskTreatment.accept");
case "TRANSFERRED":
return __("Transfer");
return t("helpers.riskTreatment.transfer");
case "AVOIDED":
return __("Avoid");
return t("helpers.riskTreatment.avoid");
default:
return __("Unknown");
return t("helpers.common.unknown");
}
}
export function getRiskLikelihoods(__: Translator) {
export function getRiskLikelihoods(t: Translator) {
return [
{
value: 1,
label: __("Improbable"),
label: t("helpers.riskLikelihood.improbable"),
},
{
value: 2,
label: __("Remote"),
label: t("helpers.riskLikelihood.remote"),
},
{
value: 3,
label: __("Occasional"),
label: t("helpers.riskLikelihood.occasional"),
},
{
value: 4,
label: __("Probable"),
label: t("helpers.riskLikelihood.probable"),
},
{
value: 5,
label: __("Frequent"),
label: t("helpers.riskLikelihood.frequent"),
},
];
}
function getRiskSeverities(__: Translator) {
function getRiskSeverities(t: Translator) {
return [
{
min: 15,
variant: "danger",
label: __("Critical"),
label: t("helpers.riskSeverity.critical"),
bg: "bg-danger",
color: "text-txt-danger",
},
{
min: 5,
variant: "warning",
label: __("High"),
label: t("helpers.riskSeverity.high"),
bg: "bg-warning",
color: "text-txt-warning",
},
{
min: 0,
variant: "neutral",
label: __("Low"),
label: t("helpers.riskSeverity.low"),
bg: "bg-txt-quaternary",
color: "text-txt-primary",
},
] as const;
}
export function getSeverity(__: Translator, score: number) {
return getRiskSeverities(__).find((s) => score >= s.min);
export function getSeverity(t: Translator, score: number) {
return getRiskSeverities(t).find((s) => score >= s.min);
}

View File

@@ -33,23 +33,23 @@ type Badge = {
variant: BadgeVariant;
};
export function getTrackerTypeBadge(type: string, __: Translator): Badge {
export function getTrackerTypeBadge(type: string, t: Translator): Badge {
switch (type) {
case "COOKIE": return { label: __("Cookie"), variant: "warning" };
case "LOCAL_STORAGE": return { label: __("localStorage"), variant: "info" };
case "SESSION_STORAGE": return { label: __("sessionStorage"), variant: "highlight" };
case "INDEXED_DB": return { label: __("IndexedDB"), variant: "success" };
case "CACHE_STORAGE": return { label: __("Cache Storage"), variant: "outline" };
case "COOKIE": return { label: t("helpers.trackerType.cookie"), variant: "warning" };
case "LOCAL_STORAGE": return { label: t("helpers.trackerType.localStorage"), variant: "info" };
case "SESSION_STORAGE": return { label: t("helpers.trackerType.sessionStorage"), variant: "highlight" };
case "INDEXED_DB": return { label: t("helpers.trackerType.indexedDb"), variant: "success" };
case "CACHE_STORAGE": return { label: t("helpers.trackerType.cacheStorage"), variant: "outline" };
default: return { label: type, variant: "neutral" };
}
}
export function getTrackerSourceBadge(source: string, __: Translator): Badge {
export function getTrackerSourceBadge(source: string, t: Translator): Badge {
switch (source) {
case "SCRIPT": return { label: __("Script"), variant: "info" };
case "PRE_EXISTING": return { label: __("Pre-existing"), variant: "outline" };
case "HTTP": return { label: __("HTTP"), variant: "neutral" };
case "EXTENSION": return { label: __("Extension"), variant: "warning" };
case "SCRIPT": return { label: t("helpers.trackerSource.script"), variant: "info" };
case "PRE_EXISTING": return { label: t("helpers.trackerSource.preExisting"), variant: "outline" };
case "HTTP": return { label: t("helpers.trackerSource.http"), variant: "neutral" };
case "EXTENSION": return { label: t("helpers.trackerSource.extension"), variant: "warning" };
default: return { label: source, variant: "neutral" };
}
}