Localize stimulus applications using I18Next
Aug 28, 2023 pm 11:53 PMIn my previous article, I introduced Stimulus - a simple JavaScript framework created by Basecamp. Today I will discuss internationalization of Stimulus applications, since the framework does not provide any internationalization tools out of the box. Internationalization is an important step, especially when your application is used by people around the world, so a basic understanding of how to do internationalization can really come in handy.
Of course, it's up to you to decide which internationalization solution to implement, whether it's jQuery.I18n, Polyglot, or something else. In this tutorial, I want to show you a popular I18n framework called I18next, which has many cool features and provides many additional third-party plugins to further simplify the development process. Even with all these features, I18next is not a complex tool and you don’t need to study a ton of documentation to get started.
In this article, you will learn how to enable I18n support in your Stimulus application with the help of the I18next library. Specifically, we will discuss:
- I18Next configuration
- Translate files and load asynchronously
- Perform translation and translate the entire page at once
- Handling plural and gender information
- Switch between locales and keep the selected locale in the GET parameters
- Set locale based on user preferences
Source code can be found in the tutorial GitHub repository.
GUIDE STIMULUS APP
First, let’s clone the Stimulus Starter project and install all dependencies using the Yarn package manager:
git clone https://github.com/stimulusjs/stimulus-starter.git cd stimulus-starter yarn install
We will build a simple web application to load information about registered users. For each user we will display his/her login name and the number of photos he/she has uploaded so far (it doesn't matter what those photos are).
Additionally, we will provide a language switcher at the top of the page. Once you select a language, the interface should be translated immediately without the need to reload the page. Additionally, the URL should be appended with the ?locale
GET parameter specifying the current locale in use. Of course, if this parameter is provided when the page loads, the correct language should be set automatically.
Okay, let's go ahead and render our user. Add the following lines of code to the public/index.html file:
<div data-controller="users" data-users-url="/api/users/index.json"></div>
Here we use the users
controller and provide a URL for loading users. In a real-world application, we might have a server-side script that fetches the user from the database and responds with JSON. However, for this tutorial we will simply put all the necessary data into the public/api/users/index.json file:
[ { "login": "johndoe", "photos_count": "15", "gender": "male" }, { "login": "annsmith", "photos_count": "20", "gender": "female" } ]
Now create a new src/controllers/users_controller.js file:
import { Controller } from "stimulus" export default class extends Controller { connect() { this.loadUsers() } }
Once the controller is connected to the DOM, we load users asynchronously with the help of the loadUsers()
method:
loadUsers() { fetch(this.data.get("url")) .then(response => response.text()) .then(json => { this.renderUsers(json) }) }
This method sends a get request to the given URL, gets the response, and finally presents the user:
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()
Parse the JSON in sequence, construct a new string containing all the content, and finally display this content on the page (this.element
is the controller connection that will be returned The actual DOM node reached, in our case div
).
I18Next
Now we will continue integrating I18next into our application. Add two libraries to our project: I18next itself and a plugin to enable asynchronous loading of translation files from the backend:
yarn add i18next i18next-xhr-backend
We will store all I18next related stuff in a separate src/i18n/config.js file, so create it now:
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 }
Let’s understand what’s going on here from top to bottom:
-
use(I18nXHR)
Enable i18next-xhr-backend plugin. -
fallbackLng
Tell it to use English as the fallback language. -
whitelist
Only English and Russian are allowed to be set. Of course, you can choose any other language. -
preload
Indicates to preload translation files from the server instead of loading them when the corresponding language is selected. -
ns
Represents "namespace" and accepts a string or array. In this example we have only one namespace, but for larger applications you can introduce other namespaces such asadmin
,cart
,profile
wait. A separate translation file should be created for each namespace. -
defaultNS
Setsusers
as the default namespace. -
fallbackNS
Disable namespace fallback. -
debug
允許在瀏覽器的控制臺中顯示調(diào)試信息。具體來說,它會說明加載哪些翻譯文件、選擇哪種語言等。您可能希望在將應(yīng)用程序部署到生產(chǎn)環(huán)境之前禁用此設(shè)置。 -
backend
為 I18nXHR 插件提供配置并指定從何處加載翻譯。請注意,路徑應(yīng)包含區(qū)域設(shè)置的標(biāo)題,而文件應(yīng)以命名空間命名并具有 .json 擴展名 -
function(err, t)
是當(dāng) I18next 準(zhǔn)備好時(或當(dāng)出現(xiàn)錯誤時)運行的回調(diào)。
接下來,讓我們制作翻譯文件。俄語翻譯應(yīng)放入 public/i18n/ru/users.json 文件中:
{ "login": "Логин" }
login
這里是翻譯鍵,而 Логин
是要顯示的值。
英文翻譯應(yīng)該轉(zhuǎn)到 public/i18n/en/users.json 文件:
{ "login": "Login" }
為了確保 I18next 正常工作,您可以將以下代碼行添加到 i18n/config.js 文件內(nèi)的回調(diào)中:
// config goes here... function(err, t) { if (err) return console.error(err) console.log(i18n.t('login')) }
在這里,我們使用一個名為 t
的方法,意思是“翻譯”。該方法接受翻譯鍵并返回相應(yīng)的值。
但是,我們可能有很多 UI 部分需要翻譯,而使用 t
方法來翻譯會非常乏味。相反,我建議您使用另一個名為 loc-i18next 的插件,它允許您一次翻譯多個元素。
一次性翻譯
安裝loc-i18next插件:
yarn add loc-i18next
將其導(dǎo)入src/i18n/config.js文件的頂部:
import locI18next from 'loc-i18next'
現(xiàn)在提供插件本身的配置:
// 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 實例創(chuàng)建一個新的插件實例。 -
selectorAttr
指定使用哪個屬性來檢測需要本地化的元素?;旧?,loc-i18next 將搜索此類元素并使用data-i18n
屬性的值作為翻譯鍵。 -
optionsAttr
指定哪個屬性包含附加翻譯選項。 -
useOptionsAttr
指示插件使用其他選項。
我們的用戶正在異步加載,因此我們必須等到此操作完成,然后才執(zhí)行本地化。現(xiàn)在,我們簡單地設(shè)置一個計時器,在調(diào)用 localize()
方法之前等待兩秒——當(dāng)然,這是一個臨時的 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 插件即可。內(nèi)部的所有元素(設(shè)置了 data-i18n
屬性)都將自動本地化。
現(xiàn)在調(diào)整 renderUsers
方法?,F(xiàn)在,我們只翻譯“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 }
不錯!重新加載頁面,等待兩秒鐘,并確保每個用戶都顯示“登錄”字樣。
復(fù)數(shù)和性別
我們對部分界面進行了本地化,這真的很酷。盡管如此,每個用戶還有兩個字段:上傳的照片數(shù)量和性別。由于我們無法預(yù)測每個用戶將擁有多少張照片,因此應(yīng)根據(jù)給定的數(shù)量將“照片”一詞正確地復(fù)數(shù)化。為此,我們需要之前配置的 data-i18n-options
屬性。要提供計數(shù),應(yīng)為 data-i18n-options
分配以下對象:{ "count": YOUR_COUNT }
。
性別信息也應(yīng)考慮在內(nèi)。英語中的“uploaded”一詞可以適用于男性和女性,但在俄語中它要么變成“загрузил”或“загрузила”,所以我們再次需要 data-i18n-options
,其中有 { "context": "GENDER" }
作為值。順便請注意,您可以利用此上下文來完成其他任務(wù),而不僅僅是提供性別信息。
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 }
現(xiàn)在更新英文翻譯:
{ "login": "Login", "uploaded": "Has uploaded", "photos": "one photo", "photos_plural": "{{count}} photos" }
這里沒什么復(fù)雜的。由于對于英語,我們不關(guān)心性別信息(即上下文),因此翻譯鍵應(yīng)該只是 uploaded
。為了提供正確的復(fù)數(shù)翻譯,我們使用 photos
和 photos_plural
鍵。 {{count}}
部分為插值,將替換為實際數(shù)字。
至于俄語,事情就更復(fù)雜了:
{ "login": "Логин", "uploaded_male": "Загрузил уже", "uploaded_female": "Загрузила уже", "photos_0": "одну фотографию", "photos_1": "{{count}} фотографии", "photos_2": "{{count}} фотографий" }
首先,請注意,我們有兩個可能的上下文的 uploaded_male
和 uploaded_female
鍵。接下來,俄語中的復(fù)數(shù)規(guī)則也比英語中更復(fù)雜,因此我們必須提供不是兩個而是三個可能的短語。 I18next 支持多種開箱即用的語言,這個小工具可以幫助您了解應(yīng)該為給定語言指定哪些復(fù)數(shù)鍵。
切換區(qū)域設(shè)置
我們已經(jīng)完成了應(yīng)用程序的翻譯,但用戶應(yīng)該能夠在區(qū)域設(shè)置之間切換。因此,向 public/index.html 文件添加一個新的“語言切換器”組件:
<ul data-controller="languages" class="switcher"></ul>
在 src/controllers/languages_controller.js 文件中制作相應(yīng)的控制器:
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()
回調(diào)來顯示支持的語言列表。每個 li
都有一個 data-action
屬性,該屬性指定單擊元素時應(yīng)觸發(fā)的方法(在本例中為 switchLanguage
)。
現(xiàn)在添加 switchLanguage()
方法:
switchLanguage(e) { this.currentLang = e.target.getAttribute("data-lang") }
它只是獲取事件的目標(biāo)并獲取 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 非常簡單——我們獲取當(dāng)前使用的語言的值并返回它。
setter 更復(fù)雜。首先,如果當(dāng)前設(shè)置的語言與所選語言不相等,我們使用 changeLanguage
方法。此外,我們將新選擇的語言環(huán)境存儲在 data-current-lang
屬性(在 getter 中訪問)下,使用 loc-i18next 插件本地化 HTML 頁面的主體,最后突出顯示當(dāng)前使用的區(qū)域設(shè)置。
讓我們編寫 highlightCurrentLang()
的代碼:
highlightCurrentLang() { this.switcherTargets.forEach((el, i) => { el.classList.toggle("current", this.currentLang === el.getAttribute("data-lang")) }) }
這里我們迭代區(qū)域設(shè)置切換器的數(shù)組,并將它們的 data-lang
屬性的值與當(dāng)前使用的區(qū)域設(shè)置的值進行比較。如果值匹配,則為切換器分配 current
CSS 類,否則刪除該類。
為了使 this.switcherTargets
構(gòu)建工作,我們需要按以下方式定義刺激目標(biāo):
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('') // ... }
另一個需要考慮的重要事項是翻譯文件可能需要一些時間來加載,我們必須等待此操作完成才能允許切換區(qū)域設(shè)置。因此,讓我們利用 loaded
回調(diào):
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 中保留區(qū)域設(shè)置
切換語言環(huán)境后,我想在包含所選語言代碼的 URL 中添加 ?lang
GET 參數(shù)。在 History API 的幫助下,可以輕松地添加 GET 參數(shù)而不重新加載頁面:
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() } }
檢測區(qū)域設(shè)置
我們今天要實現(xiàn)的最后一件事是能夠根據(jù)用戶的偏好設(shè)置區(qū)域設(shè)置。一個名為 LanguageDetector 的插件可以幫助我們解決這個任務(wù)。添加新的 Yarn 包:
yarn add i18next-browser-languagedetector
在 i18n/config.js 文件中導(dǎo)入 LanguageDetector
:
import LngDetector from 'i18next-browser-languagedetector'
現(xiàn)在調(diào)整配置:
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
選項列出了插件應(yīng)嘗試的所有技術(shù)(按重要性排序),以便“猜測”首選區(qū)域設(shè)置:
-
querystring
?表示檢查包含區(qū)域設(shè)置代碼的 GET 參數(shù)。 -
lookupQuerystring
設(shè)置要使用的 GET 參數(shù)的名稱,在我們的例子中是lang
。 -
navigator
表示從用戶的請求中獲取語言環(huán)境數(shù)據(jù)。 -
htmlTag
?涉及從html
標(biāo)記的lang
屬性獲取首選區(qū)域設(shè)置。
結(jié)論
在本文中,我們介紹了 I18next——一種輕松翻譯 JavaScript 應(yīng)用程序的流行解決方案。您已經(jīng)學(xué)習(xí)了如何將 I18next 與 Stimulus 框架集成、配置它以及以異步方式加載翻譯文件。此外,您還了解了如何在區(qū)域設(shè)置之間切換以及如何根據(jù)用戶的偏好設(shè)置默認語言。
I18next 有一些額外的配置選項和許多插件,因此請務(wù)必瀏覽其官方文檔以了解更多信息。另請注意,Stimulus 不會強制您使用特定的本地化解決方案,因此您也可以嘗試使用 jQuery.I18n 或 Polyglot 等解決方案。
That’s all for today! Thanks for reading, and until next time.
The above is the detailed content of Localize stimulus applications using I18Next. For more information, please follow other related articles on the PHP Chinese website!

Hot AI Tools

Undress AI Tool
Undress images for free

Undresser.AI Undress
AI-powered app for creating realistic nude photos

AI Clothes Remover
Online AI tool for removing clothes from photos.

Clothoff.io
AI clothes remover

Video Face Swap
Swap faces in any video effortlessly with our completely free AI face swap tool!

Hot Article

Hot Tools

Notepad++7.3.1
Easy-to-use and free code editor

SublimeText3 Chinese version
Chinese version, very easy to use

Zend Studio 13.0.1
Powerful PHP integrated development environment

Dreamweaver CS6
Visual web development tools

SublimeText3 Mac version
God-level code editing software (SublimeText3)

The main reasons why WordPress causes the surge in server CPU usage include plug-in problems, inefficient database query, poor quality of theme code, or surge in traffic. 1. First, confirm whether it is a high load caused by WordPress through top, htop or control panel tools; 2. Enter troubleshooting mode to gradually enable plug-ins to troubleshoot performance bottlenecks, use QueryMonitor to analyze the plug-in execution and delete or replace inefficient plug-ins; 3. Install cache plug-ins, clean up redundant data, analyze slow query logs to optimize the database; 4. Check whether the topic has problems such as overloading content, complex queries, or lack of caching mechanisms. It is recommended to use standard topic tests to compare and optimize the code logic. Follow the above steps to check and solve the location and solve the problem one by one.

Miniving JavaScript files can improve WordPress website loading speed by removing blanks, comments, and useless code. 1. Use cache plug-ins that support merge compression, such as W3TotalCache, enable and select compression mode in the "Minify" option; 2. Use a dedicated compression plug-in such as FastVelocityMinify to provide more granular control; 3. Manually compress JS files and upload them through FTP, suitable for users familiar with development tools. Note that some themes or plug-in scripts may conflict with the compression function, and you need to thoroughly test the website functions after activation.

The most effective way to prevent comment spam is to automatically identify and intercept it through programmatic means. 1. Use verification code mechanisms (such as Googler CAPTCHA or hCaptcha) to effectively distinguish between humans and robots, especially suitable for public websites; 2. Set hidden fields (Honeypot technology), and use robots to automatically fill in features to identify spam comments without affecting user experience; 3. Check the blacklist of comment content keywords, filter spam information through sensitive word matching, and pay attention to avoid misjudgment; 4. Judge the frequency and source IP of comments, limit the number of submissions per unit time and establish a blacklist; 5. Use third-party anti-spam services (such as Akismet, Cloudflare) to improve identification accuracy. Can be based on the website

When developing Gutenberg blocks, the correct method of enqueue assets includes: 1. Use register_block_type to specify the paths of editor_script, editor_style and style; 2. Register resources through wp_register_script and wp_register_style in functions.php or plug-in, and set the correct dependencies and versions; 3. Configure the build tool to output the appropriate module format and ensure that the path is consistent; 4. Control the loading logic of the front-end style through add_theme_support or enqueue_block_assets to ensure that the loading logic of the front-end style is ensured.

To add custom user fields, you need to select the extension method according to the platform and pay attention to data verification and permission control. Common practices include: 1. Use additional tables or key-value pairs of the database to store information; 2. Add input boxes to the front end and integrate with the back end; 3. Constrain format checks and access permissions for sensitive data; 4. Update interfaces and templates to support new field display and editing, while taking into account mobile adaptation and user experience.

The key to adding custom rewrite rules in WordPress is to use the add_rewrite_rule function and make sure the rules take effect correctly. 1. Use add_rewrite_rule to register the rule, the format is add_rewrite_rule($regex,$redirect,$after), where $regex is a regular expression matching URL, $redirect specifies the actual query, and $after controls the rule location; 2. Custom query variables need to be added through add_filter; 3. After modification, the fixed link settings must be refreshed; 4. It is recommended to place the rule in 'top' to avoid conflicts; 5. You can use the plug-in to view the current rule for convenience

robots.txt is crucial to the SEO of WordPress websites, and can guide search engines to crawl behavior, avoid duplicate content and improve efficiency. 1. Block system paths such as /wp-admin/ and /wp-includes/, but avoid accidentally blocking the /uploads/ directory; 2. Add Sitemap paths such as Sitemap: https://yourdomain.com/sitemap.xml to help search engines quickly discover site maps; 3. Limit /page/ and URLs with parameters to reduce crawler waste, but be careful not to block important archive pages; 4. Avoid common mistakes such as accidentally blocking the entire site, cache plug-in affecting updates, and ignoring the matching of mobile terminals and subdomains.

1. Use performance analysis plug-in to quickly locate problems. For example, QueryMonitor can view the number of database queries and PHP errors, BlackboxProfiler generates function execution reports, and NewRelic provides server-level analysis; 2. Analyzing PHP execution performance requires checking time-consuming functions, debugging tools usage and memory allocation, such as Xdebug generates flame graphs to assist in optimization; 3. Monitor database query efficiency can be checked through slow query logs and index checks, QueryMonitor can list all SQL and sort by time; 4. Combining external tools such as GooglePageSpeedInsights, GTmetrix and WebPageTest to evaluate front-end plus
