mirror of
https://github.com/openfoodfoundation/openfoodnetwork
synced 2026-03-19 04:49:15 +00:00
Make pluralisation code an independent lib
I considered moving the code to a service but I think that this code can be completely independent of the Open Food Network use case. It would be easy to move to a gem. The downcasing may need reconsidering for general use.
This commit is contained in:
@@ -34,12 +34,12 @@ angular.module("admin.products").factory "OptionValueNamer", (VariantUnitManager
|
||||
return unit_name if count == undefined
|
||||
unit_key = @unit_key(unit_name)
|
||||
return unit_name unless unit_key
|
||||
I18n.t(["unit_names", unit_key], {count: count, defaultValue: unit_name})
|
||||
I18n.t(["inflections", unit_key], {count: count, defaultValue: unit_name})
|
||||
|
||||
unit_key: (unit_name) ->
|
||||
unless I18n.unit_keys
|
||||
I18n.unit_keys = {}
|
||||
for key, translations of I18n.t("unit_names")
|
||||
for key, translations of I18n.t("inflections")
|
||||
for quantifier, translation of translations
|
||||
I18n.unit_keys[translation.toLowerCase()] = key
|
||||
|
||||
|
||||
@@ -2721,7 +2721,7 @@ See the %{link} to find out more about %{sitename}'s features and to start using
|
||||
have_an_account: "Already have an account?"
|
||||
action_login: "Log in now."
|
||||
|
||||
# Most popular names used in variant_unit_name.
|
||||
# Singular and plural forms of commonly used words.
|
||||
# We use these entries to pluralize unit names in every language.
|
||||
#
|
||||
# Extracted with the following query:
|
||||
@@ -2731,7 +2731,7 @@ See the %{link} to find out more about %{sitename}'s features and to start using
|
||||
# puts " one: \"#{name}\""
|
||||
# puts " other: \"#{name}s\"";
|
||||
# }
|
||||
unit_names:
|
||||
inflections:
|
||||
each:
|
||||
one: "each"
|
||||
other: "each"
|
||||
|
||||
65
lib/open_food_network/i18n_inflections.rb
Normal file
65
lib/open_food_network/i18n_inflections.rb
Normal file
@@ -0,0 +1,65 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
module OpenFoodNetwork
|
||||
# Pluralize or singularize words.
|
||||
#
|
||||
# We store some inflection data in locales and use a reverse lookup of a word
|
||||
# to find the plural or singular of the same word.
|
||||
#
|
||||
# Here is one example with a French user:
|
||||
#
|
||||
# - We have a product with the variant unit name "bouquet".
|
||||
# - The I18n.locale is set to :fr.
|
||||
# - The French locale contains:
|
||||
# bunch:
|
||||
# one: "bouquet"
|
||||
# other: "bouquets"
|
||||
# - We create a table containing:
|
||||
# "bouquet" => "bunch"
|
||||
# "bouquets" => "bunch"
|
||||
# - Looking up "bouquet" gives us the I18n key "bunch".
|
||||
# - We find the right plural by calling I18n:
|
||||
#
|
||||
# I18n.t("inflections.bunch", count: 2, default: "bouquet")
|
||||
#
|
||||
# - This returns the correct plural "bouquets".
|
||||
# - It returns the original "bouquet" if the word is missing from the locale.
|
||||
module I18nInflections
|
||||
# Make this a singleton to cache lookup tables.
|
||||
extend self
|
||||
|
||||
def pluralize(word, count)
|
||||
return word if count.nil?
|
||||
|
||||
key = i18n_key(word)
|
||||
|
||||
return word unless key
|
||||
|
||||
I18n.t(key, scope: "inflections", count: count, default: word)
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def i18n_key(word)
|
||||
@lookup ||= {}
|
||||
|
||||
# The user may switch the locale. `I18n.t` is always using the current
|
||||
# locale and we need a lookup table for each of them.
|
||||
unless @lookup.key?(I18n.locale)
|
||||
@lookup[I18n.locale] = build_i18n_key_lookup
|
||||
end
|
||||
|
||||
@lookup[I18n.locale][word.downcase]
|
||||
end
|
||||
|
||||
def build_i18n_key_lookup
|
||||
lookup = {}
|
||||
I18n.t("inflections")&.each do |key, translations|
|
||||
translations.values.each do |translation|
|
||||
lookup[translation.downcase] = key
|
||||
end
|
||||
end
|
||||
lookup
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -1,3 +1,7 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
require "open_food_network/i18n_inflections"
|
||||
|
||||
module OpenFoodNetwork
|
||||
class OptionValueNamer
|
||||
def initialize(variant = nil)
|
||||
@@ -73,37 +77,7 @@ module OpenFoodNetwork
|
||||
end
|
||||
|
||||
def pluralize(unit_name, count)
|
||||
I18nUnitNames.instance.pluralize(unit_name, count)
|
||||
end
|
||||
|
||||
# Provides efficient access to unit name inflections.
|
||||
# The singleton property ensures that the init code is run once only.
|
||||
# The OptionValueNamer is instantiated in loops.
|
||||
class I18nUnitNames
|
||||
include Singleton
|
||||
|
||||
def pluralize(unit_name, count)
|
||||
return unit_name if count.nil?
|
||||
|
||||
@unit_keys ||= unit_key_lookup
|
||||
key = @unit_keys[unit_name.downcase]
|
||||
|
||||
return unit_name unless key
|
||||
|
||||
I18n.t(key, scope: "unit_names", count: count, default: unit_name)
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def unit_key_lookup
|
||||
lookup = {}
|
||||
I18n.t("unit_names").each do |key, translations|
|
||||
translations.values.each do |translation|
|
||||
lookup[translation.downcase] = key
|
||||
end
|
||||
end
|
||||
lookup
|
||||
end
|
||||
I18nInflections.pluralize(unit_name, count)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
40
spec/lib/open_food_network/i18n_inflections_spec.rb
Normal file
40
spec/lib/open_food_network/i18n_inflections_spec.rb
Normal file
@@ -0,0 +1,40 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
require 'spec_helper'
|
||||
require 'open_food_network/i18n_inflections'
|
||||
|
||||
describe OpenFoodNetwork::I18nInflections do
|
||||
let(:subject) { described_class }
|
||||
|
||||
it "returns the same word if no plural is known" do
|
||||
expect(subject.pluralize("foo", 2)).to eq "foo"
|
||||
end
|
||||
|
||||
it "finds the plural of a word" do
|
||||
expect(subject.pluralize("bunch", 2)).to eq "bunches"
|
||||
end
|
||||
|
||||
it "finds the singular of a word" do
|
||||
expect(subject.pluralize("bunch", 1)).to eq "bunch"
|
||||
end
|
||||
|
||||
it "ignores upper case" do
|
||||
expect(subject.pluralize("Bunch", 2)).to eq "bunches"
|
||||
end
|
||||
|
||||
it "switches locales" do
|
||||
skip "French plurals not available yet"
|
||||
I18n.with_locale(:fr) do
|
||||
expect(subject.pluralize("bouquet", 2)).to eq "bouquets"
|
||||
end
|
||||
end
|
||||
|
||||
it "builds the lookup table once" do
|
||||
# Cache the table:
|
||||
subject.pluralize("bunch", 2)
|
||||
|
||||
# Expect only one call for the plural:
|
||||
expect(I18n).to receive(:t).once.and_call_original
|
||||
subject.pluralize("bunch", 2)
|
||||
end
|
||||
end
|
||||
Reference in New Issue
Block a user