Skip to content

Commit

Permalink
umputun#10 add localization
Browse files Browse the repository at this point in the history
  • Loading branch information
Mavrin committed Feb 17, 2020
1 parent 11327ce commit b8e2520
Show file tree
Hide file tree
Showing 25 changed files with 982 additions and 102 deletions.
2 changes: 1 addition & 1 deletion .dockerignore
Original file line number Diff line number Diff line change
Expand Up @@ -21,4 +21,4 @@ debug.test
*.test
remark42
/backend/var/
compose-private-backend.yml
compose-private-backend.yml
1 change: 1 addition & 0 deletions frontend/.gitignore
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
.env
extracted-messages
2 changes: 2 additions & 0 deletions frontend/app/common/config-types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,10 +16,12 @@ export interface CommentsConfig {
theme?: Theme;
page_title?: string;
node?: string;
locale?: string;
}

export interface LastCommentsConfig {
host: string;
site_id: string;
max_last_comments: number;
locale?: string;
}
83 changes: 52 additions & 31 deletions frontend/app/components/auth-panel/auth-panel.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import { Button } from '@app/components/button';
import { User, PostInfo } from '@app/common/types';

import { Props, AuthPanel } from './auth-panel';
import { IntlProvider } from 'react-intl';

const DefaultProps: Partial<Props> = {
sort: '-score',
Expand All @@ -22,7 +23,11 @@ const DefaultProps: Partial<Props> = {
describe('<AuthPanel />', () => {
describe('For not authorized user', () => {
it('should render login form with google and github provider', () => {
const element = mount(<AuthPanel {...(DefaultProps as Props)} user={null} />);
const element = mount(
<IntlProvider locale="en">
<AuthPanel {...(DefaultProps as Props)} user={null} />
</IntlProvider>
);

const authPanelColumn = element.find('.auth-panel__column');

Expand All @@ -41,12 +46,14 @@ describe('<AuthPanel />', () => {
describe('sorting', () => {
it('should place selected provider first', () => {
const element = mount(
<AuthPanel
{...(DefaultProps as Props)}
providers={['google', 'github', 'yandex']}
provider={{ name: 'github' }}
user={null}
/>
<IntlProvider locale="en">
<AuthPanel
{...(DefaultProps as Props)}
providers={['google', 'github', 'yandex']}
provider={{ name: 'github' }}
user={null}
/>
</IntlProvider>
);

const providerLinks = element
Expand All @@ -61,12 +68,14 @@ describe('<AuthPanel />', () => {

it('should do nothing if provider not found', () => {
const element = mount(
<AuthPanel
{...(DefaultProps as Props)}
providers={['google', 'github', 'yandex']}
provider={{ name: 'baidu' }}
user={null}
/>
<IntlProvider locale="en">
<AuthPanel
{...(DefaultProps as Props)}
providers={['google', 'github', 'yandex']}
provider={{ name: 'baidu' }}
user={null}
/>
</IntlProvider>
);

const providerLinks = element
Expand All @@ -82,11 +91,13 @@ describe('<AuthPanel />', () => {

it('should render login form with google and github provider for read-only post', () => {
const element = mount(
<AuthPanel
{...(DefaultProps as Props)}
user={null}
postInfo={{ ...DefaultProps.postInfo, read_only: true } as PostInfo}
/>
<IntlProvider locale="en">
<AuthPanel
{...(DefaultProps as Props)}
user={null}
postInfo={{ ...DefaultProps.postInfo, read_only: true } as PostInfo}
/>
</IntlProvider>
);

const authPanelColumn = element.find('.auth-panel__column');
Expand All @@ -105,11 +116,13 @@ describe('<AuthPanel />', () => {

it('should not render settings if there is no hidden users', () => {
const element = mount(
<AuthPanel
{...(DefaultProps as Props)}
user={null}
postInfo={{ ...DefaultProps.postInfo, read_only: true } as PostInfo}
/>
<IntlProvider locale="en">
<AuthPanel
{...(DefaultProps as Props)}
user={null}
postInfo={{ ...DefaultProps.postInfo, read_only: true } as PostInfo}
/>
</IntlProvider>
);

const adminAction = element.find('.auth-panel__admin-action');
Expand All @@ -119,12 +132,14 @@ describe('<AuthPanel />', () => {

it('should render settings if there is some hidden users', () => {
const element = mount(
<AuthPanel
{...(DefaultProps as Props)}
user={null}
postInfo={{ ...DefaultProps.postInfo, read_only: true } as PostInfo}
hiddenUsers={{ hidden_joe: {} as any }}
/>
<IntlProvider locale="en">
<AuthPanel
{...(DefaultProps as Props)}
user={null}
postInfo={{ ...DefaultProps.postInfo, read_only: true } as PostInfo}
hiddenUsers={{ hidden_joe: {} as any }}
/>
</IntlProvider>
);

const adminAction = element.find('.auth-panel__admin-action');
Expand All @@ -134,7 +149,11 @@ describe('<AuthPanel />', () => {
});
describe('For authorized user', () => {
it('should render info about current user', () => {
const element = mount(<AuthPanel {...(DefaultProps as Props)} user={{ id: `john`, name: 'John' } as User} />);
const element = mount(
<IntlProvider locale="en">
<AuthPanel {...(DefaultProps as Props)} user={{ id: `john`, name: 'John' } as User} />
</IntlProvider>
);

const authPanelColumn = element.find('.auth-panel__column');

Expand All @@ -148,7 +167,9 @@ describe('<AuthPanel />', () => {
describe('For admin user', () => {
it('should render admin action', () => {
const element = mount(
<AuthPanel {...(DefaultProps as Props)} user={{ id: `test`, admin: true, name: 'John' } as User} />
<IntlProvider locale="en">
<AuthPanel {...(DefaultProps as Props)} user={{ id: `test`, admin: true, name: 'John' } as User} />{' '}
</IntlProvider>
);

const adminAction = element.find('.auth-panel__admin-action').first();
Expand Down
7 changes: 4 additions & 3 deletions frontend/app/components/auth-panel/auth-panel.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import { StoreState } from '@app/store';
import { ProviderState } from '@app/store/provider/reducers';
import { Dropdown, DropdownItem } from '@app/components/dropdown';
import { Button } from '@app/components/button';
import { FormattedMessage } from 'react-intl';

import { AnonymousLoginForm } from './__anonymous-login-form';
import { EmailLoginFormConnected } from './__email-login-form';
Expand Down Expand Up @@ -155,7 +156,7 @@ export class AuthPanel extends Component<Props, State> {

return (
<div className="auth-panel__column">
You logged in as{' '}
<FormattedMessage id="authPanel.logged-as" defaultMessage="You logged in as" />{' '}
<Dropdown title={user.name} titleClass="auth-panel__user-dropdown-title" theme={theme}>
<DropdownItem separator={!isUserAnonymous}>
<div
Expand All @@ -176,7 +177,7 @@ export class AuthPanel extends Component<Props, State> {
)}
</Dropdown>{' '}
<Button kind="link" theme={theme} onClick={onSignOut}>
Logout?
<FormattedMessage id="authPanel.logout" defaultMessage="Logout?" />
</Button>
</div>
);
Expand Down Expand Up @@ -263,7 +264,7 @@ export class AuthPanel extends Component<Props, State> {

return (
<div className="auth-panel__column">
{'Login: '}
<FormattedMessage id="authPanel.login" defaultMessage="Login:" />{' '}
{!isAboveThreshold &&
sortedProviders.map((provider, i) => {
const comma = i === 0 ? '' : i === sortedProviders.length - 1 ? ' or ' : ', ';
Expand Down
18 changes: 10 additions & 8 deletions frontend/app/components/comment-form/comment-form.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
/** @jsx createElement */
import { createElement, Component, createRef, Fragment } from 'preact';
import { FormattedMessage } from 'react-intl';
import b, { Mix } from 'bem-react-helper';

import { User, Theme, Image, ApiError } from '@app/common/types';
Expand Down Expand Up @@ -51,12 +52,6 @@ interface State {
buttonText: null | string;
}

const Labels = {
main: 'Send',
edit: 'Save',
reply: 'Reply',
};

const ImageMimeRegex = /image\//i;

export class CommentForm extends Component<Props, State> {
Expand Down Expand Up @@ -343,6 +338,11 @@ export class CommentForm extends Component<Props, State> {
render(props: Props, { isDisabled, isErrorShown, errorMessage, preview, maxLength, text, buttonText }: State) {
const charactersLeft = maxLength - text.length;
errorMessage = props.errorMessage || errorMessage;
const Labels = {
main: <FormattedMessage id="commentForm.send" defaultMessage="Send" />,
edit: <FormattedMessage id="commentForm.save" defaultMessage="Save" />,
reply: <FormattedMessage id="commentForm.replay" defaultMessage="Replay" />,
};
const label = buttonText || Labels[props.mode || 'main'];

return (
Expand Down Expand Up @@ -406,7 +406,7 @@ export class CommentForm extends Component<Props, State> {
disabled={isDisabled}
onClick={this.getPreview}
>
Preview
<FormattedMessage id="commentForm.preview" defaultMessage="Preview" />
</Button>
)}
<Button kind="primary" size="large" mix="comment-form__button" type="submit" disabled={isDisabled}>
Expand Down Expand Up @@ -439,7 +439,9 @@ export class CommentForm extends Component<Props, State> {
!!preview && (
<div className="comment-form__preview-wrapper">
<div
className={b('comment-form__preview', { mix: b('raw-content', {}, { theme: props.theme }) })}
className={b('comment-form__preview', {
mix: b('raw-content', {}, { theme: props.theme }),
})}
dangerouslySetInnerHTML={{ __html: preview }}
/>
</div>
Expand Down
31 changes: 28 additions & 3 deletions frontend/app/components/comment/comment.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,23 @@ import { Props, Comment } from './comment';
import { User, Comment as CommentType, PostInfo } from '@app/common/types';
import { sleep } from '@app/utils/sleep';
import { StaticStore } from '@app/common/static_store';
import { defineMessages, useIntl, FormattedMessage } from 'react-intl';

jest.mock('react-intl');
(defineMessages as any).mockImplementation(() => '');
(useIntl as any).mockImplementation(() => ({ formatDate: jest.fn(), formatTime: jest.fn() }));
(FormattedMessage as any).mockImplementation(({ defaultMessage }: { defaultMessage: any }) => {
return defaultMessage;
});

const intl: any = {
formatMessage() {
return '';
},
};

const DefaultProps: Partial<Props> = {
intl,
post_info: {
read_only: false,
} as PostInfo,
Expand Down Expand Up @@ -214,7 +229,12 @@ describe('<Comment />', () => {
expect(controls.length).toBe(5);
expect(controls.at(0).text()).toEqual('Copy');
expect(controls.at(1).text()).toEqual('Pin');
expect(controls.at(2).text()).toEqual('Hide');
expect(
controls
.at(2)
.find('FormattedMessage')
.props()
).toEqual(expect.objectContaining({ defaultMessage: 'Hide' }));
expect(controls.at(3).getDOMNode().childNodes[0].textContent).toEqual('Block');
expect(controls.at(4).text()).toEqual('Delete');
});
Expand All @@ -226,7 +246,12 @@ describe('<Comment />', () => {

const controls = element.find('.comment__controls').children();
expect(controls.length).toBe(1);
expect(controls.at(0).text()).toEqual('Hide');
expect(
controls
.at(0)
.find('FormattedMessage')
.props()
).toEqual(expect.objectContaining({ defaultMessage: 'Hide' }));
});

it('verification badge clickable for admin', () => {
Expand Down Expand Up @@ -254,7 +279,7 @@ describe('<Comment />', () => {

it('should be editable', () => {
const initTime = new Date().toString();
const changedTime = new Date(Date.now() + 10 * 1000).toString();
const changedTime = new Date(Date.now() + 10 * 1000);
const props: Partial<Props> = {
...DefaultProps,
user: DefaultProps.user as User,
Expand Down
Loading

0 comments on commit b8e2520

Please sign in to comment.