diff --git a/.github/labeler.yml b/.github/labeler.yml
index cae5f5099b4..1581cb23b3d 100644
--- a/.github/labeler.yml
+++ b/.github/labeler.yml
@@ -23,4 +23,3 @@
- client/i18n/**/*
- config/crowdin/**/*
- config/i18n/**/*
- - tools/crowdin/**/*
diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml
index eb6949ec6f2..c096bb0e5fa 100644
--- a/.github/workflows/codeql-analysis.yml
+++ b/.github/workflows/codeql-analysis.yml
@@ -1,13 +1,15 @@
name: CI - Run CodeQL Analysis
on:
push:
- branches: [main]
paths-ignore:
- 'docs/**'
+ branches:
+ - 'main'
pull_request:
- branches: [main]
paths-ignore:
- 'docs/**'
+ branches:
+ - 'main'
permissions:
contents: read
@@ -32,8 +34,8 @@ jobs:
- name: Checkout repository
uses: actions/checkout@ac593985615ec2ede58e132d2e21d2b1cbd6127c # v3
- name: Setup CodeQL
- uses: github/codeql-action/init@3ebbd71c74ef574dbc558c82f70e52732c8b44fe # v2
+ uses: github/codeql-action/init@17573ee1cc1b9d061760f3a006fc4aac4f944fd5 # v2
with:
languages: ${{ matrix.language }}
- name: Perform Analysis
- uses: github/codeql-action/analyze@3ebbd71c74ef574dbc558c82f70e52732c8b44fe # v2
+ uses: github/codeql-action/analyze@17573ee1cc1b9d061760f3a006fc4aac4f944fd5 # v2
diff --git a/.github/workflows/e2e-mobile.yml b/.github/workflows/e2e-mobile.yml
index 0fac7e65a62..4b688f1bc72 100644
--- a/.github/workflows/e2e-mobile.yml
+++ b/.github/workflows/e2e-mobile.yml
@@ -4,11 +4,14 @@ on:
# push:
# paths-ignore:
# - 'docs/**'
- # branches-ignore:
- # - 'renovate/**'
+ # branches:
+ # - 'main'
# pull_request:
# paths-ignore:
# - 'docs/**'
+ # branches:
+ # - 'main'
+ # - 'next-**'
jobs:
mobile-test:
diff --git a/.github/workflows/e2e-third-party.yml b/.github/workflows/e2e-third-party.yml
index ab4d14744ac..411b42b630b 100644
--- a/.github/workflows/e2e-third-party.yml
+++ b/.github/workflows/e2e-third-party.yml
@@ -5,7 +5,7 @@ name: CI - E2E - 3rd party donation tests
on:
push:
branches:
- - 'prod-*'
+ - 'prod-**'
paths-ignore:
- 'docs/**'
diff --git a/.github/workflows/e2e-web.yml b/.github/workflows/e2e-web.yml
index 891c6ce59f3..1d8c8abe740 100644
--- a/.github/workflows/e2e-web.yml
+++ b/.github/workflows/e2e-web.yml
@@ -3,14 +3,14 @@ on:
push:
paths-ignore:
- 'docs/**'
- branches-ignore:
- - 'renovate/**'
- - 'next-api'
+ branches:
+ - 'main'
pull_request:
paths-ignore:
- 'docs/**'
- branches-ignore:
- - 'next-api'
+ branches:
+ - 'main'
+ - 'next-**'
jobs:
build-client:
diff --git a/.github/workflows/node.js-tests-upcoming.yml b/.github/workflows/node.js-tests-upcoming.yml
index a12d2165b13..ffe5f3a6f89 100644
--- a/.github/workflows/node.js-tests-upcoming.yml
+++ b/.github/workflows/node.js-tests-upcoming.yml
@@ -2,14 +2,20 @@ name: CI - Node.js Test Upcoming
env:
NODE_OPTIONS: '--max_old_space_size=6144'
on:
+ # Run on push events, but only for the below branches
push:
branches:
- # Treat the below branches as special case for working on workflows
- - actions-**
- - upcoming-**
+ - 'main'
+ - 'prod-**'
+ # Run on pull requests, but only for the below targets
+ pull_request:
+ branches:
+ - 'main'
+ - 'next-**'
schedule:
# run this Action every 14 days
- cron: '0 * */14 * *'
+ # Run on demand
workflow_dispatch:
permissions:
diff --git a/.github/workflows/node.js-tests.yml b/.github/workflows/node.js-tests.yml
index c434960e7b4..5dd1891c297 100644
--- a/.github/workflows/node.js-tests.yml
+++ b/.github/workflows/node.js-tests.yml
@@ -1,14 +1,21 @@
name: CI - Node.js Test Current
env:
NODE_OPTIONS: '--max_old_space_size=6144'
+
on:
+ # Run on push events, but only for the below branches
push:
- branches-ignore:
- - 'renovate/**'
- - 'next-api'
+ branches:
+ - 'main'
+ - 'prod-**'
+ # Run on pull requests, but only for the below targets
pull_request:
- branches-ignore:
- - 'next-api'
+ branches:
+ - 'main'
+ - 'next-**'
+ # Run on Merge Queue
+ merge_group:
+ types: [checks_requested]
permissions:
contents: read
diff --git a/.gitpod.Dockerfile b/.gitpod.Dockerfile
new file mode 100644
index 00000000000..54b67b14d53
--- /dev/null
+++ b/.gitpod.Dockerfile
@@ -0,0 +1,8 @@
+FROM gitpod/workspace-mongodb:latest
+
+# from https://www.gitpod.io/docs/introduction/languages/javascript#node-versions
+RUN bash -c 'VERSION="lts/*" \
+ && source $HOME/.nvm/nvm.sh && nvm install $VERSION \
+ && nvm use $VERSION && nvm alias default $VERSION'
+
+RUN echo "nvm use default &>/dev/null" >> ~/.bashrc.d/51-nvm-fix
diff --git a/.gitpod.yml b/.gitpod.yml
index d69ba77091e..a312e48f94a 100644
--- a/.gitpod.yml
+++ b/.gitpod.yml
@@ -1,4 +1,5 @@
-image: gitpod/workspace-mongodb
+image:
+ file: .gitpod.Dockerfile
ports:
- port: 27017 # mongodb
onOpen: ignore
diff --git a/api-server/package.json b/api-server/package.json
index 7be433231a7..8b2b9214fea 100644
--- a/api-server/package.json
+++ b/api-server/package.json
@@ -45,6 +45,7 @@
"dedent": "0.7.0",
"dotenv": "6.2.0",
"express-flash": "0.0.2",
+ "express-rate-limit": "^6.7.0",
"express-session": "1.17.3",
"express-validator": "6.14.1",
"helmet": "3.23.3",
@@ -60,12 +61,14 @@
"mongodb": "3.6.9",
"morgan": "1.10.0",
"nanoid": "3.3.4",
+ "node-fetch": "^2.6.7",
"nodemailer-ses-transport": "1.5.1",
"passport": "0.4.1",
"passport-auth0": "1.4.2",
"passport-local": "1.0.0",
"passport-mock-strategy": "2.0.0",
"query-string": "6.14.0",
+ "rate-limit-mongo": "^2.3.2",
"rx": "4.1.0",
"stripe": "8.205.0",
"uuid": "3.4.0",
diff --git a/api-server/src/common/models/user.js b/api-server/src/common/models/user.js
index 31efb6a04d5..028ffcc7dc9 100644
--- a/api-server/src/common/models/user.js
+++ b/api-server/src/common/models/user.js
@@ -162,6 +162,8 @@ export default function initializeUser(User) {
User.definition.properties.rand.default = getRandomNumber;
// increase user accessToken ttl to 900 days
User.settings.ttl = 900 * 24 * 60 * 60 * 1000;
+ // Sets ttl to 900 days for mobile login created access tokens
+ User.settings.maxTTL = 900 * 24 * 60 * 60 * 1000;
// username should not be in blocklist
User.validatesExclusionOf('username', {
@@ -341,6 +343,21 @@ export default function initializeUser(User) {
);
};
+ User.prototype.mobileLoginByRequest = function mobileLoginByRequest(
+ req,
+ res
+ ) {
+ return new Promise((resolve, reject) =>
+ this.createAccessToken({}, (err, accessToken) => {
+ if (err) {
+ return reject(err);
+ }
+ setAccessTokenToResponse({ accessToken }, req, res);
+ return resolve(accessToken);
+ })
+ );
+ };
+
User.afterRemote('logout', function ({ req, res }, result, next) {
removeCookies(req, res);
next();
diff --git a/api-server/src/server/boot/authentication.js b/api-server/src/server/boot/authentication.js
index b55a90cdc49..c7589da53ce 100644
--- a/api-server/src/server/boot/authentication.js
+++ b/api-server/src/server/boot/authentication.js
@@ -2,10 +2,9 @@ import dedent from 'dedent';
import { check } from 'express-validator';
import jwt from 'jsonwebtoken';
import passport from 'passport';
+import fetch from 'node-fetch';
import { isEmail } from 'validator';
-
import { jwtSecret } from '../../../../config/secrets';
-
import { decodeEmail } from '../../common/utils';
import {
createPassportCallbackAuthenticator,
@@ -14,7 +13,11 @@ import {
} from '../component-passport';
import { wrapHandledError } from '../utils/create-handled-error.js';
import { removeCookies } from '../utils/getSetAccessToken';
-import { ifUserRedirectTo, ifNoUserRedirectHome } from '../utils/middleware';
+import {
+ ifUserRedirectTo,
+ ifNoUserRedirectHome,
+ ifNotMobileRedirect
+} from '../utils/middleware';
import { getRedirectParams } from '../utils/redirection';
import { createDeleteUserToken } from '../middlewares/user-token';
@@ -34,6 +37,7 @@ module.exports = function enableAuthentication(app) {
// enable loopback access control authentication. see:
// loopback.io/doc/en/lb2/Authentication-authorization-and-permissions.html
app.enableAuth();
+ const ifNotMobile = ifNotMobileRedirect();
const ifUserRedirect = ifUserRedirectTo();
const ifNoUserRedirect = ifNoUserRedirectHome();
const devSaveAuthCookies = devSaveResponseAuthCookies();
@@ -87,6 +91,8 @@ module.exports = function enableAuthentication(app) {
createGetPasswordlessAuth(app)
);
+ api.get('/mobile-login', ifNotMobile, ifUserRedirect, mobileLogin(app));
+
app.use(api);
};
@@ -188,3 +194,53 @@ function createGetPasswordlessAuth(app) {
);
};
}
+
+function mobileLogin(app) {
+ const {
+ models: { User }
+ } = app;
+ return async function getPasswordlessAuth(req, res, next) {
+ try {
+ const auth0Res = await fetch(
+ `https://${process.env.AUTH0_DOMAIN}/userinfo`,
+ {
+ headers: { Authorization: req.headers.authorization }
+ }
+ );
+
+ if (!auth0Res.ok) {
+ return next(
+ wrapHandledError(new Error('Invalid Auth0 token'), {
+ type: 'danger',
+ message: 'We could not log you in, please try again in a moment.',
+ status: auth0Res.status
+ })
+ );
+ }
+
+ const { email } = await auth0Res.json();
+
+ if (!isEmail(email)) {
+ return next(
+ wrapHandledError(new TypeError('decoded email is invalid'), {
+ type: 'danger',
+ message: 'The email is incorrectly formatted',
+ status: 400
+ })
+ );
+ }
+
+ User.findOne$({ where: { email } })
+ .do(async user => {
+ if (!user) {
+ user = await User.create({ email });
+ }
+ await user.mobileLoginByRequest(req, res);
+ res.end();
+ })
+ .subscribe(() => {}, next);
+ } catch (err) {
+ next(err);
+ }
+ };
+}
diff --git a/api-server/src/server/middleware.json b/api-server/src/server/middleware.json
index 69a47f44021..df8d4ae9ff8 100644
--- a/api-server/src/server/middleware.json
+++ b/api-server/src/server/middleware.json
@@ -39,7 +39,10 @@
"./middlewares/constant-headers": {},
"./middlewares/csp": {},
"./middlewares/flash-cheaters": {},
- "./middlewares/passport-login": {}
+ "./middlewares/passport-login": {},
+ "./middlewares/rate-limit": {
+ "paths": ["/mobile-login"]
+ }
},
"files": {},
"final:after": {
diff --git a/api-server/src/server/middlewares/rate-limit.js b/api-server/src/server/middlewares/rate-limit.js
new file mode 100644
index 00000000000..b461039a12c
--- /dev/null
+++ b/api-server/src/server/middlewares/rate-limit.js
@@ -0,0 +1,23 @@
+import rateLimit from 'express-rate-limit';
+import MongoStore from 'rate-limit-mongo';
+
+const url = process.env.MONGODB || process.env.MONGOHQ_URL;
+
+// Rate limit for mobile login
+// 10 requests per 15 minute windows
+export default function rateLimitMiddleware() {
+ return rateLimit({
+ windowMs: 15 * 60 * 1000,
+ max: 10,
+ standardHeaders: true,
+ legacyHeaders: false,
+ keyGenerator: req => {
+ return req.headers['x-forwarded-for'] || 'localhost';
+ },
+ store: new MongoStore({
+ collectionName: 'UserRateLimit',
+ uri: url,
+ expireTimeMs: 15 * 60 * 1000
+ })
+ });
+}
diff --git a/api-server/src/server/middlewares/request-authorization.js b/api-server/src/server/middlewares/request-authorization.js
index b12858222bf..60aedcdb936 100644
--- a/api-server/src/server/middlewares/request-authorization.js
+++ b/api-server/src/server/middlewares/request-authorization.js
@@ -26,6 +26,7 @@ const updateHooksRE = /^\/hooks\/update-paypal$/;
// note: this would be replaced by webhooks later
const donateRE = /^\/donate\/charge-stripe$/;
const submitCoderoadChallengeRE = /^\/coderoad-challenge-completed$/;
+const mobileLoginRE = /^\/mobile-login\/?$/;
const _pathsAllowedREs = [
authRE,
@@ -41,7 +42,8 @@ const _pathsAllowedREs = [
unsubscribeRE,
updateHooksRE,
donateRE,
- submitCoderoadChallengeRE
+ submitCoderoadChallengeRE,
+ mobileLoginRE
];
export function isAllowedPath(path, pathsAllowedREs = _pathsAllowedREs) {
diff --git a/api-server/src/server/utils/middleware.js b/api-server/src/server/utils/middleware.js
index 52f5551fc85..61144fae64b 100644
--- a/api-server/src/server/utils/middleware.js
+++ b/api-server/src/server/utils/middleware.js
@@ -77,6 +77,20 @@ export function ifUserRedirectTo(status) {
};
}
+export function ifNotMobileRedirect() {
+ return (req, res, next) => {
+ //
+ // Todo: Use the below check once we have done more research on usage
+ //
+ // const isMobile = /(iPhone|iPad|Android)/.test(req.headers['user-agent']);
+ // if (!isMobile) {
+ // res.json({ error: 'not from mobile' });
+ // } else {
+ // next();
+ // }
+ next();
+ };
+}
// for use with express-validator error formatter
export const createValidatorErrorHandler =
(...args) =>
diff --git a/client/gatsby-browser.js b/client/gatsby-browser.js
index 513cc8d8a55..f93c9c10a26 100644
--- a/client/gatsby-browser.js
+++ b/client/gatsby-browser.js
@@ -6,7 +6,7 @@ import { Provider } from 'react-redux';
import i18n from './i18n/config';
import AppMountNotifier from './src/components/app-mount-notifier';
-import { createStore } from './src/redux/createStore';
+import { createStore } from './src/redux/create-store';
import layoutSelector from './utils/gatsby/layout-selector';
import GrowthBookProvider from './src/components/growth-book/growth-book-wrapper';
diff --git a/client/gatsby-ssr.js b/client/gatsby-ssr.js
index e7995a365d0..c0f6e4237c6 100644
--- a/client/gatsby-ssr.js
+++ b/client/gatsby-ssr.js
@@ -4,7 +4,7 @@ import { I18nextProvider } from 'react-i18next';
import { Provider } from 'react-redux';
import i18n from './i18n/config';
-import { createStore } from './src/redux/createStore';
+import { createStore } from './src/redux/create-store';
import layoutSelector from './utils/gatsby/layout-selector';
import { getheadTagComponents, getPostBodyComponents } from './utils/tags';
import GrowthBookProvider from './src/components/growth-book/growth-book-wrapper';
diff --git a/client/i18n/locales/arabic/intro.json b/client/i18n/locales/arabic/intro.json
index 7bd66b174b1..fdc68370e15 100644
--- a/client/i18n/locales/arabic/intro.json
+++ b/client/i18n/locales/arabic/intro.json
@@ -778,14 +778,6 @@
"أسناد: Rosetta Code"
]
},
- "the-odin-project": {
- "title": "مشروع أودين",
- "intro": ["A description is to be determined"]
- },
- "the-odin-project-projects": {
- "title": "The Odin Project Projects",
- "intro": ["A description is to be determined"]
- },
"project-euler": {
"title": "مشروع Euler",
"intro": [
@@ -795,6 +787,24 @@
}
}
},
+ "the-odin-project": {
+ "title": "مشروع أودين",
+ "intro": [
+ "The Odin Project is one of those \"What I wish I had when I was learning\" resources. ",
+ "Not everyone has access to a computer science education or the funds to attend an intensive coding school and neither of those is right for everyone anyway.",
+ "This project is designed to fill in the gap for people who are trying to hack it on their own but still want a high quality education."
+ ],
+ "blocks": {
+ "top-learn-html-foundations": {
+ "title": "Learn HTML Foundations",
+ "intro": ["A description is to be determined"]
+ },
+ "top-build-a-recipe-project": {
+ "title": "Learn HTML Foundations by Building a Recipe Page",
+ "intro": ["A description is to be determined"]
+ }
+ }
+ },
"misc-text": {
"certification": "شهادة {{cert}}",
"browse-other": "تصفح الشهادات المجانية الأخرى\n(نوصي بالقيام بها بالترتيب)",
diff --git a/client/i18n/locales/arabic/translations.json b/client/i18n/locales/arabic/translations.json
index a6f2db3f8b3..0f8b449fc7b 100644
--- a/client/i18n/locales/arabic/translations.json
+++ b/client/i18n/locales/arabic/translations.json
@@ -15,8 +15,8 @@
"show-cert": "عرض الشهادة",
"claim-cert": "المطالبة بالشهادة",
"save-progress": "حفظ التقدم",
- "accepted-honesty": "لقد قبلت سياسة الصدق الأكاديمي الخاصة بنا.",
- "agree": "موافق",
+ "accepted-honesty": "لقد وافقت على سياستنا للصدق الأكاديمي.",
+ "agree-honesty": "أوافق على سياسة freeCodeCamp للصدق الأكاديمي.",
"save-portfolio": "حفظ عنصر الحافظة هذا",
"remove-portfolio": "إزالة عنصر الحافظة هذا",
"add-portfolio": "إضافة عنصر حافظة جديد",
@@ -302,7 +302,6 @@
"certs": "شهادة {{title}}"
},
"editor-tabs": {
- "info": "معلومات",
"code": "الكود",
"tests": "الاختبارات",
"restart": "أعد التشغيل",
@@ -518,7 +517,7 @@
"opens-new-window": "فتح في نافذة جديدة"
},
"flash": {
- "honest-first": "للمطالبة بشهادة ، يجب عليك أولاً قبول سياسة الصدق الأكاديمي الخاصة بنا",
+ "honest-first": "للمطالبة بشهادة، يجب عليك أولاً الموافقة على سياسة للصدق الأكاديمي",
"really-weird": "حدث شيء غريب حقاً، إذا حدث مرة أخرى، يرجى النظر في الإبلاغ عنها على https://github.com/freeCodeCamp/freeCodeCamp/issues/new",
"not-right": "يبدو ان هناك خطأ ما. لقد تم إنشاء تقرير وتم إخطار فريق freeCodeCamp.org",
"went-wrong": "حدث خطأ ما، الرجاء التحقق والمحاولة مرة أخرى",
diff --git a/client/i18n/locales/chinese-traditional/intro.json b/client/i18n/locales/chinese-traditional/intro.json
index 3c4f393dd4b..6ee34449b97 100644
--- a/client/i18n/locales/chinese-traditional/intro.json
+++ b/client/i18n/locales/chinese-traditional/intro.json
@@ -778,14 +778,6 @@
"屬性:Rosetta 代碼"
]
},
- "the-odin-project": {
- "title": "The Odin Project",
- "intro": ["A description is to be determined"]
- },
- "the-odin-project-projects": {
- "title": "The Odin Project Projects",
- "intro": ["A description is to be determined"]
- },
"project-euler": {
"title": "歐拉計劃",
"intro": [
@@ -795,6 +787,24 @@
}
}
},
+ "the-odin-project": {
+ "title": "The Odin Project",
+ "intro": [
+ "The Odin Project is one of those \"What I wish I had when I was learning\" resources. ",
+ "Not everyone has access to a computer science education or the funds to attend an intensive coding school and neither of those is right for everyone anyway.",
+ "This project is designed to fill in the gap for people who are trying to hack it on their own but still want a high quality education."
+ ],
+ "blocks": {
+ "top-learn-html-foundations": {
+ "title": "Learn HTML Foundations",
+ "intro": ["A description is to be determined"]
+ },
+ "top-build-a-recipe-project": {
+ "title": "Learn HTML Foundations by Building a Recipe Page",
+ "intro": ["A description is to be determined"]
+ }
+ }
+ },
"misc-text": {
"certification": "{{cert}} 認證",
"browse-other": "瀏覽我們的其他免費認證\n(我們建議你按順序學習)",
diff --git a/client/i18n/locales/chinese-traditional/translations.json b/client/i18n/locales/chinese-traditional/translations.json
index 0be7fdf3146..4489ee6239e 100644
--- a/client/i18n/locales/chinese-traditional/translations.json
+++ b/client/i18n/locales/chinese-traditional/translations.json
@@ -15,8 +15,8 @@
"show-cert": "顯示認證",
"claim-cert": "申請認證",
"save-progress": "保存進度",
- "accepted-honesty": "你已接受我們的《學術誠信條例》",
- "agree": "同意",
+ "accepted-honesty": "You have agreed to our Academic Honesty Policy.",
+ "agree-honesty": "I agree to freeCodeCamp's Academic Honesty Policy.",
"save-portfolio": "保存這個作品集項目",
"remove-portfolio": "移除這個作品集項目",
"add-portfolio": "增加一個新的作品集項目",
@@ -302,7 +302,6 @@
"certs": "{{title}} 認證"
},
"editor-tabs": {
- "info": "信息",
"code": "編程",
"tests": "測試",
"restart": "重啓",
@@ -518,7 +517,7 @@
"opens-new-window": "Opens in new window"
},
"flash": {
- "honest-first": "申請認證之前,你必須先接受我們的《學術誠信條例》",
+ "honest-first": "To claim a certification, you must first agree to our academic honesty policy",
"really-weird": "出現了一些奇怪的情況。如果再出現這種情況,請考慮在 https://github.com/freeCodeCamp/freeCodeCamp/issues/new 提交 issue。",
"not-right": "有些不對勁。已生成報告,通知 freeCodeCamp.org 團隊。",
"went-wrong": "出了點問題,請檢查並重試。",
diff --git a/client/i18n/locales/chinese/intro.json b/client/i18n/locales/chinese/intro.json
index fcb1167338d..ce8beea0d5b 100644
--- a/client/i18n/locales/chinese/intro.json
+++ b/client/i18n/locales/chinese/intro.json
@@ -778,14 +778,6 @@
"属性:Rosetta 代码"
]
},
- "the-odin-project": {
- "title": "The Odin Project",
- "intro": ["A description is to be determined"]
- },
- "the-odin-project-projects": {
- "title": "The Odin Project Projects",
- "intro": ["A description is to be determined"]
- },
"project-euler": {
"title": "欧拉计划",
"intro": [
@@ -795,6 +787,24 @@
}
}
},
+ "the-odin-project": {
+ "title": "The Odin Project",
+ "intro": [
+ "The Odin Project is one of those \"What I wish I had when I was learning\" resources. ",
+ "Not everyone has access to a computer science education or the funds to attend an intensive coding school and neither of those is right for everyone anyway.",
+ "This project is designed to fill in the gap for people who are trying to hack it on their own but still want a high quality education."
+ ],
+ "blocks": {
+ "top-learn-html-foundations": {
+ "title": "Learn HTML Foundations",
+ "intro": ["A description is to be determined"]
+ },
+ "top-build-a-recipe-project": {
+ "title": "Learn HTML Foundations by Building a Recipe Page",
+ "intro": ["A description is to be determined"]
+ }
+ }
+ },
"misc-text": {
"certification": "{{cert}} 认证",
"browse-other": "浏览我们的其他免费认证\n(我们建议你按顺序学习)",
diff --git a/client/i18n/locales/chinese/translations.json b/client/i18n/locales/chinese/translations.json
index 2a0ae67e97f..8d7625d6a4c 100644
--- a/client/i18n/locales/chinese/translations.json
+++ b/client/i18n/locales/chinese/translations.json
@@ -15,8 +15,8 @@
"show-cert": "显示认证",
"claim-cert": "申请认证",
"save-progress": "保存进度",
- "accepted-honesty": "你已接受我们的《学术诚信条例》",
- "agree": "同意",
+ "accepted-honesty": "You have agreed to our Academic Honesty Policy.",
+ "agree-honesty": "I agree to freeCodeCamp's Academic Honesty Policy.",
"save-portfolio": "保存这个作品集项目",
"remove-portfolio": "移除这个作品集项目",
"add-portfolio": "增加一个新的作品集项目",
@@ -302,7 +302,6 @@
"certs": "{{title}} 认证"
},
"editor-tabs": {
- "info": "信息",
"code": "编程",
"tests": "测试",
"restart": "重启",
@@ -518,7 +517,7 @@
"opens-new-window": "Opens in new window"
},
"flash": {
- "honest-first": "申请认证之前,你必须先接受我们的《学术诚信条例》",
+ "honest-first": "To claim a certification, you must first agree to our academic honesty policy",
"really-weird": "出现了一些奇怪的情况。如果再出现这种情况,请考虑在 https://github.com/freeCodeCamp/freeCodeCamp/issues/new 提交 issue。",
"not-right": "有些不对劲。已生成报告,通知 freeCodeCamp.org 团队。",
"went-wrong": "出了点问题,请检查并重试。",
diff --git a/client/i18n/locales/english/intro.json b/client/i18n/locales/english/intro.json
index 17926a60efb..c1d679e160e 100644
--- a/client/i18n/locales/english/intro.json
+++ b/client/i18n/locales/english/intro.json
@@ -778,14 +778,6 @@
"Attribute: Rosetta Code"
]
},
- "the-odin-project": {
- "title": "The Odin Project",
- "intro": ["A description is to be determined"]
- },
- "the-odin-project-projects": {
- "title": "The Odin Project Projects",
- "intro": ["A description is to be determined"]
- },
"project-euler": {
"title": "Project Euler",
"intro": [
@@ -795,6 +787,24 @@
}
}
},
+ "the-odin-project": {
+ "title": "The Odin Project",
+ "intro": [
+ "The Odin Project is one of those \"What I wish I had when I was learning\" resources. ",
+ "Not everyone has access to a computer science education or the funds to attend an intensive coding school and neither of those is right for everyone anyway.",
+ "This project is designed to fill in the gap for people who are trying to hack it on their own but still want a high quality education."
+ ],
+ "blocks": {
+ "top-learn-html-foundations": {
+ "title": "Learn HTML Foundations",
+ "intro": ["A description is to be determined"]
+ },
+ "top-build-a-recipe-project": {
+ "title": "Learn HTML Foundations by Building a Recipe Page",
+ "intro": ["A description is to be determined"]
+ }
+ }
+ },
"misc-text": {
"certification": "{{cert}} Certification",
"browse-other": "Browse our other free certifications\n(we recommend doing these in order)",
diff --git a/client/i18n/locales/english/translations.json b/client/i18n/locales/english/translations.json
index e0d4fe5f970..874814e5f31 100644
--- a/client/i18n/locales/english/translations.json
+++ b/client/i18n/locales/english/translations.json
@@ -15,8 +15,8 @@
"show-cert": "Show Certification",
"claim-cert": "Claim Certification",
"save-progress": "Save Progress",
- "accepted-honesty": "You have accepted our Academic Honesty Policy.",
- "agree": "Agree",
+ "accepted-honesty": "You have agreed to our Academic Honesty Policy.",
+ "agree-honesty": "I agree to freeCodeCamp's Academic Honesty Policy.",
"save-portfolio": "Save this portfolio item",
"remove-portfolio": "Remove this portfolio item",
"add-portfolio": "Add a new portfolio Item",
@@ -302,7 +302,6 @@
"certs": "{{title}} Certification"
},
"editor-tabs": {
- "info": "Info",
"code": "Code",
"tests": "Tests",
"restart": "Restart",
@@ -518,7 +517,7 @@
"opens-new-window": "Opens in new window"
},
"flash": {
- "honest-first": "To claim a certification, you must first accept our academic honesty policy",
+ "honest-first": "To claim a certification, you must first agree to our academic honesty policy",
"really-weird": "Something really weird happened, if it happens again, please consider raising an issue on https://github.com/freeCodeCamp/freeCodeCamp/issues/new",
"not-right": "Something is not quite right. A report has been generated and the freeCodeCamp.org team have been notified",
"went-wrong": "Something went wrong, please check and try again",
diff --git a/client/i18n/locales/espanol/intro.json b/client/i18n/locales/espanol/intro.json
index 325a88b33da..6779cf07a7f 100644
--- a/client/i18n/locales/espanol/intro.json
+++ b/client/i18n/locales/espanol/intro.json
@@ -778,14 +778,6 @@
"Atributo: Código Rosetta"
]
},
- "the-odin-project": {
- "title": "The Odin Project",
- "intro": ["A description is to be determined"]
- },
- "the-odin-project-projects": {
- "title": "The Odin Project Projects",
- "intro": ["A description is to be determined"]
- },
"project-euler": {
"title": "Project Euler",
"intro": [
@@ -795,6 +787,24 @@
}
}
},
+ "the-odin-project": {
+ "title": "The Odin Project",
+ "intro": [
+ "The Odin Project is one of those \"What I wish I had when I was learning\" resources. ",
+ "Not everyone has access to a computer science education or the funds to attend an intensive coding school and neither of those is right for everyone anyway.",
+ "This project is designed to fill in the gap for people who are trying to hack it on their own but still want a high quality education."
+ ],
+ "blocks": {
+ "top-learn-html-foundations": {
+ "title": "Learn HTML Foundations",
+ "intro": ["A description is to be determined"]
+ },
+ "top-build-a-recipe-project": {
+ "title": "Learn HTML Foundations by Building a Recipe Page",
+ "intro": ["A description is to be determined"]
+ }
+ }
+ },
"misc-text": {
"certification": "Certificación de {{cert}}",
"browse-other": "Navega por nuestras otras certificaciones gratuitas\n(recomendamos hacerlo en orden)",
diff --git a/client/i18n/locales/espanol/translations.json b/client/i18n/locales/espanol/translations.json
index 969ab8661f6..e9b48effd1c 100644
--- a/client/i18n/locales/espanol/translations.json
+++ b/client/i18n/locales/espanol/translations.json
@@ -11,12 +11,12 @@
"view": "Ver",
"view-code": "Mostrar Código",
"view-project": "Mostrar Proyecto",
- "view-cert-title": "View {{certTitle}}",
+ "view-cert-title": "Ver {{certTitle}}",
"show-cert": "Mostrar certificación",
"claim-cert": "Solicitar certificación",
"save-progress": "Guardar progreso",
"accepted-honesty": "Has aceptado nuestra Política de Honestidad Académica.",
- "agree": "Aceptar",
+ "agree-honesty": "Estoy de acuerdo con la Política de Honestidad Académica de freeCodeCamp.",
"save-portfolio": "Guardar este elemento de portafolio",
"remove-portfolio": "Eliminar este elemento de portafolio",
"add-portfolio": "Agregar un nuevo elemento de portafolio",
@@ -53,7 +53,7 @@
"check-code": "Comprueba tu código (Ctrl + Enter)",
"check-code-2": "Comprueba tu código",
"reset": "Restablecer",
- "reset-step": "Reset This Step",
+ "reset-step": "Restablecer este paso",
"help": "Ayuda",
"get-help": "Obtener ayuda",
"watch-video": "Ver un Video",
@@ -159,7 +159,7 @@
"internet": "Tu presencia en Internet",
"portfolio": "Ajustes de portafolio",
"privacy": "Ajustes de privacidad",
- "personal-info": "Personal Information"
+ "personal-info": "Información Personal"
},
"danger": {
"heading": "Zona de peligro",
@@ -228,12 +228,12 @@
"page-number": "{{pageNumber}} de {{totalPages}}"
},
"footer": {
- "tax-exempt-status": "freeCodeCamp is a donor-supported tax-exempt 501(c)(3) charitable organization (United States Federal Tax Identification Number: 82-0779546)",
+ "tax-exempt-status": "freeCodeCamp es una organización benéfica 501(c)(3) exenta de impuestos apoyada por donantes (Número de Identificación Fiscal Federal De Los Estados Unidos: 82-0779546)",
"mission-statement": "Nuestra misión: ayudar a las personas a aprender a programar de forma gratuita. Logramos esto mediante la creación de miles de videos, artículos y lecciones de programación interactivas, todos disponibles gratuitamente para el público. También tenemos miles de grupos de estudio de FreeCodeCamp en todo el mundo.",
"donation-initiatives": "Las donaciones a freeCodeCamp se destinan a nuestras iniciativas educativas y ayudan a pagar los servidores, los servicios y el personal.",
"donate-text": "Puedes <1>hacer una donación deducible de impuestos aquí1>.",
"trending-guides": "Guías de tendencias",
- "our-nonprofit": "Our Charity",
+ "our-nonprofit": "Nuestra Caridad",
"links": {
"about": "Acerca de",
"alumni": "Red de ex-Alumnos",
@@ -274,9 +274,9 @@
"add-subtitles": "Ayudar a mejorar o agregar subtítulos",
"wrong-answer": "Lo siento, esa no es la respuesta correcta. ¡Vuelve a intentarlo!",
"check-answer": "Haz clic en el botón de abajo para verificar tu respuesta.",
- "assignment-not-complete": "Please finish the assignments",
- "assignments": "Assignments",
- "question": "Question",
+ "assignment-not-complete": "Por favor, completa las tareas",
+ "assignments": "Asignaciones",
+ "question": "Pregunta",
"solution-link": "Enlace a la solución",
"github-link": "Enlace de GitHub",
"submit-and-go": "Enviar y pasar a mi siguiente desafío",
@@ -302,7 +302,6 @@
"certs": "Certificación {{title}}"
},
"editor-tabs": {
- "info": "Info",
"code": "Código",
"tests": "Pruebas",
"restart": "Reiniciar",
@@ -336,15 +335,15 @@
"sorry-dont-giveup": "Lo sentimos, su código no pasa. No te rindas.",
"challenges-completed": "{{completedCount}} de {{totalChallenges}} desafíos completados",
"season-greetings-fcc": "Saludos de Temporada de la comunidad freeCodeCamp 🎉",
- "if-getting-value": "If you're getting a lot out of freeCodeCamp, now is a great time to donate to support our charity's mission.",
+ "if-getting-value": "Si estás obteniendo mucho de freeCodeCamp, ahora es un buen momento para donar con el fin de apoyar nuestra misión sin fines de lucro.",
"building-a-university": "Estamos construyendo un programa gratuito de grado universitario en ciencias de la computación",
"if-help-university": "Ya hemos hecho un montón de progresos. Apoyamos nuestra caridad con el largo camino por delante."
},
"donate": {
- "title": "Support our charity",
+ "title": "Apoya nuestra caridad",
"processing": "Estamos procesando tu donación.",
"redirecting": "Redirigiendo...",
- "thanks": "Thanks for donating",
+ "thanks": "Gracias por donar",
"thank-you": "Gracias por tu apoyo.",
"additional": "Puede hacer una donación adicional única de cualquier monto utilizando este enlace: <0>{{url}}0>",
"help-more": "Ayúdanos a hacer más",
@@ -364,9 +363,9 @@
"your-donation-2": "Tu donación de ${{usd}} proporcionará {{hours}} horas de aprendizaje a personas de todo el mundo cada mes.",
"your-donation-3": "Tu donación de ${{usd}} proporcionará {{hours}} horas de aprendizaje a personas de todo el mundo cada año.",
"become-supporter": "Conviértete en un colaborador",
- "duration": "Become a one-time supporter of our charity.",
- "duration-2": "Become a monthly supporter of our charity.",
- "duration-4": "Become a supporter of our charity",
+ "duration": "Se un benefactor de nuestra organización benéfica con una donación única.",
+ "duration-2": "Se un benefactor mensual de nuestra organización benéfica.",
+ "duration-4": "Se un benefactor de nuestra organización benéfica",
"nicely-done": "Bien hecho. Acabas de completar {{block}}.",
"credit-card": "Tarjeta de crédito",
"credit-card-2": "O dona con una tarjeta de crédito:",
@@ -380,25 +379,25 @@
"email-receipt": "Correo electrónico (te enviaremos un recibo de donación deducible de impuestos):",
"need-help": "¿Necesitas ayuda con tus donaciones actuales o pasadas?",
"forward-receipt": "Envía una copia de tu recibo de donación a donors@freecodecamp.org y dinos cómo podemos ayudar.",
- "efficiency": "freeCodeCamp is a highly efficient education charity.",
+ "efficiency": "freeCodeCamp es una organización benéfica educativa de gran eficacia.",
"why-donate-1": "Cuando donas a freeCodeCamp, ayudas a las personas a aprender nuevas habilidades y proveer para sus familias",
"why-donate-2": "También nos ayudas a crear nuevos recursos para que los utilices y amplíes tus propias habilidades tecnológicas.",
"bigger-donation": "¿Quieres hacer una donación más grande de una sola vez, envíanos un cheque o da de otras maneras?",
- "other-ways": "Here are many <0>other ways you can support our charity's mission0>.",
+ "other-ways": "Aquí hay muchas <0>otras formas de apoyar la misión de nuestra organización benéfica0>.",
"failed-pay": "Oh no. Parece que tu transacción no se realizó. ¿Podrías intentarlo de nuevo?",
"try-again": "Por favor, intenta de nuevo.",
"card-number": "Tu número de tarjeta:",
"expiration": "Fecha de vencimiento:",
"secure-donation": "Donación segura",
"faq": "Preguntas frecuentes",
- "only-you": "Only you can see this message. Congratulations on earning this certification. It's no easy task. Running freeCodeCamp isn't easy either. Nor is it cheap. Help us help you and many other people around the world. Make a tax-deductible supporting donation to our charity today.",
+ "only-you": "Sólo tu puedes ver este mensaje. Enhorabuena por haber obtenido esta certificación. No es tarea fácil. Administrar freeCodeCamp tampoco es fácil. Tampoco es barato. Ayúdanos a ayudarte a ti y a muchas otras personas de todo el mundo. Haz hoy una donación deducible de impuestos a nuestra organización benéfica.",
"get-help": "¿Cómo puedo obtener ayuda con mis donaciones?",
"how-transparent": "¿Qué tan transparente es freeCodeCamp.org?",
"very-transparent": "Muy Transparente. Tenemos una valoración de transparencia de platino en GuideStar.org.",
"download-irs": "Puedes <0>descargar nuestra Carta de Determinación de IRS aquí0>.",
"download-990": "Puedes <0>descargar nuestro 990 más reciente (informe anual de impuestos) aquí0>.",
"how-efficient": "¿Qué tan eficiente es freeCodeCamp?",
- "fcc-budget": "freeCodeCamp's budget is much smaller than most comparable charity. We haven't brought in professional fundraisers. Instead, Quincy does everything himself.",
+ "fcc-budget": "El presupuesto de freeCodeCamp es mucho menor que el de la mayoría de organizaciones benéficas comparables. No hemos contratado a profesionales para recaudar fondos. En su lugar, Quincy lo hace todo él mismo.",
"help-millions": "Sin embargo, con un presupuesto de tan sólo unos cientos de miles de dólares al año, hemos podido ayudar a millones de personas.",
"how-one-time": "¿Cómo puedo realizar una donación única?",
"one-time": "Si prefieres hacer donaciones únicas, puedes apoyar la misión de freeCodeCamp cuando tengas dinero suficiente para hacer una aportación. Puedes usar <0>este enlace para donar cualquier cantidad que te sea cómodo a través de PayPal0>.",
@@ -518,7 +517,7 @@
"opens-new-window": "Opens in new window"
},
"flash": {
- "honest-first": "Para reclamar una certificación, primero debes aceptar nuestra política de honestidad académica.",
+ "honest-first": "To claim a certification, you must first agree to our academic honesty policy",
"really-weird": "Sucedió algo realmente extraño. Si vuelve a ocurrir, considera hacer un reporte del problema en https://github.com/freeCodeCamp/freeCodeCamp/issues/new",
"not-right": "Algo no está bien. Se ha generado un informe y se ha notificado al equipo de freeCodeCamp.org",
"went-wrong": "Algo salió mal, verifica e intenta nuevamente",
diff --git a/client/i18n/locales/german/intro.json b/client/i18n/locales/german/intro.json
index 8a6cdac58c2..bfd275257ae 100644
--- a/client/i18n/locales/german/intro.json
+++ b/client/i18n/locales/german/intro.json
@@ -778,14 +778,6 @@
"Attribut: Rosetta Code"
]
},
- "the-odin-project": {
- "title": "The Odin Project",
- "intro": ["A description is to be determined"]
- },
- "the-odin-project-projects": {
- "title": "The Odin Project Projects",
- "intro": ["A description is to be determined"]
- },
"project-euler": {
"title": "Projekt Euler",
"intro": [
@@ -795,6 +787,24 @@
}
}
},
+ "the-odin-project": {
+ "title": "The Odin Project",
+ "intro": [
+ "The Odin Project is one of those \"What I wish I had when I was learning\" resources. ",
+ "Not everyone has access to a computer science education or the funds to attend an intensive coding school and neither of those is right for everyone anyway.",
+ "This project is designed to fill in the gap for people who are trying to hack it on their own but still want a high quality education."
+ ],
+ "blocks": {
+ "top-learn-html-foundations": {
+ "title": "Learn HTML Foundations",
+ "intro": ["A description is to be determined"]
+ },
+ "top-build-a-recipe-project": {
+ "title": "Learn HTML Foundations by Building a Recipe Page",
+ "intro": ["A description is to be determined"]
+ }
+ }
+ },
"misc-text": {
"certification": "{{cert}} Zertifikat",
"browse-other": "Stöbere in unseren anderen kostenlosen Zertifizierungen\n(Wir empfehlen, diese der Reihe nach zu erledigen)",
diff --git a/client/i18n/locales/german/translations.json b/client/i18n/locales/german/translations.json
index 6026281a64f..452779783dd 100644
--- a/client/i18n/locales/german/translations.json
+++ b/client/i18n/locales/german/translations.json
@@ -15,8 +15,8 @@
"show-cert": "Zertifikat anzeigen",
"claim-cert": "Zertifizierung anfordern",
"save-progress": "Fortschritt speichern",
- "accepted-honesty": "Du hast unsere Akademische Ehrlichkeitsrichtlinie akzeptiert.",
- "agree": "Zustimmen",
+ "accepted-honesty": "You have agreed to our Academic Honesty Policy.",
+ "agree-honesty": "I agree to freeCodeCamp's Academic Honesty Policy.",
"save-portfolio": "Dieses Portfolioelement speichern",
"remove-portfolio": "Dieses Portfolioelement entfernen",
"add-portfolio": "Neues Portfolioelement hinzufügen",
@@ -302,7 +302,6 @@
"certs": "{{title}} Zertifizierung"
},
"editor-tabs": {
- "info": "Informationen",
"code": "Code",
"tests": "Tests",
"restart": "Neustart",
@@ -518,7 +517,7 @@
"opens-new-window": "Opens in new window"
},
"flash": {
- "honest-first": "Um eine Zertifizierung zu erlangen, musst du zunächst unsere Richtlinie zur akademischen Ehrlichkeit akzeptieren",
+ "honest-first": "To claim a certification, you must first agree to our academic honesty policy",
"really-weird": "Etwas wirklich Seltsames ist passiert. Wenn es wieder passiert, erwäge bitte, einen Fehler auf https://github.com/freeCodeCamp/freeCodeCamp/issues/new zu melden.",
"not-right": "Irgendetwas ist nicht in Ordnung. Es wurde ein Bericht erstellt und das freeCodeCamp.org Team wurde benachrichtigt",
"went-wrong": "Etwas ist schief gelaufen, bitte überprüfe und versuche es erneut",
diff --git a/client/i18n/locales/italian/intro.json b/client/i18n/locales/italian/intro.json
index b5b1cfa3350..8a5f63f04c5 100644
--- a/client/i18n/locales/italian/intro.json
+++ b/client/i18n/locales/italian/intro.json
@@ -778,14 +778,6 @@
"Fonte: Codice Rosetta"
]
},
- "the-odin-project": {
- "title": "The Odin Project",
- "intro": ["A description is to be determined"]
- },
- "the-odin-project-projects": {
- "title": "The Odin Project Projects",
- "intro": ["A description is to be determined"]
- },
"project-euler": {
"title": "Progetto Eulero",
"intro": [
@@ -795,6 +787,24 @@
}
}
},
+ "the-odin-project": {
+ "title": "The Odin Project",
+ "intro": [
+ "The Odin Project is one of those \"What I wish I had when I was learning\" resources. ",
+ "Not everyone has access to a computer science education or the funds to attend an intensive coding school and neither of those is right for everyone anyway.",
+ "This project is designed to fill in the gap for people who are trying to hack it on their own but still want a high quality education."
+ ],
+ "blocks": {
+ "top-learn-html-foundations": {
+ "title": "Learn HTML Foundations",
+ "intro": ["A description is to be determined"]
+ },
+ "top-build-a-recipe-project": {
+ "title": "Learn HTML Foundations by Building a Recipe Page",
+ "intro": ["A description is to be determined"]
+ }
+ }
+ },
"misc-text": {
"certification": "Certificazione {{cert}}",
"browse-other": "Sfoglia le altre nostre certificazioni gratuite\n(consigliamo di seguirle in ordine)",
diff --git a/client/i18n/locales/italian/translations.json b/client/i18n/locales/italian/translations.json
index 5a582964c31..bb22c1ac79f 100644
--- a/client/i18n/locales/italian/translations.json
+++ b/client/i18n/locales/italian/translations.json
@@ -15,8 +15,8 @@
"show-cert": "Mostra la Certificazione",
"claim-cert": "Richiedi la Certificazione",
"save-progress": "Salva l'avanzamento",
- "accepted-honesty": "Hai accettato la nostra Politica di Onestà Accademica.",
- "agree": "Accetta",
+ "accepted-honesty": "You have agreed to our Academic Honesty Policy.",
+ "agree-honesty": "I agree to freeCodeCamp's Academic Honesty Policy.",
"save-portfolio": "Salva questo elemento del portfolio",
"remove-portfolio": "Rimuovi questo elemento del portfolio",
"add-portfolio": "Aggiungi un nuovo elemento nel portfolio",
@@ -302,7 +302,6 @@
"certs": "Certificazione {{title}}"
},
"editor-tabs": {
- "info": "Informazioni",
"code": "Codice",
"tests": "Test",
"restart": "Inizia da capo",
@@ -518,7 +517,7 @@
"opens-new-window": "Apri in una nuova finestra"
},
"flash": {
- "honest-first": "Per richiedere una certificazione, è necessario prima accettare la nostra politica di onestà accademica",
+ "honest-first": "To claim a certification, you must first agree to our academic honesty policy",
"really-weird": "È successo qualcosa di veramente strano, se succede di nuovo, ti preghiamo di considerare di sollevare un problema su https://github.com/freeCodeCamp/freeCodeCamp/issues/new",
"not-right": "Qualcosa non è del tutto giusto. Un rapporto è stato generato e il team freeCodeCamp.org è stato avvisato",
"went-wrong": "Qualcosa è andato storto, controlla e riprova",
diff --git a/client/i18n/locales/japanese/intro.json b/client/i18n/locales/japanese/intro.json
index 9af30b573f4..140caa5a7d9 100644
--- a/client/i18n/locales/japanese/intro.json
+++ b/client/i18n/locales/japanese/intro.json
@@ -778,14 +778,6 @@
"作: Rosetta Code"
]
},
- "the-odin-project": {
- "title": "The Odin Project",
- "intro": ["A description is to be determined"]
- },
- "the-odin-project-projects": {
- "title": "The Odin Project Projects",
- "intro": ["A description is to be determined"]
- },
"project-euler": {
"title": "プロジェクト・オイラー",
"intro": [
@@ -795,6 +787,24 @@
}
}
},
+ "the-odin-project": {
+ "title": "The Odin Project",
+ "intro": [
+ "The Odin Project is one of those \"What I wish I had when I was learning\" resources. ",
+ "Not everyone has access to a computer science education or the funds to attend an intensive coding school and neither of those is right for everyone anyway.",
+ "This project is designed to fill in the gap for people who are trying to hack it on their own but still want a high quality education."
+ ],
+ "blocks": {
+ "top-learn-html-foundations": {
+ "title": "Learn HTML Foundations",
+ "intro": ["A description is to be determined"]
+ },
+ "top-build-a-recipe-project": {
+ "title": "Learn HTML Foundations by Building a Recipe Page",
+ "intro": ["A description is to be determined"]
+ }
+ }
+ },
"misc-text": {
"certification": "{{cert}} 認定",
"browse-other": "他の無料の認定講座を閲覧する\n(上から順に受講することをお勧めします)",
diff --git a/client/i18n/locales/japanese/translations.json b/client/i18n/locales/japanese/translations.json
index 778950dbae5..fa2cb2b43c2 100644
--- a/client/i18n/locales/japanese/translations.json
+++ b/client/i18n/locales/japanese/translations.json
@@ -15,8 +15,8 @@
"show-cert": "認定証を表示",
"claim-cert": "認定証を取得",
"save-progress": "進行状況を保存",
- "accepted-honesty": "学問的誠実性ポリシーに同意しました。",
- "agree": "同意する",
+ "accepted-honesty": "You have agreed to our Academic Honesty Policy.",
+ "agree-honesty": "I agree to freeCodeCamp's Academic Honesty Policy.",
"save-portfolio": "このポートフォリオアイテムを保存",
"remove-portfolio": "このポートフォリオアイテムを削除",
"add-portfolio": "新規ポートフォリオアイテムを追加",
@@ -302,7 +302,6 @@
"certs": "{{title}} 認定講座"
},
"editor-tabs": {
- "info": "詳細",
"code": "コード",
"tests": "テスト",
"restart": "リスタート",
@@ -518,7 +517,7 @@
"opens-new-window": "新しいウィンドウで開く"
},
"flash": {
- "honest-first": "認定証を請求するには、まず学問的誠実性ポリシーに同意する必要があります。",
+ "honest-first": "To claim a certification, you must first agree to our academic honesty policy",
"really-weird": "予期しない問題が発生しました。この問題が何度も発生するようであれば、https://github.com/freeCodeCamp/freeCodeCamp/issues/new への Issue 登録をご検討ください。",
"not-right": "問題が発生しました。レポートが生成され、freeCodeCamp.org チームへ通知されました。",
"went-wrong": "問題が発生しました。ご確認の上もう一度お試しください。",
diff --git a/client/i18n/locales/portuguese/intro.json b/client/i18n/locales/portuguese/intro.json
index 230c7ef5be1..a0262686a7b 100644
--- a/client/i18n/locales/portuguese/intro.json
+++ b/client/i18n/locales/portuguese/intro.json
@@ -778,14 +778,6 @@
"Attribute: Rosetta Code"
]
},
- "the-odin-project": {
- "title": "The Odin Project",
- "intro": ["Uma descrição deve ser determinada"]
- },
- "the-odin-project-projects": {
- "title": "Os projetos do The Odin Project",
- "intro": ["Uma descrição deve ser determinada"]
- },
"project-euler": {
"title": "Projeto Euler",
"intro": [
@@ -795,6 +787,24 @@
}
}
},
+ "the-odin-project": {
+ "title": "The Odin Project",
+ "intro": [
+ "The Odin Project é um daqueles recursos do tipo \"O que eu gostaria de ter visto quando estava aprendendo\". ",
+ "Nem todas as pessoas têm acesso à educação em ciência da computação ou aos fundos necessários para frequentar uma escola de programação intensiva. De qualquer modo, não necessariamente, essas duas sejam a solução final para todos que queiram aprender.",
+ "Este projeto destina-se a preencher a lacuna para aquelas pessoas que tentam buscar suas próprias soluções, mas que continuam procurando uma educação de alta qualidade."
+ ],
+ "blocks": {
+ "top-learn-html-foundations": {
+ "title": "Aprenda o básico de HTML",
+ "intro": ["Uma descrição deve ser determinada"]
+ },
+ "top-build-a-recipe-project": {
+ "title": "Aprenda o básico de HTML criando uma página de receitas",
+ "intro": ["Uma descrição deve ser determinada"]
+ }
+ }
+ },
"misc-text": {
"certification": "Certificação {{cert}}",
"browse-other": "Navegue por nossas outras certificações gratuitas\n(recomendamos fazer isto em sequência)",
diff --git a/client/i18n/locales/portuguese/translations.json b/client/i18n/locales/portuguese/translations.json
index 1d3aaf2d105..8d45abc16c5 100644
--- a/client/i18n/locales/portuguese/translations.json
+++ b/client/i18n/locales/portuguese/translations.json
@@ -15,8 +15,8 @@
"show-cert": "Exibir certificado",
"claim-cert": "Solicitar certificação",
"save-progress": "Salvar progresso",
- "accepted-honesty": "Você aceitou nossa política de honestidade acadêmica.",
- "agree": "Aceitar",
+ "accepted-honesty": "Você concordou com nossa política de honestidade acadêmica.",
+ "agree-honesty": "Concordo com a Política de Honestidade Acadêmica do FreeCodeCamp.",
"save-portfolio": "Salvar esse item de portfólio",
"remove-portfolio": "Remover este item de portfólio",
"add-portfolio": "Adicionar um novo item de portfólio",
@@ -302,7 +302,6 @@
"certs": "Certificação {{title}}"
},
"editor-tabs": {
- "info": "Informações",
"code": "Código",
"tests": "Testes",
"restart": "Reiniciar",
@@ -518,7 +517,7 @@
"opens-new-window": "Abre em uma nova janela"
},
"flash": {
- "honest-first": "Para solicitar uma certificação, você precisa primeiro aceitar nossa política de honestidade acadêmica",
+ "honest-first": "Para solicitar uma certificação, você precisa primeiro concordar com nossa política de honestidade acadêmica",
"really-weird": "Algo realmente estranho aconteceu. Se acontecer novamente, considere apresentar um problema pelo endereço https://github.com/freeCodeCamp/freeCodeCamp/issues/new",
"not-right": "Algo não está certo. Um relatório foi gerado e a equipe do freeCodeCamp.org foi notificada",
"went-wrong": "Algo deu errado. Verifique e tente novamente",
diff --git a/client/i18n/locales/ukrainian/intro.json b/client/i18n/locales/ukrainian/intro.json
index 8ebbe1516d3..ec06abf9f61 100644
--- a/client/i18n/locales/ukrainian/intro.json
+++ b/client/i18n/locales/ukrainian/intro.json
@@ -778,14 +778,6 @@
"Атрибут: Rosetta Code"
]
},
- "the-odin-project": {
- "title": "Проєкт «Odin»",
- "intro": ["Опис буде надано пізніше"]
- },
- "the-odin-project-projects": {
- "title": "Проєкти «Odin»",
- "intro": ["Опис буде надано пізніше"]
- },
"project-euler": {
"title": "Проєкт «Ейлер»",
"intro": [
@@ -795,6 +787,24 @@
}
}
},
+ "the-odin-project": {
+ "title": "Проєкт «Odin»",
+ "intro": [
+ "Проєкт «Odin» є одним з тих ресурсів, про які хотіли б дізнатися ще коли навчались. ",
+ "Не кожен має доступ до технологічної освіти або коштів, необхідних для відвідування інтенсивної школи. Однак це не остаточне рішення для тих, хто хоче вчитися.",
+ "Цей проєкт розроблений, щоб заповнити прогалину для людей, які намагаються вчитись самостійно, але все ж таки хочуть високоякісну освіту."
+ ],
+ "blocks": {
+ "top-learn-html-foundations": {
+ "title": "Вивчіть основи HTML",
+ "intro": ["Опис буде надано пізніше"]
+ },
+ "top-build-a-recipe-project": {
+ "title": "Вивчіть основи HTML, побудувавши сторінку з рецептами",
+ "intro": ["Опис буде надано пізніше"]
+ }
+ }
+ },
"misc-text": {
"certification": "Сертифікація «{{cert}}»",
"browse-other": "Перегляньте інші безоплатні сертифікації\n(ми рекомендуємо виконувати їх послідовно)",
diff --git a/client/i18n/locales/ukrainian/translations.json b/client/i18n/locales/ukrainian/translations.json
index 8f33c528b3c..6d28efd4d02 100644
--- a/client/i18n/locales/ukrainian/translations.json
+++ b/client/i18n/locales/ukrainian/translations.json
@@ -15,8 +15,8 @@
"show-cert": "Показати сертифікацію",
"claim-cert": "Отримати сертифікацію",
"save-progress": "Зберегти прогрес",
- "accepted-honesty": "Ви прийняли нашу Політику академічної доброчесності.",
- "agree": "Прийняти",
+ "accepted-honesty": "Ви погодились з нашою політикою академічної доброчесності.",
+ "agree-honesty": "Я погоджуюсь з політикою академічної доброчесності freeCodeCamp.",
"save-portfolio": "Зберегти цей елемент портфоліо",
"remove-portfolio": "Видалити цей елемент портфоліо",
"add-portfolio": "Додати новий елемент портфоліо",
@@ -302,7 +302,6 @@
"certs": "Сертифікація «{{title}}»"
},
"editor-tabs": {
- "info": "Інформація",
"code": "Код",
"tests": "Тести",
"restart": "Перезапустити",
@@ -518,7 +517,7 @@
"opens-new-window": "Відкривається у новому вікні"
},
"flash": {
- "honest-first": "Щоб отримати сертифікацію, ви повинні спочатку прийняти нашу політику академічної доброчесності",
+ "honest-first": "Щоб отримати сертифікацію, ви повинні спочатку погодитись з нашою політикою академічної доброчесності",
"really-weird": "Щось пішло не так. Якщо це повториться, будь ласка, повідомте про це за посиланням: https://github.com/freeCodeCamp/freeCodeCamp/issues/new",
"not-right": "Щось пішло не так. Звіт було сформовано і команду freeCodeCamp.org вже сповістили.",
"went-wrong": "Щось пішло не так. Будь ласка, перевірте та повторіть спробу.",
diff --git a/client/i18n/schema-validation.ts b/client/i18n/schema-validation.ts
index 02c0e86a1b6..d9ccd807d23 100644
--- a/client/i18n/schema-validation.ts
+++ b/client/i18n/schema-validation.ts
@@ -215,7 +215,7 @@ const schemaValidation = (
if (
fileName === 'motivation' &&
!(fileJson.motivationalQuotes as MotivationalQuotes).every(
- (object: object) =>
+ object =>
Object.prototype.hasOwnProperty.call(object, 'quote') &&
Object.prototype.hasOwnProperty.call(object, 'author')
)
diff --git a/client/package.json b/client/package.json
index 5ca557ff12f..bc3884d4532 100644
--- a/client/package.json
+++ b/client/package.json
@@ -39,16 +39,16 @@
"@babel/preset-env": "7.20.2",
"@babel/preset-react": "7.18.6",
"@babel/standalone": "7.20.15",
- "@fortawesome/fontawesome-svg-core": "6.2.1",
- "@fortawesome/free-brands-svg-icons": "6.2.1",
- "@fortawesome/free-solid-svg-icons": "6.2.1",
+ "@fortawesome/fontawesome-svg-core": "6.3.0",
+ "@fortawesome/free-brands-svg-icons": "6.3.0",
+ "@fortawesome/free-solid-svg-icons": "6.3.0",
"@fortawesome/react-fontawesome": "0.2.0",
"@freecodecamp/curriculum-helpers": "1.1.0",
"@freecodecamp/loop-protect": "3.0.0",
"@freecodecamp/react-bootstrap": "0.32.3",
- "@freecodecamp/react-calendar-heatmap": "1.0.0",
+ "@freecodecamp/react-calendar-heatmap": "1.1.0",
"@freecodecamp/strip-comments": "3.0.1",
- "@growthbook/growthbook-react": "0.11.1",
+ "@growthbook/growthbook-react": "0.11.2",
"@loadable/component": "5.15.3",
"@reach/router": "1.3.4",
"@sentry/gatsby": "6.19.7",
@@ -133,8 +133,8 @@
},
"devDependencies": {
"@babel/types": "7.20.7",
- "@codesee/babel-plugin-instrument": "0.494.0",
- "@codesee/tracker": "0.494.0",
+ "@codesee/babel-plugin-instrument": "0.498.0",
+ "@codesee/tracker": "0.498.0",
"@testing-library/jest-dom": "5.16.5",
"@testing-library/react": "12.1.5",
"@types/react-gtm-module": "2.0.1",
diff --git a/client/src/__mocks__/fileMock.ts b/client/src/__mocks__/file-mock.ts
similarity index 100%
rename from client/src/__mocks__/fileMock.ts
rename to client/src/__mocks__/file-mock.ts
diff --git a/client/src/__mocks__/styleMock.ts b/client/src/__mocks__/style-mock.ts
similarity index 100%
rename from client/src/__mocks__/styleMock.ts
rename to client/src/__mocks__/style-mock.ts
diff --git a/client/src/assets/icons/API-icon.tsx b/client/src/assets/icons/api.tsx
similarity index 100%
rename from client/src/assets/icons/API-icon.tsx
rename to client/src/assets/icons/api.tsx
diff --git a/client/src/assets/icons/certification-icon.tsx b/client/src/assets/icons/certification.tsx
similarity index 100%
rename from client/src/assets/icons/certification-icon.tsx
rename to client/src/assets/icons/certification.tsx
diff --git a/client/src/assets/icons/D3-icon.tsx b/client/src/assets/icons/d3.tsx
similarity index 100%
rename from client/src/assets/icons/D3-icon.tsx
rename to client/src/assets/icons/d3.tsx
diff --git a/client/src/assets/icons/Database-icon.tsx b/client/src/assets/icons/database.tsx
similarity index 100%
rename from client/src/assets/icons/Database-icon.tsx
rename to client/src/assets/icons/database.tsx
diff --git a/client/src/assets/icons/FreeCodeCamp-logo.tsx b/client/src/assets/icons/freecodecamp.tsx
similarity index 100%
rename from client/src/assets/icons/FreeCodeCamp-logo.tsx
rename to client/src/assets/icons/freecodecamp.tsx
diff --git a/client/src/assets/icons/index.tsx b/client/src/assets/icons/index.tsx
index 4adcec517a3..839f2090965 100644
--- a/client/src/assets/icons/index.tsx
+++ b/client/src/assets/icons/index.tsx
@@ -1,17 +1,18 @@
import React from 'react';
import { SuperBlocks } from '../../../../config/certification-settings';
-import APIIcon from './API-icon';
-import D3Icon from './D3-icon';
-import DatabaseIcon from './Database-icon';
-import JavaScriptIcon from './JavaScript-icon';
-import ReactIcon from './React-icon';
-import TensorflowIcon from './Tensorflow-icon';
+import APIIcon from './api';
+import D3Icon from './d3';
+import DatabaseIcon from './database';
+import JavaScriptIcon from './javascript';
+import ReactIcon from './react';
+import TensorflowIcon from './tensorflow';
import Algorithm from './algorithm';
import Analytics from './analytics';
import Clipboard from './clipboard';
-import PythonIcon from './python-icon';
+import PythonIcon from './python';
import ResponsiveDesign from './responsive-design';
import Shield from './shield';
+import VikingHelmet from './viking-helmet';
const iconMap = {
[SuperBlocks.RespWebDesignNew]: ResponsiveDesign,
@@ -27,7 +28,8 @@ const iconMap = {
[SuperBlocks.DataAnalysisPy]: Analytics,
[SuperBlocks.InfoSec]: Shield,
[SuperBlocks.MachineLearningPy]: TensorflowIcon,
- [SuperBlocks.CodingInterviewPrep]: Algorithm
+ [SuperBlocks.CodingInterviewPrep]: Algorithm,
+ [SuperBlocks.TheOdinProject]: VikingHelmet
};
const generateIconComponent = (
diff --git a/client/src/assets/icons/inputReset.tsx b/client/src/assets/icons/input-reset.tsx
similarity index 100%
rename from client/src/assets/icons/inputReset.tsx
rename to client/src/assets/icons/input-reset.tsx
diff --git a/client/src/assets/icons/JavaScript-icon.tsx b/client/src/assets/icons/javascript.tsx
similarity index 100%
rename from client/src/assets/icons/JavaScript-icon.tsx
rename to client/src/assets/icons/javascript.tsx
diff --git a/client/src/assets/icons/Magnifier.tsx b/client/src/assets/icons/magnifier.tsx
similarity index 100%
rename from client/src/assets/icons/Magnifier.tsx
rename to client/src/assets/icons/magnifier.tsx
diff --git a/client/src/assets/icons/python-icon.tsx b/client/src/assets/icons/python.tsx
similarity index 100%
rename from client/src/assets/icons/python-icon.tsx
rename to client/src/assets/icons/python.tsx
diff --git a/client/src/assets/icons/React-icon.tsx b/client/src/assets/icons/react.tsx
similarity index 100%
rename from client/src/assets/icons/React-icon.tsx
rename to client/src/assets/icons/react.tsx
diff --git a/client/src/assets/icons/Tensorflow-icon.tsx b/client/src/assets/icons/tensorflow.tsx
similarity index 100%
rename from client/src/assets/icons/Tensorflow-icon.tsx
rename to client/src/assets/icons/tensorflow.tsx
diff --git a/client/src/assets/icons/viking-helmet.tsx b/client/src/assets/icons/viking-helmet.tsx
new file mode 100644
index 00000000000..ab29caa29f3
--- /dev/null
+++ b/client/src/assets/icons/viking-helmet.tsx
@@ -0,0 +1,17 @@
+import React from 'react';
+
+function VikingHelmet(
+ props: JSX.IntrinsicAttributes & React.SVGProps
+): JSX.Element {
+ return (
+ <>
+
+ >
+ );
+}
+
+VikingHelmet.displayName = 'VikingHelmet';
+
+export default VikingHelmet;
diff --git a/client/src/client-only-routes/show-certification.tsx b/client/src/client-only-routes/show-certification.tsx
index 6ac89c242ed..6edff2f96a5 100644
--- a/client/src/client-only-routes/show-certification.tsx
+++ b/client/src/client-only-routes/show-certification.tsx
@@ -9,7 +9,7 @@ import { createSelector } from 'reselect';
import envData from '../../../config/env.json';
import { getLangCode } from '../../../config/i18n';
-import FreeCodeCampLogo from '../assets/icons/FreeCodeCamp-logo';
+import FreeCodeCampLogo from '../assets/icons/freecodecamp';
import DonateForm from '../components/Donation/donate-form';
import { createFlashMessage } from '../components/Flash/redux';
diff --git a/client/src/components/Header/components/nav-logo.tsx b/client/src/components/Header/components/nav-logo.tsx
index 94111b149c7..393b8f32df0 100644
--- a/client/src/components/Header/components/nav-logo.tsx
+++ b/client/src/components/Header/components/nav-logo.tsx
@@ -1,6 +1,6 @@
import React from 'react';
import { useTranslation } from 'react-i18next';
-import FreeCodeCampLogo from '../../../assets/icons/FreeCodeCamp-logo';
+import FreeCodeCampLogo from '../../../assets/icons/freecodecamp';
const NavLogo = (): JSX.Element => {
const { t } = useTranslation();
diff --git a/client/src/components/Intro/intro.test.tsx b/client/src/components/Intro/intro.test.tsx
index d1000de9fa0..61a55fe76f0 100644
--- a/client/src/components/Intro/intro.test.tsx
+++ b/client/src/components/Intro/intro.test.tsx
@@ -1,7 +1,7 @@
import React from 'react';
import { Provider } from 'react-redux';
import renderer from 'react-test-renderer';
-import { createStore } from '../../redux/createStore';
+import { createStore } from '../../redux/create-store';
import Intro from '.';
diff --git a/client/src/components/SolutionViewer/SolutionViewer.tsx b/client/src/components/SolutionViewer/SolutionViewer.tsx
index 255d9bf607d..841146b438b 100644
--- a/client/src/components/SolutionViewer/SolutionViewer.tsx
+++ b/client/src/components/SolutionViewer/SolutionViewer.tsx
@@ -9,7 +9,7 @@ type Props = {
};
type Solution = Pick;
-function SolutionViewer({ challengeFiles, solution }: Props) {
+function SolutionViewer({ challengeFiles, solution }: Props): JSX.Element {
const isLegacy = !challengeFiles || !challengeFiles.length;
const solutions = isLegacy
? [
diff --git a/client/src/components/app-mount-notifier.test.tsx b/client/src/components/app-mount-notifier.test.tsx
index 94121daba6c..215d99c94c0 100644
--- a/client/src/components/app-mount-notifier.test.tsx
+++ b/client/src/components/app-mount-notifier.test.tsx
@@ -5,7 +5,7 @@ import { Provider } from 'react-redux';
import { i18nextCodes } from '../../../config/i18n';
import i18nTestConfig from '../../i18n/config-for-tests';
-import { createStore } from '../redux/createStore';
+import { createStore } from '../redux/create-store';
import AppMountNotifier from './app-mount-notifier';
jest.unmock('react-i18next');
diff --git a/client/src/components/formHelpers/__snapshots__/block-save-wrapper.test.tsx.snap b/client/src/components/formHelpers/__snapshots__/block-save-wrapper.test.tsx.snap
deleted file mode 100644
index b088aec7c3c..00000000000
--- a/client/src/components/formHelpers/__snapshots__/block-save-wrapper.test.tsx.snap
+++ /dev/null
@@ -1,9 +0,0 @@
-// Jest Snapshot v1, https://goo.gl/fbAQLP
-
-exports[` snapshot 1`] = `
-
diff --git a/client/src/utils/ajax.ts b/client/src/utils/ajax.ts
index fbf3b19459a..b284b6924d8 100644
--- a/client/src/utils/ajax.ts
+++ b/client/src/utils/ajax.ts
@@ -136,6 +136,8 @@ function parseApiResponseToClientUser(data: ApiUser): UserResponse {
};
}
+// TODO: this at least needs a few aliases so it's human readable
+// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types
export function mapFilesToChallengeFiles(
fileContainer: ({ files: (File & { key: string })[] } & Rest)[] = []
) {
diff --git a/client/src/utils/challenge-request-helpers.ts b/client/src/utils/challenge-request-helpers.ts
index 3451f372437..d4017894beb 100644
--- a/client/src/utils/challenge-request-helpers.ts
+++ b/client/src/utils/challenge-request-helpers.ts
@@ -13,11 +13,25 @@ interface StandardizeRequestBodyArgs {
challengeType: number;
}
+interface File {
+ contents: string;
+ ext: string;
+ history: string[];
+ key: string;
+ name: string;
+}
+
+interface Body {
+ id: string;
+ files?: File[];
+ challengeType: number;
+}
+
export function standardizeRequestBody({
id,
challengeFiles = [],
challengeType
-}: StandardizeRequestBodyArgs) {
+}: StandardizeRequestBodyArgs): Body {
return {
id,
files: challengeFiles?.map(({ fileKey, contents, ext, name, history }) => {
@@ -33,12 +47,12 @@ export function standardizeRequestBody({
};
}
-export function getStringSizeInBytes(str = '') {
+export function getStringSizeInBytes(str = ''): number {
const stringSizeInBytes = new Blob([JSON.stringify(str)]).size;
return stringSizeInBytes;
}
-export function bodySizeFits(bodySizeInBytes: number) {
+export function bodySizeFits(bodySizeInBytes: number): boolean {
return bodySizeInBytes <= MAX_BODY_SIZE;
}
diff --git a/client/src/utils/report-error.ts b/client/src/utils/report-error.ts
index d58bc024ba5..4d4bc6cdc43 100644
--- a/client/src/utils/report-error.ts
+++ b/client/src/utils/report-error.ts
@@ -1,4 +1,4 @@
-import * as Sentry from '@sentry/gatsby';
+import { captureException } from '@sentry/gatsby';
import envData from '../../../config/env.json';
const { sentryClientDSN } = envData;
@@ -11,5 +11,5 @@ export function reportClientSideError(
): string | void {
return sentryClientDSN === null
? console.error(`Client: ${message}`, e)
- : Sentry.captureException(e);
+ : captureException(e);
}
diff --git a/client/src/utils/solution-display-type.ts b/client/src/utils/solution-display-type.ts
index 8c1be9198b3..101553f5286 100644
--- a/client/src/utils/solution-display-type.ts
+++ b/client/src/utils/solution-display-type.ts
@@ -2,12 +2,20 @@ import type { CompletedChallenge } from '../redux/prop-types';
import { challengeTypes } from '../../utils/challenge-types';
import { maybeUrlRE } from '.';
+// eslint-disable-next-line @typescript-eslint/naming-convention
+type DisplayType =
+ | 'none'
+ | 'showMultifileProjectSolution'
+ | 'showUserCode'
+ | 'showProjectAndGithubLinks'
+ | 'showProjectLink';
+
export const getSolutionDisplayType = ({
solution,
githubLink,
challengeFiles,
challengeType
-}: CompletedChallenge) => {
+}: CompletedChallenge): DisplayType => {
if (challengeFiles?.length)
return challengeType === challengeTypes.multifileCertProject
? 'showMultifileProjectSolution'
diff --git a/client/src/utils/superblock-map-titles.ts b/client/src/utils/superblock-map-titles.ts
index fa2ec32c994..dae9af0dcba 100644
--- a/client/src/utils/superblock-map-titles.ts
+++ b/client/src/utils/superblock-map-titles.ts
@@ -9,9 +9,12 @@ enum SuperBlockI18nKeys {
// the key above is used to create the last word for superBlock titles used on
// the map and window. e.g. 'Certification' in Responsive Web Design
// Certification
-const superBlocksWithoutLastWord = [SuperBlocks.CodingInterviewPrep];
+const superBlocksWithoutLastWord = [
+ SuperBlocks.CodingInterviewPrep,
+ SuperBlocks.TheOdinProject
+];
-export function getSuperBlockTitleForMap(superBlock: SuperBlocks) {
+export function getSuperBlockTitleForMap(superBlock: SuperBlocks): string {
const i18nSuperBlock = i18next.t(`intro:${superBlock}.title`);
return superBlocksWithoutLastWord.includes(superBlock)
diff --git a/client/utils/gatsby/layout-selector.test.tsx b/client/utils/gatsby/layout-selector.test.tsx
index ac769f44d1a..f56a25a68fb 100644
--- a/client/utils/gatsby/layout-selector.test.tsx
+++ b/client/utils/gatsby/layout-selector.test.tsx
@@ -6,7 +6,7 @@ import ShallowRenderer from 'react-test-renderer/shallow';
import FourOhFourPage from '../../src/pages/404';
import Certification from '../../src/pages/certification';
import Learn from '../../src/pages/learn';
-import { createStore } from '../../src/redux/createStore';
+import { createStore } from '../../src/redux/create-store';
import layoutSelector from './layout-selector';
jest.mock('../../src/analytics');
diff --git a/client/utils/help-category-map.json b/client/utils/help-category-map.json
index f4f141c7b83..40ea39ab04e 100644
--- a/client/utils/help-category-map.json
+++ b/client/utils/help-category-map.json
@@ -42,8 +42,8 @@
"algorithms": "JavaScript",
"data-structures": "JavaScript",
"take-home-projects": "JavaScript",
- "the-odin-project": "HTML-CSS",
- "the-odin-project-projects": "HTML-CSS",
+ "top-learn-html-foundations": "HTML-CSS",
+ "top-build-a-recipe-project": "HTML-CSS",
"rosetta-code": "JavaScript",
"project-euler": "JavaScript",
"scientific-computing-with-python": "Python",
diff --git a/config/certification-settings.ts b/config/certification-settings.ts
index aea15811b79..dd7f943c707 100644
--- a/config/certification-settings.ts
+++ b/config/certification-settings.ts
@@ -31,7 +31,8 @@ export enum SuperBlocks {
DataAnalysisPy = 'data-analysis-with-python',
InfoSec = 'information-security',
MachineLearningPy = 'machine-learning-with-python',
- CodingInterviewPrep = 'coding-interview-prep'
+ CodingInterviewPrep = 'coding-interview-prep',
+ TheOdinProject = 'the-odin-project'
}
export const certIds = {
diff --git a/config/donation-settings.ts b/config/donation-settings.ts
index 4196a53f475..0ade9659fc6 100644
--- a/config/donation-settings.ts
+++ b/config/donation-settings.ts
@@ -80,8 +80,22 @@ export const paypalConfigTypes = {
}
};
+type DonationAmount = 500 | 1000 | 2000 | 3000 | 4000 | 5000;
+
+interface OneTimeConfig {
+ amount: DonationAmount;
+ duration: 'one-time';
+ planId: null;
+}
+
+interface SubscriptionConfig {
+ amount: DonationAmount;
+ duration: 'month';
+ planId: string;
+}
+
export const paypalConfigurator = (
- donationAmount: 500 | 1000 | 2000 | 3000 | 4000 | 5000,
+ donationAmount: DonationAmount,
donationDuration: 'one-time' | 'month',
paypalConfig: {
month: {
@@ -93,7 +107,7 @@ export const paypalConfigurator = (
5000: { planId: string };
};
}
-) => {
+): OneTimeConfig | SubscriptionConfig => {
if (donationDuration === 'one-time') {
return { amount: donationAmount, duration: donationDuration, planId: null };
}
diff --git a/config/i18n.ts b/config/i18n.ts
index 9fe7a6311d2..786c9a54261 100644
--- a/config/i18n.ts
+++ b/config/i18n.ts
@@ -68,7 +68,7 @@ export const i18nextCodes = {
};
// These are for the language selector dropdown menu in the footer
-export const LangNames = {
+export const LangNames: { [key: string]: string } = {
[Languages.English]: 'English',
[Languages.Espanol]: 'Español',
[Languages.Chinese]: '中文(简体字)',
@@ -111,7 +111,7 @@ export const rtlLangs = ['arabic'];
// locale is sourced from a JSON file, so we use getLangCode to
// find the associated enum values
-export function getLangCode(locale: PropertyKey) {
+export function getLangCode(locale: PropertyKey): string {
if (isPropertyOf(LangCodes, locale)) return LangCodes[locale];
throw new Error(`${String(locale)} is not a valid locale`);
}
diff --git a/config/superblock-order.test.ts b/config/superblock-order.test.ts
index 233f7c4d60c..cf21825a48b 100644
--- a/config/superblock-order.test.ts
+++ b/config/superblock-order.test.ts
@@ -150,6 +150,7 @@ describe("'superBlockOrder' helper functions", () => {
SuperBlocks.MachineLearningPy,
SuperBlocks.CodingInterviewPrep,
SuperBlocks.JsAlgoDataStructNew,
+ SuperBlocks.TheOdinProject,
SuperBlocks.RespWebDesign
];
expect(learnSuperBlocks).toStrictEqual(test);
@@ -188,7 +189,8 @@ describe("'superBlockOrder' helper functions", () => {
SuperBlocks.InfoSec,
SuperBlocks.MachineLearningPy,
SuperBlocks.CodingInterviewPrep,
- SuperBlocks.JsAlgoDataStructNew
+ SuperBlocks.JsAlgoDataStructNew,
+ SuperBlocks.TheOdinProject
];
expect(notAuditedSuperBlocks).toStrictEqual(test);
expect(notAuditedSuperBlocks.length).toEqual(test.length);
diff --git a/config/superblock-order.ts b/config/superblock-order.ts
index 8be124ca9e5..8857c06a228 100644
--- a/config/superblock-order.ts
+++ b/config/superblock-order.ts
@@ -78,7 +78,8 @@ export const defaultSuperBlockOrder: SuperBlocks[] = [
SuperBlocks.DataAnalysisPy,
SuperBlocks.InfoSec,
SuperBlocks.MachineLearningPy,
- SuperBlocks.CodingInterviewPrep
+ SuperBlocks.CodingInterviewPrep,
+ SuperBlocks.TheOdinProject
];
/*
@@ -124,7 +125,10 @@ export const superBlockOrder: SuperBlockOrder = {
SuperBlocks.CodingInterviewPrep
],
[SuperBlockStates.New]: [],
- [SuperBlockStates.Upcoming]: [SuperBlocks.JsAlgoDataStructNew],
+ [SuperBlockStates.Upcoming]: [
+ SuperBlocks.JsAlgoDataStructNew,
+ SuperBlocks.TheOdinProject
+ ],
[SuperBlockStates.Legacy]: [SuperBlocks.RespWebDesign]
},
[TranslationStates.NotAudited]: {
@@ -173,7 +177,10 @@ export const superBlockOrder: SuperBlockOrder = {
SuperBlocks.CodingInterviewPrep
],
[SuperBlockStates.New]: [],
- [SuperBlockStates.Upcoming]: [SuperBlocks.JsAlgoDataStructNew],
+ [SuperBlockStates.Upcoming]: [
+ SuperBlocks.JsAlgoDataStructNew,
+ SuperBlocks.TheOdinProject
+ ],
[SuperBlockStates.Legacy]: []
}
}
@@ -216,7 +223,10 @@ export const superBlockOrder: SuperBlockOrder = {
SuperBlocks.CodingInterviewPrep
],
[SuperBlockStates.New]: [],
- [SuperBlockStates.Upcoming]: [SuperBlocks.JsAlgoDataStructNew],
+ [SuperBlockStates.Upcoming]: [
+ SuperBlocks.JsAlgoDataStructNew,
+ SuperBlocks.TheOdinProject
+ ],
[SuperBlockStates.Legacy]: []
}
}
@@ -259,7 +269,10 @@ export const superBlockOrder: SuperBlockOrder = {
SuperBlocks.CodingInterviewPrep
],
[SuperBlockStates.New]: [],
- [SuperBlockStates.Upcoming]: [SuperBlocks.JsAlgoDataStructNew],
+ [SuperBlockStates.Upcoming]: [
+ SuperBlocks.JsAlgoDataStructNew,
+ SuperBlocks.TheOdinProject
+ ],
[SuperBlockStates.Legacy]: []
}
}
@@ -301,7 +314,10 @@ export const superBlockOrder: SuperBlockOrder = {
[TranslationStates.NotAudited]: {
[SuperBlockStates.Current]: [],
[SuperBlockStates.New]: [],
- [SuperBlockStates.Upcoming]: [SuperBlocks.JsAlgoDataStructNew],
+ [SuperBlockStates.Upcoming]: [
+ SuperBlocks.JsAlgoDataStructNew,
+ SuperBlocks.TheOdinProject
+ ],
[SuperBlockStates.Legacy]: []
}
}
@@ -343,7 +359,10 @@ export const superBlockOrder: SuperBlockOrder = {
[TranslationStates.NotAudited]: {
[SuperBlockStates.Current]: [],
[SuperBlockStates.New]: [],
- [SuperBlockStates.Upcoming]: [SuperBlocks.JsAlgoDataStructNew],
+ [SuperBlockStates.Upcoming]: [
+ SuperBlocks.JsAlgoDataStructNew,
+ SuperBlocks.TheOdinProject
+ ],
[SuperBlockStates.Legacy]: []
}
}
@@ -384,7 +403,10 @@ export const superBlockOrder: SuperBlockOrder = {
[TranslationStates.NotAudited]: {
[SuperBlockStates.Current]: [SuperBlocks.CodingInterviewPrep],
[SuperBlockStates.New]: [],
- [SuperBlockStates.Upcoming]: [SuperBlocks.JsAlgoDataStructNew],
+ [SuperBlockStates.Upcoming]: [
+ SuperBlocks.JsAlgoDataStructNew,
+ SuperBlocks.TheOdinProject
+ ],
[SuperBlockStates.Legacy]: []
}
}
@@ -427,7 +449,10 @@ export const superBlockOrder: SuperBlockOrder = {
[TranslationStates.NotAudited]: {
[SuperBlockStates.Current]: [],
[SuperBlockStates.New]: [],
- [SuperBlockStates.Upcoming]: [SuperBlocks.JsAlgoDataStructNew],
+ [SuperBlockStates.Upcoming]: [
+ SuperBlocks.JsAlgoDataStructNew,
+ SuperBlocks.TheOdinProject
+ ],
[SuperBlockStates.Legacy]: []
}
}
@@ -471,7 +496,10 @@ export const superBlockOrder: SuperBlockOrder = {
SuperBlocks.CodingInterviewPrep
],
[SuperBlockStates.New]: [],
- [SuperBlockStates.Upcoming]: [SuperBlocks.JsAlgoDataStructNew],
+ [SuperBlockStates.Upcoming]: [
+ SuperBlocks.JsAlgoDataStructNew,
+ SuperBlocks.TheOdinProject
+ ],
[SuperBlockStates.Legacy]: []
}
}
@@ -514,7 +542,10 @@ export const superBlockOrder: SuperBlockOrder = {
SuperBlocks.CodingInterviewPrep
],
[SuperBlockStates.New]: [],
- [SuperBlockStates.Upcoming]: [SuperBlocks.JsAlgoDataStructNew],
+ [SuperBlockStates.Upcoming]: [
+ SuperBlocks.JsAlgoDataStructNew,
+ SuperBlocks.TheOdinProject
+ ],
[SuperBlockStates.Legacy]: [SuperBlocks.RespWebDesign]
}
}
@@ -545,11 +576,17 @@ function shouldShowSuperblocks({
return true;
}
+type Config = {
+ language: string;
+ showNewCurriculum?: string;
+ showUpcomingChanges?: string;
+};
+
export function getLearnSuperBlocks({
language = 'english',
showNewCurriculum = 'false',
showUpcomingChanges = 'false'
-}) {
+}: Config): SuperBlocks[] {
const learnSuperBlocks: SuperBlocks[] = [];
Object.values(TranslationStates).forEach(translationState => {
@@ -577,7 +614,7 @@ export function getAuditedSuperBlocks({
language = 'english',
showNewCurriculum = 'false',
showUpcomingChanges = 'false'
-}) {
+}: Config): SuperBlocks[] {
const auditedSuperBlocks: SuperBlocks[] = [];
Object.values(SuperBlockStates).forEach(superBlockState => {
@@ -603,7 +640,7 @@ export function getNotAuditedSuperBlocks({
language = 'english',
showNewCurriculum = 'false',
showUpcomingChanges = 'false'
-}) {
+}: Config): SuperBlocks[] {
const notAuditedSuperBlocks: SuperBlocks[] = [];
Object.values(SuperBlockStates).forEach(superBlockState => {
diff --git a/curriculum/challenges/_meta/the-odin-project-projects/meta.json b/curriculum/challenges/_meta/top-build-a-recipe-project/meta.json
similarity index 60%
rename from curriculum/challenges/_meta/the-odin-project-projects/meta.json
rename to curriculum/challenges/_meta/top-build-a-recipe-project/meta.json
index edd3de40ae3..df2b93d2096 100644
--- a/curriculum/challenges/_meta/the-odin-project-projects/meta.json
+++ b/curriculum/challenges/_meta/top-build-a-recipe-project/meta.json
@@ -1,12 +1,11 @@
{
- "name": "The Odin Project Projects",
+ "name": "TOP build a recipe project",
"isUpcomingChange": true,
- "dashedName": "the-odin-project-projects",
- "order": 6,
+ "order": 1,
"time": "",
"template": "",
"required": [],
- "superBlock": "coding-interview-prep",
+ "superBlock": "the-odin-project",
"challengeOrder": [
[
"6391d1a4f7ac71efd0621380",
diff --git a/curriculum/challenges/_meta/the-odin-project/meta.json b/curriculum/challenges/_meta/top-learn-html-foundations/meta.json
similarity index 95%
rename from curriculum/challenges/_meta/the-odin-project/meta.json
rename to curriculum/challenges/_meta/top-learn-html-foundations/meta.json
index 44d7f47d50b..b15490e569c 100644
--- a/curriculum/challenges/_meta/the-odin-project/meta.json
+++ b/curriculum/challenges/_meta/top-learn-html-foundations/meta.json
@@ -1,12 +1,11 @@
{
- "name": "The Odin Project",
+ "name": "TOP Learn HTML Foundations",
"isUpcomingChange": true,
- "dashedName": "the-odin-project",
- "order": 5,
+ "order": 0,
"time": "",
"template": "",
"required": [],
- "superBlock": "coding-interview-prep",
+ "superBlock": "the-odin-project",
"challengeOrder": [
[
"6374f208de18c50e48ba767b",
diff --git a/curriculum/challenges/arabic/00-certifications/data-visualization-certification/data-visualization-certification.yml b/curriculum/challenges/arabic/00-certifications/data-visualization-certification/data-visualization-certification.yml
index 1a22cbbd33d..d77cefc4226 100644
--- a/curriculum/challenges/arabic/00-certifications/data-visualization-certification/data-visualization-certification.yml
+++ b/curriculum/challenges/arabic/00-certifications/data-visualization-certification/data-visualization-certification.yml
@@ -7,7 +7,7 @@ isPrivate: true
tests:
-
id: bd7168d8c242eddfaeb5bd13
- title: التصوير المرئي للبيانات باستخدام مخطط بياني للأعمدة
+ title: التصوير المرئي للبيانات باستخدام مخطط الأعمدة
-
id: bd7178d8c242eddfaeb5bd13
title: التصوير المرئي للبيانات باستخدام مخطط التشتت
diff --git a/curriculum/challenges/arabic/00-certifications/legacy-data-visualization-certification/legacy-data-visualization-certification.yml b/curriculum/challenges/arabic/00-certifications/legacy-data-visualization-certification/legacy-data-visualization-certification.yml
index 04ac3369a4f..19885830ef3 100644
--- a/curriculum/challenges/arabic/00-certifications/legacy-data-visualization-certification/legacy-data-visualization-certification.yml
+++ b/curriculum/challenges/arabic/00-certifications/legacy-data-visualization-certification/legacy-data-visualization-certification.yml
@@ -22,7 +22,7 @@ tests:
title: بناء لعبة Roguelike Dungeon Crawler
-
id: bd7168d8c242eddfaeb5bd13
- title: التصوير المرئي للبيانات باستخدام مخطط بياني للأعمدة
+ title: التصوير المرئي للبيانات باستخدام مخطط الأعمدة
-
id: bd7178d8c242eddfaeb5bd13
title: التصوير المرئي للبيانات باستخدام مخطط التشتت
diff --git a/curriculum/challenges/arabic/01-responsive-web-design/applied-accessibility/add-a-text-alternative-to-images-for-visually-impaired-accessibility.md b/curriculum/challenges/arabic/01-responsive-web-design/applied-accessibility/add-a-text-alternative-to-images-for-visually-impaired-accessibility.md
index 27f9c91bb75..a957e919b1a 100644
--- a/curriculum/challenges/arabic/01-responsive-web-design/applied-accessibility/add-a-text-alternative-to-images-for-visually-impaired-accessibility.md
+++ b/curriculum/challenges/arabic/01-responsive-web-design/applied-accessibility/add-a-text-alternative-to-images-for-visually-impaired-accessibility.md
@@ -21,7 +21,7 @@ dashedName: add-a-text-alternative-to-images-for-visually-impaired-accessibility
# --instructions--
-كامبر القط هو نينجا في البرمجة وهو أيضا نينجا حقيقي، يقوم ببناء صفحة ويب لمشاركة معرفتة. الصورة الشخصية التي يريد استخدامها تظهر مهاراته ويجب أن تحظي بتقدير جميع زوار الموقع. أضف خاصية (attribute) `alt` في الوسم (tag) `img` لتظهر أن كامبر القط يجيد لعبة الكاراتية. (خاصية الصورة `src` لا ترتبط بملف فعلي، لذا يجب أن ترى نص `alt` في العرض.)
+Camper Cat هو نينجا في البرمجة وهو أيضا نينجا حقيقي، يقوم ببناء صفحة ويب لمشاركة معرفتة. الصورة الشخصية التي يريد استخدامها تظهر مهاراته ويجب أن تحظي بتقدير جميع زوار الموقع. أضف خاصية (attribute) `alt` في الوسم (tag) `img` لتظهر أن Camper Cat يجيد لعبة الكاراتية. (خاصية الصورة `src` لا ترتبط بملف فعلي، لذا يجب أن ترى نص `alt` في العرض.)
# --hints--
diff --git a/curriculum/challenges/arabic/01-responsive-web-design/applied-accessibility/add-an-accessible-date-picker.md b/curriculum/challenges/arabic/01-responsive-web-design/applied-accessibility/add-an-accessible-date-picker.md
index f54b40bdd31..e434da4f1de 100644
--- a/curriculum/challenges/arabic/01-responsive-web-design/applied-accessibility/add-an-accessible-date-picker.md
+++ b/curriculum/challenges/arabic/01-responsive-web-design/applied-accessibility/add-an-accessible-date-picker.md
@@ -24,29 +24,29 @@ dashedName: add-an-accessible-date-picker
# --instructions--
-يقوم Camper Cat بإعداد بطولة Mortal Kombat ويريد أن يطلب من منافسيه معرفة التاريخ الأفضل. أضف وسم `input` مع سمة `type` من نوع `date` ، وسمة `id` بقيمة `pickdate`، و سمة `name` بقيمة `date`.
+يقوم Camper Cat بإعداد بطولة Mortal Kombat ويريد أن يطلب من منافسيه معرفة التاريخ الأفضل. أضف علامة `input` مع سمة `type` من نوع `date`، و مع سمة `id` بقيمة `pickdate`، و أيضا سمة `name` بقيمة `date`.
# --hints--
-يجب أن يحتوي الكود علي وسم `input` واحد لحقل محدد التاريخ.
+يجب أن يحتوي الكود علي علامة `input` واحد لخانة محدد التاريخ.
```js
assert($('input').length == 2);
```
-يجب أن يحتوي وسم `input` على سمة `type` بقيمة `date`.
+يجب أن يحتوي علامة `input` على سمة `type` بقيمة `date`.
```js
assert($('input').attr('type') == 'date');
```
-يجب أن يحتوي وسم `input` على سمة `id` بقيمة `pickdate`.
+يجب أن يحتوي علامة `input` على سمة `id` بقيمة `pickdate`.
```js
assert($('input').attr('id') == 'pickdate');
```
-يجب أن يحتوي وسم `input` على سمة `name` بقيمة `date`.
+يجب أن يحتوي علامة `input` على سمة `name` بقيمة `date`.
```js
assert($('input').attr('name') == 'date');
diff --git a/curriculum/challenges/arabic/01-responsive-web-design/applied-accessibility/avoid-colorblindness-issues-by-carefully-choosing-colors-that-convey-information.md b/curriculum/challenges/arabic/01-responsive-web-design/applied-accessibility/avoid-colorblindness-issues-by-carefully-choosing-colors-that-convey-information.md
index 2f21941dc92..ee510da38e3 100644
--- a/curriculum/challenges/arabic/01-responsive-web-design/applied-accessibility/avoid-colorblindness-issues-by-carefully-choosing-colors-that-convey-information.md
+++ b/curriculum/challenges/arabic/01-responsive-web-design/applied-accessibility/avoid-colorblindness-issues-by-carefully-choosing-colors-that-convey-information.md
@@ -19,7 +19,7 @@ dashedName: >-
# --instructions--
-تختبر Camper Cat أنماطًا مختلفة لزر مهم ، ولكن اللون الأصفر للخلفة (`#FFFF33`) `background-color` و الاخضر (`#33FF33`) `color`لون النص هي درجات مجاورة على عجلة الألوان ولا يمكن تمييزها فعليًا عند بعض المصابين بعمى الألوان. (خفتهم المتشابهة تفشل أيضًا في فحص نسبة التباين). غيّر لون النص `color` إلى اللون الأزرق المظلم (`#003366`) لحل كلتا المشكلتين.
+يختبر Camper Cat أنماطًا مختلفة لزر مهم، ولكن اللون الأصفر (`#FFFF33`) للخلفية (`background-color`) و الاخضر (`#33FF33`) للون النص (`color`) هي درجات مجاورة على عجلة الألوان ولا يمكن لبعض المصابين بعمى الألوان التمييز ببنهم. (خفتهم المتشابهة تفشل أيضًا في فحص نسبة التباين). غيّر لون النص `color` إلى اللون الأزرق المظلم (`#003366`) لحل كلتا المشكلتين.
# --hints--
diff --git a/curriculum/challenges/arabic/01-responsive-web-design/applied-accessibility/avoid-colorblindness-issues-by-using-sufficient-contrast.md b/curriculum/challenges/arabic/01-responsive-web-design/applied-accessibility/avoid-colorblindness-issues-by-using-sufficient-contrast.md
index 133f49e990f..afad525b088 100644
--- a/curriculum/challenges/arabic/01-responsive-web-design/applied-accessibility/avoid-colorblindness-issues-by-using-sufficient-contrast.md
+++ b/curriculum/challenges/arabic/01-responsive-web-design/applied-accessibility/avoid-colorblindness-issues-by-using-sufficient-contrast.md
@@ -19,7 +19,7 @@ dashedName: avoid-colorblindness-issues-by-using-sufficient-contrast
# --instructions--
-يقوم Camper Cat بتجربة استخدام الألوان لنص مدونته وخلفيتها ، ولكن دمجة الحالي لللون الأخضر للخلفية `background-color` مع لون النص الماروني `color` له نسبة تباين 2.5:1. يمكنك بسهولة ضبط إضاءة الألوان بما اته عرفها، باستخدام خاصية في الـ CSS و هي `hsl()` (التي تمثل hue بمعني التدرج و saturation بمعني التشبع و lightness بمعني الخفة) بتغيير الوسيطة (argument) الثالثة. زيادة قيمة الخفة lightness للون الخلفية `background-color` من 35% إلى 55%، وتقليل قيمة الخفة لـ `color` من 20% إلى 15%. وهذا ما يحسّن التباين إلى 5.9:1.
+يقوم Camper Cat بتجربة استخدام الألوان لنص مدونته وخلفيتها، ولكن دمجة لألون الأخضر للخلفية `background-color` مع لون النص الماروني (maroon) الحالي `color` له نسبة تباين 2.5:1. يمكنك بسهولة ضبط إضاءة الألوان بما اته عرفها، باستخدام خاصية في الـ CSS و هي `hsl()` (التي تمثل hue بمعني التدرج و saturation بمعني التشبع و lightness بمعني الخفة) بتغيير الوسيطة (argument) الثالثة. زيادة قيمة الخفة lightness للون الخلفية `background-color` من 35% إلى 55%، وتقليل قيمة الخفة لـ `color` من 20% إلى 15%. وهذا ما يحسّن التباين إلى 5.9:1.
# --hints--
diff --git a/curriculum/challenges/arabic/01-responsive-web-design/applied-accessibility/give-links-meaning-by-using-descriptive-link-text.md b/curriculum/challenges/arabic/01-responsive-web-design/applied-accessibility/give-links-meaning-by-using-descriptive-link-text.md
index f282a55ad02..6da59306862 100644
--- a/curriculum/challenges/arabic/01-responsive-web-design/applied-accessibility/give-links-meaning-by-using-descriptive-link-text.md
+++ b/curriculum/challenges/arabic/01-responsive-web-design/applied-accessibility/give-links-meaning-by-using-descriptive-link-text.md
@@ -15,7 +15,7 @@ Screen reader users have various options for what type of content their device r
# --instructions--
-The link text that Camper Cat is using is not very descriptive without the surrounding context. انقل علامات الرابط (`a`) بحيث تلتف حول النص `information about batteries` بدلاً من `Click here`.
+نص الرابط الذي يستخدمه Camper Cat ليس وصفياً جداً بدون السياق المحيط. انقل علامات الرابط (`a`) بحيث تلتف حول النص `information about batteries` بدلاً من `Click here`.
# --hints--
diff --git a/curriculum/challenges/arabic/01-responsive-web-design/applied-accessibility/improve-accessibility-of-audio-content-with-the-audio-element.md b/curriculum/challenges/arabic/01-responsive-web-design/applied-accessibility/improve-accessibility-of-audio-content-with-the-audio-element.md
index 8e5ff50fc46..0a9674923a4 100644
--- a/curriculum/challenges/arabic/01-responsive-web-design/applied-accessibility/improve-accessibility-of-audio-content-with-the-audio-element.md
+++ b/curriculum/challenges/arabic/01-responsive-web-design/applied-accessibility/improve-accessibility-of-audio-content-with-the-audio-element.md
@@ -26,7 +26,7 @@ Here's an example:
# --instructions--
-Time to take a break from Camper Cat and meet fellow camper Zersiax (@zersiax), a champion of accessibility and a screen reader user. To hear a clip of his screen reader in action, add an `audio` element after the `p`. Include the `controls` attribute. Then place a `source` tag inside the `audio` tags with the `src` attribute set to `https://s3.amazonaws.com/freecodecamp/screen-reader.mp3` and `type` attribute set to `"audio/mpeg"`.
+حان الوقت لأخذ استراحة من Camper Cat و مقابلة الزميل camper Zersiax (@zersiax), بطل ال accessibility و مستخدم لتقنية قارئ الشاشة (screen reader). To hear a clip of his screen reader in action, add an `audio` element after the `p`. Include the `controls` attribute. Then place a `source` tag inside the `audio` tags with the `src` attribute set to `https://s3.amazonaws.com/freecodecamp/screen-reader.mp3` and `type` attribute set to `"audio/mpeg"`.
**Note:** The audio clip may sound fast and be difficult to understand, but that is a normal speed for screen reader users.
diff --git a/curriculum/challenges/arabic/01-responsive-web-design/applied-accessibility/improve-chart-accessibility-with-the-figure-element.md b/curriculum/challenges/arabic/01-responsive-web-design/applied-accessibility/improve-chart-accessibility-with-the-figure-element.md
index d677e66b940..168b6351e7e 100644
--- a/curriculum/challenges/arabic/01-responsive-web-design/applied-accessibility/improve-chart-accessibility-with-the-figure-element.md
+++ b/curriculum/challenges/arabic/01-responsive-web-design/applied-accessibility/improve-chart-accessibility-with-the-figure-element.md
@@ -27,7 +27,7 @@ Here's an example - note that the `figcaption` goes inside the `figure` tags and
# --instructions--
-Camper Cat is hard at work creating a stacked bar chart showing the amount of time per week to spend training in stealth, combat, and weapons. Help him structure his page better by changing the `div` tag he used to a `figure` tag, and the `p` tag that surrounds the caption to a `figcaption` tag.
+Camper Cat شاق في العمل لإنشاء مخطط أعمدة مكدسة يبين مقدار الوقت في الأسبوع لقضاء التدريب على السرقة، القتال والأسلحة. Help him structure his page better by changing the `div` tag he used to a `figure` tag, and the `p` tag that surrounds the caption to a `figcaption` tag.
# --hints--
diff --git a/curriculum/challenges/arabic/01-responsive-web-design/applied-accessibility/improve-form-field-accessibility-with-the-label-element.md b/curriculum/challenges/arabic/01-responsive-web-design/applied-accessibility/improve-form-field-accessibility-with-the-label-element.md
index 55492a9a50e..8348eb4e910 100644
--- a/curriculum/challenges/arabic/01-responsive-web-design/applied-accessibility/improve-form-field-accessibility-with-the-label-element.md
+++ b/curriculum/challenges/arabic/01-responsive-web-design/applied-accessibility/improve-form-field-accessibility-with-the-label-element.md
@@ -26,7 +26,7 @@ The value of the `for` attribute must be the same as the value of the `id` attri
# --instructions--
-Camper Cat expects a lot of interest in his thoughtful blog posts and wants to include an email sign up form. Add a `for` attribute on the email `label` that matches the `id` on its `input` field.
+تنتظر Camper Cat الكثير من الاهتمام في مشاركاته المدونة المدروسة وترغب في تضمين نموذج تسجيل البريد الإلكتروني. Add a `for` attribute on the email `label` that matches the `id` on its `input` field.
# --hints--
diff --git a/curriculum/challenges/arabic/01-responsive-web-design/applied-accessibility/improve-readability-with-high-contrast-text.md b/curriculum/challenges/arabic/01-responsive-web-design/applied-accessibility/improve-readability-with-high-contrast-text.md
index 8bf9b62cf57..b3d9a57f918 100644
--- a/curriculum/challenges/arabic/01-responsive-web-design/applied-accessibility/improve-readability-with-high-contrast-text.md
+++ b/curriculum/challenges/arabic/01-responsive-web-design/applied-accessibility/improve-readability-with-high-contrast-text.md
@@ -15,7 +15,7 @@ The Web Content Accessibility Guidelines (WCAG) recommend at least a 4.5 to 1 co
# --instructions--
-Camper Cat's choice of light gray text on a white background for his recent blog post has a 1.5:1 contrast ratio, making it hard to read. Change the `color` of the text from the current gray (`#D3D3D3`) to a darker gray (`#636363`) to improve the contrast ratio to 6:1.
+اختيار Camper Cat للنص الرمادي الخفيف على خلفية بيضاء لمشاركته الأخيرة يحتوي على 1.5:1 نسبة التباين، مما يجعل من الصعب قراءتها. Change the `color` of the text from the current gray (`#D3D3D3`) to a darker gray (`#636363`) to improve the contrast ratio to 6:1.
# --hints--
diff --git a/curriculum/challenges/arabic/01-responsive-web-design/applied-accessibility/jump-straight-to-the-content-using-the-main-element.md b/curriculum/challenges/arabic/01-responsive-web-design/applied-accessibility/jump-straight-to-the-content-using-the-main-element.md
index 83031edde59..02ebc2ac766 100644
--- a/curriculum/challenges/arabic/01-responsive-web-design/applied-accessibility/jump-straight-to-the-content-using-the-main-element.md
+++ b/curriculum/challenges/arabic/01-responsive-web-design/applied-accessibility/jump-straight-to-the-content-using-the-main-element.md
@@ -19,7 +19,7 @@ The `main` tag also has an embedded landmark feature that assistive technology c
# --instructions--
-Camper Cat has some big ideas for his ninja weapons page. Help him set up his markup by adding opening and closing `main` tags between the `header` and `footer` (covered in other challenges). Keep the `main` tags empty for now.
+Camper Cat لديه بعض الأفكار الكبيرة لصفحة أسلحة النينجا. Help him set up his markup by adding opening and closing `main` tags between the `header` and `footer` (covered in other challenges). Keep the `main` tags empty for now.
# --hints--
diff --git a/curriculum/challenges/arabic/01-responsive-web-design/applied-accessibility/know-when-alt-text-should-be-left-blank.md b/curriculum/challenges/arabic/01-responsive-web-design/applied-accessibility/know-when-alt-text-should-be-left-blank.md
index 695445ea37e..7b46cb25d31 100644
--- a/curriculum/challenges/arabic/01-responsive-web-design/applied-accessibility/know-when-alt-text-should-be-left-blank.md
+++ b/curriculum/challenges/arabic/01-responsive-web-design/applied-accessibility/know-when-alt-text-should-be-left-blank.md
@@ -23,7 +23,7 @@ Background images usually fall under the 'decorative' label as well. However, th
# --instructions--
-Camper Cat has coded a skeleton page for the blog part of his website. He's planning to add a visual break between his two articles with a decorative image of a samurai sword. Add an `alt` attribute to the `img` tag and set it to an empty string. (Note that the image `src` doesn't link to an actual file - don't worry that there are no swords showing in the display.)
+قام Camper Cat ببرمجة هيكل الصفحة لمدونة موقعه. He's planning to add a visual break between his two articles with a decorative image of a samurai sword. Add an `alt` attribute to the `img` tag and set it to an empty string. (Note that the image `src` doesn't link to an actual file - don't worry that there are no swords showing in the display.)
# --hints--
diff --git a/curriculum/challenges/arabic/01-responsive-web-design/applied-accessibility/make-elements-only-visible-to-a-screen-reader-by-using-custom-css.md b/curriculum/challenges/arabic/01-responsive-web-design/applied-accessibility/make-elements-only-visible-to-a-screen-reader-by-using-custom-css.md
index 63bfea4b75c..f3c52234204 100644
--- a/curriculum/challenges/arabic/01-responsive-web-design/applied-accessibility/make-elements-only-visible-to-a-screen-reader-by-using-custom-css.md
+++ b/curriculum/challenges/arabic/01-responsive-web-design/applied-accessibility/make-elements-only-visible-to-a-screen-reader-by-using-custom-css.md
@@ -35,7 +35,7 @@ Here's an example of the CSS rules that accomplish this:
# --instructions--
-Camper Cat created a really cool stacked bar chart for his training page, and put the data into a table for his visually impaired users. The table already has an `sr-only` class, but the CSS rules aren't filled in yet. Give the `position` an `absolute` value, the `left` a `-10000px` value, and the `width` and `height` both `1px` values.
+أنشأ Camper Cat مخطط أعمدة مكدسة رائع جدا لصفحة التدريب، و وضع البيانات في جدول لمستخدميه ذوي الإعاقة البصرية. The table already has an `sr-only` class, but the CSS rules aren't filled in yet. Give the `position` an `absolute` value, the `left` a `-10000px` value, and the `width` and `height` both `1px` values.
# --hints--
diff --git a/curriculum/challenges/arabic/01-responsive-web-design/applied-accessibility/make-links-navigable-with-html-access-keys.md b/curriculum/challenges/arabic/01-responsive-web-design/applied-accessibility/make-links-navigable-with-html-access-keys.md
index ba72f6d7976..a738ee9f87e 100644
--- a/curriculum/challenges/arabic/01-responsive-web-design/applied-accessibility/make-links-navigable-with-html-access-keys.md
+++ b/curriculum/challenges/arabic/01-responsive-web-design/applied-accessibility/make-links-navigable-with-html-access-keys.md
@@ -21,7 +21,7 @@ Here's an example:
# --instructions--
-Camper Cat wants the links around the two blog article titles to have keyboard shortcuts so his site's users can quickly navigate to the full story. Add an `accesskey` attribute to both links and set the first one to `g` (for Garfield) and the second one to `c` (for Chuck Norris).
+يريد Camper Cat أن يكون للرابطان حول عنوان مقالات المدونة اختصارات لوحة المفاتيح (keyboard shortcuts) حتى يتمكن مستخدمو موقعه من الانتقال بسرعة إلى القصة الكاملة. Add an `accesskey` attribute to both links and set the first one to `g` (for Garfield) and the second one to `c` (for Chuck Norris).
# --hints--
diff --git a/curriculum/challenges/arabic/01-responsive-web-design/applied-accessibility/make-screen-reader-navigation-easier-with-the-footer-landmark.md b/curriculum/challenges/arabic/01-responsive-web-design/applied-accessibility/make-screen-reader-navigation-easier-with-the-footer-landmark.md
index 3330ea186d7..f536b53a165 100644
--- a/curriculum/challenges/arabic/01-responsive-web-design/applied-accessibility/make-screen-reader-navigation-easier-with-the-footer-landmark.md
+++ b/curriculum/challenges/arabic/01-responsive-web-design/applied-accessibility/make-screen-reader-navigation-easier-with-the-footer-landmark.md
@@ -13,7 +13,7 @@ Similar to `header` and `nav`, the `footer` element has a built-in landmark feat
# --instructions--
-Camper Cat's training page is making good progress. Change the `div` he used to wrap his copyright information at the bottom of the page to a `footer` element.
+صفحة تدريب Camper Cat تحرز تقدما جيدا. Change the `div` he used to wrap his copyright information at the bottom of the page to a `footer` element.
# --hints--
diff --git a/curriculum/challenges/arabic/01-responsive-web-design/applied-accessibility/make-screen-reader-navigation-easier-with-the-header-landmark.md b/curriculum/challenges/arabic/01-responsive-web-design/applied-accessibility/make-screen-reader-navigation-easier-with-the-header-landmark.md
index babce017089..5c4b0840354 100644
--- a/curriculum/challenges/arabic/01-responsive-web-design/applied-accessibility/make-screen-reader-navigation-easier-with-the-header-landmark.md
+++ b/curriculum/challenges/arabic/01-responsive-web-design/applied-accessibility/make-screen-reader-navigation-easier-with-the-header-landmark.md
@@ -9,11 +9,11 @@ dashedName: make-screen-reader-navigation-easier-with-the-header-landmark
# --description--
-عنصر الـ HTML5 التالي الذي يضيف المعنى الدلالي ويحسن إمكانية الوصول هو عنصر الـ `header`. يستخدم لإحتواء المعلومات التمهيدية أو روابط التنقل لعنصره الاساسي ويعمل بشكل جيد مع المحتوى المتكرر في الأعلى على الصفحات متعددة.
+عنصر الـ HTML5 التالي الذي يضيف المعنى الدلالي ويحسن إمكانية الوصول هو عنصر `header`. يستخدم لاحتواء المعلومات التمهيدية أو روابط التنقل لعنصره آلأساسي ويعمل بشكل جيد مع المحتوى المتكرر في الأعلى على الصفحات متعددة.
`header` يشاركك الميزة البارزة المدمجة التي رأيتها مع `main`، مما يسمح للتكنولوجيات المساعدة بالانتقال بسرعة إلى ذلك المحتوى.
-**ملاحظة:** عنصر الـ `header` يستخدم في عنصر `body` بمستند HTML الخاص بك. وهذا يختلف عن عن عنصر الـ `head` الذي يحتوي على عنوان الصفحة، معلومات تعريفية، و الخ.
+**ملاحظة:** عنصر `header` يستخدم في عنصر `body` بمستند HTML الخاص بك. وهذا يختلف عن عن عنصر الـ `head` الذي يحتوي على عنوان الصفحة، معلومات تعريفية، و الخ.
# --instructions--
@@ -21,13 +21,13 @@ dashedName: make-screen-reader-navigation-easier-with-the-header-landmark
# --hints--
-يجب أن يحتوي الكود الخاص بك على `header` واحد فقط.
+يجب أن يحتوي الكود الخاص بك على علامة `header` واحدة فقط.
```js
assert($('header').length == 1);
```
-يجب أن يحتوي عنصر `header` عنصر الـ `h1`.
+يجب أن يحتوي عنصر `header` على عنصر `h1`.
```js
assert($('header').children('h1').length == 1);
diff --git a/curriculum/challenges/arabic/01-responsive-web-design/applied-accessibility/make-screen-reader-navigation-easier-with-the-nav-landmark.md b/curriculum/challenges/arabic/01-responsive-web-design/applied-accessibility/make-screen-reader-navigation-easier-with-the-nav-landmark.md
index 906d7dd5c83..f7386921714 100644
--- a/curriculum/challenges/arabic/01-responsive-web-design/applied-accessibility/make-screen-reader-navigation-easier-with-the-nav-landmark.md
+++ b/curriculum/challenges/arabic/01-responsive-web-design/applied-accessibility/make-screen-reader-navigation-easier-with-the-nav-landmark.md
@@ -15,7 +15,7 @@ If there are repeated site links at the bottom of the page, it isn't necessary t
# --instructions--
-Camper Cat included navigation links at the top of his training page, but wrapped them in a `div`. Change the `div` to a `nav` tag to improve the accessibility on his page.
+تضمن Camper Cat وصلات تنقل من الجزء العلوي من صفحة التدريب، لكنه لفها في `div`. Change the `div` to a `nav` tag to improve the accessibility on his page.
# --hints--
diff --git a/curriculum/challenges/arabic/01-responsive-web-design/applied-accessibility/standardize-times-with-the-html5-datetime-attribute.md b/curriculum/challenges/arabic/01-responsive-web-design/applied-accessibility/standardize-times-with-the-html5-datetime-attribute.md
index d9318d50f7b..3892d3ff1bc 100644
--- a/curriculum/challenges/arabic/01-responsive-web-design/applied-accessibility/standardize-times-with-the-html5-datetime-attribute.md
+++ b/curriculum/challenges/arabic/01-responsive-web-design/applied-accessibility/standardize-times-with-the-html5-datetime-attribute.md
@@ -19,7 +19,7 @@ Here's an example:
# --instructions--
-Camper Cat's Mortal Kombat survey results are in! Wrap a `time` tag around the text `Thursday, September 15th` and add a `datetime` attribute to it set to `2016-09-15`.
+صدرت نتائج استقصاء Camper Cat في Mortal Kombat! Wrap a `time` tag around the text `Thursday, September 15th` and add a `datetime` attribute to it set to `2016-09-15`.
# --hints--
diff --git a/curriculum/challenges/arabic/01-responsive-web-design/applied-accessibility/use-headings-to-show-hierarchical-relationships-of-content.md b/curriculum/challenges/arabic/01-responsive-web-design/applied-accessibility/use-headings-to-show-hierarchical-relationships-of-content.md
index fbb95eaf6b1..4feaf983881 100644
--- a/curriculum/challenges/arabic/01-responsive-web-design/applied-accessibility/use-headings-to-show-hierarchical-relationships-of-content.md
+++ b/curriculum/challenges/arabic/01-responsive-web-design/applied-accessibility/use-headings-to-show-hierarchical-relationships-of-content.md
@@ -23,7 +23,7 @@ One final point, each page should always have one (and only one) `h1` element, w
# --instructions--
-Camper Cat wants a page on his site dedicated to becoming a ninja. Help him fix the headings so his markup gives semantic meaning to the content, and shows the proper parent-child relationships of his sections. Change all the `h5` tags to the proper heading level to indicate they are subsections of the `h2` ones. Use `h3` tags for the purpose.
+يريد Camper Cat صفحة مخصصة على موقعه الإلكتروني لتعليم النينجا. Help him fix the headings so his markup gives semantic meaning to the content, and shows the proper parent-child relationships of his sections. Change all the `h5` tags to the proper heading level to indicate they are subsections of the `h2` ones. Use `h3` tags for the purpose.
# --hints--
diff --git a/curriculum/challenges/arabic/01-responsive-web-design/applied-accessibility/use-tabindex-to-add-keyboard-focus-to-an-element.md b/curriculum/challenges/arabic/01-responsive-web-design/applied-accessibility/use-tabindex-to-add-keyboard-focus-to-an-element.md
index 0ae730fc94b..cfd48fbd829 100644
--- a/curriculum/challenges/arabic/01-responsive-web-design/applied-accessibility/use-tabindex-to-add-keyboard-focus-to-an-element.md
+++ b/curriculum/challenges/arabic/01-responsive-web-design/applied-accessibility/use-tabindex-to-add-keyboard-focus-to-an-element.md
@@ -21,7 +21,7 @@ Certain elements, such as links and form controls, automatically receive keyboar
# --instructions--
-Camper Cat created a new survey to collect information about his users. He knows input fields automatically get keyboard focus, but he wants to make sure his keyboard users pause at the instructions while tabbing through the items. Add a `tabindex` attribute to the `p` tag and set its value to `0`. Bonus - using `tabindex` also enables the CSS pseudo-class `:focus` to work on the `p` tag.
+أنشأ Camper Cat استبيانا جديدا لجمع المعلومات عن مستخدمي موقعه. He knows input fields automatically get keyboard focus, but he wants to make sure his keyboard users pause at the instructions while tabbing through the items. Add a `tabindex` attribute to the `p` tag and set its value to `0`. Bonus - using `tabindex` also enables the CSS pseudo-class `:focus` to work on the `p` tag.
# --hints--
diff --git a/curriculum/challenges/arabic/01-responsive-web-design/applied-accessibility/wrap-content-in-the-article-element.md b/curriculum/challenges/arabic/01-responsive-web-design/applied-accessibility/wrap-content-in-the-article-element.md
index f6eaf6e3697..0ec49e8d271 100644
--- a/curriculum/challenges/arabic/01-responsive-web-design/applied-accessibility/wrap-content-in-the-article-element.md
+++ b/curriculum/challenges/arabic/01-responsive-web-design/applied-accessibility/wrap-content-in-the-article-element.md
@@ -9,7 +9,7 @@ dashedName: wrap-content-in-the-article-element
# --description--
-`article` هو عنصر آخر من عناصر HTML5 الجديدة التي تضيف المعنى الدلالي الي الـ markup. `article` هو عنصر تقسيم ويستخدم لتغليف المحتوي القائم بذاته. يعمل الوسم بشكل جيد مع إدخالات المدونة أو مشاركات المنتدى أو المقالات الإخبارية.
+`article` هو عنصر آخر من عناصر HTML5 الجديدة التي تضيف المعنى الدلالي الي الـ markup. `article` هو عنصر تقسيم ويستخدم لتغليف المحتوي القائم بذاته. يعمل العلامة بشكل جيد مع إدخالات المدونة أو مشاركات المنتدى أو المقالات الإخبارية.
تحديد فيما إذا كان المحتوى يستطيع أن يكون مستقلا هو عادة حكم شخصي، ولكن هناك عدة اختبارات بسيطة يمكنك استخدامها. اسأل نفسك إذا كنت قد قمت بإزالة كل السياق المحيط، هل سيظل المحتوى منطقياً؟ وبالمثل بالنسبة للنص، هل سيظل المحتوى صحيحا إذا كان في RSS feed ؟
@@ -21,17 +21,17 @@ dashedName: wrap-content-in-the-article-element
# --instructions--
-استخدم Camper Cat وسوم `article` لتغليف المشاركات على صفحة مدونته، لكنه نسي استخدامها حول المشاركة العلوية. قم بتغيير وسم `div` لاستخدام وسم `article` بدلاً منه.
+استخدم Camper Cat علامات `article` لتغليف المشاركات على صفحة مدونته، لكنه نسي استخدامها حول المشاركة العلوية. قم بتغيير علامة `div` لاستخدام علامة `article` بدلاً منه.
# --hints--
-الكود الخاص بك يجب أن يحتوي على ثلاث وسوم `article`.
+الكود الخاص بك يجب أن يحتوي على ثلاث علامات `article`.
```js
assert($('article').length == 3);
```
-يجب ألا يحتوي الكود الخاص بك علي اي وسوم `div`.
+يجب ألا يحتوي الكود الخاص بك على أي علامة `div`.
```js
assert($('div').length == 0);
diff --git a/curriculum/challenges/arabic/01-responsive-web-design/applied-accessibility/wrap-radio-buttons-in-a-fieldset-element-for-better-accessibility.md b/curriculum/challenges/arabic/01-responsive-web-design/applied-accessibility/wrap-radio-buttons-in-a-fieldset-element-for-better-accessibility.md
index dab2077c235..794a8db4a3d 100644
--- a/curriculum/challenges/arabic/01-responsive-web-design/applied-accessibility/wrap-radio-buttons-in-a-fieldset-element-for-better-accessibility.md
+++ b/curriculum/challenges/arabic/01-responsive-web-design/applied-accessibility/wrap-radio-buttons-in-a-fieldset-element-for-better-accessibility.md
@@ -1,6 +1,6 @@
---
id: 587d778b367417b2b2512aa7
-title: Wrap Radio Buttons in a fieldset Element for Better Accessibility
+title: تغليف Radio Buttons داخل fieldset لتحسينا لإمكانيه الوصول
challengeType: 0
videoUrl: 'https://scrimba.com/c/cVJVefw'
forumTopicId: 301030
@@ -9,13 +9,13 @@ dashedName: wrap-radio-buttons-in-a-fieldset-element-for-better-accessibility
# --description--
-The next form topic covers the accessibility of radio buttons. Each choice is given a `label` with a `for` attribute tying to the `id` of the corresponding item as covered in the last challenge. Since radio buttons often come in a group where the user must choose one, there's a way to semantically show the choices are part of a set.
+يغطي الموضوع التالي إمكانية الوصول إلى أزرار الاختيار في النموذج. كل اختيار يعطى `label` وله سمة `for` مرتبطة بـسمة `id` في العنصر المقابل, كما هو مشمول في التحدي الأخير. نظرًا لأن أزرار الراديو (radio buttons) غالبا ما تأتي في مجموعات حيث يجب على المستخدم أن يختار زر واحد فقط من المجموعة، هناك طريقة لإظهار الخيارات بشكل لُغَوي أنها فعلا جزء من نفس المجموعة.
-The `fieldset` tag surrounds the entire grouping of radio buttons to achieve this. It often uses a `legend` tag to provide a description for the grouping, which screen readers read for each choice in the `fieldset` element.
+يحيط علامة (tag) `fieldset` بكامل مجموعة الأزرار الراديو ( radio buttons) لتحقيق ذلك. غالبا ما تستخدم علامة (tag) `legend` لتقديم وصف للمجموعة، أي قارئ الشاشة (screen readers) سيقرأ كل اختيار في عنصر `fieldset`.
-The `fieldset` wrapper and `legend` tag are not necessary when the choices are self-explanatory, like a gender selection. Using a `label` with the `for` attribute for each radio button is sufficient.
+العلامة `fieldset` و `legend` ليست ضرورية عندما تكون الاختيارات غنية عن التفسير، مثل اختيار نوع الجنس. استخدام `label` مع سمة `for` لكل زر راديو هو كاف.
-Here's an example:
+إليك مثال:
```html