Jak uniknąć angielskich komunikatów o błędach

Nie jest to najlepszy sposób bo nie powinno się tak robić ale :slight_smile: :
W application.rb naleÂży przeciżyć dwie metody np w taki sposób
class ApplicationController < ActionController::Base

class ActionView::Base
def error_messages_for(object_name, options = {})
options = options.symbolize_keys
object = instance_variable_get("@#{object_name}")
unless object.errors.empty?
content_tag(“div”,
content_tag(
options[:header_tag] || “h2”,
“Wystąpiła następująca ilość błędów związanych z obiektem '#{object_name.to_s.gsub(”_", " “)}’: #{object.errors.count}”
) +
content_tag(“p”, “Wystąpił problem z następującymi polami”) +
content_tag(“ul”, object.errors.full_messages.collect { |msg| content_tag(“li”, msg) }),
“id” => options[:id] || “errorExplanation”, “class” => options[:class] || “errorExplanation”
)
end
end
end

class ActiveRecord::Errors		
	def full_messages
       full_messages = [] 
	   @errors.each_key do |attr|
    	 @errors[attr].each do |msg|
       		next if msg.nil?
	           if attr == "base"
    		     full_messages << msg
	           else
    		     full_messages << msg
	           end
         end
	   end
       return full_messages
	 end
end     


end

Pierwsza definiuje message o błędach, druga pozwala na uniknięcie nazw zmiennych podczas wyświetlania błędów.
Może ktoś zna lepszy sposób, właśnie wrociłem do pisania stronki po jakichś 2 miesiącach przerwy. Coś sie ruszyło z internacjonalizacją, szczegłlnie na polu zakodowanych na sztywno messegów ?
Pozdrawiam, Jacek Balcerski

Moja propozycja rozwiazania (podpatrzona gdzies w trzewiach sieci):

  1. nowy folder w app/exts
  2. w nim tworzymy plik all.rb z zawartoscia:
Dir[File.dirname(__FILE__) + "/**/*.rb"].each { |file| require(file) }
  1. przystepujemy do prawdziwej zmiany - w app/exts tworzymy folder active_record, a w nim plik validations.rb - w ten sposob utrzymujemy konwencje z railsow; zawartosc app/exts/validations.rb:

[code=“ruby”]module ActiveRecord
class Errors
begin
# tlumaczenia ofcoz trzeba dokonac samodzielnie…
@@default_error_messages.update( {
:inclusion => “is not included in the list”,
:exclusion => “is reserved”,
:invalid => “is invalid”,
:confirmation => “doesn’t match confirmation”,
:accepted => “must be accepted”,
:empty => “can’t be empty”,
:blank => “can’t be blank”,
:too_long => “is too long (max is %d characters)”,
:too_short => “is too short (min is %d characters)”,
:wrong_length => “is the wrong length (should be %d characters)”,
:taken => “has already been taken”,
:not_a_number => “is not a number”
})
end

alias :old_on_blank :add_on_blank
# a to jest moja wersja metody ktora nie robi nic sensownego ale pokazuje 
# jakby co - to mozna ;)
def add_on_blank(attributes, msg = @@default_error_messages[:empty])
   old_on_blank(attributes, msg)
end

end
end[/code]
4. dodajemy obsluge naszych swiezych zmian do aplikacji - w pliku app/config/environment.rb na koncu dodajemy linijke wlaczajaca nasze zmiany:

require "#{RAILS_ROOT}/app/exts/all"

Taki sposob podejscia pozwala utrzymac w jednym miejscu wszystkie modyfikacje orginalnego zachowania railsow. Tworzy sie plik i umieszcza w stosownym katalogu pod app/exts i voila - wlaczone i zaaplikowane…

oops to moj pierwszy post - wiec witam wszystkich bardzo serdecznie !!

happy railing
– daniel

…i pozwala później na szybkie przekształcenie tej modyfikacji w plugina :wink:

Witam rubberów!
czy wie ktoś może jak przetłumaczyć nazwy kolumn i tabel, tak aby w walidacji nie pojawiało mi się password a hasło, chociaż w bazie mam nazwę password. Doinstalowałem plugin Globalize i chcę mieć wszystko w 2 językach.
Dzięki!

zacznij uzywac :message w validacjach ? :slight_smile: pozatym polecam gettext :slight_smile:

np uzywajac gettext zrobil bys to tak:

validation_presence_of :password, :message => _(‘Field Password Can’t Be Blank’)

Oczywiscie doradzam wtedy napisanie wlasnego helper’a do error_messages_for()

Globalize stanowczo odradzam poniewaz jest to rozwiazanie oparte na bazie danych i strasznie niewydajne.

Poważnie? Ja nie zauważyłem jakiejś słabej wydajności. Zwykle pobierane są krótkie teksty, są poindeksowane. Relacyjna baza danych jest bardzo szybka, bo wykorzystuje własny cache do takich niedużych operacji.

To, że używana jest baza danych to tylko zaleta. Np. proces tłumaczenia nie wymaga co chwilę restartowania Mongrela aby zmiany były widoczne w serwisie. Gettext nadaje się tylko do tłumaczenia interfejsu. Globalize pozwala zaś też tworzyć wiele wersji językowych dla tekstów trzymanych w bazie mi ładnie te wersje się przełączają.

No ja sie zgadzam ze gettextu nie nalezy uzywac do tworzenia roznych dokumentow juz bezposrednio na strone, natomiast przeciez jest to standardowy system lokalizawania walsnie interfejsu, ale sa gusta i gusciki :wink: Ja sie zrazilem do globalize :wink:

[quote=balcer]Nie jest to najlepszy sposób bo nie powinno się tak robić ale :slight_smile: :
W application.rb naleÂży przeciżyć dwie metody np w taki sposób
class ApplicationController < ActionController::Base

class ActionView::Base
def error_messages_for(object_name, options = {})
(…)
class ActiveRecord::Errors
(…)
end

Pierwsza definiuje message o błędach, druga pozwala na uniknięcie nazw zmiennych podczas wyświetlania błędów.
Może ktoś zna lepszy sposób, właśnie wrociłem do pisania stronki po jakichś 2 miesiącach przerwy. Coś sie ruszyło z internacjonalizacją, szczegłlnie na polu zakodowanych na sztywno messegów ?
Pozdrawiam, Jacek Balcerski[/quote]
Siedziałem nad tym ładnych parę godzin. I już praktycznie doszedłem do tego samego, ale ten post pozwolił mi to skończyć trochę wcześniej, niż jakbym robił to sam :slight_smile:
Według mnie trochę to bez sensu, że nie można ustawić np. error_messages_for i parametru :base (żeby nie pokazywał nazw kolumn), no ale cóż…

Aha, jako że to mój pierwszy post, to witam wszystkich. Pewnie zacznę częściej tu się pojawiać.

Sabon

aby zmienić nazwy pól wyświetlanych w błędach można spróbować takiego rozwiązania
jesli mamy model user
w pliku user.rb
class User < ActiveRecord::Base

validates_presence_of :login, :message => “nie powinno byc puste”
validates_presence_of :email, :message => “nie powinno byc puste”
validates_presence_of :password, :if =>

end
class << self
HUMANIZED_ATTRIBUTE_KEY_NAMES = {
“password” => “Pole hasła” ,
“email” => “Pole adresu email” ,
“password_confirmation” => “Pole potwierdzenia hasła”
}
def human_attribute_name(attribute_key_name)
HUMANIZED_ATTRIBUTE_KEY_NAMES[attribute_key_name] || super
end
end

btw Mój pierwszy post tutaj witam Wszystkich :slight_smile:

a coś takiego:

validates_presence_of :something, :message => "^Coś nie powinno być puste"

Daszek ^ chyba sprawi, że w message nie pojawi się nazwa zmiennej?

[quote=seban]a coś takiego:

validates_presence_of :something, :message => "^Coś nie powinno być puste"

Daszek ^ chyba sprawi, że w message nie pojawi się nazwa zmiennej?[/quote]
AFAIR do tego chyba trzeba cos spatchowac / wrzucic plugin, bo natywnie tego nie ma.

Jest taki plugin ale wystarczy wsadzic do liba:

[code]module ActiveRecord
class Errors
def full_messages_with_custom
full_messages = []

  @errors.each_key do |attr|
    @errors[attr].each do |msg|
      next if msg.nil?
      
      if attr == "base"
        full_messages << msg
      elsif msg =~ /^\^/ # TUTAJ
        full_messages << msg[1..-1]
      else
        full_messages << @base.class.human_attribute_name(attr) + " " + msg
      end
    end
  end
  return full_messages
end
alias_method_chain :full_messages, :custom 

end
end[/code]
… ale jak ktos dodatkowo uzywa gettexta, to:

[code]require ‘gettext/active_record’

module ActiveRecord
class Errors
include GetText

def localize_error_message(attr, msg, append_field) # :nodoc:
  custom_msg = nil
  #Ugly but... :-<
  @@default_error_messages_d.dup.merge(@base.custom_error_messages_d).each do |key, regexp|
    if regexp =~ msg
      custom_msg = @base.gettext(key)
      custom_msg = _(msg) if custom_msg == msg 
        custom_msg = _(custom_msg) % $1.to_i
      break
    end
  end

  unless custom_msg
    custom_msg = @base.gettext(msg)
    custom_msg = _(msg) if custom_msg == msg 
  end
  if attr == "base"
    full_message = custom_msg
  elsif /^\^/ =~ custom_msg
    full_message = custom_msg[1..-1]
  elsif /%\{fn\}/ =~ custom_msg
    full_message = custom_msg % {:fn => @base.class.human_attribute_name(attr)}
  elsif append_field
    full_message = @base.class.human_attribute_name(attr) + " " + custom_msg
  else
    full_message = custom_msg
  end
  full_message
end    

end
end[/code]

Polecam nam naszego plugina do zamiany komunikatów ActiveRecord na dowolny język:


(wymagane jest zainstalowanie tego pluginu w pierwszej kolejnosci)

Jeśli używacie Gibberisha plugin ten będzie dla was idealny!

Przetłumaczy dla was automatycznie komunikaty w stylu (ponizsze stringi zamiencie na polski):

error_blank: “nie może być puste”
error_too_long: “jest zbyt długie (maksimum to %d znaków)”
error_taken: “jest już zajęte”

Co wiecej przetłumaczy wam automatycznie labele do formularzy :slight_smile:
Zakladajac, ze macie 2 modele User, i Product, gdzie obydwa maja pole: body
Dodajcie do pliku z tlumaczeniem: “attr_#{table_name.singularize}_#{attribute_key_name}”. Np:

attr_user_name: Imię i Nazwisko
attr_product_name: Nazwa produktu

inne pluginy tego typu, które pozwalaja na podobne rzeczy (np. globalite) obydwa pola przetłumaczylyby wam jako: Nazwa …

Jak dla mnie najmniej inwazyjnym sposobem jest przeciążenie ActionView::Base i ActiveRecord::Errors, gdzie w ‘Errors’ pomija się nazwy zmiennych (przeważnie angielskie, bo tak konstruuję swoją bazę).
Wtedy w bardzo prosty sposób przy każdym validates_* dodaję :message, gdzie zawieram nazwę zmiennej, np.: validates_presence_of_password, :message => “Hasło nie może być puste”.
Proste, nieinwazyjne i nie trzeba w kilku miejscach tłumaczyć nazw zmiennych i pamiętać o spójności.
Pytanie tylko czy to w końcu zostało ujęte w jakimś prostym pluginie czy ciągle trzeba to wrzucać do environment.rb (tudzież app/exts).

PS. Ktokolwiek jest odpowiedzialny za ustawienia forum, powinien poprawić godzinę na serwerze. Jest 2 godz. do tyłu.

Jeszcze inna propozycja rozwiazania (chociaz podobna do jednej z poprzednich)

[code]module ServiceDesk #:nodoc:
module Humanizer
def self.included(base) # :nodoc:
base.extend ClassMethods
end

module ClassMethods
  def humanize(attributes)
    attributes.stringify_keys!
    if read_inheritable_attribute(:human_attribute_names).nil?
      class_inheritable_reader(:human_attribute_names)
      write_inheritable_attribute(:human_attribute_names, attributes)
      self.class_eval do
        def self.human_attribute_name(attr)
          self.human_attribute_names[attr] || super(attr)
        end
      end
    else
      write_inheritable_attribute(:human_attribute_names, read_inheritable_attribute(:human_attribute_names).merge(attributes))
    end
  end
end

end
end

ActiveRecord::Base.send(:include, ServiceDesk::Humanizer)[/code]
w praktyce wyglada to tak:

[code]class Ticket < ActiveRecord::Base
humanize({
:location_name => “Location”
})
end

class Ticket2 < Ticket
humanize({
:requestor_name => “Requestor”
})
end[/code]

[code]>> Ticket.human_attribute_name(“location_name”)
=> “Location”

Ticket.human_attribute_name(“requestor_name”)
=> “Requestor name”

Ticket2.human_attribute_name(“requestor_name”)
=> “Requestor”[/code]

Na dzień dzisiejszy to chyba lekko zbędny kod :slight_smile:

Railsy korzystają z tłumaczeń i18n domyślnie i korzystając z nich można bez problemu przetłumaczyć nazwy pól.

[quote=drogus]Na dzień dzisiejszy to chyba lekko zbędny kod :slight_smile:

Railsy korzystają z tłumaczeń i18n domyślnie i korzystając z nich można bez problemu przetłumaczyć nazwy pól.[/quote]
Nie korzystam z i18n, kiepsko sie skaluje na plikach tekstowych, majac 6000 string’ów w aplikacji było by mi bardzo trudno tym zarządzać. Gettext w tym przypadku jest znacznie lepszy. IMHO i18n w rails to niewypał.

Szukałem na necie rozwiązania dla małych aplikacji i nie doszukałem się niczego prostego co bym mógł zakumać.
Zrobiłem to po swojemu (popatrzyłem w źródła validation.rb).

Piszę tutaj, bo może akurat ktoś będzie miał podobny problem.

Ogólnie chodziło o to, że to co jest w standardowym pl.yml to nie za bardzo pasuje nawet do standardowego restfull authentication.
“{attribute} {message}” = “Email nie może być puste”, itp. :slight_smile:

Może da się to inaczej zrobić, ale dla mnie jest idealne rozwiązanie, gdyż pasuje do kazdej opcji. Nie muszę się trzymać schematu “{attribute} {message}”. Dodatkowo {message} w tym standardowym pl.yml musi być taki aby pasował do wszystkich {attribute} (jeżeli dobrze rozumiem), co w języku polskim jest niewygodne (“Email jest pustY”, “Hasło jest pustY”, muszą być inne {message}).

Wynik jest tutaj: http://programmers-blog.com/2009/11/16/rails-custom-validation-messages-i18n