in my application I will have a large infobox with a lot of text. They will often be on a numbered list. I would like to find the easiest way for them so that I can easily send them from a developer to a copywriter.
Is there any smart way to create an HTML list (ul or ol) using vue i18n without breaking it into multiple variables in message. I would like to use e.g. Component interpolation, but only call it once: <i18n path="info" tag="li">. Quick code to show what I mean:
//translate.js (fragment)
const messages = {
en: {
sample: 'Sample text with numbered list. 1) Find the problem 2) Ask on Slack 3) Solve the problem 4) Keep happy with vue'
}
}
export const i18n = new VueI18n({
locale: 'en',
messages
});
//App.vue (fragment)
<ul>
<i18n path="sample" tag="li">
</i18n>
</ul>
I would like the text in 'sample' to be divided by sub-points. I care about any way, it doesn't have to be Component interpolation.
You can use an array in your localization as shown in the manual - https://kazupon.github.io/vue-i18n/guide/messages.html
Then you can build the template like this:
<ul>
<i18n v-for="(item,index) in NumberedList" :key="index" :path="`sample[${index}]`" tag="li" />
</ul>
Related
I want an elegant solution for adding styling like bold, italic, or a color for only a part of a translation.
Example
Let say that I have this i18n translation:
'my-page.confim-customer.popup.warning': 'All customer files must be authorized by the customer himself, please be sure to invite the customer to scan this QR code.'
Now i want that the words "must be authorized by the customer himself" will be in bold and in yellow. (Design requirement)
What can I do?
Solution 1 - Split in i18n
I can simply split the translation to three parts in i18n file:
// i18n/en.js
'my-page.confim-customer.popup.warning.part1': 'All customer files'
'my-page.confim-customer.popup.warning.part2': 'must be authorized by the customer himself,'
'my-page.confim-customer.popup.warning.part3': 'please be sure to invite the customer to scan this QR code.'
and then apply the css i want only on the "part2".
// MyComponent.jsx
const first = i18n.t('my-page.confim-customer.popup.warning.part1');
const second = i18n.t('my-page.confim-customer.popup.warning.part2');
const thrid = i18n.t('my-page.confim-customer.popup.warning.part3');
return (
<>
<div>
{first}
<span className="bold yellow">{second}</span>
{thrid}
</div>
</>
);
Pros
It works
Cons
Polute the i18n files with lots of partial string for each lines.
When change is required it is a hassel to change the view and translation
Solution 2 - Split in code
I keep i18n in the same translation but I add seprator in it (let say $#$):
'my-page.confim-customer.popup.warning': 'All customer files $#$must be authorized by the customer himself,$#$ please be sure to invite the customer to scan this QR code.'
And then in my component i split it:
// MyComponent.jsx
const [first, second, thrid] = i18n.t('my-page.confim-customer.popup.warning').split('$#$')
return (
<>
<div>
{first}
<span className="bold yellow">{second}</span>
{thrid}
</div>
</>
);
Pros
It works
Offload the headache to javascirpt, keep the translation in one i18n key
Cons
Need to process the key before using it
When change is needed (let say we add a new seperator for a section to be italic), we need to alter the way we process the key and change the view to match. not dynamic.
Note
I'm looking for a vanilla solution, as I'm not working with React or a popular framework.
i18next has a special component Trans. There you can use any components inside your string like:
"test" : "All customer files <yelow>must be authorized by the customer himself,</yelow> please be sure to invite the customer to scan this QR code."
...
<Trans
i18nKey='test'
components={{ yelow: <span className="bold yellow"/> }}
/>
The strings split into separate keys are extremely not translator-friendly. It would be hard for translators to provide quality translations since they need to know where each part is used just not to break the UI.
In addition, the translation for some languages likely will have a different word order. The situation is even more complicated in RTL languages.
So, it's definitely recommended to avoid such approaches with string splitting.
As a Vanilla JS solution that can work in your case, I would recommend the Lingui library.
In short, it allows developers to do the following thing by using the #lingui/macro package:
import { Trans } from "#lingui/macro"
<Trans>Read the docs.</Trans>;
Then you can extract all your localizable texts to the .po or .json file by using the Lingui CLI.
Components and HTML tags are replaced with dummy indexed tags (<0></0>) which has several advantages:
both custom React components and built-in HTML tags are supported
change of component props doesn't break the translation
the message is extracted as a whole sentence (this seems to be obvious, but most i18n libs simply split the message into pieces by tags and translate them separately)
Further reading:
Internationalization of JavaScript apps
Working with LinguiJS CLI
#lingui/macro docs
I have to send some emails and I simply want to re-use as much code/knowledge as possible (just because), for this I want to render a React component to raw HTML with inline classes.
I have managed to render a React component to static markup via:
const TestMail = () => {
return (
<div>
<h1 className="text-xl font-bold border-b">You have a new Test Email on Productlane</h1>
<p className="border-b">Something something</p>
<a href="https://productlane.io/feedback" className="bg-purple-600">
Open
</a>
</div>
)
}
export function testMailer({ to }: IParams) {
const emailHtml = ReactDOMServer.renderToStaticMarkup(<TestMail />)
const processedHtml = juice(emailHtml, {
webResources: {
// relativeTo: "app/core/styles/index.css",
},
})
return {
async send() {
console.warn("trying to SEND")
console.warn(processedHtml)
},
}
}
This outputs the raw html string without the styles, so I figured I really need to pass the compiled css for the inliner to do its job
<div><h1 class="text-xl font-bold border-b">You have a new Test Email on Productlane</h1><p class="border-b">Something something</p>Open</div>
You can see from the snippet I'm trying to use Juice to inline the styles, however, I can seem to get the classes to be rendered in the html, any idea how to achieve this?
Right I’ve been doing some digging and this is my plan for handling emails.
Transpile down styles sheets to style attribute using Juice, abstract HTML4 tables as react components to allow full email client support.
Support {{ parameters }} leave them in your outputted HTML and pass it through Handlebars to replace them just before sending the email.
Option 1:
Use NextJS static html export to generate HTML files from said React components.
Configure build command to run custom Juice script on outputted files.
Reference the exported files using handlebars to apply the per user context e.g. { name: “David” }. I’m doing this in my sendEmail() function.
Option 2
Use NextJS server endpoint to compile the handlebars template with the per user context. See this article for reference.
You could also replace custom Juice script with this CLI tool or this npm package. Optionally you can even use Inky to abstract away HTML4 tables.
Alternatively if you only want partial email client support NextJS can inline the CSS into the head with this experimental flag discusses here. For full support you will need CSS in style attribute.
I have a lambda function sendEmail(email: string, templateName: string, context: Record<unknown, any>) which has the hubs template files bundle inside it. When the email is sent it then process the context using handlebars compile().
I have a legacy PHP app which I would like to slowly migrate to Vue. The PHP app renders a bunch of HTML and javascript files in quite a tangled fashion, i.e.
foo.js.php
...
<script src="mysite.com/some_js_file.js" />
...
const a = '<?=$variable_from_php?>';
so in the end, the browser obviously doesn't know how the js files are constructed, but can run them. What I'd like to do is from the outer layer Vue app, request the index page for a certain sub-section of the legacy app, and render that to a Vue node, as a micro-frontend of sorts. When I request each index, it will of course, contain a header with numerous other imports (scripts/styles) that that micro-frontend needs to function. So, two parts to this question: 1) what would be the best (or maybe least terrible) way to do this in Vue. Using v-html? iframe? (please say no iframes) And 2) will there be any showstopper security problems with this approach (since I'm basically saying fetch all the JS in the header and run it). Let me know if this question makes sense. Thanks!
Maybe you need like to : a module php or component as template.php(php server)
export const templateOfAdvanceTemplatePage = `
<div class="content edit-page management">
<md-card class="page-card">
<?php echo "My Component" ?>
</md-card>
</div>
And from node server
import * as url from "url";
var templateOfAdvanceTemplatePage = url.parse("http://www.website.com/template.php");
export default {
template: templateOfAdvanceTemplatePage,
...
}
for more information import vue here, and php as javascript file here
Vue.js can be used in two separate ways: For more complex applications you would use a build process and pre-compile the templates from the source, which are usually Single File Components SFC; *.vue files. The templates would then become render functions and no HTML is ending up in the output assets. There is, however, another way of defining Vue components. You can define them inline with the runtime-only bundle of Vue. For migrations and smaller applications this approach would be advised. You would need to include the compiler. See also the Vue documentation about that topic Vue v2 and Vue v3). If you are importing Vue as a module and are missing the compiler, see here.
If you want to render dynamically generated HTML from PHP as a Vue template, you would need the second approach. Keep in mind that, with this approach, you would always need to have the generated PHP output to be in sync with the Vue components. And you would need to fully trust the HTML, you are generating with PHP, otherwise you will risk injections.
There is, however, still another problem: You need the generated PHP output HTML as a string within JavaScript and it should not be interpreted by the browser (ideally) or removed again from the DOM. So, you need to decide (based on your project) how you want to generate the HTML so that it can be read in as a JavaScript string. Here are some approaches:
Generate the HTML directly into the page. Then, define which element you want to target, get the HTML with .innerHTML and delete the node from HTML (drawback: you will render the HTML twice, might produce short visual glitches).
Fetch the HTML via XHR from a separate page. You will directly have the HTML as a string in the response (see e.g. fetch).
Render <script type="text/x-template" id="static-html-content"></script> around the generated HTML content. Then, you do not need the HTML as string and you can directly use the id as reference (use template: '#static-html-content'). See the documentation of X-Templates in Vue.
Then, you can use the runtime-only version of Vue and define your components. Here is a live example:
const Counter = {
// retrieve and add your template string here
template: `
<div class="counter">
This is a counter: {{ counter }}
<button #click="counter++">Increase Counter</button>
</div>
`,
data: function() {
return {
counter: 0
}
}
};
const App = {
components: { Counter },
template: `
<div class="app">
This is the app component.
<hr />
<counter />
</div>
`
};
new Vue({
el: '#element',
template: '<App />',
components: { App }
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="element"></div>
Another approach would be to just render the HTML string within a component with the v-html attribute. The main drawback of this solution is, however, that the content is then not reactive. You cannot change your internal component data and expect the template to react to the changes. Therefore, you are missing out on the main benefits of Vue, but you are not restricted to a template which matches your components internal structure.
A similar question was also posed in the Vue forum: link
Problem
I'm implementing translations for my Angular 6 application. It needs to support static texts of the app in multiple languages. I don't need dynamic translations during runtime - I just need producing an app with texts in a language chosen during build.
Angular has the whole page on internationalization, but it focuses solely on situations where all the texts are hard coded in the templates, like in the code below:
<p>This is a hard coded text</p>
That's hardly a situation in my template. Additionally it seems to me impossible or extremely dirty to write the whole application with all the texts hard-coded in templates, so if that's the only mechanism available for translation, then it seems completely useless to me.
In my components I very often have texts bound to some JavaScript variables (or to be precise object properties), like in the code below:
<li *ngFor="let navigationEntry of navigationEntries">
<a [href]="navigationEntry.url">
{{ navigationEntry.text }}
</a>
</li>
Let's say the variable is still static, just being stored in the component or a service, e.g.:
navigationEntries: Array = [
{
url: "/",
text: "Home",
},
{
url: "/login",
text: "Login",
},
{
url: "/admin",
text: "Admin panel",
},
];
Question
Is there a way to use Angular native translation (internationalization) mechanisms to translate the anchor texts (or actually the text properties of the navigationEntries members) during build time? The structure of the JavaScript data and the HTML of the template can change.
If Angular native internationalization mechanisms can't handle that (but then I wonder what's their use at all), then what are other solutions that could help with implementing such translations? I found ngx-translate library, but it's providing translations which can be dynamically changed at runtime and ideally I wouldn't like to add that unnecessary overhead of dynamic solution watching on all the translated texts.
Yes, you can use Angular native translation mechanim to do this. We use the following pattern a lot to get around the missing dynamic translations in angular i18n:
You have an array of messages, and then use it with ngFor and ngSwitch to template the messages, something like this for your example:
<li *ngFor="let navigationEntry of navigationEntries">
<ng-container [ngSwitch]="navigationEntry.text">
<a [href]="navigationEntry.url" *ngSwitchCase="'Home'" i18n="##home">
Home
</a>
<a [href]="navigationEntry.url" *ngSwitchCase="'Login'" i18n="##login">
Login
</a>
<a [href]="navigationEntry.url" *ngSwitchCase="'Admin panel'" i18n="##adminPanel">
Admin panel
</a>
<ng-container>
</li>
It's somewhat verboose - but it works for a surprising amount of use cases.
Note, the i18n id (the string following the ##) is just an unique id, which is used in the xlf translation file. The string can be anything, I use the message itself, if possible, for readability and reuse.
I am trying to understand the concept behind logic-less temlpates, but I'm finding myself hitting a wall.
I want to implement a simple navigation bar, e.g. "Home, About, Contact" links at the top of every page, and the "current" one should be highlighted with a different class (I'm using bootstrap). But how can I do this in a sensible fashion? So far I have:
Move the nav to every template, and copy the whole thing (not DRY, ugly).
Use keys instead of values, i.e. render('home', { on_home_page: true }); with <a href="/" {{#on_home_page}}class="active"{{/on_home_page}}>Home</a>. This is better, but still annoying that I have to create N variables to hold 1-variable worth of data.
create the nav in the controller, i.e. pass in { 'Home': {link: '/', active: false}, 'About: {link: '/about', active: true} } or similar. I dislike this because it has the opposite problem of logic-less templates. Now I have HTML-ful controllers...
Given the above options, I like (2) the best. But what I would prefer is some way to have a single variable to check, like:
// controller
render('about', {active: 'about'});
render('home', {active: 'home'});
// mustache nav
<a href="/" {{#if active == 'home'}}class="active"{{/if}}>Home</a>
<a href="/about" {{#if active == 'about'}}class="active"{{/if}}>About</a>
I'm sure this comes up all the time for mustache experts --- what's the best way to deal with it?
There's no way to deal with this using vanilla Mustache.
You have two options that let your JSON data and template stay clean:
1- using Mustache, write a helper function so that you can use it like this:
var selected_item = function(param) {
if (param == this.active) {
return 'active';
}
};
(disclaimer: I wrote this helper out of the top of my head, might not work as-is but I think you get the point)
Home
About
then mix-in that helper into your JSON data, best way probably being overloading Mustache.render so that every call to render adds your helper into the mix. Notice that leaving the class="" part outside of the helper allows you to have multiple different classes on each menu item while still having your "logic" for the active part.
2- switch to Handlebars which allows for this kind of basic logic. Handlebars is a superset of Mustache, stuff that works on vanilla Mustache will directly work in Handlebars so upgrading is easy. Be careful though, that once you have upgraded and modified your templates to work with Handlebars there's no turning back.
I just wrote a post on this. Click here or on the banner:
The basic idea for doing this with vanilla moustache and NodeJS is like so:
app.get('/Portfolio', function(req, res) {
res.render('portfolio.html', {
Portfolio: 'class="current"',
});
});
app.get('/Blog', function(req, res) {
res.render('blog.html', {
Blog: 'class="current"',
});
});
Notice how each separate route sends a different moustache variable. If a user goes to /Portfolio the {{{Portfolio}}} variable will exist but the {{{Blog}}} variable won't.
Using this idea, set up your navigation links like so:
<div id="nav">
<ul>
<li><a href="/Portfolio" {{{ Portfolio }}}>Portfolio</a></li>
<li><a href="/Blog" {{{ Blog }}}>Blog</a></li>
</ul>
</div>
Create the .current class
#nav li a.current {
font-weight: 900;
}
Now the link will be highlighted dependent on the routing call made.