`。 下面的例子展示瞭如何使用這個表格:
+
+```js
+test('#test - submit the input "surname" : "Polo"', function (done) {
+ browser.fill('surname', 'Polo').pressButton('submit', function () {
+ browser.assert.success();
+ browser.assert.text('span#name', 'Marco');
+ browser.assert.text('span#surname', 'Polo');
+ browser.assert.element('span#dates', 1);
+ done();
+ });
+}
+```
+
+首先, `browser` 對象的 `fill` 方法在表格的 `surname` 字段中填入值 `'Polo'`。 緊接着,`pressButton` 方法調用表單的 `submit` 事件監聽器。 `pressButton` 方法是異步的。
+
+收到 AJAX 請求的響應之後,會有幾項斷言確認:
+
+1. 響應狀態是 `200`
+2. `
` 元素的文本是 `'Marco'`
+3. `
` 元素的文本是 `'Polo'`
+4. 有 `1` 個 `
` 元素。
+
+最後,執行 `done`,這是異步測試所必需的。
+
+# --instructions--
+
+在 `tests/2_functional-tests.js` 中,`'submit "surname" : "Colombo" - write your e2e test...'` 測試(`// #5`),自動化填入和提交表單:
+
+1. 填寫表單
+2. 點擊 `'submit'` 按鈕提交表單
+
+在回調中:
+
+1. 斷言狀態是正常的 `200`
+2. 斷言元素 `span#name` 中的文本是 `'Cristoforo'`
+3. 斷言元素 `span#surname` 元素中的文本是 `'Colombo'`
+4. 斷言有 `span#dates` 元素,它們的計數是 `1`
+
+不要忘記刪除 `assert.fail()` 調用。
+
+# --hints--
+
+應通過所有測試。
+
+```js
+(getUserInput) =>
+ $.get(getUserInput('url') + '/_api/get-tests?type=functional&n=4').then(
+ (data) => {
+ assert.equal(data.state, 'passed');
+ },
+ (xhr) => {
+ throw new Error(xhr.responseText);
+ }
+ );
+```
+
+應該斷言無頭瀏覽器請求成功。
+
+```js
+(getUserInput) =>
+ $.get(getUserInput('url') + '/_api/get-tests?type=functional&n=4').then(
+ (data) => {
+ assert.equal(data.assertions[0].method, 'browser.success');
+ },
+ (xhr) => {
+ throw new Error(xhr.responseText);
+ }
+ );
+```
+
+應該斷言元素 “span#name” 中的文字爲 “Cristoforo”。
+
+```js
+(getUserInput) =>
+ $.get(getUserInput('url') + '/_api/get-tests?type=functional&n=4').then(
+ (data) => {
+ assert.equal(data.assertions[1].method, 'browser.text');
+ assert.match(data.assertions[1].args[0], /('|")span#name\1/);
+ assert.match(data.assertions[1].args[1], /('|")Cristoforo\1/);
+ },
+ (xhr) => {
+ throw new Error(xhr.responseText);
+ }
+ );
+```
+
+應該斷言元素 “span#surname” 中的文字爲 “Colombo”。
+
+```js
+(getUserInput) =>
+ $.get(getUserInput('url') + '/_api/get-tests?type=functional&n=4').then(
+ (data) => {
+ assert.equal(data.assertions[2].method, 'browser.text');
+ assert.match(data.assertions[2].args[0], /('|")span#surname\1/);
+ assert.match(data.assertions[2].args[1], /('|")Colombo\1/);
+ },
+ (xhr) => {
+ throw new Error(xhr.responseText);
+ }
+ );
+```
+
+應該斷言元素 “span#dates” 存在,且它的值爲 1。
+
+```js
+(getUserInput) =>
+ $.get(getUserInput('url') + '/_api/get-tests?type=functional&n=4').then(
+ (data) => {
+ assert.equal(data.assertions[3].method, 'browser.element');
+ assert.match(data.assertions[3].args[0], /('|")span#dates\1/);
+ assert.equal(data.assertions[3].args[1], 1);
+ },
+ (xhr) => {
+ throw new Error(xhr.responseText);
+ }
+ );
+```
+
+# --solutions--
+
+```js
+/**
+ Backend challenges don't need solutions,
+ because they would need to be tested against a full working project.
+ Please check our contributing guidelines to learn more.
+*/
+```
diff --git a/curriculum/challenges/chinese-traditional/06-quality-assurance/quality-assurance-and-testing-with-chai/simulate-actions-using-a-headless-browser.md b/curriculum/challenges/chinese-traditional/06-quality-assurance/quality-assurance-and-testing-with-chai/simulate-actions-using-a-headless-browser.md
new file mode 100644
index 0000000000..181d50ce69
--- /dev/null
+++ b/curriculum/challenges/chinese-traditional/06-quality-assurance/quality-assurance-and-testing-with-chai/simulate-actions-using-a-headless-browser.md
@@ -0,0 +1,76 @@
+---
+id: 587d824f367417b2b2512c5c
+title: 使用無頭瀏覽器模擬操作
+challengeType: 2
+dashedName: simulate-actions-using-a-headless-browser
+---
+
+# --description--
+
+請注意,本項目在[這個 Repl.it 項目](https://repl.it/github/freeCodeCamp/boilerplate-mochachai)的基礎上進行開發。你也可以從 [GitHub](https://repl.it/github/freeCodeCamp/boilerplate-mochachai) 上克隆。
+
+在接下來的挑戰中,我們將使用名爲 “Headless Browser(無頭瀏覽器)” 的設備模擬人與頁面的交互。
+
+無頭瀏覽器是沒有圖形用戶界面的 Web 瀏覽器。 這種工具對於測試網頁特別有用,因爲它能夠以與瀏覽器相同的方式呈現和理解 HTML、CSS 和 JavaScript。
+
+針對這些挑戰,我們使用 Zombie.JS。 它是一個輕量級瀏覽器,完全基於 JS,而不需要額外的二進制文件來安裝。 這個功能使我們可以在 Repl.it 等環境中使用它。 還有許多其他(更強大的)選擇。
+
+Mocha 允許你在實際測試之前準備一些代碼運行的基礎。 這可能有助於例如在數據庫中創建項目,用於連續測試。
+
+使用無頭瀏覽器,在進行實際測試之前,我們需要**訪問**我們將要檢查的頁面。 `suiteSetup` “hook” 僅在套件啓動時執行。 其他不同的鉤子類型可以在每次測試之前、每次測試之後或者在套件的末尾執行。 更多信息請參閱 Mocha 文檔。
+
+# --instructions--
+
+在 `tests/2_functional-tests.js`中,緊接着 `Browser` 聲明之後,將你的項目 URL 添加到變量的 `site` 屬性:
+
+```js
+Browser.site = 'https://sincere-cone.gomix.me'; // Your URL here
+```
+
+如果你在本地環境中測試,則替換上面的代碼爲:
+
+```js
+Browser.localhost('example.com', process.env.PORT || 3000);
+```
+
+在 `tests/2_functional-tests.js` 中,在 `'Functional Tests with Zombie.js'` 套件的底部,使用以下代碼實例化一個新的 `Browser` 對象:
+
+```js
+const browser = new Browser();
+```
+
+然後,通過以下代碼,使用 `suiteSetup` 鉤子把 `browser` 指向 `/` 路由:
+
+```js
+suiteSetup(function(done) {
+ return browser.visit('/', done);
+});
+```
+
+# --hints--
+
+應通過所有測試。
+
+```js
+(getUserInput) =>
+ $.get(getUserInput('url') + '/_api/get-tests?type=functional').then(
+ (data) => {
+ data.slice(0, 4).forEach((test) => {
+ assert.equal(test.state, 'passed');
+ })
+ },
+ (xhr) => {
+ throw new Error(xhr.responseText);
+ }
+ );
+```
+
+# --solutions--
+
+```js
+/**
+ Backend challenges don't need solutions,
+ because they would need to be tested against a full working project.
+ Please check our contributing guidelines to learn more.
+*/
+```
diff --git a/curriculum/challenges/chinese-traditional/06-quality-assurance/quality-assurance-and-testing-with-chai/test-for-truthiness.md b/curriculum/challenges/chinese-traditional/06-quality-assurance/quality-assurance-and-testing-with-chai/test-for-truthiness.md
new file mode 100644
index 0000000000..e98c017cad
--- /dev/null
+++ b/curriculum/challenges/chinese-traditional/06-quality-assurance/quality-assurance-and-testing-with-chai/test-for-truthiness.md
@@ -0,0 +1,101 @@
+---
+id: 587d824b367417b2b2512c49
+title: 測試真實性
+challengeType: 2
+forumTopicId: 301596
+dashedName: test-for-truthiness
+---
+
+# --description--
+
+請注意,本項目在[這個 Repl.it 項目](https://repl.it/github/freeCodeCamp/boilerplate-mochachai)的基礎上進行開發。你也可以從 [GitHub](https://repl.it/github/freeCodeCamp/boilerplate-mochachai) 上克隆。
+
+`isTrue()` 僅當給出的值爲 Boolean 的 `true` 時可以通過測試;`isNotTrue()` 則會在給出除 `true` 以外的值時通過測試。
+
+```js
+assert.isTrue(true, 'this will pass with the boolean value true');
+assert.isTrue('true', 'this will NOT pass with the string value "true"');
+assert.isTrue(1, 'this will NOT pass with the number value 1');
+```
+
+`isFalse()` 和 `isNotFalse()` 同樣存在,與上面提到的兩個方法類似,只不過它們針對值爲 `false` 的布爾值。
+
+# --instructions--
+
+在 `tests/1_unit-tests.js` 中,`Basic Assertions` 套件中標註爲 `#4` 的測試下,修改每個 `assert` 爲 `assert.isTrue` 或 `assert.isNotTrue`,通過測試(結果應爲 `true`)。 不要修改傳入斷言的參數。
+
+# --hints--
+
+應通過所有測試。
+
+```js
+(getUserInput) =>
+ $.get(getUserInput('url') + '/_api/get-tests?type=unit&n=3').then(
+ (data) => {
+ assert.equal(data.state, 'passed');
+ },
+ (xhr) => {
+ throw new Error(xhr.responseText);
+ }
+ );
+```
+
+應該爲第一個斷言選擇正確的方法:`isTrue` 或 `isNotTrue`。
+
+```js
+(getUserInput) =>
+ $.get(getUserInput('url') + '/_api/get-tests?type=unit&n=3').then(
+ (data) => {
+ assert.equal(data.assertions[0].method, 'isTrue', 'True is true');
+ },
+ (xhr) => {
+ throw new Error(xhr.responseText);
+ }
+ );
+```
+
+應該爲第二個斷言選擇正確的方法:`isTrue` 或 `isNotTrue`。
+
+```js
+(getUserInput) =>
+ $.get(getUserInput('url') + '/_api/get-tests?type=unit&n=3').then(
+ (data) => {
+ assert.equal(
+ data.assertions[1].method,
+ 'isTrue',
+ 'Double negation of a truthy value is true'
+ );
+ },
+ (xhr) => {
+ throw new Error(xhr.responseText);
+ }
+ );
+```
+
+應該爲第三個斷言選擇正確的方法:`isTrue` 或 `isNotTrue`。
+
+```js
+(getUserInput) =>
+ $.get(getUserInput('url') + '/_api/get-tests?type=unit&n=3').then(
+ (data) => {
+ assert.equal(
+ data.assertions[2].method,
+ 'isNotTrue',
+ 'A truthy object is not true - neither is a false one'
+ );
+ },
+ (xhr) => {
+ throw new Error(xhr.responseText);
+ }
+ );
+```
+
+# --solutions--
+
+```js
+/**
+ Backend challenges don't need solutions,
+ because they would need to be tested against a full working project.
+ Please check our contributing guidelines to learn more.
+*/
+```
diff --git a/curriculum/challenges/chinese-traditional/06-quality-assurance/quality-assurance-and-testing-with-chai/test-if-a-string-contains-a-substring.md b/curriculum/challenges/chinese-traditional/06-quality-assurance/quality-assurance-and-testing-with-chai/test-if-a-string-contains-a-substring.md
new file mode 100644
index 0000000000..6e8baabc96
--- /dev/null
+++ b/curriculum/challenges/chinese-traditional/06-quality-assurance/quality-assurance-and-testing-with-chai/test-if-a-string-contains-a-substring.md
@@ -0,0 +1,79 @@
+---
+id: 587d824d367417b2b2512c53
+title: 測試字符串是否包含子字符串
+challengeType: 2
+forumTopicId: 301597
+dashedName: test-if-a-string-contains-a-substring
+---
+
+# --description--
+
+請注意,本項目在[這個 Repl.it 項目](https://repl.it/github/freeCodeCamp/boilerplate-mochachai)的基礎上進行開發。你也可以從 [GitHub](https://repl.it/github/freeCodeCamp/boilerplate-mochachai) 上克隆。
+
+`include()` 和 `notInclude()` 同樣可以用於字符串。 `include()` 用於斷言字符串中包含某個子字符串。
+
+# --instructions--
+
+在 `tests/1_unit-tests.js` 中,`Strings` 套件裏標有 `#14` 的測試下,將每個 `assert` 改成 `assert.include` 或 `assert.notInclude` 方法,通過測試(結果應該返回 `true`)。 不要修改傳給斷言的參數。
+
+# --hints--
+
+應通過所有測試。
+
+```js
+(getUserInput) =>
+ $.get(getUserInput('url') + '/_api/get-tests?type=unit&n=13').then(
+ (data) => {
+ assert.equal(data.state, 'passed');
+ },
+ (xhr) => {
+ throw new Error(xhr.responseText);
+ }
+ );
+```
+
+應該爲第一個斷言選擇正確的方法:`include` 或 `notInclude`。
+
+```js
+(getUserInput) =>
+ $.get(getUserInput('url') + '/_api/get-tests?type=unit&n=13').then(
+ (data) => {
+ assert.equal(
+ data.assertions[0].method,
+ 'include',
+ "'Arrow' contains 'row'..."
+ );
+ },
+ (xhr) => {
+ throw new Error(xhr.responseText);
+ }
+ );
+```
+
+應該爲第二個斷言選擇正確的方法:`include` 或 `notInclude`。
+
+```js
+(getUserInput) =>
+ $.get(getUserInput('url') + '/_api/get-tests?type=unit&n=13').then(
+ (data) => {
+ assert.equal(
+ data.assertions[1].method,
+ 'notInclude',
+ "... a 'dart' doesn't contain a 'queue'"
+ );
+ },
+ (xhr) => {
+ throw new Error(xhr.responseText);
+ }
+ );
+```
+
+# --solutions--
+
+```js
+/**
+ Backend challenges don't need solutions,
+ because they would need to be tested against a full working project.
+ Please check our contributing guidelines to learn more.
+*/
+```
diff --git a/curriculum/challenges/chinese-traditional/06-quality-assurance/quality-assurance-and-testing-with-chai/test-if-a-value-falls-within-a-specific-range.md b/curriculum/challenges/chinese-traditional/06-quality-assurance/quality-assurance-and-testing-with-chai/test-if-a-value-falls-within-a-specific-range.md
new file mode 100644
index 0000000000..4a5b953dcf
--- /dev/null
+++ b/curriculum/challenges/chinese-traditional/06-quality-assurance/quality-assurance-and-testing-with-chai/test-if-a-value-falls-within-a-specific-range.md
@@ -0,0 +1,87 @@
+---
+id: 587d824c367417b2b2512c4f
+title: 測試某個值是否在特定範圍內
+challengeType: 2
+forumTopicId: 301598
+dashedName: test-if-a-value-falls-within-a-specific-range
+---
+
+# --description--
+
+請注意,本項目在 [這個 Repl.it 項目](https://repl.it/github/freeCodeCamp/boilerplate-mochachai) 的基礎上進行開發。 你也可以從 [GitHub](https://repl.it/github/freeCodeCamp/boilerplate-mochachai) 上克隆。
+
+```javascript
+.approximately(actual, expected, delta, [message])
+```
+
+斷言 `actual` 等於 `expected`,在 +/- `delta` 的範圍內。
+
+# --instructions--
+
+在 `tests/1_unit-tests.js` 中,在 `Comparisons` 套件裏標有 `#10` 的測試中,將每個 `assert` 改成 `assert.approximately`,讓測試通過(結果應該返回 `true`)。
+
+選擇最小範圍(第三個參數)來通過所有測試。 它應該小於 1。
+
+# --hints--
+
+不應有未通過的測試
+
+```js
+(getUserInput) =>
+ $.get(getUserInput('url') + '/_api/get-tests?type=unit&n=9').then(
+ (data) => {
+ assert.equal(data.state, 'passed');
+ },
+ (xhr) => {
+ throw new Error(xhr.responseText);
+ }
+ );
+```
+
+應該爲第一個斷言選擇正確的範圍——`approximately(actual, expected, range)`。
+
+```js
+(getUserInput) =>
+ $.get(getUserInput('url') + '/_api/get-tests?type=unit&n=9').then(
+ (data) => {
+ assert.equal(data.assertions[0].method, 'approximately');
+ assert.equal(
+ data.assertions[0].args[2],
+ 0.5,
+ "weirdNumbers(0.5) is in the range (0.5, 1.5]. It's within 1 +/- 0.5"
+ );
+ },
+ (xhr) => {
+ throw new Error(xhr.responseText);
+ }
+ );
+```
+
+應該爲第二個斷言選擇正確的範圍——`approximately(actual, expected, range)`。
+
+```js
+(getUserInput) =>
+ $.get(getUserInput('url') + '/_api/get-tests?type=unit&n=9').then(
+ (data) => {
+ assert.equal(data.assertions[1].method, 'approximately');
+ assert.equal(
+ data.assertions[1].args[2],
+ 0.8,
+ "weirdNumbers(0.2) is in the range (0.2, 1.2]. It's within 1 +/- 0.8"
+ );
+ },
+ (xhr) => {
+ throw new Error(xhr.responseText);
+ }
+ );
+```
+
+# --solutions--
+
+```js
+/**
+ Backend challenges don't need solutions,
+ because they would need to be tested against a full working project.
+ Please check our contributing guidelines to learn more.
+*/
+```
diff --git a/curriculum/challenges/chinese-traditional/06-quality-assurance/quality-assurance-and-testing-with-chai/test-if-a-value-is-a-string.md b/curriculum/challenges/chinese-traditional/06-quality-assurance/quality-assurance-and-testing-with-chai/test-if-a-value-is-a-string.md
new file mode 100644
index 0000000000..7fb8b42b5d
--- /dev/null
+++ b/curriculum/challenges/chinese-traditional/06-quality-assurance/quality-assurance-and-testing-with-chai/test-if-a-value-is-a-string.md
@@ -0,0 +1,93 @@
+---
+id: 587d824d367417b2b2512c52
+title: 測試某個值是否爲字符串
+challengeType: 2
+forumTopicId: 301599
+dashedName: test-if-a-value-is-a-string
+---
+
+# --description--
+
+請注意,本項目在[這個 Repl.it 項目](https://repl.it/github/freeCodeCamp/boilerplate-mochachai)的基礎上進行開發。你也可以從 [GitHub](https://repl.it/github/freeCodeCamp/boilerplate-mochachai) 上克隆。
+
+`isString` 或 `isNotString` 斷言一個值是否爲字符串。
+
+# --instructions--
+
+在 `tests/1_unit-tests.js` 中,`Strings` 套件裏標有 `#13` 的測試下,將每個 `assert` 改成 `assert.isString` 或 `assert.isNotString` 方法,通過測試(結果應該返回 `true`)。 不要修改傳給斷言的參數。
+
+# --hints--
+
+應通過所有測試。
+
+```js
+(getUserInput) =>
+ $.get(getUserInput('url') + '/_api/get-tests?type=unit&n=12').then(
+ (data) => {
+ assert.equal(data.state, 'passed');
+ },
+ (xhr) => {
+ throw new Error(xhr.responseText);
+ }
+ );
+```
+
+應該爲第一個斷言選擇正確的方法:`isString` 或 `isNotString`。
+
+```js
+(getUserInput) =>
+ $.get(getUserInput('url') + '/_api/get-tests?type=unit&n=12').then(
+ (data) => {
+ assert.equal(
+ data.assertions[0].method,
+ 'isNotString',
+ 'A float number is not a string'
+ );
+ },
+ (xhr) => {
+ throw new Error(xhr.responseText);
+ }
+ );
+```
+
+應該爲第二個斷言選擇正確的方法:`isString` 或 `isNotString`。
+
+```js
+(getUserInput) =>
+ $.get(getUserInput('url') + '/_api/get-tests?type=unit&n=12').then(
+ (data) => {
+ assert.equal(
+ data.assertions[1].method,
+ 'isString',
+ 'environment vars are strings (or undefined)'
+ );
+ },
+ (xhr) => {
+ throw new Error(xhr.responseText);
+ }
+ );
+```
+
+應該爲第三個斷言選擇正確的方法:`isString` 或 `isNotString`。
+
+```js
+(getUserInput) =>
+ $.get(getUserInput('url') + '/_api/get-tests?type=unit&n=12').then(
+ (data) => {
+ assert.equal(data.assertions[2].method, 'isString', 'A JSON is a string');
+ },
+ (xhr) => {
+ throw new Error(xhr.responseText);
+ }
+ );
+```
+
+# --solutions--
+
+```js
+/**
+ Backend challenges don't need solutions,
+ because they would need to be tested against a full working project.
+ Please check our contributing guidelines to learn more.
+*/
+```
diff --git a/curriculum/challenges/chinese-traditional/06-quality-assurance/quality-assurance-and-testing-with-chai/test-if-a-value-is-an-array.md b/curriculum/challenges/chinese-traditional/06-quality-assurance/quality-assurance-and-testing-with-chai/test-if-a-value-is-an-array.md
new file mode 100644
index 0000000000..145d96600f
--- /dev/null
+++ b/curriculum/challenges/chinese-traditional/06-quality-assurance/quality-assurance-and-testing-with-chai/test-if-a-value-is-an-array.md
@@ -0,0 +1,77 @@
+---
+id: 587d824d367417b2b2512c50
+title: 測試某個值是否爲數組
+challengeType: 2
+forumTopicId: 301600
+dashedName: test-if-a-value-is-an-array
+---
+
+# --description--
+
+請注意,本項目在[這個 Repl.it 項目](https://repl.it/github/freeCodeCamp/boilerplate-mochachai)的基礎上進行開發。你也可以從 [GitHub](https://repl.it/github/freeCodeCamp/boilerplate-mochachai) 上克隆。
+
+# --instructions--
+
+在 `tests/1_unit-tests.js` 中,`Arrays` 套件裏標有 `#11` 的測試下,將每個 `assert` 改成 `assert.isArray` 或 `assert.isNotArray` 方法,通過測試(結果應該返回 `true`)。 不要改變傳入斷言的參數。
+
+# --hints--
+
+不應有未通過的測試
+
+```js
+(getUserInput) =>
+ $.get(getUserInput('url') + '/_api/get-tests?type=unit&n=10').then(
+ (data) => {
+ assert.equal(data.state, 'passed');
+ },
+ (xhr) => {
+ throw new Error(xhr.responseText);
+ }
+ );
+```
+
+請選擇正確的斷言——`isArray` 或 `isNotArray`。
+
+```js
+(getUserInput) =>
+ $.get(getUserInput('url') + '/_api/get-tests?type=unit&n=10').then(
+ (data) => {
+ assert.equal(
+ data.assertions[0].method,
+ 'isArray',
+ 'String.prototype.split() returns an Array'
+ );
+ },
+ (xhr) => {
+ throw new Error(xhr.responseText);
+ }
+ );
+```
+
+請選擇正確的斷言——`isArray` 或 `isNotArray`。
+
+```js
+(getUserInput) =>
+ $.get(getUserInput('url') + '/_api/get-tests?type=unit&n=10').then(
+ (data) => {
+ assert.equal(
+ data.assertions[1].method,
+ 'isNotArray',
+ 'Array.prototype.indexOf() returns a number'
+ );
+ },
+ (xhr) => {
+ throw new Error(xhr.responseText);
+ }
+ );
+```
+
+# --solutions--
+
+```js
+/**
+ Backend challenges don't need solutions,
+ because they would need to be tested against a full working project.
+ Please check our contributing guidelines to learn more.
+*/
+```
diff --git a/curriculum/challenges/chinese-traditional/06-quality-assurance/quality-assurance-and-testing-with-chai/test-if-a-value-is-of-a-specific-data-structure-type.md b/curriculum/challenges/chinese-traditional/06-quality-assurance/quality-assurance-and-testing-with-chai/test-if-a-value-is-of-a-specific-data-structure-type.md
new file mode 100644
index 0000000000..19f86db31e
--- /dev/null
+++ b/curriculum/challenges/chinese-traditional/06-quality-assurance/quality-assurance-and-testing-with-chai/test-if-a-value-is-of-a-specific-data-structure-type.md
@@ -0,0 +1,133 @@
+---
+id: 587d824e367417b2b2512c56
+title: 測試值是否爲特定數據結構類型
+challengeType: 2
+forumTopicId: 301601
+dashedName: test-if-a-value-is-of-a-specific-data-structure-type
+---
+
+# --description--
+
+請注意,本項目在[這個 Repl.it 項目](https://repl.it/github/freeCodeCamp/boilerplate-mochachai)的基礎上進行開發。你也可以從 [GitHub](https://repl.it/github/freeCodeCamp/boilerplate-mochachai) 上克隆。
+
+`#typeOf` 斷言一個值的類型符合給定的類型,這個類型與 `Object.prototype.toString` 一致。
+
+# --instructions--
+
+在 `tests/1_unit-tests.js` 中,`Objects` 套件裏標有 `#17` 的測試下,將每個 `assert` 改成 `assert.typeOf` 或 `assert.notTypeOf` 方法,通過測試(結果應該返回 `true`)。 不要修改傳給斷言的參數。
+
+# --hints--
+
+應通過所有測試。
+
+```js
+(getUserInput) =>
+ $.get(getUserInput('url') + '/_api/get-tests?type=unit&n=16').then(
+ (data) => {
+ assert.equal(data.state, 'passed');
+ },
+ (xhr) => {
+ throw new Error(xhr.responseText);
+ }
+ );
+```
+
+應該爲第一個斷言選擇正確的方法:`typeOf` 或 `notTypeOf`。
+
+```js
+(getUserInput) =>
+ $.get(getUserInput('url') + '/_api/get-tests?type=unit&n=16').then(
+ (data) => {
+ assert.equal(
+ data.assertions[0].method,
+ 'typeOf',
+ 'myCar is typeOf Object'
+ );
+ },
+ (xhr) => {
+ throw new Error(xhr.responseText);
+ }
+ );
+```
+
+應該爲第二個斷言選擇正確的方法:`typeOf` 或 `notTypeOf`。
+
+```js
+(getUserInput) =>
+ $.get(getUserInput('url') + '/_api/get-tests?type=unit&n=16').then(
+ (data) => {
+ assert.equal(
+ data.assertions[1].method,
+ 'typeOf',
+ 'Car.model is a String'
+ );
+ },
+ (xhr) => {
+ throw new Error(xhr.responseText);
+ }
+ );
+```
+
+應該爲第三個斷言選擇正確的方法:`typeOf` 或 `notTypeOf`。
+
+```js
+(getUserInput) =>
+ $.get(getUserInput('url') + '/_api/get-tests?type=unit&n=16').then(
+ (data) => {
+ assert.equal(
+ data.assertions[2].method,
+ 'notTypeOf',
+ 'Plane.wings is a Number (not a String)'
+ );
+ },
+ (xhr) => {
+ throw new Error(xhr.responseText);
+ }
+ );
+```
+
+應該爲第四個斷言選擇正確的方法:`typeOf` 或 `notTypeOf`。
+
+```js
+(getUserInput) =>
+ $.get(getUserInput('url') + '/_api/get-tests?type=unit&n=16').then(
+ (data) => {
+ assert.equal(
+ data.assertions[3].method,
+ 'typeOf',
+ 'Plane.engines is an Array'
+ );
+ },
+ (xhr) => {
+ throw new Error(xhr.responseText);
+ }
+ );
+```
+
+應該爲第五個斷言選擇正確的方法:`typeOf` 或 `notTypeOf`。
+
+```js
+(getUserInput) =>
+ $.get(getUserInput('url') + '/_api/get-tests?type=unit&n=16').then(
+ (data) => {
+ assert.equal(
+ data.assertions[4].method,
+ 'typeOf',
+ 'Car.wheels is a Number'
+ );
+ },
+ (xhr) => {
+ throw new Error(xhr.responseText);
+ }
+ );
+```
+
+# --solutions--
+
+```js
+/**
+ Backend challenges don't need solutions,
+ because they would need to be tested against a full working project.
+ Please check our contributing guidelines to learn more.
+*/
+```
diff --git a/curriculum/challenges/chinese-traditional/06-quality-assurance/quality-assurance-and-testing-with-chai/test-if-a-variable-or-function-is-defined.md b/curriculum/challenges/chinese-traditional/06-quality-assurance/quality-assurance-and-testing-with-chai/test-if-a-variable-or-function-is-defined.md
new file mode 100644
index 0000000000..595bf37f82
--- /dev/null
+++ b/curriculum/challenges/chinese-traditional/06-quality-assurance/quality-assurance-and-testing-with-chai/test-if-a-variable-or-function-is-defined.md
@@ -0,0 +1,95 @@
+---
+id: 587d824b367417b2b2512c47
+title: 測試變量或函數是否已定義
+challengeType: 2
+forumTopicId: 301602
+dashedName: test-if-a-variable-or-function-is-defined
+---
+
+# --description--
+
+請注意,本項目在[這個 Repl.it 項目](https://repl.it/github/freeCodeCamp/boilerplate-mochachai)的基礎上進行開發。你也可以從 [GitHub](https://repl.it/github/freeCodeCamp/boilerplate-mochachai) 上克隆。
+
+# --instructions--
+
+在 `tests/1_unit-tests.js` 中,`Basic Assertions` 套件中標註爲 `#2` 的測試下,修改每個 `assert` 爲 `assert.isDefined()` 或 `assert.isUndefined()`,通過測試(結果應爲 `true`)。 不要修改傳給斷言的參數。
+
+# --hints--
+
+應通過所有測試。
+
+```js
+(getUserInput) =>
+ $.get(getUserInput('url') + '/_api/get-tests?type=unit&n=1').then(
+ (data) => {
+ assert.equal(data.state, 'passed');
+ },
+ (xhr) => {
+ throw new Error(xhr.responseText);
+ }
+ );
+```
+
+應該爲第一個斷言選擇正確的方法:`isDefined` 或 `isUndefined`。
+
+```js
+(getUserInput) =>
+ $.get(getUserInput('url') + '/_api/get-tests?type=unit&n=1').then(
+ (data) => {
+ assert.equal(
+ data.assertions[0].method,
+ 'isDefined',
+ 'Null is not undefined'
+ );
+ },
+ (xhr) => {
+ throw new Error(xhr.responseText);
+ }
+ );
+```
+
+應該爲第二個斷言選擇正確的方法:`isDefined` 或 `isUndefined`。
+
+```js
+(getUserInput) =>
+ $.get(getUserInput('url') + '/_api/get-tests?type=unit&n=1').then(
+ (data) => {
+ assert.equal(
+ data.assertions[1].method,
+ 'isUndefined',
+ 'Undefined is undefined'
+ );
+ },
+ (xhr) => {
+ throw new Error(xhr.responseText);
+ }
+ );
+```
+
+應該爲第三個斷言選擇正確的方法:`isDefined` 或 `isUndefined`。
+
+```js
+(getUserInput) =>
+ $.get(getUserInput('url') + '/_api/get-tests?type=unit&n=1').then(
+ (data) => {
+ assert.equal(
+ data.assertions[2].method,
+ 'isDefined',
+ 'A string is not undefined'
+ );
+ },
+ (xhr) => {
+ throw new Error(xhr.responseText);
+ }
+ );
+```
+
+# --solutions--
+
+```js
+/**
+ Backend challenges don't need solutions,
+ because they would need to be tested against a full working project.
+ Please check our contributing guidelines to learn more.
+*/
+```
diff --git a/curriculum/challenges/chinese-traditional/06-quality-assurance/quality-assurance-and-testing-with-chai/test-if-an-array-contains-an-item.md b/curriculum/challenges/chinese-traditional/06-quality-assurance/quality-assurance-and-testing-with-chai/test-if-an-array-contains-an-item.md
new file mode 100644
index 0000000000..5467e7c12e
--- /dev/null
+++ b/curriculum/challenges/chinese-traditional/06-quality-assurance/quality-assurance-and-testing-with-chai/test-if-an-array-contains-an-item.md
@@ -0,0 +1,77 @@
+---
+id: 587d824d367417b2b2512c51
+title: 測試數組是否包含項目
+challengeType: 2
+forumTopicId: 301603
+dashedName: test-if-an-array-contains-an-item
+---
+
+# --description--
+
+請注意,本項目在[這個 Repl.it 項目](https://repl.it/github/freeCodeCamp/boilerplate-mochachai)的基礎上進行開發。你也可以從 [GitHub](https://repl.it/github/freeCodeCamp/boilerplate-mochachai) 上克隆。
+
+# --instructions--
+
+在 `tests/1_unit-tests.js` 中,`Arrays` 套件裏標有 `#12` 的測試下,將每個 `assert` 改成 `assert.include` 或 `assert.notInclude` 方法,通過測試(結果應該返回 `true`)。 不要修改傳給斷言的參數。
+
+# --hints--
+
+應通過所有測試。
+
+```js
+(getUserInput) =>
+ $.get(getUserInput('url') + '/_api/get-tests?type=unit&n=11').then(
+ (data) => {
+ assert.equal(data.state, 'passed');
+ },
+ (xhr) => {
+ throw new Error(xhr.responseText);
+ }
+ );
+```
+
+應該爲第一個斷言選擇正確的方法:`include` 或 `notInclude`。
+
+```js
+(getUserInput) =>
+ $.get(getUserInput('url') + '/_api/get-tests?type=unit&n=11').then(
+ (data) => {
+ assert.equal(
+ data.assertions[0].method,
+ 'notInclude',
+ "It's summer in july..."
+ );
+ },
+ (xhr) => {
+ throw new Error(xhr.responseText);
+ }
+ );
+```
+
+應該爲第二個斷言選擇正確的方法:`include` 或 `notInclude`。
+
+```js
+(getUserInput) =>
+ $.get(getUserInput('url') + '/_api/get-tests?type=unit&n=11').then(
+ (data) => {
+ assert.equal(
+ data.assertions[1].method,
+ 'include',
+ 'JavaScript is a backend language !!'
+ );
+ },
+ (xhr) => {
+ throw new Error(xhr.responseText);
+ }
+ );
+```
+
+# --solutions--
+
+```js
+/**
+ Backend challenges don't need solutions,
+ because they would need to be tested against a full working project.
+ Please check our contributing guidelines to learn more.
+*/
+```
diff --git a/curriculum/challenges/chinese-traditional/06-quality-assurance/quality-assurance-and-testing-with-chai/test-if-an-object-has-a-property.md b/curriculum/challenges/chinese-traditional/06-quality-assurance/quality-assurance-and-testing-with-chai/test-if-an-object-has-a-property.md
new file mode 100644
index 0000000000..80281bcd52
--- /dev/null
+++ b/curriculum/challenges/chinese-traditional/06-quality-assurance/quality-assurance-and-testing-with-chai/test-if-an-object-has-a-property.md
@@ -0,0 +1,93 @@
+---
+id: 587d824e367417b2b2512c55
+title: 測試對象是否具有某個屬性
+challengeType: 2
+forumTopicId: 301604
+dashedName: test-if-an-object-has-a-property
+---
+
+# --description--
+
+請注意,本項目在[這個 Repl.it 項目](https://repl.it/github/freeCodeCamp/boilerplate-mochachai)的基礎上進行開發。你也可以從 [GitHub](https://repl.it/github/freeCodeCamp/boilerplate-mochachai) 上克隆。
+
+`property` 斷言一個對象含有給定屬性。
+
+# --instructions--
+
+在 `tests/1_unit-tests.js` 中,`Objects` 套件裏標有 `#16` 的測試下,將每個 `assert` 改成 `assert.property` 或 `assert.notProperty` 方法,通過測試(結果應該返回 `true`)。 不要修改傳給斷言的參數。
+
+# --hints--
+
+應通過所有測試。
+
+```js
+(getUserInput) =>
+ $.get(getUserInput('url') + '/_api/get-tests?type=unit&n=15').then(
+ (data) => {
+ assert.equal(data.state, 'passed');
+ },
+ (xhr) => {
+ throw new Error(xhr.responseText);
+ }
+ );
+```
+
+應該爲第一個斷言選擇正確的方法:`property` 或 `notProperty`。
+
+```js
+(getUserInput) =>
+ $.get(getUserInput('url') + '/_api/get-tests?type=unit&n=15').then(
+ (data) => {
+ assert.equal(
+ data.assertions[0].method,
+ 'notProperty',
+ 'A car has not wings'
+ );
+ },
+ (xhr) => {
+ throw new Error(xhr.responseText);
+ }
+ );
+```
+
+應該爲第二個斷言選擇正確的方法:`property` 或 `notProperty`。
+
+```js
+(getUserInput) =>
+ $.get(getUserInput('url') + '/_api/get-tests?type=unit&n=15').then(
+ (data) => {
+ assert.equal(
+ data.assertions[1].method,
+ 'property',
+ 'planes have engines'
+ );
+ },
+ (xhr) => {
+ throw new Error(xhr.responseText);
+ }
+ );
+```
+
+應該爲第三個斷言選擇正確的方法:`property` 或 `notProperty`。
+
+```js
+(getUserInput) =>
+ $.get(getUserInput('url') + '/_api/get-tests?type=unit&n=15').then(
+ (data) => {
+ assert.equal(data.assertions[2].method, 'property', 'Cars have wheels');
+ },
+ (xhr) => {
+ throw new Error(xhr.responseText);
+ }
+ );
+```
+
+# --solutions--
+
+```js
+/**
+ Backend challenges don't need solutions,
+ because they would need to be tested against a full working project.
+ Please check our contributing guidelines to learn more.
+*/
+```
diff --git a/curriculum/challenges/chinese-traditional/06-quality-assurance/quality-assurance-and-testing-with-chai/test-if-an-object-is-an-instance-of-a-constructor.md b/curriculum/challenges/chinese-traditional/06-quality-assurance/quality-assurance-and-testing-with-chai/test-if-an-object-is-an-instance-of-a-constructor.md
new file mode 100644
index 0000000000..a57ff1cf4a
--- /dev/null
+++ b/curriculum/challenges/chinese-traditional/06-quality-assurance/quality-assurance-and-testing-with-chai/test-if-an-object-is-an-instance-of-a-constructor.md
@@ -0,0 +1,115 @@
+---
+id: 587d824e367417b2b2512c57
+title: 測試對象是否是構造函數的實例
+challengeType: 2
+forumTopicId: 301605
+dashedName: test-if-an-object-is-an-instance-of-a-constructor
+---
+
+# --description--
+
+請注意,本項目在[這個 Repl.it 項目](https://repl.it/github/freeCodeCamp/boilerplate-mochachai)的基礎上進行開發。你也可以從 [GitHub](https://repl.it/github/freeCodeCamp/boilerplate-mochachai) 上克隆。
+
+`#instanceOf` 斷言一個對象是一個構造器的實例。
+
+# --instructions--
+
+在 `tests/1_unit-tests.js` 中,`Objects` 套件裏標有 `#18` 的測試下,將每個 `assert` 改成 `assert.instanceOf` 或 `assert.notInstanceOf` 方法,通過測試(結果應該返回 `true`)。 不要修改傳給斷言的參數。
+
+# --hints--
+
+應通過所有測試。
+
+```js
+(getUserInput) =>
+ $.get(getUserInput('url') + '/_api/get-tests?type=unit&n=17').then(
+ (data) => {
+ assert.equal(data.state, 'passed');
+ },
+ (xhr) => {
+ throw new Error(xhr.responseText);
+ }
+ );
+```
+
+應該爲第一個斷言選擇正確的方法:`instanceOf` 或 `notInstanceOf`。
+
+```js
+(getUserInput) =>
+ $.get(getUserInput('url') + '/_api/get-tests?type=unit&n=17').then(
+ (data) => {
+ assert.equal(
+ data.assertions[0].method,
+ 'notInstanceOf',
+ 'myCar is not an instance of Plane'
+ );
+ },
+ (xhr) => {
+ throw new Error(xhr.responseText);
+ }
+ );
+```
+
+應該爲第二個斷言選擇正確的方法:`instanceOf` 或 `notInstanceOf`。
+
+```js
+(getUserInput) =>
+ $.get(getUserInput('url') + '/_api/get-tests?type=unit&n=17').then(
+ (data) => {
+ assert.equal(
+ data.assertions[1].method,
+ 'instanceOf',
+ 'airlinePlane is an instance of Plane'
+ );
+ },
+ (xhr) => {
+ throw new Error(xhr.responseText);
+ }
+ );
+```
+
+應該爲第三個斷言選擇正確的方法:`instanceOf` 或 `notInstanceOf`。
+
+```js
+(getUserInput) =>
+ $.get(getUserInput('url') + '/_api/get-tests?type=unit&n=17').then(
+ (data) => {
+ assert.equal(
+ data.assertions[2].method,
+ 'instanceOf',
+ 'everything is an Object in JavaScript...'
+ );
+ },
+ (xhr) => {
+ throw new Error(xhr.responseText);
+ }
+ );
+```
+
+應該爲第四個斷言選擇正確的方法:`instanceOf` 或 `notInstanceOf`。
+
+```js
+(getUserInput) =>
+ $.get(getUserInput('url') + '/_api/get-tests?type=unit&n=17').then(
+ (data) => {
+ assert.equal(
+ data.assertions[3].method,
+ 'notInstanceOf',
+ 'myCar.wheels is not an instance of String'
+ );
+ },
+ (xhr) => {
+ throw new Error(xhr.responseText);
+ }
+ );
+```
+
+# --solutions--
+
+```js
+/**
+ Backend challenges don't need solutions,
+ because they would need to be tested against a full working project.
+ Please check our contributing guidelines to learn more.
+*/
+```
diff --git a/curriculum/challenges/chinese-traditional/06-quality-assurance/quality-assurance-and-testing-with-chai/test-if-one-value-is-below-or-at-least-as-large-as-another.md b/curriculum/challenges/chinese-traditional/06-quality-assurance/quality-assurance-and-testing-with-chai/test-if-one-value-is-below-or-at-least-as-large-as-another.md
new file mode 100644
index 0000000000..2d94fd0f6a
--- /dev/null
+++ b/curriculum/challenges/chinese-traditional/06-quality-assurance/quality-assurance-and-testing-with-chai/test-if-one-value-is-below-or-at-least-as-large-as-another.md
@@ -0,0 +1,109 @@
+---
+id: 587d824c367417b2b2512c4e
+title: 測試一個值是否小於或等於另一個值
+challengeType: 2
+forumTopicId: 301606
+dashedName: test-if-one-value-is-below-or-at-least-as-large-as-another
+---
+
+# --description--
+
+請注意,本項目在[這個 Repl.it 項目](https://repl.it/github/freeCodeCamp/boilerplate-mochachai)的基礎上進行開發。你也可以從 [GitHub](https://repl.it/github/freeCodeCamp/boilerplate-mochachai) 上克隆。
+
+# --instructions--
+
+在 `tests/1_unit-tests.js` 中,在 `Comparisons` 套件裏標有 `#9` 的測試中,將每個 `assert` 改成 `assert.isBelow` 或 `assert.isAtLeast` 方法,讓測試通過(結果應該返回 `true`)。 不要修改傳給斷言的參數。
+
+# --hints--
+
+應通過所有測試。
+
+```js
+(getUserInput) =>
+ $.get(getUserInput('url') + '/_api/get-tests?type=unit&n=8').then(
+ (data) => {
+ assert.equal(data.state, 'passed');
+ },
+ (xhr) => {
+ throw new Error(xhr.responseText);
+ }
+ );
+```
+
+應該爲第一個斷言選擇正確的方法:`isBelow` 或 `isAtLeast`。
+
+```js
+(getUserInput) =>
+ $.get(getUserInput('url') + '/_api/get-tests?type=unit&n=8').then(
+ (data) => {
+ assert.equal(
+ data.assertions[0].method,
+ 'isAtLeast',
+ '5 is at least (>=) 5'
+ );
+ },
+ (xhr) => {
+ throw new Error(xhr.responseText);
+ }
+ );
+```
+
+應該爲第二個斷言選擇正確的方法:`isBelow` 或 `isAtLeast`。
+
+```js
+(getUserInput) =>
+ $.get(getUserInput('url') + '/_api/get-tests?type=unit&n=8').then(
+ (data) => {
+ assert.equal(
+ data.assertions[1].method,
+ 'isAtLeast',
+ '2 * Math.random() is at least 0'
+ );
+ },
+ (xhr) => {
+ throw new Error(xhr.responseText);
+ }
+ );
+```
+
+應該爲第三個斷言選擇正確的方法:`isBelow` 或 `isAtLeast`。
+
+```js
+(getUserInput) =>
+ $.get(getUserInput('url') + '/_api/get-tests?type=unit&n=8').then(
+ (data) => {
+ assert.equal(data.assertions[2].method, 'isBelow', '1 is smaller than 2');
+ },
+ (xhr) => {
+ throw new Error(xhr.responseText);
+ }
+ );
+```
+
+應該爲第四個斷言選擇正確的方法:`isBelow` 或 `isAtLeast`。
+
+```js
+(getUserInput) =>
+ $.get(getUserInput('url') + '/_api/get-tests?type=unit&n=8').then(
+ (data) => {
+ assert.equal(
+ data.assertions[3].method,
+ 'isBelow',
+ '2/3 (0.6666) is smaller than 1'
+ );
+ },
+ (xhr) => {
+ throw new Error(xhr.responseText);
+ }
+ );
+```
+
+# --solutions--
+
+```js
+/**
+ Backend challenges don't need solutions,
+ because they would need to be tested against a full working project.
+ Please check our contributing guidelines to learn more.
+*/
+```
diff --git a/curriculum/challenges/chinese-traditional/06-quality-assurance/quality-assurance-and-testing-with-chai/use-assert.isok-and-assert.isnotok.md b/curriculum/challenges/chinese-traditional/06-quality-assurance/quality-assurance-and-testing-with-chai/use-assert.isok-and-assert.isnotok.md
new file mode 100644
index 0000000000..ce7a25c4ab
--- /dev/null
+++ b/curriculum/challenges/chinese-traditional/06-quality-assurance/quality-assurance-and-testing-with-chai/use-assert.isok-and-assert.isnotok.md
@@ -0,0 +1,87 @@
+---
+id: 587d824b367417b2b2512c48
+title: 使用 Assert.isOk() 和 Assert.isNotOK()
+challengeType: 2
+forumTopicId: 301607
+dashedName: use-assert-isok-and-assert-isnotok
+---
+
+# --description--
+
+請注意,本項目在[這個 Repl.it 項目](https://repl.it/github/freeCodeCamp/boilerplate-mochachai)的基礎上進行開發。你也可以從 [GitHub](https://repl.it/github/freeCodeCamp/boilerplate-mochachai) 上克隆。
+
+`isOk()` 用來測試值是否爲真值,`isNotOk()` 用來測試值是否爲假值。
+
+可以在[過濾數組中的假值](https://www.freecodecamp.org/learn/javascript-algorithms-and-data-structures/basic-algorithm-scripting/falsy-bouncer)這個挑戰中瞭解更多關於真值和假值的信息。
+
+# --instructions--
+
+在 `tests/1_unit-tests.js` 中,`Basic Assertions` 套件中標註爲 `#3` 的測試下,修改每個 `assert` 爲 `assert.isOk()` 或 `assert.isNotOk()`,通過測試(結果應爲 `true`)。 不要修改傳入斷言的參數。
+
+# --hints--
+
+應通過所有測試。
+
+```js
+(getUserInput) =>
+ $.get(getUserInput('url') + '/_api/get-tests?type=unit&n=2').then(
+ (data) => {
+ assert.equal(data.state, 'passed');
+ },
+ (xhr) => {
+ throw new Error(xhr.responseText);
+ }
+ );
+```
+
+應該第一個斷言選擇正確的方法:`isOk` 或 `isNotOk`。
+
+```js
+(getUserInput) =>
+ $.get(getUserInput('url') + '/_api/get-tests?type=unit&n=2').then(
+ (data) => {
+ assert.equal(data.assertions[0].method, 'isNotOk', 'Null is falsy');
+ },
+ (xhr) => {
+ throw new Error(xhr.responseText);
+ }
+ );
+```
+
+應該第二個斷言選擇正確的方法:`isOk` 或 `isNotOk`。
+
+```js
+(getUserInput) =>
+ $.get(getUserInput('url') + '/_api/get-tests?type=unit&n=2').then(
+ (data) => {
+ assert.equal(data.assertions[1].method, 'isOk', 'A string is truthy');
+ },
+ (xhr) => {
+ throw new Error(xhr.responseText);
+ }
+ );
+```
+
+應該第三個斷言選擇正確的方法:`isOk` 或 `isNotOk`。
+
+```js
+(getUserInput) =>
+ $.get(getUserInput('url') + '/_api/get-tests?type=unit&n=2').then(
+ (data) => {
+ assert.equal(data.assertions[2].method, 'isOk', 'true is truthy');
+ },
+ (xhr) => {
+ throw new Error(xhr.responseText);
+ }
+ );
+```
+
+# --solutions--
+
+```js
+/**
+ Backend challenges don't need solutions,
+ because they would need to be tested against a full working project.
+ Please check our contributing guidelines to learn more.
+*/
+```
diff --git a/curriculum/challenges/chinese-traditional/06-quality-assurance/quality-assurance-and-testing-with-chai/use-regular-expressions-to-test-a-string.md b/curriculum/challenges/chinese-traditional/06-quality-assurance/quality-assurance-and-testing-with-chai/use-regular-expressions-to-test-a-string.md
new file mode 100644
index 0000000000..5c49edab68
--- /dev/null
+++ b/curriculum/challenges/chinese-traditional/06-quality-assurance/quality-assurance-and-testing-with-chai/use-regular-expressions-to-test-a-string.md
@@ -0,0 +1,79 @@
+---
+id: 587d824d367417b2b2512c54
+title: 使用正則表達式測試字符串
+challengeType: 2
+forumTopicId: 301608
+dashedName: use-regular-expressions-to-test-a-string
+---
+
+# --description--
+
+請注意,本項目在[這個 Repl.it 項目](https://repl.it/github/freeCodeCamp/boilerplate-mochachai)的基礎上進行開發。你也可以從 [GitHub](https://repl.it/github/freeCodeCamp/boilerplate-mochachai) 上克隆。
+
+`match()` 斷言一個值匹配一個正則表達式(第二個參數)。
+
+# --instructions--
+
+在 `tests/1_unit-tests.js` 中,`Strings` 套件裏標有 `#15` 的測試下,將每個 `assert` 改成 `assert.match` 或 `assert.notMatch` 方法,通過測試(結果應該返回 `true`)。 不要修改傳給斷言的參數。
+
+# --hints--
+
+應通過所有測試。
+
+```js
+(getUserInput) =>
+ $.get(getUserInput('url') + '/_api/get-tests?type=unit&n=14').then(
+ (data) => {
+ assert.equal(data.state, 'passed');
+ },
+ (xhr) => {
+ throw new Error(xhr.responseText);
+ }
+ );
+```
+
+應該爲第一個斷言選擇正確的方法:`match` 或 `notMatch`。
+
+```js
+(getUserInput) =>
+ $.get(getUserInput('url') + '/_api/get-tests?type=unit&n=14').then(
+ (data) => {
+ assert.equal(
+ data.assertions[0].method,
+ 'match',
+ "'# name:John Doe, age:35' matches the regex"
+ );
+ },
+ (xhr) => {
+ throw new Error(xhr.responseText);
+ }
+ );
+```
+
+應該爲第二個斷言選擇正確的方法:`match` 或 `notMatch`。
+
+```js
+(getUserInput) =>
+ $.get(getUserInput('url') + '/_api/get-tests?type=unit&n=14').then(
+ (data) => {
+ assert.equal(
+ data.assertions[1].method,
+ 'notMatch',
+ "'# name:Paul Smith III, age:twenty-four' does not match the regex (the age must be numeric)"
+ );
+ },
+ (xhr) => {
+ throw new Error(xhr.responseText);
+ }
+ );
+```
+
+# --solutions--
+
+```js
+/**
+ Backend challenges don't need solutions,
+ because they would need to be tested against a full working project.
+ Please check our contributing guidelines to learn more.
+*/
+```
diff --git a/curriculum/challenges/chinese-traditional/06-quality-assurance/quality-assurance-and-testing-with-chai/use-the-double-equals-to-assert-equality.md b/curriculum/challenges/chinese-traditional/06-quality-assurance/quality-assurance-and-testing-with-chai/use-the-double-equals-to-assert-equality.md
new file mode 100644
index 0000000000..52f3cee5cc
--- /dev/null
+++ b/curriculum/challenges/chinese-traditional/06-quality-assurance/quality-assurance-and-testing-with-chai/use-the-double-equals-to-assert-equality.md
@@ -0,0 +1,111 @@
+---
+id: 587d824b367417b2b2512c4a
+title: 用兩個等號斷言相等
+challengeType: 2
+forumTopicId: 301609
+dashedName: use-the-double-equals-to-assert-equality
+---
+
+# --description--
+
+請注意,本項目在[這個 Repl.it 項目](https://repl.it/github/freeCodeCamp/boilerplate-mochachai)的基礎上進行開發。你也可以從 [GitHub](https://repl.it/github/freeCodeCamp/boilerplate-mochachai) 上克隆。
+
+`equal()` 使用 `==` 比較對象。
+
+# --instructions--
+
+在 `tests/1_unit-tests.js` 中,在 `Equality` 套件裏標有 `#5` 的測試裏,將每個 `assert` 改爲 `assert.equal` 或 `assert.notEqual`,通過測試(應該返回 `true`)。 不要修改傳給斷言的參數。
+
+# --hints--
+
+應通過所有測試。
+
+```js
+(getUserInput) =>
+ $.get(getUserInput('url') + '/_api/get-tests?type=unit&n=4').then(
+ (data) => {
+ assert.equal(data.state, 'passed');
+ },
+ (xhr) => {
+ throw new Error(xhr.responseText);
+ }
+ );
+```
+
+應該爲第一個斷言選擇正確的方法:`equal` 或 `notEqual`。
+
+```js
+(getUserInput) =>
+ $.get(getUserInput('url') + '/_api/get-tests?type=unit&n=4').then(
+ (data) => {
+ assert.equal(
+ data.assertions[0].method,
+ 'equal',
+ 'Numbers are coerced into strings with == '
+ );
+ },
+ (xhr) => {
+ throw new Error(xhr.responseText);
+ }
+ );
+```
+
+應該爲第二個斷言選擇正確的方法:`equal` 或 `notEqual`。
+
+```js
+(getUserInput) =>
+ $.get(getUserInput('url') + '/_api/get-tests?type=unit&n=4').then(
+ (data) => {
+ assert.equal(
+ data.assertions[1].method,
+ 'notEqual',
+ ' == compares object references'
+ );
+ },
+ (xhr) => {
+ throw new Error(xhr.responseText);
+ }
+ );
+```
+
+應該爲第三個斷言選擇正確的方法:`equal` 或 `notEqual`。
+
+```js
+(getUserInput) =>
+ $.get(getUserInput('url') + '/_api/get-tests?type=unit&n=4').then(
+ (data) => {
+ assert.equal(
+ data.assertions[2].method,
+ 'equal',
+ "6 * '2' is 12 ! It should be equal to '12'"
+ );
+ },
+ (xhr) => {
+ throw new Error(xhr.responseText);
+ }
+ );
+```
+
+應該爲第四個斷言選擇正確的方法:`equal` 或 `notEqual`。
+
+```js
+(getUserInput) =>
+ $.get(getUserInput('url') + '/_api/get-tests?type=unit&n=4').then(
+ (data) => {
+ assert.equal(data.assertions[3].method, 'notEqual', "6 + '2' is '62'...");
+ },
+ (xhr) => {
+ throw new Error(xhr.responseText);
+ }
+ );
+```
+
+# --solutions--
+
+```js
+/**
+ Backend challenges don't need solutions,
+ because they would need to be tested against a full working project.
+ Please check our contributing guidelines to learn more.
+*/
+```
diff --git a/curriculum/challenges/chinese-traditional/06-quality-assurance/quality-assurance-and-testing-with-chai/use-the-triple-equals-to-assert-strict-equality.md b/curriculum/challenges/chinese-traditional/06-quality-assurance/quality-assurance-and-testing-with-chai/use-the-triple-equals-to-assert-strict-equality.md
new file mode 100644
index 0000000000..966823adc3
--- /dev/null
+++ b/curriculum/challenges/chinese-traditional/06-quality-assurance/quality-assurance-and-testing-with-chai/use-the-triple-equals-to-assert-strict-equality.md
@@ -0,0 +1,111 @@
+---
+id: 587d824b367417b2b2512c4b
+title: 用三個等號斷言嚴格相等
+challengeType: 2
+forumTopicId: 301610
+dashedName: use-the-triple-equals-to-assert-strict-equality
+---
+
+# --description--
+
+請注意,本項目在[這個 Repl.it 項目](https://repl.it/github/freeCodeCamp/boilerplate-mochachai)的基礎上進行開發。你也可以從 [GitHub](https://repl.it/github/freeCodeCamp/boilerplate-mochachai) 上克隆。
+
+`strictEqual()` 使用 `===` 比較對象。
+
+# --instructions--
+
+在 `tests/1_unit-tests.js` 中,在 `Equality` 套件裏標有 `#6` 的測試下,將每個 `assert` 改爲 `assert.strictEqual` 或 `assert.notStrictEqual`,讓測試通過(應該返回 `true`)。 不要修改傳給斷言的參數。
+
+# --hints--
+
+應通過所有測試。
+
+```js
+(getUserInput) =>
+ $.get(getUserInput('url') + '/_api/get-tests?type=unit&n=5').then(
+ (data) => {
+ assert.equal(data.state, 'passed');
+ },
+ (xhr) => {
+ throw new Error(xhr.responseText);
+ }
+ );
+```
+
+應該爲第一個斷言選擇正確的方法:`strictEqual` 或 `notStrictEqual`。
+
+```js
+(getUserInput) =>
+ $.get(getUserInput('url') + '/_api/get-tests?type=unit&n=5').then(
+ (data) => {
+ assert.equal(
+ data.assertions[0].method,
+ 'notStrictEqual',
+ 'with strictEqual the type must match'
+ );
+ },
+ (xhr) => {
+ throw new Error(xhr.responseText);
+ }
+ );
+```
+
+應該爲第二個斷言選擇正確的方法:`strictEqual` 或 `notStrictEqual`。
+
+```js
+(getUserInput) =>
+ $.get(getUserInput('url') + '/_api/get-tests?type=unit&n=5').then(
+ (data) => {
+ assert.equal(data.assertions[1].method, 'strictEqual', '3*2 = 6...');
+ },
+ (xhr) => {
+ throw new Error(xhr.responseText);
+ }
+ );
+```
+
+應該爲第三個斷言選擇正確的方法:`strictEqual` 或 `notStrictEqual`。
+
+```js
+(getUserInput) =>
+ $.get(getUserInput('url') + '/_api/get-tests?type=unit&n=5').then(
+ (data) => {
+ assert.equal(
+ data.assertions[2].method,
+ 'strictEqual',
+ "6 * '2' is 12. Types match !"
+ );
+ },
+ (xhr) => {
+ throw new Error(xhr.responseText);
+ }
+ );
+```
+
+應該爲第四個斷言選擇正確的方法:`strictEqual` 或 `notStrictEqual`。
+
+```js
+(getUserInput) =>
+ $.get(getUserInput('url') + '/_api/get-tests?type=unit&n=5').then(
+ (data) => {
+ assert.equal(
+ data.assertions[3].method,
+ 'notStrictEqual',
+ 'Even if they have the same elements, the Arrays are notStrictEqual'
+ );
+ },
+ (xhr) => {
+ throw new Error(xhr.responseText);
+ }
+ );
+```
+
+# --solutions--
+
+```js
+/**
+ Backend challenges don't need solutions,
+ because they would need to be tested against a full working project.
+ Please check our contributing guidelines to learn more.
+*/
+```
diff --git a/curriculum/challenges/chinese-traditional/06-quality-assurance/quality-assurance-projects/american-british-translator.md b/curriculum/challenges/chinese-traditional/06-quality-assurance/quality-assurance-projects/american-british-translator.md
new file mode 100644
index 0000000000..9d14ecfa6f
--- /dev/null
+++ b/curriculum/challenges/chinese-traditional/06-quality-assurance/quality-assurance-projects/american-british-translator.md
@@ -0,0 +1,336 @@
+---
+id: 5e601c0d5ac9d0ecd8b94afe
+title: 美式英語英式英語轉換器
+challengeType: 4
+dashedName: american-british-translator
+---
+
+# --description--
+
+構建一個 JavaScript 的全棧應用,在功能上與這個應用相似:
。 可以採用下面的任意一種方式完成這個挑戰:
+
+- 克隆 [這個 GitHub 倉庫](https://github.com/freeCodeCamp/boilerplate-project-american-british-english-translator/) 並在本地完成項目。
+- 使用 [repl.it 上的初始化項目](https://repl.it/github/freeCodeCamp/boilerplate-project-american-british-english-translator) 來完成項目。
+- 使用您選擇的站點生成器來完成項目。 並確保包含了我們 GitHub 倉庫的所有文件。
+
+當完成本項目,請確認有一個可以公開訪問的正常運行 demo 。 然後將 URL 提交到 `Solution Link` 中。 此外,還可以將項目的源碼提交到 `GitHub Link` 中。
+
+# --instructions--
+
+- 所有邏輯都可以進入 `/components/translator.js`
+- 在 `/routes/api.js` 中完成 `/api/translate` 路由
+- 在 `tests/1_unit-tests.js` 和 `tests/2_functional-tests.js` 中創建所有 unit/functional 測試
+- 查看 `/components` 中的 JavaScript 文件以獲取應用程序應該翻譯的條款以及不同的拼寫
+- 在 `.env` 文件中將 `NODE_ENV` 設置爲 `test`(沒有引號),運行 Repl.it 上的測試。
+- 使用 `npm run test` 命令,在 console 運行測試。 按 Ctrl+Shift+P (在 Mac 上是 Cmd+Shift+P) 並輸入"open shell",打開 Repl.it 控制檯。
+
+在 `tests/1_unit-tests.js` 中寫下以下測試:
+
+- 將 `Mangoes are my favorite fruit.` 轉換成英式英語。
+- 將 `I ate yogurt for breakfast.` 轉換成英式英語。
+- 將 `We had a party at my friend's condo.` 轉換成英式英語。
+- 將 `Can you toss this in the trashcan for me?` 轉換成英式英語。
+- 將 `The parking lot was full.` 轉換成英式英語。
+- 將 `Like a high tech Rube Goldberg machine.` 轉換成英式英語。
+- 將 `To play hooky means to skip class or work.` 轉換成英式英語。
+- 將 `No Mr. Bond, I expect you to die.` 轉換成英式英語。
+- 將 `Dr. Grosh will see you now.` 轉換成英式英語。
+- 將 `Lunch is at 12:15 today.` 轉換成英式英語。
+- 將 `We watched the footie match for a while.` 轉換成美式英語。
+- 將 `Paracetamol takes up to an hour to work.` 轉換成美式英語。
+- 將 `First, caramelise the onions.` 轉換成美式英語。
+- 將 `I spent the bank holiday at the funfair.` 轉換成美式英語。
+- 將 `I had a bicky then went to the chippy.` 轉換成美式英語。
+- 將 `I've just got bits and bobs in my bum bag.` 轉換成美式英語。
+- 將 `The car boot sale at Boxted Airfield was called off.` 轉換成美式英語。
+- 將 `Have you met Mrs Kalyani?` 轉換成美式英語。
+- 將 `Prof Joyner of King's College, London.` 轉換成美式英語。
+- 將 `Tea time is usually around 4 or 4.30.` 轉換成美式英語。
+- 將 `Mangoes are my favorite fruit.` 裏的轉換高亮。
+- 高亮 `I ate yogurt for breakfast.` 裏的轉換。
+- 高亮 `We watched the footie match for a while.` 裏的轉換。
+- 高亮 `Paracetamol takes up to an hour to work.` 裏的轉換。
+
+在 `tests/2_functional-tests.js` 中寫下以下測試:
+
+- 翻譯文本字段和本地化字段: POST 請求到 `/api/translate`
+- 翻譯文本字段和無效的本地化字段: POST 請求到 `/api/translate`
+- 翻譯缺失的文本字段: POST 請求到 `/api/translate`
+- 翻譯缺失的本地化字段: POST 請求到 `/api/translate`
+- 翻譯空的文本: POST 請求到 `/api/translate`
+- 翻譯無需翻譯的文本: POST 請求到 `/api/translate`
+
+# --hints--
+
+我可以提供我自己的項目,而不是示例 URL。
+
+```js
+(getUserInput) => {
+ assert(
+ !/.*\/american-british-translator\.freecodecamp\.rocks/.test(
+ getUserInput('url')
+ )
+ );
+};
+```
+
+可以向 `/api/translate` 發送 `POST` 請求,對請求體內的 `text` 文本進行翻譯, `locale` 字段可以是 `american-to-british` 或 `british-to-american`。 返回的對象應該包含提交的 `text` 以及翻譯的文本 `translation`。
+
+```js
+async (getUserInput) => {
+ try {
+ const text = 'Mangoes are my favorite fruit.';
+ const locale = 'american-to-british';
+ const output = {
+ text: 'Mangoes are my favorite fruit.',
+ translation:
+ 'Mangoes are my
favourite fruit.'
+ };
+ let data = await fetch(getUserInput('url') + '/api/translate', {
+ method: 'POST',
+ headers: { 'Content-Type': 'application/json' },
+ body: JSON.stringify({ text, locale })
+ });
+ let parsed = await data.json();
+ assert.isObject(parsed);
+ assert.property(parsed, 'text');
+ assert.property(parsed, 'translation');
+ assert.deepEqual(parsed, output);
+ } catch (err) {
+ throw new Error(err.responseText || err.message);
+ }
+};
+```
+
+`/api/translate` 路由應該可以處理用英美方式英語寫的時間。 例如,十點半英式英語寫爲“10.30”,而美式英語寫爲“10:30”。 `span` 元素應該包裹整個時間字符串,即 `
10:30`。
+
+```js
+async (getUserInput) => {
+ try {
+ const text = 'Lunch is at 12:15 today.';
+ const locale = 'american-to-british';
+ const output = {
+ text: text,
+ translation: 'Lunch is at
12.15 today.'
+ };
+ let data = await fetch(getUserInput('url') + '/api/translate', {
+ method: 'POST',
+ headers: { 'Content-Type': 'application/json' },
+ body: JSON.stringify({ text, locale })
+ });
+ let parsed = await data.json();
+ assert.isObject(parsed);
+ assert.property(parsed, 'text');
+ assert.property(parsed, 'translation');
+ assert.deepEqual(parsed, output);
+ } catch (err) {
+ throw new Error(err.responseText || err.message);
+ }
+};
+```
+
+`/api/translate` 路由也應該處理美式英語和英式英語中頭銜/尊稱的縮寫方式。 例如,Doctor Wright 在英式英語中縮寫爲 “Dr Wright”,在美式英語中縮寫爲 “Dr. Wright"”。 查看 `/public/american-to-british-titles.js` 了應用程序應該處理的不同的頭銜。
+
+```js
+async (getUserInput) => {
+ try {
+ const text = 'Dr. Grosh will see you now.';
+ const locale = 'american-to-british';
+ const output = {
+ text: text,
+ translation: '
Dr Grosh will see you now.'
+ };
+ let data = await fetch(getUserInput('url') + '/api/translate', {
+ method: 'POST',
+ headers: { 'Content-Type': 'application/json' },
+ body: JSON.stringify({ text, locale })
+ });
+ let parsed = await data.json();
+ assert.isObject(parsed);
+ assert.property(parsed, 'text');
+ assert.property(parsed, 'translation');
+ assert.deepEqual(parsed, output);
+ } catch (err) {
+ throw new Error(err.responseText || err.message);
+ }
+};
+```
+
+將任何翻譯過的拼寫或條目放在 `
...` 標籤內以使其顯示爲綠色。
+
+```js
+async (getUserInput) => {
+ try {
+ const text = 'Mangoes are my favorite fruit.';
+ const locale = 'american-to-british';
+ const output = {
+ text: 'Mangoes are my favorite fruit.',
+ translation:
+ 'Mangoes are my
favourite fruit.'
+ };
+ let data = await fetch(getUserInput('url') + '/api/translate', {
+ method: 'POST',
+ headers: { 'Content-Type': 'application/json' },
+ body: JSON.stringify({ text, locale })
+ });
+ let parsed = await data.json();
+ assert.isObject(parsed);
+ assert.property(parsed, 'text');
+ assert.property(parsed, 'translation');
+ assert.deepEqual(parsed, output);
+ } catch (err) {
+ throw new Error(err.responseText || err.message);
+ }
+};
+```
+
+如果缺少一個或多個必填字段,返回 `{ error: 'Required field(s) missing' }`。
+
+```js
+async (getUserInput) => {
+ try {
+ const locale = 'american-to-british';
+ let data = await fetch(getUserInput('url') + '/api/translate', {
+ method: 'POST',
+ headers: { 'Content-Type': 'application/json' },
+ body: JSON.stringify({ locale })
+ });
+ let parsed = await data.json();
+ assert.isObject(parsed);
+ assert.property(parsed, 'error');
+ assert.equal(parsed.error, 'Required field(s) missing');
+ } catch (err) {
+ throw new Error(err.responseText || err.message);
+ }
+};
+```
+
+如果 `text` 爲空,返回 `{ error: 'No text to translate' }`
+
+```js
+async (getUserInput) => {
+ try {
+ const locale = 'american-to-british';
+ let data = await fetch(getUserInput('url') + '/api/translate', {
+ method: 'POST',
+ headers: { 'Content-Type': 'application/json' },
+ body: JSON.stringify({ text: '', locale })
+ });
+ let parsed = await data.json();
+ assert.isObject(parsed);
+ assert.property(parsed, 'error');
+ assert.equal(parsed.error, 'No text to translate');
+ } catch (err) {
+ throw new Error(err.responseText || err.message);
+ }
+};
+```
+
+如果 `locale` 與兩個指定的 locales 都不匹配,返回 `{ error: 'Invalid value for locale field' }`。
+
+```js
+async (getUserInput) => {
+ try {
+ const text = "Ceci n'est pas une pipe";
+ const locale = 'french-to-american';
+ let data = await fetch(getUserInput('url') + '/api/translate', {
+ method: 'POST',
+ headers: { 'Content-Type': 'application/json' },
+ body: JSON.stringify({ text, locale })
+ });
+ let parsed = await data.json();
+ assert.isObject(parsed);
+ assert.property(parsed, 'error');
+ assert.equal(parsed.error, 'Invalid value for locale field');
+ } catch (err) {
+ throw new Error(err.responseText || err.message);
+ }
+};
+```
+
+如果 `text` 不需要翻譯,返回的 `translation` 值爲`"Everything looks good to me!"`。
+
+```js
+async (getUserInput) => {
+ try {
+ const locale = 'british-to-american';
+ const output = {
+ text: 'SaintPeter and nhcarrigan give their regards!',
+ translation: 'Everything looks good to me!'
+ };
+ let data = await fetch(getUserInput('url') + '/api/translate', {
+ method: 'POST',
+ headers: { 'Content-Type': 'application/json' },
+ body: JSON.stringify({ text: output.text, locale })
+ });
+ let parsed = await data.json();
+ assert.isObject(parsed);
+ assert.isObject(parsed);
+ assert.property(parsed, 'text');
+ assert.property(parsed, 'translation');
+ assert.deepEqual(parsed, output);
+ } catch (err) {
+ throw new Error(err.responseText || err.message);
+ }
+};
+```
+
+所有 24 個單元的測試都已完成並通過。 請參閱 `/tests/1_unit-tests.js` 來了解你應該寫的測試的預期行爲。
+
+```js
+async (getUserInput) => {
+ try {
+ const getTests = await $.get(getUserInput('url') + '/_api/get-tests');
+ assert.isArray(getTests);
+ const unitTests = getTests.filter((test) => {
+ return !!test.context.match(/Unit Tests/gi);
+ });
+ assert.isAtLeast(unitTests.length, 24, 'At least 24 tests passed');
+ unitTests.forEach((test) => {
+ assert.equal(test.state, 'passed', 'Tests in Passed State');
+ assert.isAtLeast(
+ test.assertions.length,
+ 1,
+ 'At least one assertion per test'
+ );
+ });
+ } catch (err) {
+ throw new Error(err.responseText || err.message);
+ }
+};
+```
+
+所有 6 項功能測試都已完成並通過。 請參閱 `/tests/2_functional-tests.js` 來了解你應該寫的測試的功能。
+
+```js
+async (getUserInput) => {
+ try {
+ const getTests = await $.get(getUserInput('url') + '/_api/get-tests');
+ assert.isArray(getTests);
+ const functTests = getTests.filter((test) => {
+ return !!test.context.match(/Functional Tests/gi);
+ });
+ assert.isAtLeast(functTests.length, 6, 'At least 6 tests passed');
+ functTests.forEach((test) => {
+ assert.equal(test.state, 'passed', 'Tests in Passed State');
+ assert.isAtLeast(
+ test.assertions.length,
+ 1,
+ 'At least one assertion per test'
+ );
+ });
+ } catch (err) {
+ throw new Error(err.responseText || err.message);
+ }
+};
+```
+
+# --solutions--
+
+```js
+/**
+ Backend challenges don't need solutions,
+ because they would need to be tested against a full working project.
+ Please check our contributing guidelines to learn more.
+*/
+```
diff --git a/curriculum/challenges/chinese-traditional/06-quality-assurance/quality-assurance-projects/issue-tracker.md b/curriculum/challenges/chinese-traditional/06-quality-assurance/quality-assurance-projects/issue-tracker.md
new file mode 100644
index 0000000000..c056bddf10
--- /dev/null
+++ b/curriculum/challenges/chinese-traditional/06-quality-assurance/quality-assurance-projects/issue-tracker.md
@@ -0,0 +1,369 @@
+---
+id: 587d8249367417b2b2512c42
+title: 問題跟蹤器
+challengeType: 4
+forumTopicId: 301569
+dashedName: issue-tracker
+---
+
+# --description--
+
+構建一個 JavaScript 的全棧應用,在功能上與這個應用相似:
。 可以採用下面的任意一種方式完成這個挑戰:
+
+- 克隆 [GitHub 倉庫](https://github.com/freeCodeCamp/boilerplate-project-issuetracker/) 並在本地完成你的項目。
+- 使用 [repl.it 上的初始化項目](https://repl.it/github/freeCodeCamp/boilerplate-project-issuetracker) 來完成項目。
+- 使用一個你喜歡的站點生成器來完成項目。 需要確定包含了我們 GitHub 倉庫的所有文件。
+
+完成本項目後,請將一個正常運行的 demo(項目演示)託管在可以公開訪問的平臺。 然後在 `Solution Link` 框中提交你的項目 URL。 此外,還可以將項目的源碼提交到 `GitHub Link` 中。
+
+# --instructions--
+
+- 在 `/routes/api.js` 中完成必要的路由
+- 在 `tests/2_functional-tests.js` 中創建所有的功能測試
+- 複製 `sample.env` 文件到 `.env` 並按需設置變量
+- 要運行測試,在 `.env` 文件中取消註釋 `NODE_ENV=test`
+- 使用 `npm run test` 命令,在 console 運行測試。 按 Ctrl+Shift+P (在 Mac 上是 Cmd+Shift+P) 並輸入"open shell",打開 Repl.it 控制檯。
+
+在 `tests/2_functional-tests.js` 中編寫下以下測試:
+
+- 用所有字段創建 issue:POST 請求到 `/api/issues/{project}`
+- 用必填字段創建 issue:POST 請求到 `/api/issues/{project}`
+- 用缺失必填字段創建 issue:POST 請求到 `/api/issues/{project}`
+- 查看 project 裏的 issue:GET 請求到 `/api/issues/{project}`
+- 用 filter 過濾 project 裏的 issue:GET 請求到 `/api/issues/{project}`
+- 用多個 filter 過濾 project 裏的 issue:GET 請求到 `/api/issues/{project}`
+- 更新 issue 裏的一個字段:PUT 請求到 `/api/issues/{project}`
+- 更新 issue 裏的多個字段:PUT 請求到 `/api/issues/{project}`
+- 在缺少 `_id` 字段的情況下更新 issue: PUT 請求到 `/api/issues/{project}`
+- 在沒有字段更新的情況下調用更新:PUT 請求到 `/api/issues/{project}`
+- 傳入一個無效的的 `_id` 來調用更新:PUT 請求到 `/api/issues/{project}`
+- 刪除一個 issue:DELETE 請求到 `/api/issues/{project}`
+- 傳入一個無效的的 `_id` 來調用刪除:DELETE 請求到 `/api/issues/{project}`
+- 在缺失 `_id` 的情況下來調用刪除:DELETE 請求到 `/api/issues/{project}`
+
+# --hints--
+
+提交自己的項目,而不是示例的 URL。
+
+```js
+(getUserInput) => {
+ assert(!/.*\/issue-tracker\.freecodecamp\.rocks/.test(getUserInput('url')));
+};
+```
+
+可以發送 `POST` 請求到 `/api/issues/{projectname}`,表單數據包含必填字段 `issue_title`、`issue_text`、`created_by` 和可選字段 `assigned_to` 以及 `status_text`。
+
+```js
+async (getUserInput) => {
+ try {
+ let test_data = {
+ issue_title: 'Faux Issue Title',
+ issue_text: 'Functional Test - Required Fields Only',
+ created_by: 'fCC'
+ };
+ const data = await $.post(
+ getUserInput('url') + '/api/issues/fcc-project',
+ test_data
+ );
+ assert.isObject(data);
+ assert.nestedInclude(data, test_data);
+ } catch (err) {
+ throw new Error(err.responseText || err.message);
+ }
+};
+```
+
+`POST` 請求到 `/api/issues/{projectname}` 將返回創建的對象,必須包含所有提交的全部字段。 如果沒有填選填字段將作爲空字符串返回. 此外,包含 `created_on` (日期/時間)、 `updated_on` (日期/時間)、 `open` (布爾型) `true` 用於打開 - 默認值, `false` 用於關閉, `_id`。
+
+```js
+async (getUserInput) => {
+ try {
+ let test_data = {
+ issue_title: 'Faux Issue Title 2',
+ issue_text: 'Functional Test - Every field filled in',
+ created_by: 'fCC',
+ assigned_to: 'Chai and Mocha'
+ };
+ const data = await $.post(
+ getUserInput('url') + '/api/issues/fcc-project',
+ test_data
+ );
+ assert.isObject(data);
+ assert.nestedInclude(data, test_data);
+ assert.property(data, 'created_on');
+ assert.isNumber(Date.parse(data.created_on));
+ assert.property(data, 'updated_on');
+ assert.isNumber(Date.parse(data.updated_on));
+ assert.property(data, 'open');
+ assert.isBoolean(data.open);
+ assert.isTrue(data.open);
+ assert.property(data, '_id');
+ assert.isNotEmpty(data._id);
+ assert.property(data, 'status_text');
+ assert.isEmpty(data.status_text);
+ } catch (err) {
+ throw new Error(err.responseText || err.message);
+ }
+};
+```
+
+如果發送一個 `POST` 請求到 `/api/issues/{projectname}` 且缺少必填字段,會返回錯誤 `{ error: 'required field(s) missing' }`。
+
+```js
+async (getUserInput) => {
+ try {
+ let test_data = { created_by: 'fCC' };
+ const data = await $.post(getUserInput('url') + '/api/issues/fcc-project', {
+ created_by: 'fCC'
+ });
+ assert.isObject(data);
+ assert.property(data, 'error');
+ assert.equal(data.error, 'required field(s) missing');
+ } catch (err) {
+ throw new Error(err.responseText || err.message);
+ }
+};
+```
+
+可以發送 `GET` 請求到 `/api/issues/{projectname}` 請求所有指定 `projectname` 的 issues 數組,會展示每個 issue 的所有字段。
+
+```js
+async (getUserInput) => {
+ try {
+ let test_data = { issue_text: 'Get Issues Test', created_by: 'fCC' };
+ const url =
+ getUserInput('url') +
+ '/api/issues/get_issues_test_' +
+ Date.now().toString().substring(7);
+ const data1 = await $.post(
+ url,
+ Object.assign(test_data, { issue_title: 'Faux Issue 1' })
+ );
+ assert.isObject(data1);
+ const data2 = await $.post(
+ url,
+ Object.assign(test_data, { issue_title: 'Faux Issue 2' })
+ );
+ assert.isObject(data2);
+ const data3 = await $.post(
+ url,
+ Object.assign(test_data, { issue_title: 'Faux Issue 3' })
+ );
+ assert.isObject(data3);
+ const getIssues = await $.get(url);
+ assert.isArray(getIssues);
+ assert.lengthOf(getIssues, 3);
+ let re = new RegExp('Faux Issue \\d');
+ getIssues.forEach((issue) => {
+ assert.property(issue, 'issue_title');
+ assert.match(issue.issue_title, re);
+ assert.property(issue, 'issue_text');
+ assert.property(issue, 'created_by');
+ assert.property(issue, 'assigned_to');
+ assert.property(issue, 'status_text');
+ assert.property(issue, 'open');
+ assert.property(issue, 'created_on');
+ assert.property(issue, 'updated_on');
+ assert.property(issue, '_id');
+ });
+ } catch (err) {
+ throw new Error(err.responseText || err.message);
+ }
+};
+```
+
+可以發送 `GET` 請求到 `/api/issues/{projectname}` 通過 URL 查詢傳入字段名和值過濾請求(如, `/api/issues/{project}?open=false`)。 你可以一次通過一個或多個字段/值對。
+
+```js
+async (getUserInput) => {
+ try {
+ let test_data = {
+ issue_title: 'To be Filtered',
+ issue_text: 'Filter Issues Test'
+ };
+ const url =
+ getUserInput('url') +
+ '/api/issues/get_issues_test_' +
+ Date.now().toString().substring(7);
+ const data1 = await $.post(
+ url,
+ Object.assign(test_data, { created_by: 'Alice', assigned_to: 'Bob' })
+ );
+ const data2 = await $.post(
+ url,
+ Object.assign(test_data, { created_by: 'Alice', assigned_to: 'Bob' })
+ );
+ const data3 = await $.post(
+ url,
+ Object.assign(test_data, { created_by: 'Alice', assigned_to: 'Eric' })
+ );
+ const data4 = await $.post(
+ url,
+ Object.assign(test_data, { created_by: 'Carol', assigned_to: 'Eric' })
+ );
+ const getSingle = await $.get(url + '?created_by=Alice');
+ assert.isArray(getSingle);
+ assert.lengthOf(getSingle, 3);
+ const getMultiple = await $.get(url + '?created_by=Alice&assigned_to=Bob');
+ assert.isArray(getMultiple);
+ assert.lengthOf(getMultiple, 2);
+ } catch (err) {
+ throw new Error(err.responseText || err.message);
+ }
+};
+```
+
+你可以發送一個 `PUT` 請求到 `/api/issues/{projectname}` 帶有一個 `_id` 以及一個或多個字段進行更新。 成功後, `updated_on` field 應該被更新,返回的應該是 `{ result: 'successfully updated', '_id': _id }`。
+
+```js
+async (getUserInput) => {
+ try {
+ let initialData = {
+ issue_title: 'Issue to be Updated',
+ issue_text: 'Functional Test - Put target',
+ created_by: 'fCC'
+ };
+ const url = getUserInput('url') + '/api/issues/fcc-project';
+ const itemToUpdate = await $.post(url, initialData);
+ const updateSucccess = await $.ajax({
+ url: url,
+ type: 'PUT',
+ data: { _id: itemToUpdate._id, issue_text: 'New Issue Text' }
+ });
+ assert.isObject(updateSucccess);
+ assert.deepEqual(updateSucccess, {
+ result: 'successfully updated',
+ _id: itemToUpdate._id
+ });
+ const getUpdatedId = await $.get(url + '?_id=' + itemToUpdate._id);
+ assert.isArray(getUpdatedId);
+ assert.isObject(getUpdatedId[0]);
+ assert.isAbove(
+ Date.parse(getUpdatedId[0].updated_on),
+ Date.parse(getUpdatedId[0].created_on)
+ );
+ } catch (err) {
+ throw new Error(err.responseText || err.message);
+ }
+};
+```
+
+當 `PUT` 請求發送給 `/api/issues/{projectname}` 的請求體不包含 `_id` 時應返回`{ error: 'missing _id' }`。
+
+```js
+async (getUserInput) => {
+ try {
+ const url = getUserInput('url') + '/api/issues/fcc-project';
+ const badUpdate = await $.ajax({ url: url, type: 'PUT' });
+ assert.isObject(badUpdate);
+ assert.property(badUpdate, 'error');
+ assert.equal(badUpdate.error, 'missing _id');
+ } catch (err) {
+ throw new Error(err.responseText || err.message);
+ }
+};
+```
+
+當 `PUT` 請求發送給 `/api/issues/{projectname}` 的請求體不包含任何更新的字段,應返回 `{ error: 'no update field(s) sent', '_id': _id }`。 在任何其他錯誤,應返回 `{ error: 'could not update', '_id': _id }`。
+
+```js
+async (getUserInput) => {
+ try {
+ const url = getUserInput('url') + '/api/issues/fcc-project';
+ const badUpdate = await $.ajax({
+ url: url,
+ type: 'PUT',
+ data: { _id: '5f665eb46e296f6b9b6a504d' }
+ });
+ assert.deepEqual(badUpdate, {
+ error: 'no update field(s) sent',
+ _id: '5f665eb46e296f6b9b6a504d'
+ });
+ const badIdUpdate = await $.ajax({
+ url: url,
+ type: 'PUT',
+ data: { _id: '5f665eb46e296f6b9b6a504d', issue_text: 'New Issue Text' }
+ });
+ assert.deepEqual(badIdUpdate, {
+ error: 'could not update',
+ _id: '5f665eb46e296f6b9b6a504d'
+ });
+ } catch (err) {
+ throw new Error(err.responseText || err.message);
+ }
+};
+```
+
+你可以發送一個 `DELETE` 請求到 `/api/issues/{projectname}` 帶有一個 `_id` 來刪除 issue。 如果沒有發送 `_id` ,返回值爲 `{ error: 'missing _id' }`。 成功後,返回值爲 `{ result: 'successfully deleted', '_id': _id }`。 失敗時,返回值爲 `{ error: 'could not delete', '_id': _id }`。
+
+```js
+async (getUserInput) => {
+ try {
+ let initialData = {
+ issue_title: 'Issue to be Deleted',
+ issue_text: 'Functional Test - Delete target',
+ created_by: 'fCC'
+ };
+ const url = getUserInput('url') + '/api/issues/fcc-project';
+ const itemToDelete = await $.post(url, initialData);
+ assert.isObject(itemToDelete);
+ const deleteSuccess = await $.ajax({
+ url: url,
+ type: 'DELETE',
+ data: { _id: itemToDelete._id }
+ });
+ assert.isObject(deleteSuccess);
+ assert.deepEqual(deleteSuccess, {
+ result: 'successfully deleted',
+ _id: itemToDelete._id
+ });
+ const noId = await $.ajax({ url: url, type: 'DELETE' });
+ assert.isObject(noId);
+ assert.deepEqual(noId, { error: 'missing _id' });
+ const badIdDelete = await $.ajax({
+ url: url,
+ type: 'DELETE',
+ data: { _id: '5f665eb46e296f6b9b6a504d', issue_text: 'New Issue Text' }
+ });
+ assert.isObject(badIdDelete);
+ assert.deepEqual(badIdDelete, {
+ error: 'could not delete',
+ _id: '5f665eb46e296f6b9b6a504d'
+ });
+ } catch (err) {
+ throw new Error(err.responseText || err.message);
+ }
+};
+```
+
+所有 14 項功能測試都已完成並通過。
+
+```js
+async (getUserInput) => {
+ try {
+ const getTests = await $.get(getUserInput('url') + '/_api/get-tests');
+ assert.isArray(getTests);
+ assert.isAtLeast(getTests.length, 14, 'At least 14 tests passed');
+ getTests.forEach((test) => {
+ assert.equal(test.state, 'passed', 'Test in Passed State');
+ assert.isAtLeast(
+ test.assertions.length,
+ 1,
+ 'At least one assertion per test'
+ );
+ });
+ } catch (err) {
+ throw new Error(err.responseText || err.message);
+ }
+};
+```
+
+# --solutions--
+
+```js
+/**
+ Backend challenges don't need solutions,
+ because they would need to be tested against a full working project.
+ Please check our contributing guidelines to learn more.
+*/
+```
diff --git a/curriculum/challenges/chinese-traditional/06-quality-assurance/quality-assurance-projects/metric-imperial-converter.md b/curriculum/challenges/chinese-traditional/06-quality-assurance/quality-assurance-projects/metric-imperial-converter.md
new file mode 100644
index 0000000000..85dbce9eee
--- /dev/null
+++ b/curriculum/challenges/chinese-traditional/06-quality-assurance/quality-assurance-projects/metric-imperial-converter.md
@@ -0,0 +1,318 @@
+---
+id: 587d8249367417b2b2512c41
+title: 公制 - 英制轉換器
+challengeType: 4
+forumTopicId: 301570
+dashedName: metric-imperial-converter
+---
+
+# --description--
+
+構建一個 JavaScript 的全棧應用,在功能上與這個應用相似:
。 可以採用下面的任意一種方式完成這個挑戰:
+
+- 克隆 [GitHub 倉庫](https://github.com/freeCodeCamp/boilerplate-project-metricimpconverter/) 並在本地完成你的項目。
+- 使用 [repl.it 上的初始化項目](https://repl.it/github/freeCodeCamp/boilerplate-project-metricimpconverter) 來完成項目。
+- 使用一個你喜歡的站點生成器來完成項目。 需要確定包含了我們 GitHub 倉庫的所有文件。
+
+完成本項目後,請將一個正常運行的 demo(項目演示)託管在可以公開訪問的平臺。 然後在 `Solution Link` 框中提交你的項目 URL。 此外,還可以將項目的源碼提交到 `GitHub Link` 中。
+
+# --instructions--
+
+- 在 `/controllers/convertHandler.js` 中完成必要的轉換邏輯
+- 在 `/routes/api.js` 中完成必要的路由
+- 複製 `sample.env` 文件到 `.env` 並按需設置變量
+- 在 `.env` 文件中取消註釋 `NODE_ENV=test` 來運行測試
+- 使用 `npm run test` 命令在 console 中運行測試。 按 Ctrl+Shift+P (在 Mac 上是 Cmd+Shift+P) 並輸入"open shell",打開 Repl.it 控制檯。
+
+在 `tests/1_unit-tests.js` 中寫下以下測試:
+
+- `convertHandler` 應該正確地讀取整個數字輸入。
+- `convertHandler` 應該正確地讀取十進制數字輸入。
+- `convertHandler` 應該正確地讀取一個分數輸入。
+- `convertHandler` 應該正確地讀取一個帶小數點的分數輸入。
+- `convertHandler` 當輸入雙分數時應該返回錯誤 ( `3/2/3`)。
+- `convertHandler` 在沒有提供數字輸入時應該默認爲 `1`。
+- `convertHandler` 應該正確地讀取每個有效的單位輸入。
+- `convertHandler` 在輸入無效單位時應返回錯誤。
+- `convertHandler` 在輸入有效單位時應返回正確的單位。
+- `convertHandler` 應該正確返回每個有效輸入單位的拼寫字符串。
+- `convertHandler` 應該正確地將 `gal` 轉換爲 `L`。
+- `convertHandler` 應該正確地將 `L` 轉換爲 `gal`。
+- `convertHandler` 應該正確地將 `mi` 轉換爲 `km`。
+- `convertHandler` 應該正確地將 `km` 轉換爲 `mi`。
+- `convertHandler` 應該正確地將 `lbs` 轉換爲 `kg`。
+- `convertHandler` 應該正確地將 `kg` 轉換爲 `lbs`。
+
+在 `tests/2_functional-tests.js` 中寫下以下測試:
+
+- 將有效的輸入轉換爲 `10L`: `GET` 請求到 `/api/convert`。
+- 轉換無效的輸如 `32g`: `GET` 請求到 `/api/convert`.
+- 轉換無效的數字,例如 `3/7.2/4kg`: `GET` 請求到 `/api/convert`。
+- 轉換無效的數字和單位如 `3/7.2/4kilomegagram`: `GET` 請求到 `/api/convert`.
+- 轉換時沒有數字,例如 `kg`: `GET` 請求到 `/api/convert`。
+
+# --hints--
+
+提交自己的項目,而不是示例的 URL。
+
+```js
+getUserInput => {
+ assert(
+ !/.*\/metric-imperial-converter\.freecodecamp\.rocks/.test(
+ getUserInput('url')
+ )
+ );
+};
+```
+
+通過 `GET` 請求 `/api/convert`,傳入數字和單位的單個參數,可以將其轉換。 (提示:通過尋找第一個字符的索引來分割輸入,這將標記單位的開始)
+
+```js
+
+```
+
+可以將 `'gal'` 轉換爲 `'L'`,反之亦然。 (1 gal to 3.78541 L)
+
+```js
+async getUserInput => {
+ try {
+ const data1 = await $.get(getUserInput('url') + '/api/convert?input=1gal');
+ assert.equal(data1.returnNum, 3.78541);
+ assert.equal(data1.returnUnit, 'L');
+ const data2 = await $.get(getUserInput('url') + '/api/convert?input=10gal');
+ assert.equal(data2.returnNum, 37.8541);
+ assert.equal(data2.returnUnit, 'L');
+ const data3 = await $.get(getUserInput('url') + '/api/convert?input=1l');
+ assert.equal(data3.returnNum, 0.26417);
+ assert.equal(data3.returnUnit, 'gal');
+ const data4 = await $.get(getUserInput('url') + '/api/convert?input=10l');
+ assert.equal(data4.returnNum, 2.64172);
+ assert.equal(data4.returnUnit, 'gal');
+ } catch (xhr) {
+ throw new Error(xhr.responseText || xhr.message);
+ }
+};
+```
+
+可以將 `'lbs'` 轉換爲 `'kg'`,反之亦然。 (1 lbs to 0.453592 kg)
+
+```js
+async getUserInput => {
+ try {
+ const data1 = await $.get(getUserInput('url') + '/api/convert?input=1lbs');
+ assert.equal(data1.returnNum, 0.45359);
+ assert.equal(data1.returnUnit, 'kg');
+ const data2 = await $.get(getUserInput('url') + '/api/convert?input=10lbs');
+ assert.equal(data2.returnNum, 4.53592);
+ assert.equal(data2.returnUnit, 'kg');
+ const data3 = await $.get(getUserInput('url') + '/api/convert?input=1kg');
+ assert.equal(data3.returnNum, 2.20462);
+ assert.equal(data3.returnUnit, 'lbs');
+ const data4 = await $.get(getUserInput('url') + '/api/convert?input=10kg');
+ assert.equal(data4.returnNum, 22.04624);
+ assert.equal(data4.returnUnit, 'lbs');
+ } catch (xhr) {
+ throw new Error(xhr.responseText || xhr.message);
+ }
+};
+```
+
+你可以將 `'mi'` 轉換爲 `'km'` 反之亦然。 (1 mi to 1.60934 km)
+
+```js
+async getUserInput => {
+ try {
+ const data1 = await $.get(getUserInput('url') + '/api/convert?input=1mi');
+ assert.equal(data1.returnNum, 1.60934);
+ assert.equal(data1.returnUnit, 'km');
+ const data2 = await $.get(getUserInput('url') + '/api/convert?input=10mi');
+ assert.equal(data2.returnNum, 16.0934);
+ assert.equal(data2.returnUnit, 'km');
+ const data3 = await $.get(getUserInput('url') + '/api/convert?input=1km');
+ assert.equal(data3.returnNum, 0.62137);
+ assert.equal(data3.returnUnit, 'mi');
+ const data4 = await $.get(getUserInput('url') + '/api/convert?input=10km');
+ assert.equal(data4.returnNum, 6.21373);
+ assert.equal(data4.returnUnit, 'mi');
+ } catch (xhr) {
+ throw new Error(xhr.responseText || xhr.message);
+ }
+};
+```
+
+所有輸入單位以大寫和小寫形式都應該被接受,但在 `initUnit` 和 `returnUnit` 中應以小寫形式返回,升除外,應將其表示爲大寫的 `'L'`。
+
+```js
+async getUserInput => {
+ try {
+ const data1 = await $.get(getUserInput('url') + '/api/convert?input=1gal');
+ assert.equal(data1.initUnit, 'gal');
+ assert.equal(data1.returnUnit, 'L');
+ const data2 = await $.get(getUserInput('url') + '/api/convert?input=10L');
+ assert.equal(data2.initUnit, 'L');
+ assert.equal(data2.returnUnit, 'gal');
+ const data3 = await $.get(getUserInput('url') + '/api/convert?input=1l');
+ assert.equal(data3.initUnit, 'L');
+ assert.equal(data3.returnUnit, 'gal');
+ const data4 = await $.get(getUserInput('url') + '/api/convert?input=10KM');
+ assert.equal(data4.initUnit, 'km');
+ assert.equal(data4.returnUnit, 'mi');
+ } catch (xhr) {
+ throw new Error(xhr.responseText || xhr.message);
+ }
+};
+```
+
+如果測量單位無效,返回將爲 `'invalid unit'`。
+
+```js
+async getUserInput => {
+ try {
+ const data = await $.get(getUserInput('url') + '/api/convert?input=1min');
+ assert(data.error === 'invalid unit' || data === 'invalid unit');
+ } catch (xhr) {
+ throw new Error(xhr.responseText || xhr.message);
+ }
+};
+```
+
+如果數字無效,返回將爲 `'invalid number'`。
+
+```js
+async getUserInput => {
+ try {
+ const data = await $.get(
+ getUserInput('url') + '/api/convert?input=1//2gal'
+ );
+ assert(data.error === 'invalid number' || data === 'invalid number');
+ } catch (xhr) {
+ throw new Error(xhr.responseText || xhr.message);
+ }
+};
+```
+
+如果單位和數字都無效,返回將爲 `'invalid number and unit'`。
+
+```js
+async getUserInput => {
+ try {
+ const data = await $.get(
+ getUserInput('url') + '/api/convert?input=1//2min'
+ );
+ assert(
+ data.error === 'invalid number and unit' ||
+ data === 'invalid number and unit'
+ );
+ } catch (xhr) {
+ throw new Error(xhr.responseText || xhr.message);
+ }
+};
+```
+
+可以在參數中使用分數、小數或小數分數 (例如 5, 1/2, 2.5/6),如果沒有提供任何內容,則默認值爲 1。
+
+```js
+async getUserInput => {
+ try {
+ const data1 = await $.get(getUserInput('url') + '/api/convert?input=mi');
+ assert.approximately(data1.initNum, 1, 0.001);
+ assert.approximately(data1.returnNum, 1.60934, 0.001);
+ assert.equal(data1.returnUnit, 'km');
+ const data2 = await $.get(getUserInput('url') + '/api/convert?input=1/5mi');
+ assert.approximately(data2.initNum, 1 / 5, 0.1);
+ assert.approximately(data2.returnNum, 0.32187, 0.001);
+ assert.equal(data2.returnUnit, 'km');
+ const data3 = await $.get(
+ getUserInput('url') + '/api/convert?input=1.5/7km'
+ );
+ assert.approximately(data3.initNum, 1.5 / 7, 0.001);
+ assert.approximately(data3.returnNum, 0.13315, 0.001);
+ assert.equal(data3.returnUnit, 'mi');
+ const data4 = await $.get(
+ getUserInput('url') + '/api/convert?input=3/2.7km'
+ );
+ assert.approximately(data4.initNum, 3 / 2.7, 0.001);
+ assert.approximately(data4.returnNum, 0.69041, 0.001);
+ assert.equal(data4.returnUnit, 'mi');
+ } catch (err) {
+ throw new Error(err.responseText || err.message);
+ }
+};
+```
+
+返回將包含 `initNum`、`initUnit`、`returnNum`、`returnUnit` 和 `string` 拼寫單位格式 `'{initNum} {initUnitString} converts to {returnNum} {returnUnitString}'` 結果四捨五入爲 5 小數。
+
+```js
+async getUserInput => {
+ try {
+ const data = await $.get(getUserInput('url') + '/api/convert?input=2mi');
+ assert.equal(data.initNum, 2);
+ assert.equal(data.initUnit, 'mi');
+ assert.approximately(data.returnNum, 3.21868, 0.001);
+ assert.equal(data.returnUnit, 'km', 'returnUnit did not match');
+ assert.equal(data.string, '2 miles converts to 3.21868 kilometers');
+ } catch (xhr) {
+ throw new Error(xhr.responseText || xhr.message);
+ }
+};
+```
+
+所有 16 個單元的測試都已完成並通過。
+
+```js
+async getUserInput => {
+ try {
+ const getTests = await $.get(getUserInput('url') + '/_api/get-tests');
+ assert.isArray(getTests);
+ const unitTests = getTests.filter(test => {
+ return !!test.context.match(/Unit Tests/gi);
+ });
+ assert.isAtLeast(unitTests.length, 16, 'At least 16 tests passed');
+ unitTests.forEach(test => {
+ assert.equal(test.state, 'passed', 'Tests in Passed State');
+ assert.isAtLeast(
+ test.assertions.length,
+ 1,
+ 'At least one assertion per test'
+ );
+ });
+ } catch (err) {
+ throw new Error(err.responseText || err.message);
+ }
+};
+```
+
+所有 5 項功能測試都已完成並通過。
+
+```js
+async getUserInput => {
+ try {
+ const getTests = await $.get(getUserInput('url') + '/_api/get-tests');
+ assert.isArray(getTests);
+ const functTests = getTests.filter(test => {
+ return !!test.context.match(/Functional Tests/gi);
+ });
+ assert.isAtLeast(functTests.length, 5, 'At least 5 tests passed');
+ functTests.forEach(test => {
+ assert.equal(test.state, 'passed', 'Tests in Passed State');
+ assert.isAtLeast(
+ test.assertions.length,
+ 1,
+ 'At least one assertion per test'
+ );
+ });
+ } catch (err) {
+ throw new Error(err.responseText || err.message);
+ }
+};
+```
+
+# --solutions--
+
+```js
+/**
+ Backend challenges don't need solutions,
+ because they would need to be tested against a full working project.
+ Please check our contributing guidelines to learn more.
+*/
+```
diff --git a/curriculum/challenges/chinese-traditional/06-quality-assurance/quality-assurance-projects/personal-library.md b/curriculum/challenges/chinese-traditional/06-quality-assurance/quality-assurance-projects/personal-library.md
new file mode 100644
index 0000000000..f67c5107d0
--- /dev/null
+++ b/curriculum/challenges/chinese-traditional/06-quality-assurance/quality-assurance-projects/personal-library.md
@@ -0,0 +1,221 @@
+---
+id: 587d824a367417b2b2512c43
+title: 個人圖書館
+challengeType: 4
+forumTopicId: 301571
+dashedName: personal-library
+---
+
+# --description--
+
+構建一個 JavaScript 的全棧應用,在功能上與這個應用相似:
。 可以採用下面的任意一種方式完成這個挑戰:
+
+- 克隆 [這個 GitHub 倉庫](https://github.com/freeCodeCamp/boilerplate-project-library) 並在本地完成項目。
+- 使用 [repl.it 上的初始化項目](https://repl.it/github/freeCodeCamp/boilerplate-project-library) 來完成項目。
+- 使用一個你喜歡的站點生成器來完成項目。 需要確定包含了我們 GitHub 倉庫的所有文件。
+
+完成本項目後,請將一個正常運行的 demo(項目演示)託管在可以公開訪問的平臺。 然後在 `Solution Link` 框中提交你的項目 URL。 此外,還可以將項目的源碼提交到 `GitHub Link` 中。
+
+# --instructions--
+
+1. 將的 MongoDB 連接字符串添加到 `.env` 中(沒有引號),`DB`
+ 示例: `DB=mongodb://admin:pass@1234.mlab.com:1234/fccpersonallib`
+2. 在 `.env` 文件中設置 `NODE_ENV` 爲 `test`中,沒有引號
+3. 需要在 `routes/api.js` 中創建所有路由
+4. 在 `tests/2_functional-tests.js` 中創建所有的功能測試
+
+# --hints--
+
+提交自己的項目,而不是示例的 URL。
+
+```js
+(getUserInput) => {
+ assert(
+ !/.*\/personal-library\.freecodecamp\.rocks/.test(getUserInput('url'))
+ );
+};
+```
+
+可以發送
POST 請求到 `/api/books`,帶有 `title` 作爲表單數據的一部分,來添加一本書。 返回的響應將是一個包含 `title` 和唯一的 `_id` 作爲鍵的對象。 如果 `title` 未包含在請求中,返回的響應應該是字符串 `missing required field title`。
+
+```js
+async (getUserInput) => {
+ try {
+ let data1 = await $.post(getUserInput('url') + '/api/books', {
+ title: 'Faux Book 1'
+ });
+ assert.isObject(data1);
+ assert.property(data1, 'title');
+ assert.equal(data1.title, 'Faux Book 1');
+ assert.property(data1, '_id');
+ let data2 = await $.post(getUserInput('url') + '/api/books');
+ assert.isString(data2);
+ assert.equal(data2, 'missing required field title');
+ } catch (err) {
+ throw new Error(err.responseText || err.message);
+ }
+};
+```
+
+可以向 `/api/books` 發送
GET 請求,並返回代表所有書的 JSON 響應。 JSON 響應應該是一個包含有 `title`、`_id` 和 `commentcount` 屬性的對象數組 。
+
+```js
+async (getUserInput) => {
+ try {
+ let url = getUserInput('url') + '/api/books';
+ let a = $.post(url, { title: 'Faux Book A' });
+ let b = $.post(url, { title: 'Faux Book B' });
+ let c = $.post(url, { title: 'Faux Book C' });
+ Promise.all([a, b, c]).then(async () => {
+ let data = await $.get(url);
+ assert.isArray(data);
+ assert.isAtLeast(data.length, 3);
+ data.forEach((book) => {
+ assert.isObject(book);
+ assert.property(book, 'title');
+ assert.isString(book.title);
+ assert.property(book, '_id');
+ assert.property(book, 'commentcount');
+ assert.isNumber(book.commentcount);
+ });
+ });
+ } catch (err) {
+ throw new Error(err.responseText || err.message);
+ }
+};
+```
+
+可以發送
GET 請求到 `/api/books/{_id}` 來檢索一本書的單個對象,返回屬性 `title`、`_id` 和 `comments` 數組 (如果沒有評論,則展示空數組)。 如果找不到書, 返回字符串 `no book exists`。
+
+```js
+async (getUserInput) => {
+ try {
+ let url = getUserInput('url') + '/api/books';
+ let noBook = await $.get(url + '/5f665eb46e296f6b9b6a504d');
+ assert.isString(noBook);
+ assert.equal(noBook, 'no book exists');
+ let sampleBook = await $.post(url, { title: 'Faux Book Alpha' });
+ assert.isObject(sampleBook);
+ let bookId = sampleBook._id;
+ let bookQuery = await $.get(url + '/' + bookId);
+ assert.isObject(bookQuery);
+ assert.property(bookQuery, 'title');
+ assert.equal(bookQuery.title, 'Faux Book Alpha');
+ assert.property(bookQuery, 'comments');
+ assert.isArray(bookQuery.comments);
+ } catch (err) {
+ throw new Error(err.responseText || err.message);
+ }
+};
+```
+
+可以發送一個
POST 請求,其中包含 `comment` 作爲表單正文數據,請求到 `/api/books/{_id}` 以便將評論添加到書中。 返回的響應將是書對象,在先前測試中
GET `/api/books/{_id}` 類似。 如果請求中沒有包含 `comment` ,返回字符串 `missing required field comment`。 如果找不到書, 返回字符串 `no book exists`。
+
+```js
+async (getUserInput) => {
+ try {
+ let url = getUserInput('url') + '/api/books';
+ let commentTarget = await $.post(url, { title: 'Notable Book' });
+ assert.isObject(commentTarget);
+ let bookId = commentTarget._id;
+ let bookCom1 = await $.post(url + '/' + bookId, {
+ comment: 'This book is fab!'
+ });
+ let bookCom2 = await $.post(url + '/' + bookId, {
+ comment: 'I did not care for it'
+ });
+ assert.isObject(bookCom2);
+ assert.property(bookCom2, '_id');
+ assert.property(bookCom2, 'title');
+ assert.property(bookCom2, 'comments');
+ assert.lengthOf(bookCom2.comments, 2);
+ bookCom2.comments.forEach((comment) => {
+ assert.isString(comment);
+ assert.oneOf(comment, ['This book is fab!', 'I did not care for it']);
+ });
+ let commentErr = await $.post(url + '/' + bookId);
+ assert.isString(commentErr);
+ assert.equal(commentErr, 'missing required field comment');
+ let failingComment = await $.post(url + '/5f665eb46e296f6b9b6a504d', {
+ comment: 'Never Seen Comment'
+ });
+ assert.isString(failingComment);
+ assert.equal(failingComment, 'no book exists');
+ } catch (err) {
+ throw new Error(err.responseText || err.message);
+ }
+};
+```
+
+可以向 `/api/books/{_id}` 發送
DELETE 請求,從收藏中刪除一本書。 如果成功,返回的響應將是字符串 `delete successful`。 如果找不到書, 返回字符串 `no book exists`。
+
+```js
+async (getUserInput) => {
+ try {
+ let url = getUserInput('url') + '/api/books';
+ let deleteTarget = await $.post(url, { title: 'Deletable Book' });
+ assert.isObject(deleteTarget);
+ let bookId = deleteTarget._id;
+ let doDelete = await $.ajax({ url: url + '/' + bookId, type: 'DELETE' });
+ assert.isString(doDelete);
+ assert.equal(doDelete, 'delete successful');
+ let failingDelete = await $.ajax({
+ url: url + '/5f665eb46e296f6b9b6a504d',
+ type: 'DELETE'
+ });
+ assert.isString(failingDelete);
+ assert.equal(failingDelete, 'no book exists');
+ } catch (err) {
+ throw new Error(err.responseText || err.message);
+ }
+};
+```
+
+可以向 `/api/books` 發送
DELETE 請求來刪除數據庫中的所有書籍。 如果成功,返回的響應將是字符串 `'complete delete successful`。
+
+```js
+async (getUserInput) => {
+ try {
+ const deleteAll = await $.ajax({
+ url: getUserInput('url') + '/api/books',
+ type: 'DELETE'
+ });
+ assert.isString(deleteAll);
+ assert.equal(deleteAll, 'complete delete successful');
+ } catch (err) {
+ throw new Error(err.responseText || err.message);
+ }
+};
+```
+
+所有 10 項功能測試都已完成並通過。
+
+```js
+async (getUserInput) => {
+ try {
+ const getTests = await $.get(getUserInput('url') + '/_api/get-tests');
+ assert.isArray(getTests);
+ assert.isAtLeast(getTests.length, 10, 'At least 10 tests passed');
+ getTests.forEach((test) => {
+ assert.equal(test.state, 'passed', 'Test in Passed State');
+ assert.isAtLeast(
+ test.assertions.length,
+ 1,
+ 'At least one assertion per test'
+ );
+ });
+ } catch (err) {
+ throw new Error(err.responseText || err.message);
+ }
+};
+```
+
+# --solutions--
+
+```js
+/**
+ Backend challenges don't need solutions,
+ because they would need to be tested against a full working project.
+ Please check our contributing guidelines to learn more.
+*/
+```
diff --git a/curriculum/challenges/chinese-traditional/06-quality-assurance/quality-assurance-projects/sudoku-solver.md b/curriculum/challenges/chinese-traditional/06-quality-assurance/quality-assurance-projects/sudoku-solver.md
new file mode 100644
index 0000000000..d33d14ae13
--- /dev/null
+++ b/curriculum/challenges/chinese-traditional/06-quality-assurance/quality-assurance-projects/sudoku-solver.md
@@ -0,0 +1,378 @@
+---
+id: 5e601bf95ac9d0ecd8b94afd
+title: 數獨求解器
+challengeType: 4
+dashedName: sudoku-solver
+---
+
+# --description--
+
+構建一個 JavaScript 的全棧應用,在功能上與這個應用相似:
。 可以採用下面的任意一種方式完成這個挑戰:
+
+- 克隆 [GitHub 倉庫](https://github.com/freecodecamp/boilerplate-project-sudoku-solver) 並在本地完成你的項目。
+- 使用 [repl.it 初始化項目](https://repl.it/github/freeCodeCamp/boilerplate-project-sudoku-solver) 來完成項目。
+- 使用一個你喜歡的站點生成器來完成項目。 需要確定包含了我們 GitHub 倉庫的所有文件。
+
+完成本項目後,請將一個正常運行的 demo(項目演示)託管在可以公開訪問的平臺。 然後在 `Solution Link` 框中提交你的項目 URL。 此外,還可以將項目的源碼提交到 `GitHub Link` 中。
+
+# --instructions--
+
+- 所有解謎邏輯都可以進入 `/controllers/sudoku-solver.js`
+ - `validate` 函數應該使用給定的解謎字符串,然後檢查它是否是 81 個有效的輸入字符。
+ - `check` 函數應對棋盤的 *current* 進行驗證。
+ - `solve` 函數應該處理任何給定的解謎字符串,而不僅僅是測試輸入和解決方法。 你需要寫出解決這個問題的邏輯。
+- 所有路由邏輯都可以進入 `/routes/api.js`
+- 閱讀 `/controllers` 中的 `puzzle-strings.js` 文件來了解一些應用程序應該解決的示例謎題
+- 在 `.env` 文件中將 `NODE_ENV` 設置爲 `test` (沒有引號),運行這個頁面的挑戰測試。
+- 使用 `npm run test` 命令在 console 中運行測試。 按 Ctrl+Shift+P (在 Mac 上是 Cmd+Shift+P) 並輸入"open shell",打開 Repl.it 控制檯。
+
+在 `tests/1_unit-tests.js` 中寫下以下測試:
+
+- 邏輯處理 81 個字符的解謎字符串
+- 邏輯處理無效的解謎字符串 (不是 1-9 或 `.`)
+- 邏輯處理一個長度不是 81 個字符的解謎字符串
+- 邏輯處理有效行的位置
+- 邏輯處理無效行的位置
+- 邏輯處理一個有效的列位置
+- 邏輯處理無效列位置
+- 邏輯處理一個有效的區域 (3x3 網格)
+- 邏輯處理一個無效的區域 (3x3 網格)
+- 有效解謎字符串通過 solver
+- 無效解謎字符串無法通過 solver
+- solver 返回一個不完整謎題的的預期解決方案
+
+在 `tests/2_functional-tests.js` 中編寫下以下測試:
+
+- 用有效的解謎字符串解決一個謎題:POST 請求到 `/api/solve`
+- 用缺失的解謎字符串解決一個謎題:POST 請求到 `/api/solve`
+- 用無效字符解決一個謎題:POST 請求到 `/api/solve`
+- 用不正確的長度解決一個謎題:POST 請求到 `/api/solve`
+- 解決一個無法解決的謎題:POST 請求到 `/api/solve`
+- 檢查所有字段的解謎位置:POST 請求到 `/api/check`
+- 用單個位置衝突檢查解謎位置:POST 請求到 `/api/check`
+- 檢查一個有多個位置衝突的解謎位置: POST 請求到 `/api/check`
+- 檢查與所有位置衝突的解謎位置: POST 請求到 `/api/check`
+- 檢查缺失所需字段的解謎位置:POST 請求到 `/api/check`
+- 檢查一個有無效字符的解謎位置: POST 請求到 `/api/check`
+- 檢查不正確長度的解謎位置:POST 請求到 `/api/check`
+- 檢查一個無效的放置座標的解謎位置:POST 請求到 `/api/check`
+- 檢查具有無效的放置值的解謎位置:POST 請求到 `/api/check`
+
+# --hints--
+
+提交自己的項目,而不是示例的 URL。
+
+```js
+(getUserInput) => {
+ const url = getUserInput('url');
+ assert(!/.*\/sudoku-solver\.freecodecamp\.rocks/.test(getUserInput('url')));
+};
+```
+
+可以發送 `POST` 請求到 `/api/solve`,使用包含 `puzzle` 的表單數據這將是一個包含數字 (1-9) 和點號的字符串組合,`.` 表示空格。 返回的對象將包含一個 `solution` 屬性與解決的謎題。
+
+```js
+async (getUserInput) => {
+ const input =
+ '..9..5.1.85.4....2432......1...69.83.9.....6.62.71...9......1945....4.37.4.3..6..';
+ const output =
+ '769235418851496372432178956174569283395842761628713549283657194516924837947381625';
+ const data = await fetch(getUserInput('url') + '/api/solve', {
+ method: 'POST',
+ headers: { 'Content-Type': 'application/json' },
+ body: JSON.stringify({ puzzle: input })
+ });
+ const parsed = await data.json();
+ assert.property(parsed, 'solution');
+ assert.equal(parsed.solution, output);
+};
+```
+
+如果提交給 `/api/solve` 的對象缺失 `puzzle`,返回的值將是 `{ error: 'Required field missing' }`
+
+```js
+async (getUserInput) => {
+ const input =
+ '..9..5.1.85.4....2432......1...69.83.9.....6.62.71...9......1945....4.37.4.3..6..';
+ const output = 'Required field missing';
+ const data = await fetch(getUserInput('url') + '/api/solve', {
+ method: 'POST',
+ headers: { 'Content-Type': 'application/json' },
+ body: JSON.stringify({ notpuzzle: input })
+ });
+ const parsed = await data.json();
+ assert.property(parsed, 'error');
+ assert.equal(parsed.error, output);
+};
+```
+
+如果提交給 `/api/solve` 謎題包含非數字或點號的值。 返回的值將是 `{ error: 'Invalid characters in puzzle' }`
+
+```js
+async (getUserInput) => {
+ const input =
+ 'AA9..5.1.85.4....2432......1...69.83.9.....6.62.71...9......1945....4.37.4.3..6..';
+ const output = 'Invalid characters in puzzle';
+ const data = await fetch(getUserInput('url') + '/api/solve', {
+ method: 'POST',
+ headers: { 'Content-Type': 'application/json' },
+ body: JSON.stringify({ puzzle: input })
+ });
+ const parsed = await data.json();
+ assert.property(parsed, 'error');
+ assert.equal(parsed.error, output);
+};
+```
+
+如果提交給 `/api/solve` 的謎題大於或小於 81 個字符, 返回的值將是 `{ error: 'Expected puzzle to be 81 characters long' }`
+
+```js
+async (getUserInput) => {
+ const input =
+ '9..5.1.85.4....2432......1...69.83.9.....6.62.71...9......1945....4.37.4.3..6..';
+ const output = 'Expected puzzle to be 81 characters long';
+ const data = await fetch(getUserInput('url') + '/api/solve', {
+ method: 'POST',
+ headers: { 'Content-Type': 'application/json' },
+ body: JSON.stringify({ puzzle: input })
+ });
+ const parsed = await data.json();
+ assert.property(parsed, 'error');
+ assert.equal(parsed.error, output);
+};
+```
+
+如果提交給 `/api/solve` 的謎題無效或無法解決, 返回的值將是 `{ error: 'Puzzle cannot be solved' }`
+
+```js
+async (getUserInput) => {
+ const input =
+ '9.9..5.1.85.4....2432......1...69.83.9.....6.62.71...9......1945....4.37.4.3..6..';
+ const output = 'Puzzle cannot be solved';
+ const data = await fetch(getUserInput('url') + '/api/solve', {
+ method: 'POST',
+ headers: { 'Content-Type': 'application/json' },
+ body: JSON.stringify({ puzzle: input })
+ });
+ const parsed = await data.json();
+ assert.property(parsed, 'error');
+ assert.equal(parsed.error, output);
+};
+```
+
+可以發送 `POST` 請求到 `/api/check`,包含 `puzzle`、`coordinate` 和 `value` 的對象,其中 `coordinate` 是表示行的字母 A-I,後跟表示列的數字 1-9,而 `value` 是 1-9 的數字。
+
+```js
+async (getUserInput) => {
+ const input =
+ '..9..5.1.85.4....2432......1...69.83.9.....6.62.71...9......1945....4.37.4.3..6..';
+ const coordinate = 'A1';
+ const value = '7';
+ const data = await fetch(getUserInput('url') + '/api/check', {
+ method: 'POST',
+ headers: { 'Content-Type': 'application/json' },
+ body: JSON.stringify({ puzzle: input, coordinate, value })
+ });
+ const parsed = await data.json();
+ assert.property(parsed, 'valid');
+ assert.isTrue(parsed.valid);
+};
+```
+
+發送 `POST` 請求到 `/api/check`,返回值是一個包含 `valid` 屬性的對象,如果數字可能放置在提供的座標中則是 `true`,否則是`false`。 如果錯誤,返回的對象還將包含一個 `conflict` 屬性,它是一個字符串 `"row"`、`"column"`, 和/或 取決於哪個區域使位置無效的`"region"` 。
+
+```js
+async (getUserInput) => {
+ const input =
+ '..9..5.1.85.4....2432......1...69.83.9.....6.62.71...9......1945....4.37.4.3..6..';
+ const coordinate = 'A1';
+ const value = '1';
+ const conflict = ['row', 'column'];
+ const data = await fetch(getUserInput('url') + '/api/check', {
+ method: 'POST',
+ headers: { 'Content-Type': 'application/json' },
+ body: JSON.stringify({ puzzle: input, coordinate, value })
+ });
+ const parsed = await data.json();
+ assert.property(parsed, 'valid');
+ assert.isFalse(parsed.valid);
+ assert.property(parsed, 'conflict');
+ assert.include(parsed.conflict, 'row');
+ assert.include(parsed.conflict, 'column');
+};
+```
+
+如果提交給 `/api/check` 的 `value` 已放置在該 `coordinate` 上的 `puzzle`中,如果 `value` 不衝突,則返回的是 `valid` 屬性爲 `true` 的對象。
+
+```js
+async (getUserInput) => {
+ const input =
+ '..9..5.1.85.4....2432......1...69.83.9.....6.62.71...9......1945....4.37.4.3..6..';
+ const coordinate = 'C3';
+ const value = '2';
+ const data = await fetch(getUserInput('url') + '/api/check', {
+ method: 'POST',
+ headers: { 'Content-Type': 'application/json' },
+ body: JSON.stringify({ puzzle: input, coordinate, value })
+ });
+ const parsed = await data.json();
+ assert.property(parsed, 'valid');
+ assert.isTrue(parsed.valid);
+};
+```
+
+如果提交給 `/api/check` 的謎題包含非數字或點號的值。 返回的值將是 `{ error: 'Invalid characters in puzzle' }`
+
+```js
+async (getUserInput) => {
+ const input =
+ 'AA9..5.1.85.4....2432......1...69.83.9.....6.62.71...9......1945....4.37.4.3..6..';
+ const coordinate = 'A1';
+ const value = '1';
+ const output = 'Invalid characters in puzzle';
+ const data = await fetch(getUserInput('url') + '/api/check', {
+ method: 'POST',
+ headers: { 'Content-Type': 'application/json' },
+ body: JSON.stringify({ puzzle: input, coordinate, value })
+ });
+ const parsed = await data.json();
+ assert.property(parsed, 'error');
+ assert.equal(parsed.error, output);
+};
+```
+
+如果提交給 `/api/check` 的謎題大於或小於 81 個字符, 返回的值將是 `{ error: 'Expected puzzle to be 81 characters long' }`
+
+```js
+async (getUserInput) => {
+ const input =
+ '9..5.1.85.4....2432......1...69.83.9.....6.62.71...9......1945....4.37.4.3..6..';
+ const coordinate = 'A1';
+ const value = '1';
+ const output = 'Expected puzzle to be 81 characters long';
+ const data = await fetch(getUserInput('url') + '/api/check', {
+ method: 'POST',
+ headers: { 'Content-Type': 'application/json' },
+ body: JSON.stringify({ puzzle: input, coordinate, value })
+ });
+ const parsed = await data.json();
+ assert.property(parsed, 'error');
+ assert.equal(parsed.error, output);
+};
+```
+
+如果提交給 `/api/check` 的對象缺失 `puzzle`、`coordinate` 或 `value`,返回的值將是 `{ error: Required field(s) missing }`
+
+```js
+async (getUserInput) => {
+ const input =
+ '..9..5.1.85.4....2432......1...69.83.9.....6.62.71...9......1945....4.37.4.3..6..';
+ const output = 'Required field(s) missing';
+ const data = await fetch(getUserInput('url') + '/api/check', {
+ method: 'POST',
+ headers: { 'Content-Type': 'application/json' },
+ body: JSON.stringify({ puzzle: input })
+ });
+ const parsed = await data.json();
+ assert.property(parsed, 'error');
+ assert.equal(parsed.error, output);
+};
+```
+
+如果提交給 `api/check` 的座標不指向現有的網格單元格, 返回的值將是 `{ error: 'Invalid coordinate'}`
+
+```js
+async (getUserInput) => {
+ const input =
+ '..9..5.1.85.4....2432......1...69.83.9.....6.62.71...9......1945....4.37.4.3..6..';
+ const output = 'Invalid coordinate';
+ const coordinate = 'XZ18';
+ const value = '7';
+ const data = await fetch(getUserInput('url') + '/api/check', {
+ method: 'POST',
+ headers: { 'Content-Type': 'application/json' },
+ body: JSON.stringify({ puzzle: input, coordinate, value })
+ });
+ const parsed = await data.json();
+ assert.property(parsed, 'error');
+ assert.equal(parsed.error, output);
+};
+```
+
+如果提交給 `/api/check` 的 `value` 不是一個介於 1 到 9 之間的數字,則返回的值將是 `{ error: 'Invalid value' }`
+
+```js
+async (getUserInput) => {
+ const input =
+ '..9..5.1.85.4....2432......1...69.83.9.....6.62.71...9......1945....4.37.4.3..6..';
+ const output = 'Invalid value';
+ const coordinate = 'A1';
+ const value = 'X';
+ const data = await fetch(getUserInput('url') + '/api/check', {
+ method: 'POST',
+ headers: { 'Content-Type': 'application/json' },
+ body: JSON.stringify({ puzzle: input, coordinate, value })
+ });
+ const parsed = await data.json();
+ assert.property(parsed, 'error');
+ assert.equal(parsed.error, output);
+};
+```
+
+所有 12 個單元的測試都已完成並通過。 請參閱 `/tests/1_unit-tests.js` 來了解你應該寫的測試的預期行爲。
+
+```js
+async (getUserInput) => {
+ try {
+ const getTests = await $.get(getUserInput('url') + '/_api/get-tests');
+ assert.isArray(getTests);
+ const units = getTests.filter((el) => el.context.includes('UnitTests'));
+ assert.isAtLeast(units.length, 12, 'At least 12 tests passed');
+ units.forEach((test) => {
+ assert.equal(test.state, 'passed', 'Test in Passed State');
+ assert.isAtLeast(
+ test.assertions.length,
+ 1,
+ 'At least one assertion per test'
+ );
+ });
+ } catch (err) {
+ throw new Error(err.responseText || err.message);
+ }
+};
+```
+
+所有 14 項功能測試都已完成並通過。 請參閱 `/tests/2_functional-tests.js` 來了解你應該編寫的測試的功能。
+
+```js
+async (getUserInput) => {
+ try {
+ const getTests = await $.get(getUserInput('url') + '/_api/get-tests');
+ assert.isArray(getTests);
+ const funcs = getTests.filter((el) =>
+ el.context.includes('Functional Tests')
+ );
+ assert.isAtLeast(funcs.length, 14, 'At least 14 tests passed');
+ funcs.forEach((test) => {
+ assert.equal(test.state, 'passed', 'Test in Passed State');
+ assert.isAtLeast(
+ test.assertions.length,
+ 1,
+ 'At least one assertion per test'
+ );
+ });
+ } catch (err) {
+ throw new Error(err.responseText || err.message);
+ }
+};
+```
+
+# --solutions--
+
+```js
+/**
+ Backend challenges don't need solutions,
+ because they would need to be tested against a full working project.
+ Please check our contributing guidelines to learn more.
+*/
+```
diff --git a/curriculum/challenges/chinese-traditional/07-scientific-computing-with-python/python-for-everybody/build-your-own-functions.md b/curriculum/challenges/chinese-traditional/07-scientific-computing-with-python/python-for-everybody/build-your-own-functions.md
new file mode 100644
index 0000000000..a1797e50eb
--- /dev/null
+++ b/curriculum/challenges/chinese-traditional/07-scientific-computing-with-python/python-for-everybody/build-your-own-functions.md
@@ -0,0 +1,67 @@
+---
+id: 5e7b9f060b6c005b0e76f05b
+title: Build your own Functions
+challengeType: 11
+videoId: nLDychdBwUg
+dashedName: build-your-own-functions
+---
+
+# --description--
+
+More resources:
+
+\- [Exercise](https://www.youtube.com/watch?v=ksvGhDsjtpw)
+
+# --question--
+
+## --text--
+
+What will the following Python program print out?:
+
+```python
+def fred():
+ print("Zap")
+def jane():
+ print("ABC")
+
+jane()
+fred()
+jane()
+```
+
+## --answers--
+
+
Zap
+ABC
+jane
+fred
+jane
+
+---
+
+
Zap
+ABC
+Zap
+
+---
+
+
ABC
+Zap
+jane
+
+---
+
+
ABC
+Zap
+ABC
+
+---
+
+
Zap
+Zap
+Zap
+
+## --video-solution--
+
+4
+
diff --git a/curriculum/challenges/chinese-traditional/07-scientific-computing-with-python/python-for-everybody/comparing-and-sorting-tuples.md b/curriculum/challenges/chinese-traditional/07-scientific-computing-with-python/python-for-everybody/comparing-and-sorting-tuples.md
new file mode 100644
index 0000000000..011f26213e
--- /dev/null
+++ b/curriculum/challenges/chinese-traditional/07-scientific-computing-with-python/python-for-everybody/comparing-and-sorting-tuples.md
@@ -0,0 +1,57 @@
+---
+id: 5e7b9f0b0b6c005b0e76f06d
+title: Comparing and Sorting Tuples
+challengeType: 11
+videoId: dZXzBXUxxCs
+dashedName: comparing-and-sorting-tuples
+---
+
+# --description--
+
+More resources:
+
+\- [Exercise](https://www.youtube.com/watch?v=EhQxwzyT16E)
+
+# --question--
+
+## --text--
+
+Which does the same thing as the following code?:
+
+```python
+lst = []
+for key, val in counts.items():
+ newtup = (val, key)
+ lst.append(newtup)
+lst = sorted(lst, reverse=True)
+print(lst)
+```
+
+## --answers--
+
+```python
+print( sorted( [ (v,k) for k,v in counts.items() ], reverse=True ) )
+```
+
+---
+
+```python
+print( [ (k,v) for k,v in counts.items().sorted() ] )
+```
+
+---
+
+```python
+print( sorted( [ (v,k) for k,v in counts.keys() ] ) )
+```
+
+---
+
+```python
+print( [ (k,v) for k,v in counts.values().sort() ] )
+```
+
+## --video-solution--
+
+1
+
diff --git a/curriculum/challenges/chinese-traditional/07-scientific-computing-with-python/python-for-everybody/conditional-execution.md b/curriculum/challenges/chinese-traditional/07-scientific-computing-with-python/python-for-everybody/conditional-execution.md
new file mode 100644
index 0000000000..2e973cd5f6
--- /dev/null
+++ b/curriculum/challenges/chinese-traditional/07-scientific-computing-with-python/python-for-everybody/conditional-execution.md
@@ -0,0 +1,50 @@
+---
+id: 5e7b9f050b6c005b0e76f058
+title: Conditional Execution
+challengeType: 11
+videoId: gz_IfIsZQtc
+dashedName: conditional-execution
+---
+
+# --question--
+
+## --text--
+
+Which code is indented correctly to print "Yes" if x = 0 and y = 10?
+
+## --answers--
+
+```python
+if 0 == x:
+if y == 10:
+print("Yes")
+```
+
+---
+
+```python
+if 0 == x:
+ if y == 10:
+ print("Yes")
+```
+
+---
+
+```python
+if 0 == x:
+if y == 10:
+ print("Yes")
+```
+
+---
+
+```python
+if 0 == x:
+ if y == 10:
+ print("Yes")
+```
+
+## --video-solution--
+
+4
+
diff --git a/curriculum/challenges/chinese-traditional/07-scientific-computing-with-python/python-for-everybody/data-visualization-mailing-lists.md b/curriculum/challenges/chinese-traditional/07-scientific-computing-with-python/python-for-everybody/data-visualization-mailing-lists.md
new file mode 100644
index 0000000000..b4030f10ff
--- /dev/null
+++ b/curriculum/challenges/chinese-traditional/07-scientific-computing-with-python/python-for-everybody/data-visualization-mailing-lists.md
@@ -0,0 +1,52 @@
+---
+id: 5e7b9f6a0b6c005b0e76f097
+title: 'Data Visualization: Mailing Lists'
+challengeType: 11
+videoId: RYdW660KkaQ
+dashedName: data-visualization-mailing-lists
+---
+
+# --description--
+
+More resources:
+
+\- [Exercise: Geodata](https://www.youtube.com/watch?v=KfhslNzopxo)
+
+\- [Exercise: Gmane Model](https://www.youtube.com/watch?v=wSpl1-7afAk)
+
+\- [Exercise: Gmane Spider](https://www.youtube.com/watch?v=H3w4lOFBUOI)
+
+\- [Exercise: Gmane Viz](https://www.youtube.com/watch?v=LRqVPMEXByw)
+
+\- [Exercise: Page Rank](https://www.youtube.com/watch?v=yFRAZBkBDBs)
+
+\- [Exercise: Page Spider](https://www.youtube.com/watch?v=sXedPQ_AnWA)
+
+\- [Exercise: Page Viz](https://www.youtube.com/watch?v=Fm0hpkxsZoo)
+
+# --question--
+
+## --text--
+
+Which is a common JavaScript visualization library?
+
+## --answers--
+
+DataViz.js
+
+---
+
+D3
+
+---
+
+Lowcharts
+
+---
+
+DATA6
+
+## --video-solution--
+
+2
+
diff --git a/curriculum/challenges/chinese-traditional/07-scientific-computing-with-python/python-for-everybody/data-visualization-page-rank.md b/curriculum/challenges/chinese-traditional/07-scientific-computing-with-python/python-for-everybody/data-visualization-page-rank.md
new file mode 100644
index 0000000000..fd0b04359a
--- /dev/null
+++ b/curriculum/challenges/chinese-traditional/07-scientific-computing-with-python/python-for-everybody/data-visualization-page-rank.md
@@ -0,0 +1,30 @@
+---
+id: 5e7b9f6a0b6c005b0e76f096
+title: 'Data Visualization: Page Rank'
+challengeType: 11
+videoId: 6-w_qIUwaxU
+dashedName: data-visualization-page-rank
+---
+
+# --question--
+
+## --text--
+
+How does the PageRank algorithm work?
+
+## --answers--
+
+It determines which pages are most highly connected.
+
+---
+
+It ranks pages based on view counts.
+
+---
+
+It figures out which pages contain the most important content.
+
+## --video-solution--
+
+1
+
diff --git a/curriculum/challenges/chinese-traditional/07-scientific-computing-with-python/python-for-everybody/dictionaries-and-loops.md b/curriculum/challenges/chinese-traditional/07-scientific-computing-with-python/python-for-everybody/dictionaries-and-loops.md
new file mode 100644
index 0000000000..e11c4968a0
--- /dev/null
+++ b/curriculum/challenges/chinese-traditional/07-scientific-computing-with-python/python-for-everybody/dictionaries-and-loops.md
@@ -0,0 +1,49 @@
+---
+id: 5e7b9f0a0b6c005b0e76f069
+title: Dictionaries and Loops
+challengeType: 11
+videoId: EEmekKiKG70
+dashedName: dictionaries-and-loops
+---
+
+# --description--
+
+More resources:
+
+\- [Exercise](https://www.youtube.com/watch?v=PrhZ9qwBDD8)
+
+# --question--
+
+## --text--
+
+What will the following code print?:
+
+```python
+counts = { 'chuck' : 1 , 'annie' : 42, 'jan': 100}
+for key in counts:
+ if counts[key] > 10:
+ print(key, counts[key])
+```
+
+## --answers--
+
+
annie 42
+jan 100
+
+---
+
+
chuck 1
+annie 42
+jan 100
+
+---
+
+
chuck 1
+
+---
+
+
[Error]
+
+## --video-solution--
+
+1
diff --git a/curriculum/challenges/chinese-traditional/07-scientific-computing-with-python/python-for-everybody/dictionaries-common-applications.md b/curriculum/challenges/chinese-traditional/07-scientific-computing-with-python/python-for-everybody/dictionaries-common-applications.md
new file mode 100644
index 0000000000..f1f0855d93
--- /dev/null
+++ b/curriculum/challenges/chinese-traditional/07-scientific-computing-with-python/python-for-everybody/dictionaries-common-applications.md
@@ -0,0 +1,43 @@
+---
+id: 5e7b9f090b6c005b0e76f068
+title: 'Dictionaries: Common Applications'
+challengeType: 11
+videoId: f17xPfIXct0
+dashedName: dictionaries-common-applications
+---
+
+# --question--
+
+## --text--
+
+What will the following code print?
+
+```python
+counts = { 'quincy' : 1 , 'mrugesh' : 42, 'beau': 100, '0': 10}
+print(counts.get('kris', 0))
+```
+
+## --answers--
+
+2
+
+---
+
+quincy
+
+---
+
+0
+
+---
+
+10
+
+---
+
+[will return error]
+
+## --video-solution--
+
+3
+
diff --git a/curriculum/challenges/chinese-traditional/07-scientific-computing-with-python/python-for-everybody/files-as-a-sequence.md b/curriculum/challenges/chinese-traditional/07-scientific-computing-with-python/python-for-everybody/files-as-a-sequence.md
new file mode 100644
index 0000000000..dff23f6561
--- /dev/null
+++ b/curriculum/challenges/chinese-traditional/07-scientific-computing-with-python/python-for-everybody/files-as-a-sequence.md
@@ -0,0 +1,40 @@
+---
+id: 5e7b9f080b6c005b0e76f063
+title: Files as a Sequence
+challengeType: 11
+videoId: cIA0EokbaHE
+dashedName: files-as-a-sequence
+---
+
+# --description--
+
+More resources:
+
+\- [Exercise](https://www.youtube.com/watch?v=il1j4wkte2E)
+
+# --question--
+
+## --text--
+
+What does the word 'continue' do in the middle of a loop?
+
+## --answers--
+
+Skips to the code directly after the loop.
+
+---
+
+Skips to the next line in the code.
+
+---
+
+Skips to the next iteration of the loop.
+
+---
+
+Skips the next block of code.
+
+## --video-solution--
+
+3
+
diff --git a/curriculum/challenges/chinese-traditional/07-scientific-computing-with-python/python-for-everybody/intermediate-expressions.md b/curriculum/challenges/chinese-traditional/07-scientific-computing-with-python/python-for-everybody/intermediate-expressions.md
new file mode 100644
index 0000000000..06951beb68
--- /dev/null
+++ b/curriculum/challenges/chinese-traditional/07-scientific-computing-with-python/python-for-everybody/intermediate-expressions.md
@@ -0,0 +1,52 @@
+---
+id: 5e7b9f050b6c005b0e76f057
+title: Intermediate Expressions
+challengeType: 11
+videoId: dKgUaIa5ATg
+dashedName: intermediate-expressions
+---
+
+# --description--
+
+More resources:
+
+\- [Exercise 1](https://youtu.be/t_4DPwsaGDY)
+
+\- [Exercise 2](https://youtu.be/wgkC8SxraAQ)
+
+# --question--
+
+## --text--
+
+What will print out after running this code:
+
+```python
+width = 15
+height = 12.0
+print(height/3)
+```
+
+## --answers--
+
+39
+
+---
+
+4
+
+---
+
+4.0
+
+---
+
+5.0
+
+---
+
+5
+
+## --video-solution--
+
+3
+
diff --git a/curriculum/challenges/chinese-traditional/07-scientific-computing-with-python/python-for-everybody/intermediate-strings.md b/curriculum/challenges/chinese-traditional/07-scientific-computing-with-python/python-for-everybody/intermediate-strings.md
new file mode 100644
index 0000000000..216b951cc5
--- /dev/null
+++ b/curriculum/challenges/chinese-traditional/07-scientific-computing-with-python/python-for-everybody/intermediate-strings.md
@@ -0,0 +1,49 @@
+---
+id: 5e7b9f070b6c005b0e76f061
+title: Intermediate Strings
+challengeType: 11
+videoId: KgT_fYLXnyk
+dashedName: intermediate-strings
+---
+
+# --description--
+
+More resources:
+
+\- [Exercise](https://www.youtube.com/watch?v=1bSqHot-KwE)
+
+# --question--
+
+## --text--
+
+What is the value of i in the following code?
+
+```python
+word = "bananana"
+i = word.find("na")
+```
+
+## --answers--
+
+nanana
+
+---
+
+2
+
+---
+
+3
+
+---
+
+True
+
+---
+
+na
+
+## --video-solution--
+
+2
+
diff --git a/curriculum/challenges/chinese-traditional/07-scientific-computing-with-python/python-for-everybody/introduction-elements-of-python.md b/curriculum/challenges/chinese-traditional/07-scientific-computing-with-python/python-for-everybody/introduction-elements-of-python.md
new file mode 100644
index 0000000000..04796a35f1
--- /dev/null
+++ b/curriculum/challenges/chinese-traditional/07-scientific-computing-with-python/python-for-everybody/introduction-elements-of-python.md
@@ -0,0 +1,36 @@
+---
+id: 5e6a54c358d3af90110a60a3
+title: 'Introduction: Elements of Python'
+challengeType: 11
+videoId: aRY_xjL35v0
+dashedName: introduction-elements-of-python
+---
+
+# --question--
+
+## --text--
+
+What will the following program print out:
+
+```python
+x = 43
+x = x + 1
+print(x)
+```
+
+## --answers--
+
+x
+
+---
+
+x + 1
+
+---
+
+44
+
+## --video-solution--
+
+3
+
diff --git a/curriculum/challenges/chinese-traditional/07-scientific-computing-with-python/python-for-everybody/introduction-hardware-achitecture.md b/curriculum/challenges/chinese-traditional/07-scientific-computing-with-python/python-for-everybody/introduction-hardware-achitecture.md
new file mode 100644
index 0000000000..02afa120f1
--- /dev/null
+++ b/curriculum/challenges/chinese-traditional/07-scientific-computing-with-python/python-for-everybody/introduction-hardware-achitecture.md
@@ -0,0 +1,30 @@
+---
+id: 5e6a54af58d3af90110a60a1
+title: 'Introduction: Hardware Architecture'
+challengeType: 11
+videoId: H6qtjRTfSog
+dashedName: introduction-hardware-architecture
+---
+
+# --question--
+
+## --text--
+
+Where are your programs stored when they are running?
+
+## --answers--
+
+Hard Drive.
+
+---
+
+Memory.
+
+---
+
+Central Processing Unit.
+
+## --video-solution--
+
+2
+
diff --git a/curriculum/challenges/chinese-traditional/07-scientific-computing-with-python/python-for-everybody/introduction-python-as-a-language.md b/curriculum/challenges/chinese-traditional/07-scientific-computing-with-python/python-for-everybody/introduction-python-as-a-language.md
new file mode 100644
index 0000000000..a9ad7a9e2b
--- /dev/null
+++ b/curriculum/challenges/chinese-traditional/07-scientific-computing-with-python/python-for-everybody/introduction-python-as-a-language.md
@@ -0,0 +1,39 @@
+---
+id: 5e6a54ba58d3af90110a60a2
+title: 'Introduction: Python as a Language'
+challengeType: 11
+videoId: 0QeGbZNS_bY
+dashedName: introduction-python-as-a-language
+---
+
+# --question--
+
+## --text--
+
+What will print out after running these two lines of code:
+
+```python
+x = 6
+print(x)
+```
+
+## --answers--
+
+x
+
+---
+
+6
+
+---
+
+x = 6
+
+---
+
+(x)
+
+## --video-solution--
+
+2
+
diff --git a/curriculum/challenges/chinese-traditional/07-scientific-computing-with-python/python-for-everybody/introduction-why-program.md b/curriculum/challenges/chinese-traditional/07-scientific-computing-with-python/python-for-everybody/introduction-why-program.md
new file mode 100644
index 0000000000..f1549dbb07
--- /dev/null
+++ b/curriculum/challenges/chinese-traditional/07-scientific-computing-with-python/python-for-everybody/introduction-why-program.md
@@ -0,0 +1,38 @@
+---
+id: 5e6a54a558d3af90110a60a0
+title: 'Introduction: Why Program?'
+challengeType: 11
+videoId: 3muQV-Im3Z0
+dashedName: introduction-why-program
+---
+
+# --description--
+
+More resources:
+
+\- [Install Python on Windows](https://youtu.be/F7mtLrYzZP8)
+
+\- [Install Python on MacOS](https://youtu.be/wfLnZP-4sZw)
+
+# --question--
+
+## --text--
+
+Who should learn to program?
+
+## --answers--
+
+College students.
+
+---
+
+People who want to become software developers.
+
+---
+
+Everyone.
+
+## --video-solution--
+
+3
+
diff --git a/curriculum/challenges/chinese-traditional/07-scientific-computing-with-python/python-for-everybody/iterations-definite-loops.md b/curriculum/challenges/chinese-traditional/07-scientific-computing-with-python/python-for-everybody/iterations-definite-loops.md
new file mode 100644
index 0000000000..6438238f03
--- /dev/null
+++ b/curriculum/challenges/chinese-traditional/07-scientific-computing-with-python/python-for-everybody/iterations-definite-loops.md
@@ -0,0 +1,39 @@
+---
+id: 5e7b9f070b6c005b0e76f05d
+title: 'Iterations: Definite Loops'
+challengeType: 11
+videoId: hiRTRAqNlpE
+dashedName: iterations-definite-loops
+---
+
+# --question--
+
+## --text--
+
+How many lines will the following code print?:
+
+```python
+for i in [2,1,5]:
+ print(i)
+```
+
+## --answers--
+
+1
+
+---
+
+2
+
+---
+
+3
+
+---
+
+5
+
+## --video-solution--
+
+3
+
diff --git a/curriculum/challenges/chinese-traditional/07-scientific-computing-with-python/python-for-everybody/iterations-loop-idioms.md b/curriculum/challenges/chinese-traditional/07-scientific-computing-with-python/python-for-everybody/iterations-loop-idioms.md
new file mode 100644
index 0000000000..d0f5059a2f
--- /dev/null
+++ b/curriculum/challenges/chinese-traditional/07-scientific-computing-with-python/python-for-everybody/iterations-loop-idioms.md
@@ -0,0 +1,45 @@
+---
+id: 5e7b9f070b6c005b0e76f05e
+title: 'Iterations: Loop Idioms'
+challengeType: 11
+videoId: AelGAcoMXbI
+dashedName: iterations-loop-idioms
+---
+
+# --question--
+
+## --text--
+
+Below is code to find the smallest value from a list of values. One line has an error that will cause the code to not work as expected. Which line is it?:
+
+```python
+smallest = None
+print("Before:", smallest)
+for itervar in [3, 41, 12, 9, 74, 15]:
+ if smallest is None or itervar < smallest:
+ smallest = itervar
+ break
+ print("Loop:", itervar, smallest)
+print("Smallest:", smallest)
+```
+
+## --answers--
+
+3
+
+---
+
+4
+
+---
+
+6
+
+---
+
+7
+
+## --video-solution--
+
+3
+
diff --git a/curriculum/challenges/chinese-traditional/07-scientific-computing-with-python/python-for-everybody/iterations-more-patterns.md b/curriculum/challenges/chinese-traditional/07-scientific-computing-with-python/python-for-everybody/iterations-more-patterns.md
new file mode 100644
index 0000000000..829fe70011
--- /dev/null
+++ b/curriculum/challenges/chinese-traditional/07-scientific-computing-with-python/python-for-everybody/iterations-more-patterns.md
@@ -0,0 +1,48 @@
+---
+id: 5e7b9f070b6c005b0e76f05f
+title: 'Iterations: More Patterns'
+challengeType: 11
+videoId: 9Wtqo6vha1M
+dashedName: iterations-more-patterns
+---
+
+# --description--
+
+More resources:
+
+\- [Exercise](https://www.youtube.com/watch?v=kjxXZQw0uPg)
+
+# --question--
+
+## --text--
+
+Which of these evaluates to False?
+
+## --answers--
+
+```python
+0 == 0.0
+```
+
+---
+
+```python
+0 is 0.0
+```
+
+---
+
+```python
+0 is not 0.0
+```
+
+---
+
+```python
+0 = 0.0
+```
+
+## --video-solution--
+
+2
+
diff --git a/curriculum/challenges/chinese-traditional/07-scientific-computing-with-python/python-for-everybody/loops-and-iterations.md b/curriculum/challenges/chinese-traditional/07-scientific-computing-with-python/python-for-everybody/loops-and-iterations.md
new file mode 100644
index 0000000000..5bf4c6df17
--- /dev/null
+++ b/curriculum/challenges/chinese-traditional/07-scientific-computing-with-python/python-for-everybody/loops-and-iterations.md
@@ -0,0 +1,51 @@
+---
+id: 5e7b9f060b6c005b0e76f05c
+title: Loops and Iterations
+challengeType: 11
+videoId: dLA-szNRnUY
+dashedName: loops-and-iterations
+---
+
+# --question--
+
+## --text--
+
+What will the following code print out?:
+
+```python
+n = 0
+while True:
+ if n == 3:
+ break
+ print(n)
+ n = n + 1
+```
+
+## --answers--
+
+
0
+1
+2
+
+---
+
+
0
+1
+2
+3
+
+---
+
+
1
+2
+
+---
+
+
1
+2
+3
+
+## --video-solution--
+
+1
+
diff --git a/curriculum/challenges/chinese-traditional/07-scientific-computing-with-python/python-for-everybody/make-a-relational-database.md b/curriculum/challenges/chinese-traditional/07-scientific-computing-with-python/python-for-everybody/make-a-relational-database.md
new file mode 100644
index 0000000000..36e76238d4
--- /dev/null
+++ b/curriculum/challenges/chinese-traditional/07-scientific-computing-with-python/python-for-everybody/make-a-relational-database.md
@@ -0,0 +1,48 @@
+---
+id: 5e7b9f170b6c005b0e76f08b
+title: Make a Relational Database
+challengeType: 11
+videoId: MQ5z4bdF92U
+dashedName: make-a-relational-database
+---
+
+# --question--
+
+## --text--
+
+What SQL command would you use to retrieve all users that have the email address `quincy@freecodecamp.org`?
+
+## --answers--
+
+```sql
+SELECT Users WHERE email="quincy@freecodecamp.org"
+```
+
+---
+
+```sql
+SELECT Users WHERE email IS "quincy@freecodecamp.org"
+```
+
+---
+
+```sql
+SELECT ALL Users WHERE email="quincy@freecodecamp.org"
+```
+
+---
+
+```sql
+SELECT * FROM Users WHERE email IS "quincy@freecodecamp.org"
+```
+
+---
+
+```sql
+SELECT * FROM Users WHERE email="quincy@freecodecamp.org"
+```
+
+## --video-solution--
+
+5
+
diff --git a/curriculum/challenges/chinese-traditional/07-scientific-computing-with-python/python-for-everybody/more-conditional-structures.md b/curriculum/challenges/chinese-traditional/07-scientific-computing-with-python/python-for-everybody/more-conditional-structures.md
new file mode 100644
index 0000000000..135632d2b7
--- /dev/null
+++ b/curriculum/challenges/chinese-traditional/07-scientific-computing-with-python/python-for-everybody/more-conditional-structures.md
@@ -0,0 +1,56 @@
+---
+id: 5e7b9f060b6c005b0e76f059
+title: More Conditional Structures
+challengeType: 11
+videoId: HdL82tAZR20
+dashedName: more-conditional-structures
+---
+
+# --description--
+
+More resources:
+
+\- [Exercise 1](https://www.youtube.com/watch?v=crLerB4ZxMI)
+
+\- [Exercise 2](https://www.youtube.com/watch?v=KJN3-7HH6yk)
+
+# --question--
+
+## --text--
+
+Given the following code:
+
+```python
+temp = "5 degrees"
+cel = 0
+fahr = float(temp)
+cel = (fahr - 32.0) * 5.0 / 9.0
+print(cel)
+```
+
+Which line/lines should be surrounded by `try` block?
+
+## --answers--
+
+1
+
+---
+
+3
+
+---
+
+3,4
+
+---
+
+4
+
+---
+
+None
+
+## --video-solution--
+
+3
+
diff --git a/curriculum/challenges/chinese-traditional/07-scientific-computing-with-python/python-for-everybody/networking-protocol.md b/curriculum/challenges/chinese-traditional/07-scientific-computing-with-python/python-for-everybody/networking-protocol.md
new file mode 100644
index 0000000000..7196aa02da
--- /dev/null
+++ b/curriculum/challenges/chinese-traditional/07-scientific-computing-with-python/python-for-everybody/networking-protocol.md
@@ -0,0 +1,34 @@
+---
+id: 5e7b9f0c0b6c005b0e76f072
+title: Networking Protocol
+challengeType: 11
+videoId: c6vZGescaSc
+dashedName: networking-protocol
+---
+
+# --question--
+
+## --text--
+
+What type of HTTP request is usually used to access a website?
+
+## --answers--
+
+POST
+
+---
+
+GET
+
+---
+
+WEB
+
+---
+
+ACCESS
+
+## --video-solution--
+
+2
+
diff --git a/curriculum/challenges/chinese-traditional/07-scientific-computing-with-python/python-for-everybody/networking-text-processing.md b/curriculum/challenges/chinese-traditional/07-scientific-computing-with-python/python-for-everybody/networking-text-processing.md
new file mode 100644
index 0000000000..bf0c895949
--- /dev/null
+++ b/curriculum/challenges/chinese-traditional/07-scientific-computing-with-python/python-for-everybody/networking-text-processing.md
@@ -0,0 +1,30 @@
+---
+id: 5e7b9f0c0b6c005b0e76f074
+title: 'Networking: Text Processing'
+challengeType: 11
+videoId: Pv_pJgVu8WI
+dashedName: networking-text-processing
+---
+
+# --question--
+
+## --text--
+
+Which type of encoding do most websites use?
+
+## --answers--
+
+UTF-8
+
+---
+
+UTF-16
+
+---
+
+UTF-32
+
+## --video-solution--
+
+1
+
diff --git a/curriculum/challenges/chinese-traditional/07-scientific-computing-with-python/python-for-everybody/networking-using-urllib-in-python.md b/curriculum/challenges/chinese-traditional/07-scientific-computing-with-python/python-for-everybody/networking-using-urllib-in-python.md
new file mode 100644
index 0000000000..a8f26e7a47
--- /dev/null
+++ b/curriculum/challenges/chinese-traditional/07-scientific-computing-with-python/python-for-everybody/networking-using-urllib-in-python.md
@@ -0,0 +1,37 @@
+---
+id: 5e7b9f0d0b6c005b0e76f075
+title: 'Networking: Using urllib in Python'
+challengeType: 11
+videoId: 7lFM1T_CxBs
+dashedName: networking-using-urllib-in-python
+---
+
+# --question--
+
+## --text--
+
+What will the output of the following code be like?:
+
+```python
+import urllib.request
+fhand = urllib.request.urlopen('http://data.pr4e.org/romeo.txt')
+for line in fhand:
+ print(line.decode().strip())
+```
+
+## --answers--
+
+Just contents of "romeo.txt".
+
+---
+
+A header and the contents of "romeo.txt".
+
+---
+
+A header, a footer, and the contents of "romeo.txt".
+
+## --video-solution--
+
+1
+
diff --git a/curriculum/challenges/chinese-traditional/07-scientific-computing-with-python/python-for-everybody/networking-web-scraping-with-python.md b/curriculum/challenges/chinese-traditional/07-scientific-computing-with-python/python-for-everybody/networking-web-scraping-with-python.md
new file mode 100644
index 0000000000..ed31a3848a
--- /dev/null
+++ b/curriculum/challenges/chinese-traditional/07-scientific-computing-with-python/python-for-everybody/networking-web-scraping-with-python.md
@@ -0,0 +1,56 @@
+---
+id: 5e7b9f0d0b6c005b0e76f076
+title: 'Networking: Web Scraping with Python'
+challengeType: 11
+videoId: Uyioq2q4cEg
+dashedName: networking-web-scraping-with-python
+---
+
+# --description--
+
+More resources:
+
+\- [Exercise: socket1](https://www.youtube.com/watch?v=dWLdI143W-g)
+
+\- [Exercise: urllib](https://www.youtube.com/watch?v=8yis2DvbBkI)
+
+\- [Exercise: urllinks](https://www.youtube.com/watch?v=g9flPDG9nnY)
+
+# --question--
+
+## --text--
+
+What Python library is used for parsing HTML documents and extracting data from HTML documents?
+
+## --answers--
+
+socket
+
+---
+
+http
+
+---
+
+BeautifulSoup
+
+---
+
+PrettyBiscuit
+
+---
+
+WonderfulSalad
+
+---
+
+HttpParser
+
+---
+
+GrunkleStan
+
+## --video-solution--
+
+3
+
diff --git a/curriculum/challenges/chinese-traditional/07-scientific-computing-with-python/python-for-everybody/networking-with-python.md b/curriculum/challenges/chinese-traditional/07-scientific-computing-with-python/python-for-everybody/networking-with-python.md
new file mode 100644
index 0000000000..b90462a16d
--- /dev/null
+++ b/curriculum/challenges/chinese-traditional/07-scientific-computing-with-python/python-for-everybody/networking-with-python.md
@@ -0,0 +1,34 @@
+---
+id: 5e7b9f0c0b6c005b0e76f071
+title: Networking with Python
+challengeType: 11
+videoId: _kJvneKVdNM
+dashedName: networking-with-python
+---
+
+# --question--
+
+## --text--
+
+What Python library gives access to TCP Sockets?
+
+## --answers--
+
+tcp
+
+---
+
+socket
+
+---
+
+http
+
+---
+
+port
+
+## --video-solution--
+
+2
+
diff --git a/curriculum/challenges/chinese-traditional/07-scientific-computing-with-python/python-for-everybody/networking-write-a-web-browser.md b/curriculum/challenges/chinese-traditional/07-scientific-computing-with-python/python-for-everybody/networking-write-a-web-browser.md
new file mode 100644
index 0000000000..41fc6959e0
--- /dev/null
+++ b/curriculum/challenges/chinese-traditional/07-scientific-computing-with-python/python-for-everybody/networking-write-a-web-browser.md
@@ -0,0 +1,50 @@
+---
+id: 5e7b9f0c0b6c005b0e76f073
+title: 'Networking: Write a Web Browser'
+challengeType: 11
+videoId: zjyT9DaAjx4
+dashedName: networking-write-a-web-browser
+---
+
+# --question--
+
+## --text--
+
+What does the following code create?:
+
+```py
+import socket
+
+mysock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
+mysock.connect(('data.pr4e.org', 80))
+cmd = 'GET http://data.pr4e.org/romeo.txt HTTP/1.0\r\n\r\n'.encode()
+mysock.send(cmd)
+
+while True:
+ data = mysock.recv(512)
+ if len(data) < 1:
+ break
+ print(data.decode(),end='')
+mysock.close()
+```
+
+## --answers--
+
+A simple web server.
+
+---
+
+A simple email client.
+
+---
+
+A simple todo list.
+
+---
+
+A simple web browser.
+
+## --video-solution--
+
+4
+
diff --git a/curriculum/challenges/chinese-traditional/07-scientific-computing-with-python/python-for-everybody/object-lifecycle.md b/curriculum/challenges/chinese-traditional/07-scientific-computing-with-python/python-for-everybody/object-lifecycle.md
new file mode 100644
index 0000000000..c85eabd257
--- /dev/null
+++ b/curriculum/challenges/chinese-traditional/07-scientific-computing-with-python/python-for-everybody/object-lifecycle.md
@@ -0,0 +1,67 @@
+---
+id: 5e7b9f170b6c005b0e76f087
+title: Object Lifecycle
+challengeType: 11
+videoId: p1r3h_AMMIM
+dashedName: object-lifecycle
+---
+
+# --question--
+
+## --text--
+
+What will the following program print?:
+
+```python
+class PartyAnimal:
+ x = 0
+ name = ''
+ def __init__(self, nam):
+ self.name = nam
+ print(self.name,'constructed')
+ def party(self):
+ self.x = self.x + 1
+ print(self.name,'party count',self.x)
+
+q = PartyAnimal('Quincy')
+m = PartyAnimal('Miya')
+
+q.party()
+m.party()
+q.party()
+```
+
+## --answers--
+
+
+Quincy constructed
+Miya constructed
+Quincy party count 1
+Miya party count 2
+Quincy party count 3
+
+
+---
+
+
+Quincy constructed
+Miya constructed
+Quincy party count 1
+Miya party count 1
+Quincy party count 2
+
+
+---
+
+
+Quincy constructed
+Quincy party count 1
+Quincy party count 2
+Miya constructed
+Miya party count 1
+
+
+## --video-solution--
+
+2
+
diff --git a/curriculum/challenges/chinese-traditional/07-scientific-computing-with-python/python-for-everybody/objects-a-sample-class.md b/curriculum/challenges/chinese-traditional/07-scientific-computing-with-python/python-for-everybody/objects-a-sample-class.md
new file mode 100644
index 0000000000..41d127dc0f
--- /dev/null
+++ b/curriculum/challenges/chinese-traditional/07-scientific-computing-with-python/python-for-everybody/objects-a-sample-class.md
@@ -0,0 +1,58 @@
+---
+id: 5e7b9f160b6c005b0e76f086
+title: 'Objects: A Sample Class'
+challengeType: 11
+videoId: FiABKEuaSJ8
+dashedName: objects-a-sample-class
+---
+
+# --question--
+
+## --text--
+
+What will the following program print?:
+
+```python
+class PartyAnimal:
+ x = 0
+ def party(self):
+ self.x = self.x + 2
+ print(self.x)
+
+an = PartyAnimal()
+an.party()
+an.party()
+```
+
+## --answers--
+
+
+So far 1
+So far 2
+
+
+---
+
+
+0
+0
+
+
+---
+
+
+2
+2
+
+
+---
+
+
+2
+4
+
+
+## --video-solution--
+
+4
+
diff --git a/curriculum/challenges/chinese-traditional/07-scientific-computing-with-python/python-for-everybody/objects-inheritance.md b/curriculum/challenges/chinese-traditional/07-scientific-computing-with-python/python-for-everybody/objects-inheritance.md
new file mode 100644
index 0000000000..1de3c04012
--- /dev/null
+++ b/curriculum/challenges/chinese-traditional/07-scientific-computing-with-python/python-for-everybody/objects-inheritance.md
@@ -0,0 +1,34 @@
+---
+id: 5e7b9f170b6c005b0e76f088
+title: 'Objects: Inheritance'
+challengeType: 11
+videoId: FBL3alYrxRM
+dashedName: objects-inheritance
+---
+
+# --question--
+
+## --text--
+
+What is inheritance in object-oriented programming?
+
+## --answers--
+
+A new class created when a parent class is extended.
+
+---
+
+A constructed instance of a class.
+
+---
+
+The ability to create a new class by extending an existing class.
+
+---
+
+A method that is called at the moment when a class is being used to construct an object.
+
+## --video-solution--
+
+3
+
diff --git a/curriculum/challenges/chinese-traditional/07-scientific-computing-with-python/python-for-everybody/python-dictionaries.md b/curriculum/challenges/chinese-traditional/07-scientific-computing-with-python/python-for-everybody/python-dictionaries.md
new file mode 100644
index 0000000000..0c86c3bd09
--- /dev/null
+++ b/curriculum/challenges/chinese-traditional/07-scientific-computing-with-python/python-for-everybody/python-dictionaries.md
@@ -0,0 +1,55 @@
+---
+id: 5e7b9f090b6c005b0e76f067
+title: Python Dictionaries
+challengeType: 11
+videoId: dnzvfimrRMg
+dashedName: python-dictionaries
+---
+
+# --question--
+
+## --text--
+
+What does dict equal after running this code?:
+
+```python
+dict = {"Fri": 20, "Thu": 6, "Sat": 1}
+dict["Thu"] = 13
+dict["Sat"] = 2
+dict["Sun"] = 9
+```
+
+## --answers--
+
+```python
+{'Fri': 20, 'Thu': 6, 'Sat': 1}
+```
+
+---
+
+```python
+{'Fri': 20, 'Thu': 6, 'Sat': 1, 'Thu': 13, 'Sat': 2, 'Sun': 9}
+```
+
+---
+
+```python
+{'Sun': 9}
+```
+
+---
+
+```python
+{'Thu': 13, 'Sat': 2, 'Sun': 9}
+```
+
+---
+
+```python
+{'Fri': 20, 'Thu': 13, 'Sat': 2, 'Sun': 9}
+```
+
+## --video-solution--
+
+5
+
diff --git a/curriculum/challenges/chinese-traditional/07-scientific-computing-with-python/python-for-everybody/python-functions.md b/curriculum/challenges/chinese-traditional/07-scientific-computing-with-python/python-for-everybody/python-functions.md
new file mode 100644
index 0000000000..2f4bb2bbd0
--- /dev/null
+++ b/curriculum/challenges/chinese-traditional/07-scientific-computing-with-python/python-for-everybody/python-functions.md
@@ -0,0 +1,38 @@
+---
+id: 5e7b9f060b6c005b0e76f05a
+title: Python Functions
+challengeType: 11
+videoId: 3JGF-n3tDPU
+dashedName: python-functions
+---
+
+# --question--
+
+## --text--
+
+What is the purpose of the "def" keyword in Python?
+
+## --answers--
+
+It is slang that means "The following code is really cool."
+
+---
+
+It indicates the start of a function.
+
+---
+
+It indicates that the following indented section of code is to be stored for later.
+
+---
+
+It indicates the start of a function, and the following indented section of code is to be stored for later.
+
+---
+
+None of the above.
+
+## --video-solution--
+
+4
+
diff --git a/curriculum/challenges/chinese-traditional/07-scientific-computing-with-python/python-for-everybody/python-lists.md b/curriculum/challenges/chinese-traditional/07-scientific-computing-with-python/python-for-everybody/python-lists.md
new file mode 100644
index 0000000000..be78cd10c7
--- /dev/null
+++ b/curriculum/challenges/chinese-traditional/07-scientific-computing-with-python/python-for-everybody/python-lists.md
@@ -0,0 +1,39 @@
+---
+id: 5e7b9f080b6c005b0e76f064
+title: Python Lists
+challengeType: 11
+videoId: Y0cvfDpYC_c
+dashedName: python-lists
+---
+
+# --question--
+
+## --text--
+
+What is the value of x after running this code:
+
+```python
+fruit = "banana"
+x = fruit[1]
+```
+
+## --answers--
+
+banana
+
+---
+
+a
+
+---
+
+b
+
+---
+
+True
+
+## --video-solution--
+
+2
+
diff --git a/curriculum/challenges/chinese-traditional/07-scientific-computing-with-python/python-for-everybody/python-objects.md b/curriculum/challenges/chinese-traditional/07-scientific-computing-with-python/python-for-everybody/python-objects.md
new file mode 100644
index 0000000000..6082df58eb
--- /dev/null
+++ b/curriculum/challenges/chinese-traditional/07-scientific-computing-with-python/python-for-everybody/python-objects.md
@@ -0,0 +1,34 @@
+---
+id: 5e7b9f160b6c005b0e76f085
+title: Python Objects
+challengeType: 11
+videoId: uJxGeTYy0us
+dashedName: python-objects
+---
+
+# --question--
+
+## --text--
+
+Which is NOT true about objects in Python?
+
+## --answers--
+
+Objects get created and used.
+
+---
+
+Objects are bits of code and data.
+
+---
+
+Objects hide detail.
+
+---
+
+Objects are one of the five standard data types.
+
+## --video-solution--
+
+4
+
diff --git a/curriculum/challenges/chinese-traditional/07-scientific-computing-with-python/python-for-everybody/reading-files.md b/curriculum/challenges/chinese-traditional/07-scientific-computing-with-python/python-for-everybody/reading-files.md
new file mode 100644
index 0000000000..9be11dc4f3
--- /dev/null
+++ b/curriculum/challenges/chinese-traditional/07-scientific-computing-with-python/python-for-everybody/reading-files.md
@@ -0,0 +1,38 @@
+---
+id: 5e7b9f080b6c005b0e76f062
+title: Reading Files
+challengeType: 11
+videoId: Fo1tW09KIwo
+dashedName: reading-files
+---
+
+# --question--
+
+## --text--
+
+What is used to indicate a new line in a string?
+
+## --answers--
+
+\\n
+
+---
+
+{new_line}
+
+---
+
+{n}
+
+---
+
+/n
+
+---
+
+/new
+
+## --video-solution--
+
+1
+
diff --git a/curriculum/challenges/chinese-traditional/07-scientific-computing-with-python/python-for-everybody/regular-expressions-matching-and-extracting-data.md b/curriculum/challenges/chinese-traditional/07-scientific-computing-with-python/python-for-everybody/regular-expressions-matching-and-extracting-data.md
new file mode 100644
index 0000000000..87a9824144
--- /dev/null
+++ b/curriculum/challenges/chinese-traditional/07-scientific-computing-with-python/python-for-everybody/regular-expressions-matching-and-extracting-data.md
@@ -0,0 +1,41 @@
+---
+id: 5e7b9f0b0b6c005b0e76f06f
+title: 'Regular Expressions: Matching and Extracting Data'
+challengeType: 11
+videoId: LaCZnTbQGkE
+dashedName: regular-expressions-matching-and-extracting-data
+---
+
+# --question--
+
+## --text--
+
+What will the following program print?:
+
+```python
+import re
+s = 'A message from csev@umich.edu to cwen@iupui.edu about meeting @2PM'
+lst = re.findall('\\S+@\\S+', s)
+print(lst)
+```
+
+## --answers--
+
+['csev@umich.edu', 'cwen@iupui.edu']
+
+---
+
+['csev@umich.edu']
+
+---
+
+['umich.edu', 'iupui.edu']
+
+---
+
+['csev@', 'cwen@']
+
+## --video-solution--
+
+1
+
diff --git a/curriculum/challenges/chinese-traditional/07-scientific-computing-with-python/python-for-everybody/regular-expressions-practical-applications.md b/curriculum/challenges/chinese-traditional/07-scientific-computing-with-python/python-for-everybody/regular-expressions-practical-applications.md
new file mode 100644
index 0000000000..cf1d7b3825
--- /dev/null
+++ b/curriculum/challenges/chinese-traditional/07-scientific-computing-with-python/python-for-everybody/regular-expressions-practical-applications.md
@@ -0,0 +1,34 @@
+---
+id: 5e7b9f0b0b6c005b0e76f070
+title: 'Regular Expressions: Practical Applications'
+challengeType: 11
+videoId: xCjFU9G6x48
+dashedName: regular-expressions-practical-applications
+---
+
+# --question--
+
+## --text--
+
+What will search for a "$" in a regular expression?
+
+## --answers--
+
+$
+
+---
+
+\\dollar\\
+
+---
+
+\\$
+
+---
+
+!$
+
+## --video-solution--
+
+3
+
diff --git a/curriculum/challenges/chinese-traditional/07-scientific-computing-with-python/python-for-everybody/regular-expressions.md b/curriculum/challenges/chinese-traditional/07-scientific-computing-with-python/python-for-everybody/regular-expressions.md
new file mode 100644
index 0000000000..0bff237a4a
--- /dev/null
+++ b/curriculum/challenges/chinese-traditional/07-scientific-computing-with-python/python-for-everybody/regular-expressions.md
@@ -0,0 +1,38 @@
+---
+id: 5e7b9f0b0b6c005b0e76f06e
+title: Regular Expressions
+challengeType: 11
+videoId: Yud_COr6pZo
+dashedName: regular-expressions
+---
+
+# --question--
+
+## --text--
+
+Which regex matches only a white space character?
+
+## --answers--
+
+\\S
+
+---
+
+\\s
+
+---
+
+.
+
+---
+
+\_
+
+---
+
+\\.
+
+## --video-solution--
+
+2
+
diff --git a/curriculum/challenges/chinese-traditional/07-scientific-computing-with-python/python-for-everybody/relational-database-design.md b/curriculum/challenges/chinese-traditional/07-scientific-computing-with-python/python-for-everybody/relational-database-design.md
new file mode 100644
index 0000000000..21f07615fc
--- /dev/null
+++ b/curriculum/challenges/chinese-traditional/07-scientific-computing-with-python/python-for-everybody/relational-database-design.md
@@ -0,0 +1,34 @@
+---
+id: 5e7b9f180b6c005b0e76f08c
+title: Relational Database Design
+challengeType: 11
+videoId: AqdfbrpkbHk
+dashedName: relational-database-design
+---
+
+# --question--
+
+## --text--
+
+What is the best practice for how many times a piece of string data should be stored in a database?
+
+## --answers--
+
+0
+
+---
+
+1
+
+---
+
+2
+
+---
+
+3
+
+## --video-solution--
+
+2
+
diff --git a/curriculum/challenges/chinese-traditional/07-scientific-computing-with-python/python-for-everybody/relational-databases-and-sqlite.md b/curriculum/challenges/chinese-traditional/07-scientific-computing-with-python/python-for-everybody/relational-databases-and-sqlite.md
new file mode 100644
index 0000000000..26732397d0
--- /dev/null
+++ b/curriculum/challenges/chinese-traditional/07-scientific-computing-with-python/python-for-everybody/relational-databases-and-sqlite.md
@@ -0,0 +1,34 @@
+---
+id: 5e7b9f170b6c005b0e76f08a
+title: Relational Databases and SQLite
+challengeType: 11
+videoId: QlNod5-kFpA
+dashedName: relational-databases-and-sqlite
+---
+
+# --question--
+
+## --text--
+
+Which is NOT a primary data structure in a database?
+
+## --answers--
+
+index
+
+---
+
+table
+
+---
+
+row
+
+---
+
+column
+
+## --video-solution--
+
+1
+
diff --git a/curriculum/challenges/chinese-traditional/07-scientific-computing-with-python/python-for-everybody/relational-databases-join-operation.md b/curriculum/challenges/chinese-traditional/07-scientific-computing-with-python/python-for-everybody/relational-databases-join-operation.md
new file mode 100644
index 0000000000..451e8b5107
--- /dev/null
+++ b/curriculum/challenges/chinese-traditional/07-scientific-computing-with-python/python-for-everybody/relational-databases-join-operation.md
@@ -0,0 +1,30 @@
+---
+id: 5e7b9f180b6c005b0e76f08f
+title: 'Relational Databases: Join Operation'
+challengeType: 11
+videoId: jvDw3D9GKac
+dashedName: relational-databases-join-operation
+---
+
+# --question--
+
+## --text--
+
+When using a JOIN clause in an SQL statement, what does ON do?
+
+## --answers--
+
+It indicates what tables to perform the JOIN on.
+
+---
+
+It specifies the fields to use for the JOIN.
+
+---
+
+It indicates how the two tables are to be joined.
+
+## --video-solution--
+
+3
+
diff --git a/curriculum/challenges/chinese-traditional/07-scientific-computing-with-python/python-for-everybody/relational-databases-many-to-many-relationships.md b/curriculum/challenges/chinese-traditional/07-scientific-computing-with-python/python-for-everybody/relational-databases-many-to-many-relationships.md
new file mode 100644
index 0000000000..252eca3a88
--- /dev/null
+++ b/curriculum/challenges/chinese-traditional/07-scientific-computing-with-python/python-for-everybody/relational-databases-many-to-many-relationships.md
@@ -0,0 +1,48 @@
+---
+id: 5e7b9f190b6c005b0e76f090
+title: 'Relational Databases: Many-to-many Relationships'
+challengeType: 11
+videoId: z-SBYcvEQOc
+dashedName: relational-databases-many-to-many-relationships
+---
+
+# --description--
+
+More resources:
+
+\- [Exercise: Email](https://www.youtube.com/watch?v=uQ3Qv1z_Vao)
+
+\- [Exercise: Roster](https://www.youtube.com/watch?v=qEkUEAz8j3o)
+
+\- [Exercise: Tracks](https://www.youtube.com/watch?v=I-E7avcPeSE)
+
+\- [Exercise: Twfriends](https://www.youtube.com/watch?v=RZRAoBFIH6A)
+
+\- [Exercise: Twspider](https://www.youtube.com/watch?v=xBaJddvJL4A)
+
+# --question--
+
+## --text--
+
+Which is an example of a many-to-many relationship?
+
+## --answers--
+
+teacher to student
+
+---
+
+customer to order
+
+---
+
+book to pages
+
+---
+
+city to country
+
+## --video-solution--
+
+1
+
diff --git a/curriculum/challenges/chinese-traditional/07-scientific-computing-with-python/python-for-everybody/relational-databases-relationship-building.md b/curriculum/challenges/chinese-traditional/07-scientific-computing-with-python/python-for-everybody/relational-databases-relationship-building.md
new file mode 100644
index 0000000000..87e0754a62
--- /dev/null
+++ b/curriculum/challenges/chinese-traditional/07-scientific-computing-with-python/python-for-everybody/relational-databases-relationship-building.md
@@ -0,0 +1,30 @@
+---
+id: 5e7b9f180b6c005b0e76f08e
+title: 'Relational Databases: Relationship Building'
+challengeType: 11
+videoId: CSbqczsHVnc
+dashedName: relational-databases-relationship-building
+---
+
+# --question--
+
+## --text--
+
+What does the INSERT command do in SQL?
+
+## --answers--
+
+It defines a new row by listing the fields we want to include followed by the values we want placed in the new row.
+
+---
+
+It defines a new column by listing the rows we want to include followed by the values we want placed in the new column.
+
+---
+
+It defines a new table by listing the rows and fields we want to include followed by the values that we want placed in the table.
+
+## --video-solution--
+
+1
+
diff --git a/curriculum/challenges/chinese-traditional/07-scientific-computing-with-python/python-for-everybody/representing-relationships-in-a-relational-database.md b/curriculum/challenges/chinese-traditional/07-scientific-computing-with-python/python-for-everybody/representing-relationships-in-a-relational-database.md
new file mode 100644
index 0000000000..03b88fb678
--- /dev/null
+++ b/curriculum/challenges/chinese-traditional/07-scientific-computing-with-python/python-for-everybody/representing-relationships-in-a-relational-database.md
@@ -0,0 +1,34 @@
+---
+id: 5e7b9f180b6c005b0e76f08d
+title: Representing Relationships in a Relational Database
+challengeType: 11
+videoId: '-orenCNdC2Q'
+dashedName: representing-relationships-in-a-relational-database
+---
+
+# --question--
+
+## --text--
+
+What is a foreign key?
+
+## --answers--
+
+A key that is not supposed to be there.
+
+---
+
+A key that uses non-latin characters.
+
+---
+
+A number that points to the primary key of an associated row in a different table.
+
+---
+
+A key that the "real world" might use to look up a row.
+
+## --video-solution--
+
+3
+
diff --git a/curriculum/challenges/chinese-traditional/07-scientific-computing-with-python/python-for-everybody/strings-and-lists.md b/curriculum/challenges/chinese-traditional/07-scientific-computing-with-python/python-for-everybody/strings-and-lists.md
new file mode 100644
index 0000000000..51ab3b952b
--- /dev/null
+++ b/curriculum/challenges/chinese-traditional/07-scientific-computing-with-python/python-for-everybody/strings-and-lists.md
@@ -0,0 +1,47 @@
+---
+id: 5e7b9f090b6c005b0e76f066
+title: Strings and Lists
+challengeType: 11
+videoId: lxcFa7ldCi0
+dashedName: strings-and-lists
+---
+
+# --description--
+
+More resources:
+
+\- [Exercise](https://www.youtube.com/watch?v=-9TfJF2dwHI)
+
+# --question--
+
+## --text--
+
+What does n equal in this code?
+
+```python
+words = 'His e-mail is q-lar@freecodecamp.org'
+pieces = words.split()
+parts = pieces[3].split('-')
+n = parts[1]
+```
+
+## --answers--
+
+mail
+
+---
+
+q
+
+---
+
+lar
+
+---
+
+`lar@freecodecamp.org`
+
+## --video-solution--
+
+4
+
diff --git a/curriculum/challenges/chinese-traditional/07-scientific-computing-with-python/python-for-everybody/strings-in-python.md b/curriculum/challenges/chinese-traditional/07-scientific-computing-with-python/python-for-everybody/strings-in-python.md
new file mode 100644
index 0000000000..2136025df6
--- /dev/null
+++ b/curriculum/challenges/chinese-traditional/07-scientific-computing-with-python/python-for-everybody/strings-in-python.md
@@ -0,0 +1,59 @@
+---
+id: 5e7b9f070b6c005b0e76f060
+title: Strings in Python
+challengeType: 11
+videoId: LYZj207fKpQ
+dashedName: strings-in-python
+---
+
+# --question--
+
+## --text--
+
+What will the following code print?:
+
+```python
+for n in "banana":
+ print(n)
+```
+
+## --answers--
+
+
+n
+n
+
+
+---
+
+
+0
+1
+
+
+---
+
+
+0
+1
+2
+3
+4
+5
+
+
+---
+
+
+b
+a
+n
+a
+n
+a
+
+
+## --video-solution--
+
+4
+
diff --git a/curriculum/challenges/chinese-traditional/07-scientific-computing-with-python/python-for-everybody/the-tuples-collection.md b/curriculum/challenges/chinese-traditional/07-scientific-computing-with-python/python-for-everybody/the-tuples-collection.md
new file mode 100644
index 0000000000..e11e5398ab
--- /dev/null
+++ b/curriculum/challenges/chinese-traditional/07-scientific-computing-with-python/python-for-everybody/the-tuples-collection.md
@@ -0,0 +1,59 @@
+---
+id: 5e7b9f0a0b6c005b0e76f06c
+title: The Tuples Collection
+challengeType: 11
+videoId: 3Lxpladfh2k
+dashedName: the-tuples-collection
+---
+
+# --question--
+
+## --text--
+
+What will the following code print?:
+
+```python
+d = dict()
+d['quincy'] = 1
+d['beau'] = 5
+d['kris'] = 9
+for (k,i) in d.items():
+ print(k, i)
+```
+
+## --answers--
+
+
+k i
+k i
+k i
+
+
+---
+
+
+quincy 0
+beau 1
+kris 2
+
+
+---
+
+
+quincy 1
+beau 5
+kris 9
+
+
+---
+
+
+1 quincy
+5 beau
+9 kris
+
+
+## --video-solution--
+
+3
+
diff --git a/curriculum/challenges/chinese-traditional/07-scientific-computing-with-python/python-for-everybody/using-web-services.md b/curriculum/challenges/chinese-traditional/07-scientific-computing-with-python/python-for-everybody/using-web-services.md
new file mode 100644
index 0000000000..5c2f7814c1
--- /dev/null
+++ b/curriculum/challenges/chinese-traditional/07-scientific-computing-with-python/python-for-everybody/using-web-services.md
@@ -0,0 +1,38 @@
+---
+id: 5e7b9f0e0b6c005b0e76f07a
+title: Using Web Services
+challengeType: 11
+videoId: oNl1OVDPGKE
+dashedName: using-web-services
+---
+
+# --question--
+
+## --text--
+
+What are the two most common ways to send data over the internet?
+
+## --answers--
+
+JSON and TXT
+
+---
+
+JSON and XML
+
+---
+
+XML and TXT
+
+---
+
+XML and PHP
+
+---
+
+PHP and TXT
+
+## --video-solution--
+
+2
+
diff --git a/curriculum/challenges/chinese-traditional/07-scientific-computing-with-python/python-for-everybody/variables-expressions-and-statements.md b/curriculum/challenges/chinese-traditional/07-scientific-computing-with-python/python-for-everybody/variables-expressions-and-statements.md
new file mode 100644
index 0000000000..50b6caa84b
--- /dev/null
+++ b/curriculum/challenges/chinese-traditional/07-scientific-computing-with-python/python-for-everybody/variables-expressions-and-statements.md
@@ -0,0 +1,34 @@
+---
+id: 5e7b9f050b6c005b0e76f056
+title: 'Variables, Expressions, and Statements'
+challengeType: 11
+videoId: nELR-uyyrok
+dashedName: variables-expressions-and-statements
+---
+
+# --question--
+
+## --text--
+
+What is the symbol used in an assignment statement?
+
+## --answers--
+
+~
+
+---
+
+&
+
+---
+
+=
+
+---
+
+\|
+
+## --video-solution--
+
+3
+
diff --git a/curriculum/challenges/chinese-traditional/07-scientific-computing-with-python/python-for-everybody/visualizing-data-with-python.md b/curriculum/challenges/chinese-traditional/07-scientific-computing-with-python/python-for-everybody/visualizing-data-with-python.md
new file mode 100644
index 0000000000..c258b56bce
--- /dev/null
+++ b/curriculum/challenges/chinese-traditional/07-scientific-computing-with-python/python-for-everybody/visualizing-data-with-python.md
@@ -0,0 +1,38 @@
+---
+id: 5e7b9f690b6c005b0e76f095
+title: Visualizing Data with Python
+challengeType: 11
+videoId: e3lydkH0prw
+dashedName: visualizing-data-with-python
+---
+
+# --question--
+
+## --text--
+
+Most data needs to be \_\_\_\_\_\_ before using it.
+
+## --answers--
+
+converted to JSON format
+
+---
+
+graphed
+
+---
+
+cleaned
+
+---
+
+memorized
+
+---
+
+turned into song
+
+## --video-solution--
+
+3
+
diff --git a/curriculum/challenges/chinese-traditional/07-scientific-computing-with-python/python-for-everybody/web-services-api-rate-limiting-and-security.md b/curriculum/challenges/chinese-traditional/07-scientific-computing-with-python/python-for-everybody/web-services-api-rate-limiting-and-security.md
new file mode 100644
index 0000000000..992602d0c6
--- /dev/null
+++ b/curriculum/challenges/chinese-traditional/07-scientific-computing-with-python/python-for-everybody/web-services-api-rate-limiting-and-security.md
@@ -0,0 +1,46 @@
+---
+id: 5e7b9f150b6c005b0e76f080
+title: 'Web Services: API Rate Limiting and Security'
+challengeType: 11
+videoId: pI-g0lI8ngs
+dashedName: web-services-api-rate-limiting-and-security
+---
+
+# --description--
+
+More resources:
+
+\- [Exercise: GeoJSON](https://www.youtube.com/watch?v=TJGJN0T8tak)
+
+\- [Exercise: JSON](https://www.youtube.com/watch?v=vTmw5RtfGMY)
+
+\- [Exercise: Twitter](https://www.youtube.com/watch?v=2c7YwhvpCro)
+
+\- [Exercise: XML](https://www.youtube.com/watch?v=AopYOlDa-vY)
+
+# --question--
+
+## --text--
+
+When making a request from the Twitter API, what information must always be sent with the request?
+
+## --answers--
+
+Twitter username
+
+---
+
+date range
+
+---
+
+search term
+
+---
+
+key
+
+## --video-solution--
+
+4
+
diff --git a/curriculum/challenges/chinese-traditional/07-scientific-computing-with-python/python-for-everybody/web-services-apis.md b/curriculum/challenges/chinese-traditional/07-scientific-computing-with-python/python-for-everybody/web-services-apis.md
new file mode 100644
index 0000000000..285380bd7b
--- /dev/null
+++ b/curriculum/challenges/chinese-traditional/07-scientific-computing-with-python/python-for-everybody/web-services-apis.md
@@ -0,0 +1,34 @@
+---
+id: 5e7b9f150b6c005b0e76f07f
+title: 'Web Services: APIs'
+challengeType: 11
+videoId: oUNn1psfBJg
+dashedName: web-services-apis
+---
+
+# --question--
+
+## --text--
+
+What does API stand for?
+
+## --answers--
+
+Application Portable Intelligence
+
+---
+
+Associate Programming International
+
+---
+
+Application Program Interface
+
+---
+
+Action Portable Interface
+
+## --video-solution--
+
+3
+
diff --git a/curriculum/challenges/chinese-traditional/07-scientific-computing-with-python/python-for-everybody/web-services-json.md b/curriculum/challenges/chinese-traditional/07-scientific-computing-with-python/python-for-everybody/web-services-json.md
new file mode 100644
index 0000000000..336373054c
--- /dev/null
+++ b/curriculum/challenges/chinese-traditional/07-scientific-computing-with-python/python-for-everybody/web-services-json.md
@@ -0,0 +1,56 @@
+---
+id: 5e7b9f140b6c005b0e76f07d
+title: 'Web Services: JSON'
+challengeType: 11
+videoId: ZJE-U56BppM
+dashedName: web-services-json
+---
+
+# --question--
+
+## --text--
+
+What will the following code print?:
+
+```python
+import json
+data = '''
+ [
+ { "id" : "001",
+ "x" : "2",
+ "name" : "Quincy"
+ } ,
+ { "id" : "009",
+ "x" : "7",
+ "name" : "Mrugesh"
+ }
+ ]
+'''
+info = json.loads(data)
+print(info[1]['name'])
+```
+
+## --answers--
+
+Quincy
+
+---
+
+Mrugesh
+
+---
+
+001
+
+---
+
+009
+
+---
+
+[Error]
+
+## --video-solution--
+
+2
+
diff --git a/curriculum/challenges/chinese-traditional/07-scientific-computing-with-python/python-for-everybody/web-services-service-oriented-approach.md b/curriculum/challenges/chinese-traditional/07-scientific-computing-with-python/python-for-everybody/web-services-service-oriented-approach.md
new file mode 100644
index 0000000000..614f5336a3
--- /dev/null
+++ b/curriculum/challenges/chinese-traditional/07-scientific-computing-with-python/python-for-everybody/web-services-service-oriented-approach.md
@@ -0,0 +1,30 @@
+---
+id: 5e7b9f140b6c005b0e76f07e
+title: 'Web Services: Service Oriented Approach'
+challengeType: 11
+videoId: muerlsCHExI
+dashedName: web-services-service-oriented-approach
+---
+
+# --question--
+
+## --text--
+
+With a services oriented approach to developing web apps, where is the data located?
+
+## --answers--
+
+Spread across many computer systems connected via the internet or internal network.
+
+---
+
+Within different services on the main web server.
+
+---
+
+On a separate database server.
+
+## --video-solution--
+
+1
+
diff --git a/curriculum/challenges/chinese-traditional/07-scientific-computing-with-python/python-for-everybody/web-services-xml-schema.md b/curriculum/challenges/chinese-traditional/07-scientific-computing-with-python/python-for-everybody/web-services-xml-schema.md
new file mode 100644
index 0000000000..8ccc87accb
--- /dev/null
+++ b/curriculum/challenges/chinese-traditional/07-scientific-computing-with-python/python-for-everybody/web-services-xml-schema.md
@@ -0,0 +1,30 @@
+---
+id: 5e7b9f0e0b6c005b0e76f07c
+title: 'Web Services: XML Schema'
+challengeType: 11
+videoId: yWU9kTxW-nc
+dashedName: web-services-xml-schema
+---
+
+# --question--
+
+## --text--
+
+What is XSD?
+
+## --answers--
+
+The W3C Schema specification for XML.
+
+---
+
+The standard JSON schema from MOZ.
+
+---
+
+Extensible Situational Driver
+
+## --video-solution--
+
+1
+
diff --git a/curriculum/challenges/chinese-traditional/07-scientific-computing-with-python/python-for-everybody/web-services-xml.md b/curriculum/challenges/chinese-traditional/07-scientific-computing-with-python/python-for-everybody/web-services-xml.md
new file mode 100644
index 0000000000..efda106867
--- /dev/null
+++ b/curriculum/challenges/chinese-traditional/07-scientific-computing-with-python/python-for-everybody/web-services-xml.md
@@ -0,0 +1,43 @@
+---
+id: 5e7b9f0e0b6c005b0e76f07b
+title: 'Web Services: XML'
+challengeType: 11
+videoId: _pZ0srbg7So
+dashedName: web-services-xml
+---
+
+# --question--
+
+## --text--
+
+What is wrong with the following XML?:
+
+```xml
+
+ Chuck
+
+ +1 734 303 4456
+
+
+```
+
+## --answers--
+
+Email tag is missing closing tag.
+
+---
+
+Spacing will cause XML to be invalid.
+
+---
+
+Phone tag is missing closing tag.
+
+---
+
+Plain text should be encoded using UTF-8.
+
+## --video-solution--
+
+3
+
diff --git a/curriculum/challenges/chinese-traditional/07-scientific-computing-with-python/python-for-everybody/working-with-lists.md b/curriculum/challenges/chinese-traditional/07-scientific-computing-with-python/python-for-everybody/working-with-lists.md
new file mode 100644
index 0000000000..dbeda928ee
--- /dev/null
+++ b/curriculum/challenges/chinese-traditional/07-scientific-computing-with-python/python-for-everybody/working-with-lists.md
@@ -0,0 +1,34 @@
+---
+id: 5e7b9f090b6c005b0e76f065
+title: Working with Lists
+challengeType: 11
+videoId: lCnHfTHkhbE
+dashedName: working-with-lists
+---
+
+# --question--
+
+## --text--
+
+Which method is used to add an item at the end of a list?
+
+## --answers--
+
+insert
+
+---
+
+push
+
+---
+
+append
+
+---
+
+new
+
+## --video-solution--
+
+3
+
diff --git a/curriculum/challenges/chinese-traditional/07-scientific-computing-with-python/scientific-computing-with-python-projects/arithmetic-formatter.md b/curriculum/challenges/chinese-traditional/07-scientific-computing-with-python/scientific-computing-with-python-projects/arithmetic-formatter.md
new file mode 100644
index 0000000000..c6f3482dba
--- /dev/null
+++ b/curriculum/challenges/chinese-traditional/07-scientific-computing-with-python/scientific-computing-with-python-projects/arithmetic-formatter.md
@@ -0,0 +1,42 @@
+---
+id: 5e44412c903586ffb414c94c
+title: Arithmetic Formatter
+challengeType: 10
+dashedName: arithmetic-formatter
+---
+
+# --description--
+
+Create a function that receives a list of strings that are arithmetic problems and returns the problems arranged vertically and side-by-side.
+
+You can access [the full project description and starter code on Repl.it](https://repl.it/github/freeCodeCamp/boilerplate-arithmetic-formatter).
+
+After going to that link, fork the project. Once you complete the project based on the instructions in 'README.md', submit your project link below.
+
+We are still developing the interactive instructional part of the Python curriculum. For now, here are some videos on the freeCodeCamp.org YouTube channel that will teach you everything you need to know to complete this project:
+
+
-
+ Python for Everybody Video Course (14 hours)
+
+ -
+ Learn Python Video Course (2 hours)
+
+
+
+# --hints--
+
+It should correctly format an arithmetic problem and pass all tests.
+
+```js
+
+```
+
+# --solutions--
+
+```js
+/**
+ Backend challenges don't need solutions,
+ because they would need to be tested against a full working project.
+ Please check our contributing guidelines to learn more.
+*/
+```
diff --git a/curriculum/challenges/chinese-traditional/07-scientific-computing-with-python/scientific-computing-with-python-projects/budget-app.md b/curriculum/challenges/chinese-traditional/07-scientific-computing-with-python/scientific-computing-with-python-projects/budget-app.md
new file mode 100644
index 0000000000..f67225f534
--- /dev/null
+++ b/curriculum/challenges/chinese-traditional/07-scientific-computing-with-python/scientific-computing-with-python-projects/budget-app.md
@@ -0,0 +1,43 @@
+---
+id: 5e44413e903586ffb414c94e
+title: Budget App
+challengeType: 10
+dashedName: budget-app
+---
+
+# --description--
+
+Create a "Category" class that can be used to create different budget categories.
+
+You can access [the full project description and starter code on Repl.it](https://repl.it/github/freeCodeCamp/boilerplate-budget-app).
+
+After going to that link, fork the project. Once you complete the project based on the instructions in 'README.md', submit your project link below.
+
+We are still developing the interactive instructional part of the Python curriculum. For now, here are some videos on the freeCodeCamp.org YouTube channel that will teach you everything you need to know to complete this project:
+
+
+
+# --hints--
+
+It should create a Category class and pass all tests.
+
+```js
+
+```
+
+# --solutions--
+
+```js
+/**
+ Backend challenges don't need solutions,
+ because they would need to be tested against a full working project.
+ Please check our contributing guidelines to learn more.
+*/
+```
diff --git a/curriculum/challenges/chinese-traditional/07-scientific-computing-with-python/scientific-computing-with-python-projects/polygon-area-calculator.md b/curriculum/challenges/chinese-traditional/07-scientific-computing-with-python/scientific-computing-with-python-projects/polygon-area-calculator.md
new file mode 100644
index 0000000000..21c24f9652
--- /dev/null
+++ b/curriculum/challenges/chinese-traditional/07-scientific-computing-with-python/scientific-computing-with-python-projects/polygon-area-calculator.md
@@ -0,0 +1,43 @@
+---
+id: 5e444147903586ffb414c94f
+title: Polygon Area Calculator
+challengeType: 10
+dashedName: polygon-area-calculator
+---
+
+# --description--
+
+In this project you will use object oriented programming to create a Rectangle class and a Square class. The Square class should be a subclass of Rectangle and inherit methods and attributes.
+
+You can access [the full project description and starter code on Repl.it](https://repl.it/github/freeCodeCamp/boilerplate-polygon-area-calculator).
+
+After going to that link, fork the project. Once you complete the project based on the instructions in 'README.md', submit your project link below.
+
+We are still developing the interactive instructional part of the Python curriculum. For now, here are some videos on the freeCodeCamp.org YouTube channel that will teach you everything you need to know to complete this project:
+
+
+
+# --hints--
+
+It should create a Rectangle class and Square class and pass all tests.
+
+```js
+
+```
+
+# --solutions--
+
+```js
+/**
+ Backend challenges don't need solutions,
+ because they would need to be tested against a full working project.
+ Please check our contributing guidelines to learn more.
+*/
+```
diff --git a/curriculum/challenges/chinese-traditional/07-scientific-computing-with-python/scientific-computing-with-python-projects/probability-calculator.md b/curriculum/challenges/chinese-traditional/07-scientific-computing-with-python/scientific-computing-with-python-projects/probability-calculator.md
new file mode 100644
index 0000000000..58cf748584
--- /dev/null
+++ b/curriculum/challenges/chinese-traditional/07-scientific-computing-with-python/scientific-computing-with-python-projects/probability-calculator.md
@@ -0,0 +1,41 @@
+---
+id: 5e44414f903586ffb414c950
+title: Probability Calculator
+challengeType: 10
+dashedName: probability-calculator
+---
+
+# --description--
+
+Write a program to determine the approximate probability of drawing certain balls randomly from a hat.
+
+You can access [the full project description and starter code on Repl.it](https://repl.it/github/freeCodeCamp/boilerplate-probability-calculator). After going to that link, fork the project. Once you complete the project based on the instructions in 'README.md', submit your project link below.
+
+We are still developing the interactive instructional part of the Python curriculum. For now, here are some videos on the freeCodeCamp.org YouTube channel that will teach you everything you need to know to complete this project:
+
+
+
+# --hints--
+
+It should correctly calculate probabilities and pass all tests.
+
+```js
+
+```
+
+# --solutions--
+
+```js
+/**
+ Backend challenges don't need solutions,
+ because they would need to be tested against a full working project.
+ Please check our contributing guidelines to learn more.
+*/
+```
diff --git a/curriculum/challenges/chinese-traditional/07-scientific-computing-with-python/scientific-computing-with-python-projects/time-calculator.md b/curriculum/challenges/chinese-traditional/07-scientific-computing-with-python/scientific-computing-with-python-projects/time-calculator.md
new file mode 100644
index 0000000000..2037f57ad7
--- /dev/null
+++ b/curriculum/challenges/chinese-traditional/07-scientific-computing-with-python/scientific-computing-with-python-projects/time-calculator.md
@@ -0,0 +1,41 @@
+---
+id: 5e444136903586ffb414c94d
+title: Time Calculator
+challengeType: 10
+dashedName: time-calculator
+---
+
+# --description--
+
+Write a function named "add_time" that can add a duration to a start time and return the result.
+
+You can access [the full project description and starter code on Repl.it](https://repl.it/github/freeCodeCamp/boilerplate-time-calculator). After going to that link, fork the project. Once you complete the project based on the instructions in 'README.md', submit your project link below.
+
+We are still developing the interactive instructional part of the Python curriculum. For now, here are some videos on the freeCodeCamp.org YouTube channel that will teach you everything you need to know to complete this project:
+
+
+
+# --hints--
+
+It should correctly add times and pass all tests.
+
+```js
+
+```
+
+# --solutions--
+
+```js
+/**
+ Backend challenges don't need solutions,
+ because they would need to be tested against a full working project.
+ Please check our contributing guidelines to learn more.
+*/
+```
diff --git a/curriculum/challenges/chinese-traditional/08-data-analysis-with-python/data-analysis-with-python-course/data-analysis-example-a.md b/curriculum/challenges/chinese-traditional/08-data-analysis-with-python/data-analysis-with-python-course/data-analysis-example-a.md
new file mode 100644
index 0000000000..9a24560aa9
--- /dev/null
+++ b/curriculum/challenges/chinese-traditional/08-data-analysis-with-python/data-analysis-with-python-course/data-analysis-example-a.md
@@ -0,0 +1,43 @@
+---
+id: 5e9a093a74c4063ca6f7c14d
+title: Data Analysis Example A
+challengeType: 11
+videoId: nVAaxZ34khk
+dashedName: data-analysis-example-a
+---
+
+# --description--
+
+*Instead of using notebooks.ai like it shows in the video, you can use Google Colab instead.*
+
+More resources:
+
+- [Notebooks on GitHub](https://github.com/ine-rmotr-curriculum/FreeCodeCamp-Pandas-Real-Life-Example)
+- [How to open Notebooks from GitHub using Google Colab.](https://colab.research.google.com/github/googlecolab/colabtools/blob/master/notebooks/colab-github-demo.ipynb)
+
+# --question--
+
+## --text--
+
+What does the shape of our dataframe tell us?
+
+## --answers--
+
+The size in gigabytes the dataframe we loaded into memory is.
+
+---
+
+How many rows and columns our dataframe has.
+
+---
+
+How many rows the source data had before loading.
+
+---
+
+How many columns the source data had before loading.
+
+## --video-solution--
+
+2
+
diff --git a/curriculum/challenges/chinese-traditional/08-data-analysis-with-python/data-analysis-with-python-course/data-analysis-example-b.md b/curriculum/challenges/chinese-traditional/08-data-analysis-with-python/data-analysis-with-python-course/data-analysis-example-b.md
new file mode 100644
index 0000000000..67f603c129
--- /dev/null
+++ b/curriculum/challenges/chinese-traditional/08-data-analysis-with-python/data-analysis-with-python-course/data-analysis-example-b.md
@@ -0,0 +1,39 @@
+---
+id: 5e9a093a74c4063ca6f7c14e
+title: Data Analysis Example B
+challengeType: 11
+videoId: 0kJz0q0pvgQ
+dashedName: data-analysis-example-b
+---
+
+# --description--
+
+*Instead of using notebooks.ai like it shows in the video, you can use Google Colab instead.*
+
+More resources:
+
+- [Notebooks on GitHub](https://github.com/ine-rmotr-curriculum/FreeCodeCamp-Pandas-Real-Life-Example)
+- [How to open Notebooks from GitHub using Google Colab.](https://colab.research.google.com/github/googlecolab/colabtools/blob/master/notebooks/colab-github-demo.ipynb)
+
+# --question--
+
+## --text--
+
+What does the `loc` method allow you to do?
+
+## --answers--
+
+Retrieve a subset of rows and columns by supplying integer-location arguments.
+
+---
+
+Access a group of rows and columns by supplying label(s) arguments.
+
+---
+
+Returns the first `n` rows based on the integer argument supplied.
+
+## --video-solution--
+
+2
+
diff --git a/curriculum/challenges/chinese-traditional/08-data-analysis-with-python/data-analysis-with-python-course/data-cleaning-and-visualizations.md b/curriculum/challenges/chinese-traditional/08-data-analysis-with-python/data-analysis-with-python-course/data-cleaning-and-visualizations.md
new file mode 100644
index 0000000000..13cd90fb08
--- /dev/null
+++ b/curriculum/challenges/chinese-traditional/08-data-analysis-with-python/data-analysis-with-python-course/data-cleaning-and-visualizations.md
@@ -0,0 +1,43 @@
+---
+id: 5e9a093a74c4063ca6f7c160
+title: Data Cleaning and Visualizations
+challengeType: 11
+videoId: mHjxzFS5_Z0
+dashedName: data-cleaning-and-visualizations
+---
+
+# --description--
+
+*Instead of using notebooks.ai like it shows in the video, you can use Google Colab instead.*
+
+More resources:
+
+- [Notebooks on GitHub](https://github.com/ine-rmotr-curriculum/data-cleaning-rmotr-freecodecamp)
+- [How to open Notebooks from GitHub using Google Colab.](https://colab.research.google.com/github/googlecolab/colabtools/blob/master/notebooks/colab-github-demo.ipynb)
+
+# --question--
+
+## --text--
+
+When using Matplotlib's global API, what does the order of numbers mean here?
+
+```py
+plt.subplot(1, 2, 1)
+```
+
+## --answers--
+
+My figure will have one column, two rows, and I am going to start drawing in the first (left) plot.
+
+---
+
+I am going to start drawing in the first (left) plot, my figure will have two rows, and my figure will have one column.
+
+---
+
+My figure will have one row, two columns, and I am going to start drawing in the first (left) plot.
+
+## --video-solution--
+
+3
+
diff --git a/curriculum/challenges/chinese-traditional/08-data-analysis-with-python/data-analysis-with-python-course/data-cleaning-duplicates.md b/curriculum/challenges/chinese-traditional/08-data-analysis-with-python/data-analysis-with-python-course/data-cleaning-duplicates.md
new file mode 100644
index 0000000000..cf159cead1
--- /dev/null
+++ b/curriculum/challenges/chinese-traditional/08-data-analysis-with-python/data-analysis-with-python-course/data-cleaning-duplicates.md
@@ -0,0 +1,39 @@
+---
+id: 5e9a093a74c4063ca6f7c15f
+title: Data Cleaning Duplicates
+challengeType: 11
+videoId: kj7QqjXhH6A
+dashedName: data-cleaning-duplicates
+---
+
+# --description--
+
+*Instead of using notebooks.ai like it shows in the video, you can use Google Colab instead.*
+
+More resources:
+
+- [Notebooks on GitHub](https://github.com/ine-rmotr-curriculum/data-cleaning-rmotr-freecodecamp)
+- [How to open Notebooks from GitHub using Google Colab.](https://colab.research.google.com/github/googlecolab/colabtools/blob/master/notebooks/colab-github-demo.ipynb)
+
+# --question--
+
+## --text--
+
+The Python method `.duplicated()` returns a boolean Series for your DataFrame. `True` is the return value for rows that:
+
+## --answers--
+
+contain a duplicate, where the value for the row contains the first occurrence of that value.
+
+---
+
+contain a duplicate, where the value for the row is at least the second occurrence of that value.
+
+---
+
+contain a duplicate, where the value for the row contains either the first or second occurrence.
+
+## --video-solution--
+
+2
+
diff --git a/curriculum/challenges/chinese-traditional/08-data-analysis-with-python/data-analysis-with-python-course/data-cleaning-introduction.md b/curriculum/challenges/chinese-traditional/08-data-analysis-with-python/data-analysis-with-python-course/data-cleaning-introduction.md
new file mode 100644
index 0000000000..575be65eef
--- /dev/null
+++ b/curriculum/challenges/chinese-traditional/08-data-analysis-with-python/data-analysis-with-python-course/data-cleaning-introduction.md
@@ -0,0 +1,58 @@
+---
+id: 5e9a093a74c4063ca6f7c15d
+title: Data Cleaning Introduction
+challengeType: 11
+videoId: ovYNhnltVxY
+dashedName: data-cleaning-introduction
+---
+
+# --description--
+
+*Instead of using notebooks.ai like it shows in the video, you can use Google Colab instead.*
+
+More resources:
+
+- [Notebooks on GitHub](https://github.com/ine-rmotr-curriculum/data-cleaning-rmotr-freecodecamp)
+- [How to open Notebooks from GitHub using Google Colab.](https://colab.research.google.com/github/googlecolab/colabtools/blob/master/notebooks/colab-github-demo.ipynb)
+
+# --question--
+
+## --text--
+
+What will the following code print out?
+
+```py
+import pandas as pd
+import numpy as np
+
+s = pd.Series(['a', 3, np.nan, 1, np.nan])
+
+print(s.notnull().sum())
+```
+
+## --answers--
+
+3
+
+---
+
+
0 True
+1 True
+2 False
+3 True
+4 False
+dtype: bool
+
+---
+
+
0 False
+1 False
+2 True
+3 False
+4 True
+dtype: bool
+
+## --video-solution--
+
+1
+
diff --git a/curriculum/challenges/chinese-traditional/08-data-analysis-with-python/data-analysis-with-python-course/data-cleaning-with-dataframes.md b/curriculum/challenges/chinese-traditional/08-data-analysis-with-python/data-analysis-with-python-course/data-cleaning-with-dataframes.md
new file mode 100644
index 0000000000..887af41702
--- /dev/null
+++ b/curriculum/challenges/chinese-traditional/08-data-analysis-with-python/data-analysis-with-python-course/data-cleaning-with-dataframes.md
@@ -0,0 +1,70 @@
+---
+id: 5e9a093a74c4063ca6f7c15e
+title: Data Cleaning with DataFrames
+challengeType: 11
+videoId: sTMN_pdI6S0
+dashedName: data-cleaning-with-dataframes
+---
+
+# --description--
+
+*Instead of using notebooks.ai like it shows in the video, you can use Google Colab instead.*
+
+More resources:
+
+- [Notebooks on GitHub](https://github.com/ine-rmotr-curriculum/data-cleaning-rmotr-freecodecamp)
+- [How to open Notebooks from GitHub using Google Colab.](https://colab.research.google.com/github/googlecolab/colabtools/blob/master/notebooks/colab-github-demo.ipynb)
+
+# --question--
+
+## --text--
+
+What will the following code print out?
+
+```py
+import pandas as pd
+import numpy as np
+
+s = pd.Series([np.nan, 1, 2, np.nan, 3])
+s = s.fillna(method='ffill')
+
+print(s)
+```
+
+## --answers--
+
+
+0 1.0
+1 1.0
+2 2.0
+3 3.0
+4 3.0
+dtype: float64
+
+
+---
+
+
+0 NaN
+1 1.0
+2 2.0
+3 2.0
+4 3.0
+dtype: float64
+
+
+---
+
+
+0 NaN
+1 1.0
+2 2.0
+3 NaN
+4 3.0
+dtype: float64
+
+
+## --video-solution--
+
+2
+
diff --git a/curriculum/challenges/chinese-traditional/08-data-analysis-with-python/data-analysis-with-python-course/how-to-use-jupyter-notebooks-intro.md b/curriculum/challenges/chinese-traditional/08-data-analysis-with-python/data-analysis-with-python-course/how-to-use-jupyter-notebooks-intro.md
new file mode 100644
index 0000000000..4ac7363d87
--- /dev/null
+++ b/curriculum/challenges/chinese-traditional/08-data-analysis-with-python/data-analysis-with-python-course/how-to-use-jupyter-notebooks-intro.md
@@ -0,0 +1,40 @@
+---
+id: 5e9a093a74c4063ca6f7c14f
+title: How to use Jupyter Notebooks Intro
+challengeType: 11
+videoId: h8caJq2Bb9w
+dashedName: how-to-use-jupyter-notebooks-intro
+---
+
+# --description--
+
+*Instead of using notebooks.ai like it shows in the video, you can use Google Colab instead.*
+
+More resources:
+
+- [Notebooks on GitHub](https://github.com/ine-rmotr-curriculum/ds-content-interactive-jupyterlab-tutorial)
+- [How to open Notebooks from GitHub using Google Colab.](https://colab.research.google.com/github/googlecolab/colabtools/blob/master/notebooks/colab-github-demo.ipynb)
+- [Twitter Cheat Sheet](https://twitter.com/rmotr_com/status/1122176794696847361)
+
+# --question--
+
+## --text--
+
+What is **not** allowed in a Jupyter Notebook's cell?
+
+## --answers--
+
+Markdown
+
+---
+
+Python code
+
+---
+
+An Excel sheet
+
+## --video-solution--
+
+3
+
diff --git a/curriculum/challenges/chinese-traditional/08-data-analysis-with-python/data-analysis-with-python-course/introduction-to-data-analysis.md b/curriculum/challenges/chinese-traditional/08-data-analysis-with-python/data-analysis-with-python-course/introduction-to-data-analysis.md
new file mode 100644
index 0000000000..a092ad93a8
--- /dev/null
+++ b/curriculum/challenges/chinese-traditional/08-data-analysis-with-python/data-analysis-with-python-course/introduction-to-data-analysis.md
@@ -0,0 +1,40 @@
+---
+id: 5e9a093a74c4063ca6f7c14c
+title: Introduction to Data Analysis
+challengeType: 11
+videoId: VJrP2FUzKP0
+dashedName: introduction-to-data-analysis
+---
+
+# --description--
+
+More resources:
+
+\- [Slides](https://docs.google.com/presentation/d/1fDpjlyMiOMJyuc7_jMekcYLPP2XlSl1eWw9F7yE7byk)
+
+# --question--
+
+## --text--
+
+Why should you choose R over Python for data analysis?
+
+## --answers--
+
+It's simple to learn.
+
+---
+
+It's better at dealing with advanced statistical methods.
+
+---
+
+There are many powerful libraries that support R.
+
+---
+
+It's free and open source.
+
+## --video-solution--
+
+2
+
diff --git a/curriculum/challenges/chinese-traditional/08-data-analysis-with-python/data-analysis-with-python-course/jupyter-notebooks-cells.md b/curriculum/challenges/chinese-traditional/08-data-analysis-with-python/data-analysis-with-python-course/jupyter-notebooks-cells.md
new file mode 100644
index 0000000000..137b9f53bd
--- /dev/null
+++ b/curriculum/challenges/chinese-traditional/08-data-analysis-with-python/data-analysis-with-python-course/jupyter-notebooks-cells.md
@@ -0,0 +1,39 @@
+---
+id: 5e9a093a74c4063ca6f7c150
+title: Jupyter Notebooks Cells
+challengeType: 11
+videoId: 5PPegAs9aLA
+dashedName: jupyter-notebooks-cells
+---
+
+# --description--
+
+*Instead of using notebooks.ai like it shows in the video, you can use Google Colab instead.*
+
+More resources:
+
+- [Notebooks on GitHub](https://github.com/ine-rmotr-curriculum/ds-content-interactive-jupyterlab-tutorial)
+- [How to open Notebooks from GitHub using Google Colab.](https://colab.research.google.com/github/googlecolab/colabtools/blob/master/notebooks/colab-github-demo.ipynb)
+- [Twitter Cheat Sheet](https://twitter.com/rmotr_com/status/1122176794696847361)
+
+# --question--
+
+## --text--
+
+What are the three main types of Jupyter Notebook Cell?
+
+## --answers--
+
+Code, Markdown, and Python
+
+---
+
+Code, Markdown, and Raw
+
+---
+
+Markdown, Python, and Raw
+
+## --video-solution--
+
+2
diff --git a/curriculum/challenges/chinese-traditional/08-data-analysis-with-python/data-analysis-with-python-course/jupyter-notebooks-importing-and-exporting-data.md b/curriculum/challenges/chinese-traditional/08-data-analysis-with-python/data-analysis-with-python-course/jupyter-notebooks-importing-and-exporting-data.md
new file mode 100644
index 0000000000..104edcae19
--- /dev/null
+++ b/curriculum/challenges/chinese-traditional/08-data-analysis-with-python/data-analysis-with-python-course/jupyter-notebooks-importing-and-exporting-data.md
@@ -0,0 +1,48 @@
+---
+id: 5e9a093a74c4063ca6f7c151
+title: Jupyter Notebooks Importing and Exporting Data
+challengeType: 11
+videoId: k1msxD3JIxE
+dashedName: jupyter-notebooks-importing-and-exporting-data
+---
+
+# --description--
+
+*Instead of using notebooks.ai like it shows in the video, you can use Google Colab instead.*
+
+More resources:
+
+- [Notebooks on GitHub](https://github.com/ine-rmotr-curriculum/ds-content-interactive-jupyterlab-tutorial)
+- [How to open Notebooks from GitHub using Google Colab.](https://colab.research.google.com/github/googlecolab/colabtools/blob/master/notebooks/colab-github-demo.ipynb)
+- [Twitter Cheat Sheet](https://twitter.com/rmotr_com/status/1122176794696847361)
+
+# --question--
+
+## --text--
+
+What kind of data can you import and work with in a Jupyter Notebook?
+
+## --answers--
+
+Excel files.
+
+---
+
+CSV files.
+
+---
+
+XML files.
+
+---
+
+Data from an API.
+
+---
+
+All of the above.
+
+## --video-solution--
+
+5
+
diff --git a/curriculum/challenges/chinese-traditional/08-data-analysis-with-python/data-analysis-with-python-course/numpy-algebra-and-size.md b/curriculum/challenges/chinese-traditional/08-data-analysis-with-python/data-analysis-with-python-course/numpy-algebra-and-size.md
new file mode 100644
index 0000000000..454f7c5646
--- /dev/null
+++ b/curriculum/challenges/chinese-traditional/08-data-analysis-with-python/data-analysis-with-python-course/numpy-algebra-and-size.md
@@ -0,0 +1,43 @@
+---
+id: 5e9a093a74c4063ca6f7c157
+title: Numpy Algebra and Size
+challengeType: 11
+videoId: XAT97YLOKD8
+dashedName: numpy-algebra-and-size
+---
+
+# --description--
+
+*Instead of using notebooks.ai like it shows in the video, you can use Google Colab instead.*
+
+More resources:
+
+- [Notebooks on GitHub](https://github.com/ine-rmotr-curriculum/freecodecamp-intro-to-numpy)
+- [How to open Notebooks from GitHub using Google Colab.](https://colab.research.google.com/github/googlecolab/colabtools/blob/master/notebooks/colab-github-demo.ipynb)
+
+# --question--
+
+## --text--
+
+What is the relationship between size of objects (such as lists and datatypes) in memory in Python's standard library and the NumPy library? Knowing this, what are the implications for performance?
+
+## --answers--
+
+Standard Python objects take up much more memory to store than NumPy objects; operations on comparable standard Python and NumPy objects complete in roughly the same time.
+
+---
+
+NumPy objects take up much more memory than standard Python objects; operations on NumPy objects complete very quickly compared to comparable objects in standard Python.
+
+---
+
+NumPy objects take up much less memory than Standard Python objects; operations on Standard Python objects complete very quickly compared to comparable objects on NumPy Object.
+
+---
+
+Standard Python objects take up more memory than NumPy objects; operations on NumPy objects complete very quickly compared to comparable objects in standard Python.
+
+## --video-solution--
+
+4
+
diff --git a/curriculum/challenges/chinese-traditional/08-data-analysis-with-python/data-analysis-with-python-course/numpy-arrays.md b/curriculum/challenges/chinese-traditional/08-data-analysis-with-python/data-analysis-with-python-course/numpy-arrays.md
new file mode 100644
index 0000000000..b5504e836e
--- /dev/null
+++ b/curriculum/challenges/chinese-traditional/08-data-analysis-with-python/data-analysis-with-python-course/numpy-arrays.md
@@ -0,0 +1,59 @@
+---
+id: 5e9a093a74c4063ca6f7c154
+title: Numpy Arrays
+challengeType: 11
+videoId: VDYVFHBL1AM
+dashedName: numpy-arrays
+---
+
+# --description--
+
+*Instead of using notebooks.ai like it shows in the video, you can use Google Colab instead.*
+
+More resources:
+
+- [Notebooks on GitHub](https://github.com/ine-rmotr-curriculum/freecodecamp-intro-to-numpy)
+- [How to open Notebooks from GitHub using Google Colab.](https://colab.research.google.com/github/googlecolab/colabtools/blob/master/notebooks/colab-github-demo.ipynb)
+
+# --question--
+
+## --text--
+
+What will the following code print out?
+
+```py
+A = np.array([
+ ['a', 'b', 'c'],
+ ['d', 'e', 'f'],
+ ['g', 'h', 'i']
+])
+
+print(A[:, :2])
+```
+
+## --answers--
+
+```py
+[['a' 'b']]
+```
+
+---
+
+```py
+[['b' 'c']
+['e' 'f']
+['h' 'i']]
+```
+
+---
+
+```py
+[['a' 'b']
+['d' 'e']
+['g' 'h']]
+```
+
+## --video-solution--
+
+3
+
diff --git a/curriculum/challenges/chinese-traditional/08-data-analysis-with-python/data-analysis-with-python-course/numpy-boolean-arrays.md b/curriculum/challenges/chinese-traditional/08-data-analysis-with-python/data-analysis-with-python-course/numpy-boolean-arrays.md
new file mode 100644
index 0000000000..f271dff4b0
--- /dev/null
+++ b/curriculum/challenges/chinese-traditional/08-data-analysis-with-python/data-analysis-with-python-course/numpy-boolean-arrays.md
@@ -0,0 +1,57 @@
+---
+id: 5e9a093a74c4063ca6f7c156
+title: Numpy Boolean Arrays
+challengeType: 11
+videoId: N1ttsMmcVMM
+dashedName: numpy-boolean-arrays
+---
+
+# --description--
+
+*Instead of using notebooks.ai like it shows in the video, you can use Google Colab instead.*
+
+More resources:
+
+- [Notebooks on GitHub](https://github.com/ine-rmotr-curriculum/freecodecamp-intro-to-numpy)
+- [How to open Notebooks from GitHub using Google Colab.](https://colab.research.google.com/github/googlecolab/colabtools/blob/master/notebooks/colab-github-demo.ipynb)
+
+# --question--
+
+## --text--
+
+What will the following code print out?
+
+```py
+a = np.arange(5)
+
+print(a <= 3)
+```
+
+## --answers--
+
+```python
+[False, False, False, False, True]
+```
+
+---
+
+```python
+[5]
+```
+
+---
+
+```python
+[0, 1, 2, 3]
+```
+
+---
+
+```python
+[True, True, True, True, False]
+```
+
+## --video-solution--
+
+4
+
diff --git a/curriculum/challenges/chinese-traditional/08-data-analysis-with-python/data-analysis-with-python-course/numpy-introduction-a.md b/curriculum/challenges/chinese-traditional/08-data-analysis-with-python/data-analysis-with-python-course/numpy-introduction-a.md
new file mode 100644
index 0000000000..fc38697f40
--- /dev/null
+++ b/curriculum/challenges/chinese-traditional/08-data-analysis-with-python/data-analysis-with-python-course/numpy-introduction-a.md
@@ -0,0 +1,39 @@
+---
+id: 5e9a093a74c4063ca6f7c152
+title: Numpy Introduction A
+challengeType: 11
+videoId: P-JjV6GBCmk
+dashedName: numpy-introduction-a
+---
+
+# --description--
+
+*Instead of using notebooks.ai like it shows in the video, you can use Google Colab instead.*
+
+More resources:
+
+- [Notebooks on GitHub](https://github.com/ine-rmotr-curriculum/freecodecamp-intro-to-numpy)
+- [How to open Notebooks from GitHub using Google Colab.](https://colab.research.google.com/github/googlecolab/colabtools/blob/master/notebooks/colab-github-demo.ipynb)
+
+# --question--
+
+## --text--
+
+Why is Numpy an important, but unpopular Python library?
+
+## --answers--
+
+Often you won't work directly with Numpy.
+
+---
+
+It is extremely slow.
+
+---
+
+Working with Numpy is difficult.
+
+## --video-solution--
+
+1
+
diff --git a/curriculum/challenges/chinese-traditional/08-data-analysis-with-python/data-analysis-with-python-course/numpy-introduction-b.md b/curriculum/challenges/chinese-traditional/08-data-analysis-with-python/data-analysis-with-python-course/numpy-introduction-b.md
new file mode 100644
index 0000000000..a863e42074
--- /dev/null
+++ b/curriculum/challenges/chinese-traditional/08-data-analysis-with-python/data-analysis-with-python-course/numpy-introduction-b.md
@@ -0,0 +1,43 @@
+---
+id: 5e9a093a74c4063ca6f7c153
+title: Numpy Introduction B
+challengeType: 11
+videoId: YIqgrNLAZkA
+dashedName: numpy-introduction-b
+---
+
+# --description--
+
+*Instead of using notebooks.ai like it shows in the video, you can use Google Colab instead.*
+
+More resources:
+
+- [Notebooks on GitHub](https://github.com/ine-rmotr-curriculum/freecodecamp-intro-to-numpy)
+- [How to open Notebooks from GitHub using Google Colab.](https://colab.research.google.com/github/googlecolab/colabtools/blob/master/notebooks/colab-github-demo.ipynb)
+
+# --question--
+
+## --text--
+
+About how much memory does the integer `5` consume in plain Python?
+
+## --answers--
+
+32 bits
+
+---
+
+20 bytes
+
+---
+
+16 bytes
+
+---
+
+8 bits
+
+## --video-solution--
+
+2
+
diff --git a/curriculum/challenges/chinese-traditional/08-data-analysis-with-python/data-analysis-with-python-course/numpy-operations.md b/curriculum/challenges/chinese-traditional/08-data-analysis-with-python/data-analysis-with-python-course/numpy-operations.md
new file mode 100644
index 0000000000..0072c84019
--- /dev/null
+++ b/curriculum/challenges/chinese-traditional/08-data-analysis-with-python/data-analysis-with-python-course/numpy-operations.md
@@ -0,0 +1,50 @@
+---
+id: 5e9a093a74c4063ca6f7c155
+title: Numpy Operations
+challengeType: 11
+videoId: eqSVcJbaPdk
+dashedName: numpy-operations
+---
+
+# --description--
+
+*Instead of using notebooks.ai like it shows in the video, you can use Google Colab instead.*
+
+More resources:
+
+- [Notebooks on GitHub](https://github.com/ine-rmotr-curriculum/freecodecamp-intro-to-numpy)
+- [How to open Notebooks from GitHub using Google Colab.](https://colab.research.google.com/github/googlecolab/colabtools/blob/master/notebooks/colab-github-demo.ipynb)
+
+# --question--
+
+## --text--
+
+What is the value of `a` after you run the following code?
+
+```py
+a = np.arange(5)
+a + 20
+```
+
+## --answers--
+
+```python
+[20, 21, 22, 24, 24]
+```
+
+---
+
+```python
+[0, 1, 2, 3, 4]
+```
+
+---
+
+```python
+[25, 26, 27, 28, 29]
+```
+
+## --video-solution--
+
+2
+
diff --git a/curriculum/challenges/chinese-traditional/08-data-analysis-with-python/data-analysis-with-python-course/pandas-conditional-selection-and-modifying-dataframes.md b/curriculum/challenges/chinese-traditional/08-data-analysis-with-python/data-analysis-with-python-course/pandas-conditional-selection-and-modifying-dataframes.md
new file mode 100644
index 0000000000..3d17a73a55
--- /dev/null
+++ b/curriculum/challenges/chinese-traditional/08-data-analysis-with-python/data-analysis-with-python-course/pandas-conditional-selection-and-modifying-dataframes.md
@@ -0,0 +1,73 @@
+---
+id: 5e9a093a74c4063ca6f7c15b
+title: Pandas Conditional Selection and Modifying DataFrames
+challengeType: 11
+videoId: BFlH0fN5xRQ
+dashedName: pandas-conditional-selection-and-modifying-dataframes
+---
+
+# --description--
+
+*Instead of using notebooks.ai like it shows in the video, you can use Google Colab instead.*
+
+More resources:
+
+- [Notebooks on GitHub](https://github.com/ine-rmotr-curriculum/freecodecamp-intro-to-pandas)
+- [How to open Notebooks from GitHub using Google Colab.](https://colab.research.google.com/github/googlecolab/colabtools/blob/master/notebooks/colab-github-demo.ipynb)
+
+# --question--
+
+## --text--
+
+What will the following code print out?
+
+```py
+import pandas as pd
+
+certificates_earned = pd.DataFrame({
+ 'Certificates': [8, 2, 5, 6],
+ 'Time (in months)': [16, 5, 9, 12]
+})
+names = ['Tom', 'Kris', 'Ahmad', 'Beau']
+
+certificates_earned.index = names
+longest_streak = pd.Series([13, 11, 9, 7], index=names)
+certificates_earned['Longest streak'] = longest_streak
+
+print(certificates_earned)
+```
+
+## --answers--
+
+
+Tom 13
+Kris 11
+Ahmad 9
+Beau 7
+Name: Longest streak, dtype: int64
+
+
+---
+
+
+ Certificates Time (in months) Longest streak
+Tom 8 16 13
+Kris 2 5 11
+Ahmad 5 9 9
+Beau 6 12 7
+
+
+---
+
+
+ Certificates Longest streak
+Tom 8 13
+Kris 2 11
+Ahmad 5 9
+Beau 6 7
+
+
+## --video-solution--
+
+2
+
diff --git a/curriculum/challenges/chinese-traditional/08-data-analysis-with-python/data-analysis-with-python-course/pandas-creating-columns.md b/curriculum/challenges/chinese-traditional/08-data-analysis-with-python/data-analysis-with-python-course/pandas-creating-columns.md
new file mode 100644
index 0000000000..7d20f76c4a
--- /dev/null
+++ b/curriculum/challenges/chinese-traditional/08-data-analysis-with-python/data-analysis-with-python-course/pandas-creating-columns.md
@@ -0,0 +1,58 @@
+---
+id: 5e9a093a74c4063ca6f7c15c
+title: Pandas Creating Columns
+challengeType: 11
+videoId: _sSo2XZoB3E
+dashedName: pandas-creating-columns
+---
+
+# --description--
+
+*Instead of using notebooks.ai like it shows in the video, you can use Google Colab instead.*
+
+More resources:
+
+- [Notebooks on GitHub](https://github.com/ine-rmotr-curriculum/freecodecamp-intro-to-pandas)
+- [How to open Notebooks from GitHub using Google Colab.](https://colab.research.google.com/github/googlecolab/colabtools/blob/master/notebooks/colab-github-demo.ipynb)
+
+# --question--
+
+## --text--
+
+What code would add a "Certificates per month" column to the `certificates_earned` DataFrame like the one below?
+
+
Certificates Time (in months) Certificates per month
+Tom 8 16 0.50
+Kris 2 5 0.40
+Ahmad 5 9 0.56
+Beau 6 12 0.50
+
+## --answers--
+
+```py
+certificates_earned['Certificates'] /
+certificates_earned['Time (in months)']
+```
+
+---
+
+```py
+certificates_earned['Certificates per month'] = round(
+ certificates_earned['Certificates'] /
+ certificates_earned['Time (in months)']
+)
+```
+
+---
+
+```py
+certificates_earned['Certificates per month'] = round(
+ certificates_earned['Certificates'] /
+ certificates_earned['Time (in months)'], 2
+)
+```
+
+## --video-solution--
+
+3
+
diff --git a/curriculum/challenges/chinese-traditional/08-data-analysis-with-python/data-analysis-with-python-course/pandas-dataframes.md b/curriculum/challenges/chinese-traditional/08-data-analysis-with-python/data-analysis-with-python-course/pandas-dataframes.md
new file mode 100644
index 0000000000..fd5eedb176
--- /dev/null
+++ b/curriculum/challenges/chinese-traditional/08-data-analysis-with-python/data-analysis-with-python-course/pandas-dataframes.md
@@ -0,0 +1,66 @@
+---
+id: 5e9a093a74c4063ca6f7c15a
+title: Pandas DataFrames
+challengeType: 11
+videoId: 7SgFBYXaiH0
+dashedName: pandas-dataframes
+---
+
+# --description--
+
+*Instead of using notebooks.ai like it shows in the video, you can use Google Colab instead.*
+
+More resources:
+
+- [Notebooks on GitHub](https://github.com/ine-rmotr-curriculum/freecodecamp-intro-to-pandas)
+- [How to open Notebooks from GitHub using Google Colab.](https://colab.research.google.com/github/googlecolab/colabtools/blob/master/notebooks/colab-github-demo.ipynb)
+
+# --question--
+
+## --text--
+
+What will the following code print out?
+
+```py
+import pandas as pd
+
+certificates_earned = pd.DataFrame({
+ 'Certificates': [8, 2, 5, 6],
+ 'Time (in months)': [16, 5, 9, 12]
+})
+
+certificates_earned.index = ['Tom', 'Kris', 'Ahmad', 'Beau']
+
+print(certificates_earned.iloc[2])
+```
+
+## --answers--
+
+
+Tom 16
+Kris 5
+Ahmad 9
+Beau 12
+Name: Time (in months), dtype: int64
+
+
+---
+
+
+Certificates 6
+Time (in months) 12
+Name: Beau, dtype: int64
+
+
+---
+
+
+Certificates 5
+Time (in months) 9
+Name: Ahmad, dtype: int64
+
+
+## --video-solution--
+
+3
+
diff --git a/curriculum/challenges/chinese-traditional/08-data-analysis-with-python/data-analysis-with-python-course/pandas-indexing-and-conditional-selection.md b/curriculum/challenges/chinese-traditional/08-data-analysis-with-python/data-analysis-with-python-course/pandas-indexing-and-conditional-selection.md
new file mode 100644
index 0000000000..1107f217f4
--- /dev/null
+++ b/curriculum/challenges/chinese-traditional/08-data-analysis-with-python/data-analysis-with-python-course/pandas-indexing-and-conditional-selection.md
@@ -0,0 +1,65 @@
+---
+id: 5e9a093a74c4063ca6f7c159
+title: Pandas Indexing and Conditional Selection
+challengeType: 11
+videoId: '-ZOrgV_aA9A'
+dashedName: pandas-indexing-and-conditional-selection
+---
+
+# --description--
+
+*Instead of using notebooks.ai like it shows in the video, you can use Google Colab instead.*
+
+More resources:
+
+- [Notebooks on GitHub](https://github.com/ine-rmotr-curriculum/freecodecamp-intro-to-pandas)
+- [How to open Notebooks from GitHub using Google Colab.](https://colab.research.google.com/github/googlecolab/colabtools/blob/master/notebooks/colab-github-demo.ipynb)
+
+# --question--
+
+## --text--
+
+What will the following code print out?
+
+```py
+import pandas as pd
+
+certificates_earned = pd.Series(
+ [8, 2, 5, 6],
+ index=['Tom', 'Kris', 'Ahmad', 'Beau']
+)
+
+print(certificates_earned[certificates_earned > 5])
+```
+
+## --answers--
+
+
+Tom True
+Kris False
+Ahmad False
+Beau True
+dtype: int64
+
+
+---
+
+
+Tom 8
+Ahmad 5
+Beau 6
+dtype: int64
+
+
+---
+
+
+Tom 8
+Beau 6
+dtype: int64
+
+
+## --video-solution--
+
+3
+
diff --git a/curriculum/challenges/chinese-traditional/08-data-analysis-with-python/data-analysis-with-python-course/pandas-introduction.md b/curriculum/challenges/chinese-traditional/08-data-analysis-with-python/data-analysis-with-python-course/pandas-introduction.md
new file mode 100644
index 0000000000..45a30bc050
--- /dev/null
+++ b/curriculum/challenges/chinese-traditional/08-data-analysis-with-python/data-analysis-with-python-course/pandas-introduction.md
@@ -0,0 +1,68 @@
+---
+id: 5e9a093a74c4063ca6f7c158
+title: Pandas Introduction
+challengeType: 11
+videoId: 0xACW-8cZU0
+dashedName: pandas-introduction
+---
+
+# --description--
+
+*Instead of using notebooks.ai like it shows in the video, you can use Google Colab instead.*
+
+More resources:
+
+- [Notebooks on GitHub](https://github.com/ine-rmotr-curriculum/freecodecamp-intro-to-pandas)
+- [How to open Notebooks from GitHub using Google Colab.](https://colab.research.google.com/github/googlecolab/colabtools/blob/master/notebooks/colab-github-demo.ipynb)
+
+# --question--
+
+## --text--
+
+What will the following code print out?
+
+```py
+import pandas as pd
+
+certificates_earned = pd.Series(
+ [8, 2, 5, 6],
+ index=['Tom', 'Kris', 'Ahmad', 'Beau']
+)
+
+print(certificates_earned)
+```
+
+## --answers--
+
+```
+Tom 8
+Kris 2
+Ahmad 5
+Beau 6
+dtype: int64
+```
+
+---
+
+```
+Kris 2
+Ahmad 5
+Beau 6
+Tom 8
+dtype: int64
+```
+
+---
+
+```
+Tom 8
+Kris 2
+Ahmad 5
+Beau 6
+Name: certificates_earned dtype: int64
+```
+
+## --video-solution--
+
+1
+
diff --git a/curriculum/challenges/chinese-traditional/08-data-analysis-with-python/data-analysis-with-python-course/parsing-html-and-saving-data.md b/curriculum/challenges/chinese-traditional/08-data-analysis-with-python/data-analysis-with-python-course/parsing-html-and-saving-data.md
new file mode 100644
index 0000000000..284a5eabab
--- /dev/null
+++ b/curriculum/challenges/chinese-traditional/08-data-analysis-with-python/data-analysis-with-python-course/parsing-html-and-saving-data.md
@@ -0,0 +1,43 @@
+---
+id: 5e9a093a74c4063ca6f7c164
+title: Parsing HTML and Saving Data
+challengeType: 11
+videoId: bJaqnTWQmb0
+dashedName: parsing-html-and-saving-data
+---
+
+# --description--
+
+*Instead of using notebooks.ai like it shows in the video, you can use Google Colab instead.*
+
+More resources:
+
+- [Notebooks on GitHub](https://github.com/ine-rmotr-curriculum/RDP-Reading-Data-with-Python-and-Pandas)
+- [How to open Notebooks from GitHub using Google Colab.](https://colab.research.google.com/github/googlecolab/colabtools/blob/master/notebooks/colab-github-demo.ipynb)
+
+# --question--
+
+## --text--
+
+What Python library has the `.read_html()` method we can we use for parsing HTML documents and extracting tables?
+
+## --answers--
+
+BeautifierSoupy
+
+---
+
+WebReader
+
+---
+
+HTTP-master
+
+---
+
+Pandas
+
+## --video-solution--
+
+4
+
diff --git a/curriculum/challenges/chinese-traditional/08-data-analysis-with-python/data-analysis-with-python-course/python-functions-and-collections.md b/curriculum/challenges/chinese-traditional/08-data-analysis-with-python/data-analysis-with-python-course/python-functions-and-collections.md
new file mode 100644
index 0000000000..75a9a71e77
--- /dev/null
+++ b/curriculum/challenges/chinese-traditional/08-data-analysis-with-python/data-analysis-with-python-course/python-functions-and-collections.md
@@ -0,0 +1,39 @@
+---
+id: 5e9a093a74c4063ca6f7c166
+title: Python Functions and Collections
+challengeType: 11
+videoId: NzpU17ZVlUw
+dashedName: python-functions-and-collections
+---
+
+# --description--
+
+*Instead of using notebooks.ai like it shows in the video, you can use Google Colab instead.*
+
+More resources:
+
+- [Notebooks on GitHub](https://github.com/ine-rmotr-curriculum/ds-content-python-under-10-minutes)
+- [How to open Notebooks from GitHub using Google Colab.](https://colab.research.google.com/github/googlecolab/colabtools/blob/master/notebooks/colab-github-demo.ipynb)
+
+# --question--
+
+## --text--
+
+What is the main difference between lists and tuples in Python?
+
+## --answers--
+
+Tuples are immutable.
+
+---
+
+Lists are ordered.
+
+---
+
+Tuples are unordered.
+
+## --video-solution--
+
+1
+
diff --git a/curriculum/challenges/chinese-traditional/08-data-analysis-with-python/data-analysis-with-python-course/python-introduction.md b/curriculum/challenges/chinese-traditional/08-data-analysis-with-python/data-analysis-with-python-course/python-introduction.md
new file mode 100644
index 0000000000..f67c83c389
--- /dev/null
+++ b/curriculum/challenges/chinese-traditional/08-data-analysis-with-python/data-analysis-with-python-course/python-introduction.md
@@ -0,0 +1,43 @@
+---
+id: 5e9a093a74c4063ca6f7c165
+title: Python Introduction
+challengeType: 11
+videoId: PrQV9JkLhb4
+dashedName: python-introduction
+---
+
+# --description--
+
+*Instead of using notebooks.ai like it shows in the video, you can use Google Colab instead.*
+
+More resources:
+
+- [Notebooks on GitHub](https://github.com/ine-rmotr-curriculum/ds-content-python-under-10-minutes)
+- [How to open Notebooks from GitHub using Google Colab.](https://colab.research.google.com/github/googlecolab/colabtools/blob/master/notebooks/colab-github-demo.ipynb)
+
+# --question--
+
+## --text--
+
+How do we define blocks of code in the body of functions in Python?
+
+## --answers--
+
+We use a set of curly braces, one on either side of each new block of our code.
+
+---
+
+We use indentation, usually right-aligned 4 spaces.
+
+---
+
+We do not denote blocks of code.
+
+---
+
+We could use curly braces or indentation to denote blocks of code.
+
+## --video-solution--
+
+2
+
diff --git a/curriculum/challenges/chinese-traditional/08-data-analysis-with-python/data-analysis-with-python-course/python-iteration-and-modules.md b/curriculum/challenges/chinese-traditional/08-data-analysis-with-python/data-analysis-with-python-course/python-iteration-and-modules.md
new file mode 100644
index 0000000000..4d4350768f
--- /dev/null
+++ b/curriculum/challenges/chinese-traditional/08-data-analysis-with-python/data-analysis-with-python-course/python-iteration-and-modules.md
@@ -0,0 +1,56 @@
+---
+id: 5e9a093a74c4063ca6f7c167
+title: Python Iteration and Modules
+challengeType: 11
+videoId: XzosGWLafrY
+dashedName: python-iteration-and-modules
+---
+
+# --description--
+
+*Instead of using notebooks.ai like it shows in the video, you can use Google Colab instead.*
+
+More resources:
+
+- [Notebooks on GitHub](https://github.com/ine-rmotr-curriculum/ds-content-python-under-10-minutes)
+- [How to open Notebooks from GitHub using Google Colab.](https://colab.research.google.com/github/googlecolab/colabtools/blob/master/notebooks/colab-github-demo.ipynb)
+
+# --question--
+
+## --text--
+
+How would you iterate over and print the keys and values of a dictionary named `user`?
+
+## --answers--
+
+```python
+for key in user.items():
+ print(key)
+```
+
+---
+
+```python
+for key, value in user.all():
+ print(key, value)
+ print(value)
+```
+
+---
+
+```python
+for key, value in user.items():
+ print(key, value)
+```
+
+---
+
+```python
+for key, value in user
+ print(key, value)
+```
+
+## --video-solution--
+
+3
+
diff --git a/curriculum/challenges/chinese-traditional/08-data-analysis-with-python/data-analysis-with-python-course/reading-data-csv-and-txt.md b/curriculum/challenges/chinese-traditional/08-data-analysis-with-python/data-analysis-with-python-course/reading-data-csv-and-txt.md
new file mode 100644
index 0000000000..18d6487a0a
--- /dev/null
+++ b/curriculum/challenges/chinese-traditional/08-data-analysis-with-python/data-analysis-with-python-course/reading-data-csv-and-txt.md
@@ -0,0 +1,55 @@
+---
+id: 5e9a093a74c4063ca6f7c162
+title: Reading Data CSV and TXT
+challengeType: 11
+videoId: ViGEv0zOzUk
+dashedName: reading-data-csv-and-txt
+---
+
+# --description--
+
+*Instead of using notebooks.ai like it shows in the video, you can use Google Colab instead.*
+
+More resources:
+
+- [Notebooks on GitHub](https://github.com/ine-rmotr-curriculum/RDP-Reading-Data-with-Python-and-Pandas)
+- [How to open Notebooks from GitHub using Google Colab.](https://colab.research.google.com/github/googlecolab/colabtools/blob/master/notebooks/colab-github-demo.ipynb)
+
+# --question--
+
+## --text--
+
+How would you import the CSV file `data.csv` and store it in a DataFrame using the Pandas module?
+
+## --answers--
+
+```python
+import pandas as pd
+df = pd.csv("data.csv")
+```
+
+---
+
+```python
+import pandas as pd
+df = pd.read_csv("data.csv")
+```
+
+---
+
+```python
+import pandas as pd
+pd.read_csv("data.csv")
+```
+
+---
+
+```python
+import pandas as pd
+df = pd.csv_reader("data.csv")
+```
+
+## --video-solution--
+
+2
+
diff --git a/curriculum/challenges/chinese-traditional/08-data-analysis-with-python/data-analysis-with-python-course/reading-data-from-databases.md b/curriculum/challenges/chinese-traditional/08-data-analysis-with-python/data-analysis-with-python-course/reading-data-from-databases.md
new file mode 100644
index 0000000000..5502a07e7a
--- /dev/null
+++ b/curriculum/challenges/chinese-traditional/08-data-analysis-with-python/data-analysis-with-python-course/reading-data-from-databases.md
@@ -0,0 +1,39 @@
+---
+id: 5e9a093a74c4063ca6f7c163
+title: Reading Data from Databases
+challengeType: 11
+videoId: MtgXS1MofRw
+dashedName: reading-data-from-databases
+---
+
+# --description--
+
+*Instead of using notebooks.ai like it shows in the video, you can use Google Colab instead.*
+
+More resources:
+
+- [Notebooks on GitHub](https://github.com/ine-rmotr-curriculum/RDP-Reading-Data-with-Python-and-Pandas)
+- [How to open Notebooks from GitHub using Google Colab.](https://colab.research.google.com/github/googlecolab/colabtools/blob/master/notebooks/colab-github-demo.ipynb)
+
+# --question--
+
+## --text--
+
+What method does a `Cursor` instance have and what does it allow?
+
+## --answers--
+
+The `Cursor` instance has a `.run()` method which allows you to run SQL queries.
+
+---
+
+The `Cursor` instance has a `.select()` method which allows you to select records.
+
+---
+
+The `Cursor` instance has an `.execute()` method which will receive SQL parameters to run against the database.
+
+## --video-solution--
+
+3
+
diff --git a/curriculum/challenges/chinese-traditional/08-data-analysis-with-python/data-analysis-with-python-course/reading-data-introduction.md b/curriculum/challenges/chinese-traditional/08-data-analysis-with-python/data-analysis-with-python-course/reading-data-introduction.md
new file mode 100644
index 0000000000..a16b2d0176
--- /dev/null
+++ b/curriculum/challenges/chinese-traditional/08-data-analysis-with-python/data-analysis-with-python-course/reading-data-introduction.md
@@ -0,0 +1,72 @@
+---
+id: 5e9a093a74c4063ca6f7c161
+title: Reading Data Introduction
+challengeType: 11
+videoId: cDnt02BcHng
+dashedName: reading-data-introduction
+---
+
+# --description--
+
+*Instead of using notebooks.ai like it shows in the video, you can use Google Colab instead.*
+
+More resources:
+
+- [Notebooks on GitHub](https://github.com/ine-rmotr-curriculum/RDP-Reading-Data-with-Python-and-Pandas)
+- [How to open Notebooks from GitHub using Google Colab.](https://colab.research.google.com/github/googlecolab/colabtools/blob/master/notebooks/colab-github-demo.ipynb)
+
+# --question--
+
+## --text--
+
+Given a file named `certificates.csv` with these contents:
+
+
+Name$Certificates$Time (in months)
+Tom$8$16
+Kris$2$5
+Ahmad$5$9
+Beau$6$12
+
+
+Fill in the blanks for the missing arguments below:
+
+```py
+import csv
+
+with open(__A__, 'r') as fp:
+ reader = csv.reader(fp, delimiter=__B__)
+ next(reader)
+ for index, values in enumerate(reader):
+ name, certs_num, months_num = values
+ print(f"{name} earned {__C__} certificates in {months_num} months")
+```
+
+## --answers--
+
+A: `'certificates.csv'`
+
+B: `'-'`
+
+C: `values`
+
+---
+
+A: `'certificates.csv'`
+
+B: `'$'`
+
+C: `certs_num`
+
+---
+
+A: `'certificates'`
+
+B: `'$'`
+
+C: `certs_num`
+
+## --video-solution--
+
+2
+
diff --git a/curriculum/challenges/chinese-traditional/08-data-analysis-with-python/data-analysis-with-python-projects/demographic-data-analyzer.md b/curriculum/challenges/chinese-traditional/08-data-analysis-with-python/data-analysis-with-python-projects/demographic-data-analyzer.md
new file mode 100644
index 0000000000..66246f3d5d
--- /dev/null
+++ b/curriculum/challenges/chinese-traditional/08-data-analysis-with-python/data-analysis-with-python-projects/demographic-data-analyzer.md
@@ -0,0 +1,32 @@
+---
+id: 5e46f7e5ac417301a38fb929
+title: Demographic Data Analyzer
+challengeType: 10
+dashedName: demographic-data-analyzer
+---
+
+# --description--
+
+In this challenge you must analyze demographic data using Pandas. You are given a dataset of demographic data that was extracted from the 1994 Census database.
+
+You can access [the full project description and starter code on Repl.it](https://repl.it/github/freeCodeCamp/boilerplate-demographic-data-analyzer).
+
+After going to that link, fork the project. Once you complete the project based on the instructions in 'README.md', submit your project link below.
+
+We are still developing the interactive instructional part of the data analysis with Python curriculum. For now, you will have to use other resources to learn how to pass this challenge.
+
+# --hints--
+
+It should pass all Python tests.
+
+```js
+
+```
+
+# --solutions--
+
+```py
+ # Python challenges don't need solutions,
+ # because they would need to be tested against a full working project.
+ # Please check our contributing guidelines to learn more.
+```
diff --git a/curriculum/challenges/chinese-traditional/08-data-analysis-with-python/data-analysis-with-python-projects/mean-variance-standard-deviation-calculator.md b/curriculum/challenges/chinese-traditional/08-data-analysis-with-python/data-analysis-with-python-projects/mean-variance-standard-deviation-calculator.md
new file mode 100644
index 0000000000..76fed744e5
--- /dev/null
+++ b/curriculum/challenges/chinese-traditional/08-data-analysis-with-python/data-analysis-with-python-projects/mean-variance-standard-deviation-calculator.md
@@ -0,0 +1,32 @@
+---
+id: 5e46f7e5ac417301a38fb928
+title: Mean-Variance-Standard Deviation Calculator
+challengeType: 10
+dashedName: mean-variance-standard-deviation-calculator
+---
+
+# --description--
+
+Create a function that uses Numpy to output the mean, variance, and standard deviation of the rows, columns, and elements in a 3 x 3 matrix.
+
+You can access [the full project description and starter code on Repl.it](https://repl.it/github/freeCodeCamp/boilerplate-mean-variance-standard-deviation-calculator).
+
+After going to that link, fork the project. Once you complete the project based on the instructions in 'README.md', submit your project link below.
+
+We are still developing the interactive instructional part of the data analysis with Python curriculum. For now, you will have to use other resources to learn how to pass this challenge.
+
+# --hints--
+
+It should pass all Python tests.
+
+```js
+
+```
+
+# --solutions--
+
+```py
+ # Python challenges don't need solutions,
+ # because they would need to be tested against a full working project.
+ # Please check our contributing guidelines to learn more.
+```
diff --git a/curriculum/challenges/chinese-traditional/08-data-analysis-with-python/data-analysis-with-python-projects/medical-data-visualizer.md b/curriculum/challenges/chinese-traditional/08-data-analysis-with-python/data-analysis-with-python-projects/medical-data-visualizer.md
new file mode 100644
index 0000000000..4d7cfc2f13
--- /dev/null
+++ b/curriculum/challenges/chinese-traditional/08-data-analysis-with-python/data-analysis-with-python-projects/medical-data-visualizer.md
@@ -0,0 +1,32 @@
+---
+id: 5e46f7f8ac417301a38fb92a
+title: Medical Data Visualizer
+challengeType: 10
+dashedName: medical-data-visualizer
+---
+
+# --description--
+
+In this project, you will visualize and make calculations from medical examination data using matplotlib, seaborn, and pandas.
+
+You can access [the full project description and starter code on Repl.it](https://repl.it/github/freeCodeCamp/boilerplate-medical-data-visualizer).
+
+After going to that link, fork the project. Once you complete the project based on the instructions in 'README.md', submit your project link below.
+
+We are still developing the interactive instructional part of the data analysis with Python curriculum. For now, you will have to use other resources to learn how to pass this challenge.
+
+# --hints--
+
+It should pass all Python tests.
+
+```js
+
+```
+
+# --solutions--
+
+```py
+ # Python challenges don't need solutions,
+ # because they would need to be tested against a full working project.
+ # Please check our contributing guidelines to learn more.
+```
diff --git a/curriculum/challenges/chinese-traditional/08-data-analysis-with-python/data-analysis-with-python-projects/page-view-time-series-visualizer.md b/curriculum/challenges/chinese-traditional/08-data-analysis-with-python/data-analysis-with-python-projects/page-view-time-series-visualizer.md
new file mode 100644
index 0000000000..b33d2eeed6
--- /dev/null
+++ b/curriculum/challenges/chinese-traditional/08-data-analysis-with-python/data-analysis-with-python-projects/page-view-time-series-visualizer.md
@@ -0,0 +1,32 @@
+---
+id: 5e46f802ac417301a38fb92b
+title: Page View Time Series Visualizer
+challengeType: 10
+dashedName: page-view-time-series-visualizer
+---
+
+# --description--
+
+For this project you will visualize time series data using a line chart, bar chart, and box plots. You will use Pandas, matplotlib, and seaborn to visualize a dataset containing the number of page views each day on the freeCodeCamp.org forum from 2016-05-09 to 2019-12-03. The data visualizations will help you understand the patterns in visits and identify yearly and monthly growth.
+
+You can access [the full project description and starter code on Repl.it](https://repl.it/github/freeCodeCamp/boilerplate-page-view-time-series-visualizer).
+
+After going to that link, fork the project. Once you complete the project based on the instructions in 'README.md', submit your project link below.
+
+We are still developing the interactive instructional part of the data analysis with Python curriculum. For now, you will have to use other resources to learn how to pass this challenge.
+
+# --hints--
+
+It should pass all Python tests.
+
+```js
+
+```
+
+# --solutions--
+
+```py
+ # Python challenges don't need solutions,
+ # because they would need to be tested against a full working project.
+ # Please check our contributing guidelines to learn more.
+```
diff --git a/curriculum/challenges/chinese-traditional/08-data-analysis-with-python/data-analysis-with-python-projects/sea-level-predictor.md b/curriculum/challenges/chinese-traditional/08-data-analysis-with-python/data-analysis-with-python-projects/sea-level-predictor.md
new file mode 100644
index 0000000000..58075caec0
--- /dev/null
+++ b/curriculum/challenges/chinese-traditional/08-data-analysis-with-python/data-analysis-with-python-projects/sea-level-predictor.md
@@ -0,0 +1,32 @@
+---
+id: 5e4f5c4b570f7e3a4949899f
+title: Sea Level Predictor
+challengeType: 10
+dashedName: sea-level-predictor
+---
+
+# --description--
+
+In this project, you will analyze a dataset of the global average sea level change since 1880. You will use the data to predict the sea level change through year 2050.
+
+You can access [the full project description and starter code on Repl.it](https://repl.it/github/freeCodeCamp/boilerplate-sea-level-predictor).
+
+After going to that link, fork the project. Once you complete the project based on the instructions in 'README.md', submit your project link below.
+
+We are still developing the interactive instructional part of the data analysis with Python curriculum. For now, you will have to use other resources to learn how to pass this challenge.
+
+# --hints--
+
+It should pass all Python tests.
+
+```js
+
+```
+
+# --solutions--
+
+```py
+ # Python challenges don't need solutions,
+ # because they would need to be tested against a full working project.
+ # Please check our contributing guidelines to learn more.
+```
diff --git a/curriculum/challenges/chinese-traditional/08-data-analysis-with-python/numpy/accessing-and-changing-elements,-rows,-columns.md b/curriculum/challenges/chinese-traditional/08-data-analysis-with-python/numpy/accessing-and-changing-elements,-rows,-columns.md
new file mode 100644
index 0000000000..3c833be5ad
--- /dev/null
+++ b/curriculum/challenges/chinese-traditional/08-data-analysis-with-python/numpy/accessing-and-changing-elements,-rows,-columns.md
@@ -0,0 +1,50 @@
+---
+id: 5e9a0a8e09c5df3cc3600ed4
+title: 'Accessing and Changing Elements, Rows, Columns'
+challengeType: 11
+videoId: v-7Y7koJ_N0
+dashedName: accessing-and-changing-elements-rows-columns
+---
+
+# --question--
+
+## --text--
+
+What code would change the values in the 3rd column of both of the following Numpy arrays to 20?
+
+```py
+a = np.array([[1, 2, 3, 4, 5], [6, 7, 8, 9, 10]])
+
+# Output:
+# [[ 1 2 20 4 5]
+# [ 6 7 20 9 10]]
+```
+
+## --answers--
+
+```python
+a[:, 3] = 20
+```
+
+---
+
+```python
+a[2, :] = 20
+```
+
+---
+
+```python
+a[:, 2] = 20
+```
+
+---
+
+```python
+a[1, 2] = 20
+```
+
+## --video-solution--
+
+3
+
diff --git a/curriculum/challenges/chinese-traditional/08-data-analysis-with-python/numpy/basics-of-numpy.md b/curriculum/challenges/chinese-traditional/08-data-analysis-with-python/numpy/basics-of-numpy.md
new file mode 100644
index 0000000000..57b1924af7
--- /dev/null
+++ b/curriculum/challenges/chinese-traditional/08-data-analysis-with-python/numpy/basics-of-numpy.md
@@ -0,0 +1,45 @@
+---
+id: 5e9a0a8e09c5df3cc3600ed3
+title: Basics of Numpy
+challengeType: 11
+videoId: f9QrZrKQMLI
+dashedName: basics-of-numpy
+---
+
+# --question--
+
+## --text--
+
+What will the following code print?
+
+```python
+b = np.array([[1.0,2.0,3.0],[3.0,4.0,5.0]])
+print(b)
+```
+
+## --answers--
+
+```python
+[[1.0 2.0 3.0]
+[3.0 4.0 5.0]]
+```
+
+---
+
+```python
+[[1. 2. 3.]
+[3. 4. 5.]]
+```
+
+---
+
+```python
+[[1. 3.]
+[2. 4.]
+[3. 5.]
+```
+
+## --video-solution--
+
+2
+
diff --git a/curriculum/challenges/chinese-traditional/08-data-analysis-with-python/numpy/copying-arrays-warning.md b/curriculum/challenges/chinese-traditional/08-data-analysis-with-python/numpy/copying-arrays-warning.md
new file mode 100644
index 0000000000..afe7b8d83e
--- /dev/null
+++ b/curriculum/challenges/chinese-traditional/08-data-analysis-with-python/numpy/copying-arrays-warning.md
@@ -0,0 +1,44 @@
+---
+id: 5e9a0a8e09c5df3cc3600ed7
+title: Copying Arrays Warning
+challengeType: 11
+videoId: iIoQ0_L0GvA
+dashedName: copying-arrays-warning
+---
+
+# --question--
+
+## --text--
+
+What is the value of `a` after running the following code?
+
+```py
+import numpy as np
+
+a = np.array([1, 2, 3, 4, 5])
+b = a
+b[2] = 20
+```
+
+## --answers--
+
+```python
+[1 2 3 4 5]
+```
+
+---
+
+```python
+[1 2 20 4 5]
+```
+
+---
+
+```python
+[1 20 3 4 5]
+```
+
+## --video-solution--
+
+2
+
diff --git a/curriculum/challenges/chinese-traditional/08-data-analysis-with-python/numpy/initialize-array-problem.md b/curriculum/challenges/chinese-traditional/08-data-analysis-with-python/numpy/initialize-array-problem.md
new file mode 100644
index 0000000000..34e8f27604
--- /dev/null
+++ b/curriculum/challenges/chinese-traditional/08-data-analysis-with-python/numpy/initialize-array-problem.md
@@ -0,0 +1,61 @@
+---
+id: 5e9a0a8e09c5df3cc3600ed6
+title: Initialize Array Problem
+challengeType: 11
+videoId: 0jGfH8BPfOk
+dashedName: initialize-array-problem
+---
+
+# --question--
+
+## --text--
+
+What is another way to produce the following array?
+
+```py
+[[0. 0. 0. 0. 0. 0. 0.]
+[0. 1. 1. 1. 1. 1. 0.]
+[0. 1. 1. 1. 1. 1. 0.]
+[0. 1. 1. 5. 1. 1. 0.]
+[0. 1. 1. 1. 1. 1. 0.]
+[0. 1. 1. 1. 1. 1. 0.]
+[0. 0. 0. 0. 0. 0. 0.]]
+```
+
+## --answers--
+
+```py
+output = np.ones((7, 7))
+
+z = np.zeros((5, 5))
+z[2, 2] = 5
+
+output[1:1, -1:-1] = z
+```
+
+---
+
+```py
+output = np.zeros((7,7))
+
+z = np.ones((5, 5))
+z[2, 2] = 5
+
+output[1:-1, 1:-1] = z
+```
+
+---
+
+```py
+output = np.ones((7, 7))
+
+z = np.zeros((5, 5))
+z[3, 3] = 5
+
+output[1:-1, 1:-1] = z
+```
+
+## --video-solution--
+
+2
+
diff --git a/curriculum/challenges/chinese-traditional/08-data-analysis-with-python/numpy/initializing-different-arrays.md b/curriculum/challenges/chinese-traditional/08-data-analysis-with-python/numpy/initializing-different-arrays.md
new file mode 100644
index 0000000000..a6957cc7e4
--- /dev/null
+++ b/curriculum/challenges/chinese-traditional/08-data-analysis-with-python/numpy/initializing-different-arrays.md
@@ -0,0 +1,44 @@
+---
+id: 5e9a0a8e09c5df3cc3600ed5
+title: Initializing Different Arrays
+challengeType: 11
+videoId: CEykdsKT4U4
+dashedName: initializing-different-arrays
+---
+
+# --question--
+
+## --text--
+
+What will the following code print?
+
+```py
+a = np.array([[1, 2, 3, 4, 5], [6, 7, 8, 9, 10]])
+
+print(np.full_like(a, 100))
+```
+
+## --answers--
+
+```py
+[[100 100 100 100 100]]
+```
+
+---
+
+```py
+[[100 100 100 100 100]
+[100 100 100 100 100]]
+```
+
+---
+
+```py
+[[ 1 2 3 4 5]
+[ 6 7 20 9 10]]
+```
+
+## --video-solution--
+
+2
+
diff --git a/curriculum/challenges/chinese-traditional/08-data-analysis-with-python/numpy/loading-data-and-advanced-indexing.md b/curriculum/challenges/chinese-traditional/08-data-analysis-with-python/numpy/loading-data-and-advanced-indexing.md
new file mode 100644
index 0000000000..5afa96f450
--- /dev/null
+++ b/curriculum/challenges/chinese-traditional/08-data-analysis-with-python/numpy/loading-data-and-advanced-indexing.md
@@ -0,0 +1,56 @@
+---
+id: 5e9a0a8e09c5df3cc3600eda
+title: Loading Data and Advanced Indexing
+challengeType: 11
+videoId: tUdBZ7pF8Jg
+dashedName: loading-data-and-advanced-indexing
+---
+
+# --question--
+
+## --text--
+
+Given a file named `data.txt` with these contents:
+
+
+29,97,32,100,45
+15,88,5,75,22
+
+
+What code would produce the following array?
+
+```py
+[29. 32. 45. 15. 5. 22.]
+```
+
+## --answers--
+
+```py
+filedata = np.genfromtxt('data.txt', delimiter=',')
+output = np.any(filedata < 50)
+
+print(output)
+```
+
+---
+
+```py
+filedata = np.genfromtxt('data.txt', delimiter=',')
+output = np.all(filedata < 50, axis=1)
+
+print(output)
+```
+
+---
+
+```py
+filedata = np.genfromtxt('data.txt', delimiter=',')
+output = filedata[filedata < 50]
+
+print(output)
+```
+
+## --video-solution--
+
+3
+
diff --git a/curriculum/challenges/chinese-traditional/08-data-analysis-with-python/numpy/mathematics.md b/curriculum/challenges/chinese-traditional/08-data-analysis-with-python/numpy/mathematics.md
new file mode 100644
index 0000000000..f3b1a3c111
--- /dev/null
+++ b/curriculum/challenges/chinese-traditional/08-data-analysis-with-python/numpy/mathematics.md
@@ -0,0 +1,49 @@
+---
+id: 5e9a0a8e09c5df3cc3600ed8
+title: Mathematics
+challengeType: 11
+videoId: 7txegvyhtVk
+dashedName: mathematics
+---
+
+# --question--
+
+## --text--
+
+What is the value of `b` after running the following code?
+
+```py
+import numpy as np
+
+a = np.array(([1, 2, 3, 4, 5], [6, 7, 8, 9, 10]))
+b = np.max(a, axis=1).sum()
+```
+
+## --answers--
+
+```py
+10
+```
+
+---
+
+```py
+7
+```
+
+---
+
+```py
+5
+```
+
+---
+
+```py
+15
+```
+
+## --video-solution--
+
+4
+
diff --git a/curriculum/challenges/chinese-traditional/08-data-analysis-with-python/numpy/reorganizing-arrays.md b/curriculum/challenges/chinese-traditional/08-data-analysis-with-python/numpy/reorganizing-arrays.md
new file mode 100644
index 0000000000..7e4903c864
--- /dev/null
+++ b/curriculum/challenges/chinese-traditional/08-data-analysis-with-python/numpy/reorganizing-arrays.md
@@ -0,0 +1,49 @@
+---
+id: 5e9a0a8e09c5df3cc3600ed9
+title: Reorganizing Arrays
+challengeType: 11
+videoId: VNWAQbEM-C8
+dashedName: reorganizing-arrays
+---
+
+# --question--
+
+## --text--
+
+What code would produce the following array?
+
+```py
+[[1. 1.]
+[1. 1.]
+[1. 1.]
+[1. 1.]]
+```
+
+## --answers--
+
+```py
+a = np.ones((2, 4))
+b = a.reshape((4, 2))
+print(b)
+```
+
+---
+
+```py
+a = np.ones((2, 4))
+b = a.reshape((2, 4))
+print(b)
+```
+
+---
+
+```py
+a = np.ones((2, 4))
+b = a.reshape((8, 1))
+print(b)
+```
+
+## --video-solution--
+
+1
+
diff --git a/curriculum/challenges/chinese-traditional/08-data-analysis-with-python/numpy/what-is-numpy.md b/curriculum/challenges/chinese-traditional/08-data-analysis-with-python/numpy/what-is-numpy.md
new file mode 100644
index 0000000000..03b2f65fc7
--- /dev/null
+++ b/curriculum/challenges/chinese-traditional/08-data-analysis-with-python/numpy/what-is-numpy.md
@@ -0,0 +1,34 @@
+---
+id: 5e9a0a8e09c5df3cc3600ed2
+title: What is NumPy
+challengeType: 11
+videoId: 5Nwfs5Ej85Q
+dashedName: what-is-numpy
+---
+
+# --question--
+
+## --text--
+
+Why are Numpy arrays faster than regular Python lists?
+
+## --answers--
+
+Numpy does not perform type checking while iterating through objects.
+
+---
+
+Numpy uses fixed types.
+
+---
+
+Numpy uses contiguous memory.
+
+---
+
+All of the above.
+
+## --video-solution--
+
+4
+
diff --git a/curriculum/challenges/chinese-traditional/09-information-security/information-security-projects/anonymous-message-board.md b/curriculum/challenges/chinese-traditional/09-information-security/information-security-projects/anonymous-message-board.md
new file mode 100644
index 0000000000..20970a8e21
--- /dev/null
+++ b/curriculum/challenges/chinese-traditional/09-information-security/information-security-projects/anonymous-message-board.md
@@ -0,0 +1,146 @@
+---
+id: 587d824a367417b2b2512c45
+title: Anonymous Message Board
+challengeType: 4
+forumTopicId: 301568
+dashedName: anonymous-message-board
+---
+
+# --description--
+
+Build a full stack JavaScript app that is functionally similar to this:
.
+
+Working on this project will involve you writing your code using one of the following methods:
+
+- Clone [this GitHub repo](https://github.com/freeCodeCamp/boilerplate-project-messageboard/) and complete your project locally.
+- Use [our repl.it starter project](https://repl.it/github/freeCodeCamp/boilerplate-project-messageboard) to complete your project.
+- Use a site builder of your choice to complete the project. Be sure to incorporate all the files from our GitHub repo.
+
+When you are done, make sure a working demo of your project is hosted somewhere public. Then submit the URL to it in the `Solution Link` field. Optionally, also submit a link to your projects source code in the `GitHub Link` field.
+
+# --instructions--
+
+1. Set `NODE_ENV` to test without quotes when ready to write tests and DB to your databases connection string (in `.env`)
+2. Recommended to create controllers/handlers and handle routing in `routes/api.js`
+3. You will add any security features to `server.js`
+
+Write the following tests in `tests/2_functional-tests.js`:
+
+- Creating a new thread: POST request to `/api/threads/{board}`
+- Viewing the 10 most recent threads with 3 replies each: GET request to `/api/threads/{board}`
+- Deleting a thread with the incorrect password: DELETE request to `/api/threads/{board}` with an invalid `delete_password`
+- Deleting a thread with the correct password: DELETE request to `/api/threads/{board}` with a valid `delete_password`
+- Reporting a thread: PUT request to `/api/threads/{board}`
+- Creating a new reply: POST request to `/api/replies/{board}`
+- Viewing a single thread with all replies: GET request to `/api/replies/{board}`
+- Deleting a reply with the incorrect password: DELETE request to `/api/threads/{board}` with an invalid `delete_password`
+- Deleting a reply with the correct password: DELETE request to `/api/threads/{board}` with a valid `delete_password`
+- Reporting a reply: PUT request to `/api/replies/{board}`
+
+# --hints--
+
+You can provide your own project, not the example URL.
+
+```js
+(getUserInput) => {
+ assert(
+ !/.*\/anonymous-message-board\.freecodecamp\.rocks/.test(
+ getUserInput('url')
+ )
+ );
+};
+```
+
+Only allow your site to be loaded in an iFrame on your own pages.
+
+```js
+async (getUserInput) => {
+ const data = await fetch(getUserInput('url') + '/_api/app-info');
+ const parsed = await data.json();
+ assert.isTrue(parsed.headers['x-frame-options']?.includes('SAMEORIGIN'));
+};
+```
+
+Do not allow DNS prefetching.
+
+```js
+async (getUserInput) => {
+ const data = await fetch(getUserInput('url') + '/_api/app-info');
+ const parsed = await data.json();
+ assert.isTrue(parsed.headers['x-dns-prefetch-control']?.includes('off'));
+};
+```
+
+Only allow your site to send the referrer for your own pages.
+
+```js
+async (getUserInput) => {
+ const data = await fetch(getUserInput('url') + '/_api/app-info');
+ const parsed = await data.json();
+ assert.isTrue(parsed.headers['referrer-policy']?.includes('same-origin'));
+};
+```
+
+You can send a POST request to `/api/threads/{board}` with form data including `text` and `delete_password`. The saved database record will have at least the fields `_id`, `text`, `created_on`(date & time), `bumped_on`(date & time, starts same as `created_on`), `reported` (boolean), `delete_password`, & `replies` (array).
+
+```js
+
+```
+
+You can send a POST request to `/api/replies/{board}` with form data including `text`, `delete_password`, & `thread_id`. This will update the `bumped_on` date to the comment's date. In the thread's `replies` array, an object will be saved with at least the properties `_id`, `text`, `created_on`, `delete_password`, & `reported`.
+
+```js
+
+```
+
+You can send a GET request to `/api/threads/{board}`. Returned will be an array of the most recent 10 bumped threads on the board with only the most recent 3 replies for each. The `reported` and `delete_password` fields will not be sent to the client.
+
+```js
+
+```
+
+You can send a GET request to `/api/replies/{board}?thread_id={thread_id}`. Returned will be the entire thread with all its replies, also excluding the same fields from the client as the previous test.
+
+```js
+
+```
+
+You can send a DELETE request to `/api/threads/{board}` and pass along the `thread_id` & `delete_password` to delete the thread. Returned will be the string `incorrect password` or `success`.
+
+```js
+
+```
+
+You can send a DELETE request to `/api/replies/{board}` and pass along the `thread_id`, `reply_id`, & `delete_password`. Returned will be the string `incorrect password` or `success`. On success, the text of the `reply_id` will be changed to `[deleted]`.
+
+```js
+
+```
+
+You can send a PUT request to `/api/threads/{board}` and pass along the `thread_id`. Returned will be the string `success`. The `reported` value of the `thread_id` will be changed to `true`.
+
+```js
+
+```
+
+You can send a PUT request to `/api/replies/{board}` and pass along the `thread_id` & `reply_id`. Returned will be the string `success`. The `reported` value of the `reply_id` will be changed to `true`.
+
+```js
+
+```
+
+All 10 functional tests are complete and passing.
+
+```js
+
+```
+
+# --solutions--
+
+```js
+/**
+ Backend challenges don't need solutions,
+ because they would need to be tested against a full working project.
+ Please check our contributing guidelines to learn more.
+*/
+```
diff --git a/curriculum/challenges/chinese-traditional/09-information-security/information-security-projects/port-scanner.md b/curriculum/challenges/chinese-traditional/09-information-security/information-security-projects/port-scanner.md
new file mode 100644
index 0000000000..5c776ca7d0
--- /dev/null
+++ b/curriculum/challenges/chinese-traditional/09-information-security/information-security-projects/port-scanner.md
@@ -0,0 +1,42 @@
+---
+id: 5e46f979ac417301a38fb932
+title: Port Scanner
+challengeType: 10
+helpCategory: Python
+dashedName: port-scanner
+---
+
+# --description--
+
+Create a port scanner using Python.
+
+You can access [the full project description and starter code on Repl.it](https://repl.it/github/freeCodeCamp/boilerplate-port-scanner).
+
+After going to that link, fork the project. Once you complete the project based on the instructions in 'README.md', submit your project link below.
+
+We are still developing the interactive instructional part of the Python curriculum. For now, here are some videos on the freeCodeCamp.org YouTube channel that will teach you some of the Python skills required for this project:
+
+
+
+# --hints--
+
+It should pass all Python tests.
+
+```js
+
+```
+
+# --solutions--
+
+```py
+ # Python challenges don't need solutions,
+ # because they would need to be tested against a full working project.
+ # Please check our contributing guidelines to learn more.
+```
diff --git a/curriculum/challenges/chinese-traditional/09-information-security/information-security-projects/secure-real-time-multiplayer-game.md b/curriculum/challenges/chinese-traditional/09-information-security/information-security-projects/secure-real-time-multiplayer-game.md
new file mode 100644
index 0000000000..3ced001df3
--- /dev/null
+++ b/curriculum/challenges/chinese-traditional/09-information-security/information-security-projects/secure-real-time-multiplayer-game.md
@@ -0,0 +1,148 @@
+---
+id: 5e601c775ac9d0ecd8b94aff
+title: Secure Real Time Multiplayer Game
+challengeType: 4
+dashedName: secure-real-time-multiplayer-game
+---
+
+# --description--
+
+Develop a 2D real time multiplayer game using the HTML Canvas API and [Socket.io](https://socket.io/) that is functionally similar to this:
. Working on this project will involve you writing your code using one of the following methods:
+
+- Clone [this GitHub repo](https://github.com/freeCodeCamp/boilerplate-project-secure-real-time-multiplayer-game/) and complete your project locally.
+- Use [our repl.it starter project](https://repl.it/github/freeCodeCamp/boilerplate-project-secure-real-time-multiplayer-game) to complete your project.
+- Use a site builder of your choice to complete the project. Be sure to incorporate all the files from our GitHub repo.
+
+When you are done, make sure a working demo of your project is hosted somewhere public. Then submit the URL to it in the `Solution Link` field. Optionally, also submit a link to your project's source code in the `GitHub Link` field.
+
+# --hints--
+
+You can provide your own project, not the example URL.
+
+```js
+(getUserInput) => {
+ assert(
+ !/.*\/secure-real-time-multiplayer-game\.freecodecamp\.rocks/.test(
+ getUserInput('url')
+ )
+ );
+};
+```
+
+Multiple players can connect to a server and play.
+
+```js
+
+```
+
+Each player has an avatar.
+
+```js
+
+```
+
+Each player is represented by an object created by the `Player` class in `Player.mjs`.
+
+```js
+
+```
+
+At a minimum, each player object should contain a unique `id`, a `score`, and `x` and `y` coordinates representing the player's current position.
+
+```js
+
+```
+
+The game has at least one type of collectible item. Complete the `Collectible` class in `Collectible.mjs` to implement this.
+
+```js
+
+```
+
+At a minimum, each collectible item object created by the `Collectible` class should contain a unique `id`, a `value`, and `x` and `y` coordinates representing the item's current position.
+
+```js
+
+```
+
+Players can use the WASD and/or arrow keys to move their avatar. Complete the `movePlayer` method in `Player.mjs` to implement this.
+
+```js
+
+```
+
+The `movePlayer` method should accept two arguments: a string of "up", "down", "left", or "right", and a number for the amount of pixels the player's position should change. `movePlayer` should adjust the `x` and `y` coordinates of the player object it's called from.
+
+```js
+
+```
+
+The player's score should be used to calculate their rank among the other players. Complete the `calculateRank` method in the `Player` class to implement this.
+
+```js
+
+```
+
+The `calculateRank` method should accept an array of objects representing all connected players and return the string `Rank: currentRanking/totalPlayers`. For example, in a game with two players, if Player A has a score of 3 and Player B has a score of 5, `calculateRank` for Player A should return `Rank: 2/2`.
+
+```js
+
+```
+
+Players can collide with a collectible item. Complete the `collision` method in `Player.mjs` to implement this.
+
+```js
+
+```
+
+The `collision` method should accept a collectible item's object as an argument. If the player's avatar intersects with the item, the `collision` method should return `true`.
+
+```js
+
+```
+
+All players are kept in sync.
+
+```js
+
+```
+
+Players can disconnect from the game at any time.
+
+```js
+
+```
+
+Prevent the client from trying to guess / sniff the MIME type.
+
+```js
+
+```
+
+Prevent cross-site scripting (XSS) attacks.
+
+```js
+
+```
+
+Nothing from the website is cached in the client.
+
+```js
+
+```
+
+The headers say that the site is powered by "PHP 7.4.3" even though it isn't (as a security measure).
+
+```js
+
+```
+
+# --solutions--
+
+```js
+/**
+ Backend challenges don't need solutions,
+ because they would need to be tested against a full working project.
+ Please check our contributing guidelines to learn more.
+*/
+```
diff --git a/curriculum/challenges/chinese-traditional/09-information-security/information-security-projects/sha-1-password-cracker.md b/curriculum/challenges/chinese-traditional/09-information-security/information-security-projects/sha-1-password-cracker.md
new file mode 100644
index 0000000000..8a79bb1b7c
--- /dev/null
+++ b/curriculum/challenges/chinese-traditional/09-information-security/information-security-projects/sha-1-password-cracker.md
@@ -0,0 +1,42 @@
+---
+id: 5e46f983ac417301a38fb933
+title: SHA-1 Password Cracker
+challengeType: 10
+helpCategory: Python
+dashedName: sha-1-password-cracker
+---
+
+# --description--
+
+For this project you will learn about the importance of good security by creating a password cracker to figure out passwords that were hashed using SHA-1.
+
+You can access [the full project description and starter code on Repl.it](https://repl.it/github/freeCodeCamp/boilerplate-SHA-1-password-cracker).
+
+After going to that link, fork the project. Once you complete the project based on the instructions in 'README.md', submit your project link below.
+
+We are still developing the interactive instructional part of the Python curriculum. For now, here are some videos on the freeCodeCamp.org YouTube channel that will teach you some of the Python skills required for this project:
+
+
+
+# --hints--
+
+It should pass all Python tests.
+
+```js
+
+```
+
+# --solutions--
+
+```py
+ # Python challenges don't need solutions,
+ # because they would need to be tested against a full working project.
+ # Please check our contributing guidelines to learn more.
+```
diff --git a/curriculum/challenges/chinese-traditional/09-information-security/information-security-projects/stock-price-checker.md b/curriculum/challenges/chinese-traditional/09-information-security/information-security-projects/stock-price-checker.md
new file mode 100644
index 0000000000..ce584c58d5
--- /dev/null
+++ b/curriculum/challenges/chinese-traditional/09-information-security/information-security-projects/stock-price-checker.md
@@ -0,0 +1,134 @@
+---
+id: 587d824a367417b2b2512c44
+title: Stock Price Checker
+challengeType: 4
+forumTopicId: 301572
+dashedName: stock-price-checker
+---
+
+# --description--
+
+Build a full stack JavaScript app that is functionally similar to this:
.
+
+Since all reliable stock price APIs require an API key, we've built a workaround. Use
to get up-to-date stock price information without needing to sign up for your own key.
+
+Working on this project will involve you writing your code using one of the following methods:
+
+- Clone [this GitHub repo](https://github.com/freeCodeCamp/boilerplate-project-stockchecker/) and complete your project locally.
+- Use [our repl.it starter project](https://repl.it/github/freeCodeCamp/boilerplate-project-stockchecker) to complete your project.
+- Use a site builder of your choice to complete the project. Be sure to incorporate all the files from our GitHub repo.
+
+When you are done, make sure a working demo of your project is hosted somewhere public. Then submit the URL to it in the `Solution Link` field. Optionally, also submit a link to your projects source code in the `GitHub Link` field.
+
+# --instructions--
+
+1. SET `NODE_ENV` to `test` without quotes and set `DB` to your MongoDB connection string
+2. Complete the project in `routes/api.js` or by creating a handler/controller
+3. You will add any security features to `server.js`
+4. You will create all of the functional tests in `tests/2_functional-tests.js`
+
+Write the following tests in `tests/2_functional-tests.js`:
+
+- Viewing one stock: GET request to `/api/stock-prices/`
+- Viewing one stock and liking it: GET request to `/api/stock-prices/`
+- Viewing the same stock and liking it again: GET request to `/api/stock-prices/`
+- Viewing two stocks: GET request to `/api/stock-prices/`
+- Viewing two stocks and liking them: GET request to `/api/stock-prices/`
+
+# --hints--
+
+You can provide your own project, not the example URL.
+
+```js
+(getUserInput) => {
+ assert(
+ !/.*\/stock-price-checker\.freecodecamp\.rocks/.test(getUserInput('url'))
+ );
+};
+```
+
+You should set the content security policies to only allow loading of scripts and CSS from your server.
+
+```js
+async (getUserInput) => {
+ const data = await fetch(getUserInput('url') + '/_api/app-info');
+ const parsed = await data.json();
+ assert.isTrue(
+ parsed.headers['content-security-policy'].includes("script-src 'self'")
+ );
+ assert.isTrue(
+ parsed.headers['content-security-policy'].includes("style-src 'self'")
+ );
+};
+```
+
+You can send a `GET` request to `/api/stock-prices` with form data containing a NASDAQ stock ticker. Returned will be an object with the property `stockData`.
+
+```js
+async (getUserInput) => {
+ const data = await fetch(
+ getUserInput('url') + '/api/stock-prices?stock=GOOG'
+ );
+ const parsed = await data.json();
+ assert.property(parsed, 'stockData');
+};
+```
+
+The `stockData` property includes the `stock` ticker as a string, the `price` as a number, and `likes` as a number.
+
+```js
+async (getUserInput) => {
+ const data = await fetch(
+ getUserInput('url') + '/api/stock-prices?stock=GOOG'
+ );
+ const parsed = await data.json();
+ const ticker = parsed.stockData;
+ assert.typeOf(ticker.price, 'number');
+ assert.typeOf(ticker.likes, 'number');
+ assert.typeOf(ticker.stock, 'string');
+};
+```
+
+You can also pass along a `like` field as `true` (boolean) to have your like added to the stock(s). Only 1 like per IP should be accepted.
+
+```js
+
+```
+
+If you pass along 2 stocks, the returned value will be an array with information about both stocks. Instead of `likes`, it will display `rel_likes` (the difference between the likes on both stocks) for both `stockData` objects.
+
+```js
+async (getUserInput) => {
+ const data = await fetch(
+ getUserInput('url') + '/api/stock-prices?stock=GOOG&stock=MSFT'
+ );
+ const parsed = await data.json();
+ const ticker = parsed.stockData;
+ assert.typeOf(ticker, 'array');
+ assert.property(ticker[0], 'rel_likes');
+ assert.property(ticker[1], 'rel_likes');
+};
+```
+
+All 5 functional tests are complete and passing.
+
+```js
+async (getUserInput) => {
+ const tests = await fetch(getUserInput('url') + '/_api/get-tests');
+ const parsed = await tests.json();
+ assert.isTrue(parsed.length >= 5);
+ parsed.forEach((test) => {
+ assert.equal(test.state, 'passed');
+ });
+};
+```
+
+# --solutions--
+
+```js
+/**
+ Backend challenges don't need solutions,
+ because they would need to be tested against a full working project.
+ Please check our contributing guidelines to learn more.
+*/
+```
diff --git a/curriculum/challenges/chinese-traditional/09-information-security/information-security-with-helmetjs/ask-browsers-to-access-your-site-via-https-only-with-helmet.hsts.md b/curriculum/challenges/chinese-traditional/09-information-security/information-security-with-helmetjs/ask-browsers-to-access-your-site-via-https-only-with-helmet.hsts.md
new file mode 100644
index 0000000000..6bf78cfccc
--- /dev/null
+++ b/curriculum/challenges/chinese-traditional/09-information-security/information-security-with-helmetjs/ask-browsers-to-access-your-site-via-https-only-with-helmet.hsts.md
@@ -0,0 +1,63 @@
+---
+id: 587d8248367417b2b2512c3c
+title: Ask Browsers to Access Your Site via HTTPS Only with helmet.hsts()
+challengeType: 2
+forumTopicId: 301573
+dashedName: ask-browsers-to-access-your-site-via-https-only-with-helmet-hsts
+---
+
+# --description--
+
+As a reminder, this project is being built upon the following starter project on [Repl.it](https://repl.it/github/freeCodeCamp/boilerplate-infosec), or cloned from [GitHub](https://github.com/freeCodeCamp/boilerplate-infosec/).
+
+HTTP Strict Transport Security (HSTS) is a web security policy which helps to protect websites against protocol downgrade attacks and cookie hijacking. If your website can be accessed via HTTPS you can ask user’s browsers to avoid using insecure HTTP. By setting the header Strict-Transport-Security, you tell the browsers to use HTTPS for the future requests in a specified amount of time. This will work for the requests coming after the initial request.
+
+# --instructions--
+
+Configure `helmet.hsts()` to use HTTPS for the next 90 days. Pass the config object `{maxAge: timeInSeconds, force: true}`. You can create a variable `ninetyDaysInSeconds = 90*24*60*60;` to use for the `timeInSeconds`. Repl.it already has hsts enabled. To override its settings you need to set the field "force" to true in the config object. We will intercept and restore the Repl.it header, after inspecting it for testing.
+
+Note: Configuring HTTPS on a custom website requires the acquisition of a domain, and a SSL/TLS Certificate.
+
+# --hints--
+
+helmet.hsts() middleware should be mounted correctly
+
+```js
+(getUserInput) =>
+ $.get(getUserInput('url') + '/_api/app-info').then(
+ (data) => {
+ assert.include(data.appStack, 'hsts');
+ assert.property(data.headers, 'strict-transport-security');
+ },
+ (xhr) => {
+ throw new Error(xhr.responseText);
+ }
+ );
+```
+
+maxAge should be equal to 7776000 s (90 days)
+
+```js
+(getUserInput) =>
+ $.get(getUserInput('url') + '/_api/app-info').then(
+ (data) => {
+ assert.match(
+ data.headers['strict-transport-security'],
+ /^max-age=7776000;?/
+ );
+ },
+ (xhr) => {
+ throw new Error(xhr.responseText);
+ }
+ );
+```
+
+# --solutions--
+
+```js
+/**
+ Backend challenges don't need solutions,
+ because they would need to be tested against a full working project.
+ Please check our contributing guidelines to learn more.
+*/
+```
diff --git a/curriculum/challenges/chinese-traditional/09-information-security/information-security-with-helmetjs/avoid-inferring-the-response-mime-type-with-helmet.nosniff.md b/curriculum/challenges/chinese-traditional/09-information-security/information-security-with-helmetjs/avoid-inferring-the-response-mime-type-with-helmet.nosniff.md
new file mode 100644
index 0000000000..2bbd3bf02e
--- /dev/null
+++ b/curriculum/challenges/chinese-traditional/09-information-security/information-security-with-helmetjs/avoid-inferring-the-response-mime-type-with-helmet.nosniff.md
@@ -0,0 +1,42 @@
+---
+id: 587d8248367417b2b2512c3a
+title: Avoid Inferring the Response MIME Type with helmet.noSniff()
+challengeType: 2
+forumTopicId: 301574
+dashedName: avoid-inferring-the-response-mime-type-with-helmet-nosniff
+---
+
+# --description--
+
+As a reminder, this project is being built upon the following starter project on [Repl.it](https://repl.it/github/freeCodeCamp/boilerplate-infosec), or cloned from [GitHub](https://github.com/freeCodeCamp/boilerplate-infosec/). Browsers can use content or MIME sniffing to override response `Content-Type` headers to guess and process the data using an implicit content type. While this can be convenient in some scenarios, it can also lead to some dangerous attacks. This middleware sets the X-Content-Type-Options header to `nosniff`, instructing the browser to not bypass the provided `Content-Type`.
+
+# --instructions--
+
+Use the `helmet.noSniff()` method on your server.
+
+# --hints--
+
+helmet.noSniff() middleware should be mounted correctly
+
+```js
+(getUserInput) =>
+ $.get(getUserInput('url') + '/_api/app-info').then(
+ (data) => {
+ assert.include(data.appStack, 'nosniff');
+ assert.equal(data.headers['x-content-type-options'], 'nosniff');
+ },
+ (xhr) => {
+ throw new Error(xhr.responseText);
+ }
+ );
+```
+
+# --solutions--
+
+```js
+/**
+ Backend challenges don't need solutions,
+ because they would need to be tested against a full working project.
+ Please check our contributing guidelines to learn more.
+*/
+```
diff --git a/curriculum/challenges/chinese-traditional/09-information-security/information-security-with-helmetjs/configure-helmet-using-the-parent-helmet-middleware.md b/curriculum/challenges/chinese-traditional/09-information-security/information-security-with-helmetjs/configure-helmet-using-the-parent-helmet-middleware.md
new file mode 100644
index 0000000000..6865d3896c
--- /dev/null
+++ b/curriculum/challenges/chinese-traditional/09-information-security/information-security-with-helmetjs/configure-helmet-using-the-parent-helmet-middleware.md
@@ -0,0 +1,50 @@
+---
+id: 587d8249367417b2b2512c40
+title: Configure Helmet Using the ‘parent’ helmet() Middleware
+challengeType: 2
+forumTopicId: 301575
+dashedName: configure-helmet-using-the-parent-helmet-middleware
+---
+
+# --description--
+
+As a reminder, this project is being built upon the following starter project on [Repl.it](https://repl.it/github/freeCodeCamp/boilerplate-infosec), or cloned from [GitHub](https://github.com/freeCodeCamp/boilerplate-infosec/).
+
+`app.use(helmet())` will automatically include all the middleware introduced above, except `noCache()`, and `contentSecurityPolicy()`, but these can be enabled if necessary. You can also disable or configure any other middleware individually, using a configuration object.
+
+**Example:**
+
+```js
+app.use(helmet({
+ frameguard: { // configure
+ action: 'deny'
+ },
+ contentSecurityPolicy: { // enable and configure
+ directives: {
+ defaultSrc: ["self"],
+ styleSrc: ['style.com'],
+ }
+ },
+ dnsPrefetchControl: false // disable
+}))
+```
+
+We introduced each middleware separately for teaching purposes and for ease of testing. Using the ‘parent’ `helmet()` middleware is easy to implement in a real project.
+
+# --hints--
+
+no tests - it's a descriptive challenge
+
+```js
+assert(true);
+```
+
+# --solutions--
+
+```js
+/**
+ Backend challenges don't need solutions,
+ because they would need to be tested against a full working project.
+ Please check our contributing guidelines to learn more.
+*/
+```
diff --git a/curriculum/challenges/chinese-traditional/09-information-security/information-security-with-helmetjs/disable-client-side-caching-with-helmet.nocache.md b/curriculum/challenges/chinese-traditional/09-information-security/information-security-with-helmetjs/disable-client-side-caching-with-helmet.nocache.md
new file mode 100644
index 0000000000..21cf373400
--- /dev/null
+++ b/curriculum/challenges/chinese-traditional/09-information-security/information-security-with-helmetjs/disable-client-side-caching-with-helmet.nocache.md
@@ -0,0 +1,47 @@
+---
+id: 587d8249367417b2b2512c3e
+title: Disable Client-Side Caching with helmet.noCache()
+challengeType: 2
+forumTopicId: 301576
+dashedName: disable-client-side-caching-with-helmet-nocache
+---
+
+# --description--
+
+As a reminder, this project is being built upon the following starter project on [Repl.it](https://repl.it/github/freeCodeCamp/boilerplate-infosec), or cloned from [GitHub](https://github.com/freeCodeCamp/boilerplate-infosec/).
+
+If you are releasing an update for your website, and you want the users to always download the newer version, you can (try to) disable caching on client’s browser. It can be useful in development too. Caching has performance benefits, which you will lose, so only use this option when there is a real need.
+
+# --instructions--
+
+Use the `helmet.noCache()` method on your server.
+
+# --hints--
+
+helmet.noCache() middleware should be mounted correctly
+
+```js
+(getUserInput) =>
+ $.get(getUserInput('url') + '/_api/app-info').then(
+ (data) => {
+ assert.include(data.appStack, 'nocache');
+ assert.equal(
+ data.headers['cache-control'],
+ 'no-store, no-cache, must-revalidate, proxy-revalidate'
+ );
+ },
+ (xhr) => {
+ throw new Error(xhr.responseText);
+ }
+ );
+```
+
+# --solutions--
+
+```js
+/**
+ Backend challenges don't need solutions,
+ because they would need to be tested against a full working project.
+ Please check our contributing guidelines to learn more.
+*/
+```
diff --git a/curriculum/challenges/chinese-traditional/09-information-security/information-security-with-helmetjs/disable-dns-prefetching-with-helmet.dnsprefetchcontrol.md b/curriculum/challenges/chinese-traditional/09-information-security/information-security-with-helmetjs/disable-dns-prefetching-with-helmet.dnsprefetchcontrol.md
new file mode 100644
index 0000000000..5d212b6dbe
--- /dev/null
+++ b/curriculum/challenges/chinese-traditional/09-information-security/information-security-with-helmetjs/disable-dns-prefetching-with-helmet.dnsprefetchcontrol.md
@@ -0,0 +1,44 @@
+---
+id: 587d8248367417b2b2512c3d
+title: Disable DNS Prefetching with helmet.dnsPrefetchControl()
+challengeType: 2
+forumTopicId: 301577
+dashedName: disable-dns-prefetching-with-helmet-dnsprefetchcontrol
+---
+
+# --description--
+
+As a reminder, this project is being built upon the following starter project on [Repl.it](https://repl.it/github/freeCodeCamp/boilerplate-infosec), or cloned from [GitHub](https://github.com/freeCodeCamp/boilerplate-infosec/).
+
+To improve performance, most browsers prefetch DNS records for the links in a page. In that way the destination ip is already known when the user clicks on a link. This may lead to over-use of the DNS service (if you own a big website, visited by millions people…), privacy issues (one eavesdropper could infer that you are on a certain page), or page statistics alteration (some links may appear visited even if they are not). If you have high security needs you can disable DNS prefetching, at the cost of a performance penalty.
+
+# --instructions--
+
+Use the `helmet.dnsPrefetchControl()` method on your server.
+
+# --hints--
+
+helmet.dnsPrefetchControl() middleware should be mounted correctly
+
+```js
+(getUserInput) =>
+ $.get(getUserInput('url') + '/_api/app-info').then(
+ (data) => {
+ assert.include(data.appStack, 'dnsPrefetchControl');
+ assert.equal(data.headers['x-dns-prefetch-control'], 'off');
+ },
+ (xhr) => {
+ throw new Error(xhr.responseText);
+ }
+ );
+```
+
+# --solutions--
+
+```js
+/**
+ Backend challenges don't need solutions,
+ because they would need to be tested against a full working project.
+ Please check our contributing guidelines to learn more.
+*/
+```
diff --git a/curriculum/challenges/chinese-traditional/09-information-security/information-security-with-helmetjs/hash-and-compare-passwords-asynchronously.md b/curriculum/challenges/chinese-traditional/09-information-security/information-security-with-helmetjs/hash-and-compare-passwords-asynchronously.md
new file mode 100644
index 0000000000..5d6126daa7
--- /dev/null
+++ b/curriculum/challenges/chinese-traditional/09-information-security/information-security-with-helmetjs/hash-and-compare-passwords-asynchronously.md
@@ -0,0 +1,81 @@
+---
+id: 58a25bcff9fc0f352b528e7d
+title: Hash and Compare Passwords Asynchronously
+challengeType: 2
+forumTopicId: 301578
+dashedName: hash-and-compare-passwords-asynchronously
+---
+
+# --description--
+
+As a reminder, this project is being built upon the following starter project on [Repl.it](https://repl.it/github/freeCodeCamp/boilerplate-bcrypt), or cloned from [GitHub](https://github.com/freeCodeCamp/boilerplate-bcrypt/).
+
+As hashing is designed to be computationally intensive, it is recommended to do so asynchronously on your server as to avoid blocking incoming connections while you hash. All you have to do to hash a password asynchronous is call
+
+```js
+bcrypt.hash(myPlaintextPassword, saltRounds, (err, hash) => {
+ /*Store hash in your db*/
+});
+```
+
+# --instructions--
+
+Add this hashing function to your server(we've already defined the variables used in the function for you to use) and log it to the console for you to see! At this point you would normally save the hash to your database.
+
+Now when you need to figure out if a new input is the same data as the hash you would just use the compare function.
+
+```js
+bcrypt.compare(myPlaintextPassword, hash, (err, res) => {
+ /*res == true or false*/
+});
+```
+
+Add this into your existing hash function(since you need to wait for the hash to complete before calling the compare function) after you log the completed hash and log 'res' to the console within the compare. You should see in the console a hash then 'true' is printed! If you change 'myPlaintextPassword' in the compare function to 'someOtherPlaintextPassword' then it should say false.
+
+```js
+bcrypt.hash('passw0rd!', 13, (err, hash) => {
+ console.log(hash);
+ //$2a$12$Y.PHPE15wR25qrrtgGkiYe2sXo98cjuMCG1YwSI5rJW1DSJp0gEYS
+ bcrypt.compare('passw0rd!', hash, (err, res) => {
+ console.log(res); //true
+ });
+});
+
+```
+
+Submit your page when you think you've got it right.
+
+# --hints--
+
+Async hash should be generated and correctly compared.
+
+```js
+(getUserInput) =>
+ $.get(getUserInput('url') + '/_api/server.js').then(
+ (data) => {
+ assert.match(
+ data,
+ /START_ASYNC[^]*bcrypt.hash.*myPlaintextPassword( |),( |)saltRounds( |),( |).*err( |),( |)hash[^]*END_ASYNC/gi,
+ 'You should call bcrypt.hash on myPlaintextPassword and saltRounds and handle err and hash as a result in the callback'
+ );
+ assert.match(
+ data,
+ /START_ASYNC[^]*bcrypt.hash[^]*bcrypt.compare.*myPlaintextPassword( |),( |)hash( |),( |).*err( |),( |)res[^]*}[^]*}[^]*END_ASYNC/gi,
+ 'Nested within the hash function should be the compare function comparing myPlaintextPassword to hash'
+ );
+ },
+ (xhr) => {
+ throw new Error(xhr.statusText);
+ }
+ );
+```
+
+# --solutions--
+
+```js
+/**
+ Backend challenges don't need solutions,
+ because they would need to be tested against a full working project.
+ Please check our contributing guidelines to learn more.
+*/
+```
diff --git a/curriculum/challenges/chinese-traditional/09-information-security/information-security-with-helmetjs/hash-and-compare-passwords-synchronously.md b/curriculum/challenges/chinese-traditional/09-information-security/information-security-with-helmetjs/hash-and-compare-passwords-synchronously.md
new file mode 100644
index 0000000000..c03109f16a
--- /dev/null
+++ b/curriculum/challenges/chinese-traditional/09-information-security/information-security-with-helmetjs/hash-and-compare-passwords-synchronously.md
@@ -0,0 +1,66 @@
+---
+id: 58a25bcff9fc0f352b528e7e
+title: Hash and Compare Passwords Synchronously
+challengeType: 2
+forumTopicId: 301579
+dashedName: hash-and-compare-passwords-synchronously
+---
+
+# --description--
+
+As a reminder, this project is being built upon the following starter project on [Repl.it](https://repl.it/github/freeCodeCamp/boilerplate-bcrypt), or cloned from [GitHub](https://github.com/freeCodeCamp/boilerplate-bcrypt/).
+
+Hashing synchronously is just as easy to do but can cause lag if using it server side with a high cost or with hashing done very often. Hashing with this method is as easy as calling
+
+```js
+var hash = bcrypt.hashSync(myPlaintextPassword, saltRounds);
+```
+
+Add this method of hashing to your code and then log the result to the console. Again, the variables used are already defined in the server so you won't need to adjust them. You may notice even though you are hashing the same password as in the async function, the result in the console is different- this is due to the salt being randomly generated each time as seen by the first 22 characters in the third string of the hash. Now to compare a password input with the new sync hash, you would use the compareSync method:
+
+```js
+var result = bcrypt.compareSync(myPlaintextPassword, hash);
+```
+
+with the result being a boolean true or false.
+
+# --instructions--
+
+Add the function in and log the result to the console to see it working.
+
+Submit your page when you think you've got it right.
+
+# --hints--
+
+Sync hash should be generated and correctly compared.
+
+```js
+(getUserInput) =>
+ $.get(getUserInput('url') + '/_api/server.js').then(
+ (data) => {
+ assert.match(
+ data,
+ /START_SYNC[^]*hash.*=.*bcrypt.hashSync.*myPlaintextPassword( |),( |)saltRounds[^]*END_SYNC/gi,
+ 'You should call bcrypt.hashSync on myPlaintextPassword with saltRounds'
+ );
+ assert.match(
+ data,
+ /START_SYNC[^]*result.*=.*bcrypt.compareSync.*myPlaintextPassword( |),( |)hash[^]*END_SYNC/gi,
+ 'You should call bcrypt.compareSync on myPlaintextPassword with the hash generated in the last line'
+ );
+ },
+ (xhr) => {
+ throw new Error(xhr.statusText);
+ }
+ );
+```
+
+# --solutions--
+
+```js
+/**
+ Backend challenges don't need solutions,
+ because they would need to be tested against a full working project.
+ Please check our contributing guidelines to learn more.
+*/
+```
diff --git a/curriculum/challenges/chinese-traditional/09-information-security/information-security-with-helmetjs/hide-potentially-dangerous-information-using-helmet.hidepoweredby.md b/curriculum/challenges/chinese-traditional/09-information-security/information-security-with-helmetjs/hide-potentially-dangerous-information-using-helmet.hidepoweredby.md
new file mode 100644
index 0000000000..630302ea38
--- /dev/null
+++ b/curriculum/challenges/chinese-traditional/09-information-security/information-security-with-helmetjs/hide-potentially-dangerous-information-using-helmet.hidepoweredby.md
@@ -0,0 +1,40 @@
+---
+id: 587d8247367417b2b2512c37
+title: Hide Potentially Dangerous Information Using helmet.hidePoweredBy()
+challengeType: 2
+forumTopicId: 301580
+dashedName: hide-potentially-dangerous-information-using-helmet-hidepoweredby
+---
+
+# --description--
+
+As a reminder, this project is being built upon the following starter project on [Repl.it](https://repl.it/github/freeCodeCamp/boilerplate-infosec), or cloned from [GitHub](https://github.com/freeCodeCamp/boilerplate-infosec/).
+
+Hackers can exploit known vulnerabilities in Express/Node if they see that your site is powered by Express. X-Powered-By: Express is sent in every request coming from Express by default. The `helmet.hidePoweredBy()` middleware will remove the X-Powered-By header. You can also explicitly set the header to something else, to throw people off. e.g. `app.use(helmet.hidePoweredBy({ setTo: 'PHP 4.2.0' }))`
+
+# --hints--
+
+helmet.hidePoweredBy() middleware should be mounted correctly
+
+```js
+(getUserInput) =>
+ $.get(getUserInput('url') + '/_api/app-info').then(
+ (data) => {
+ assert.include(data.appStack, 'hidePoweredBy');
+ assert.notEqual(data.headers['x-powered-by'], 'Express');
+ },
+ (xhr) => {
+ throw new Error(xhr.responseText);
+ }
+ );
+```
+
+# --solutions--
+
+```js
+/**
+ Backend challenges don't need solutions,
+ because they would need to be tested against a full working project.
+ Please check our contributing guidelines to learn more.
+*/
+```
diff --git a/curriculum/challenges/chinese-traditional/09-information-security/information-security-with-helmetjs/install-and-require-helmet.md b/curriculum/challenges/chinese-traditional/09-information-security/information-security-with-helmetjs/install-and-require-helmet.md
new file mode 100644
index 0000000000..0a58f1d597
--- /dev/null
+++ b/curriculum/challenges/chinese-traditional/09-information-security/information-security-with-helmetjs/install-and-require-helmet.md
@@ -0,0 +1,46 @@
+---
+id: 587d8247367417b2b2512c36
+title: Install and Require Helmet
+challengeType: 2
+forumTopicId: 301581
+dashedName: install-and-require-helmet
+---
+
+# --description--
+
+As a reminder, this project is being built upon the following starter project on [Repl.it](https://repl.it/github/freeCodeCamp/boilerplate-infosec), or cloned from [GitHub](https://github.com/freeCodeCamp/boilerplate-infosec/).
+
+Helmet helps you secure your Express apps by setting various HTTP headers.
+
+# --instructions--
+
+All your code for these lessons goes in the `myApp.js` file between the lines of code we have started you off with. Do not change or delete the code we have added for you.
+
+Install Helmet version `3.21.3`, then require it.
+
+# --hints--
+
+`helmet` version `3.21.3` should be in `package.json`
+
+```js
+(getUserInput) =>
+ $.get(getUserInput('url') + '/_api/package.json').then(
+ (data) => {
+ var packJson = JSON.parse(data);
+ assert(packJson.dependencies.helmet === '3.21.3');
+ },
+ (xhr) => {
+ throw new Error(xhr.responseText);
+ }
+ );
+```
+
+# --solutions--
+
+```js
+/**
+ Backend challenges don't need solutions,
+ because they would need to be tested against a full working project.
+ Please check our contributing guidelines to learn more.
+*/
+```
diff --git a/curriculum/challenges/chinese-traditional/09-information-security/information-security-with-helmetjs/mitigate-the-risk-of-clickjacking-with-helmet.frameguard.md b/curriculum/challenges/chinese-traditional/09-information-security/information-security-with-helmetjs/mitigate-the-risk-of-clickjacking-with-helmet.frameguard.md
new file mode 100644
index 0000000000..1759d2e727
--- /dev/null
+++ b/curriculum/challenges/chinese-traditional/09-information-security/information-security-with-helmetjs/mitigate-the-risk-of-clickjacking-with-helmet.frameguard.md
@@ -0,0 +1,64 @@
+---
+id: 587d8247367417b2b2512c38
+title: Mitigate the Risk of Clickjacking with helmet.frameguard()
+challengeType: 2
+forumTopicId: 301582
+dashedName: mitigate-the-risk-of-clickjacking-with-helmet-frameguard
+---
+
+# --description--
+
+As a reminder, this project is being built upon the following starter project on [Repl.it](https://repl.it/github/freeCodeCamp/boilerplate-infosec), or cloned from [GitHub](https://github.com/freeCodeCamp/boilerplate-infosec/).
+
+Your page could be put in a `
` or `
+
+# --hints--
+
+`gamma` should be a function.
+
+```js
+assert(typeof gamma == 'function');
+```
+
+`gamma(.1)` should return a number.
+
+```js
+assert(typeof gamma(0.1) == 'number');
+```
+
+`gamma(.1)` should return `9.513507698668736`.
+
+```js
+assert.equal(round(gamma(0.1)), round(9.513507698668736));
+```
+
+`gamma(.2)` should return `4.590843711998803`.
+
+```js
+assert.equal(round(gamma(0.2)), round(4.590843711998803));
+```
+
+`gamma(.3)` should return `2.9915689876875904`.
+
+```js
+assert.equal(round(gamma(0.3)), round(2.9915689876875904));
+```
+
+`gamma(.4)` should return `2.218159543757687`.
+
+```js
+assert.equal(round(gamma(0.4)), round(2.218159543757687));
+```
+
+`gamma(.5)` should return `1.7724538509055159`.
+
+```js
+assert.equal(round(gamma(0.5)), round(1.7724538509055159));
+```
+
+# --seed--
+
+## --after-user-code--
+
+```js
+function round(x) {
+ return Number(x).toPrecision(13);
+}
+```
+
+## --seed-contents--
+
+```js
+function gamma(x) {
+
+}
+```
+
+# --solutions--
+
+```js
+function gamma(x) {
+ var p = [0.99999999999980993, 676.5203681218851, -1259.1392167224028,
+ 771.32342877765313, -176.61502916214059, 12.507343278686905,
+ -0.13857109526572012, 9.9843695780195716e-6, 1.5056327351493116e-7
+ ];
+
+ var g = 7;
+ if (x < 0.5) {
+ return Math.PI / (Math.sin(Math.PI * x) * gamma(1 - x));
+ }
+
+ x -= 1;
+ var a = p[0];
+ var t = x + g + 0.5;
+ for (var i = 1; i < p.length; i++) {
+ a += p[i] / (x + i);
+ }
+
+ var result=Math.sqrt(2 * Math.PI) * Math.pow(t, x + 0.5) * Math.exp(-t) * a;
+
+ return result;
+}
+```
diff --git a/curriculum/challenges/chinese-traditional/10-coding-interview-prep/rosetta-code/gaussian-elimination.md b/curriculum/challenges/chinese-traditional/10-coding-interview-prep/rosetta-code/gaussian-elimination.md
new file mode 100644
index 0000000000..2e5adf4bd9
--- /dev/null
+++ b/curriculum/challenges/chinese-traditional/10-coding-interview-prep/rosetta-code/gaussian-elimination.md
@@ -0,0 +1,230 @@
+---
+id: 5a23c84252665b21eecc7e77
+title: Gaussian elimination
+challengeType: 5
+forumTopicId: 302272
+dashedName: gaussian-elimination
+---
+
+# --description--
+
+Write a function to solve \\(Ax = b\\) using Gaussian elimination then backwards substitution.
+
+\\(A\\) being an \\(n \\times n\\) matrix. Also, \\(x\\) and \\(b\\) are \\(n\\) by 1 vectors.
+
+To improve accuracy, please use partial pivoting and scaling.
+
+# --hints--
+
+`gaussianElimination` should be a function.
+
+```js
+assert(typeof gaussianElimination == 'function');
+```
+
+`gaussianElimination([[1,1],[1,-1]], [5,1])` should return an array.
+
+```js
+assert(
+ Array.isArray(
+ gaussianElimination(
+ [
+ [1, 1],
+ [1, -1]
+ ],
+ [5, 1]
+ )
+ )
+);
+```
+
+`gaussianElimination([[1,1],[1,-1]], [5,1])` should return `[ 3, 2 ]`.
+
+```js
+assert.deepEqual(
+ gaussianElimination(
+ [
+ [1, 1],
+ [1, -1]
+ ],
+ [5, 1]
+ ),
+ [3, 2]
+);
+```
+
+`gaussianElimination([[2,3],[2,1]] , [8,4])` should return `[ 1, 2 ]`.
+
+```js
+assert.deepEqual(
+ gaussianElimination(
+ [
+ [2, 3],
+ [2, 1]
+ ],
+ [8, 4]
+ ),
+ [1, 2]
+);
+```
+
+`gaussianElimination([[1,3],[5,-2]], [14,19])` should return `[ 5, 3 ]`.
+
+```js
+assert.deepEqual(
+ gaussianElimination(
+ [
+ [1, 3],
+ [5, -2]
+ ],
+ [14, 19]
+ ),
+ [5, 3]
+);
+```
+
+`gaussianElimination([[1,1],[5,-1]] , [10,14])` should return `[ 4, 6 ]`.
+
+```js
+assert.deepEqual(
+ gaussianElimination(
+ [
+ [1, 1],
+ [5, -1]
+ ],
+ [10, 14]
+ ),
+ [4, 6]
+);
+```
+
+`gaussianElimination([[1,2,3],[4,5,6],[7,8,8]] , [6,15,23])` should return `[ 1, 1, 1 ]`.
+
+```js
+assert.deepEqual(
+ gaussianElimination(
+ [
+ [1, 2, 3],
+ [4, 5, 6],
+ [7, 8, 8]
+ ],
+ [6, 15, 23]
+ ),
+ [1, 1, 1]
+);
+```
+
+# --seed--
+
+## --seed-contents--
+
+```js
+function gaussianElimination(A,b) {
+
+}
+```
+
+# --solutions--
+
+```js
+function gaussianElimination(A, b) {
+ // Lower Upper Decomposition
+ function ludcmp(A) {
+ // A is a matrix that we want to decompose into Lower and Upper matrices.
+ var d = true
+ var n = A.length
+ var idx = new Array(n) // Output vector with row permutations from partial pivoting
+ var vv = new Array(n) // Scaling information
+
+ for (var i=0; i