使用 I18Next 本地化刺激應用程式

WBOY
發布: 2023-08-28 23:53:07
原創
1446 人瀏覽過

使用 I18Next 本地化刺激应用程序

在我之前的文章中,我介紹了 Stimulus——一個由 Basecamp 創建的簡單的 JavaScript 框架。今天我將討論 Stimulus 應用程式的國際化,因為該框架不提供任何開箱即用的國際化工具。國際化是重要的一步,特別是當您的應用程式被世界各地的人們使用時,因此對如何進行國際化的基本了解可能真的會派上用場。

當然,由您決定實作哪種國際化解決方案,無論是 jQuery.I18n、Polyglot 或其他解決方案。在本教程中,我想向您展示一個名為 I18next 的流行 I18n 框架,它具有許多很酷的功能,並提供許多額外的第三方插件來進一步簡化開發過程。即使具有所有這些功能,I18next 也不是一個複雜的工具,您不需要學習大量文件即可開始使用。

在本文中,您將了解如何借助 I18next 程式庫在 Stimulus 應用程式中啟用 I18n 支援。具體來說,我們將討論:

  • I18下一個設定
  • 翻譯檔案並非同步載入
  • 執行翻譯並一次翻譯整個頁面
  • 處理複數和性別資訊
  • 在區域設定之間切換並將所選區域設定保留在 GET 參數中
  • 根據使用者的偏好設定區域設定

原始程式碼可在教學 GitHub 儲存庫中找到。

引導刺激應用程式

首先,讓我們複製 Stimulus Starter 專案並使用 Yarn 套件管理器安裝所有依賴項:

git clone https://github.com/stimulusjs/stimulus-starter.git
cd stimulus-starter
yarn install
登入後複製

我們將建立一個簡單的 Web 應用程式來載入有關註冊用戶的資訊。對於每個用戶,我們將顯示他/她的登入名稱以及他/她迄今為止上傳的照片數量(這些照片是什麼並不重要)。

此外,我們將在頁面頂部提供一個語言切換器。選擇語言後,介面應立即翻譯,無需重新載入頁面。此外,URL 應附加 ?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 )。

I18下一個

現在我們將繼續將 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 表示“命名空間”,接受字串或陣列。在此範例中,我們只有一個命名空間,但對於較大的應用程序,您可以引入其他命名空間,例如admincartprofile等。每個命名空間都應該建立一個單獨的翻譯檔案。

  • defaultNSusers 設定為預設命名空間。

  • fallbackNS 停用名稱空間回退。

  • 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。为了提供正确的复数翻译,我们使用 photosphotos_plural 键。 {{count}} 部分为插值,将替换为实际数字。

至于俄语,事情就更复杂了:

{
  "login": "Логин",
  "uploaded_male": "Загрузил уже",
  "uploaded_female": "Загрузила уже",
  "photos_0": "одну фотографию",
  "photos_1": "{{count}} фотографии",
  "photos_2": "{{count}} фотографий"
}
登入後複製

首先,请注意,我们有两个可能的上下文的 uploaded_maleuploaded_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" ]
登入後複製

此外,为 lis 添加值为 switcherdata-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 中保留区域设置

切换语言环境后,我想在包含所选语言代码的 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 等解决方案。

這就是今天的全部內容!感謝您的閱讀,直到下次。

以上是使用 I18Next 本地化刺激應用程式的詳細內容。更多資訊請關注PHP中文網其他相關文章!

來源:php.cn
本網站聲明
本文內容由網友自願投稿,版權歸原作者所有。本站不承擔相應的法律責任。如發現涉嫌抄襲或侵權的內容,請聯絡admin@php.cn
熱門教學
更多>
最新下載
更多>
網站特效
網站源碼
網站素材
前端模板
關於我們 免責聲明 Sitemap
PHP中文網:公益線上PHP培訓,幫助PHP學習者快速成長!