# frozen_string_literal: truerequire'i18n/backend/base'moduleI18nbeginrequire'oj'classJSONclass<<selfdefencode(value)Oj::Rails.encode(value)enddefdecode(value)Oj.load(value)endendendrescueLoadErrorrequire'active_support/json'JSON=ActiveSupport::JSONendmoduleBackend# This is a basic backend for key value stores. It receives on# initialization the store, which should respond to three methods:## * store#[](key) - Used to get a value# * store#[]=(key, value) - Used to set a value# * store#keys - Used to get all keys## Since these stores only supports string, all values are converted# to JSON before being stored, allowing it to also store booleans,# hashes and arrays. However, this store does not support Procs.## As the ActiveRecord backend, Symbols are just supported when loading# translations from the filesystem or through explicit store translations.## Also, avoid calling I18n.available_locales since it's a somehow# expensive operation in most stores.## == Example## To setup I18n to use TokyoCabinet in memory is quite straightforward:## require 'rufus/tokyo/cabinet' # gem install rufus-tokyo# I18n.backend = I18n::Backend::KeyValue.new(Rufus::Tokyo::Cabinet.new('*'))## == Performance## You may make this backend even faster by including the Memoize module.# However, notice that you should properly clear the cache if you change# values directly in the key-store.## == Subtrees## In most backends, you are allowed to retrieve part of a translation tree:## I18n.backend.store_translations :en, :foo => { :bar => :baz }# I18n.t "foo" #=> { :bar => :baz }## This backend supports this feature by default, but it slows down the storage# of new data considerably and makes hard to delete entries. That said, you are# allowed to disable the storage of subtrees on initialization:## I18n::Backend::KeyValue.new(@store, false)## This is useful if you are using a KeyValue backend chained to a Simple backend.classKeyValuemoduleImplementationattr_accessor:storeincludeBase,Flattendefinitialize(store,subtrees=true)@store,@subtrees=store,subtreesenddefinitialized?!@store.nil?enddefstore_translations(locale,data,options=EMPTY_HASH)escape=options.fetch(:escape,true)flatten_translations(locale,data,escape,@subtrees).eachdo|key,value|key="#{locale}.#{key}"casevaluewhenHashif@subtrees&&(old_value=@store[key])old_value=JSON.decode(old_value)value=Utils.deep_merge!(Utils.deep_symbolize_keys(old_value),value)ifold_value.is_a?(Hash)endwhenProcraise"Key-value stores cannot handle procs"end@store[key]=JSON.encode(value)unlessvalue.is_a?(Symbol)endenddefavailable_localeslocales=@store.keys.map{|k|k=~/\./;$`}locales.uniq!locales.compact!locales.map!{|k|k.to_sym}localesendprotected# Queries the translations from the key-value store and converts# them into a hash such as the one returned from loading the# haml filesdeftranslations@translations=Utils.deep_symbolize_keys(@store.keys.clone.mapdo|main_key|main_value=JSON.decode(@store[main_key])main_key.to_s.split(".").reverse.inject(main_value)do|value,key|{key.to_sym=>value}endend.inject{|hash,elem|Utils.deep_merge!(hash,elem)})enddefinit_translations# NO OP# This call made also inside Simple Backend and accessed by# other plugins like I18n-js and babilu and# to use it along with the Chain backend we need to# provide a uniform API even for protected methods :Senddefsubtrees?@subtreesenddeflookup(locale,key,scope=[],options=EMPTY_HASH)key=normalize_flat_keys(locale,key,scope,options[:separator])value=@store["#{locale}.#{key}"]value=JSON.decode(value)ifvalueifvalue.is_a?(Hash)Utils.deep_symbolize_keys(value)elsif!value.nil?valueelsif!@subtreesSubtreeProxy.new("#{locale}.#{key}",@store)endenddefpluralize(locale,entry,count)ifsubtrees?superelsereturnentryunlessentry.is_a?(Hash)key=pluralization_key(entry,count)entry[key]endendendclassSubtreeProxydefinitialize(master_key,store)@master_key=master_key@store=store@subtree=nilenddefhas_key?(key)@subtree&&@subtree.has_key?(key)||self[key]enddef[](key)unless@subtree&&value=@subtree[key]value=@store["#{@master_key}.#{key}"]ifvaluevalue=JSON.decode(value)(@subtree||={})[key]=valueendendvalueenddefis_a?(klass)Hash==klass||superendalias:kind_of?:is_a?definstance_of?(klass)Hash==klass||superenddefnil?@subtree.nil?enddefinspect@subtree.inspectendendincludeImplementationendendend