Cara Menginternasionalkan Aplikasi AngularJS Anda
Diterbitkan: 2022-03-11Menginternasionalkan aplikasi Anda dapat membuat pengembangan perangkat lunak menjadi pengalaman yang menyakitkan, terutama jika Anda tidak mulai melakukannya dari awal atau Anda mengambil pendekatan mau tak mau ke arah itu.
Aplikasi modern, di mana front-end dan back-end secara jelas terpisah satu sama lain, dapat menjadi lebih sulit untuk ditangani dalam hal internasionalisasi. Tiba-tiba Anda tidak lagi memiliki akses ke sejumlah besar alat yang telah teruji waktu yang pernah membantu menginternasionalkan aplikasi web yang dihasilkan halaman sisi server tradisional Anda.
Oleh karena itu, aplikasi AngularJS memerlukan pengiriman data internasionalisasi (i18n) dan lokalisasi (l10n) sesuai permintaan untuk dikirimkan ke klien untuk merender dirinya sendiri di lokal yang sesuai. Tidak seperti aplikasi yang dirender sisi server tradisional, Anda tidak dapat lagi mengandalkan server untuk mengirimkan halaman yang sudah dilokalkan. Anda dapat mempelajari tentang membangun aplikasi PHP multibahasa di sini
Dalam artikel ini, Anda akan belajar bagaimana Anda dapat menginternasionalkan aplikasi AngularJS Anda, dan akan belajar tentang alat yang dapat Anda gunakan untuk memudahkan prosesnya. Membuat aplikasi AngularJS Anda multibahasa dapat menimbulkan beberapa tantangan yang menarik, tetapi pendekatan tertentu dapat mempermudah untuk mengatasi sebagian besar tantangan tersebut.
Aplikasi AngularJS Berkemampuan i18n Sederhana
Untuk memungkinkan klien mengubah bahasa dan lokal dengan cepat berdasarkan preferensi pengguna, Anda perlu membuat sejumlah keputusan desain utama:
- Bagaimana Anda mendesain aplikasi Anda menjadi agnostik bahasa dan lokal sejak awal?
- Bagaimana Anda menyusun data i18n dan l10n?
- Bagaimana Anda mengirimkan data ini secara efisien kepada klien?
- Bagaimana Anda mengabstraksikan sebanyak mungkin detail implementasi tingkat rendah untuk menyederhanakan alur kerja pengembang?
Menjawab pertanyaan-pertanyaan ini sedini mungkin dapat membantu menghindari hambatan dalam proses pengembangan di kemudian hari. Masing-masing tantangan ini akan dibahas dalam artikel ini; beberapa melalui perpustakaan AngularJS yang kuat, yang lain melalui strategi dan pendekatan tertentu.
Pustaka Internasionalisasi untuk AngularJS
Ada sejumlah pustaka JavaScript yang dibuat khusus untuk menginternasionalkan aplikasi AngularJS.
angular-translate
adalah modul AngularJS yang menyediakan filter dan arahan, bersama dengan kemampuan untuk memuat data i18n secara asinkron. Ini mendukung pluralisasi melalui MessageFormat
, dan dirancang agar sangat dapat diperluas dan dikonfigurasi.
Jika Anda menggunakan angular-translate
dalam proyek Anda, Anda mungkin menemukan beberapa paket berikut sangat berguna:
-
angular-sanitize
: dapat digunakan untuk mencegah serangan XSS dalam terjemahan. -
angular-translate-interpolation-messageformat
: pluralisasi dengan dukungan untuk pemformatan teks sensitif gender. -
angular-translate-loader-partial
: digunakan untuk mengirimkan string yang diterjemahkan ke klien.
Untuk pengalaman yang benar-benar dinamis, Anda dapat menambahkan angular-dynamic-locale
ke kelompok tersebut. Pustaka ini memungkinkan Anda untuk mengubah lokal secara dinamis—dan itu termasuk cara tanggal, angka, mata uang, dll. diformat.
Memulai: Memasang Paket yang Relevan
Dengan asumsi Anda sudah menyiapkan boilerplate AngularJS Anda, Anda dapat menggunakan NPM untuk menginstal paket internasionalisasi:
npm i -S angular-translate angular-translate-interpolation-messageformat angular-translate-loader-partial angular-sanitize messageformat
Setelah paket diinstal, jangan lupa untuk menambahkan modul sebagai dependensi aplikasi Anda:
// /src/app/core/core.module.js app.module('app.core', ['pascalprecht.translate', ...]);
Perhatikan bahwa nama modul berbeda dengan nama paket.
Menerjemahkan String Pertama Anda
Misalkan aplikasi Anda memiliki bilah alat dengan beberapa teks dan bidang dengan beberapa teks placeholder:
<nav class="navbar navbar-default"> <div class="container-fluid"> <div class="navbar-header"> <a class="navbar-brand" href="#">Hello</a> </div> <div class="collapse navbar-collapse"> <form class="navbar-form navbar-left"> <div class="form-group"> <input type="text" class="form-control" ng-model="vm.query" placeholder="Search"> </div> ... </div> </div> </nav>
Tampilan di atas memiliki dua bit teks yang dapat Anda internasionalkan: "Halo" dan "Cari". Dalam hal HTML, satu muncul sebagai teks bagian dalam dari tag jangkar, sementara yang lain muncul sebagai nilai atribut.
Untuk menginternasionalkannya, Anda harus mengganti kedua literal string dengan token yang kemudian dapat diganti oleh AngularJS dengan string terjemahan yang sebenarnya, berdasarkan preferensi pengguna, saat merender halaman.
AngularJS dapat melakukan ini dengan menggunakan token Anda untuk melakukan pencarian di tabel terjemahan yang Anda berikan. Modul angular-translate
mengharapkan tabel terjemahan ini disediakan sebagai objek JavaScript biasa atau sebagai objek JSON (jika memuat dari jarak jauh).
Berikut adalah contoh tampilan tabel terjemahan ini secara umum:
// /src/app/toolbar/i18n/en.json { "TOOLBAR": { "HELLO": "Hello", "SEARCH": "Search" } } // /src/app/toolbar/i18n/tr.json { "TOOLBAR": { "HELLO": "Merhaba", "SEARCH": "Ara" } }
Untuk menginternasionalkan tampilan bilah alat dari atas, Anda perlu mengganti literal string dengan token yang dapat digunakan AngularJS untuk mencari di tabel terjemahan:
<!-- /src/app/toolbar/toolbar.html --> <a class="navbar-brand" href="#" translate="TOOLBAR.HELLO"></a> <!-- or --> <a class="navbar-brand" href="#">{{'TOOLBAR.HELLO' | translate}}</a>
Perhatikan bagaimana, untuk teks dalam, Anda dapat menggunakan direktif translate
atau filter translate
. (Anda dapat mempelajari lebih lanjut tentang arahan translate
di sini dan tentang filter translate
di sini.)
Dengan perubahan ini, saat tampilan dirender, angular-translate
akan secara otomatis memasukkan terjemahan yang sesuai dengan TOOLBAR.HELLO
ke dalam DOM berdasarkan bahasa saat ini.
Untuk tokenize string literal yang muncul sebagai nilai atribut, Anda dapat menggunakan pendekatan berikut:
<!-- /src/app/toolbar/toolbar.html --> <input type="text" class="form-control" ng-model="vm.query" translate translate-attr-placeholder="TOOLBAR.SEARCH">
Sekarang, bagaimana jika string tokenized Anda berisi variabel?
Untuk menangani kasus seperti “Halo, {{name}}.”, Anda dapat melakukan penggantian variabel menggunakan sintaks interpolator yang sama yang sudah didukung AngularJS:
Tabel terjemahan:
// /src/app/toolbar/i18n/en.json { "TOOLBAR": { "HELLO": "Hello, {{name}}." } }
Anda kemudian dapat mendefinisikan variabel dalam beberapa cara. Berikut adalah beberapa:
<!-- /src/app/toolbar/toolbar.html --> <a ... translate="TOOLBAR.HELLO" translate-values='{ name: vm.user.name }'></a> <!-- or --> <a ... translate="TOOLBAR.HELLO" translate-value-name='{{vm.user.name}}'></a> <!-- or --> <a ...>{{'TOOLBAR.HELLO | translate:'{ name: vm.user.name }'}}</a>
Menghadapi Pluralisasi dan Gender
Pluralisasi adalah topik yang cukup sulit dalam hal i18n dan l10n. Bahasa dan budaya yang berbeda memiliki aturan yang berbeda tentang bagaimana suatu bahasa menangani pluralisasi dalam berbagai situasi.
Karena tantangan ini, pengembang perangkat lunak terkadang tidak mengatasi masalah (atau setidaknya tidak menanganinya secara memadai), menghasilkan perangkat lunak yang menghasilkan kalimat konyol seperti ini:
He saw 1 person(s) on floor 1. She saw 1 person(s) on floor 3. Number of people seen on floor 2: 2.
Untungnya, ada standar untuk cara menangani ini, dan implementasi JavaScript dari standar tersebut tersedia sebagai MessageFormat.
Dengan MessageFormat, Anda dapat mengganti kalimat yang tidak terstruktur dengan baik di atas dengan yang berikut:
He saw 1 person on the 2nd floor. She saw 1 person on the 3rd floor. They saw 2 people on the 5th floor.
MessageFormat
menerima ekspresi seperti berikut:
var message = [ '{GENDER, select, male{He} female{She} other{They}}', 'saw', '{COUNT, plural, =0{no one} one{1 person} other{# people}}', 'on the', '{FLOOR, selectordinal, one{#st} two{#nd} few{#rd} other{#th}}', 'floor.' ].join(' ');
Anda dapat membuat formatter dengan array di atas, dan menggunakannya untuk menghasilkan string:
var messageFormatter = new MessageFormat('en').compile(message); messageFormatter({ GENDER: 'male', COUNT: 1, FLOOR: 2 }) // 'He saw 1 person on the 2nd floor.' messageFormatter({ GENDER: 'female', COUNT: 1, FLOOR: 3 }) // 'She saw 1 person on the 3rd floor.' messageFormatter({ COUNT: 2, FLOOR: 5 }) // 'They saw 2 people on the 5th floor.'
Bagaimana Anda bisa menggunakan MessageFormat
dengan angular-translate
untuk memanfaatkan fungsionalitas penuhnya dalam aplikasi Anda?
Dalam konfigurasi aplikasi Anda, Anda cukup memberi tahu angular-translate
bahwa interpolasi format pesan tersedia sebagai berikut:
/src/app/core/core.config.js app.config(function ($translateProvider) { $translateProvider.addInterpolation('$translateMessageFormatInterpolation'); });
Berikut adalah tampilan entri dalam tabel terjemahan:
// /src/app/main/social/i18n/en.json { "SHARED": "{GENDER, select, male{He} female{She} other{They}} shared this." }
Dan dalam tampilan:
<!-- /src/app/main/social/social.html --> <div translate="SHARED" translate-values="{ GENDER: 'male' }" translate-interpolation="messageformat"></div> <div> {{ 'SHARED' | translate:"{ GENDER: 'male' }":'messageformat' }} </div>
Di sini Anda harus secara eksplisit menunjukkan bahwa interpolator format pesan harus digunakan sebagai ganti interpolator default di AngularJS. Ini karena kedua interpolator sedikit berbeda dalam sintaksisnya. Anda dapat membaca lebih lanjut tentang ini di sini.
Menyediakan Tabel Terjemahan ke Aplikasi Anda
Sekarang setelah Anda mengetahui bagaimana AngularJS dapat mencari terjemahan untuk token Anda dari tabel terjemahan, bagaimana aplikasi Anda mengetahui tentang tabel terjemahan? Bagaimana Anda memberi tahu aplikasi Anda lokal/bahasa mana yang harus digunakan?
Di sinilah Anda belajar tentang $translateProvider
.
Anda dapat menyediakan tabel terjemahan untuk setiap lokal yang ingin Anda dukung secara langsung di file core.config.js
aplikasi Anda sebagai berikut:

// /src/app/core/core.config.js app.config(function ($translateProvider) { $translateProvider.addInterpolation('$translateMessageFormatInterpolation'); $translateProvider.translations('en', { TOOLBAR: { HELLO: 'Hello, {{name}}.' } }); $translateProvider.translations('tr', { TOOLBAR: { HELLO: 'Merhaba, {{name}}.' } }); $translateProvider.preferredLanguage('en'); });
Di sini Anda menyediakan tabel terjemahan sebagai objek JavaScript untuk bahasa Inggris (en) dan Turki (tr), sambil mendeklarasikan bahasa saat ini sebagai bahasa Inggris (en). Jika pengguna ingin mengubah bahasa, Anda dapat melakukannya dengan layanan $translate:
// /src/app/toolbar/toolbar.controller.js app.controller('ToolbarCtrl', function ($scope, $translate) { $scope.changeLanguage = function (languageKey) { $translate.use(languageKey); // Persist selection in cookie/local-storage/database/etc... }; });
Masih ada pertanyaan bahasa mana yang harus digunakan secara default. Hard-coding bahasa awal aplikasi kami mungkin tidak selalu dapat diterima. Dalam kasus seperti itu, alternatifnya adalah mencoba menentukan bahasa secara otomatis menggunakan $translateProvider:
// /src/app/core/core.config.js app.config(function ($translateProvider) { ... $translateProvider.determinePreferredLanguage(); });
determinePreferredLanguage
mencari nilai di window.navigator
dan memilih default cerdas hingga sinyal yang jelas diberikan oleh pengguna.
Tabel Terjemahan yang memuat lambat
Bagian sebelumnya menunjukkan bagaimana Anda dapat menyediakan tabel terjemahan secara langsung dalam kode sumber sebagai objek JavaScript. Ini mungkin dapat diterima untuk aplikasi kecil, tetapi pendekatannya tidak dapat diskalakan, itulah sebabnya tabel terjemahan sering diunduh sebagai file JSON dari server jauh.
Mempertahankan tabel terjemahan dengan cara ini mengurangi ukuran muatan awal yang dikirimkan ke klien tetapi menimbulkan kerumitan tambahan. Sekarang Anda dihadapkan pada tantangan desain untuk mengirimkan data i18n ke klien. Jika hal ini tidak ditangani dengan hati-hati, kinerja aplikasi Anda dapat menurun dengan sia-sia.
Mengapa begitu kompleks? Aplikasi AngularJS diatur ke dalam modul. Dalam aplikasi yang kompleks, mungkin ada banyak modul, masing-masing dengan data i18n yang berbeda. Oleh karena itu, pendekatan naif, seperti memuat dan menyediakan data i18n sekaligus, harus dihindari.
Yang Anda butuhkan adalah cara untuk mengatur data i18n Anda berdasarkan modul. Ini akan memungkinkan Anda untuk memuat apa yang Anda butuhkan saat Anda membutuhkannya, dan untuk menyimpan apa yang sebelumnya telah dimuat untuk menghindari memuat ulang data yang sama (setidaknya sampai cache tidak valid).
Di sinilah partialLoader
berperan.
Katakanlah tabel terjemahan aplikasi Anda terstruktur seperti ini:
/src/app/main/i18n/en.json /src/app/main/i18n/tr.json /src/app/toolbar/i18n/en.json /src/app/toolbar/i18n/tr.json
Anda dapat mengonfigurasi $translateProvider
untuk menggunakan partialLoader
dengan pola URL yang cocok dengan struktur ini:
// /src/app/core/core.config.js app.config(function ($translateProvider) { ... $translateProvider.useLoader('$translatePartialLoader', { urlTemplate: '/src/app/{part}/i18n/{lang}.json' }); });
Seperti yang diharapkan, "lang" diganti dengan kode bahasa saat runtime (misalnya "en" atau "tr"). Bagaimana dengan "bagian"? Bagaimana $translateProvider mengetahui "bagian" mana yang akan dimuat?
Anda dapat memberikan informasi ini di dalam pengontrol dengan $translatePartialLoader
:
// /src/app/main/main.controller.js app.controller('MainCtrl', function ($translatePartialLoader) { $translatePartialLoader.addPart('main'); }); // /src/app/toolbar/toolbar.config.js app.controller('ToolbarCtrl', function ($translatePartialLoader) { $translatePartialLoader.addPart('toolbar'); });
Polanya sekarang selesai, dan data i18n untuk tampilan tertentu dimuat saat pengontrolnya pertama kali dijalankan, yang persis seperti yang Anda inginkan.
Caching: Mengurangi Waktu Muat
Bagaimana dengan caching?
Anda dapat mengaktifkan cache standar di konfigurasi aplikasi dengan $translateProvider
:
// /src/app/core/core.config.js app.config(function ($translateProvider) { ... $translateProvider.useLoaderCache(true); // default is false });
Jika Anda perlu memecahkan cache untuk bahasa tertentu, Anda dapat menggunakan $translate
:
$translate.refresh(languageKey); // omit languageKey to refresh all
Dengan bagian-bagian ini di tempat, aplikasi Anda sepenuhnya diinternasionalkan dan mendukung banyak bahasa.
Melokalkan Angka, Mata Uang, dan Tanggal
Di bagian ini, Anda akan mempelajari bagaimana Anda dapat menggunakan angular-dynamic-locale
untuk mendukung pemformatan elemen UI seperti angka, mata uang, tanggal, dan sejenisnya, dalam aplikasi AngularJS.
Anda perlu menginstal dua paket lagi untuk ini:
npm i -S angular-dynamic-locale angular-i18n
Setelah paket diinstal, Anda dapat menambahkan modul ke dependensi aplikasi Anda:
// /src/app/core/core.module.js app.module('app.core', ['tmh.dynamicLocale', ...]);
Aturan Lokal
Aturan lokal adalah file JavaScript sederhana yang memberikan spesifikasi tentang bagaimana tanggal, angka, mata uang, dan sejenisnya harus diformat oleh komponen yang bergantung pada layanan $locale.
Daftar lokal yang saat ini didukung tersedia di sini.
Berikut ini cuplikan dari angular-locale_en-us.js
mengilustrasikan pemformatan bulan dan tanggal:
... "MONTH": [ "January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December" ], "SHORTDAY": [ "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat" ], ...
Tidak seperti data i18n, aturan lokal bersifat global untuk aplikasi, membutuhkan aturan untuk lokal tertentu untuk dimuat sekaligus.
Secara default, angular-dynamic-locale
mengharapkan file aturan lokal ditempatkan di angular/i18n/angular-locale_{{locale}}.js
. Jika mereka berada di tempat lain, tmhDynamicLocaleProvider
harus digunakan untuk mengganti default:
// /src/app/core/core.config.js app.config(function (tmhDynamicLocaleProvider) { tmhDynamicLocaleProvider.localeLocationPattern( '/node_modules/angular-i18n/angular-locale_{{locale}}.js'); });
Caching ditangani secara otomatis oleh layanan tmhDynamicLocaleCache
.
Memvalidasi cache kurang menjadi perhatian di sini, karena aturan lokal cenderung tidak berubah daripada terjemahan string.
Untuk beralih antar lokal, angular-dynamic-locale
menyediakan layanan tmhDynamicLocale
:
// /src/app/toolbar/toolbar.controller.js app.controller('ToolbarCtrl', function ($scope, tmhDynamicLocale) { $scope.changeLocale = function (localeKey) { tmhDynamicLocale.set(localeKey); // Persist selection in cookie/local-storage/database/etc... }; });
Membuat Tabel Terjemahan dengan Terjemahan Otomatis
Aturan lokal dikirimkan dengan paket angular-i18n
, jadi yang harus Anda lakukan adalah membuat konten paket tersedia untuk aplikasi Anda sesuai kebutuhan. Tetapi bagaimana Anda menghasilkan file JSON untuk tabel terjemahan Anda? Tidak ada paket yang dapat Anda unduh dan pasang ke aplikasi kami.
Salah satu opsinya adalah menggunakan API terjemahan terprogram, terutama jika string dalam aplikasi Anda adalah literal sederhana tanpa variabel atau ekspresi jamak.
Dengan Gulp dan beberapa paket tambahan, meminta terjemahan terprogram untuk aplikasi Anda sangatlah mudah:
import gulp from 'gulp'; import map from 'map-stream'; import rename from 'gulp-rename'; import traverse from 'traverse'; import transform from 'vinyl-transform'; import jsonFormat from 'gulp-json-format'; function translateTable(to) { return transform(() => { return map((data, done) => { const table = JSON.parse(data); const strings = []; traverse(table).forEach(function (value) { if (typeof value !== 'object') { strings.push(value); } }); Promise.all(strings.map((s) => getTranslation(s, to))) .then((translations) => { let index = 0; const translated = traverse(table).forEach(function (value) { if (typeof value !== 'object') { this.update(translations[index++]); } }); done(null, JSON.stringify(translated)); }) .catch(done); }); }); } function translate(to) { return gulp.src('src/app/**/i18n/en.json') .pipe(translateTable(to)) .pipe(jsonFormat(2)) .pipe(rename({ basename: to })) .pipe(gulp.dest('src/app')); } gulp.task('translate:tr', () => translate('tr')); This task assumes the following folder structure: /src/app/main/i18n/en.json /src/app/toolbar/i18n/en.json /src/app/navigation/i18n/en.json ...
Script pertama membaca semua tabel terjemahan bahasa Inggris, secara asinkron meminta terjemahan untuk sumber daya string mereka, dan kemudian mengganti string bahasa Inggris dengan string yang diterjemahkan untuk menghasilkan tabel terjemahan dalam bahasa baru.
Akhirnya, tabel terjemahan baru ditulis sebagai saudara dari tabel terjemahan bahasa Inggris, menghasilkan:
/src/app/main/i18n/en.json /src/app/main/i18n/tr.json /src/app/toolbar/i18n/en.json /src/app/toolbar/i18n/tr.json /src/app/navigation/i18n/en.json /src/app/navigation/i18n/tr.json ...
Implementasi getTranslation
juga mudah:
import bluebird from 'bluebird'; import MicrosoftTranslator from 'mstranslator'; bluebird.promisifyAll(MicrosoftTranslator.prototype); const Translator = new MicrosoftTranslator({ client_id: process.env.MICROSOFT_TRANSLATOR_CLIENT_ID, client_secret: process.env.MICROSOFT_TRANSLATOR_CLIENT_SECRET }, true); function getTranslation(string, to) { const text = string; const from = 'en'; return Translator.translateAsync({ text, from, to }); }
Di sini, kami menggunakan Microsoft Translate, tetapi seseorang dapat dengan mudah menggunakan penyedia lain seperti Google Translate atau Yandex Translate.
Meskipun terjemahan terprogram nyaman, ada beberapa kelemahan, termasuk:
- Terjemahan robot bagus untuk string pendek, tetapi meskipun demikian, mungkin ada jebakan dengan kata-kata yang memiliki arti berbeda dalam konteks yang berbeda (misalnya, "kolam" dapat berarti berenang atau berkelompok).
- API mungkin tidak dapat menangani string dengan variabel atau string yang bergantung pada format pesan.
Dalam kasus ini dan lainnya, terjemahan manusia mungkin diperlukan; Namun, itu topik untuk posting blog lain.
Internasionalisasi Front-ends Hanya Terlihat Menakutkan
Dalam artikel ini, Anda mempelajari cara menggunakan paket-paket ini untuk menginternasionalkan dan melokalkan aplikasi AngularJS.
angular-translate
, angular-dynamic-locale
, dan gulp
adalah alat yang ampuh untuk menginternasionalkan aplikasi AngularJS yang merangkum detail implementasi tingkat rendah yang menyakitkan.
Untuk aplikasi demo yang mengilustrasikan ide-ide yang dibahas dalam posting ini, lihat repositori GitHub ini.