Class: RailsErrorDashboard::Services::LocalizedTimeFormatter
- Inherits:
-
Object
- Object
- RailsErrorDashboard::Services::LocalizedTimeFormatter
- Defined in:
- lib/rails_error_dashboard/services/localized_time_formatter.rb
Overview
strftime with the word-producing directives resolved from RED's own dictionary.
WHY THIS EXISTS
Ruby's strftime is not locale-aware: %B is "August" whatever RED's locale is. On the dashboard that does not matter, because a timestamp is rendered as a and the browser re-renders it through formatDateTime() using the red.js.* month/day/meridian vocabulary P3-T2 added. Anywhere there is no browser — email (P4-T2), Slack and Discord payloads (P4-T3) — the server has to do that substitution itself.
Sharing one implementation is the point: an email, a Slack message and the dashboard should not disagree about what a date looks like.
Takes the locale as an argument rather than reading Current, because every caller runs inside a job where Current is nil or belongs to an unrelated request.
Constant Summary collapse
- LOCALIZED_DIRECTIVES =
The directives whose output is a word, and therefore locale-dependent: %B %b (month), %A %a (weekday), %p %P (meridian). Everything else (%Y, %m, %d, %H, %M, %S, %Z …) is digits or a timezone abbreviation and is left to strftime.
/%[BbAaPp]/- PLACEHOLDER =
Stand-in for a localized word while strftime runs. Contains no percent, so strftime cannot read it as a directive, and no space, so it cannot alter the spacing the pattern asked for. The index keeps two occurrences of the same directive independent.
"\u0000RED%<index>d\u0000"
Class Method Summary collapse
Instance Method Summary collapse
-
#initialize(locale) ⇒ LocalizedTimeFormatter
constructor
A new instance of LocalizedTimeFormatter.
-
#render(time, pattern) ⇒ Object
Two-pass, via placeholders: substituting a translated month name directly would put arbitrary text into the pattern, and while a French "%A" -> "mardi" is harmless, a translation containing a literal percent sign would corrupt every directive after it.
Constructor Details
#initialize(locale) ⇒ LocalizedTimeFormatter
Returns a new instance of LocalizedTimeFormatter.
44 45 46 |
# File 'lib/rails_error_dashboard/services/localized_time_formatter.rb', line 44 def initialize(locale) @locale = locale end |
Class Method Details
.call(time, pattern:, locale:) ⇒ String
40 41 42 |
# File 'lib/rails_error_dashboard/services/localized_time_formatter.rb', line 40 def self.call(time, pattern:, locale:) new(locale).render(time, pattern) end |
Instance Method Details
#render(time, pattern) ⇒ Object
Two-pass, via placeholders: substituting a translated month name directly would put arbitrary text into the pattern, and while a French "%A" -> "mardi" is harmless, a translation containing a literal percent sign would corrupt every directive after it. The placeholder carries no percent, so strftime cannot misread it.
53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 |
# File 'lib/rails_error_dashboard/services/localized_time_formatter.rb', line 53 def render(time, pattern) replacements = [] staged = pattern.gsub(LOCALIZED_DIRECTIVES) do |directive| word = localized_word(directive, time) next directive if word.nil? replacements << word format(PLACEHOLDER, index: replacements.length - 1) end rendered = time.strftime(staged) replacements.each_with_index do |word, index| rendered = rendered.sub(format(PLACEHOLDER, index: index), word) end rendered end |