Dalam artikel saya sebelum ini, saya memperkenalkan Stimulus - rangka kerja JavaScript ringkas yang dicipta oleh Basecamp. Hari ini saya akan membincangkan pengantarabangsaan aplikasi Rangsangan, kerana rangka kerja itu tidak menyediakan sebarang alat pengantarabangsaan di luar kotak. Pengantarabangsaan ialah langkah penting, terutamanya apabila aplikasi anda digunakan oleh orang di seluruh dunia, jadi pemahaman asas tentang cara melakukan pengantarabangsaan benar-benar berguna.
Sudah tentu, terpulang kepada anda untuk memutuskan penyelesaian pengantarabangsaan yang hendak dilaksanakan, sama ada jQuery.I18n, Polyglot atau sesuatu yang lain. Dalam tutorial ini, saya ingin menunjukkan kepada anda rangka kerja I18n yang popular dipanggil I18next, yang mempunyai banyak ciri hebat dan menyediakan banyak pemalam pihak ketiga tambahan untuk memudahkan lagi proses pembangunan. Walaupun dengan semua ciri ini, I18next bukanlah alat yang kompleks dan anda tidak perlu mempelajari satu tan dokumentasi untuk bermula.
Dalam artikel ini, anda akan belajar cara mendayakan sokongan I18n dalam aplikasi Rangsangan anda dengan bantuan perpustakaan I18next. Secara khusus, kita akan membincangkan:
Kod sumber tersedia dalam repositori GitHub tutorial.
Mula-mula, mari kita mengklonkan projek Stimulus Starter dan memasang semua kebergantungan menggunakan pengurus pakej Benang:
git clone https://github.com/stimulusjs/stimulus-starter.git cd stimulus-starter yarn install
Kami akan membina aplikasi web mudah untuk memuatkan maklumat tentang pengguna berdaftar. Bagi setiap pengguna kami akan memaparkan nama log masuknya dan bilangan foto yang telah dimuat naik setakat ini (tidak kira apa foto tersebut).
Selain itu, kami akan menyediakan penukar bahasa di bahagian atas halaman. Sebaik sahaja anda memilih bahasa, antara muka hendaklah diterjemahkan serta-merta tanpa perlu memuatkan semula halaman. Selain itu, URL harus dilampirkan dengan parameter ?locale
GET yang menyatakan tempat yang sedang digunakan. Sudah tentu, jika parameter ini disediakan semasa halaman dimuatkan, bahasa yang betul harus ditetapkan secara automatik. ?locale
GET 参数,指定当前使用的区域设置。当然,如果页面加载时已提供此参数,则应自动设置正确的语言。
好的,让我们继续渲染我们的用户。将以下代码行添加到 public/index.html 文件中:
<div data-controller="users" data-users-url="/api/users/index.json"></div>
在这里,我们使用 users
控制器并提供一个用于加载用户的 URL。在现实应用程序中,我们可能会有一个服务器端脚本,用于从数据库中获取用户并使用 JSON 进行响应。然而,在本教程中,我们只需将所有必要的数据放入 public/api/users/index.json 文件中即可:
[ { "login": "johndoe", "photos_count": "15", "gender": "male" }, { "login": "annsmith", "photos_count": "20", "gender": "female" } ]
现在创建一个新的src/controllers/users_controller.js文件:
import { Controller } from "stimulus" export default class extends Controller { connect() { this.loadUsers() } }
一旦控制器连接到 DOM,我们就会借助 loadUsers()
方法异步加载用户:
loadUsers() { fetch(this.data.get("url")) .then(response => response.text()) .then(json => { this.renderUsers(json) }) }
此方法向给定 URL 发送获取请求,获取响应,最后呈现用户:
renderUsers(users) { let content = '' JSON.parse(users).forEach((user) => { content += `<div>Login: ${user.login}<br>Has uploaded ${user.photos_count} photo(s)</div><hr>` }) this.element.innerHTML = content }
renderUsers()
依次解析 JSON,构造一个包含所有内容的新字符串,最后将此内容显示在页面上(this.element
是将返回控制器连接到的实际 DOM 节点,在我们的例子中是 div
)。
现在我们将继续将 I18next 集成到我们的应用程序中。向我们的项目添加两个库:I18next 本身和一个插件,以实现从后端异步加载翻译文件:
yarn add i18next i18next-xhr-backend
我们将把所有与 I18next 相关的东西存储在一个单独的 src/i18n/config.js 文件中,所以现在就创建它:
import i18next from 'i18next' import I18nXHR from 'i18next-xhr-backend' const i18n = i18next.use(I18nXHR).init({ fallbackLng: 'en', whitelist: ['en', 'ru'], preload: ['en', 'ru'], ns: 'users', defaultNS: 'users', fallbackNS: false, debug: true, backend: { loadPath: '/i18n/{{lng}}/{{ns}}.json', } }, function(err, t) { if (err) return console.error(err) }); export { i18n as i18n }
让我们从上到下了解这里发生了什么:
use(I18nXHR)
启用 i18next-xhr-backend 插件。
fallbackLng
告诉它使用英语作为后备语言。
whitelist
只允许设置英语和俄语。当然,您可以选择任何其他语言。
preload
指示从服务器预加载翻译文件,而不是在选择相应语言时加载它们。
ns
表示“命名空间”,接受字符串或数组。在此示例中,我们只有一个命名空间,但对于较大的应用程序,您可以引入其他命名空间,例如 admin
、cart
、profile
等。每个命名空间都应该创建一个单独的翻译文件。
defaultNS
将 users
设置为默认命名空间。
fallbackNS
Baiklah, mari teruskan dan berikan pengguna kami. Tambahkan baris kod berikut pada fail public/index.html: 🎜
{ "login": "Логин" }
pengguna
dan menyediakan URL untuk memuatkan pengguna. Dalam aplikasi dunia nyata, kami mungkin mempunyai skrip sebelah pelayan yang mengambil pengguna daripada pangkalan data dan bertindak balas dengan JSON. Walau bagaimanapun, untuk tutorial ini kami hanya meletakkan semua data yang diperlukan ke dalam fail public/api/users/index.json: 🎜
{ "login": "Login" }
// config goes here... function(err, t) { if (err) return console.error(err) console.log(i18n.t('login')) }
loadUsers()
: 🎜
yarn add loc-i18next
import locI18next from 'loc-i18next'
renderUsers()
menghuraikan JSON dalam urutan, membina rentetan baharu yang mengandungi semua kandungan dan akhirnya memaparkan kandungan ini pada halaman (ini. elemen
ialah nod DOM sebenar yang pengawal pemulangan akan disambungkan, dalam kes kami div
). 🎜
🎜I18Seterusnya🎜
🎜Sekarang kami akan meneruskan untuk menyepadukan I18next ke dalam aplikasi kami. Tambahkan dua perpustakaan pada projek kami: I18next sendiri dan pemalam untuk membolehkan pemuatan tak segerak fail terjemahan dari bahagian belakang: 🎜
// other config const loci18n = locI18next.init(i18n, { selectorAttr: 'data-i18n', optionsAttr: 'data-i18n-options', useOptionsAttr: true }); export { loci18n as loci18n, i18n as i18n }
import { loci18n } from '../i18n/config' // other code... loadUsers() { fetch(this.data.get("url")) .then(response => response.text()) .then(json => { this.renderUsers(json) setTimeout(() => { // <--- this.localize() }, '2000') }) }
use(I18nXHR)
Mendayakan pemalam i18next-xhr-backend. 🎜🎜
🎜
🎜
fallbackLng
memberitahunya untuk menggunakan bahasa Inggeris sebagai bahasa sandaran. 🎜🎜
🎜
🎜
senarai putih
hanya membenarkan tetapan bahasa Inggeris dan Rusia. Sudah tentu, anda boleh memilih mana-mana bahasa lain. 🎜🎜
🎜
🎜
pramuat
mengarahkan untuk pramuat fail terjemahan daripada pelayan dan bukannya memuatkannya apabila bahasa yang sepadan dipilih. 🎜🎜
🎜
🎜
ns
mewakili "ruang nama" dan menerima rentetan atau tatasusunan. Dalam contoh ini kami hanya mempunyai satu ruang nama, tetapi untuk aplikasi yang lebih besar anda boleh memperkenalkan ruang nama lain seperti admin
, cart
, profil
, dsb. Fail terjemahan yang berasingan harus dibuat untuk setiap ruang nama. 🎜🎜
🎜
🎜
defaultNS
menetapkan pengguna
sebagai ruang nama lalai. 🎜🎜
🎜
🎜
fallbackNS
melumpuhkan sandaran ruang nama. 🎜
debug
允许在浏览器的控制台中显示调试信息。具体来说,它会说明加载哪些翻译文件、选择哪种语言等。您可能希望在将应用程序部署到生产环境之前禁用此设置。
backend
为 I18nXHR 插件提供配置并指定从何处加载翻译。请注意,路径应包含区域设置的标题,而文件应以命名空间命名并具有 .json 扩展名
function(err, t)
是当 I18next 准备好时(或当出现错误时)运行的回调。
接下来,让我们制作翻译文件。俄语翻译应放入 public/i18n/ru/users.json 文件中:
{ "login": "Логин" }
login
这里是翻译键,而 Логин
是要显示的值。
英文翻译应该转到 public/i18n/en/users.json 文件:
{ "login": "Login" }
为了确保 I18next 正常工作,您可以将以下代码行添加到 i18n/config.js 文件内的回调中:
// config goes here... function(err, t) { if (err) return console.error(err) console.log(i18n.t('login')) }
在这里,我们使用一个名为 t
的方法,意思是“翻译”。该方法接受翻译键并返回相应的值。
但是,我们可能有很多 UI 部分需要翻译,而使用 t
方法来翻译会非常乏味。相反,我建议您使用另一个名为 loc-i18next 的插件,它允许您一次翻译多个元素。
安装loc-i18next插件:
yarn add loc-i18next
将其导入src/i18n/config.js文件的顶部:
import locI18next from 'loc-i18next'
现在提供插件本身的配置:
// other config const loci18n = locI18next.init(i18n, { selectorAttr: 'data-i18n', optionsAttr: 'data-i18n-options', useOptionsAttr: true }); export { loci18n as loci18n, i18n as i18n }
这里有几点需要注意:
locI18next.init(i18n)
基于之前定义的 I18next 实例创建一个新的插件实例。selectorAttr
指定使用哪个属性来检测需要本地化的元素。基本上,loc-i18next 将搜索此类元素并使用 data-i18n
属性的值作为翻译键。
optionsAttr
指定哪个属性包含附加翻译选项。
useOptionsAttr
指示插件使用其他选项。
我们的用户正在异步加载,因此我们必须等到此操作完成,然后才执行本地化。现在,我们简单地设置一个计时器,在调用 localize()
方法之前等待两秒——当然,这是一个临时的 hack。
import { loci18n } from '../i18n/config' // other code... loadUsers() { fetch(this.data.get("url")) .then(response => response.text()) .then(json => { this.renderUsers(json) setTimeout(() => { // <--- this.localize() }, '2000') }) }
编写 localize()
方法本身的代码:
localize() { loci18n('.users') }
如您所见,我们只需要将选择器传递给 loc-i18next 插件即可。内部的所有元素(设置了 data-i18n
属性)都将自动本地化。
现在调整 renderUsers
方法。现在,我们只翻译“Login”一词:
renderUsers(users) { let content = '' JSON.parse(users).forEach((user) => { content += `<div class="users">ID: ${user.id}<br><span data-i18n="login"></span>: ${user.login}<br>Has uploaded ${user.photos_count} photo(s)</div><hr>` }) this.element.innerHTML = content }
不错!重新加载页面,等待两秒钟,并确保每个用户都显示“登录”字样。
我们对部分界面进行了本地化,这真的很酷。尽管如此,每个用户还有两个字段:上传的照片数量和性别。由于我们无法预测每个用户将拥有多少张照片,因此应根据给定的数量将“照片”一词正确地复数化。为此,我们需要之前配置的 data-i18n-options
属性。要提供计数,应为 data-i18n-options
分配以下对象:{ "count": YOUR_COUNT }
。
性别信息也应考虑在内。英语中的“uploaded”一词可以适用于男性和女性,但在俄语中它要么变成“загрузил”或“загрузила”,所以我们再次需要 data-i18n-options
,其中有 { "context": "GENDER" }
作为值。顺便请注意,您可以利用此上下文来完成其他任务,而不仅仅是提供性别信息。
renderUsers(users) { let content = '' JSON.parse(users).forEach((user) => { content += `<div class="users"><span data-i18n="login"></span>: ${user.login}<br><span data-i18n="uploaded" data-i18n-options="{ 'context': '${user.gender}' }"></span> <span data-i18n="photos" data-i18n-options="{ 'count': ${user.photos_count} }"></span></div><hr>` }) this.element.innerHTML = content }
现在更新英文翻译:
{ "login": "Login", "uploaded": "Has uploaded", "photos": "one photo", "photos_plural": "{{count}} photos" }
这里没什么复杂的。由于对于英语,我们不关心性别信息(即上下文),因此翻译键应该只是 uploaded
。为了提供正确的复数翻译,我们使用 photos
和 photos_plural
键。 {{count}}
部分为插值,将替换为实际数字。
至于俄语,事情就更复杂了:
{ "login": "Логин", "uploaded_male": "Загрузил уже", "uploaded_female": "Загрузила уже", "photos_0": "одну фотографию", "photos_1": "{{count}} фотографии", "photos_2": "{{count}} фотографий" }
首先,请注意,我们有两个可能的上下文的 uploaded_male
和 uploaded_female
键。接下来,俄语中的复数规则也比英语中更复杂,因此我们必须提供不是两个而是三个可能的短语。 I18next 支持多种开箱即用的语言,这个小工具可以帮助您了解应该为给定语言指定哪些复数键。
我们已经完成了应用程序的翻译,但用户应该能够在区域设置之间切换。因此,向 public/index.html 文件添加一个新的“语言切换器”组件:
<ul data-controller="languages" class="language-switcher"></ul>
在 src/controllers/languages_controller.js 文件中制作相应的控制器:
import { Controller } from "stimulus" import { i18n, loci18n } from '../i18n/config' export default class extends Controller { initialize() { let languages = [ {title: 'English', code: 'en'}, {title: 'Русский', code: 'ru'} ] this.element.innerHTML = languages.map((lang) => { return `<li data-action="click->languages#switchLanguage" data-lang="${lang.code}">${lang.title}</li>` }).join('') } }
这里我们使用 initialize()
回调来显示支持的语言列表。每个 li
都有一个 data-action
属性,该属性指定单击元素时应触发的方法(在本例中为 switchLanguage
)。
现在添加 switchLanguage()
方法:
switchLanguage(e) { this.currentLang = e.target.getAttribute("data-lang") }
它只是获取事件的目标并获取 data-lang
属性的值。
我还想为 currentLang
属性添加 getter 和 setter:
get currentLang() { return this.data.get("currentLang") } set currentLang(lang) { if(i18n.language !== lang) { i18n.changeLanguage(lang) } if(this.currentLang !== lang) { this.data.set("currentLang", lang) loci18n('body') this.highlightCurrentLang() } }
getter 非常简单——我们获取当前使用的语言的值并返回它。
setter 更复杂。首先,如果当前设置的语言与所选语言不相等,我们使用 changeLanguage
方法。此外,我们将新选择的语言环境存储在 data-current-lang
属性(在 getter 中访问)下,使用 loc-i18next 插件本地化 HTML 页面的主体,最后突出显示当前使用的区域设置。
让我们编写 highlightCurrentLang()
的代码:
highlightCurrentLang() { this.switcherTargets.forEach((el, i) => { el.classList.toggle("current", this.currentLang === el.getAttribute("data-lang")) }) }
这里我们迭代区域设置切换器的数组,并将它们的 data-lang
属性的值与当前使用的区域设置的值进行比较。如果值匹配,则为切换器分配 current
CSS 类,否则删除该类。
为了使 this.switcherTargets
构建工作,我们需要按以下方式定义刺激目标:
static targets = [ "switcher" ]
此外,为 li
s 添加值为 switcher
的 data-target
属性:
initialize() { // ... this.element.innerHTML = languages.map((lang) => { return `<li data-action="click->languages#switchLanguage" data-target="languages.switcher" data-lang="${lang.code}">${lang.title}</li>` }).join('') // ... }
另一个需要考虑的重要事项是翻译文件可能需要一些时间来加载,我们必须等待此操作完成才能允许切换区域设置。因此,让我们利用 loaded
回调:
initialize() { i18n.on('loaded', (loaded) => { // <--- let languages = [ {title: 'English', code: 'en'}, {title: 'Русский', code: 'ru'} ] this.element.innerHTML = languages.map((lang) => { return `<li data-action="click->languages#switchLanguage" data-target="languages.switcher" data-lang="${lang.code}">${lang.title}</li>` }).join('') this.currentLang = i18n.language }) }
最后,不要忘记从 loadUsers()
方法中删除 setTimeout
:
loadUsers() { fetch(this.data.get("url")) .then(response => response.text()) .then(json => { this.renderUsers(json) this.localize() }) }
切换语言环境后,我想在包含所选语言代码的 URL 中添加 ?lang
GET 参数。在 History API 的帮助下,可以轻松地添加 GET 参数而不重新加载页面:
set currentLang(lang) { if(i18n.language !== lang) { i18n.changeLanguage(lang) window.history.pushState(null, null, `?lang=${lang}`) // <--- } if(this.currentLang !== lang) { this.data.set("currentLang", lang) loci18n('body') this.highlightCurrentLang() } }
我们今天要实现的最后一件事是能够根据用户的偏好设置区域设置。一个名为 LanguageDetector 的插件可以帮助我们解决这个任务。添加新的 Yarn 包:
yarn add i18next-browser-languagedetector
在 i18n/config.js 文件中导入 LanguageDetector
:
import LngDetector from 'i18next-browser-languagedetector'
现在调整配置:
const i18n = i18next.use(I18nXHR).use(LngDetector).init({ // <--- // other options go here... detection: { order: ['querystring', 'navigator', 'htmlTag'], lookupQuerystring: 'lang', } }, function(err, t) { if (err) return console.error(err) });
order
选项列出了插件应尝试的所有技术(按重要性排序),以便“猜测”首选区域设置:
querystring
表示检查包含区域设置代码的 GET 参数。lookupQuerystring
设置要使用的 GET 参数的名称,在我们的例子中是 lang
。navigator
表示从用户的请求中获取语言环境数据。htmlTag
涉及从 html
标记的 lang
属性获取首选区域设置。在本文中,我们介绍了 I18next——一种轻松翻译 JavaScript 应用程序的流行解决方案。您已经学习了如何将 I18next 与 Stimulus 框架集成、配置它以及以异步方式加载翻译文件。此外,您还了解了如何在区域设置之间切换以及如何根据用户的偏好设置默认语言。
I18next 有一些额外的配置选项和许多插件,因此请务必浏览其官方文档以了解更多信息。另请注意,Stimulus 不会强制您使用特定的本地化解决方案,因此您也可以尝试使用 jQuery.I18n 或 Polyglot 等解决方案。
Itu sahaja untuk hari ini! Terima kasih kerana membaca, dan sehingga kali seterusnya.
Atas ialah kandungan terperinci Setempatkan aplikasi rangsangan menggunakan I18Next. Untuk maklumat lanjut, sila ikut artikel berkaitan lain di laman web China PHP!