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

@@ -7,6 +7,7 @@ import { PlusIcon } from "outline-icons";
import queryString from "query-string";
import * as React from "react";
import ReactDOM from "react-dom";
import { withTranslation, Trans, type TFunction } from "react-i18next";
import keydown from "react-keydown";
import { withRouter, Link } from "react-router-dom";
import type { RouterHistory, Match } from "react-router-dom";
@@ -44,11 +45,12 @@ type Props = {
documents: DocumentsStore,
users: UsersStore,
notFound: ?boolean,
t: TFunction,
};
@observer
class Search extends React.Component<Props> {
firstDocument: ?React.Component<typeof DocumentPreview>;
firstDocument: ?React.Component<any>;
lastQuery: string = "";
@observable
@@ -67,7 +69,7 @@ class Search extends React.Component<Props> {
}
}
componentDidUpdate(prevProps) {
componentDidUpdate(prevProps: Props) {
if (prevProps.location.search !== this.props.location.search) {
this.handleQueryChange();
}
@@ -81,7 +83,7 @@ class Search extends React.Component<Props> {
this.props.history.goBack();
}
handleKeyDown = (ev) => {
handleKeyDown = (ev: SyntheticKeyboardEvent<>) => {
if (ev.key === "Enter") {
this.fetchResults();
return;
@@ -124,7 +126,12 @@ class Search extends React.Component<Props> {
this.fetchResultsDebounced();
};
handleFilterChange = (search) => {
handleFilterChange = (search: {
collectionId?: ?string,
userId?: ?string,
dateFilter?: ?string,
includeArchived?: ?string,
}) => {
this.props.history.replace({
pathname: this.props.location.pathname,
search: queryString.stringify({
@@ -170,7 +177,7 @@ class Search extends React.Component<Props> {
get title() {
const query = this.query;
const title = "Search";
const title = this.props.t("Search");
if (query) return `${query} ${title}`;
return title;
}
@@ -231,20 +238,19 @@ class Search extends React.Component<Props> {
trailing: true,
});
updateLocation = (query) => {
updateLocation = (query: string) => {
this.props.history.replace({
pathname: searchUrl(query),
search: this.props.location.search,
});
};
setFirstDocumentRef = (ref) => {
// $FlowFixMe
setFirstDocumentRef = (ref: any) => {
this.firstDocument = ref;
};
render() {
const { documents, notFound, location } = this.props;
const { documents, notFound, location, t } = this.props;
const results = documents.searchResults(this.query);
const showEmpty = !this.isLoading && this.query && results.length === 0;
const showShortcutTip =
@@ -256,12 +262,15 @@ class Search extends React.Component<Props> {
{this.isLoading && <LoadingIndicator />}
{notFound && (
<div>
<h1>Not Found</h1>
<Empty>We were unable to find the page youre looking for.</Empty>
<h1>{t("Not Found")}</h1>
<Empty>
{t("We were unable to find the page youre looking for.")}
</Empty>
</div>
)}
<ResultsWrapper pinToTop={this.pinToTop} column auto>
<SearchField
placeholder={t("Search…")}
onKeyDown={this.handleKeyDown}
onChange={this.updateLocation}
defaultValue={this.query}
@@ -269,8 +278,10 @@ class Search extends React.Component<Props> {
{showShortcutTip && (
<Fade>
<HelpText small>
Use the <strong>{meta}+K</strong> shortcut to search from
anywhere in Outline
<Trans>
Use the <strong>{{ meta }}+K</strong> shortcut to search from
anywhere in your knowledge base
</Trans>
</HelpText>
</Fade>
)}
@@ -304,8 +315,10 @@ class Search extends React.Component<Props> {
<Fade>
<Centered column>
<HelpText>
No documents found for your search filters. <br />
Create a new document?
<Trans>
No documents found for your search filters. <br />
Create a new document?
</Trans>
</HelpText>
<Wrapper>
{this.collectionId ? (
@@ -314,14 +327,14 @@ class Search extends React.Component<Props> {
icon={<PlusIcon />}
primary
>
New doc
{t("New doc")}
</Button>
) : (
<NewDocumentMenu />
)}
&nbsp;&nbsp;
<Button as={Link} to="/search" neutral>
Clear filters
{t("Clear filters")}
</Button>
</Wrapper>
</Centered>
@@ -414,4 +427,6 @@ const Filters = styled(Flex)`
}
`;
export default withRouter(inject("documents")(Search));
export default withTranslation()<Search>(
withRouter(inject("documents")(Search))
);