accept_language 2.0.8 → 2.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 0afa534a141f111edb4f47449bccb2b44795abd11512319bee75909f672133ab
4
- data.tar.gz: e90f24a3a8104eb8fff13ee15ce7660705e31565769c95e62d4fb4577ff1c816
3
+ metadata.gz: c1d4f90fc40c062ac4f250c1c3b8ac8540796b232cf63b7d5c25b5e2e3c9124e
4
+ data.tar.gz: 150bb9def9e5f4799c432df6d6c364fedf88132b117d28e79a6ed44f467dcc70
5
5
  SHA512:
6
- metadata.gz: 6d098319c113b5ed61b7b57a57ce78cde8b799d9256240dc076de8626d5021ad4840f96b23b94bcba1d64dc7930e66514da17d4d55d8107c417a9c102a99925b
7
- data.tar.gz: c4695ea31539c0c94713b569e424fd2901a85a4ac67a448ef1c9cd200333fdaa1ab4bfe7429d1b7155fb2c4f4d2ffcf4218d2fee2060f9ffb6b629accc50a56e
6
+ metadata.gz: 537d924a23dc3c0fe8fb523556a6da653e16471b6b8ca94e0ee57f36fa1d6842103e380c3ea1e2dbd468b06a65a698ecdbf8923235f4eac0031a52801ba446f8
7
+ data.tar.gz: 8e48f57d2f0a4005483d29cf5503accc39eecffe19f78e8848dfa216422d1b2792f50cc5199c938674a483ef90ca20127fbfe452c0eebae9e30b36d7b8dc0bc2
data/LICENSE.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # The MIT License
2
2
 
3
- Copyright (c) 2019-2024 Cyril Kato
3
+ Copyright (c) 2019-2026 Cyril Kato
4
4
 
5
5
  Permission is hereby granted, free of charge, to any person obtaining a copy
6
6
  of this software and associated documentation files (the "Software"), to deal
data/README.md CHANGED
@@ -1,148 +1,93 @@
1
- # Accept Language 🌐
1
+ # AcceptLanguage
2
2
 
3
- Web applications often need to cater to users from around the world. One of the ways they can provide a better user experience is by presenting the content in the user's preferred language. This is where the `Accept-Language` HTTP header comes into play. Sent by the client (usually a web browser), this header tells the server the list of languages the user understands, and the user's preference order.
4
-
5
- Parsing the `Accept-Language` header can be complex due to its flexible format defined in [RFC 2616](https://tools.ietf.org/html/rfc2616#section-14.4). For instance, it can specify languages, countries, and scripts with varying degrees of preference (quality values).
6
-
7
- `Accept Language` is a lightweight, thread-safe Ruby library designed to parse the `Accept-Language` header, making it easier for your application to determine the best language to respond with. It calculates the intersection of the languages the user prefers and the languages your application supports, handling all the complexity of quality values and wildcards.
8
-
9
- Whether you're building a multilingual web application or just trying to make your service more accessible to users worldwide, `Accept Language` offers a reliable, simple solution.
10
-
11
- ## Status
3
+ A lightweight, thread-safe Ruby library for parsing `Accept-Language` HTTP headers as defined in [RFC 2616](https://tools.ietf.org/html/rfc2616#section-14.4), with full support for [BCP 47](https://tools.ietf.org/html/bcp47) language tags.
12
4
 
13
5
  [![Version](https://img.shields.io/github/v/tag/cyril/accept_language.rb?label=Version&logo=github)](https://github.com/cyril/accept_language.rb/tags)
14
6
  [![Yard documentation](https://img.shields.io/badge/Yard-documentation-blue.svg?logo=github)](https://rubydoc.info/github/cyril/accept_language.rb/main)
15
- [![Ruby](https://github.com/cyril/accept_language.rb/workflows/Ruby/badge.svg?branch=main)](https://github.com/cyril/accept_language.rb/actions?query=workflow%3Aruby+branch%3Amain)
16
- [![RuboCop](https://github.com/cyril/accept_language.rb/workflows/RuboCop/badge.svg?branch=main)](https://github.com/cyril/accept_language.rb/actions?query=workflow%3Arubocop+branch%3Amain)
7
+ ![Ruby](https://github.com/cyril/accept_language.rb/actions/workflows/ruby.yml/badge.svg?branch=main)
8
+ ![RuboCop](https://github.com/cyril/accept_language.rb/actions/workflows/rubocop.yml/badge.svg?branch=main)
17
9
  [![License](https://img.shields.io/github/license/cyril/accept_language.rb?label=License&logo=github)](https://github.com/cyril/accept_language.rb/raw/main/LICENSE.md)
18
10
 
19
- ## Why Choose Accept Language?
11
+ ## Features
20
12
 
21
- There are a myriad of tools out there, so why should you consider Accept Language for your next project? Here's why:
22
-
23
- - **Thread-Safe**: Multithreading can present unique challenges when dealing with shared resources. Our implementation is designed to be thread-safe, preventing potential concurrency issues.
24
- - **Compact and Robust**: Despite being small in size, Accept Language can handle even the trickiest cases with grace, ensuring you have a reliable tool at your disposal.
25
- - **Case-Insensitive Matching**: In line with the principle of robustness, our tool matches both strings and symbols regardless of case, providing greater flexibility.
26
- - **Independent of Framework**: While many tools require Rails, Rack, or i18n to function, Accept Language stands on its own. It works perfectly well without these dependencies, increasing its adaptability.
27
- - **BCP 47 Support**: BCP 47 defines a standard for language tags. This is crucial for specifying languages unambiguously. Accept Language complies with this standard, ensuring accurate language identification.
13
+ - Thread-safe
14
+ - No framework dependencies
15
+ - Case-insensitive matching
16
+ - BCP 47 language tag support
17
+ - Wildcard and exclusion handling
28
18
 
29
19
  ## Installation
30
20
 
31
- Add this line to your application's Gemfile:
32
-
33
21
  ```ruby
34
22
  gem "accept_language"
35
23
  ```
36
24
 
37
- And then execute:
38
-
39
- ```sh
40
- bundle install
41
- ```
42
-
43
- Or install it yourself as:
44
-
45
- ```sh
46
- gem install accept_language
47
- ```
48
-
49
25
  ## Usage
50
26
 
51
- The `Accept Language` library helps web applications serve content in the user's preferred language by parsing the `Accept-Language` HTTP header. This header indicates the user's language preferences and their priority order.
52
-
53
- ### Basic Syntax
54
-
55
- The library has two main methods:
56
-
57
- - `AcceptLanguage.parse(header)`: Parses the Accept-Language header
58
- - `match(*available_languages)`: Matches against the languages your application supports
59
-
60
27
  ```ruby
61
- AcceptLanguage.parse("fr-CH, fr;q=0.9").match(:fr, :"fr-CH") # => :"fr-CH"
28
+ AcceptLanguage.parse("en-GB, en;q=0.9").match(:en, :"en-GB")
29
+ # => :"en-GB"
62
30
  ```
63
31
 
64
- ### Understanding Language Preferences
32
+ ### Quality values
65
33
 
66
- #### Simple Language Matching
34
+ Quality values (q-values) indicate preference order from 0 to 1:
67
35
 
68
36
  ```ruby
69
- # Header: "da" (Danish is the preferred language)
70
- # Available: :en and :da
71
- AcceptLanguage.parse("da").match(:en, :da) # => :da
37
+ parser = AcceptLanguage.parse("da, en-GB;q=0.8, en;q=0.7")
72
38
 
73
- # No match available - returns nil
74
- AcceptLanguage.parse("da").match(:fr, :en) # => nil
39
+ parser.match(:en, :da) # => :da
40
+ parser.match(:en, :"en-GB") # => :"en-GB"
41
+ parser.match(:fr) # => nil
75
42
  ```
76
43
 
77
- #### Quality Values (q-values)
44
+ ### Language variants
78
45
 
79
- Q-values range from 0 to 1 and indicate preference order:
46
+ A generic language tag matches its regional variants, but not the reverse:
80
47
 
81
48
  ```ruby
82
- # Header: "da, en-GB;q=0.8, en;q=0.7"
83
- # Means:
84
- # - Danish (da): q=1.0 (highest priority)
85
- # - British English (en-GB): q=0.8 (second choice)
86
- # - Generic English (en): q=0.7 (third choice)
87
- AcceptLanguage.parse("da, en-GB;q=0.8, en;q=0.7").match(:en, :da) # => :da
88
- AcceptLanguage.parse("da, en-GB;q=0.8, en;q=0.7").match(:en, :"en-GB") # => :"en-GB"
49
+ AcceptLanguage.parse("fr").match(:"fr-CH") # => :"fr-CH"
50
+ AcceptLanguage.parse("fr-CH").match(:fr) # => nil
89
51
  ```
90
52
 
91
- #### Language Variants
53
+ ### Wildcards and exclusions
92
54
 
93
- The library handles specific language variants (regional or script variations):
55
+ The wildcard `*` matches any language. A q-value of 0 explicitly excludes a language:
94
56
 
95
57
  ```ruby
96
- # Specific variants must match exactly
97
- AcceptLanguage.parse("fr-CH").match(:fr) # => nil (Swiss French ≠ Generic French)
98
-
99
- # But generic variants can match specific ones
100
- AcceptLanguage.parse("fr").match(:"fr-CH") # => :"fr-CH"
101
-
102
- # Script variants are also supported
103
- AcceptLanguage.parse("uz-Latn-UZ").match("uz-Latn-UZ") # => "uz-Latn-UZ"
58
+ AcceptLanguage.parse("de-DE, *;q=0.5").match(:fr) # => :fr
59
+ AcceptLanguage.parse("*, en;q=0").match(:en) # => nil
60
+ AcceptLanguage.parse("*, en;q=0").match(:fr) # => :fr
104
61
  ```
105
62
 
106
- #### Wildcards and Exclusions
63
+ ### Case sensitivity
107
64
 
108
- The `*` wildcard matches any language, and `q=0` excludes languages:
65
+ Matching is case-insensitive but preserves the case of the available language tag:
109
66
 
110
67
  ```ruby
111
- # Accept any language but prefer German
112
- AcceptLanguage.parse("de-DE, *;q=0.5").match(:fr) # => :fr (matched by wildcard)
113
-
114
- # Accept any language EXCEPT English
115
- AcceptLanguage.parse("*, en;q=0").match(:en) # => nil (explicitly excluded)
116
- AcceptLanguage.parse("*, en;q=0").match(:fr) # => :fr (matched by wildcard)
68
+ AcceptLanguage.parse("en-GB").match("en-gb") # => "en-gb"
69
+ AcceptLanguage.parse("en-gb").match("en-GB") # => "en-GB"
117
70
  ```
118
71
 
119
- #### Complex Example
72
+ ### BCP 47 support
120
73
 
121
- ```ruby
122
- # Header: "de-LU, fr;q=0.9, en;q=0.7, *;q=0.5"
123
- # Means:
124
- # - Luxembourg German: q=1.0 (highest priority)
125
- # - French: q=0.9 (second choice)
126
- # - English: q=0.7 (third choice)
127
- # - Any other language: q=0.5 (lowest priority)
128
- header = "de-LU, fr;q=0.9, en;q=0.7, *;q=0.5"
129
- parser = AcceptLanguage.parse(header)
130
-
131
- parser.match(:de, :"de-LU") # => :"de-LU" (exact match)
132
- parser.match(:fr, :en) # => :fr (higher q-value)
133
- parser.match(:es, :it) # => :es (matched by wildcard)
134
- ```
135
-
136
- ### Case Sensitivity
74
+ This library supports [BCP 47](https://tools.ietf.org/html/bcp47) language tags, including:
137
75
 
138
- The matching is case-insensitive but preserves the case of the returned value:
76
+ - **Script subtags**: `zh-Hans` (Simplified Chinese), `zh-Hant` (Traditional Chinese)
77
+ - **Region subtags**: `en-US`, `pt-BR`
78
+ - **Variant subtags**: `sl-nedis` (Slovenian Nadiza dialect), `de-1996` (German orthography reform)
139
79
 
140
80
  ```ruby
141
- AcceptLanguage.parse("en-GB").match("en-gb") # => "en-gb"
142
- AcceptLanguage.parse("en-gb").match("en-GB") # => "en-GB"
81
+ # Script variants
82
+ AcceptLanguage.parse("zh-Hans").match(:"zh-Hans-CN", :"zh-Hant-TW")
83
+ # => :"zh-Hans-CN"
84
+
85
+ # Orthography variants (numeric subtags)
86
+ AcceptLanguage.parse("de-1996, de;q=0.9").match(:"de-CH-1996", :"de-CH")
87
+ # => :"de-CH-1996"
143
88
  ```
144
89
 
145
- ### Rails integration example
90
+ ## Rails integration
146
91
 
147
92
  ```ruby
148
93
  # app/controllers/application_controller.rb
@@ -171,15 +116,16 @@ class ApplicationController < ActionController::Base
171
116
  end
172
117
  ```
173
118
 
174
- ## Read more
119
+ ## Documentation
175
120
 
121
+ - [API Documentation](https://rubydoc.info/github/cyril/accept_language.rb/main)
176
122
  - [Language negotiation with Ruby](https://dev.to/cyri_/language-negotiation-with-ruby-5166)
177
123
  - [Rubyで言語ネゴシエーション](https://qiita.com/cyril/items/45dc233edb7be9d614e7)
178
124
 
179
125
  ## Versioning
180
126
 
181
- __AcceptLanguage__ uses [Semantic Versioning 2.0.0](https://semver.org/)
127
+ This library follows [Semantic Versioning 2.0.0](https://semver.org/).
182
128
 
183
129
  ## License
184
130
 
185
- The [gem](https://rubygems.org/gems/accept_language) is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
131
+ Available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
@@ -5,25 +5,26 @@ module AcceptLanguage
5
5
  # the optimal language choice. Handles quality values, wildcards, and language tag matching
6
6
  # according to RFC 2616 specifications.
7
7
  #
8
- # @example
9
- # Matcher.new("da" => 1.0, "en-GB" => 0.8, "en" => 0.7).call(:ug, :kk, :ru, :en) # => :en
10
- # Matcher.new("da" => 1.0, "en-GB" => 0.8, "en" => 0.7).call(:fr, :en, :"en-GB") # => :"en-GB"
8
+ # @api private
9
+ # @note This class is intended for internal use by {Parser} and should not be instantiated directly.
11
10
  class Matcher
11
+ # @api private
12
12
  WILDCARD = "*"
13
13
 
14
+ # @api private
14
15
  attr_reader :excluded_langtags, :preferred_langtags
15
16
 
16
- # Initialize a new Matcher object with the languages_range parameter representing the user's
17
- # preferred languages and their respective quality values.
18
- #
19
- # @param [Hash<String, BigDecimal>] languages_range A hash where keys represent languages and
20
- # values are the quality of preference for each language. A value of zero means the language is not acceptable.
17
+ # @api private
21
18
  def initialize(**languages_range)
22
19
  @excluded_langtags = ::Set[]
23
20
  langtags = []
24
21
 
25
22
  languages_range.select do |langtag, quality|
26
23
  if quality.zero?
24
+ # Exclude specific language tags, but NOT the wildcard.
25
+ # When "*;q=0" is specified, all non-listed languages become
26
+ # unacceptable implicitly (they won't match any preferred_langtags).
27
+ # Adding "*" to excluded_langtags would break prefix_match? logic.
27
28
  @excluded_langtags << langtag unless wildcard?(langtag)
28
29
  else
29
30
  level = (quality * 1_000).to_i
@@ -34,15 +35,7 @@ module AcceptLanguage
34
35
  @preferred_langtags = langtags.compact.reverse
35
36
  end
36
37
 
37
- # Finds the optimal language match by comparing user preferences against available languages.
38
- # Handles priorities based on:
39
- # 1. Explicit quality values (q-values)
40
- # 2. Language tag specificity (exact matches preferred over partial matches)
41
- # 3. Order of preference in the original Accept-Language header
42
- #
43
- # @param [Array<String, Symbol>] available_langtags Languages supported by your application
44
- # @return [String, Symbol, nil] Best matching language or nil if no acceptable match found
45
- # @raise [ArgumentError] If any language tag is nil
38
+ # @api private
46
39
  def call(*available_langtags)
47
40
  raise ::ArgumentError, "Language tags cannot be nil" if available_langtags.any?(&:nil?)
48
41
 
@@ -72,30 +65,43 @@ module AcceptLanguage
72
65
  end
73
66
 
74
67
  def find_matching_tag(preferred_tag, available_langtags)
75
- available_langtags.find { |tag| tag.match?(/\A#{preferred_tag}/i) }
68
+ available_langtags.find { |tag| prefix_match?(preferred_tag, String(tag.downcase)) }
76
69
  end
77
70
 
78
71
  def any_other_langtag(*available_langtags)
72
+ langtags = preferred_langtags - [WILDCARD]
73
+
79
74
  available_langtags.find do |available_langtag|
80
- langtags = preferred_langtags - [WILDCARD]
81
- langtags.none? { |tag| available_langtag.match?(/\A#{tag}/i) }
75
+ available_downcased = available_langtag.downcase
76
+ langtags.none? { |tag| prefix_match?(tag, String(available_downcased)) }
82
77
  end
83
78
  end
84
79
 
85
80
  def drop_unacceptable(*available_langtags)
86
- available_langtags.inject(::Set[]) do |langtags, available_langtag|
87
- next langtags if unacceptable?(available_langtag)
88
-
89
- langtags + ::Set[available_langtag]
81
+ available_langtags.each_with_object(::Set[]) do |available_langtag, langtags|
82
+ langtags << available_langtag unless unacceptable?(available_langtag)
90
83
  end
91
84
  end
92
85
 
93
86
  def unacceptable?(langtag)
94
- excluded_langtags.any? { |excluded_tag| langtag.match?(/\A#{excluded_tag}/i) }
87
+ langtag_downcased = langtag.downcase
88
+ excluded_langtags.any? { |excluded_tag| prefix_match?(excluded_tag, String(langtag_downcased)) }
95
89
  end
96
90
 
97
91
  def wildcard?(value)
98
92
  value.eql?(WILDCARD)
99
93
  end
94
+
95
+ # Implements RFC 2616 Section 14.4 prefix matching rule:
96
+ # "A language-range matches a language-tag if it exactly equals the tag,
97
+ # or if it exactly equals a prefix of the tag such that the first tag
98
+ # character following the prefix is '-'."
99
+ #
100
+ # @param prefix [String] The language-range to match (downcased)
101
+ # @param tag [String] The language-tag to test (downcased)
102
+ # @return [Boolean] true if prefix matches tag per RFC 2616 rules
103
+ def prefix_match?(prefix, tag)
104
+ tag == prefix || tag.start_with?("#{prefix}-")
105
+ end
100
106
  end
101
107
  end
@@ -8,27 +8,44 @@ module AcceptLanguage
8
8
  # and handles edge cases like malformed inputs and implicit quality values.
9
9
  #
10
10
  # @example
11
- # Parser.new("da, en-GB;q=0.8, en;q=0.7")
12
- # # => #<AcceptLanguage::Parser:0x00007 @languages_range={"da"=>1.0, "en-GB"=>0.8, "en"=>0.7}>
11
+ # parser = Parser.new("da, en-GB;q=0.8, en;q=0.7")
12
+ # parser.match(:en, :da) # => :da
13
13
  #
14
- # @see https://tools.ietf.org/html/rfc2616#section-14.4 for more information on Accept-Language header fields.
14
+ # @see https://tools.ietf.org/html/rfc2616#section-14.4
15
15
  class Parser
16
+ # @api private
16
17
  DEFAULT_QUALITY = "1"
18
+ # @api private
17
19
  SEPARATOR = ","
20
+ # @api private
18
21
  SPACE = " "
22
+ # @api private
19
23
  SUFFIX = ";q="
24
+ # @api private
25
+ # RFC 2616 Section 3.9 qvalue syntax:
26
+ # qvalue = ( "0" [ "." 0*3DIGIT ] ) | ( "1" [ "." 0*3("0") ] )
27
+ QVALUE_PATTERN = /\A(?:0(?:\.[0-9]{1,3})?|1(?:\.0{1,3})?)\z/
28
+ # @api private
29
+ # Language tag pattern supporting BCP 47 (RFC 5646) alphanumeric subtags.
30
+ #
31
+ # RFC 2616 Section 3.10 references RFC 1766, which only allowed ALPHA in subtags.
32
+ # However, BCP 47 (the current standard) permits alphanumeric subtags:
33
+ # subtag = 1*8alphanum
34
+ # alphanum = ALPHA / DIGIT
35
+ #
36
+ # Examples of valid BCP 47 tags with numeric subtags:
37
+ # - "de-CH-1996" (German, Switzerland, orthography variant 1996)
38
+ # - "sl-IT-nedis" (Slovenian, Italy, Nadiza dialect)
39
+ # - "zh-Hans-CN" (Chinese, Simplified script, China)
40
+ LANGTAG_PATTERN = /\A(?:\*|[a-zA-Z]{1,8}(?:-[a-zA-Z0-9]{1,8})*)\z/
20
41
 
21
- # Validates q-values according to RFC 2616:
22
- # - Must be between 0 and 1
23
- # - Can have up to 3 decimal places
24
- # - Allows both forms: .8 and 0.8
25
- QVALUE_PATTERN = /\A(?:0(?:\.[0-9]{1,3})?|1(?:\.0{1,3})?|\.[0-9]{1,3})\z/
26
-
42
+ # @api private
43
+ # @return [Hash<String, BigDecimal>] Parsed language tags and their quality values
27
44
  attr_reader :languages_range
28
45
 
29
46
  # Initializes a new Parser instance by importing and processing the given Accept-Language header field.
30
47
  #
31
- # @param [String] field The Accept-Language header field to parse.
48
+ # @param field [String] The Accept-Language header field to parse.
32
49
  def initialize(field)
33
50
  @languages_range = import(field)
34
51
  end
@@ -36,8 +53,9 @@ module AcceptLanguage
36
53
  # Finds the best matching language from available options based on user preferences.
37
54
  # Considers quality values and language tag specificity (e.g., "en-US" vs "en").
38
55
  #
39
- # @param [Array<String, Symbol>] available_langtags Languages supported by your application
56
+ # @param available_langtags [Array<String, Symbol>] Languages supported by your application
40
57
  # @return [String, Symbol, nil] Best matching language tag or nil if no match found
58
+ #
41
59
  # @example Match against specific language options
42
60
  # parser.match("en", "fr", "de") # => "en" if English is preferred
43
61
  # @example Match with region-specific tags
@@ -48,15 +66,8 @@ module AcceptLanguage
48
66
 
49
67
  private
50
68
 
51
- # Processes the Accept-Language header field to extract language tags and their respective quality values.
52
- #
53
- # @example
54
- # import('da, en-GB;q=0.8, en;q=0.7')
55
- # # => {"da"=>1.0, "en-GB"=>0.8, "en"=>0.7}
56
- #
57
- # @return [Hash<String, BigDecimal>] A hash where keys represent language tags and values are their respective quality values.
58
69
  def import(field)
59
- "#{field}".delete(SPACE).split(SEPARATOR).inject({}) do |hash, lang|
70
+ "#{field}".downcase.delete(SPACE).split(SEPARATOR).inject({}) do |hash, lang|
60
71
  tag, quality = lang.split(SUFFIX)
61
72
  next hash unless valid_tag?(tag)
62
73
 
@@ -72,7 +83,9 @@ module AcceptLanguage
72
83
  end
73
84
 
74
85
  def valid_tag?(tag)
75
- !tag.nil? && !tag.empty?
86
+ return false if tag.nil?
87
+
88
+ tag.match?(LANGTAG_PATTERN)
76
89
  end
77
90
  end
78
91
  end
@@ -1,44 +1,30 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- # AcceptLanguage is a lightweight library that parses Accept-Language HTTP headers (RFC 2616) to determine
4
- # user language preferences. It converts raw header values into a structured format for matching against
5
- # your application's supported languages.
3
+ # AcceptLanguage is a lightweight library for parsing Accept-Language HTTP headers
4
+ # as defined in RFC 2616. It determines user language preferences and matches them
5
+ # against your application's supported languages.
6
6
  #
7
- # @example
8
- # AcceptLanguage.parse("da, en-GB;q=0.8, en;q=0.7")
9
- # # => #<AcceptLanguage::Parser:0x00007 @languages_range={"da"=>1.0, "en-GB"=>0.8, "en"=>0.7}>
7
+ # @example Basic usage
8
+ # AcceptLanguage.parse("da, en-GB;q=0.8, en;q=0.7").match(:en, :da)
9
+ # # => :da
10
10
  #
11
- # @example Integration with Rails
12
- # class ApplicationController < ActionController::Base
13
- # before_action :set_locale
14
- #
15
- # private
16
- #
17
- # def set_locale
18
- # header = request.env["HTTP_ACCEPT_LANGUAGE"]
19
- # locale = AcceptLanguage.parse(header).match(*I18n.available_locales)
20
- # I18n.locale = locale || I18n.default_locale
21
- # end
22
- # end
11
+ # @example With regional variants
12
+ # AcceptLanguage.parse("fr-CH, fr;q=0.9").match(:fr, :"fr-CH")
13
+ # # => :"fr-CH"
23
14
  #
24
15
  # @see https://tools.ietf.org/html/rfc2616#section-14.4
25
16
  module AcceptLanguage
26
- # Parses an Accept-Language header field value into a Parser object, which can then be used to match
27
- # user's preferred languages against the languages your application supports.
28
- # This method accepts a string argument in the format as described in RFC 2616 Section 14.4, and returns
29
- # a Parser object which responds to the #match method.
17
+ # Parses an Accept-Language header field value.
30
18
  #
31
- # @param field [String] the Accept-Language header field value.
19
+ # @param field [String] The Accept-Language header field value
20
+ # @return [Parser] A parser object that responds to {Parser#match}
32
21
  #
33
22
  # @example
34
- # AcceptLanguage.parse("da, en-GB;q=0.8, en;q=0.7")
35
- # # => #<AcceptLanguage::Parser:0x00007 @languages_range={"da"=>1.0, "en-GB"=>0.8, "en"=>0.7}>
36
- #
37
- # @return [Parser] a Parser object that responds to #match method.
23
+ # parser = AcceptLanguage.parse("en-GB, en;q=0.9")
24
+ # parser.match(:en, :"en-GB") # => :"en-GB"
38
25
  def self.parse(field)
39
26
  Parser.new(field)
40
27
  end
41
28
  end
42
29
 
43
- # Load the Parser class
44
30
  require_relative File.join("accept_language", "parser")
metadata CHANGED
@@ -1,15 +1,29 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: accept_language
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.0.8
4
+ version: 2.1.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Cyril Kato
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2024-12-15 00:00:00.000000000 Z
12
- dependencies: []
11
+ date: 2026-01-19 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: bigdecimal
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ">="
18
+ - !ruby/object:Gem::Version
19
+ version: '0'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ">="
25
+ - !ruby/object:Gem::Version
26
+ version: '0'
13
27
  description: Parses the Accept-Language header from an HTTP request and produces a
14
28
  hash of languages and qualities.
15
29
  email: contact@cyril.email
@@ -35,14 +49,14 @@ required_ruby_version: !ruby/object:Gem::Requirement
35
49
  requirements:
36
50
  - - ">="
37
51
  - !ruby/object:Gem::Version
38
- version: 3.2.2
52
+ version: 3.2.0
39
53
  required_rubygems_version: !ruby/object:Gem::Requirement
40
54
  requirements:
41
55
  - - ">="
42
56
  - !ruby/object:Gem::Version
43
57
  version: '0'
44
58
  requirements: []
45
- rubygems_version: 3.4.10
59
+ rubygems_version: 3.4.19
46
60
  signing_key:
47
61
  specification_version: 4
48
62
  summary: "Parser for Accept-Language request HTTP header \U0001F310"