From f6b9dcf18748bceb97b947f58c668158fecb2f25 Mon Sep 17 00:00:00 2001 From: songtianlun Date: Fri, 21 Feb 2025 17:51:25 +0800 Subject: [PATCH] feat: add internationalization support - Implement locale extraction and fallback mechanism - Add translation files for English and Chinese - Update views to use translated strings for various UI elements This commit introduces support for multiple languages in the application, enhancing accessibility for users. It includes a fallback mechanism for locales and updates to the user interface to display translated content. --- app/controllers/application_controller.rb | 12 ++- app/models/weather_art.rb | 30 +++++-- app/views/arts/index.html.erb | 12 +-- app/views/cities/_city.html.erb | 4 +- app/views/cities/_search_city.html.erb | 2 +- app/views/cities/index.html.erb | 10 +-- app/views/cities/show.html.erb | 2 +- app/views/home/_arts.html.erb | 2 +- app/views/home/index.html.erb | 13 ++- app/views/layouts/_navbar.html.erb | 21 +++-- app/views/layouts/_user_menu.html.erb | 6 +- app/views/shared/_language_switcher.html.erb | 17 ++++ app/views/shared/_pagination.html.erb | 9 +- app/views/weather_arts/_card.html.erb | 2 +- app/views/weather_arts/show.html.erb | 2 +- config/initializers/locale.rb | 19 +++++ config/locales/en.yml | 45 +++++++++- config/locales/zh-CN.yml | 48 +++++++++++ config/routes.rb | 88 ++++++++++---------- 19 files changed, 253 insertions(+), 91 deletions(-) create mode 100644 app/views/shared/_language_switcher.html.erb create mode 100644 config/initializers/locale.rb create mode 100644 config/locales/zh-CN.yml diff --git a/app/controllers/application_controller.rb b/app/controllers/application_controller.rb index a48f134..adbdc9f 100644 --- a/app/controllers/application_controller.rb +++ b/app/controllers/application_controller.rb @@ -77,7 +77,17 @@ class ApplicationController < ActionController::Base private def set_locale - I18n.locale = params[:locale] || I18n.default_locale + I18n.locale = extract_locale || I18n.default_locale + I18n.fallbacks[I18n.locale] = [ I18n.locale, I18n.default_locale ].uniq + end + + def extract_locale + parsed_locale = params[:locale] + I18n.available_locales.map(&:to_s).include?(parsed_locale) ? parsed_locale : nil + end + + def default_url_options + { locale: I18n.locale } end def set_content_type_for_rss diff --git a/app/models/weather_art.rb b/app/models/weather_art.rb index 2f96114..67684e5 100644 --- a/app/models/weather_art.rb +++ b/app/models/weather_art.rb @@ -79,11 +79,23 @@ class WeatherArt < ApplicationRecord ActiveSupport::TimeZone["UTC"] time = self.updated_at - date_string = self.weather_date&.strftime("%B %d, %Y") + # 使用 I18n 本地化格式化日期 + date_string = I18n.l(self.weather_date, format: :long) + + # 格式化时间 + time_format = use_local_timezone ? time.in_time_zone(time_zone) : time.utc time_string = - use_local_timezone ? - "#{time.in_time_zone(time_zone).strftime('%H:%M')} #{timezone_info['gmtOffsetName']}" : - "#{time.utc.strftime('%H:%M')} UTC" + if use_local_timezone + I18n.t("time.formats.with_zone", + time: I18n.l(time_format, format: :time_only), + zone: timezone_info["gmtOffsetName"] + ) + else + I18n.t("time.formats.with_zone", + time: I18n.l(time_format, format: :time_only), + zone: "UTC" + ) + end case type when :date @@ -91,9 +103,15 @@ class WeatherArt < ApplicationRecord when :time time_string when :all - "#{date_string} #{time_string}" + I18n.t("time.formats.date_and_time", + date: date_string, + time: time_string + ) else - "#{date_string} #{time_string}" + I18n.t("time.formats.date_and_time", + date: date_string, + time: time_string + ) end end diff --git a/app/views/arts/index.html.erb b/app/views/arts/index.html.erb index 5499093..cbead18 100644 --- a/app/views/arts/index.html.erb +++ b/app/views/arts/index.html.erb @@ -17,10 +17,10 @@

- Weather Arts Gallery + <%= t("arts.title") %>

- Discover AI-generated weather art from cities around the world + <%= t("arts.subtitle") %>

@@ -70,14 +70,14 @@ - <%= @current_region&.name || 'All Regions' %> + <%= @current_region&.name || t("text.all_regions") %> \ No newline at end of file diff --git a/app/views/shared/_language_switcher.html.erb b/app/views/shared/_language_switcher.html.erb new file mode 100644 index 0000000..373e85b --- /dev/null +++ b/app/views/shared/_language_switcher.html.erb @@ -0,0 +1,17 @@ + \ No newline at end of file diff --git a/app/views/shared/_pagination.html.erb b/app/views/shared/_pagination.html.erb index 58ec111..e0d9c62 100644 --- a/app/views/shared/_pagination.html.erb +++ b/app/views/shared/_pagination.html.erb @@ -78,9 +78,12 @@
- Showing <%= collection.offset_value + 1 %> to - <%= collection.last_page? ? collection.total_count : collection.offset_value + collection.limit_value %> - of <%= collection.total_count %> <%= collection_name || 'items' %> + <%= t('pagination.showing_items', + from: collection.offset_value + 1, + to: collection.last_page? ? collection.total_count : collection.offset_value + collection.limit_value, + total: collection.total_count, + items: t("pagination.items.#{collection_name}", default: t('pagination.items.default')) + ) %>
<% end %> \ No newline at end of file diff --git a/app/views/weather_arts/_card.html.erb b/app/views/weather_arts/_card.html.erb index bead5f5..ac0743f 100644 --- a/app/views/weather_arts/_card.html.erb +++ b/app/views/weather_arts/_card.html.erb @@ -35,7 +35,7 @@ <%= link_to city_weather_art_path(weather_art.city, weather_art), class: "btn btn-primary btn-block" do %> - View Details + <%= t("button.view_detail") %> diff --git a/app/views/weather_arts/show.html.erb b/app/views/weather_arts/show.html.erb index ec1899e..77740cb 100644 --- a/app/views/weather_arts/show.html.erb +++ b/app/views/weather_arts/show.html.erb @@ -12,7 +12,7 @@ - Back to <%= @weather_art.city.name %> + <%= "#{t("button.back_to")} #{@weather_art.city.name}" %> <% end %> diff --git a/config/initializers/locale.rb b/config/initializers/locale.rb new file mode 100644 index 0000000..37d0f4b --- /dev/null +++ b/config/initializers/locale.rb @@ -0,0 +1,19 @@ +# config/initializers/locale.rb +require "i18n/backend/fallbacks" + +# Where the I18n library should search for translation files +I18n.load_path += Dir[Rails.root.join("config", "locales", "*.{rb,yml}")] + +# Permitted locales available for the application +I18n.available_locales = [ :en, :"zh-CN" ] + +I18n::Backend::Simple.send(:include, I18n::Backend::Fallbacks) +# I18n::Backend::Simple.include I18n::Backend::Fallbacks +# I18n.fallbacks[:en] +I18n.fallbacks = I18n::Locale::Fallbacks.new( + en: [ :en ], + 'zh-CN': [ :zh, :zh_cn, :en ] +) + +# Set default locale to something other than :en +I18n.default_locale = :en diff --git a/config/locales/en.yml b/config/locales/en.yml index 4e65d5c..f73d8f2 100644 --- a/config/locales/en.yml +++ b/config/locales/en.yml @@ -28,4 +28,47 @@ # enabled: "ON" en: - hello: "Hello world" \ No newline at end of file + hello: "Hello world" + brand: + name: "Today AI Weather" + title: + cities: "Cities" + arts: "Arts" + sign_in: "Sign in" + sign_out: "Sign out" + settings: "Settings" + admin_dashboard: "Admin Dashboard" + latest_weather_art: "Latest Weather Art" + popular_weather_art: "Popular Weather Art" + text: + latest_from: "Latest from" + search_cities: "Search cities..." + all_regions: "All Regions" + showing: "Showing" + weather_arts: "Weather Arts" + cities: + title: "Explore Cities" + arts: + title: "Weather Arts Gallery" + subtitle: "Discover AI-generated weather art from cities around the world" + home: + headline_html: Where Weather Meets
Artificial Intelligence + subtitle: + Experience weather through the lens of AI-generated art, + bringing a new perspective to daily meteorological phenomena. + button: + explore_cities: "Explore Cities" + view_detail: "View Details" + view_all_weather_arts: "View All Weather Arts" + back_to_cities: "Back to Cities" + back_to: "Back to" + pagination: + showing_items: "Showing %{from} to %{to} of %{total} %{items}" + items: + weather: "weather records" + default: "items" + time: + formats: + time_only: "%H:%M" + with_zone: "%{time} %{zone}" + date_and_time: "%{date} %{time}" diff --git a/config/locales/zh-CN.yml b/config/locales/zh-CN.yml new file mode 100644 index 0000000..82cb45e --- /dev/null +++ b/config/locales/zh-CN.yml @@ -0,0 +1,48 @@ +zh-CN: + hello: "你好" + brand: + name: "全球艺术天气" + title: + cities: "城市探索" + arts: "艺术巡览" + sign_in: "用户登录" + sign_out: "退出登录" + settings: "系统设置" + admin_dashboard: "管理中枢" + latest_weather_art: "气象绘卷·新作" + popular_weather_art: "气象绘卷·佳作" + text: + latest_from: "新至之城" + search_cities: "寻城觅境…" + all_regions: "寰宇之境" + showing: "映现" + weather_arts: "气象艺境" + cities: + title: "云游四海" + arts: + title: "天象画廊" + subtitle: "邂逅寰宇都市AI气象绘卷" + home: + headline_html: 当气象邂逅
人工智能之美 + subtitle: + 通过AI生成的艺术视角感受气象,为日常天气现象带来全新解读。 + button: + explore_cities: "云游四海" + view_detail: "详阅此卷" + view_all_weather_arts: "尽览天工" + back_to_cities: "继续探索城市" + back_to: "回到" + pagination: + showing_items: "显示第 %{from} 到第 %{to} 条,共 %{total} 条%{items}" + items: + weather: "天气记录" + default: "记录" + time: + formats: + time_only: "%H:%M" + with_zone: "%{time} %{zone}" + date_and_time: "%{date} %{time}" + date: + formats: + short: "%Y-%m-%d" + long: "%Y 年 %m 月 %d 日" diff --git a/config/routes.rb b/config/routes.rb index cf99dc1..deb73f3 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -1,50 +1,52 @@ require "sidekiq/web" Rails.application.routes.draw do - devise_for :users - root "home#index" + scope "(:locale)", locale: /#{I18n.available_locales.join("|")}/ do + devise_for :users + root "home#index" - resources :cities, only: [ :index, :show ] do - resources :weather_arts, path: "weather", only: [ :show ], param: :slug - end - resources :cities do - member do - post :generate_weather_art, param: :slug + resources :cities, only: [ :index, :show ] do + resources :weather_arts, path: "weather", only: [ :show ], param: :slug end + resources :cities do + member do + post :generate_weather_art, param: :slug + end + end + resources :arts, only: [ :index ] + + # namespace :admin do + # resources :cities + # resources :weather_arts + # root to: "cities#index" + # end + + get "weather_arts/show" + get "cities/index" + get "cities/show" + get "home/index" + get "sitemaps/*path", to: "sitemaps#show", format: false + get "feed", to: "rss#feed", format: "rss", as: :rss_feed + + devise_for :admin_users, ActiveAdmin::Devise.config + ActiveAdmin.routes(self) + + # mount Sidekiq::Web => '/sidekiq' + # authenticate :admin_user do + authenticate :user, lambda { |u| u.admin? } do + mount Sidekiq::Web => "/admin/tasks" + end + # Define your application routes per the DSL in https://guides.rubyonrails.org/routing.html + + # Reveal health status on /up that returns 200 if the app boots with no exceptions, otherwise 500. + # Can be used by load balancers and uptime monitors to verify that the app is live. + get "up" => "rails/health#show", as: :rails_health_check + + # Render dynamic PWA files from app/views/pwa/* (remember to link manifest in application.html.erb) + # get "manifest" => "rails/pwa#manifest", as: :pwa_manifest + # get "service-worker" => "rails/pwa#service_worker", as: :pwa_service_worker + + # Defines the root path route ("/") + # root "posts#index" end - resources :arts, only: [ :index ] - - # namespace :admin do - # resources :cities - # resources :weather_arts - # root to: "cities#index" - # end - - get "weather_arts/show" - get "cities/index" - get "cities/show" - get "home/index" - get "sitemaps/*path", to: "sitemaps#show", format: false - get "feed", to: "rss#feed", format: "rss", as: :rss_feed - - devise_for :admin_users, ActiveAdmin::Devise.config - ActiveAdmin.routes(self) - - # mount Sidekiq::Web => '/sidekiq' - # authenticate :admin_user do - authenticate :user, lambda { |u| u.admin? } do - mount Sidekiq::Web => "/admin/tasks" - end - # Define your application routes per the DSL in https://guides.rubyonrails.org/routing.html - - # Reveal health status on /up that returns 200 if the app boots with no exceptions, otherwise 500. - # Can be used by load balancers and uptime monitors to verify that the app is live. - get "up" => "rails/health#show", as: :rails_health_check - - # Render dynamic PWA files from app/views/pwa/* (remember to link manifest in application.html.erb) - # get "manifest" => "rails/pwa#manifest", as: :pwa_manifest - # get "service-worker" => "rails/pwa#service_worker", as: :pwa_service_worker - - # Defines the root path route ("/") - # root "posts#index" end