mirror of
https://github.com/bluewave-labs/Checkmate.git
synced 2026-01-15 22:29:42 -06:00
Merge pull request #2230 from bluewave-labs/fix/untranslated-menu-uptime
Fix/untranslated sidebar and uptime page
This commit is contained in:
@@ -6,7 +6,7 @@ import { useTheme } from "@emotion/react";
|
||||
|
||||
const CreateMonitorHeader = ({
|
||||
isAdmin,
|
||||
label = "Create new",
|
||||
label,
|
||||
isLoading = true,
|
||||
path,
|
||||
bulkPath,
|
||||
@@ -14,6 +14,8 @@ const CreateMonitorHeader = ({
|
||||
const navigate = useNavigate();
|
||||
const { t } = useTranslation();
|
||||
const theme = useTheme();
|
||||
|
||||
// Use the provided label or fall back to the translated default
|
||||
|
||||
if (!isAdmin) return null;
|
||||
|
||||
@@ -30,7 +32,7 @@ const CreateMonitorHeader = ({
|
||||
color="accent"
|
||||
onClick={() => navigate(path)}
|
||||
>
|
||||
{label}
|
||||
{label || t("createNew")}
|
||||
</Button>
|
||||
{bulkPath && (
|
||||
<Button
|
||||
|
||||
@@ -47,46 +47,47 @@ import "./index.css";
|
||||
import { useLocation, useNavigate } from "react-router";
|
||||
import { useTheme } from "@emotion/react";
|
||||
import { useDispatch, useSelector } from "react-redux";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { clearAuthState } from "../../Features/Auth/authSlice";
|
||||
import { toggleSidebar } from "../../Features/UI/uiSlice";
|
||||
import { clearUptimeMonitorState } from "../../Features/UptimeMonitors/uptimeMonitorsSlice";
|
||||
|
||||
const menu = [
|
||||
{ name: "Uptime", path: "uptime", icon: <Monitors /> },
|
||||
{ name: "Pagespeed", path: "pagespeed", icon: <PageSpeed /> },
|
||||
{ name: "Infrastructure", path: "infrastructure", icon: <Integrations /> },
|
||||
const getMenu = (t) => [
|
||||
{ name: t("menu.uptime"), path: "uptime", icon: <Monitors /> },
|
||||
{ name: t("menu.pagespeed"), path: "pagespeed", icon: <PageSpeed /> },
|
||||
{ name: t("menu.infrastructure"), path: "infrastructure", icon: <Integrations /> },
|
||||
{
|
||||
name: "Distributed uptime",
|
||||
name: t("menu.distributedUptime"),
|
||||
path: "distributed-uptime",
|
||||
icon: <DistributedUptimeIcon />,
|
||||
},
|
||||
{ name: "Incidents", path: "incidents", icon: <Incidents /> },
|
||||
{ name: t("menu.incidents"), path: "incidents", icon: <Incidents /> },
|
||||
|
||||
{ name: "Status pages", path: "status", icon: <StatusPages /> },
|
||||
{ name: "Maintenance", path: "maintenance", icon: <Maintenance /> },
|
||||
// { name: "Integrations", path: "integrations", icon: <Integrations /> },
|
||||
{ name: t("menu.statusPages"), path: "status", icon: <StatusPages /> },
|
||||
{ name: t("menu.maintenance"), path: "maintenance", icon: <Maintenance /> },
|
||||
// { name: t("menu.integrations"), path: "integrations", icon: <Integrations /> },
|
||||
{
|
||||
name: "Settings",
|
||||
name: t("menu.settings"),
|
||||
icon: <Settings />,
|
||||
path: "settings",
|
||||
},
|
||||
];
|
||||
|
||||
const otherMenuItems = [
|
||||
{ name: "Support", path: "support", icon: <Support /> },
|
||||
const getOtherMenuItems = (t) => [
|
||||
{ name: t("menu.support"), path: "support", icon: <Support /> },
|
||||
{
|
||||
name: "Discussions",
|
||||
name: t("menu.discussions"),
|
||||
path: "discussions",
|
||||
icon: <Discussions />,
|
||||
},
|
||||
{ name: "Docs", path: "docs", icon: <Docs /> },
|
||||
{ name: "Changelog", path: "changelog", icon: <ChangeLog /> },
|
||||
{ name: t("menu.docs"), path: "docs", icon: <Docs /> },
|
||||
{ name: t("menu.changelog"), path: "changelog", icon: <ChangeLog /> },
|
||||
];
|
||||
|
||||
const accountMenuItems = [
|
||||
{ name: "Profile", path: "account/profile", icon: <UserSvg /> },
|
||||
{ name: "Password", path: "account/password", icon: <LockSvg /> },
|
||||
{ name: "Team", path: "account/team", icon: <TeamSvg /> },
|
||||
const getAccountMenuItems = (t) => [
|
||||
{ name: t("menu.profile"), path: "account/profile", icon: <UserSvg /> },
|
||||
{ name: t("menu.password"), path: "account/password", icon: <LockSvg /> },
|
||||
{ name: t("menu.team"), path: "account/team", icon: <TeamSvg /> },
|
||||
];
|
||||
|
||||
/* TODO this could be a key in nested Path would be the link */
|
||||
@@ -118,7 +119,12 @@ function Sidebar() {
|
||||
const navigate = useNavigate();
|
||||
const location = useLocation();
|
||||
const dispatch = useDispatch();
|
||||
const { t } = useTranslation();
|
||||
const authState = useSelector((state) => state.auth);
|
||||
|
||||
const menu = getMenu(t);
|
||||
const otherMenuItems = getOtherMenuItems(t);
|
||||
const accountMenuItems = getAccountMenuItems(t);
|
||||
const collapsed = useSelector((state) => state.ui.sidebar.collapsed);
|
||||
const [open, setOpen] = useState({ Dashboard: false, Account: false, Other: false });
|
||||
const [anchorEl, setAnchorEl] = useState(null);
|
||||
@@ -726,7 +732,11 @@ function Sidebar() {
|
||||
{authState.user?.firstName} {authState.user?.lastName}
|
||||
</Typography>
|
||||
<Typography sx={{ textTransform: "capitalize" }}>
|
||||
{authState.user?.role}
|
||||
{authState.user?.role?.includes("superadmin") ? t("roles.superAdmin") :
|
||||
authState.user?.role?.includes("admin") ? t("roles.admin") :
|
||||
authState.user?.role?.includes("user") ? t("roles.teamMember") :
|
||||
authState.user?.role?.includes("demo") ? t("roles.demoUser") :
|
||||
authState.user?.role}
|
||||
</Typography>
|
||||
</Box>
|
||||
<Stack
|
||||
|
||||
@@ -2,11 +2,11 @@ import { useTheme } from "@emotion/react";
|
||||
import TabPanel from "@mui/lab/TabPanel";
|
||||
import { Button, ButtonGroup, Stack, Typography } from "@mui/material";
|
||||
import { useEffect, useState } from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import TextInput from "../../Inputs/TextInput";
|
||||
import { credentials } from "../../../Validation/validation";
|
||||
import { networkService } from "../../../main";
|
||||
import { createToast } from "../../../Utils/toastUtils";
|
||||
import { useSelector } from "react-redux";
|
||||
import Select from "../../Inputs/Select";
|
||||
import { GenericDialog } from "../../Dialog/genericDialog";
|
||||
import DataTable from "../../Table/";
|
||||
@@ -21,6 +21,7 @@ import { useGetInviteToken } from "../../../Hooks/inviteHooks";
|
||||
|
||||
const TeamPanel = () => {
|
||||
const theme = useTheme();
|
||||
const { t } = useTranslation();
|
||||
const SPACING_GAP = theme.spacing(12);
|
||||
|
||||
const [toInvite, setToInvite] = useState({
|
||||
@@ -39,7 +40,7 @@ const TeamPanel = () => {
|
||||
const headers = [
|
||||
{
|
||||
id: "name",
|
||||
content: "Name",
|
||||
content: t("teamPanel.table.name"),
|
||||
render: (row) => {
|
||||
return (
|
||||
<Stack>
|
||||
@@ -47,16 +48,16 @@ const TeamPanel = () => {
|
||||
{row.firstName + " " + row.lastName}
|
||||
</Typography>
|
||||
<Typography>
|
||||
Created {new Date(row.createdAt).toLocaleDateString()}
|
||||
{t("teamPanel.table.created")} {new Date(row.createdAt).toLocaleDateString()}
|
||||
</Typography>
|
||||
</Stack>
|
||||
);
|
||||
},
|
||||
},
|
||||
{ id: "email", content: "Email", render: (row) => row.email },
|
||||
{ id: "email", content: t("teamPanel.table.email"), render: (row) => row.email },
|
||||
{
|
||||
id: "role",
|
||||
content: "Role",
|
||||
content: t("teamPanel.table.role"),
|
||||
render: (row) => row.role,
|
||||
},
|
||||
];
|
||||
@@ -78,10 +79,10 @@ const TeamPanel = () => {
|
||||
|
||||
useEffect(() => {
|
||||
const ROLE_MAP = {
|
||||
superadmin: "Super admin",
|
||||
admin: "Admin",
|
||||
user: "Team member",
|
||||
demo: "Demo User",
|
||||
superadmin: t("roles.superAdmin"),
|
||||
admin: t("roles.admin"),
|
||||
user: t("roles.teamMember"),
|
||||
demo: t("roles.demoUser"),
|
||||
};
|
||||
let team = members;
|
||||
if (filter !== "all")
|
||||
@@ -98,7 +99,7 @@ const TeamPanel = () => {
|
||||
role: member.role.map((role) => ROLE_MAP[role]).join(","),
|
||||
}));
|
||||
setData(team);
|
||||
}, [filter, members]);
|
||||
}, [members, filter, t]);
|
||||
|
||||
useEffect(() => {
|
||||
setIsDisabled(Object.keys(errors).length !== 0 || toInvite.email === "");
|
||||
@@ -197,7 +198,7 @@ const TeamPanel = () => {
|
||||
spellCheck="false"
|
||||
gap={SPACING_GAP}
|
||||
>
|
||||
<Typography component="h1">Team members</Typography>
|
||||
<Typography component="h1">{t("teamPanel.teamMembers")}</Typography>
|
||||
<Stack
|
||||
direction="row"
|
||||
justifyContent="space-between"
|
||||
@@ -214,21 +215,21 @@ const TeamPanel = () => {
|
||||
filled={(filter === "all").toString()}
|
||||
onClick={() => setFilter("all")}
|
||||
>
|
||||
All
|
||||
{t("teamPanel.filter.all")}
|
||||
</Button>
|
||||
<Button
|
||||
variant="group"
|
||||
filled={(filter === "admin").toString()}
|
||||
onClick={() => setFilter("admin")}
|
||||
>
|
||||
Super admin
|
||||
{t("roles.superAdmin")}
|
||||
</Button>
|
||||
<Button
|
||||
variant="group"
|
||||
filled={(filter === "user").toString()}
|
||||
onClick={() => setFilter("user")}
|
||||
>
|
||||
Member
|
||||
{t("teamPanel.filter.member")}
|
||||
</Button>
|
||||
</ButtonGroup>
|
||||
</Stack>
|
||||
@@ -237,22 +238,20 @@ const TeamPanel = () => {
|
||||
color="accent"
|
||||
onClick={() => setIsOpen(true)}
|
||||
>
|
||||
Invite a team member
|
||||
{t("teamPanel.inviteTeamMember")}
|
||||
</Button>
|
||||
</Stack>
|
||||
|
||||
<DataTable
|
||||
headers={headers}
|
||||
data={data}
|
||||
config={{ emptyView: "There are no team members with this role" }}
|
||||
config={{ emptyView: t("teamPanel.noMembers") }}
|
||||
/>
|
||||
</Stack>
|
||||
|
||||
<GenericDialog
|
||||
title={"Invite new team member"}
|
||||
description={
|
||||
"When you add a new team member, they will get access to all monitors."
|
||||
}
|
||||
title={t("teamPanel.inviteNewTeamMember")}
|
||||
description={t("teamPanel.inviteDescription")}
|
||||
open={isOpen}
|
||||
onClose={closeInviteModal}
|
||||
theme={theme}
|
||||
@@ -261,7 +260,7 @@ const TeamPanel = () => {
|
||||
marginBottom={SPACING_GAP}
|
||||
type="email"
|
||||
id="input-team-member"
|
||||
placeholder="Email"
|
||||
placeholder={t("teamPanel.email")}
|
||||
value={toInvite.email}
|
||||
onChange={handleChange}
|
||||
error={errors.email ? true : false}
|
||||
@@ -269,7 +268,7 @@ const TeamPanel = () => {
|
||||
/>
|
||||
<Select
|
||||
id="team-member-role"
|
||||
placeholder="Select role"
|
||||
placeholder={t("teamPanel.selectRole")}
|
||||
isHidden={true}
|
||||
value={toInvite.role[0]}
|
||||
onChange={(event) =>
|
||||
@@ -279,11 +278,11 @@ const TeamPanel = () => {
|
||||
}))
|
||||
}
|
||||
items={[
|
||||
{ _id: "admin", name: "Admin" },
|
||||
{ _id: "user", name: "User" },
|
||||
{ _id: "admin", name: t("roles.admin") },
|
||||
{ _id: "user", name: t("roles.teamMember") },
|
||||
]}
|
||||
/>
|
||||
{token && <Typography>Invite link</Typography>}
|
||||
{token && <Typography>{t("teamPanel.inviteLink")}</Typography>}
|
||||
{token && (
|
||||
<TextInput
|
||||
id="invite-token"
|
||||
@@ -302,7 +301,7 @@ const TeamPanel = () => {
|
||||
color="error"
|
||||
onClick={closeInviteModal}
|
||||
>
|
||||
Cancel
|
||||
{t("teamPanel.cancel")}
|
||||
</Button>
|
||||
<Button
|
||||
variant="contained"
|
||||
@@ -311,7 +310,7 @@ const TeamPanel = () => {
|
||||
loading={isSendingInvite}
|
||||
disabled={isDisabled}
|
||||
>
|
||||
Get token
|
||||
{t("teamPanel.getToken")}
|
||||
</Button>
|
||||
<Button
|
||||
variant="contained"
|
||||
@@ -320,7 +319,7 @@ const TeamPanel = () => {
|
||||
loading={isSendingInvite}
|
||||
disabled={isDisabled}
|
||||
>
|
||||
E-mail token
|
||||
{t("teamPanel.emailToken")}
|
||||
</Button>
|
||||
</Stack>
|
||||
</GenericDialog>
|
||||
|
||||
@@ -27,22 +27,14 @@ import { useTranslation } from "react-i18next";
|
||||
* @returns {JSX.Element} The rendered Filter component.
|
||||
*/
|
||||
|
||||
const typeOptions = [
|
||||
const getTypeOptions = () => [
|
||||
{ value: "http", label: "HTTP(S)" },
|
||||
{ value: "ping", label: "Ping" },
|
||||
{ value: "docker", label: "Docker" },
|
||||
{ value: "port", label: "Port" },
|
||||
];
|
||||
|
||||
const statusOptions = [
|
||||
{ value: "Up", label: "Up" },
|
||||
{ value: "Down", label: "Down" },
|
||||
];
|
||||
|
||||
const stateOptions = [
|
||||
{ value: "Active", label: "Active" },
|
||||
{ value: "Paused", label: "Paused" },
|
||||
];
|
||||
// These functions were moved inline to ensure translations are applied correctly
|
||||
|
||||
const Filter = ({
|
||||
selectedTypes,
|
||||
@@ -58,6 +50,18 @@ const Filter = ({
|
||||
const theme = useTheme();
|
||||
const { t } = useTranslation();
|
||||
|
||||
const typeOptions = getTypeOptions();
|
||||
// Create status options with translations
|
||||
const statusOptions = [
|
||||
{ value: "Up", label: t("monitorStatus.up") },
|
||||
{ value: "Down", label: t("monitorStatus.down") },
|
||||
];
|
||||
// Create state options with translations
|
||||
const stateOptions = [
|
||||
{ value: "Active", label: t("monitorState.active") },
|
||||
{ value: "Paused", label: t("monitorState.paused") },
|
||||
];
|
||||
|
||||
const handleTypeChange = (event) => {
|
||||
const selectedValues = event.target.value;
|
||||
setSelectedTypes(selectedValues.length > 0 ? selectedValues : undefined);
|
||||
|
||||
@@ -2,10 +2,12 @@ import PropTypes from "prop-types";
|
||||
import { Stack } from "@mui/material";
|
||||
import StatusBox from "./statusBox";
|
||||
import { useTheme } from "@emotion/react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import SkeletonLayout from "./skeleton";
|
||||
|
||||
const StatusBoxes = ({ shouldRender, monitorsSummary }) => {
|
||||
const theme = useTheme();
|
||||
const { t } = useTranslation();
|
||||
if (!shouldRender) return <SkeletonLayout shouldRender={shouldRender} />;
|
||||
return (
|
||||
<Stack
|
||||
@@ -14,15 +16,15 @@ const StatusBoxes = ({ shouldRender, monitorsSummary }) => {
|
||||
justifyContent="space-between"
|
||||
>
|
||||
<StatusBox
|
||||
title="up"
|
||||
title={t("monitorStatus.up").toUpperCase()}
|
||||
value={monitorsSummary?.upMonitors ?? 0}
|
||||
/>
|
||||
<StatusBox
|
||||
title="down"
|
||||
title={t("monitorStatus.down").toUpperCase()}
|
||||
value={monitorsSummary?.downMonitors ?? 0}
|
||||
/>
|
||||
<StatusBox
|
||||
title="paused"
|
||||
title={t("monitorStatus.paused").toUpperCase()}
|
||||
value={monitorsSummary?.pausedMonitors ?? 0}
|
||||
/>
|
||||
</Stack>
|
||||
@@ -31,6 +33,7 @@ const StatusBoxes = ({ shouldRender, monitorsSummary }) => {
|
||||
|
||||
StatusBoxes.propTypes = {
|
||||
monitorsSummary: PropTypes.object,
|
||||
shouldRender: PropTypes.bool,
|
||||
};
|
||||
|
||||
export default StatusBoxes;
|
||||
|
||||
@@ -31,11 +31,11 @@ import PropTypes from "prop-types";
|
||||
import useFetchMonitorsWithSummary from "../../../Hooks/useFetchMonitorsWithSummary";
|
||||
import useFetchMonitorsWithChecks from "../../../Hooks/useFetchMonitorsWithChecks";
|
||||
import { useTranslation } from "react-i18next";
|
||||
const BREADCRUMBS = [{ name: `Uptime`, path: "/uptime" }];
|
||||
const TYPES = ["http", "ping", "docker", "port"];
|
||||
const CreateMonitorButton = ({ shouldRender }) => {
|
||||
// Utils
|
||||
const navigate = useNavigate();
|
||||
const { t } = useTranslation();
|
||||
if (shouldRender === false) {
|
||||
return;
|
||||
}
|
||||
@@ -49,7 +49,7 @@ const CreateMonitorButton = ({ shouldRender }) => {
|
||||
navigate("/uptime/create");
|
||||
}}
|
||||
>
|
||||
Create new
|
||||
{t("createNew")}
|
||||
</Button>
|
||||
</Box>
|
||||
);
|
||||
@@ -78,10 +78,13 @@ const UptimeMonitors = () => {
|
||||
|
||||
// Utils
|
||||
const theme = useTheme();
|
||||
const navigate = useNavigate();
|
||||
const isAdmin = useIsAdmin();
|
||||
const dispatch = useDispatch();
|
||||
const { t } = useTranslation();
|
||||
|
||||
const BREADCRUMBS = [{ name: t("menu.uptime"), path: "/uptime" }];
|
||||
|
||||
// Handlers
|
||||
const handleChangePage = (event, newPage) => {
|
||||
setPage(newPage);
|
||||
|
||||
@@ -3,6 +3,7 @@ import { useTheme } from "@emotion/react";
|
||||
import { Box, Typography } from "@mui/material";
|
||||
import { useDispatch, useSelector } from "react-redux";
|
||||
import { useEffect } from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { setGreeting } from "../Features/UI/uiSlice";
|
||||
|
||||
const early = [
|
||||
@@ -133,6 +134,7 @@ const evening = [
|
||||
const Greeting = ({ type = "" }) => {
|
||||
const theme = useTheme();
|
||||
const dispatch = useDispatch();
|
||||
const { t } = useTranslation();
|
||||
const { firstName } = useSelector((state) => state.auth.user);
|
||||
const index = useSelector((state) => state.ui.greeting.index);
|
||||
const lastUpdate = useSelector((state) => state.ui.greeting.lastUpdate);
|
||||
@@ -147,7 +149,7 @@ const Greeting = ({ type = "" }) => {
|
||||
let random = Math.floor(Math.random() * 5);
|
||||
dispatch(setGreeting({ index: random, lastUpdate: hour }));
|
||||
}
|
||||
}, [dispatch, hour]);
|
||||
}, [dispatch, hour, lastUpdate]);
|
||||
|
||||
let greetingArray =
|
||||
hour < 6 ? early : hour < 12 ? morning : hour < 18 ? afternoon : evening;
|
||||
@@ -165,7 +167,7 @@ const Greeting = ({ type = "" }) => {
|
||||
fontSize="inherit"
|
||||
color={theme.palette.primary.contrastTextTertiary}
|
||||
>
|
||||
{prepend},{" "}
|
||||
{t("greeting.prepend", { defaultValue: prepend })}, {" "}
|
||||
</Typography>
|
||||
<Typography
|
||||
component="span"
|
||||
@@ -181,7 +183,7 @@ const Greeting = ({ type = "" }) => {
|
||||
lineHeight={1}
|
||||
color={theme.palette.primary.contrastTextTertiary}
|
||||
>
|
||||
{append} — Here’s an overview of your {type} monitors.
|
||||
{t("greeting.append", { defaultValue: append })} — {t("greeting.overview", { type: t(`menu.${type}`) })}
|
||||
</Typography>
|
||||
</Box>
|
||||
);
|
||||
|
||||
@@ -439,6 +439,69 @@
|
||||
"pageSpeedApiKeyFieldLabel": "PageSpeed API key",
|
||||
"pageSpeedApiKeyFieldDescription": "Enter your Google PageSpeed API key to enable pagespeed monitoring. Click Reset to update the key.",
|
||||
"pageSpeedApiKeyFieldResetLabel": "API key is set. Click Reset to change it.",
|
||||
"reset": "Reset"
|
||||
"reset": "Reset",
|
||||
"createNew": "Create new",
|
||||
"greeting": {
|
||||
"prepend": "Hey there",
|
||||
"append": "The afternoon is your playground—let's make it epic!",
|
||||
"overview": "Here's an overview of your {{type}} monitors."
|
||||
},
|
||||
"monitorStatus": {
|
||||
"up": "up",
|
||||
"down": "down",
|
||||
"paused": "paused"
|
||||
},
|
||||
"roles": {
|
||||
"superAdmin": "Super admin",
|
||||
"admin": "Admin",
|
||||
"teamMember": "Team member",
|
||||
"demoUser": "Demo user"
|
||||
},
|
||||
"teamPanel": {
|
||||
"teamMembers": "Team members",
|
||||
"filter": {
|
||||
"all": "All",
|
||||
"member": "Member"
|
||||
},
|
||||
"inviteTeamMember": "Invite a team member",
|
||||
"inviteNewTeamMember": "Invite new team member",
|
||||
"inviteDescription": "When you add a new team member, they will get access to all monitors.",
|
||||
"email": "Email",
|
||||
"selectRole": "Select role",
|
||||
"inviteLink": "Invite link",
|
||||
"cancel": "Cancel",
|
||||
"noMembers": "There are no team members with this role",
|
||||
"getToken": "Get token",
|
||||
"emailToken": "E-mail token",
|
||||
"table": {
|
||||
"name": "Name",
|
||||
"email": "Email",
|
||||
"role": "Role",
|
||||
"created": "Created"
|
||||
}
|
||||
},
|
||||
"monitorState": {
|
||||
"paused": "paused",
|
||||
"resumed": "resumed",
|
||||
"active": "active"
|
||||
},
|
||||
"menu": {
|
||||
"uptime": "Uptime",
|
||||
"pagespeed": "Pagespeed",
|
||||
"infrastructure": "Infrastructure",
|
||||
"distributedUptime": "Distributed uptime",
|
||||
"incidents": "Incidents",
|
||||
"statusPages": "Status pages",
|
||||
"maintenance": "Maintenance",
|
||||
"integrations": "Integrations",
|
||||
"settings": "Settings",
|
||||
"support": "Support",
|
||||
"discussions": "Discussions",
|
||||
"docs": "Docs",
|
||||
"changelog": "Changelog",
|
||||
"profile": "Profile",
|
||||
"password": "Password",
|
||||
"team": "Team"
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -65,7 +65,18 @@
|
||||
"authRegisterLastName": "Фамилия",
|
||||
"authRegisterEmail": "Эл. почта",
|
||||
"authRegisterEmailRequired": "Чтобы продолжить, пожалуйста, введите ваш адрес электронной почты",
|
||||
"authRegisterEmailInvalid": "Пожалуйста, введите корректный адрес электронной почты",
|
||||
"authRegisterEmailInvalid": "Пожалуйста, введите действительный адрес электронной почты",
|
||||
"bulkImport": {
|
||||
"title": "Массовый импорт",
|
||||
"selectFileTips": "Выберите CSV-файл для загрузки",
|
||||
"selectFileDescription": "Вы можете скачать наш <template>шаблон</template> или <sample>пример</sample>",
|
||||
"selectFile": "Выбрать файл",
|
||||
"parsingFailed": "Ошибка анализа",
|
||||
"uploadSuccess": "Мониторы успешно созданы!",
|
||||
"validationFailed": "Ошибка проверки",
|
||||
"noFileSelected": "Файл не выбран",
|
||||
"fallbackPage": "Импортируйте файл для загрузки списка серверов"
|
||||
},
|
||||
"distributedStatusHeaderText": "Охват реального времени и реального устройства",
|
||||
"distributedStatusSubHeaderText": "Работает на миллионах устройств по всему миру, просматривайте производительность системы по глобальному региону, стране или городу",
|
||||
"settingsGeneralSettings": "Общие настройки",
|
||||
@@ -393,5 +404,68 @@
|
||||
"maintenanceTableActionMenuDialogTitle": "",
|
||||
"pageSpeedWarning": "Предупреждение: Вы не добавили ключ API Google PageSpeed. Без него монитор PageSpeed не будет работать.",
|
||||
"pageSpeedLearnMoreLink": "Нажмите здесь, чтобы узнать",
|
||||
"pageSpeedAddApiKey": "как добавить ваш ключ API."
|
||||
"pageSpeedAddApiKey": "как добавить ваш ключ API.",
|
||||
"createNew": "Создать новый",
|
||||
"greeting": {
|
||||
"prepend": "Привет",
|
||||
"append": "День прекрасен для новых достижений!",
|
||||
"overview": "Вот обзор ваших мониторов {{type}}."
|
||||
},
|
||||
"monitorStatus": {
|
||||
"up": "работает",
|
||||
"down": "не работает",
|
||||
"paused": "приостановлен"
|
||||
},
|
||||
"roles": {
|
||||
"superAdmin": "Суперадминистратор",
|
||||
"admin": "Администратор",
|
||||
"teamMember": "Член команды",
|
||||
"demoUser": "Демо-пользователь"
|
||||
},
|
||||
"teamPanel": {
|
||||
"teamMembers": "Члены команды",
|
||||
"filter": {
|
||||
"all": "Все",
|
||||
"member": "Член"
|
||||
},
|
||||
"inviteTeamMember": "Пригласить члена команды",
|
||||
"inviteNewTeamMember": "Пригласить нового члена команды",
|
||||
"inviteDescription": "Когда вы добавляете нового члена команды, он получит доступ ко всем мониторам.",
|
||||
"email": "Эл. почта",
|
||||
"selectRole": "Выберите роль",
|
||||
"inviteLink": "Ссылка для приглашения",
|
||||
"cancel": "Отмена",
|
||||
"noMembers": "Нет членов команды с этой ролью",
|
||||
"getToken": "Получить токен",
|
||||
"emailToken": "Отправить токен по эл. почте",
|
||||
"table": {
|
||||
"name": "Имя",
|
||||
"email": "Эл. почта",
|
||||
"role": "Роль",
|
||||
"created": "Создан"
|
||||
}
|
||||
},
|
||||
"monitorState": {
|
||||
"paused": "приостановлен",
|
||||
"resumed": "возобновлен",
|
||||
"active": "активный"
|
||||
},
|
||||
"menu": {
|
||||
"uptime": "Аптайм",
|
||||
"pagespeed": "Скорость страницы",
|
||||
"infrastructure": "Инфраструктура",
|
||||
"distributedUptime": "Распределенный аптайм",
|
||||
"incidents": "Инциденты",
|
||||
"statusPages": "Страницы статуса",
|
||||
"maintenance": "Обслуживание",
|
||||
"integrations": "Интеграции",
|
||||
"settings": "Настройки",
|
||||
"support": "Поддержка",
|
||||
"discussions": "Обсуждения",
|
||||
"docs": "Документация",
|
||||
"changelog": "История изменений",
|
||||
"profile": "Профиль",
|
||||
"password": "Пароль",
|
||||
"team": "Команда"
|
||||
}
|
||||
}
|
||||
@@ -418,14 +418,77 @@
|
||||
"authRegisterEmailRequired": "Devam etmek için lütfen e-posta adresinizi girin",
|
||||
"authRegisterEmailInvalid": "Lütfen geçerli bir e-posta adresi girin",
|
||||
"bulkImport": {
|
||||
"title": "",
|
||||
"selectFileTips": "",
|
||||
"selectFileDescription": "",
|
||||
"selectFile": "",
|
||||
"parsingFailed": "",
|
||||
"uploadSuccess": "",
|
||||
"validationFailed": "",
|
||||
"noFileSelected": "",
|
||||
"fallbackPage": ""
|
||||
"title": "Toplu İçe Aktar",
|
||||
"selectFileTips": "Yüklemek için CSV dosyası seçin",
|
||||
"selectFileDescription": "<template>Şablonumuzu</template> veya <sample>örneğimizi</sample> indirebilirsiniz",
|
||||
"selectFile": "Dosya Seç",
|
||||
"parsingFailed": "Ayrıştırma başarısız oldu",
|
||||
"uploadSuccess": "Monitörler başarıyla oluşturuldu!",
|
||||
"validationFailed": "Doğrulama başarısız oldu",
|
||||
"noFileSelected": "Dosya seçilmedi",
|
||||
"fallbackPage": "Sunucuların listesini toplu olarak yüklemek için bir dosya içe aktarın"
|
||||
},
|
||||
"createNew": "Yeni oluştur",
|
||||
"greeting": {
|
||||
"prepend": "Merhaba",
|
||||
"append": "Öğleden sonra senin oyun alanın—hadi onu muhteşem yapalım!",
|
||||
"overview": "İşte {{type}} monitörlerinizin genel görünümü."
|
||||
},
|
||||
"monitorStatus": {
|
||||
"up": "aktif",
|
||||
"down": "devre dışı",
|
||||
"paused": "duraklatıldı"
|
||||
},
|
||||
"roles": {
|
||||
"superAdmin": "Süper yönetici",
|
||||
"admin": "Yönetici",
|
||||
"teamMember": "Takım üyesi",
|
||||
"demoUser": "Demo kullanıcı"
|
||||
},
|
||||
"teamPanel": {
|
||||
"teamMembers": "Takım üyeleri",
|
||||
"filter": {
|
||||
"all": "Tümü",
|
||||
"member": "Üye"
|
||||
},
|
||||
"inviteTeamMember": "Takım üyesi davet et",
|
||||
"inviteNewTeamMember": "Yeni takım üyesi davet et",
|
||||
"inviteDescription": "Yeni bir takım üyesi eklediğinizde, tüm monitörlere erişim hakkı alacaktır.",
|
||||
"email": "E-posta",
|
||||
"selectRole": "Rol seçin",
|
||||
"inviteLink": "Davet bağlantısı",
|
||||
"cancel": "İptal",
|
||||
"noMembers": "Bu role sahip takım üyesi bulunmamaktadır",
|
||||
"getToken": "Token al",
|
||||
"emailToken": "Token e-posta ile gönder",
|
||||
"table": {
|
||||
"name": "Ad",
|
||||
"email": "E-posta",
|
||||
"role": "Rol",
|
||||
"created": "Oluşturuldu"
|
||||
}
|
||||
},
|
||||
"monitorState": {
|
||||
"paused": "duraklatıldı",
|
||||
"resumed": "devam ettirildi",
|
||||
"active": "aktif"
|
||||
},
|
||||
"menu": {
|
||||
"uptime": "Çalışma Süresi",
|
||||
"pagespeed": "Sayfa Hızı",
|
||||
"infrastructure": "Altyapı",
|
||||
"distributedUptime": "Dağıtılmış Çalışma Süresi",
|
||||
"incidents": "Olaylar",
|
||||
"statusPages": "Durum Sayfaları",
|
||||
"maintenance": "Bakım",
|
||||
"integrations": "Entegrasyonlar",
|
||||
"settings": "Ayarlar",
|
||||
"support": "Destek",
|
||||
"discussions": "Tartışmalar",
|
||||
"docs": "Belgeler",
|
||||
"changelog": "Değişiklik Günlüğü",
|
||||
"profile": "Profil",
|
||||
"password": "Şifre",
|
||||
"team": "Takım"
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user