feat: I18n (#1653)

* feat: i18n

* Changing language single source of truth from TEAM to USER

* Changes according to @tommoor comments on PR

* Changed package.json for build:i18n and translation label

* Finished 1st MVP of i18n for outline

* new translation labels & Portuguese from Portugal translation

* Fixes from PR request

* Described language dropdown as an experimental feature

* Set keySeparator to false in order to cowork with html keys

* Added useTranslation to Breadcrumb

* Repositioned <strong> element

* Removed extra space from TemplatesMenu

* Fortified the test suite for i18n

* Fixed trans component problematic

* Check if selected language is available

* Update yarn.lock

* Removed unused Trans

* Removing debug variable from i18n init

* Removed debug variable

* test: update snapshots

* flow: Remove decorator usage to get proper flow typing
It's a shame, but hopefully we'll move to Typescript in the next 6 months and we can forget this whole Flow mistake ever happened

* translate: Drafts

* More translatable strings

* Mo translation strings

* translation: Search

* async translations loading

* cache translations in client

* Revert "cache translations in client"

This reverts commit 08fb61ce36384ff90a704faffe4761eccfb76da1.

* Revert localStorage cache for cache headers

* Update Crowdin configuration file

* Moved translation files to locales folder and fixed english text

* Added CONTRIBUTING File for CrowdIn

* chore: Move translations again to please CrowdIn

* fix: loading paths
chore: Add strings for editor

* fix: Improve validation on documents.import endpoint

* test: mock bull

* fix: Unknown mimetype should fallback to Markdown parsing if markdown extension (#1678)

* closes #1675

* Update CONTRIBUTING

* chore: Add link to translation portal from app UI

* refactor: Centralize language config

* fix: Ensure creation of i18n directory in build

* feat: Add language prompt

* chore: Improve contributing guidelines, add link from README

* chore: Normalize tab header casing

* chore: More string externalization

* fix: Language prompt in dark mode

Co-authored-by: André Glatzl <andreglatzl@gmail.com>
This commit is contained in:
Tom Moor
2020-11-29 20:04:58 -08:00
committed by GitHub
parent 63c73c9a51
commit 1285efc49a
85 changed files with 6432 additions and 2613 deletions

View File

@@ -11,6 +11,7 @@ import {
} from "outline-icons";
import { transparentize, darken } from "polished";
import * as React from "react";
import { withTranslation, Trans, type TFunction } from "react-i18next";
import { Redirect } from "react-router-dom";
import styled from "styled-components";
import breakpoint from "styled-components-breakpoint";
@@ -55,6 +56,7 @@ type Props = {
publish?: boolean,
autosave?: boolean,
}) => void,
t: TFunction,
};
@observer
@@ -131,6 +133,7 @@ class Header extends React.Component<Props> {
publishingIsDisabled,
ui,
auth,
t,
} = this.props;
const share = shares.getByDocumentId(document.id);
@@ -153,7 +156,7 @@ class Header extends React.Component<Props> {
<Modal
isOpen={this.showShareModal}
onRequestClose={this.handleCloseShareModal}
title="Share document"
title={t("Share document")}
>
<DocumentShare
document={document}
@@ -166,7 +169,9 @@ class Header extends React.Component<Props> {
<>
<Slash />
<Tooltip
tooltip={ui.tocVisible ? "Hide contents" : "Show contents"}
tooltip={
ui.tocVisible ? t("Hide contents") : t("Show contents")
}
shortcut={`ctrl+${meta}+h`}
delay={250}
placement="bottom"
@@ -190,14 +195,15 @@ class Header extends React.Component<Props> {
{this.isScrolled && (
<Title onClick={this.handleClickTitle}>
<Fade>
{document.title} {document.isArchived && <Badge>Archived</Badge>}
{document.title}{" "}
{document.isArchived && <Badge>{t("Archived")}</Badge>}
</Fade>
</Title>
)}
<Wrapper align="center" justify="flex-end">
{isSaving && !isPublishing && (
<Action>
<Status>Saving</Status>
<Status>{t("Saving…")}</Status>
</Action>
)}
&nbsp;
@@ -217,10 +223,10 @@ class Header extends React.Component<Props> {
<Tooltip
tooltip={
isPubliclyShared ? (
<>
<Trans>
Anyone with the link <br />
can view this document
</>
</Trans>
) : (
""
)
@@ -234,7 +240,7 @@ class Header extends React.Component<Props> {
neutral
small
>
Share
{t("Share")}
</Button>
</Tooltip>
</Action>
@@ -243,7 +249,7 @@ class Header extends React.Component<Props> {
<>
<Action>
<Tooltip
tooltip="Save"
tooltip={t("Save")}
shortcut={`${meta}+enter`}
delay={500}
placement="bottom"
@@ -255,7 +261,7 @@ class Header extends React.Component<Props> {
neutral={isDraft}
small
>
{isDraft ? "Save Draft" : "Done Editing"}
{isDraft ? t("Save Draft") : t("Done Editing")}
</Button>
</Tooltip>
</Action>
@@ -264,7 +270,7 @@ class Header extends React.Component<Props> {
{canEdit && (
<Action>
<Tooltip
tooltip={`Edit ${document.noun}`}
tooltip={t("Edit {{noun}}", { noun: document.noun })}
shortcut="e"
delay={500}
placement="bottom"
@@ -275,7 +281,7 @@ class Header extends React.Component<Props> {
neutral
small
>
Edit
{t("Edit")}
</Button>
</Tooltip>
</Action>
@@ -286,13 +292,13 @@ class Header extends React.Component<Props> {
document={document}
label={
<Tooltip
tooltip="New document"
tooltip={t("New document")}
shortcut="n"
delay={500}
placement="bottom"
>
<Button icon={<PlusIcon />} neutral>
New doc
{t("New doc")}
</Button>
</Tooltip>
}
@@ -307,25 +313,25 @@ class Header extends React.Component<Props> {
primary
small
>
New from template
{t("New from template")}
</Button>
</Action>
)}
{can.update && isDraft && !isRevision && (
<Action>
<Tooltip
tooltip="Publish"
tooltip={t("Publish")}
shortcut={`${meta}+shift+p`}
delay={500}
placement="bottom"
>
<Button
onClick={this.handlePublish}
title="Publish document"
title={t("Publish document")}
disabled={publishingIsDisabled}
small
>
{isPublishing ? "Publishing…" : "Publish"}
{isPublishing ? t("Publishing…") : t("Publish")}
</Button>
</Tooltip>
</Action>
@@ -425,4 +431,6 @@ const Title = styled.div`
`};
`;
export default inject("auth", "ui", "policies", "shares")(Header);
export default withTranslation()<Header>(
inject("auth", "ui", "policies", "shares")(Header)
);

View File

@@ -1,5 +1,6 @@
// @flow
import * as React from "react";
import { useTranslation } from "react-i18next";
import CenteredContent from "components/CenteredContent";
import LoadingPlaceholder from "components/LoadingPlaceholder";
import PageTitle from "components/PageTitle";
@@ -11,9 +12,13 @@ type Props = {|
|};
export default function Loading({ location }: Props) {
const { t } = useTranslation();
return (
<Container column auto>
<PageTitle title={location.state ? location.state.title : "Untitled"} />
<PageTitle
title={location.state ? location.state.title : t("Untitled")}
/>
<CenteredContent>
<LoadingPlaceholder />
</CenteredContent>