2009-11-03 42 views
9

我有處理貨幣輸入的an application。但是,如果您在美國,則可以輸入12,345.67;在法國,它可能是12.345,67在Ruby on Rails中處理國際貨幣輸入

在Rails中是否有一種簡單的方法來使貨幣條目適應區域設置?

請注意,我不是在尋找顯示的貨幣(ala number_to_currency),我正在尋找處理某人輸入貨幣字符串,並將其轉換爲小數。

+0

您是否經常輸入,在鍵入數字時分開分數?我個人從來沒有。 – 2009-11-03 18:47:03

+1

我沒有,但我不能保證沒有人會。我寧願在用戶的幸福方面犯錯,而不是沮喪。此外,如果您從網上銀行對帳單複製價值數據,則可能已經使用$和,和。格式化了它們。和所有其他人物的方式。 – 2009-11-03 19:00:26

回答

11

你可以這樣給一個鏡頭:

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 

而且測試用例:

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

添,

你可以嘗試使用'aggregation' feature,與委託類相結合。我會做這樣的事情:

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 

這樣,你就可以直接分配:

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

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

的好處是,你不必修改控制器/視圖上的任何東西

2

在內置I18n中使用translations for numbers應該允許您以一種格式(1234.56)輸入價格,然後使用I18n將它們退出並使用number_to_currency將它們自動打印出正確的語言環境。

當然,您必須使用before_filter來設置I18n.locale,請查看I18n guide,第2.3節。

4

您需要清理輸入,以便用戶可以輸入任何他們想要的內容,並且您將獲得一致的內容存儲在數據庫中。假設你的模型被稱爲「DoughEntry」,你的屬性是「數量」,並且它被存儲爲一個整數。

這是一個將字符串輸入轉換爲分的方法(如果字符串以分隔符後的兩位數結尾,則假定爲分)。你不妨讓這個聰明的,但這裏的概念:

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 

然後覆蓋默認的「量」訪問,將其通過方法:

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 

在現在,你存儲美分數據庫。雷達有正確的想法將其拉回。

+0

小心,那些gsub會放下減號,所以如果你有負數,你會以絕對值結束。 – 2010-07-22 22:04:05

+0

Thx Matt,固定。 – Ben 2010-07-23 17:11:02

5

我們寫這:

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 

當然,你將不得不設置I18n.locale在使用之前。而且它目前僅將字符串轉換爲設置的語言環境的浮點數。(在我們的情況下,如果用戶在法國網站上,我們預計貨幣金額文本只有符號和格式與法語區域相關)。