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 +4 -4
- data/LICENSE.md +1 -1
- data/README.md +48 -102
- data/lib/accept_language/matcher.rb +31 -25
- data/lib/accept_language/parser.rb +33 -20
- data/lib/accept_language.rb +14 -28
- metadata +19 -5
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: c1d4f90fc40c062ac4f250c1c3b8ac8540796b232cf63b7d5c25b5e2e3c9124e
|
|
4
|
+
data.tar.gz: 150bb9def9e5f4799c432df6d6c364fedf88132b117d28e79a6ed44f467dcc70
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 537d924a23dc3c0fe8fb523556a6da653e16471b6b8ca94e0ee57f36fa1d6842103e380c3ea1e2dbd468b06a65a698ecdbf8923235f4eac0031a52801ba446f8
|
|
7
|
+
data.tar.gz: 8e48f57d2f0a4005483d29cf5503accc39eecffe19f78e8848dfa216422d1b2792f50cc5199c938674a483ef90ca20127fbfe452c0eebae9e30b36d7b8dc0bc2
|
data/LICENSE.md
CHANGED
data/README.md
CHANGED
|
@@ -1,148 +1,93 @@
|
|
|
1
|
-
#
|
|
1
|
+
# AcceptLanguage
|
|
2
2
|
|
|
3
|
-
|
|
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
|
[](https://github.com/cyril/accept_language.rb/tags)
|
|
14
6
|
[](https://rubydoc.info/github/cyril/accept_language.rb/main)
|
|
15
|
-
|
|
16
|
-
|
|
7
|
+

|
|
8
|
+

|
|
17
9
|
[](https://github.com/cyril/accept_language.rb/raw/main/LICENSE.md)
|
|
18
10
|
|
|
19
|
-
##
|
|
11
|
+
## Features
|
|
20
12
|
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
-
|
|
24
|
-
-
|
|
25
|
-
-
|
|
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("
|
|
28
|
+
AcceptLanguage.parse("en-GB, en;q=0.9").match(:en, :"en-GB")
|
|
29
|
+
# => :"en-GB"
|
|
62
30
|
```
|
|
63
31
|
|
|
64
|
-
###
|
|
32
|
+
### Quality values
|
|
65
33
|
|
|
66
|
-
|
|
34
|
+
Quality values (q-values) indicate preference order from 0 to 1:
|
|
67
35
|
|
|
68
36
|
```ruby
|
|
69
|
-
|
|
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
|
-
|
|
74
|
-
|
|
39
|
+
parser.match(:en, :da) # => :da
|
|
40
|
+
parser.match(:en, :"en-GB") # => :"en-GB"
|
|
41
|
+
parser.match(:fr) # => nil
|
|
75
42
|
```
|
|
76
43
|
|
|
77
|
-
|
|
44
|
+
### Language variants
|
|
78
45
|
|
|
79
|
-
|
|
46
|
+
A generic language tag matches its regional variants, but not the reverse:
|
|
80
47
|
|
|
81
48
|
```ruby
|
|
82
|
-
#
|
|
83
|
-
#
|
|
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
|
-
|
|
53
|
+
### Wildcards and exclusions
|
|
92
54
|
|
|
93
|
-
The
|
|
55
|
+
The wildcard `*` matches any language. A q-value of 0 explicitly excludes a language:
|
|
94
56
|
|
|
95
57
|
```ruby
|
|
96
|
-
#
|
|
97
|
-
AcceptLanguage.parse("
|
|
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
|
-
|
|
63
|
+
### Case sensitivity
|
|
107
64
|
|
|
108
|
-
|
|
65
|
+
Matching is case-insensitive but preserves the case of the available language tag:
|
|
109
66
|
|
|
110
67
|
```ruby
|
|
111
|
-
#
|
|
112
|
-
AcceptLanguage.parse("
|
|
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
|
-
|
|
72
|
+
### BCP 47 support
|
|
120
73
|
|
|
121
|
-
|
|
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
|
-
|
|
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
|
-
|
|
142
|
-
AcceptLanguage.parse("
|
|
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
|
-
|
|
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
|
-
##
|
|
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
|
-
|
|
127
|
+
This library follows [Semantic Versioning 2.0.0](https://semver.org/).
|
|
182
128
|
|
|
183
129
|
## License
|
|
184
130
|
|
|
185
|
-
|
|
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
|
-
# @
|
|
9
|
-
#
|
|
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
|
-
#
|
|
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
|
-
#
|
|
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|
|
|
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
|
-
|
|
81
|
-
langtags.none? { |tag|
|
|
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.
|
|
87
|
-
|
|
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
|
-
|
|
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
|
-
# # =>
|
|
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
|
|
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
|
-
#
|
|
22
|
-
#
|
|
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]
|
|
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>]
|
|
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
|
-
|
|
86
|
+
return false if tag.nil?
|
|
87
|
+
|
|
88
|
+
tag.match?(LANGTAG_PATTERN)
|
|
76
89
|
end
|
|
77
90
|
end
|
|
78
91
|
end
|
data/lib/accept_language.rb
CHANGED
|
@@ -1,44 +1,30 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
-
# AcceptLanguage is a lightweight library
|
|
4
|
-
#
|
|
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
|
-
# # =>
|
|
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
|
|
12
|
-
#
|
|
13
|
-
#
|
|
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
|
|
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]
|
|
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("
|
|
35
|
-
#
|
|
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.
|
|
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:
|
|
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.
|
|
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.
|
|
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"
|