Penggunaan yang benar untuk terjemahan-sudut di pengontrol

121

Saya menggunakan angular-translate untuk i18n di aplikasi AngularJS.

Untuk setiap tampilan aplikasi, ada pengontrol khusus. Di pengontrol di bawah ini, saya menetapkan nilai yang akan ditampilkan sebagai judul halaman.

Kode

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';
    }])

Saya memuat file terjemahan menggunakan ekstensi angular-translate-loader-url .

Masalah

Pada pemuatan halaman awal, kunci terjemahan yang ditampilkan, bukan terjemahan untuk kunci itu. Terjemahan adalah Hello, World!, tapi saya melihat HELLO_WORLD.

Kedua kalinya saya membuka halaman, semuanya baik-baik saja dan versi terjemahan ditampilkan.

Saya berasumsi bahwa masalahnya ada hubungannya dengan fakta bahwa mungkin file terjemahan belum dimuat saat pengontrol menetapkan nilainya $scope.pageTitle.

Ucapan

Saat menggunakan <h1>{{ pageTitle | translate }}</h1>dan $scope.pageTitle = 'HELLO_WORLD';, terjemahan berfungsi sempurna sejak pertama kali. Masalahnya adalah saya tidak selalu ingin menggunakan terjemahan (misalnya untuk pengontrol kedua saya hanya ingin meneruskan string mentah).

Pertanyaan

Apakah ini masalah / batasan yang diketahui? Bagaimana ini bisa diselesaikan?

ndequeker
sumber

Jawaban:

69

EDIT : Silakan lihat jawaban dari PascalPrecht (penulis angular-translate) untuk solusi yang lebih baik.


Sifat asinkron dari pemuatan menyebabkan masalah. Anda lihat, dengan {{ pageTitle | translate }}, Angular akan memperhatikan ekspresi; ketika data lokalisasi dimuat, nilai ekspresi berubah dan layar diperbarui.

Jadi, Anda dapat melakukannya sendiri:

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

Namun, ini akan menjalankan ekspresi yang diawasi pada setiap siklus intisari. Ini kurang optimal dan mungkin atau mungkin tidak menyebabkan penurunan kinerja yang terlihat. Pokoknya itu yang dilakukan Angular, jadi tidak mungkin seburuk itu ...

Nikos Paraskevopoulos
sumber
Terima kasih! Saya berharap bahwa menggunakan filter di View atau di Controller akan berperilaku persis sama. Sepertinya tidak demikian di sini.
ndequeker
Saya akan mengatakan menggunakan $scope.$watchagak berlebihan karena Angular Translate menawarkan Layanan untuk digunakan dalam pengontrol. Lihat jawaban saya di bawah.
Robin van Baalen
1
Filter Angular Translate tidak diperlukan, karena $translate.instant()menawarkan sama seperti layanan. Selain itu, perhatikan juga jawaban Pascal.
knalli
Saya setuju, menggunakan $ watch berlebihan. Jawaban di bawah ini adalah penggunaan yang lebih tepat.
jpblancoder
141

Disarankan: jangan terjemahkan di pengontrol, terjemahkan dalam tampilan Anda

Saya akan merekomendasikan untuk menjaga pengontrol Anda bebas dari logika terjemahan dan menerjemahkan string Anda langsung di dalam tampilan Anda seperti ini:

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

Menggunakan layanan yang disediakan

Angular Translate menyediakan $translatelayanan yang dapat Anda gunakan di Pengontrol Anda.

Contoh penggunaan $translatelayanan dapat berupa:

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

Layanan terjemahan juga memiliki metode untuk menerjemahkan string secara langsung tanpa perlu menangani promise, dengan menggunakan $translate.instant():

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

Kelemahan dengan menggunakan $translate.instant()mungkin file bahasa belum dimuat jika Anda memuatnya async.

Menggunakan filter yang disediakan

Ini adalah cara yang saya sukai karena saya tidak harus menangani janji dengan cara ini. Output dari filter dapat langsung disetel ke variabel cakupan.

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

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

Menggunakan direktif yang disediakan

Karena @PascalPrecht adalah pencipta perpustakaan yang luar biasa ini, saya akan merekomendasikan untuk mengikuti sarannya (lihat jawabannya di bawah) dan menggunakan petunjuk yang disediakan yang tampaknya menangani terjemahan dengan sangat cerdas.

Direktif menangani eksekusi asynchronous dan juga cukup pintar untuk membuka id terjemahan pada lingkup jika terjemahan tidak memiliki nilai dinamis.

Robin van Baalen
sumber
Jika Anda mencobanya daripada menulis komentar yang tidak berhubungan itu, Anda pasti sudah tahu jawabannya sekarang. Jawaban singkatnya: ya. Itu mungkin.
Robin van Baalen
1
dalam contoh Anda dengan filter di pengontrol: seperti dengan instant (), jika file bahasa tidak dimuat, ini tidak akan berfungsi bukan? Bukankah kita harus menggunakan jam tangan dalam kasus itu? Atau Anda bermaksud mengatakan 'gunakan filter hanya jika Anda tahu terjemahannya dimuat?
Bombinosh
@Bombinosh Saya akan mengatakan menggunakan metode filter jika Anda tahu terjemahan dimuat. Secara pribadi saya bahkan akan merekomendasikan untuk tidak memuat terjemahan secara dinamis jika Anda tidak perlu melakukannya. Ini adalah bagian wajib dari aplikasi Anda, jadi sebaiknya Anda tidak ingin pengguna menunggu. Tapi itu pendapat pribadi.
Robin van Baalen
Inti dari terjemahan adalah bahwa mereka dapat berubah berdasarkan preferensi pengguna atau bahkan tindakan pengguna. Jadi Anda perlu, secara umum, memuatnya secara dinamis. Setidaknya jika jumlah string yang akan diterjemahkan itu penting, dan / atau jika Anda memiliki banyak terjemahan.
PhiLho
4
Ketika terjemahan selesai dalam HTML, siklus intisari dijalankan dua kali, tetapi hanya berjalan satu kali di pengontrol. 99% kasus ini mungkin tidak masalah, tetapi saya memiliki masalah dengan kinerja yang buruk dalam kisi ui sudut dengan terjemahan di banyak sel. Kasus tepi yang pasti, hanya sesuatu yang harus diperhatikan
tykowale
123

Sebenarnya, Anda harus menggunakan direktif terjemahan untuk hal semacam itu.

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

Direktif menangani eksekusi asynchronous dan juga cukup pintar untuk membuka id terjemahan pada lingkup jika terjemahan tidak memiliki nilai dinamis.

Namun, jika tidak ada jalan lain dan Anda benar - benar harus menggunakan $translatelayanan di pengontrol, Anda harus menggabungkan panggilan dalam $translateChangeSuccessacara menggunakan $rootScopekombinasi $translate.instant()seperti ini:

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

Jadi kenapa $rootScopedan tidak $scope? Alasannya adalah, bahwa di angular-translate, acara $emitdiedit $rootScopedaripada $broadcastdiedit $scopekarena kami tidak perlu menyiarkan melalui seluruh hierarki cakupan.

Mengapa $translate.instant()dan tidak hanya asinkron $translate()? Saat $translateChangeSuccessperistiwa dipicu, dipastikan bahwa data terjemahan yang diperlukan ada dan tidak ada eksekusi asinkron yang terjadi (misalnya eksekusi loader asinkron), oleh karena itu kita dapat menggunakan $translate.instant()yang sinkron dan hanya mengasumsikan bahwa terjemahan tersedia.

Sejak versi 2.8.0 ada juga $translate.onReady(), yang mengembalikan janji yang diselesaikan segera setelah terjemahan siap. Lihat changelog .

Pascal Precht
sumber
Mungkinkah ada masalah kinerja jika saya menggunakan perintah terjemahan alih-alih filter? Juga saya percaya secara internal, itu melihat nilai pengembalian instan (). Jadi apakah itu menghapus jam tangan ketika ruang lingkup saat ini dihancurkan?
Nilesh
Saya mencoba menggunakan saran Anda tetapi tidak berhasil ketika nilai variabel lingkup berubah secara dinamis.
Nilesh
10
Sebenarnya selalu lebih baik untuk menghindari filter jika memungkinkan, karena filter memperlambat aplikasi Anda karena selalu menyiapkan jam tangan baru. Namun, arahannya sedikit lebih jauh. Ia memeriksa apakah ia harus memperhatikan nilai dari sebuah id terjemahan atau tidak. Itu memungkinkan kinerja aplikasi Anda lebih baik. Bisakah Anda membuat plunk dan menautkan saya ke sana, agar saya dapat melihatnya lebih jauh?
Pascal Precht
Plunk: plnkr.co/edit/j53xL1EdJ6bT20ldlhxr Mungkin dalam contoh saya, direktif memutuskan untuk tidak memperhatikan nilai. Juga sebagai masalah terpisah, penangan kesalahan khusus saya dipanggil jika kunci tidak ditemukan, tetapi tidak menampilkan string yang dikembalikan. Saya akan membuat potongan lain untuk itu.
Nilesh
2
@PascalPrecht Hanya sebuah pertanyaan, apakah praktik yang baik menggunakan bind-once dengan terjemahan? Seperti ini {{::'HELLO_WORLD | translate}}'.
Zunair Zubair
5

Untuk membuat terjemahan di pengontrol, Anda dapat menggunakan $translatelayanan:

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

Pernyataan itu hanya menerjemahkan pada aktivasi pengontrol tetapi tidak mendeteksi perubahan runtime dalam bahasa. Untuk mencapai perilaku itu, Anda dapat mendengarkan $rootScopeacara tersebut: $translateChangeSuccessdan melakukan terjemahan yang sama di sana:

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

Tentu saja, Anda dapat merangkum $translatelayanan dalam sebuah metode dan memanggilnya di pengontrol dan $translateChangeSucesspendengar.

MacLeod
sumber
1

Apa yang terjadi adalah Angular-translate mengamati ekspresi dengan sistem berbasis peristiwa, dan seperti dalam kasus pengikatan atau pengikatan dua arah lainnya, peristiwa dijalankan ketika data diambil, dan nilainya berubah, yang mana jelas tidak berfungsi untuk terjemahan. Data terjemahan, tidak seperti data dinamis lain di halaman, tentu saja harus segera ditampilkan kepada pengguna. Itu tidak bisa muncul setelah halaman dimuat.

Bahkan jika Anda berhasil men-debug masalah ini, masalah yang lebih besar adalah bahwa pekerjaan pengembangan yang terlibat sangat besar. Pengembang harus mengekstrak setiap string di situs secara manual, memasukkannya ke dalam file .json, mereferensikannya secara manual dengan kode string (misalnya, 'pageTitle' dalam kasus ini). Sebagian besar situs komersial memiliki ribuan string yang perlu dilakukan. Dan itu baru permulaan. Anda sekarang memerlukan sistem untuk menjaga terjemahan tetap selaras ketika teks yang mendasarinya berubah di beberapa di antaranya, sistem untuk mengirim file terjemahan ke berbagai penerjemah, mengintegrasikannya kembali ke dalam build, menerapkan ulang situs sehingga penerjemah dapat melihat perubahan mereka dalam konteks, dan seterusnya.

Selain itu, karena ini adalah sistem berbasis peristiwa yang 'mengikat', peristiwa dijalankan untuk setiap string tunggal di halaman, yang tidak hanya merupakan cara yang lebih lambat untuk mengubah halaman tetapi juga dapat memperlambat semua tindakan di halaman, jika Anda mulai menambahkan banyak acara ke dalamnya.

Bagaimanapun, menggunakan platform terjemahan pasca-pemrosesan lebih masuk akal bagi saya. Misalnya, menggunakan GlobalizeIt, penerjemah dapat langsung membuka halaman di situs dan mulai mengedit teks langsung di halaman tersebut untuk bahasa mereka, dan hanya itu: https://www.globalizeit.com/HowItWorks . Tidak diperlukan pemrograman (meskipun dapat dikembangkan secara terprogram), ini terintegrasi dengan mudah dengan Angular: https://www.globalizeit.com/Translate/Angular , transformasi halaman terjadi dalam sekali jalan, dan selalu menampilkan teks yang diterjemahkan dengan render awal halaman.

Pengungkapan penuh: Saya adalah salah satu pendiri :)

Jeff W.
sumber