{"record":{"id":"e3cba1b38b48c0d2","repo":"activerecord-hackery/ransack","slug":"a-context-is-required-to-translate-attributes","errorCode":null,"errorMessage":"A context is required to translate attributes","messagePattern":"A context is required to translate attributes","errorType":"exception","errorClass":"ArgumentError","httpStatus":null,"severity":"error","filePath":"lib/ransack/translate.rb","lineNumber":20,"sourceCode":"\nI18n.load_path += Dir[\n  File.join(File.dirname(__FILE__), 'locale'.freeze, '*.yml'.freeze)\n]\n\nmodule Ransack\n  module Translate\n    class << self\n      def word(key, options = {})\n        I18n.translate(:\"ransack.#{key}\", default: key.to_s)\n      end\n\n      def predicate(key, options = {})\n        I18n.translate(:\"ransack.predicates.#{key}\", default: key.to_s)\n      end\n\n      def attribute(key, options = {})\n        unless context = options.delete(:context)\n          raise ArgumentError, \"A context is required to translate attributes\"\n        end\n\n        original_name = key.to_s\n        base_class = context.klass\n        base_ancestors = base_class.ancestors.select {\n          |x| x.respond_to?(:model_name)\n        }\n        attributes_str = original_name.dup # will be modified by ⬇\n        predicate = Predicate.detect_and_strip_from_string!(attributes_str)\n        attribute_names = attributes_str.split(/_and_|_or_/)\n        combinator = attributes_str =~ /_and_/ ? :and : :or\n        defaults = base_ancestors.map do |klass|\n          \"ransack.attributes.#{i18n_key(klass)}.#{original_name}\".to_sym\n        end\n        defaults << options.delete(:default) if options[:default]\n\n        translated_names = attribute_names.map do |name|\n          attribute_name(context, name, options[:include_associations])","sourceCodeStart":2,"sourceCodeEnd":38,"githubUrl":"https://github.com/activerecord-hackery/ransack/blob/e82f6bab3956c5597e7debbefa218d9e94e58ceb/lib/ransack/translate.rb#L2-L38","documentation":"Ransack::Translate.attribute resolves human-readable names for an attribute (or attribute+predicate) key via I18n, using the search context's model class and its ancestors to build translation lookup keys ('ransack.attributes.<model>.<key>', activerecord.attributes..., etc.). It requires the calling search's context to be passed in options[:context]; without it there is no model to translate against and this ArgumentError is raised.","triggerScenarios":"Calling Ransack::Translate.attribute('name_cont') directly with no options; calling search.base.conditions.first.attributes.first.translate(key, options) with options that omit :context (the Grouping#translate path merges context: context, but a custom call may not); helper code that copies the translate API without forwarding the context.","commonSituations":"Writing custom label helpers or decorators that use Translate.attribute for consistent I18n; refactoring view code and losing the context: keyword; testing translation logic in isolation without constructing a search first.","solutions":["Pass the search context: Ransack::Translate.attribute('name_cont', context: @q.context)","Prefer the higher-level API: @q.translate('name_cont') or the form builder's label, which forward the context automatically","In specs, build a real search first: search = Person.ransack(name_cont: 'x'); use search.context"],"exampleFix":"# before\nRansack::Translate.attribute('name_cont')\n# => ArgumentError: A context is required to translate attributes\n\n# after\nsearch = Person.ransack(name_cont: 'x')\nRansack::Translate.attribute('name_cont', context: search.context)","handlingStrategy":"validation","validationCode":"search = Person.ransack(name_cont: 'x')\nopts = { context: search.context }.merge(user_opts)\nRansack::Translate.attribute('name_cont', opts)","typeGuard":"def translatable_attribute_call?(key, options)\n  options.key?(:context) && options[:context].respond_to?(:klass)\nend","tryCatchPattern":null,"preventionTips":["Always pass context: search.context when calling Translate.attribute directly","Prefer @q.translate / form-builder labels, which wire the context for you","In extracted helpers, thread the original options hash through unmodified so :context survives"],"tags":["ruby","rails","ransack","i18n","translate","argumenterror"],"backgroundTag":"missing-required-option","analyzedSha":"e82f6bab3956c5597e7debbefa218d9e94e58ceb","analyzedAt":"2026-08-21T19:30:23.639Z","schemaVersion":2},"datasetVersion":"2026-08-21T23:17:16.201Z"}