diff --git a/.editorconfig b/.editorconfig index d8da069..c27b003 100644 --- a/.editorconfig +++ b/.editorconfig @@ -9,4 +9,4 @@ end_of_line = lf insert_final_newline = true trim_trailing_whitespace = true indent_style = space -indent_size = 2 +indent_size = 4 diff --git a/.eslintrc.js b/.eslintrc.js index ac7f796..030e3fd 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -8,7 +8,6 @@ module.exports = { extends: [ 'eslint:recommended', 'plugin:jsdoc/recommended', - 'plugin:@typescript-eslint/eslint-recommended', 'plugin:@typescript-eslint/recommended', 'prettier/@typescript-eslint', 'plugin:prettier/recommended', diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 13e6041..b3d7b85 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -64,6 +64,8 @@ Yarn: policy: pull-push variables: CYPRESS_INSTALL_BINARY: 0 # Don't install binary to increase caching performance between jobs + before_script: + - apk add git script: - yarn install --frozen-lockfile @@ -74,6 +76,8 @@ Yarn E2E: - .yarnE2ECache cache: policy: pull-push + before_script: + - apk add git script: - yarn install --frozen-lockfile only: @@ -114,6 +118,24 @@ Jest: script: - yarn test +Crowdin: + stage: test + image: $NODE_IMAGE + needs: + - Yarn + extends: + - .yarnCache + script: + - yarn i18n:extract + - yarn i18n:push + - yarn i18n:pull + artifacts: + name: "i18n for $CI_COMMIT_BRANCH-$CI_COMMIT_SHORT_SHA" + paths: + - packages/app/i18n/*.json + - packages/app/i18n/index.js + expire_in: 1 week + Cypress: stage: test image: $NODE_E2E_IMAGE @@ -151,6 +173,7 @@ Build: - Lint - TypeScript - Jest + - Crowdin extends: - .yarnCache before_script: @@ -158,7 +181,6 @@ Build: script: - yarn build # Remove unneeded files - - rm -rf build/messages - rm -rf build/*.css.map # Move all source maps to its own directory - mkdir -p source-maps @@ -179,6 +201,7 @@ Storybook: image: $NODE_IMAGE needs: - Yarn + - Crowdin extends: - .yarnCache script: diff --git a/@types/cwordin-api.d.ts b/@types/cwordin-api.d.ts deleted file mode 100644 index 9ca2289..0000000 --- a/@types/cwordin-api.d.ts +++ /dev/null @@ -1,98 +0,0 @@ -declare module 'crowdin-api' { - export interface ProjectInfoFile { - node_type: 'file'; - id: number; - name: string; - created: string; - last_updated: string; - last_accessed: string; - last_revision: string; - } - - export interface ProjectInfoDirectory { - node_type: 'directory'; - id: number; - name: string; - files: Array; - } - - export interface ProjectInfoResponse { - details: { - source_language: { - name: string; - code: string; - }; - name: string; - identifier: string; - created: string; - description: string; - join_policy: string; - last_build: string | null; - last_activity: string; - participants_count: string; // it's number, but string in the response - logo_url: string | null; - total_strings_count: string; // it's number, but string in the response - total_words_count: string; // it's number, but string in the response - duplicate_strings_count: number; - duplicate_words_count: number; - invite_url: { - translator: string; - proofreader: string; - }; - }; - languages: Array<{ - name: string; // English language name - code: string; - can_translate: 0 | 1; - can_approve: 0 | 1; - }>; - files: Array; - } - - export interface LanguageStatusNode { - node_type: 'directory' | 'file'; - id: number; - name: string; - phrases: number; - translated: number; - approved: number; - words: number; - words_translated: number; - words_approved: number; - files: Array; - } - - export interface LanguageStatusResponse { - files: Array; - } - - type FilesList = Record; - - export default class CrowdinApi { - constructor(params: { apiKey: string; projectName: string; baseUrl?: string }); - projectInfo(): Promise; - languageStatus(language: string): Promise; - exportFile( - file: string, - language: string, - params?: { - branch?: string; - format?: 'xliff'; - export_translated_only?: boolean; - export_approved_only?: boolean; - }, - ): Promise; // TODO: not sure about Promise return type - updateFile( - files: FilesList, - params: { - titles?: Record; - export_patterns?: Record; - new_names?: Record; - first_line_contains_header?: string; - scheme?: string; - update_option?: 'update_as_unapproved' | 'update_without_changes'; - branch?: string; - }, - ): Promise; - } -} diff --git a/@types/multi-progress.d.ts b/@types/multi-progress.d.ts deleted file mode 100644 index 67df384..0000000 --- a/@types/multi-progress.d.ts +++ /dev/null @@ -1,10 +0,0 @@ -declare module 'multi-progress' { - export default class MultiProgress { - constructor(stream?: string); - newBar(schema: string, options: ProgressBar.ProgressBarOptions): ProgressBar; - terminate(): void; - move(index: number): void; - tick(index: number, value?: number, options?: any): void; - update(index: number, value?: number, options?: any): void; - } -} diff --git a/@types/prompt.d.ts b/@types/prompt.d.ts deleted file mode 100644 index 14a2e70..0000000 --- a/@types/prompt.d.ts +++ /dev/null @@ -1,56 +0,0 @@ -// Type definitions for Prompt.js 1.0.0 -// Project: https://github.com/flatiron/prompt - -declare module 'prompt' { - type PropertiesType = Array | prompt.PromptSchema | Array; - - namespace prompt { - interface PromptSchema { - properties: PromptProperties; - } - - interface PromptProperties { - [propName: string]: PromptPropertyOptions; - } - - interface PromptPropertyOptions { - name?: string; - pattern?: RegExp; - message?: string; - required?: boolean; - hidden?: boolean; - description?: string; - type?: string; - default?: string; - before?: (value: any) => any; - conform?: (result: any) => boolean; - } - - export function start(): void; - - export function get( - properties: T, - callback: ( - err: Error, - result: T extends Array - ? Record - : T extends PromptSchema - ? Record - : T extends Array - ? Record - : never, - ) => void, - ): void; - - export function addProperties(obj: any, properties: PropertiesType, callback: (err: Error) => void): void; - - export function history(propertyName: string): any; - - export let override: any; - export let colors: boolean; - export let message: string; - export let delimiter: string; - } - - export = prompt; -} diff --git a/@types/react-intl.d.ts b/@types/react-intl.d.ts new file mode 100644 index 0000000..b7d88fb --- /dev/null +++ b/@types/react-intl.d.ts @@ -0,0 +1,24 @@ +import { PrimitiveType } from 'intl-messageformat'; +import { Formatters, IntlConfig, IntlFormatters, MessageDescriptor } from 'react-intl'; + +declare module 'react-intl' { + // Babel's plugin react-intl-auto always converts passed strings messages into the MessageDescriptor + export declare function defineMessages>( + msgs: U, + ): Record; + + // Babel's plugin react-intl-auto allows to call the formatMessage function with "key" field to automatically + // generate message's id + export interface KeyBasedMessageDescriptor { + key: string; + defaultMessage: string; + } + + export declare interface IntlShape extends IntlConfig, IntlFormatters { + formatters: Formatters; + formatMessage( + descriptor: MessageDescriptor | KeyBasedMessageDescriptor, + values?: Record, + ): string; + } +} diff --git a/babel.config.js b/babel.config.js index 028a217..f458c89 100644 --- a/babel.config.js +++ b/babel.config.js @@ -1,40 +1,64 @@ /* eslint-env node */ -module.exports = { - presets: [ - [ - '@babel/preset-typescript', - { - allowDeclareFields: true, - }, - ], - '@babel/preset-react', - '@babel/preset-env', - ], - plugins: [ - '@babel/plugin-syntax-dynamic-import', - '@babel/plugin-proposal-function-bind', - '@babel/plugin-proposal-class-properties', - [ - '@babel/plugin-transform-runtime', - { - corejs: 3, - }, - ], - ['react-intl', { messagesDir: './build/messages/' }], - ], - env: { - webpack: { - plugins: ['react-hot-loader/babel'], - presets: [ - [ - '@babel/preset-env', - { - modules: false, - useBuiltIns: 'usage', // or "entry" - corejs: 3, - }, - ], +// @ts-nocheck +module.exports = function (api) { + const env = api.env(); + api.cache(true); + + return { + presets: [ + [ + '@babel/preset-typescript', + { + allowDeclareFields: true, + }, ], + '@babel/preset-react', + [ + '@babel/preset-env', + { + ignoreBrowserslistConfig: true, + targets: { + node: true, + }, + modules: 'commonjs', + }, + ], + ], + plugins: [ + '@babel/plugin-syntax-dynamic-import', + '@babel/plugin-proposal-function-bind', + '@babel/plugin-proposal-class-properties', + [ + '@babel/plugin-transform-runtime', + { + corejs: 3, + }, + ], + [ + 'react-intl-auto', + { + removePrefix: 'packages.app', + messagesDir: './build/messages/', + useKey: true, + removeDefaultMessage: env === 'production', + }, + ], + ], + env: { + webpack: { + plugins: ['react-hot-loader/babel'], + presets: [ + [ + '@babel/preset-env', + { + ignoreBrowserslistConfig: false, + modules: false, + useBuiltIns: 'usage', // or "entry" + corejs: 3, + }, + ], + ], + }, }, - }, + }; }; diff --git a/config.js b/config.js index fe65a79..ff7ed81 100644 --- a/config.js +++ b/config.js @@ -1,4 +1,5 @@ /* eslint-env node */ +/* eslint-disable @typescript-eslint/no-var-requires */ require('dotenv').config(); @@ -9,5 +10,12 @@ module.exports = { apiHost: env.API_HOST || 'https://dev.account.ely.by', ga: env.GA_ID && { id: env.GA_ID }, sentryDSN: env.SENTRY_DSN, - crowdinApiKey: env.CROWDIN_API_KEY, + crowdin: { + apiKey: env.CROWDIN_API_KEY, + projectId: 350687, + filePath: 'accounts/site.json', + sourceLang: 'en', + basePath: `${__dirname}/packages/app/i18n`, + minApproved: 80, // Minimal ready percent before translation can be published + }, }; diff --git a/package.json b/package.json index 4d16a5c..8889f95 100644 --- a/package.json +++ b/package.json @@ -35,7 +35,7 @@ "ts:check": "tsc", "ci:check": "yarn lint:check && yarn ts:check && yarn test", "analyze": "yarn run clean && yarn run build:webpack --analyze", - "i18n:collect": "babel-node --extensions \".ts\" ./packages/scripts/i18n-collect.ts", + "i18n:extract": "extract-messages -l=en -o ./packages/app/i18n --flat true 'packages/app/!(node_modules)/**/*.[tj]s?(x)'", "i18n:push": "babel-node --extensions \".ts\" ./packages/scripts/i18n-crowdin.ts push", "i18n:pull": "babel-node --extensions \".ts\" ./packages/scripts/i18n-crowdin.ts pull", "build": "yarn run clean && yarn run build:webpack", @@ -73,7 +73,6 @@ "\\.(css|less|scss)$": "identity-obj-proxy" }, "transform": { - "\\.intl\\.json$": "/jest/__mocks__/intlMock.js", "^.+\\.[tj]sx?$": "babel-jest" } }, @@ -117,7 +116,7 @@ "@typescript-eslint/eslint-plugin": "^3.0.0", "@typescript-eslint/parser": "^3.0.0", "babel-loader": "^8.0.0", - "babel-plugin-react-intl": "^7.5.10", + "babel-plugin-react-intl-auto": "https://github.com/elyby/babel-plugin-react-intl-auto.git#build", "core-js": "3.6.5", "csp-webpack-plugin": "^2.0.2", "css-loader": "^3.5.3", @@ -130,6 +129,7 @@ "eslint-plugin-prettier": "^3.1.3", "eslint-plugin-react": "^7.20.0", "exports-loader": "^0.7.0", + "extract-react-intl-messages": "^4.1.1", "file-loader": "^6.0.0", "html-loader": "^1.1.0", "html-webpack-plugin": "^4.3.0", diff --git a/packages/app/components/accounts/AccountSwitcher.intl.json b/packages/app/components/accounts/AccountSwitcher.intl.json deleted file mode 100644 index e2ceb9c..0000000 --- a/packages/app/components/accounts/AccountSwitcher.intl.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "addAccount": "Add account", - "goToEly": "Go to Ely.by profile", - "logout": "Log out" -} diff --git a/packages/app/components/accounts/AccountSwitcher.tsx b/packages/app/components/accounts/AccountSwitcher.tsx index 60113fd..c39d712 100644 --- a/packages/app/components/accounts/AccountSwitcher.tsx +++ b/packages/app/components/accounts/AccountSwitcher.tsx @@ -11,7 +11,6 @@ import { getActiveAccount, Account } from 'app/components/accounts/reducer'; import { RootState } from 'app/reducers'; import styles from './accountSwitcher.scss'; -import messages from './AccountSwitcher.intl.json'; interface Props { switchAccount: (account: Account) => Promise; @@ -70,7 +69,7 @@ export class AccountSwitcher extends React.Component {
@@ -80,7 +79,7 @@ export class AccountSwitcher extends React.Component { onClick={this.onRemove(activeAccount)} href="#" > - +
@@ -127,7 +126,7 @@ export class AccountSwitcher extends React.Component { small className={styles.addAccount} label={ - + {(message) => (
diff --git a/packages/app/components/accounts/actions.test.ts b/packages/app/components/accounts/actions.test.ts index 89e606a..d19115b 100644 --- a/packages/app/components/accounts/actions.test.ts +++ b/packages/app/components/accounts/actions.test.ts @@ -12,6 +12,23 @@ import { Dispatch, RootState } from 'app/reducers'; import { Account } from './reducer'; +jest.mock('app/i18n', () => ({ + en: { + code: 'en', + name: 'English', + englishName: 'English', + progress: 100, + isReleased: true, + }, + be: { + code: 'be', + name: 'Беларуская', + englishName: 'Belarusian', + progress: 97, + isReleased: true, + }, +})); + const token = 'eyJhbGciOiJIUzI1NiJ9.eyJzdWIiOiJlbHl8MSJ9.pRJ7vakt2eIscjqwG__KhSxKb3qwGsdBBeDbBffJs_I'; const legacyToken = 'eyJhbGciOiJIUzI1NiJ9.eyJqdGkiOjF9.cRF-sQNrwWQ94xCb3vWioVdjxAZeefEE7GMGwh7708o'; diff --git a/packages/app/components/auth/acceptRules/AcceptRules.intl.json b/packages/app/components/auth/acceptRules/AcceptRules.intl.json deleted file mode 100644 index e2a0943..0000000 --- a/packages/app/components/auth/acceptRules/AcceptRules.intl.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "title": "User Agreement", - "accept": "Accept", - "declineAndLogout": "Decline and logout", - "description1": "We have updated our {link}.", - "termsOfService": "terms of service", - "description2": "In order to continue using {name} service, you need to accept them." -} diff --git a/packages/app/components/auth/acceptRules/AcceptRules.ts b/packages/app/components/auth/acceptRules/AcceptRules.ts index a8cd377..3d02480 100644 --- a/packages/app/components/auth/acceptRules/AcceptRules.ts +++ b/packages/app/components/auth/acceptRules/AcceptRules.ts @@ -1,6 +1,12 @@ +import { defineMessages } from 'react-intl'; import factory from '../factory'; import Body from './AcceptRulesBody'; -import messages from './AcceptRules.intl.json'; + +const messages = defineMessages({ + title: 'User Agreement', + accept: 'Accept', + declineAndLogout: 'Decline and logout', +}); export default factory({ title: messages.title, diff --git a/packages/app/components/auth/acceptRules/AcceptRulesBody.tsx b/packages/app/components/auth/acceptRules/AcceptRulesBody.tsx index c690545..371c7fc 100644 --- a/packages/app/components/auth/acceptRules/AcceptRulesBody.tsx +++ b/packages/app/components/auth/acceptRules/AcceptRulesBody.tsx @@ -5,10 +5,9 @@ import { Link } from 'react-router-dom'; import icons from 'app/components/ui/icons.scss'; import BaseAuthBody from 'app/components/auth/BaseAuthBody'; -import appInfo from 'app/components/auth/appInfo/AppInfo.intl.json'; +import appName from 'app/components/auth/appInfo/appName.intl'; import styles from './acceptRules.scss'; -import messages from './AcceptRules.intl.json'; export default class AcceptRulesBody extends BaseAuthBody { static displayName = 'AcceptRulesBody'; @@ -25,20 +24,22 @@ export default class AcceptRulesBody extends BaseAuthBody {

- + ), }} />
, + name: , }} />

diff --git a/packages/app/components/auth/activation/Activation.intl.json b/packages/app/components/auth/activation/Activation.intl.json deleted file mode 100644 index 0c5efd3..0000000 --- a/packages/app/components/auth/activation/Activation.intl.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "accountActivationTitle": "Account activation", - "activationMailWasSent": "Please check {email} for the message with further instructions", - "activationMailWasSentNoEmail": "Please check your E‑mail for the message with further instructions", - "confirmEmail": "Confirm E‑mail", - "didNotReceivedEmail": "Did not received E‑mail?", - "enterTheCode": "Enter the code from E‑mail here" -} diff --git a/packages/app/components/auth/activation/Activation.ts b/packages/app/components/auth/activation/Activation.ts index 9c2e737..48caced 100644 --- a/packages/app/components/auth/activation/Activation.ts +++ b/packages/app/components/auth/activation/Activation.ts @@ -1,7 +1,13 @@ +import { defineMessages } from 'react-intl'; import factory from '../factory'; -import messages from './Activation.intl.json'; import Body from './ActivationBody'; +const messages = defineMessages({ + accountActivationTitle: 'Account activation', + confirmEmail: 'Confirm E‑mail', + didNotReceivedEmail: 'Did not received E‑mail?', +}); + export default factory({ title: messages.accountActivationTitle, body: Body, diff --git a/packages/app/components/auth/activation/ActivationBody.tsx b/packages/app/components/auth/activation/ActivationBody.tsx index a27f4cb..dc4d4ba 100644 --- a/packages/app/components/auth/activation/ActivationBody.tsx +++ b/packages/app/components/auth/activation/ActivationBody.tsx @@ -1,12 +1,15 @@ import React from 'react'; -import { FormattedMessage as Message } from 'react-intl'; +import { defineMessages, FormattedMessage as Message } from 'react-intl'; import { Input } from 'app/components/ui/form'; import BaseAuthBody from 'app/components/auth/BaseAuthBody'; import styles from './activation.scss'; -import messages from './Activation.intl.json'; + +const messages = defineMessages({ + enterTheCode: 'Enter the code from E‑mail here', +}); export default class ActivationBody extends BaseAuthBody { static displayName = 'ActivationBody'; @@ -28,13 +31,17 @@ export default class ActivationBody extends BaseAuthBody {
{email ? ( {email}, }} /> ) : ( - + )}
diff --git a/packages/app/components/auth/appInfo/AppInfo.intl.json b/packages/app/components/auth/appInfo/AppInfo.intl.json deleted file mode 100644 index 1f25d49..0000000 --- a/packages/app/components/auth/appInfo/AppInfo.intl.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "appName": "Ely Accounts", - "goToAuth": "Go to auth", - "appDescription": "You are on the Ely.by authorization service, that allows you to safely perform any operations on your account. This single entry point for websites and desktop software, including game launchers.", - "useItYourself": "Visit our {link}, to learn how to use this service in you projects.", - "documentation": "documentation" -} diff --git a/packages/app/components/auth/appInfo/AppInfo.tsx b/packages/app/components/auth/appInfo/AppInfo.tsx index 004f819..06f9d33 100644 --- a/packages/app/components/auth/appInfo/AppInfo.tsx +++ b/packages/app/components/auth/appInfo/AppInfo.tsx @@ -1,10 +1,14 @@ import React from 'react'; -import { FormattedMessage as Message } from 'react-intl'; +import { defineMessages, FormattedMessage as Message } from 'react-intl'; import { Button } from 'app/components/ui/form'; import { FooterMenu } from 'app/components/footerMenu'; +import appName from './appName.intl'; import styles from './appInfo.scss'; -import messages from './AppInfo.intl.json'; + +const messages = defineMessages({ + goToAuth: 'Go to auth', +}); export default class AppInfo extends React.Component<{ name?: string; @@ -17,7 +21,7 @@ export default class AppInfo extends React.Component<{ return (
-

{name ? name : }

+

{name ? name : }

{description ? ( @@ -25,15 +29,19 @@ export default class AppInfo extends React.Component<{ ) : (

- +

- + ), }} diff --git a/packages/app/components/auth/appInfo/appName.intl.ts b/packages/app/components/auth/appInfo/appName.intl.ts new file mode 100644 index 0000000..b7a5cb9 --- /dev/null +++ b/packages/app/components/auth/appInfo/appName.intl.ts @@ -0,0 +1,7 @@ +import { defineMessages } from 'react-intl'; + +const { appName } = defineMessages({ + appName: 'Ely Accounts', +}); + +export default appName; diff --git a/packages/app/components/auth/chooseAccount/ChooseAccount.intl.json b/packages/app/components/auth/chooseAccount/ChooseAccount.intl.json deleted file mode 100644 index 1770688..0000000 --- a/packages/app/components/auth/chooseAccount/ChooseAccount.intl.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "chooseAccountTitle": "Choose an account", - "addAccount": "Log into another account", - "logoutAll": "Log out from all accounts", - "pleaseChooseAccount": "Please select an account you're willing to use", - "pleaseChooseAccountForApp": "Please select an account that you want to use to authorize {appName}" -} diff --git a/packages/app/components/auth/chooseAccount/ChooseAccount.ts b/packages/app/components/auth/chooseAccount/ChooseAccount.ts index cfa04ee..60e9272 100644 --- a/packages/app/components/auth/chooseAccount/ChooseAccount.ts +++ b/packages/app/components/auth/chooseAccount/ChooseAccount.ts @@ -1,7 +1,13 @@ +import { defineMessages } from 'react-intl'; import factory from '../factory'; -import messages from './ChooseAccount.intl.json'; import Body from './ChooseAccountBody'; +const messages = defineMessages({ + chooseAccountTitle: 'Choose an account', + addAccount: 'Log into another account', + logoutAll: 'Log out from all accounts', +}); + export default factory({ title: messages.chooseAccountTitle, body: Body, diff --git a/packages/app/components/auth/chooseAccount/ChooseAccountBody.tsx b/packages/app/components/auth/chooseAccount/ChooseAccountBody.tsx index 906e457..2361a9e 100644 --- a/packages/app/components/auth/chooseAccount/ChooseAccountBody.tsx +++ b/packages/app/components/auth/chooseAccount/ChooseAccountBody.tsx @@ -7,7 +7,6 @@ import { AccountSwitcher } from 'app/components/accounts'; import { Account } from 'app/components/accounts/reducer'; import styles from './chooseAccount.scss'; -import messages from './ChooseAccount.intl.json'; export default class ChooseAccountBody extends BaseAuthBody { static displayName = 'ChooseAccountBody'; @@ -23,14 +22,18 @@ export default class ChooseAccountBody extends BaseAuthBody {

{client ? ( {client.name}, }} /> ) : (
- +
)}
diff --git a/packages/app/components/auth/finish/Finish.intl.json b/packages/app/components/auth/finish/Finish.intl.json deleted file mode 100644 index a1aba85..0000000 --- a/packages/app/components/auth/finish/Finish.intl.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "authForAppSuccessful": "Authorization for {appName} was successfully completed", - "authForAppFailed": "Authorization for {appName} was failed", - "waitAppReaction": "Please, wait till your application response", - "passCodeToApp": "To complete authorization process, please, provide the following code to {appName}", - "copy": "Copy" -} diff --git a/packages/app/components/auth/finish/Finish.tsx b/packages/app/components/auth/finish/Finish.tsx index 19560a4..90078b4 100644 --- a/packages/app/components/auth/finish/Finish.tsx +++ b/packages/app/components/auth/finish/Finish.tsx @@ -1,14 +1,17 @@ import React, { MouseEventHandler } from 'react'; import { connect } from 'react-redux'; -import { FormattedMessage as Message } from 'react-intl'; +import { defineMessages, FormattedMessage as Message } from 'react-intl'; import { Helmet } from 'react-helmet-async'; import { Button } from 'app/components/ui/form'; import copy from 'app/services/copy'; import { RootState } from 'app/reducers'; -import messages from './Finish.intl.json'; import styles from './finish.scss'; +const messages = defineMessages({ + copy: 'Copy', +}); + interface Props { appName: string; code?: string; @@ -36,7 +39,8 @@ class Finish extends React.Component {
{appName}, }} @@ -45,7 +49,11 @@ class Finish extends React.Component { {displayCode ? (
- +
{code}
@@ -54,7 +62,10 @@ class Finish extends React.Component {
) : (
- +
)}
@@ -63,14 +74,18 @@ class Finish extends React.Component {
{appName}, }} />
- +
)} diff --git a/packages/app/components/auth/forgotPassword/ForgotPassword.intl.json b/packages/app/components/auth/forgotPassword/ForgotPassword.intl.json deleted file mode 100644 index 04eca46..0000000 --- a/packages/app/components/auth/forgotPassword/ForgotPassword.intl.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "title": "Forgot password", - "sendMail": "Send mail", - "specifyEmail": "Specify the registration E‑mail address or last used username for your account and we will send an E‑mail with instructions for further password recovery.", - "pleasePressButton": "Please press the button bellow to get an E‑mail with password recovery code.", - "alreadyHaveCode": "Already have a code" -} diff --git a/packages/app/components/auth/forgotPassword/ForgotPassword.ts b/packages/app/components/auth/forgotPassword/ForgotPassword.ts index 79641fb..c4013a6 100644 --- a/packages/app/components/auth/forgotPassword/ForgotPassword.ts +++ b/packages/app/components/auth/forgotPassword/ForgotPassword.ts @@ -1,7 +1,13 @@ +import { defineMessages } from 'react-intl'; import factory from '../factory'; -import messages from './ForgotPassword.intl.json'; import Body from './ForgotPasswordBody'; +const messages = defineMessages({ + title: 'Forgot password', + sendMail: 'Send mail', + alreadyHaveCode: 'Already have a code', +}); + export default factory({ title: messages.title, body: Body, diff --git a/packages/app/components/auth/forgotPassword/ForgotPasswordBody.tsx b/packages/app/components/auth/forgotPassword/ForgotPasswordBody.tsx index 2dfc790..0a9745e 100644 --- a/packages/app/components/auth/forgotPassword/ForgotPasswordBody.tsx +++ b/packages/app/components/auth/forgotPassword/ForgotPasswordBody.tsx @@ -1,6 +1,6 @@ import React from 'react'; -import { FormattedMessage as Message } from 'react-intl'; +import { defineMessages, FormattedMessage as Message } from 'react-intl'; import { Input, Captcha } from 'app/components/ui/form'; import { getLogin } from 'app/components/auth/reducer'; @@ -8,7 +8,10 @@ import { PanelIcon } from 'app/components/ui/Panel'; import BaseAuthBody from 'app/components/auth/BaseAuthBody'; import styles from './forgotPassword.scss'; -import messages from './ForgotPassword.intl.json'; + +const messages = defineMessages({ + emailOrUsername: 'E‑mail or username', +}); export default class ForgotPasswordBody extends BaseAuthBody { static displayName = 'ForgotPasswordBody'; @@ -36,14 +39,17 @@ export default class ForgotPasswordBody extends BaseAuthBody { {isLoginEditShown ? (

- +

@@ -54,7 +60,10 @@ export default class ForgotPasswordBody extends BaseAuthBody {

- +

)} diff --git a/packages/app/components/auth/login/Login.intl.json b/packages/app/components/auth/login/Login.intl.json deleted file mode 100644 index 93f82eb..0000000 --- a/packages/app/components/auth/login/Login.intl.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "createNewAccount": "Create new account", - "loginTitle": "Sign in", - "emailOrUsername": "E‑mail or username", - "next": "Next" -} diff --git a/packages/app/components/auth/login/Login.ts b/packages/app/components/auth/login/Login.ts index 86a9575..e4134e2 100644 --- a/packages/app/components/auth/login/Login.ts +++ b/packages/app/components/auth/login/Login.ts @@ -1,6 +1,12 @@ +import { defineMessages } from 'react-intl'; import factory from '../factory'; import Body from './LoginBody'; -import messages from './Login.intl.json'; + +const messages = defineMessages({ + createNewAccount: 'Create new account', + loginTitle: 'Sign in', + next: 'Next', +}); export default factory({ title: messages.loginTitle, diff --git a/packages/app/components/auth/login/LoginBody.tsx b/packages/app/components/auth/login/LoginBody.tsx index 421efe5..c74302b 100644 --- a/packages/app/components/auth/login/LoginBody.tsx +++ b/packages/app/components/auth/login/LoginBody.tsx @@ -1,10 +1,13 @@ import React from 'react'; +import { defineMessages } from 'react-intl'; import { Input } from 'app/components/ui/form'; import BaseAuthBody from 'app/components/auth/BaseAuthBody'; import { User } from 'app/components/user/reducer'; -import messages from './Login.intl.json'; +const messages = defineMessages({ + emailOrUsername: 'E‑mail or username', +}); export default class LoginBody extends BaseAuthBody { static displayName = 'LoginBody'; diff --git a/packages/app/components/auth/mfa/Mfa.intl.json b/packages/app/components/auth/mfa/Mfa.intl.json deleted file mode 100644 index b19e7e8..0000000 --- a/packages/app/components/auth/mfa/Mfa.intl.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "enterTotp": "Enter code", - "description": "In order to sign in this account, you need to enter a one-time password from mobile application" -} diff --git a/packages/app/components/auth/mfa/Mfa.tsx b/packages/app/components/auth/mfa/Mfa.tsx index ffda313..350b5d6 100644 --- a/packages/app/components/auth/mfa/Mfa.tsx +++ b/packages/app/components/auth/mfa/Mfa.tsx @@ -1,13 +1,17 @@ +import { defineMessages } from 'react-intl'; import factory from '../factory'; import Body from './MfaBody'; -import messages from './Mfa.intl.json'; -import passwordMessages from '../password/Password.intl.json'; + +const messages = defineMessages({ + enterTotp: 'Enter code', + signInButton: 'Sign in', +}); export default factory({ title: messages.enterTotp, body: Body, footer: { color: 'green', - label: passwordMessages.signInButton, + label: messages.signInButton, }, }); diff --git a/packages/app/components/auth/mfa/MfaBody.tsx b/packages/app/components/auth/mfa/MfaBody.tsx index 02ea0f3..cb29216 100644 --- a/packages/app/components/auth/mfa/MfaBody.tsx +++ b/packages/app/components/auth/mfa/MfaBody.tsx @@ -1,11 +1,14 @@ import React from 'react'; -import { FormattedMessage as Message } from 'react-intl'; +import { defineMessages, FormattedMessage as Message } from 'react-intl'; import { PanelIcon } from 'app/components/ui/Panel'; import { Input } from 'app/components/ui/form'; import BaseAuthBody from 'app/components/auth/BaseAuthBody'; import styles from './mfa.scss'; -import messages from './Mfa.intl.json'; + +const messages = defineMessages({ + enterTotp: 'Enter code', +}); export default class MfaBody extends BaseAuthBody { static panelId = 'mfa'; @@ -21,7 +24,10 @@ export default class MfaBody extends BaseAuthBody {

- +

: }
- +
{user.username}
- +
- +
    {scopes.map((scope) => { const key = `scope_${scope}`; - const message = messages[key]; + // @ts-ignore + const message = scopesMessages[key]; return (
  • diff --git a/packages/app/components/auth/recoverPassword/RecoverPassword.intl.json b/packages/app/components/auth/recoverPassword/RecoverPassword.intl.json deleted file mode 100644 index ce776a5..0000000 --- a/packages/app/components/auth/recoverPassword/RecoverPassword.intl.json +++ /dev/null @@ -1,12 +0,0 @@ -{ - "title": "Restore password", - "contactSupport": "Contact support", - "messageWasSent": "The recovery code was sent to your account E‑mail.", - "messageWasSentTo": "The recovery code was sent to your E‑mail {email}.", - "enterCodeBelow": "Please enter the code received into the field below:", - "enterNewPasswordBelow": "Enter and repeat new password below:", - "change": "Change password", - "newPassword": "Enter new password", - "newRePassword": "Repeat new password", - "enterTheCode": "Enter confirmation code" -} diff --git a/packages/app/components/auth/recoverPassword/RecoverPassword.ts b/packages/app/components/auth/recoverPassword/RecoverPassword.ts index fb54884..aeaaffa 100644 --- a/packages/app/components/auth/recoverPassword/RecoverPassword.ts +++ b/packages/app/components/auth/recoverPassword/RecoverPassword.ts @@ -1,7 +1,13 @@ +import { defineMessages } from 'react-intl'; import factory from '../factory'; -import messages from './RecoverPassword.intl.json'; import Body from './RecoverPasswordBody'; +const messages = defineMessages({ + title: 'Restore password', + contactSupport: 'Contact support', + change: 'Change password', +}); + export default factory({ title: messages.title, body: Body, diff --git a/packages/app/components/auth/recoverPassword/RecoverPasswordBody.tsx b/packages/app/components/auth/recoverPassword/RecoverPasswordBody.tsx index e75a338..0389d95 100644 --- a/packages/app/components/auth/recoverPassword/RecoverPasswordBody.tsx +++ b/packages/app/components/auth/recoverPassword/RecoverPasswordBody.tsx @@ -1,15 +1,20 @@ import React from 'react'; -import { FormattedMessage as Message } from 'react-intl'; +import { defineMessages, FormattedMessage as Message } from 'react-intl'; import { Input } from 'app/components/ui/form'; import BaseAuthBody from 'app/components/auth/BaseAuthBody'; import styles from './recoverPassword.scss'; -import messages from './RecoverPassword.intl.json'; // TODO: activation code field may be decoupled into common component and reused here and in activation panel +const placeholders = defineMessages({ + newPassword: 'Enter new password', + newRePassword: 'Repeat new password', + enterTheCode: 'Enter confirmation code', +}); + export default class RecoverPasswordBody extends BaseAuthBody { static displayName = 'RecoverPasswordBody'; static panelId = 'recoverPassword'; @@ -28,15 +33,22 @@ export default class RecoverPasswordBody extends BaseAuthBody {

    {user.maskedEmail ? ( {user.maskedEmail}, }} /> ) : ( - + )}{' '} - +

    - +

); diff --git a/packages/app/components/auth/register/Register.intl.json b/packages/app/components/auth/register/Register.intl.json deleted file mode 100644 index 39e167b..0000000 --- a/packages/app/components/auth/register/Register.intl.json +++ /dev/null @@ -1,10 +0,0 @@ -{ - "registerTitle": "Sign Up", - "yourNickname": "Your nickname", - "yourEmail": "Your E‑mail", - "accountPassword": "Account password", - "repeatPassword": "Repeat password", - "signUpButton": "Register", - "acceptRules": "I agree with {link}", - "termsOfService": "terms of service" -} diff --git a/packages/app/components/auth/register/Register.ts b/packages/app/components/auth/register/Register.ts index 6536bd1..74c66e9 100644 --- a/packages/app/components/auth/register/Register.ts +++ b/packages/app/components/auth/register/Register.ts @@ -1,9 +1,14 @@ +import { defineMessages } from 'react-intl'; import factory from '../factory'; -import activationMessages from '../activation/Activation.intl.json'; -import forgotPasswordMessages from '../forgotPassword/ForgotPassword.intl.json'; -import messages from './Register.intl.json'; import Body from './RegisterBody'; +const messages = defineMessages({ + registerTitle: 'Sign Up', + signUpButton: 'Register', + didNotReceivedEmail: 'Did not received E‑mail?', + alreadyHaveCode: 'Already have a code', +}); + export default factory({ title: messages.registerTitle, body: Body, @@ -13,11 +18,11 @@ export default factory({ }, links: [ { - label: activationMessages.didNotReceivedEmail, + label: messages.didNotReceivedEmail, payload: { requestEmail: true }, }, { - label: forgotPasswordMessages.alreadyHaveCode, + label: messages.alreadyHaveCode, }, ], }); diff --git a/packages/app/components/auth/register/RegisterBody.tsx b/packages/app/components/auth/register/RegisterBody.tsx index 80aad09..7cc3a05 100644 --- a/packages/app/components/auth/register/RegisterBody.tsx +++ b/packages/app/components/auth/register/RegisterBody.tsx @@ -1,15 +1,20 @@ import React from 'react'; -import { FormattedMessage as Message } from 'react-intl'; +import { defineMessages, FormattedMessage as Message } from 'react-intl'; import { Link } from 'react-router-dom'; import { Input, Checkbox, Captcha } from 'app/components/ui/form'; import BaseAuthBody from 'app/components/auth/BaseAuthBody'; -import passwordMessages from '../password/Password.intl.json'; import styles from '../auth.scss'; -import messages from './Register.intl.json'; // TODO: password and username can be validate for length and sameness +const placeholders = defineMessages({ + yourNickname: 'Your nickname', + yourEmail: 'Your E‑mail', + accountPassword: 'Account password', + repeatPassword: 'Repeat password', +}); + export default class RegisterBody extends BaseAuthBody { static panelId = 'register'; @@ -26,7 +31,7 @@ export default class RegisterBody extends BaseAuthBody { color="blue" type="text" required - placeholder={messages.yourNickname} + placeholder={placeholders.yourNickname} /> @@ -65,11 +70,12 @@ export default class RegisterBody extends BaseAuthBody { required label={ - + ), }} diff --git a/packages/app/components/auth/resendActivation/ResendActivation.intl.json b/packages/app/components/auth/resendActivation/ResendActivation.intl.json deleted file mode 100644 index b2d7652..0000000 --- a/packages/app/components/auth/resendActivation/ResendActivation.intl.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "title": "Did not received an E‑mail", - "specifyYourEmail": "Please, enter an E‑mail you've registered with and we will send you new activation code", - "sendNewEmail": "Send new E‑mail" -} diff --git a/packages/app/components/auth/resendActivation/ResendActivation.ts b/packages/app/components/auth/resendActivation/ResendActivation.ts index d56a772..f283fa5 100644 --- a/packages/app/components/auth/resendActivation/ResendActivation.ts +++ b/packages/app/components/auth/resendActivation/ResendActivation.ts @@ -1,8 +1,13 @@ +import { defineMessages } from 'react-intl'; import factory from '../factory'; -import forgotPasswordMessages from '../forgotPassword/ForgotPassword.intl.json'; -import messages from './ResendActivation.intl.json'; import Body from './ResendActivationBody'; +const messages = defineMessages({ + title: 'Did not received an E‑mail', + sendNewEmail: 'Send new E‑mail', + alreadyHaveCode: 'Already have a code', +}); + export default factory({ title: messages.title, body: Body, @@ -11,6 +16,6 @@ export default factory({ label: messages.sendNewEmail, }, links: { - label: forgotPasswordMessages.alreadyHaveCode, + label: messages.alreadyHaveCode, }, }); diff --git a/packages/app/components/auth/resendActivation/ResendActivationBody.tsx b/packages/app/components/auth/resendActivation/ResendActivationBody.tsx index 9fd24cd..c468515 100644 --- a/packages/app/components/auth/resendActivation/ResendActivationBody.tsx +++ b/packages/app/components/auth/resendActivation/ResendActivationBody.tsx @@ -1,11 +1,13 @@ import React from 'react'; -import { FormattedMessage as Message } from 'react-intl'; +import { defineMessages, FormattedMessage as Message } from 'react-intl'; import { Input, Captcha } from 'app/components/ui/form'; import BaseAuthBody from '../BaseAuthBody'; -import registerMessages from '../register/Register.intl.json'; import styles from './resendActivation.scss'; -import messages from './ResendActivation.intl.json'; + +const placeholders = defineMessages({ + yourEmail: 'Your E‑mail', +}); export default class ResendActivation extends BaseAuthBody { static displayName = 'ResendActivation'; @@ -20,7 +22,10 @@ export default class ResendActivation extends BaseAuthBody { {this.renderErrors()}
- +
diff --git a/packages/app/components/contact/ContactForm.tsx b/packages/app/components/contact/ContactForm.tsx index 2bbe241..0fff30d 100644 --- a/packages/app/components/contact/ContactForm.tsx +++ b/packages/app/components/contact/ContactForm.tsx @@ -1,7 +1,7 @@ import React from 'react'; import { connect } from 'react-redux'; import clsx from 'clsx'; -import { FormattedMessage as Message } from 'react-intl'; +import { FormattedMessage as Message, defineMessages } from 'react-intl'; import { Input, TextArea, Button, Form, FormModel, Dropdown } from 'app/components/ui/form'; import feedback from 'app/services/api/feedback'; import icons from 'app/components/ui/icons.scss'; @@ -11,17 +11,27 @@ import logger from 'app/services/logger'; import { User } from 'app/components/user'; import styles from './contactForm.scss'; -import messages from './contactForm.intl.json'; const CONTACT_CATEGORIES = { // TODO: сюда позже проставить реальные id категорий с backend - 0: , - 1: , - 2: , - 3: , - 4: , + 0: , + 1: , + 2: , + 3: , + 4: , }; +const labels = defineMessages({ + subject: 'Subject', + email: 'E‑mail', + message: 'Message', + whichQuestion: 'What are you interested in?', + + send: 'Send', + + close: 'Close', +}); + export class ContactForm extends React.Component< { onClose: () => void; @@ -54,7 +64,7 @@ export class ContactForm extends React.Component<

- +

- +
- +
- +
@@ -115,7 +131,7 @@ export class ContactForm extends React.Component<