1
0
Fork 0
mirror of https://codeberg.org/forgejo/forgejo.git synced 2025-09-30 19:22:08 +00:00
forgejo/web_src/js/webcomponents/relative-time.js

241 lines
8.9 KiB
JavaScript
Raw Normal View History

Replace the 'relative-time' element scripting with custom, translatable rewrite (#6154) This is my take to fix #6078 Should also resolve #6111 As far as I can tell, Forgejo uses only a subset of the relative-time functionality, and as far as I can see, this subset can be implemented using browser built-in date conversion and arithmetic. So I wrote a JavaScript to format the relative-time element accordingly, and a Go binding to generate the translated elements. This is my first time writing Go code, and my first time coding for a large-scale server application, so please tell me if I'm doing something wrong, or if the whole approach is not acceptable. --- Screenshot: Localized times in Low German ![grafik](/attachments/6f787e17-e666-4b88-8599-af0b8357ffbe) Screenshot: The same with Forgejo in English ![grafik](/attachments/af09c873-b9f3-423d-b12b-7e62093e2623) --- ## Checklist The [contributor guide](https://forgejo.org/docs/next/contributor/) contains information that will be helpful to first time contributors. There also are a few [conditions for merging Pull Requests in Forgejo repositories](https://codeberg.org/forgejo/governance/src/branch/main/PullRequestsAgreement.md). You are also welcome to join the [Forgejo development chatroom](https://matrix.to/#/#forgejo-development:matrix.org). ### Tests - I added test coverage for Go changes... - [x] in their respective `*_test.go` for unit tests. - [ ] in the `tests/integration` directory if it involves interactions with a live Forgejo server. - I added test coverage for JavaScript changes... - [x] in `web_src/js/*.test.js` if it can be unit tested. - [ ] in `tests/e2e/*.test.e2e.js` if it requires interactions with a live Forgejo server (see also the [developer guide for JavaScript testing](https://codeberg.org/forgejo/forgejo/src/branch/forgejo/tests/e2e/README.md#end-to-end-tests)). ### Documentation - [ ] I created a pull request [to the documentation](https://codeberg.org/forgejo/docs) to explain to Forgejo users how to use this change. - [x] I did not document these changes and I do not expect someone else to do it. ### Release notes - [x] I do not want this change to show in the release notes. - [ ] I want the title to show in the release notes with a link to this pull request. - [ ] I want the content of the `release-notes/<pull request number>.md` to be be used for the release notes instead of the title. Reviewed-on: https://codeberg.org/forgejo/forgejo/pulls/6154 Reviewed-by: 0ko <0ko@noreply.codeberg.org> Reviewed-by: Michael Kriese <michael.kriese@gmx.de> Co-authored-by: Benedikt Straub <benedikt-straub@web.de> Co-committed-by: Benedikt Straub <benedikt-straub@web.de>
2025-05-03 14:11:01 +00:00
import dayjs from 'dayjs';
test: fix failure in relative-time tests in US/Canada DST-aware timezones (#8858) When running relative-time tests on my local system, the tests fail. I've verified this failure does occur when the local system time is configured to use these timezones: - America/Edmonton - America/Boise - America/Goose Bay But the tests pass if the local system time is in these timezones: - America/Phoenix (has no DST) - Europe/Berlin (different DST rules) "7 months ago" (the existing test case output) is accurate considering the offsets used in the test, but internally dayjs calculates it to be 7.99 months when the system TZ is in the "success" TZs, and 8.0001 months in the "failure" TZs. I believe the source of the difference is that... - dayjs takes timestamps with offsets, applies the offset, and then treats them as local times... - Summer Time ended on Oct 27 (the "now" date under test) in Europe, but did not end until November 3rd in the US/Canada... - In Europe there would have been one hour less between those local timestamps than there would be in US/Canada. My proposed fix is to perform the diff calculations after converting the timestamps to UTC values. This provides consistent successful output regardless of the local system TZ. As the tests are sensitive to system timezone, I've modified the CI build to run the frontend tests twice with two different TZ environment variables. This is a pretty coarse way to exercise the problem and I'm open to improvements. Reviewed-on: https://codeberg.org/forgejo/forgejo/pulls/8858 Reviewed-by: Gusted <gusted@noreply.codeberg.org> Co-authored-by: Mathieu Fenniak <mathieu@fenniak.net> Co-committed-by: Mathieu Fenniak <mathieu@fenniak.net>
2025-08-12 02:47:58 +02:00
import utc from 'dayjs/plugin/utc.js';
Replace the 'relative-time' element scripting with custom, translatable rewrite (#6154) This is my take to fix #6078 Should also resolve #6111 As far as I can tell, Forgejo uses only a subset of the relative-time functionality, and as far as I can see, this subset can be implemented using browser built-in date conversion and arithmetic. So I wrote a JavaScript to format the relative-time element accordingly, and a Go binding to generate the translated elements. This is my first time writing Go code, and my first time coding for a large-scale server application, so please tell me if I'm doing something wrong, or if the whole approach is not acceptable. --- Screenshot: Localized times in Low German ![grafik](/attachments/6f787e17-e666-4b88-8599-af0b8357ffbe) Screenshot: The same with Forgejo in English ![grafik](/attachments/af09c873-b9f3-423d-b12b-7e62093e2623) --- ## Checklist The [contributor guide](https://forgejo.org/docs/next/contributor/) contains information that will be helpful to first time contributors. There also are a few [conditions for merging Pull Requests in Forgejo repositories](https://codeberg.org/forgejo/governance/src/branch/main/PullRequestsAgreement.md). You are also welcome to join the [Forgejo development chatroom](https://matrix.to/#/#forgejo-development:matrix.org). ### Tests - I added test coverage for Go changes... - [x] in their respective `*_test.go` for unit tests. - [ ] in the `tests/integration` directory if it involves interactions with a live Forgejo server. - I added test coverage for JavaScript changes... - [x] in `web_src/js/*.test.js` if it can be unit tested. - [ ] in `tests/e2e/*.test.e2e.js` if it requires interactions with a live Forgejo server (see also the [developer guide for JavaScript testing](https://codeberg.org/forgejo/forgejo/src/branch/forgejo/tests/e2e/README.md#end-to-end-tests)). ### Documentation - [ ] I created a pull request [to the documentation](https://codeberg.org/forgejo/docs) to explain to Forgejo users how to use this change. - [x] I did not document these changes and I do not expect someone else to do it. ### Release notes - [x] I do not want this change to show in the release notes. - [ ] I want the title to show in the release notes with a link to this pull request. - [ ] I want the content of the `release-notes/<pull request number>.md` to be be used for the release notes instead of the title. Reviewed-on: https://codeberg.org/forgejo/forgejo/pulls/6154 Reviewed-by: 0ko <0ko@noreply.codeberg.org> Reviewed-by: Michael Kriese <michael.kriese@gmx.de> Co-authored-by: Benedikt Straub <benedikt-straub@web.de> Co-committed-by: Benedikt Straub <benedikt-straub@web.de>
2025-05-03 14:11:01 +00:00
const {pageData} = window.config;
test: fix failure in relative-time tests in US/Canada DST-aware timezones (#8858) When running relative-time tests on my local system, the tests fail. I've verified this failure does occur when the local system time is configured to use these timezones: - America/Edmonton - America/Boise - America/Goose Bay But the tests pass if the local system time is in these timezones: - America/Phoenix (has no DST) - Europe/Berlin (different DST rules) "7 months ago" (the existing test case output) is accurate considering the offsets used in the test, but internally dayjs calculates it to be 7.99 months when the system TZ is in the "success" TZs, and 8.0001 months in the "failure" TZs. I believe the source of the difference is that... - dayjs takes timestamps with offsets, applies the offset, and then treats them as local times... - Summer Time ended on Oct 27 (the "now" date under test) in Europe, but did not end until November 3rd in the US/Canada... - In Europe there would have been one hour less between those local timestamps than there would be in US/Canada. My proposed fix is to perform the diff calculations after converting the timestamps to UTC values. This provides consistent successful output regardless of the local system TZ. As the tests are sensitive to system timezone, I've modified the CI build to run the frontend tests twice with two different TZ environment variables. This is a pretty coarse way to exercise the problem and I'm open to improvements. Reviewed-on: https://codeberg.org/forgejo/forgejo/pulls/8858 Reviewed-by: Gusted <gusted@noreply.codeberg.org> Co-authored-by: Mathieu Fenniak <mathieu@fenniak.net> Co-committed-by: Mathieu Fenniak <mathieu@fenniak.net>
2025-08-12 02:47:58 +02:00
dayjs.extend(utc);
Replace the 'relative-time' element scripting with custom, translatable rewrite (#6154) This is my take to fix #6078 Should also resolve #6111 As far as I can tell, Forgejo uses only a subset of the relative-time functionality, and as far as I can see, this subset can be implemented using browser built-in date conversion and arithmetic. So I wrote a JavaScript to format the relative-time element accordingly, and a Go binding to generate the translated elements. This is my first time writing Go code, and my first time coding for a large-scale server application, so please tell me if I'm doing something wrong, or if the whole approach is not acceptable. --- Screenshot: Localized times in Low German ![grafik](/attachments/6f787e17-e666-4b88-8599-af0b8357ffbe) Screenshot: The same with Forgejo in English ![grafik](/attachments/af09c873-b9f3-423d-b12b-7e62093e2623) --- ## Checklist The [contributor guide](https://forgejo.org/docs/next/contributor/) contains information that will be helpful to first time contributors. There also are a few [conditions for merging Pull Requests in Forgejo repositories](https://codeberg.org/forgejo/governance/src/branch/main/PullRequestsAgreement.md). You are also welcome to join the [Forgejo development chatroom](https://matrix.to/#/#forgejo-development:matrix.org). ### Tests - I added test coverage for Go changes... - [x] in their respective `*_test.go` for unit tests. - [ ] in the `tests/integration` directory if it involves interactions with a live Forgejo server. - I added test coverage for JavaScript changes... - [x] in `web_src/js/*.test.js` if it can be unit tested. - [ ] in `tests/e2e/*.test.e2e.js` if it requires interactions with a live Forgejo server (see also the [developer guide for JavaScript testing](https://codeberg.org/forgejo/forgejo/src/branch/forgejo/tests/e2e/README.md#end-to-end-tests)). ### Documentation - [ ] I created a pull request [to the documentation](https://codeberg.org/forgejo/docs) to explain to Forgejo users how to use this change. - [x] I did not document these changes and I do not expect someone else to do it. ### Release notes - [x] I do not want this change to show in the release notes. - [ ] I want the title to show in the release notes with a link to this pull request. - [ ] I want the content of the `release-notes/<pull request number>.md` to be be used for the release notes instead of the title. Reviewed-on: https://codeberg.org/forgejo/forgejo/pulls/6154 Reviewed-by: 0ko <0ko@noreply.codeberg.org> Reviewed-by: Michael Kriese <michael.kriese@gmx.de> Co-authored-by: Benedikt Straub <benedikt-straub@web.de> Co-committed-by: Benedikt Straub <benedikt-straub@web.de>
2025-05-03 14:11:01 +00:00
export const HALF_MINUTE = 30 * 1000;
export const ONE_MINUTE = 60 * 1000;
export const ONE_HOUR = 60 * ONE_MINUTE;
export const ONE_DAY = 24 * ONE_HOUR;
const ABSOLUTE_DATETIME_FORMAT = new Intl.DateTimeFormat(navigator.language, {
year: 'numeric',
month: 'short',
day: 'numeric',
hour: '2-digit',
minute: '2-digit',
timeZoneName: 'short',
});
const FALLBACK_DATETIME_FORMAT = new Intl.RelativeTimeFormat(navigator.language, {style: 'long'});
frontend: generic lazy loader for webcomponents (#8510) After seeing #8111 use a webcomponent, I think that they are a neat usecase for Forgejo where most of the frontend is backend-generated, with some "island of enhancements". I am considering using a webcomponent for the CITATION management (last occurrence of [`Blob.GetBlobContent`](https://codeberg.org/forgejo/forgejo/issues/8222)), however I noticed that the developer experience wasn't ideal. With this PR it would be very easy to declare a webcomponent, which will be loaded only if needed (I converted `model-viewer` and `pdf-object` to this technique). Some cleanup in the neighbor webcomponents. ## Testing 1) Create a new repository or use an existing one. 2) Upload a `.pdf` or `.glb` file (such as https://codeberg.org/forgejo/forgejo/src/branch/forgejo/tests/testdata/data/viewer/Unicode%E2%9D%A4%E2%99%BBTest.glb) 3) Open the Network inspector and view the file in the repository. - After a short loading spinner, the PDF or 3D model should be rendered in a viewer - the related JS should have been loaded (e.g. http://localhost:3000/assets/js/model-viewer.494bf0cd.js) - visiting another page and check that this JS file isn't loaded Reviewed-on: https://codeberg.org/forgejo/forgejo/pulls/8510 Reviewed-by: Gusted <gusted@noreply.codeberg.org> Reviewed-by: 0ko <0ko@noreply.codeberg.org> Reviewed-by: Beowulf <beowulf@beocode.eu> Co-authored-by: oliverpool <git@olivier.pfad.fr> Co-committed-by: oliverpool <git@olivier.pfad.fr>
2025-07-23 04:10:50 +02:00
/**
* A list of plural rules for all languages.
* `plural_rules.go` defines the index for each of the 14 known plural rules.
*
* `pageData.PLURAL_RULE_LANG` is the index of the plural rule for the current language.
* `pageData.PLURAL_RULE_FALLBACK` is the index of the plural rule for the default language,
* to be used when a string is not translated in the current language.
*
* Each plural rule is a function that maps an amount `n` to the appropriate plural form index.
* Which index means which rule is specific for each language and also defined in `plural_rules.go`.
* The actual strings are in `pageData.PLURALSTRINGS_LANG` and `pageData.PLURALSTRINGS_FALLBACK`
* respectively, which is an array indexed by the plural form index.
*
* Links to the language plural rule and form definitions:
* https://codeberg.org/forgejo/forgejo/src/branch/forgejo/modules/translation/plural_rules.go
* https://www.unicode.org/cldr/charts/46/supplemental/language_plural_rules.html
* https://translate.codeberg.org/languages/$LANGUAGE_CODE/#information
* https://github.com/WeblateOrg/language-data/blob/main/languages.csv
*/
const PLURAL_RULES = [
// [ 0] Common 2-form, e.g. English, German
function (n) { return n !== 1 ? 1 : 0 },
// [ 1] Bengali 2-form
function (n) { return n > 1 ? 1 : 0 },
// [ 2] Icelandic 2-form
function (n) { return n % 10 !== 1 || n % 100 === 11 ? 1 : 0 },
// [ 3] Filipino 2-form
function (n) { return n !== 1 && n !== 2 && n !== 3 && (n % 10 === 4 || n % 10 === 6 || n % 10 === 9) ? 1 : 0 },
// [ 4] One form
function (_) { return 0 },
// [ 5] Czech 3-form
function (n) { return (n === 1) ? 0 : (n >= 2 && n <= 4) ? 1 : 2 },
// [ 6] Russian 3-form
function (n) { return n % 10 === 1 && n % 100 !== 11 ? 0 : n % 10 >= 2 && n % 10 <= 4 && (n % 100 < 10 || n % 100 >= 20) ? 1 : 2 },
// [ 7] Polish 3-form
function (n) { return n === 1 ? 0 : n % 10 >= 2 && n % 10 <= 4 && (n % 100 < 10 || n % 100 >= 20) ? 1 : 2 },
// [ 8] Latvian 3-form
function (n) { return (n % 10 === 0 || n % 100 >= 11 && n % 100 <= 19) ? 0 : ((n % 10 === 1 && n % 100 !== 11) ? 1 : 2) },
// [ 9] Lithunian 3-form
function (n) { return (n % 10 === 1 && (n % 100 < 11 || n % 100 > 19)) ? 0 : ((n % 10 >= 2 && n % 10 <= 9 && (n % 100 < 11 || n % 100 > 19)) ? 1 : 2) },
// [10] French 3-form
function (n) { return (n === 0 || n === 1) ? 0 : ((n !== 0 && n % 1000000 === 0) ? 1 : 2) },
// [11] Catalan 3-form
function (n) { return (n === 1) ? 0 : ((n !== 0 && n % 1000000 === 0) ? 1 : 2) },
// [12] Slovenian 4-form
function (n) { return n % 100 === 1 ? 0 : n % 100 === 2 ? 1 : n % 100 === 3 || n % 100 === 4 ? 2 : 3 },
// [13] Arabic 6-form
function (n) { return n === 0 ? 0 : n === 1 ? 1 : n === 2 ? 2 : n % 100 >= 3 && n % 100 <= 10 ? 3 : n % 100 >= 11 ? 4 : 5 },
];
/**
* Look up the correct localized plural form for amount `n` for the string with the translation key `key`.
* If the current language does not contain a translation for this key, fallback to the browser's formatting.
*/
Replace the 'relative-time' element scripting with custom, translatable rewrite (#6154) This is my take to fix #6078 Should also resolve #6111 As far as I can tell, Forgejo uses only a subset of the relative-time functionality, and as far as I can see, this subset can be implemented using browser built-in date conversion and arithmetic. So I wrote a JavaScript to format the relative-time element accordingly, and a Go binding to generate the translated elements. This is my first time writing Go code, and my first time coding for a large-scale server application, so please tell me if I'm doing something wrong, or if the whole approach is not acceptable. --- Screenshot: Localized times in Low German ![grafik](/attachments/6f787e17-e666-4b88-8599-af0b8357ffbe) Screenshot: The same with Forgejo in English ![grafik](/attachments/af09c873-b9f3-423d-b12b-7e62093e2623) --- ## Checklist The [contributor guide](https://forgejo.org/docs/next/contributor/) contains information that will be helpful to first time contributors. There also are a few [conditions for merging Pull Requests in Forgejo repositories](https://codeberg.org/forgejo/governance/src/branch/main/PullRequestsAgreement.md). You are also welcome to join the [Forgejo development chatroom](https://matrix.to/#/#forgejo-development:matrix.org). ### Tests - I added test coverage for Go changes... - [x] in their respective `*_test.go` for unit tests. - [ ] in the `tests/integration` directory if it involves interactions with a live Forgejo server. - I added test coverage for JavaScript changes... - [x] in `web_src/js/*.test.js` if it can be unit tested. - [ ] in `tests/e2e/*.test.e2e.js` if it requires interactions with a live Forgejo server (see also the [developer guide for JavaScript testing](https://codeberg.org/forgejo/forgejo/src/branch/forgejo/tests/e2e/README.md#end-to-end-tests)). ### Documentation - [ ] I created a pull request [to the documentation](https://codeberg.org/forgejo/docs) to explain to Forgejo users how to use this change. - [x] I did not document these changes and I do not expect someone else to do it. ### Release notes - [x] I do not want this change to show in the release notes. - [ ] I want the title to show in the release notes with a link to this pull request. - [ ] I want the content of the `release-notes/<pull request number>.md` to be be used for the release notes instead of the title. Reviewed-on: https://codeberg.org/forgejo/forgejo/pulls/6154 Reviewed-by: 0ko <0ko@noreply.codeberg.org> Reviewed-by: Michael Kriese <michael.kriese@gmx.de> Co-authored-by: Benedikt Straub <benedikt-straub@web.de> Co-committed-by: Benedikt Straub <benedikt-straub@web.de>
2025-05-03 14:11:01 +00:00
function GetPluralizedStringOrFallback(key, n, unit) {
frontend: generic lazy loader for webcomponents (#8510) After seeing #8111 use a webcomponent, I think that they are a neat usecase for Forgejo where most of the frontend is backend-generated, with some "island of enhancements". I am considering using a webcomponent for the CITATION management (last occurrence of [`Blob.GetBlobContent`](https://codeberg.org/forgejo/forgejo/issues/8222)), however I noticed that the developer experience wasn't ideal. With this PR it would be very easy to declare a webcomponent, which will be loaded only if needed (I converted `model-viewer` and `pdf-object` to this technique). Some cleanup in the neighbor webcomponents. ## Testing 1) Create a new repository or use an existing one. 2) Upload a `.pdf` or `.glb` file (such as https://codeberg.org/forgejo/forgejo/src/branch/forgejo/tests/testdata/data/viewer/Unicode%E2%9D%A4%E2%99%BBTest.glb) 3) Open the Network inspector and view the file in the repository. - After a short loading spinner, the PDF or 3D model should be rendered in a viewer - the related JS should have been loaded (e.g. http://localhost:3000/assets/js/model-viewer.494bf0cd.js) - visiting another page and check that this JS file isn't loaded Reviewed-on: https://codeberg.org/forgejo/forgejo/pulls/8510 Reviewed-by: Gusted <gusted@noreply.codeberg.org> Reviewed-by: 0ko <0ko@noreply.codeberg.org> Reviewed-by: Beowulf <beowulf@beocode.eu> Co-authored-by: oliverpool <git@olivier.pfad.fr> Co-committed-by: oliverpool <git@olivier.pfad.fr>
2025-07-23 04:10:50 +02:00
const translation = pageData.PLURALSTRINGS_LANG[key]?.[PLURAL_RULES[pageData.PLURAL_RULE_LANG](n)];
Replace the 'relative-time' element scripting with custom, translatable rewrite (#6154) This is my take to fix #6078 Should also resolve #6111 As far as I can tell, Forgejo uses only a subset of the relative-time functionality, and as far as I can see, this subset can be implemented using browser built-in date conversion and arithmetic. So I wrote a JavaScript to format the relative-time element accordingly, and a Go binding to generate the translated elements. This is my first time writing Go code, and my first time coding for a large-scale server application, so please tell me if I'm doing something wrong, or if the whole approach is not acceptable. --- Screenshot: Localized times in Low German ![grafik](/attachments/6f787e17-e666-4b88-8599-af0b8357ffbe) Screenshot: The same with Forgejo in English ![grafik](/attachments/af09c873-b9f3-423d-b12b-7e62093e2623) --- ## Checklist The [contributor guide](https://forgejo.org/docs/next/contributor/) contains information that will be helpful to first time contributors. There also are a few [conditions for merging Pull Requests in Forgejo repositories](https://codeberg.org/forgejo/governance/src/branch/main/PullRequestsAgreement.md). You are also welcome to join the [Forgejo development chatroom](https://matrix.to/#/#forgejo-development:matrix.org). ### Tests - I added test coverage for Go changes... - [x] in their respective `*_test.go` for unit tests. - [ ] in the `tests/integration` directory if it involves interactions with a live Forgejo server. - I added test coverage for JavaScript changes... - [x] in `web_src/js/*.test.js` if it can be unit tested. - [ ] in `tests/e2e/*.test.e2e.js` if it requires interactions with a live Forgejo server (see also the [developer guide for JavaScript testing](https://codeberg.org/forgejo/forgejo/src/branch/forgejo/tests/e2e/README.md#end-to-end-tests)). ### Documentation - [ ] I created a pull request [to the documentation](https://codeberg.org/forgejo/docs) to explain to Forgejo users how to use this change. - [x] I did not document these changes and I do not expect someone else to do it. ### Release notes - [x] I do not want this change to show in the release notes. - [ ] I want the title to show in the release notes with a link to this pull request. - [ ] I want the content of the `release-notes/<pull request number>.md` to be be used for the release notes instead of the title. Reviewed-on: https://codeberg.org/forgejo/forgejo/pulls/6154 Reviewed-by: 0ko <0ko@noreply.codeberg.org> Reviewed-by: Michael Kriese <michael.kriese@gmx.de> Co-authored-by: Benedikt Straub <benedikt-straub@web.de> Co-committed-by: Benedikt Straub <benedikt-straub@web.de>
2025-05-03 14:11:01 +00:00
if (translation) return translation.replace('%d', n);
return FALLBACK_DATETIME_FORMAT.format(-n, unit);
}
/**
* Update the displayed text of the given relative-time DOM element with its
* human-readable, localized relative time string.
* Returns the recommended interval in milliseconds until the object should be updated again,
* or null if the object is invalid.
*/
export function DoUpdateRelativeTime(object, now) {
const absoluteTime = object.getAttribute('datetime');
if (!absoluteTime) {
return null; // Object does not contain a datetime.
}
if (!now) now = Date.now();
test: fix failure in relative-time tests in US/Canada DST-aware timezones (#8858) When running relative-time tests on my local system, the tests fail. I've verified this failure does occur when the local system time is configured to use these timezones: - America/Edmonton - America/Boise - America/Goose Bay But the tests pass if the local system time is in these timezones: - America/Phoenix (has no DST) - Europe/Berlin (different DST rules) "7 months ago" (the existing test case output) is accurate considering the offsets used in the test, but internally dayjs calculates it to be 7.99 months when the system TZ is in the "success" TZs, and 8.0001 months in the "failure" TZs. I believe the source of the difference is that... - dayjs takes timestamps with offsets, applies the offset, and then treats them as local times... - Summer Time ended on Oct 27 (the "now" date under test) in Europe, but did not end until November 3rd in the US/Canada... - In Europe there would have been one hour less between those local timestamps than there would be in US/Canada. My proposed fix is to perform the diff calculations after converting the timestamps to UTC values. This provides consistent successful output regardless of the local system TZ. As the tests are sensitive to system timezone, I've modified the CI build to run the frontend tests twice with two different TZ environment variables. This is a pretty coarse way to exercise the problem and I'm open to improvements. Reviewed-on: https://codeberg.org/forgejo/forgejo/pulls/8858 Reviewed-by: Gusted <gusted@noreply.codeberg.org> Co-authored-by: Mathieu Fenniak <mathieu@fenniak.net> Co-committed-by: Mathieu Fenniak <mathieu@fenniak.net>
2025-08-12 02:47:58 +02:00
const nowJS = dayjs.utc(now);
const thenJS = dayjs.utc(absoluteTime);
Replace the 'relative-time' element scripting with custom, translatable rewrite (#6154) This is my take to fix #6078 Should also resolve #6111 As far as I can tell, Forgejo uses only a subset of the relative-time functionality, and as far as I can see, this subset can be implemented using browser built-in date conversion and arithmetic. So I wrote a JavaScript to format the relative-time element accordingly, and a Go binding to generate the translated elements. This is my first time writing Go code, and my first time coding for a large-scale server application, so please tell me if I'm doing something wrong, or if the whole approach is not acceptable. --- Screenshot: Localized times in Low German ![grafik](/attachments/6f787e17-e666-4b88-8599-af0b8357ffbe) Screenshot: The same with Forgejo in English ![grafik](/attachments/af09c873-b9f3-423d-b12b-7e62093e2623) --- ## Checklist The [contributor guide](https://forgejo.org/docs/next/contributor/) contains information that will be helpful to first time contributors. There also are a few [conditions for merging Pull Requests in Forgejo repositories](https://codeberg.org/forgejo/governance/src/branch/main/PullRequestsAgreement.md). You are also welcome to join the [Forgejo development chatroom](https://matrix.to/#/#forgejo-development:matrix.org). ### Tests - I added test coverage for Go changes... - [x] in their respective `*_test.go` for unit tests. - [ ] in the `tests/integration` directory if it involves interactions with a live Forgejo server. - I added test coverage for JavaScript changes... - [x] in `web_src/js/*.test.js` if it can be unit tested. - [ ] in `tests/e2e/*.test.e2e.js` if it requires interactions with a live Forgejo server (see also the [developer guide for JavaScript testing](https://codeberg.org/forgejo/forgejo/src/branch/forgejo/tests/e2e/README.md#end-to-end-tests)). ### Documentation - [ ] I created a pull request [to the documentation](https://codeberg.org/forgejo/docs) to explain to Forgejo users how to use this change. - [x] I did not document these changes and I do not expect someone else to do it. ### Release notes - [x] I do not want this change to show in the release notes. - [ ] I want the title to show in the release notes with a link to this pull request. - [ ] I want the content of the `release-notes/<pull request number>.md` to be be used for the release notes instead of the title. Reviewed-on: https://codeberg.org/forgejo/forgejo/pulls/6154 Reviewed-by: 0ko <0ko@noreply.codeberg.org> Reviewed-by: Michael Kriese <michael.kriese@gmx.de> Co-authored-by: Benedikt Straub <benedikt-straub@web.de> Co-committed-by: Benedikt Straub <benedikt-straub@web.de>
2025-05-03 14:11:01 +00:00
object.setAttribute('data-tooltip-content', ABSOLUTE_DATETIME_FORMAT.format(thenJS.toDate()));
if (nowJS.isBefore(thenJS)) {
// Datetime is in the future.
object.textContent = pageData.DATETIMESTRINGS.FUTURE;
return -Math.floor(nowJS.diff(thenJS, 'millisecond'));
}
const years = Math.floor(nowJS.diff(thenJS, 'year'));
if (years >= 1) {
// Datetime is at least one year ago.
if (years === 1 && pageData.DATETIMESTRINGS['relativetime.1year']) {
// Datetime is one year ago.
object.textContent = pageData.DATETIMESTRINGS['relativetime.1year'];
} else if (years === 2 && pageData.DATETIMESTRINGS['relativetime.2years']) {
// Datetime is two year ago.
object.textContent = pageData.DATETIMESTRINGS['relativetime.2years'];
} else {
// Datetime is more than a year ago.
object.textContent = GetPluralizedStringOrFallback('relativetime.years', years, 'year');
}
return ONE_DAY;
}
const months = Math.floor(nowJS.diff(thenJS, 'month'));
if (months >= 1) {
// Datetime is at least one month but less than a year ago.
if (months === 1 && pageData.DATETIMESTRINGS['relativetime.1month']) {
// Datetime is one month ago.
object.textContent = pageData.DATETIMESTRINGS['relativetime.1month'];
} else if (months === 2 && pageData.DATETIMESTRINGS['relativetime.2months']) {
// Datetime is two months ago.
object.textContent = pageData.DATETIMESTRINGS['relativetime.2months'];
} else {
// Datetime is several months ago (but less than a year).
object.textContent = GetPluralizedStringOrFallback('relativetime.months', months, 'month');
}
return ONE_DAY;
}
const weeks = Math.floor(nowJS.diff(thenJS, 'week'));
if (weeks >= 1) {
// Datetime is at least one week but less than a month ago.
if (weeks === 1 && pageData.DATETIMESTRINGS['relativetime.1week']) {
// Datetime is one week ago.
object.textContent = pageData.DATETIMESTRINGS['relativetime.1week'];
} else if (weeks === 2 && pageData.DATETIMESTRINGS['relativetime.2weeks']) {
// Datetime is two week ago.
object.textContent = pageData.DATETIMESTRINGS['relativetime.2weeks'];
} else {
// Datetime is several weeks ago (but less than a month).
object.textContent = GetPluralizedStringOrFallback('relativetime.weeks', weeks, 'week');
}
return ONE_DAY;
}
const days = Math.floor(nowJS.diff(thenJS, 'day'));
if (days >= 1) {
if (days === 1 && pageData.DATETIMESTRINGS['relativetime.1day']) {
// Datetime is one day ago.
object.textContent = pageData.DATETIMESTRINGS['relativetime.1day'];
} else if (days === 2 && pageData.DATETIMESTRINGS['relativetime.2days']) {
// Datetime is two days ago.
object.textContent = pageData.DATETIMESTRINGS['relativetime.2days'];
} else {
// Datetime is several days but less than a week ago.
object.textContent = GetPluralizedStringOrFallback('relativetime.days', days, 'day');
}
return ONE_DAY;
}
const hours = Math.floor(nowJS.diff(thenJS, 'hour'));
if (hours >= 1) {
// Datetime is one or more hours but less than a day ago.
object.textContent = GetPluralizedStringOrFallback('relativetime.hours', hours, 'hour');
return ONE_HOUR;
}
const minutes = Math.floor(nowJS.diff(thenJS, 'minute'));
if (minutes >= 1) {
// Datetime is one or more minutes but less than an hour ago.
object.textContent = GetPluralizedStringOrFallback('relativetime.mins', minutes, 'minute');
return ONE_MINUTE;
}
// Datetime is very recent.
object.textContent = pageData.DATETIMESTRINGS.NOW;
return HALF_MINUTE;
}
window.customElements.define('relative-time', class extends HTMLElement {
static observedAttributes = ['datetime'];
Replace the 'relative-time' element scripting with custom, translatable rewrite (#6154) This is my take to fix #6078 Should also resolve #6111 As far as I can tell, Forgejo uses only a subset of the relative-time functionality, and as far as I can see, this subset can be implemented using browser built-in date conversion and arithmetic. So I wrote a JavaScript to format the relative-time element accordingly, and a Go binding to generate the translated elements. This is my first time writing Go code, and my first time coding for a large-scale server application, so please tell me if I'm doing something wrong, or if the whole approach is not acceptable. --- Screenshot: Localized times in Low German ![grafik](/attachments/6f787e17-e666-4b88-8599-af0b8357ffbe) Screenshot: The same with Forgejo in English ![grafik](/attachments/af09c873-b9f3-423d-b12b-7e62093e2623) --- ## Checklist The [contributor guide](https://forgejo.org/docs/next/contributor/) contains information that will be helpful to first time contributors. There also are a few [conditions for merging Pull Requests in Forgejo repositories](https://codeberg.org/forgejo/governance/src/branch/main/PullRequestsAgreement.md). You are also welcome to join the [Forgejo development chatroom](https://matrix.to/#/#forgejo-development:matrix.org). ### Tests - I added test coverage for Go changes... - [x] in their respective `*_test.go` for unit tests. - [ ] in the `tests/integration` directory if it involves interactions with a live Forgejo server. - I added test coverage for JavaScript changes... - [x] in `web_src/js/*.test.js` if it can be unit tested. - [ ] in `tests/e2e/*.test.e2e.js` if it requires interactions with a live Forgejo server (see also the [developer guide for JavaScript testing](https://codeberg.org/forgejo/forgejo/src/branch/forgejo/tests/e2e/README.md#end-to-end-tests)). ### Documentation - [ ] I created a pull request [to the documentation](https://codeberg.org/forgejo/docs) to explain to Forgejo users how to use this change. - [x] I did not document these changes and I do not expect someone else to do it. ### Release notes - [x] I do not want this change to show in the release notes. - [ ] I want the title to show in the release notes with a link to this pull request. - [ ] I want the content of the `release-notes/<pull request number>.md` to be be used for the release notes instead of the title. Reviewed-on: https://codeberg.org/forgejo/forgejo/pulls/6154 Reviewed-by: 0ko <0ko@noreply.codeberg.org> Reviewed-by: Michael Kriese <michael.kriese@gmx.de> Co-authored-by: Benedikt Straub <benedikt-straub@web.de> Co-committed-by: Benedikt Straub <benedikt-straub@web.de>
2025-05-03 14:11:01 +00:00
alive = false;
contentSpan = null;
update = (recurring) => {
if (!this.alive) return;
if (!this.shadowRoot) {
this.attachShadow({mode: 'open'});
this.contentSpan = document.createElement('span');
this.shadowRoot.append(this.contentSpan);
}
const next = DoUpdateRelativeTime(this);
if (recurring && next !== null) setTimeout(() => { this.update(true) }, next);
};
connectedCallback() {
this.alive = true;
this.update(true);
}
disconnectedCallback() {
this.alive = false;
}
Replace the 'relative-time' element scripting with custom, translatable rewrite (#6154) This is my take to fix #6078 Should also resolve #6111 As far as I can tell, Forgejo uses only a subset of the relative-time functionality, and as far as I can see, this subset can be implemented using browser built-in date conversion and arithmetic. So I wrote a JavaScript to format the relative-time element accordingly, and a Go binding to generate the translated elements. This is my first time writing Go code, and my first time coding for a large-scale server application, so please tell me if I'm doing something wrong, or if the whole approach is not acceptable. --- Screenshot: Localized times in Low German ![grafik](/attachments/6f787e17-e666-4b88-8599-af0b8357ffbe) Screenshot: The same with Forgejo in English ![grafik](/attachments/af09c873-b9f3-423d-b12b-7e62093e2623) --- ## Checklist The [contributor guide](https://forgejo.org/docs/next/contributor/) contains information that will be helpful to first time contributors. There also are a few [conditions for merging Pull Requests in Forgejo repositories](https://codeberg.org/forgejo/governance/src/branch/main/PullRequestsAgreement.md). You are also welcome to join the [Forgejo development chatroom](https://matrix.to/#/#forgejo-development:matrix.org). ### Tests - I added test coverage for Go changes... - [x] in their respective `*_test.go` for unit tests. - [ ] in the `tests/integration` directory if it involves interactions with a live Forgejo server. - I added test coverage for JavaScript changes... - [x] in `web_src/js/*.test.js` if it can be unit tested. - [ ] in `tests/e2e/*.test.e2e.js` if it requires interactions with a live Forgejo server (see also the [developer guide for JavaScript testing](https://codeberg.org/forgejo/forgejo/src/branch/forgejo/tests/e2e/README.md#end-to-end-tests)). ### Documentation - [ ] I created a pull request [to the documentation](https://codeberg.org/forgejo/docs) to explain to Forgejo users how to use this change. - [x] I did not document these changes and I do not expect someone else to do it. ### Release notes - [x] I do not want this change to show in the release notes. - [ ] I want the title to show in the release notes with a link to this pull request. - [ ] I want the content of the `release-notes/<pull request number>.md` to be be used for the release notes instead of the title. Reviewed-on: https://codeberg.org/forgejo/forgejo/pulls/6154 Reviewed-by: 0ko <0ko@noreply.codeberg.org> Reviewed-by: Michael Kriese <michael.kriese@gmx.de> Co-authored-by: Benedikt Straub <benedikt-straub@web.de> Co-committed-by: Benedikt Straub <benedikt-straub@web.de>
2025-05-03 14:11:01 +00:00
attributeChangedCallback(name, oldValue, newValue) {
if (name === 'datetime' && oldValue !== newValue) this.update(false);
}
set textContent(value) {
if (this.contentSpan) this.contentSpan.textContent = value;
}
get textContent() {
return this.contentSpan?.textContent;
}
});