00:00:17.199
Yeah. Hello everybody. Um, I just wanted
00:00:19.439
to start off by saying thank you to the
00:00:21.279
Rails Comp program committee for giving
00:00:23.119
me the opportunity here to give this
00:00:25.199
give this talk on this topic. Um it's
00:00:27.359
one that I find really interesting and
00:00:29.439
uh really fun to learn about. Um so
00:00:31.760
without further ado, this is unpacking
00:00:34.160
the Rails ITN toolkit.
00:00:37.520
Um so yeah, so first up, my name is
00:00:39.120
Chris Fun as uh as I was so introduced.
00:00:41.520
Um I've been working with Ruby and Rails
00:00:43.280
for over 10 years and currently I am a
00:00:45.840
staff engineer at a startup called
00:00:47.600
Binty.
00:00:49.280
And just really quick, just to give a
00:00:50.719
rundown of what Binty is, we are a
00:00:52.719
missiondriven SAS company that believes
00:00:54.800
every child deserves the care and
00:00:56.559
support of a loving family. Over 550
00:00:59.680
agencies use Binty in 36 states, serving
00:01:03.359
46% of children in care nationwide. Um,
00:01:06.640
over 85,000 families have used Benty to
00:01:09.040
become foster or adoptive parents. And
00:01:11.760
over 12,000 social workers use Binty and
00:01:14.479
benefit from up to 40% time savings
00:01:16.479
every day.
00:01:19.520
So this talk is about localization. But
00:01:22.080
um to start off with what is
00:01:23.680
localization and uh why should you care
00:01:26.000
about it?
00:01:28.720
Well, localization means to adapt
00:01:30.960
content from one local into another. A
00:01:33.600
classic example of this would be say you
00:01:35.600
have a Rails app like an online store or
00:01:38.400
a CMS where all of the text is written
00:01:41.280
in English and the process of adapting
00:01:43.840
that text into French or German or
00:01:46.880
Japanese for example would be called
00:01:48.799
localization.
00:01:50.320
Um you might also see localization
00:01:52.640
written as L10N. This is called a numer
00:01:56.720
where the 10 stands for the 10 letters
00:01:58.960
between the L and N of localization.
00:02:03.200
And so in that previous definition, I
00:02:04.960
specifically said localization is not
00:02:07.040
about adapting between languages, but
00:02:09.360
between local. And a local is a
00:02:12.239
combination of a language and a region.
00:02:14.480
Um, and potentially also a script.
00:02:17.040
They're often represented as codes such
00:02:19.440
as en fr--ca
00:02:22.800
or z-honty-h.
00:02:26.879
And the important bit here is that a
00:02:28.800
local doesn't represent just a language
00:02:31.360
because languages can be spoken by
00:02:33.440
different people all around the world in
00:02:35.920
different situations, different regions,
00:02:38.160
like all over the place. And these
00:02:40.080
regional variations, they affect how the
00:02:42.160
text and the data in your applications
00:02:44.000
need to be represented.
00:02:47.440
And so the last term I want to define
00:02:48.959
before we really get started is
00:02:50.480
internationalization, also called IEN.
00:02:54.720
And so if localization is the process of
00:02:57.200
adapting content from one local into
00:02:59.440
another, then internationalization is
00:03:01.920
the process of designing software so it
00:03:03.920
can be easily localized. There are a lot
00:03:06.959
of little pitfalls and blind spots that
00:03:09.360
could trip you up when it comes to
00:03:10.720
internationalization.
00:03:12.239
But luckily um Rails provides us with a
00:03:14.640
very good set of tools built right into
00:03:16.640
the framework that are very easy to use
00:03:18.959
and mostly just stay out of your way.
00:03:23.760
So, that's all well and good, right? But
00:03:26.159
why should you care? Uh, don't most
00:03:29.120
people speak English, right? Isn't
00:03:31.040
English like good enough to get to most
00:03:32.799
people? What benefits do you actually
00:03:34.879
gain from localization?
00:03:38.640
Well, first off, give me a show of
00:03:40.879
hands. Who here speaks English as a
00:03:43.120
second language?
00:03:45.440
Okay. Okay.
00:03:48.159
How about who knows someone who speaks
00:03:50.159
English as a second language?
00:03:52.319
Yeah, right. That's pretty much
00:03:53.360
everybody, right?
00:03:55.360
So, worldwide there are about 1.5
00:03:58.000
billion English speakers, but the total
00:04:00.000
world population is actually closer to 8
00:04:02.239
billion. And that means there's 6.5
00:04:05.040
billion people out there for whom
00:04:07.120
English is not their first language.
00:04:10.560
And even in the US, according to the US
00:04:12.959
census, there are 21.7% of residents who
00:04:16.799
speak a language other than English at
00:04:18.639
home.
00:04:20.959
And 8.2% of US residents say they speak
00:04:24.240
English less than very well.
00:04:27.600
And so that's all to say uh no, English
00:04:30.720
is not enough. You might default to
00:04:32.720
English and it's a it's a pretty good
00:04:34.639
place to start, but um even in the US,
00:04:37.280
English is not enough if you want to
00:04:38.800
reach the most people, especially people
00:04:41.280
who may have been underserved or
00:04:43.120
excluded from technology due to their
00:04:45.199
language.
00:04:47.840
Okay, great. So, let's say I've
00:04:49.600
convinced you, you've decided you want
00:04:51.600
to localize. Where do you start?
00:04:54.560
And luckily, again, Rails makes this
00:04:56.320
very easy. With just a small toolkit of
00:04:58.720
built-in utilities and a few gems from
00:05:00.800
the community, Rails lets us easily go
00:05:02.960
from a monolingual app to one that can
00:05:05.280
support however many languages we want
00:05:07.520
without needing to become an expert in
00:05:09.440
software localization, languages, or
00:05:11.680
linguistics. So, in this talk, we're
00:05:13.840
going to look at how you can use these
00:05:15.120
tools to translate your UI text, adapt
00:05:18.160
your UI to different data formatting
00:05:20.080
patterns, um, and even support different
00:05:22.160
rules for constructing plurals and
00:05:24.000
lists. And all at the end, we'll have an
00:05:26.639
app that feels just as natural in any
00:05:29.600
language as it does in English.
00:05:32.800
Okay, part one, translate your text. So,
00:05:35.680
the first step to translating your text
00:05:37.919
is actually to externalize your text.
00:05:40.400
And there's a lot more to externalizing
00:05:42.000
than what I'm going to be able to cover
00:05:43.280
here. Uh but the basic idea is you need
00:05:46.080
to look for hard-coded text anywhere in
00:05:48.560
your application that it might be shown
00:05:50.560
to a user.
00:05:52.639
And so let's take for example this
00:05:54.240
excerpt from a hypothetical view. It's
00:05:56.720
got a H1 tag says create post and then
00:05:59.520
there's a paragraph tag that says write
00:06:01.360
your post below. And I hope it's pretty
00:06:03.440
clear that since the text here is in
00:06:05.759
English, uh, the view can only ever
00:06:07.919
render in English because that's just
00:06:10.160
how it's written. If we wanted to
00:06:11.759
display the text in another in another
00:06:13.600
language, we we can't.
00:06:19.600
And so this brings us to the first of
00:06:21.440
Rails IN utilities, a little helper
00:06:24.400
called translate. This is available in
00:06:26.560
controllers and views and has a pretty
00:06:28.240
simple signature. First, it takes a key.
00:06:31.280
So this is an identifier like greeting
00:06:33.600
or errors not found. Um and then it
00:06:37.039
takes a bunch of different keyword
00:06:38.160
arguments and the these are used for a
00:06:39.680
ve a lot of different purposes but
00:06:41.440
mostly for variables as we'll see later
00:06:43.600
on.
00:06:45.680
So translate's purpose is to let us
00:06:47.520
dynamically render text in a different
00:06:49.280
language at runtime. It uses the first
00:06:51.759
argument the translation key to look up
00:06:54.400
what string to return based on the
00:06:56.160
current local. And if we change the
00:06:58.319
local for example from English to French
00:07:01.280
then we also change where translate will
00:07:03.520
look up the string. So instead of hello
00:07:05.840
world we get bonjour lemon.
00:07:10.080
So if we go back to the example from
00:07:11.520
before
00:07:13.840
we can replace the hardcoded text with
00:07:16.000
calls to translate. And here I'm just
00:07:18.080
using t which is an alias for brevity.
00:07:20.880
Um now the template no longer contains
00:07:22.720
the hard-coded text. Instead, it'll be
00:07:25.039
fetched at runtime from the current
00:07:26.560
local.
00:07:29.360
But of course, we do still also have to
00:07:31.360
provide the text for translated display.
00:07:33.840
And Rails does this with YAML files
00:07:35.919
which are conventionally placed in the
00:07:37.840
config/locals
00:07:39.360
directory. Um, since we only have
00:07:41.360
English text for now, we'll still place
00:07:43.120
these two strings we extracted from the
00:07:44.800
template into en.yaml. Encal code for
00:07:48.880
English.
00:07:52.479
And externalization doesn't just apply
00:07:54.319
to views, however, it applies anywhere a
00:07:57.120
string that might be shown to the user
00:07:59.120
originates. So another big example is in
00:08:02.080
controllers like flash messages for
00:08:04.080
example.
00:08:06.960
And since it's ex since the translate
00:08:09.360
method is also available here, we can
00:08:11.039
just replace the the text with translate
00:08:13.440
just like we did in the view.
00:08:18.000
Okay. So once we've externalized all the
00:08:19.840
text, the next step is we need to choose
00:08:21.759
target loces. And you may already know
00:08:25.039
what loc you want to target. But if you
00:08:27.599
don't, a good place to start is by
00:08:29.599
asking yourself who do you who do you
00:08:31.680
want to reach with your application.
00:08:34.640
Um if you're a global product, uh one
00:08:36.959
way to start is by looking at what
00:08:38.560
regions you want to target. So you can
00:08:40.560
use a site like this one. This is ethnol
00:08:42.399
log. Um to research what languages are
00:08:45.040
most spoken in those regions. And this
00:08:47.279
could give you a good idea of which
00:08:48.880
languages would help make your app
00:08:50.320
available to the most people.
00:08:53.519
If your main market is in the US, on the
00:08:55.600
other hand, like it is for us at Binty,
00:08:57.600
um you could look at the US census data
00:08:59.360
instead. If you go to data.sensus.gov,
00:09:03.040
you can browse all sorts of different
00:09:04.560
tables with data about education,
00:09:07.200
employment, health, and language. Um,
00:09:10.399
specifically here I have highlighted the
00:09:12.399
language spoken at home data set which
00:09:14.800
can give you a pretty good indication of
00:09:16.560
what languages people speak besides
00:09:18.800
English.
00:09:23.200
Okay. So once we know which languages we
00:09:25.360
want to target, we have to convert those
00:09:27.120
languages into local codes. And like I
00:09:29.600
mentioned in the beginning, a local code
00:09:31.839
is just a standardized identifier to
00:09:33.839
refer to the language. Um, the easiest
00:09:36.080
way to do this is actually just look
00:09:37.680
look up the language on Wikipedia.
00:09:39.519
Normally, you'll see the local code
00:09:41.040
there in the sidebar. Um, but you could
00:09:42.959
also use a tool like the one I have
00:09:44.640
linked here to look up the language um
00:09:46.640
and its its correct tag. Um, there are
00:09:50.320
also a few different formats for your
00:09:52.800
your local codes that you can follow.
00:09:55.120
Uh, but if you're unsure if like it
00:09:57.040
doesn't really matter to you, I
00:09:58.480
recommend sticking with the IEF language
00:10:00.959
tag format, which uses dashes here. Um,
00:10:04.160
this is the one that Rails uses
00:10:05.519
internally. um and it's the one that's
00:10:07.760
used by all the gems that I'm going to
00:10:09.120
mention today.
00:10:12.160
Okay. So, once we know the local we want
00:10:14.240
to target, now we can translate the
00:10:15.920
text.
00:10:17.839
And here again, there are two options.
00:10:20.160
On the one hand, you could hire a
00:10:22.560
language services provider, also called
00:10:24.880
an LSP. These are big organizations that
00:10:28.079
provide services like translation, proof
00:10:30.480
reading, QA, etc. Um, they also may be
00:10:34.160
able to provide translators who
00:10:36.079
specialize in specific regions or
00:10:38.560
specialize in specific domains. And so
00:10:41.360
they can provide more accurate or
00:10:43.360
culturally appropriate translations for
00:10:45.360
your text. Um, if you're a larger
00:10:48.079
organization, this might be a good
00:10:49.600
option for you to consider. But on the
00:10:52.399
other hand, there's also a DIY route
00:10:54.399
where we can use machine translation
00:10:56.000
instead. And it's a lot easier to get
00:10:58.480
going because all you need is an API
00:11:00.480
key. But of course, the caveat is that
00:11:02.720
while machine translation and AI
00:11:04.959
translation have improved a lot over the
00:11:06.959
years, they're still imperfect and they
00:11:09.120
can still struggle. Um, so it's
00:11:11.040
important that you're able to give the
00:11:12.320
AI as much context as possible about how
00:11:14.959
your text is used so it can generate
00:11:17.040
like a good translation.
00:11:20.240
Okay, but let's say we want to go down
00:11:22.800
number two and go the DIY route. So, how
00:11:24.959
do we do that?
00:11:26.880
And this brings us to the first
00:11:28.240
community gem that I'm going to mention
00:11:29.680
today. um has a somewhat generic
00:11:32.720
sounding name, i8N tasks, but what it
00:11:35.760
really is is a bunch is a CLI with a
00:11:38.240
bunch of different commands that help
00:11:39.920
you manage the loces in your app.
00:11:44.240
Um so it's just a gem. So all we have to
00:11:46.320
do is add it to the gem file and bundle
00:11:48.399
install. Um then we got to choose a
00:11:50.800
translation provider. So like Google
00:11:52.560
translate for example. Um IN task uses
00:11:57.120
environment variables. So, we put the
00:11:58.800
API key into the appropriate variable.
00:12:02.320
Um, and then finally, we can run the
00:12:04.240
translate-missing task. This will take
00:12:07.120
the source local, so English for in our
00:12:09.279
example, um, and then our list of target
00:12:11.760
loces, and translate them using using
00:12:14.720
Google Translate. And so once we've done
00:12:17.120
that, if you look in your config local f
00:12:19.600
directory, you'll see a bunch of new
00:12:21.920
YAML files for each of your target
00:12:24.399
languages with the machine translated
00:12:26.320
text.
00:12:28.320
And congrats, right? Your app is
00:12:30.480
translated. You're all done.
00:12:34.639
Well, uh, no. Unfortunately, uh,
00:12:36.959
fortunately, you're you're not done.
00:12:40.480
Part two, advanced localization.
00:12:44.639
And that's because localization is about
00:12:47.600
a lot more than just text. Especially in
00:12:50.240
software today, there's all sorts of
00:12:52.240
data um besides text that also needs to
00:12:55.200
be considered when you're trying to
00:12:56.800
provide a localized experience for your
00:12:59.120
users.
00:13:01.839
So, I want to start off just talking
00:13:03.440
about dates. Um a lot of what I'm going
00:13:05.360
to cover here is also applicable to
00:13:08.160
other kinds of data like numbers or
00:13:10.160
currency. Um but just to keep it short,
00:13:12.240
I just want to focus on dates for now.
00:13:16.160
So, here's another view. Um, we want to
00:13:18.800
add a list showing some metadata about
00:13:21.839
our posts. Um, we specifically want to
00:13:25.120
show when the post was posted. And since
00:13:27.680
we're using Rails, we can um, the post
00:13:30.800
model has a created timestamp. And one
00:13:33.600
way we could display that is by using
00:13:35.200
the strif function just to format the
00:13:37.519
timestamp. This would end up showing
00:13:39.360
something like July 10th, 2025.
00:13:42.880
Uh, but there's a problem here. Like in
00:13:45.279
the previous example, we can see some
00:13:46.959
hard-coded English text. So, let's
00:13:49.360
externalize that.
00:13:53.760
And we also need to include the
00:13:55.519
formatted date in addition to just the
00:13:58.480
static text. So, like Iuded before,
00:14:01.920
translate lets us pass variables into
00:14:03.760
the translation. So, we can pass the
00:14:05.839
formatted date as a variable called
00:14:08.160
date.
00:14:11.279
And then when we define the string in.l,
00:14:13.680
AML we can include the variable with
00:14:15.839
curly brackets. Uh this variables are
00:14:18.560
really important whenever a translation
00:14:20.160
needs to include some sort of dynamic
00:14:21.920
data because the word order of a phrase
00:14:24.720
in a different language may may differ
00:14:27.040
than your source language and you may
00:14:28.880
have to move the parts around.
00:14:32.160
For example, like in Japanese, the date
00:14:34.320
would actually come first in this
00:14:35.839
phrase. And so using the variable lets
00:14:38.399
um the translators move the parts around
00:14:40.480
into their appropriate place for the for
00:14:42.480
the language's grammar.
00:14:44.720
So this gets us pretty far, but there's
00:14:47.680
still another problem.
00:14:50.000
So this is website yall should be really
00:14:52.160
familiar with at this point. Um this is
00:14:54.639
how we write a date in American English.
00:14:59.040
But the same date in the UK would be
00:15:01.120
written like this. Uh the date the day
00:15:04.639
comes first. uh before the month if if
00:15:07.199
that's unclear.
00:15:09.600
Um and in Japanese the format is even
00:15:12.000
more different. So how do we account for
00:15:14.240
this?
00:15:16.399
Well, just like with text, Rails
00:15:18.240
provides us with a helper method to
00:15:19.920
handle data called localize.
00:15:23.440
Um
00:15:25.199
it takes the object to be localized
00:15:27.600
which is something like a date, a time
00:15:29.600
or a number.
00:15:32.320
And then it can also take some options
00:15:34.000
which are generally used to choose a
00:15:36.320
specific format other than the default
00:15:39.040
uh the default format for that data
00:15:40.880
type.
00:15:43.600
So if we go back to the previous
00:15:44.800
example, we can replace surf time with
00:15:47.680
localize instead.
00:15:50.880
And now the date format can change based
00:15:52.800
on the current local. Just like how
00:15:54.560
translate would use en.yaml to look up
00:15:57.199
translations, localize will use the YAML
00:16:00.000
file to look up the formats.
00:16:04.320
But of course we have to provide the
00:16:05.600
formats. So let's go back to.yaml and we
00:16:08.560
can enter in a default format for dates.
00:16:11.680
There's also a possibility to define
00:16:13.519
other named formats if you want like a
00:16:16.639
long format or a short format or for
00:16:19.120
some other specific purpose.
00:16:22.160
But this leaves us with a question.
00:16:26.880
How do we know what the data format
00:16:28.800
should be for another language? What
00:16:31.839
should we fill in here for Japanese?
00:16:37.040
And so to find this data, we can turn to
00:16:39.279
Unicode. And you may have heard of
00:16:41.680
Unicode and the Unicode Consortium as
00:16:44.399
the ones who are responsible for
00:16:46.079
deciding what emojis make it into your
00:16:48.079
devices every year. Um, but as it turns
00:16:51.040
out, uh, Unicode is about a lot more
00:16:53.120
than just emojis. Um, one of their the
00:16:56.160
goals of their project is to enable
00:16:58.079
software to be used by anyone in any
00:17:00.720
language anywhere in the world.
00:17:04.480
And so to further that goal, uh, Unicode
00:17:07.280
hosts the common local data repository
00:17:10.240
or CLLDR. CLLDR is this huge collection
00:17:13.839
of building blocks for software to
00:17:15.679
support different languages. Um, it
00:17:17.760
includes things like translations of
00:17:19.760
language and country names, rules for
00:17:22.240
how to sort lists in different scripts,
00:17:24.959
preferences for calendars and, uh, week
00:17:27.919
systems, and more importantly for this
00:17:30.640
part of this talk, uh, formatting rules
00:17:32.880
for many data types, including numbers,
00:17:34.960
currencies, and dates.
00:17:38.799
So for dates in particular, CLLDR
00:17:41.280
provides us with a handful of standard
00:17:43.039
formats for long, medium, and short
00:17:46.080
dates. Um, and here on screen, this is
00:17:49.679
just a small sample of what the long
00:17:51.919
format looks like in the CLLDR data. On
00:17:54.799
the right, you have the format string,
00:17:57.039
which is written in CLLDR's um, special
00:18:00.240
formatting DSL. Um, and then on the
00:18:02.960
left, you have the list of local that
00:18:05.440
the format applies to.
00:18:08.240
So that's that's all great, but how do
00:18:10.320
we use this data in Rails?
00:18:13.600
And once again, there's a gem for that.
00:18:15.760
Uh there's actually a couple different
00:18:17.360
gems that clean up and reexport the
00:18:19.520
CLLDR data, but the one I want to focus
00:18:21.520
on here is this one called Twitter CLDR.
00:18:24.799
I think Twitter is like a social network
00:18:27.440
or something. Um, at its core, what
00:18:31.760
Twitter CLDR does is it uses the CLLDR
00:18:34.480
data to format different data types into
00:18:37.200
their localized equivalents, but it also
00:18:40.160
provides a very nice interface into the
00:18:42.000
CLLDR directly, which lets us access
00:18:44.400
that data and transform it into a format
00:18:46.799
that works better with Rails. So, let's
00:18:49.440
take a quick look at how we might do
00:18:51.039
that.
00:18:53.760
So here's a little script demonstrating
00:18:55.679
how we can use Twitter CLDR's date data
00:18:59.120
reader uh to programmatically access
00:19:01.600
data from the CLLDR. So first we set the
00:19:04.480
local to English, then we set the date
00:19:07.120
style to long. Um and then when we make
00:19:10.240
a new data reader object, we can call
00:19:12.320
pattern on it. And then we get the
00:19:14.480
format for the requested local which for
00:19:17.280
English in this example is 4 m d, y.
00:19:25.120
And then we can repeat that for any
00:19:26.720
other local such as Japanese here and we
00:19:29.039
get the format back.
00:19:32.960
But you might have noticed and I kind of
00:19:35.120
alluded to this as well. Um the data
00:19:37.520
formats that we get back from those past
00:19:39.200
two slides are slightly different than
00:19:41.120
the one that we saw at the beginning of
00:19:42.799
this section. Um and that's because
00:19:44.480
CLLDR it uses its own formatting
00:19:47.039
language when it's specifying these
00:19:48.799
formats. But Rails expects us to use
00:19:52.000
Ruby's surfime language. So that means
00:19:54.480
they're not directly compatible. Uh, but
00:19:56.960
converting between them is actually
00:19:58.559
pretty easy thanks to um a map such as
00:20:03.039
this one which I copied out of the
00:20:04.640
Shopify uhworldwide gem.
00:20:09.919
So then if we combine all that stuff
00:20:11.840
together, we take the map from the
00:20:13.360
previous slide and the formats that we
00:20:15.520
got from the scripts earlier, we can
00:20:17.280
finally generate the date format for
00:20:19.120
Japanese. Um, and then we can use all
00:20:21.679
that to repeat this process for any
00:20:23.520
other language that we want to target.
00:20:27.760
So with those formats loaded, we can see
00:20:30.080
that localize will pick them up
00:20:31.679
automatically to generate the correct
00:20:33.600
format string in English
00:20:37.039
or in Japanese. So neat.
00:20:42.240
Okay. Um let's talk about plurals.
00:20:47.840
So let's add another list item to our
00:20:49.679
view. We want to show how many comments
00:20:51.919
this post has. Uh, we've all probably
00:20:54.480
done the thing where we just stick a
00:20:56.720
parentheses s onto the end of the word
00:20:59.200
if it has maybe has to be plural.
00:21:02.559
Um, and maybe you've even done something
00:21:04.720
a little more clever like this where you
00:21:06.799
conditionally add the s if the count is
00:21:09.440
more than one. Um, there's a couple
00:21:12.480
problems here. So, first off, of course,
00:21:14.799
we have hard-coded text. There's the
00:21:16.799
word comment in this view. Uh, but
00:21:19.440
there's also some hard-coded logic.
00:21:22.720
uh specifically the handling of that
00:21:24.559
plural s. And although English is
00:21:27.520
generally pretty regular when it comes
00:21:29.280
to plurals, there are many examples of
00:21:31.280
words that have no plural like sheep or
00:21:35.280
maybe they have an irregular plural like
00:21:37.360
mice. So how do we account for this in
00:21:40.240
localization?
00:21:43.120
And once again though, luckily Rails has
00:21:45.840
support for plurals as well baked right
00:21:47.840
in. So we start by externalizing the
00:21:50.320
string as before, but then we also pass
00:21:52.720
this special variable called count with
00:21:55.280
the value being the number of comments
00:21:57.039
or the number of things that we're
00:21:58.320
counting.
00:22:02.559
And then when we go to the local file to
00:22:04.960
write the translations, instead of
00:22:06.320
writing the strings directly, we supply
00:22:08.640
these two special subkeys. Uh for one we
00:22:13.039
write the message as if there was only
00:22:14.640
one item and then for other we write the
00:22:17.679
message in the plural case. And these
00:22:19.760
keys are what signal to Rails that this
00:22:21.919
this translation it has pluralization
00:22:24.080
logic. So it's going to look at the
00:22:26.240
count to choose which variant to use.
00:22:32.080
But once again how do we handle other
00:22:34.720
languages? English is relatively simple
00:22:37.840
as we just described, but other
00:22:39.679
languages are different. Chinese and
00:22:42.080
Japanese, for example, they generally
00:22:44.240
don't have special plural forms. Uh,
00:22:46.799
languages like Hebrew, for example, have
00:22:49.280
a special form for just two items. And
00:22:52.400
other languages like Russian, they have
00:22:54.320
even more categories. So, how do we know
00:22:57.120
which categories a language needs and
00:23:00.320
how do we ch how we're supposed to
00:23:01.840
choose between them?
00:23:04.159
So once again we can turn to CLLDR.
00:23:07.120
CLLDR contains all of this data on the
00:23:09.200
plural categories used by every language
00:23:11.360
it covers and it also includes the rules
00:23:13.919
for how to choose between uh which form
00:23:17.280
for a given count. So here on on screen
00:23:20.559
it's this is the data for Irish which
00:23:23.760
uses a total of five different plural
00:23:26.000
categories.
00:23:30.559
So how do we how do we use this data?
00:23:32.320
How do we get into Rails? And once
00:23:33.919
again, there's a community gem for that.
00:23:36.000
This one is called Rails ITN. This is
00:23:38.720
another very generic name, but um what
00:23:42.000
it is is like a it's a combination of a
00:23:45.120
bunch of different local data that is uh
00:23:48.080
just really easy to plug into your Rails
00:23:50.240
application.
00:23:53.679
One thing it does include is the
00:23:55.200
pluralization rules for many languages
00:23:57.520
coded as Ruby classes that are directly
00:23:59.840
usable by um Rails's I8NN. So after you
00:24:04.080
install the gem, it will allow translate
00:24:06.480
to look up the appropriate pluralization
00:24:08.559
algorithm for the current local. The
00:24:11.679
major caveat here of course is that you
00:24:13.679
still need to provide the actual
00:24:15.600
translations for the different
00:24:16.960
categories in all of the languages you
00:24:19.279
want to support.
00:24:21.760
So here's an example of what that might
00:24:23.600
look like. Um on the left we have
00:24:26.159
English like from the previous slides
00:24:29.120
and then on the right we have Russian.
00:24:31.360
So we know that Russian in addition to
00:24:34.480
one and other it also uses few and many.
00:24:37.760
So when we make the translations, we
00:24:39.760
have to make sure that the Russian
00:24:41.679
includes few and many variants for this
00:24:44.799
this specific key in addition to the one
00:24:48.000
and other.
00:24:51.200
And with that data um input into our
00:24:54.480
local files, when we run the code, we
00:24:56.720
can see that the phrase gets correctly
00:24:58.400
pluralized based on the local. Neat.
00:25:09.440
Okay, finally let's talk about lists
00:25:16.000
to our post metadata. Post might have
00:25:19.039
many tags. So, we're just going to list
00:25:21.200
them all by joining the tags together
00:25:22.880
with a comma.
00:25:26.159
Of course, can't have hard-coded text.
00:25:28.400
So, let's externalize and apply
00:25:30.240
translate. Uh, but there's one piece of
00:25:33.520
hard-coded text left here. Can Can
00:25:36.240
anybody anybody spot it?
00:25:39.760
Right. Yeah. Yeah. It's the It's the
00:25:41.360
comma.
00:25:44.320
So, different languages may use
00:25:46.640
different punctuation, especially if
00:25:48.799
they use a different script than
00:25:50.400
English. My favorite example of this is
00:25:52.720
Japanese, uh, which uses this very
00:25:55.520
special looking comma on screen here.
00:25:58.240
And no, that is not a space inside the
00:26:00.960
quotes. That is padding that is built
00:26:03.520
directly into the comma character.
00:26:07.120
Um, additionally, languages may have
00:26:09.360
different rules about how punctuation is
00:26:11.279
used, such as whether a punctuation mark
00:26:14.000
is used to join two items of a list or
00:26:16.960
whether there is a special connector
00:26:18.960
word that's used instead. Um, this data
00:26:22.320
is actually already loaded inside of
00:26:24.320
RailsN, so we don't need to do anything
00:26:26.320
else to bring it in. But how do we how
00:26:29.039
do we put it to work?
00:26:32.480
So going back to our example, we can
00:26:35.520
replace join
00:26:38.400
with two sentence. Two sentence is an
00:26:41.520
array extension provided by Rails. Uh
00:26:43.919
but you can also call it as a helper if
00:26:45.600
you don't want to use the the monkey
00:26:47.360
patch. It simply joins the items of the
00:26:50.320
array by looking up the appropriate word
00:26:52.240
connectors from the local YAML.
00:26:56.960
And this is just an example showing two
00:26:59.039
sentence in action. So we can see just
00:27:01.200
by changing the the local uh two
00:27:04.080
sentence will look up the correct
00:27:05.520
punctuation marks and connectors to join
00:27:08.159
the array together. So we can see in um
00:27:11.279
Japanese here, Japanese actually doesn't
00:27:13.200
use a connector word at the end. It just
00:27:15.919
uses punctuation all throughout. Whereas
00:27:18.240
we can see in English we get the commas
00:27:20.720
and then we get a special connector word
00:27:22.400
for the last item.
00:27:26.799
Okay. Uh wrapping up. So to recap, so by
00:27:30.880
this point we've got a pretty robustly
00:27:32.960
internationalized app. We've
00:27:34.799
externalized all of the hard-coded text
00:27:37.039
and all of the data formats. We have
00:27:39.520
machine translated the local files and
00:27:42.080
we can easily update those translations
00:27:44.080
as the source text changes. And we're
00:27:46.960
leveraging data formats, plural rules,
00:27:49.600
list connectors, all from the standard
00:27:51.600
CLDR data. And to get all of this, we
00:27:54.559
only had to use four small gems. I8NN,
00:27:57.840
which is bundled with Rails by default,
00:28:00.640
and the three other gems that I
00:28:02.480
mentioned mentioned here. IN tasks,
00:28:05.200
Twitter CLDR, and Rails IN.
00:28:10.399
So if you have one takeaway from this
00:28:12.240
talk, I hope it's this. Rails makes
00:28:14.559
internationalization very easy. You
00:28:17.120
don't have to become an expert in
00:28:18.720
software localization, languages,
00:28:21.200
linguistics, or the Unicode standard.
00:28:23.840
All of this knowledge is already
00:28:25.520
collected for you and right there
00:28:27.919
available at your fingertips in this
00:28:29.919
very easy to consume, very easy to use
00:28:32.399
package.
00:28:35.120
So that's it. That's that's all I have.
00:28:37.039
Thank you and good luck on your
00:28:38.799
localization journey.
00:28:45.679
These are my uh links and credits. Um
00:28:48.240
and yeah, if you want to ask me ask me
00:28:50.240
questions, I'll stick around by the by
00:28:52.159
the stage. Um feel free to come up and
00:28:54.000
and talk to me.