2009-11-03 15 views
9

Mam an application, który obsługuje wejścia walutowe. Jeśli jednak jesteś w Stanach Zjednoczonych, możesz wpisać numer jako 12,345.67; we Francji może to być 12.345,67.Obsługa międzynarodowego wkładu walutowego w Ruby on Rails

Czy istnieje prosty sposób w Railsach, aby dostosować pozycję waluty do lokalizacji?

Należy zauważyć, że nie szukam sposobu wyświetlania waluty (ala number_to_currency), staram się zająć się kimś, kto wpisuje ciąg waluty i konwertuje go na dziesiętne.

+0

Czy często wpisujesz, aby oddzielić wyniki, gdy wpiszesz cyfrę? Osobiście nigdy. –

+1

Nie, ale nie mogę zagwarantować, że nikt nie będzie. Wolę raczej błądzić po stronie przyjemności dla użytkownika niż frustracji. Dodatkowo, jeśli kopiujesz wartości z, powiedzmy, wyciągu bankowego online, może on już być sformatowany za pomocą $ i, i. i wszystkie inne postacie. –

Odpowiedz

11

Można dać ten strzał:

def string_to_float(string) 

     string.gsub!(/[^\d.,]/,'')   # Replace all Currency Symbols, Letters and -- from the string 

     if string =~ /^.*[\.,]\d{1}$/  # If string ends in a single digit (e.g. ,2) 
     string = string + "0"    # make it ,20 in order for the result to be in "cents" 
     end 

     unless string =~ /^.*[\.,]\d{2}$/ # If does not end in ,00/.00 then 
     string = string + "00"   # add trailing 00 to turn it into cents 
     end 

     string.gsub!(/[\.,]/,'')   # Replace all (.) and (,) so the string result becomes in "cents" 
     string.to_f/100     # Let to_float do the rest 
    end 

a przypadki testowe:

describe Currency do 
    it "should mix and match" do 
    Currency.string_to_float("$ 1,000.50").should eql(1000.50) 
    Currency.string_to_float("€ 1.000,50").should eql(1000.50) 
    Currency.string_to_float("€ 1.000,--").should eql(1000.to_f) 
    Currency.string_to_float("$ 1,000.--").should eql(1000.to_f)  
    end  

    it "should strip the € sign" do 
    Currency.string_to_float("€1").should eql(1.to_f) 
    end 

    it "should strip the $ sign" do 
    Currency.string_to_float("$1").should eql(1.to_f) 
    end 

    it "should strip letter characters" do 
    Currency.string_to_float("a123bc2").should eql(1232.to_f) 
    end 

    it "should strip - and --" do 
    Currency.string_to_float("100,-").should eql(100.to_f) 
    Currency.string_to_float("100,--").should eql(100.to_f) 
    end 

    it "should convert the , as delimitor to a ." do 
    Currency.string_to_float("100,10").should eql(100.10) 
    end 

    it "should convert ignore , and . as separators" do 
    Currency.string_to_float("1.000,10").should eql(1000.10) 
    Currency.string_to_float("1,000.10").should eql(1000.10) 
    end 

    it "should be generous if you make a type in the last '0' digit" do 
    Currency.string_to_float("123,2").should eql(123.2) 
    end 
2

Tim,

Można spróbować użyć 'aggregation' feature, w połączeniu z klasą delegacji. Chciałbym zrobić coś takiego:

class Product 
    composed_of :balance, 
     :class_name => "Money", 
     :mapping => %w(amount) 
end 

class Money < SimpleDelegator.new 
    include Comparable 
    attr_reader :amount 

    def initialize(amount) 
    @amount = Money.special_transform(amount) 
    super(@amount) 
    end 

    def self.special_transform(amount) 
    # your special convesion function here 
    end 

    def to_s 
    nummber_to_currency @amount 
    end 
end 

W ten sposób będzie można bezpośrednio przypisać:

Product.update_attributes(:price => '12.244,6') 

lub

Product.update_attributes(:price => '12,244.6') 

Zaletą jest to, że nie trzeba zmieniać cokolwiek na kontrolerach/widokach.

2

Używanie translations for numbers we wbudowanej w I18n powinny umożliwić wpisanie ceny w jednym formacie (1234.56), a następnie za pomocą i18n doprowadzając je z powrotem z number_to_currency mieć je automatycznie drukowane w prawidłowej lokalizacji.

Oczywiście musisz ustawić I18n.locale używając before_filter, sprawdź I18n guide, sekcja 2.3.

4

Musisz wyczyścić dane wejściowe, aby użytkownicy mogli wpisać dokładnie to, co chcą, a otrzymasz coś spójnego do przechowywania w bazie danych. Zakładając, że twój model nazywa się "DoughEntry", a twoim atrybutem jest "kwota", i jest przechowywany jako liczba całkowita.

Oto metoda, która konwertuje dane wejściowe ciągu na centy (jeśli ciąg kończy się dwiema cyframi po ograniczniku, przyjmuje się, że jest to cent). Może chcesz, aby ten mądrzejszy, ale tutaj jest pojęcie:

def convert_to_cents(input) 
    if input =~ /^.*[\.,]\d{2}$/ 
    input.gsub(/[^\d-]/,'').to_i 
    else 
    "#{input.gsub(/[^\d-]/,'')}00".to_i 
    end 
end 

>> convert_to_cents "12,345" 
=> 1234500 
>> convert_to_cents "12.345,67" 
=> 1234567 
>> convert_to_cents "$12.345,67" 
=> 1234567 

Następnie nadpisać domyślny „ilość” akcesor, przepuszczając ją przez tą metodą:

class DoughEntry << ActiveRecord::Base 

    def amount=(input) 
    write_attribute(:amount, convert_to_cents(input)) 
    end 

protected 
    def convert_to_cents(input) 
    if input =~ /^.*[\.,]\d{2}$/ 
     input.gsub(/[^\d-]/,'').to_i 
    else 
     "#{input.gsub(/[^\d-]/,'')}00".to_i 
    end 
    end 
end 

Teraz jesteś przechowywanie centów w Baza danych. Radar ma dobry pomysł na wycofanie go.

+0

Ostrożnie, te gsuby zrzucą symbol minus, więc jeśli masz ujemne kwoty, otrzymasz wartość bezwzględną. –

+0

Thx Matt, naprawiony. – Ben

5

Pisaliśmy to:

class String 
    def safe_parse 
    self.gsub(I18n.t("number.currency.format.unit"), '').gsub(I18n.t("number.currency.format.delimiter"), '').gsub(I18n.t("number.currency.format.separator"), '.').to_f 
    end 
end 

Oczywiście, trzeba będzie ustawić I18n.locale przed użyciem tego. I obecnie konwertuje tylko ciąg na zmienną dla ustawionych ustawień narodowych.(W naszym przypadku, jeśli użytkownik jest na stronie francuskiej, oczekujemy, że tekst kwoty waluty będzie zawierał tylko symbole i formatowanie dotyczące francuskiego ustawienia narodowego).