ใช้อย่างถูกต้องสำหรับการแปลเชิงมุมในคอนโทรลเลอร์


121

ฉันใช้angular-translateสำหรับ i18n ในแอปพลิเคชัน AngularJS

สำหรับทุกมุมมองแอปพลิเคชันมีตัวควบคุมเฉพาะ ในตัวควบคุมด้านล่างฉันตั้งค่าให้แสดงเป็นชื่อหน้า

รหัส

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

ฉันกำลังโหลดไฟล์แปลโดยใช้นามสกุลangular-translate-loader-url

ปัญหา

ในการโหลดหน้าเริ่มต้นคีย์การแปลจะแสดงแทนการแปลสำหรับคีย์นั้น แปลเป็นแต่ฉันเห็นHello, World!HELLO_WORLD

ครั้งที่สองที่ฉันไปที่หน้านั้นทุกอย่างเรียบร้อยดีและมีการแสดงเวอร์ชันแปล

$scope.pageTitleผมถือว่าปัญหาได้จะทำอย่างไรกับความจริงที่ว่าอาจจะไฟล์แปลยังไม่ได้โหลดเมื่อควบคุมคือการกำหนดค่า

สังเกต

เมื่อใช้<h1>{{ pageTitle | translate }}</h1>และ$scope.pageTitle = 'HELLO_WORLD';การแปลจะทำงานได้อย่างสมบูรณ์แบบตั้งแต่ครั้งแรก ปัญหานี้คือฉันไม่ต้องการใช้คำแปลเสมอไป (เช่นสำหรับคอนโทรลเลอร์ตัวที่สองฉันแค่ต้องการส่งสตริงดิบ)

คำถาม

นี่เป็นปัญหา / ข้อ จำกัด ที่ทราบหรือไม่? จะแก้ไขได้อย่างไร?

คำตอบ:


69

แก้ไข : โปรดดูคำตอบจาก PascalPrecht (ผู้เขียน angular-translate) เพื่อหาทางออกที่ดีกว่า


ลักษณะการโหลดแบบอะซิงโครนัสทำให้เกิดปัญหา คุณจะเห็นด้วย{{ pageTitle | translate }}Angular จะดูการแสดงออก เมื่อโหลดข้อมูลการแปลค่าของนิพจน์จะเปลี่ยนไปและหน้าจอจะถูกอัพเดต

ดังนั้นคุณสามารถทำได้ด้วยตัวเอง:

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

อย่างไรก็ตามสิ่งนี้จะเรียกใช้นิพจน์ที่เฝ้าดูในทุกรอบการสรุปย่อย นี่เป็นเรื่องที่ไม่เหมาะสมและอาจทำให้ประสิทธิภาพลดลงหรือไม่ก็ได้ อย่างไรก็ตามมันเป็นสิ่งที่ Angular ทำดังนั้นมันจึงไม่เลวร้ายขนาดนั้น ...


ขอบคุณ! ฉันคาดหวังว่าการใช้ตัวกรองในมุมมองหรือในคอนโทรลเลอร์จะทำงานเหมือนกันทุกประการ ดูเหมือนจะไม่เป็นเช่นนั้นที่นี่
ndequeker

ฉันจะบอกว่าการใช้ a $scope.$watchนั้นค่อนข้างเกินความจำเป็นเนื่องจาก Angular Translate เสนอบริการที่จะใช้ในคอนโทรลเลอร์ ดูคำตอบของฉันด้านล่าง
Robin van Baalen

1
ไม่จำเป็นต้องใช้ตัวกรอง Angular Translate เนื่องจาก$translate.instant()มีข้อเสนอเหมือนกับบริการ นอกจากนี้โปรดใส่ใจกับคำตอบของ Pascal
knalli

ฉันเห็นด้วยการใช้ $ watch นั้นมากเกินไป คำตอบด้านล่างนี้คือการใช้งานที่เหมาะสมยิ่งขึ้น
jpblancoder

141

แนะนำ: อย่าแปลในคอนโทรลเลอร์แปลในมุมมองของคุณ

ฉันขอแนะนำให้ควบคุมคอนโทรลเลอร์ของคุณให้ปราศจากตรรกะในการแปลและแปลสตริงของคุณโดยตรงในมุมมองของคุณดังนี้:

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

การใช้บริการที่มีให้

Angular Translate ให้$translateบริการที่คุณสามารถใช้ในคอนโทรลเลอร์ของคุณ

ตัวอย่างการใช้$translateบริการสามารถ:

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

บริการแปลภาษายังมีวิธีการแปลสตริงโดยตรงโดยไม่จำเป็นต้องจัดการสัญญาโดยใช้$translate.instant():

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

ข้อเสียของการใช้$translate.instant()อาจเป็นเพราะไฟล์ภาษายังไม่ถูกโหลดหากคุณกำลังโหลดแบบ async

การใช้ตัวกรองที่ให้มา

นี่เป็นวิธีที่ฉันชอบเพราะฉันไม่ต้องจัดการกับคำสัญญาด้วยวิธีนี้ เอาต์พุตของตัวกรองสามารถตั้งค่าเป็นตัวแปรขอบเขตได้โดยตรง

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

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

การใช้คำสั่งที่ให้มา

เนื่องจาก @PascalPrecht เป็นผู้สร้างห้องสมุดที่ยอดเยี่ยมนี้ฉันขอแนะนำให้ไปตามคำแนะนำของเขา (ดูคำตอบด้านล่าง)และใช้คำสั่งที่ให้มาซึ่งดูเหมือนว่าจะจัดการกับการแปลได้อย่างชาญฉลาด

คำสั่งจะดูแลการดำเนินการแบบอะซิงโครนัสและยังฉลาดพอที่จะยกเลิกการดูรหัสการแปลในขอบเขตหากการแปลไม่มีค่าไดนามิก


หากคุณลองใช้แทนการเขียนความคิดเห็นที่ไม่เกี่ยวข้องตอนนี้คุณคงจะรู้คำตอบแล้ว คำตอบสั้น ๆ : ใช่ นั่นเป็นไปได้
Robin van Baalen

1
ในตัวอย่างของคุณด้วยตัวกรองในคอนโทรลเลอร์: เช่นเดียวกับทันที () หากไม่ได้โหลดไฟล์ภาษาสิ่งนี้จะไม่ทำงานใช่ไหม เราไม่ควรใช้นาฬิกาในกรณีนั้นหรือ? หรือคุณหมายถึงพูดว่า 'ใช้ตัวกรองเฉพาะเมื่อคุณรู้ว่ามีการโหลดคำแปล?
Bombinosh

@Bombinosh ฉันจะบอกว่าให้ใช้วิธีการกรองถ้าคุณรู้ว่ามีการโหลดคำแปล โดยส่วนตัวแล้วฉันอยากจะแนะนำว่าอย่าโหลดคำแปลแบบไดนามิกหากคุณไม่จำเป็นต้องทำ เป็นส่วนบังคับของแอปพลิเคชันของคุณดังนั้นคุณไม่ต้องการให้ผู้ใช้รอ แต่นั่นเป็นความคิดเห็นส่วนตัว
Robin van Baalen

ประเด็นของการแปลคือสามารถเปลี่ยนแปลงตามการตั้งค่าของผู้ใช้หรือแม้กระทั่งการกระทำของผู้ใช้ โดยทั่วไปแล้วคุณต้องโหลดแบบไดนามิก อย่างน้อยถ้าจำนวนสตริงที่จะแปลมีความสำคัญและ / หรือหากคุณมีคำแปลจำนวนมาก
PhiLho

4
เมื่อการแปลเสร็จสิ้นใน HTML วงจรการย่อยจะทำงานสองครั้ง แต่เรียกใช้เพียงครั้งเดียวในคอนโทรลเลอร์ 99% ของกรณีนี้อาจไม่สำคัญ แต่ฉันมีปัญหากับประสิทธิภาพที่แย่มากในตาราง ui เชิงมุมที่มีการแปลในหลายเซลล์ เป็นกรณีพิเศษที่ต้องระวัง
tykowale

123

จริงๆแล้วคุณควรใช้คำสั่งการแปลสำหรับเนื้อหาดังกล่าวแทน

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

คำสั่งจะดูแลการดำเนินการแบบอะซิงโครนัสและยังฉลาดพอที่จะยกเลิกการดูรหัสการแปลในขอบเขตหากการแปลไม่มีค่าไดนามิก

อย่างไรก็ตามหากไม่มีวิธีการใด ๆ และคุณต้องใช้$translateบริการในคอนโทรลเลอร์จริงๆคุณควรปิดการโทรใน$translateChangeSuccessเหตุการณ์โดยใช้$rootScopeร่วมกับ$translate.instant()สิ่งนี้:

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

แล้วทำไม$rootScopeไม่$scope? เหตุผลก็คือในเหตุการณ์ของการแปลเชิงมุมจะถูก$emitแก้ไข$rootScopeมากกว่าที่จะ$broadcastแก้ไข$scopeเนื่องจากเราไม่จำเป็นต้องออกอากาศผ่านลำดับชั้นของขอบเขตทั้งหมด

ทำไม$translate.instant()ไม่ใช่แค่ async $translate()? เมื่อ$translateChangeSuccessเหตุการณ์ถูกเริ่มทำงานตรวจสอบให้แน่ใจว่ามีข้อมูลการแปลที่ต้องการและไม่มีการดำเนินการแบบอะซิงโครนัสเกิดขึ้น (ตัวอย่างเช่นการเรียกใช้ตัวโหลดแบบอะซิงโครนัส) ดังนั้นเราจึงสามารถใช้$translate.instant()สิ่งที่ซิงโครนัสได้และเพียงแค่ถือว่ามีการแปล

นอกจากนี้ยังมีเวอร์ชัน 2.8.0 $translate.onReady()ซึ่งส่งคืนสัญญาที่ได้รับการแก้ไขทันทีที่การแปลพร้อมดูการเปลี่ยนแปลง


อาจมีปัญหาด้านประสิทธิภาพหรือไม่หากฉันใช้คำสั่งแปลแทนตัวกรอง นอกจากนี้ฉันเชื่อว่าภายในจะเฝ้าดูมูลค่าการส่งคืนของทันที () ดังนั้นจะลบนาฬิกาเมื่อขอบเขตปัจจุบันถูกทำลายหรือไม่?
Nilesh

ฉันลองใช้คำแนะนำของคุณ แต่ไม่ได้ผลเมื่อค่าของตัวแปรขอบเขตเปลี่ยนแปลงแบบไดนามิก
Nilesh

10
ที่จริงแล้วการหลีกเลี่ยงตัวกรองจะดีกว่าเสมอเพราะมันทำให้แอปของคุณช้าลงเพราะตั้งค่านาฬิกาใหม่อยู่เสมอ อย่างไรก็ตามคำสั่งจะไปอีกเล็กน้อย ตรวจสอบว่าต้องดูค่าของรหัสการแปลหรือไม่ ซึ่งช่วยให้แอปของคุณทำงานได้ดีขึ้น คุณช่วยสร้างท่อนซุงและเชื่อมโยงให้ฉันได้ไหมเพื่อที่ฉันจะได้ดูเพิ่มเติม
Pascal Precht

Plunk: plnkr.co/edit/j53xL1EdJ6bT20ldlhxrน่าจะเป็นในตัวอย่างของฉันคำสั่งกำลังตัดสินใจที่จะไม่ดูมูลค่า นอกจากนี้ยังเป็นอีกปัญหาที่แยกต่างหากตัวจัดการข้อผิดพลาดที่กำหนดเองของฉันจะถูกเรียกหากไม่พบคีย์ แต่ไม่แสดงสตริงที่ส่งคืน ฉันจะทำอีกก้อนสำหรับมัน
Nilesh

2
@PascalPrecht เพียงแค่มีคำถามการใช้ bind-once กับการแปลเป็นวิธีปฏิบัติที่ดีหรือไม่? แบบนี้{{::'HELLO_WORLD | translate}}'.
Zunair Zubair

5

หากต้องการแปลในคอนโทรลเลอร์คุณสามารถใช้$translateบริการ:

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

คำสั่งดังกล่าวจะแปลเฉพาะการเปิดใช้งานคอนโทรลเลอร์ แต่ไม่พบการเปลี่ยนแปลงรันไทม์ในภาษา เพื่อให้บรรลุพฤติกรรมนั้นคุณสามารถฟัง$rootScopeเหตุการณ์: $translateChangeSuccessและทำการแปลแบบเดียวกันที่นั่น:

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

แน่นอนคุณสามารถห่อหุ้ม$translateบริการด้วยวิธีการและเรียกใช้ในตัวควบคุมและในตัว$translateChangeSucessฟัง


1

สิ่งที่เกิดขึ้นคือ Angular-translate กำลังดูนิพจน์ด้วยระบบอิงเหตุการณ์และเช่นเดียวกับในกรณีอื่น ๆ ของการโยงหรือการโยงสองทางเหตุการณ์จะเริ่มทำงานเมื่อมีการดึงข้อมูลและค่าจะเปลี่ยนไปซึ่ง เห็นได้ชัดว่าใช้ไม่ได้กับการแปล ข้อมูลการแปลซึ่งแตกต่างจากข้อมูลไดนามิกอื่น ๆ ในหน้าแน่นอนต้องแสดงต่อผู้ใช้ทันที ไม่สามารถปรากฏขึ้นหลังจากโหลดหน้าเว็บ

แม้ว่าคุณจะสามารถแก้ไขปัญหานี้ได้สำเร็จ แต่ปัญหาที่ใหญ่กว่าคืองานพัฒนาที่เกี่ยวข้องนั้นมีมาก นักพัฒนาต้องแยกทุกสตริงบนไซต์ด้วยตนเองใส่ไว้ในไฟล์. json อ้างอิงด้วยตนเองด้วยรหัสสตริง (เช่น 'pageTitle' ในกรณีนี้) ไซต์เชิงพาณิชย์ส่วนใหญ่มีสตริงหลายพันรายการที่จำเป็นต้องเกิดขึ้น และนั่นเป็นเพียงจุดเริ่มต้น ตอนนี้คุณจำเป็นต้องมีระบบในการเก็บคำแปลไว้ในการซิงโครไนซ์เมื่อข้อความที่อยู่เบื้องหลังเปลี่ยนไปในบางส่วนระบบสำหรับการส่งไฟล์การแปลไปยังนักแปลหลาย ๆ คนในการรวมไฟล์เหล่านั้นเข้าไปในบิลด์ของการปรับใช้ไซต์ใหม่เพื่อให้นักแปลสามารถมองเห็นได้ การเปลี่ยนแปลงในบริบทและในและต่อไป

นอกจากนี้เนื่องจากเป็นระบบที่อิงตามเหตุการณ์เหตุการณ์จะเริ่มทำงานสำหรับทุกสตริงในหน้าซึ่งไม่เพียง แต่เป็นวิธีที่ช้ากว่าในการแปลงหน้า แต่ยังสามารถชะลอการดำเนินการทั้งหมดในเพจได้อีกด้วย หากคุณเริ่มเพิ่มเหตุการณ์จำนวนมากเข้าไป

อย่างไรก็ตามการใช้แพลตฟอร์มการแปลหลังการประมวลผลนั้นเหมาะสมกับฉันมากกว่า ใช้ GlobalizeIt เช่นล่ามก็สามารถไปที่หน้าบนเว็บไซต์และเริ่มต้นการแก้ไขข้อความโดยตรงบนหน้าสำหรับภาษาของพวกเขาและที่มัน: https://www.globalizeit.com/HowItWorks ไม่จำเป็นต้องเขียนโปรแกรม (แม้ว่าจะสามารถขยายโปรแกรมได้) แต่ก็รวมเข้ากับ Angular ได้อย่างง่ายดาย: https://www.globalizeit.com/Translate/Angularการเปลี่ยนแปลงของหน้าจะเกิดขึ้นในครั้งเดียวและจะแสดงข้อความที่แปลด้วย การเรนเดอร์เริ่มต้นของเพจ

การเปิดเผยข้อมูลทั้งหมด: ฉันเป็นผู้ร่วมก่อตั้ง :)

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.