Localize all timestamps (#21440)

Following
* #21410 

We are now able to localize all timestamps. Some examples:

`short-date` format, French, user profile page:

![image](https://user-images.githubusercontent.com/20454870/195622461-aa0d5b93-f8df-42ad-881c-9c16606bf387.png)

`date-time` format, Portuguese, mirror repository settings page:

![image](https://user-images.githubusercontent.com/20454870/195623191-7a37d77c-4a02-4140-846d-f290a65ea21d.png)

Signed-off-by: Yarden Shoham <hrsi88@gmail.com>

Signed-off-by: Yarden Shoham <hrsi88@gmail.com>
Co-authored-by: Gusted <williamzijl7@hotmail.com>
Co-authored-by: techknowlogick <techknowlogick@gitea.io>
Co-authored-by: Lunny Xiao <xiaolunwen@gmail.com>
This commit is contained in:
Yarden Shoham 2022-10-17 07:08:21 +03:00 committed by GitHub
parent 683c9af89f
commit acdb92ad42
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
20 changed files with 49 additions and 27 deletions

View file

@ -29,8 +29,8 @@
<td><a href="{{AppSubUrl}}/admin/auths/{{.ID}}">{{.Name}}</a></td> <td><a href="{{AppSubUrl}}/admin/auths/{{.ID}}">{{.Name}}</a></td>
<td>{{.TypeName}}</td> <td>{{.TypeName}}</td>
<td>{{if .IsActive}}{{svg "octicon-check"}}{{else}}{{svg "octicon-x"}}{{end}}</td> <td>{{if .IsActive}}{{svg "octicon-check"}}{{else}}{{svg "octicon-x"}}{{end}}</td>
<td><span class="tooltip" data-content="{{.UpdatedUnix.FormatShort}}">{{.UpdatedUnix.FormatShort}}</span></td> <td><span class="tooltip" data-content="{{.UpdatedUnix.FormatShort}}"><time data-format="short-date" datetime="{{.UpdatedUnix.FormatLong}}">{{.UpdatedUnix.FormatShort}}</time></span></td>
<td><span class="tooltip" data-content="{{.CreatedUnix.FormatLong}}">{{.CreatedUnix.FormatShort}}</span></td> <td><span class="tooltip" data-content="{{.CreatedUnix.FormatLong}}"><time data-format="short-date" datetime="{{.CreatedUnix.FormatLong}}">{{.CreatedUnix.FormatShort}}</time></span></td>
<td><a href="{{AppSubUrl}}/admin/auths/{{.ID}}">{{svg "octicon-pencil"}}</a></td> <td><a href="{{AppSubUrl}}/admin/auths/{{.ID}}">{{svg "octicon-pencil"}}</a></td>
</tr> </tr>
{{end}} {{end}}

View file

@ -29,7 +29,7 @@
<td>{{.ID}}</td> <td>{{.ID}}</td>
<td>{{$.locale.Tr .TrStr}}</td> <td>{{$.locale.Tr .TrStr}}</td>
<td class="view-detail"><span class="notice-description text truncate">{{.Description}}</span></td> <td class="view-detail"><span class="notice-description text truncate">{{.Description}}</span></td>
<td><span class="notice-created-time tooltip" data-content="{{.CreatedUnix.AsTime}}">{{.CreatedUnix.FormatShort}}</span></td> <td><span class="notice-created-time tooltip" data-content="{{.CreatedUnix.AsTime}}"><time data-format="short-date" datetime="{{.CreatedUnix.FormatLong}}">{{.CreatedUnix.FormatShort}}</time></span></td>
<td><a href="#">{{svg "octicon-note" 16 "view-detail"}}</a></td> <td><a href="#">{{svg "octicon-note" 16 "view-detail"}}</a></td>
</tr> </tr>
{{end}} {{end}}

View file

@ -44,7 +44,7 @@
<td>{{.NumTeams}}</td> <td>{{.NumTeams}}</td>
<td>{{.NumMembers}}</td> <td>{{.NumMembers}}</td>
<td>{{.NumRepos}}</td> <td>{{.NumRepos}}</td>
<td><span title="{{.CreatedUnix.FormatLong}}">{{.CreatedUnix.FormatShort}}</span></td> <td><span title="{{.CreatedUnix.FormatLong}}"><time data-format="short-date" datetime="{{.CreatedUnix.FormatLong}}">{{.CreatedUnix.FormatShort}}</time></span></td>
<td><a href="{{.OrganisationLink}}/settings">{{svg "octicon-pencil"}}</a></td> <td><a href="{{.OrganisationLink}}/settings">{{svg "octicon-pencil"}}</a></td>
</tr> </tr>
{{end}} {{end}}

View file

@ -75,7 +75,7 @@
{{end}} {{end}}
</td> </td>
<td>{{FileSize .CalculateBlobSize}}</td> <td>{{FileSize .CalculateBlobSize}}</td>
<td><span title="{{.Version.CreatedUnix.FormatLong}}">{{.Version.CreatedUnix.FormatShort}}</span></td> <td><span title="{{.Version.CreatedUnix.FormatLong}}"><time data-format="short-date" datetime="{{.Version.CreatedUnix.FormatLong}}">{{.Version.CreatedUnix.FormatShort}}</time></span></td>
<td><a class="delete-button" href="" data-url="{{$.Link}}/delete?page={{$.Page.Paginater.Current}}&sort={{$.SortType}}" data-id="{{.Version.ID}}" data-name="{{.Package.Name}}" data-data-version="{{.Version.Version}}">{{svg "octicon-trash"}}</a></td> <td><a class="delete-button" href="" data-url="{{$.Link}}/delete?page={{$.Page.Paginater.Current}}&sort={{$.SortType}}" data-id="{{.Version.ID}}" data-name="{{.Package.Name}}" data-data-version="{{.Version.Version}}">{{svg "octicon-trash"}}</a></td>
</tr> </tr>
{{end}} {{end}}

View file

@ -83,7 +83,7 @@
<td>{{.NumForks}}</td> <td>{{.NumForks}}</td>
<td>{{.NumIssues}}</td> <td>{{.NumIssues}}</td>
<td>{{FileSize .Size}}</td> <td>{{FileSize .Size}}</td>
<td><span title="{{.CreatedUnix.FormatLong}}">{{.CreatedUnix.FormatShort}}</span></td> <td><span title="{{.CreatedUnix.FormatLong}}"><time data-format="short-date" datetime="{{.CreatedUnix.FormatLong}}">{{.CreatedUnix.FormatShort}}</time></span></td>
<td><a class="delete-button" href="" data-url="{{$.Link}}/delete?page={{$.Page.Paginater.Current}}&sort={{$.SortType}}" data-id="{{.ID}}" data-name="{{.Name}}">{{svg "octicon-trash"}}</a></td> <td><a class="delete-button" href="" data-url="{{$.Link}}/delete?page={{$.Page.Paginater.Current}}&sort={{$.SortType}}" data-id="{{.ID}}" data-name="{{.Name}}">{{svg "octicon-trash"}}</a></td>
</tr> </tr>
{{end}} {{end}}

View file

@ -94,9 +94,9 @@
<td>{{if .IsRestricted}}{{svg "octicon-check"}}{{else}}{{svg "octicon-x"}}{{end}}</td> <td>{{if .IsRestricted}}{{svg "octicon-check"}}{{else}}{{svg "octicon-x"}}{{end}}</td>
<td>{{if index $.UsersTwoFaStatus .ID}}{{svg "octicon-check"}}{{else}}{{svg "octicon-x"}}{{end}}</td> <td>{{if index $.UsersTwoFaStatus .ID}}{{svg "octicon-check"}}{{else}}{{svg "octicon-x"}}{{end}}</td>
<td>{{.NumRepos}}</td> <td>{{.NumRepos}}</td>
<td><span title="{{.CreatedUnix.FormatLong}}">{{.CreatedUnix.FormatShort}}</span></td> <td><span title="{{.CreatedUnix.FormatLong}}"><time data-format="short-date" datetime="{{.CreatedUnix.FormatLong}}">{{.CreatedUnix.FormatShort}}</time></span></td>
{{if .LastLoginUnix}} {{if .LastLoginUnix}}
<td><span title="{{.LastLoginUnix.FormatLong}}">{{.LastLoginUnix.FormatShort}}</span></td> <td><span title="{{.LastLoginUnix.FormatLong}}"><time data-format="short-date" datetime="{{.LastLoginUnix.FormatLong}}">{{.LastLoginUnix.FormatShort}}</time></span></td>
{{else}} {{else}}
<td><span>{{$.locale.Tr "admin.users.never_login"}}</span></td> <td><span>{{$.locale.Tr "admin.users.never_login"}}</span></td>
{{end}} {{end}}

View file

@ -23,7 +23,7 @@
{{svg "octicon-link"}} {{svg "octicon-link"}}
<a href="{{.Website}}" rel="nofollow">{{.Website}}</a> <a href="{{.Website}}" rel="nofollow">{{.Website}}</a>
{{end}} {{end}}
{{svg "octicon-clock"}} {{$.locale.Tr "user.join_on"}} {{.CreatedUnix.FormatShort}} {{svg "octicon-clock"}} {{$.locale.Tr "user.join_on"}} <time data-format="short-date" datetime="{{.CreatedUnix.FormatLong}}">{{.CreatedUnix.FormatShort}}</time>
</div> </div>
</div> </div>
</div> </div>

View file

@ -18,7 +18,7 @@
{{svg "octicon-mail"}} {{svg "octicon-mail"}}
<a href="mailto:{{.Email}}" rel="nofollow">{{.Email}}</a> <a href="mailto:{{.Email}}" rel="nofollow">{{.Email}}</a>
{{end}} {{end}}
{{svg "octicon-clock"}} {{$.locale.Tr "user.join_on"}} {{.CreatedUnix.FormatShort}} {{svg "octicon-clock"}} {{$.locale.Tr "user.join_on"}} <time data-format="short-date" datetime="{{.CreatedUnix.FormatLong}}">{{.CreatedUnix.FormatShort}}</time>
</div> </div>
</div> </div>
</div> </div>

View file

@ -413,7 +413,7 @@
<div class="df sb ac"> <div class="df sb ac">
<div class="due-date tooltip {{if .Issue.IsOverdue}}text red{{end}}" {{if .Issue.IsOverdue}}data-content="{{.locale.Tr "repo.issues.due_date_overdue"}}"{{end}}> <div class="due-date tooltip {{if .Issue.IsOverdue}}text red{{end}}" {{if .Issue.IsOverdue}}data-content="{{.locale.Tr "repo.issues.due_date_overdue"}}"{{end}}>
{{svg "octicon-calendar" 16 "mr-3"}} {{svg "octicon-calendar" 16 "mr-3"}}
{{.Issue.DeadlineUnix.FormatDate}} <time data-format="date" datetime="{{.Issue.DeadlineUnix.FormatLong}}">{{.Issue.DeadlineUnix.FormatDate}}</time>
</div> </div>
<div> <div>
{{if and .HasIssuesOrPullsWritePermission (not .Repository.IsArchived)}} {{if and .HasIssuesOrPullsWritePermission (not .Repository.IsArchived)}}

View file

@ -64,7 +64,7 @@
{{.Fingerprint}} {{.Fingerprint}}
</div> </div>
<div class="activity meta"> <div class="activity meta">
<i>{{$.locale.Tr "settings.add_on"}} <span>{{.CreatedUnix.FormatShort}}</span> — {{svg "octicon-info"}} {{if .HasUsed}}{{$.locale.Tr "settings.last_used"}} <span {{if .HasRecentActivity}}class="green"{{end}}>{{.UpdatedUnix.FormatShort}}</span>{{else}}{{$.locale.Tr "settings.no_activity"}}{{end}} - <span>{{$.locale.Tr "settings.can_read_info"}}{{if not .IsReadOnly}} / {{$.locale.Tr "settings.can_write_info"}} {{end}}</span></i> <i>{{$.locale.Tr "settings.add_on"}} <span><time data-format="short-date" datetime="{{.CreatedUnix.FormatLong}}">{{.CreatedUnix.FormatShort}}</time></span> — {{svg "octicon-info"}} {{if .HasUsed}}{{$.locale.Tr "settings.last_used"}} <span {{if .HasRecentActivity}}class="green"{{end}}><time data-format="short-date" datetime="{{.UpdatedUnix.FormatLong}}">{{.UpdatedUnix.FormatShort}}</time></span>{{else}}{{$.locale.Tr "settings.no_activity"}}{{end}} - <span>{{$.locale.Tr "settings.can_read_info"}}{{if not .IsReadOnly}} / {{$.locale.Tr "settings.can_write_info"}} {{end}}</span></i>
</div> </div>
</div> </div>
</div> </div>

View file

@ -93,7 +93,7 @@
<tr> <tr>
<td>{{(MirrorRemoteAddress $.Context .Repository .Mirror.GetRemoteName false).Address}}</td> <td>{{(MirrorRemoteAddress $.Context .Repository .Mirror.GetRemoteName false).Address}}</td>
<td>{{$.locale.Tr "repo.settings.mirror_settings.direction.pull"}}</td> <td>{{$.locale.Tr "repo.settings.mirror_settings.direction.pull"}}</td>
<td>{{.Mirror.UpdatedUnix.AsTime}}</td> <td><time data-format="date-time" datetime="{{.Mirror.UpdatedUnix.FormatLong}}">{{.Mirror.UpdatedUnix.AsTime}}</time></td>
<td class="right aligned"> <td class="right aligned">
<form method="post" style="display: inline-block"> <form method="post" style="display: inline-block">
{{.CsrfTokenHtml}} {{.CsrfTokenHtml}}
@ -171,7 +171,7 @@
{{$address := MirrorRemoteAddress $.Context $.Repository .GetRemoteName true}} {{$address := MirrorRemoteAddress $.Context $.Repository .GetRemoteName true}}
<td>{{$address.Address}}</td> <td>{{$address.Address}}</td>
<td>{{$.locale.Tr "repo.settings.mirror_settings.direction.push"}}</td> <td>{{$.locale.Tr "repo.settings.mirror_settings.direction.push"}}</td>
<td>{{if .LastUpdateUnix}}{{.LastUpdateUnix.AsTime}}{{else}}{{$.locale.Tr "never"}}{{end}} {{if .LastError}}<div class="ui red label tooltip" data-content="{{.LastError}}">{{$.locale.Tr "error"}}</div>{{end}}</td> <td>{{if .LastUpdateUnix}}<time data-format="date-time" datetime="{{.LastUpdateUnix.FormatLong}}">{{.LastUpdateUnix.AsTime}}</time>{{else}}{{$.locale.Tr "never"}}{{end}} {{if .LastError}}<div class="ui red label tooltip" data-content="{{.LastError}}">{{$.locale.Tr "error"}}</div>{{end}}</td>
<td class="right aligned"> <td class="right aligned">
<form method="post" style="display: inline-block"> <form method="post" style="display: inline-block">
{{$.CsrfTokenHtml}} {{$.CsrfTokenHtml}}

View file

@ -18,7 +18,7 @@
{{else if .Location}} {{else if .Location}}
{{svg "octicon-location"}} {{.Location}} {{svg "octicon-location"}} {{.Location}}
{{else}} {{else}}
{{svg "octicon-clock"}} {{$.locale.Tr "user.join_on"}} {{.CreatedUnix.FormatShort}} {{svg "octicon-clock"}} {{$.locale.Tr "user.join_on"}} <time data-format="short-date" datetime="{{.CreatedUnix.FormatLong}}">{{.CreatedUnix.FormatShort}}</time>
{{end}} {{end}}
</div> </div>
</li> </li>

View file

@ -109,7 +109,7 @@
<span class="due-date tooltip" data-content="{{$.locale.Tr "repo.issues.due_date"}}" data-position="right center"> <span class="due-date tooltip" data-content="{{$.locale.Tr "repo.issues.due_date"}}" data-position="right center">
<span{{if .IsOverdue}} class="overdue"{{end}}> <span{{if .IsOverdue}} class="overdue"{{end}}>
{{svg "octicon-calendar" 14 "mr-2"}} {{svg "octicon-calendar" 14 "mr-2"}}
{{.DeadlineUnix.FormatShort}} <time data-format="short-date" datetime="{{.DeadlineUnix.FormatLong}}">{{.DeadlineUnix.FormatShort}}</time>
</span> </span>
</span> </span>
{{end}} {{end}}

View file

@ -53,7 +53,7 @@
</li> </li>
{{end}} {{end}}
{{end}} {{end}}
<li>{{svg "octicon-clock"}} {{.locale.Tr "user.join_on"}} {{.Owner.CreatedUnix.FormatShort}}</li> <li>{{svg "octicon-clock"}} {{.locale.Tr "user.join_on"}} <time data-format="short-date" datetime="{{.Owner.CreatedUnix.FormatLong}}">{{.Owner.CreatedUnix.FormatShort}}</time></li>
{{if and .Orgs .HasOrgsVisible}} {{if and .Orgs .HasOrgsVisible}}
<li> <li>
<ul class="user-orgs"> <ul class="user-orgs">

View file

@ -23,7 +23,7 @@
<div class="content"> <div class="content">
<strong>{{.Name}}</strong> <strong>{{.Name}}</strong>
<div class="activity meta"> <div class="activity meta">
<i>{{$.locale.Tr "settings.add_on"}} <span>{{.CreatedUnix.FormatShort}}</span> — {{svg "octicon-info"}} {{if .HasUsed}}{{$.locale.Tr "settings.last_used"}} <span {{if .HasRecentActivity}}class="green"{{end}}>{{.UpdatedUnix.FormatShort}}</span>{{else}}{{$.locale.Tr "settings.no_activity"}}{{end}}</i> <i>{{$.locale.Tr "settings.add_on"}} <span><time data-format="short-date" datetime="{{.CreatedUnix.FormatLong}}">{{.CreatedUnix.FormatShort}}</time></span> — {{svg "octicon-info"}} {{if .HasUsed}}{{$.locale.Tr "settings.last_used"}} <span {{if .HasRecentActivity}}class="green"{{end}}><time data-format="short-date" datetime="{{.UpdatedUnix.FormatLong}}">{{.UpdatedUnix.FormatShort}}</time></span>{{else}}{{$.locale.Tr "settings.no_activity"}}{{end}}</i>
</div> </div>
</div> </div>
</div> </div>

View file

@ -20,7 +20,7 @@
<div class="content"> <div class="content">
<strong>{{$grant.Application.Name}}</strong> <strong>{{$grant.Application.Name}}</strong>
<div class="activity meta"> <div class="activity meta">
<i>{{$.locale.Tr "settings.add_on"}} <span>{{$grant.CreatedUnix.FormatShort}}</span></i> <i>{{$.locale.Tr "settings.add_on"}} <span><time data-format="short-date" datetime="{{$grant.CreatedUnix.FormatLong}}">{{$grant.CreatedUnix.FormatShort}}</time></span></i>
</div> </div>
</div> </div>
</div> </div>

View file

@ -68,9 +68,9 @@
<b>{{$.locale.Tr "settings.subkeys"}}:</b> {{range .SubsKey}} {{.PaddedKeyID}} {{end}} <b>{{$.locale.Tr "settings.subkeys"}}:</b> {{range .SubsKey}} {{.PaddedKeyID}} {{end}}
</div> </div>
<div class="activity meta"> <div class="activity meta">
<i>{{$.locale.Tr "settings.add_on"}} <span>{{.AddedUnix.FormatShort}}</span></i> <i>{{$.locale.Tr "settings.add_on"}} <span><time data-format="short-date" datetime="{{.AddedUnix.FormatLong}}">{{.AddedUnix.FormatShort}}</time></span></i>
- -
<i>{{if not .ExpiredUnix.IsZero}}{{$.locale.Tr "settings.valid_until"}} <span>{{.ExpiredUnix.FormatShort}}</span>{{else}}{{$.locale.Tr "settings.valid_forever"}}{{end}}</i> <i>{{if not .ExpiredUnix.IsZero}}{{$.locale.Tr "settings.valid_until"}} <span><time data-format="short-date" datetime="{{.ExpiredUnix.FormatLong}}">{{.ExpiredUnix.FormatShort}}</time></span>{{else}}{{$.locale.Tr "settings.valid_forever"}}{{end}}</i>
</div> </div>
</div> </div>
</div> </div>

View file

@ -25,7 +25,7 @@
<div class="content"> <div class="content">
<strong>{{.Name}}</strong> <strong>{{.Name}}</strong>
<div class="activity meta"> <div class="activity meta">
<i>{{$.locale.Tr "settings.add_on"}} <span>{{.CreatedUnix.FormatShort}}</span> — {{svg "octicon-info" 16}} {{if .HasUsed}}{{$.locale.Tr "settings.last_used"}} <span {{if .HasRecentActivity}}class="green"{{end}}>{{.UpdatedUnix.FormatShort}}</span>{{else}}{{$.locale.Tr "settings.no_activity"}}{{end}}</i> <i>{{$.locale.Tr "settings.add_on"}} <span><time data-format="short-date" datetime="{{.CreatedUnix.FormatLong}}">{{.CreatedUnix.FormatShort}}</time></span> — {{svg "octicon-info" 16}} {{if .HasUsed}}{{$.locale.Tr "settings.last_used"}} <span {{if .HasRecentActivity}}class="green"{{end}}><time data-format="short-date" datetime="{{.UpdatedUnix.FormatLong}}">{{.UpdatedUnix.FormatShort}}</time></span>{{else}}{{$.locale.Tr "settings.no_activity"}}{{end}}</i>
</div> </div>
</div> </div>
</div> </div>

View file

@ -58,7 +58,7 @@
{{.Fingerprint}} {{.Fingerprint}}
</div> </div>
<div class="activity meta"> <div class="activity meta">
<i>{{$.locale.Tr "settings.add_on"}} <span>{{.CreatedUnix.FormatShort}}</span> — {{svg "octicon-info"}} {{if .HasUsed}}{{$.locale.Tr "settings.last_used"}} <span {{if .HasRecentActivity}}class="green"{{end}}>{{.UpdatedUnix.FormatShort}}</span>{{else}}{{$.locale.Tr "settings.no_activity"}}{{end}}</i> <i>{{$.locale.Tr "settings.add_on"}} <span><time data-format="short-date" datetime="{{.CreatedUnix.FormatLong}}">{{.CreatedUnix.FormatShort}}</time></span> — {{svg "octicon-info"}} {{if .HasUsed}}{{$.locale.Tr "settings.last_used"}} <span {{if .HasRecentActivity}}class="green"{{end}}><time data-format="short-date" datetime="{{.UpdatedUnix.FormatLong}}">{{.UpdatedUnix.FormatShort}}</time></span>{{else}}{{$.locale.Tr "settings.no_activity"}}{{end}}</i>
</div> </div>
</div> </div>
</div> </div>

View file

@ -1,7 +1,10 @@
import {prettyNumber} from '../utils.js'; import {prettyNumber} from '../utils.js';
const {lang} = document.documentElement; const {lang} = document.documentElement;
const dateFormatter = new Intl.DateTimeFormat(lang, {year: 'numeric', month: 'long', day: 'numeric'}); const dateFormatter = new Intl.DateTimeFormat(lang, {year: 'numeric', month: 'long', day: 'numeric'});
const shortDateFormatter = new Intl.DateTimeFormat(lang, {year: 'numeric', month: 'short', day: 'numeric'});
const dateTimeFormatter = new Intl.DateTimeFormat(lang, {year: 'numeric', month: 'short', day: 'numeric', hour: 'numeric', minute: 'numeric', second: 'numeric'});
export function initFormattingReplacements() { export function initFormattingReplacements() {
// replace english formatted numbers with locale-specific separators // replace english formatted numbers with locale-specific separators
@ -13,9 +16,28 @@ export function initFormattingReplacements() {
} }
} }
// for each <time></time> tag, if it has the data-format="date" attribute, format // for each <time></time> tag, if it has the data-format attribute, format
// the text according to the user's chosen locale // the text according to the user's chosen locale and formatter.
for (const timeElement of document.querySelectorAll('time[data-format="date"]')) { formatAllTimeElements();
timeElement.textContent = dateFormatter.format(new Date(timeElement.dateTime)); }
function formatAllTimeElements() {
const timeElements = document.querySelectorAll('time[data-format]');
for (const timeElement of timeElements) {
const formatter = getFormatter(timeElement.dataset.format);
timeElement.textContent = formatter.format(new Date(timeElement.dateTime));
}
}
function getFormatter(format) {
switch (format) {
case 'date':
return dateFormatter;
case 'short-date':
return shortDateFormatter;
case 'date-time':
return dateTimeFormatter;
default:
throw new Error('Unknown format');
} }
} }