i18next best practice - javascript

I've successfully implemented i18next, which by the way is a great library! Though I'm still in search for the "best practice". This is the setup I have right now, which in general I like:
var userLanguage = 'en'; // set at runtime
i18n.init({
lng : userLanguage,
shortcutFunction : 'defaultValue',
fallbackLng : false,
load : 'unspecific',
resGetPath : 'locales/__lng__/__ns__.json'
});
In the DOM I do stuff like this:
<span data-i18n="demo.myFirstExample">My first example</span>
And in JS I do stuff like this:
return i18n.t('demo.mySecondExample', 'My second example');
This means I maintain the English translation within the code itself. I do however maintain other languages using separate translation.json files, using i18next-parser:
gulp.task('i18next', function()
{
gulp.src('app/**')
.pipe(i18next({
locales : ['nl','de'],
output : '../locales'
}))
.pipe(gulp.dest('locales'));
});
It all works great. The only problem is that when I've set 'en' as the userLanguage, i18next insists on fetching the /locales/en/translation.json file, even though it doesn't contain any translations. To prevent a 404, I currently serve an empty json object {} in that file.
Is there a way to prevent loading the empty .json file at all?

Maybe I'm missing something here but couldn't you simply do this:
if (userLanguage != 'en') {
i18n.init({
lng : userLanguage,
shortcutFunction : 'defaultValue',
fallbackLng : false,
load : 'unspecific',
resGetPath : 'locales/__lng__/__ns__.json'
});
}
That way your script i18n wouldn't be initialized unless you actually needed the translation service.

i18next-parser author here, I will explain how I use i18next and hopefully it will help:
1/ I do not use defaultTranslation in the code. The reason is that it doesn't belong in the code. I understand the benefit of having the actual text but the code can get bloated quickly. The difficult part consists in defining intelligible translation keys. If you do that, you don't really need the defaultTranslation text anymore. The translation keys are self-explainatory.
2/ If you have a 404 on the /locales/en/translation.json, then probably that you don't have the file in your public directory or something similar. With gulp you can have multiple destination and do dest('locales').dest('public/locales') for instance.
3/ If there is no translation in the catalog, make sure you run the gulp task first. Regarding populating the catalog with the defaultTranslation you have, it is a tricky problem to solve with regexes. Think of this case <div data-i18n="key">Default <div>translation</div></div>. It needs to be able to parse the inner html and extract all the content. I just never took the time to implement it as I don't use it.

See http://i18next.com/pages/doc_init.html under "whitelist languages to be allowed on init" (can't fragment link on those docs...):
i18n.init({ lngWhitelist: ['de-DE', 'de', 'fr'] });
Only specified languages will be allowed to load.
That should solve your problem. Though I suppose a blacklist would be even better.

Related

how to handle javascript that only runs on certain pages when bundled together

When working on small client sites, I often end up working with a main.js file that includes a bunch of jQuery plugins and small toggle functionality. Some of these code snippets are only relevant on certain pages, but ends up bundled together in one main.min.js file.
My question is, how do people write the individual code snippets in order to only execute that code when the correct page is being rendered?
Here's an example: Let's say I have a page with a search input field. This input is hooked up with jQuery autocomplete in order to show search suggestions as the user types. the code in main.js could look something like this:
var data = [
{
value: 'some value',
data: 'some data'
},
{...}
]
$('#autocomplete').autocomplete({
lookup: data,
lookupLimit: 10,
minChars: 3,
});
This code is only useful on the template that has that input field, but as main.js contains a bunch of other smaller bits like this that are useful globally and on other pages, the whole file is loaded on every pageview. What strategy should I use to only execute that piece of code when the page needs it?
I though of a few ways my self:
Check if the DOM-element (in this case #autocomplete) exists.
Check if the URL is == '/page-with-autocomplete'.
Use a class on , and check for that class i n order to run the script.
Other ideas? Any standard way to do this sort of thing? Anything considered a "best practice"?
Stick your JS in an if block and check for the unique DOM element on the page you want the script to run.
Although you can't just do:
if ( $('#my-el') ) {}
You have to check if the element has a length, like:
if ( $('#my-el').length ) {}

Pug Conditional to Include All Subdirectories of a File Path

Simple problem that I must be overcomplicating...
I want to include a script file for any URL that contains /edit-post. Problem is, my editor URL will always be suffixed with a post title like this: /edit-post/some-post-title, therefore my Pug conditional never evaluates to true.
In other words, I'm looking for a conditional statement that will be true in both the following example conditions:
/edit-post/some-post-title
/edit-post/an-even-better-post-title
I have access to the path url variable already, and my conditionals are working fine when it's an exact match, so I'm trying to extend that to suffixed subdirectories. Here's what I'm starting with:
if path === '/edit-post'
//my script file
Is there maybe a way to include a regex expression or possibly some sort of "contains" statement in Pug?
I was thinking something like "/edit-post.*" or "edit-post[s/S]". Also got desperate and tried using .indexOf() which all you folks smarter than me will already know doesn't work in Pug templates!
Maybe there's a better way to achieve this altogether? Searched for hours. Tried a hundred combos. Pug's documentation is sparse. Plz send help! :)
Side note: I did find this neat little Pug/Jade conditional doc I've been using as a tester. Might be of use. http://learnjade.com/tour/conditionals/
Thanks in advance!
Actually I recommend You:
1) to rethink Your routing
2) create /layouts/admin for admin panel layout
3) create /admin/posts/edit view for edit purposes
But for Your question here is straightforward example:
router.get('/post/:slug/edit', (req, res) => {
Post
.findOne({slug: req.params.slug})
.exec((err, post) => {
res.render('post', {includeEdit: true, post});
});
});
Post is mongoose model
and in Your view file:
if includeEdit === true
include partials/edit

How can I combine my JavaScript files and still have my callbacks wait for a ready state?

I have lots of functions and event handlers that are split across multiple javascript files which are included on different pages throughout my site.
For performance reasons I want to combine all of those files into 1 file that is global across the site.
The problem is I will have event handlers called on elements that won't necessarily exist and same function names.
This is an example of a typical javascript file...
$(document).ready(function(){
$('#blah').keypress(function(e){
if (e.which == 13) {
checkMap();
return false;
}
});
});
function checkMap() {
// code
}
function loadMap() {
// code
}
I would need to seperate this code into an object that is called on that specific page.
My thoughts are I could re-write it like this:
(function($) {
$.homepage = {
checkMap: function(){
// code
},
loadMap: function(){
//code
}
};
})(jQuery);
And then on the page that requires it I could call $.homepage.checkMap() etc.
But then how would I declare event handlers like document.ready without containing it in it's own function?
First of all: Depending on how much code you have, you should consider, if serving all your code in one file is really a good idea. It's okay to save http-requests, but if you load a huge chunk of code, from which you use 5% on a single page, you might be better of by keeping those js files separated (especially in mobile environments!).
Remember, you can let the browser cache those files. Depending on how frequent your code changes, and how much of the source changes, you might want to separate your code into stable core-functionality and additional .js packages for special purposes. This way you might be better off traffic- and maintainance-wise.
Encapsulating your functions into different objects is a good idea to prevent unnecessary function-hoisting and global namespace pollution.
Finally you can prevent calling needless event handlers by either:
Introducing some kind of pagetype which helps you decide calling only the necessary functions.
or
checking for the existence of certain elements like this if( $("specialelement").length > 0 ){ callhandlers}
to speed up your JS, you could use the Google Closure Compiler. It minifies and optimizes your code.
I think that all you need is a namespace for you application. A namespace is a simple JSON object that could look like this:
var myApp = {
homepage : {
showHeader : function(){},
hideHeader : function(){},
animationDelay : 3400,
start : function(){} // the function that start the entire homepage logic
},
about : {
....
}
}
You can split it in more files:
MyApp will contain the myApp = { } object, maybe with some useful utilities like object.create or what have you.
Homepage.js will contain myApp.homepage = { ... } with all the methods of your homepage page.
The list goes on and on with the rest of the pages.
Think of it as packages. You don't need to use $ as the main object.
<script src="myapp.js"></script>
<script src="homepage.js"></script>
<-....->
<script>
myApp.homepage.start();
</script>
Would be the way I would use the homepage object.
When compressing with YUI, you should have:
<script src="scripts.min.js"></script>
<script>
myApp.homepage.start();
</script>
Just to make sure I've understood you correctly, you have one js file with all your code, but you want to still be in control of what is executed on a certain page?
If that is the case, then the Terrific JS framework could interest you. It allows you to apply javascript functionality to a module. A module is a component on your webpage, like the navigation, header, a currency converter. Terrific JS scans the dom and executes the js for the modules it finds so you don't have to worry about execution. Terrific JS requires OOCSS naming conventions to identify modules. It's no quick solution to your problem but it will help if you're willing to take the time. Here are some more links you may find useful:
Hello World Example:
http://jsfiddle.net/brunschgi/uzjSM/
Blogpost on using:
http://thomas.junghans.co.za/blog/2011/10/14/using-terrificjs-in-your-website/
I would use something like YUI compressor to merge all files into one min.js file that is minified. If you are looking for performance both merging and minifiying is the way to go. http://developer.yahoo.com/yui/compressor/
Example:
Javascript input files: jquery.js, ads.js support.js
run yui with jquery.js, ads.js, support.js output it into min.js
Javascript output files: min.js
then use min.js in your html code.

Call a JavaScript function from C++

I have a CDHTMLDialog, with which I have 2 HTML pages and a .js file with a few fairly simple functions.
I would like to be able to call one of the JS functions from my program with a simple data type passed with it. e.g. MyFunc(int). Nothing needs to be returned.
I would appreciate any guidance on how I go about this,
thanks.
Edit: Thanks to CR for his answer, and everyone else who submitted there ideas too.
Something a little like this worked in the end (stripped a little error handling from it for clarity):
void callJavaScriptFunc(int Fruit)
{
HRESULT hRes;
CString FuncStr;
CString LangStr = "javascript";
VARIANT vEmpty = {0};
CComPtr<IHTMLDocument2> HTML2Doc;
CComPtr<IHTMLWindow2> HTML2Wind;
hRes = GetDHtmlDocument(&HTML2Doc);
hRes = HTML2Doc->get_parentWindow(&HTML2Wind);
if( Fruit > 0 )
{
FuncStr = "myFunc(808)"; // Javascript parameters can be used
hRes = HTML2Wind->execScript(FuncStr.AllocSysString(), LangStr.AllocSysString(), &vEmpty);
}
}
Easiest approach would be to use the execScript() method in the IHTMLWindow2 interface.
So you could get the IHTMLDocument2 interface from your CDHTMLDialog by calling GetDHtmlDocument, then get the parentWindow from IHTMLDocument2. The parent window will have the IHTMLWindow2 interface that supports execScript().
There might be an easier way to get the IHTMLWindow2 interface from your CDHTMLDialog but I'm used to working at a lower level.
the SpiderMonkey library can "Call a JavaScript function from C++", please refer to
http://egachine.berlios.de/embedding-sm-best-practice/ar01s02.html#id2464522
but in your case, maybe this is not the answer.
To give you a hint - javascript injection in server-side-technologies is usually performed through bulk-load at startup (GWT) or injected when the HTML is generated and served each post-back (ASP.NET).
The important point of both approaches is that they inject the javascript calls somewhere in the page (or in a separated .js file linked in the HTML in case of GWT) when generating the HTML page.
Even if you're on win development (looks like it since you're on MFCs) it might be the case that you have to insert your js method call in the HTML and then load (or reload if you wish to interact with the html from your MFC app) the HTML file in your CHTMLDialog.
I don't see any other way of achieving this (maybe I am just not aware of some suitable out-of-the-box functionality) other than editing your HTML and (re)loading it - which is pretty convenient and workable if you have to call your js method once off or just inject some kind of event-handling logic.
Might be a bit of a pain if you have to interact with the page from your MFC app. In this case you have to re-generate your HTML and reload it in your CHTMLDialog.
Either way you can simply have some kind of placeholder in your HTML file, look for that and replace with your javascript code, then load the page in your CHTMLDialog:
onclick="__my_Javascript_Call_HERE__"

Best Practice for Storing JSON Data That Will Be Passed to jQuery Plugin

I'm looking for the "best practice" as to where the JSON should be stored if it's just a string array. Should it be stored in a variable in a script block in the HTML page? Should it be stored in a JavaScript file outside of the HTML for separation? Or should it be stored in the plugin itself?
If it should be an external js file, what's the "best practice" naming scheme for the file? I know the accepted jQuery plugin name is jquery.plugin.js or jquery.plugin-min.js (for the minified file).
Depends, if you need the JSON right away you can store it anywhere to get it executed:
<script> var myJsonObj = { ... }; </script>
If it's a lot of Data and you don't need the data right away, you can always make an ajax call to a file named something like "data.json".
For naming the plugin name, well it's really up to you, but yeah I believe jquery.pluginname.js is the standard way of doing it.
I'll second sktrdie to add the extension .json for a file like this. A gotcha that I ran across when first playing with JSON is that a JSON string is not a valid JavaScript File.
For example, If I call a file with this content:
{
'foos': 'whatever',
'bar': false,
'items': [1,2,3]
}
as the src of a <script> tag, I get this error:
Error: invalid label
Line: 2, Column: 1
Source Code:
'foos': 'whatever',
In the past I've actually hidden JSON strings in <divs> or spans like this:
<div id="jsonStorage" style="display:none">
{'foos': 'whatever','bar': false,'items': [1,2,3]}
</div>
I've also used hidden form fields for this.
If it's part of the plugin, i.e. default config, I'd store it in the plugin file itself. If it's an external config for the plugin, then it depends. It might make sense to store it in a variable in the HTML, i.e.
<script>
var myConfig = {
"foo" : "bar"
};
</script>
This could especially be the case if you need any of the JSON to be generated by your back-end code.
Really, the answer is "it depends" -- can you give more details?

Categories

Resources