We're using the VueI18n plugin for internationalizing our Vue application which is working fine so far. Text contents are managed and translated by our editorial staff using Zanata.
However there is a major drawback in our current approach. We're pulling the translations from Zanata during build time, i.e. "baking" them into the application. As a consequence, we need to rebuild and redeploy the application each time a text is edited.
Are there any approaches which pull the translations (or the translation files) when running the application so that the user is always presented the latest content?
You should be able to do this if you load the translation file and then use setLocaleMessage( locale, message ) to load them. I'm assuming you're using axios.
axios.get('/path/to/locale_de')
.then((response) => {
Vuei18nInstance.setLocaleMessage('de', response)
});
The response should be a plain JSON.
Related
I am coding a little website to be my homepage.
So far I managed to do what I wanted with express and handlebars templates but I am doing this website to test my boundaries, and I lack using a frontend framework and working with web components.
so for the sake of challenge and learning, I settled on svelte
which is not exactly a framework, I read, but is a delight to write with and seems very promising on the performance side.
The problem is I want to keep the hand on my website, and sapper, the full framework that comes with svelte, is a bit too much of a black box for me.
(you put this file here and that file there,
compile with this complex configuration you shouldn't touch
and BOOM you got routes)
What I would like is to use express to manage the routes and then render the svelte app, either with a different page/app for a route, or the same app with different variables.
Could anybody point me in the right direction?
I considered using sessions or a socket, but I have no idea of how to listen to this client-side and the documentation/articles about svelte are sparse and all talk about sapper.
here are a few lines of code I wrote in a test app. I know it's not the way you do it but it was for curiosity's sake
// --------------server.js
// ...
app.set('view engine', 'hbs')
app.set('views', path.join(__dirname, '../views'))
// Using a view engine on TOP of svelte
// is certainly a bad choice
app.use(express.static('app/public'))
// to make the svelte compiled .js and .css available
app.get('/test', (req, res) => {
res.render('home', {user:true})
// I tried to send props like this
// it did not get the props in svelte,
// but what I found is that svelte ADDS itself to the page
// and does not remove the HBS code, event though it is in the target container
})
edit
I will finally use sapper, thank you for the advices...
I really would like them to implement some sort of svelte middleware that allows to render/serve pages individually (once they are build of course)
If you do it like that, splitting your web application into several independent svelte apps that are served from express, then you need to keep the (web-)application wide state on the backend or keep it in the local storage on the browser. You can't use the states provided by sveltes store (writeable, etc), because that's in-memory and destroyed whenever you navigate to a new page (via express).
If sapper is to much magic but you want to keep running a single-page-app, have a look at svelte-spa-router. That isn't configuration based.
Good afternoon.
I have an interesting problem at the moment. We have a third party server that offer translations for static html content. I need to fetch this content via Ajax and display it in my Vue components.
The current situation
These translations are fetched via a dictionary-like data structure, i.e. via a category and a key. We have incorporated a Vue plugin to load these into our components via a function t, like this:
<template>
<section>
<h1>{{ t('CommonHeaders', 'HomePage') }}</h1>
<p v-html="t('Articles', 'SiteDescription')"></p>
</section>
</template>
At the moment these translations are shipped to the browser by embedding them in the HTML, after which our client-side hydration mechanism reads them and adds them to the Vuex store. The t function then looks up the translations and displays them where needed. These translations are reactive and accept data parameters to format translations.
We use Vue SFC to render user flows in an SPA-like fashion, although the site is not yet an SPA.
The problem
In order for this to work the translations required for a page have to be listed in the back-end controller methods in a dictionary.
This has become un-maintainable and much more data is shipped to the front-end than what is necessary. Additionally, the back-end system has no definitive end-point when a page is built before being shipped to the browser that we can hook into in order to add the translations to the HTML and content often end up being duplicated.
The back-end system was built using DotNet MVC 4, so we have no SSR capabilities at this point.
The solution (hopefully)
In order to better maintain our code I would like to utilise the 't' function from the plugin to load translations via an AJAX call before the vue engine has rendered the template, i.e. via the beforeCreate or created hook. The problem is that the Vue instance will have to know about translations required in child component templates before the AJAX call can be fired, and I have no idea how to accomplish this.
On a side note, delaying rendering like this goes against all my instincts but it doesn't look like we have a choice at this point.
I am planning to cache the translations client side with a content hash in case they get updated, so the ajax calls will hopefully not be required very often, only on first load.
The site is gradually being converted into an SPA, at which point I will be able to split off the FE and utilise SSR via node. Up until that point though this is the best idea I could come up with.
Any help will be greatly appreciated.
I have been thinking about this my self as at the moment I just send an entire cached json to the client on App Init with a loading screen, is not bad at the moment since there is not a lot to translate but was considering the following approach otherwise:
Have an array in the translation vuex module store a list of keys that need to translate (array).
Have t() push the keys to translate if not already in translated store and return either empty string or a placeholder until the translation is re-actively available.
On mounted dispatch a fetch method on the store to perform the ajax call and set the translation state and clear out the translate list when complete.
Vue should by default it's behavior re-render upon the VUEX state being changed and cause t() in the template body to be recalled and return matching values on nextTick instead of placeholder value previously returned.
I'm working on a Web Application which uses Vue.js as the frontend framework. Currently we are handling static text directly where it is used i.e. inputting it directly at source in components data.
<script>
export default {
name: "ExampleComponent",
data: function () {
return {
title: 'Business Title',
modalBodyText: 'Some business text that devs don't care about'
}
}
}
</script>
However the Web App requires a lot of input from the business team on how things are phrased and how text is worded. Due to compliance some of the text in the application changes frequently and I am wondering whether there is a better way of handling static text in frontend frameworks such as Vue.js?
I feel we should be abstracting out the static text from the code and importing a file globally but I'm unsure if this is common practice or how best to do it. Any input on this would be much appreciated.
Easiest thing you can do is to create some json file (or multiple files ...depending how big and structured your app is) with all the texts and simple import it into your components
import content from './content.json'
Webpack will make content of such file available as JS object so you can easily reference keys in your code (not directly from template tho)
Slightly more sophisticated way would by to use of I18n libraries like vue-i18n - still keeping your text in separate json files with some utilities on top for accessing it directly from templates - but with an option of changing text storage mechanisms later and using many open source tools available for managing and changing the content by non-developer users...
Can I dynamically add a mixin? I think I read that runtime insertion of mixins is not supported and will never be. Is the below runtime insertion possible?
My usecase is; all our pages are stored in a database, each page standard properties like; title, content and template. We have components for each template. Each template component displays the title and content differently. So I need to build the routes and say this page uses this (template) component. Maybe I can use sub-components to achieve this? Can I dynamically add sub-components at runtime?
The easiest solution is to do the following:
Router:
// myPages retrieved by REST call
const routes = _.map(myPages, page => {
return {
path: `/${page.url}`,
name: page.name,
component: DefaultPage // make all pages use DefaultPage component
}
});
DefaultPage.vue
<template>
</template>
<script>
import mixins from './mixins';
export default {
mixins: [mixins.Base]
beforeMount() {
// I dont think this is possible?
let templateMixin = mixins[ this.page.template ]
this.mixins.push( templateMixin );
}
}
</script>
Maybe its possible to assign a sub-component at runtime?
<template>
// Somehow call the sub-component (template)?
<template></template>
</template>
<script>
import templates from './templates';
export default {
components: {},
beforeMount() {
// Is this possible?
let templateCmp = templates[ this.page.template ]
this.components = {
templateCmp
}
}
}
</script>
Unfortunately there is a lot of misinformation on the web stating that "because of userland perils, it's not safe or secure to load dynamic or "runtime" components", and that doing so is "a security risk".
This stems from the reasoning that Vue uses the eval statement when compiling components. However, so does React and Angular. There is in fact no way to compile components without this. while eval is a sharp knife, so is any JavaScript. Saying that a sharp knife is insecure is false as long as you keep that sharp knife itself secure.
The notion that runtime components is insecure is utterly and completely false. There are tons of officially supported ways to do this if you control the source. In fact, there is even an officially Vue supported "userland" method to load dynamic/async components even if you don't control the source(!!)
Async components
Probably the easiest way to load a page from a database (which itself may have it's own sub-components, mixins and dependencies), is with async components.
This is an official part of Vue and I've used it in dozens of production apps. It works exactly like expected; nothing is invoked by your application until all the conditions are met, and when the load conditions are met, you are free to obtain the component however you see fit.
Note: You must control the source of these components, or XSS injection and other hacking is possible.
If you're using webpack, it's easy to get all the components dependencies into a single file. In the above URL are specific how-to articles including a video tutorial of how to produce a single js file for your pages dependencies that aren't part of your main application.
One caveat for loading multiple sub components this way is you may overlap/repeat loading (i.e. page A may load 5 sub components that no other static page loads, and page B may load 5 sub components, 3 of which are shared with page A, and 2 of which are specific to page B). Caveats like these can mess up your optimization techniques if you don't think them through. This can be unavoidable and fine, and still much faster than loading the entire app at once though.
Async components in userland
If you only want to load custom templates from a database (i.e. if you don't want to allow users to load their own custom components, mixins, filters, directives, etc), with each template, then you are in luck; Vue even has official support for async templates that are locked-down to only allowing template changes. This is enough to offer your component builders scaffolding for creating an app, but prevents them from executing arbitrary JavaScript code (this method won't let them setup a data section, or hook into the component lifecycle for example).
v-runtime-template is officially recognized by the Vue.js team as the official method for creating userland-safe Vue templates.
I've used v-runtime-template in platforms used by some of the biggest names in the industry across tens of millions of users without a single security breach, because this method only exposes the components you say are OK.
Further template lockdown using JSON schemas
If you need only form generation, or you can simplify your components further, and you only need to reason about data in simple ways like queries (i.e. if you're building a survey generator, or an analytics or other widget dashboard), you can build components out of JSON Schemas using vue-form-json-schema. This method takes 2 schemas: one for your data, and one for your form. The parent component you load specifies all the components that are accessible to the schema, so you must whitelist components that userland has access to, further, the forms cannot run arbitrary JavaScript, they can only call functions that you make available in the parent component, which are your JavaScript whitelist.
Userland-Safe Queries on arbitrary data
You can let users query specific JSON objects for use in completely-safe userland queries that can be provided by the public using JSONata. Developed by IBM, JSONata is a way to safely query and, with the exposure of a few functions, allows your users to even manipulate data in verified-safe ways.
Although JSONata is logically proven to be safe in unserland, it is also Turing-complete without functions, meaning your users can manipulate the Schema provided data by outputting new data that can technically do anything; i.e. your users can create Doom 3 using JSONata queries. People have made games like Pong and other interesting things out of JSONata.
JSONata's power cannot be overstated. It powers the magic behind nearly all no-code/low-code platforms like the open source Node-RED, and is behind many other proprietary no-code/low-code platforms that are closed source, including Google, Microsoft, Amazon and IBM platforms.
They use JSONata to translate data schemas between platforms, and whenever business logic is needed on data manipulation, and the manipulation must be left in user-land (i.e. an app that needs to manipulate data, but you want to run that app on your platform and not have to worry about QA or people being able to hack or write a nefarious app that breaks into your platform).
I have an existing FineUploader implementation for small files using the Traditional (upload-to-server) version which is working great. However, I'd like to also allow Direct S3 uploads from a different part of the application which deals with large attachments, without rewriting the existing code for small files.
Is there some way to allow both Direct S3 and Traditional uploads to work alongside each other? This is a single-page application, so I can't just load one or the other fine-uploader versions depending on which page I'm on.
I tried just including both fine-uploader JS files, but it seemed to break my existing code.
Client-side code:
$uploadContainer = this.$('.uploader')
$uploadButton = this.$('.upload-button')
$uploadContainer.fineUploader(
request:
endpoint: #uploadUrl
inputName: #inputName
params:
authenticity_token: $('meta[name="csrf-token"]').attr('content')
button: $uploadButton
).on 'complete', (event, id, fileName, response) =>
#get('controller').receiveUpload(response)
Good find, #Melinda.
Fine Uploader lives within a custom-named namespace so that it does not conflict with other potential global variables, this is the qq namespace (historically named). What is happening is that each custom build is redeclaring this namespace along with all member objects when you include it in the <script> tags on your page.
I've opened up an issue on our bug tracker that explains the issue in more technical details, and we're looking to prioritize a fix to the customize page so that in the future no one will have this issue.