VueI18n locale message loading issue - javascript

I have the vueI18n package installed for language localization in the app and the locale messages object is fetched through an api call. I have config file where the default language is specified and based on which the locale will be loaded before the app.vue is created. ie; I'm loading the locale in beforeCreate lifecycle method. But still the text are not loaded properly.
Initially the message object is null because based on the user config we are fetching the language and loading the messages. I am assuming the initial empty object is causing the issue.
Could someone suggest a way we can delay the page load until we confirm the message object is loaded successfully.
vueI18 config file
import Vue from 'vue'
import VueI18n from 'vue-i18n'
import userConfig from './userConfig'
import apiService from '#/service/apiService.js'
let selectedLanguage = ''
Vue.use(VueI18n)
const loadLocaleMessages = async (lang) => {
const messages = await apiService.getLocale(lang)
return messages
}
export const i18n = new VueI18n({
locale: selectedLanguage,
fallbackLocale: userConfig.defaultLocalizationKey,
formatFallbackMessages: true,
messages: {}
})
export const loadLanguage = async lang => {
const messages = await loadLocaleMessages(lang)
selectedLanguage = lang
return i18n.setLocaleMessage(selectedLanguage, messages[lang])
}
App.vue
async beforeCreate () {
await loadLanguage(this.$userConfig.defaultLocalizationKey)
this.$i18n.locale = this.$userConfig.defaultLocalizationKey
}

The good practice is to always have a default/fallback language inside vue-i18n, e.g. English. If you can't do this for whatever reason, then you can simply put a v-if on the App's template so that nothing is rendered until you fetch at least 1 language into vue-i18n.

Related

Next.js import with variables or conditionally import

import { keyFeatures } from 'common/data/AppClassic';
I am new to Next.js and using a template.
I have at least managed to succesfully add i18n, and I don't want to rebuild the whole template and the components... There is already a file in AppClassic that serves the content (pictures, text content ect). The easiest thing I thought of would be just duplicating this, and putting these files in different subpaths like 'en/common/data/AppClassic' or 'de/common/data/AppClassic' - And then somehow to import it with the dynamic locale const or conditionally render it, so if the locale const is 'en' then one file is imported, but if the const is 'de', then the other file is imported.
const router = useRouter();
const { locale } = router;
import { keyFeatures } from { locale } + '/common/data/AppClassic';
Is there a way to do something like that, and if so, could you provide some examples - since I have actually no Idea what I am doing.
I would be very grateful.
You could work your way with Next.js dynamic imports like the example:
import dynamic from 'next/dynamic'
const DynamicComponent = dynamic(() =>
import('../components/hello').then((mod) => mod.Hello)
)
For more info check their official docs:
https://nextjs.org/docs/advanced-features/dynamic-import

ReferenceError: navigator is not defined (mxgraph with next.js) [duplicate]

Trying to create an xterm react component in Next.js I got stuck as I'm not able to get over an error message I've never got before.
I'm trying to import a npm client-side module called xterm, but if I add the import line the application crashes.
import { Terminal } from 'xterm'
The error reads Server Error... ReferenceError: self is not defined
and then shows this chunk of code as Source
module.exports = require("xterm");
According to some research I did, this has to do with Webpack and could be helped if something like this was done:
output: {
globalObject: 'this'
}
Would you know how to fix this?
The error occurs because the library requires Web APIs to work, which are not available when Next.js pre-renders the page on the server-side.
In your case, xterm tries to access the window object which is not present on the server. To fix it, you have to dynamically import xterm so it only gets loaded on the client-side.
There are a couple of ways to achieve this in Next.js.
#1 Using dynamic import()
Move the import to your component's useEffect, then dynamically import the library and add your logic there.
useEffect(() => {
const initTerminal = async () => {
const { Terminal } = await import('xterm')
const term = new Terminal()
// Add logic with `term`
}
initTerminal()
}, [])
#2 Using next/dynamic with ssr: false
Create a component where you add the xterm logic.
// components/terminal-component
import { Terminal } from 'xterm'
function TerminalComponent() {
const term = new Terminal()
// Add logic around `term`
return <></>
}
export default TerminalComponent
Then dynamically import that component when using it.
import dynamic from 'next/dynamic'
const TerminalComponent = dynamic(() => import('<path-to>/components/terminal-component'), {
ssr: false
})
As an alternative, you could add the logic directly when dynamically importing the library with next/dynamic to avoid having an extra file for it.
import dynamic from 'next/dynamic'
const Terminal = dynamic(
{
loader: () => import('xterm').then((mod) => mod.Terminal),
render: (props, Terminal) => {
const term = new Terminal()
// Add logic with `term`
return <></>
}
},
{
ssr: false
}
)

How to access fastify app instance inside an imported file

Im working on a fastify powered rest api and im trying to separate my code into logical files. I have some class objects that i import into my server where fastify is defined and the fastify.listen happens. What i cant figure out is how to access the fastify instance inside a file that i import.
app.js
import fastify from 'fastify'
import autoload from 'fastify-autoload'
import { join } from 'desm'
export default function (opts) {
const app = fastify(opts)
app.register(autoload, {
dir: join(import.meta.url, 'routes')
})
return app
}
server.js
import createApp from './app.js'
import 'dotenv/config.js'
import Sessions from './scripts/sessions.js'
import emitter from 'central-event'
async function start () {
const app = createApp({ logger: true })
await app.listen(process.env.PORT || 3000, process.env.IP || '0.0.0.0')
const intervalSeconds = process.env.intervalSeconds * 1000
setInterval(function () {
emitter.emit('heartbeat')
}, intervalSeconds)
}
start()
I want to access the fastify app instance inside sessions.js that is imported into server.js I have tried various things like importing fastify and creating the app in there hoping that it would be inherited etc. Any help would be appreciated.
I found the info i needed. Inside the imports for sessions and others im exporting the class via export default (app) => class Sessions{}
then inside server i import it:
import SessionsImp from './scripts/sessions.js'
then pass the app via:
const SessionsClass = SessionsImp(app)
const Sessions = new SessionsClass()
It seems convoluted but it works as i wish, then i can decorate app with the sessions and other classes im loading and use them inside routes and plugins as i wish.

How can i dynamically load CommonJs module?

I'am working on app with React, TS and Webpack stack.
I need to implement feature that allows my app work with client plugins - js files that override existing functionality of some classes. It can be loaded from anywhere - local file system or remote repository and should be fetched in the runtime, because i need to have an option to specify new extension in config and just press F5.
Dynamic import is not my case, because as far as i understand Webpack needs to be able to at least guess roughly what an import() is meant to be referencing. Using simple 'get' request might be an option, but how can i use loaded script as CommonJS module in this case? And am i correct about dynamic import behavior?
You can use #paciolan/remote-module-loader to remotely load a common js module.
import { createLoadRemoteModule } from "#paciolan/remote-module-loader"
const main = async() => {
const loadRemoteModule = createLoadRemoteModule()
const myModule = await loadRemoteModule("http://fake.url/modules/my-module.js")
const value = myModule.default()
console.log({ value })
}
main()
If you need to pass dependencies to the module:
import {
createLoadRemoteModule,
createRequires
} from "#paciolan/remote-module-loader"
const dependencies = {
react: require("react")
}
const main = async() => {
const requires = createRequires(dependencies)
const loadRemoteModule = createLoadRemoteModule({ requires })
const myModule = await loadRemoteModule("http://fake.url/modules/my-module.js")
const value = myModule.default()
console.log({ value })
}
main()
If need to load a React Component, check out #paciolan/remote-component
You may have to take extra steps if you have a Content Security Policy (CSP) set.

Webpack Dependency Management and Vue.js async component loading

I am trying to achieve Vue.js dynamic async component registration. This video gave me code that works perfectly fine, but it loads all modules even if they are not used.
import Vue from 'vue'
import upperFirst from 'lodash/upperFirst'
import camelCase from 'lodash/camelCase'
// Require in a base component context
const requireComponent = require.context(
'../components/base', false, /base-[\w-]+\.vue$/,
)
requireComponent.keys().forEach(fileName => {
// Get component config
const componentConfig = requireComponent(fileName)
// Get PascalCase name of component
const componentName = upperFirst(
camelCase(fileName.replace(/^\.\//,
'').replace(/\.\w+$/,
'')),
)
// Register component globally
Vue.component(componentName, componentConfig.default || componentConfig)
})
What I tried to achieve was to create async components instead. Like so
import Vue from 'vue'
import upperFirst from 'lodash/upperFirst'
import camelCase from 'lodash/camelCase'
// Require in a base component context
const requireComponent = require.context(
'../components/base', false, /base-[\w-]+\.vue$/,
)
requireComponent.keys().forEach(fileName => {
const componentPath = fileName.replace('./', '../components/base/');
// Get PascalCase name of component
const componentName = upperFirst(
camelCase(fileName.replace(/^\.\//,
'').replace(/\.\w+$/,
'')),
)
// Register component globally
Vue.component(componentName, () => import(componentPath))
})
If the case of the code above vue throws an error
vue.esm.js:591 [Vue warn]: Failed to resolve async component: function () {
return __webpack_require__("./lib lazy recursive")(componentPath);
}
Reason: Error: Cannot find module '../components/base/base-button.vue'
If I manually write down Vue.component('BaseButton', () => import('../components/base/base-button.vue'))
it works without problem but when I try to do that dynamically it fails. Is it possible to do such async component registration if so how?
This doesn't work either:
const button = '../components/base/base-button.vue'
Vue.component('BaseButton', () => import(button))
only if I literally right the string into import function.
import cannot be used when the module path is fully dynamic. See the docs:
Fully dynamic statements, such as import(foo), will fail because webpack requires at least some file location information. This is because foo could potentially be any path to any file in your system or project. The import() must contain at least some information about where the module is located, so bundling can be limited to a specific directory or set of files.
You didn't specify the mode argument for require.context which defaults to "sync"; this means all modules matched will be loaded straight away. You want to use "lazy" which generates a lazy-loadable chunk for each module.
Untested, but I imagine it'll be something like this:
const context = require.context('../components/base', false, /base-[\w-]+\.vue$/, 'lazy');
context.keys().forEach(fileName => {
const componentPath = fileName.replace('./', '../components/base/');
// Get PascalCase name of component
const componentName = upperFirst(
camelCase(fileName.replace(/^\.\//,
'').replace(/\.\w+$/,
'')),
);
// Register component globally
Vue.component(componentName, () => context(fileName));
});

Categories

Resources