Problem to use VueI18n outside a component - javascript

I'm trying to use i18n outside a component I've found this solution https://github.com/dkfbasel/vuex-i18n/issues/16 telling to use Vue.i18n.translate('str'), but when I call this occurs an error Cannot read property 'translate' of undefined.
I'm using the following configuration
main.js
import i18n from './i18n/i18n';
new Vue({
router,
store,
i18n: i18n,
render: h => h(App)
}).$mount('#app')
i18n.js
import Vue from 'vue'
import VueI18n from 'vue-i18n'
import i18nData from './i18nData'
Vue.use(VueI18n);
export default new VueI18n({
locale: 'en',
messages: i18nData,
});
i18nData.js
export default {
en: {
//my messages
}
}
Then I trying to use this
import Vue from 'vue';
Vue.i18n.translate('someMessage');
Can anyone help me?

To use i18n with Vue 3's composition API, but outside a component's setup(), you can access its translation API (such as the t function) on its global property.
E. g. in a file with unit-testable composition functions:
// i18n/index.js
import { createI18n } from 'vue-i18n'
import en from './en.json'
...
export default createI18n({
datetimeFormats: {en: {...}},
locale: 'en',
messages: { en }
})
// features/utils.js
//import { useI18n } from 'vue-i18n'
//const { t } = useI18n() // Uncaught SyntaxError: Must be called at the top of a `setup` function
import i18n from '../i18n'
const { t } = i18n.global

You should import i18n instead of Vue
import i18n from './i18n'
i18n.tc('someMessage')

You can use VueI18n outside components by importing i18n then, use "t" from i18n.global.
"t" doesn't need "$" and you can change Locale with i18n.global.locale.
import i18n from '../i18n';
const { t } = i18n.global;
i18n.global.locale = 'en-US'; // Change "Locale"
const data = {
name: t('name'), // "t" doesn't need "$"
description: t('description'), // "t" doesn't need "$"
};

I managed to make it work this way:
import router from '../router';
Translate a text:
let translatedMessage = router.app.$t('someMessage');
Get the current language:
let language = router.app.$i18n.locale;

Related

How do I enable namespacing on an imported VueX module?

I'm using a helper file to import VueX modules:
const requireModule = require.context('.', false, /\.store\.js$/)
const modules = {}
requireModule.keys().forEach(filename => {
const moduleName = filename
.replace(/(\.\/|\.store\.js)/g, '')
.replace(/^\w/, c => c.toUpperCase())
modules[moduleName] = requireModule(filename).default || requireModule(filename)
})
export default modules
This lives in #/store/modules/index.js and is imported by #/store/index.js:
import Vue from 'vue'
import Vuex from 'vuex'
import modules from './modules'
Vue.use(Vuex)
export default new Vuex.Store({
modules,
actions: {
reset({commit}) {
Object.keys(modules).forEach(moduleName => {
commit(`${moduleName}/RESET`);
})
}
}
})
Imported in to Vue: #/main.js:
import Vue from 'vue'
import App from './App.vue'
import router from './router'
import store from './store'
Vue.config.productionTip = false
new Vue({
router,
store,
render: h => h(App)
}).$mount('#app')
Works great for all of my store modules! Each of which are namespaced:
const initialState = () => ({})
const state = initialState()
const mutations = {
RESET(state) {
const newState = initialState();
Object.keys(newState).forEach(key => {
state[key] = newState[key]
});
}
}
const getters = {}
const actions = {}
export default {
namespaced: true,
state,
getters,
actions,
mutations
}
Now, I'm trying to import a package as a state module. Which I don't have any experience with. This might seem silly... but I'm not sure how to inject the namespace enablement into the package importation in #/store/modules/Auth.store.js:
import AmazonCognitoVuexModule from 'amazon-cognito-vuex-module';
const cognito = new AmazonCognitoVuexModule({
region: process.env.VUE_APP_COGNITO_REGION,
userPoolId: process.env.VUE_APP_COGNITO_USERPOOL_ID,
clientId: process.env.VUE_APP_COGNITO_CLIENT_ID,
})
export default cognito
So when I try to call the imported store module's actions with $store.dispatch('Auth/...') they're not found... because they're not namespaced. I want to namespace this module "Auth". I bet I'm overlooking something really simple. Any help appreciated.
My first try would be (based on the docs of this package on NPM):
// source: https://www.npmjs.com/package/amazon-cognito-vuex-module
const store = new Vuex.Store({
modules: {
cognito: new AmazonCognitoVuexModule({
region: '<region>',
userPoolId: '<user pool id>',
clientId: '<client id>'
})
}
});
The code above shows that you can import it as a module (they named it cognito, you want auth - that should make no difference; I use the naming in the official docs). So you need to update your code, like this:
import Vue from 'vue'
import Vuex from 'vuex'
import modules from './modules'
import cognito from './path/to/cognito'
Vue.use(Vuex)
export default new Vuex.Store({
modules: {
...modules,
cognito,
},
actions: {
reset({commit}) {
Object.keys(modules).forEach(moduleName => {
commit(`${moduleName}/RESET`);
})
}
}
})
I'm not sure this works, but this would be my first try :)

[Vue warn]: Failed to resolve component when tried to create global component

I new on Vue Typescript. I've tried to create global components but I got a warning and component not loaded on the template. This how I tried to create global components
App.vue
import { createApp } from "vue"
import App from "./App.vue"
import "./registerServiceWorker"
import "./globalComponents"
import router from "./router"
import store from "./store"
createApp(App)
.use(store)
.use(router)
.mount("#app")
globalComponents.ts
import { createApp } from "vue"
const app = createApp({})
// Forms
app.component("ui-input", () => import("#/components/core/ui/Input.vue"))
Input.vue
<template lang="pug">
.ui-input
input(v-model="$attrs" v-on="$listeners")
</template>
<script lang="ts">
import { defineComponent } from "vue"
export default defineComponent({
inheritAttrs: false
})
</script>
Hope you all can help me, Thanks in advance
As of Vue 3, if you create an app using createApp, it will be a standalone Vue App instance. So if you need to add a global component then you will need to add it on the app object created from createApp, here's the code for that:
const app = createApp({});
app.component('my-component-name', MyComponent) // <-- here you can register.
app.mount("#app");
But if there are a lot of components then adding them in the main.ts file will be a mess, so we can create another file, like you did, so:
Your current globalComponents.ts
import { createApp } from "vue"
const app = createApp({})
// Forms
app.component("ui-input", () => import("#/components/core/ui/Input.vue"))
The Problem
But notice here's a mistake. What is it? You created another app using createApp. As I referred earlier that if you need to create a global component, you can only create on the same instance.
Fix
As we know the problem here is that we are creating another instance, which is again a new and standalone instance, so we need to figure out the way we can have the same app instance in globalComponents.ts as well, so we will pass the app from main-ts to globalComponents.ts, like:
globalComponents.ts
import { App } from "vue";
// register components
export const registerComponents = (app: App): void => {
app.component("ui-input", () => import("#/components/core/ui/Input.vue"));
}
And now you can call registerComponents in main.ts as:
main.ts
const app = createApp(App)
.use(store)
.use(router);
registerComponents(app); // <-- here you go
app.mount("#app");
You will still get something like:
[Vue warn]: Invalid VNode type: undefined (undefined).
You can read more here about how to define an async component in vue 3. To fix that error you will need to wrap your import in defineAsyncComponent as:
globalComponents.ts
import { defineAsyncComponent } from "vue";
// register components
export const registerComponents = (app) => {
app.component(
"ui-input",
defineAsyncComponent(() => import("#/components/Input.vue"))
);
};

How to initialize NCForms library in VueJs

I'm new to Vue CLI, and I'm trying to build a small application. As part of this I want to generate some forms.
I've tested a few libraries, and NCForms seems to do all I need to do. (specifically, I need to handle capturing of multiple arrays).
I tried to initialize the library as described in the documentation - but it fails in the Template saying that it can't find some of the element-ui components.
I'm pretty sure that I've followed the instructions properly - but I must be missing something small.
My main.js file looks like this:
import 'ant-design-vue/lib/style/index.less' // antd core styles
import './#kit/vendors/antd/themes/default.less' // default theme antd components
import './#kit/vendors/antd/themes/dark.less' // dark theme antd components
import './global.scss' // app & third-party component styles
import Vue from 'vue'
import VuePageTitle from 'vue-page-title'
import NProgress from 'vue-nprogress'
import VueLayers from 'vuelayers'
import BootstrapVue from 'bootstrap-vue'
import VueFormulate from '#braid/vue-formulate'
// Form generator: Vue-Form-Generator: https://github.com/vue-generators/vue-form-generator
import VueFormGenerator from 'vue-form-generator'
import 'vue-form-generator/dist/vfg.css'
// Form generator: NCForms: https://github.com/ncform/ncform
import vueNcform from '#ncform/ncform'
// eslint-disable-next-line no-unused-vars
import Element from 'element-ui'
import 'element-ui/lib/theme-chalk/index.css'
import ncformStdComps from '#ncform/ncform-theme-elementui'
// REST Calls: Axios: https://github.com/axios/axios
import axios from 'axios'
// Local files
import App from './App.vue'
import router from './router'
import store from './store'
import { i18n } from './localization'
import './antd'
import './registerServiceWorker'
// mocking api
import './services/axios/fakeApi'
Vue.use(BootstrapVue)
Vue.use(VueLayers)
Vue.use(NProgress)
Vue.use(VuePageTitle, {
prefix: 'Nedbank PhishTank | ',
router,
})
// Form generator: Vue-Form-Generator
Vue.use(VueFormGenerator)
// Form generator: NCForms
Vue.use(vueNcform, { extComponents: ncformStdComps, lang: 'en' })
window.$http = Vue.prototype.$http = axios
Vue.use(VueFormulate)
Vue.config.productionTip = false
const nprogress = new NProgress({ parent: 'body' })
new Vue({
router,
store,
nprogress,
i18n,
render: h => h(App),
}).$mount('#app')
My template looks like this:
<template>
<div>
<ncform :form-schema="formSchema" form-name="settings-form" v-model="item" #submit="submit()"></ncform>
<el-button #click="submit()">Submit</el-button>
</div>
</template>
<script>
export default {
data() {
return {
formSchema: {
type: 'object',
properties: {
name: {
type: 'string',
},
},
},
item: {
name: 'Peter Pan',
},
}
},
methods: {
submit () {
this.$ncformValidate('settings-form').then(data => {
if (data.result) {
console.log(this.$data.formSchema.value)
// do what you like to do
alert('finally!!!')
}
})
},
},
}
</script>
The error is:
Unknown custom element: - did you register the component correctly? For recursive components, make sure to provide the "name" option.
Am I missing something like registering the individual components? I thought this line would take care of it: Vue.use(vueNcform, { extComponents: ncformStdComps, lang: 'en' })
It feels like I should put something into the "new Vue()" statement - but I'm not sure what....
In main.js, you need to specify: Vue.use(Element);

bootstrapping vuetify with a factory pattern

I have been trying for some time to upgrade my app from vuetify 1.5 to 2, after a lot of thought I think the issue is that the way my app initializes causes the issue, I cannot put my finger on exactly what it is though, the issue is that when I run the app I get errors in the console that none of the vuetify components are recognized.
Attached is a screenshot of the error message.
My main.js file:
import 'babel-polyfill' // IE support
import VueI18n from 'vue-i18n'
import {localizationFactory} from "./localization";
import {apiFactory, apiPluginFactory} from './api/api';
import {storeFactory} from "./store/store";
import {configServiceFactory} from "./services/configService";
import {Services, Security, Utils} from 'em-common-vue';
import Vue from 'vue'
import App from './App.vue'
import router from './router'
import {filtersFactory} from './filters/index';
import Vuetify from "vuetify/lib";
const appsService = new Services.appsService(process.env);
const loginDetails = {
loginHost: appsService.getLoginStorage()
};
Security.ServiceFactory(loginDetails).then($security => {
Vue.config.productionTip = false;
Vue.use(Utils.EventBusPlugin);
Vue.use($security);
var vInstance = new Vue();
const $api = apiFactory(vInstance, $security);
configServiceFactory($security, $api).then($config => {
Vue.use($config);
Vue.use(apiPluginFactory($api));
// for now
const store = storeFactory($api, null);
Vue.use(VueI18n);
filtersFactory($config.$service);
localizationFactory($config.$service).then(messages => {
const i18n = new VueI18n({
locale: 'en', // set locale
messages, // set locale messages
});
Vue.use(Vuetify);
let vuetify = new Vuetify({
icons: {
iconfont: 'mdiSvg',
},
});
new Vue({
router,
store,
i18n,
vuetify,
render: h => h(App),
computed: {
title: {
set(val) {
document.querySelector('title').innerText = val;
},
get(val) {
return document.querySelector('title').innerText;
}
}
},
mounted() {
if (!document.querySelector('title')) {
let title = document.createElement('title');
document.head.append(title);
}
this.title = this.$config.get().title;
}
}).$mount('#app')
});
});
}).catch(err => {
console.error(err);
if (err.loginUrl) {
const nextUrl = appsService.getLogin(window.location.href);
window.location.href = nextUrl;
} else { // for now
alert(err);
}
});
How might I change my code to load vuetify properly? Alternatively what might be a pattern that might work for my code?
TL;DR:
Replace import Vuetify from "vuetify/lib"; with import Vuetify from "vuetify";
Alternatively if you want to use vuetify-loader:
Add vuetify-loader to your project (if already present, update it)
If you're using webpack directly add the vuetify-loader plugin to your webpack plugins:
const VuetifyLoaderPlugin = require('vuetify-loader/lib/plugin');
exports.plugins.push(new VuetifyLoaderPlugin());
If you used #vue/cli to setup your project, you can use the configureWebpack option in vue.config.js to add the plugin:
const VuetifyLoaderPlugin = require('vuetify-loader/lib/plugin');
module.exports = {
configureWebpack: {
plugins: [
new VuetifyLoaderPlugin()
]
}
}
Why?
vuetify comes in two "flavors":
A-la-carte
All vuetify components will not be directly required, but only loaded when you explicitly import them.
This greatly reduces your final package size, since you only include the parts of the vuetify framework that you're actually using.
You're using A-la-carte if you're importing vuetify from vuetify/lib:
import Vue from "vue";
import Vuetify from "vuetify/lib";
Vue.use(Vuetify);
const vuetify = new Vuetify({ /* ... */});
new Vue({vuetify}).$mount('#root');
The downside of a-la-carte is that you need to manually import each vuetify component you want to use:
import {VIcon} from "vuetify/lib";
export default {
name: 'foo-component',
components: { VIcon },
template: '<v-icon>user</v-icon>'
};
To make this less a hassle, you can either:
use vuetify-loader, it will automatically add those imports for you if you set it up correctly.
globally load components you use a lot:
import Vue from 'vue';
import Vuetify, { VLayout } from 'vuetify/lib';
// globally register v-layout.
// now you don't need to import it in every component that uses it
Vue.use(Vuetify, { components: { VLayout } });
Normal Mode
If you import vuetify directly, it'll automatically load all its components for you and is ready to use without further configuration:
// not a-la-carte, will load all vuetify components
import Vue from "vue";
import Vuetify from "vuetify"; // **not** /lib
Vue.use(Vuetify);
const vuetify = new Vuetify({ /* ... */});
new Vue({vuetify}).$mount('#root');

Vue i18N SAP multilingual best practice?

I'm new to VueJS. I'm trying to make a SAP website with multilingual support. I'm using this helper plugin :
Vue-I18n
Based on this example:
vue-i18n/example/locale/src/App.vue
It works good, however how can I extend this to have the language available to multiple components (pages)? Do I really need to use a store for this, from Vuex?
I did the following and it works like a charm.
main.js:
import Vue from 'vue';
import router from '#/router';
import { store } from '#vue/store/index.js';
import i18n from '#vue/i18n.js'
import App from '#vue/components/App.vue';
Vue.config.productionTip = false;
new Vue({
store,
i18n,
router,
render: h => h(App),
}).$mount(`#app`);
#vue/i18n.js:
import Vue from 'vue'
import VueI18n from 'vue-i18n'
import fr from '../json/fr.json';
import en from '../json/en.json';
Vue.use(VueI18n);
let format = function(lang) {
let messages = {};
lang.forEach(function(message)
{
messages[message.key] = message.text
})
return messages;
}
let populateTexts = function(lang) {
return { ui : format(lang[8]) }
}
let conf = {
locale: 'fr',
fallbackLocale: 'fr',
messages : {
fr: populateTexts(fr),
en: populateTexts(en)
}
}
const i18n = new VueI18n(conf)
export default i18n
You probably won't have the same JSON, so you won't need format and populateTexts.
Although it is late:
1- When using this package in a project it will be available for all components and pages.
2- Using store is not a must, but it is like data layer that all vars and data collected in and make data management easy.

Categories