How to localize your API

Full disclosure: I was born and live in the USA, American English is my native language, and I spend USD every day. However, I work with customers in 18 countries and regions on five continents, and the list is getting bigger all the time.Internationalization localization and currency

Operating a business in the global market is not as trivial as it might seem, if you’ve never seen it through. Everyone not only speaks different languages and has different currencies (not to mention driving on the different sides of the road), but they also have different cultural norms. In many situations, we can merely provide raw values for dates and currency and avoid formatting. However, when content includes localizable information, especially dates and currencies, display formatting needs to be sensitive to the localized culture.

If you’re considering making the jump off of your own soil to the rest of the globe, hopefully this will give you some insights.

Terms

Let’s go over some terms you might have heard in regards to these topics:

  • Internationalization (I18n) means structuring applications in a way that makes it possible for them to be localized.
  • Localization (L10n) means making an application work for a particular market, such as French-speaking Canadians.
  • Native Language Support (NLS) essentially describes the translation of content into the correct language and locale.

Example of the problem

Now a practical example of localization issues when we change the parameters on a simple piece of text:

  • English, USA, US Dollars – “Price: $99.00, Delivery: 8/1/2013 8:15:00 PM”
  • Spanish, Mexico, Mexican Pesos – “Precio: $1216.28, Entrega: 1/8/2013 20:15:00″

Note the differences in language, currency, and date format.

In addition, there are particular issues in ambiguity with currency symbols, especially when operating in foreign markets where symbols could potentially collide with local currency.

  • English, Canada, Canadian Dollars – “Price: CA$1.00, Delivery: 2013-08-01 8:15:00 PM”
  • English, Canada, Peruvian Pesos – “Price: 464.34 CLP, Delivery: 2013-08-01 8:15:00 PM”

Note that currency symbols changed, as well as the position of the currency. However there are no changes to language or date formatting in this scenario.

ISO Standards

In order to formulate a strategy, it’s best to know what the international standards are for specifying globalization parameters. Language, country and currency are most often specified.

Localization parameters

Specifying localization parameters can be done via HTTP headers, query string, or potentially in content. Query string approaches are inadvisable in HTTP-based APIs (dare I say REST), as this implies a ‘query’, and in some rare cases can affect GET caching. Headers typically work quite nicely as URIs are unaffected and HTTP actually gives us some of this built-in.

HTTP Headers

Accept-Language and Content-Language headers
Armed with our knowledge of language and country codes, we can utilize the built-in header in HTTP, “Accept-Language”. This works in the same content negotiation pattern as “Accept” and “Content-Type”, except it utilizes Language Tags, “where any two-letter primary-tag is an ISO-639 language abbreviation and any two-letter initial subtag is an ISO-3166 country code”, as administered by IANA.
The best description of this I’ve seen worked through is in one my favorite books, RESTful Web Services Cookbook, Section 7.3:

The protocol for language negotiation is similar to media type negotiation. The client
expresses its intent by supplying an Accept-Language header with acceptable languages
and their q header parameter values, and the server decides which one to use for the
response.

# Request
GET /movie/gone_with_the_wind HTTP/1.1
Host: www.example.org
Accept-Language: en,en-US,fr;q=0.6
{noformat}
{noformat}
# Response
HTTP/1.1 200 OK
Content-Type: application/xml;charset=UTF-8
Content-Language: en
Vary: Accept-Language

1936

This approach is best suited when representations in different languages differ only in
terms of the language used for any human-readable text in the representation, as in the
following representation:

# Request
GET /movie/gone_with_the_wind HTTP/1.1
Host: www.example.org
Accept-Language: en,en-US,fr;q=0.6

Non-standard HTTP headers

For currency, the HTTP spec does not provide us with any standards. As such, we are left to define custom headers. Prefixing the header name with “X-” signals to many servers that this should pass through, and often prevents collision with standard headers.
“X-Currency” is often a great choice for setting currency in “Accept” for the request, and consequently the same thing in the “Content-Type” in the Request (for POST/PUT) and Response.
The key to educating our API consumers is that the ISO 4217 standard provides us with various means to communicate currencies. As part of this spec, 2-3 character codes can indicate specific currencies (great for input/request), and within any formatted currency values, there are also currency symbols defined (useful in output/response).

UPDATE: As was pointed out to me by John Sheehan, and readers on Hackernews, the “X-” has been deprecated in RFC 6648, also more detail here. A more appropriate value might be “-Currency”. It also might be better to use “Accept-Currency” on request, and “Content-Currency” on response.

To continue with the example above, but for currencies:

# Request
GET /movie/gone_with_the_wind HTTP/1.1
Host: www.example.org
X-Currency: USD

Note that the three-letter currency code should be utilized here. This helps dictate the formatting of any currency in content, such as:

  • Currency symbol: e.g. “$” US Dollars, “€” Euro
  • Decimals and commas: e.g. “1,000,000.00″ in USA, “1 234,56″ in Portugal
  • Symbol alignment: e.g. “$1,000,000.00″ in USA, “1 234,56$” in Portugal (same as above but with symbols, note right alignment for Portugal)

Summary

The only real support we get from HTTP is “Accept-Language” and “Content-Language”. For currency, we’re often forced to provide an artificial standard to our API users. The good news is, by following the established ISO 4217 standard, currencies are very crisply communicated, both in terms of API input and output.
Language, country, and currency typically describe all the globalization parameters. Depending on your web strategy, there could be other parameters to consider. Utilize “X-”, and consider including your company/project name in order to avoid conflict with existing HTTP standards or other companies’ usages.
Learn the existing international standards for language, country, currency, and dates. Avoid formatted outputs as much as possible. Sticking to these fundamentals should keep you out of trouble.

Please join in the discussion on this article over on Hacker News and reddit.

7 thoughts on “How to localize your API

  1. Mathieu Fenniak

    Great article, thanks for publishing it.

    Typically I think of prices and other money values as fixed in a specific currency, but they can also be displayed approximately in other currencies for localization. This article doesn’t touch on a write-capable API. Would you accept a client-side request for a specific currency (eg. X-Currency header) and actually affect something material, like the charge for an ecommerce purchase?

    @mfenniak

    Reply
    1. Jason Harmon Post author

      In write-capable scenarios, I would recommend some additional internal systems design. Namely ensuring that users have a currency preference set in their account preferences, and only allow updates in that currency. In scenarios where they have no ‘account’ (unlikely with money involved, but possible in financial industries), the X-Currency header can be utilized as a non-negotiated request header, specifying the currency in the POST/PUT data.

      Reply
  2. Andrei Neculau (@andreineculau)

    For future readers, a clarification and a reminder to KISS:

    1) ISO 4217 only holds information about the currency code (alpha & numeric) and number of decimals. For the rest, it’s really wrong to say the code “helps to dictate” but “helps to make fragile assumptions” – e.g. EUR isn’t to be appended to the amount, just because it’s EUR; in English, Latvian and other locales, it’s supposed to be prepended. Point is: use locale to determine things currency format settings. As an American (en-us), you want the code/symbol to be prepended USD 12, $12, even if we’re not talking USD – €12. Same goes for formatting an amount – thousand symbol (dot, comma, space, nothing), decimal symbol (comma, dot)

    2) I might be in the wrong here, but I see no reason why one would make the distinction between language and locale in terms of the Accept-Language header. If the API is localized (though IMO it’s the client that should care about localization), then Accept-Language: it-it should output localized content (not just take care of translation, while leaving out the format of 12$ as is).

    3) Before one dives into using X-Currency, one has to ask a few questions
    - is my product/service/whatever offered in more than a handful of currencies? If not, then consider just dumping all prices and all available currencies. e.g. instead of {price:10, currency:”EUR”}, output {prices: [{amount:10, currency:"EUR"}, {amount:20, currency:"USD"}]
    - I use more than a handful of currencies, but am I in the business of currency conversion? Most probably not, because your prices are biased towards marketing and other factors, and thus you fix your prices manually – you don’t just say I want to make 100 EUR on this product, and then people pay the equivalent in their currency. If you are certain of it, build up or recommend a 3rd party currency-conversion service endpoint. This way, your regular output is still in 1..5 major currencies; if another currency is desired, the client can use the service endpoint to convert prices.

    Thanks

    Reply
  3. Pingback: Implementing API Content Negotiation | API UX

  4. Jason Harmon

    Just to address some finer points you made Andrei:
    #2: Locale is critical in translations. The easiest examples are fr-FR vs fr-CA and pt_BR vs pt-PT. French in Canada translates quite different from French in France, as well as Portuguese in Brazil and Portugal.
    3. If there are multiple currencies at play in one body of content, specifying currency alongside amount is probably useful. In most situations, one currency is at play per call, so the headers are a more consistent manner across the platform to specify currency. As far as currency conversion goes, it’s definitely a risky business. There’s lots more work in providing business process on the backend that can mitigate those risks than there are in technology solutions from an API perspective.

    Reply
  5. Pingback: Interview with Jason Harmon, API Architect at uShip.com

  6. Pingback: When API time zones make the difference

Leave a Reply