Merhaba arkadaşlar,
Bu yazımda sizlere Ruby on Rails ile çok dilli web sitesi yapımından ve Ruby on Rails te active record hata mesajlarının nasıl türkçe yapıldığını anlatmaya çalışacağım. Bu yazı ile beraber birde Ruby on Rails ile çok dilli web sitesi uygulaması geliştireceğiz. Uygulamayı githup adresimden ulaşabilirsiniz. Kaynak Kodlar
Öncelikle projemizi oluşturalım.
$ rails new multilingual-site --database=postgresql
Burada ben database olarak postgresql kullandım çünkü veritabanını da içeren bir örnek yapmak istiyorum. Projemizi oluşturduk veritabanı ayarlarını yaptık.
Hızlıca scaffold programming sayesinde product isminde bir controller oluşturalım.
$ rails generate scaffold Product name description 'price:decimal{7,2}'
Scaffold programming hakkında bir bilginiz yoksa görecekleriniz karşısında değşete kapılmanız normal :) isterseniz şu linkten Scaffold Programming hakkında bilgi edinebilirsiniz.
Product controller’ı oluşturduk ve eleman olarak name, description ve price atadık. Scaffold programming bizim için basit bir uygulama oluşturdu. Silme, güncelleme, görüntüleme ve tüm ürünleri listeleme sayfaları hazır hale geldi.
Şimdi gelelim veritabanına product modelimizi migrate etmeye. Yukarıdaki fotoğrafta gördüğünüz şekliye başka bir elaman eklemeden veritabanımızı migrate ediyoruz.
$ rake db:migrate
Bu işlemden sonra config/routes.rb
dosyasında yönlendirilecek sayfamızı belirliyoruz.
MultilingualSite::Application.routes.draw do resources :products root :to => 'products#index' end
Sayfada aktif olan dili görüntülemek için app/views/layouts/application.html.erb
sayfasına şu şeklide değiştiriyoruz.
<!DOCTYPE html> <html> <head> <title>MultilingualSite</title> <%= stylesheet_link_tag "application", media: "all", "data-turbolinks-track" => true %> <%= javascript_include_tag "application", "data-turbolinks-track" => true %> <%= csrf_meta_tags %> </head> <body> <p> <b>I18n.locale:</b> <%= I18n.locale %> </p> <%= yield %> </body> </html>
Uygulamamız da rails server’ı çalıştırdığımızda ekran görüntüsü aşşağıdaki gibidir.
$ rails s
Şimdi gelelim dil için yapmamız gereken değişikliklere. config/application.rb
dosyasında sayfa ilk yüklendiğinde geçerli olacak dil yer alır. Bunun tr olmasını istiyorum. Uygulamamız da Türkçe ve ingilizce dil seçenekleri olacak ve default olarak Türkçe sayfa yüklenecek.
# The default locale is :en and all translations from config/locales/*.rb,yml are auto loaded. # config.i18n.load_path += Dir[Rails.root.join('my', 'locales', '*.{rb,yml}').to_s] config.i18n.default_locale = :tr
Sayfa yönlendirmeleri için config/routes.rb
dosyasında şu değişiklikler yapılır.
MultilingualSite::Application.routes.draw do resources :products scope "(:locale)", :locale => /en|tr/ do root :to => 'products#index' end end
Daha sonrada urlden yapılan isteklere göre dil ayarlanması için app/controllers/application_controller.rb
dosyasını aşşağıdaki gibi düzenliyoruz
class ApplicationController < ActionController::Base # Prevent CSRF attacks by raising an exception. # For APIs, you may want to use :null_session instead. protect_from_forgery with: :exception # Added by İsmail before_filter :set_locale private def set_locale I18n.locale = params[:locale] || I18n.default_locale Rails.application.routes.default_url_options[:locale]= I18n.locale end end
Yönlendirmeler için app/views/layouts/application.html.erb
dosyasını aşşağıdaki gibi düzenliyoruz.
<!DOCTYPE html> <html> <head> <title>MultilingualSite</title> <%= stylesheet_link_tag "application", media: "all", "data-turbolinks-track" => true %> <%= javascript_include_tag "application", "data-turbolinks-track" => true %> <%= csrf_meta_tags %> </head> <body> <p> <% if I18n.locale.to_s == "en" %> <%= link_to_unless I18n.locale == :tr, "Türkçe", locale: :tr %> <% else %> <%= link_to_unless I18n.locale == :en, "English", locale: :en %> <% end %> </p> <%= yield %> </body> </html>
Artık uygulamamız Türkçe ve İngilizce olarak hazır. Artık dil dosyalarınıda ekleyebiliriz. Bu işlemi yapmadan önce activerecod ta hata mesajlarını da gözlemleyebilmek için app/models/product.rb
dosyasınına bazı doğrulamalar ekliyoruz
class Product < ActiveRecord::Base validates :name, :presence => true, :uniqueness => true, :length => { :within => 2..255 } validates :price, :presence => true, :numericality => { :greater_than => 0 } end
Eğer dil dosyalarını oluşturmadan uygulamayı çalıştırırsanız şekildeki gibi hatalar alabilirsiniz.
Dil dosyalarını config/locales
dizinine ekliyoruz. Ayrıca bu dizinde config/locales/en.yml
ile config/locales/tr.yml
dosyaları genel çevirilerin bulunduğu dosyalardır. Bu dizine şu klasörleri oluşturuyoruz. config/locales/models/product
config/locales/views/product
Tahmin edeceğiniz gibi app/views
de yer alan dosyalar için views klasörünü ve app/models
klasöründe yer alan dosyalar içinde models klasörü içindeki dil dosyalarını kullancağız.
Peki bu dil dosyaları nasıl yüklenecek? Bu işlemide halletmek için config/application.rb
dosyasında şu değişiklikleri yapıyoruz.
# Added by İsmail config.i18n.load_path += Dir[Rails.root.join('config', 'locales', 'models', '*', '*.yml').to_s] config.i18n.load_path += Dir[Rails.root.join('config', 'locales', 'views', '*', '*.yml').to_s] config.i18n.default_locale = :tr
Şimdi dil dosyalarını düzenleyebiliriz. Türkçe için config/locales/models/product/tr.yml
tr: activerecord: models: product: 'Ürün' attributes: product: name: 'İsim' description: 'Açıklama' price: 'Fiyat'
İngilizce için config/locales/models/product/en.yml
en: activerecord: models: product: 'Product' attributes: product: name: 'Name' description: 'Description' price: 'Price'
Türkçe için config/locales/views/product/tr.yml
tr: views: show: 'Göster' edit: 'Düzenle' destroy: 'Sil' are_you_sure: 'Silmek istediğinize emin misiniz?' back: 'Geri' edit: 'Düzenle' product: index: title: 'Ürünler listesi' new_product: 'Yeni Ürün' edit: title: 'Ürün Düzenle' update: 'Ürünü Güncelle' new: title: 'Yeni Ürün' create: 'Oluştur' show: title: 'Ürün detayları' flash_messages: product_was_successfully_created: 'Ürün başarıyla oluşturuldu.' product_was_successfully_updated: 'Ürün başarıyla güncellendi.'
İngilizce için config/locales/views/product/en.yml
en: views: show: "Show" edit: "Edit" destroy: "Delete" are_you_sure: "Are you sure?" back: "Back" edit: "Edit" product: index: title: "List of all products" new_product: "New product" edit: title: "Edit Product" update: "Update Product" new: title: "New product" create: "Create product" show: title: "Product detail" flash_messages: product_was_successfully_created: "Product was successfully created." product_was_successfully_updated: "Product was successfully updated."
Türkçe için config/locales/tr.yml
tr: errors: error: one: "%{count} hata gerçekleşti" other: "%{count} hata gerçekleşti" format: "%{attribute} %{message}" messages: &errors_messages inclusion: "is not included in the list" exclusion: "is reserved" invalid: "geçersiz" confirmation: " alanı %{attribute} alanı ile aynı değil" accepted: "must be accepted" empty: "alanı boş olamaz" blank: "alanı boş geçilemez" present: "alanı boş olmamalıdır" too_long: one: "alanı çok uzun (maksimum 1 karakter)" other: "alanı çok uzun (maksimum %{count} karakter)" too_short: one: "alanı çok kısa (minimum 1 karakter)" other: "alanı çok kısa (minimum %{count} karakter)" wrong_length: one: "alanının uzunluğu doğru değil (1 karakter olmalı)" other: "alanının uzunluğu doğru değil (%{count} karakter olmalı)" not_a_number: "alanı sayı değil" not_an_integer: "alanı sayı olmalı" greater_than: "alanı %{count} e göre büyük olmalıdır" greater_than_or_equal_to: "alanı %{count} e göre büyük yada eşit olmalı" equal_to: "alanı %{count} e göre eşit olmalıdır" less_than: "alanı %{count} küçük olmalıdır" less_than_or_equal_to: "alanı %{count} e göre küçük yada eşit olmalıdır" other_than: "alanı %{count} e göre farklı olmalıdır" odd: "alanı tek olmalıdır" even: "alanı çift olmalıdır" taken: "kullanılıyor" activerecord: errors: template: header: one: "%{model} kaydı yapılırken 1 hata gerçekleşti" other: "%{model} kaydı yapılırken %{count} hata gerçekleşti" body: "Aşşağıdaki alanlarda hata oluştu :" messages: record_invalid: "Doğrulama hatalı: %{errors}" restrict_dependent_destroy: one: "%{record} kaydına bağlı olduğu için silinemez" many: "%{record} kayıtlarına bağlı olduğu için silinemez" <<: *errors_messages full_messages: format: "%{attribute} %{message}"
İngilizce için config/locales/en.yml
en: errors: error: one: "%{count} error occurred" other: "%{count} errors occurred" format: " %{attribute} %{message}" messages: &errors_messages inclusion: "is not included in the list" exclusion: "is reserved" invalid: "is invalid" confirmation: "doesn't match %{attribute}" accepted: "must be accepted" empty: "can't be empty" blank: "can't be blank" present: "must be blank" too_long: one: "is too long (maximum is 1 character)" other: "is too long (maximum is %{count} characters)" too_short: one: "is too short (minimum is 1 character)" other: "is too short (minimum is %{count} characters)" wrong_length: one: "is the wrong length (should be 1 character)" other: "is the wrong length (should be %{count} characters)" not_a_number: "is not a number" not_an_integer: "must be an integer" greater_than: "must be greater than %{count}" greater_than_or_equal_to: "must be greater than or equal to %{count}" equal_to: "must be equal to %{count}" less_than: "must be less than %{count}" less_than_or_equal_to: "must be less than or equal to %{count}" other_than: "must be other than %{count}" odd: "must be odd" even: "must be even" taken: "already been taken by another" activerecord: errors: template: header: one: "1 error prohibited this %{model} from being saved" other: "%{count} errors prohibited this %{model} from being saved" body: "There were problems with the following fields:" messages: record_invalid: "Validation failed: %{errors}" restrict_dependent_destroy: one: "Cannot delete record because a dependent %{record} exists" many: "Cannot delete record because dependent %{record} exist" <<: *errors_messages full_messages: format: "%{attribute} %{message}"
Evet dil dosyalarımız bu şekilde şimdi dil dosyalarımızı uygulamamıza geçirmeye geldi. Bu işlemi kodları size sunarak göstermek istiyorum
app/views/products/_form.html.erb
dosyasının yeni hali
<%= form_for(@product) do |f| %> <% if @product.errors.any? %> <div id="error_explanation"> <h2><%= t 'errors.error', :count => @product.errors.count %> </h2> <ul> <% @product.errors.full_messages.each do |msg| %> <li><%= msg %></li> <% end %> </ul> </div> <% end %> <div class="field"> <%= f.label :name %><br> <%= f.text_field :name %> </div> <div class="field"> <%= f.label :description %><br> <%= f.text_field :description %> </div> <div class="field"> <%= f.label :price %><br> <%= f.text_field :price %> </div> <div class="actions"> <%= f.submit %> </div> <% end %>
app/views/products/edit.html.erb
dosyasının yeni hali
<h1> <%= t 'views.product.edit.title' %> </h1> <%= render 'form' %> <%= link_to I18n.t('views.show'), @product %> | <%= link_to I18n.t('views.back'), products_path %>
app/views/products/index.html.erb
dosyasının yeni hali
<h1> <%= t 'views.product.index.title' %> </h1> <table> <thead> <tr> <th><%= Product.human_attribute_name(:name) %></th> <th><%= Product.human_attribute_name(:description) %></th> <th><%= Product.human_attribute_name(:price) %></th> <th></th> <th></th> <th></th> </tr> </thead> <tbody> <% @products.each do |product| %> <tr> <td><%= product.name %></td> <td><%= product.description %></td> <td><%= product.price %></td> <td><%= link_to I18n.t('views.show'), product %></td> <td><%= link_to I18n.t('views.edit'), edit_product_path(product) %></td> <td><%= link_to I18n.t('views.destroy'), product, method: :delete, data: { confirm: I18n.t('views.are_you_sure') } %></td> </tr> <% end %> </tbody> </table> <br> <%= link_to I18n.t('views.product.index.new_product'), new_product_path %>
app/views/products/new.html.erb
dosyasının yeni hali
<h1> <%= t 'views.product.new.title' %> </h1> <%= render 'form' %> <%= link_to I18n.t('views.back'), products_path %>
app/views/products/show.html.erb
dosyasının yeni hali
<p id="notice"><%= notice %></p> <p> <strong><%= Product.human_attribute_name(:name) %>:</strong> <%= @product.name %> </p> <p> <strong><%= Product.human_attribute_name(:description) %>:</strong> <%= @product.description %> </p> <p> <strong><%= Product.human_attribute_name(:price) %>:</strong> <%= @product.price %> </p> <%= link_to I18n.t('views.edit'), edit_product_path(@product) %> | <%= link_to I18n.t('views.back'), products_path %>
app/controllers/products_controller.rb
dosyasının yeni hali
class ProductsController < ApplicationController before_action :set_product, only: [:show, :edit, :update, :destroy] # GET /products # GET /products.json def index @products = Product.all end # GET /products/1 # GET /products/1.json def show end # GET /products/new def new @product = Product.new end # GET /products/1/edit def edit end # POST /products # POST /products.json def create @product = Product.new(product_params) respond_to do |format| if @product.save format.html { redirect_to @product, notice: I18n.t('views.product.flash_messages.product_was_successfully_created')} format.json { render action: 'show', status: :created, location: @product } else format.html { render action: 'new' } format.json { render json: @product.errors, status: :unprocessable_entity } end end end # PATCH/PUT /products/1 # PATCH/PUT /products/1.json def update respond_to do |format| if @product.update(product_params) format.html { redirect_to @product, notice: I18n.t('views.product.flash_messages.product_was_successfully_updated') } format.json { head :no_content } else format.html { render action: 'edit' } format.json { render json: @product.errors, status: :unprocessable_entity } end end end # DELETE /products/1 # DELETE /products/1.json def destroy @product.destroy respond_to do |format| format.html { redirect_to products_url } format.json { head :no_content } end end private # Use callbacks to share common setup or constraints between actions. def set_product @product = Product.find(params[:id]) end # Never trust parameters from the scary internet, only allow the white list through. def product_params params.require(:product).permit(:name, :description, :price) end end
Evet bu şekilde uygulamamızı bitirmiş olduk. Aşşağıdaki ekran alıntılarında gördüğünüz gibi uygulamamız başarıyla çalışıyor. Ürün oluştururken ve güncellerken dikkatinizi Create Ürün ve Update Ürün çekmiştir. Bunun hangi dil dosyasından oluşturulduğunu bulamadım ama aklıma şöyle bir çözüm geldi Ürün oluşturma ve ürün güncelleme sayfaları app/views/products/_form.html.erb
dosyasından oluştuğu için bu dosyadaki buttonu çıkarıp app/views/products/edit.html.erb
ve app/views/products/new.html.erb
dosyalarına kendi butonlarını yerleştirirsek şu şekilde
app/views/products/edit.html.erb
için
<div class="actions"> <%= f.submit I18n.t('views.product.edit.update') %> </div>
app/views/products/new.html.erb
için
<div class="actions"> <%= f.submit I18n.t('views.product.new.create') %> </div>
Bu sorunu da çözmüş oluruz.
Uygulamamızın kaynak kodlarına githup üzerinden erişebilir ve indirebilirsiniz.
Sağlıcakla kalın kolay gelsin….