ExtJS MVC, dynamic loading and i18n - extjs

I would like to translate my ExtJS application in different languages. My issue is that I'm using ExtJS MVC framework, and most of my JS files are downloaded dynamically by the framework itself.
The ideal solution (that I thought of) would be to have an extra option in the Ext.Loader (or in my Ext.app.Application) that would define the language to use, and depending on this to automatically download such file as "a.MyClass.fr.js" after loading my "a.MyClass.js" (which would contain an Ext.apply, overriding my string resources). That's probably not available in the ExtJS framework at the moment.
The alternative solution I can see, is to perform a trick on the server-side. First, a cookie would be created on the client, to set to the language. On the server-side, I could catch all the requests to JS files, then if a cookie is set (='fr' for example), I'd combine the requested JS file (MyClass.js) with its i18n's friend (MyClass.fr.js) dynamically on the server and return the result. That would work, but it's really tricky because it implies other things (caching...).
Maybe the best way is to implement the first behavior I described in the ExtJS framework myself...
What do you think? I'm looking for a really clean and neat way of doing it! Thanks :)

I recently struggled with the same problem.
Finding a clean way to do this was quite a challenge - most alternatives were either..
1) Duplicate your code base per locale (WTH)
2) Download localized files overriding each of your components (Maintenance hell? What about the poor translators?)
3) Use/generate a static file containing translations and refer to it (All languages are downloaded? Extra build step to generate it? How do you keep them in synch?)
I tried to get the best of all worlds and ended up with a utility class responsible for:
1) Loading the ExtJS translation files (which basically apply overrides to extjs base components)
2) Loading a locale specific property resourcebundle (specifying which locale to load) from the server.
3) Prototyping String with a translate() method which queries the loaded store (containing the message bundle from the server) and returns the translation based on the value of the string.
This is the gist of things:
Bundle & prototyping:
localeStore.load({
callback : function(records, operation, success) {
// Define translation function (NB! Must be defined before any components which want to use it.)
function translate() {
var record = localeStore.getById(this.valueOf()) ;
if(record === null) {
alert('Missing translation for: ' + this.valueOf()); // Key is not found in the corresponding messages_<locale>.properties file.
return this.valueOf(); // Return key name as placeholder
} else {
var value = record.get('value');
}
return value;
}
String.prototype.translate = translate;
callback.call(); // call back to caller(app.js / Ext.Application), loading rest of application
}
});
As an example from a view:
this.copyButton = Ext.create('Ext.button.Button', {
disabled: true,
text: 'DOCUMENT_LIBRARY_MENU_COPYTO_BUTTON'.translate(),
action: 'openCopyDialog'
});
Bundle on the server (mesages_en.properties):
DOCUMENT_LIBRARY_MENU_COPYTO_BUTTON=Copy file
etc..
Pros:
No-fuss code, 'Your_key'.translate() makes it easy to read and aware that this is a localized string
None/little maintenance overhead (Keeping an override file for each locale? Jesus..)
You only load the locale you need - not the whole shabang.
If you really want to, you could even have your own translation for the ExtJS locale files in the same bundle.
You could write unit tests to ensure that all bundles contain the same keys, thus avoiding orphaned translations later
Cons:
Synchronous - the store must be loaded before your main app starts. I solved this by adding a callback from the utility class which was called once all texts were loaded.
No real-time population of texts.. though I didn't want to make my users overload the server either :P
So far my approach has worked out pretty well for my requirements.
Site load isn't noticeably slower and the bundles (containing ~200 keys/values per bundle) measure out at ~10kb during load.

There is currently no solution so I decided to create my own hack/addon on the Ext.Loader. I uploaded the code on GitHub: https://github.com/TigrouMeow/extjs-locale-loader. It's exactly what I needed and I really hope it will help others as well!

You should first complete your development phase and build your project or use ext-all.js file to I18s translate your UI

see: http://docs.sencha.com/ext-js/4-0/#!/example/locale/multi-lang.html
The appropriate language modifier script (/ext/local/ext-lang-xxx.js) needs to be loaded after ext is loaded (including dynamically loaded classes). In the example above, I would have probably used Ext.Loader.loadScriptFile but they eval a downloaded one directly. The only other thing is that your classes need to be built in different languages or you just use variables and reference the lang-specific variable file.
you could also use a variable in the Loader paths:
var lang='fr';
Loader
{
paths:
{
'Ext': '.',
'My': './src/my_own_folder'+'/'+lang
}

Related

How to merge multiple html files into one with brunch.js

I'm using underscore.js for my templates which are stored in multiple separate XXX_tpl.html files inside sections similar to:
<script type="text/x-template" id="tpl_XXX">
<h1>hi</h1>
</script>
Which I am then using inside backbone.js views as follows:
render: function () {
this.$el.html($('#tpl_XXX').text());
}
I am now using brunch.js build tool which nicely outputs all my libs/js/css code into several optimized files but I am having issue with managing / organizing my templates. How do I make brunch.js build tool to append all *_tpl.html files at the end of index.html? All the examples I am seeing online show how to use brunch.js to merge templates into .js files but I don't yet understand how that works (the templates are a mix of html/js and I lose both access by ID and syntax formatting/highlighting when storing templates in .js files).
Q1. If what I'm doing is right (multiple templates in multiple different .tpl.html files all appended at the end of index.html when built) then how do I make build.js merge all of that?
Q2. If what I'm doing isn't right, what's a better approach to:
have multiple templates that are organized and easily managed
not create additional http requests to pull / all compiled into a single file
have easy access from backbone.js models
want to achieve syntax highlighting in my IDE for the template markup (i.e. no JS string concatenations, etc)
Nice question, but I don't know if you understood how underscore templates should work precisely. Let's try to clear that up first.
Template compilation
An underscore template source is any text with interpolated code. For example:
var myTemplateString = "hello: <%= name %>";
When you want to use that template, you need to compile it into a function first. What? Here's how it works:
var myTemplateFunction = _.template(myTemplateString);
This creates a myTemplateFunction which contains your template logic. In a very simplified, pseudo-code way, you can expect myTemplateFunction to work somewhat like this:
function (context) { return "hello: " + context.name };
So, now you understand why you can call this function and produce a string!
myTemplateFunction({name: 'moe'}) // hello: moe
Using compiled templates
OK, but why do you need to compile it previously? Why not always call directly:
_.template(myTemplateString)({name: 'moe'})
Because compilation can be CPU-intensive. Therefore, it's much faster to use a pre-compiled template. You should not force the user's browser to do it! You should do it for him!
Delivering compiled templates
By now, you understand you don't care about delivering the text of your functions to your client, only the compiled template functions. There are many ways to accomplish that.
Brunch has a bunch of plugins for pre-compiled templates, but apparently none for underscore: http://brunch.io/plugins.html
You can use webpack and it's EJS template loader.
Your code would look something like this:
var myTemplateFunction = require('./template.html')
console.log(myTemplateFunction);
You can also use Grunt and it's underscore template task: grunt-contrib-jst.
Whichever you choose, they will all work similarly: they'll compile your template into a function and you'll be able to use that function. Personally, I recommend learning webpack!

media files converter plugin/component in CakePHP

I am trying to develop a plugin/component that can change the media file format from one to another. Specifically, I need it to convert the "tiff" file to array/single copy of "jpg" image file.
Kindly guide, how I can implement it or is there any kind of tutorial link from where either I can download it or take some help to develop it. Thanks in advance.
We did this in our CMS (built on CakePHP 1.2; sorry if there are any significant discrepancies I'm not aware of) using a behaviour. That makes the controller logic very easy (in fact we use a baked controller without any modification at all).
Unfortunately TIFF isn't a supported file format in GD (the default image manipulation library in PHP). You'll need to use ImageMagick or an equivalent tool to do the actual conversion itself, but the logic for implementing it in your CakePHP project won't be any different to what I describe here.
The behaviour (in our case) was used to generate images as thumbnails as well as page resolution and to convert the uploaded file format into JPEG.
In its beforeSave() method it checked that data was specified (and that there was no error), and then pulled the tmp_name value from the posted data (and removed the posted data object).
In its afterSave() method, it actually performed the image conversion task itself (putting the generated images in the expected location on disk), then updated any foreign keys on extended models with the uploaded image's ID. We do this in the afterSave() operation so we have a database ID to use to name the files on disk.
In its afterDelete() method we unlink the files on disk.
Using the behaviour in the model is as simple as telling the model (where ContentImage is the name of the behaviour):
var $actsAs = array('ContentImage');
Although we also use the model to define the output directory since we had a few models that implemented the behaviour, and it felt like the right thing to do, e.g. in the model:
function getThumbnailDir() {
return WWW_ROOT.'img'.DS.'upload'.DS.'thumb';
}
and in the behaviour itself the output path becomes:
$Model->getThumbnailDir().DS.$Model->id.'.jpg'

CakePHP: Best way to handle asset minification / combining?

I know that it's good to minify assets because doing so reduces their file size, which reduces the amount of time it takes for the page to load. I also know that it's good to combine assets because doing so reduces the number of HTTP requests, which, once again, reduces the amount of time it takes for the page to load. This is important because there are still people on dial-up and mobile devices often don't have a fast connection.
The thing I'm struggling with is how to easily add asset minification and combining into my workflow. I develop locally using CakePHP and I use Git for version control. When it's time to go live, I ssh into the server hosting the live site and merge in the latest commit.
Here's how I would go about rolling my own solution (only accounts for minification and is not tested!):
1.) My development environment's "app/Config/core.php" file would always have its "debug" level set to a value greater than 0 and the production environment's would always be at 0.
2.) On the file system, all CSS and JavaScript would be stored in external files, like so:
app/webroot/css/used-site-wide.css
app/webroot/css/used-on-a-few-pages.css
app/webroot/css/used-on-one-page.css
app/webroot/js/used-site-wide.js
app/webroot/js/used-on-a-few-pages.js
app/webroot/js/used-on-one-page.js
3.) Rather than using echo $this->Html->script(array('used-on-a-few-pages', 'used-on-one-page'), array('inline' => false)); in the view file, I would use this:
Configure::write('external_js', array('used-on-a-few-pages'));
Configure::write('inline_js', array('used-on-one-page'));
4.) Rather than using echo $this->fetch('script'); in the layout file, I would use this:
if (Configure::read('external_js') !== null) {
$external_js = Configure::read('external_js');
if (Configure::read('debug') == 0) {
foreach ($external_js as &$external_js_filename) {
$external_js_filename .= '-min';
}
}
echo $this->Html->script($external_js);
}
if (Configure::read('inline_js') !== null) {
$inline_js = Configure::read('inline_js');
if (Configure::read('debug') == 0) {
foreach ($inline_js as &$inline_js_filename) {
$inline_js_filename .= '-min';
}
}
echo "\n<script type=\"text/javascript\">\n\t/* <![CDATA[ */";
foreach ($inline_js as $inline_js_filename) {
echo file_get_contents(JS . Configure::read('inline_js') . '.js');
}
echo "\n\t/* ]]> */\n</script>";
}
5.) Finally, I would set up Git to create the minified assets whenever a commit is made.
Using this setup, I would be working with the unminified assets in development and the minified ones in production. The thing is, I don't want to re-invent the wheel if I don't have to. I believe that re-inventing the wheel should only be done if you're solving a problem that is both significant and uncommon.
How do you all handle this?
Thanks!
If you're after something more simple than Mark Story's AssetCompress plugin, check this out:
https://github.com/joshuapaling/CakePHP-Combinator-Plugin
It will combine and minify JS and CSS files, and you can easily make it only combine/minify when debug mode is 0 (there's a example of that in the .markdown on GitHub). It uses the date modified of the included JS/CSS files to decide when it needs to make a new cached file.
It isn't nearly as full-featured as Mark Story's plugin, but it is simple, does the job, and it should only take you 10 or 15 mins to set up.
I've recently stumbled upon this situation myself and I have tried the (now deprecated) Combinator plugin which uses jsmin that isn't very reliable. So then I tried Mark Story's plugin which is way to complicated and it doesn't even do the builds automagically when files have changed, you have to do some cake bake every time you wish to create the combined and minified files.
Therefore I have wrote my own simple helper which you can check out here: https://github.com/Highstrike/cakephp-compressor. Everything is explained in the readme file and it's very easy to use, taking advantage of google closure for js minification. It also handles HTML minification.
It's even easier than maurymmarques's solution because it's just a helper file, no controller or configuration needed.
I'm hoping it will help someone.
You also can check this plugin https://github.com/maurymmarques/minify-cakephp
It's very easy to install and configure.

Changing Extjs 4 default MVC folder structure

I am writing an application that has both extjs and sencha touch version. my current folder structure is like
root
...extjs4application
......app
.........model
.........store
.........view
.........controller
...senchatouch2application
......app
.........model
.........store
.........view
.........controller
model and store are similar in both application so i need to organize my folder structure in such a way that both application could share single/common model and store folders. What could be the possible solution? Please help
Based on a cursory glance over the source for Ext.app.Application it looks like it's possible to change the paths without overriding anything.
The path to the app folder is controlled by the appFolder config which defaults to "app." You can change this as you see fit but it's not necessary to do so.
Also included in the application class is an undocumented config called paths which is an object containing simple (key, value) pairs. Example:
paths: {
"Ext": "/path/to/Ext",
"Ext.ux": "/path/to/Ext/ux"
// etc...
}
The Ext.app.Application constructor checks for the presence of the paths config and calls Ext.Loader#setPath for each entry. You can read more about Ext.Loader at Sencha Docs
I don't like including disclaimers with my answers, but in this case I feel I should: I haven't personally used this to create an application so I can't completely vouch for its correctness, but it should be a start. If this should fail, you may need to override or extend the library classes to suit your needs (probably either Ext.app.Application or Ext.Loader).

How to enumerate images included as "Content" in the XAP?

I'm including a number of images as "Content" in my deployed XAP for Mango.
I'd like to enumerate these at runtime - is there any way to do this?
I've tried enumerating resources like:
foreach (string key in Application.Current.Resources.Keys)
{
Debug.WriteLine("Resource:" + key);
}
But the images aren't included in the list. I've also tried using embedded resources instead - but that didn't help. I can read the streams using Application.GetResourceStream(uri) but obviously I need to know the names in order to do this.
This is no API baked in to WP7 that allows you to enumerate the contents of the Xap. You need to know the name of the content items before you can retreive them.
There probably is some code floating around somewhere that is able to sniff out the Zip catalog in the XAP however I would strongly recommend that you don't bother. Instead include some sensible resource such as an Xml file or ResourceDictionary that lists them.
Having found no practical way to read the Content files from a XAP I build such a list at design time using T4.
See an example at https://github.com/mrlacey/phonegap-wp7/blob/master/WP7Gap/WP7Gap/MainPage.xaml.cs
This seems the right way to go as:
a) I'd rather build the list once at design time rather than on every phone which needs the code.
and
b) I shouldn't ever be building the XAP without being certain about what files I'm including anyway.
Plus it's a manual step to set the build action on all such files so adding a manual step to "Run Custom Tool" once for each build isn't an issue for me.
There is no way to enumerate the files set as "Content".
However, there is a way to enumerate files at runtime, if you set your files as "Embedded Resource".
Here is how you can do this:
Set the Build Action of your images as "Embedded Resource".
Use Assembly.GetCallingAssembly().GetManifestResourceNames() to
enumerate the resources names
Use
Assembly.GetCallingAssembly().GetManifestResourceStream(resName)
to get the file streams.
Here is the code:
public void Test()
{
foreach (String resName in GetResourcesNames())
{
Stream s = GetStreamFromEmbeddedResource(resName);
}
}
string[] GetResourcesNames()
{
return Assembly.GetCallingAssembly().GetManifestResourceNames();
}
Stream GetStreamFromEmbeddedResource(string resName)
{
return Assembly.GetCallingAssembly().GetManifestResourceStream(resName);
}
EDIT : As quetzalcoatl noted, the drawback of this solution is that images are embedded in the DLL, so if you a high volume of images, the app load time might take a hit.

Resources