others-How to add internationalization(i18n) to your vue 3 project and how to solve ' TypeError: Cannot read properties of undefined (reading '$t') problem?
1. Purpose
In this post, I will demonstrate how to add internationalization(i18n) to a vue3 project and how to solve TypeError: Cannot read properties of undefined (reading '$t') problem
when trying to use $t
outside the vue tempate.
2. How to add i18n to your vue3 project
You can just follow the below steps to add i18n to your project. Before you start, you should have already installed vue3
, vue-cli
and npm
in your machine.
Step1: create a vue project
vue create vue3-i18n-example
you will get this output:
...
📄 Generating README.md...
🎉 Successfully created project vue3-i18n-example.
👉 Get started with the following commands:
Then test the new project by running it:
cd vue3-i18n-example
npm run serve
You should get this output:
App running at:
- Local: http://localhost:8080/
- Network: http://10.1.1.2:8080/
Note that the development build is not optimized.
To create a production build, run npm run build.
Then open your favorate web browser,visit http://localhost:8080, you will get this:
Step 2: Add i18n plugin to our project
1) What is i18n?
Internationalization (sometimes shortened to “I18N , meaning “I - eighteen letters -N”) is the process of planning and implementing products and services so that they can easily be adapted to specific local languages and cultures, a process called localization
2) What is vue i18n plugin?
The vue i18n plugin is an internationalization plugin of Vue. js. It easily integrates some localization features to your Vue. js Application.
3) How to add vue 18n plugin to our project?
We can use vue cli
command line to install the plugin for our project as follows:
➜ vue3-i18n-example git:(main) ✗ vue add i18n
? Still proceed? (y/N) y
```shell
Then we got this:
```shell
📦 Installing vue-cli-plugin-i18n...
added 29 packages, and audited 1580 packages in 12s
103 packages are looking for funding
run `npm fund` for details
42 vulnerabilities (21 moderate, 16 high, 5 critical)
To address issues that do not require attention, run:
npm audit fix
To address all issues (including breaking changes), run:
npm audit fix --force
Run `npm audit` for details.
✔ Successfully installed plugin: vue-cli-plugin-i18n
? The locale of project localization. (en)
It ask us the default locale of our project, we can just press enter to use the default value ‘en’, and then :
? The fallback locale of project localization. (en)
The above answer will tell the i18n to use which locale when the translation for specific key is not found. We can just press enter to use the default value. And then:
? The directory where store localization messages of project. It's stored under `src` directory. (locales)
The above question let us choose the directory where to put our locale files , press enter to use the default value, and then:
? Enable legacy API (compatible [email protected]) mode ? (y/N)
If you don’t migrate from the old i18n plugin, just choose N.
So the total questions and answers of vue add i18n
is as follows:
? The locale of project localization. en
? The fallback locale of project localization. en
? The directory where store localization messages of project. It's stored under `src` directory. locales
? Enable legacy API (compatible [email protected]) mode ? No
After above selections, we got this output:
🚀 Invoking generator for vue-cli-plugin-i18n...
📦 Installing additional dependencies...
added 18 packages, changed 1 package, and audited 1598 packages in 6s
104 packages are looking for funding
run `npm fund` for details
42 vulnerabilities (21 moderate, 16 high, 5 critical)
To address issues that do not require attention, run:
npm audit fix
To address all issues (including breaking changes), run:
npm audit fix --force
Run `npm audit` for details.
⚓ Running completion hooks...
✔ Successfully invoked generator for plugin: vue-cli-plugin-i18n
We can test if our project is fine by running npm run serve
again. And open your favorate web browser,visit http://localhost:8080, you will get this:
It does not change anything yet.
The project’s directory structure is as follows:
➜ vue3-i18n-example git:(main) ✗ tree . -I 'node_modules|dist'
.
├── README.md
├── babel.config.js
├── package-lock.json
├── package.json
├── public
│ ├── favicon.ico
│ └── index.html
├── src
│ ├── App.vue
│ ├── assets
│ │ └── logo.png
│ ├── components
│ │ ├── HelloI18n.vue
│ │ └── HelloWorld.vue
│ ├── i18n.js
│ ├── locales
│ │ └── en.json
│ ├── main.js
│ ├── router
│ │ └── index.js
│ ├── store
│ │ └── index.js
│ └── views
│ ├── About.vue
│ └── Home.vue
├── tests
│ └── unit
│ └── example.spec.js
└── vue.config.js
10 directories, 19 files
You can see that the i18n
plugin created some new directories and files:
- src/i18n.js, this js file is responsible for create and initialize i18n settings ```js import { createI18n } from ‘vue-i18n’
/**
- Load locale messages *
- The loaded
JSON
locale messages is pre-compiled by@intlify/vue-i18n-loader
, which is integrated intovue-cli-plugin-i18n
. - See: https://github.com/intlify/vue-i18n-loader#rocket-i18n-resource-pre-compilation */ function loadLocaleMessages() { const locales = require.context(‘./locales’, true, /[A-Za-z0-9-_,\s]+.json$/i) const messages = {} locales.keys().forEach(key => { const matched = key.match(/([A-Za-z0-9-_]+)./i) if (matched && matched.length > 1) { const locale = matched[1] messages[locale] = locales(key).default } }) return messages }
export default createI18n({ legacy: false, locale: process.env.VUE_APP_I18N_LOCALE || ‘en’, fallbackLocale: process.env.VUE_APP_I18N_FALLBACK_LOCALE || ‘en’, messages: loadLocaleMessages() })
- src/main.js, this file is changed to add `i18n` to the app:
import { createApp } from ‘vue’ import App from ‘./App.vue’ import router from ‘./router’ import store from ‘./store’ import i18n from ‘./i18n’
createApp(App).use(i18n).use(store).use(router).mount(‘#app’)
- locales/en.json, this file is a `en` locale translation file for our project, we can add more locale files to this directory, e.g. cn.json for Chinese translation or fr.json for French translation.
```json
{
"message": "hello i18n !!"
}
- package.json is changed too:
{ "name": "vue3-i18n-example", "version": "0.1.0", "private": true, "scripts": { "serve": "vue-cli-service serve", "build": "vue-cli-service build", "test:unit": "vue-cli-service test:unit", "i18n:report": "vue-cli-service i18n:report --src \"./src/**/*.?(js|vue)\" --locales \"./src/locales/**/*.json\"" }, "dependencies": { "core-js": "^3.6.5", "vue": "^3.0.0", "vue-i18n": "^9.1.0", "vue-router": "^4.0.0-0", "vuex": "^4.0.0-0" }, "devDependencies": { "@intlify/vue-i18n-loader": "^3.0.0", "@vue/cli-plugin-babel": "~4.5.13", "@vue/cli-plugin-router": "~4.5.13", "@vue/cli-plugin-unit-jest": "~4.5.13", "@vue/cli-plugin-vuex": "~4.5.13", "@vue/cli-service": "~4.5.13", "@vue/compiler-sfc": "^3.0.0", "@vue/test-utils": "^2.0.0-0", "typescript": "~3.9.3", "vue-cli-plugin-i18n": "~2.3.1", "vue-jest": "^5.0.0-0" }, "browserslist": [ "> 1%", "last 2 versions", "not dead" ], "jest": { "preset": "@vue/cli-plugin-unit-jest", "transform": { "^.+\\.vue$": "vue-jest" } } }
It added
"vue-i18n": "^9.1.0",
to dependencies, and"vue-cli-plugin-i18n": "~2.3.1",
to devDependencies.
Step 3: Add hello world
translation to our project.
Open our src/App.vue
, change as follows:
<template>
<div class="home">
<img alt="Vue logo" src="../assets/logo.png" />
<HelloWorld :msg="$t('welcomeMessage')" /> <!-- first change -->
</div>
</template>
<script>
// @ is an alias to /src
import HelloWorld from "@/components/HelloWorld.vue";
export default {
name: "Home",
data() { //second chagne
msg: "";
},
components: {
HelloWorld,
},
};
</script>
You can see that we make two changes to the file, the first:
<HelloWorld :msg="$t('welcomeMessage')" />
We added a colon to msg
to make it a vue binding variable, then it can use vue methods $t
,
$t('welcomeMessage')
The above line means that the app should find the translation by key welcomeMessage
in src/locales/xxx.json
.
And the second change:
data() { //second chagne
return {
msg: "",
};
},
It use data()
function to export a variable named msg
to be bound in the vue template.
Then we open src/locales/en.json
to add the English locale:
{
"message": "hello i18n !!",
"welcomeMessage": "Welcome to our new Vue.js and i18n App"
}
Save all the above changed files and refresh your browser , you should got this:
Step 4: add a new locale and change default locale to it
Now we can add a new locale ,e.g. cn(Chinese) for test.
- Create a
src/locales/cn.json
with the following content: ```json { “message”: “hello i18n !!”, “welcomeMessage”: “你好,vue” }
And then open `src/i18n.js`, change as follows:
```js
import { createI18n } from 'vue-i18n'
/**
* Load locale messages
*
* The loaded `JSON` locale messages is pre-compiled by `@intlify/vue-i18n-loader`, which is integrated into `vue-cli-plugin-i18n`.
* See: https://github.com/intlify/vue-i18n-loader#rocket-i18n-resource-pre-compilation
*/
function loadLocaleMessages() {
const locales = require.context('./locales', true, /[A-Za-z0-9-_,\s]+\.json$/i)
const messages = {}
locales.keys().forEach(key => {
const matched = key.match(/([A-Za-z0-9-_]+)\./i)
if (matched && matched.length > 1) {
const locale = matched[1]
messages[locale] = locales(key).default
}
})
return messages
}
export default createI18n({
legacy: false,
locale: 'cn',
fallbackLocale: process.env.VUE_APP_I18N_FALLBACK_LOCALE || 'en',
messages: loadLocaleMessages()
})
The line that is changed as follows:
locale: 'cn',
The above configuration means that the default locale is cn
, if not found use en
as the locale.
Then reboot the app again, we got this:
3. Problem and solution
Sometimes, if we want to use the $t
outside the tempate as follows, we will encounter problems:
<template>
<div>
</div>
</template>
<script>
import Card from "@/components/Card.vue";
const cardInfo = { cardName: this.$t("titleBase64Encoding"), cardView: "base64" }
export default {
data() {
return {
cardName: cardInfo.cardName
}
}
}
</script>
We got this error message:
log.js?1afd:24 [HMR] Waiting for update signal from WDS...
CardList.vue?c995:22 Uncaught TypeError: Cannot read properties of undefined (reading '$t')
at eval (CardList.vue?c995:22:1)
at ./node_modules/cache-loader/dist/cjs.js?!./node_modules/babel-loader/lib/index.js!./node_modules/cache-loader/dist/cjs.js?!./node_modules/vue-loader-v16/dist/index.js?!./src/components/CardList.vue?vue&type=script&lang=js (app.js:974:1)
at __webpack_require__ (app.js:849:30)
at fn (app.js:151:20)
at eval (CardList.vue?1afb:1:1)
at ./src/components/CardList.vue?vue&type=script&lang=js (app.js:1627:1)
at __webpack_require__ (app.js:849:30)
at fn (app.js:151:20)
at eval (CardList.vue?c958:1:1)
at ./src/components/CardList.vue (app.js:1615:1)
the error line of code:
const cardInfo = { cardName: this.$t("titleBase64Encoding"), cardView: "base64" }
core error:
Uncaught TypeError: Cannot read properties of undefined (reading '$t')
The “cannot read property of undefined” error occurs when you attempt to access a property or method of a variable that is undefined . You can fix it by adding an undefined check on the variable before accessing it.
reason:
The “cannot read property of undefined” error occurs when you attempt to access a property or method of a variable that is undefined . You can fix it by adding an undefined check on the variable before accessing it.
solution:
change your i18n string definition as follows:
<template>
<div>
...
</div>
</template>
<script>
import Card from "@/components/Card.vue";
const cardInfo = { cardName: "base64CardTitle", cardView: "base64" }
export default {
data() {
return {
cardName: cardInfo.cardName
}
}
}
then in your locales/en.json, code like this:
{
"base64CardTitle": "Base64 encoding",
}
Now it works!
4. The code
The project’s source code has been uploaded to github, you can download or clone it: https://github.com/bswen/vuejs-project/tree/main/vue3-i18n-example
5. Summary
In this post, I demonstrated how to add internationalization(i18n) to your vue 3 project and how to solve ‘ TypeError: Cannot read properties of undefined (reading ‘$t’) problem . That’s it, thanks for your reading.