Hướng dẫn dùng ngu alexander trong PHP

Đây là phần bốn trong loạt bài Xây dựng Khởi nghiệp Với PHP của bạn trên Tuts. Trong loạt bài này, tôi hướng dẫn bạn thông qua việc khởi chạy một khởi động từ khái niệm này sang thực tế bằng ứng dụng Meeting Planner của tôi như một ví dụ thực tế về cuộc sống. Mỗi bước trong bài viết, chúng tôi sẽ phát hành code của Meeting Planner làm ví dụ nguồn mở mà bạn có thể học hỏi. Chúng tôi cũng sẽ giải quyết các vấn đề kinh doanh phát sinh có liên quan đến startup.

Trong hướng dẫn này, tôi muốn quay trở lại và thêm phần hỗ trợ quốc tế hóa I18n vào ứng dụng của chúng ta trước khi chúng ta xây dựng nhiều code hơn nữa. Theo Wikipedia, I18n là một tên số:

18 là viết tắt của số lượng chữ cái giữa chữ I đầu tiên và n cuối cùng trong quá trình quốc tế hóa, một cách sử dụng được đặt ra vào tháng 12 năm 1970 hoặc thập niên 80.

Với I18n, tất cả các chuỗi văn bản được hiển thị cho người dùng từ ứng dụng được thay thế bằng các việc gọi hàm có thể tự động tải chuỗi đã dịch cho bất kỳ ngôn ngữ nào mà người dùng chọn.

Tất cả mã cho Công cụ lập kế hoạch cuộc họp được viết trong Khung công tác Yii2 cho PHP, đã hỗ trợ sẵn cho I18n. Nếu bạn muốn tìm hiểu thêm về Yii2, hãy xem loạt bài Programming with Yii2 của chúng tôi tại Tuts.

Chỉ xin nhắc rằng, tôi tham gia vào các chủ đề bình luận dưới đây. Tôi đặc biệt quan tâm nếu bạn có các cách tiếp cận khác nhau hoặc các ý tưởng bổ sung hoặc muốn đề xuất các chủ đề cho các hướng dẫn trong tương lai.

Mục tiêu của việc quốc tế hóa

Khi xây dựng một startup, sẽ rất hữu ích khi suy nghĩ toàn cầu ngay từ đầu - nhưng không phải lúc nào cũng vậy. Thay vào đó, có thể chỉ tập trung vào xây dựng cho thị trường nội địa của bạn. Sản phẩm tối thiểu của bạn có cần phải hoạt động ở các ngôn ngữ khác cho người dùng từ các quốc gia khác nhau không?

Trong trường hợp của chúng tôi, framework Yii cung cấp hỗ trợ xây dựng sẵn cho I18n, vì vậy việc xây dựng hỗ trợ cho I18n ngay từ đầu cũng rất dễ dàng và cần nhiều thời gian để bổ sung nó sau này.

Cách hoạt động của I18n

I18n hoạt động bằng cách thay thế tất cả các tham chiếu đến văn bản được hiển thị cho người dùng bằng các cuộc gọi hàm cung cấp bản dịch khi cần.

Ví dụ, đây là những gì tên trường thuộc tính trong model Place trông giống như trước I18n:

public function attributeLabels()
{
    return [
      'id' => 'ID',
       'name' => 'Name',
       'place_type' => 'Place Type',
       ...

Việc cung cấp các phiên bản mã đã dịch sẽ trở nên rất phức tạp. Người dịch không hiểu về kỹ thuật sẽ phải dịch mã, có khả năng phá vỡ cú pháp.

Đoạn code phía trên sẽ trông thế này với I18n:

public function attributeLabels()
    {
        return [
          'id' => Yii::t('frontend', 'ID'),
           'name' => Yii::t('frontend', 'Name'),
           'place_type' => Yii::t('frontend', 'Place Type'),

Yii:t() là một hàm để kiểm tra xem ngôn ngữ nào đang được chọn và hiển thị chuỗi đã dịch phù hợp. "front-end" đề cập đến một phần của ứng dụng của chúng tôi. Các bản dịch có thể được sắp xếp tùy theo các loại khác nhau. Nhưng, những chuỗi dịch này xuất hiện ở đâu?

Ngôn ngữ mặc định, trong trường hợp này là tiếng Anh, được viết vào mã, như được hiển thị ở trên. File tài nguyên ngôn ngữ là danh sách các mảng chuỗi có khóa là văn bản ngôn ngữ mặc định — ví dụ: "Place Type" — và mỗi file cung cấp giá trị văn bản đã dịch cho ngôn ngữ thích hợp của chúng.

Dưới đây là ví dụ về tệp dịch tiếng Tây Ban Nha hoàn chỉnh của chúng tôi, mã ngôn ngữ Hàm Yii:t() sử dụng tệp này để tìm bản dịch phù hợp để hiển thị:

 'Agregar ubicación actual',
    'Add a Google {modelClass}' => 'Añadir un Google {modelClass}',
    'Created By' => 'Creado por',
    'Full Address' => 'Dirección completa',
    'Google Place ID' => 'Google Place ID',
    'Name' => 'Nombre',
    'Notes' => 'Notas',
    'Place Type' => 'Place Tipo',
    'Places' => 'Lugares',

Khi việc này có vẻ tốn thời gian, Yii cung cấp các script để tự động hóa việc tạo và tổ chức các file này.

Bằng cách tách văn bản ra khỏi code, chúng tôi giúp các chuyên gia đa ngôn ngữ không chuyên về kỹ thuật dễ dàng dịch các ứng dụng cho chúng tôi hơn — mà không phá võ các cú pháp code.

I18n cũng cung cấp các hàm chuyên biệt để dịch thời gian, tiền tệ, số nhiều v.v. Tôi sẽ không đi vào chi tiết của những điều này trong hướng dẫn này.

Cấu hình hỗ trợ I18n

Thật không may, tài liệu Yii2 cho I18n chưa được mô tả rõ - và rất khó để tìm ra các ví dụ chi tiết từng bước một. May mắn cho bạn, tôi sẽ hướng dẫn bạn những gì tôi đã học được từ việc tìm kiếm tài liệu và web. Tôi tìm thấy ví dụ hữu ích I18n của Code Ninja và Yii2 Definitive Guide về I18n, và người đóng góp cho Yii Alexander Makarov cũng đã đề nghị hỗ trợ tôi.

Tạo file cấu hình I18n

Chúng tôi đang sử dụng template Yii2 nâng cao cho Meeting Planner. Điều này tạo ra hai ứng dụng Yii trong codebase, frontend và backend của chúng tôi. Và, nó tạo ra một khu vực chung cho các mô hình được chia sẻ giữa cả hai ứng dụng. Tệp cấu hình của Yii được tải bất cứ khi nào yêu cầu trang được thực hiện. Chúng ta sẽ sử dụng các kịch bản lệnh I18n của Yii để xây dựng một tệp cấu hình cho I18n trong đường dẫn common/config.

Từ gốc codebase của chúng ta, chúng ta sẽ chạy script message/config:

 ./yii message/config @common/config/i18n.php

Điều này tạo ra template sau đây mà chúng ta có thể tùy chỉnh:

 __DIR__,
    // array, required, list of language codes that the extracted messages
    // should be translated to. For example, ['zh-CN', 'de'].
    'languages' => ['de'],
    // string, the name of the function for translating messages.
    // Defaults to 'Yii::t'. This is used as a mark to find the messages to be
    // translated. You may use a string for single function name or an array for
    // multiple function names.
    'translator' => 'Yii::t',
    // boolean, whether to sort messages by keys when merging new messages
    // with the existing ones. Defaults to false, which means the new (untranslated)
    // messages will be separated from the old (translated) ones.
    'sort' => false,
    // boolean, whether to remove messages that no longer appear in the source code.
    // Defaults to false, which means each of these messages will be enclosed with a pair of '@@' marks.
    'removeUnused' => false,
    // array, list of patterns that specify which files/directories should NOT be processed.
    // If empty or not set, all files/directories will be processed.
    // A path matches a pattern if it contains the pattern string at its end. For example,
    // '/a/b' will match all files and directories ending with '/a/b';
    // the '*.svn' will match all files and directories whose name ends with '.svn'.
    // and the '.svn' will match all files and directories named exactly '.svn'.
    // Note, the '/' characters in a pattern matches both '/' and '\'.
    // See helpers/FileHelper::findFiles() description for more details on pattern matching rules.
    'only' => ['*.php'],
    // array, list of patterns that specify which files (not directories) should be processed.
    // If empty or not set, all files will be processed.
    // Please refer to "except" for details about the patterns.
    // If a file/directory matches both a pattern in "only" and "except", it will NOT be processed.
    'except' => [
        '.svn',
        '.git',
        '.gitignore',
        '.gitkeep',
        '.hgignore',
        '.hgkeep',
        '/messages',
    ],

    // 'php' output format is for saving messages to php files.
    'format' => 'php',
    // Root directory containing message translations.
    'messagePath' => __DIR__ . DIRECTORY_SEPARATOR . 'messages',
    // boolean, whether the message file should be overwritten with the merged messages
    'overwrite' => true,


    /*
    // 'db' output format is for saving messages to database.
    'format' => 'db',
    // Connection component to use. Optional.
    'db' => 'db',
    // Custom source message table. Optional.
    // 'sourceMessageTable' => '{{%source_message}}',
    // Custom name for translation message table. Optional.
    // 'messageTable' => '{{%message}}',
    */

    /*
    // 'po' output format is for saving messages to gettext po files.
    'format' => 'po',
    // Root directory containing message translations.
    'messagePath' => __DIR__ . DIRECTORY_SEPARATOR . 'messages',
    // Name of the file that will be used for translations.
    'catalog' => 'messages',
    // boolean, whether the message file should be overwritten with the merged messages
    'overwrite' => true,
    */
];

Tôi đang tùy chỉnh tệp của mình. Tôi di chuyển messagePath lên đến đỉnh và tùy chỉnh sourcePath và messagePath. Tôi cũng chỉ định ngôn ngữ tôi muốn ứng dụng của mình hỗ trợ ngoài tiếng Anh — trong trường hợp này là tiếng Tây Ban Nha và tiếng Đức, 'es' và 'de'. Dưới đây là danh sách tất cả các mã ngôn ngữ I18n.

return [
    // string, required, root directory of all source files
    'sourcePath' => __DIR__. DIRECTORY_SEPARATOR . '..' . DIRECTORY_SEPARATOR . '..' . DIRECTORY_SEPARATOR,
    // Root directory containing message translations.
    'messagePath' => __DIR__ . DIRECTORY_SEPARATOR .'..'. DIRECTORY_SEPARATOR . 'messages',
    // array, required, list of language codes that the extracted messages
    // should be translated to. For example, ['zh-CN', 'de'].
    'languages' => ['es','de'],

Trong bước tiếp theo, chúng tôi sẽ chạy tập lệnh trích xuất của Yii, sẽ quét tất cả mã trong cây sourcePath để tạo các tệp chuỗi mặc định cho tất cả các nhãn được sử dụng trong mã của chúng tôi. Tôi đang tùy biến sourcePath để quét toàn bộ cây mã. Tôi đang tùy biến messagePath để tạo ra các tập tin kết quả chung / tin nhắn.

 ./yii message/extract @common/config/i18n.php

Bạn sẽ thấy Yii quét tất cả các file code của bạn:

Extracting messages from /Users/Jeff/Sites/mp/frontend/models/Place.php...
Extracting messages from /Users/Jeff/Sites/mp/frontend/models/PlaceGPS.php...
Extracting messages from /Users/Jeff/Sites/mp/frontend/models/PlaceSearch.php...
Extracting messages from /Users/Jeff/Sites/mp/frontend/models/ResetPasswordForm.php...
Extracting messages from /Users/Jeff/Sites/mp/frontend/models/SignupForm.php...
Extracting messages from /Users/Jeff/Sites/mp/frontend/views/layouts/main.php...
Extracting messages from /Users/Jeff/Sites/mp/frontend/views/meeting/_form.php...
Extracting messages from /Users/Jeff/Sites/mp/frontend/views/meeting/_search.php...
Extracting messages from /Users/Jeff/Sites/mp/frontend/views/meeting/create.php...
Extracting messages from /Users/Jeff/Sites/mp/frontend/views/meeting/index.php...
Extracting messages from /Users/Jeff/Sites/mp/frontend/views/meeting/update.php...
Extracting messages from /Users/Jeff/Sites/mp/frontend/views/meeting/view.php...
Extracting messages from /Users/Jeff/Sites/mp/frontend/views/place/_form.php...
Extracting messages from /Users/Jeff/Sites/mp/frontend/views/place/_formGeolocate.php...
Extracting messages from /Users/Jeff/Sites/mp/frontend/views/place/_formPlaceGoogle.php...
Extracting messages from /Users/Jeff/Sites/mp/frontend/views/place/_search.php...
Extracting messages from /Users/Jeff/Sites/mp/frontend/views/place/create.php...
Extracting messages from /Users/Jeff/Sites/mp/frontend/views/place/create_geo.php...
Extracting messages from /Users/Jeff/Sites/mp/frontend/views/place/create_place_google.php...
Extracting messages from /Users/Jeff/Sites/mp/frontend/views/place/index.php...
Extracting messages from /Users/Jeff/Sites/mp/frontend/views/place/locate.php...
Extracting messages from /Users/Jeff/Sites/mp/frontend/views/place/update.php...
Extracting messages from /Users/Jeff/Sites/mp/frontend/views/place/view.php...
Extracting messages from /Users/Jeff/Sites/mp/frontend/views/site/about.php...
Extracting messages from /Users/Jeff/Sites/mp/frontend/views/site/contact.php...
Extracting messages from /Users/Jeff/Sites/mp/frontend/views/site/error.php...
Extracting messages from /Users/Jeff/Sites/mp/frontend/views/site/index.php...
Extracting messages from /Users/Jeff/Sites/mp/frontend/views/site/login.php...
Extracting messages from /Users/Jeff/Sites/mp/frontend/views/site/requestPasswordResetToken.php...
Extracting messages from /Users/Jeff/Sites/mp/frontend/views/site/resetPassword.php...
Extracting messages from /Users/Jeff/Sites/mp/frontend/views/site/signup.php...
Extracting messages from /Users/Jeff/Sites/mp/frontend/web/index-test.php...
Extracting messages from /Users/Jeff/Sites/mp/frontend/web/index.php...
Extracting messages from /Users/Jeff/Sites/mp/frontend/widgets/Alert.php...

Khi hoàn thành, bạn sẽ thấy kết quả như thế này trong codebase:

Hướng dẫn dùng ngu alexander trong PHP
Hướng dẫn dùng ngu alexander trong PHP
Hướng dẫn dùng ngu alexander trong PHP

Kích hoạt I18n và chọn một ngôn ngữ

Trong tập tin cấu hình chung, common/config/main.php, chúng ta sẽ nói với Yii về sự hỗ trợ ngôn ngữ mới của chúng ta. Tôi sẽ làm tiếng Tây Ban Nha cho ngôn ngữ mặc định của tôi:

 dirname(dirname(__DIR__)) . '/vendor',
    'language' => 'es', // spanish
    'components' => [
        'cache' => [
            'class' => 'yii\caching\FileCache',
        ],
        'i18n' => [
            'translations' => [
                'frontend*' => [
                    'class' => 'yii\i18n\PhpMessageSource',
                    'basePath' => '@common/messages',
                ],
                'backend*' => [
                    'class' => 'yii\i18n\PhpMessageSource',
                    'basePath' => '@common/messages',
                ],
            ],
        ],        
    ],
];

Nhưng vẫn còn nhiều việc phải làm. Chúng ta phải làm cho code của chúng ta biết đến I18n.

Sử dụng bộ tạo mã Gii của Yii với I18n

Trong phần hai của loạt bài, Xây dựng Startup của bạn Với PHP: Yêu cầu tính năng và Thiết kế cơ sở dữ liệu, chúng tôi đã dùng trình tạo mã của Yii, Gii, để tạo ra các model, controller và view của chúng ta. Nhưng, chúng tôi không kích hoạt I18n, vì vậy tất cả code của chúng tôi nhúng các chuỗi văn bản.. Hãy làm lại điều này.

Chúng tôi dùng lại Gii, có thể là http://localhost:8888/mp/gii trong trình duyệt của bạn và chạy lại trình tạo model và controller với I18n được kích hoạt.

Lưu ý: Nếu bạn tiếp tục với phần ba của loạt bài này, có thể bạn cần lưu ý sự khác biệt của từng file và sao chép code theo cách thủ công. Hoặc, có thể dễ dàng thay thế code của bạn bằng repo trên Github cho hướng dẫn này, được liên kết phía trên bên phải.

Dưới đây là ví dụ về việc tạo mã mô hình Cuộc họp với kích hoạt I18n. Lưu ý rằng chúng tôi chỉ định "frontend" cho Message Category. Chúng tôi đang đặt tất cả các chuỗi văn cho người dùng trong một file danh mục cho người dùng.

Hướng dẫn dùng ngu alexander trong PHP
Hướng dẫn dùng ngu alexander trong PHP
Hướng dẫn dùng ngu alexander trong PHP

Tương tự thực hiện tạo mã CRUD cho các controller và view:

Hướng dẫn dùng ngu alexander trong PHP
Hướng dẫn dùng ngu alexander trong PHP
Hướng dẫn dùng ngu alexander trong PHP

Nếu bạn duyệt code được tạo trong các model, controller và view, bạn sẽ thấy các chuỗi văn bản được thay thế bằng hàm Yii:t('frontend',...):

title = Yii::t('frontend', 'Places');
$this->params['breadcrumbs'][] = $this->title;
?>

title) ?>

render('_search', ['model' => $searchModel]); ?>

'Place', ]), ['create'], ['class' => 'btn btn-success']) ?> 'btn btn-success']) ?> 'Place' ]), ['create_place_google'], ['class' => 'btn btn-success']) ?>

Dịch các file Message của bạn

Hãy xem file Spanish (tiếng Tây Ban Nha) của chúng tôi, /common/messages/es/frontend.php. Đó là danh sách dài gồm các mảng giá trị rỗng:

return [
    'Add Current Location' => '',
    'Add a Google {modelClass}' => '',
    'Are you sure you want to delete this item?' => '',
    'Create' => '',
    'Create {modelClass}' => '',
    'Created At' => '',
    'Created By' => '',
    'Delete' => '',
    'Full Address' => '',
    'Google Place ID' => '',
    'ID' => '',
    'Meeting Type' => '',
    'Meetings' => '',
    'Message' => '',
    'Name' => '',
    'Notes' => '',
    'Owner ID' => '',
    'Place Type' => '',
    'Places' => '',
    'Reset' => '',
    'Search' => '',
    'Slug' => '',
    'Status' => '',
    'Update' => '',
    'Update {modelClass}: ' => '',
    'Updated At' => '',
    'Vicinity' => '',
    'Website' => '',
];

Với mục đích điền các bản dịch tiếng Tây Ban Nha của chúng tôi cho hướng dẫn này, tôi sẽ sử dụng Google Translate. Khôn ranh nhỉ, đúng không?

Hướng dẫn dùng ngu alexander trong PHP
Hướng dẫn dùng ngu alexander trong PHP
Hướng dẫn dùng ngu alexander trong PHP

Sau đó, chúng tôi sao chép các bản dịch này vào file message.

return [
    'Add Current Location' => 'Agregar ubicación actual',
    'Add a Google {modelClass}' => 'Añadir un Google {modelClass}',
    'Created By' => 'Creado por',
    'Full Address' => 'Dirección completa',
    'Google Place ID' => 'Google Place ID',
    'Name' => 'Nombre',
    'Notes' => 'Notas',
    'Place Type' => 'Place Tipo',
    'Places' => 'Lugares',
    'Slug' => 'Slug',
    'Vicinity' => 'Alrededores',
    'Website' => 'Sitio Web',
    'Are you sure you want to delete this item?' => '¿Estás seguro que quieres borrar este elemento?',
    'Create' => 'Crear',
    'Create {modelClass}' => 'Crear Lugar',
    'Created At' => 'Creado El',
    'Delete' => 'Eliminar',
    'ID' => 'ID',
    'Meeting Type' => 'Tipo de Reunión',
    'Meetings' => 'Encuentros',
    'Message' => 'Mensaje',
    'Owner ID' => 'Propietario ID',
    'Reset' => 'Reset',
    'Search' => 'Buscar',
    'Status' => 'Estado',
    'Update' => 'Actualizar',
    'Update {modelClass}: ' => 'Actualizar Lugar',
    'Updated At' => 'Actualización A',
];

Khi chúng tôi truy cập trang Place index, bạn sẽ thấy phiên bản tiếng Tây Ban Nha — tuyệt phải không?

Hướng dẫn dùng ngu alexander trong PHP
Hướng dẫn dùng ngu alexander trong PHP
Hướng dẫn dùng ngu alexander trong PHP

Lưu ý rằng thanh điều hướng vẫn còn bằng tiếng Anh - đó là vì file Message/Extract không hiểu định nghĩa mảng phần điều hướng Bootstrap và chuyển đổi chúng sang sử dụng Yii:t(). Chúng tôi sẽ tự thực hiện việc này. Ngoài ra, lưu ý rằng Home và paging text được dịch tự động — codebase của Yii bao gồm các bản dịch ngôn ngữ cho các chuỗi chuẩn này.

Đây là form Create A Place:

Hướng dẫn dùng ngu alexander trong PHP
Hướng dẫn dùng ngu alexander trong PHP
Hướng dẫn dùng ngu alexander trong PHP

Nếu tôi muốn trở về tiếng Anh, chỉ cần thay đổi file cấu hình, /common/main.php, sang English:

 dirname(dirname(__DIR__)) . '/vendor',
    'language' => 'en', // english
//    'language' => 'es', // spanish
Hướng dẫn dùng ngu alexander trong PHP
Hướng dẫn dùng ngu alexander trong PHP
Hướng dẫn dùng ngu alexander trong PHP

Bạn cũng sẽ nhận thấy khi bạn tiến hành thay thế các chuỗi trong JavaScript có những phức tạp riêng của nó. Tôi chưa tự khám phá nó, nhưng phần mở rộng Yii 1.x JsTrans có thể cung cấp một hướng dẫn hữu ích để hỗ trợ việc này.

Tiến xa hơn với I18n

Cuối cùng, chúng tôi có thể muốn dịch ứng dụng của mình sang một số ngôn ngữ. Tôi đã đăng yêu cầu tính năng Yii để mở rộng tập lệnh Message/Extract để sử dụng API Google Translate để tự động hóa quá trình này. Tôi cũng đã yêu cầu Tuts+ để cho tôi viết một hướng dẫn về nó, vì vậy hãy đợi nhé. Tất nhiên, chỉ cung cấp một bản dịch cơ bản. Có thể bạn muốn thuê các dịch giả chuyên nghiệp để điều chỉnh các file sau đó.

Một số ứng dụng cho phép người dùng chọn ngôn ngữ mẹ đẻ của họ để khi họ đăng nhập, giao diện người dùng sẽ tự động dịch cho họ. Trong Yii, biến $app->language thực hiện điều này:

\Yii::$app->language = 'es';

Các ứng dụng khác, như JScrambler.com bên dưới, tận dụng đường dẫn URL để chuyển đổi ngôn ngữ. Người dùng chỉ cần nhấp vào tiền tố ngôn ngữ mà họ muốn, ví dụ: "FR", và ứng dựng tự động được dịch.

Hướng dẫn dùng ngu alexander trong PHP
Hướng dẫn dùng ngu alexander trong PHP
Hướng dẫn dùng ngu alexander trong PHP

Lưu ý: Xem trang hướng dẫn Tuts+ của tôi để biết hướng dẫn sắp tới về JScrambler — đó là một dịch vụ khá hữu ích. Có thể bài viết đã xuất hiện khi bạn đọc hướng dẫn này.

Trình quản lý URL của Yii cũng có thể cung cấp chức năng này. Tôi có thể sẽ triển khai các tính năng này cho Meeting Planner trong một hướng dẫn trong sắp đến.

TIếp theo là gì?

Tôi hy vọng bạn đã học được điều gì đó mới mẻ với hướng dẫn này. Tôi đã sử dụng I18n với Rails trước đây - nhưng đây là lần đầu tiên tôi thực hiện nó với PHP. Xem các hướng dẫn sắp tới trong loạt bài Building Your Startup With PHP  — có rất nhiều tính năng thú vị sắp ra mắt.

Xin vui lòng thêm câu hỏi và bình luận của bạn dưới đây; Tôi thường tham gia vào các cuộc thảo luận. Bạn cũng có thể liên hệ với tôi trên Twitter @reifman hoặc email trực tiếp đến tôi.

Liên kết liên quan

  • Programming with Yii2: Getting Started
  • Introduction to the Yii Framework
  • The Yii2 Definitive Guide: Internationalization