Skip to content

Commit 59b7d04

Browse files
committed
add i18n & localize validation
1 parent 3053e23 commit 59b7d04

File tree

8 files changed

+202
-117
lines changed

8 files changed

+202
-117
lines changed

package-lock.json

Lines changed: 41 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
"dependencies": {
1818
"@popperjs/core": "^2.11.8",
1919
"bootstrap": "^5.3.6",
20+
"i18next": "^25.1.3",
2021
"on-change": "^5.0.1",
2122
"yup": "^1.6.1"
2223
}

src/i18n.js

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
import i18next from "i18next";
2+
import translations from "./locales/index.js";
3+
4+
i18next.init({
5+
resources: translations,
6+
fallbackLng: "ru",
7+
supportedLngs: ["en", "ru"],
8+
});
9+
10+
export default i18next;

src/index.html

Lines changed: 113 additions & 113 deletions
Original file line numberDiff line numberDiff line change
@@ -1,121 +1,121 @@
1-
<!doctype html>
2-
<html lang="en">
3-
<head>
4-
<meta charset="utf-8" />
5-
<meta name="viewport" content="width=device-width, initial-scale=1" />
6-
<title>Bootstrap w/ Vite</title>
7-
<link
8-
href="https://cdn.jsdelivr.net/npm/[email protected]/dist/css/bootstrap.min.css"
9-
rel="stylesheet"
10-
/>
11-
</head>
1+
<!doctype html>
2+
<html lang="en">
3+
<head>
4+
<meta charset="utf-8" />
5+
<meta name="viewport" content="width=device-width, initial-scale=1" />
6+
<title>RSS агрегатор</title>
7+
<link
8+
href="https://cdn.jsdelivr.net/npm/[email protected]/dist/css/bootstrap.min.css"
9+
rel="stylesheet"
10+
/>
11+
</head>
1212

13-
<body class="d-flex flex-column min-vh-100">
14-
<div
15-
class="modal fade"
16-
id="modal"
17-
tabindex="-1"
18-
role="dialog"
19-
aria-labelledby="modal"
20-
aria-hidden="true"
21-
>
22-
<div class="modal-dialog" role="document">
23-
<div class="modal-content">
24-
<div class="modal-header">
25-
<h5 class="modal-title"></h5>
26-
<button
27-
type="button"
28-
class="btn-close close"
29-
data-bs-dismiss="modal"
30-
aria-label="Close"
31-
></button>
32-
</div>
33-
<div class="modal-body text-break"></div>
34-
<div class="modal-footer">
35-
<a
36-
class="btn btn-primary full-article"
37-
href="#"
38-
role="button"
39-
target="_blank"
40-
rel="noopener noreferrer"
41-
>Читать полностью </a
42-
><button
43-
type="button"
44-
class="btn btn-secondary"
45-
data-bs-dismiss="modal"
46-
>
47-
Закрыть
48-
</button>
13+
<body class="d-flex flex-column min-vh-100">
14+
<div
15+
class="modal fade"
16+
id="modal"
17+
tabindex="-1"
18+
role="dialog"
19+
aria-labelledby="modal"
20+
aria-hidden="true"
21+
>
22+
<div class="modal-dialog" role="document">
23+
<div class="modal-content">
24+
<div class="modal-header">
25+
<h5 class="modal-title"></h5>
26+
<button
27+
type="button"
28+
class="btn-close close"
29+
data-bs-dismiss="modal"
30+
aria-label="Close"
31+
></button>
32+
</div>
33+
<div class="modal-body text-break"></div>
34+
<div class="modal-footer">
35+
<a
36+
class="btn btn-primary full-article"
37+
href="#"
38+
role="button"
39+
target="_blank"
40+
rel="noopener noreferrer"
41+
>Читать полностью</a
42+
><button
43+
type="button"
44+
class="btn btn-secondary"
45+
data-bs-dismiss="modal"
46+
>
47+
Закрыть
48+
</button>
49+
</div>
4950
</div>
5051
</div>
5152
</div>
52-
</div>
53-
<main class="flex-grow-1">
54-
<section class="container-fluid bg-dark p-5">
55-
<div class="row">
56-
<div class="col-md-10 col-lg-8 mx-auto text-white">
57-
<h1 class="display-3 mb-0">RSS агрегатор</h1>
58-
<p class="lead">
59-
Начните читать RSS сегодня! Это легко, это красиво.
60-
</p>
61-
<form action="" id="rss-form" class="rss-form text-body">
62-
<div class="row">
63-
<div class="col">
64-
<div class="form-floating">
65-
<input
66-
id="url-input"
67-
autofocus=""
68-
required=""
69-
name="url"
70-
aria-label="url"
71-
class="form-control w-100"
72-
placeholder="ссылка RSS"
73-
autocomplete="off"
74-
/>
75-
<label for="url-input">Ссылка RSS</label>
53+
<main class="flex-grow-1">
54+
<section class="container-fluid bg-dark p-5">
55+
<div class="row">
56+
<div class="col-md-10 col-lg-8 mx-auto text-white">
57+
<h1 class="display-3 mb-0">RSS агрегатор</h1>
58+
<p class="lead">
59+
Начните читать RSS сегодня! Это легко, это красиво.
60+
</p>
61+
<form action="" id="rss-form" class="rss-form text-body">
62+
<div class="row">
63+
<div class="col">
64+
<div class="form-floating">
65+
<input
66+
id="url-input"
67+
autofocus=""
68+
required=""
69+
name="url"
70+
aria-label="url"
71+
class="form-control w-100"
72+
placeholder="ссылка RSS"
73+
autocomplete="off"
74+
/>
75+
<label for="url-input">Ссылка RSS</label>
76+
</div>
77+
</div>
78+
<div class="col-auto">
79+
<button
80+
id="add-feed-btn"
81+
type="submit"
82+
aria-label="add"
83+
class="h-100 btn btn-lg btn-primary px-sm-5"
84+
>
85+
Добавить
86+
</button>
7687
</div>
7788
</div>
78-
<div class="col-auto">
79-
<button
80-
id="add-feed-btn"
81-
type="submit"
82-
aria-label="add"
83-
class="h-100 btn btn-lg btn-primary px-sm-5"
84-
>
85-
Добавить
86-
</button>
87-
</div>
88-
</div>
89-
</form>
90-
<p class="mt-2 mb-0 text-muted">
91-
Пример: https://lorem-rss.hexlet.app/feed
92-
</p>
93-
<p class="feedback m-0 position-absolute small text-danger"></p>
89+
</form>
90+
<p class="mt-2 mb-0 text-muted">
91+
Пример: https://lorem-rss.hexlet.app/feed
92+
</p>
93+
<p class="feedback m-0 position-absolute small text-danger"></p>
94+
</div>
95+
</div>
96+
</section>
97+
<section class="container-fluid container-xxl p-5">
98+
<div class="row">
99+
<div class="col-md-10 col-lg-8 order-1 mx-auto posts"></div>
100+
<div
101+
class="col-md-10 col-lg-4 mx-auto order-0 order-lg-1 feeds"
102+
></div>
103+
</div>
104+
</section>
105+
</main>
106+
<footer class="footer border-top py-3 mt-5 bg-light">
107+
<div class="container-xl">
108+
<div class="text-center">
109+
created by
110+
<a
111+
href="https://ru.hexlet.io/professions/frontend/projects/11"
112+
target="_blank"
113+
>Hexlet</a
114+
>
94115
</div>
95116
</div>
96-
</section>
97-
<section class="container-fluid container-xxl p-5">
98-
<div class="row">
99-
<div class="col-md-10 col-lg-8 order-1 mx-auto posts"></div>
100-
<div
101-
class="col-md-10 col-lg-4 mx-auto order-0 order-lg-1 feeds"
102-
></div>
103-
</div>
104-
</section>
105-
</main>
106-
<footer class="footer border-top py-3 mt-5 bg-light">
107-
<div class="container-xl">
108-
<div class="text-center">
109-
created by
110-
<a
111-
href="https://ru.hexlet.io/professions/frontend/projects/11"
112-
target="_blank"
113-
>Hexlet</a
114-
>
115-
</div>
116-
</div>
117-
</footer>
118-
<deepl-input-controller translate="no"></deepl-input-controller>
119-
<script type="module" src="./js/main.js"></script>
120-
</body>
121-
</html>
117+
</footer>
118+
<deepl-input-controller translate="no"></deepl-input-controller>
119+
<script type="module" src="./js/main.js"></script>
120+
</body>
121+
</html>

src/js/model.js

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import onChange from "on-change";
22
import * as yup from "yup";
3+
import i18next from "../i18n.js";
34

45
import { renderErrors, renderInputValue } from "./view.js";
56

@@ -24,12 +25,12 @@ const state = onChange(object, (path, value) => {
2425

2526
const schema = yup
2627
.string()
27-
.url()
28-
.required("Обязательное поле")
28+
.url(i18next.t("invalid_url"))
29+
.required(i18next.t("required_field"))
2930
.test(
3031
"no-duplicate",
31-
"Эта лента уже добавлена",
32-
(value) => !state.rssFeed.includes(value),
32+
i18next.t("no_duplicate"),
33+
(value) => !state.rssFeed.includes(value)
3334
);
3435

3536
export const validateInput = () => {

src/locales/en.js

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
export default {
2+
translation: {
3+
title: "RSS aggregator",
4+
subtitle: "Start reading RSS today! It's easy, it's beautiful.",
5+
form_placeholder: "RSS Link",
6+
add_btn: "Add",
7+
full_btn: "Full",
8+
close: "Close",
9+
example: "Example",
10+
invalid_url: "Link must be a valid URL",
11+
required_field: "Required field",
12+
no_duplicate: "This feed has already been added",
13+
},
14+
};

src/locales/index.js

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
import en from "./en.js";
2+
import ru from "./ru.js";
3+
4+
export default { en, ru };

src/locales/ru.js

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
export default {
2+
translation: {
3+
title: "RSS агрегатор",
4+
subtitle: "Начните читать RSS сегодня! Это легко, это красиво.",
5+
form_placeholder: "Ссылка RSS",
6+
add_btn: "Добавить",
7+
full_btn: "Читать полностью",
8+
close: "Закрыть",
9+
example: "Пример",
10+
invalid_url: "Ссылка должна быть валидным URL",
11+
required_field: "Обязательное поле",
12+
no_duplicate: "Эта лента уже добавлена",
13+
},
14+
};

0 commit comments

Comments
 (0)