Sử dụng đúng cho dịch góc trong bộ điều khiển


121

Tôi đang sử dụng dịch góc cho i18n trong ứng dụng AngularJS.

Đối với mọi chế độ xem ứng dụng, có một bộ điều khiển chuyên dụng. Trong bộ điều khiển bên dưới, tôi đặt giá trị được hiển thị dưới dạng tiêu đề trang.

HTML

<h1>{{ pageTitle }}</h1>

JavaScript

.controller('FirstPageCtrl', ['$scope', '$filter', function ($scope, $filter) {
        $scope.pageTitle = $filter('translate')('HELLO_WORLD');
    }])

.controller('SecondPageCtrl', ['$scope', '$filter', function ($scope, $filter) {
        $scope.pageTitle = 'Second page title';
    }])

Tôi đang tải các tệp dịch bằng phần mở rộng góc-dịch-bộ tải-url .

Vấn đề

Khi tải trang đầu tiên, khóa dịch được hiển thị thay vì bản dịch cho khóa đó. Bản dịch là vậy Hello, World!, nhưng tôi đang thấy HELLO_WORLD.

Lần thứ hai tôi vào trang, tất cả đều tốt và bản dịch được hiển thị.

Tôi cho rằng vấn đề liên quan đến thực tế là có thể tệp dịch chưa được tải khi bộ điều khiển đang gán giá trị cho $scope.pageTitle.

Nhận xét

Khi sử dụng <h1>{{ pageTitle | translate }}</h1>$scope.pageTitle = 'HELLO_WORLD';, bản dịch hoạt động hoàn hảo ngay từ lần đầu tiên. Vấn đề với điều này là tôi không phải lúc nào cũng muốn sử dụng các bản dịch (ví dụ: đối với bộ điều khiển thứ hai, tôi chỉ muốn chuyển một chuỗi thô).

Câu hỏi

Đây có phải là một vấn đề / hạn chế đã biết không? Làm thế nào điều này có thể được giải quyết?

Câu trả lời:


69

CHỈNH SỬA : Vui lòng xem câu trả lời từ PascalPrecht (tác giả của góc dịch) để có giải pháp tốt hơn.


Bản chất không đồng bộ của tải gây ra sự cố. Bạn thấy đấy, với {{ pageTitle | translate }}, Angular sẽ xem biểu thức; khi dữ liệu bản địa hóa được tải, giá trị của biểu thức sẽ thay đổi và màn hình được cập nhật.

Vì vậy, bạn có thể tự làm điều đó:

.controller('FirstPageCtrl', ['$scope', '$filter', function ($scope, $filter) {
    $scope.$watch(
        function() { return $filter('translate')('HELLO_WORLD'); },
        function(newval) { $scope.pageTitle = newval; }
    );
});

Tuy nhiên, điều này sẽ chạy biểu thức đã xem trên mọi chu kỳ thông báo. Điều này là không tối ưu và có thể hoặc không gây ra sự suy giảm hiệu suất có thể nhìn thấy được. Dù sao đó là những gì Angular làm, vì vậy nó không thể tệ như vậy ...


Cảm ơn bạn! Tôi hy vọng rằng việc sử dụng bộ lọc trong Chế độ xem hoặc trong Bộ điều khiển sẽ hoạt động giống hệt nhau. Đó dường như không phải là trường hợp ở đây.
ndequeker 12/1213

Tôi muốn nói rằng việc sử dụng a $scope.$watchlà quá mức cần thiết vì Angular Translate đang cung cấp một Dịch vụ được sử dụng trong bộ điều khiển. Xem câu trả lời của tôi dưới đây.
Robin van Baalen

1
Bộ lọc Angular Translate là không bắt buộc, vì nó $translate.instant()cung cấp giống như một dịch vụ. Bên cạnh đó, hãy chú ý đến câu trả lời của Pascal.
knalli

Tôi đồng ý, sử dụng $ watch là quá mức cần thiết. Câu trả lời dưới đây là cách sử dụng thích hợp hơn.
jpblancoder

141

Khuyến nghị: không dịch trong bộ điều khiển, dịch theo cách nhìn của bạn

Tôi khuyên bạn nên giữ bộ điều khiển của bạn không có logic dịch và dịch các chuỗi của bạn trực tiếp bên trong chế độ xem của bạn như sau:

<h1>{{ 'TITLE.HELLO_WORLD' | translate }}</h1>

Sử dụng dịch vụ được cung cấp

Angular Translate cung cấp $translatedịch vụ mà bạn có thể sử dụng trong Bộ điều khiển của mình.

Một ví dụ sử dụng $translatedịch vụ có thể là:

.controller('TranslateMe', ['$scope', '$translate', function ($scope, $translate) {
    $translate('PAGE.TITLE')
        .then(function (translatedValue) {
            $scope.pageTitle = translatedValue;
        });
});

Dịch vụ dịch cũng có một phương pháp để dịch trực tiếp các chuỗi mà không cần phải xử lý một lời hứa, sử dụng $translate.instant():

.controller('TranslateMe', ['$scope', '$translate', function ($scope, $translate) {
    $scope.pageTitle = $translate.instant('TITLE.DASHBOARD'); // Assuming TITLE.DASHBOARD is defined
});

Nhược điểm của việc sử dụng $translate.instant()có thể là tệp ngôn ngữ chưa được tải nếu bạn đang tải nó không đồng bộ.

Sử dụng bộ lọc được cung cấp

Đây là cách ưa thích của tôi vì tôi không phải xử lý các lời hứa theo cách này. Đầu ra của bộ lọc có thể được đặt trực tiếp thành một biến phạm vi.

.controller('TranslateMe', ['$scope', '$filter', function ($scope, $filter) {
    var $translate = $filter('translate');

    $scope.pageTitle = $translate('TITLE.DASHBOARD'); // Assuming TITLE.DASHBOARD is defined
});

Sử dụng chỉ thị được cung cấp

Vì @PascalPrecht là người tạo ra thư viện tuyệt vời này, tôi khuyên bạn nên làm theo lời khuyên của anh ấy (xem câu trả lời của anh ấy bên dưới) và sử dụng lệnh được cung cấp để xử lý các bản dịch rất thông minh.

Lệnh xử lý việc thực thi không đồng bộ và cũng đủ thông minh để mở các id dịch trên phạm vi nếu bản dịch không có giá trị động.


Nếu bạn đã thử nó thay vì viết bình luận không liên quan đó, bạn sẽ biết câu trả lời ngay bây giờ. Câu trả lời ngắn gọn: có. Điều đó là khả thi.
Robin van Baalen

1
trong ví dụ của bạn với bộ lọc trong bộ điều khiển: như với Instant (), nếu tệp ngôn ngữ không được tải, điều này sẽ không hoạt động đúng không? Chúng ta có nên sử dụng đồng hồ trong trường hợp đó không? Hay bạn muốn nói 'chỉ sử dụng bộ lọc nếu bạn biết bản dịch đã được tải?
Bombinosh

@Bombinosh Tôi muốn nói rằng hãy sử dụng phương pháp lọc nếu bạn biết bản dịch đã được tải. Cá nhân tôi thậm chí khuyên bạn không nên tải các bản dịch động nếu bạn không cần thiết. Đó là một phần bắt buộc trong ứng dụng của bạn, vì vậy bạn không nên muốn người dùng chờ đợi nó. Nhưng đó là ý kiến ​​cá nhân.
Robin van Baalen,

Điểm của các bản dịch là chúng có thể thay đổi theo sở thích của người dùng hoặc thậm chí theo hành động của người dùng. Vì vậy, nói chung, bạn cần tải chúng một cách linh hoạt. Ít nhất nếu số lượng chuỗi cần dịch là quan trọng và / hoặc nếu bạn có nhiều bản dịch.
PhiLho

4
Khi quá trình dịch được thực hiện trong HTML, chu trình thông báo được chạy hai lần, nhưng chỉ chạy một lần trong bộ điều khiển. 99% trường hợp điều này có thể sẽ không thành vấn đề, nhưng tôi đã gặp vấn đề với hiệu suất khủng khiếp trong một lưới ui góc với các bản dịch trong nhiều ô. Một trường hợp cạnh chắc chắn, chỉ cần một cái gì đó phải nhận thức được
tykowale

123

Trên thực tế, bạn nên sử dụng chỉ thị dịch cho những thứ như vậy thay thế.

<h1 translate="{{pageTitle}}"></h1>

Lệnh xử lý việc thực thi không đồng bộ và cũng đủ thông minh để mở các id dịch trên phạm vi nếu bản dịch không có giá trị động.

Tuy nhiên, nếu không còn cách nào khác và bạn thực sự phải sử dụng $translatedịch vụ trong bộ điều khiển, bạn nên kết hợp cuộc gọi trong một $translateChangeSuccesssự kiện bằng cách sử dụng $rootScopekết hợp với $translate.instant()như sau:

.controller('foo', function ($rootScope, $scope, $translate) {
  $rootScope.$on('$translateChangeSuccess', function () {
    $scope.pageTitle = $translate.instant('PAGE.TITLE');
  });
})

Vậy tại sao $rootScopevà không $scope? Lý do cho điều đó là trong các sự kiện của angle-translate được $emited on $rootScopethay vì $broadcasted on $scopebởi vì chúng tôi không cần phải truyền phát thông qua toàn bộ hệ thống phân cấp phạm vi.

Tại sao $translate.instant()và không chỉ async $translate()? Khi $translateChangeSuccesssự kiện được kích hoạt, chắc chắn rằng dữ liệu dịch cần thiết ở đó và không có quá trình thực thi không đồng bộ nào đang diễn ra (ví dụ: thực thi trình tải không đồng bộ), do đó chúng ta có thể chỉ sử dụng $translate.instant()dữ liệu đồng bộ và chỉ cần giả định rằng bản dịch có sẵn.

Kể từ phiên bản 2.8.0 cũng có $translate.onReady(), trả về một lời hứa sẽ được giải quyết ngay khi bản dịch sẵn sàng. Xem bảng thay đổi .


Có thể có bất kỳ vấn đề hiệu suất nào nếu tôi sử dụng chỉ thị dịch thay vì bộ lọc không? Ngoài ra, tôi tin rằng trong nội bộ, nó xem giá trị trả về của tức thì (). Vì vậy, nó có loại bỏ đồng hồ khi phạm vi hiện tại bị phá hủy?
Nilesh

Tôi đã thử sử dụng đề xuất của bạn nhưng nó không hoạt động khi giá trị của biến phạm vi thay đổi động.
Nilesh

10
Trên thực tế, tốt hơn hết bạn nên tránh các bộ lọc nếu có thể, vì chúng làm chậm ứng dụng của bạn vì chúng luôn thiết lập đồng hồ mới. Tuy nhiên, chỉ thị đi xa hơn một chút. Nó kiểm tra xem nó có phải xem giá trị của id dịch hay không. Điều đó cho phép hoạt động ứng dụng của bạn tốt hơn. Bạn có thể tạo một plunk và liên kết tôi với nó, để tôi có thể xem xét thêm được không?
Pascal Precht

Plunk: plnkr.co/edit/j53xL1EdJ6bT20ldlhxr Có thể trong ví dụ của tôi, chỉ thị quyết định không xem giá trị. Cũng là một vấn đề riêng biệt, trình xử lý lỗi tùy chỉnh của tôi được gọi nếu không tìm thấy khóa, nhưng nó không hiển thị chuỗi được trả về. Tôi sẽ thực hiện một plunk khác cho nó.
Nilesh

2
@PascalPrecht Chỉ là một câu hỏi, sử dụng bind-once với bản dịch có phải là một phương pháp hay không? Như thế này {{::'HELLO_WORLD | translate}}'.
Zunair Zubair

5

Để tạo bản dịch trong bộ điều khiển, bạn có thể sử dụng $translatedịch vụ:

$translate(['COMMON.SI', 'COMMON.NO']).then(function (translations) {
    vm.si = translations['COMMON.SI'];
    vm.no = translations['COMMON.NO'];
});

Câu lệnh đó chỉ thực hiện bản dịch khi kích hoạt bộ điều khiển nhưng nó không phát hiện sự thay đổi thời gian chạy trong ngôn ngữ. Để đạt được hành vi đó, bạn có thể lắng nghe $rootScopesự kiện: $translateChangeSuccessvà thực hiện bản dịch tương tự ở đó:

    $rootScope.$on('$translateChangeSuccess', function () {
        $translate(['COMMON.SI', 'COMMON.NO']).then(function (translations) {
            vm.si = translations['COMMON.SI'];
            vm.no = translations['COMMON.NO'];
        });
    });

Tất nhiên, bạn có thể đóng gói $translatedịch vụ trong một phương thức và gọi nó trong bộ điều khiển và trong bộ $translateChangeSucessnghe.


1

Điều đang xảy ra là Angular-translate đang xem biểu thức với một hệ thống dựa trên sự kiện và cũng giống như trong bất kỳ trường hợp ràng buộc hoặc ràng buộc hai chiều nào khác, một sự kiện được kích hoạt khi dữ liệu được truy xuất và giá trị đã thay đổi, rõ ràng là không hoạt động cho bản dịch. Dữ liệu dịch, không giống như các dữ liệu động khác trên trang, tất nhiên phải hiển thị ngay lập tức cho người dùng. Nó không thể xuất hiện sau khi tải trang.

Ngay cả khi bạn có thể gỡ lỗi thành công vấn đề này, vấn đề lớn hơn là công việc phát triển liên quan là rất lớn. Nhà phát triển phải trích xuất thủ công mọi chuỗi trên trang web, đặt nó vào tệp .json, tham chiếu thủ công bằng mã chuỗi (tức là 'pageTitle' trong trường hợp này). Hầu hết các trang web thương mại có hàng nghìn chuỗi mà điều này cần phải xảy ra. Va đo mơi chỉ la khởi đâu. Bây giờ bạn cần một hệ thống giữ các bản dịch được đồng bộ khi văn bản cơ bản thay đổi ở một số trong số chúng, một hệ thống để gửi các tệp bản dịch đến những người dịch khác nhau, để tích hợp lại chúng vào bản dựng, triển khai lại trang web để người dịch có thể xem những thay đổi của chúng trong ngữ cảnh và cứ thế tiếp tục.

Ngoài ra, vì đây là hệ thống dựa trên sự kiện 'ràng buộc', một sự kiện đang được kích hoạt cho mọi chuỗi đơn lẻ trên trang, điều này không chỉ là cách chậm hơn để chuyển đổi trang mà còn có thể làm chậm tất cả các hành động trên trang, nếu bạn bắt đầu thêm số lượng lớn sự kiện vào nó.

Dù sao, việc sử dụng một nền tảng dịch sau xử lý có ý nghĩa hơn đối với tôi. Sử dụng GlobalizeIt chẳng hạn, một người dịch chỉ cần truy cập một trang trên trang web và bắt đầu chỉnh sửa văn bản trực tiếp trên trang đó cho ngôn ngữ của họ, và đó là: https://www.globalizeit.com/HowItWorks . Không cần lập trình (mặc dù có thể mở rộng theo chương trình), nó tích hợp dễ dàng với Angular: https://www.globalizeit.com/Translate/Angular , quá trình chuyển đổi trang diễn ra trong một lần và nó luôn hiển thị văn bản đã dịch với kết xuất ban đầu của trang.

Tiết lộ đầy đủ: Tôi là người đồng sáng lập :)

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.