From effbef7d0b783f06bbbce9f93c6da37a0f0e06f5 Mon Sep 17 00:00:00 2001 From: Bianca Nenciu Date: Thu, 25 Oct 2018 13:54:01 +0300 Subject: [PATCH] UX: Use user locale for locations. (#6527) * UX: Use user locale for locations. * DEV: Added MaxMindDB test data and fixed test. --- .../concerns/user_auth_tokens_mixin.rb | 2 +- lib/discourse_ip_info.rb | 28 ++++++++++++------ spec/fixtures/mmdb/GeoLite2-City.mmdb | Bin 0 -> 20831 bytes .../user_auth_token_serializer_spec.rb | 23 ++++++++++++++ 4 files changed, 43 insertions(+), 10 deletions(-) create mode 100644 spec/fixtures/mmdb/GeoLite2-City.mmdb create mode 100644 spec/serializers/user_auth_token_serializer_spec.rb diff --git a/app/serializers/concerns/user_auth_tokens_mixin.rb b/app/serializers/concerns/user_auth_tokens_mixin.rb index 39e6e3232a..95315c4ed7 100644 --- a/app/serializers/concerns/user_auth_tokens_mixin.rb +++ b/app/serializers/concerns/user_auth_tokens_mixin.rb @@ -20,7 +20,7 @@ module UserAuthTokensMixin end def location - ipinfo = DiscourseIpInfo.get(client_ip) + ipinfo = DiscourseIpInfo.get(client_ip, I18n.locale) location = [ipinfo[:city], ipinfo[:region], ipinfo[:country]].reject { |x| x.blank? }.join(", ") return I18n.t('staff_action_logs.unknown') if location.blank? diff --git a/lib/discourse_ip_info.rb b/lib/discourse_ip_info.rb index 3623c77255..11703cff79 100644 --- a/lib/discourse_ip_info.rb +++ b/lib/discourse_ip_info.rb @@ -4,8 +4,12 @@ class DiscourseIpInfo include Singleton def initialize + open_db(File.join(Rails.root, 'vendor', 'data')) + end + + def open_db(path) begin - @mmdb_filename = File.join(Rails.root, 'vendor', 'data', 'GeoLite2-City.mmdb') + @mmdb_filename = File.join(path, 'GeoLite2-City.mmdb') @mmdb = MaxMindDB.new(@mmdb_filename, MaxMindDB::LOW_MEMORY_FILE_READER) @cache = LruRedux::ThreadSafeCache.new(1000) rescue Errno::ENOENT => e @@ -15,7 +19,7 @@ class DiscourseIpInfo end end - def lookup(ip) + def lookup(ip, locale = :en) return {} unless @mmdb begin @@ -26,22 +30,28 @@ class DiscourseIpInfo return {} if !result || !result.found? + locale = locale.to_s.sub('_', '-') + { - country: result.country.name, + country: result.country.name(locale) || result.country.name, country_code: result.country.iso_code, - region: result.subdivisions.most_specific.name, - city: result.city.name, + region: result.subdivisions.most_specific.name(locale) || result.subdivisions.most_specific.name, + city: result.city.name(locale) || result.city.name, } end - def get(ip) + def get(ip, locale = :en) return {} unless @mmdb ip = ip.to_s - @cache[ip] ||= lookup(ip) + @cache["#{ip}-#{locale}"] ||= lookup(ip, locale) end - def self.get(ip) - instance.get(ip) + def self.open_db(path) + instance.open_db(path) + end + + def self.get(ip, locale = :en) + instance.get(ip, locale) end end diff --git a/spec/fixtures/mmdb/GeoLite2-City.mmdb b/spec/fixtures/mmdb/GeoLite2-City.mmdb new file mode 100644 index 0000000000000000000000000000000000000000..2e7c95c7ce942405f1af8a2470fce650950030af GIT binary patch literal 20831 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