ibm-fed-globalization



ibm-fed-globalization

1 0


ibm-fed-globalization

A presentation about internationalization and globalization for the IBM Front End Design roadshow

On Github leereamsnyder / ibm-fed-globalization

IBM Design

Globalization & Internationalization (for IBM Bluemix)

Who am I?

Lee Reamsnyder

Front-end developer for IBM Containers

On Alchemy Slack: @lee

ljreamsn@us.ibm.com

@leereamsnyder

Put your speaker notes here. You can see them pressing 's'.

What is Globalization?

Globalization is the acknowledgement that not everyone speaks English all the time

Date formatting

US: 8/31/2015

China: 2015.08.31

France: 31-08-2015

Canada: ¯\_(ツ)_/¯

Source: Date formats by country

Everyone formats dates just a little bit (or a lot bit) differently. Here's August 31, 2015 with a short datestamp in a couple of countries. Note the different order of the numbers; the different use of 8 or zero-8; different separators expected between elements. I love you, Canada, but you're a hot mess. Canada, depending on application, uses all of the above and then some.

Number formatting

US: 4,294,967,295.00

France: 4 294 967 295,000

Italy: 4.294.967.295,000

Source: http://docs.oracle.com/cd/E19455-01/806-0169/overview-9/index.html

Here's a big honking number in a variety of formats. Note the different separators, and sometimes the decimal point is a comma. This stuff is so hard-coded in our heads that I can't even see those other two as large numbers.

Currency

Not everyone uses US Dollars for everything! And they don't like when we rub that in their faces. Like, not everyone wants to do currency conversions in their head, ya know?

Left-to-Right

Not everyone reads left-to-right!

Why listen to me?

Why should you listen to me? "The guy's got some skill with GIFs, but what does he know about all this complicated stuff?" Well, I hadn't done any of this before, and I had to figure it out. Often painfully. So, learn from my many, many, many mistakes.

Start early

I studied personal finance in college. There's an old saying that the best time to start saving for retirement was 20 years ago; the second-best time is right now. The same goes for thinking about translating your application. A globalized application is not super, super different from a non-globalized application, but there are enough differences that the sooner you start planning for it, the less of a pain it will be when you wake up one morning and your PM is saying your app needs to be globalized.

Re-think strings

This is the biggest mental leap you have to make: Every time you use a string of text, you have to not just write it. You can't have hard-coded text anywhere.

What strings look like now

locales/en/resources.json:

{
  "backToDashboard": "Back to Dashboard",
  […]
}

locales/es/resources.json:

{
  "backToDashboard": "Volver al Panel de control",
  […]
}
So this is what strings look like now. We keep them in a set of JSON files in a bunch of folders by language. Those JSON files have keys like "backToDashboard"; the values are the corresponding text, in that language.

Obvious strings

Nope:

<a href="#">Back to Dashboard</a>

Yep:

<a href="#">{ __('backToDashboard') }</a>

Sometimes, strings are obvious. Text in an element that gets displayed. Easy. Every place that you would have text, you will instead have a function that looks for a translated version of that text. Every single time. I'm showing these examples using the Dust template language, and I've created a shortcut "double underscore" function to look up text by key.

Less obvious strings

Nope:

<img alt="A lovely sunset" src="sunset.jpg">
var text = 'A thing I want to say';

throw new Error('The request failed')

Yep:

<img alt="{ __('lovelySunset') }" src="sunset.jpg">
var text = __('thingToSay');

throw new Error( __('errors.requestFailed') )
When you start thinking this way, you realize you're using strings everywhere. Attributes, variables, messages… they're everywhere. And you can't just write one anymore. This is why I said it's better to start early.

Multi-part strings

Nope:

var greeting = 'Hello ' + user.name + '! You are '
  + user.age + ' years old'

Yep:

Resources:

"greeting": "Hello __name__! You are __age__ years old"
"greeting": "¡Hola __name__! Tiene __age__ años"

Later…

var greeting = __('greeting', user)
You can totally forget about building messages one tiny piece at a time, right? What if subject/verb order is different? What if there are differences in punctuation? You can see in the second example a little bit of that with Spanish. The library I use lets you pass in an object and replace variables that way. Personally, dealing with strings in this manner is a lot more pleasant than piecing them together with "+" sign.

Text in images

Everyone remember my astro dog? You see that there's text in the image? That's a no-no! How do you translate that? It's baked into the file! That means if you're using charts, graphs, screen captures, anything with text… you might have to rethink that. Or be ready to provide images with the text translated into every language you support.

Charts are images too!

The idea of "images" can be fairly abstract, so here's another in our application. It shows you CPU usage over time. It's like 400 pixels. It's a globalization nightmare. You've got numbers; you've got dates; you've got times; you've got labels. All of them have to be altered for different languages. We were generating these with a JavaScript library and SVG on the fly. Does your charting library let you customize every string of text?

Outside libraries

Which leads us to another consideration: if you're using someone else's code, do they let you customize the strings? What I'm showing here is our UI using one of the many libraries for creating modal dialogs. Do you know why we chose this particular one? Because it let us override the default "Cancel" and "OK" buttons with our own translated text. If you use libraries that use text anywhere and don't let you override it, well, you really can't use that library.

Give up control

If you've worked in the web, you should already know this, but you have to abandon any sense that you can control the size of things. We always get them: you get a mockup from your designer. They want two buttons that say "Save" and "Reset". And they've given you in the mockup juuuuuust enough space for those exact words.
Did you know that the tiny little word "Save" in French becomes "Suavegarder"? That's awesome. Don't get me started on German. Conversely, in Chinese, those are 2 characters long. Give up any sense that you can control even the content you think you can control. Design your UIs for flexibility. Design for lists and collections to collapse gracefully. You can see here these buttons were side-by-side in English when they fit, but collapse above-and-below when there isn't. DO NOT FIGHT THAT. Forget about using width and height in your CSS pretty much ever. They will always come back to bite you when your text is suddenly several times larger or smaller. You can also push back against designers when you get mockups that assume that buttons will always be the same size.

Use libraries

This, I feel, should also be obvious, but don't write your own code to handle this. These are not original problems, and a lot of smart people have made some very smart libraries to help you out.

Meet i18next

http://i18next.com/

Wha?

Language detection; message formatting

Where?

Node.js and browsers

As far as I know, i18next is the de-facto standard for services on Bluemix. We use it to control the list of supported languages, detect a user's language (more on that soon), store messages, and grab translated messages as I was showing earlier. It runs both server-side and in the browser, which is handy so both your server and client can use the same messages.

jQuery Globalize

https://github.com/jquery/globalize

Wha?

Date/time formatting; Numbers; Relative times

Where?

Node.js and browsers

Unfortunately for us i18next doesn't do everything, so I supplement it with a relatively new library from the jQuery Foundation called "Globalize". It helps you format dates, times, numbers, currency, relative times… It's pretty nice. Don't let the name throw you: if you're one of those post-jQuery developers, jQuery is not required. It's just from the same team. There are a bunch of other libraries, but most of the teams in our little world have settled on i18next and Globalize, and it helps to be using the same stuff so you can compare notes.

Read the manual

For any library you choose, be sure to read the dang manual. Even highly-customizable libraries have defaults, and you should familiarize yourself with the assumptions that the library is making.

Don't trust the defaults

i18next defaults options:

{
  useCookie: true,
  fallbackLng: "dev",
  fallbackOnNull: false
}
These are some of the default options for i18next. They are troublesome. By default, i18next tracks languages with a cookie. As we'll discuss in a moment, that's a dumb idea. It also assumes that there will be a language to fallback to named "dev", which is a not a language I've ever heard of. Change that to English. It also doesn't try to use a fallback by default. Maybe you like that.

Never trust a browser

Particularly, when it comes to detecting what language the user wants. YOU HAD ONE JOB, GEESH

Client-side language detection

Nope :(

navigator.language

Nope :(

navigator.languages

Nope :(

navigator.userLanguage
Here's an incomplete list of issues around detecting language in the browser. That first one, navigator.language, in Chrome is permanently the language in which you downloaded Chrome. It doesn't change with your preferences. Also, not in IE. That second one, languageS, Chrome will inject extra languages in there against your will. Also, not in IE. that userLanguage one, that one is Internet Explorer, but it reflects your system preference, not the language preference in the browser. Cute. If anyone has a reliable way to detect the preferred language in the browser, I'd love to hear it.

Headers are your friend

The only reliable way I've found to detect language is server-side, using Accept-Language headers. Despite all that weirdness with the DOM, browsers always get this right. Only detect languages using those headers, and only do so server-side.

Keep it D.R.Y.

I shouldn't have to tell this to programmers, but try to keep things DRY: Don't Repeat Yourself.

Don't repeat languages

/de
/en-US
/es
/fr
/it
/ja
/ko
/pt-BR
/zh-CN
/zh-TW
Here's one example. You'll start out with this, the 10 standard languages we support. But the the testers will do something like set their browser to "English", but not specifically "US English."

Don't repeat languages!

/de
/en      #same as en-US
/en-US
/es
/fr
/it
/ja
/ko
/pt-BR
/zh-CN
/zh-TW
The quick fix there is to copy "en-us" to "en" and call it a day.

Don't repeat languages! Really!

/de
/en      #same as en-US
/en-US
/es
/fr
/it
/ja
/ko
/pt      #same as pt-BR
/pt-BR
/zh      #same as zh-CN
/zh-CN
/zh-TW
Oh, except you have to do that for Portuguese and Chinese also.

Stop repeating languages, gosh!

/de
/en      #same as en-US
/en-US
/es
/fr
/it
/ja
/ko
/pt      #same as pt-BR
/pt-BR
/zh      #same as zh-CN
/zh-CN
/zh-Hans #same as zh-CN
/zh-Hant #same as zh-TW
/zh-TW
Oh, except that there are sometimes two different codes for the same Chinese language, depending on the browser COUGH Internet Explorer COUGH This doesn't even fit any more. You can see how this gets unsustainable. And you have to remember to copy the things every time. Not good.

Don't repeat languages

Better!

/de
/en
/es
/fr
/it
/ja
/ko
/pt
/zh
/zh-TW

Express configuration

app.use('/locales/zh-Hant', express.static('locales/zh-TW'));
One trick is to only get specific if you absolutely have to. For example, most translation libraries assume if you say "Portuguese" but don't specify a country, they assume you meant Brazil. Same with English and the United States. So you can work with only the language code instead of languages and countries. If you can't get away with that, have your server serve a single directory of files to multiple requests so you don't have to copy the files all over the place. In that second snippet, the server will use the same files for a request for zh-Hant as it would for the identical zh-TW. One line of code, so much sanity saved forever.

Love your testers

You probably already do, but make good friends with your translation testers. They'll do all kinds of unexpected stuff, they'll uncover a huge number of bugs, and they'll keep you honest. They're your first line of defense. Love them.

Use the machines

Definitely look into automating as much as you possibly can. For example, our app has a task that runs that scans all of our HTML and JavaScript files, sees every time we run the i18next functions, collects the keys, and automatically updates the resources JSON files. So every time we save a file, the JSON has already been updated. If we stop using a string, this task has already removed it. I estimate that this little task has saved me approximately a billion years. At least that's what it feels like.

Just do it

This last bit is a tiny bit controversial. Once you start translating your product, now you've got an automatic delay: when you change or add text, it has to be translated. Particularly very close to launch, this means you hold back on fixing things, because you might not get the translations back in time. I try not to roll that way. If you have a fix, just do it! I think it's better to address problems immediately and push the fix, even if that leaves a small part of your product untranslated for a while. You might disagree! That's OK! But have this discussion with your product managers, testers, and translation teams so you're all on the same page, at least.

So what?

So that's all interesting, but you might be asking, "So what?" OK: brass tacks…

It makes us money

I'm super bummed I used that money fight GIF already. But this is great too. Globalization makes us money. It is a competitive advantage. We can sell our products to more people. That's a pretty solid reason to do it, but let me put it another way…

We are not jerks

Life is hard, ya know? Imagine you're trying to get something done, and then BAM some application you're trying to use starts talking to you in a language you don't understand. Now your life is that much harder. And it didn't have to be! Look: the only people who should be making life harder for other people are jerks, and we are not jerks here.

Unexpectedly nice

International support is an often-unexpected bit of niceness. It reinforces the connection between us and our users, even if that connection happens through our stuffy enterprise software. It shows that we care. It's a small way to make life easier for people. I think we can all get behind that.

Learn more

IBM Globalization Design Guidelines

http://www-01.ibm.com/software/globalization/guidelines/index.html

Using i18next for an IBM/Bluemix Service

https://hub.jazz.net/project/ljreamsn/i18next-guide/overview

Gulp i18next Parser task

https://github.com/i18next/i18next-parser

Some resources out there. That second one is a readme I wrote detailing some of the "Gotchas" of getting an app globalized to run as a part of Bluemix. The last one is the template watcher that I mentioned that keeps our resources updated.
IBM Design

IBM Designcamp :: © 2015 IBM Corporation