Not a blog

Yet another pointless personal blog

Bedcon 2015

I did a talk on this years BED-Con about React.js + Flux (more specifically Redux). I don’t think there is a video of the talk, but my slides are available here. They are at least partially german, since I gave the talk in german. Maybe I’ll try to condense it also into a blog post. But don’t hold your breath.

Zaunberg Talks

I finally get a chance to sit down and write a short post about the Zaunberg Talks workshop day last monday. It was a lot of fun to give my AngularJS workshop to a great group of attendees!

From the feedback I got so far, everyone there – me included – had a great time. We had some very interesting discussions along the way and I got some good ideas and suggestions to make it even more awesome the next time! Although, it was a lot more exhausting than I thought. At the end of the day my voice was close to collapsing and my brain was basically toast. I guess I’m not used to be the center of attention for a whole day :)

Anyways, a big thank you to everyone involved! The organization was flawless, the venue was professional (the wifi actually worked!) and it was especially nice that most of the attendees and speakers got together in the evening for beer, food and a chat. All in all a great day…

(Oh, by the way: Some impressions of the workshops are available at the Zaunberg Talks website.)

AngularJS Workshop

Only a little over a week left until my full-day AngularJS introduction workshop and I’m really looking forward to it!

Once again, it shows that the best way to get as deep as possible into a topic is teaching it. And although I have a firm grasp of AngularJS, I have the feeling, there will be questions, hitting me completely out of the blue. Bring it on!

If you are interested in learning the Basics of AngularJS (and can handle a German workshop), there are still a few places left.

The workshop will be held during the Zaunberg Talks on the 18th of November in Berlin. Ping me if you want to join!

See you all at Zaunberg Talks!


tl;dr: This post describes how to build a multi-lingual angular.js application with a Play! backend.

I get asked from time to time what I would use to start a new greenfield web project these days. Of course the answer is always ‘it depends’. Nonetheless, a lot of the times, angular.js comes up in those discussions.

Angular.js is a client-side Model-View-Whatever framework developed by Google with a lot of nice aspects and features. It provides robust data binding to the DOM, URL routing for single-page applications and has a very nice way to flexibly extend HTML for nice, declarative code. Angular.js has no requirements for the server side so the question remains, what one might use on the server.

I’ve long been suspecting that angular.js would play very well with a Play! server but up until now I never actually tried it. The Play! framework is a stateless, lightweight and scalable web framework written in Scala that allows developing applications in either Scala or Java. One of the very nice aspects of Play! is its template language, which is in fact also Scala. This gives us compiled, statically type checked templates. While this might seem irrelevant in the face of single-page application, I believe this actually enables us to overcome one of angulars more serious shortcomings, namely internationalization (i18n).

While angular.js does not completely ignore i18n, I think out of the box it lacks an opinionated way of how to handle i18n, forcing developers to come up with their own ways to handle multi-language applications.

So, I too came up with my own way, combining the strengths of Play! and angular.js to handle i18n. As an example, I created a useless little todo-list app (the hello world of web applications). The todo-list uses Play! 2.1.3 on the server. The front-end is written in angular.js 1.2.0rc1 (using CoffeeScript, just because) and the ugly styling was my first exposure to the new Bootstrap v3. An instance of the app is hosted on Heroku and can be laughed at here. And the code is of course over on GitHub.

What is required for proper i18n?

Proper multi language applications have to take care of three aspects:

  1. the static texts in the templates must be translated
  2. locale specific formatting must be done (especially regarding dates and numbers)
  3. the user must be able to select a language

The first thing is what angular.js has no strong opinion about. Since we don’t want to duplicate our templates for all supported languages, this is where Play! will help us. Play! will generate the localized templates for us. The second aspect is where angular is already doing quite well on its own. And finally with the third aspect, we have to bring Play! and angular.js together.

So let’s tackle them in opposite order.

Selecting a language

The actual selection of a language is currently done in a simple select box. One could go wild here. But please make sure to also list the language in their own lingua (ever tried switching a japanese Windows to another language? I knew I was in the language settings but I had no idea what all the options were supposed to mean).

The actual selection is then stored in a simple cookie. This way, the server knows the selected language in every request. I also reload the page after setting the language. This reloads the angular application with the new language.

The actual language setting in angular is done via a LanguageService

app.service('LanguageService', ['$http', '$cookies', '$window', ($http, $cookies, $window) ->
  @setLanguage = (key) ->
    $cookies.language = key

  @currentLanguage = () ->
    $cookies.language or "en"

The service stores the current language in a cookie (don’t forget to add ngCookies as a dependency of your angular app!) and retrieves it either from the cookie or defaults to "en".

That Service is used by the view via a simple little controller j

app.controller('LanguageController', ['$scope', 'LanguageService', ($scope, LanguageService) ->
  $scope.language = LanguageService.currentLanguage()

  $scope.updateLanguage = (language) ->

And finally the view (ignore the implicit lang for now – we’ll come to that):

@(implicit lang: Lang)

<div class="navbar-right" ng-controller="LanguageController">
    <select name="language" id="language" ng-model="language">
        <option value="en">English (@Messages(""))</option>
        <option value="de">Deutsch (@Messages(""))</option>
        <option value="ja">日本語 (@Messages(""))</option>
    <button ng-click="updateLanguage(language)">

The relevant parts in the view are the ng-model directive on the select element. The controllers sets that to the current language from the service. And finally the button that triggers the language update via its ng-click directive.

Locale specific formatting

The way angular handles locale specific formatting is via a locale specific JavaScript file that is either included separately or concatenated to the end of the actual angular.js file. While this approach works, it is not possible to change the formatting on the fly. But since we reload the angular application on language changes anyway, this is ok for us. The actual include on the server now looks like this:

<script src=""javascripts/angular.js")" type="text/javascript"></script>
<script src=""javascripts/angular-locale_" + lang.language + ".js")" type="text/javascript"></script>

Where lang is a play.api.i18n.Lang object representing the current language used for the request on the server (more on that later). That way the angular application is always running with the language set by the user and correctly reinitialized on language switching.

Since my todo app would not actually make use of any of this, I’ve simply added a footer that shows todays date formatted by angular. (Therefore my application gained a neat feature: If you ever need todays date in Japanese for whatever reason, just use my todo app).

Translated templates

The actual meat of the i18n happens on the server. All the templates are translated by Play!. Play! has a very nice way of dealing with different languages. All supported languages must be declared in the application.conf like so:


The actual translations are stored in files called messages.XXX where XXX is the language as declared previously. For all practical intents and purposes, those files behave exactly like Javas .properties files with one critical difference. Play! actually expects those files to be in UTF-8 (There goes the need for my escapist).

And now it’s finally time to come the implicit language parameter that we already saw in the language switch view.

The way you access those messages files is via a call to Messages("propery.key"). This method also takes an implicit parameter of type play.api.i18n.Lang. So all we have to do is fetch the language from the cookie, transform it into a valid Lang and pass it to our templates.

In Play! template files are a weird mix of HTML and Scala code that is actually compiled (and type checked at compile time!) into a regular function that you call from your server-side controllers. To declare, that our template requires a Lang Parameter, that should be used as an implicit, we have to start our template with a parameter declaration like so:

@(implicit lang: Lang)

We already saw that in the language parameter view. The view is normal HTML and everything that starts with an @ is expected to be Scala code. So we can now write @Messages("propery.key") everywhere in our template and it will magically choose the right language.

One word about Scalas implicits. They are a powerful feature of the language that can reduce a lot of useless boilerplate code. However they are very close to magic and should therefore be used with caution and only where really needed. If in doubt, avoid implicits.

Finally, here is the trait I use to get the language out of the cookie header:

trait LocaleFromRequest {
  def localeFromRequest(implicit request: RequestHeader): Lang = {
    request.cookies.get("language") match {
      case None => Application.lang(request)
      case Some(cookie) => Lang.get(cookie.value).getOrElse(Application.lang(request))

If there is no language key in the cookie, just use the Applications default way of determining the language. Otherwise create a Lang from the cookie-value. If that fails, fall back again to let play determine a language from its known set of languages.

Why don’t you use the Accept-Language HTTP header?

Good question. Because I want to give my users an easy way of changing the application language. And changing the Accept-Language of your browser is not at all easy or accessible to the common user.

Pitfall: Bootstraps default fonts

Bootstrap 3 by default has a font-family declaration of:

font-family: "Helvetica Neue", Helvetica, Arial, sans-serif;

This is all nice and good on real systems. However on a default windows box, there is neither “Helvetica Neue” nor Helvetica. And Arial apparently has no full Japanese character support. The result are a visually very ugly mix of the different character classes in Japanese.

See the obvious missmatch in width and serif-style of the の with respect to the other kanji?

Upon recommendation by Fabian Prinz, I’ve added some font to the mix that I’ve never heard of before:

font-family: "Helvetica Neue", Helvetica, "MS PGothic", Arial, sans-serif;

Much better. Now even Japanese Windows Users can enjoy my awesome todo-list.

Pitfall: JSON in Play! 2.1

Play!-Scala has great support for working with JSON. But due to the fact that Scala has a strong type-system and Play! 2.1 introduced some substantial changes to the way JSON is handled, it might be a bit difficult to get started (the documentation is a bit thin and most examples in the web refer to the old ways).

The basics for handling JSON is creating a reader and a writer. The reader also doubles as a validator. The following is a reader for a ToDo that parses the JSON string {"name": "A Todo", "done": false, "id": 42} with the little caveat that the id key is optional:

implicit val toDoJsonReader = new Reads[ToDo] {
  def reads(json: JsValue) = {
    (json \ "name").validate[String].flatMap { name =>
        (json \ "done").validate[Boolean].map { done =>
          (json \ "id").asOpt[Long] match {
            case Some(id) => ToDo(Id(id), name, done)
            case _ => ToDo(Id(0L), name, done)

While this is very straight forward and idiomatic Scala, it can be a bit overwhelming for people new to Scala. It took me a while to really get the hang of it.

However, the Reads object is then implicitly used like in the following example:

def create = Action(parse.json) { request =>
      valid = todo => Ok(Json.toJson(ToDoService.createNew(todo))),
      invalid = e => BadRequest("invalid request")

Pitfall: Minification

A well known pitfall in angular.js is the danger of JavaScript minifiers to the dependency-injection mechanism of angular. It is therefore best to get in the habit of using the more verbose array-syntax for controllers even in CoffeeScript. So instead of writing

app.controller 'MyBrokenController', ($scope) ->
  $scope.value = 'will break in minification'

Always write the long form, that survives minification:

app.controller 'MyWorkingController', ['$scope', ($scope) ->
  $scope.value = 'will work minified'

Some words about the example

The code for the application is over on GitHub. Since I’m fairly new to Scala, it might not be the most idiomatic Scala (or Play! for that matter). I also did not set up a real database. The todo list uses the H2 in-memory DB even over on Heroku. So don’t expect your todos to stay there. I actually don’t know when and whether heroku restarts the application. But when they do, the in-memory database will be cleared!

One other thing I kind of regret, when I started I was on the fence of writing todo or to-do. The results are inconsequently named Objects. Sometimes I used todo and sometimes toDo. This inconsistency annoys me and If it wouldn’t be so annoying to change only the case of files in git under OS X, I would probably already have done it. This proves once more, that there are only two hard problems in computer science:

  1. Cache invalidation
  2. Naming things
  3. Off-by-one errors


So is the combination of Play! and angular.js a good fit for multi-language single-page applications? Absolutely! It is a breeze to write a RESTful API with Play! Once you get a hang on the way Play! handles JSON there is very little friction. And the templating system in Play! is one of the best server side templates I’ve ever used.

However there are also downsides. My biggest gripe is with the asset pipeline in Play! While on the first look it seems to be a very good system with CoffeeScript and LESS support out of the box, it actually turned out to be not as flexible as I would have wished. Maybe I’m just not smart enough but I was not able to get Play! to execute Jasmine Test Specs written in CoffeeScript. And no testing in angular.js means, you are missing out on one of the other great features of angular.

On the last Play! Usergroup Meetup in Berlin was a presentation by my good friend Yann Simon showing how to use Grunt instead of the asset pipeline in Play! I would recommend to use that for real projects as it provides more flexibility in asset handling, is a technology known to many front-end developers and provides an even cleaner separation between the GUI/front-end and the server/back-end.

One more thing. Writing angular.js in CoffeeScript is a great pleasure! The more friendly syntax of CoffeeScript really helps a lot and makes the code much more readable.

What next?

One important thing that I havn’t yet looked into is caching. Currently, there is the real danger of browsers caching the templates and therefore not updating correctly to a language switch. The easy (and ugly) way out would be to just disable caching on all html files. A better approach would be to add the language as query- or url-parameter to give the browser a chance to cache the files for every language separately.

There should also be a way to set the language via a URL to make it possible to link into a specific language. This should be quite easy with Play! What else are you missing from the todo-app in respect to i18n?

I hope this post was helpful for someone. I intend to make this the first in a series of more technical posts here. If you have any questions, feel free to ask. I’m looking forward to your feedback!

(Thanks a lot to Fabian Prinz for the Japanese translation)

Brain for Hire

I guess it’s time to make it official:

I am now a freelancer

After years of constant employment, this is the first time I am standing on my own two feet. I’m still on the fence whether this is the best decision or biggest mistake in my career so far.

I greatly enjoyed working at akquinet for the last two and a half years. So, the decision to take this step was not an easy one. But I just have to do this personal experiment. I don’t want to look back at my life later on and regret never having tried this. The time is right and for the first few months I should be all set.

Nonetheless, here is some shameless self promotion: If you are looking for an experienced technical consultant to maneuver your JVM based project out of a tight corner – contact me. If you are looking for an excellent developer to help on your next kick-ass web application – contact me. If you need someone coaching your team to get up to speed on modern web technologies – contact me. And if you are actually looking for a highly motivated clojure developer – please, please contact me.

Bye Bye Beijing

On another note, today is actually my last day in Beijing. My next stop will be Seoul and I’m looking forward to it (and to finally having proper Internet again).

However, I’ll continue my journey without the mac that I’m currently typing on. I’ll only have an iPad with me. Therefore, I can’t post any further updates to this blog until we are back home.

Until further notice, all updates will go to either tumblr, twitter or

See you on the other side…

Hops Coffee

Last weekend, we’ve been to Qingdao (青岛). Qingdao is quite often also referred to by its german name-mangling Tsingtao. Tsingtao happens to be the biggest brewer and beer exporter in China and therefore also one of the largest in the world (Chinas economy of scale at work here).

Therefore it can’t come as a huge surprise, that Qingdao features a rather nice beer museum. The history of the brewery and the old as well as the current brewing equipment is on display along with a bar for tasting and of course a gift shop with lot’s of souvenirs. Some more related to beer than others.

The one, that really caught my eye was Hops Coffee. The direct combination of my two most favorite beverages in the world can’t be recommended at all. But the idea of a slight hops aroma in a nice cup of coffee sounds at least interesting to me.

"Hops Coffee, brewed and ready to drink"

Sadly, the actual product was more of a multi stage disappointment than a great new beverage. First, it’s instant. I can live with that and was kind of expecting it to be.

Second, the instant mix also includes sweeteners (yuck) and milk powder, ruining an otherwise perfectly good instant coffee.

And then, there is the actual Hops. Or better yet, the lack of it. The hops aroma exists purely by the power of your mind. It is so weak, that after my first cup, I searched the box to see if the hops aroma might be in another sachet. It isn’t. If you concentrate hard and really want to smell some hops it is just barely there. But nothing more.…

All in all, the hops coffee was rather disappointing. It is a sweetened, whitened, otherwise boring instant coffee. However, I’ll probably get hold of some actual hops for proper brewing some time early next year. I’ll use that to make some proper hops coffee. Brewed from good beans and otherwise black as coffee is intended to be. Looking forward to see how that turns out…


Yesterday, I finally finished my second play through of Bastion. The game is so great, that I want to recommend it here.


On first sight, The Bastion appears to be little more than a flying peace of rock drifting through the remains of what the Calamity left of the old world. In the beginning of the game, The Bastion finds The Kid (or The Kid finds The Bastion). On the Bastion, the Kid meets The Stranger and – guided by him – The Kid searches the land to find other survivors and ultimately to uncover the secrets of the Calamity, that fractured the earth, destroyed Caelondia and left so few survivors.

When The Kid and The Stranger first met, the Bastion is weak and powerless. To bring her back to old glory, The Kid first has to search for the Cores, that are scattered throughout the land. With every Core that is recovered, the Bastion gains new Energy and grows, enabling them to travel much further into the lands. Soon they realize, they are not the only ones who survived the Calamity. Other Survivors, like Zulf from the folk of the Ura, join them on the Bastion.

In the end, they figure out, what the Calamity actually was, what led to this huge catastrophe. A discovery, not everyone deals with equally…


One could think of Bastion as a hack-n-slash RPG. You control The Kid and roam the world in an isometric top-down perspective. Battle in Bastion is realtime and The Kid gathers quite an arsenal of close-combat and long-range weapons. Throw in his Bullhead Shield and some special abilities and you are set. There is also some leveling involved, but apart from packing a bigger punch and more health, leveling up doesn’t gain you much. So don’t expect a Diablo-Style skill tree or anything.

Bastion started out as an Indie game on XBox Live but is now available on nearly everything that has a display. I’ve played it on the iPad, but if you can, play it on something that has a bigger screen – Bastion is beautiful. Bastion is drawn in living, vibrant colors and has a comic-art-style that really hits my taste. It’s meant to be played on your big screen TV, with the rocking volume from your speakers. But I’ll get to the audio later.

The difficulty was quite right for me. I had no problem, finishing the game. The occasional death happened, but it never got frustrating. And if you like a challenge, the last few achievements are really hardcore (I haven’t managed to finish any of the dreams with all 10 gods enabled. I already struggle with 5).

The Bad

Since we are on the go, it was most convenient for me to play Bastion on the iPad. The iPad is really great for recreational gaming. Melt with a couch or a comfy chair, pop in your favorite headphones and dive into the space between your hands. However a touchscreen isn’t the perfect input device for games like bastion. While it did get the job done most of the time, a real gamepad would have been a lot better. It is just too easy to hit the wrong touch-area (aka button) in busy moment and do a barrel roll into the abyss instead of pushing the enemy into the same.

So, the touchscreen controls, while being ok, have to go into the bad section. Along with the german localization. Luckily they just translated onscreen texts and added subtitles. Yet, I would have preferred having the game the way it was intended to be. But I couldn’t find a way to switch the language on my iPad although my entire iPad is set to english anyways. So either I am too stupid or the lack of a language switch has to be my biggest complaint about Bastion. Really, that is my biggest complaint about the game. That I couldn’t set it to another language. The game itself, I have nothing to complain about.

I could imagine, that some people might complain about replay-value. To be honest, once you’ve finished the game a second time, there is not much left to do. The story has been told. The secrets are uncovered. There is not a lot left to pull you back into the game afterwards. But I consider that a plus. I am no student anymore and my time for games is scarce. I like it, when a game has a clear cut ending and I can put it away and move on. Nothing worse than those kind of games, that hook you up and never ever let you go again.

The Good

So the length of the game goes into The Good. The story had me hooked from the get go and unfolded at a great pace. You get a good value out of your buck. The game is usually priced between 10 and 20 $ and goes on different sales or bundles from time to time. And for that, you get many hours of fun gameplay, a great story and an atmosphere that will hook you right in.

A big part of that atmosphere are the beautiful visuals. Bastion looks great with living colors and fluid animations. The whole world feels alive.

Another equally big part is the music. Bastion has one of the best soundtracks I’ve heard in a game for a long time (although this doesn’t really mean a lot considering how much I game these days). It’s often dominated by acoustic guitars and strings, has a great beat and sometimes drifts a bit into the oriental space. The music perfectly underlines what is happening on the screen and you really want to turn it up. The soundtrack can even stand on its own. I have to admit, that the soundtrack had many rotations in my playlist long, long before I actually played the game. Bastion even manages to sneak in a few songs with lyrics in certain areas of the game – lyrics… in a game!

I guess, whether you like this game or not boils down to whether you like the story and atmosphere. If they don’t get you hooked, you probably won’t like the game as much as I did. Yeah, it is fun. It is fun walking around and engaging in battle. It is fun getting new weapons and leveling them up. It feels great to counter block an enemy and than smash him in the ground. But if you are not hooked into the story, you’ll probably put the controller aside sooner than later. From a pure gameplay-perspective, there are probably a few better hack-n-slash RPGs.

But this one has a twist. A catch, that the others don’t have. A nugget of awesome.

The Awesome

Two words: Logan Cunningham. It is quite a challenge writing so much about Bastion without mentioning Logan Cunningham until this point. Logan is the voice actor of Rucks, The Stranger. And with his smokey, rough voice, he comments and narrates the entire story from beginning to end. And this is Bastions unique and awesome twist. Most games tell their story between the Levels, advancing it via cutscenes and the like. But in Bastion, there is a story teller, a narrator. He is telling you a story; the story you are just playing. And it works. It works beautifully. Even though there is rarely a minute where Rucks is not narrating what is happening on the screen, it never feels repetitive or out of place. The only downside of this is, the game is very linear. You don’t have a lot of choice how to advance next.

Trust me, just writing/reading this doesn’t give it justice. You have to hear and see it in action.


As you might have guessed, I enjoyed Bastion a lot. I enjoyed it so much that I decided to tell the world about it. I give it ★★★★★ without hesitation. I know, I’m a bit late to the party (at least, Bastion isn’t 5 years old by now). Bastion was first released well over a year. So you have no excuse as well. If you like games with some action at all, you should at least give it a try. So, if you don’t yet own a copy, head over to your favorite digital store and get it. I recommend making sure to get the Soundtrack as well. As I said, it can easily stand on its own as a cool tune.

If you give it a try, let me know, how you liked it!

Turn up the volume all the way to eleven, take your Breaker’s Bow and your Cael Hammer and let the Bastion guide your way. Ain’t much Kid can’t handle with Hammer and Bow in hand.

Opportunities of Scale

We tend to forget how huge China is. Yeah, China is big and there are many Chinese. But how many Chinese there are, just needs a reminder from time to time.

According to Wolfram Alpha, the population of china was estimated to be 1.3 billion in 2010. That is easily more, than the population of the EU and the USA combined.

This has a some peculiar consequences. For once, there are quite a few multi-million citizen cities, you’ve likely never heard of before. Would you know, where to search for Zhoukou or Linyi on the map? They both have a population of around 10 Million – give or take.

Another interesting consequence, there are a lots of hugely popular services and companies, that you have also probably never heard of – some of them more interesting than others.

The Twitter equivalent of China for example is Sina Weibo. While being mostly unknown in the western world, Weibo is about the same size as Twitter. According to the wikipedia, Twitter has currently about 500 million users and generates about 340 million messages a day. The 3 year old Weibo currently has about 368 million users and generates 100 million messages a day – not as much but the same order of magnitude (source and source).

Just as Weibo is quite similar to Twitter, there are many other china-only competitors to the services, we know. Yesterday, we used a very neat one. The site is called (Strangely, many of the Chinese alternatives use .com instead of .cn TLDs). From the looks of it, they appear to be a competitor to amazon. Yet, 360buy has a cool feature: In many areas, including Beijing, they offer same-day-delivery. For free. Just like that.

We decided, we needed to get a cheep DVD player for the TV in our apartment. We went to 360buy, used Chrome to translate the page, chose a device, entered our address and clicked on order. This was around 9:00 am. The same day at around 5:00 pm, it knocked on the door. We payed in cash and got our DVD player.

The whole experience was fast, friendly and due to Chromes page translate-feature, even possible for lazy aliens, that don’t speak Chinese.

Part of me wants that from amazon (in Germany)!

Small Differences: Money

Of course china uses money like everywhere else. And a lot of payments are done using cash. But the cash here is a bit different than in Europe.

This mainly boils down to the fact, that the biggest bill available in china is the 100 RMB bill. This is currently worth roughly 12.50 €. All other bills are worth even less. Also, coins are used far less often and only go up to 1 RMB (about 0.12 €).

This has two interesting consequences. First: You always have a trousers pocket full of papermoney. A thick stack of bills which is worth about 10 €. I kind of like that. Far more comfy than having a pocket full of €-coins. And still worth so little, that you don’t really care if you lose a bill. (I guess this is similar in the US with their 1 $ bill).

The second consequence is, whenever you have to pay for something a bit more expensive, you deal with huge stacks of money. Making you feel a bit like a gangster in the first moment and making you wish, you had a counting machine the moment after that. (Counting machines are extremely common in china. Basically every cash register has one).

Did I mention, we payed our entire 3 month rent and a safety deposit in cash? This is how some of it looked:

Really, you have to deal with silly big stacks of cash from time to time. You have to make multiple trips to an ATM to get reasonable amounts of cash because they can’t spit out stacks big enough. Stuff like that.

Apparently there is also quite a lot of counterfeit money in the wild. The Chinese will always check 50 and 100 RMB bills for validity. Either by hand or by feeding it through a counting machine.

And there is also the other side of the value spectrum. The smallest bill in circulation (and not quite uncommon) is the 1 Yi Jiao bill, aka 0.1 RMB. So it is roughly worth 1 euro cent.

On the street, prices are usually multiples of 1 RMB. Amounts below 1 RMB are usually only found in supermarkets and similar stores. So you usually put your Yi Jiaos somewhere and hope to remember theme the next time you are grocery shopping.

This is how it looks:

It has roughly the same size and feel as monopoly money. Only, it is worth slightly less then monopoly money.