mirror of
https://github.com/outline/outline.git
synced 2026-01-24 20:29:18 -06:00
new translation labels & Portuguese from Portugal translation
This commit is contained in:
@@ -4,6 +4,7 @@ import { observable } from "mobx";
|
||||
import { observer } from "mobx-react";
|
||||
import { EditIcon } from "outline-icons";
|
||||
import * as React from "react";
|
||||
import { withTranslation } from "react-i18next";
|
||||
import styled from "styled-components";
|
||||
import User from "models/User";
|
||||
import UserProfile from "scenes/UserProfile";
|
||||
@@ -18,6 +19,7 @@ type Props = {
|
||||
lastViewedAt: string,
|
||||
};
|
||||
|
||||
@withTranslation()
|
||||
@observer
|
||||
class AvatarWithPresence extends React.Component<Props> {
|
||||
@observable isOpen: boolean = false;
|
||||
@@ -37,6 +39,7 @@ class AvatarWithPresence extends React.Component<Props> {
|
||||
isPresent,
|
||||
isEditing,
|
||||
isCurrentUser,
|
||||
t,
|
||||
} = this.props;
|
||||
|
||||
return (
|
||||
@@ -44,13 +47,20 @@ class AvatarWithPresence extends React.Component<Props> {
|
||||
<Tooltip
|
||||
tooltip={
|
||||
<Centered>
|
||||
<strong>{user.name}</strong> {isCurrentUser && "(You)"}
|
||||
<br />
|
||||
{isPresent
|
||||
? isEditing
|
||||
? "currently editing"
|
||||
: "currently viewing"
|
||||
: `viewed ${distanceInWordsToNow(new Date(lastViewedAt))} ago`}
|
||||
{t(
|
||||
"<strong>{{ userName }} {{ you }} <br /> {{ action }}</strong>",
|
||||
{
|
||||
userName: user.name,
|
||||
you: isCurrentUser && t("(You)"),
|
||||
action: isPresent
|
||||
? isEditing
|
||||
? t("currently editing")
|
||||
: t("currently viewing")
|
||||
: t("viewed {{ timeAgo }} ago", {
|
||||
timeAgo: distanceInWordsToNow(new Date(lastViewedAt)),
|
||||
}),
|
||||
}
|
||||
)}
|
||||
</Centered>
|
||||
}
|
||||
placement="bottom"
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
// @flow
|
||||
import i18n from "i18next";
|
||||
import { observer, inject } from "mobx-react";
|
||||
import {
|
||||
ArchiveIcon,
|
||||
@@ -21,6 +22,8 @@ import Flex from "components/Flex";
|
||||
import BreadcrumbMenu from "./BreadcrumbMenu";
|
||||
import { collectionUrl } from "utils/routeHelpers";
|
||||
|
||||
const t = (k) => i18n.t(k);
|
||||
|
||||
type Props = {
|
||||
document: Document,
|
||||
collections: CollectionsStore,
|
||||
@@ -34,7 +37,7 @@ function Icon({ document }) {
|
||||
<CollectionName to="/trash">
|
||||
<TrashIcon color="currentColor" />
|
||||
|
||||
<span>Trash</span>
|
||||
<span>{t("Trash")}</span>
|
||||
</CollectionName>
|
||||
<Slash />
|
||||
</>
|
||||
@@ -46,7 +49,7 @@ function Icon({ document }) {
|
||||
<CollectionName to="/archive">
|
||||
<ArchiveIcon color="currentColor" />
|
||||
|
||||
<span>Archive</span>
|
||||
<span>{t("Archive")}</span>
|
||||
</CollectionName>
|
||||
<Slash />
|
||||
</>
|
||||
@@ -58,7 +61,7 @@ function Icon({ document }) {
|
||||
<CollectionName to="/drafts">
|
||||
<EditIcon color="currentColor" />
|
||||
|
||||
<span>Drafts</span>
|
||||
<span>{t("Drafts")}</span>
|
||||
</CollectionName>
|
||||
<Slash />
|
||||
</>
|
||||
@@ -70,7 +73,7 @@ function Icon({ document }) {
|
||||
<CollectionName to="/templates">
|
||||
<ShapesIcon color="currentColor" />
|
||||
|
||||
<span>Templates</span>
|
||||
<span>{t("Templates")}</span>
|
||||
</CollectionName>
|
||||
<Slash />
|
||||
</>
|
||||
|
||||
@@ -3,6 +3,7 @@ import { observable } from "mobx";
|
||||
import { observer } from "mobx-react";
|
||||
import { StarredIcon, PlusIcon } from "outline-icons";
|
||||
import * as React from "react";
|
||||
import { withTranslation } from "react-i18next";
|
||||
import { Link, Redirect } from "react-router-dom";
|
||||
import styled, { withTheme } from "styled-components";
|
||||
import Document from "models/Document";
|
||||
@@ -29,6 +30,7 @@ type Props = {
|
||||
|
||||
const SEARCH_RESULT_REGEX = /<b\b[^>]*>(.*?)<\/b>/gi;
|
||||
|
||||
@withTranslation()
|
||||
@observer
|
||||
class DocumentPreview extends React.Component<Props> {
|
||||
@observable redirectTo: ?string;
|
||||
@@ -72,6 +74,7 @@ class DocumentPreview extends React.Component<Props> {
|
||||
showTemplate,
|
||||
highlight,
|
||||
context,
|
||||
t,
|
||||
} = this.props;
|
||||
|
||||
if (this.redirectTo) {
|
||||
@@ -91,7 +94,7 @@ class DocumentPreview extends React.Component<Props> {
|
||||
>
|
||||
<Heading>
|
||||
<Title text={document.titleWithDefault} highlight={highlight} />
|
||||
{document.isNew && <Badge yellow>New</Badge>}
|
||||
{document.isNew && <Badge yellow>{t("New")}</Badge>}
|
||||
{!document.isDraft &&
|
||||
!document.isArchived &&
|
||||
!document.isTemplate && (
|
||||
@@ -104,12 +107,16 @@ class DocumentPreview extends React.Component<Props> {
|
||||
</Actions>
|
||||
)}
|
||||
{document.isDraft && showDraft && (
|
||||
<Tooltip tooltip="Only visible to you" delay={500} placement="top">
|
||||
<Badge>Draft</Badge>
|
||||
<Tooltip
|
||||
tooltip={t("Only visible to you")}
|
||||
delay={500}
|
||||
placement="top"
|
||||
>
|
||||
<Badge>{t("Draft")}</Badge>
|
||||
</Tooltip>
|
||||
)}
|
||||
{document.isTemplate && showTemplate && (
|
||||
<Badge primary>Template</Badge>
|
||||
<Badge primary>{t("Template")}</Badge>
|
||||
)}
|
||||
<SecondaryActions>
|
||||
{document.isTemplate &&
|
||||
@@ -120,7 +127,7 @@ class DocumentPreview extends React.Component<Props> {
|
||||
icon={<PlusIcon />}
|
||||
neutral
|
||||
>
|
||||
New doc
|
||||
{t("New doc")}
|
||||
</Button>
|
||||
)}
|
||||
|
||||
|
||||
@@ -5,6 +5,7 @@ import { observer } from "mobx-react";
|
||||
import { MoreIcon } from "outline-icons";
|
||||
import { rgba } from "polished";
|
||||
import * as React from "react";
|
||||
import { withTranslation } from "react-i18next";
|
||||
import { PortalWithState } from "react-portal";
|
||||
import styled from "styled-components";
|
||||
import { fadeAndScaleIn } from "shared/styles/animations";
|
||||
@@ -29,6 +30,7 @@ type Props = {
|
||||
position?: "left" | "right" | "center",
|
||||
};
|
||||
|
||||
@withTranslation()
|
||||
@observer
|
||||
class DropdownMenu extends React.Component<Props> {
|
||||
id: string = `menu${counter++}`;
|
||||
@@ -51,8 +53,9 @@ class DropdownMenu extends React.Component<Props> {
|
||||
) => {
|
||||
return (ev: SyntheticMouseEvent<HTMLElement>) => {
|
||||
ev.preventDefault();
|
||||
const { t } = this.props;
|
||||
const currentTarget = ev.currentTarget;
|
||||
invariant(document.body, "why you not here");
|
||||
invariant(document.body, t("why you not here"));
|
||||
|
||||
if (currentTarget instanceof HTMLDivElement) {
|
||||
this.bodyRect = document.body.getBoundingClientRect();
|
||||
@@ -150,7 +153,7 @@ class DropdownMenu extends React.Component<Props> {
|
||||
};
|
||||
|
||||
render() {
|
||||
const { className, hover, label, children } = this.props;
|
||||
const { className, hover, label, children, t } = this.props;
|
||||
|
||||
return (
|
||||
<div className={className}>
|
||||
@@ -177,7 +180,7 @@ class DropdownMenu extends React.Component<Props> {
|
||||
{label || (
|
||||
<NudeButton
|
||||
id={`${this.id}button`}
|
||||
aria-label="More options"
|
||||
aria-label={t("More options")}
|
||||
aria-haspopup="true"
|
||||
aria-expanded={isOpen ? "true" : "false"}
|
||||
aria-controls={this.id}
|
||||
|
||||
@@ -3,6 +3,7 @@ import { observable } from "mobx";
|
||||
import { observer } from "mobx-react";
|
||||
import { SearchIcon } from "outline-icons";
|
||||
import * as React from "react";
|
||||
import { withTranslation } from "react-i18next";
|
||||
import keydown from "react-keydown";
|
||||
import { withRouter, type RouterHistory } from "react-router-dom";
|
||||
import styled, { withTheme } from "styled-components";
|
||||
@@ -18,6 +19,7 @@ type Props = {
|
||||
collectionId?: string,
|
||||
};
|
||||
|
||||
@withTranslation()
|
||||
@observer
|
||||
class InputSearch extends React.Component<Props> {
|
||||
input: ?Input;
|
||||
@@ -51,13 +53,13 @@ class InputSearch extends React.Component<Props> {
|
||||
};
|
||||
|
||||
render() {
|
||||
const { theme, placeholder = "Search…" } = this.props;
|
||||
const { theme, t } = this.props;
|
||||
|
||||
return (
|
||||
<InputMaxWidth
|
||||
ref={(ref) => (this.input = ref)}
|
||||
type="search"
|
||||
placeholder={placeholder}
|
||||
placeholder={t("Search...")}
|
||||
onInput={this.handleSearchInput}
|
||||
icon={
|
||||
<SearchIcon
|
||||
|
||||
@@ -12,6 +12,7 @@ import {
|
||||
PlusIcon,
|
||||
} from "outline-icons";
|
||||
import * as React from "react";
|
||||
import { withTranslation } from "react-i18next";
|
||||
import styled from "styled-components";
|
||||
|
||||
import AuthStore from "stores/AuthStore";
|
||||
@@ -36,6 +37,7 @@ type Props = {
|
||||
policies: PoliciesStore,
|
||||
};
|
||||
|
||||
@withTranslation()
|
||||
@observer
|
||||
class MainSidebar extends React.Component<Props> {
|
||||
@observable inviteModalOpen = false;
|
||||
@@ -65,7 +67,7 @@ class MainSidebar extends React.Component<Props> {
|
||||
};
|
||||
|
||||
render() {
|
||||
const { auth, documents, policies } = this.props;
|
||||
const { auth, documents, policies, t } = this.props;
|
||||
const { user, team } = auth;
|
||||
if (!user || !team) return null;
|
||||
|
||||
@@ -90,7 +92,7 @@ class MainSidebar extends React.Component<Props> {
|
||||
to="/home"
|
||||
icon={<HomeIcon color="currentColor" />}
|
||||
exact={false}
|
||||
label="Home"
|
||||
label={t("Home")}
|
||||
/>
|
||||
<SidebarLink
|
||||
to={{
|
||||
@@ -98,20 +100,20 @@ class MainSidebar extends React.Component<Props> {
|
||||
state: { fromMenu: true },
|
||||
}}
|
||||
icon={<SearchIcon color="currentColor" />}
|
||||
label="Search"
|
||||
label={t("Search")}
|
||||
exact={false}
|
||||
/>
|
||||
<SidebarLink
|
||||
to="/starred"
|
||||
icon={<StarredIcon color="currentColor" />}
|
||||
exact={false}
|
||||
label="Starred"
|
||||
label={t("Starred")}
|
||||
/>
|
||||
<SidebarLink
|
||||
to="/templates"
|
||||
icon={<ShapesIcon color="currentColor" />}
|
||||
exact={false}
|
||||
label="Templates"
|
||||
label={t("Templates")}
|
||||
active={
|
||||
documents.active ? documents.active.template : undefined
|
||||
}
|
||||
@@ -121,7 +123,7 @@ class MainSidebar extends React.Component<Props> {
|
||||
icon={<EditIcon color="currentColor" />}
|
||||
label={
|
||||
<Drafts align="center">
|
||||
Drafts
|
||||
{t("Drafts")}
|
||||
{documents.totalDrafts > 0 && (
|
||||
<Bubble count={documents.totalDrafts} />
|
||||
)}
|
||||
@@ -146,7 +148,7 @@ class MainSidebar extends React.Component<Props> {
|
||||
to="/archive"
|
||||
icon={<ArchiveIcon color="currentColor" />}
|
||||
exact={false}
|
||||
label="Archive"
|
||||
label={t("Archive")}
|
||||
active={
|
||||
documents.active
|
||||
? documents.active.isArchived && !documents.active.isDeleted
|
||||
@@ -157,7 +159,7 @@ class MainSidebar extends React.Component<Props> {
|
||||
to="/trash"
|
||||
icon={<TrashIcon color="currentColor" />}
|
||||
exact={false}
|
||||
label="Trash"
|
||||
label={t("Trash")}
|
||||
active={
|
||||
documents.active ? documents.active.isDeleted : undefined
|
||||
}
|
||||
@@ -167,21 +169,21 @@ class MainSidebar extends React.Component<Props> {
|
||||
to="/settings/people"
|
||||
onClick={this.handleInviteModalOpen}
|
||||
icon={<PlusIcon color="currentColor" />}
|
||||
label="Invite people…"
|
||||
label={t("Invite people…")}
|
||||
/>
|
||||
)}
|
||||
</Section>
|
||||
</Scrollable>
|
||||
</Flex>
|
||||
<Modal
|
||||
title="Invite people"
|
||||
title={t("Invite people")}
|
||||
onRequestClose={this.handleInviteModalClose}
|
||||
isOpen={this.inviteModalOpen}
|
||||
>
|
||||
<Invite onSubmit={this.handleInviteModalClose} />
|
||||
</Modal>
|
||||
<Modal
|
||||
title="Create a collection"
|
||||
title={t("Create a collection")}
|
||||
onRequestClose={this.handleCreateCollectionModalClose}
|
||||
isOpen={this.createCollectionModalOpen}
|
||||
>
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
import { observer, inject } from "mobx-react";
|
||||
import { PlusIcon } from "outline-icons";
|
||||
import * as React from "react";
|
||||
import { withTranslation } from "react-i18next";
|
||||
import keydown from "react-keydown";
|
||||
import { withRouter, type RouterHistory } from "react-router-dom";
|
||||
|
||||
@@ -26,6 +27,7 @@ type Props = {
|
||||
ui: UiStore,
|
||||
};
|
||||
|
||||
@withTranslation()
|
||||
@observer
|
||||
class Collections extends React.Component<Props> {
|
||||
isPreloaded: boolean = !!this.props.collections.orderedData.length;
|
||||
@@ -52,7 +54,7 @@ class Collections extends React.Component<Props> {
|
||||
}
|
||||
|
||||
render() {
|
||||
const { collections, ui, policies, documents } = this.props;
|
||||
const { collections, ui, policies, documents, t } = this.props;
|
||||
|
||||
const content = (
|
||||
<>
|
||||
@@ -71,7 +73,7 @@ class Collections extends React.Component<Props> {
|
||||
to="/collections"
|
||||
onClick={this.props.onCreateCollection}
|
||||
icon={<PlusIcon color="currentColor" />}
|
||||
label="New collection…"
|
||||
label={t("New collection…")}
|
||||
exact
|
||||
/>
|
||||
</>
|
||||
@@ -79,7 +81,7 @@ class Collections extends React.Component<Props> {
|
||||
|
||||
return (
|
||||
<Flex column>
|
||||
<Header>Collections</Header>
|
||||
<Header>{t("Collections")}</Header>
|
||||
{collections.isLoaded ? (
|
||||
this.isPreloaded ? (
|
||||
content
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
import { observable } from "mobx";
|
||||
import { observer } from "mobx-react";
|
||||
import * as React from "react";
|
||||
import { withTranslation } from "react-i18next";
|
||||
import styled from "styled-components";
|
||||
import DocumentsStore from "stores/DocumentsStore";
|
||||
import Collection from "models/Collection";
|
||||
@@ -25,6 +26,7 @@ type Props = {|
|
||||
depth: number,
|
||||
|};
|
||||
|
||||
@withTranslation()
|
||||
@observer
|
||||
class DocumentLink extends React.Component<Props> {
|
||||
@observable menuOpen = false;
|
||||
@@ -84,6 +86,7 @@ class DocumentLink extends React.Component<Props> {
|
||||
prefetchDocument,
|
||||
depth,
|
||||
canUpdate,
|
||||
t,
|
||||
} = this.props;
|
||||
|
||||
const showChildren = !!(
|
||||
@@ -96,7 +99,7 @@ class DocumentLink extends React.Component<Props> {
|
||||
this.isActiveDocument())
|
||||
);
|
||||
const document = documents.get(node.id);
|
||||
const title = node.title || "Untitled";
|
||||
const title = node.title || t("Untitled");
|
||||
|
||||
return (
|
||||
<Flex
|
||||
|
||||
@@ -6,6 +6,7 @@ import * as React from "react";
|
||||
import { render } from "react-dom";
|
||||
import { BrowserRouter as Router } from "react-router-dom";
|
||||
|
||||
import { outlineTranslation } from "shared/translations/i18n";
|
||||
import stores from "stores";
|
||||
import ErrorBoundary from "components/ErrorBoundary";
|
||||
import ScrollToTop from "components/ScrollToTop";
|
||||
@@ -14,8 +15,6 @@ import Toasts from "components/Toasts";
|
||||
import Routes from "./routes";
|
||||
import env from "env";
|
||||
|
||||
import { outlineTranslation } from "shared/translations/i18n";
|
||||
|
||||
outlineTranslation.init();
|
||||
|
||||
const element = document.getElementById("root");
|
||||
|
||||
@@ -3,6 +3,7 @@ import { observable } from "mobx";
|
||||
import { inject, observer } from "mobx-react";
|
||||
import { SunIcon, MoonIcon } from "outline-icons";
|
||||
import * as React from "react";
|
||||
import { withTranslation } from "react-i18next";
|
||||
import { Link } from "react-router-dom";
|
||||
import styled from "styled-components";
|
||||
import AuthStore from "stores/AuthStore";
|
||||
@@ -25,6 +26,7 @@ type Props = {
|
||||
auth: AuthStore,
|
||||
};
|
||||
|
||||
@withTranslation()
|
||||
@observer
|
||||
class AccountMenu extends React.Component<Props> {
|
||||
@observable keyboardShortcutsOpen: boolean = false;
|
||||
@@ -42,14 +44,14 @@ class AccountMenu extends React.Component<Props> {
|
||||
};
|
||||
|
||||
render() {
|
||||
const { ui } = this.props;
|
||||
const { ui, t } = this.props;
|
||||
|
||||
return (
|
||||
<>
|
||||
<Modal
|
||||
isOpen={this.keyboardShortcutsOpen}
|
||||
onRequestClose={this.handleCloseKeyboardShortcuts}
|
||||
title="Keyboard shortcuts"
|
||||
title={t("Keyboard shortcuts")}
|
||||
>
|
||||
<KeyboardShortcuts />
|
||||
</Modal>
|
||||
@@ -58,23 +60,23 @@ class AccountMenu extends React.Component<Props> {
|
||||
label={this.props.label}
|
||||
>
|
||||
<DropdownMenuItem as={Link} to={settings()}>
|
||||
Settings
|
||||
{t("Settings")}
|
||||
</DropdownMenuItem>
|
||||
<DropdownMenuItem onClick={this.handleOpenKeyboardShortcuts}>
|
||||
Keyboard shortcuts
|
||||
{t("Keyboard shortcuts")}
|
||||
</DropdownMenuItem>
|
||||
<DropdownMenuItem href={developers()} target="_blank">
|
||||
API documentation
|
||||
{t("API documentation")}
|
||||
</DropdownMenuItem>
|
||||
<hr />
|
||||
<DropdownMenuItem href={changelog()} target="_blank">
|
||||
Changelog
|
||||
{t("Changelog")}
|
||||
</DropdownMenuItem>
|
||||
<DropdownMenuItem href={mailToUrl()} target="_blank">
|
||||
Send us feedback
|
||||
{t("Send us feedback")}
|
||||
</DropdownMenuItem>
|
||||
<DropdownMenuItem href={githubIssuesUrl()} target="_blank">
|
||||
Report a bug
|
||||
{t("Report a bug")}
|
||||
</DropdownMenuItem>
|
||||
<hr />
|
||||
<DropdownMenu
|
||||
@@ -87,7 +89,7 @@ class AccountMenu extends React.Component<Props> {
|
||||
label={
|
||||
<DropdownMenuItem>
|
||||
<ChangeTheme justify="space-between">
|
||||
Appearance
|
||||
{t("Appearance")}
|
||||
{ui.resolvedTheme === "light" ? <SunIcon /> : <MoonIcon />}
|
||||
</ChangeTheme>
|
||||
</DropdownMenuItem>
|
||||
@@ -98,24 +100,24 @@ class AccountMenu extends React.Component<Props> {
|
||||
onClick={() => ui.setTheme("system")}
|
||||
selected={ui.theme === "system"}
|
||||
>
|
||||
System
|
||||
{t("System")}
|
||||
</DropdownMenuItem>
|
||||
<DropdownMenuItem
|
||||
onClick={() => ui.setTheme("light")}
|
||||
selected={ui.theme === "light"}
|
||||
>
|
||||
Light
|
||||
{t("Light")}
|
||||
</DropdownMenuItem>
|
||||
<DropdownMenuItem
|
||||
onClick={() => ui.setTheme("dark")}
|
||||
selected={ui.theme === "dark"}
|
||||
>
|
||||
Dark
|
||||
{t("Dark")}
|
||||
</DropdownMenuItem>
|
||||
</DropdownMenu>
|
||||
<hr />
|
||||
<DropdownMenuItem onClick={this.handleLogout}>
|
||||
Log out
|
||||
{t("Log out")}
|
||||
</DropdownMenuItem>
|
||||
</DropdownMenu>
|
||||
</>
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
import { observable } from "mobx";
|
||||
import { inject, observer } from "mobx-react";
|
||||
import * as React from "react";
|
||||
import { withTranslation } from "react-i18next";
|
||||
import { withRouter, type RouterHistory } from "react-router-dom";
|
||||
import DocumentsStore from "stores/DocumentsStore";
|
||||
import PoliciesStore from "stores/PoliciesStore";
|
||||
@@ -28,6 +29,7 @@ type Props = {
|
||||
onClose?: () => void,
|
||||
};
|
||||
|
||||
@withTranslation()
|
||||
@observer
|
||||
class CollectionMenu extends React.Component<Props> {
|
||||
file: ?HTMLInputElement;
|
||||
@@ -111,6 +113,7 @@ class CollectionMenu extends React.Component<Props> {
|
||||
position,
|
||||
onOpen,
|
||||
onClose,
|
||||
t,
|
||||
} = this.props;
|
||||
const can = policies.abilities(collection.id);
|
||||
|
||||
@@ -127,7 +130,7 @@ class CollectionMenu extends React.Component<Props> {
|
||||
</VisuallyHidden>
|
||||
|
||||
<Modal
|
||||
title="Collection permissions"
|
||||
title={t("Collection permissions")}
|
||||
onRequestClose={this.handleMembersModalClose}
|
||||
isOpen={this.showCollectionMembers}
|
||||
>
|
||||
@@ -143,40 +146,40 @@ class CollectionMenu extends React.Component<Props> {
|
||||
<>
|
||||
{can.update && (
|
||||
<DropdownMenuItem onClick={this.onNewDocument}>
|
||||
New document
|
||||
{t("New document")}
|
||||
</DropdownMenuItem>
|
||||
)}
|
||||
{can.update && (
|
||||
<DropdownMenuItem onClick={this.onImportDocument}>
|
||||
Import document
|
||||
{t("Import document")}
|
||||
</DropdownMenuItem>
|
||||
)}
|
||||
{can.update && <hr />}
|
||||
{can.update && (
|
||||
<DropdownMenuItem onClick={this.handleEditCollectionOpen}>
|
||||
Edit…
|
||||
{t("Edit…")}
|
||||
</DropdownMenuItem>
|
||||
)}
|
||||
{can.update && (
|
||||
<DropdownMenuItem onClick={this.handleMembersModalOpen}>
|
||||
Permissions…
|
||||
{t("Permissions…")}
|
||||
</DropdownMenuItem>
|
||||
)}
|
||||
{can.export && (
|
||||
<DropdownMenuItem onClick={this.handleExportCollectionOpen}>
|
||||
Export…
|
||||
{t("Export…")}
|
||||
</DropdownMenuItem>
|
||||
)}
|
||||
</>
|
||||
)}
|
||||
{can.delete && (
|
||||
<DropdownMenuItem onClick={this.handleDeleteCollectionOpen}>
|
||||
Delete…
|
||||
{t("Delete…")}
|
||||
</DropdownMenuItem>
|
||||
)}
|
||||
</DropdownMenu>
|
||||
<Modal
|
||||
title="Edit collection"
|
||||
title={t("Edit collection")}
|
||||
isOpen={this.showCollectionEdit}
|
||||
onRequestClose={this.handleEditCollectionClose}
|
||||
>
|
||||
@@ -186,7 +189,7 @@ class CollectionMenu extends React.Component<Props> {
|
||||
/>
|
||||
</Modal>
|
||||
<Modal
|
||||
title="Delete collection"
|
||||
title={t("Delete collection")}
|
||||
isOpen={this.showCollectionDelete}
|
||||
onRequestClose={this.handleDeleteCollectionClose}
|
||||
>
|
||||
@@ -196,7 +199,7 @@ class CollectionMenu extends React.Component<Props> {
|
||||
/>
|
||||
</Modal>
|
||||
<Modal
|
||||
title="Export collection"
|
||||
title={t("Export collection")}
|
||||
isOpen={this.showCollectionExport}
|
||||
onRequestClose={this.handleExportCollectionClose}
|
||||
>
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
import { observable } from "mobx";
|
||||
import { inject, observer } from "mobx-react";
|
||||
import * as React from "react";
|
||||
import { withTranslation } from "react-i18next";
|
||||
import { Redirect } from "react-router-dom";
|
||||
import AuthStore from "stores/AuthStore";
|
||||
import CollectionStore from "stores/CollectionsStore";
|
||||
@@ -43,6 +44,7 @@ type Props = {
|
||||
onClose?: () => void,
|
||||
};
|
||||
|
||||
@withTranslation()
|
||||
@observer
|
||||
class DocumentMenu extends React.Component<Props> {
|
||||
@observable redirectTo: ?string;
|
||||
@@ -86,7 +88,8 @@ class DocumentMenu extends React.Component<Props> {
|
||||
|
||||
// when duplicating, go straight to the duplicated document content
|
||||
this.redirectTo = duped.url;
|
||||
this.props.ui.showToast("Document duplicated");
|
||||
const { t } = this.props;
|
||||
this.props.ui.showToast(t("Document duplicated"));
|
||||
};
|
||||
|
||||
handleOpenTemplateModal = () => {
|
||||
@@ -103,7 +106,8 @@ class DocumentMenu extends React.Component<Props> {
|
||||
|
||||
handleArchive = async (ev: SyntheticEvent<>) => {
|
||||
await this.props.document.archive();
|
||||
this.props.ui.showToast("Document archived");
|
||||
const { t } = this.props;
|
||||
this.props.ui.showToast(t("Document archived"));
|
||||
};
|
||||
|
||||
handleRestore = async (
|
||||
@@ -111,12 +115,14 @@ class DocumentMenu extends React.Component<Props> {
|
||||
options?: { collectionId: string }
|
||||
) => {
|
||||
await this.props.document.restore(options);
|
||||
this.props.ui.showToast("Document restored");
|
||||
const { t } = this.props;
|
||||
this.props.ui.showToast(t("Document restored"));
|
||||
};
|
||||
|
||||
handleUnpublish = async (ev: SyntheticEvent<>) => {
|
||||
await this.props.document.unpublish();
|
||||
this.props.ui.showToast("Document unpublished");
|
||||
const { t } = this.props;
|
||||
this.props.ui.showToast(t("Document unpublished"));
|
||||
};
|
||||
|
||||
handlePin = (ev: SyntheticEvent<>) => {
|
||||
@@ -167,6 +173,7 @@ class DocumentMenu extends React.Component<Props> {
|
||||
label,
|
||||
onOpen,
|
||||
onClose,
|
||||
t,
|
||||
} = this.props;
|
||||
|
||||
const can = policies.abilities(document.id);
|
||||
@@ -185,17 +192,17 @@ class DocumentMenu extends React.Component<Props> {
|
||||
>
|
||||
{can.unarchive && (
|
||||
<DropdownMenuItem onClick={this.handleRestore}>
|
||||
Restore
|
||||
{t("Restore")}
|
||||
</DropdownMenuItem>
|
||||
)}
|
||||
{can.restore &&
|
||||
(collection ? (
|
||||
<DropdownMenuItem onClick={this.handleRestore}>
|
||||
Restore
|
||||
{t("Restore")}
|
||||
</DropdownMenuItem>
|
||||
) : (
|
||||
<DropdownMenu
|
||||
label={<DropdownMenuItem>Restore…</DropdownMenuItem>}
|
||||
label={<DropdownMenuItem>{t("Restore…")}</DropdownMenuItem>}
|
||||
style={{
|
||||
left: -170,
|
||||
position: "relative",
|
||||
@@ -203,7 +210,7 @@ class DocumentMenu extends React.Component<Props> {
|
||||
}}
|
||||
hover
|
||||
>
|
||||
<Header>Choose a collection</Header>
|
||||
<Header>{t("Choose a collection")}</Header>
|
||||
{collections.orderedData.map((collection) => {
|
||||
const can = policies.abilities(collection.id);
|
||||
|
||||
@@ -226,42 +233,42 @@ class DocumentMenu extends React.Component<Props> {
|
||||
(document.pinned
|
||||
? can.unpin && (
|
||||
<DropdownMenuItem onClick={this.handleUnpin}>
|
||||
Unpin
|
||||
{t("Unpin")}
|
||||
</DropdownMenuItem>
|
||||
)
|
||||
: can.pin && (
|
||||
<DropdownMenuItem onClick={this.handlePin}>
|
||||
Pin to collection
|
||||
{t("Pin to collection")}
|
||||
</DropdownMenuItem>
|
||||
))}
|
||||
{document.isStarred
|
||||
? can.unstar && (
|
||||
<DropdownMenuItem onClick={this.handleUnstar}>
|
||||
Unstar
|
||||
{t("Unstar")}
|
||||
</DropdownMenuItem>
|
||||
)
|
||||
: can.star && (
|
||||
<DropdownMenuItem onClick={this.handleStar}>
|
||||
Star
|
||||
{t("Star")}
|
||||
</DropdownMenuItem>
|
||||
)}
|
||||
{canShareDocuments && (
|
||||
<DropdownMenuItem
|
||||
onClick={this.handleShareLink}
|
||||
title="Create a public share link"
|
||||
title={t("Create a public share link")}
|
||||
>
|
||||
Share link…
|
||||
{t("Share link…")}
|
||||
</DropdownMenuItem>
|
||||
)}
|
||||
{showToggleEmbeds && (
|
||||
<>
|
||||
{document.embedsDisabled ? (
|
||||
<DropdownMenuItem onClick={document.enableEmbeds}>
|
||||
Enable embeds
|
||||
{t("Enable embeds")}
|
||||
</DropdownMenuItem>
|
||||
) : (
|
||||
<DropdownMenuItem onClick={document.disableEmbeds}>
|
||||
Disable embeds
|
||||
{t("Disable embeds")}
|
||||
</DropdownMenuItem>
|
||||
)}
|
||||
</>
|
||||
@@ -271,61 +278,69 @@ class DocumentMenu extends React.Component<Props> {
|
||||
{can.createChildDocument && (
|
||||
<DropdownMenuItem
|
||||
onClick={this.handleNewChild}
|
||||
title="Create a nested document inside the current document"
|
||||
title={t("Create a nested document inside the current document")}
|
||||
>
|
||||
New nested document
|
||||
{t("New nested document")}
|
||||
</DropdownMenuItem>
|
||||
)}
|
||||
{can.update && !document.isTemplate && (
|
||||
<DropdownMenuItem onClick={this.handleOpenTemplateModal}>
|
||||
Create template…
|
||||
{t("Create template…")}
|
||||
</DropdownMenuItem>
|
||||
)}
|
||||
{can.unpublish && (
|
||||
<DropdownMenuItem onClick={this.handleUnpublish}>
|
||||
Unpublish
|
||||
{t("Unpublish")}
|
||||
</DropdownMenuItem>
|
||||
)}
|
||||
{can.update && (
|
||||
<DropdownMenuItem onClick={this.handleEdit}>Edit</DropdownMenuItem>
|
||||
<DropdownMenuItem onClick={this.handleEdit}>
|
||||
{t("Edit")}
|
||||
</DropdownMenuItem>
|
||||
)}
|
||||
{can.update && (
|
||||
<DropdownMenuItem onClick={this.handleDuplicate}>
|
||||
Duplicate
|
||||
{t("Duplicate")}
|
||||
</DropdownMenuItem>
|
||||
)}
|
||||
{can.archive && (
|
||||
<DropdownMenuItem onClick={this.handleArchive}>
|
||||
Archive
|
||||
{t("Archive")}
|
||||
</DropdownMenuItem>
|
||||
)}
|
||||
{can.delete && (
|
||||
<DropdownMenuItem onClick={this.handleDelete}>
|
||||
Delete…
|
||||
{t("Delete…")}
|
||||
</DropdownMenuItem>
|
||||
)}
|
||||
{can.move && (
|
||||
<DropdownMenuItem onClick={this.handleMove}>Move…</DropdownMenuItem>
|
||||
<DropdownMenuItem onClick={this.handleMove}>
|
||||
{t("Move…")}
|
||||
</DropdownMenuItem>
|
||||
)}
|
||||
<hr />
|
||||
{canViewHistory && (
|
||||
<>
|
||||
<DropdownMenuItem onClick={this.handleDocumentHistory}>
|
||||
History
|
||||
{t("History")}
|
||||
</DropdownMenuItem>
|
||||
</>
|
||||
)}
|
||||
{can.download && (
|
||||
<DropdownMenuItem onClick={this.handleExport}>
|
||||
Download
|
||||
{t("Download")}
|
||||
</DropdownMenuItem>
|
||||
)}
|
||||
{showPrint && (
|
||||
<DropdownMenuItem onClick={window.print}>Print</DropdownMenuItem>
|
||||
<DropdownMenuItem onClick={window.print}>
|
||||
{t("Print")}
|
||||
</DropdownMenuItem>
|
||||
)}
|
||||
</DropdownMenu>
|
||||
<Modal
|
||||
title={`Delete ${this.props.document.noun}`}
|
||||
title={t("Delete {{ documentName }}", {
|
||||
documentName: this.props.document.noun,
|
||||
})}
|
||||
onRequestClose={this.handleCloseDeleteModal}
|
||||
isOpen={this.showDeleteModal}
|
||||
>
|
||||
@@ -335,7 +350,7 @@ class DocumentMenu extends React.Component<Props> {
|
||||
/>
|
||||
</Modal>
|
||||
<Modal
|
||||
title="Create template"
|
||||
title={t("Create template")}
|
||||
onRequestClose={this.handleCloseTemplateModal}
|
||||
isOpen={this.showTemplateModal}
|
||||
>
|
||||
@@ -345,7 +360,7 @@ class DocumentMenu extends React.Component<Props> {
|
||||
/>
|
||||
</Modal>
|
||||
<Modal
|
||||
title="Share document"
|
||||
title={t("Share document")}
|
||||
onRequestClose={this.handleCloseShareModal}
|
||||
isOpen={this.showShareModal}
|
||||
>
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
import { observable } from "mobx";
|
||||
import { inject, observer } from "mobx-react";
|
||||
import * as React from "react";
|
||||
import { withTranslation } from "react-i18next";
|
||||
import { withRouter, type RouterHistory } from "react-router-dom";
|
||||
import PoliciesStore from "stores/PoliciesStore";
|
||||
import UiStore from "stores/UiStore";
|
||||
@@ -22,6 +23,7 @@ type Props = {
|
||||
onClose?: () => void,
|
||||
};
|
||||
|
||||
@withTranslation()
|
||||
@observer
|
||||
class GroupMenu extends React.Component<Props> {
|
||||
@observable editModalOpen: boolean = false;
|
||||
@@ -46,13 +48,13 @@ class GroupMenu extends React.Component<Props> {
|
||||
};
|
||||
|
||||
render() {
|
||||
const { policies, group, onOpen, onClose } = this.props;
|
||||
const { policies, group, onOpen, onClose, t } = this.props;
|
||||
const can = policies.abilities(group.id);
|
||||
|
||||
return (
|
||||
<>
|
||||
<Modal
|
||||
title="Edit group"
|
||||
title={t("Edit group")}
|
||||
onRequestClose={this.handleEditModalClose}
|
||||
isOpen={this.editModalOpen}
|
||||
>
|
||||
@@ -63,7 +65,7 @@ class GroupMenu extends React.Component<Props> {
|
||||
</Modal>
|
||||
|
||||
<Modal
|
||||
title="Delete group"
|
||||
title={t("Delete group")}
|
||||
onRequestClose={this.handleDeleteModalClose}
|
||||
isOpen={this.deleteModalOpen}
|
||||
>
|
||||
@@ -77,18 +79,20 @@ class GroupMenu extends React.Component<Props> {
|
||||
{group && (
|
||||
<>
|
||||
<DropdownMenuItem onClick={this.props.onMembers}>
|
||||
Members…
|
||||
{t("Members…")}
|
||||
</DropdownMenuItem>
|
||||
|
||||
{(can.update || can.delete) && <hr />}
|
||||
|
||||
{can.update && (
|
||||
<DropdownMenuItem onClick={this.onEdit}>Edit…</DropdownMenuItem>
|
||||
<DropdownMenuItem onClick={this.onEdit}>
|
||||
{t("Edit…")}
|
||||
</DropdownMenuItem>
|
||||
)}
|
||||
|
||||
{can.delete && (
|
||||
<DropdownMenuItem onClick={this.onDelete}>
|
||||
Delete…
|
||||
{t("Delete…")}
|
||||
</DropdownMenuItem>
|
||||
)}
|
||||
</>
|
||||
|
||||
@@ -3,6 +3,7 @@ import { observable } from "mobx";
|
||||
import { observer, inject } from "mobx-react";
|
||||
import { MoreIcon } from "outline-icons";
|
||||
import * as React from "react";
|
||||
import { withTranslation } from "react-i18next";
|
||||
import { Redirect } from "react-router-dom";
|
||||
|
||||
import CollectionsStore from "stores/CollectionsStore";
|
||||
@@ -16,6 +17,7 @@ type Props = {
|
||||
collections: CollectionsStore,
|
||||
};
|
||||
|
||||
@withTranslation()
|
||||
@observer
|
||||
class NewChildDocumentMenu extends React.Component<Props> {
|
||||
@observable redirectTo: ?string;
|
||||
@@ -39,19 +41,19 @@ class NewChildDocumentMenu extends React.Component<Props> {
|
||||
render() {
|
||||
if (this.redirectTo) return <Redirect to={this.redirectTo} push />;
|
||||
|
||||
const { label, document, collections, ...rest } = this.props;
|
||||
const { label, document, collections, t, ...rest } = this.props;
|
||||
const collection = collections.get(document.collectionId);
|
||||
|
||||
return (
|
||||
<DropdownMenu label={label || <MoreIcon />} {...rest}>
|
||||
<DropdownMenuItem onClick={this.handleNewDocument}>
|
||||
<span>
|
||||
New document in{" "}
|
||||
<strong>{collection ? collection.name : "collection"}</strong>
|
||||
{t("New document in")}{" "}
|
||||
<strong>{collection ? collection.name : t("collection")}</strong>
|
||||
</span>
|
||||
</DropdownMenuItem>
|
||||
<DropdownMenuItem onClick={this.handleNewChild}>
|
||||
New nested document
|
||||
{t("New nested document")}
|
||||
</DropdownMenuItem>
|
||||
</DropdownMenu>
|
||||
);
|
||||
|
||||
@@ -3,6 +3,7 @@ import { observable } from "mobx";
|
||||
import { inject, observer } from "mobx-react";
|
||||
import { PlusIcon } from "outline-icons";
|
||||
import * as React from "react";
|
||||
import { withTranslation } from "react-i18next";
|
||||
import { Redirect } from "react-router-dom";
|
||||
|
||||
import CollectionsStore from "stores/CollectionsStore";
|
||||
@@ -24,6 +25,7 @@ type Props = {
|
||||
policies: PoliciesStore,
|
||||
};
|
||||
|
||||
@withTranslation()
|
||||
@observer
|
||||
class NewDocumentMenu extends React.Component<Props> {
|
||||
@observable redirectTo: ?string;
|
||||
@@ -47,7 +49,7 @@ class NewDocumentMenu extends React.Component<Props> {
|
||||
render() {
|
||||
if (this.redirectTo) return <Redirect to={this.redirectTo} push />;
|
||||
|
||||
const { collections, documents, policies, label, ...rest } = this.props;
|
||||
const { collections, documents, policies, label, t, ...rest } = this.props;
|
||||
const singleCollection = collections.orderedData.length === 1;
|
||||
|
||||
return (
|
||||
@@ -55,14 +57,15 @@ class NewDocumentMenu extends React.Component<Props> {
|
||||
label={
|
||||
label || (
|
||||
<Button icon={<PlusIcon />} small>
|
||||
New doc{singleCollection ? "" : "…"}
|
||||
{t("New doc")}
|
||||
{singleCollection ? "" : "…"}
|
||||
</Button>
|
||||
)
|
||||
}
|
||||
onOpen={this.onOpen}
|
||||
{...rest}
|
||||
>
|
||||
<Header>Choose a collection</Header>
|
||||
<Header>{t("Choose a collection")}</Header>
|
||||
{collections.orderedData.map((collection) => {
|
||||
const can = policies.abilities(collection.id);
|
||||
|
||||
|
||||
@@ -3,6 +3,7 @@ import { observable } from "mobx";
|
||||
import { inject, observer } from "mobx-react";
|
||||
import { PlusIcon } from "outline-icons";
|
||||
import * as React from "react";
|
||||
import { withTranslation } from "react-i18next";
|
||||
import { Redirect } from "react-router-dom";
|
||||
|
||||
import CollectionsStore from "stores/CollectionsStore";
|
||||
@@ -22,6 +23,7 @@ type Props = {
|
||||
policies: PoliciesStore,
|
||||
};
|
||||
|
||||
@withTranslation()
|
||||
@observer
|
||||
class NewTemplateMenu extends React.Component<Props> {
|
||||
@observable redirectTo: ?string;
|
||||
@@ -39,20 +41,20 @@ class NewTemplateMenu extends React.Component<Props> {
|
||||
render() {
|
||||
if (this.redirectTo) return <Redirect to={this.redirectTo} push />;
|
||||
|
||||
const { collections, policies, label, ...rest } = this.props;
|
||||
const { collections, policies, label, t, ...rest } = this.props;
|
||||
|
||||
return (
|
||||
<DropdownMenu
|
||||
label={
|
||||
label || (
|
||||
<Button icon={<PlusIcon />} small>
|
||||
New template…
|
||||
{t("New template…")}
|
||||
</Button>
|
||||
)
|
||||
}
|
||||
{...rest}
|
||||
>
|
||||
<Header>Choose a collection</Header>
|
||||
<Header>{t("Choose a collection")}</Header>
|
||||
{collections.orderedData.map((collection) => {
|
||||
const can = policies.abilities(collection.id);
|
||||
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
// @flow
|
||||
import { inject } from "mobx-react";
|
||||
import * as React from "react";
|
||||
import { withTranslation } from "react-i18next";
|
||||
import { withRouter, type RouterHistory } from "react-router-dom";
|
||||
|
||||
import UiStore from "stores/UiStore";
|
||||
@@ -21,20 +22,23 @@ type Props = {
|
||||
ui: UiStore,
|
||||
};
|
||||
|
||||
@withTranslation()
|
||||
class RevisionMenu extends React.Component<Props> {
|
||||
handleRestore = async (ev: SyntheticEvent<>) => {
|
||||
ev.preventDefault();
|
||||
await this.props.document.restore({ revisionId: this.props.revision.id });
|
||||
this.props.ui.showToast("Document restored");
|
||||
const { t } = this.props;
|
||||
this.props.ui.showToast(t("Document restored"));
|
||||
this.props.history.push(this.props.document.url);
|
||||
};
|
||||
|
||||
handleCopy = () => {
|
||||
this.props.ui.showToast("Link copied");
|
||||
const { t } = this.props;
|
||||
this.props.ui.showToast(t("Link copied"));
|
||||
};
|
||||
|
||||
render() {
|
||||
const { className, label, onOpen, onClose } = this.props;
|
||||
const { className, label, onOpen, onClose, t } = this.props;
|
||||
const url = `${window.location.origin}${documentHistoryUrl(
|
||||
this.props.document,
|
||||
this.props.revision.id
|
||||
@@ -48,11 +52,11 @@ class RevisionMenu extends React.Component<Props> {
|
||||
label={label}
|
||||
>
|
||||
<DropdownMenuItem onClick={this.handleRestore}>
|
||||
Restore version
|
||||
{t("Restore version")}
|
||||
</DropdownMenuItem>
|
||||
<hr />
|
||||
<CopyToClipboard text={url} onCopy={this.handleCopy}>
|
||||
<DropdownMenuItem>Copy link</DropdownMenuItem>
|
||||
<DropdownMenuItem>{t("Copy link")}</DropdownMenuItem>
|
||||
</CopyToClipboard>
|
||||
</DropdownMenu>
|
||||
);
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
import { observable } from "mobx";
|
||||
import { inject, observer } from "mobx-react";
|
||||
import * as React from "react";
|
||||
import { withTranslation } from "react-i18next";
|
||||
import { Redirect } from "react-router-dom";
|
||||
|
||||
import SharesStore from "stores/SharesStore";
|
||||
@@ -18,6 +19,7 @@ type Props = {
|
||||
share: Share,
|
||||
};
|
||||
|
||||
@withTranslation()
|
||||
@observer
|
||||
class ShareMenu extends React.Component<Props> {
|
||||
@observable redirectTo: ?string;
|
||||
@@ -36,32 +38,34 @@ class ShareMenu extends React.Component<Props> {
|
||||
|
||||
try {
|
||||
await this.props.shares.revoke(this.props.share);
|
||||
this.props.ui.showToast("Share link revoked");
|
||||
const { t } = this.props;
|
||||
this.props.ui.showToast(t("Share link revoked"));
|
||||
} catch (err) {
|
||||
this.props.ui.showToast(err.message);
|
||||
}
|
||||
};
|
||||
|
||||
handleCopy = () => {
|
||||
this.props.ui.showToast("Share link copied");
|
||||
const { t } = this.props;
|
||||
this.props.ui.showToast(t("Share link copied"));
|
||||
};
|
||||
|
||||
render() {
|
||||
if (this.redirectTo) return <Redirect to={this.redirectTo} push />;
|
||||
|
||||
const { share, onOpen, onClose } = this.props;
|
||||
const { share, onOpen, onClose, t } = this.props;
|
||||
|
||||
return (
|
||||
<DropdownMenu onOpen={onOpen} onClose={onClose}>
|
||||
<CopyToClipboard text={share.url} onCopy={this.handleCopy}>
|
||||
<DropdownMenuItem>Copy link</DropdownMenuItem>
|
||||
<DropdownMenuItem>{t("Copy link")}</DropdownMenuItem>
|
||||
</CopyToClipboard>
|
||||
<DropdownMenuItem onClick={this.handleGoToDocument}>
|
||||
Go to document
|
||||
{t("Go to document")}
|
||||
</DropdownMenuItem>
|
||||
<hr />
|
||||
<DropdownMenuItem onClick={this.handleRevoke}>
|
||||
Revoke link
|
||||
{t("Revoke link")}
|
||||
</DropdownMenuItem>
|
||||
</DropdownMenu>
|
||||
);
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
import { observer, inject } from "mobx-react";
|
||||
import { DocumentIcon } from "outline-icons";
|
||||
import * as React from "react";
|
||||
import { withTranslation } from "react-i18next";
|
||||
import styled from "styled-components";
|
||||
import DocumentsStore from "stores/DocumentsStore";
|
||||
import Document from "models/Document";
|
||||
@@ -13,10 +14,11 @@ type Props = {
|
||||
documents: DocumentsStore,
|
||||
};
|
||||
|
||||
@withTranslation()
|
||||
@observer
|
||||
class TemplatesMenu extends React.Component<Props> {
|
||||
render() {
|
||||
const { documents, document, ...rest } = this.props;
|
||||
const { documents, document, t, ...rest } = this.props;
|
||||
const templates = documents.templatesInCollection(document.collectionId);
|
||||
|
||||
if (!templates.length) {
|
||||
@@ -28,7 +30,7 @@ class TemplatesMenu extends React.Component<Props> {
|
||||
position="left"
|
||||
label={
|
||||
<Button disclosure neutral>
|
||||
Templates
|
||||
{t("Templates")}
|
||||
</Button>
|
||||
}
|
||||
{...rest}
|
||||
@@ -42,7 +44,10 @@ class TemplatesMenu extends React.Component<Props> {
|
||||
<div>
|
||||
<strong>{template.titleWithDefault}</strong>
|
||||
<br />
|
||||
<Author>By {template.createdBy.name}</Author>
|
||||
<Author>
|
||||
{" "}
|
||||
{t("By {{ author }}", { author: template.createdBy.name })}
|
||||
</Author>
|
||||
</div>
|
||||
</DropdownMenuItem>
|
||||
))}
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
import { inject, observer } from "mobx-react";
|
||||
import * as React from "react";
|
||||
|
||||
import { withTranslation } from "react-i18next";
|
||||
import UsersStore from "stores/UsersStore";
|
||||
import User from "models/User";
|
||||
import { DropdownMenu, DropdownMenuItem } from "components/DropdownMenu";
|
||||
@@ -11,14 +12,18 @@ type Props = {
|
||||
users: UsersStore,
|
||||
};
|
||||
|
||||
@withTranslation()
|
||||
@observer
|
||||
class UserMenu extends React.Component<Props> {
|
||||
handlePromote = (ev: SyntheticEvent<>) => {
|
||||
ev.preventDefault();
|
||||
const { user, users } = this.props;
|
||||
const { user, users, t } = this.props;
|
||||
if (
|
||||
!window.confirm(
|
||||
`Are you want to make ${user.name} an admin? Admins can modify team and billing information.`
|
||||
t(
|
||||
"Are you want to make {{ userName }} an admin? Admins can modify team and billing information.",
|
||||
{ userName: user.name }
|
||||
)
|
||||
)
|
||||
) {
|
||||
return;
|
||||
@@ -28,8 +33,14 @@ class UserMenu extends React.Component<Props> {
|
||||
|
||||
handleDemote = (ev: SyntheticEvent<>) => {
|
||||
ev.preventDefault();
|
||||
const { user, users } = this.props;
|
||||
if (!window.confirm(`Are you want to make ${user.name} a member?`)) {
|
||||
const { user, users, t } = this.props;
|
||||
if (
|
||||
!window.confirm(
|
||||
t("Are you want to make {{ userName }} a member?", {
|
||||
userName: user.name,
|
||||
})
|
||||
)
|
||||
) {
|
||||
return;
|
||||
}
|
||||
users.demote(user);
|
||||
@@ -37,10 +48,12 @@ class UserMenu extends React.Component<Props> {
|
||||
|
||||
handleSuspend = (ev: SyntheticEvent<>) => {
|
||||
ev.preventDefault();
|
||||
const { user, users } = this.props;
|
||||
const { user, users, t } = this.props;
|
||||
if (
|
||||
!window.confirm(
|
||||
"Are you want to suspend this account? Suspended users will be prevented from logging in."
|
||||
t(
|
||||
"Are you want to suspend this account? Suspended users will be prevented from logging in."
|
||||
)
|
||||
)
|
||||
) {
|
||||
return;
|
||||
@@ -61,33 +74,33 @@ class UserMenu extends React.Component<Props> {
|
||||
};
|
||||
|
||||
render() {
|
||||
const { user } = this.props;
|
||||
const { user, t } = this.props;
|
||||
|
||||
return (
|
||||
<DropdownMenu>
|
||||
{user.isAdmin && (
|
||||
<DropdownMenuItem onClick={this.handleDemote}>
|
||||
Make {user.name} a member…
|
||||
{t("Make {{ userName }} a member…", { userName: user.name })}
|
||||
</DropdownMenuItem>
|
||||
)}
|
||||
{!user.isAdmin && !user.isSuspended && (
|
||||
<DropdownMenuItem onClick={this.handlePromote}>
|
||||
Make {user.name} an admin…
|
||||
{t("Make {{ userName }} an admin…", { userName: user.name })}
|
||||
</DropdownMenuItem>
|
||||
)}
|
||||
{!user.lastActiveAt && (
|
||||
<DropdownMenuItem onClick={this.handleRevoke}>
|
||||
Revoke invite…
|
||||
{t("Revoke invite…")}
|
||||
</DropdownMenuItem>
|
||||
)}
|
||||
{user.lastActiveAt &&
|
||||
(user.isSuspended ? (
|
||||
<DropdownMenuItem onClick={this.handleActivate}>
|
||||
Activate account
|
||||
{t("Activate account")}
|
||||
</DropdownMenuItem>
|
||||
) : (
|
||||
<DropdownMenuItem onClick={this.handleSuspend}>
|
||||
Suspend account…
|
||||
{t("Suspend account…")}
|
||||
</DropdownMenuItem>
|
||||
))}
|
||||
</DropdownMenu>
|
||||
|
||||
@@ -4,6 +4,7 @@ import { observer, inject } from "mobx-react";
|
||||
|
||||
import { NewDocumentIcon, PlusIcon, PinIcon } from "outline-icons";
|
||||
import * as React from "react";
|
||||
import { withTranslation } from "react-i18next";
|
||||
import { Redirect, Link, Switch, Route, type Match } from "react-router-dom";
|
||||
import styled, { withTheme } from "styled-components";
|
||||
|
||||
@@ -49,6 +50,7 @@ type Props = {
|
||||
theme: Theme,
|
||||
};
|
||||
|
||||
@withTranslation()
|
||||
@observer
|
||||
class CollectionScene extends React.Component<Props> {
|
||||
@observable collection: ?Collection;
|
||||
@@ -132,7 +134,7 @@ class CollectionScene extends React.Component<Props> {
|
||||
};
|
||||
|
||||
renderActions() {
|
||||
const { match, policies } = this.props;
|
||||
const { match, policies, t } = this.props;
|
||||
const can = policies.abilities(match.params.id || "");
|
||||
|
||||
return (
|
||||
@@ -142,19 +144,19 @@ class CollectionScene extends React.Component<Props> {
|
||||
<Action>
|
||||
<InputSearch
|
||||
source="collection"
|
||||
placeholder="Search in collection…"
|
||||
placeholder={t("Search in collection…")}
|
||||
collectionId={match.params.id}
|
||||
/>
|
||||
</Action>
|
||||
<Action>
|
||||
<Tooltip
|
||||
tooltip="New document"
|
||||
tooltip={t("New document")}
|
||||
shortcut="n"
|
||||
delay={500}
|
||||
placement="bottom"
|
||||
>
|
||||
<Button onClick={this.onNewDocument} icon={<PlusIcon />}>
|
||||
New doc
|
||||
{t("New doc")}
|
||||
</Button>
|
||||
</Tooltip>
|
||||
</Action>
|
||||
@@ -169,7 +171,7 @@ class CollectionScene extends React.Component<Props> {
|
||||
}
|
||||
|
||||
render() {
|
||||
const { documents, theme } = this.props;
|
||||
const { documents, theme, t } = this.props;
|
||||
|
||||
if (this.redirectTo) return <Redirect to={this.redirectTo} push />;
|
||||
if (!this.isFetching && !this.collection) return <Search notFound />;
|
||||
@@ -188,26 +190,31 @@ class CollectionScene extends React.Component<Props> {
|
||||
{collection.isEmpty ? (
|
||||
<Centered column>
|
||||
<HelpText>
|
||||
<strong>{collection.name}</strong> doesn’t contain any
|
||||
documents yet.
|
||||
<br />
|
||||
Get started by creating a new one!
|
||||
{t(
|
||||
`
|
||||
<strong>{{ collectionName }}</strong> doesn’t contain any
|
||||
documents yet.
|
||||
<br />
|
||||
Get started by creating a new one!
|
||||
`,
|
||||
{ collectionName: collection.name }
|
||||
)}
|
||||
</HelpText>
|
||||
<Wrapper>
|
||||
<Link to={newDocumentUrl(collection.id)}>
|
||||
<Button icon={<NewDocumentIcon color={theme.buttonText} />}>
|
||||
Create a document
|
||||
{t("Create a document")}
|
||||
</Button>
|
||||
</Link>
|
||||
|
||||
{collection.private && (
|
||||
<Button onClick={this.onPermissions} neutral>
|
||||
Manage members…
|
||||
{t("Manage members…")}
|
||||
</Button>
|
||||
)}
|
||||
</Wrapper>
|
||||
<Modal
|
||||
title="Collection permissions"
|
||||
title={t("Collection permissions")}
|
||||
onRequestClose={this.handlePermissionsModalClose}
|
||||
isOpen={this.permissionsModalOpen}
|
||||
>
|
||||
@@ -218,7 +225,7 @@ class CollectionScene extends React.Component<Props> {
|
||||
/>
|
||||
</Modal>
|
||||
<Modal
|
||||
title="Edit collection"
|
||||
title={t("Edit collection")}
|
||||
onRequestClose={this.handleEditModalClose}
|
||||
isOpen={this.editModalOpen}
|
||||
>
|
||||
@@ -249,7 +256,7 @@ class CollectionScene extends React.Component<Props> {
|
||||
{hasPinnedDocuments && (
|
||||
<>
|
||||
<Subheading>
|
||||
<TinyPinIcon size={18} /> Pinned
|
||||
<TinyPinIcon size={18} /> {t("Pinned")}
|
||||
</Subheading>
|
||||
<DocumentList documents={pinnedDocuments} showPin />
|
||||
</>
|
||||
@@ -257,16 +264,16 @@ class CollectionScene extends React.Component<Props> {
|
||||
|
||||
<Tabs>
|
||||
<Tab to={collectionUrl(collection.id)} exact>
|
||||
Recently updated
|
||||
{t("Recently updated")}
|
||||
</Tab>
|
||||
<Tab to={collectionUrl(collection.id, "recent")} exact>
|
||||
Recently published
|
||||
{t("Recently published")}
|
||||
</Tab>
|
||||
<Tab to={collectionUrl(collection.id, "old")} exact>
|
||||
Least recently updated
|
||||
{t("Least recently updated")}
|
||||
</Tab>
|
||||
<Tab to={collectionUrl(collection.id, "alphabetical")} exact>
|
||||
A–Z
|
||||
{t("A–Z")}
|
||||
</Tab>
|
||||
</Tabs>
|
||||
<Switch>
|
||||
|
||||
@@ -3,6 +3,7 @@ import { debounce } from "lodash";
|
||||
import { observable } from "mobx";
|
||||
import { inject, observer } from "mobx-react";
|
||||
import * as React from "react";
|
||||
import { withTranslation } from "react-i18next";
|
||||
import styled from "styled-components";
|
||||
import AuthStore from "stores/AuthStore";
|
||||
import CollectionGroupMembershipsStore from "stores/CollectionGroupMembershipsStore";
|
||||
@@ -28,6 +29,7 @@ type Props = {
|
||||
onSubmit: () => void,
|
||||
};
|
||||
|
||||
@withTranslation()
|
||||
@observer
|
||||
class AddGroupsToCollection extends React.Component<Props> {
|
||||
@observable newGroupModalOpen: boolean = false;
|
||||
@@ -53,49 +55,54 @@ class AddGroupsToCollection extends React.Component<Props> {
|
||||
}, 250);
|
||||
|
||||
handleAddGroup = (group) => {
|
||||
const { t } = this.props;
|
||||
try {
|
||||
this.props.collectionGroupMemberships.create({
|
||||
collectionId: this.props.collection.id,
|
||||
groupId: group.id,
|
||||
permission: "read_write",
|
||||
});
|
||||
this.props.ui.showToast(`${group.name} was added to the collection`);
|
||||
this.props.ui.showToast(
|
||||
t("{{ groupName }} was added to the collection", {
|
||||
groupName: group.name,
|
||||
})
|
||||
);
|
||||
} catch (err) {
|
||||
this.props.ui.showToast("Could not add user");
|
||||
this.props.ui.showToast(t("Could not add user"));
|
||||
console.error(err);
|
||||
}
|
||||
};
|
||||
|
||||
render() {
|
||||
const { groups, collection, auth } = this.props;
|
||||
const { groups, collection, auth, t } = this.props;
|
||||
const { user, team } = auth;
|
||||
if (!user || !team) return null;
|
||||
|
||||
return (
|
||||
<Flex column>
|
||||
<HelpText>
|
||||
Can’t find the group you’re looking for?{" "}
|
||||
{t("Can’t find the group you’re looking for?")}{" "}
|
||||
<a role="button" onClick={this.handleNewGroupModalOpen}>
|
||||
Create a group
|
||||
{t("Create a group")}
|
||||
</a>
|
||||
.
|
||||
</HelpText>
|
||||
|
||||
<Input
|
||||
type="search"
|
||||
placeholder="Search by group name…"
|
||||
placeholder={t("Search by group name…")}
|
||||
value={this.query}
|
||||
onChange={this.handleFilter}
|
||||
label="Search groups"
|
||||
label={t("Search groups")}
|
||||
labelHidden
|
||||
flex
|
||||
/>
|
||||
<PaginatedList
|
||||
empty={
|
||||
this.query ? (
|
||||
<Empty>No groups matching your search</Empty>
|
||||
<Empty>{t("No groups matching your search")}</Empty>
|
||||
) : (
|
||||
<Empty>No groups left to add</Empty>
|
||||
<Empty>{t("No groups left to add")}</Empty>
|
||||
)
|
||||
}
|
||||
items={groups.notInCollection(collection.id, this.query)}
|
||||
@@ -108,7 +115,7 @@ class AddGroupsToCollection extends React.Component<Props> {
|
||||
renderActions={() => (
|
||||
<ButtonWrap>
|
||||
<Button onClick={() => this.handleAddGroup(item)} neutral>
|
||||
Add
|
||||
{t("Add")}
|
||||
</Button>
|
||||
</ButtonWrap>
|
||||
)}
|
||||
@@ -116,7 +123,7 @@ class AddGroupsToCollection extends React.Component<Props> {
|
||||
)}
|
||||
/>
|
||||
<Modal
|
||||
title="Create a group"
|
||||
title={t("Create a group")}
|
||||
onRequestClose={this.handleNewGroupModalClose}
|
||||
isOpen={this.newGroupModalOpen}
|
||||
>
|
||||
|
||||
@@ -3,6 +3,7 @@ import { debounce } from "lodash";
|
||||
import { observable } from "mobx";
|
||||
import { inject, observer } from "mobx-react";
|
||||
import * as React from "react";
|
||||
import { withTranslation } from "react-i18next";
|
||||
import AuthStore from "stores/AuthStore";
|
||||
import MembershipsStore from "stores/MembershipsStore";
|
||||
import UiStore from "stores/UiStore";
|
||||
@@ -26,6 +27,7 @@ type Props = {
|
||||
onSubmit: () => void,
|
||||
};
|
||||
|
||||
@withTranslation()
|
||||
@observer
|
||||
class AddPeopleToCollection extends React.Component<Props> {
|
||||
@observable inviteModalOpen: boolean = false;
|
||||
@@ -51,39 +53,42 @@ class AddPeopleToCollection extends React.Component<Props> {
|
||||
}, 250);
|
||||
|
||||
handleAddUser = (user) => {
|
||||
const { t } = this.props;
|
||||
try {
|
||||
this.props.memberships.create({
|
||||
collectionId: this.props.collection.id,
|
||||
userId: user.id,
|
||||
permission: "read_write",
|
||||
});
|
||||
this.props.ui.showToast(`${user.name} was added to the collection`);
|
||||
this.props.ui.showToast(
|
||||
t("{{ userName }} was added to the collection", { userName: user.name })
|
||||
);
|
||||
} catch (err) {
|
||||
this.props.ui.showToast("Could not add user");
|
||||
this.props.ui.showToast(t("Could not add user"));
|
||||
}
|
||||
};
|
||||
|
||||
render() {
|
||||
const { users, collection, auth } = this.props;
|
||||
const { users, collection, auth, t } = this.props;
|
||||
const { user, team } = auth;
|
||||
if (!user || !team) return null;
|
||||
|
||||
return (
|
||||
<Flex column>
|
||||
<HelpText>
|
||||
Need to add someone who’s not yet on the team yet?{" "}
|
||||
{t("Need to add someone who’s not yet on the team yet?")}{" "}
|
||||
<a role="button" onClick={this.handleInviteModalOpen}>
|
||||
Invite people to {team.name}
|
||||
{t("Invite people to {{ teamName }}", { teamName: team.name })}
|
||||
</a>
|
||||
.
|
||||
</HelpText>
|
||||
|
||||
<Input
|
||||
type="search"
|
||||
placeholder="Search by name…"
|
||||
placeholder={t("Search by name…")}
|
||||
value={this.query}
|
||||
onChange={this.handleFilter}
|
||||
label="Search people"
|
||||
label={t("Search people")}
|
||||
autoFocus
|
||||
labelHidden
|
||||
flex
|
||||
@@ -91,9 +96,9 @@ class AddPeopleToCollection extends React.Component<Props> {
|
||||
<PaginatedList
|
||||
empty={
|
||||
this.query ? (
|
||||
<Empty>No people matching your search</Empty>
|
||||
<Empty>{t("No people matching your search")}</Empty>
|
||||
) : (
|
||||
<Empty>No people left to add</Empty>
|
||||
<Empty>{t("No people left to add")}</Empty>
|
||||
)
|
||||
}
|
||||
items={users.notInCollection(collection.id, this.query)}
|
||||
@@ -108,7 +113,7 @@ class AddPeopleToCollection extends React.Component<Props> {
|
||||
)}
|
||||
/>
|
||||
<Modal
|
||||
title="Invite people"
|
||||
title={t("Invite people")}
|
||||
onRequestClose={this.handleInviteModalClose}
|
||||
isOpen={this.inviteModalOpen}
|
||||
>
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
// @flow
|
||||
import i18n from "i18next";
|
||||
import * as React from "react";
|
||||
import styled from "styled-components";
|
||||
import CollectionGroupMembership from "models/CollectionGroupMembership";
|
||||
@@ -7,9 +8,11 @@ import { DropdownMenu, DropdownMenuItem } from "components/DropdownMenu";
|
||||
import GroupListItem from "components/GroupListItem";
|
||||
import InputSelect from "components/InputSelect";
|
||||
|
||||
const t = (k) => i18n.t(k);
|
||||
|
||||
const PERMISSIONS = [
|
||||
{ label: "Read only", value: "read" },
|
||||
{ label: "Read & Edit", value: "read_write" },
|
||||
{ label: t("Read only"), value: "read" },
|
||||
{ label: t("Read & Edit"), value: "read_write" },
|
||||
];
|
||||
type Props = {
|
||||
group: Group,
|
||||
@@ -32,7 +35,7 @@ const MemberListItem = ({
|
||||
renderActions={({ openMembersModal }) => (
|
||||
<>
|
||||
<Select
|
||||
label="Permissions"
|
||||
label={t("Permissions")}
|
||||
options={PERMISSIONS}
|
||||
value={
|
||||
collectionGroupMembership
|
||||
@@ -45,10 +48,12 @@ const MemberListItem = ({
|
||||
<ButtonWrap>
|
||||
<DropdownMenu>
|
||||
<DropdownMenuItem onClick={openMembersModal}>
|
||||
Members…
|
||||
{t("Members…")}
|
||||
</DropdownMenuItem>
|
||||
<hr />
|
||||
<DropdownMenuItem onClick={onRemove}>Remove</DropdownMenuItem>
|
||||
<DropdownMenuItem onClick={onRemove}>
|
||||
{t("Remove")}
|
||||
</DropdownMenuItem>
|
||||
</DropdownMenu>
|
||||
</ButtonWrap>
|
||||
</>
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
// @flow
|
||||
import i18n from "i18next";
|
||||
import * as React from "react";
|
||||
import styled from "styled-components";
|
||||
import Membership from "models/Membership";
|
||||
@@ -12,9 +13,11 @@ import InputSelect from "components/InputSelect";
|
||||
import ListItem from "components/List/Item";
|
||||
import Time from "components/Time";
|
||||
|
||||
const t = (k) => i18n.t(k);
|
||||
|
||||
const PERMISSIONS = [
|
||||
{ label: "Read only", value: "read" },
|
||||
{ label: "Read & Edit", value: "read_write" },
|
||||
{ label: t("Read only"), value: "read" },
|
||||
{ label: t("Read & Edit"), value: "read_write" },
|
||||
];
|
||||
type Props = {
|
||||
user: User,
|
||||
@@ -40,13 +43,15 @@ const MemberListItem = ({
|
||||
<>
|
||||
{user.lastActiveAt ? (
|
||||
<>
|
||||
Active <Time dateTime={user.lastActiveAt} /> ago
|
||||
{t("Active {{ lastActiveAt }} ago", {
|
||||
lastActiveAt: <Time dateTime={user.lastActiveAt} />,
|
||||
})}
|
||||
</>
|
||||
) : (
|
||||
"Never signed in"
|
||||
t("Never signed in")
|
||||
)}
|
||||
{!user.lastActiveAt && <Badge>Invited</Badge>}
|
||||
{user.isAdmin && <Badge primary={user.isAdmin}>Admin</Badge>}
|
||||
{!user.lastActiveAt && <Badge>{t("Invited")}</Badge>}
|
||||
{user.isAdmin && <Badge primary={user.isAdmin}>{t("Admin")}</Badge>}
|
||||
</>
|
||||
}
|
||||
image={<Avatar src={user.avatarUrl} size={40} />}
|
||||
@@ -54,7 +59,7 @@ const MemberListItem = ({
|
||||
<Flex align="center">
|
||||
{canEdit && onUpdate && (
|
||||
<Select
|
||||
label="Permissions"
|
||||
label={t("Permissions")}
|
||||
options={PERMISSIONS}
|
||||
value={membership ? membership.permission : undefined}
|
||||
onChange={(ev) => onUpdate(ev.target.value)}
|
||||
@@ -64,12 +69,14 @@ const MemberListItem = ({
|
||||
|
||||
{canEdit && onRemove && (
|
||||
<DropdownMenu>
|
||||
<DropdownMenuItem onClick={onRemove}>Remove</DropdownMenuItem>
|
||||
<DropdownMenuItem onClick={onRemove}>
|
||||
{t("Remove")}
|
||||
</DropdownMenuItem>
|
||||
</DropdownMenu>
|
||||
)}
|
||||
{canEdit && onAdd && (
|
||||
<Button onClick={onAdd} neutral>
|
||||
Add
|
||||
{t("Add")}
|
||||
</Button>
|
||||
)}
|
||||
</Flex>
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
// @flow
|
||||
import i18n from "i18next";
|
||||
import { PlusIcon } from "outline-icons";
|
||||
import * as React from "react";
|
||||
import User from "models/User";
|
||||
@@ -8,6 +9,8 @@ import Button from "components/Button";
|
||||
import ListItem from "components/List/Item";
|
||||
import Time from "components/Time";
|
||||
|
||||
const t = (k) => i18n.t(k);
|
||||
|
||||
type Props = {
|
||||
user: User,
|
||||
canEdit: boolean,
|
||||
@@ -23,19 +26,21 @@ const UserListItem = ({ user, onAdd, canEdit }: Props) => {
|
||||
<>
|
||||
{user.lastActiveAt ? (
|
||||
<>
|
||||
Active <Time dateTime={user.lastActiveAt} /> ago
|
||||
{t("Active {{ lastActiveAt }} ago", {
|
||||
lastActiveAt: <Time dateTime={user.lastActiveAt} />,
|
||||
})}
|
||||
</>
|
||||
) : (
|
||||
"Never signed in"
|
||||
t("Never signed in")
|
||||
)}
|
||||
{!user.lastActiveAt && <Badge>Invited</Badge>}
|
||||
{user.isAdmin && <Badge primary={user.isAdmin}>Admin</Badge>}
|
||||
{!user.lastActiveAt && <Badge>{t("Invited")}</Badge>}
|
||||
{user.isAdmin && <Badge primary={user.isAdmin}>{t("Admin")}</Badge>}
|
||||
</>
|
||||
}
|
||||
actions={
|
||||
canEdit ? (
|
||||
<Button type="button" onClick={onAdd} icon={<PlusIcon />} neutral>
|
||||
Add
|
||||
{t("Add")}
|
||||
</Button>
|
||||
) : undefined
|
||||
}
|
||||
|
||||
@@ -125,6 +125,7 @@ class Profile extends React.Component<Props> {
|
||||
options={[
|
||||
{ label: "English (US)", value: "en_US" },
|
||||
{ label: "Deutsch (Deutschland)", value: "de_DE" },
|
||||
{ label: "Português (Portugal)", value: "pt_PT" },
|
||||
]}
|
||||
value={this.language}
|
||||
onChange={this.handleLanguageChange}
|
||||
|
||||
@@ -1,19 +1,143 @@
|
||||
{
|
||||
"Home": "Startseite",
|
||||
"Recently updated": "Zuletzt geändert",
|
||||
"Recently viewed": "Zuletzt gesehen",
|
||||
"Created by me": "Von mir erstellt",
|
||||
"Profile saved": "Profil gespeichert",
|
||||
"Profile picture updated": "Profilfoto wurde aktualisiert",
|
||||
"Unable to upload new profile picture": "Neuer Avatar kann nicht hochgeladen werden",
|
||||
"Profile": "Profil",
|
||||
"Photo": "Foto",
|
||||
"Upload": "Hochladen",
|
||||
"Full name": "Vorname",
|
||||
"Language": "Sprache",
|
||||
"Saving…": "Am speichern…",
|
||||
"Save": "Speichern",
|
||||
"Delete Account": "Konto löschen",
|
||||
"You may delete your account at any time, note that this is unrecoverable": "Sie können Ihr Konto jederzeit löschen. Beachten Sie, dass dies nicht wiederherstellbar ist",
|
||||
"Delete account": "Konto löschen"
|
||||
}
|
||||
"<strong>{{ userName }} {{ you }} <br /> {{ action }}</strong>": "<strong>{{ userName }} {{ you }} <br /> {{ action }}</strong>",
|
||||
"Trash": "Papierkorb",
|
||||
"Archive": "Archiv",
|
||||
"Drafts": "Entwürfe",
|
||||
"Templates": "Vorlagen",
|
||||
"New": "Neu",
|
||||
"Only visible to you": "Nur für Sie sichtbar",
|
||||
"Draft": "Entwurf",
|
||||
"Template": "Vorlag",
|
||||
"New doc": "Neues Dok",
|
||||
"why you not here": "warum sind sie nicht hier",
|
||||
"More options": "Mehr Optionen",
|
||||
"Search...": "Suche...",
|
||||
"New collection…": "Neue Kollektion…",
|
||||
"Collections": "Kollektionen",
|
||||
"Untitled": "Ohne Titel",
|
||||
"Home": "Startseite",
|
||||
"Search": "Suche",
|
||||
"Starred": "Favoriten",
|
||||
"Invite people…": "Personen einladen…",
|
||||
"Invite people": "Personen einalden",
|
||||
"Create a collection": "Kollektion erstellen",
|
||||
"Keyboard shortcuts": "Tastarturkürzel",
|
||||
"Settings": "Einstellungen",
|
||||
"API documentation": "API Dokumentation",
|
||||
"Changelog": "Changelog",
|
||||
"Send us feedback": "Schicken Sie uns Feedback",
|
||||
"Report a bug": "Ein technischen Fehler melden",
|
||||
"Appearance": "Darstellung",
|
||||
"System": "System",
|
||||
"Light": "Hell",
|
||||
"Dark": "Dunkel",
|
||||
"Log out": "Ausloggen",
|
||||
"Collection permissions": "Kollektionsberechtigungen",
|
||||
"New document": "Neues Dokument",
|
||||
"Import document": "Dokument importieren",
|
||||
"Edit…": "Bearbeiten…",
|
||||
"Permissions…": "Berechtigungen…",
|
||||
"Export…": "Export…",
|
||||
"Delete…": "Löschen…",
|
||||
"Edit collection": "Kollektion bearbeiten",
|
||||
"Delete collection": "Kollektion löschen",
|
||||
"Export collection": "Kollektion exportieren",
|
||||
"Document duplicated": "Kollektion wurde dupliziert",
|
||||
"Document archived": "Kollektion wurde archiviert",
|
||||
"Document restored": "Kollektion wurde wiederhergestellt",
|
||||
"Document unpublished": "Kollektion wurde unveröffentlicht",
|
||||
"Restore": "Wiederherstellen",
|
||||
"Restore…": "Wiederherstellen…",
|
||||
"Choose a collection": "Kollektion auswählen",
|
||||
"Unpin": "Unpin",
|
||||
"Pin to collection": "Zur Kollektion anpinnen",
|
||||
"Unstar": "Stern entfernen",
|
||||
"Star": "Stern",
|
||||
"Create a public share link": "Einen öffentlichen Freigabelink erstellen",
|
||||
"Share link…": "Link teilen…",
|
||||
"Enable embeds": "Embeds aktivieren",
|
||||
"Disable embeds": "Embeds deaktivieren",
|
||||
"Create a nested document inside the current document": "Erstellen Sie ein verschachteltes Dokument im aktuellen Dokument",
|
||||
"New nested document": "Neues verschachteltes Dokument",
|
||||
"Create template…": "Vorlage erstellen…",
|
||||
"Unpublish": "Unveröffentlichen",
|
||||
"Edit": "Bearbeiten",
|
||||
"Duplicate": "Duplizieren",
|
||||
"Move…": "Verschieben…",
|
||||
"History": "Verlauf",
|
||||
"Download": "Download",
|
||||
"Print": "Drucken",
|
||||
"Delete {{ documentName }}": "{{ documentName }} löschen",
|
||||
"Create template": "Vorlage erstellen",
|
||||
"Share document": "Dokument teilen",
|
||||
"Edit group": "Gruppe bearbeiten",
|
||||
"Delete group": "Gruppe löschen",
|
||||
"Members…": "Mitglieder…",
|
||||
"New document in": "Neues Dokument in der ",
|
||||
"collection": "kollektion",
|
||||
"New template…": "Neue Vorlage…",
|
||||
"Link copied": "Link wurde kopiert",
|
||||
"Restore version": "Version wiederherstellen",
|
||||
"Copy link": "Link kopieren",
|
||||
"Share link revoked": "Freigabelink wurde gelöscht",
|
||||
"Share link copied": "Freigabelink wurde kopiert",
|
||||
"Go to document": "Zum Dokument gehen",
|
||||
"Revoke link": "Link löschen",
|
||||
"By {{ author }}": "Von {{ author }}",
|
||||
"Are you want to make {{ userName }} an admin? Admins can modify team and billing information.": "Möchten Sie {{userName}} zum Administrator machen? Administratoren können Team- und Rechnungsinformationen ändern.",
|
||||
"Are you want to make {{ userName }} a member?": "Möchten Sie {{userName}} zu einem Mitglied machen?",
|
||||
"Are you want to suspend this account? Suspended users will be prevented from logging in.": "Möchten Sie dieses Konto sperren? Gesperrte Benutzer können sich nicht anmelden.",
|
||||
"Make {{ userName }} a member…": "Machen Sie {{userName}} zu einem Mitglied…",
|
||||
"Make {{ userName }} an admin…": "Machen Sie {{userName}} zu einem Administrator…",
|
||||
"Revoke invite…": "Einladung löschen…",
|
||||
"Activate account": "Konto aktivieren",
|
||||
"Suspend account…": "Konto suspendieren…",
|
||||
"Search in collection…": "Innerhalb der Kollektion suchen…",
|
||||
"\n <strong>{{ collectionName }}</strong> doesn’t contain any\n documents yet.\n <br />\n Get started by creating a new one!\n ": "\n <strong>{{ collectionName }}</strong> enthält noch keine\n Dokumente.\n <br />\n Erstellen Sie zunächst ein neues!\n ",
|
||||
"Create a document": "Dokument erstellen",
|
||||
"Manage members…": "Mitglieder verwalten…",
|
||||
"Pinned": "Gepinned",
|
||||
"Recently updated": "Vor Kurzem aktualisiert",
|
||||
"Recently published": "Vor Kurzem veröffentlicht",
|
||||
"Least recently updated": "Vor kurzem aktualisiert",
|
||||
"A–Z": "A–Z",
|
||||
"{{ groupName }} was added to the collection": "{{ groupName }} wurde zur Kollektion hinzugefügt",
|
||||
"Could not add user": "Benutzer kann nicht hinzugefügt werden",
|
||||
"Can’t find the group you’re looking for?": "Können Sie die Gruppe nicht finden?",
|
||||
"Create a group": "Gruppe erstellen",
|
||||
"Search by group name…": "Nach Name der Gruppe suchen…",
|
||||
"Search groups": "Gruppen suchen",
|
||||
"No groups matching your search": "Keine Gruppen, die Ihrer Suche entsprechen",
|
||||
"No groups left to add": "Keine Gruppen mehr zum Hinzufügen",
|
||||
"Add": "Hinzufügen",
|
||||
"{{ userName }} was added to the collection": "{{ userName }} wurde zur Kollektion hinzugefügt",
|
||||
"Need to add someone who’s not yet on the team yet?": "Müssen Sie jemanden hinzufügen, der noch nicht im Team ist?",
|
||||
"Invite people to {{ teamName }}": "Personen zu {{ teamName }} einladen",
|
||||
"Search by name…": "Nach Name suchen…",
|
||||
"Search people": "Personen suchen",
|
||||
"No people matching your search": "Keine Personen, die Ihrer Suche entsprechen",
|
||||
"No people left to add": "Keine Personen mehr zum Hinzufügen",
|
||||
"Read only": "Schreibgeschützt",
|
||||
"Read & Edit": "Lesen & Schreiben",
|
||||
"Permissions": "Berechtigungen",
|
||||
"Remove": "Entfernen",
|
||||
"Active {{ lastActiveAt }} ago": "Vor {{ lastActiveAt }} aktiv",
|
||||
"Never signed in": "Nie angemeldet",
|
||||
"Invited": "Eingeladet",
|
||||
"Admin": "Admin",
|
||||
"Recently viewed": "Vor Kurzem angesehen",
|
||||
"Created by me": "Von mir erstellt",
|
||||
"Profile saved": "Profil gespeichert",
|
||||
"Profile picture updated": "Profilbild wurde aktualisiert",
|
||||
"Unable to upload new profile picture": "Neues Profilbild kann nicht hochgeladen werden",
|
||||
"Profile": "Profil",
|
||||
"Photo": "Foto",
|
||||
"Upload": "Upload",
|
||||
"Full name": "Vorname",
|
||||
"Language": "Sprache",
|
||||
"Saving…": "Am speichern…",
|
||||
"Save": "Speichern",
|
||||
"Delete Account": "Konto löschen",
|
||||
"You may delete your account at any time, note that this is unrecoverable": "Sie können Ihr Konto jederzeit löschen. Beachten Sie, dass dies nicht wiederherstellbar ist",
|
||||
"Delete account": "Konto löschen"
|
||||
}
|
||||
|
||||
@@ -1,6 +1,130 @@
|
||||
{
|
||||
"<strong>{{ userName }} {{ you }} <br /> {{ action }}</strong>": "<strong>{{ userName }} {{ you }} <br /> {{ action }}</strong>",
|
||||
"Trash": "Trash",
|
||||
"Archive": "Archive",
|
||||
"Drafts": "Drafts",
|
||||
"Templates": "Templates",
|
||||
"New": "New",
|
||||
"Only visible to you": "Only visible to you",
|
||||
"Draft": "Draft",
|
||||
"Template": "Template",
|
||||
"New doc": "New doc",
|
||||
"why you not here": "why you not here",
|
||||
"More options": "More options",
|
||||
"Search...": "Search...",
|
||||
"New collection…": "New collection…",
|
||||
"Collections": "Collections",
|
||||
"Untitled": "Untitled",
|
||||
"Home": "Home",
|
||||
"Search": "Search",
|
||||
"Starred": "Starred",
|
||||
"Invite people…": "Invite people…",
|
||||
"Invite people": "Invite people",
|
||||
"Create a collection": "Create a collection",
|
||||
"Keyboard shortcuts": "Keyboard shortcuts",
|
||||
"Settings": "Settings",
|
||||
"API documentation": "API documentation",
|
||||
"Changelog": "Changelog",
|
||||
"Send us feedback": "Send us feedback",
|
||||
"Report a bug": "Report a bug",
|
||||
"Appearance": "Appearance",
|
||||
"System": "System",
|
||||
"Light": "Light",
|
||||
"Dark": "Dark",
|
||||
"Log out": "Log out",
|
||||
"Collection permissions": "Collection permissions",
|
||||
"New document": "New document",
|
||||
"Import document": "Import document",
|
||||
"Edit…": "Edit…",
|
||||
"Permissions…": "Permissions…",
|
||||
"Export…": "Export…",
|
||||
"Delete…": "Delete…",
|
||||
"Edit collection": "Edit collection",
|
||||
"Delete collection": "Delete collection",
|
||||
"Export collection": "Export collection",
|
||||
"Document duplicated": "Document duplicated",
|
||||
"Document archived": "Document archived",
|
||||
"Document restored": "Document restored",
|
||||
"Document unpublished": "Document unpublished",
|
||||
"Restore": "Restore",
|
||||
"Restore…": "Restore…",
|
||||
"Choose a collection": "Choose a collection",
|
||||
"Unpin": "Unpin",
|
||||
"Pin to collection": "Pin to collection",
|
||||
"Unstar": "Unstar",
|
||||
"Star": "Star",
|
||||
"Create a public share link": "Create a public share link",
|
||||
"Share link…": "Share link…",
|
||||
"Enable embeds": "Enable embeds",
|
||||
"Disable embeds": "Disable embeds",
|
||||
"Create a nested document inside the current document": "Create a nested document inside the current document",
|
||||
"New nested document": "New nested document",
|
||||
"Create template…": "Create template…",
|
||||
"Unpublish": "Unpublish",
|
||||
"Edit": "Edit",
|
||||
"Duplicate": "Duplicate",
|
||||
"Move…": "Move…",
|
||||
"History": "History",
|
||||
"Download": "Download",
|
||||
"Print": "Print",
|
||||
"Delete {{ documentName }}": "Delete {{ documentName }}",
|
||||
"Create template": "Create template",
|
||||
"Share document": "Share document",
|
||||
"Edit group": "Edit group",
|
||||
"Delete group": "Delete group",
|
||||
"Members…": "Members…",
|
||||
"New document in": "New document in",
|
||||
"collection": "collection",
|
||||
"New template…": "New template…",
|
||||
"Link copied": "Link copied",
|
||||
"Restore version": "Restore version",
|
||||
"Copy link": "Copy link",
|
||||
"Share link revoked": "Share link revoked",
|
||||
"Share link copied": "Share link copied",
|
||||
"Go to document": "Go to document",
|
||||
"Revoke link": "Revoke link",
|
||||
"By {{ author }}": "By {{ author }}",
|
||||
"Are you want to make {{ userName }} an admin? Admins can modify team and billing information.": "Are you want to make {{ userName }} an admin? Admins can modify team and billing information.",
|
||||
"Are you want to make {{ userName }} a member?": "Are you want to make {{ userName }} a member?",
|
||||
"Are you want to suspend this account? Suspended users will be prevented from logging in.": "Are you want to suspend this account? Suspended users will be prevented from logging in.",
|
||||
"Make {{ userName }} a member…": "Make {{ userName }} a member…",
|
||||
"Make {{ userName }} an admin…": "Make {{ userName }} an admin…",
|
||||
"Revoke invite…": "Revoke invite…",
|
||||
"Activate account": "Activate account",
|
||||
"Suspend account…": "Suspend account…",
|
||||
"Search in collection…": "Search in collection…",
|
||||
"\n <strong>{{ collectionName }}</strong> doesn’t contain any\n documents yet.\n <br />\n Get started by creating a new one!\n ": "\n <strong>{{ collectionName }}</strong> doesn’t contain any\n documents yet.\n <br />\n Get started by creating a new one!\n ",
|
||||
"Create a document": "Create a document",
|
||||
"Manage members…": "Manage members…",
|
||||
"Pinned": "Pinned",
|
||||
"Recently updated": "Recently updated",
|
||||
"Recently published": "Recently published",
|
||||
"Least recently updated": "Least recently updated",
|
||||
"A–Z": "A–Z",
|
||||
"{{ groupName }} was added to the collection": "{{ groupName }} was added to the collection",
|
||||
"Could not add user": "Could not add user",
|
||||
"Can’t find the group you’re looking for?": "Can’t find the group you’re looking for?",
|
||||
"Create a group": "Create a group",
|
||||
"Search by group name…": "Search by group name…",
|
||||
"Search groups": "Search groups",
|
||||
"No groups matching your search": "No groups matching your search",
|
||||
"No groups left to add": "No groups left to add",
|
||||
"Add": "Add",
|
||||
"{{ userName }} was added to the collection": "{{ userName }} was added to the collection",
|
||||
"Need to add someone who’s not yet on the team yet?": "Need to add someone who’s not yet on the team yet?",
|
||||
"Invite people to {{ teamName }}": "Invite people to {{ teamName }}",
|
||||
"Search by name…": "Search by name…",
|
||||
"Search people": "Search people",
|
||||
"No people matching your search": "No people matching your search",
|
||||
"No people left to add": "No people left to add",
|
||||
"Read only": "Read only",
|
||||
"Read & Edit": "Read & Edit",
|
||||
"Permissions": "Permissions",
|
||||
"Remove": "Remove",
|
||||
"Active {{ lastActiveAt }} ago": "Active {{ lastActiveAt }} ago",
|
||||
"Never signed in": "Never signed in",
|
||||
"Invited": "Invited",
|
||||
"Admin": "Admin",
|
||||
"Recently viewed": "Recently viewed",
|
||||
"Created by me": "Created by me",
|
||||
"Profile saved": "Profile saved",
|
||||
|
||||
@@ -2,8 +2,8 @@
|
||||
import i18n from "i18next";
|
||||
import { initReactI18next } from "react-i18next";
|
||||
|
||||
import en_US from "./default.json";
|
||||
import de_DE from "./de_DE.json";
|
||||
import en_US from "./default.json";
|
||||
import pt_PT from "./pt_PT.json";
|
||||
|
||||
const outlineTranslation = {
|
||||
@@ -15,7 +15,10 @@ const outlineTranslation = {
|
||||
react: {
|
||||
useSuspense: false,
|
||||
},
|
||||
lng: "DEFAULT_LANGUAGE" in process.env ? process.env.DEFAULT_LANGUAGE : "en_US",
|
||||
lng:
|
||||
"DEFAULT_LANGUAGE" in process.env
|
||||
? process.env.DEFAULT_LANGUAGE
|
||||
: "en_US",
|
||||
debug: process.env.NODE_ENV !== "production",
|
||||
resources: {
|
||||
en_US: {
|
||||
@@ -25,11 +28,11 @@ const outlineTranslation = {
|
||||
translation: de_DE,
|
||||
},
|
||||
pt_PT: {
|
||||
translation: pt_PT
|
||||
}
|
||||
translation: pt_PT,
|
||||
},
|
||||
},
|
||||
});
|
||||
}
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
export { outlineTranslation, i18n, en_US, de_DE, pt_PT };
|
||||
|
||||
@@ -1,97 +1,97 @@
|
||||
/* eslint-disable flowtype/require-valid-file-annotation */
|
||||
import { outlineTranslation, i18n, en_US, de_DE, pt_PT } from "./i18n";
|
||||
import { isEqual } from "lodash";
|
||||
import { outlineTranslation, i18n, en_US, de_DE, pt_PT } from "./i18n";
|
||||
|
||||
describe("i18n configuration", () => {
|
||||
beforeEach(() => {
|
||||
outlineTranslation.init();
|
||||
});
|
||||
it("all languages should have same keys", () => {
|
||||
const en_US_Keys = Object.keys(en_US);
|
||||
const de_DE_Keys = Object.keys(de_DE);
|
||||
const pt_PT_Keys = Object.keys(pt_PT);
|
||||
beforeEach(() => {
|
||||
outlineTranslation.init();
|
||||
});
|
||||
it("all languages should have same keys", () => {
|
||||
const en_US_Keys = Object.keys(en_US);
|
||||
const de_DE_Keys = Object.keys(de_DE);
|
||||
const pt_PT_Keys = Object.keys(pt_PT);
|
||||
|
||||
expect(isEqual(en_US_Keys, de_DE_Keys)).toBe(true);
|
||||
expect(isEqual(en_US_Keys, pt_PT_Keys)).toBe(true);
|
||||
});
|
||||
expect(isEqual(en_US_Keys, de_DE_Keys)).toBe(true);
|
||||
expect(isEqual(en_US_Keys, pt_PT_Keys)).toBe(true);
|
||||
});
|
||||
});
|
||||
|
||||
describe("i18n process.env is unset", () => {
|
||||
beforeEach(() => {
|
||||
delete process.env.DEFAULT_LANGUAGE;
|
||||
outlineTranslation.init();
|
||||
});
|
||||
beforeEach(() => {
|
||||
delete process.env.DEFAULT_LANGUAGE;
|
||||
outlineTranslation.init();
|
||||
});
|
||||
|
||||
it("translation of key should match", () => expect(i18n.t("Saving…")).toBe("Saving…"));
|
||||
it("translation of key should match", () =>
|
||||
expect(i18n.t("Saving…")).toBe("Saving…"));
|
||||
|
||||
it ("translation if changed to de_DE", () => {
|
||||
i18n.changeLanguage("de_DE");
|
||||
expect(i18n.t("Saving…")).toBe("Am speichern…");
|
||||
});
|
||||
|
||||
it ("translation if changed to pt_PT", () => {
|
||||
i18n.changeLanguage("pt_PT");
|
||||
expect(i18n.t("Saving…")).toBe("A guardar…");
|
||||
});
|
||||
it("translation if changed to de_DE", () => {
|
||||
i18n.changeLanguage("de_DE");
|
||||
expect(i18n.t("Saving…")).toBe("Am speichern…");
|
||||
});
|
||||
|
||||
it("translation if changed to pt_PT", () => {
|
||||
i18n.changeLanguage("pt_PT");
|
||||
expect(i18n.t("Saving…")).toBe("A guardar…");
|
||||
});
|
||||
});
|
||||
|
||||
describe("i18n process.env is en_US", () => {
|
||||
beforeEach(() => {
|
||||
process.env.DEFAULT_LANGUAGE = "en_US";
|
||||
outlineTranslation.init();
|
||||
});
|
||||
beforeEach(() => {
|
||||
process.env.DEFAULT_LANGUAGE = "en_US";
|
||||
outlineTranslation.init();
|
||||
});
|
||||
|
||||
it("translation of key should match", () => expect(i18n.t("Saving…")).toBe("Saving…"));
|
||||
it("translation of key should match", () =>
|
||||
expect(i18n.t("Saving…")).toBe("Saving…"));
|
||||
|
||||
it ("translation if changed to de_DE", () => {
|
||||
i18n.changeLanguage("de_DE");
|
||||
expect(i18n.t("Saving…")).toBe("Am speichern…");
|
||||
});
|
||||
|
||||
it ("translation if changed to pt_PT", () => {
|
||||
i18n.changeLanguage("pt_PT");
|
||||
expect(i18n.t("Saving…")).toBe("A guardar…");
|
||||
});
|
||||
it("translation if changed to de_DE", () => {
|
||||
i18n.changeLanguage("de_DE");
|
||||
expect(i18n.t("Saving…")).toBe("Am speichern…");
|
||||
});
|
||||
|
||||
it("translation if changed to pt_PT", () => {
|
||||
i18n.changeLanguage("pt_PT");
|
||||
expect(i18n.t("Saving…")).toBe("A guardar…");
|
||||
});
|
||||
});
|
||||
|
||||
describe("i18n process.env is de_DE", () => {
|
||||
beforeEach(() => {
|
||||
process.env.DEFAULT_LANGUAGE = "de_DE";
|
||||
outlineTranslation.init();
|
||||
});
|
||||
beforeEach(() => {
|
||||
process.env.DEFAULT_LANGUAGE = "de_DE";
|
||||
outlineTranslation.init();
|
||||
});
|
||||
|
||||
it("translation of key should match", () => expect(i18n.t("Saving…")).toBe("Am speichern…"));
|
||||
it("translation of key should match", () =>
|
||||
expect(i18n.t("Saving…")).toBe("Am speichern…"));
|
||||
|
||||
it ("translation if changed to en_US", () => {
|
||||
i18n.changeLanguage("en_US");
|
||||
expect(i18n.t("Saving…")).toBe("Saving…");
|
||||
});
|
||||
|
||||
it ("translation if changed to pt_PT", () => {
|
||||
i18n.changeLanguage("pt_PT");
|
||||
expect(i18n.t("Saving…")).toBe("A guardar…");
|
||||
});
|
||||
it("translation if changed to en_US", () => {
|
||||
i18n.changeLanguage("en_US");
|
||||
expect(i18n.t("Saving…")).toBe("Saving…");
|
||||
});
|
||||
|
||||
it("translation if changed to pt_PT", () => {
|
||||
i18n.changeLanguage("pt_PT");
|
||||
expect(i18n.t("Saving…")).toBe("A guardar…");
|
||||
});
|
||||
});
|
||||
|
||||
describe("i18n process.env is pt_PT", () => {
|
||||
beforeEach(() => {
|
||||
process.env.DEFAULT_LANGUAGE = "pt_PT";
|
||||
outlineTranslation.init();
|
||||
});
|
||||
beforeEach(() => {
|
||||
process.env.DEFAULT_LANGUAGE = "pt_PT";
|
||||
outlineTranslation.init();
|
||||
});
|
||||
|
||||
it("translation of key should match", () => expect(i18n.t("Saving…")).toBe("A guardar…"));
|
||||
it("translation of key should match", () =>
|
||||
expect(i18n.t("Saving…")).toBe("A guardar…"));
|
||||
|
||||
it ("translation if changed to en_US", () => {
|
||||
i18n.changeLanguage("en_US");
|
||||
expect(i18n.t("Saving…")).toBe("Saving…");
|
||||
});
|
||||
it("translation if changed to en_US", () => {
|
||||
i18n.changeLanguage("en_US");
|
||||
expect(i18n.t("Saving…")).toBe("Saving…");
|
||||
});
|
||||
|
||||
it ("translation if changed to de_DE", () => {
|
||||
i18n.changeLanguage("de_DE");
|
||||
expect(i18n.t("Saving…")).toBe("Am speichern…");
|
||||
});
|
||||
|
||||
});
|
||||
it("translation if changed to de_DE", () => {
|
||||
i18n.changeLanguage("de_DE");
|
||||
expect(i18n.t("Saving…")).toBe("Am speichern…");
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,20 +1,143 @@
|
||||
{
|
||||
"Home": "Home",
|
||||
"Recently updated": "Recently updated",
|
||||
"Recently viewed": "Recently viewed",
|
||||
"Created by me": "Created by me",
|
||||
"Profile saved": "Profile saved",
|
||||
"Profile picture updated": "Profile picture updated",
|
||||
"Unable to upload new profile picture": "Unable to upload new avatar",
|
||||
"Profile": "Profile",
|
||||
"Photo": "Photo",
|
||||
"Upload": "Upload",
|
||||
"Full name": "Full name",
|
||||
"Language": "Language",
|
||||
"Saving…": "A guardar…",
|
||||
"Save": "Save",
|
||||
"Delete Account": "Delete Account",
|
||||
"You may delete your account at any time, note that this is unrecoverable": "You may delete your account at any time, note that this is unrecoverable",
|
||||
"Delete account": "Delete account"
|
||||
}
|
||||
|
||||
"<strong>{{ userName }} {{ you }} <br /> {{ action }}</strong>": "<strong>{{ userName }} {{ you }} <br /> {{ action }}</strong>",
|
||||
"Trash": "Reciclagem",
|
||||
"Archive": "Arquivo",
|
||||
"Drafts": "Rascunhos",
|
||||
"Templates": "Templates",
|
||||
"New": "Novo",
|
||||
"Only visible to you": "Apenas visível para ti",
|
||||
"Draft": "Rascunho",
|
||||
"Template": "Template",
|
||||
"New doc": "Novo doc",
|
||||
"why you not here": "porquê é que não estás aquí",
|
||||
"More options": "Mais opções",
|
||||
"Search...": "Pesquisa...",
|
||||
"New collection…": "Nova coleção…",
|
||||
"Collections": "Coleções",
|
||||
"Untitled": "Sem título",
|
||||
"Home": "Página inicial",
|
||||
"Search": "Pesquisa",
|
||||
"Starred": "Favoritos",
|
||||
"Invite people…": "Convidar pessoas…",
|
||||
"Invite people": "Convidar pessoas",
|
||||
"Create a collection": "Criar coleção",
|
||||
"Keyboard shortcuts": "Atalhos do teclado",
|
||||
"Settings": "Configurações",
|
||||
"API documentation": "Documentação da API",
|
||||
"Changelog": "Changelog",
|
||||
"Send us feedback": "Envia-nos Feedback",
|
||||
"Report a bug": "Reportar um erro técnico",
|
||||
"Appearance": "Aparência",
|
||||
"System": "Sistema",
|
||||
"Light": "Claro",
|
||||
"Dark": "Escuro",
|
||||
"Log out": "Deslogar",
|
||||
"Collection permissions": "Permissões da coleção",
|
||||
"New document": "Novo documento",
|
||||
"Import document": "Importar documento",
|
||||
"Edit…": "Editar…",
|
||||
"Permissions…": "Permissões…",
|
||||
"Export…": "Exportar…",
|
||||
"Delete…": "Apagar…",
|
||||
"Edit collection": "Editar coleção",
|
||||
"Delete collection": "Apagar coleção",
|
||||
"Export collection": "Exportar coleção",
|
||||
"Document duplicated": "Documento duplicado",
|
||||
"Document archived": "Documento arquivado",
|
||||
"Document restored": "Documento restaurado",
|
||||
"Document unpublished": "Documento não publicado",
|
||||
"Restore": "Restaurar",
|
||||
"Restore…": "Restaurar…",
|
||||
"Choose a collection": "Escolher coleção",
|
||||
"Unpin": "Tirar pino",
|
||||
"Pin to collection": "Pin coleção",
|
||||
"Unstar": "Tirar estrela",
|
||||
"Star": "Estrela",
|
||||
"Create a public share link": "Criar um link de partilha público",
|
||||
"Share link…": "Partilhar link…",
|
||||
"Enable embeds": "Ativar embeds",
|
||||
"Disable embeds": "Desativar embeds",
|
||||
"Create a nested document inside the current document": "Criar um documento aninhado dentro do documento atual",
|
||||
"New nested document": "Novo documento aninhado",
|
||||
"Create template…": "Criar template…",
|
||||
"Unpublish": "Despublicar",
|
||||
"Edit": "Editar",
|
||||
"Duplicate": "Duplicar",
|
||||
"Move…": "Mover…",
|
||||
"History": "Historia",
|
||||
"Download": "Download",
|
||||
"Print": "Imprimir",
|
||||
"Delete {{ documentName }}": "Apagar {{ documentName }}",
|
||||
"Create template": "Criar template",
|
||||
"Share document": "Partilhar documento",
|
||||
"Edit group": "Editar grupo",
|
||||
"Delete group": "Apagar grupo",
|
||||
"Members…": "Membros…",
|
||||
"New document in": "Novo documento em",
|
||||
"collection": "coleção",
|
||||
"New template…": "Novo template…",
|
||||
"Link copied": "Link copiado",
|
||||
"Restore version": "Restaurar versão",
|
||||
"Copy link": "Copiar link",
|
||||
"Share link revoked": "Link de partilha foi revogado",
|
||||
"Share link copied": "Link de partilha foi copiado",
|
||||
"Go to document": "Ir para documento",
|
||||
"Revoke link": "Revogar link",
|
||||
"By {{ author }}": "De {{ author }}",
|
||||
"Are you want to make {{ userName }} an admin? Admins can modify team and billing information.": "Deseja tornar {{userName}} um administrador? Os administradores podem modificar as informações da equipe e do faturamento.",
|
||||
"Are you want to make {{ userName }} a member?": "Deseja tornar {{userName}} um membro?",
|
||||
"Are you want to suspend this account? Suspended users will be prevented from logging in.": "Deseja suspender esta conta? Os usuários suspensos serão impedidos de fazer login.",
|
||||
"Make {{ userName }} a member…": "Tornar {{userName}} um membro…",
|
||||
"Make {{ userName }} an admin…": "Tornar {{userName}} um administrador…",
|
||||
"Revoke invite…": "Revogar convite…",
|
||||
"Activate account": "Ativar conta",
|
||||
"Suspend account…": "Suspender conta…",
|
||||
"Search in collection…": "Procurar na coleção…",
|
||||
"\n <strong>{{ collectionName }}</strong> doesn’t contain any\n documents yet.\n <br />\n Get started by creating a new one!\n ": "\n <strong>{{ collectionName }}</strong> não contém nenhum documento\n ainda.\n <br />\n Para começar crie um documento novo!\n ",
|
||||
"Create a document": "Criar documento",
|
||||
"Manage members…": "Gerenciar membros…",
|
||||
"Pinned": "Marcado",
|
||||
"Recently updated": "Atualizado recentemente",
|
||||
"Recently published": "Publicado recentemente",
|
||||
"Least recently updated": "Menos atualizado recentemente",
|
||||
"A–Z": "A–Z",
|
||||
"{{ groupName }} was added to the collection": "{{ groupName }} foi adicionado à coleção",
|
||||
"Could not add user": "Não foi possível adicionar usuário",
|
||||
"Can’t find the group you’re looking for?": "Não consegue encontrar o grupo que procura?",
|
||||
"Create a group": "Criar grupo",
|
||||
"Search by group name…": "Procurar grupo pelo nome…",
|
||||
"Search groups": "Procurar grupos",
|
||||
"No groups matching your search": "Nenhum grupo corresponde à sua pesquisa",
|
||||
"No groups left to add": "Nenhum grupo restante para adicionar",
|
||||
"Add": "Adicionar",
|
||||
"{{ userName }} was added to the collection": "{{userName}} foi adicionado à coleção",
|
||||
"Need to add someone who’s not yet on the team yet?": "Precisa adicionar alguém que ainda não faz parte da equipe?",
|
||||
"Invite people to {{ teamName }}": "Convide pessoas para {{teamName}}",
|
||||
"Search by name…": "Procurar pelo nome…",
|
||||
"Search people": "Procurar pessoas",
|
||||
"No people matching your search": "Nenhuma pessoa corresponde à sua pesquisa",
|
||||
"No people left to add": "Não sobrou ninguém para adicionar",
|
||||
"Read only": "Somente leitura",
|
||||
"Read & Edit": "Leitura & Escrita",
|
||||
"Permissions": "Permissões",
|
||||
"Remove": "Remover",
|
||||
"Active {{ lastActiveAt }} ago": "Ativo há {{ lastActiveAt }}",
|
||||
"Never signed in": "Nunca conectado",
|
||||
"Invited": "Convidado",
|
||||
"Admin": "Admin",
|
||||
"Recently viewed": "Visto recentemente",
|
||||
"Created by me": "Criado por mim",
|
||||
"Profile saved": "Profil foi guardado",
|
||||
"Profile picture updated": "Foto de perfil foi atualizada",
|
||||
"Unable to upload new profile picture": "Não é possível carregar uma nova foto de perfil",
|
||||
"Profile": "Profil",
|
||||
"Photo": "Foto",
|
||||
"Upload": "Upload",
|
||||
"Full name": "Nome completo",
|
||||
"Language": "Língua",
|
||||
"Saving…": "A guardar…",
|
||||
"Save": "Guardar",
|
||||
"Delete Account": "Apagar conta",
|
||||
"You may delete your account at any time, note that this is unrecoverable": "Pode apagar a sua conta a qualquer momento, atenção que isso é irrecuperável",
|
||||
"Delete account": "Apagar conta"
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user