Hướng dẫn lập trình Flask – Phần 14: Sử dụng Ajax

flask_tutorial_1

Trong phần này, chúng ta sẽ tìm hiểu cách sử dụng dịch vụ dịch thuật của Microsoft để thêm chức năng phiên dịch tức thời vào ứng dụng và một ít về JavaScript.

Để giúp cho bạn dễ theo dõi, sau đây là danh sách các bài viết trong loạt bài hướng dẫn này:

Bạn có thể truy cập mã nguồn cho phần này tại GitHub.

Trong phần này, chúng ta sẽ bắt đầu rời khỏi vùng “an toàn” với việc chỉ viết các mã nguồn cho server. Thay vào đó, chúng ta sẽ phát triển một tính năng mới với các thành phần trên cả server và client (cụ thể là trên trình duyệt của user). Đã bao giờ bạn thấy các liên kết “Phiên dịch” (Translate) trên một số Web site chưa? Các liên kết này sẽ kích hoạt quá trình phiên dịch tự động theo thời gian thực với các nội dung không sử dụng tiếng mẹ đẻ của user. Các nội dung được dịch ra thường được đặt bên dưới các nội dung gốc. Google hiển thị các nội dung này trong phần tiếng nước ngoài, Facebook và Twitter cũng làm như vậy với các bài viết. Hôm nay, chúng ta cũng sẽ đưa tính năng này vào ứng dụng nhỏ của chúng ta.

Phân biệt các thành phần của phần mềm chạy trên Server và trên Client

Theo mô hình truyền thống của các phần mềm chạy trên Server mà chúng ta đang sử dụng trong ứng dụng, chương trình khách (client) – trong trường hợp này là các trình duyệt đang chạy trên máy tính của user – sẽ gởi các yêu cầu HTTP đến máy chủ ứng dụng (server). Một yêu cầu có thể chỉ đơn giản là lấy một trang HTML từ máy chủ về máy khách như trong trường hợp bạn bấm vào liên kết “Hồ sơ cá nhân” hoặc kích hoạt một quá trình nào đó như khi bạn bấm vào nút “Submit” sau khi soạn thảo hồ sơ cá nhân của bạn. Trong cả hai trường hợp, server sẽ hoàn tất yêu cầu với việc gởi một trang Web mới cho client một cách trực tiếp hoặc thi hành một lệnh chuyển hướng. Client sẽ thay thế trang hiện tại bằng một trang mới. Chu trình này sẽ được lặp lại trong khi user còn sử dụng ứng dụng. Trong mô hình này, server sẽ làm tất cả mọi công việc cần thiết. còn client chỉ hiển thị các trang Web và nhận các dữ liệu do user nhập vào.

Tuy vậy, đó không phải là mô hình duy nhất trong các ứng dụng kiểu này. Trong một số mô hình khác, client có thể đóng vai trò chủ động hơn. Trong mô hình này, client vẫn gởi các yêu cầu đến server và server vẫn đáp ứng bằng cách gởi trả các trang Web. Tuy nhiên, không như trong mô hình đầu tiên, không phải toàn bộ các dữ liệu trong trang Web được server gởi về là HTML mà một phần trong đó sẽ chứa mã có thể được thực thi, thường là JavaScript. Khi client nhận được các trang Web này, nó sẽ hiển thị phần HTML và thi hành phần mã kèm theo. Kể từ đó, client sẽ có thể thực hiện một số công việc tự nó mà chỉ cần rất ít hoặc không cần tương tác với server. Trong các ứng dụng thuần client, toàn bộ ứng dụng sẽ được tải về và được thực thi ở phía client sau yêu cầu đầu tiên từ client đến server. Client chỉ liên lạc với server khi cần đọc hoặc lưu dữ liệu và sẽ tự cập nhật các thành phần hiển thị trên trang Web đầu tiên và duy nhất đó. Các ứng dụng loại này được gọi là Single Page Applications (ứng dụng đơn trang hay SPAs).

Phần lớn các ứng dụng kết hợp hai mô hình trên và các kỹ thuật được sử dụng trong hai mô hình này. Ứng dụng blog của chúng ta chủ yếu đi theo mô hình phía server, nhưng hôm nay chúng ta sẽ thêm một ít mã cho client với tính năng phiên dịch tức thời. Để thực hiện công việc này, trình duyệt sẽ gởi một yêu cầu bất đồng bộ (asynchronous requests) đến server. Khi server trả lời, client sẽ tiến hành cập nhật trang Web hiện tại bằng cách chèn thêm các nội dung được dịch vào trang mà không làm ảnh hưởng đến các phần khác của trang. Kỹ thuật này được gọi là Ajax, viết tắt của cụm từ Asynchronous JavaScript and XML (JavaScript bất đồng bộ và XML – tuy vậy ngày nay XML thường được thay thế bởi JSON trong phần lớn các ứng dụng).

Tiến trình dịch tức thời (Live Translation)

Nhờ Flask-Babel, ứng dụng đã được hỗ trợ đa ngôn ngữ. Tuy vậy, vẫn còn một chỗ chưa hoàn thiện. Các user có thể viết bài bằng tiếng mẹ đẻ của họ, vì vậy hoàn toàn có thể có trường hợp một user nào đó gặp bài viết bằng các thứ tiếng khác mà họ không hiểu. Chất lượng của việc phiên dịch tự động không phải lúc nào cũng tốt, nhưng trong phần lớn trường hợp, nó tạm đủ để chúng ta có thể hiểu được nội dung cơ bản của một văn bản trong một ngôn ngữ khác.

Đây là một tính năng lý tưởng để thực hiện bằng kỹ thuật Ajax. Chúng ta có thể tưởng tượng trường hợp trang chủ hoặc trang “Explore” (Khám phá) có các bài viết với các thứ tiếng khác nhau. Nếu chúng ta thực hiện việc phiên dịch chỉ ở phía server, mỗi một yêu cầu phiên dịch từ client sẽ được server trả lời bằng một trang mới và bắt buộc client thay thế trang hiện hành bằng trang mới này. Thực sự thì việc phiên dịch một vài bài trên một trang không cần thiết để phải cập nhật toàn bộ trang. Vì vậy, sẽ tốt hơn rất nhiều nếu ứng dụng chỉ phiên dịch các bài cần được phiên dịch và chèn các nội dung này bên dưới các bài gốc mà không làm ảnh hưởng đến các phần khác trên cùng trang.

Việc xây dựng chức năng dịch tức thời cần đi qua một số bước. Đầu tiên, chúng ta phải tìm ra cách để xác định ngôn ngữ gốc của văn bản cần dịch. Chúng ta cũng cần biết ngôn ngữ thích hợp cho mỗi user bởi vì chúng ta chỉ muốn hiển thị liên kết “translate” cho các bài viết không sử dụng tiếng mẹ đẻ của mỗi user. Khi các liên kết này được hiển thị và user bấm vào chúng, chúng ta sẽ gởi các yêu cầu Ajax từ client đến server, và server sẽ liên lạc với các nhà cung cấp dịch vụ phiên dịch thông qua các API để dịch và nhận được văn bản đã được phiên dịch. Sau khi server trả lời với văn bản đã dịch, client sẽ thực thi mã JavaScript để chèn văn bản này vào trang hiện hành. Có một vài điểm quan trọng trong tiến trình này và chúng ta sẽ lần lượt giải quyết từng điểm một.

Nhận dạng ngôn ngữ gốc

Bước đầu tiên trong tiến trình dịch là xác định ngôn ngữ gốc của bài viết. Không phải lúc nào chúng ta cũng có thể xác định được ngôn ngữ gốc một cách dễ dàng, nhưng trong phần lớn trường hợp, việc tìm ra ngôn ngữ bằng các công cụ tự động làm việc tương đối tốt. Với Python, chúng ta có thể sử dụng thư viện guess_language để xác định ngôn ngữ. Phiên bản gốc của thư viện này đã cũ và không sử dụng Python 3, vì vậy chúng ta sẽ dùng một phiên bản được phát triển từ thư viện này có hỗ trợ cả Python 2 và 3:

Chúng ta sẽ sử dụng thư viện này để xác định ngôn ngữ gốc trong mỗi bài viết. Vì công việc này rất mất thời gian, chúng ta không muốn lặp lại quá trình này mỗi khi hiển thị một bài viết. Thay vào đó, chúng ta sẽ tìm cách xác định và thiết lập ngôn ngữ gốc khi bài viết được đăng. Thông tin về ngôn ngữ gốc sẽ được lưu trong bảng post của cơ sở dữ liệu.

Đầu tiên, chúng ta sẽ thêm trường language vào mô hình Post:

app/models.py: Thêm ngôn ngữ gốc vào mô hình Post.

Nếu bạn còn nhớ, mỗi lần chúng ta thay đổi mô hình dữ liệu, chúng ta cần thực hiện quá trình cập nhập cơ sở dữ liệu, bắt đầu với việc tạo ra mã kịch bản chuyển đổi dữ liệu.

Tiếp theo, chúng ta sẽ thực thi mã kịch bản này để cập nhật cơ sở dữ liệu:

Từ đây, chúng ta có thể tiến hành nhận dạng và lưu ngôn ngữ gốc vào cơ sở dữ liệu khi một bài viết được đăng:

app/routes.py: Nhận dạng và lưu ngôn ngữ gốc của bài viết.

Mỗi khi một bài viết được đăng, chúng ta sẽ đưa văn bản này vào hàm guess_language để nhận dạng ngôn ngữ gốc. Nếu kết quả do hàm này trả về là “Unknown” (không nhận ra) hoặc quá dài, chúng ta sẽ dùng phương pháp an toàn nhất là lưu một chuỗi trống vào cơ sở dữ liệu. Đồng thời, chúng ta cũng sẽ quy ước rằng bất kỳ bài viết nào trong cơ sở dữ liệu có ngôn ngữ được gán bởi một chuỗi trống là các bài viết không nhận dạng được ngôn ngữ gốc.

Hiển thị liên kết “Translate” (Phiên dịch)

Bước thứ hai dễ dàng hơn. Chúng ta sẽ thêm liên kết “Translate” vào các bài viết không sử dụng ngôn ngữ được thiết lập làm ngôn ngữ chính trong hệ thống của user.

app/templates/_post.html: Thêm liên kết “Translate” vào các bài viết.

Chúng ta cập nhật template con _post.html để áp dụng điều này trên tất cả các trang có hiển thị bài viết. Liên kết phiên dịch chỉ xuất hiện trong các bài viết thỏa mãn hai điều kiện:

  1. Ứng dụng có thể nhận dạng ngôn ngữ trong bài viết
  2. Ngôn ngữ trong bài viết khác với ngôn ngữ được chọn trong hàm có sử dụng decorator localeselector từ thư viện Flask-Babel

Nếu bạn còn nhớ, trong Phần 13, chúng ta lưu các thiết lập địa phương trong đối tượng g.locale. Chuỗi văn bản được hiển thị trong liên kết cũng cần được phiên dịch, vì vậy chúng ta đã sử dụng hàm _() như trên.

Lưu ý rằng chúng ta chưa đặc tả hành động cho liên kết phiên dịch trong đoạn mã trên. Trước hết, chúng ta cần tìm ra cách để thực hiện việc dịch tự động.

Sử dụng dịch vụ dịch thuật từ một nhà cung cấp thứ ba

HIện nay, hai dịch vụ dịch thuật chủ yếu là Google Cloud Translation APIMicrosoft Translator Text API. Cả hai dịch vụ này đều là dịch vụ trả phí, nhưng Microsoft cung cấp dịch vụ miễn phí cho các yêu cầu  dịch thuật cơ bản và có mức độ sử dụng thấp. Trong quá khứ, Google cũng cung cấp dịch vụ miễn phí, nhưng hiện nay thì kể các dịch vụ cơ bản của họ cũng được tính phí. Và bởi vì chúng ta đang thử nghiệm dịch vụ dịch thuật trong ứng dụng, chúng ta sẽ sử dụng dịch vụ của Microsoft để không tốn kém.

Để có thể sử dụng các API của dịch vụ dịch thuật của Microsoft, bạn cần có một tài khoản Azure – dịch vụ điện toán đám mây của Microsoft. Trong quá trình đăng ký, bạn có thể chọn tier (lớp) miễn phí. Dù rằng bạn vẫn phải đưa vào số thẻ tín dụng trong quá trình đăng ký, bạn sẽ không mất tiền khi sử dụng lớp dịch vụ này.

Sau khi đã có tài khoản Azure, bạn có thể vào Azure Portal và bấm vào nút “Create a resource” ở góc trên bên trái và nhập vào “Translator Text” trong hộp tìm kiếm “Search the Marketplace”. Khi bạn bấm vào nút “Create”, bạn sẽ thấy một form để định nghĩa các tài nguyên sẽ được dùng trong quá trình phiên dịch vào tài khoản của bạn. Sau đây là một ví dụ về các thông tin mà chúng ta sẽ đặc tả trong form này:

MS Translation Service Registration information

Sau đó, bấm vào nút “Create” ở cuối form để hoàn tất thủ tục khởi tạo và Azure sẽ thêm một tài nguyên cho phép truy xuất các API phiên dịch vào tài khoản của bạn. Nếu bạn đợi một chút, bạn sẽ nhận được thông báo là quá trình triển khai (deploy) API đã hoàn tất. Bạn có thể bấm vào nút “Go to resource” để vào trang thông tin về các API phiên dịch và bấm vào mục “Keys” trong bảng bên trái. Bạn sẽ thấy hai khóa và được đặt tên là “Key 1” và “Key 2”. Bạn có thể chọn một trong hai khóa này và copy vào biến môi trường “MS_TRANSLATOR_KEY” trong cửa sổ lệnh:

Khóa này được dùng để xác thực tài khoản của bạn với dịch vụ phiên dịch của Microsoft, vì vậy, chúng ta cần đưa nó vào thiết lập cấu hình cho ứng dụng:

config.py: Thêm khóa cho Microsoft Translator API vào cấu hình.

Cũng như các tham số cấu hình khác, chúng ta nên đưa các giá trị cấu hình vào các biến môi trường và tham chiếu trong đến các biến môi trường thay vì trực tiếp sử dụng chúng trong file cấu hình. Điều này rất quan trọng để bảo đảm sự an toàn cho các thông tin nhạy cảm như khóa hoặc mật mã để sử dụng các dịch vụ từ các nhà cung cấp thứ ba (third-party).

Microsoft Translator API là một dịch vụ Web (Web service) và sẽ nhận các dữ liệu đầu vào từ các yêu cầu HTTP. Có một số các thư viện HTTP trong Python, nhưng thư viện phổ biến và dễ sử dụng nhất là requests. Chúng ta sẽ cài đặt thư viện này như sau:

Dưới đây là mã nguồn để sử dụng Microsoft Translator API cho quá trình dịch thuật tự động trong module  app/translate.py:

app/translate.py: Hàm phiên dịch văn bản.

Hàm này nhận các tham số là văn bản cần được phiên dịch, mã của ngôn ngữ nguồn và ngôn ngữ đích và trả về một chuỗi là văn bản đã được dịch. Đầu tiên, nó sẽ kiểm tra khóa để sử dụng dịch vụ phiên dịch trong cấu hình ứng dụng, và nếu không tìm được khóa này, nó sẽ trả về một lỗi. Bản thân lỗi được trả về cũng là một chuỗi văn bản. Vì vậy, đối với người sử dụng, nó trông như là một văn bản đã được dịch. Điều này bảo đảm rằng trong trường hợp phát sinh lỗi, ngưởi sử dụng sẽ thấy một thông báo lỗi mà họ có thể đọc và hiểu được.

Hàm post() trong thư viện requests gởi một yêu cầu HTTP với phương thức POST đến địa chỉ (URL) được truyền vào trong tham số đầu tiên. Địa chỉ /api.cognitive.microsofttranslator.com/translate mà chúng ta sử dụng là một điểm cuối (endpoint) của dịch vụ phiên dịch và trả về chuỗi được dịch ra trong một gói dữ liệu theo định dạng JSON. Chuỗi văn bản cần dịch, ngôn ngữ nguồn và ngôn ngữ đích cũng cần được đưa vào địa chỉ này dưới dạng các tham số tương ứng gọi là text, fromto. Để được xác thực với dịch vụ, chúng ta cần truyền vào khóa mà chúng ta đã thêm vào cấu hình ứng dụng lúc trước. Khóa này cần được đặt vào một phần đầu (header) HTTP tùy biến tên là Ocp-Apim-Subscription-Key. Để làm điều này, chúng ta tạo một từ điển gồm có khóa này và kiểu dữ liệu (application/json) và đưa từ điển này vào phần headers khi sử dụng thư viện requests.

Phương thức requests.post() sẽ trả về một đối tượng chứa các thông tin trong phản hồi (response hoặc r theo đoạn mã trên) của dịch vụ. Đầu tiên, chúng ta sẽ kiểm tra xem mã trạng thái trong phản hồi này có phải là 200 hay không (đây là mã trạng thái khi truy cập API thành công). Nếu chúng ta nhận được các mã trạng thái khác, chúng ta biết rằng có lỗi phát sinh trong quá trình gọi API. Trong trường hợp đó, chúng ta sẽ sẽ về một chuỗi báo lỗi. Nếu mã trạng thái là 200, phần thân (body) của dữ liệu trả về từ server sẽ chứa một chuỗi JSON được mã hóa với văn bản đã được dịch. Vì vậy, việc chúng ta cần làm là đổi chuỗi này sang một đối tượng JSON và trả về văn bản đã được dịch từ thuộc tính text trong đối tượng này nhờ đoạn mã r.json()[0]['translations'][0]['text'].

Dưới đây là ví dụ minh họa cách sử dụng hàm translate() trực tiếp với trình thông dịch Python:

Tuyệt vời phải không? Đã đến lúc chúng ta tích hợp chức năng này vào ứng dụng.

Xây dựng chức năng AJAX ở Server

Chúng ta sẽ bắt đầu với việc viết mã cho server. Khi user bấm vào liên kết Translate dưới một bài viết, một yêu cầu HTTP bất đồng bộ (asynchronous HTTP request) sẽ được gởi đến server. Tạm thời chúng ta sẽ chưa xây dựng mã để tạo ra các yêu cầu này, thay vào đó, chúng ta sẽ tập trung vào vấn đề xử lý yêu cầu này tại server.

Một yêu cầu bất đồng bộ (hay Ajax) tương tự như là các định tuyến và hàm hiển thị mà chúng ta đã tạo ra trước đây. Điểm khác biệt duy nhất là thay vì trả về chuỗi HTML hay chuyển hướng người sử dụng, nó sẽ trả về dữ liệu trong định dạng XML hay phổ thông hơn là JSON. Dưới đây là hàm hiển thị cho quá trình phiên dịch. Hàm này sẽ gọi Microsoft Translator API và trả về chuỗi đã được dịch với định dạng JSON:

app/routes.py: Hàm hiển thị phiên dịch.

Như bạn thấy, hàm translate_text() rất đơn giản. Nó sẽ xử lý các yêu cầu theo dạng POST được trình duyệt gởi đến. Cần lưu ý là không có quy định tuyệt đối khi nào thì sử dụng POST hoặc GET (hoặc các dạng khác mà bạn còn chưa thấy). Nhưng vì ở đây chúng ta cần nhận dữ liệu từ trình duyệt, chúng ta sẽ sử dụng các yêu cầu POST tương tự như khi xử lý các yêu cầu từ các trang có dùng form. Thuộc tính form.data là một từ điển (dictionary) được Flask dùng để chứa tất cả các dữ liệu được gởi đến khi user submit một form. Trước đây, khi làm việc với các web form, chúng ta không cần sử dụng đến request.form vì Flask-WTF đã làm sẵn những việc cần thiết.  Nhưng trong trường hợp này, bởi vì không có web form, chúng ta phải truy cập dữ liệu trực tiếp từ request.form.

Hàm này sẽ gọi hàm translate() mà chúng ta đã tìm hiểu ở phần đầu của bài này và truyền vào ba tham số có trong dữ liệu được gởi đến từ trình duyệt của user. Kết quả sẽ được kết hợp vào một từ điển có một khóa duy nhất là text và được đưa vào hàm jsonify() của Flask để chuyển đổi thành dữ liệu dạng JSON. Kết quả trả về của hàm jsonify() sẽ được gởi trả về cho trình duyệt của user như là một phản hồi HTTP. Ví dụ khi user muốn dịch chuỗi Hello, World! sang tiếng Việt,  phản hồi của yêu cầu này sẽ có dữ liệu như sau:

Xây dựng chức năng AJAX cho client

Bây giờ, server đã có chức năng dịch tại địa chỉ /translate, chúng ta cần sử dụng địa chỉ này khi user bấm vào liên kết “Translate” đã được thêm vào ở trên, truyền vào văn bản cần dịch, ngôn ngữ nguồn và ngôn ngữ đích. Nếu bạn chưa quen thuộc với JavaScript, đây sẽ là một cơ hội tốt để bạn tìm hiểu.

Đối với trình duyệt, một trang Web mà nó hiển thị sẽ được đại diện bằng một cấu trúc nội bộ gọi là Document Object Model hay gọi tắt là DOM. Đây là một cấu trúc phân cấp (hierarchical) với tham chiếu đến tất cả các thành phần có trong trang. Khi mã JavaScript chạy trong một trang thay đổi các thành phần cấu trúc này, các thành phần tương ứng trong trang Web cũng sẽ được thay đổi và hiển thị một cách phù hợp.

Vậy làm sao mã JavaScript có thể lấy được các tham số mà chúng ta cần gởi cho hàm phiên dịch tại server? Để lấy được văn bản cần dịch, chúng ta cần tìm ra nút (node) tương ứng trong DOM có chứa bài được đăng và đọc nội dung của nó. Để dễ xác định các node trong DOM có chứa các bài viết, chúng ta sẽ gán một ID đơn nhất cho chúng. Trong template _post.html, dòng để hiển thị bài viết là {{ post.body }}. Chúng ta sẽ đặt dòng này vào bên trong một thẻ . Điều này không làm ảnh hưởng đến giao diện, nhưng sẽ cho phép chúng ta gán ID cần thiết:

app/templates/_post.html: Gán ID cho mỗi bài viết.

Đoạn mã HTML trên sẽ gán các ID khác nhau cho từng bài viết theo định dạng post1, post2, … với số nguyên trong định danh được lấy ra từ định danh của bài viết tương ứng trong cơ sở dữ liệu. Sau khi mỗi bài viết đã có một ID, chúng ta có thể dùng jQuery để xác định vị trí của nội dung bài viết nằm trong thẻ với ID tương ứng và lấy ra chuỗi văn bản của bài viết đó. Ví dụ như để lấy được nội dung của bài viết với ID là 100, chúng ta có thể dùng đoạn mã jQuery như sau:

Dấu $ trong đoạn mã trên là tên của một hàm do thư viện jQuery cung cấp. Thư viện jQuery có sẵn trong Flask-Bootstrap vì Bootstrap sử dụng nó. Theo quy ước của jQuery, dấu # được dùng để tìm và chọn một thành phần HTML bằng ID của nó.

Chúng ta cũng cần có một nơi để chèn đoạn văn bản đã được dịch vào. Để đơn giản, chúng ta sẽ thay thế liên kết “Translate” với văn bản mới, và do đó, chúng ta cũng cần gán ID cho cho nút đó:

app/templates/_post.html: Gán ID cho các liên kết “Translate”.

Từ bây giờ, khi đã có ID của một bài viết, chúng ta có thể định vị bài viết đó tại node có định danh post và đặt nội dung đã được dịch tương ứng vào node có định danh translation.

Tiếp theo, chúng ta sẽ viết một hàm để thực hiện việc phiên dịch. Hàm này sẽ nhận và trả về các nút trong DOM kèm theo ngôn ngữ nguồn và đích, phát ra một yêu cầu bất đồng bộ đến server với bat ham số cần thiết. Và cuối cùng nó sẽ thay thế liên kết “Translate” với văn bản đã được dịch sau khi nhận được văn bản này từ server. Nói thì có vẻ nhiều, nhưng mã nguồn cho hàm này lại tương đối đơn giản:

app/templates/base.html: Hàm dịch thuật cho client.

Hai tham số đầu tiên của hàm lần lượt là ID của các nút HTML có chứa bài viết và liên kết “Translate”. Hai tham số cuối là ngôn ngữ nguồn và đích.

Khi hàm này bắt đầu thực hiện, nó sẽ thay thế liên kết “Translate” bằng một icon quay vòng (spinner) để thông báo trực quan với user rằng quá trình phiên dịch đang được thực hiện. Chúng ta làm điều này bằng cách gọi hàm $(destElem).html() trong thư viện jQuery để thay thế mã HTML hiển thị liên kết “Translate” với mã HTML để hiển thị ảnh (sử dụng thẻ ). Để tạo ra icon này, chúng ta sẽ thêm một ảnh GIF động gọi là loading.gif vào thư mục chứa các file tĩnh app/static/ theo quy ước của Flask. Để tạo ra URL cho ảnh này, chúng ta dùng hàm url_for() và truyền vào một định tuyến đặc biệt là static kèm theo một tham số là tên của file ảnh. Bạn có thể tải về file ảnh này trong mã nguồn cho chương này tại GitHub.

Bây giờ chúng ta đã có một icon động khá đẹp để user biết rằng họ cần chờ trong khi quá trình dịch đang được thực hiện. Bước tiếp theo là gởi một yêu cầu dạng POST đến địa chỉ /translate mà chúng ta đã định nghĩa trước đó. Chúng ta sẽ sử dụng jQuery một lần nữa, nhưng lần này chúng ta sẽ gọi hàm $.post(). Hàm này sẽ gởi dữ liệu về server tương tự như cách trình duyệt làm khi user bấm nút “Submit” trên một form nào đó. Vì vậy, các dữ liệu được gởi đi cũng được đóng gói vào trong từ điển request.form tương tự như trường hợp khi submit một form. Khi gọi hàm này, chúng ta đưa vào hai tham số là URL để gởi dữ liệu đến và một từ điển (hay một đối tượng theo cách gọi của JavaScript) với ba giá trị mà server cần như đã nói ở trên. Có lẽ bạn đã biết rằng trong JavaScript, chúng ta sử dụng các hàm callback (gọi lại) hoặc một dạng cao cấp hơn của các hàm gọi lại là promise rất thường xuyên. Các hàm này cho phép các chương trình JavaScript thực hiện các công việc cần thiết sau khi chúng nhận được kết quả từ các yêu cầu bất đồng bộ đến server trước đó. Lý do là vì khi thực hiện một yêu cầu bất đồng bộ, các chương trình JavaScript sẽ không ngừng lại để chờ kết quả từ server mà sẽ tiếp tục thực hiện. Do đó, có rất nhiều khả năng là tại thời điểm server xử lý xong yêu cầu và trả về kết quả, các chương trình JavaScript tại client đã thi hành xong. Vì thế, các hàm callback là cần thiết để chương trình được kích hoạt trở lại và xử lý các kết quả do server trả về hoặc xử lý lỗi nếu có phát sinh. Có một số phương pháp để định nghĩa các hàm callback, nhưng trong trường hợp này, chúng ta sẽ định nghĩa một hàm promise theo cú pháp như sau:

Cú pháp của hàm promise cho phép chúng ta ghép (chain) các hàm callback vào giá trị trả về từ hàm $.post(). Trong trường hợp hàm được gọi thành công, chúng ta chỉ cần gọi $(destElem).text() với văn bản đã được dịch có trong từ điển dữ liệu do server gởi về với khóa text. Nếu việc phiên dịch tại server gặp lỗi hoặc có lỗi trong quá trình gởi yêu cầu từ trình duyệt đến server, chúng ta cũng xử lý giống như vậy nhưng sẽ hiển thị một thông báo lỗi tổng quát. Thông báo lỗi này sẽ được đưa vào template base.html và cũng sẽ được dịch ra các ngôn ngữ khác.

Cuối cùng, chúng ta cần tìm cách kích hoạt hàm translate() với các tham số cần thiết khi user bấm vào liên kết “Translate”. Cũng có vài cách khác nhau để làm điều này, nhưng chúng ta sẽ chọn cách đơn giản nhất là đặt trực tiếp hàm này vào thuộc tính href của liên kết:

app/templates/_post.html: Phương pháp xử lý liên kết “Translate”.

Thuộc tính href của các liên kết trong HTML có thể chấp nhận mã JavaScript miễn là nó bắt đầu với chuỗi javascript:. Điều này cho phép chúng ta có thể gọi hàm này một cách thuận lợi. Bởi vì liên kết này sẽ được tạo ra ở server khi trình duyệt yêu cầu trang Web tương ứng, chúng ta có thể sử dụng biểu thức {{ }} để tạo ra bốn tham số cần có cho hàm này. Mỗi bài viết sẽ có liên kết dịch thuật khác nhau với các ID riêng biệt. Ký tự # đứng trước các thành phần posttranslation chỉ ra rằng đây là các ID cho các thành phần tương ứng.

Đến đây, chức năng dịch tức thời xem như đã hoàn tất. Nếu bạn sử dụng một khóa hợp lệ cho Microsoft Translator API trong môi trường phát triển của bạn, chức năng này sẽ làm việc. Giả sử bạn thiết lập ngôn ngữ mặc định trong trình duyệt của bạn là tiếng Việt, bạn cần phải đăng bài viết bằng một ngôn ngữ khác để thấy liên kết “Translate”. Sau đây là một ví dụ:

Live translation from Vietnamese to English

Trong phần này, chúng ta đã sử dụng một số chuỗi văn bản mới trong ứng dụng. Vì vậy, chúng ta cũng phải dịch các chuỗi này sang các ngôn ngữ được ứng dụng hỗ trợ. Để làm điều này, chúng ta cần cập nhật các chuỗi cần phiên dịch:

Tùy thuộc vào các thiết lập trong môi trường làm việc của bạn, bạn sẽ phải soạn thảo file messages.po cho mỗi ngôn ngữ mà ứng dụng của bạn hỗ trợ. Nhưng trong mã nguồn cho phần này từ GitHub, các chuỗi tiếng Việt đã được phiên dịch sẵn và bạn có thể tải về.

Sau khi đã soạn thảo các file .po, bạn cần thực hiện lệnh chuyển đổi:

Chúng ta sẽ kết thúc phần này ở đây. Hẹn gặp bạn trong phần tiếp theo.

Leave a Reply

Your email address will not be published. Required fields are marked *