` 元素為空的,代表著畫面不會印出任何訊息,直到我們添加內容進去。我們還給了它 `id`,讓 JavaScript 可以容易地存取它。
+
+回到檔案 `app.js`,建立新的補助函數 `updateElement`:
+
+```js
+function updateElement(id, text) {
+ const element = document.getElementById(id);
+ element.textContent = text;
+}
+```
+
+這條就很直觀:給定元素的 *id* 與 *text*,它會更新 DOM 元素內符合 `id` 條件的文字內容。我們也使用這個方法到前面 `login` 函式的錯誤訊息中:
+
+```js
+if (data.error) {
+ return updateElement('loginError', data.error);
+}
+```
+
+現在,試著以不合法的帳戶進行登入,你應該能看到像這樣的畫面:
+
+
+
+現在我們印出錯誤訊息,但螢幕報讀器並沒有做任何報讀。為了讓被動態加入的文字能被螢幕報讀器閱讀出來,我們需要使用 [Live Region](https://developer.mozilla.org/en-US/docs/Web/Accessibility/ARIA/ARIA_Live_Regions)。這邊我們使用一種 Live Region 的類型 alert:
+
+```html
+
+```
+
+建立同樣的行為到函式 `register` 的錯誤訊息當中,也別忘了更新你的 HTML。
+
+## 在儀表板顯示資訊
+
+使用同樣的技巧,我們需要將帳戶資訊印在儀表板頁面中。
+
+這是從伺服器接收到的帳戶資料物件:
+
+```json
+{
+ "user": "test",
+ "currency": "$",
+ "description": "Test account",
+ "balance": 75,
+ "transactions": [
+ { "id": "1", "date": "2020-10-01", "object": "Pocket money", "amount": 50 },
+ { "id": "2", "date": "2020-10-03", "object": "Book", "amount": -10 },
+ { "id": "3", "date": "2020-10-04", "object": "Sandwich", "amount": -5 }
+ ],
+}
+```
+
+> 筆記:為了讓開發更加的容易,你可以使用已經存在資料的帳戶 `test`。
+
+### 課題
+
+我們開始置換掉 HTML 檔內的 "Balance" 區域,加入放置區:
+
+```html
+
+```
+
+我們還需要在下方新增區域來顯示帳戶資訊:
+
+```html
+
+```
+
+✅ 表示帳戶資訊的函式剛好為在內容的標題處,我們可以將它作為語義化的標頭。學習更多關於[標頭結構](https://www.nomensa.com/blog/2017/how-structure-headings-web-accessibility),它對於網頁親和力格外重要,也明顯地表達出頁面的標頭位置。
+
+接著,我們在 `app.js` 檔案中加入新的函式來為放置區新增內容:
+
+```js
+function updateDashboard() {
+ if (!account) {
+ return navigate('/login');
+ }
+
+ updateElement('description', account.description);
+ updateElement('balance', account.balance.toFixed(2));
+ updateElement('currency', account.currency);
+}
+```
+
+首先,我們需要先檢查帳戶的資料。使用我們之前建立的函式 `updateElement()` 來更新 HTML 檔。
+
+> 為了讓帳戶餘額漂亮地呈現,我們使用 [`toFixed(2)`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number/toFixed) 這個方法,強迫數值只顯示到小數點第二位。
+
+現在,每當儀表板被載入時,我們就需要呼叫函式 `updateDashboard()`。如果你已經完成[課程一的作業](../../1-template-route/translations/assignment.zh-tw.md),就不需要額外做處理,不然你可以使用接下來的設定。
+
+加入這段程式碼到函式 `updateRoute()` 的下方:
+
+```js
+if (typeof route.init === 'function') {
+ route.init();
+}
+```
+
+並更新路由定義:
+
+```js
+const routes = {
+ '/login': { templateId: 'login' },
+ '/dashboard': { templateId: 'dashboard', init: updateDashboard }
+};
+```
+
+做完這些更動後,當儀表板要被呈現時,函式 `updateDashboard() 就會被呼叫。在你登入後就能看到帳戶的描述、餘額與交易狀況。
+
+## 利用 HTML 模板動態建立表格列
+
+在[第一堂課](../../1-template-route/translations/README.zh-tw.md)中,我們使用 HTML 模板與方法 [`appendChild()`](https://developer.mozilla.org/en-US/docs/Web/API/Node/appendChild) 來做出應用程式內的轉換。模板還能執行更小規模的行為,動態地改變一部份的頁面
+
+我們使用類似的方式來顯示 HTML 表格中的交易清單。
+
+### 課題
+
+加入新的模板到 HTML 的 `` 中:
+
+```html
+
+
+ |
+ |
+ |
+
+
+```
+
+這個模板表示單一條的表格列,其中包含了三格欄位:交易的*日期*、*物件* 與 *金額*。
+
+接著,加入 `id` 屬性到模板的表格 `
` 元素中,讓 JavaScript 能更容易地取得:
+
+```html
+
+```
+
+當我們的 HTML 準備好時,我們切換到 JavaScript 檔案中,加入新函式 `createTransactionRow`:
+
+```js
+function createTransactionRow(transaction) {
+ const template = document.getElementById('transaction');
+ const transactionRow = template.content.cloneNode(true);
+ const tr = transactionRow.querySelector('tr');
+ tr.children[0].textContent = transaction.date;
+ tr.children[1].textContent = transaction.object;
+ tr.children[2].textContent = transaction.amount.toFixed(2);
+ return transactionRow;
+}
+```
+
+這個函式做就如它名字的功能:藉由剛建立的模板,建立出新的表格列並填入交易明細的資料。我們會在函式 `updateDashboard()` 中,利用它來更新表格:
+
+```js
+const transactionsRows = document.createDocumentFragment();
+for (const transaction of account.transactions) {
+ const transactionRow = createTransactionRow(transaction);
+ transactionsRows.appendChild(transactionRow);
+}
+updateElement('transactions', transactionsRows);
+```
+
+這裡我們使用了方法 [`document.createDocumentFragment()`](https://developer.mozilla.org/en-US/docs/Web/API/Document/createDocumentFragment),建立新的 DOM 分段,再接到我們的 HTML 表格中。
+
+我們還需要做一件事才能讓程式運作正常,目前函式 `updateElement()` 只能接受文字類型的內容。我們稍微修改一下程式碼:
+
+```js
+function updateElement(id, textOrNode) {
+ const element = document.getElementById(id);
+ element.textContent = ''; // Removes all children
+ element.append(textOrNode);
+}
+```
+
+我們使用方法 [`append()`](https://developer.mozilla.org/en-US/docs/Web/API/ParentNode/append),它能連接文字或者是 [DOM 節點](https://developer.mozilla.org/en-US/docs/Web/API/Node)到父元素中,正好滿足我們的需求。
+
+試著以 `test` 帳戶來登入,你應該能看到儀表板顯示出交易明細了 🎉。
+
+---
+
+## 🚀 挑戰
+
+花功夫讓儀表板頁面看起來像是正規的銀行界面。如果你已經為你的應用程式做好造型,你可以試試 [media queries](https://developer.mozilla.org/en-US/docs/Web/CSS/Media_Queries) 來建立出[回應式網頁設計](https://developer.mozilla.org/en-US/docs/Web/Progressive_web_apps/Responsive/responsive_design_building_blocks),它能完美地呈現在電腦或是行動裝置上。
+
+這邊有造型過後的儀表板例子:
+
+
+
+## 課後測驗
+
+[課後測驗](https://nice-beach-0fe9e9d0f.azurestaticapps.net/quiz/46)
+
+## 作業
+
+[重構並註解你的程式碼](assignment.zh-tw.md)
diff --git a/7-bank-project/3-data/translations/assignment.zh-tw.md b/7-bank-project/3-data/translations/assignment.zh-tw.md
new file mode 100644
index 00000000..91b20b47
--- /dev/null
+++ b/7-bank-project/3-data/translations/assignment.zh-tw.md
@@ -0,0 +1,15 @@
+# 重構並註解你的程式碼
+
+## 簡介
+
+正當你的程式碼越來越多時,頻繁地重構你的程式,讓程式碼容易去閱讀與維護變得十分重要。加入一些註解並重構檔案 `app.js` 來增進檔案的品質:
+
+- 取出常數,好比說伺服器 API 的根網址
+- 重構相同的程式碼:舉例來說,你可以建立函式 `sendRequest()` 來合併 `createAccount()` 與 `getAccount()`。
+- 重新編排你的程式和加入註解,讓它更容易地閱讀。
+
+## 學習評量
+
+| 作業內容 | 優良 | 普通 | 待改進 |
+| -------- | ----------------------------------------------------------------------------- | ------------------------------------------------------------ | ---------------------------------------------- |
+| | 程式碼有做註解,分塊地整理好。常數有被取出來且函式 `sendRequest()` 已設定完成 | 程式碼有做過處理,但仍可以藉由加入註解、排版與重構來增進品質 | 程式碼很雜亂,缺乏註解,常數與函式並沒有做規劃 |
diff --git a/7-bank-project/4-state-management/translations/README.zh-tw.md b/7-bank-project/4-state-management/translations/README.zh-tw.md
new file mode 100644
index 00000000..4626a56c
--- /dev/null
+++ b/7-bank-project/4-state-management/translations/README.zh-tw.md
@@ -0,0 +1,284 @@
+# 建立銀行網頁應用程式 Part 4: 狀態控管的概念
+
+## 課前測驗
+
+[課前測驗](https://nice-beach-0fe9e9d0f.azurestaticapps.net/quiz/47)
+
+### 大綱
+
+隨著網頁應用越來越龐大,追蹤資料流的動向也是一種挑戰。程式取得了何種資料、網頁如何處理它、何時何處被更新上去……這些很容易地導致程式碼凌亂而難以維護。尤其是當你需要在不同頁面上做資料共享時,好比說使用者的資料。*狀態控管(state management)* 的觀念已經存在於所有程式中,我們也開始需要在開發複雜的網頁應用程式時,注意這個關鍵點。
+
+在這個最終章內,我們會總覽整個程式並重新思考該如何管理程式狀態,讓瀏覽器能在任何時刻做重新整理,在不同的使用者階段維持資料的狀態。
+
+### 開始之前
+
+你需要先完成[取得資料](../../3-data/translations/README.zh-tw.md)的網頁開發章節。你還需要安裝 [Node.js](https://nodejs.org) 並於本地端[執行伺服器 API](../../api/translations/README.zh-tw.md)以管理使用者資料。
+
+你可以測試伺服器是否運作正常,在終端機中輸入指令:
+
+```sh
+curl http://localhost:5000/api
+# -> should return "Bank API v1.0.0" as a result
+```
+
+---
+
+## 思考狀態控管
+
+在[前一堂課](../../3-data/translations/README.zh-tw.md)中,我們介紹了應用程式基本的狀態,全域變數 `account` 提供登入帳戶的相關銀行資料。然而,現在的專案存在著一些瑕疵。試著在儀表板介面中重新整理。發生了什麼事?
+
+目前我們的程式碼有三個問題:
+
+- 網頁狀態並沒有被儲存,當瀏覽器重新整理時,會被導回登入頁面。
+- 有許多函式會修改網頁狀態。隨著應用程式變大,我們很難去追蹤之後的改變,時刻地去更新相關的網頁狀態。
+- 網頁狀態並不完整,當你*登出*帳戶時,帳戶資訊仍然顯示在登入頁面上。
+
+我們是可以逐一的解決這些問題,但這樣會創造出許多獨立的程式碼,讓應用程式更複雜而難以去管理。或者是我們停下來思考一下我們的策略。
+
+> 我們究竟要解決什麼問題?
+
+[狀態控管(State management)](https://en.wikipedia.org/wiki/State_management)可以為兩項問題提供良好的解決方案:
+
+- 如何讓應用程式中的資料流容易理解?
+- 如何讓網頁狀態一直與使用者介面,或是相關物件進行同步?
+
+一旦你處理好這些問題,其他問題可以被簡化,甚至被一併解決。有許多可能的方法能解決這些問題,但我們使用一種常見的解法:**中心化資料與更新方式**。資料流會呈現下列模式:
+
+
+
+> 我們不會處理如何讓資料同步觸發頁面的更新,這比較像是關於[回應式程式設計](https://zh.wikipedia.org/wiki/%E5%93%8D%E5%BA%94%E5%BC%8F%E7%BC%96%E7%A8%8B)的更進階知識。當你更深入網頁開發領域時,這是個很好的發展方向。
+
+✅ 有許多函式庫提供狀態管理的方式,[Redux](https://redux.js.org) 就是常見的選擇。閱讀它的概念與運作模式,這是種有效的的學習方式,讓你在大型的網頁開發中預測潛在的風險,並預想解決方案。
+
+### 課題
+
+我們會先做一些程式重構。替換掉 `account` 的定義:
+
+```js
+let account = null;
+```
+
+變成:
+
+```js
+let state = {
+ account: null
+};
+```
+
+這個構想是要*中心化*應用程式資料到一個狀態物件中。目前我們只有 `account` 在狀態中,但這能提供未來新增新功能的基礎。
+
+我們還需要更新與它相關的函式。在函式 `register()` 和 `login()` ,將 `account = ...` 替換為 `state.account = ...`。
+
+在函式 `updateDashboard()` 的上方,加入此行:
+
+```js
+const account = state.account;
+```
+
+這個重構並不會帶來任何提升,但這是之後改變上的基礎。
+This refactoring by itself did not bring much improvements, but the idea was to lay out the foundation for the next changes.
+
+## 追蹤資料改變
+
+現在我們有 `state` 物件儲存資料了,接下來要來中心化這些更新。目標是能輕易地追蹤任何被觸發的改變。
+
+為了避免改動 `state` 物件,我們考慮使它[*不可變*](https://zh.wikipedia.org/wiki/%E4%B8%8D%E5%8F%AF%E8%AE%8A%E7%89%A9%E4%BB%B6),意味著它不能被做任何的修改。
+這也代表你必須建立新的狀態物件來替換它。藉由這個方式,你就有一套保護措施阻絕潛在非預期[風險](https://zh.wikipedia.org/wiki/%E5%89%AF%E4%BD%9C%E7%94%A8_(%E8%AE%A1%E7%AE%97%E6%9C%BA%E7%A7%91%E5%AD%A6)),也開創出應用程式內還原與重做的功能,讓程式偵錯更加的容易。舉例來說,你可以紀錄狀態的改變,儲存狀態的歷史紀錄來了解錯誤的來源。
+
+在 JavaScript 中,你可以使用 [`Object.freeze()`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/freeze) 來建立不可變物件。若你想在不可變物件上做更動,例外處理(exception)就會發生。
+
+✅ 你知道*淺複製(shallow)*和*深複製(deep)*這兩種不可變物件的差別嗎?你可以從[這裡](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/freeze#What_is_shallow_freeze)閱讀相關資訊。
+
+### 課題
+
+我們來建立新的函式 `updateState()`:
+
+```js
+function updateState(property, newData) {
+ state = Object.freeze({
+ ...state,
+ [property]: newData
+ });
+}
+```
+
+在這個函式中,我們會建立新的狀態物件,並利用[*展開運算子(`...`)(Spread Operator)*](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Spread_syntax#Spread_in_object_literals)複製前一個資料狀態。接著,我們使用[括弧記法(Bracket Notation)](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Working_with_Objects#Objects_and_properties) `[property]` 賦予並覆蓋特定的狀態物件。最後,我們為物件上鎖,`Object.freeze()` 避免任何的改動。目前我們只有 `account` 資料存在狀態中,利用此方法可以讓你新增任何你想要的資料。
+
+我們會更新 `state` 初始化設定,確保初始狀態也被上鎖:
+
+```js
+let state = Object.freeze({
+ account: null
+});
+```
+
+接著,更新函式 `register`,將 `state.account = result;` 替換為:
+
+```js
+updateState('account', result);
+```
+
+在函式 `login` 上做一樣的事,將 `state.account = data;` 替換為:
+
+```js
+updateState('account', data);
+```
+
+藉由這個機會,我們能解決帳戶資料在*登出*時,不會被清除的問題。
+
+建立新的函式 `logout()`:
+
+```js
+function logout() {
+ updateState('account', null);
+ navigate('/login');
+}
+```
+
+在 `updateDashboard()` 中,替換重新導向 `return navigate('/login');` 為 `return logout()`。
+
+試著註冊新的帳戶,登入登出以確保功能都運作正常。
+
+> 提示:你可以觀察所有的狀態改變,在 `updateState()` 裡的最下方加入 `console.log(state)`,開啟瀏覽器開發工具,命令欄就會顯示狀態的紀錄。
+
+## 紀錄狀態
+
+多數的網頁應用程式需要儲存資料以確保運作正常。所有重要的資料都會存在資料庫中,並藉由伺服器 API 來存取,就像我們專案中的帳戶資料。但有時候,瀏覽器用戶端的應用程式也需要儲存一些資料,提供更好的使用者體驗與增進負載效能。
+
+當你想在瀏覽器內儲存資料,你必須思考幾項重要的問題:
+
+- *這項資料很危險嗎?* 你應該要避免在用戶端儲存敏感的資料,例如帳戶密碼。
+- *你需要儲存資料多久?* 你打算短時間內做存取,還是永久地保存?
+
+網頁應用程式中有許多儲存資訊的方法,一切都取決於你想達成的目標。舉例來說,你可以利用網址來儲存搜尋資訊,讓使用者間能共享資訊。若資料需要與伺服器共享,好比說[認證](https://zh.wikipedia.org/wiki/%E8%BA%AB%E4%BB%BD%E9%AA%8C%E8%AF%81)資訊,你可以使用 [HTTP cookies](https://developer.mozilla.org/en-US/docs/Web/HTTP/Cookies)。
+
+另一個選擇是使用其中一個廣大的瀏覽器 API 來儲存資料。下列這兩項就特別有趣:
+
+- [`localStorage`](https://developer.mozilla.org/en-US/docs/Web/API/Window/localStorage):[Key/Value 儲存法](https://zh.wikipedia.org/wiki/%E9%94%AE-%E5%80%BC%E5%AD%98%E5%82%A8)可以保存不同時刻的網頁資料。這些資料不會有期限的限制。
+- [`sessionStorage`](https://developer.mozilla.org/en-US/docs/Web/API/Window/sessionStorage):它的運作模式與 `localStorage` 相同,只差在資料會在網頁段落結束時被清除,如瀏覽器關閉時。
+
+紀錄一下這兩個 API 只能儲存[字串](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String)格式。
+如果你想儲存更複雜的物件,你需要利用 [`JSON.stringify()`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/JSON/stringify) 將資料整理成 [JSON](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/JSON) 格式。
+
+✅ 如果你想要建立不仰賴伺服器的網頁應用程式,你有辦法在用戶端建立資料庫。[`IndexedDB` API](https://developer.mozilla.org/en-US/docs/Web/API/IndexedDB_API) 可以應用在更進階的案例上,儲存更大量的資料,當然使用上也相對複雜。
+
+### 課題
+
+我們想讓使用者在登出之前,保持登入狀態。所以我們使用 `localStorage` 來儲存帳戶資料。首先,定義一組 key 來紀錄我們的資料內容。
+
+```js
+const storageKey = 'savedAccount';
+```
+
+在函式 `updateState()` 末端加入此行:
+
+```js
+localStorage.setItem(storageKey, JSON.stringify(state.account));
+```
+
+藉由此方式,帳戶資料就能保存下來,並隨著之前中心化後的狀態而更新。我們開始從之前的重構獲取效益了 🙂。
+
+當資料被儲存後,我們還需要在程式讀取時載入資料。在 `app.js` 下方編寫更多的初始化程式,建立新的函式 `init` 並收入之前的程式碼:
+
+```js
+function init() {
+ const savedAccount = localStorage.getItem(storageKey);
+ if (savedAccount) {
+ updateState('account', JSON.parse(savedAccount));
+ }
+
+ // 之前的初始化程式
+ window.onpopstate = () => updateRoute();
+ updateRoute();
+}
+
+init();
+```
+
+我們在此接收了儲存資料,並同步地更新狀態資訊。這必須在更新路由*之前*完成,否則有些程式碼會在頁面更新時,依據狀態來決定其行為。
+
+當儲存完帳戶資料後,我們也定義了*儀表板*頁面為我們的預設首頁。若程式沒有找到資料,儀表板頁面也能重新導向回*登入*頁面。在 `updateRoute()` 中,替換回傳值 `return navigate('/login');` 為 `return navigate('/dashboard');`。
+
+登入應用程式並重新整理頁面。你應該能維持在儀表板那頁。這個改變也解決了我們最初面臨的問題......
+
+## 重整資料
+
+......但我們可能也產生了新問題。啊呀!
+
+使用 `test` 帳戶進入儀表板頁面,在終端機內執行下列指令以建立新的交易項目:
+
+```sh
+curl --request POST \
+ --header "Content-Type: application/json" \
+ --data "{ \"date\": \"2020-07-24\", \"object\": \"Bought book\", \"amount\": -20 }" \
+ http://localhost:5000/api/accounts/test/transactions
+```
+
+試著重新整理瀏覽器內儀表板頁面。發生了什麼事?你有看到新的交易項目嗎?
+
+感謝 `localStorage` 的幫助,狀態成功的儲存下來,但也代表我們在登出登入之前,不能再改變它的內容了!
+
+一個可能的修復策略是在儀表板載入時,重新載入帳戶資訊以避免資料不同步。
+
+### 課題
+
+建立新的函式 `updateAccountData`:
+
+```js
+async function updateAccountData() {
+ const account = state.account;
+ if (!account) {
+ return logout();
+ }
+
+ const data = await getAccount(account.user);
+ if (data.error) {
+ return logout();
+ }
+
+ updateState('account', data);
+}
+```
+
+這個方法能檢查我們是否已經登入,重新從伺服器載入用戶資料。
+
+建立另一個函式 `refresh`:
+
+```js
+async function refresh() {
+ await updateAccountData();
+ updateDashboard();
+}
+```
+
+這能更新帳戶資料,更新 HTML 中的儀表板頁面。這是在儀表板路由被載入時,我們所需要呼叫的函式。更新路由定義為:
+
+```js
+const routes = {
+ '/login': { templateId: 'login' },
+ '/dashboard': { templateId: 'dashboard', init: refresh }
+};
+```
+
+試著重新載入儀表板,它現在應該能顯示更新後的帳戶資料。
+
+---
+
+## 🚀 挑戰
+
+每一次儀表板載入時,我們都會重新載入帳戶資料,你認為我們還需要在用戶端儲存*所有的帳戶*資料嗎?
+
+試著改變 `localStorage` 內的儲存內容,只包含我們能運行程式的必要資料。
+
+## 課後測驗
+
+[課後測驗](https://nice-beach-0fe9e9d0f.azurestaticapps.net/quiz/48)
+
+## 作業
+
+[編寫"加入交易明細"視窗](assignment.zh-tw.md)
+
+這邊有完成之後的結果:
+
+
diff --git a/7-bank-project/4-state-management/translations/assignment.zh-tw.md b/7-bank-project/4-state-management/translations/assignment.zh-tw.md
new file mode 100644
index 00000000..6d58308b
--- /dev/null
+++ b/7-bank-project/4-state-management/translations/assignment.zh-tw.md
@@ -0,0 +1,25 @@
+# 編寫"加入交易明細"視窗
+
+## 簡介
+
+我們的銀行應用程式還缺乏一項重要的功能:輸入新的交易明細。
+使用你在這四堂課中學到的知識,編寫"加入交易明細"視窗:
+
+- 在儀表板頁面新增"加入交易明細"按鈕
+- 加入新的 HTML 模板建立新頁面,或是在同一頁面中使用 JavaScript 顯示 HTML 窗格(可以使用 [`hidden`](https://developer.mozilla.org/en-US/docs/Web/HTML/Global_attributes/hidden) 屬性,或是 CSS classes)
+- 確保視窗能滿足[鍵盤與螢幕報讀器的相容性](https://developer.paciellogroup.com/blog/2018/06/the-current-state-of-modal-dialog-accessibility/)
+- 編寫 HTML 表單來接收輸入資料
+- 建立 JSON 表單資料並傳送到 API 上
+- 使用新資料更新到儀表板頁面上
+
+看看[伺服器 API 規格](../../api/README.zh-tw.md)來查詢你需要呼叫的 API 和所需的 JSON 格式。
+
+這邊有完成作業後的成果:
+
+
+
+## 學習評量
+
+| 作業內容 | 優良 | 普通 | 待改進 |
+| -------- | ------------------------------------ | -------------------------------------------------- | ---------------------- |
+| | 利用課程內容完美的製作出交易明細功能 | 有製作出交易明細功能,但有缺少部分要點且功能不完全 | 新的交易明細功能不正常 |
diff --git a/7-bank-project/api/translations/README.zh-tw.md b/7-bank-project/api/translations/README.zh-tw.md
new file mode 100644
index 00000000..6dda5b4d
--- /dev/null
+++ b/7-bank-project/api/translations/README.zh-tw.md
@@ -0,0 +1,33 @@
+# 銀行 API (Bank API)
+
+> 由 [Node.js](https://nodejs.org) 與 [Express](https://expressjs.com/) 建立而成。
+
+這套 API 已經建好而不在本次課程的範疇內。
+
+然而,如果你想學習如何建立 API,你可以追蹤這一系列的影片:https://aka.ms/NodeBeginner (影片 17 到 21 為這套 API)。
+
+你也可以看看這套互動式教學: https://aka.ms/learn/express-api
+
+## 運行伺服器
+
+確保你的 [Node.js](https://nodejs.org) 已經安裝完成。
+
+1. Git clone 這個數據庫.
+2. 在資料夾 `api` 中開啟終端機,執行 `npm install`。
+3. 執行 `npm start`。
+
+伺服器應該要在連接埠 `5000` 上監聽訊息。
+
+> 筆記:所有儲存的資料不是永久保存的,伺服器終止時會遺失所有資料。
+
+## API 項目
+
+路由 | 描述
+---------------------------------------------|------------------------------------
+GET /api/ | 取得伺服器資訊
+POST /api/accounts/ | 建立新的帳戶,範例: `{ user: 'Yohan', description: 'My budget', currency: 'EUR', balance: 100 }`
+GET /api/accounts/:user | 取得特定帳戶的所有資料
+DELETE /api/accounts/:user | 移除特定帳戶
+POST /api/accounts/:user/transactions | 建立新的交易明細,範例: `{ date: '2020-07-23T18:25:43.511Z', object: 'Bought a book', amount: -20 }`
+DELETE /api/accounts/:user/transactions/:id | 移除特定交易明細
+
diff --git a/quiz-app/src/assets/translations/zh-tw.json b/quiz-app/src/assets/translations/zh-tw.json
index 98cc0464..6a54b33b 100644
--- a/quiz-app/src/assets/translations/zh-tw.json
+++ b/quiz-app/src/assets/translations/zh-tw.json
@@ -2362,7 +2362,7 @@
"questionText": "為什麼阻止用戶輸入不信任的資料很重要?",
"answerOptions": [
{
- "answerText": "因為使用奇特字元讓UI變很醜。",
+ "answerText": "因為使用奇特字元讓 UI 變得很醜。",
"isCorrect": "false"
},
{
@@ -2433,7 +2433,7 @@
]
},
{
- "questionText": "下列何者為最合適的方式來儲存不同session中重要的用戶資料?",
+ "questionText": "下列何者為最合適的方式來儲存不同 session 中重要的用戶資料?",
"answerOptions": [
{
"answerText": "利用檔案。",