Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Unpacking the Rails I18n Toolkit

Unpacking the Rails I18n Toolkit

Originally presented at RailsConf 2025 in Philadelphia.

Avatar for Chris Fung

Chris Fung

July 10, 2025
Tweet

More Decks by Chris Fung

Other Decks in Programming

Transcript

  1. Unpacking the Rails I18n Toolkit Chris Fung - RailsConf 2025

    Internationalization on Rails 🌐 🚄 Source: Photo by Isaac Chou on Unsplash
  2. • We believe every child deserves a loving family! •

    550 + agencies in 36 states, serving 46% of children in care nationwide • 85,000 + families; 12,000 + social workers; 20 - 40% time saved What is 💙 Binti?
  3. Locale A combination of a language and region (and script).

    Often presented as codes: en, fr-CA, zh-Hant-HK
  4. 1.5 billion / 8B Worldwide English speakers / total population

    Source: https://www.statista.com/chart/26884/languages-on-the-internet/
  5. 21.7% US residents speak a language other than English at

    home Source: https://www.census.gov/acs/www/about/why-we-ask-each-question/language/
  6. 8.2% US residents speak English less than very well Source:

    https://www.census.gov/acs/www/about/why-we-ask-each-question/language/
  7. Rails makes this very easy! 1. Translate UI text 2.

    Adapt to different data formatting patterns 3. Support different pluralization grammar 4. Support different rules for constructing lists
  8. 1.1 - Externalize! translate(key, **options) An identifier, like “greeting ”

    or “errors.not_found ” Used for various purposes, mostly variables
  9. 1.1 - Externalize! I18n. wi th_locale(:en) { translate("greeting") } #=>

    "Hello, world!" I18n. wi th_locale(:fr) { translate("greeting") } #=> "Bonjour, le monde!"
  10. 1.1 - Externalize! class PostsController < ApplicationController def create unless

    @post.valid? flash[:error] = "Could not save" end end end
  11. 1.1 - Externalize! class PostsController < ApplicationController def create unless

    @post.valid? flash[:error] = t("errors.could_not_save") end end end
  12. 1.2 - Choose locale codes • Use online tools to

    find appropriate codes for each language • https://r12a.github.io/app-subtags/index.html • Follow a standard format for your locale codes • IETF language tag: en, pt-BR • GNU language code: pt_BR
  13. 1.3 - Translate! 🚪 Hire a Language Services Provider (

    LSP ) 🚪 AI Machine Translation ( MT )
  14. 1.3 - i18n-tasks • Add gem 'i18n-tasks', group: :development •

    Choose a translation provider, e.g. Google Translate • Put your Google Translate API key in GOOGLE _T RANSLA TE _API_KEY • Run i18n-tasks translate- m i ssing -f en -l <locales…>
  15. 2.1 - Localizing dates localize(object, **options) A date, time, or

    number Used for various purposes, like different formats
  16. 2.1 - twitter_cldr require ' tw i tter_cldr' locale =

    :en date_style = { type: :long } T wi tterCldr::DataReaders::DateDataReader.ne w ( locale, date_style ).pattern # => "MMMM d, y"
  17. 2.1 - Shopify/worldwide CLDR _T O_ S T R FT

    IM E _M AP = { cccc: "%A", # Weekday, full form (e.g. "Wednesday") EEEE: "%A", # Weekday, full form (e.g. "Wednesday") LLLL: "%B", # Month, full name (e.g. "December") MMMM: "%B", # Month, full name (e.g. "December") ccc: "%a", # Weekday, abbreviated form (e.g. "Wed.") EEE: "%a", # Weekday, abbreviated form (e.g. "Wed.") LLL: "%b", # Month, abbreviated name (e.g. "Dec.") MMM: "%b", # Month, abbreviated name (e.g. "Dec.") dd: "%02d", # Day of month, zero padded (00..31) hh: "%02l", # Hour, zero padded (01..12) HH: "%02k", # Hour, zero padded (00..23) mm: "%M", # M i nute (00..59) MM: "%02m", # Month of year, zero padded (01..12) ss: " %S ", # Second (00..60) yy: "%02y", # Year, t w o digits (e.g. 19) a: "%P", # am/pm indicator b: "%P", # Should be am/noon/pm/ m i dnight; use am/ pm B: "%P", # Should be "in the morning"/"in the evening"; use am/pm d: "%-d", # Day of month, no padding (1..31) E: "%A", # Weekday (e.g. Wednesday) h: "%-l", # Hour (1..12) H: "%02k", # Hour (0..23) m: "%-M", # M i nute, no padding (0..59) M: "%-m", # Month of the year, no padding (1..12) s: "%-S", # Second, no padding (0..60) v: "%z", # T i m e zone offset (e.g., -0500) y: "%Y", # Year, all digits (e.g. 2019) z: "%z", # T i m e zone offset (e.g., -0500) xxx: "%:z" # T im e zone offset w i th colon (e.g., -05:00) }
  18. 2.2 - Plural rules # en.y ml posts: comments: one:

    " % {count} comment" other: " % {count} comments"
  19. 2.2 - Plural rules • English plurals have just 2

    categories: one (singular); other (plural) • Other languages may have more or less! • 1 form for all numbers ( Chinese, Japanese, Korean) • Special form for 2 items ( Hebrew) • Special form for small or large groups ( Russian, Czech)
  20. 2.2 - Plural rules • rails-i18n provides implementations of the

    CLDR pluralization algorithm for many languages. • You still need to provide translations with the correct keys for each language you support.
  21. 2.2 - Plural rules # en.y ml posts: comments: one:

    " % {count} comment" other: " % {count} comments" # ru.y ml posts: comments: one: " % {count} комментарий" few: " % {count} комментария" many: " % {count} комментариев" other: " % {count} комментариев"
  22. 2.2 - Plural rules I18n. wi th_locale(:en) { I18n.t("posts.comments", count:

    2) } #=> "2 comments" I18n. wi th_locale(:ru) { I18n.t("posts.comments", count: 2) } #=> "2 комментария"
  23. 2.3 - List separators • Languages may use different punctuation

    • e.g. U + 3001 “ɺ ” • Languages may have different rules about how punctuation is used • rails-i18n includes all this data!
  24. 2.3 - List separators tags = % w (i18n l10n

    g11n) I18n. wi th_locale(:ja) { tags.to_sentence } #=> "i18nɺl10nɺg11n" I18n. wi th_locale(:en) { tags.to_sentence } #=> "i18n, l10n, and g11n"
  25. 3.1 - Recap • No hard-coded text or data formats

    • Machine-translated locale files • Data formats, plural rules, list separators based on CLDR standards • Just 4 gems (i18n, i18n-tasks, t w i tter_cldr, rails-i18n)
  26. Links, credits, etc. • Stock images from Unsplash • Illustrations

    from Irasutoya • Gems mentioned: • https://github.com/glebm/i18n-tasks • https://github.com/twitter/twitter-cldr-rb • https://github.com/svenfuchs/rails-i18n