Ruby on Rails ile Çok Dilli Web Sitesi

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.

Ruby on Rails

Ruby on Rails

 

Ş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

Ruby on Rails

Ruby on Rails

Ş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.

Ruby on Rails

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.

Ruby on Rails

Ruby on RailsRuby on Rails

Ruby on Rails Ruby on Rails

 

Uygulamamızın kaynak kodlarına githup üzerinden erişebilir ve indirebilirsiniz.

Kaynak Kodlar

Sağlıcakla kalın kolay gelsin….

Categories:

Leave a Reply

Your email address will not be published. Required fields are marked *