Feature: Added support for English am/pm meridian time format.
This commit is contained in:
parent
04ce38a6f8
commit
50e3b98955
14 changed files with 63 additions and 25 deletions
|
@ -348,6 +348,9 @@ class _i18nSingleton extends Spine.Module
|
||||||
S = timeObject.getSeconds()
|
S = timeObject.getSeconds()
|
||||||
M = timeObject.getMinutes()
|
M = timeObject.getMinutes()
|
||||||
H = timeObject.getHours()
|
H = timeObject.getHours()
|
||||||
|
l = (H + 11) % 12 + 1
|
||||||
|
l = ' ' + l if l < 10
|
||||||
|
|
||||||
format = format
|
format = format
|
||||||
.replace(/dd/, @formatNumber(d, 2))
|
.replace(/dd/, @formatNumber(d, 2))
|
||||||
.replace(/d/, d)
|
.replace(/d/, d)
|
||||||
|
@ -358,4 +361,6 @@ class _i18nSingleton extends Spine.Module
|
||||||
.replace(/SS/, @formatNumber(S, 2))
|
.replace(/SS/, @formatNumber(S, 2))
|
||||||
.replace(/MM/, @formatNumber(M, 2))
|
.replace(/MM/, @formatNumber(M, 2))
|
||||||
.replace(/HH/, @formatNumber(H, 2))
|
.replace(/HH/, @formatNumber(H, 2))
|
||||||
|
.replace(/l/, l)
|
||||||
|
.replace(/P/, if H >= 12 then 'pm' else 'am')
|
||||||
format
|
format
|
||||||
|
|
|
@ -164,6 +164,8 @@ or
|
||||||
record.sub!('SS', format('%<second>02d', second: timestamp.sec.to_s))
|
record.sub!('SS', format('%<second>02d', second: timestamp.sec.to_s))
|
||||||
record.sub!('MM', format('%<minute>02d', minute: timestamp.min.to_s))
|
record.sub!('MM', format('%<minute>02d', minute: timestamp.min.to_s))
|
||||||
record.sub!('HH', format('%<hour>02d', hour: timestamp.hour.to_s))
|
record.sub!('HH', format('%<hour>02d', hour: timestamp.hour.to_s))
|
||||||
|
record.sub!('l', timestamp.strftime('%l'))
|
||||||
|
record.sub!('P', timestamp.strftime('%P'))
|
||||||
"#{record} (#{timezone})"
|
"#{record} (#{timezone})"
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -20,7 +20,9 @@ msgstr ""
|
||||||
#. - 'yy' - last 2 digits of year
|
#. - 'yy' - last 2 digits of year
|
||||||
#. - 'SS' - 2-digit second
|
#. - 'SS' - 2-digit second
|
||||||
#. - 'MM' - 2-digit minute
|
#. - 'MM' - 2-digit minute
|
||||||
#. - 'HH' - 2-digit hour
|
#. - 'HH' - 2-digit hour (24h)
|
||||||
|
#. - 'l' - hour (12h)
|
||||||
|
#. - 'P' - Meridian indicator ('am' or 'pm')
|
||||||
msgid "FORMAT_DATE"
|
msgid "FORMAT_DATE"
|
||||||
msgstr "mm/dd/yyyy"
|
msgstr "mm/dd/yyyy"
|
||||||
|
|
||||||
|
@ -34,9 +36,11 @@ msgstr "mm/dd/yyyy"
|
||||||
#. - 'yy' - last 2 digits of year
|
#. - 'yy' - last 2 digits of year
|
||||||
#. - 'SS' - 2-digit second
|
#. - 'SS' - 2-digit second
|
||||||
#. - 'MM' - 2-digit minute
|
#. - 'MM' - 2-digit minute
|
||||||
#. - 'HH' - 2-digit hour
|
#. - 'HH' - 2-digit hour (24h)
|
||||||
|
#. - 'l' - hour (12h)
|
||||||
|
#. - 'P' - Meridian indicator ('am' or 'pm')
|
||||||
msgid "FORMAT_DATETIME"
|
msgid "FORMAT_DATETIME"
|
||||||
msgstr "mm/dd/yyyy HH:MM"
|
msgstr "mm/dd/yyyy l:MM P"
|
||||||
|
|
||||||
#: db/seeds/settings.rb
|
#: db/seeds/settings.rb
|
||||||
msgid "\"Database\" stores all attachments in the database (not recommended for storing large amounts of data). \"Filesystem\" stores the data in the filesystem. You can switch between the modules even on a system that is already in production without any loss of data."
|
msgid "\"Database\" stores all attachments in the database (not recommended for storing large amounts of data). \"Filesystem\" stores the data in the filesystem. You can switch between the modules even on a system that is already in production without any loss of data."
|
||||||
|
|
|
@ -32,7 +32,9 @@ class Generators::TranslationCatalog::TranslationCatalogGenerator < Rails::Gener
|
||||||
#. - 'yy' - last 2 digits of year
|
#. - 'yy' - last 2 digits of year
|
||||||
#. - 'SS' - 2-digit second
|
#. - 'SS' - 2-digit second
|
||||||
#. - 'MM' - 2-digit minute
|
#. - 'MM' - 2-digit minute
|
||||||
#. - 'HH' - 2-digit hour
|
#. - 'HH' - 2-digit hour (24h)
|
||||||
|
#. - 'l' - hour (12h)
|
||||||
|
#. - 'P' - Meridian indicator ('am' or 'pm')
|
||||||
LEGEND
|
LEGEND
|
||||||
|
|
||||||
def extract_strings(path)
|
def extract_strings(path)
|
||||||
|
@ -91,6 +93,9 @@ class Generators::TranslationCatalog::TranslationCatalogGenerator < Rails::Gener
|
||||||
|
|
||||||
POT_HEADER
|
POT_HEADER
|
||||||
|
|
||||||
|
# Add the default date/time format strings for 'en_US' as translations to
|
||||||
|
# the source catalog file. They will be read into the Translation model
|
||||||
|
# and can be customized via the translations admin GUI.
|
||||||
pot += <<~FORMAT_STRINGS if !options['addon_path']
|
pot += <<~FORMAT_STRINGS if !options['addon_path']
|
||||||
#. Default date format to use for the current locale.
|
#. Default date format to use for the current locale.
|
||||||
#{DATE_FORMAT_LEGEND}
|
#{DATE_FORMAT_LEGEND}
|
||||||
|
@ -100,7 +105,7 @@ class Generators::TranslationCatalog::TranslationCatalogGenerator < Rails::Gener
|
||||||
#. Default date/time format to use for the current locale.
|
#. Default date/time format to use for the current locale.
|
||||||
#{DATE_FORMAT_LEGEND}
|
#{DATE_FORMAT_LEGEND}
|
||||||
msgid "FORMAT_DATETIME"
|
msgid "FORMAT_DATETIME"
|
||||||
msgstr "mm/dd/yyyy HH:MM"
|
msgstr "mm/dd/yyyy l:MM P"
|
||||||
|
|
||||||
FORMAT_STRINGS
|
FORMAT_STRINGS
|
||||||
|
|
||||||
|
|
|
@ -1362,7 +1362,13 @@ QUnit.test("check replace tags", assert => {
|
||||||
yfull = localTime.getFullYear()
|
yfull = localTime.getFullYear()
|
||||||
M = formatNumber(localTime.getMinutes(), 2)
|
M = formatNumber(localTime.getMinutes(), 2)
|
||||||
H = formatNumber(localTime.getHours(), 2)
|
H = formatNumber(localTime.getHours(), 2)
|
||||||
return m + '/' + d + '/' + yfull + ' ' + H + ':' + M
|
l = (H + 11) % 12 + 1
|
||||||
|
if (l < 10) {
|
||||||
|
l = ' ' + l
|
||||||
|
}
|
||||||
|
P = H >= 12 ? 'pm' : 'am'
|
||||||
|
|
||||||
|
return m + '/' + d + '/' + yfull + ' ' + l + ':' + M + ' ' + P
|
||||||
}
|
}
|
||||||
|
|
||||||
var message = "<div>#{user.firstname} #{user.lastname}</div>"
|
var message = "<div>#{user.firstname} #{user.lastname}</div>"
|
||||||
|
|
|
@ -217,8 +217,11 @@ QUnit.test('i18n', assert => {
|
||||||
translated = App.i18n.translateContent('Enables user authentication via %s. Register your app first at [%s](%s).', 'XXX', 'YYY', 'http://lalala')
|
translated = App.i18n.translateContent('Enables user authentication via %s. Register your app first at [%s](%s).', 'XXX', 'YYY', 'http://lalala')
|
||||||
assert.equal(translated, 'Enables user authentication via XXX. Register your app first at <a href="http://lalala" target="_blank">YYY</a>.', 'en-us - link')
|
assert.equal(translated, 'Enables user authentication via XXX. Register your app first at <a href="http://lalala" target="_blank">YYY</a>.', 'en-us - link')
|
||||||
|
|
||||||
timestamp = App.i18n.translateTimestamp('2012-11-06T21:07:24Z', offset)
|
timestamp = App.i18n.translateTimestamp('2012-11-06T09:07:24Z', offset)
|
||||||
assert.equal(timestamp, '11/06/2012 21:07', 'en - timestamp translated correctly')
|
assert.equal(timestamp, '11/06/2012 9:07 am', 'en - timestamp translated correctly (pm)')
|
||||||
|
|
||||||
|
timestamp = App.i18n.translateTimestamp('2012-11-06T22:07:24Z', offset)
|
||||||
|
assert.equal(timestamp, '11/06/2012 10:07 pm', 'en - timestamp translated correctly (pm)')
|
||||||
|
|
||||||
timestamp = App.i18n.translateTimestamp('', offset);
|
timestamp = App.i18n.translateTimestamp('', offset);
|
||||||
assert.equal(timestamp, '', 'en - timestamp translated correctly')
|
assert.equal(timestamp, '', 'en - timestamp translated correctly')
|
||||||
|
@ -242,7 +245,7 @@ QUnit.test('i18n', assert => {
|
||||||
assert.equal(date, undefined, 'en - date translated correctly')
|
assert.equal(date, undefined, 'en - date translated correctly')
|
||||||
|
|
||||||
date = App.i18n.timeFormat()
|
date = App.i18n.timeFormat()
|
||||||
assert.deepEqual(date, { "FORMAT_DATE": "mm/dd/yyyy", "FORMAT_DATETIME": "mm/dd/yyyy HH:MM" }, 'timeFormat property')
|
assert.deepEqual(date, { "FORMAT_DATE": "mm/dd/yyyy", "FORMAT_DATETIME": "mm/dd/yyyy l:MM P" }, 'timeFormat property')
|
||||||
|
|
||||||
// Verify that the datepicker gets the correct format too.
|
// Verify that the datepicker gets the correct format too.
|
||||||
el_date = App.UiElement.date.render({name: 'test', value: '2018-07-06'})
|
el_date = App.UiElement.date.render({name: 'test', value: '2018-07-06'})
|
||||||
|
|
|
@ -54,7 +54,7 @@ QUnit.test( "model ui basic tests", assert => {
|
||||||
assert.equal( App.viewPrint( ticket, 'state' ), 'open')
|
assert.equal( App.viewPrint( ticket, 'state' ), 'open')
|
||||||
assert.equal( App.viewPrint( ticket, 'state_id' ), 'open')
|
assert.equal( App.viewPrint( ticket, 'state_id' ), 'open')
|
||||||
assert.equal( App.viewPrint( ticket, 'not_existing' ), '-')
|
assert.equal( App.viewPrint( ticket, 'not_existing' ), '-')
|
||||||
assert.equal( App.viewPrint( ticket, 'updated_at' ), '<time class="humanTimeFromNow " datetime="2014-11-07T23:43:08.000Z" title="11/07/2014 23:43">11/07/2014</time>')
|
assert.equal( App.viewPrint( ticket, 'updated_at' ), '<time class="humanTimeFromNow " datetime="2014-11-07T23:43:08.000Z" title="11/07/2014 11:43 pm">11/07/2014</time>')
|
||||||
assert.equal( App.viewPrint( ticket, 'date' ), '02/07/2015')
|
assert.equal( App.viewPrint( ticket, 'date' ), '02/07/2015')
|
||||||
assert.equal( App.viewPrint( ticket, 'textarea' ), '<div>some new</div><div>line</div>')
|
assert.equal( App.viewPrint( ticket, 'textarea' ), '<div>some new</div><div>line</div>')
|
||||||
assert.equal( App.viewPrint( ticket, 'link1' ), '<a href="http://zammad.com" target="blank">closed</a>')
|
assert.equal( App.viewPrint( ticket, 'link1' ), '<a href="http://zammad.com" target="blank">closed</a>')
|
||||||
|
@ -65,7 +65,7 @@ QUnit.test( "model ui basic tests", assert => {
|
||||||
let attr = App.Ticket.configure_attributes.find(e => { return e.name == 'updated_at' })
|
let attr = App.Ticket.configure_attributes.find(e => { return e.name == 'updated_at' })
|
||||||
attr.include_timezone = true
|
attr.include_timezone = true
|
||||||
|
|
||||||
assert.equal( App.viewPrint( ticket, 'updated_at' ), '<time class="humanTimeFromNow " datetime="2014-11-07T23:43:08.000Z" title="11/07/2014 23:43 Example/Timezone" timezone="Example/Timezone">11/07/2014</time>')
|
assert.equal( App.viewPrint( ticket, 'updated_at' ), '<time class="humanTimeFromNow " datetime="2014-11-07T23:43:08.000Z" title="11/07/2014 11:43 pm Example/Timezone" timezone="Example/Timezone">11/07/2014</time>')
|
||||||
|
|
||||||
attr.include_timezone = false
|
attr.include_timezone = false
|
||||||
stub.restore()
|
stub.restore()
|
||||||
|
|
|
@ -362,9 +362,14 @@ QUnit.test("check pretty date", assert => {
|
||||||
yshort = date.getYear()-100
|
yshort = date.getYear()-100
|
||||||
M = date.getMinutes()
|
M = date.getMinutes()
|
||||||
H = date.getHours()
|
H = date.getHours()
|
||||||
|
l = (H + 11) % 12 + 1
|
||||||
|
if (l < 10) {
|
||||||
|
l = ' ' + l
|
||||||
|
}
|
||||||
|
P = H >= 12 ? 'pm' : 'am'
|
||||||
|
|
||||||
// YYYY-MM-DD HH::MM
|
// YYYY-MM-DD HH::MM
|
||||||
return (m < 10 ? '0':'') + m + '/' + (d < 10 ? '0':'') + d + '/' + (yfull) + ' ' + (H < 10 ? '0':'') + H + ':' + (M < 10 ? '0':'') + M
|
return (m < 10 ? '0':'') + m + '/' + (d < 10 ? '0':'') + d + '/' + (yfull) + ' ' + l + ':' + (M < 10 ? '0':'') + M + ' ' + P
|
||||||
}
|
}
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
|
@ -60,7 +60,7 @@ RSpec.describe NotificationFactory::Slack do
|
||||||
.to match(%r{Updated by #{current_user.fullname}})
|
.to match(%r{Updated by #{current_user.fullname}})
|
||||||
.and match(%r{state: aaa -> bbb})
|
.and match(%r{state: aaa -> bbb})
|
||||||
.and match(%r{group: xxx -> yyy})
|
.and match(%r{group: xxx -> yyy})
|
||||||
.and match(%r{pending_time: 04/01/2019 12:00 \(Europe/Berlin\) -> 04/02/2019 01:00 \(Europe/Berlin\)})
|
.and match(%r{pending_time: 04/01/2019 12:00 pm \(Europe/Berlin\) -> 04/02/2019 1:00 am \(Europe/Berlin\)})
|
||||||
.and match(%r{#{article.body}\z})
|
.and match(%r{#{article.body}\z})
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
@ -90,7 +90,7 @@ RSpec.describe NotificationFactory::Slack do
|
||||||
it 'returns a hash with body: <ticket customer, escalation time, & body>' do
|
it 'returns a hash with body: <ticket customer, escalation time, & body>' do
|
||||||
expect(template[:body])
|
expect(template[:body])
|
||||||
.to match(%r{A ticket \(#{ticket.title}\) from "#{ticket.customer.fullname}"})
|
.to match(%r{A ticket \(#{ticket.title}\) from "#{ticket.customer.fullname}"})
|
||||||
.and match(%r{is escalated since "04/01/2019 12:00 \(Europe/Berlin\)"!})
|
.and match(%r{is escalated since "04/01/2019 12:00 pm \(Europe/Berlin\)"!})
|
||||||
.and match(%r{#{article.body}\z})
|
.and match(%r{#{article.body}\z})
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -56,7 +56,7 @@ RSpec.describe Translation do
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'contains the translation for "FORMAT_DATE_TIME"' do
|
it 'contains the translation for "FORMAT_DATE_TIME"' do
|
||||||
expect(described_class.strings_for_locale('en-us')['FORMAT_DATETIME']).to have_attributes(translation: 'mm/dd/yyyy HH:MM', translation_file: 'i18n/zammad.pot')
|
expect(described_class.strings_for_locale('en-us')['FORMAT_DATETIME']).to have_attributes(translation: 'mm/dd/yyyy l:MM P', translation_file: 'i18n/zammad.pot')
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -210,7 +210,7 @@ RSpec.describe Translation do
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'adds the en-us FORMAT_DATETIME entry' do
|
it 'adds the en-us FORMAT_DATETIME entry' do
|
||||||
expect(described_class.find_source('en-us', 'FORMAT_DATETIME')).to have_attributes(is_synchronized_from_codebase: true, synchronized_from_translation_file: 'i18n/zammad.pot', target: 'mm/dd/yyyy HH:MM')
|
expect(described_class.find_source('en-us', 'FORMAT_DATETIME')).to have_attributes(is_synchronized_from_codebase: true, synchronized_from_translation_file: 'i18n/zammad.pot', target: 'mm/dd/yyyy l:MM P')
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'adds the custom translated entry with content' do
|
it 'adds the custom translated entry with content' do
|
||||||
|
|
|
@ -69,12 +69,20 @@ RSpec.describe Translation do
|
||||||
expect(described_class.timestamp('en-us', 'Invalid/TimeZone', '2018-10-10T10:00:00Z0')).to eq(Time.zone.parse('2018-10-10T10:00:00Z0').to_s)
|
expect(described_class.timestamp('en-us', 'Invalid/TimeZone', '2018-10-10T10:00:00Z0')).to eq(Time.zone.parse('2018-10-10T10:00:00Z0').to_s)
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'en-us with timestamp as string' do
|
it 'en-us with timestamp as string (am)' do
|
||||||
expect(described_class.timestamp('en-us', 'Europe/Berlin', '2018-10-10T10:00:00Z0')).to eq('10/10/2018 12:00 (Europe/Berlin)')
|
expect(described_class.timestamp('en-us', 'Europe/Berlin', '2018-10-10T01:00:00Z0')).to eq('10/10/2018 3:00 am (Europe/Berlin)')
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'en-us with time object' do
|
it 'en-us with timestamp as string (pm)' do
|
||||||
expect(described_class.timestamp('en-us', 'Europe/Berlin', Time.zone.parse('2018-10-10T10:00:00Z0'))).to eq('10/10/2018 12:00 (Europe/Berlin)')
|
expect(described_class.timestamp('en-us', 'Europe/Berlin', '2018-10-10T10:00:00Z0')).to eq('10/10/2018 12:00 pm (Europe/Berlin)')
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'en-us with time object (am)' do
|
||||||
|
expect(described_class.timestamp('en-us', 'Europe/Berlin', Time.zone.parse('2018-10-10T01:00:00Z0'))).to eq('10/10/2018 3:00 am (Europe/Berlin)')
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'en-us with time object (pm)' do
|
||||||
|
expect(described_class.timestamp('en-us', 'Europe/Berlin', Time.zone.parse('2018-10-10T10:00:00Z0'))).to eq('10/10/2018 12:00 pm (Europe/Berlin)')
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'de-de with timestamp as string' do
|
it 'de-de with timestamp as string' do
|
||||||
|
|
|
@ -375,7 +375,7 @@ RSpec.describe 'Ticket > Update > Full Quote Header', current_user_id: -> { curr
|
||||||
expected
|
expected
|
||||||
.created_at
|
.created_at
|
||||||
.in_time_zone('Europe/London')
|
.in_time_zone('Europe/London')
|
||||||
.strftime('%m/%d/%Y %H:%M')
|
.strftime('%m/%d/%Y %1I:%M %P')
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -58,7 +58,7 @@ class NotificationFactoryRendererTest < ActiveSupport::TestCase
|
||||||
timezone: 'Europe/Berlin',
|
timezone: 'Europe/Berlin',
|
||||||
template: template,
|
template: template,
|
||||||
).render
|
).render
|
||||||
assert_equal('11/12/2016 13:00 (Europe/Berlin)', result)
|
assert_equal('11/12/2016 1:00 pm (Europe/Berlin)', result)
|
||||||
|
|
||||||
template = "\#{ticket.created_by.firstname}"
|
template = "\#{ticket.created_by.firstname}"
|
||||||
result = described_class.new(
|
result = described_class.new(
|
||||||
|
@ -80,7 +80,7 @@ class NotificationFactoryRendererTest < ActiveSupport::TestCase
|
||||||
timezone: 'Europe/Berlin',
|
timezone: 'Europe/Berlin',
|
||||||
template: template,
|
template: template,
|
||||||
).render
|
).render
|
||||||
assert_equal('11/12/2016 15:00 (Europe/Berlin)', result)
|
assert_equal('11/12/2016 3:00 pm (Europe/Berlin)', result)
|
||||||
|
|
||||||
template = "\#{ticket.updated_by.firstname}"
|
template = "\#{ticket.updated_by.firstname}"
|
||||||
result = described_class.new(
|
result = described_class.new(
|
||||||
|
|
|
@ -1194,7 +1194,7 @@ class TicketNotificationTest < ActiveSupport::TestCase
|
||||||
assert_match(%r{1 low}, result[:body])
|
assert_match(%r{1 low}, result[:body])
|
||||||
assert_match(%r{2 normal}, result[:body])
|
assert_match(%r{2 normal}, result[:body])
|
||||||
assert_match(%r{Pending till}, result[:body])
|
assert_match(%r{Pending till}, result[:body])
|
||||||
assert_match('01/11/2015 19:33 (America/St_Lucia)', result[:body])
|
assert_match('01/11/2015 7:33 pm (America/St_Lucia)', result[:body])
|
||||||
assert_match(%r{update}, result[:body])
|
assert_match(%r{update}, result[:body])
|
||||||
assert_no_match(%r{pending_till}, result[:body])
|
assert_no_match(%r{pending_till}, result[:body])
|
||||||
assert_no_match(%r{i18n}, result[:body])
|
assert_no_match(%r{i18n}, result[:body])
|
||||||
|
@ -1322,7 +1322,7 @@ class TicketNotificationTest < ActiveSupport::TestCase
|
||||||
)
|
)
|
||||||
|
|
||||||
assert_match('Ticket is escalated (some notification template test 1 Bobs\'s resumé', result[:subject])
|
assert_match('Ticket is escalated (some notification template test 1 Bobs\'s resumé', result[:subject])
|
||||||
assert_match('is escalated since "04/01/2019 06:00 (America/St_Lucia)"!', result[:body])
|
assert_match('is escalated since "04/01/2019 6:00 am (America/St_Lucia)"!', result[:body])
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue