diff --git a/jest/setup-tests.js b/jest/setup-tests.js
index 406b4da..e45e5b1 100644
--- a/jest/setup-tests.js
+++ b/jest/setup-tests.js
@@ -4,5 +4,13 @@ import {
} from '@testing-library/jest-dom/matchers';
import 'src/icons/icons';
import 'src/utils/i18n';
+import 'whatwg-fetch';
expect.extend({ toHaveClass, toHaveAttribute });
+
+// mock fetch
+beforeAll(() => {
+ jest.spyOn(window, 'fetch');
+});
+afterEach(() => jest.clearAllMocks());
+afterAll(() => window.fetch.mockRestore());
diff --git a/package-lock.json b/package-lock.json
index a1f4d09..e482233 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -5195,6 +5195,16 @@
"integrity": "sha512-Phlt0plgpIIBOGTT/ehfFnbNlfsDEiqmzE2KRXoX1bLIlir4X/MR+zSyBEkL05ffWgnRSf/DXv+WrUAVr93/ow==",
"dev": true
},
+ "bindings": {
+ "version": "1.5.0",
+ "resolved": "https://registry.npmjs.org/bindings/-/bindings-1.5.0.tgz",
+ "integrity": "sha512-p2q/t/mhvuOj/UeLlV6566GD/guowlr0hHxClI0W9m7MWYkL1F0hLo+0Aexs9HSPCtR1SXQ0TD3MMKrXZajbiQ==",
+ "dev": true,
+ "optional": true,
+ "requires": {
+ "file-uri-to-path": "1.0.0"
+ }
+ },
"bl": {
"version": "4.0.2",
"resolved": "https://registry.npmjs.org/bl/-/bl-4.0.2.tgz",
@@ -8883,6 +8893,13 @@
}
}
},
+ "file-uri-to-path": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/file-uri-to-path/-/file-uri-to-path-1.0.0.tgz",
+ "integrity": "sha512-0Zt+s3L7Vf1biwWZ29aARiVYLx7iMGnEUl9x33fbB/j3jR81u/O2LbqK+Bm1CDSNDKVtJ/YjwY7TUd5SkeLQLw==",
+ "dev": true,
+ "optional": true
+ },
"filesize": {
"version": "3.6.1",
"resolved": "https://registry.npmjs.org/filesize/-/filesize-3.6.1.tgz",
@@ -9415,6 +9432,8 @@
"dev": true,
"optional": true,
"requires": {
+ "bindings": "^1.5.0",
+ "nan": "^2.12.1",
"node-pre-gyp": "*"
},
"dependencies": {
@@ -14923,6 +14942,13 @@
"integrity": "sha512-nnbWWOkoWyUsTjKrhgD0dcz22mdkSnpYqbEjIm2nhwhuxlSkpywJmBo8h0ZqJdkp73mb90SssHkN4rsRaBAfAA==",
"dev": true
},
+ "nan": {
+ "version": "2.14.1",
+ "resolved": "https://registry.npmjs.org/nan/-/nan-2.14.1.tgz",
+ "integrity": "sha512-isWHgVjnFjh2x2yuJ/tj3JbwoHu3UC2dX5G/88Cm24yB6YopVgxvBObDY7n5xW6ExmFhJpSEQqFPvq9zaXc8Jw==",
+ "dev": true,
+ "optional": true
+ },
"nanomatch": {
"version": "1.2.13",
"resolved": "https://registry.npmjs.org/nanomatch/-/nanomatch-1.2.13.tgz",
@@ -21853,6 +21879,8 @@
"dev": true,
"optional": true,
"requires": {
+ "bindings": "^1.5.0",
+ "nan": "^2.12.1",
"node-pre-gyp": "*"
},
"dependencies": {
@@ -23205,7 +23233,11 @@
"resolved": "https://registry.npmjs.org/fsevents/-/fsevents-1.2.13.tgz",
"integrity": "sha512-oWb1Z6mkHIskLzEJ/XWX0srkpkTQ7vaopMQkyaEIoq0fmtFVxOthb8cCxeT+p3ynTdkk/RZwbgG4brR5BeWECw==",
"dev": true,
- "optional": true
+ "optional": true,
+ "requires": {
+ "bindings": "^1.5.0",
+ "nan": "^2.12.1"
+ }
},
"glob-parent": {
"version": "3.1.0",
diff --git a/package.json b/package.json
index 4737e97..215e946 100644
--- a/package.json
+++ b/package.json
@@ -98,7 +98,8 @@
"webpack": "^4.43.0",
"webpack-bundle-analyzer": "^3.8.0",
"webpack-cli": "^3.3.11",
- "webpack-dev-server": "^3.11.0"
+ "webpack-dev-server": "^3.11.0",
+ "whatwg-fetch": "^3.0.0"
},
"dependencies": {
"@fortawesome/fontawesome-svg-core": "^1.2.28",
diff --git a/src/containers/app/app.js b/src/containers/app/app.js
index 4e8623c..d95d024 100644
--- a/src/containers/app/app.js
+++ b/src/containers/app/app.js
@@ -1,21 +1,17 @@
import { hot } from 'react-hot-loader/root';
import React, { Component } from 'react';
import { HashRouter as Router, Route, Switch } from 'react-router-dom';
+import PropTypes from 'prop-types';
import copy from 'copy-to-clipboard';
import { withTranslation } from 'react-i18next';
import { ToastContainer, Slide } from 'react-toastify';
-import {
- CARDS_DICTIONARIES,
- CARDS_TYPES,
- FIELD_SIZES,
- LANGUAGES,
- TEAMS,
-} from 'src/data/constants';
+import { CARDS_TYPES, FIELD_SIZES, LANGUAGES, TEAMS } from 'src/data/constants';
import * as Location from 'src/utils/location';
import * as FirebaseService from 'src/service';
import { getRemainingCardsCount } from 'src/utils/team-progress';
import { toast } from 'src/utils/toast';
import { Loader } from 'src/components/loader';
+import { getDefaultDictionary } from 'src/utils/data-provider';
import { Lobby } from '../lobby';
import { NotFound } from '../not-found';
import { ProtectedGame } from '../game';
@@ -29,7 +25,7 @@ class App extends Component {
settings: {
language: LANGUAGES['ru'],
fieldSize: FIELD_SIZES['5x5'],
- dictionary: CARDS_DICTIONARIES['GAGA'],
+ dictionaryFileName: '',
},
users: [],
cards: [],
@@ -46,24 +42,46 @@ class App extends Component {
isLoading: true,
};
this.sessionId = '';
- // eslint-disable-next-line react/prop-types
this.t = props.t;
+ this.i18n = props.i18n;
}
async componentDidMount() {
- await this.initialize();
+ await this.setDefaultDictionary();
+ await this.initializeSession();
}
- async initialize() {
+ async initializeSession() {
try {
await this.connectToSession();
this.addListeners();
} catch (error) {
- console.log(error.message);
- this.setState({ errorMessage: error.message });
+ this.setState({ isLoading: false }, () => {
+ toast.error(error.message);
+ });
}
}
+ setDefaultDictionary() {
+ return new Promise((resolve) => {
+ const dictionary = getDefaultDictionary(this.i18n.language);
+ if (!dictionary) {
+ this.setState({ isLoading: false }, () => {
+ toast.error(this.t('error.common'));
+ });
+ }
+ this.setState(
+ {
+ settings: {
+ ...this.state.settings,
+ dictionaryFileName: dictionary.fileName,
+ },
+ },
+ resolve,
+ );
+ });
+ }
+
async connectToSession() {
let sessionId = Location.getGameSessionId(window.location.href);
let userId;
@@ -119,10 +137,10 @@ class App extends Component {
this.setState({ users, currentUser });
}
- saveSettings({ language, dictionary, fieldSize }) {
+ saveSettings({ language, dictionaryFileName, fieldSize }) {
FirebaseService.saveSettings(this.sessionId, {
language,
- dictionary,
+ dictionaryFileName,
fieldSize,
});
}
@@ -255,6 +273,11 @@ class App extends Component {
}
}
+App.propTypes = {
+ t: PropTypes.func.isRequired,
+ i18n: PropTypes.object.isRequired,
+};
+
const HotApp = hot(withTranslation()(App));
export { HotApp as App };
diff --git a/src/containers/lobby/lobby.js b/src/containers/lobby/lobby.js
index 43ca7a0..5a4bd24 100644
--- a/src/containers/lobby/lobby.js
+++ b/src/containers/lobby/lobby.js
@@ -28,26 +28,33 @@ function Lobby({
onClickShare,
onClickGenerateCards,
}) {
- const { t } = useTranslation();
+ const { t, i18n } = useTranslation();
const history = useHistory();
const onChangeFieldSize = (e) => {
onChangeSettings({ ...settings, fieldSize: e.target.value });
};
const onChangeDictionary = (e) => {
- onChangeSettings({ ...settings, dictionary: e.target.value });
+ onChangeSettings({ ...settings, dictionaryFileName: e.target.value });
};
const fieldSizes = Object.entries(FIELD_SIZES).map(([label, value]) => (
));
- const dictionaries = Object.entries(CARDS_DICTIONARIES).map(
- ([label, value]) => (
-
- ),
+
+ const dictionaries = CARDS_DICTIONARIES.reduce(
+ (acc, { name, fileName, language }) => {
+ if (i18n.language === language) {
+ acc.push(
+ ,
+ );
+ }
+ return acc;
+ },
+ [],
);
let color = 'default';
@@ -127,7 +134,10 @@ function Lobby({
-
@@ -177,7 +187,7 @@ Lobby.propTypes = {
settings: PropTypes.shape({
language: PropTypes.string.isRequired,
fieldSize: PropTypes.string.isRequired,
- dictionary: PropTypes.string.isRequired,
+ dictionaryFileName: PropTypes.string.isRequired,
}).isRequired,
users: PropTypes.arrayOf(
PropTypes.shape({
diff --git a/src/containers/lobby/lobby.stories.js b/src/containers/lobby/lobby.stories.js
index 87d9c05..8a0ba68 100644
--- a/src/containers/lobby/lobby.stories.js
+++ b/src/containers/lobby/lobby.stories.js
@@ -32,7 +32,7 @@ export const Common = () => {
const settings = {
language: LANGUAGES['ru'],
fieldSize: FIELD_SIZES['5x5'],
- dictionary: CARDS_DICTIONARIES['GAGA'],
+ dictionaryFileName: CARDS_DICTIONARIES[0],
};
const captains = {
[TEAMS['blue']]: '2',
diff --git a/src/data/constants.js b/src/data/constants.js
index adc456c..9ff37b9 100644
--- a/src/data/constants.js
+++ b/src/data/constants.js
@@ -8,9 +8,28 @@ export const FIELD_SIZES = {
'5x6': '5x6',
};
-export const CARDS_DICTIONARIES = {
- GAGA: 'gaga',
-};
+export const CARDS_DICTIONARIES = [
+ {
+ name: 'Стандартный (400+ слов)',
+ fileName: 'default-ru.json',
+ language: 'ru',
+ },
+ {
+ name: 'Расширенный (1000+ слов)',
+ fileName: 'extended-ru.json',
+ language: 'ru',
+ },
+ {
+ name: 'IT: frontend (200+ терминов)',
+ fileName: 'frontend-ru.json',
+ language: 'ru',
+ },
+ {
+ name: 'IT: frontend (200+ words)',
+ fileName: 'frontend-en.json',
+ language: 'en',
+ },
+];
export const CARDS_TYPES = {
agent: 'agent',
diff --git a/src/data/dictionaries.json b/src/data/dictionaries.json
deleted file mode 100644
index b2cd519..0000000
--- a/src/data/dictionaries.json
+++ /dev/null
@@ -1,404 +0,0 @@
-{
- "gaga": [
- "Австралия",
- "автомат",
- "агент",
- "адвокат",
- "Азия",
- "акт",
- "альбом",
- "Альпы",
- "Америка",
- "амфибия",
- "ангел",
- "Англия",
- "Антарктида",
- "аппарат",
- "Атлантида",
- "Африка",
- "ацтек",
- "бабочка",
- "база",
- "Байкал",
- "банк",
- "баня",
- "бар",
- "барьер",
- "бассейн",
- "батарея",
- "башня",
- "берёза",
- "Берлин",
- "Бермуды",
- "билет",
- "биржа",
- "блин",
- "блок",
- "боевик",
- "бокс",
- "болезнь",
- "больница",
- "бомба",
- "боров",
- "борт",
- "ботинок",
- "бочка",
- "брак",
- "бревно",
- "бумага",
- "бутылка",
- "бык",
- "вагон",
- "вал",
- "ведьма",
- "век",
- "венец",
- "вертолёт",
- "верфь",
- "вес",
- "ветер",
- "взгляд",
- "вид",
- "вилка",
- "вирус",
- "вода",
- "водолаз",
- "вождь",
- "воздух",
- "война",
- "волна",
- "вор",
- "время",
- "высота",
- "газ",
- "галоп",
- "гвоздь",
- "гений",
- "Германия",
- "гигант",
- "глаз",
- "Голливуд",
- "голова",
- "горло",
- "горн",
- "гранат",
- "гребень",
- "Греция",
- "гриф",
- "груша",
- "дама",
- "декрет",
- "день",
- "десна",
- "динозавр",
- "диск",
- "доктор",
- "дракон",
- "дробь",
- "дума",
- "дух",
- "дыра",
- "дятел",
- "Европа",
- "Египет",
- "единорог",
- "ёрш",
- "жизнь",
- "жила",
- "жук",
- "журавль",
- "залог",
- "замок",
- "заноза",
- "запад",
- "запах",
- "заяц",
- "звезда",
- "зебра",
- "земля",
- "знак",
- "золото",
- "зона",
- "зуб",
- "игла",
- "игра",
- "икра",
- "Индия",
- "институт",
- "кабинет",
- "кавалер",
- "кадр",
- "казино",
- "камень",
- "камера",
- "канал",
- "караул",
- "карлик",
- "карта",
- "каша",
- "кенгуру",
- "кентавр",
- "кетчуп",
- "киви",
- "кисть",
- "кит",
- "Китай",
- "клетка",
- "ключ",
- "кокетка",
- "кол",
- "колода",
- "колонна",
- "кольцо",
- "команда",
- "конёк",
- "контрабандист",
- "концерт",
- "кора",
- "корабль",
- "королева",
- "король",
- "корона",
- "коса",
- "кость",
- "косяк",
- "кошка",
- "край",
- "кран",
- "крест",
- "кролик",
- "крошка",
- "круг",
- "крыло",
- "кулак",
- "курс",
- "лад",
- "лазер",
- "лама",
- "ласка",
- "лев",
- "лёд",
- "лейка",
- "лес",
- "лимузин",
- "линия",
- "липа",
- "лист",
- "лицо",
- "ложе",
- "Лондон",
- "лошадь",
- "лук",
- "луна",
- "луч",
- "масло",
- "масса",
- "мат",
- "машина",
- "мёд",
- "медведь",
- "Мексика",
- "мелочь",
- "место",
- "механизм",
- "микроскоп",
- "миллионер",
- "мир",
- "морковь",
- "мороженое",
- "Москва",
- "мост",
- "мотив",
- "мушка",
- "мышь",
- "налёт",
- "наряд",
- "небоскрёб",
- "ниндзя",
- "нож",
- "номер",
- "норка",
- "нота",
- "ночь",
- "НьюЙорк",
- "няня",
- "область",
- "облом",
- "образ",
- "образование",
- "обрез",
- "овсянка",
- "огонь",
- "Олимп",
- "опера",
- "операция",
- "орган",
- "орёл",
- "осьминог",
- "отель",
- "падение",
- "палата",
- "палец",
- "палочка",
- "панель",
- "пара",
- "парашют",
- "парк",
- "партия",
- "пассаж",
- "паук",
- "пачка",
- "Пекин",
- "перевод",
- "перемена",
- "перо",
- "перчатка",
- "пилот",
- "пингвин",
- "пирамида",
- "пират",
- "пистолет",
- "плата",
- "платье",
- "площадь",
- "пляж",
- "побег",
- "повар",
- "подкова",
- "подъём",
- "покров",
- "пол",
- "поле",
- "полис",
- "полиция",
- "помёт",
- "порода",
- "посольство",
- "поток",
- "почка",
- "пояс",
- "право",
- "предложение",
- "предприниматель",
- "прибор",
- "привод",
- "призрак",
- "принцесса",
- "пришелец",
- "пробка",
- "проводник",
- "проказа",
- "прокат",
- "проспект",
- "профиль",
- "путь",
- "Пушкин",
- "развод",
- "разворот",
- "рак",
- "раковина",
- "раствор",
- "рейд",
- "Рим",
- "робот",
- "рог",
- "род",
- "рок",
- "рубашка",
- "рукав",
- "рулетка",
- "рыба",
- "рысь",
- "рыцарь",
- "салют",
- "сантехник",
- "Сатурн",
- "свет",
- "свидетель",
- "секрет",
- "секция",
- "сердце",
- "сеть",
- "сила",
- "скат",
- "смерть",
- "снаряд",
- "снег",
- "снеговик",
- "собака",
- "совет",
- "солдат",
- "соль",
- "состав",
- "спутник",
- "среда",
- "ссылка",
- "стадион",
- "стан",
- "станок",
- "ствол",
- "стекло",
- "стена",
- "стойка",
- "стол",
- "стопа",
- "стрела",
- "строй",
- "струна",
- "стул",
- "ступень",
- "судьба",
- "супергерой",
- "такса",
- "танец",
- "тарелка",
- "театр",
- "телескоп",
- "течение",
- "титан",
- "Токио",
- "точка",
- "трава",
- "треугольник",
- "труба",
- "туба",
- "тур",
- "ударник",
- "удел",
- "узел",
- "урал",
- "урна",
- "утка",
- "утконос",
- "учёный",
- "учитель",
- "факел",
- "фаланга",
- "фига",
- "флейта",
- "фокус",
- "форма",
- "Франция",
- "хвост",
- "хлопок",
- "центр",
- "церковь",
- "частица",
- "червь",
- "шар",
- "шоколад",
- "шпагат",
- "шпион",
- "штат",
- "шуба",
- "экран",
- "эльф",
- "эфир",
- "Юпитер",
- "яблоко",
- "яд",
- "язык",
- "якорь",
- "ясли"
- ]
-}
diff --git a/src/data/dictionaries/default-ru.json b/src/data/dictionaries/default-ru.json
new file mode 100644
index 0000000..cb360fb
--- /dev/null
+++ b/src/data/dictionaries/default-ru.json
@@ -0,0 +1,402 @@
+[
+ "Австралия",
+ "автомат",
+ "агент",
+ "адвокат",
+ "Азия",
+ "акт",
+ "альбом",
+ "Альпы",
+ "Америка",
+ "амфибия",
+ "ангел",
+ "Англия",
+ "Антарктида",
+ "аппарат",
+ "Атлантида",
+ "Африка",
+ "ацтек",
+ "бабочка",
+ "база",
+ "Байкал",
+ "банк",
+ "баня",
+ "бар",
+ "барьер",
+ "бассейн",
+ "батарея",
+ "башня",
+ "берёза",
+ "Берлин",
+ "Бермуды",
+ "билет",
+ "биржа",
+ "блин",
+ "блок",
+ "боевик",
+ "бокс",
+ "болезнь",
+ "больница",
+ "бомба",
+ "боров",
+ "борт",
+ "ботинок",
+ "бочка",
+ "брак",
+ "бревно",
+ "бумага",
+ "бутылка",
+ "бык",
+ "вагон",
+ "вал",
+ "ведьма",
+ "век",
+ "венец",
+ "вертолёт",
+ "верфь",
+ "вес",
+ "ветер",
+ "взгляд",
+ "вид",
+ "вилка",
+ "вирус",
+ "вода",
+ "водолаз",
+ "вождь",
+ "воздух",
+ "война",
+ "волна",
+ "вор",
+ "время",
+ "высота",
+ "газ",
+ "галоп",
+ "гвоздь",
+ "гений",
+ "Германия",
+ "гигант",
+ "глаз",
+ "Голливуд",
+ "голова",
+ "горло",
+ "горн",
+ "гранат",
+ "гребень",
+ "Греция",
+ "гриф",
+ "груша",
+ "дама",
+ "декрет",
+ "день",
+ "десна",
+ "динозавр",
+ "диск",
+ "доктор",
+ "дракон",
+ "дробь",
+ "дума",
+ "дух",
+ "дыра",
+ "дятел",
+ "Европа",
+ "Египет",
+ "единорог",
+ "ёрш",
+ "жизнь",
+ "жила",
+ "жук",
+ "журавль",
+ "залог",
+ "замок",
+ "заноза",
+ "запад",
+ "запах",
+ "заяц",
+ "звезда",
+ "зебра",
+ "земля",
+ "знак",
+ "золото",
+ "зона",
+ "зуб",
+ "игла",
+ "игра",
+ "икра",
+ "Индия",
+ "институт",
+ "кабинет",
+ "кавалер",
+ "кадр",
+ "казино",
+ "камень",
+ "камера",
+ "канал",
+ "караул",
+ "карлик",
+ "карта",
+ "каша",
+ "кенгуру",
+ "кентавр",
+ "кетчуп",
+ "киви",
+ "кисть",
+ "кит",
+ "Китай",
+ "клетка",
+ "ключ",
+ "кокетка",
+ "кол",
+ "колода",
+ "колонна",
+ "кольцо",
+ "команда",
+ "конёк",
+ "контрабандист",
+ "концерт",
+ "кора",
+ "корабль",
+ "королева",
+ "король",
+ "корона",
+ "коса",
+ "кость",
+ "косяк",
+ "кошка",
+ "край",
+ "кран",
+ "крест",
+ "кролик",
+ "крошка",
+ "круг",
+ "крыло",
+ "кулак",
+ "курс",
+ "лад",
+ "лазер",
+ "лама",
+ "ласка",
+ "лев",
+ "лёд",
+ "лейка",
+ "лес",
+ "лимузин",
+ "линия",
+ "липа",
+ "лист",
+ "лицо",
+ "ложе",
+ "Лондон",
+ "лошадь",
+ "лук",
+ "луна",
+ "луч",
+ "масло",
+ "масса",
+ "мат",
+ "машина",
+ "мёд",
+ "медведь",
+ "Мексика",
+ "мелочь",
+ "место",
+ "механизм",
+ "микроскоп",
+ "миллионер",
+ "мир",
+ "морковь",
+ "мороженое",
+ "Москва",
+ "мост",
+ "мотив",
+ "мушка",
+ "мышь",
+ "налёт",
+ "наряд",
+ "небоскрёб",
+ "ниндзя",
+ "нож",
+ "номер",
+ "норка",
+ "нота",
+ "ночь",
+ "НьюЙорк",
+ "няня",
+ "область",
+ "облом",
+ "образ",
+ "образование",
+ "обрез",
+ "овсянка",
+ "огонь",
+ "Олимп",
+ "опера",
+ "операция",
+ "орган",
+ "орёл",
+ "осьминог",
+ "отель",
+ "падение",
+ "палата",
+ "палец",
+ "палочка",
+ "панель",
+ "пара",
+ "парашют",
+ "парк",
+ "партия",
+ "пассаж",
+ "паук",
+ "пачка",
+ "Пекин",
+ "перевод",
+ "перемена",
+ "перо",
+ "перчатка",
+ "пилот",
+ "пингвин",
+ "пирамида",
+ "пират",
+ "пистолет",
+ "плата",
+ "платье",
+ "площадь",
+ "пляж",
+ "побег",
+ "повар",
+ "подкова",
+ "подъём",
+ "покров",
+ "пол",
+ "поле",
+ "полис",
+ "полиция",
+ "помёт",
+ "порода",
+ "посольство",
+ "поток",
+ "почка",
+ "пояс",
+ "право",
+ "предложение",
+ "предприниматель",
+ "прибор",
+ "привод",
+ "призрак",
+ "принцесса",
+ "пришелец",
+ "пробка",
+ "проводник",
+ "проказа",
+ "прокат",
+ "проспект",
+ "профиль",
+ "путь",
+ "Пушкин",
+ "развод",
+ "разворот",
+ "рак",
+ "раковина",
+ "раствор",
+ "рейд",
+ "Рим",
+ "робот",
+ "рог",
+ "род",
+ "рок",
+ "рубашка",
+ "рукав",
+ "рулетка",
+ "рыба",
+ "рысь",
+ "рыцарь",
+ "салют",
+ "сантехник",
+ "Сатурн",
+ "свет",
+ "свидетель",
+ "секрет",
+ "секция",
+ "сердце",
+ "сеть",
+ "сила",
+ "скат",
+ "смерть",
+ "снаряд",
+ "снег",
+ "снеговик",
+ "собака",
+ "совет",
+ "солдат",
+ "соль",
+ "состав",
+ "спутник",
+ "среда",
+ "ссылка",
+ "стадион",
+ "стан",
+ "станок",
+ "ствол",
+ "стекло",
+ "стена",
+ "стойка",
+ "стол",
+ "стопа",
+ "стрела",
+ "строй",
+ "струна",
+ "стул",
+ "ступень",
+ "судьба",
+ "супергерой",
+ "такса",
+ "танец",
+ "тарелка",
+ "театр",
+ "телескоп",
+ "течение",
+ "титан",
+ "Токио",
+ "точка",
+ "трава",
+ "треугольник",
+ "труба",
+ "туба",
+ "тур",
+ "ударник",
+ "удел",
+ "узел",
+ "урал",
+ "урна",
+ "утка",
+ "утконос",
+ "учёный",
+ "учитель",
+ "факел",
+ "фаланга",
+ "фига",
+ "флейта",
+ "фокус",
+ "форма",
+ "Франция",
+ "хвост",
+ "хлопок",
+ "центр",
+ "церковь",
+ "частица",
+ "червь",
+ "шар",
+ "шоколад",
+ "шпагат",
+ "шпион",
+ "штат",
+ "шуба",
+ "экран",
+ "эльф",
+ "эфир",
+ "Юпитер",
+ "яблоко",
+ "яд",
+ "язык",
+ "якорь",
+ "ясли"
+]
\ No newline at end of file
diff --git a/src/data/dictionaries/extended-ru.json b/src/data/dictionaries/extended-ru.json
new file mode 100644
index 0000000..e0ca4d5
--- /dev/null
+++ b/src/data/dictionaries/extended-ru.json
@@ -0,0 +1,1002 @@
+[
+ "август",
+ "автобус",
+ "автомат",
+ "автомобиль",
+ "автор",
+ "администрация",
+ "адрес",
+ "академия",
+ "акт",
+ "актер",
+ "активность",
+ "акция",
+ "американец",
+ "анализ",
+ "аппарат",
+ "апрель",
+ "армия",
+ "артист",
+ "атмосфера",
+ "баба",
+ "бабушка",
+ "база",
+ "банк",
+ "беда",
+ "безопасность",
+ "берег",
+ "беседа",
+ "библиотека",
+ "бизнес",
+ "билет",
+ "блок",
+ "бог",
+ "бой",
+ "бок",
+ "болезнь",
+ "боль",
+ "больница",
+ "больной",
+ "большинство",
+ "борьба",
+ "брак",
+ "брат",
+ "будущее",
+ "буква",
+ "бумага",
+ "бутылка",
+ "бюджет",
+ "вагон",
+ "вариант",
+ "век",
+ "величина",
+ "вера",
+ "версия",
+ "вершина",
+ "вес",
+ "весна",
+ "ветер",
+ "вечер",
+ "вещество",
+ "вещь",
+ "взаимодействие",
+ "взгляд",
+ "взрыв",
+ "вид",
+ "вина",
+ "вино",
+ "вирус",
+ "вкус",
+ "владелец",
+ "власть",
+ "влияние",
+ "внимание",
+ "вода",
+ "водитель",
+ "водка",
+ "возвращение",
+ "воздействие",
+ "воздух",
+ "возможность",
+ "возраст",
+ "война",
+ "войско",
+ "волна",
+ "волос",
+ "воля",
+ "вопрос",
+ "ворота",
+ "воспитание",
+ "воспоминание",
+ "восток",
+ "восторг",
+ "впечатление",
+ "враг",
+ "врач",
+ "время",
+ "встреча",
+ "вход",
+ "выбор",
+ "выборы",
+ "вывод",
+ "выполнение",
+ "выпуск",
+ "выражение",
+ "высота",
+ "выставка",
+ "выступление",
+ "выход",
+ "газ",
+ "газета",
+ "генерал",
+ "герой",
+ "глава",
+ "главное",
+ "глаз",
+ "глубина",
+ "год",
+ "голова",
+ "голос",
+ "гора",
+ "горло",
+ "город",
+ "господин",
+ "господь",
+ "гостиница",
+ "гость",
+ "государство",
+ "гражданин",
+ "граница",
+ "грех",
+ "грудь",
+ "группа",
+ "губа",
+ "губернатор",
+ "давление",
+ "дама",
+ "данные",
+ "дача",
+ "дверь",
+ "двигатель",
+ "движение",
+ "двор",
+ "дворец",
+ "девочка",
+ "девушка",
+ "дед",
+ "действие",
+ "действительность",
+ "декабрь",
+ "дело",
+ "день",
+ "деньги",
+ "депутат",
+ "деревня",
+ "дерево",
+ "десяток",
+ "деталь",
+ "детство",
+ "деятельность",
+ "диван",
+ "директор",
+ "длина",
+ "дно",
+ "добро",
+ "договор",
+ "дождь",
+ "доказательство",
+ "доклад",
+ "доктор",
+ "документ",
+ "долг",
+ "должность",
+ "доллар",
+ "доля",
+ "дом",
+ "дорога",
+ "доска",
+ "достижение",
+ "достоинство",
+ "доход",
+ "дочка",
+ "дочь",
+ "друг",
+ "дружба",
+ "дума",
+ "дурак",
+ "дух",
+ "душа",
+ "дым",
+ "дыхание",
+ "дядя",
+ "еврей",
+ "еда",
+ "единица",
+ "желание",
+ "жена",
+ "женщина",
+ "жертва",
+ "живот",
+ "животное",
+ "жизнь",
+ "жилье",
+ "житель",
+ "журнал",
+ "журналист",
+ "забота",
+ "зависимость",
+ "завод",
+ "задача",
+ "заказ",
+ "заключение",
+ "закон",
+ "законодательство",
+ "зал",
+ "заместитель",
+ "замок",
+ "занятие",
+ "запад",
+ "запас",
+ "запах",
+ "записка",
+ "запись",
+ "зарплата",
+ "заседание",
+ "затрата",
+ "защита",
+ "заявление",
+ "звезда",
+ "звонок",
+ "звук",
+ "здание",
+ "здоровье",
+ "земля",
+ "зеркало",
+ "зима",
+ "зло",
+ "знак",
+ "знакомый",
+ "знание",
+ "значение",
+ "золото",
+ "зона",
+ "зрение",
+ "зритель",
+ "зуб",
+ "игра",
+ "идея",
+ "известие",
+ "издание",
+ "изменение",
+ "изображение",
+ "изучение",
+ "имущество",
+ "имя",
+ "инженер",
+ "инициатива",
+ "институт",
+ "инструмент",
+ "интерес",
+ "информация",
+ "исключение",
+ "искусство",
+ "исполнение",
+ "использование",
+ "испытание",
+ "исследование",
+ "история",
+ "источник",
+ "итог",
+ "июль",
+ "июнь",
+ "кабинет",
+ "кадр",
+ "камень",
+ "камера",
+ "канал",
+ "кандидат",
+ "капитал",
+ "капитан",
+ "карман",
+ "карта",
+ "картина",
+ "категория",
+ "качество",
+ "квартира",
+ "километр",
+ "кино",
+ "класс",
+ "клетка",
+ "клиент",
+ "клуб",
+ "ключ",
+ "книга",
+ "книжка",
+ "князь",
+ "кодекс",
+ "кожа",
+ "колено",
+ "колесо",
+ "количество",
+ "коллега",
+ "коллектив",
+ "кольцо",
+ "команда",
+ "командир",
+ "комиссия",
+ "комитет",
+ "коммунист",
+ "комната",
+ "компания",
+ "комплекс",
+ "компьютер",
+ "конец",
+ "конкурс",
+ "конструкция",
+ "контакт",
+ "контроль",
+ "конференция",
+ "конфликт",
+ "концепция",
+ "концерт",
+ "корабль",
+ "корень",
+ "коридор",
+ "король",
+ "корпус",
+ "кость",
+ "костюм",
+ "кофе",
+ "край",
+ "краска",
+ "красота",
+ "кредит",
+ "кресло",
+ "крест",
+ "кризис",
+ "крик",
+ "кровать",
+ "кровь",
+ "круг",
+ "крыло",
+ "крыша",
+ "кулак",
+ "культура",
+ "курс",
+ "кусок",
+ "куст",
+ "кухня",
+ "лагерь",
+ "ладонь",
+ "лев",
+ "лед",
+ "лейтенант",
+ "лес",
+ "лестница",
+ "лето",
+ "лидер",
+ "линия",
+ "лист",
+ "литература",
+ "лицо",
+ "личность",
+ "лоб",
+ "лодка",
+ "лошадь",
+ "любовь",
+ "магазин",
+ "май",
+ "майор",
+ "мальчик",
+ "мальчишка",
+ "мама",
+ "март",
+ "масло",
+ "масса",
+ "мастер",
+ "масштаб",
+ "материал",
+ "мать",
+ "машина",
+ "мгновение",
+ "мера",
+ "мероприятие",
+ "место",
+ "месяц",
+ "металл",
+ "метод",
+ "метр",
+ "механизм",
+ "мечта",
+ "мешок",
+ "миг",
+ "милиция",
+ "миллион",
+ "министерство",
+ "министр",
+ "минута",
+ "мир",
+ "мнение",
+ "множество",
+ "модель",
+ "мозг",
+ "молодежь",
+ "молоко",
+ "момент",
+ "монастырь",
+ "море",
+ "москвич",
+ "мост",
+ "мощность",
+ "муж",
+ "мужик",
+ "мужчина",
+ "музей",
+ "музыка",
+ "мысль",
+ "мясо",
+ "наблюдение",
+ "набор",
+ "надежда",
+ "название",
+ "назначение",
+ "наличие",
+ "налог",
+ "направление",
+ "народ",
+ "нарушение",
+ "население",
+ "настроение",
+ "наука",
+ "начало",
+ "начальник",
+ "начальство",
+ "небо",
+ "неделя",
+ "недостаток",
+ "немец",
+ "необходимость",
+ "нефть",
+ "новость",
+ "нога",
+ "нож",
+ "номер",
+ "норма",
+ "нос",
+ "ночь",
+ "ноябрь",
+ "обед",
+ "обеспечение",
+ "область",
+ "оборона",
+ "оборудование",
+ "образ",
+ "образец",
+ "образование",
+ "обращение",
+ "обстановка",
+ "обстоятельство",
+ "обучение",
+ "общение",
+ "общество",
+ "объединение",
+ "объект",
+ "объем",
+ "объяснение",
+ "обязанность",
+ "обязательство",
+ "огонь",
+ "одежда",
+ "ожидание",
+ "озеро",
+ "окно",
+ "окончание",
+ "округ",
+ "октябрь",
+ "опасность",
+ "операция",
+ "описание",
+ "оплата",
+ "определение",
+ "опыт",
+ "орган",
+ "организация",
+ "организм",
+ "оружие",
+ "осень",
+ "основа",
+ "основание",
+ "основное",
+ "особенность",
+ "остаток",
+ "остров",
+ "ответ",
+ "ответственность",
+ "отдел",
+ "отделение",
+ "отдых",
+ "отец",
+ "отказ",
+ "открытие",
+ "отличие",
+ "отношение",
+ "отрасль",
+ "отсутствие",
+ "офицер",
+ "охрана",
+ "оценка",
+ "очередь",
+ "очки",
+ "ошибка",
+ "ощущение",
+ "пакет",
+ "палата",
+ "палец",
+ "памятник",
+ "память",
+ "папа",
+ "пара",
+ "параметр",
+ "парень",
+ "парк",
+ "партия",
+ "партнер",
+ "пенсия",
+ "перевод",
+ "переговоры",
+ "передача",
+ "переход",
+ "период",
+ "перспектива",
+ "песня",
+ "песок",
+ "печать",
+ "пиво",
+ "писатель",
+ "пистолет",
+ "письмо",
+ "план",
+ "плата",
+ "платье",
+ "плечо",
+ "площадка",
+ "площадь",
+ "победа",
+ "поведение",
+ "поверхность",
+ "повод",
+ "поворот",
+ "повышение",
+ "подарок",
+ "подготовка",
+ "поддержка",
+ "подразделение",
+ "подруга",
+ "подход",
+ "подъезд",
+ "поезд",
+ "поездка",
+ "позиция",
+ "поиск",
+ "показатель",
+ "покой",
+ "поколение",
+ "покупатель",
+ "пол",
+ "поле",
+ "полет",
+ "политика",
+ "полковник",
+ "половина",
+ "положение",
+ "полоса",
+ "получение",
+ "польза",
+ "помещение",
+ "помощник",
+ "помощь",
+ "понимание",
+ "понятие",
+ "попытка",
+ "пора",
+ "порог",
+ "портрет",
+ "порядок",
+ "поселок",
+ "последствие",
+ "пост",
+ "постановление",
+ "постель",
+ "потеря",
+ "поток",
+ "потолок",
+ "потребность",
+ "почва",
+ "поэзия",
+ "поэт",
+ "появление",
+ "правда",
+ "правило",
+ "правительство",
+ "право",
+ "праздник",
+ "практика",
+ "предел",
+ "предложение",
+ "предмет",
+ "предприятие",
+ "председатель",
+ "представитель",
+ "представление",
+ "президент",
+ "премия",
+ "препарат",
+ "преступление",
+ "прием",
+ "признак",
+ "признание",
+ "приказ",
+ "применение",
+ "пример",
+ "принцип",
+ "принятие",
+ "природа",
+ "присутствие",
+ "причина",
+ "приятель",
+ "проблема",
+ "проведение",
+ "проверка",
+ "программа",
+ "продажа",
+ "продукт",
+ "продукция",
+ "проект",
+ "произведение",
+ "производитель",
+ "производство",
+ "прокурор",
+ "промышленность",
+ "пространство",
+ "просьба",
+ "противник",
+ "профессия",
+ "профессор",
+ "процедура",
+ "процент",
+ "процесс",
+ "прошлое",
+ "психология",
+ "птица",
+ "публика",
+ "пункт",
+ "путь",
+ "пыль",
+ "пьеса",
+ "работа",
+ "работник",
+ "рабочий",
+ "радость",
+ "раз",
+ "развитие",
+ "разговор",
+ "размер",
+ "разница",
+ "разработка",
+ "разрешение",
+ "район",
+ "ракета",
+ "рамка",
+ "рассказ",
+ "рассмотрение",
+ "расстояние",
+ "растение",
+ "расход",
+ "расчет",
+ "реакция",
+ "реализация",
+ "реальность",
+ "ребенок",
+ "ребята",
+ "революция",
+ "регион",
+ "редактор",
+ "редакция",
+ "режим",
+ "режиссер",
+ "результат",
+ "река",
+ "реклама",
+ "ремонт",
+ "республика",
+ "ресторан",
+ "ресурс",
+ "реформа",
+ "речь",
+ "решение",
+ "риск",
+ "рисунок",
+ "род",
+ "родина",
+ "родитель",
+ "родственник",
+ "рождение",
+ "роль",
+ "роман",
+ "рост",
+ "рот",
+ "рубеж",
+ "рубль",
+ "рука",
+ "руководитель",
+ "руководство",
+ "русский",
+ "ручка",
+ "рыба",
+ "рынок",
+ "ряд",
+ "сад",
+ "самолет",
+ "сапог",
+ "сбор",
+ "сведение",
+ "свет",
+ "свидетель",
+ "свобода",
+ "свойство",
+ "связь",
+ "сделка",
+ "сезон",
+ "секретарь",
+ "секунда",
+ "село",
+ "семья",
+ "сентябрь",
+ "сердце",
+ "середина",
+ "серия",
+ "сестра",
+ "сеть",
+ "сигарета",
+ "сигнал",
+ "сила",
+ "система",
+ "ситуация",
+ "сказка",
+ "скорость",
+ "слава",
+ "след",
+ "следователь",
+ "следствие",
+ "слеза",
+ "СЛОВО",
+ "слово",
+ "слой",
+ "служба",
+ "слух",
+ "случай",
+ "смена",
+ "смерть",
+ "смех",
+ "смысл",
+ "снег",
+ "снижение",
+ "собака",
+ "собрание",
+ "собственность",
+ "событие",
+ "совесть",
+ "совет",
+ "соглашение",
+ "содержание",
+ "соединение",
+ "сожаление",
+ "создание",
+ "сознание",
+ "солдат",
+ "солнце",
+ "сомнение",
+ "сон",
+ "сообщение",
+ "соответствие",
+ "сосед",
+ "состав",
+ "состояние",
+ "сотня",
+ "сотрудник",
+ "сотрудничество",
+ "сочинение",
+ "союз",
+ "спектакль",
+ "специалист",
+ "спина",
+ "список",
+ "спор",
+ "спорт",
+ "способ",
+ "способность",
+ "сравнение",
+ "среда",
+ "средство",
+ "срок",
+ "ставка",
+ "стакан",
+ "станция",
+ "старик",
+ "старуха",
+ "статус",
+ "статья",
+ "стекло",
+ "стена",
+ "степень",
+ "стиль",
+ "стихи",
+ "стоимость",
+ "стол",
+ "столик",
+ "столица",
+ "сторона",
+ "страна",
+ "страница",
+ "страсть",
+ "страх",
+ "строительство",
+ "строй",
+ "строка",
+ "структура",
+ "студент",
+ "стул",
+ "субъект",
+ "суд",
+ "судьба",
+ "судья",
+ "сумка",
+ "сумма",
+ "сутки",
+ "суть",
+ "существо",
+ "существование",
+ "сущность",
+ "сфера",
+ "схема",
+ "сцена",
+ "счастье",
+ "счет",
+ "сын",
+ "сюжет",
+ "таблица",
+ "тайна",
+ "талант",
+ "танец",
+ "танк",
+ "творчество",
+ "театр",
+ "текст",
+ "телевизор",
+ "телефон",
+ "тело",
+ "тема",
+ "темнота",
+ "температура",
+ "тенденция",
+ "тень",
+ "теория",
+ "территория",
+ "тетя",
+ "техника",
+ "технология",
+ "течение",
+ "тип",
+ "тишина",
+ "товар",
+ "товарищ",
+ "толпа",
+ "том",
+ "тон",
+ "торговля",
+ "точка",
+ "трава",
+ "традиция",
+ "транспорт",
+ "требование",
+ "труба",
+ "трубка",
+ "труд",
+ "тысяча",
+ "тюрьма",
+ "убийство",
+ "увеличение",
+ "угол",
+ "угроза",
+ "удар",
+ "удивление",
+ "удовольствие",
+ "ужас",
+ "указание",
+ "улица",
+ "улыбка",
+ "ум",
+ "университет",
+ "управление",
+ "уровень",
+ "урок",
+ "усилие",
+ "условие",
+ "услуга",
+ "успех",
+ "установка",
+ "устройство",
+ "утро",
+ "ухо",
+ "уход",
+ "участие",
+ "участник",
+ "участок",
+ "ученик",
+ "ученый",
+ "учет",
+ "учитель",
+ "учреждение",
+ "факт",
+ "фактор",
+ "фамилия",
+ "февраль",
+ "федерация",
+ "фигура",
+ "философия",
+ "фильм",
+ "фирма",
+ "фон",
+ "фонд",
+ "форма",
+ "формирование",
+ "формула",
+ "фотография",
+ "фраза",
+ "фронт",
+ "функция",
+ "характер",
+ "характеристика",
+ "хвост",
+ "хлеб",
+ "ход",
+ "хозяин",
+ "хозяйка",
+ "хозяйство",
+ "храм",
+ "художник",
+ "царь",
+ "цвет",
+ "цветок",
+ "целое",
+ "цель",
+ "цена",
+ "ценность",
+ "центр",
+ "церковь",
+ "цифра",
+ "чай",
+ "час",
+ "частность",
+ "часть",
+ "часы",
+ "человек",
+ "человечество",
+ "черт",
+ "черта",
+ "честь",
+ "чиновник",
+ "число",
+ "читатель",
+ "член",
+ "чтение",
+ "чувство",
+ "чудо",
+ "шаг",
+ "шанс",
+ "шея",
+ "школа",
+ "штаб",
+ "штат",
+ "штука",
+ "шум",
+ "шутка",
+ "щека",
+ "экономика",
+ "экран",
+ "эксперимент",
+ "эксперт",
+ "эксплуатация",
+ "элемент",
+ "энергия",
+ "эпоха",
+ "этаж",
+ "этап",
+ "эффект",
+ "эффективность",
+ "яблоко",
+ "явление",
+ "язык",
+ "январь",
+ "ящик"
+]
\ No newline at end of file
diff --git a/src/data/dictionaries/frontend-en.json b/src/data/dictionaries/frontend-en.json
new file mode 100644
index 0000000..5088fff
--- /dev/null
+++ b/src/data/dictionaries/frontend-en.json
@@ -0,0 +1,214 @@
+[
+ "script",
+ "primitive",
+ "double",
+ "integer",
+ "number",
+ "string",
+ "null",
+ "undefined",
+ "Symbol",
+ "Boolean",
+ "object",
+ "array",
+ "RegExp",
+ "Date",
+ "Math",
+ "array",
+ "function",
+ "ECMAScript",
+ "console",
+ "const",
+ "let",
+ "var",
+ "window",
+ "globalThis",
+ "context",
+ "this",
+ ".call",
+ ".apply",
+ ".bind",
+ "prototype",
+ "class",
+ "constructor",
+ "arrow function",
+ "spread",
+ "rest",
+ "yield",
+ "generator",
+ "promise",
+ "setTimeout",
+ "setInterval",
+ "JSON",
+ "instanceof",
+ "typeof",
+ "new",
+ "arguments",
+ "use strict",
+ "Map",
+ "WeakMap",
+ "Set",
+ "WeakSet",
+ "Proxy",
+ "Infinity",
+ "NaN",
+ "closure",
+ "scope",
+ "hoisting",
+ "async/await",
+ "lifecycle",
+ "hooks",
+ "virtual dom",
+ "callback",
+ "interpolation",
+ "ref",
+ "shadow dom",
+ "web component",
+ ".prototype",
+ "__proto__",
+ "writable",
+ "configurable",
+ "enumerable",
+ "frozen",
+ "bootstrap",
+ "polyfill",
+ "history",
+ "hash",
+ "canvas",
+ "document",
+ "DOM",
+ "EventListener",
+ "EventTarget",
+ "node",
+ "element",
+ "WebRTC",
+ "WebStorage",
+ "WebWorkers",
+ "IndexedDB",
+ "lighthouse",
+ "debugger",
+ "devtools",
+ "BFF",
+ "SSR",
+ "CSR",
+ "Hydration",
+ "Webpack",
+ "Babel",
+ "css-modules",
+ "styled components",
+ "scoped css",
+ "Rollup",
+ "RequireJS",
+ "JQuery",
+ "React",
+ "Vue.js",
+ "Backbone.js",
+ "Ember.js",
+ "Parcel",
+ "CoffeScript",
+ "Postcss",
+ "Mocha",
+ "LiveScript",
+ "TypeScript",
+ "Flow",
+ "Hegel",
+ "Angular",
+ "Nuxt.js",
+ "Next.js",
+ "Nest.js",
+ "Gatsby",
+ "Express",
+ "Storybook",
+ "JSX",
+ "SpiderMonkey",
+ "V8",
+ "Firefox",
+ "Opera",
+ "Safari",
+ "Chrome",
+ "Node.js",
+ "Deno",
+ "GraalVM",
+ "XMLHttpRequest",
+ "ajax",
+ "fetch",
+ "HTTP",
+ "IE",
+ "WebSocket",
+ "Apollo",
+ "graphql",
+ "axios",
+ "superagent",
+ "POST",
+ "GET",
+ "url",
+ "Firebase",
+ "import",
+ "export",
+ "require",
+ "AMD",
+ "IIFE",
+ "UMD",
+ "Redux",
+ "Vuex",
+ "NgRx",
+ "state",
+ "flux",
+ "Jest",
+ "Sinon",
+ "Mocha",
+ "Chai",
+ "Cypress",
+ "Puppeteer",
+ "Selenium",
+ "Hermione",
+ "cookies",
+ "sessions",
+ "npm",
+ "yarn",
+ "node_modules",
+ "float",
+ "grid",
+ "flex",
+ "table",
+ "id",
+ "eslint",
+ "prettier",
+ "stylelint",
+ "husky",
+ "lint-staged",
+ "editorconfig",
+ "markup",
+ "link",
+ "site",
+ "image",
+ "text",
+ "tag",
+ "adaptivity",
+ "form",
+ "validation",
+ "browser",
+ "attribute",
+ "focus",
+ "environment",
+ "error",
+ "exception",
+ "comment",
+ "typing",
+ "component",
+ "server",
+ "interface",
+ "application",
+ "plugin",
+ "compilation",
+ "building",
+ "prop",
+ "template",
+ "page",
+ "api",
+ "update",
+ "model",
+ "view",
+ "controller",
+ "client"
+]
diff --git a/src/data/dictionaries/frontend-ru.json b/src/data/dictionaries/frontend-ru.json
new file mode 100644
index 0000000..4b9ae42
--- /dev/null
+++ b/src/data/dictionaries/frontend-ru.json
@@ -0,0 +1,213 @@
+[
+ "script",
+ "primitive",
+ "double",
+ "integer",
+ "number",
+ "string",
+ "null",
+ "undefined",
+ "Symbol",
+ "Boolean",
+ "object",
+ "array",
+ "RegExp",
+ "Date",
+ "Math",
+ "array",
+ "function",
+ "ECMAScript",
+ "console",
+ "const",
+ "let",
+ "var",
+ "window",
+ "globalThis",
+ "context",
+ "this",
+ ".call",
+ ".apply",
+ ".bind",
+ "prototype",
+ "class",
+ "constructor",
+ "arrow function",
+ "spread",
+ "rest",
+ "yield",
+ "generator",
+ "promise",
+ "setTimeout",
+ "setInterval",
+ "JSON",
+ "instanceof",
+ "typeof",
+ "new",
+ "arguments",
+ "use strict",
+ "Map",
+ "WeakMap",
+ "Set",
+ "WeakSet",
+ "Proxy",
+ "Infinity",
+ "NaN",
+ "closure",
+ "scope",
+ "hoisting",
+ "async/await",
+ "lifecycle",
+ "hooks",
+ "virtual dom",
+ "callback",
+ "interpolation",
+ "ref",
+ "shadow dom",
+ "web component",
+ ".prototype",
+ "__proto__",
+ "writable",
+ "configurable",
+ "enumerable",
+ "frozen",
+ "bootstrap",
+ "polyfill",
+ "history",
+ "hash",
+ "canvas",
+ "document",
+ "DOM",
+ "EventListener",
+ "EventTarget",
+ "node",
+ "element",
+ "WebRTC",
+ "WebStorage",
+ "WebWorkers",
+ "IndexedDB",
+ "lighthouse",
+ "debugger",
+ "devtools",
+ "BFF",
+ "SSR",
+ "CSR",
+ "Hydration",
+ "Webpack",
+ "Babel",
+ "css-modules",
+ "styled components",
+ "scoped css",
+ "Rollup",
+ "RequireJS",
+ "JQuery",
+ "React",
+ "Vue.js",
+ "Backbone.js",
+ "Ember.js",
+ "Parcel",
+ "CoffeScript",
+ "Postcss",
+ "Mocha",
+ "LiveScript",
+ "TypeScript",
+ "Flow",
+ "Hegel",
+ "Angular",
+ "Nuxt.js",
+ "Next.js",
+ "Nest.js",
+ "Gatsby",
+ "Express",
+ "Storybook",
+ "JSX",
+ "SpiderMonkey",
+ "V8",
+ "Firefox",
+ "Opera",
+ "Safari",
+ "Chrome",
+ "Node.js",
+ "Deno",
+ "GraalVM",
+ "XMLHttpRequest",
+ "ajax",
+ "fetch",
+ "HTTP",
+ "IE",
+ "WebSocket",
+ "Apollo",
+ "graphql",
+ "axios",
+ "superagent",
+ "POST",
+ "GET",
+ "url",
+ "Firebase",
+ "import",
+ "export",
+ "require",
+ "AMD",
+ "IIFE",
+ "UMD",
+ "Redux",
+ "Vuex",
+ "NgRx",
+ "state",
+ "flux",
+ "Jest",
+ "Sinon",
+ "Mocha",
+ "Chai",
+ "Cypress",
+ "Puppeteer",
+ "Selenium",
+ "Hermione",
+ "cookies",
+ "sessions",
+ "npm",
+ "yarn",
+ "node_modules",
+ "float",
+ "grid",
+ "flex",
+ "table",
+ "id",
+ "eslint",
+ "prettier",
+ "stylelint",
+ "husky",
+ "lint-staged",
+ "editorconfig",
+ "разметка",
+ "ссылка",
+ "сайт",
+ "картинка",
+ "текст",
+ "тэг",
+ "адаптивность",
+ "форма",
+ "валидация",
+ "браузер",
+ "атрибут",
+ "фокус",
+ "окружение",
+ "ошибка",
+ "исключение",
+ "комментарий",
+ "типизация",
+ "компонент",
+ "сервер",
+ "интерфейс",
+ "приложение",
+ "плагин",
+ "компиляция",
+ "сборка",
+ "проп",
+ "шаблон",
+ "страница",
+ "api",
+ "обновление",
+ "модель",
+ "представление",
+ "контроллер"
+]
diff --git a/src/data/translation-ru.json b/src/data/translation-ru.json
index 040d485..2d0f355 100644
--- a/src/data/translation-ru.json
+++ b/src/data/translation-ru.json
@@ -14,7 +14,8 @@
"chooseTeam": "Выберите команду",
"updateCards": "Обновить карточки",
"startGame": "Погнали",
- "dictionary": "Словарь"
+ "dictionary": "Словарь",
+ "words": "Слов"
},
"game": {
"leave": "Покинуть игру",
@@ -32,6 +33,8 @@
},
"error": {
"linkNotCopied": "Ошибка. Не удалось скопировать ссылку",
- "cardsNotUpdated": "Ошибка. Не удалось обновить карточки"
+ "cardsNotUpdated": "Ошибка. Не удалось обновить карточки",
+ "common": "Произошла ошибка",
+ "getDictionary": "Не удалось получить словарь"
}
}
diff --git a/src/dictionaries-client.js b/src/dictionaries-client.js
new file mode 100644
index 0000000..585ab39
--- /dev/null
+++ b/src/dictionaries-client.js
@@ -0,0 +1,11 @@
+import i18next from 'i18next';
+
+export async function getDictionary(dictionary) {
+ try {
+ const response = await window.fetch(`/dictionaries/${dictionary}`);
+ if (response.ok) return await response.json();
+ } catch (e) {
+ throw new Error(i18next.t('error.getDictionary'));
+ }
+ throw new Error(i18next.t('error.getDictionary'));
+}
diff --git a/src/service.js b/src/service.js
index df9d022..fd50c5f 100644
--- a/src/service.js
+++ b/src/service.js
@@ -25,16 +25,16 @@ const DB_CARDS_REF = 'sessions_cards';
* Инициализация игровой сессии
* @param language
* @param fieldSize
- * @param dictionary
+ * @param dictionaryFileName
* @returns {Promise<{sessionId: string, userId: string}>}
*/
-export async function initialize({ language, fieldSize, dictionary }) {
+export async function initialize({ language, fieldSize, dictionaryFileName }) {
const { sessionId, userId } = await createSession({
language,
fieldSize,
- dictionary,
+ dictionaryFileName,
});
- await setNewCards(sessionId, { dictionary, fieldSize });
+ await setNewCards(sessionId, { dictionaryFileName, fieldSize });
return { sessionId, userId };
}
@@ -42,10 +42,10 @@ export async function initialize({ language, fieldSize, dictionary }) {
* Создание игровой сессии
* @param language
* @param fieldSize
- * @param dictionary
+ * @param dictionaryFileName
* @returns {Promise<{sessionId: string, userId: string}>}
*/
-async function createSession({ language, fieldSize, dictionary }) {
+async function createSession({ language, fieldSize, dictionaryFileName }) {
const sessionRef = database.ref(DB_SESSIONS_REF).push();
const sessionId = sessionRef.key;
const sessionPath = getSessionPath(sessionId);
@@ -54,7 +54,7 @@ async function createSession({ language, fieldSize, dictionary }) {
settings: {
language,
fieldSize,
- dictionary,
+ dictionaryFileName,
},
captains: {
red: '',
@@ -123,13 +123,16 @@ export function setWinnerTeam(sessionId, winnerTeam) {
/**
* Установка карточек
* @param sessionId
- * @param dictionary
+ * @param dictionaryFileName
* @param fieldSize
* @returns {Promise
}
*/
-export async function setNewCards(sessionId, { dictionary, fieldSize }) {
+export async function setNewCards(
+ sessionId,
+ { dictionaryFileName, fieldSize },
+) {
const cardsRef = getCardsRef(sessionId);
- const cards = getGamingCards(dictionary, fieldSize);
+ const cards = await getGamingCards(dictionaryFileName, fieldSize);
return cardsRef.set(cards);
}
@@ -148,22 +151,22 @@ export async function updateCard(sessionId, cardId) {
* Сохранение настроек
* @param sessionId
* @param language
- * @param dictionary
+ * @param dictionaryFileName
* @param fieldSize
* @returns {Promise}
*/
export async function saveSettings(
sessionId,
- { language, dictionary, fieldSize },
+ { language, dictionaryFileName, fieldSize },
) {
const settingsPath = getSettingsPath(sessionId);
const winnerTeamPath = getWinnerTeamPath(sessionId);
const cardsPath = getCardsPath(sessionId);
- const cards = getGamingCards(dictionary, fieldSize);
+ const cards = await getGamingCards(dictionaryFileName, fieldSize);
const updates = {
[winnerTeamPath]: '',
- [settingsPath]: { language, dictionary, fieldSize },
+ [settingsPath]: { language, dictionaryFileName, fieldSize },
[cardsPath]: cards,
};
return database.ref().update(updates);
diff --git a/src/utils/data-provider.js b/src/utils/data-provider.js
index 7b33628..ee75cbb 100644
--- a/src/utils/data-provider.js
+++ b/src/utils/data-provider.js
@@ -1,18 +1,21 @@
import { getRandomInt } from 'src/utils/math';
-import dictionaries from 'src/data/dictionaries';
import {
TEAMS,
CARDS_DICTIONARIES,
CARDS_TYPES,
FIELD_SIZES,
} from 'src/data/constants';
+import { getDictionary } from 'src/dictionaries-client';
-export function getGamingCards(
- dictionaryName = CARDS_DICTIONARIES['GAGA'],
- fieldSize = FIELD_SIZES['5x5'],
-) {
+export function getDefaultDictionary(language) {
+ return CARDS_DICTIONARIES.find((dict) => dict.language === language);
+}
+
+export async function getGamingCards(dictionaryFileName, fieldSize) {
+ const dictionary = await getDictionary(dictionaryFileName);
const cardsDivision = getCardsDivision(fieldSize);
- const wordsGenerator = generateDictionaryWord(dictionaryName);
+ const wordsGenerator = generateDictionaryWord(dictionary);
+
const cards = [];
// killer cards
for (let i = 0; i < cardsDivision['killerCards']; i++) {
@@ -55,13 +58,7 @@ export function getGamingCards(
return shuffle(cards);
}
-export function* generateDictionaryWord(
- dictionaryName = CARDS_DICTIONARIES['GAGA'],
-) {
- if (!(dictionaryName in dictionaries)) {
- throw new TypeError('Словарь не найден');
- }
- const dictionary = dictionaries[dictionaryName];
+export function* generateDictionaryWord(dictionary) {
let wordsIndexes = {};
for (let i = 0; i < dictionary.length; i++) {
let wordIndex = getRandomInt(dictionary.length);
diff --git a/src/utils/data-provider.spec.js b/src/utils/data-provider.spec.js
index 151d513..03ce794 100644
--- a/src/utils/data-provider.spec.js
+++ b/src/utils/data-provider.spec.js
@@ -1,24 +1,82 @@
-import { generateDictionaryWord } from './data-provider';
-
-jest.mock('../data/dictionaries.json', () => ({
- test_dictionary: ['alphabet', 'car', 'tiger'],
-}));
-
-describe('generateDictionaryWord', () => {
- test('should return unique dictionary words', () => {
- let words = [];
- const wordsGenerator = generateDictionaryWord('test_dictionary');
- for (let i = 0; i < 3; i++) {
- words.push(wordsGenerator.next().value);
+import dictionary from 'src/data/dictionaries/default-ru.json';
+import { CARDS_TYPES, FIELD_SIZES } from 'src/data/constants';
+import { i18n } from 'src/utils/i18n';
+import { getGamingCards } from './data-provider';
+
+const setup = async ({ dictionaryName, fieldSize }) => {
+ window.fetch.mockResolvedValueOnce({
+ ok: true,
+ json: async () => dictionary,
+ });
+
+ return await getGamingCards(dictionaryName, fieldSize);
+};
+
+const getCardsOfType = (cards, type) =>
+ cards.filter((card) => card.type === type);
+
+describe('generateDictionaryWords', () => {
+ test('should return correct cards distribution for 5x4 field', async () => {
+ const cards = await setup({
+ dictionaryName: 'dictionary_name',
+ fieldSize: FIELD_SIZES['5x4'],
+ });
+
+ expect(getCardsOfType(cards, CARDS_TYPES['killer'])).toHaveLength(1);
+ expect(getCardsOfType(cards, CARDS_TYPES['agent'])).toHaveLength(15);
+ expect(getCardsOfType(cards, CARDS_TYPES['citizen'])).toHaveLength(4);
+ });
+
+ test('should return correct cards distribution for 5x5 field', async () => {
+ const cards = await setup({
+ dictionaryName: 'dictionary_name',
+ fieldSize: FIELD_SIZES['5x5'],
+ });
+
+ expect(getCardsOfType(cards, CARDS_TYPES['killer'])).toHaveLength(1);
+ expect(getCardsOfType(cards, CARDS_TYPES['agent'])).toHaveLength(17);
+ expect(getCardsOfType(cards, CARDS_TYPES['citizen'])).toHaveLength(7);
+ });
+
+ test('should return correct cards distribution for 5x6 field', async () => {
+ const cards = await setup({
+ dictionaryName: 'dictionary_name',
+ fieldSize: FIELD_SIZES['5x6'],
+ });
+
+ expect(getCardsOfType(cards, CARDS_TYPES['killer'])).toHaveLength(1);
+ expect(getCardsOfType(cards, CARDS_TYPES['agent'])).toHaveLength(19);
+ expect(getCardsOfType(cards, CARDS_TYPES['citizen'])).toHaveLength(10);
+ });
+
+ test('should throw error on bad response', async () => {
+ window.fetch.mockRejectedValueOnce({
+ ok: false,
+ });
+
+ let error;
+ try {
+ await getGamingCards('bad_dictionary_name', FIELD_SIZES['5x6']);
+ } catch (e) {
+ error = e.message;
}
- const uniqueWords = [...new Set([...words])];
- expect(words).toEqual(uniqueWords);
+
+ expect(error).toEqual(i18n.t('error.getDictionary'));
});
- test('should throw exception for non-existing dictionary', () => {
- expect(() => {
- const generator = generateDictionaryWord('unknown');
- generator.next();
- }).toThrowError(TypeError);
+ test('should throw error on bad dictionary', async () => {
+ window.fetch.mockRejectedValueOnce({
+ ok: true,
+ json: async () => null,
+ });
+
+ let error;
+ try {
+ await getGamingCards('dictionary_name', FIELD_SIZES['5x6']);
+ } catch (e) {
+ error = e.message;
+ }
+
+ expect(error).toEqual(i18n.t('error.getDictionary'));
});
});
diff --git a/webpack.config.js b/webpack.config.js
index 080021c..d9273cf 100644
--- a/webpack.config.js
+++ b/webpack.config.js
@@ -22,6 +22,10 @@ const plugins = [
from: path.resolve(__dirname, 'assets/favicons'),
to: path.resolve(__dirname, 'public/favicons'),
},
+ {
+ from: path.resolve(__dirname, 'src/data/dictionaries'),
+ to: path.resolve(__dirname, 'public/dictionaries'),
+ },
],
}),
];