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.
This commit is contained in:
parent
517e3038cc
commit
f6b9dcf187
@ -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
|
||||
|
@ -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
|
||||
|
||||
|
@ -17,10 +17,10 @@
|
||||
<div class="container mx-auto px-4">
|
||||
<div class="max-w-3xl mx-auto text-center space-y-6">
|
||||
<h1 class="text-4xl md:text-5xl font-display font-bold">
|
||||
Weather Arts Gallery
|
||||
<%= t("arts.title") %>
|
||||
</h1>
|
||||
<p class="text-xl text-base-content/70">
|
||||
Discover AI-generated weather art from cities around the world
|
||||
<%= t("arts.subtitle") %>
|
||||
</p>
|
||||
|
||||
<!-- 如果有特色图片,显示其信息 -->
|
||||
@ -70,14 +70,14 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" class="h-5 w-5" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M3.055 11H5a2 2 0 012 2v1a2 2 0 002 2 2 2 0 012 2v2.945M8 3.935V5.5A2.5 2.5 0 0010.5 8h.5a2 2 0 012 2 2 2 0 104 0 2 2 0 012-2h1.064M15 20.488V18a2 2 0 012-2h3.064M21 12a9 9 0 11-18 0 9 9 0 0118 0z" />
|
||||
</svg>
|
||||
<%= @current_region&.name || 'All Regions' %>
|
||||
<%= @current_region&.name || t("text.all_regions") %>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" class="h-4 w-4" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M19 9l-7 7-7-7" />
|
||||
</svg>
|
||||
</button>
|
||||
<ul class="dropdown-content z-[1] menu p-2 shadow-lg bg-base-100 rounded-box w-52">
|
||||
<li>
|
||||
<%= link_to "All Regions", arts_path(sort: params[:sort]),
|
||||
<%= link_to t("text.all_regions"), arts_path(sort: params[:sort]),
|
||||
class: "#{'active' unless @current_region}" %>
|
||||
</li>
|
||||
<div class="divider my-1"></div>
|
||||
@ -93,7 +93,7 @@
|
||||
|
||||
<!-- 结果统计 -->
|
||||
<div class="text-center text-sm text-base-content/70 mt-4">
|
||||
Showing <%= @weather_arts.total_count %> weather arts
|
||||
<%= "#{t("text.showing")} #{@weather_arts.total_count} #{t("text.weather_arts")} " %>
|
||||
<% if @current_region %>
|
||||
from <%= @current_region.name %>
|
||||
<% end %>
|
||||
@ -154,7 +154,7 @@
|
||||
|
||||
<%= link_to city_weather_art_path(art.city, art),
|
||||
class: "btn btn-primary btn-sm w-full" do %>
|
||||
View Details
|
||||
<%= t("button.view_detail") %>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" class="h-4 w-4" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M14 5l7 7m0 0l-7 7m7-7H3" />
|
||||
</svg>
|
||||
|
@ -75,7 +75,7 @@
|
||||
<div class="card-actions justify-end">
|
||||
<%= link_to city_path(city),
|
||||
class: "btn btn-primary btn-sm gap-2", data: { turbo_frame: "_top" } do %>
|
||||
View Details
|
||||
<%= t("button.view_detail") %>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" class="h-4 w-4" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M14 5l7 7m0 0l-7 7m7-7H3" />
|
||||
</svg>
|
||||
@ -115,7 +115,7 @@
|
||||
<div class="card-actions justify-end">
|
||||
<%= link_to city_path(city),
|
||||
class: "btn btn-primary btn-sm gap-2", data: { turbo_frame: "_top" } do %>
|
||||
View Details
|
||||
<%= t("button.view_detail") %>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" class="h-4 w-4" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M14 5l7 7m0 0l-7 7m7-7H3" />
|
||||
</svg>
|
||||
|
@ -18,7 +18,7 @@
|
||||
<%= f.text_field :query,
|
||||
value: params[:query] ? URI.decode_www_form_component(params[:query]) : nil,
|
||||
class: "w-full pl-12 pr-12 py-3 rounded-full bg-base-200/80 backdrop-blur border border-base-300 focus:outline-none focus:ring-2 focus:ring-primary/50 transition",
|
||||
placeholder: "Search cities...",
|
||||
placeholder: t("text.search_cities"),
|
||||
autocomplete: "off",
|
||||
data: {
|
||||
action: "input->search#submit",
|
||||
|
@ -16,16 +16,16 @@
|
||||
<div class="container mx-auto px-4">
|
||||
<div class="max-w-3xl mx-auto text-center space-y-6">
|
||||
<h1 class="text-5xl md:text-6xl font-display font-bold leading-tight">
|
||||
Explore Cities
|
||||
<%= t("cities.title") %>
|
||||
</h1>
|
||||
<p class="text-xl md:text-2xl text-base-content/70 font-light max-w-2xl mx-auto">
|
||||
Discover AI-generated weather art from cities around the world
|
||||
<%= t("arts.subtitle") %>
|
||||
</p>
|
||||
|
||||
<!-- 特色图片信息 -->
|
||||
<% if featured_art %>
|
||||
<div class="inline-block mt-6 px-4 py-2 bg-base-100/80 backdrop-blur-sm rounded-full text-sm">
|
||||
Latest from
|
||||
<%= t("text.latest_from") %>
|
||||
<span class="font-semibold"><%= featured_art.city.name %></span>,
|
||||
<%= featured_art.city.country.name %>
|
||||
<span class="mx-2">•</span>
|
||||
@ -50,7 +50,7 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" class="h-5 w-5" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M3.055 11H5a2 2 0 012 2v1a2 2 0 002 2 2 2 0 012 2v2.945M8 3.935V5.5A2.5 2.5 0 0010.5 8h.5a2 2 0 012 2 2 2 0 104 0 2 2 0 012-2h1.064M15 20.488V18a2 2 0 012-2h3.064M21 12a9 9 0 11-18 0 9 9 0 0118 0z" />
|
||||
</svg>
|
||||
<%= @current_region&.name || 'All Regions' %>
|
||||
<%= @current_region&.name || t("text.all_regions") %>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" class="h-4 w-4" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M19 9l-7 7-7-7" />
|
||||
</svg>
|
||||
@ -59,7 +59,7 @@
|
||||
<li>
|
||||
<%= link_to cities_path,
|
||||
class: "#{@current_region ? '' : 'active'}" do %>
|
||||
All Regions
|
||||
<%= t("text.all_regions") %>
|
||||
<% end %>
|
||||
</li>
|
||||
<div class="divider my-1"></div>
|
||||
|
@ -16,7 +16,7 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" class="h-5 w-5" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M15 19l-7-7 7-7" />
|
||||
</svg>
|
||||
Back to Cities
|
||||
<%= t("button.back_to_cities") %>
|
||||
<% end %>
|
||||
</div>
|
||||
|
||||
|
@ -20,7 +20,7 @@
|
||||
</div>
|
||||
</div>
|
||||
<div class="card-actions justify-end mt-4">
|
||||
<%= link_to "View Details", city_weather_art_path(art.city, art),
|
||||
<%= link_to t("button.view_detail"), city_weather_art_path(art.city, art),
|
||||
class: "btn btn-primary btn-outline" %>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -11,13 +11,12 @@
|
||||
<div class="container mx-auto px-4 h-full flex items-center relative">
|
||||
<div class="max-w-2xl space-y-6">
|
||||
<h1 class="text-5xl md:text-6xl font-display font-bold leading-tight">
|
||||
Where Weather Meets<br>Artificial Intelligence
|
||||
<%= t("home.headline_html") %>
|
||||
</h1>
|
||||
<p class="text-xl text-base-content/70 font-sans">
|
||||
Experience weather through the lens of AI-generated art,
|
||||
bringing a new perspective to daily meteorological phenomena.
|
||||
<%= t("home.subtitle") %>
|
||||
</p>
|
||||
<%= link_to "Explore Cities", cities_path,
|
||||
<%= link_to t("button.explore_cities"), cities_path,
|
||||
class: "btn btn-primary btn-lg mt-8 font-sans" %>
|
||||
</div>
|
||||
</div>
|
||||
@ -25,9 +24,9 @@
|
||||
|
||||
<!-- 最新天气艺术 -->
|
||||
<section class="container mx-auto px-4 py-16 space-y-12">
|
||||
<h2 class="text-3xl font-display font-bold text-center">Latest Weather Art</h2>
|
||||
<h2 class="text-3xl font-display font-bold text-center"><%= t("title.latest_weather_art") %></h2>
|
||||
<%= render 'home/arts', arts: @latest_arts %>
|
||||
<h2 class="text-3xl font-display font-bold text-center">Popular Weather Art</h2>
|
||||
<h2 class="text-3xl font-display font-bold text-center"><%= t("title.popular_weather_art") %></h2>
|
||||
<%= render 'home/arts', arts: @popular_arts %>
|
||||
<!-- <h2 class="text-3xl font-display font-bold text-center">Random Weather Art</h2>-->
|
||||
<%#= render 'home/arts', arts: @random_arts %>
|
||||
@ -35,7 +34,7 @@
|
||||
</div>
|
||||
<div class="text-center mt-12 mb-12">
|
||||
<%= link_to arts_path, class: "btn btn-primary btn-lg gap-2" do %>
|
||||
View All Weather Arts
|
||||
<%= t("button.view_all_weather_arts") %>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" class="h-5 w-5" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M14 5l7 7m0 0l-7 7m7-7H3" />
|
||||
</svg>
|
||||
|
@ -3,14 +3,14 @@
|
||||
<!-- Logo -->
|
||||
<div class="flex-1">
|
||||
<%= link_to root_path, class: "text-xl md:text-2xl font-display font-bold hover:text-primary transition-colors" do %>
|
||||
Today AI Weather
|
||||
<%= t('brand.name') %>
|
||||
<% end %>
|
||||
</div>
|
||||
|
||||
<!-- Desktop Menu -->
|
||||
<div class="hidden md:flex flex-none gap-2 items-center">
|
||||
<%= link_to "Cities", cities_path, class: "btn btn-ghost btn-sm font-sans" %>
|
||||
<%= link_to "Arts", arts_path, class: "btn btn-ghost btn-sm font-sans" %>
|
||||
<%= link_to t("title.cities"), cities_path, class: "btn btn-ghost btn-sm font-sans" %>
|
||||
<%= link_to t("title.arts"), arts_path, class: "btn btn-ghost btn-sm font-sans" %>
|
||||
|
||||
<% if user_signed_in? %>
|
||||
<div class="dropdown dropdown-end">
|
||||
@ -27,12 +27,15 @@
|
||||
</label>
|
||||
<%= render 'layouts/user_menu' %>
|
||||
</div>
|
||||
|
||||
<%= render 'shared/language_switcher' %>
|
||||
<% else %>
|
||||
<%= link_to new_user_session_path, class: "btn btn-primary btn-sm" do %>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="w-4 h-4">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" d="M15.75 9V5.25A2.25 2.25 0 0013.5 3h-6a2.25 2.25 0 00-2.25 2.25v13.5A2.25 2.25 0 007.5 21h6a2.25 2.25 0 002.25-2.25V15m3 0l3-3m0 0l-3-3m3 3H9" />
|
||||
</svg>
|
||||
<span>Sign in</span>
|
||||
<span><%= t("title.sign_in") %></span>
|
||||
<%= render 'shared/language_switcher' %>
|
||||
<% end %>
|
||||
<% end %>
|
||||
</div>
|
||||
@ -46,8 +49,8 @@
|
||||
</svg>
|
||||
</label>
|
||||
<ul tabindex="0" class="dropdown-content z-[1] menu p-2 shadow bg-base-100 rounded-box w-52">
|
||||
<li><%= link_to "Cities", cities_path %></li>
|
||||
<li><%= link_to "Arts", arts_path %></li>
|
||||
<li><%= link_to t("title.cities"), cities_path %></li>
|
||||
<li><%= link_to t("title.arts"), arts_path %></li>
|
||||
<div class="divider my-1"></div>
|
||||
<% if user_signed_in? %>
|
||||
<li>
|
||||
@ -56,7 +59,7 @@
|
||||
<path stroke-linecap="round" stroke-linejoin="round" d="M9.594 3.94c.09-.542.56-.94 1.11-.94h2.593c.55 0 1.02.398 1.11.94l.213 1.281c.063.374.313.686.645.87.074.04.147.083.22.127.324.196.72.257 1.075.124l1.217-.456a1.125 1.125 0 011.37.49l1.296 2.247a1.125 1.125 0 01-.26 1.431l-1.003.827c-.293.24-.438.613-.431.992a6.759 6.759 0 010 .255c-.007.378.138.75.43.99l1.005.828c.424.35.534.954.26 1.43l-1.298 2.247a1.125 1.125 0 01-1.369.491l-1.217-.456c-.355-.133-.75-.072-1.076.124a6.57 6.57 0 01-.22.128c-.331.183-.581.495-.644.869l-.213 1.28c-.09.543-.56.941-1.11.941h-2.594c-.55 0-1.02-.398-1.11-.94l-.213-1.281c-.062-.374-.312-.686-.644-.87a6.52 6.52 0 01-.22-.127c-.325-.196-.72-.257-1.076-.124l-1.217.456a1.125 1.125 0 01-1.369-.49l-1.297-2.247a1.125 1.125 0 01.26-1.431l1.004-.827c.292-.24.437-.613.43-.992a6.932 6.932 0 010-.255c.007-.378-.138-.75-.43-.99l-1.004-.828a1.125 1.125 0 01-.26-1.43l1.297-2.247a1.125 1.125 0 011.37-.491l1.216.456c.356.133.751.072 1.076-.124.072-.044.146-.087.22-.128.332-.183.582-.495.644-.869l.214-1.281z" />
|
||||
<path stroke-linecap="round" stroke-linejoin="round" d="M15 12a3 3 0 11-6 0 3 3 0 016 0z" />
|
||||
</svg>
|
||||
Settings
|
||||
<%= t("title.settings") %>
|
||||
<% end %>
|
||||
</li>
|
||||
<li>
|
||||
@ -67,7 +70,7 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="w-4 h-4">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" d="M15.75 9V5.25A2.25 2.25 0 0013.5 3h-6a2.25 2.25 0 00-2.25 2.25v13.5A2.25 2.25 0 007.5 21h6a2.25 2.25 0 002.25-2.25V15M12 9l-3 3m0 0l3 3m-3-3h12.75" />
|
||||
</svg>
|
||||
<span>Sign out</span>
|
||||
<span><% t("title.sign_out") %></span>
|
||||
</div>
|
||||
<% end %>
|
||||
</li>
|
||||
@ -77,7 +80,7 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="w-4 h-4">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" d="M15.75 9V5.25A2.25 2.25 0 0013.5 3h-6a2.25 2.25 0 00-2.25 2.25v13.5A2.25 2.25 0 007.5 21h6a2.25 2.25 0 002.25-2.25V15m3 0l3-3m0 0l-3-3m3 3H9" />
|
||||
</svg>
|
||||
Sign in
|
||||
<%= t("title.sign_in") %>
|
||||
<% end %>
|
||||
</li>
|
||||
<% end %>
|
||||
|
@ -5,7 +5,7 @@
|
||||
<path stroke-linecap="round" stroke-linejoin="round" d="M9.594 3.94c.09-.542.56-.94 1.11-.94h2.593c.55 0 1.02.398 1.11.94l.213 1.281c.063.374.313.686.645.87.074.04.147.083.22.127.324.196.72.257 1.075.124l1.217-.456a1.125 1.125 0 011.37.49l1.296 2.247a1.125 1.125 0 01-.26 1.431l-1.003.827c-.293.24-.438.613-.431.992a6.759 6.759 0 010 .255c-.007.378.138.75.43.99l1.005.828c.424.35.534.954.26 1.43l-1.298 2.247a1.125 1.125 0 01-1.369.491l-1.217-.456c-.355-.133-.75-.072-1.076.124a6.57 6.57 0 01-.22.128c-.331.183-.581.495-.644.869l-.213 1.28c-.09.543-.56.941-1.11.941h-2.594c-.55 0-1.02-.398-1.11-.94l-.213-1.281c-.062-.374-.312-.686-.644-.87a6.52 6.52 0 01-.22-.127c-.325-.196-.72-.257-1.076-.124l-1.217.456a1.125 1.125 0 01-1.369-.49l-1.297-2.247a1.125 1.125 0 01.26-1.431l1.004-.827c.292-.24.437-.613.43-.992a6.932 6.932 0 010-.255c.007-.378-.138-.75-.43-.99l-1.004-.828a1.125 1.125 0 01-.26-1.43l1.297-2.247a1.125 1.125 0 011.37-.491l1.216.456c.356.133.751.072 1.076-.124.072-.044.146-.087.22-.128.332-.183.582-.495.644-.869l.214-1.281z" />
|
||||
<path stroke-linecap="round" stroke-linejoin="round" d="M15 12a3 3 0 11-6 0 3 3 0 016 0z" />
|
||||
</svg>
|
||||
<span>Settings</span>
|
||||
<span><%= t("title.settings") %></span>
|
||||
<% end %>
|
||||
</li>
|
||||
|
||||
@ -15,7 +15,7 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="w-4 h-4">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" d="M9 12h3.75M9 15h3.75M9 18h3.75m3 .75H18a2.25 2.25 0 002.25-2.25V6.108c0-1.135-.845-2.098-1.976-2.192a48.424 48.424 0 00-1.123-.08m-5.801 0c-.065.21-.1.433-.1.664 0 .414.336.75.75.75h4.5a.75.75 0 00.75-.75 2.25 2.25 0 00-.1-.664m-5.8 0A2.251 2.251 0 0113.5 2.25H15c1.012 0 1.867.668 2.15 1.586m-5.8 0c-.376.023-.75.05-1.124.08C9.095 4.01 8.25 4.973 8.25 6.108V19.5a2.25 2.25 0 002.25 2.25h.75m0-3H21m-3.75 3h.008v.008h-.008v-.008zm0-6h.008v.008h-.008v-.008zm0-6h.008v.008h-.008v-.008z" />
|
||||
</svg>
|
||||
<span>Admin Dashboard</span>
|
||||
<span><%= t("title.admin_dashboard") %></span>
|
||||
<% end %>
|
||||
</li>
|
||||
<% end %>
|
||||
@ -29,7 +29,7 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="w-4 h-4">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" d="M15.75 9V5.25A2.25 2.25 0 0013.5 3h-6a2.25 2.25 0 00-2.25 2.25v13.5A2.25 2.25 0 007.5 21h6a2.25 2.25 0 002.25-2.25V15M12 9l-3 3m0 0l3 3m-3-3h12.75" />
|
||||
</svg>
|
||||
<span>Sign out</span>
|
||||
<span><%= t("title.sign_out") %></span>
|
||||
<% end %>
|
||||
</li>
|
||||
</ul>
|
17
app/views/shared/_language_switcher.html.erb
Normal file
17
app/views/shared/_language_switcher.html.erb
Normal file
@ -0,0 +1,17 @@
|
||||
<div class="dropdown dropdown-end">
|
||||
<div tabindex="0" role="button" class="btn btn-ghost btn-circle">
|
||||
<% if I18n.locale.to_s == 'en' %>
|
||||
🇺🇸
|
||||
<% else %>
|
||||
🇨🇳
|
||||
<% end %>
|
||||
</div>
|
||||
<ul tabindex="0" class="dropdown-content z-[1] menu p-2 shadow bg-base-100 rounded-box w-52">
|
||||
<%= link_to url_for(locale: :en), class: "#{I18n.locale == :en ? 'active' : ''}" do %>
|
||||
<li><span class="flex items-center gap-2">🇺🇸 English</span></li>
|
||||
<% end %>
|
||||
<%= link_to url_for(locale: :'zh-CN'), class: "#{I18n.locale == :'zh-CN' ? 'active' : ''}" do %>
|
||||
<li><span class="flex items-center gap-2">🇨🇳 简体中文</span></li>
|
||||
<% end %>
|
||||
</ul>
|
||||
</div>
|
@ -78,9 +78,12 @@
|
||||
|
||||
<!-- 结果统计 -->
|
||||
<div class="text-sm text-base-content/60 font-light">
|
||||
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'))
|
||||
) %>
|
||||
</div>
|
||||
</div>
|
||||
<% end %>
|
@ -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") %>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" class="h-4 w-4 ml-2" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 5l7 7-7 7" />
|
||||
</svg>
|
||||
|
@ -12,7 +12,7 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" class="h-5 w-5" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M15 19l-7-7 7-7" />
|
||||
</svg>
|
||||
Back to <%= @weather_art.city.name %>
|
||||
<%= "#{t("button.back_to")} #{@weather_art.city.name}" %>
|
||||
<% end %>
|
||||
|
||||
<!-- 标题区域 -->
|
||||
|
19
config/initializers/locale.rb
Normal file
19
config/initializers/locale.rb
Normal file
@ -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
|
@ -29,3 +29,46 @@
|
||||
|
||||
en:
|
||||
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<br>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}"
|
||||
|
48
config/locales/zh-CN.yml
Normal file
48
config/locales/zh-CN.yml
Normal file
@ -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: 当气象邂逅<br>人工智能之美
|
||||
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 日"
|
@ -1,6 +1,7 @@
|
||||
require "sidekiq/web"
|
||||
|
||||
Rails.application.routes.draw do
|
||||
scope "(:locale)", locale: /#{I18n.available_locales.join("|")}/ do
|
||||
devise_for :users
|
||||
root "home#index"
|
||||
|
||||
@ -47,4 +48,5 @@ Rails.application.routes.draw do
|
||||
|
||||
# Defines the root path route ("/")
|
||||
# root "posts#index"
|
||||
end
|
||||
end
|
||||
|
Loading…
Reference in New Issue
Block a user