-
Notifications
You must be signed in to change notification settings - Fork 8
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
8 changed files
with
185 additions
and
5 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,9 +1,28 @@ | ||
require "./spec_helper" | ||
|
||
describe Citrine::I18n do | ||
# TODO: Write tests | ||
require "http" | ||
|
||
it "works" do | ||
false.should eq(true) | ||
I18n.load_path += ["./spec/fixtures/"] | ||
I18n.init | ||
|
||
describe Citrine::Pipe::I18n do | ||
|
||
it "should set language from header" do | ||
request = HTTP::Request.new("GET", "/") | ||
request.headers["Accept-Language"] = "fr,en-US;q=0.7,en;q=0.3" | ||
context = create_context(request) | ||
handler = Citrine::Pipe::I18n.new | ||
handler.call(context) | ||
I18n.locale.should eq "fr" | ||
end | ||
|
||
it "should set language from complicated header" do | ||
request = HTTP::Request.new("GET", "/") | ||
request.headers["Accept-Language"] = "fr;q=0.6,en-US;q=0.7" | ||
context = create_context(request) | ||
handler = Citrine::Pipe::I18n.new | ||
handler.call(context) | ||
I18n.locale.should eq "en" | ||
end | ||
|
||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,2 @@ | ||
--- | ||
test: "" |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,2 @@ | ||
--- | ||
test: "" |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,2 +1,9 @@ | ||
require "spec" | ||
require "../src/citrine-i18n" | ||
|
||
def create_context(request) | ||
io = IO::Memory.new | ||
response = HTTP::Server::Response.new(io) | ||
HTTP::Server::Context.new(request, response) | ||
end | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,4 +1,10 @@ | ||
require "./citrine-i18n/*" | ||
require "amber" | ||
require "i18n" | ||
require "./citrine-i18n/parser" | ||
require "./citrine-i18n/pipes/i18n" | ||
|
||
module Citrine::I18n | ||
def self.configure | ||
yield ::I18n.config | ||
end | ||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,116 @@ | ||
module Citrine::I18n | ||
|
||
# Taken from https://github.com/iain/http_accept_language/blob/master/lib/http_accept_language/parser.rb | ||
# Taken from https://github.com/TechMagister/kemalyst-i18n/src/kemalyst-i18n/parser.cr | ||
class Parser | ||
getter :header | ||
|
||
@header : String | ||
@user_preferred_languages : Array(String)? | ||
|
||
def initialize(header) | ||
@header = header | ||
end | ||
|
||
# Returns a sorted array based on user preference in HTTP_ACCEPT_LANGUAGE. | ||
# Browsers send this HTTP header, so don't think this is holy. | ||
# | ||
# Example: | ||
# | ||
# request.user_preferred_languages | ||
# # => [ 'nl-NL', 'nl-BE', 'nl', 'en-US', 'en' ] | ||
# | ||
def user_preferred_languages | ||
@user_preferred_languages ||= begin | ||
header.to_s.gsub(/\s+/, "").split(",").map do |language| | ||
splited = language.split(";q=") | ||
locale, quality = splited[0], splited[1]? | ||
raise ArgumentError.new "Not correctly formatted" unless locale =~ /^[a-z\-0-9]+|\*$/i | ||
|
||
locale = locale.downcase.gsub(/-[a-z0-9]+$/i, &.upcase) # Uppercase territory | ||
locale = nil if locale == "*" # Ignore wildcards | ||
|
||
quality = quality ? quality.to_f : 1.0 | ||
|
||
{locale, quality} | ||
end.sort do |(_, left), (_, right)| | ||
right <=> left | ||
end.map(&.first).compact | ||
rescue ArgumentError # Just rescue anything if the browser messed up badly. | ||
[] of String | ||
end | ||
end | ||
|
||
# Sets the user languages preference, overriding the browser | ||
# | ||
def user_preferred_languages=(languages) | ||
@user_preferred_languages = languages | ||
end | ||
|
||
# Finds the locale specifically requested by the browser. | ||
# | ||
# Example: | ||
# | ||
# request.preferred_language_from I18n.available_locales | ||
# # => 'nl' | ||
# | ||
def preferred_language_from(array) | ||
(user_preferred_languages & array.map(&:to_s)).first | ||
end | ||
|
||
# Returns the first of the user_preferred_languages that is compatible | ||
# with the available locales. Ignores region. | ||
# | ||
# Example: | ||
# | ||
# request.compatible_language_from I18n.available_locales | ||
# | ||
def compatible_language_from(available_languages) | ||
user_preferred_languages.map do |preferred| #en-US | ||
preferred = preferred.downcase | ||
preferred_language = preferred.split("-", 2).first | ||
|
||
available_languages.find do |available| # en | ||
available = available.to_s.downcase | ||
preferred == available || preferred_language == available.split("-", 2).first | ||
end | ||
end.compact.first? | ||
end | ||
|
||
# Returns a supplied list of available locals without any extra application info | ||
# that may be attached to the locale for storage in the application. | ||
# | ||
# Example: | ||
# [ja_JP-x1, en-US-x4, en_UK-x5, fr-FR-x3] => [ja-JP, en-US, en-UK, fr-FR] | ||
# | ||
def sanitize_available_locales(available_languages) | ||
available_languages.map do |available| | ||
available.to_s.split(/[_-]/).reject { |part| part.start_with?("x") }.join("-") | ||
end | ||
end | ||
|
||
# Returns the first of the user preferred languages that is | ||
# also found in available languages. Finds best fit by matching on | ||
# primary language first and secondarily on region. If no matching region is | ||
# found, return the first language in the group matching that primary language. | ||
# | ||
# Example: | ||
# | ||
# request.language_region_compatible(available_languages) | ||
# | ||
def language_region_compatible_from(available_languages) | ||
available_languages = sanitize_available_locales(available_languages) | ||
user_preferred_languages.map do |preferred| #en-US | ||
preferred = preferred.downcase | ||
preferred_language = preferred.split("-", 2).first | ||
|
||
lang_group = available_languages.select do |available| # en | ||
preferred_language == available.downcase.split("-", 2).first | ||
end | ||
|
||
lang_group.find { |lang| lang.downcase == preferred } || lang_group.first #en-US, en-UK | ||
end.compact.first | ||
end | ||
end | ||
end | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,19 @@ | ||
module Citrine | ||
module Pipe | ||
# Handler to initialize I18n for the request. | ||
class I18n < Amber::Pipe::Base | ||
HEADER = "Accept-Language" | ||
|
||
def call(context : HTTP::Server::Context) | ||
if languages = context.request.headers[HEADER]? | ||
parser = Citrine::I18n::Parser.new languages | ||
compat = parser.compatible_language_from ::I18n.available_locales | ||
::I18n.locale = compat if compat | ||
#Amber.logger.debug "Languages available: #{languages.to_s}" | ||
#Amber.logger.debug "Language chosen: #{::I18n.locale}" | ||
end | ||
call_next(context) | ||
end | ||
end | ||
end | ||
end |