feat: add Prism and I18n
This commit is contained in:
1 parent
bb80e7d3c4
commit
069b348302
1 file changed
+124
@@ -0,0 +1,124 @@
|
|||||||
|
require_relative 'i18n/backend'
|
||||||
|
require_relative 'i18n/translator'
|
||||||
|
|
||||||
|
module Prism
|
||||||
|
# Rails-i18n-like translation API: JSON translation files (this Ruby
|
||||||
|
# build has no psych/libyaml, so YAML is not an option), a per-thread
|
||||||
|
# current locale, fallback chains, and Accept-Language negotiation.
|
||||||
|
module I18n
|
||||||
|
class << self
|
||||||
|
def backend
|
||||||
|
@backend ||= Backend.new
|
||||||
|
end
|
||||||
|
|
||||||
|
def default_locale
|
||||||
|
@default_locale ||= :en
|
||||||
|
end
|
||||||
|
|
||||||
|
def default_locale=(locale)
|
||||||
|
@translator = nil
|
||||||
|
@default_locale = locale.to_sym
|
||||||
|
end
|
||||||
|
|
||||||
|
def fallbacks
|
||||||
|
@fallbacks ||= {}
|
||||||
|
end
|
||||||
|
|
||||||
|
def fallbacks=(hash)
|
||||||
|
@translator = nil
|
||||||
|
@fallbacks = hash
|
||||||
|
end
|
||||||
|
|
||||||
|
def locale
|
||||||
|
Thread.current[:prism_i18n_locale] || default_locale
|
||||||
|
end
|
||||||
|
|
||||||
|
def locale=(new_locale)
|
||||||
|
Thread.current[:prism_i18n_locale] = new_locale&.to_sym
|
||||||
|
end
|
||||||
|
|
||||||
|
def with_locale(new_locale)
|
||||||
|
previous = locale
|
||||||
|
self.locale = new_locale
|
||||||
|
yield
|
||||||
|
ensure
|
||||||
|
self.locale = previous
|
||||||
|
end
|
||||||
|
|
||||||
|
def load_path
|
||||||
|
@load_path ||= []
|
||||||
|
end
|
||||||
|
|
||||||
|
def load_translations!
|
||||||
|
load_path.each do |path|
|
||||||
|
Dir.glob(File.join(path, '**', '*.json')).sort.each { |file| backend.load_file(file) }
|
||||||
|
end
|
||||||
|
@translator = nil
|
||||||
|
end
|
||||||
|
|
||||||
|
def store_translations(locale, data)
|
||||||
|
@translator = nil
|
||||||
|
backend.store_translations(locale, data)
|
||||||
|
end
|
||||||
|
|
||||||
|
def available_locales
|
||||||
|
backend.locales
|
||||||
|
end
|
||||||
|
|
||||||
|
def translate(key, **options)
|
||||||
|
options[:locale] ||= locale
|
||||||
|
translator.translate(key, **options)
|
||||||
|
end
|
||||||
|
alias_method :t, :translate
|
||||||
|
|
||||||
|
def localize(value, **options)
|
||||||
|
options[:locale] ||= locale
|
||||||
|
translator.localize(value, **options)
|
||||||
|
end
|
||||||
|
alias_method :l, :localize
|
||||||
|
|
||||||
|
# Picks the first locale in the client's Accept-Language header that
|
||||||
|
# this app has translations for, honoring q-values (like
|
||||||
|
# Mime.best_from_accept but for BCP-47 language tags, matching on
|
||||||
|
# the primary subtag: "en-US" negotiates against an :en backend).
|
||||||
|
def best_locale_from_accept(header, available: available_locales)
|
||||||
|
return nil if header.nil? || header.strip.empty?
|
||||||
|
wanted = available.map(&:to_s)
|
||||||
|
parse_accept_language(header).each do |tag|
|
||||||
|
exact = wanted.find { |candidate| candidate.casecmp?(tag) }
|
||||||
|
return exact.to_sym if exact
|
||||||
|
primary = tag.split('-').first
|
||||||
|
match = wanted.find { |candidate| candidate.casecmp?(primary) }
|
||||||
|
return match.to_sym if match
|
||||||
|
end
|
||||||
|
nil
|
||||||
|
end
|
||||||
|
|
||||||
|
def reset!
|
||||||
|
@backend = nil
|
||||||
|
@translator = nil
|
||||||
|
@default_locale = nil
|
||||||
|
@fallbacks = nil
|
||||||
|
@load_path = nil
|
||||||
|
Thread.current[:prism_i18n_locale] = nil
|
||||||
|
end
|
||||||
|
|
||||||
|
private
|
||||||
|
|
||||||
|
def translator
|
||||||
|
@translator ||= Translator.new(backend, default_locale: default_locale, fallbacks: fallbacks)
|
||||||
|
end
|
||||||
|
|
||||||
|
def parse_accept_language(header)
|
||||||
|
entries = header.split(',').filter_map do |part|
|
||||||
|
tag, *params = part.strip.split(';').map(&:strip)
|
||||||
|
next if tag.nil? || tag.empty?
|
||||||
|
quality = 1.0
|
||||||
|
params.each { |param| quality = param.sub('q=', '').to_f if param.start_with?('q=') }
|
||||||
|
[tag, quality]
|
||||||
|
end
|
||||||
|
entries.sort_by.with_index { |(_, quality), index| [-quality, index] }.map(&:first)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
Reference in new issue
Block a user