views:

660

answers:

4

Howdy,

We internationalized our site months ago, but forgot one part: The drop down where a user picks their timezone.

How do you translate the following line:

  = f.time_zone_select :timezone, ActiveSupport::TimeZone.all
+1  A: 

I would imagine that this would need to be done manually in the same way that any other I18n translations are done in Rails. This would mean setting up locale files with the translations. Something like:

# es.yml
es:
  timezones:
    "International Date Line West":    "Línea de fecha internacional del oeste"
    "Pacific Time (US & Canada)":      "Tiempo pacífico (& de los E.E.U.U.; Canadá)"
    # and so on

You could overwrite the time_zone_options_for_select method (which is used by time_zone_select) with the following:

def time_zone_options_for_select(selected = nil, priority_zones = nil, model = ::ActiveSupport::TimeZone)
  zone_options = ""

  zones = model.all
  convert_zones = lambda do |list|
    list.map do |z|
      localized_name = I18n.t(z.name, :scope => :timezones, :default => z.name)
      [ "(GMT#{z.formatted_offset}) #{localized_name}", z.name ]
    end
  end

  if priority_zones
    if priority_zones.is_a?(Regexp)
      priority_zones = model.all.find_all {|z| z =~ priority_zones}
    end
    zone_options += options_for_select(convert_zones[priority_zones], selected)
    zone_options += "<option value=\"\" disabled=\"disabled\">-------------</option>\n"

    zones = zones.reject { |z| priority_zones.include?( z ) }
  end

  zone_options += options_for_select(convert_zones[zones], selected)
  zone_options
end

The changes are:

convert_zones = lambda do |list|
  list.map do |z|
    localized_name = I18n.t(z.name, :scope => :timezones, :default => z.name)
    [ "(GMT#{z.formatted_offset}) #{localized_name}", z.name ]
  end
end

What we're doing is, getting the localized name from the TimeZone name with I18n.t which is looking in config/locales/LANG.yml formatted as shown above. If we can't find the translation, we just fallback on using the TimeZone name.

Now that we've done this setup, we should be able to use :

f.time_zone_select :timezone, ActiveSupport::TimeZone.all

or the shorter

f.time_zone_select :timezone # defaults to ActiveSupport::TimeZone.all
Peter Wagenet
+4  A: 

I've come across the same problem. However, when I was trying to implement Peter's solution, a simpler solution occurred to me. The time_zone_select helper takes a :model option, which defaults to ActiveSupport::TimeZone. According to the API documentation, all this model has to do is return an array of timezone objects in the all method. We can then override the to_s method to return the translation (defaulting to the original if the translation isn't found). Here is the class:

# lib/i18n_time_zone.rb
class I18nTimeZone < ActiveSupport::TimeZone
  def self.all
    super.map { |z| create(z.name, z.utc_offset) }
  end

  def to_s
    translated_name = I18n.t(name, :scope => :timezones, :default => name)
    "(GMT#{formatted_offset}) #{translated_name}"
  end
end

And in the view:

<%= time_zone_select :user, :time_zone, nil, :model => I18nTimeZone %>

With the translations specified in the translation file as before:

# es.yml
es:
  timezones:
    "International Date Line West":    "Línea de fecha internacional del oeste"
    "Pacific Time (US & Canada)":      "Tiempo pacífico (& de los E.E.U.U.; Canadá)"
    # and so on
fishwebby
Even simpler, you don't need to override self.all, that's already provided by ActiveSupport::TimeZone. So only +to_s+ is needed.
Jim Soho
+2  A: 

I had the same problem and fishwebby's answer made the trick easily. I needed the time zones translated to spanish so here I leave the list:

    "International Date Line West": "Línea de fecha internacional del oeste"
    "Midway Island": "Isla de Midway"
    "Samoa": "Samoa"
    "Hawaii": "Hawai"
    "Alaska": "Alaska"
    "Pacific Time (US & Canada)": "Hora del Pacífico (EE.UU. y Canadá)"
    "Tijuana": "Tijuana"
    "Mountain Time (US & Canada)": "Hora de las Montañas (EE.UU. y Canadá)"
    "Arizona": "Arizona"
    "Chihuahua": "Chihuahua"
    "Mazatlan": "Mazatlán"
    "Central Time (US & Canada)": "Hora central (EE.UU. y Canadá)"
    "Saskatchewan": "Saskatchewan"
    "Guadalajara": "Guadalajara"
    "Mexico City": "Ciudad de México"
    "Monterrey": "Monterrey"
    "Central America": "América Central"
    "Eastern Time (US & Canada)": "Hora del Este (EE.UU. y Canadá)"
    "Indiana (East)": "Indiana (Este)"
    "Bogota": "Bogotá"
    "Lima": "Lima"
    "Quito": "Quito"
    "Atlantic Time (Canada)": "Hora del Atlántico (Canadá)"
    "Caracas": "Caracas"
    "La Paz": "La Paz"
    "Santiago": "Santiago"
    "Newfoundland": "Terranova"
    "Brasilia": "Brasilia"
    "Buenos Aires": "Buenos Aires"
    "Georgetown": "Georgetown"
    "Greenland": "Groenlandia"
    "Mid-Atlantic": "Atlántico medio"
    "Azores": "Azores"
    "Cape Verde Is.": "Isla Cabo Verde"
    "Dublin": "Dublín"
    "Edinburgh": "Edimburgo"
    "Lisbon": "Lisboa"
    "London": "Londres"
    "Casablanca": "Casablanca"
    "Monrovia": "Monrovia"
    "UTC": "UTC"
    "Belgrade": "Belgrado"
    "Bratislava": "Bratislava"
    "Budapest": "Budapest"
    "Ljubljana": "Ljubljana"
    "Prague": "Praga"
    "Sarajevo": "Sarajevo"
    "Skopje": "Skopje"
    "Warsaw": "Varsovia"
    "Zagreb": "Zagreb"
    "Brussels": "Bruselas"
    "Copenhagen": "Copenhague"
    "Madrid": "Madrid"
    "Paris": "París"
    "Amsterdam": "Amsterdam"
    "Berlin": "Berlín"
    "Bern": "Berna"
    "Rome": "Roma"
    "Stockholm": "Estocolmo"
    "Vienna": "Viena"
    "West Central Africa": "Centro-Oeste de África"
    "Bucharest": "Bucarest"
    "Cairo": "El Cairo"
    "Helsinki": "Helsinki"
    "Kyev": "Kyev"
    "Riga": "Riga"
    "Sofia": "Sofía"
    "Tallinn": "Tallin"
    "Vilnius": "Vilnius"
    "Athens": "Atenas"
    "Istanbul": "Estambul"
    "Minsk": "Minsk"
    "Jerusalem": "Jerusalén"
    "Harare": "Harare"
    "Pretoria": "Pretoria"
    "Moscow": "Moscú"
    "St. Petersburg": "San Petersburgo"
    "Volgograd": "Volgogrado"
    "Kuwait": "Kuwait"
    "Riyadh": "Riad"
    "Nairobi": "Nairobi"
    "Baghdad": "Bagdad"
    "Tehran": "Teherán"
    "Abu Dhabi": "Abu Dhabi"
    "Muscat": "Moscatel"
    "Baku": "Bakú"
    "Tbilisi": "Tbilisi"
    "Yerevan": "Ereván"
    "Kabul": "Kabul"
    "Ekaterinburg": "Ekaterinburg"
    "Islamabad": "Islamabad"
    "Karachi": "Karachi"
    "Tashkent": "Tashkent"
    "Chennai": "Chennai"
    "Kolkata": "Kolkata"
    "Mumbai": "Mumbai"
    "New Delhi": "Nueva Delhi"
    "Kathmandu": "Katmandú"
    "Astana": "Astana"
    "Dhaka": "Dhaka"
    "Sri Jayawardenepura": "Sri Jayawardenepura"
    "Almaty": "Almaty"
    "Novosibirsk": "Novosibirsk"
    "Rangoon": "Rangún"
    "Bangkok": "Bangkok"
    "Hanoi": "Hanoi"
    "Jakarta": "Yakarta"
    "Krasnoyarsk": "Krasnoyarsk"
    "Beijing": "Beijing"
    "Chongqing": "Chongqing"
    "Hong Kong": "Hong Kong"
    "Urumqi": "Urumqi"
    "Kuala Lumpur": "Kuala Lumpur"
    "Singapore": "Singapur"
    "Taipei": "Taipei"
    "Perth": "Perth"
    "Irkutsk": "Irkutsk"
    "Ulaan Bataar": "Ulán Bator"
    "Seoul": "Seúl"
    "Osaka": "Osaka"
    "Sapporo": "Sapporo"
    "Tokyo": "Tokio"
    "Yakutsk": "Yakutsk"
    "Darwin": "Darwin"
    "Adelaide": "Adelaida"
    "Canberra": "Canberra"
    "Melbourne": "Melbourne"
    "Sydney": "Sydney"
    "Brisbane": "Brisbane"
    "Hobart": "Hobart"
    "Vladivostok": "Vladivostok"
    "Guam": "Guam"
    "Port Moresby": "Port Moresby"
    "Magadan": "Magadan"
    "Solomon Is.": "Islas Salomón"
    "New Caledonia": "Nueva Caledonia"
    "Fiji": "Fiji"
    "Kamchatka": "Kamchatka"
    "Marshall Is.": "Islas Marshall"
    "Auckland": "Auckland"
    "Wellington": "Wellington"
    "Nuku'alofa": "Nuku'alofa"
miligraf
Oh and to output the country from the model whan already saved:<%=t @user.zona_horaria, :scope => "timezones" %>
miligraf
+1  A: 

fishwebby's solution didn't work for me when priority time zones where provided in the time_zone_select. Easiest is to just stick this in /config/initializers/time_zone_patch.rb :

  # See http://stackoverflow.com/questions/1396623/translating-rails-timezones
  class ActiveSupport::TimeZone
    def to_s
      offset = "(GMT#{formatted_offset})"
      translated_name = I18n.t(name, :scope => :time_zones, :default => name)
      %(#{offset} #{translated_name})
    end
  end

No need to modify your view code either.

Jim Soho