feat: add translatable name module for countries and regions
- Introduced `TranslatableName` module to allow for localized names for `Country` and `Region` models. - Updated views to display `localized_name` instead of `name` for improved internationalization. - Refactored JSON serialization for `translations` attribute. - Enhanced localization support by adding new languages: Japanese and Korean, with updated locale files. - Removed outdated English and Chinese locales for countries and regions to clean up the codebase.
This commit is contained in:
parent
f6b9dcf187
commit
bd42833953
@ -1 +1 @@
|
||||
ruby-3.3.5
|
||||
3.3.5
|
||||
|
22
app/models/concerns/translatable_name.rb
Normal file
22
app/models/concerns/translatable_name.rb
Normal file
@ -0,0 +1,22 @@
|
||||
# app/models/concerns/translatable_name.rb
|
||||
module TranslatableName
|
||||
extend ActiveSupport::Concern
|
||||
|
||||
def localized_name(default_locale = "en")
|
||||
return name unless translations.present?
|
||||
|
||||
translations_hash = translations.is_a?(String) ? JSON.parse(translations) : translations
|
||||
|
||||
# 尝试完全匹配当前语言设置
|
||||
current_locale = I18n.locale.to_s
|
||||
return translations_hash[current_locale] if translations_hash[current_locale].present?
|
||||
|
||||
# 尝试匹配语言的基础部分(例如 'zh-CN' => 'zh')
|
||||
base_locale = current_locale.split("-").first
|
||||
matching_key = translations_hash.keys.find { |k| k.start_with?(base_locale) }
|
||||
return translations_hash[matching_key] if matching_key.present?
|
||||
|
||||
# 如果没有匹配,返回默认语言的翻译或原始名称
|
||||
translations_hash[default_locale] || name
|
||||
end
|
||||
end
|
@ -1,8 +1,9 @@
|
||||
class Country < ApplicationRecord
|
||||
include TranslatableName
|
||||
extend FriendlyId
|
||||
friendly_id :name, use: :slugged
|
||||
|
||||
before_save :format_timezones
|
||||
# before_save :format_json_attributes, :timezones, :translations
|
||||
|
||||
belongs_to :region, optional: true
|
||||
belongs_to :subregion, optional: true
|
||||
@ -13,14 +14,15 @@ class Country < ApplicationRecord
|
||||
validates :code, presence: true, uniqueness: true
|
||||
validates :iso2, uniqueness: true, allow_blank: true
|
||||
|
||||
serialize :translations, coder: JSON
|
||||
|
||||
def to_s
|
||||
name
|
||||
end
|
||||
|
||||
def localized_name
|
||||
I18n.t("countries.#{code}")
|
||||
end
|
||||
# def localized_name
|
||||
# I18n.t("countries.#{code}")
|
||||
# end
|
||||
|
||||
def self.ransackable_attributes(auth_object = nil)
|
||||
[ "code", "created_at", "id", "id_value", "name", "region_id", "slug", "updated_at" ]
|
||||
@ -32,26 +34,26 @@ class Country < ApplicationRecord
|
||||
|
||||
private
|
||||
|
||||
def format_timezones
|
||||
return unless timezones.is_a?(String)
|
||||
|
||||
# 使用正则替换 => 为 :
|
||||
json_string = timezones.gsub(/=>/, ":")
|
||||
|
||||
# 清理多余的空格
|
||||
json_string = json_string.gsub(/\s+/, " ").strip
|
||||
|
||||
begin
|
||||
# 验证是否为有效的 JSON
|
||||
parsed_json = JSON.parse(json_string)
|
||||
self.timezones = parsed_json.to_json
|
||||
rescue JSON::ParserError
|
||||
# 如果转换失败,可以选择:
|
||||
# 1. 保持原值
|
||||
# 2. 设置为空数组
|
||||
# 3. 记录错误日志
|
||||
Rails.logger.error("Invalid JSON format for country #{id}: #{timezones}")
|
||||
self.timezones = "[]"
|
||||
end
|
||||
end
|
||||
# def format_timezones
|
||||
# return unless timezones.is_a?(String)
|
||||
#
|
||||
# # 使用正则替换 => 为 :
|
||||
# json_string = timezones.gsub(/=>/, ":")
|
||||
#
|
||||
# # 清理多余的空格
|
||||
# json_string = json_string.gsub(/\s+/, " ").strip
|
||||
#
|
||||
# begin
|
||||
# # 验证是否为有效的 JSON
|
||||
# parsed_json = JSON.parse(json_string)
|
||||
# self.timezones = parsed_json.to_json
|
||||
# rescue JSON::ParserError
|
||||
# # 如果转换失败,可以选择:
|
||||
# # 1. 保持原值
|
||||
# # 2. 设置为空数组
|
||||
# # 3. 记录错误日志
|
||||
# Rails.logger.error("Invalid JSON format for country #{id}: #{timezones}")
|
||||
# self.timezones = "[]"
|
||||
# end
|
||||
# end
|
||||
end
|
||||
|
@ -1,4 +1,6 @@
|
||||
class Region < ApplicationRecord
|
||||
include TranslatableName
|
||||
|
||||
extend FriendlyId
|
||||
friendly_id :name, use: :slugged
|
||||
|
||||
@ -9,13 +11,15 @@ class Region < ApplicationRecord
|
||||
validates :name, presence: true, uniqueness: true
|
||||
validates :code, presence: true, uniqueness: true
|
||||
|
||||
serialize :translations, coder: JSON
|
||||
|
||||
def to_s
|
||||
name
|
||||
end
|
||||
|
||||
def localized_name
|
||||
I18n.t("regions.#{code}")
|
||||
end
|
||||
# def localized_name
|
||||
# I18n.t("regions.#{code}")
|
||||
# end
|
||||
|
||||
# 模型中允许被搜索的关联
|
||||
def self.ransackable_associations(auth_object = nil)
|
||||
|
@ -26,7 +26,7 @@
|
||||
<!-- 如果有特色图片,显示其信息 -->
|
||||
<% if featured_art %>
|
||||
<div class="text-sm text-base-content/60 pt-4">
|
||||
Latest from <%= featured_art.city.name %>, <%= featured_art.city.country.name %>
|
||||
<%= "#{t("text.latest_from")} #{featured_art.city.full_name}" %>
|
||||
<span class="mx-2">•</span>
|
||||
<%= featured_art.formatted_time(:date) %>
|
||||
</div>
|
||||
@ -47,18 +47,18 @@
|
||||
<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="M12 8v4l3 3m6-3a9 9 0 11-18 0 9 9 0 0118 0z" />
|
||||
</svg>
|
||||
<%= params[:sort] == 'oldest' ? 'Oldest First' : 'Newest First' %>
|
||||
<%= params[:sort] == 'oldest' ? t("text.oldest_first") : t("text.newest_first") %>
|
||||
<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 "Newest First", arts_path(sort: 'newest', region: params[:region]),
|
||||
<%= link_to t("text.newest_first"), arts_path(sort: 'newest', region: params[:region]),
|
||||
class: "#{'active' if params[:sort] != 'oldest'}" %>
|
||||
</li>
|
||||
<li>
|
||||
<%= link_to "Oldest First", arts_path(sort: 'oldest', region: params[:region]),
|
||||
<%= link_to t("text.oldest_first"), arts_path(sort: 'oldest', region: params[:region]),
|
||||
class: "#{'active' if params[:sort] == 'oldest'}" %>
|
||||
</li>
|
||||
</ul>
|
||||
@ -70,7 +70,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 || t("text.all_regions") %>
|
||||
<%= @current_region&.localized_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>
|
||||
@ -83,7 +83,7 @@
|
||||
<div class="divider my-1"></div>
|
||||
<% @regions.each do |region| %>
|
||||
<li>
|
||||
<%= link_to region.name, arts_path(region: region.id, sort: params[:sort]),
|
||||
<%= link_to region.localized_name, arts_path(region: region.id, sort: params[:sort]),
|
||||
class: "#{'active' if @current_region == region}" %>
|
||||
</li>
|
||||
<% end %>
|
||||
@ -95,7 +95,7 @@
|
||||
<div class="text-center text-sm text-base-content/70 mt-4">
|
||||
<%= "#{t("text.showing")} #{@weather_arts.total_count} #{t("text.weather_arts")} " %>
|
||||
<% if @current_region %>
|
||||
from <%= @current_region.name %>
|
||||
from <%= @current_region.localized_name %>
|
||||
<% end %>
|
||||
</div>
|
||||
</div>
|
||||
@ -118,7 +118,7 @@
|
||||
<%= art.city.name %>
|
||||
</h3>
|
||||
<p class="text-sm text-white/80">
|
||||
<%= art.city.country.name %>
|
||||
<%= "#{art.city&.country&.emoji + " " || ""}#{art.city&.country&.localized_name}" %>
|
||||
</p>
|
||||
<div class="flex items-center gap-2 text-white/90">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" class="h-5 w-5" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
||||
|
@ -27,7 +27,7 @@
|
||||
<div class="inline-block mt-6 px-4 py-2 bg-base-100/80 backdrop-blur-sm rounded-full text-sm">
|
||||
<%= t("text.latest_from") %>
|
||||
<span class="font-semibold"><%= featured_art.city.name %></span>,
|
||||
<%= featured_art.city.country.name %>
|
||||
<%= featured_art.city.country.localized_name %>
|
||||
<span class="mx-2">•</span>
|
||||
<%= featured_art.formatted_time(:date) %>
|
||||
</div>
|
||||
@ -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 || t("text.all_regions") %>
|
||||
<%= @current_region&.localized_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>
|
||||
@ -65,7 +65,7 @@
|
||||
<div class="divider my-1"></div>
|
||||
<% @regions.each do |region| %>
|
||||
<li>
|
||||
<%= link_to region.name,
|
||||
<%= link_to region.localized_name,
|
||||
cities_path(region: region.slug),
|
||||
class: "#{@current_region == region ? 'active' : ''}" %>
|
||||
</li>
|
||||
@ -79,21 +79,21 @@
|
||||
<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 21v-4m0 0V5a2 2 0 012-2h6.5l1 1H21l-3 6 3 6h-8.5l-1-1H5a2 2 0 00-2 2zm9-13.5V9" />
|
||||
</svg>
|
||||
<%= @current_country&.name || "All Countries" %>
|
||||
<%= @current_country&.localized_name || t("text.all_countries") %>
|
||||
<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 max-h-80 overflow-y-auto flex-nowrap">
|
||||
<li>
|
||||
<%= link_to "All in #{@current_region.name}",
|
||||
<%= link_to "#{t("text.all_in")} #{@current_region.localized_name}",
|
||||
cities_path(region: @current_region.slug),
|
||||
class: "#{@current_country ? '' : 'active'}" %>
|
||||
</li>
|
||||
<div class="divider my-1"></div>
|
||||
<% @current_region.countries.order(:name).each do |country| %>
|
||||
<li>
|
||||
<%= link_to "#{country&.emoji + " " || ""}#{country.name}",
|
||||
<%= link_to "#{country&.emoji + " " || ""}#{country.localized_name}",
|
||||
cities_path(region: @current_region.slug, country: country.slug),
|
||||
class: "#{@current_country == country ? 'active' : ''}" %>
|
||||
</li>
|
||||
@ -106,9 +106,9 @@
|
||||
<div class="text-sm text-base-content/70 hidden">
|
||||
<%= @cities.count %> <%= 'city'.pluralize(@cities.count) %>
|
||||
<% if @current_country %>
|
||||
in <%= @current_country.name %>
|
||||
in <%= @current_country.localized_name %>
|
||||
<% elsif @current_region %>
|
||||
in <%= @current_region.name %>
|
||||
in <%= @current_region.localized_name %>
|
||||
<% end %>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -35,9 +35,10 @@
|
||||
<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><%= t("title.sign_in") %></span>
|
||||
<%= render 'shared/language_switcher' %>
|
||||
<% end %>
|
||||
<% end %>
|
||||
|
||||
<%= render 'shared/language_switcher' %>
|
||||
</div>
|
||||
|
||||
<!-- Mobile Menu -->
|
||||
|
@ -14,8 +14,10 @@
|
||||
url: request.original_url
|
||||
},
|
||||
alternate: {
|
||||
"en" => url_for(locale: 'en'),
|
||||
"zh-CN" => url_for(locale: 'zh-CN'),
|
||||
"en" => url_for(locale: 'en')
|
||||
"ja" => url_for(locale: 'ja'),
|
||||
"ko" => url_for(locale: 'ko')
|
||||
}
|
||||
) %>
|
||||
<%= csrf_meta_tags %>
|
||||
|
@ -1,17 +1,26 @@
|
||||
<%# app/views/shared/_language_switcher.html.erb %>
|
||||
<div class="dropdown dropdown-end">
|
||||
<div tabindex="0" role="button" class="btn btn-ghost btn-circle">
|
||||
<% if I18n.locale.to_s == 'en' %>
|
||||
🇺🇸
|
||||
<% else %>
|
||||
🇨🇳
|
||||
<label tabindex="0" class="btn btn-ghost btn-sm">
|
||||
<%= t("language.#{I18n.locale}") %>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" class="h-4 w-4 ml-1" viewBox="0 0 20 20" fill="currentColor">
|
||||
<path fill-rule="evenodd" d="M5.293 7.293a1 1 0 011.414 0L10 10.586l3.293-3.293a1 1 0 111.414 1.414l-4 4a1 1 0 01-1.414 0l-4-4a1 1 0 010-1.414z" clip-rule="evenodd" />
|
||||
</svg>
|
||||
</label>
|
||||
<ul tabindex="0" class="dropdown-content menu p-2 shadow bg-base-100 rounded-box w-32">
|
||||
<%= link_to url_for(locale: :en), class: "px-4 py-2 hover:bg-base-200 rounded-lg #{I18n.locale == :en ? 'bg-base-200' : ''}" do %>
|
||||
<%= t("language.en") %>
|
||||
<% 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>
|
||||
|
||||
<%= link_to url_for(locale: :'zh-CN'), class: "px-4 py-2 hover:bg-base-200 rounded-lg #{I18n.locale == :'zh-CN' ? 'bg-base-200' : ''}" do %>
|
||||
<%= t("language.zh-CN") %>
|
||||
<% 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>
|
||||
|
||||
<%= link_to url_for(locale: :ja), class: "px-4 py-2 hover:bg-base-200 rounded-lg #{I18n.locale == :ja ? 'bg-base-200' : ''}" do %>
|
||||
<%= t("language.ja") %>
|
||||
<% end %>
|
||||
|
||||
<%= link_to url_for(locale: :ko), class: "px-4 py-2 hover:bg-base-200 rounded-lg #{I18n.locale == :ko ? 'bg-base-200' : ''}" do %>
|
||||
<%= t("language.ko") %>
|
||||
<% end %>
|
||||
</ul>
|
||||
</div>
|
@ -1,37 +1,37 @@
|
||||
<%# Partial _weather_stats.html.erb %>
|
||||
|
||||
<div class="stat bg-gradient-to-br from-primary/10 to-primary/20 hover:from-primary hover:to-primary/30 p-4 rounded-lg">
|
||||
<div class="stat-title font-medium text-base">Temperature</div>
|
||||
<div class="stat-title font-medium text-base"><%= t("card.temperature") %></div>
|
||||
<div class="stat-value text-3xl"><%= weather_art.temperature %>°C</div>
|
||||
<div class="stat-desc">Feels like <%= weather_art.feeling_temp %>°C</div>
|
||||
<div class="stat-desc"><%= t("card.feel_like") %> <%= weather_art.feeling_temp %>°C</div>
|
||||
</div>
|
||||
|
||||
<div class="stat bg-gradient-to-br from-secondary/10 to-secondary/20 hover:from-secondary hover:to-secondary/30 p-4 rounded-lg">
|
||||
<div class="stat-title font-medium text-base">Wind</div>
|
||||
<div class="stat-title font-medium text-base"><%= t("card.wind") %></div>
|
||||
<div class="stat-value text-3xl"><%= weather_art.wind_scale %></div>
|
||||
<div class="stat-desc"><%= weather_art.wind_speed %> km/h</div>
|
||||
</div>
|
||||
|
||||
<div class="stat bg-base-300 hover:bg-base-400 p-4 rounded-lg">
|
||||
<div class="stat-title font-medium text-base">Humidity</div>
|
||||
<div class="stat-title font-medium text-base"><%= t("card.humidity") %></div>
|
||||
<div class="stat-value text-3xl"><%= weather_art.humidity %>%</div>
|
||||
<div class="stat-desc">Relative humidity</div>
|
||||
<div class="stat-desc"><%= t("card.relative_humidity") %></div>
|
||||
</div>
|
||||
|
||||
<div class="stat bg-base-300 hover:bg-base-400 p-4 rounded-lg">
|
||||
<div class="stat-title font-medium text-base">Visibility</div>
|
||||
<div class="stat-title font-medium text-base"><%= t("card.visibility") %></div>
|
||||
<div class="stat-value text-3xl"><%= weather_art.visibility %> km</div>
|
||||
<div class="stat-desc">Clear view distance</div>
|
||||
<div class="stat-desc"><%= t("card.clear_view_distance") %></div>
|
||||
</div>
|
||||
|
||||
<div class="stat bg-accent/10 hover:bg-accent p-4 rounded-lg">
|
||||
<div class="stat-title font-medium text-base">Pressure</div>
|
||||
<div class="stat-title font-medium text-base"><%= t("card.pressure") %></div>
|
||||
<div class="stat-value text-3xl"><%= weather_art.pressure %> hPa</div>
|
||||
<div class="stat-desc">Atmospheric pressure</div>
|
||||
<div class="stat-desc"><%= t("card.atmospheric_pressure") %></div>
|
||||
</div>
|
||||
|
||||
<div class="stat bg-base-200 hover:bg-base-100 p-4 rounded-lg">
|
||||
<div class="stat-title font-medium text-base">Cloud Cover</div>
|
||||
<div class="stat-title font-medium text-base"><%= t("card.cloud_cover") %></div>
|
||||
<div class="stat-value text-3xl"><%= weather_art.cloud %>%</div>
|
||||
<div class="stat-desc">Sky coverage</div>
|
||||
<div class="stat-desc"><%= t("card.sky_coverage") %></div>
|
||||
</div>
|
@ -23,7 +23,7 @@
|
||||
|
||||
<div class="flex flex-wrap gap-4 mb-6">
|
||||
<div class="badge badge-lg badge-primary">
|
||||
<%= "#{@weather_art&.city&.country&.emoji + " " || ""}#{@city&.country&.name}" %>
|
||||
<%= "#{@weather_art&.city&.country&.emoji + " " || ""}#{@city&.country&.localized_name}" %>
|
||||
</div>
|
||||
<div class="badge badge-lg badge-secondary">
|
||||
<%= @weather_art&.city&.state&.name %>
|
||||
@ -82,7 +82,7 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" class="h-6 w-6 text-primary" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M13 10V3L4 14h7v7l9-11h-7z" />
|
||||
</svg>
|
||||
<h3 class="font-display font-bold text-lg">AI Prompt</h3>
|
||||
<h3 class="font-display font-bold text-lg"><%= t("title.ai_prompt") %></h3>
|
||||
</div>
|
||||
<p class="text-base-content/80 leading-relaxed">
|
||||
<%= @weather_art.prompt %>
|
||||
|
@ -5,7 +5,7 @@ require "i18n/backend/fallbacks"
|
||||
I18n.load_path += Dir[Rails.root.join("config", "locales", "*.{rb,yml}")]
|
||||
|
||||
# Permitted locales available for the application
|
||||
I18n.available_locales = [ :en, :"zh-CN" ]
|
||||
I18n.available_locales = [ :en, :"zh-CN", :ja, :ko ]
|
||||
|
||||
I18n::Backend::Simple.send(:include, I18n::Backend::Fallbacks)
|
||||
# I18n::Backend::Simple.include I18n::Backend::Fallbacks
|
||||
|
@ -1,55 +0,0 @@
|
||||
en:
|
||||
countries:
|
||||
# East Asia
|
||||
CN: 'China'
|
||||
JP: 'Japan'
|
||||
KR: 'South Korea'
|
||||
TW: 'Taiwan'
|
||||
HK: 'Hong Kong'
|
||||
|
||||
# South Asia
|
||||
IN: 'India'
|
||||
PK: 'Pakistan'
|
||||
BD: 'Bangladesh'
|
||||
|
||||
# Southeast Asia
|
||||
ID: 'Indonesia'
|
||||
VN: 'Vietnam'
|
||||
TH: 'Thailand'
|
||||
MM: 'Myanmar'
|
||||
SG: 'Singapore'
|
||||
|
||||
# Middle East
|
||||
TR: 'Turkey'
|
||||
IR: 'Iran'
|
||||
SA: 'Saudi Arabia'
|
||||
IQ: 'Iraq'
|
||||
|
||||
# Africa
|
||||
NG: 'Nigeria'
|
||||
EG: 'Egypt'
|
||||
CD: 'Democratic Republic of the Congo'
|
||||
TZ: 'Tanzania'
|
||||
ZA: 'South Africa'
|
||||
KE: 'Kenya'
|
||||
AO: 'Angola'
|
||||
ML: 'Mali'
|
||||
CI: 'Ivory Coast'
|
||||
|
||||
# Europe
|
||||
RU: 'Russia'
|
||||
GB: 'United Kingdom'
|
||||
DE: 'Germany'
|
||||
|
||||
# North America
|
||||
US: 'United States'
|
||||
MX: 'Mexico'
|
||||
|
||||
# South America
|
||||
BR: 'Brazil'
|
||||
PE: 'Peru'
|
||||
CO: 'Colombia'
|
||||
CL: 'Chile'
|
||||
|
||||
# Oceania
|
||||
AU: 'Australia'
|
@ -1,55 +0,0 @@
|
||||
zh-CN:
|
||||
countries:
|
||||
# East Asia
|
||||
CN: '中国'
|
||||
JP: '日本'
|
||||
KR: '韩国'
|
||||
TW: '台湾'
|
||||
HK: '香港'
|
||||
|
||||
# South Asia
|
||||
IN: '印度'
|
||||
PK: '巴基斯坦'
|
||||
BD: '孟加拉国'
|
||||
|
||||
# Southeast Asia
|
||||
ID: '印度尼西亚'
|
||||
VN: '越南'
|
||||
TH: '泰国'
|
||||
MM: '缅甸'
|
||||
SG: '新加坡'
|
||||
|
||||
# Middle East
|
||||
TR: '土耳其'
|
||||
IR: '伊朗'
|
||||
SA: '沙特阿拉伯'
|
||||
IQ: '伊拉克'
|
||||
|
||||
# Africa
|
||||
NG: '尼日利亚'
|
||||
EG: '埃及'
|
||||
CD: '刚果民主共和国'
|
||||
TZ: '坦桑尼亚'
|
||||
ZA: '南非'
|
||||
KE: '肯尼亚'
|
||||
AO: '安哥拉'
|
||||
ML: '马里'
|
||||
CI: '科特迪瓦'
|
||||
|
||||
# Europe
|
||||
RU: '俄罗斯'
|
||||
GB: '英国'
|
||||
DE: '德国'
|
||||
|
||||
# North America
|
||||
US: '美国'
|
||||
MX: '墨西哥'
|
||||
|
||||
# South America
|
||||
BR: '巴西'
|
||||
PE: '秘鲁'
|
||||
CO: '哥伦比亚'
|
||||
CL: '智利'
|
||||
|
||||
# Oceania
|
||||
AU: '澳大利亚'
|
@ -28,6 +28,11 @@
|
||||
# enabled: "ON"
|
||||
|
||||
en:
|
||||
language:
|
||||
en: "English"
|
||||
zh-CN: "简体中文"
|
||||
ja: "日本語"
|
||||
ko: "한국어"
|
||||
hello: "Hello world"
|
||||
brand:
|
||||
name: "Today AI Weather"
|
||||
@ -40,12 +45,17 @@ en:
|
||||
admin_dashboard: "Admin Dashboard"
|
||||
latest_weather_art: "Latest Weather Art"
|
||||
popular_weather_art: "Popular Weather Art"
|
||||
ai_prompt: "AI Prompt"
|
||||
text:
|
||||
latest_from: "Latest from"
|
||||
search_cities: "Search cities..."
|
||||
all_regions: "All Regions"
|
||||
all_countries: "All Countries"
|
||||
all_in: "All in"
|
||||
showing: "Showing"
|
||||
weather_arts: "Weather Arts"
|
||||
newest_first: "Newest First"
|
||||
oldest_first: "Oldest First"
|
||||
cities:
|
||||
title: "Explore Cities"
|
||||
arts:
|
||||
@ -62,6 +72,18 @@ en:
|
||||
view_all_weather_arts: "View All Weather Arts"
|
||||
back_to_cities: "Back to Cities"
|
||||
back_to: "Back to"
|
||||
card:
|
||||
temperature: "Temperature"
|
||||
wind: "Wind"
|
||||
humidity: "Humidity"
|
||||
visibility: "Visibility"
|
||||
pressure: "Pressure"
|
||||
cloud_cover: "Cloud Cover"
|
||||
feel_like: "Feels like"
|
||||
relative_humidity: "Relative humidity"
|
||||
clear_view_distance: "Clear view distance"
|
||||
atmospheric_pressure: "Atmospheric pressure"
|
||||
sky_coverage: "Sky coverage"
|
||||
pagination:
|
||||
showing_items: "Showing %{from} to %{to} of %{total} %{items}"
|
||||
items:
|
||||
|
62
config/locales/ja.yml
Normal file
62
config/locales/ja.yml
Normal file
@ -0,0 +1,62 @@
|
||||
ja:
|
||||
hello: "こんにちは世界"
|
||||
brand:
|
||||
name: "今日のAI天気"
|
||||
title:
|
||||
cities: "都市"
|
||||
arts: "アート"
|
||||
sign_in: "サインイン"
|
||||
sign_out: "サインアウト"
|
||||
settings: "設定"
|
||||
admin_dashboard: "管理者ダッシュボード"
|
||||
latest_weather_art: "最新の天気アート"
|
||||
popular_weather_art: "人気の天気アート"
|
||||
ai_prompt: "AIプロンプト"
|
||||
text:
|
||||
latest_from: "最新情報"
|
||||
search_cities: "都市を検索..."
|
||||
all_regions: "すべての地域"
|
||||
all_countries: "すべての国"
|
||||
all_in: "すべて含む"
|
||||
showing: "表示中"
|
||||
weather_arts: "天気アート"
|
||||
newest_first: "最新順"
|
||||
oldest_first: "古い順"
|
||||
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: "戻る"
|
||||
card:
|
||||
temperature: "温度"
|
||||
wind: "風"
|
||||
humidity: "湿度"
|
||||
visibility: "視界"
|
||||
pressure: "圧力"
|
||||
cloud_cover: "雲の覆い"
|
||||
feel_like: "体感温度"
|
||||
relative_humidity: "相対湿度"
|
||||
clear_view_distance: "クリアビュー距離"
|
||||
atmospheric_pressure: "大気圧"
|
||||
sky_coverage: "空の覆い"
|
||||
pagination:
|
||||
showing_items: "合計 %{total} %{items} のうち %{from} から %{to} まで表示"
|
||||
items:
|
||||
weather: "天気記録"
|
||||
default: "アイテム"
|
||||
time:
|
||||
formats:
|
||||
time_only: "%H:%M"
|
||||
with_zone: "%{time} %{zone}"
|
||||
date_and_time: "%{date} %{time}"
|
62
config/locales/ko.yml
Normal file
62
config/locales/ko.yml
Normal file
@ -0,0 +1,62 @@
|
||||
ko:
|
||||
hello: "안녕하세요 세계"
|
||||
brand:
|
||||
name: "오늘의 AI 날씨"
|
||||
title:
|
||||
cities: "도시"
|
||||
arts: "예술"
|
||||
sign_in: "로그인"
|
||||
sign_out: "로그아웃"
|
||||
settings: "설정"
|
||||
admin_dashboard: "관리자 대시보드"
|
||||
latest_weather_art: "최신 날씨 예술"
|
||||
popular_weather_art: "인기 있는 날씨 예술"
|
||||
ai_prompt: "AI 프롬프트"
|
||||
text:
|
||||
latest_from: "최신 소식"
|
||||
search_cities: "도시 검색..."
|
||||
all_regions: "모든 지역"
|
||||
all_countries: "모든 국가"
|
||||
all_in: "모두 포함"
|
||||
showing: "표시 중"
|
||||
weather_arts: "날씨 예술"
|
||||
newest_first: "최신순"
|
||||
oldest_first: "오래된 순"
|
||||
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: "돌아가기"
|
||||
card:
|
||||
temperature: "온도"
|
||||
wind: "바람"
|
||||
humidity: "습도"
|
||||
visibility: "가시성"
|
||||
pressure: "압력"
|
||||
cloud_cover: "구름 덮개"
|
||||
feel_like: "체감 온도"
|
||||
relative_humidity: "상대 습도"
|
||||
clear_view_distance: "맑은 시야 거리"
|
||||
atmospheric_pressure: "대기압"
|
||||
sky_coverage: "하늘 덮개"
|
||||
pagination:
|
||||
showing_items: "총 %{total} %{items} 중 %{from}에서 %{to}까지 표시"
|
||||
items:
|
||||
weather: "날씨 기록"
|
||||
default: "항목"
|
||||
time:
|
||||
formats:
|
||||
time_only: "%H:%M"
|
||||
with_zone: "%{time} %{zone}"
|
||||
date_and_time: "%{date} %{time}"
|
@ -1,15 +0,0 @@
|
||||
en:
|
||||
regions:
|
||||
AS: 'Asia'
|
||||
SA: 'South Asia'
|
||||
SEA: 'Southeast Asia'
|
||||
EA: 'East Asia'
|
||||
ME: 'Middle East'
|
||||
AF: 'Africa'
|
||||
NA: 'North Africa'
|
||||
SSA: 'Sub-Saharan Africa'
|
||||
EU: 'Europe'
|
||||
NAM: 'North America'
|
||||
SAM: 'South America'
|
||||
CAM: 'Central America'
|
||||
OC: 'Oceania'
|
@ -1,15 +0,0 @@
|
||||
zh-CN:
|
||||
regions:
|
||||
AS: '亚洲'
|
||||
SA: '南亚'
|
||||
SEA: '东南亚'
|
||||
EA: '东亚'
|
||||
ME: '中东'
|
||||
AF: '非洲'
|
||||
NA: '北非'
|
||||
SSA: '撒哈拉以南非洲'
|
||||
EU: '欧洲'
|
||||
NAM: '北美洲'
|
||||
SAM: '南美洲'
|
||||
CAM: '中美洲'
|
||||
OC: '大洋洲'
|
@ -11,12 +11,17 @@ zh-CN:
|
||||
admin_dashboard: "管理中枢"
|
||||
latest_weather_art: "气象绘卷·新作"
|
||||
popular_weather_art: "气象绘卷·佳作"
|
||||
ai_prompt: "天气描述"
|
||||
text:
|
||||
latest_from: "新至之城"
|
||||
search_cities: "寻城觅境…"
|
||||
all_regions: "寰宇之境"
|
||||
all_countries: "所有国家"
|
||||
all_in: "全部"
|
||||
showing: "映现"
|
||||
weather_arts: "气象艺境"
|
||||
newest_first: "最新优先"
|
||||
oldest_first: "最早优先"
|
||||
cities:
|
||||
title: "云游四海"
|
||||
arts:
|
||||
@ -32,6 +37,18 @@ zh-CN:
|
||||
view_all_weather_arts: "尽览天工"
|
||||
back_to_cities: "继续探索城市"
|
||||
back_to: "回到"
|
||||
card:
|
||||
temperature: "温度"
|
||||
wind: "风力"
|
||||
humidity: "湿度"
|
||||
visibility: "能见度"
|
||||
pressure: "气压"
|
||||
cloud_cover: "云量"
|
||||
feel_like: "体感温度"
|
||||
relative_humidity: "相对湿度"
|
||||
clear_view_distance: "清晰视距"
|
||||
atmospheric_pressure: "大气压力"
|
||||
sky_coverage: "天空覆盖"
|
||||
pagination:
|
||||
showing_items: "显示第 %{from} 到第 %{to} 条,共 %{total} 条%{items}"
|
||||
items:
|
||||
|
@ -32,7 +32,7 @@ namespace :geo do
|
||||
region.update!(
|
||||
name: data["name"],
|
||||
code: data["name"],
|
||||
translations: data["translations"],
|
||||
translations: data["translations"].to_json,
|
||||
flag: data["flag"] || true,
|
||||
wiki_data_id: data["wikiDataId"]
|
||||
)
|
||||
@ -55,7 +55,7 @@ namespace :geo do
|
||||
count += 1
|
||||
|
||||
subregion.update!(
|
||||
translations: data["translations"],
|
||||
translations: data["translations"].to_json,
|
||||
flag: data["flag"] || true,
|
||||
wiki_data_id: data["wikiDataId"]
|
||||
)
|
||||
@ -106,8 +106,8 @@ namespace :geo do
|
||||
tld: data["tld"],
|
||||
native: data["native"],
|
||||
nationality: data["nationality"],
|
||||
timezones: data["timezones"],
|
||||
translations: data["translations"],
|
||||
timezones: data["timezones"].to_json,
|
||||
translations: data["translations"].to_json,
|
||||
latitude: data["latitude"],
|
||||
longitude: data["longitude"],
|
||||
emoji: data["emoji"],
|
||||
|
Loading…
Reference in New Issue
Block a user