About Kellan Elliott-McCrea

Kay is a Community Manager for Flickr and passionate about extraordinary photography. As an editor on Flickr Blog he loves to showcase the beauty and diversity of Flickr in his posts. When he's not blogging or making Flickr more awesome (in front of and behind the scenes), you can find him taking pictures with his beloved Nikon and iPhone, listening to Hans Zimmer's music or playing board games. | On Flickr you can find him at https://flic.kr/quicksilver

Galleries APIs

Giant Isopod

We love galleries. After all, without galleries how would you find your giant sea bugs?

This post is to quickly announce we’ve added galleries to the API.

A Rose+GUID by Any Other Name ….

Galleries in the API use “compound-ids”. Like tags. An example gallery compound id might look like 9634-72157621980433950. Unlike photos you can’t simply grab the last number off a gallery url and stick it into the API. Yeah, I’m not thrilled about it either, but there are good (read boring) reasons why it works that way.

So when an API method says it takes a gallery_id, we’re talking about the compound-id.

You can however use the flickr.urls.lookupGallery method to go from gallery url to gallery_id. Pass the method the URL for the gallery, and we’ll give you back the gallery info blob.

You can also get gallery IDs from flickr.galleries.getList and flickr.galleries.getInfo.

Behold, a gallery info blob:

<gallery id="6065-72157617483228192" 
    url="http://www.flickr.com/photos/straup/galleries/72157617483228192" 
    owner="35034348999@N01" 
    primary_photo_id="292882708" primary_photo_server="112" primary_photo_farm="1" primary_photo_secret="7f29861bc4" 
    date_create="1241028772" date_update="1270111667" count_photos="17" count_videos="0" >
 <title>Cat Pictures I've Sent To Kevin Collins</title>
 <description>dive dive dive</description>
 </gallery>

The primary_photo_* attributes refer to the “cover photo” for the gallery. The owner is the Flickr user_id (aka NSID) of the member who created the gallery. The id is that compound-id we talked about.

Lists of Galleries

You can fetch all of a member’s galleries using flickr.galleries.getList, sorted from newest to oldest, returning a list of gallery info blobs.

Or you can fetch all the galleries a given photo is in with flickr.galleries.getListForPhoto.

A Bag of Photos

Perhaps most interesting, flickr.galleries.getPhotos will return a list of all the photos for a given gallery. It’s a standard photo response, with a twist.

<photos page="1" pages="1" perpage="500" total="15"> 
   <photo id="2935475111" owner="8147452@N05" secret="e20746148b" server="3068" farm="4" title="Day off from the Death Star." ispublic="1" isfriend="0" isfamily="0" is_primary="1" has_comment="1">
        <comment>best cat picture ever!</comment>
   </photo>
   <photo id="3078977730" owner="68779755@N00" secret="dba9d8105e" server="3229" farm="4" title="&quot;We could stuff it with Kleenex...&quot;" ispublic="1" isfriend="0" isfamily="0" is_primary="0" has_comment="0" /> 
   <photo id="3212123792" owner="10983978@N03" secret="4231501383" server="3391" farm="4" title="1-19-09: Some People Just Don't Get It" ispublic="1" isfriend="0" isfamily="0" is_primary="0" has_comment="0" /> 
     ....
</photos>

In addition to standard photo response attributes, there is also a has_comment attribute which signals whether the gallery creator added a comment about why she included the photo, and whether the child comment element is present. Also is_primary, when set to 1, indicates this is the gallery’s “cover photo”.

CRUD

flickr.galleries.create creates a gallery, with a title, description, and optional primary photo, and will return a gallery element with the compound-id and the URL of the gallery.

<gallery id="50736-72157623680420409" url="http://www.flickr.com/photos/kellan/galleries/72157623680420409" />

flickr.galleries.editMeta is simply for updating the title and description. flickr.galleries.editPhoto confusingly doesn’t edit a photo, but rather the comment about a photo in a gallery.

Of course the money is all in flickr.galleries.addPhoto which allows you to actually build a gallery of photos.

Nota bene: Remember only public-safe can be added to galleries.

The Curated Life

We’ve also added the ability to restrict searches to only photos in galleries, with the in_gallery argument to flickr.photos.search

So whether you’re interested in kittens deemed cute enough for galleries, or hand selected pink photos, or Flickr Commons photos in galleries, or simply photos taken near you (assuming you’re in Brooklyn), in galleries, that’s available.

Flickr Stats API

graph

photo by xgray

After collections, the most frequently requested addition to the Flickr API has been Stats. Today, alongside access to some archived older data, we’re announcing lots of new API methods that give you access to all the stats data we’re logging.

First up is flickr.stats.getTotalViews. If we call this with no arguments, it will give you the all time view counts for a user’s photos, photostream, sets and collections:

# ?method=flickr.stats.getTotalViews

<stats>
	<total views="584957"/>
	<photos views="391325"/>
	<photostream views="175691"/>
	<sets views="17856"/>
	<collections views="85"/>
</stats>

Sounds simple right? There’s a couple of details that are worth highlighting:

  • Not all Flickr members have stats enabled. If you get back an error, then someone needs to visit www.flickr.com/photos/me/stats/ and press the big pink button.
  • “All time” is a surprisingly vague concept. We started recording “All-time” view counts for photos, photostreams and sets sometime back in 2006, and started recording collection counts on October 21, 2008.

Now we know how many views someone got on their account, you’ll probably want to drill down into when the views happened, where those views came from, and what photos were popular.

When

Getting details on when the views happened is simple, we can pass a date to flickr.stats.getTotalViews, and get the counts for just that that day. The date can be a unix timestamp, or something like "2010-03-24", but it has to be in the last 28 days as we don’t keep detailed data around longer than that.

What

Getting an idea of exactly what photos have been viewed is also simple – you can call flickr.stats.getPopularPhotos. This returns a standard photos response, with an extra <stats> element giving the view, comment and favorite counts. As with flickr.stats.getTotalViews, this can be called with a date if you’d like to narrow down your results to just one day.

Where

Lastly, if you want to know where the views came from, you can call flickr.stats.getPhotoDomains. This gives a list of all of the domains that sent traffic to the photo. If you want even more detail on a domain, flickr.stats.getPhotoReferrers will tell you exactly what pages we’ve seen as referrers. Both methods can be narrowed down by both date or photo id if you want to filter the results more.

But wait! There’s more!

So far we’ve only talked about detailed data for photos, because that’s all we’ve ever shown on the flickr stats pages. But, behind the scenes we’re also recording data for sets, collections and photostreams, so we’ve added API methods to get at this data too. Full details are available as part of the full API documentation.

Between them these new API methods give you programatic access to all of the data we’re recording as part of the stats system. So go ahead and make awesome things with them!

5 Questions for Simon Willison

DSC00838.JPG

Simon Willison kindly took time out of his llama-spotting, Python wrangling (Django co-creating), MP expenses crowdsourcing day, to answer a few questions about he and his co-conspirator Natalie Downe‘s latest journalistic foray.

1. What are you currently building that integrates with Flickr, or a past favorite that you think is cool, neat, popular and worth telling folks about? Or both.

Simon: Our main project at the moment is WildlifeNearYou.com. It started life as a holiday hacking project with twelve geeks, on a Napoleonic Sea Fort, in the channel islands, half way between England and France, with no internet connection (see devfort.com for background info). Natalie and I continued to work on it after we returned to civilisation and we finally put it live last month. 170 people have imported more than 6,500 photos from Flickr in just the past few weeks, so people seem to like it!

The site’s principle purpose is to help people see wildlife – both in the wild and in places like nature reserves or zoos. We ask people to report wildlife they have seen by adding trips, sightings and photos – they get to build up their own profile showing everything they’ve spotted, and we get to build a search engine over the top that can answer queries like llamas near brighton or otters near san francisco.

In addition to that core functionality, we have a couple of fun extra features based around people’s photos. Our users can import their pictures from Flickr, and use WildlifeNearYou’s species database (actually sourced from Freebase.com) to tag those photos. We then push the tags back to their Flickr account in the form of text tags and machine tags – if they tell us the location (e.g. London Zoo) we’ll geotag their photo on Flickr as well.

If they don’t know what the animal in the photograph is, other users of the site can suggest a species. If the owner of the photograph agrees with the suggestion the species will be added to their list of sightings and the correct tags applied (and pushed through to Flickr).

Finally, we have a really fun crowdsourcing system for identifying and rewarding the best photos. If you go to http://www.wildlifenearyou.com/best/ we’ll show you two photos of the same species and ask you to pick the best one. Once we’ve collected a few opinions, we award gold, silver and bronze medals to the top three photographs for each species. The best photo is then used as the thumbnail for that species all over our site. Here are our best giraffe photos, as voted by the community:

http://www.wildlifenearyou.com/animals/giraffe/photos/best/

2. What are the best tricks or tips you’ve learned working with the > Flickr API?

Simon: It really is amazing how much benefit we got out of pushing machine tags over to Flickr. One feature we got for free was slideshows – once you have a machine tag, it’s easy to compose a URL which will present all of the photos tagged with that machine tag as a slideshow on Flickr. Here’s one for all of our photos of London Zoo:

http://www.flickr.com/photos/tags/wlny:place%3Dp1/show/

Even better, our site can now be queried using the Flickr API! Here’s a fun example: finding all of the Red Pandas that have been spotted in Europe. WildlifeNearYou doesn’t yet have a concept of Europe, but Flickr’s API can search using Yahoo! WhereOnEarth IDs. We can find the WOEID for Europe using the GeoPlanet API:

http://where.yahooapis.com/v1/places.q(‘europe&#8217;)?appid=%5Byourappidhere%5D

The WOEID for Europe is 24865675. Next we need the WildlifeNearYou identifier for red pandas, so we can figure out the correct machine tag to search for. We re-use the codes from our custom URL shortener for our machine tags, so we can find that ID by looking for the “Short URL” link on http://www.wildlifenearyou.com/animals/red-panda/ (at the bottom of the right hand column). The short URL is http://wlny.eu/s2f which means the machine tag we need is wlny:species=s2f

Armed with the WOEID and the machine tag, we can compose a Flickr search API call:

http://api.flickr.com/services/rest/?method=flickr.photos.search&machine_tags=wlny:species=s2f&woe_id=24865675&api_key=…

That gives us back a list of photos of Red Pandas taken in places that are within Europe. Add &extras=geo,url_s,tags to get back the tags, latitude/longitude and photo URL at the same time. The wlny: machine tags that come back can be used to link back to the place, species and trip pages – for example, wlny:place=p6p means the photo was taken at the place linked to by http://wlny.eu/p6p

This is pretty powerful stuff, and it’s all a natural consequence of writing machine tags back to Flickr.

(editors note: or even a slideshow of European red pandas)

3. As a Flickr developer what would you like to see Flickr do more of and why?

Simon: One thing that would make my life an enormous amount easier would be a Flickr-hosted photo picking application. For WildlifeNearYou I had to build a full interface for selecting photos from scratch, with options to search your photos, browse your sets, browse photos by place and so forth. The Flick API makes this pretty easy to do from a back end code perspective, but designing and implementing a pleasant front end is a pretty major job.

I’ve wanted to implement simple photo picking from Flickr on various other projects, but have been put off by the effort involved. WildlifeNearYou is the first time I’ve actually taken the challenge on properly.

What I’d love to see instead is an OAuth-style flow for selecting photos. I’d like to (for example) redirect my users to somewhere like http://www.flickr.com/pickr/?return_to=http://www.wildlifenearyou.com/selected/ and have Flickr present them with a full UI for searching and selecting from their photos. Once they had selected some photos, Flickr could redirect them back to http://www.wildlifenearyou.com/selected/?photo_ids=4303651932,4282571384,4282571396 and my application would know which photos they had selected.

This would make integrating “pick a photo / some photos from Flickr” in to any application much, much easier.

On a less exotic note, we have to do quite a few bulk operations against Flickr and having a bulk version of the flickr.photos.getInfo call would make this a lot faster – just the ability to pass up to 10 photo IDs at a time would reduce the number of HTTP calls we have to make by a huge amount.

4. What excites you about Flick and hacking? What do you think you’ll > build next or would like someone else to build so you don’t have to?

Simon: We’re going to be working on WildlifeNearYou for quite a while – I certainly don’t expect to get tired of looking at people’s photos of wildlife for a long time. We have a bunch of improvements planned for the “best photo” feature – we want to start showing your medals on your profile, and maybe have a league table for the best photographers based on who has won the most “best picture of X” awards. Once we’ve improved our species and location data we should be able to break that down in to best photographer for a certain area, or even for a category of animal (best owl photographer is sure to be hotly contested).

We also want to streamline our “add trip” flow based on Flickr metadata. If you import a bunch of geotagged photos, we can guess that they were probably taken on a trip to London Zoo an the 5th of February based on the location and date information from the pictures.

As for other projects… I’d love it if someone else would build the general purpose photo picker idea above – it doesn’t necessarily have to be Flickr, anyone could provide it as a service.

5. Besides your own, what Flickr projects and hacks do you use on a regular basis? Who should we interview next?

Simon: Matthew Somerville’s work is always interesting, and his current project, Theatricalia, is a single-handed attempt to create an IMDB for theatre productions. Naturally, he’s pulling in photos from Flickr based on his own machine tags.

Ticket Servers: Distributed Unique Primary Keys on the Cheap

This is the first post in the Using, Abusing and Scaling MySQL at Flickr series.

Ticket servers aren’t inherently interesting, but they’re an important building block at Flickr. They are core to topics we’ll be talking about later, like sharding and master-master. Ticket servers give us globally (Flickr-wide) unique integers to serve as primary keys in our distributed setup.

Why?

Sharding (aka data partioning) is how we scale Flickr’s datastore. Instead of storing all our data on one really big database, we have lots of databases, each with some of the data, and spread the load between them. Sometimes we need to migrate data between databases, so we need our primary keys to be globally unique. Additionally our MySQL shards are built as master-master replicant pairs for resiliency. This means we need to be able to guarantee uniqueness within a shard in order to avoid key collisions. We’d love to go on using MySQL auto-incrementing columns for primary keys like everyone else, but MySQL can’t guarantee uniqueness across physical and logical databases.

GUIDs?

Given the need for globally unique ids the obvious question is, why not use GUIDs? Mostly because GUIDs are big, and they index badly in MySQL. One of the ways we keep MySQL fast is we index everything we want to query on, and we only query on indexes. So index size is a key consideration. If you can’t keep your indexes in memory, you can’t keep your database fast. Additionally ticket servers give us sequentiality which has some really nice properties including making reporting and debugging more straightforward, and enabling some caching hacks.

Consistent Hashing?

Some projects like Amazon’s Dynamo provide a consistent hashing ring on top of the datastore to handle the GUID/sharding issue. This is better suited for write-cheap environments (e.g. LSMTs), while MySQL is optimized for fast random reads.

Centralizing Auto-Increments

If we can’t make MySQL auto-increments work across multiple databases, what if we just used one database? If we inserted a new row into this one database every time someone uploaded a photo we could then just use the auto-incrementing ID from that table as the primary key for all of our databases.

Of course at 60+ photos a second that table is going to get pretty big. We can get rid of all the extra data about the photo, and just have the ID in the centralized database. Even then the table gets unmanageably big quickly. And there are comments, and favorites, and group postings, and tags, and so on, and those all need IDs too.

REPLACE INTO

A little over a decade ago MySQL shipped with a non-standard extension to the ANSI SQL spec, “REPLACE INTO”. Later “INSERT ON DUPLICATE KEY UPDATE” came along and solved the original problem much better. However REPLACE INTO is still supported.

REPLACE works exactly like INSERT, except that if an old row in the table has the same value as a new row for a PRIMARY KEY or a UNIQUE index, the old row is deleted before the new row is inserted.

This allows us to atomically update in a place a single row in a database, and get a new auto-incremented primary ID.

Putting It All Together

A Flickr ticket server is a dedicated database server, with a single database on it, and in that database there are tables like Tickets32 for 32-bit IDs, and Tickets64 for 64-bit IDs.

The Tickets64 schema looks like:

CREATE TABLE `Tickets64` (
  `id` bigint(20) unsigned NOT NULL auto_increment,
  `stub` char(1) NOT NULL default '',
  PRIMARY KEY  (`id`),
  UNIQUE KEY `stub` (`stub`)
) ENGINE=MyISAM

SELECT * from Tickets64 returns a single row that looks something like:

+-------------------+------+
| id                | stub |
+-------------------+------+
| 72157623227190423 |    a |
+-------------------+------+

When I need a new globally unique 64-bit ID I issue the following SQL:

REPLACE INTO Tickets64 (stub) VALUES ('a');
SELECT LAST_INSERT_ID();

SPOFs

You really really don’t know want provisioning your IDs to be a single point of failure. We achieve “high availability” by running two ticket servers. At this write/update volume replicating between the boxes would be problematic, and locking would kill the performance of the site. We divide responsibility between the two boxes by dividing the ID space down the middle, evens and odds, using:

TicketServer1:
auto-increment-increment = 2
auto-increment-offset = 1

TicketServer2:
auto-increment-increment = 2
auto-increment-offset = 2

We round robin between the two servers to load balance and deal with down time. The sides do drift a bit out of sync, I think we have a few hundred thousand more odd number objects then evenly numbered objects at the moment, but this hurts no one.

More Sequences

We actually have more tables then just Tickets32 and Tickets64 on the ticket servers. We have a sequences for Photos, for Accounts, for OfflineTasks, and for Groups, etc. OfflineTasks get their own sequence because we burn through so many of them we don’t want to unnecessarily run up the counts on other things. Groups, and Accounts get their own sequence because we get comparatively so few of them. Photos have their own sequence that we made sure to sync to our old auto-increment table when we cut over because its nice to know how many photos we’ve had uploaded, and we use the ID as a short hand for keeping track.

So There’s That

It’s not particularly elegant, but it works shockingly well for us having been in production since Friday the 13th, January 2006, and is a great example of the Flickr engineering dumbest possible thing that will work design principle.

More soon.

Belorussian translation provided by PC.

Using, Abusing and Scaling MySQL at Flickr

I like “NoSQL”. But at Flickr, MySQL is our hammer, and we use it for nearly everything. It’s our federated data store, our key-value store, and our document store. We’ve built an event queue, and a job server on top of it, a stats feature, and a data warehouse.

We’ve spent the last several years abusing, twisting, and generally mis-using MySQL in ways that could only be called “post relational”. Our founding architect is famously in print saying, “Normalization is for sissies.”

So while it’s great to see folks going back to basics — instead of
assuming a complex and historically dictated series of interfaces, assuming just disks, RAM, data, and problem to solve — I think it’s also worth looking a bit harder at what you can do with MySQL. Because frankly MySQL brings some difficult to beat advantages.

  • it is a very well known component. When you’re scaling a complex app everything that can go wrong, will. Anything which cuts down on your debugging time is gold. All of MySQL’s flags and stats can be a bit overwhelming at times, but they’ve accumulated over time to solve real problems.

  • it’s pretty darn fast and stable. Speed is usually one of the key appeals of the new NoSQL architectures, but MySQL isn’t exactly slow (if you’re doing it right). I’ve seen two large, commercial “NoSQL” services flounder, stall and eventually get rewritten on top of MySQL. (and you’ve used services backed by both of them)

Over the next bit I’ll be writing a series of blog posts looking into how Flickr scales MySQL to do all sorts of things it really wasn’t intended for. I can’t promise you these are the best techniques, they are merely our techniques, there are others, but these are ours. They’re in production, and they work. I was tempted to call the series “YesSQL”, but that really doesn’t capture the spirit, so instead I’m calling it “Using and Abusing MySQL”.

And the first article is on ticket servers.

People in Photos: The API Methods

I’ve been racking my brains for a while trying to think of something informative to tell you about the API methods we’re releasing today, but all I keep coming back to is a venerable British advertising slogan for a brand of woodstain: “It does exactly what it says on the tin“.

What does the tin say, then?

photo by 5500

First off, we have a simple accessor method which will return you a list of people for a given photo. It’s called flickr.photos.people.getList, and it takes a photo ID as its sole argument.

But what about the reverse – finding all the photos of a given person? Fret not, that’s why we have flickr.people.getPhotosOf. This method takes a user ID, and since it returns a Standard Photos Response, you can also specify any extra data you want through the extras parameter.

Sometimes, simply consuming data isn’t enough – you will feel a need to create some. If you want to add a person to a photo, simply use flickr.photos.people.add. This method takes a photo_id and a user_id, and can optionally take another 4 arguments (person_x, person_y, person_w, person_h) to specify a “face boundary” for that person.

If you decide you don’t like the face boundary later, there’s always flickr.photos.people.deleteCoords to remove it entirely, or flickr.photos.people.editCoords to update.

Last, but not least, you can remove someone from a photo with flickr.photos.people.delete.

Obviously, all of the above methods require that the calling user is permitted to perform the action in question.

Why so late?

We’re aware that some people have been clamoring for access to these methods for a little while now. And while I’m not generally one for making excuses, I do have a pretty good reason for the delay this time – I broke my leg snowboarding a month ago.

What’s more, I have evidence, in the form of a photo which I marked as containing me, using the Flickr API…

Loaded onto the sledge

So, yeah, sorry about that.

A Chinese puzzle: Unicode and EXIF metadata parsing

Le Presque-Cube

At flickr, there are quite a few photos and you can browse the site in 8 different languages, including Korean and Chinese. Common metadata such as title, description and tags can be pre-populated based on information contained in the image itself, using what is commonly called EXIF information. So, it makes sense to implement this with respect to language and, above all, alphabet/character encodings. 

Un peu de lecture...?

Well, what made sense did not make so much sense anymore when using existing specifications. Here is how we coped with them.

The standards

To begin with, there is not just EXIF. Metadata can actually be written within a picture file in at least 3 different formats:
EXIF itself.
IPTC-IIM headers.
XMP by Adobe.

Of course, these formats are neither mutually exclusive nor completely redundant and this is where this can get tricky.

But let’s step back a moment to describe the specifics of these formats, not in details, but with regards to our need, which is to extract information in a reliable way, independently from how/when/where the image was created.

The EXIF is the oldest form

Old Cars Part 1
Hence, with the most limitations yet the most widespread, as this is often the case in our industry. Thus, we need to deal with it, even though it is radically flawed from an internationalization stand point: text fields are not stored using UTF and most of the time there is no indication of the character set encoding.

IPTC-IIM

In its later versions, it added the optional (Grr!!!) support for a field indicating the encoding of all the string properties in the container.

XMP

At last, text from XMP format is stored in UTF-8. On a side note, the XML based openness of this format is not making things easier, for each application can come up with its own set of metadata. Nevertheless, from an internationalization and structural standpoint, this format is modernly adequate: finally!

So, now that we know what hides behind each of these standards we can start tackling our problem.
Hide n Seek

A solution ?

Rely on existing libraries, when performant

For flickr Desktop client (Windows, Mac), we are using Exiv2 Image metadata library, which helps the initial reconciliation between all fields (especially with EXIF and IPTC-IIM contained within XMP).

The “guesstimation” of character set from EXIF

坐在问号足球上的3D小人高清图片_zcool.com.cn
We first scan the string to see if all bytes are in the range: 0 to 127. If so, we treat the string as ASCII. If not, we scan the string to see if it is consistent with valid UTF-8. If so, we treat the string as UTF-8. Checking against UTF8 validity is not a bullet-proof test. But statistically, this is better than any other scenario. At the last resort, we pick a “reasonable” fallback encoding. For the desktop application, we use hints from the user system. On windows, the GetLocaleInfo function provides with the user default ANSI code page (LOCALE_IDEFAULTANSICODEPAGE), which can be used to specify an appropriate locale for string conversion methods. On Mac OS X, CFStringGetSystemEncoding is our ally. In our case, there is no point to use the characterset of our own application, which we control and is not linked to the characterset of the application that “wrote” the EXIF.

Consolidation: the easy case

The workflow followed by the image we handle is unknown. We can have all 3 formats filled-in but not necessarily by the same application. So, we need a mechanism to consolidate the data. The easy case is for single field such as title and description. We follow the obvious quality hierarchy of the different formats. To extract the description for instance, we first look for the XMP.dc.description field, then XMP.photoshop.Headline (to support the extensibility mentioned before as a side note), then IPTC.Application2.Caption and finally the Exif.Image.ImageDescription. We only keep the first data found and ignore the others. There is only one title and one description per image: might as well take the one we are sure about.

Consolidation: it becomes even trickier for tags.

puzzle pieces
The singleness of the final result disappearing (we deal with a list of tags, not just one single tag), we cannot ignore the “EXIF” tags as easily as for the title and the description case. Fortunately, IPTC Keywords are supposed to be mapped to XMP (dc:subject). Therefore we can take into account the number of keywords that would be extracted from EXIF and the number that would be extracted from XMP. If those equal, we plainly ignore the EXIF. If they don’t, we try to match each guestimation of EXIF keyword against the XMP keywords to avoid duplicates.
 
All in one, quite an interesting issue where, per design, the final solution is going to be an approximation with different results depending on the context. Computers: an exact science?
Computer History Museum

For more information and main reference: http://www.metadataworkinggroup.com/

Photos by Groume, Raïssa Bandou, seanmcgrath, tcp909, aftab., 姒儿喵喵 and Laughing Squid

Language Detection: A Witch’s Brew?

photo by Arbron

Software developers sometimes have a tendency to cling to bits of ancient lore, even after they’ve long-since become irrelevant. Globalization, and specifically language detection, raises an interesting case-in-point.

These days, language detection is really simple in most cases – just use the “Accept-Language” HTTP header, which pretty much every client on the planet passes to you.

“But wait!” I hear you cry. “I thought the Accept-Language header wasn’t to be trusted?”

“Ancient and outdated lore, my friend” I will reply. “Ancient and outdated lore.”

It’s true that the Accept-Language header has a troubled history. Because of this, many developers regard it the way medieval villagers might have regarded a woman with a warty nose and a pet cat – it should be shunned, avoided and possibly burned at the stake.

In the early days of the web, Accept-Language really couldn’t be trusted – many browsers made no real effort to send a meaningful value, often sending nothing (or worse, defaulting to requesting English) unless the user worked their way through a complicated configuration process buried 3 panes back in a labyrinthine dialog box.

I’m rarely comfortable being categorical when talking about software engineering – there are usually too many variables, and too many specific circumstances for blanket assertions to be of use. But I can state with certainty that the “Accept-Language” header works these days, and any case where it doesn’t can (and will) be considered an error on the part of the browser vendor.

In two and a half years of running as an international site, we’ve only ever had one case where it didn’t work. Helio, a cellphone company, had a browser which was custom-built for them in Korea, and had its “Accept-Language” header hard-coded to always request Korean, something which led to much confusion for the Flickr users amongst their American customers.

When we alerted Helio to the problem, however, they were highly responsive – the next release of their software returned the correct value.

Location != Language

Perhaps driven by their superstitious fear of Accept-Language, many developers fall back on other means of determining their visitors’ preferred language. Top of the list is IP-detection – pinpointing the visitor’s current location using a lookup database. This isn’t necessarily a terrible solution, but it’s prone to a couple of problems.

Firstly, even the best IP location databases contain mistakes, or outdated information where netblocks have been reassigned over time.

Secondly, if you only rely on IP detection in order to provide language, travelers will often be highly confused when they reach their destination, connect to your site and are greeted with a language they don’t speak. This can be especially disconcerting if you’ve been using a site regularly for years. This error is easily avoided, simply by applying a cookie the first time you pick a language for a user. By using a cookie, you can guarantee a consistent experience, regardless of where in the world a laptop happens to fly.

None of this is to say that IP detection doesn’t have its place. It’s a useful fallback if you come across a user-agent that doesn’t send Accept-Language, or you have a specific case where you believe you can’t trust the header. But it general it should be just that – a fallback.

Always an option…

To guard against any possibility that you’ll detect the wrong language for a user, it doesn’t hurt to always provide language-switching as an option on every page. You’ll see that we do that on Flickr (with the exception of Organizr)…

You’ll notice that we always render the language in its native name, so that people can find their preferred language, even if they understand nothing else on the page.

Rights and Wrongs

“But!” some of you will cry. “We are a site that deals with media content. There are rights issues and legal constraints – we have to send people in France to our French site”…

…which nicely brings me to the last point of this post. Many global sites will find themselves dealing with various jurisdictional concerns. There’s no reason, however, that the “legal” logic in your code needs to be tied to the interface presentation.

Whilst certain legal texts (Terms and Conditions, etc) can provide interesting challenges, if you keep presentation as an entirely separate consideration from jurisdiction, it’s much easier to provide everyone with the best possible experience, regardless of where they are and what language they speak.

In Summary

All the above can be reduced to a very simple set of points to cut-out-and-keep for the next time you’re asked to create a Global version of a website.

  • Use Accept-Language – it just works
  • Use IP detection as a fallback for language, or a separate test to determine jurisdiction
  • Cookie your visitors’ language preferences, so you are consistent
  • Provide a simple way to switch language, everywhere
  • Treat language and compliance issues as two entirely separate problems

…at least, that’s how we’ve been doing it on Flickr for two and a half years, and it’s worked out pretty well for us so far.

Building authorized Flickr apps for the iPhone

only

You want to develop an iPhone app that interacts with Flickr content? This sounds pretty good. And the Flickr API provides you with an authorization workflow that is particularly adapted for this device. And Flickr members love their iPhones.

An authorization workflow you say, but why? Let me step back for a moment. First you may not need to authorize your application. This is needed only if your app needs to make authenticated API calls. That means if the users of your app access in a non anonymous way the Flickrverse: accessing non-public content, commenting, tagging, deleting, etc… In order for this to happen in a safe environment (for you and our users), a 3-way authorization needs to happen between Flickr, you and our mutual users, typical of social engineering interactions.

So how does this work?

Step 0

You need to setup your application.

  • Get an API key that uniquely identifies your application.
  • Configure your application to be web-based (yes, this can seem odd but this is the smoothest user experience on the iPhone) and specify the authorization callback URL (it will be used in Step 3.) I suggest not to use the http:// protocol reserved for the web but your own, like myapp://

There exists already a workflow explanation from developer stand point, but I’d like to add the specificities introduced by the use of a web-based authorization in an iPhone environment.

In the app, for each user you want to authorize:

Step 1

Create a login URL, specific to you application in the shape of http://flickr.com/services/auth/?api_key=%5Bapi_key%5D&perms=%5Bperms%5D&api_sig=%5Bapi_sig%5D and launch a web browser with this URL.

Step 2

Flickr will ask the user to sign-in into their account and present them with a page prompting them to authorize your application.

Step 3

If the user decides to authorize your application, Flickr will call the callback URL specified in Step 0. Here is the nice trick! This can actually launch back your application. All you have to do is to add a new entry for CFBundleURLTypes in your Info.plist:

 <key>CFBundleURLTypes</key>
 <array>
   <dict>
     <key>CFBundleURLName</key>
     <string>MyApp's URL</string>
     <key>CFBundleURLSchemes</key>
     <array>
       <string>myapp</string>
     </array>
   </dict>
 </array>

See Apple’s documentation on Implementing Custom URL Schemes for more details.

Step 4

Your application is launched back by Flickr (through the browser and the iPhone OS) with a frob as one of the argument of the URL. The app is effectively a Flickr auth handler. You can implement application:handleOpenURL: in a similar way as:

 - (BOOL)application:(UIApplication *)application handleOpenURL:(NSURL *)url
 {
        if (NSOrderedSame == [[url scheme] caseInsensitiveCompare:@”flickrApp”]) {
                // query has the form of "&frob=", the rest is the frob
                NSString *frob = [[url query] substringFromIndex:6];

                // Keep the frob for Step 5 and schedule Step 5

               return YES;
        } else {
               Return NO;
        }       
  }

Step 5

Your app makes an API call to convert this frob into a token. The frob is valid only for a certain time. The token will be used for the API calls that require authentication. This token is what uniquely identifies the use of your API key for a specific user on Flickr. You can save it using NSUserDefaults for next time the user uses the application without having to reauthorize the application. Even better to use KeyChain. Note that you should use checkToken to make sure the user has not de-authorized the application otherwise your authenticated call may fail for no apparent reasons.

I would like to take the opportunity of this blog post to recommend an excellent library to develop iPhone apps interacting with flickr: ObjectiveFlickr.

Have a good hack!

Jérôme Decq, from his home outside of Paris, singled handedly runs the Flickr Desktop Uploadr development, as well as hacking on making Flickr avaiable on a wide range of platforms, and photographing purple ducks.

In case you wanted to bake us a cake ….

Ian Sanchez has proposed a new geo data “test for freeness” in the spirit of the Debian project’s tests for free software.

A set of geodata, or a map, is libre only if somebody can give you a cake with that map on top, as a present. – Ian Sanchez

I mention this because the Flickr Shapefiles can be used unencumbered as a cake decoration. And we like cake. We like photos of cake as well. But we prefer cake.