LINGUIJS

UserUser CultureCulture trên URLTest CultureChuyển đổi ngôn ngữInternationalizationLanguages, Charset, và EncodingTemplatesi18n:extractTranslations với ArgumentsFormsPropel ObjectsAdmin GeneratorLocalizationTemplates
Ngày hôm qua, chúng ta đã hoàn thành chức năng tìm kiếm bằng cách thêm vào một vàitiện ích AJAX.

Bạn đang xem: Linguijs

Hôm nay, chúng ta sẽ nói về việc internationalization (i18n) vàlocalization (l10n) ứng dụng Jobeet.

From Wikipedia:

Internationalization is the process of designing a software application so that it can be adapted to various languages and regions without engineering changes.

Localization is the process of adapting software for a specific region or language by adding locale-specific components and translating text.

Như thường lệ, cqaugusta.com framework không "làm lại cái bánh xe" và hỗ trợ i18n vàl10n dựa trên ICU standard.

User

Không thể internationalization mà không có user. Khi website của bạncó các lựa chọn ngôn ngữ khác nhau cho cac vùng trên thế giới, usersẽ chọn ngôn ngữ phù hợp với mình.


note

Chúng ta đã nói về class cqaugusta.com User ở ngày 13.


User Culture

Tính năng i18n và l10n của cqaugusta.com dựa trên user culture. Culture là sự kết hợp giữa ngôn ngữ và quốc gia của user. Ví dụ, culture của user nói tiếng Pháp (fr) và sống ở nước Pháp sẽ là fr_FR.

Bạn có thể quản lý user culture thông qua phương thức setCulture() vàgetCulture() của object User:


// in an action$this->getUser()->setCulture("fr_BE");echo $this->getUser()->getCulture();
tip

Mã ngôn ngữ là 2 kí tự viết thường, theo tiêu chuẩn ISO 639-1, và mã nước là 2 kí tự viết hoa, theo tiêu chuẩn ISO 3166-1.


Culture mặc định

Culture mặc định được xác định trong file settings.yml:


# apps/frontend/config/settings.ymlall: .settings: default_culture: it_IT
tip

Do culture được quản lý bởi object User, nên nó được chứa trong session. Trong quá trình phát triển, nếu bạn thay đổi culture mặc định, bạn cần phải xóa session cookie để thiết lập mới có hiệu quả.


Khi một user bắt đầu một session trên Jobeet website, chúng ta có thể quyết địnhculture phù hợp nhất, dựa trên thông tin cung cấp bởi Accept-Language HTTPheader.

Phương thức getLanguages() của request object trả về mảng các ngôn ngữ phù hợp vớiuser hiện tại, sắp xếp theo thứ tự ưu tiên:


// in an action$languages = $request->getLanguages();
Nhưng website của bạn thường không có hết 136 ngôn ngữ chính trên thế giới. Phương thức getPreferredCulture() trả về ngôn ngữ thích hợp nhất bằng cách so sánh các ngôn ngữ phù hợp vớiuser với các ngôn ngữ mà website hỗ trợ:


// in an action$language = $request->getPreferredCulture(array("en", "fr"));
Nếu không có ngôn ngữ nào phù hợp, ngôn ngữ đầu tiên trong mảng (ở đây là English) sẽ được chọn.

Culture trên URL

Website Jobeet hỗ trợ English và French. Do mỗi URL chỉ có thể tương ứng với mộtresource, nên culture phải được nhúng trong URL. Để thực hiện điều này, mở file routing.yml,và thêm biến :sf_culture vào tất cả routes trừ api_jobs và homepage. Để đơn giảnroute, ta thêm /:sf_culture vào trước của url. Với collection route, ta thêm mộtprefix_path option bắt đầu với /:sf_culture.


# apps/frontend/config/routing.ymlaffiliate: class: sfPropelRouteCollection options: model: JobeetAffiliate actions: object_actions: { wait: get } prefix_path: /:sf_culture/affiliatecategory: url: /:sf_culture/category/:slug.:sf_format class: sfPropelRoute param: { module: category, action: show, sf_format: html } options: { model: JobeetCategory, type: object } requirements: sf_format: (?:html|atom)job_search: url: /:sf_culture/search param: { module: job, action: search }job: class: sfPropelRouteCollection options: model: JobeetJob column: token object_actions: { publish: put, extend: put } prefix_path: /:sf_culture/job requirements: token: \w+job_show_user: url: /:sf_culture/job/:company_slug/:location_slug/:id/:position_slug class: sfPropelRoute options: model: JobeetJob type: object method_for_criteria: doSelectActive param: { module: job, action: show } requirements: id: \d+ sf_method: get
Khi biến sf_culture được sử dụng ở route, cqaugusta.com sẽ tự động sử dụng giá trị nàyđể xác định culture của user.

Do chúng ta cần trang chủ hỗ trợ nhiều ngôn ngữ (/en/, /fr/, ...),nên trang chủ mặc định (/) phải được chuyển sang ngôn ngữ phù hợp với culture của user. Nhưng nếu user chưa xác định culture, do anh ta truy cập vào Jobeet lần đầu tiên, hệ thống sẽ tự chon culture thích hợp.

Thêm phương thức isFirstRequest() vào myUser, trả về true nếu làrequest đầu tiên của một user session:


// apps/frontend/lib/myUser.class.phppublic function isFirstRequest($boolean = null){ if (is_null($boolean)) { return $this->getAttribute("first_request", true); } else { $this->setAttribute("first_request", $boolean); }}
Thêm route localized_homepage :


# apps/frontend/config/routing.ymllocalized_homepage: url: /:sf_culture/ param: { module: job, action: index } requirements: sf_culture: (?:fr|en)
Sửa lại action index của module job để chuyểnngười dùng trang trang chủ với ngôn ngữ phù hợp:


// apps/frontend/modules/job/actions/actions.class.phppublic function executeIndex(sfWebRequest $request){ if (!$request->getParameter("sf_culture")) { if ($this->getUser()->isFirstRequest()) { $culture = $request->getPreferredCulture(array("en", "fr")); $this->getUser()->setCulture($culture); $this->getUser()->isFirstRequest(false); } else { $culture = $this->getUser()->getCulture(); } $this->redirect("
localized_homepage"); } $this->categories = JobeetCategoryPeer::getWithJobs();}
Nếu biến sf_culture chưa có trong request, có nghĩa làuser truy cập vào trang web từ URL /. Trong trường hợp này và session là mới,culture thích hợp nhất sẽ được chọn. Ngược lại, culture hiện tại chứa trong session sẽ được sử dụng.

Cuối cùng, chuyển user tới URL localized_homepage. Chú ý rằng, ta ko cần cung cấpbiến sf_culture, cqaugusta.com sẽ tự động làm việc này cho bạn.

Khi truy cập URL /it/, cqaugusta.com sẽ trả về một 404 error do chúng tagiới hạn biến sf_culture là en, hoặc fr. Thêm yêu cầu này vào tất cả cácroute có chứa culture:


requirements: sf_culture: (?:fr|en)

Test Culture

Bây giờ, đã đến lúc test những gì chúng ta đã làm. Nhưng trước khi thêm các test, chúng ta cần sửa lại các test cũ.Do tất cả các URL đã thay đổi, ta cần sửa lại tất cả các functional testtrong test/functional/frontend/ và thêm /en vào trước các URL. Đừng quênđổi lại các URL trong file lib/test/JobeetTestFunctional.class.php. Chạy test suite để kiểm tra xem bạn đã sửa đúng chưa:


$ php cqaugusta.com test:functional frontend
User tester cung cấp phương thức isCulture() để kiểm tra culture hiện tại của user. Mở file jobActionsTest và thêm đoạn test sau:


// test/functional/frontend/jobActionsTest.php$browser->setHttpHeader("ACCEPT_LANGUAGE", "fr_FR,fr,en;q=0.7");$browser-> info("6 - User culture")-> restart()-> info(" 6.1 - For the first request, cqaugusta.com guesses the best culture")-> get("/")-> isRedirected()->followRedirect()-> with("user")->isCulture("fr")-> info(" 6.2 - Available cultures are en and fr")-> get("/it/")-> with("response")->isStatusCode(404);$browser->setHttpHeader("ACCEPT_LANGUAGE", "en,fr;q=0.7");$browser-> info(" 6.3 - The culture guessing is only for the first request")-> get("/")-> isRedirected()->followRedirect()-> with("user")->isCulture("fr");

Chuyển đổi ngôn ngữ

Để user có thể chuyển sang ngôn ngữ khác, ta cần thêm một language form vàolayout. Form framework không cung cấp form ngoài nhưng điều này là cần thiết đối với websites đa ngôn ngữ, do đó cqaugusta.com core team đã phát triểnsfFormExtraPlugin,chứa các validator, widget, và form hữu ích.

Xem thêm: Cách Nấu Lẩu Gà Rượu Nếp Hỏa Lò 21 Nguyễn Siêu, Lẩu Gà Hỏa Lò 21 Nguyễn Siêu

Cài đặt plugin với task plugin:install:


$ php cqaugusta.com plugin:install sfFormExtraPlugin
Xóa cache để các class mới của plugin có tác dụng:


$ php cqaugusta.com cc
note

sfFormExtraPlugin chứa các widget yêu cầu một số thư viện liên quan, như thư viện JavaScript. Bạn sẽ tìm thấy một widget cho rich date selectors, một cho WYSIWYG editor, ... Hãy dành chút thời gian đọc hướng dẫn bạn sẽ tìm thấy nhiều thông tin hữu ích.


Plugin sfFormExtraPlugin cung cấp một form sfFormLanguage để quản lý việc chọn ngôn ngữ.Thêm language form vào layout như sau:


note

Code dưới đây chứa một vài lỗi để chỉ cho bạn thấy cách viết code không đúng. Chúng tôi sẽ chỉ cho bạn cách viết đúng ngay sau đó.


// apps/frontend/templates/layout.php
"footer">
class="content"> $form = new sfFormLanguage( $sf_user, array("languages" => array("en", "fr")) ) ?> ""> echo $form ?>"submit" value="ok" />
Vấn đề ở đây là gì? Đó chính là việc tạo form object không nằm ởView layer. Nó phải được tạo từ một action. Nhưng code nằm tronglayout, và form phải được tạo với mọi action.Trong trường hợp này, bạn có thể sử dụng component. Một component tương tự như partialnhưng có thêm một vài đoạn code gắn với nó. CÓ thể coi nó là một lightweight action.

Include một component trong template được thực hiện thông qua helperinclude_component():


// apps/frontend/templates/layout.php
"footer">
class="content"> include_component("language", "language") ?>
Helper nhận tham số là tên module và component. Tham số thứ 3là các giá trị cung cấp cho component đó.

Tạo module language để chứa component và action thực hiện việc chuyển đổi ngôn ngữ cho user:


$ php cqaugusta.com generate:module frontend language
Components được xác định trong file actions/components.class.php.

Nội dung file này:


// apps/frontend/modules/language/actions/components.class.phpclass languageComponents extends sfComponents{ public function executeLanguage(sfWebRequest $request) { $this->form = new sfFormLanguage( $this->getUser(), array("languages" => array("en", "fr")) ); }}
Như bạn có thể thấy, class component tương tự như một class action.

Template cho một component có cách đặt tên tương tự partial: một kí tự gạch dưới (_) sau đó là tên component:


// apps/frontend/modules/language/templates/_language.php""> echo $form ?>"submit" value="ok" />
Do plugin không cung cấp action thực hiện việc chuyển userculture, thêm vào file routing.yml route change_language:


# apps/frontend/config/routing.ymlchange_language: url: /change_language param: { module: language, action: changeLanguage }
Và tạo action tương ứng:


// apps/frontend/modules/language/actions/actions.class.phpclass languageActions extends sfActions{ public function executeChangeLanguage(sfWebRequest $request) { $form = new sfFormLanguage( $this->getUser(), array("languages" => array("en", "fr")) ); $form->process($request); return $this->redirect("
localized_homepage"); }}
Phương thức process() của sfFormLanguage thực hiện việc đổi culture cho user, dựa vào form submission của user.

*

Internationalization

Languages, Charset, và Encoding

Những ngôn ngữ khác nhau có tập các kí tự khác nhau. Tiếng Anh là ngôn ngữ đơn giản nhất do chỉ sử dụngcác kí tự ASCII, tiếng Pháp phức tạp hơn một chút với các kí tự nhấn trọng âm như"é", những ngôn ngữ như Russian, Chinese, hay Arabic thì rất phức tạp do các kí tự nằm ngoài dải kí tựASCII.

Khi làm việc với dữ liệu international, tốt hơn là sử dụng tiêu chuẩn unicode.Tư tưởng của unicode là thiết lập một tập chung chứa các kí tự của tất cả các ngôn ngữ.Vấn đề với unicode là một kí tự đơn cần 21 bits để mô tả. Do đó,với web, chúng ta sử dụng UTF-8, sẽ map Unicode code points với variable-lengthsequences của octets. Trong UTF-8, hầu hết các ngôn ngữ có mã kí tự với độ dài ít hơn3 bits.

UTF-8 là encode mặc định trong cqaugusta.com, được xác định trong file cấu hìnhsettings.yml:


# apps/frontend/config/settings.ymlall: .settings: charset: utf-8
Để enable internationalization layer của cqaugusta.com, bạn phải bậti18n setting trong settings.yml:


# apps/frontend/config/settings.ymlall: .settings: i18n: on

Templates

Một website đa ngôn ngữ có nghĩa là nội dung sẽ được dịch ra vài ngôn ngữ.

Trong một template, tất cả các câu phụ thuộc ngôn ngữ được chứa trong helper__() (chú ý rằng có 2 kí tự gạch dưới).

Helper __() là một phần của I18N helper group, chứa các helperđể dễ dàng quản lý i18n trong template. Do helper group này mặc định không được load,bạn cần tự thêm nó vào template với use_helper("I18N") như chúng ta đã làm với Text helper group, hoặc có thểload nó ở global bằng cách thêm vào standard_helpers setting:


# apps/frontend/config/settings.ymlall: .settings: standard_helpers:
Đây là cách sử dụng __() helper cho Jobeet footer:


// apps/frontend/templates/layout.php
"footer">
class="content"> class="cqaugusta.com">

*

Nhúng form i18n sử dụng method embedI18N():


// lib/form/JobeetCategoryForm.class.phpclass JobeetCategoryForm extends BaseJobeetCategoryForm{ public function configure() { unset($this<"jobeet_category_affiliate_list">); $this->embedI18n(array("en", "fr")); $this->widgetSchema->setLabel("en", "English"); $this->widgetSchema->setLabel("fr", "French"); }}
Giao diện admin generator hỗ trợ internationalization.Nó chứa sẵn bản dịch của hơn 20 ngôn ngữ, và dễ dàng thêm một bản dịch mới, hoặc sửa bản dịch có sẵn.Sửa file cho ngôn ngữ bạn muốn thay đổi (admin translations có thể tìm tronglib/vendor/cqaugusta.com/lib/plugins/sfPropelPlugin/i18n/) ở applicationi18n. Do những file trong ứng dụng của bạn cũng được sử dụng, nên chỉ cần quan tâm đến các chuỗi chưa có trong file ứng dụng.

Bạn có thể để ý thấy rằng file dịch của admin generator có tên làsf_admin.fr.xml, thay vì fr/messages.xml. Thực tế là,messages là tên của catalogue, và được thay đổi để có thể dùng ở tất cả các phần của ứng dụng.Sử dụng một catalogue thay vì mặc định yêu cầu bạn phải chỉ rõ nơi dịch với helper __():


echo __("About Jobeet", array(), "jobeet") ?>
Trong lời gọi __() trên, cqaugusta.com sẽ tìm chuỗi "About Jobeet" trong catalogue jobeet.

Tests

Fixing tests is an integral part of the internationalization migration. First,update the test fixtures for categories by copying the fixtures we havedefined above in test/fixtures/010_categories.yml.

Rebuild the model for the test environment:


$ php cqaugusta.com propel:build-all-load --no-confirmation --env=test
You can now launch all tests to check that they are running fine:


$ php cqaugusta.com test:all
note

When we have developed the backend interface for Jobeet, we have not written functional tests. But whenever you create a module with the cqaugusta.com command line, cqaugusta.com also generate test stubs. These stubs are safe to remove.


Localization

Templates

Hỗ trợ các culture khác nhau cũng có nghĩa là hỗ trợ các cách khác nhau để formatdate và number. Trong một template, có vài helper giúp bạn thực hiện điều này dựa trên culture hiện tại của user:

Trong Date helpergroup:

Helper Mô tả
format_date() Formats a date
format_datetime() Formats a date

Trong Number helpergroup:

Helper Mô tả
format_number() Formats a number
format_currency() Formats a currency

Trong I18N helpergroup:

Helper Mô tả
format_country() Displays the name of a country
format_language() Displays the name of a language

Forms

Form framework cung cấp một vài widgets and validators cho dữ liệu local:

Hẹn gặp lại ngày mai

Internationalization và localization là tính năng được hỗ trợ sẵn trong cqaugusta.com.Xây dựng một website đa ngôn ngữ thật đơn giản do cqaugusta.com cung cấp tất cả các công cụ cơ bảnvà các task từ dòng lệnh để thực hiện điều đó nhanh chóng.

Để chuẩn bị cho hướng dẫn đặc biệt ngày mai, chúng ta sẽ chuyển rất nhiềufiles và khám phá sự khác nhau trong cách tổ chức một cqaugusta.com project.