Private Bower

05 May 2014 | By Alex Young | Comments | Tags bower build-tools

Private bower

If you’ve ever wanted to set up a private Bower repository, private-bower (GitHub: Hacklone/private-bower, License: MIT, npm: private-bower) by Barna Tóth might be what you’re looking for.

You can install it with npm install -g private-bower, and then run it with private-bower. It accepts some command-line options to change what port it listens on, but all you really need to do is add some lines to your .bowerrc file:

{ "registry": "http://localhost:5678" }

That changes the repository Bower will use. Now whenever Bower attempts to fetch a package, it will check your private server.

private-bower itself will serve packages if they’ve been downloaded, otherwise it will fall over to the public repository. It’s built using Express, and the author has put the API methods in the readme so you can see how it works.

Cammy and n3-charts

02 May 2014 | By Alex Young | Comments | Tags animations angular d3
Cammy's demo.

Cammy (GitHub: lorem–ipsum / cammy, License: MIT) by Sébastien Fragnaud (“lorem–ipsum”) is an orthogonal projection camera built on AngularJS and D3.js. The demo is completely gorgeous, so I was compelled to waste a good 15 minutes playing with it. If you look at the source you can see how the Angular directives are used to hook up the UI controls with the renderer.

Sébastien also wrote n3-charts (GitHub: n3-charts / line-chart):

$scope.options = {
  axes: {
    x: {
      key: 'x',
      labelFunction: function(value) { return value; },
      type: 'linear',
      tooltipFormatter: function(x) { return x; }
    },
    y: { type: 'linear' },
    y2: { type: 'linear' }
  },
  series: [{
    y: 'value',
    color: 'steelblue',
    thickness: '2px',
    type: 'area',
    striped: true,
    label: 'Pouet'
  }, {
    y: 'otherValue',
    axis: 'y2',
    color: 'lightsteelblue'
  }],
  lineMode: 'linear',
  tension: 0.7
};

Velocity.js

01 May 2014 | By Alex Young | Comments | Tags animations jquery
From the Velocity Playground teaser.

Julian Shapiro sent in Velocity.js (GitHub: julianshapiro / velocity, License: MIT), a performance-focused animation library that is an alternative to jQuery’s $.animate method. If you’re struggling with CSS animations and would prefer to use JavaScript, then Velocity may help. Judging by the project’s Playground teaser, it seems like it’s more ambitious than just supporting basic animations, however.

Basic animation works as you’d expect if you’ve used $.animate:

$('div').velocity({ top: 50 });

You can supply additional arguments:

$('div').velocity({ opacity: 1 }, { duration: 1000 });

Easing and queues are also supported:

$('div').velocity({
  borderBottomWidth: ['2px', 'spring' ],
  width: '100px',
  height: '100px'
}, {
  easing: 'easeInSine'
});

The documentation has more examples. The source has some useful comments about how the project works, and why it’s fast:

JavaScript and jQuery are falsely conflated. JavaScript animation, which Velocity uses, is fast; it’s jQuery that’s slow. Although Velocity is a jQuery plugin, it uses its own animation stack that delivers its performance through two underlying design principles: 1) synchronize the DOM → tween stack to minimize layout thrashing, and 2) cache values to minimize the occurrence of DOM querying.

There’s also a post about Velocity on the David Walsh Blog, and the author is promising more features and tests by May 5th.

Node Roundup: Who's Hiring, Melkor, Isosurface

30 Apr 2014 | By Alex Young | Comments | Tags node modules npm jobs graphics es6 generators

npm: Who’s Hiring?

I noticed npm’s blog had a post about who’s hiring in the Node community. There’s a new Who’s Hiring page on npmjs.org, that lists Voxer, The Financial Times, Branding Brand, and Dash.

Melkor

More people are getting interested in Koa.js now, so it’s good to start collecting example applications that use it. Ramesh Nair sent in Melkor (License: MIT), a wiki that uses Waigo which is built on Koa.js, and Git as the storage system.

You can see generators used right away in index.js:

exports.init = function*(folder, options) {
  debug('Folder: ' + folder);

  yield* waigo.init({
    appFolder: path.join(__dirname, 'src')
  });

  var App = waigo.load('application');

  yield* App.start({
    postConfig: function(config) {
      config.startupSteps.unshift('wiki');

      config.middleware.push({ id: 'methodOverride' });

      debug('Port: ' + config.port);
      config.port = options.port;
      config.baseURL = 'http://localhost:' + config.port;

      config.staticResources.folder = '../public/build';

      config.wikiTitle = options.title;
      config.wikiFolder = folder;
    }
  });

  return App;
};

The controllers also make heavy use of yield, so things like form validation and template rendering have a synchronous feel.

Isosurface

Isosurface

Isosurface (GitHub: mikolalysenko / isosurface, License: MIT, npm: isosurface) by Mikola Lysenko is a set of isosurface polygonizer algorithms.

Here’s an example:

var isosurface = require('isosurface');

var mesh = isosurface.surfaceNets([64,64,64], function(x,y,z) {
  return x*x + y*y + z*z - 100;
}, [[-11,-11,-11], [11,11,11]]);

I found this module (and the impressive demo) while looking through Mikola’s graphics related modules on npmjs.org/~mikolalysenko.

Using jQuery UI with AMD

29 Apr 2014 | By Alex Young | Comments | Tags jqueryui jquery libraries amd

As of version 1.11 you can use AMD with jQuery UI. RequireJS will work, or any other AMD compatible loader.

That means you can do this:

require(['jquery-ui/autocomplete'], function(autocomplete) {
  autocomplete({ source: ['One', 'Two', 'Three'] }, '<input>')
    .element
    .appendTo('body');
});

Doesn’t that look a lot better than what we had before (script tags, monolithic $)? The old Autocomplete style would have used $(selector).autocomplete, which is low on syntax but high in coupling. Even though Autocomplete’s API is always going to be coupled to jQuery, I prefer the idea of being able to load it in a modular way, and to potentially be able to inject a different dependency.

The jQuery/jQuery UI stack is still more monolithic than some of the newer MVVM or component-based solutions, but this may help you use some AMD-based techniques with your existing jQuery UI projects.

Also, I know there’s always someone who wants to point out how great Dojo is, so here’s Dojo’s AMD post from 2011.

GestureKit

28 Apr 2014 | By Alex Young | Comments | Tags mobile libraries

GestureKit

GestureKit (GitHub: RoamTouch / gesturekit.js, License: Apache 2.0) by Guille Paz and RoamTouch is an SDK for creating and recognising gestures in mobile interfaces. There’s an open source client-side library that has an event-based API for responding to gestures.

Gestures can be recorded on a mobile device and then used on different platforms. The project’s homepage has examples for Android, iOS, and JavaScript.

GestureKit is backed by a service for storing metrics on gestures. Applications can work offline though, the underlying data is cached on startup.

Right now it seems like the server part of the project is closed source, but the FAQ states that part of the API will be opened, and they’re looking for contributors:

GestureKit has a Github repos for community pushes that you will be able to contribute. We are planning to open a part of the API, specially the actions performed with the gestures and a plugin interface to integrate new stuff to the service.

The thing I like about this project is the idea that you can draw shapes to trigger specific functionality. The example is a music app that uses two arrows to mean skip forward or back.

Managing Node.js Callback Hell, this Considered Harmful

25 Apr 2014 | By Alex Young | Comments | Tags node tutorials

Marc Harter, who is working with me on Node.js in Practice, recently published Managing Node.js Callback Hell:

Callback hell is subjective, as heavily nested code can be perfectly fine sometimes. Asynchronous code is hellish when it becomes overly complex to manage the flow. A good question to see how much “hell” you are in is: how much refactoring pain would I endure if doAsync2 happened before doAsync1? The goal isn’t about removing levels of indentation but rather writing modular (and testable!) code that is easy to reason about and resilient.

The example he uses is nested asynchronous file operations, with a counter to determine completion. This is compared to a version that uses async, and another that uses promises with q.

The post was originally published on StrongLoop’s blog, here: Managing Node.js Callback Hell with Promises, Generators and Other Approaches. StrongLoop’s blog is worth subscribing to if you’re a Node developer – it has general tutorials and coverage of interesting npm modules.

Tom Boutell sent in a blog post about OO in JavaScript, called “this” considered harmful (sometimes):

Most JavaScript implementations of “classes” do have certain features in common. They rely on the “this” keyword to refer to the current object; after all, it’s built into the language. They provide a convenience function to implement subclassing, because it’s tricky to get right, especially in older browsers. And they have a really tough time handling callbacks in methods, because “this” ceases to refer to the object you expect.

Tim Oxley posted some comments discussing how to use Function.prototype.bind as well.

Query IndexedDB Like MongoDB

24 Apr 2014 | By Alex Young | Comments | Tags indexeddb storage apis

Kent Safranski sent in Indexed, a library that wraps around IndexedDB with a friendly MongoDB-inspired API.

You can insert objects like this:

indexed('myDB').insert({
  name: 'John Doe'
  email: 'jdoe@email.com'
}, function(err, data) {
  if (err) {
    console.error(err);
  } else {
    console.log(data);
  }
});

And fetch them again with find:

indexed('myDB').find({
  _id: 28972387982
}, function(err, data) {
});

You can even use MongoDB-style operators:

indexed('myDB').find({
  someNumber: { $gt : 25 }
}, function(err, data) {
});

You’ll probably like this if you work with databases in Node. It’s actually part of a larger project called Riggr, a framework based around RequireJS, Knockout and jQuery. Although I think indexed.js is cool enough that it should be a separate module, rather than being bundled in with Riggr.

Kent wrote a detailed blog post about indexed.js here: Indexed: Query IndexedDB Like Mongo.

Node Roundup: npm-pkgr, Promised Land, Stash.js

23 Apr 2014 | By Alex Young | Comments | Tags node modules npm caching

npm-pkgr

npm-pkgr (GitHub: vvo / npm-pkgr, License: ISC, npm: npm-pkgr) by Vincent Voyer caches npm install results based on your package.json and npm-shrinkwrap.json files.

Depending on your set up, this should reduce the time taken to deploy Node projects. The basic usage is npm-pkgr instead of npm install, and npm-pkgr --production to use npm-shrinkwrap.json.

Promised Land

Promised Land (GitHub: FredyC / promised-land, License: MIT, npm: promised-land) provides a way of wrapping modules with an EventEmitter that allows you to use promises across your application.

For example, let’s say you’ve got a module that connects to a database. Once the connection is ready, it does this:

var Land = require('promised-land');
Land.emit('database:connected', db);

Any other module that needs to know when the database is ready can now do so with promises:

var Land = require('promised-land');
Land.promise('database:connected').then(function(db) {
  doSomethingWithDatabase();
});

It also allows events to be repeated, in a stream-like way:

Land.stream('database:row').onValue(function(val) {
  doSomethingRepeatedly();
});

The project has tests and more examples in the readme.

Stash.js

Stash.js (GitHub: tadeuzagallo / stash.js, License: MIT, npm: stash.js) by Tadeu Zagallo is a multilayer cache manager. That means you can define and use different storage systems for caching, based on “drivers”.

So far the author has added a driver for ephemeral storage and localStorage support for browsers.

Cache items have methods for determining cache miss, locking, and setting values:

var stash = new Stash.Pool();
var item = stash.getItem('my/key/path');
var data = item.get();

if (item.isMiss()) {
  item.lock();
  data = 'example';
  item.set(data, cacheDuration);
}

What lock does here is dependent on the cache policy. There are four policies that are explained in the readme.

DOM4 Elements, ng-Fx

22 Apr 2014 | By Alex Young | Comments | Tags libraries dom queryAll

DOM4 Elements

DOM4 Elements (GitHub: barberboy / dom4-elements, License: MIT) by Ben Barber is a polyfill for document.query and document.queryAll, from the DOM Level 4 ParentNode interface.

Using document.queryAll returns a new class called Elements, which extends Array.prototype so you can do things like document.queryAll('.classname').map(fn).

Browser support is documented by Testling, and the project includes a small test suite.

ng-Fx

ng-Fx (GitHub: Hendrixer/ng-Fx, License: MIT) by Scott Moss is an AngularJS version of Animate.css.

You can use classes to apply effects, then directives to control their behaviour:

<ul ng-init="foods=['apple', 'chips', 'muffin']">
  <li class='fx-fade-down fx-easing-bounce' ng-repeat="food in foods">
    
  </li>
</ul>

Some options are controlled by classes where I expected to see directives – I think the author has tried to use CSS as much as possible rather than using JavaScript to write out style tags to do the necessary styling. For example: fx-speed-800 compared to ng-fx-speed="800". A hypothetical ng-fx-speed could accept any speed rather than predefined ones.

Ripple.js

21 Apr 2014 | By Alex Young | Comments | Tags data-binding mmvm libraries

Ripple.js (GitHub: ripplejs / ripple, License: MIT) from Segment.io is a library for templating and data binding. It’s based around templates, and has a chainable API, allowing plugins to be added to components:

var Person = ripple('<div>{{name}}</div>')
  .use(events)
  .use(each)
  .use(dispatch);

var person = new Person({
  name: 'Tom'
});

person.appendTo(document.body);

The plugins can add directives in a similar way to AngularJS: take a look at each, for example.

There’s a JSFiddle Markdown example that uses a template embedded in a script element, and two plugins: ripple.events and ripple.markdown. The plugins allow changes to be observed on the textarea and Markdown is set to render using a directive on a div element.

One of the features that I like is the pattern for view composition. Views can be embedded, and data will still be synced when it changes.

Also, the library seems well tested, so you can get more usage ideas from the tests.

Live Reloading Chrome Apps, Chrome Extensions with React, sendMessage Tutorial

18 Apr 2014 | By Alex Young | Comments | Tags chrome extensions browsers

Live Reloading Chrome Apps

The Chrome Store itself is a pile of rubbish apps with very few exceptions.

Working with Chrome extensions and apps is pleasant enough in some ways – the JavaScript APIs are generally intuitive, and you can make native-feeling UIs without too much effort. However, the development experience feels a little bit dated and painful in places.

Konstantin Raev sent in Live Reloading Chrome Apps, an article about using Gulp with Chrome apps. It shows how live reloading isn’t as easy as it could be, and how to fix it. There’s also a full example on GitHub: bestander / clock-chrome-app-livereload-example.

Creating Chrome Extensions with React

Brandon Tilley sent in Creating Chrome Extensions with React:

If you’re into client-side web development to any extent, you’ve probably heard of Facebook’s React library. I was working on a Chrome extension, and decided to see how well React fit in to the development I was doing.

He also uses Browserify as well, which I’m interested in because I tried using RequireJS for sharing code between Chrome extensions and Firefox add-ons, and I struggled to get it to work in Firefox. My Firefox add-ons are using the Jetpack SDK rather than the old XUL way.

Passing data around in Chrome extensions

Passing data around in Chrome extensions by Erica Salvaneschi is an introduction to using chrome.runtime.sendMessage and addListener:

We wanted to get data from the current web page and then use it to populate a form that appears in a new window. It was easy to create a context menu item that triggered an event, but sending data based on the current page to the new window wasn’t obvious.

You might find this useful if you’re just getting into Chrome extensions and want more concrete examples than what Google provides.

Browser Synths with Code Studio

17 Apr 2014 | By Alex Young | Comments | Tags node modules audio
Code Studio running polytropon.

Code Studio (GitHub: substack / code-music-studio, License: MIT, npm: code-music-studio) by substack is a tool for “designing musical algorithms”. It provides a browser-based interface that allows you to return functions that manipulate amplitudes. You can experiment and share sounds using studio.substack.net, or install it with npm and run it locally.

If you want to find some code to play with, look at substack’s Twitter account, or /-/recent.

The audio API is based on baudio, which works by accepting a function that takes a time value and returns an amplitude value between -1 and 1.

That means you can generate a sound just by running something like this:

return function(t) {
  return sin(441);
  function sin(x) { return Math.sin(2 * Math.PI * t * x); }
}

That example creates a function called sin that generates values for a given pitch. Code Studio will render an oscilloscope so you can visualise the output as well as hear it. The waveform is rendered using amplitude-viewer, another module by substack that creates graphs with SVG.

If you’re interested in the server-side portion of the code, then take a look at bin/cmd.js. This uses Node’s http module, and ecstatic for static assets.

I think my favourite example so far is polytropon. It has a function called Moog, so you can’t go wrong!

Node Roundup: Do We Need peerDependencies, lazy-install, Time Require

16 Apr 2014 | By Alex Young | Comments | Tags node modules npm benchmarks

Do We Need peerDependencies?

Isaac Schlueter asked this question on Twitter:

Recently, peerDependencies has gotten more and more contentious. Srsly considering deprecation/removal. Thoughts? github.com/npm/npm/issues/5080

The discussion on Twitter and GitHub seems strongly for removing peerDependencies. I agree with it simply because I hate explaining how it works.

Eran Hammer made an interesting point about the idea of a compatibleWith property:

What we need is compatibleWith concept that simply warns you when you are using bad combinations. I think we can rename peerDeps and with some minor adjustment keep the functionality without annoying everyone. Making it a warning sign instead of a blocking feature would remove the “hell” part and those who chose to ignore warnings (as many already do with node versions) can continue to ignore.

lazy-install

If that isn’t enough npm craziness for you, then how about this? lazy-install (GitHub: adamrenklint / lazy-install, License: MIT, npm: lazy-install) by Adam Renklint can be used to install dependencies based on “group” names.

Let’s say you wanted to specify dependencies for your production and test environments. Your package.json could look like this:

{
  "name": "myProject",
  "lazyDependencies": {
    "server": {
      "express": "4.0.0"
    },
    "test": {
      "mocha": "1.18.2"
    }
  }
}

Then in your code you can run lazy.install to trigger an npm install with the right options. In fact, the module itself is basically a wrapper around npm.

Time Require

Time Require

How much time did that require take to require? Now you can find out, with time-require! (GitHub: jaguard / time-require, License: MIT, npm: time-require).

It shows the execution time for each module by changing require, and then displaying the elapsed time for each file once the 'exit' event is emitted on the process object.

You can use it by adding a require('time-require') as the first line of your main script.

Responsive SVG with VLEX

15 Apr 2014 | By Alex Young | Comments | Tags responsive vlex

VLEX

VLEX (GitHub: indus / VLEX, License: MIT) by Stefan Keim is a library for defining the logic necessary to resize SVG images. It works using a vlex attribute that controls how things are resized, and there’s also a VLEX function for initialising and updating elements.

A “vlexpression” contains property descriptions separated by semi-colons. Using a $ allows predefined values to be referenced: $x is element.clientWidth, and $cX is $x / 2 – the horizontal centre. Using a colon allows a value to be set: key:value.

Given those rules, you can centre a circle with cx:{$cX};cy:{$cY}.

Internally it splits the expression with String.prototype.split(';'), and then splits on : to get the keys and values. It’s a fairly simple parser, but it seems small and flexible enough to do get some clever effects using SVG. Take a look at the examples in the readme to see what’s possible.

Reactive X-Tags

14 Apr 2014 | By Alex Young | Comments | Tags elements xtag

Reactive Elements

Here’s a cool idea I haven’t seen before: ReactiveElements with X-Tag. X-Tag is a library from Mozilla that uses JavaScript to provide support for the current W3 Web Components draft. This basically allows modern browsers to use custom elements:

X-Tag allows you to easily create elements to encapsulate common behavior or use existing custom elements to quickly get the behavior you’re looking for.

X-Tag provides several powerful features that streamline element creation such as: Custom events and delegation, mixins, accessors, component lifecycle functions, pseudos and more.

The PixelsCommander / ReactiveElements project by Denis Radin is a small MIT licensed library that builds on X-Tag so you can use it with React.js.

First you create a React component definition:

MyComponent = React.createClass({
  render: function() {
    console.log(this.props.items);
    return <ul><li>React content</li></ul>;
  }
});

xtag.registerReact('my-react-component', MyComponent);

And then you can use my-react-component in your HTML and see items if it has been specified as an attribute.

I like the idea of combining these two libraries, it has an AngularJS feel while embracing upcoming standards.

The BMEAN Stack and Offline-First Design

11 Apr 2014 | By Daishi Kato | Comments | Tags express node caching
This is a guest post written by Daishi Kato.

Many of you would know the MEAN stack, the software stack consisting of MongoDB, Express, AngularJS and Node. However, the BMEAN stack might be new to you. The B stands for Breeze, a data management library with support for client caching.

The current trend in web development is mobile first design. Recently, the “offline first” approach got attention for contributing toward better mobile user experience. Breeze allows you to develop offline web applications easily thanks to support for querying the client’s cache.

Breeze provides a sample BMEAN application called Zza, but it’s not an offline application. I thought there could be a simpler but more practical application using social-cms-backend.

Social-cms-backend is Express middleware and it demonstrates how to develop a simple Twitter clone in a matter of minutes. I updated social-cms-backend so that it supports Breeze and you don’t have to code the server side logic.

Using the new social-cms-backend and the BMEAN stack, I developed a sample application called notes-app-sample. It’s an offline application with HTML5 cache manifest thanks to connect-cache-manifest.

The usage of the application is pretty straightforward: you write text which will be stored in localStorage. Once you login, you can synchronize data with MongoDB on the server. The login is handled by passport, and currently it works with the Facebook strategy.

The source code of notes-app-sample is available here and the working application is available here.

I hope these sample applications give you some ideas about how to use Breeze with your MEAN apps.

Game Graphics with CutJS

10 Apr 2014 | By Alex Young | Comments | Tags html5 graphics 2d games
CutJS demos and examples.

CutJS (GitHub: piqnt/cutjs, License: MIT) is a new game graphics library by Ali Shakiba. It’s aimed at 2D graphics for cross-browser development, with support for desktop and mobile browsers.

The API is jQuery inspired, but because it works with the Canvas the author has introduced an interesting way of styling elements called “pinning”:

A CutJS app consists of a tree of nodes, each node is pinned (transformed) against its parent and have zero, one or more image cutouts. Image cutouts comes from either image textures or Canvas drawing.

Each rendering cycle consists of ticking and painting the tree. On ticking nodes adjust themselves to recent updates and then on painting each node transforms according to its pinning, pastes its cutouts and then delegates to its children.

Cut “apps” are created by calling Cut with a function, and then textures can be added and manipulated with tween animations and events.

// Create new app
Cut(function(root, container) {
  // Subscribe to Cut.Mouse
  Cut.Mouse(root, container);

  // Set view box
  root.viewbox(500, 300);

  // Create an image node
  Cut.image('base:box').appendTo(root)
    // on mouse click on this node
    .on(Cut.Mouse.CLICK, function(ev, point) {
      // Tween scale values of this node
      this.tween().clear().pin({
        scaleX: Math.random() + 0.5,
        scaleY: Math.random() + 0.5
      });
      return true;
    });
});

// Register an image texture
Cut.addTexture({
  name: 'base',
  imagePath: 'base.png',
  cutouts: [
    { name: 'box', x: 0, y: 0, width: 30, height: 30 }
  ]
});

There’s a cool Asteroids game example that shows off some of the main features of the library.

Node Roundup: npm and Heartbleed, sipster

09 Apr 2014 | By Alex Young | Comments | Tags node modules npm audio telephony voip

npm and Heartbleed

The npm blog has an article about npm and Heartbleed:

We started patching machines within 30 minutes of the revelation of the bug, and our last vulnerable machine was patched at 7.30am Pacific today.

There has been no evidence so far that our keys were compromised during this period, but nevertheless we are regenerating all our SSL keys anyway and will be rolling them out over the next couple of days (we are very cautious about testing and rolling out new certs since an earlier incident in which we broke a lot of older npm clients while doing so).

sipster

Brian White sent in sipster (GitHub: mscdex / sipster, License: MIT, npm: sipster), a pjsip binding for Node. This is the basis for the SIP driver used by Asterisk 12+. He hasn’t yet been able to get a working build environment set up for Windows, but it should work for Unix systems.

Here’s a list of what Brian says it can do so far:

  • Make and receive calls
  • Play either individual or a playlist of wav files (ulaw, alaw, or pcm)
  • Record audio to wav file (ulaw, alaw, or pcm)
  • Hook up audio streams from different calls (e.g. create your own conference or record a mix of streams to wav)
  • Adjust volume levels of audio streams
  • Detect/Send DTMF digits
  • Hold/un-hold
  • Call transfer

The API is event based – for example: call.on('dtmf', cb), and I think the C++ binding is a cool example of a Node native module: src/binding.cc.

AngularJS Localization

08 Apr 2014 | By Alex Young | Comments | Tags ui libraries AngularJS

Sebastian Tschan, also known as blueimp, is the author of the hugely popular jQuery-File-Upload project. It turns out he also writes AngularJS modules. His recent AngularJS project is dedicated to localization, and works well with a Grunt task that builds locales:

By running grunt locales:build you can get a set of JavaScript locale files that can be used to help translate content.

The basic angular-localize directive is used by adding the localize attribute to a tag. The text in the element will be used as the translation key, but you can use localize="key" to specify the key instead, which is useful if the final content hasn’t yet been copy edited.

The localize directive also observes data-* attributes and passes them as objects to translation functions, so data can be inserted into text dynamically.

There’s also a localize service for translating text in your JavaScript code, and there’s even a localizeFactory for creating your own attribute-based localize directives.