From 051197137c7f9c4568573cd3087743e8b471d487 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Juan=20Jos=C3=A9=20Mata?= Date: Thu, 22 Jan 2026 14:27:12 +0100 Subject: [PATCH] Fix Polish translation template error (and increase testing/fix other locales as well) (#734) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Add Polish locale support for money formatting The Money::Formatting module handles locale-specific currency formatting for French, German, Spanish, Italian, and Portuguese (Brazil), but was missing support for Polish locale. This caused formatting issues when users with Polish locale viewed account valuations. - Add Polish locale handling to locale_options method - Polish formatting uses: space as thousands delimiter, comma as decimal separator, symbol after number ("%n %u" format) - Add test coverage for Polish locale formatting * Add locale support for all supported locales in Money::Formatting Extend the Money::Formatting module to handle all locales from SUPPORTED_LOCALES to prevent template errors when users select different languages. Added locale-specific formatting for: - Turkish (tr): dot delimiter, comma separator, symbol after number - Norwegian Bokmål (nb): space delimiter, comma separator, symbol after - Catalan (ca): dot delimiter, comma separator, symbol after number - Romanian (ro): dot delimiter, comma separator, symbol after number - Dutch (nl): dot delimiter, comma separator, symbol before number Also improved Dutch handling to work with all currencies (not just EUR). Added comprehensive tests for: - All newly supported locales - Chinese (zh-CN, zh-TW) which use default English-style formatting - A test that verifies all SUPPORTED_LOCALES can format without errors * Fix broken money formatting tests - Fix Chinese Traditional locale test: TWD currency uses "TW$" symbol (prefixed with first 2 chars of ISO code to distinguish from USD) - Fix all supported locales test: replace assert_nothing_raised (which doesn't accept message argument in Minitest) with explicit assertions * Refactor Money::Formatting to consolidate locale patterns Group locales by their formatting patterns into constants to reduce repetition and make it easier to add new locales: - EUROPEAN_SYMBOL_AFTER: de, es, it, tr, ca, ro (dot delimiter, comma separator, symbol after) - SPACE_DELIMITER_SYMBOL_AFTER: pl, nb (space delimiter, comma separator, symbol after) - EUROPEAN_SYMBOL_BEFORE: nl, pt-BR (dot delimiter, comma separator, symbol before) French locale remains separate due to its unique non-breaking space usage. --------- Co-authored-by: Claude --- lib/money/formatting.rb | 31 +++++++++++---------- test/lib/money_test.rb | 61 +++++++++++++++++++++++++++++++++++++++++ 2 files changed, 78 insertions(+), 14 deletions(-) diff --git a/lib/money/formatting.rb b/lib/money/formatting.rb index 9c4df8f58..9d6152fe6 100644 --- a/lib/money/formatting.rb +++ b/lib/money/formatting.rb @@ -1,6 +1,14 @@ module Money::Formatting include ActiveSupport::NumberHelper + # Locale groups by formatting pattern + # European style: dot as thousands delimiter, comma as decimal separator, symbol after number + EUROPEAN_SYMBOL_AFTER = %i[de es it tr ca ro].freeze + # Scandinavian/Eastern European: space as thousands delimiter, comma as decimal separator, symbol after number + SPACE_DELIMITER_SYMBOL_AFTER = %i[pl nb].freeze + # European style: dot as thousands delimiter, comma as decimal separator, symbol before number + EUROPEAN_SYMBOL_BEFORE = %i[nl pt-BR].freeze + def format(options = {}) locale = options[:locale] || I18n.locale default_opts = format_options(locale) @@ -33,34 +41,29 @@ module Money::Formatting def locale_options(locale) locale_sym = (locale || I18n.locale || :en).to_sym - # French locale: symbol after number with non-breaking space, comma as decimal separator + # French locale: uses non-breaking spaces (unique formatting) if locale_sym == :fr return { delimiter: "\u00A0", separator: ",", format: "%n\u00A0%u" } end - # German locale: symbol after number with space, comma as decimal separator - if locale_sym == :de + # European style: dot delimiter, comma separator, symbol after number + if EUROPEAN_SYMBOL_AFTER.include?(locale_sym) return { delimiter: ".", separator: ",", format: "%n %u" } end - # Spanish locale: symbol after number with space, comma as decimal separator - if locale_sym == :es - return { delimiter: ".", separator: ",", format: "%n %u" } + # Space delimiter, comma separator, symbol after number + if SPACE_DELIMITER_SYMBOL_AFTER.include?(locale_sym) + return { delimiter: " ", separator: ",", format: "%n %u" } end - # Italian locale: symbol after number with space, comma as decimal separator - if locale_sym == :it - return { delimiter: ".", separator: ",", format: "%n %u" } - end - - # Portuguese (Brazil) locale: symbol before, comma as decimal separator - if locale_sym == :"pt-BR" + # European style: dot delimiter, comma separator, symbol before number + if EUROPEAN_SYMBOL_BEFORE.include?(locale_sym) return { delimiter: ".", separator: ",", format: "%u %n" } end # Currency-specific overrides for remaining locales case [ currency.iso_code, locale_sym ] - when [ "EUR", :nl ], [ "EUR", :pt ] + when [ "EUR", :pt ] { delimiter: ".", separator: ",", format: "%u %n" } when [ "EUR", :en ], [ "EUR", :en_IE ] { delimiter: ",", separator: "." } diff --git a/test/lib/money_test.rb b/test/lib/money_test.rb index 7acf814c9..1a43c8e8e 100644 --- a/test/lib/money_test.rb +++ b/test/lib/money_test.rb @@ -113,6 +113,67 @@ class MoneyTest < ActiveSupport::TestCase assert_equal "R$ 1.000,12", Money.new(1000.12, :brl).format(locale: :"pt-BR") end + test "formats correctly for Polish locale" do + # Polish uses space as thousands delimiter, comma as decimal separator, symbol after number + assert_equal "1 000,12 zł", Money.new(1000.12, :pln).format(locale: :pl) + assert_equal "1 000,12 €", Money.new(1000.12, :eur).format(locale: :pl) + end + + test "formats correctly for Turkish locale" do + # Turkish uses dot as thousands delimiter, comma as decimal separator, symbol after number + assert_equal "1.000,12 ₺", Money.new(1000.12, :try).format(locale: :tr) + assert_equal "1.000,12 €", Money.new(1000.12, :eur).format(locale: :tr) + end + + test "formats correctly for Norwegian Bokmål locale" do + # Norwegian uses space as thousands delimiter, comma as decimal separator, symbol after number + assert_equal "1 000,12 kr", Money.new(1000.12, :nok).format(locale: :nb) + assert_equal "1 000,12 €", Money.new(1000.12, :eur).format(locale: :nb) + end + + test "formats correctly for Catalan locale" do + # Catalan uses dot as thousands delimiter, comma as decimal separator, symbol after number + assert_equal "1.000,12 €", Money.new(1000.12, :eur).format(locale: :ca) + end + + test "formats correctly for Romanian locale" do + # Romanian uses dot as thousands delimiter, comma as decimal separator, symbol after number + assert_equal "1.000,12 Lei", Money.new(1000.12, :ron).format(locale: :ro) + assert_equal "1.000,12 €", Money.new(1000.12, :eur).format(locale: :ro) + end + + test "formats correctly for Dutch locale" do + # Dutch uses dot as thousands delimiter, comma as decimal separator, symbol before number + assert_equal "€ 1.000,12", Money.new(1000.12, :eur).format(locale: :nl) + assert_equal "$ 1.000,12", Money.new(1000.12, :usd).format(locale: :nl) + end + + test "formats correctly for Chinese Simplified locale" do + # Chinese Simplified uses English-style formatting (comma as thousands delimiter, dot as decimal separator) + assert_equal "¥1,000.12", Money.new(1000.12, :cny).format(locale: :"zh-CN") + end + + test "formats correctly for Chinese Traditional locale" do + # Chinese Traditional uses English-style formatting (comma as thousands delimiter, dot as decimal separator) + # TWD symbol is prefixed with "TW" to distinguish from other dollar currencies + assert_equal "TW$1,000.12", Money.new(1000.12, :twd).format(locale: :"zh-TW") + end + + test "all supported locales can format money without errors" do + # Ensure all supported locales from LanguagesHelper::SUPPORTED_LOCALES work + supported_locales = %w[en fr de es tr nb ca ro pt-BR zh-CN zh-TW nl] + + supported_locales.each do |locale| + locale_sym = locale.to_sym + # Format with USD and EUR to ensure locale handling works for different currencies + result_usd = Money.new(1000.12, :usd).format(locale: locale_sym) + result_eur = Money.new(1000.12, :eur).format(locale: locale_sym) + + assert result_usd.present?, "Locale #{locale} should format USD without errors" + assert result_eur.present?, "Locale #{locale} should format EUR without errors" + end + end + test "converts currency when rate available" do ExchangeRate.expects(:find_or_fetch_rate).returns(OpenStruct.new(rate: 1.2))