diff --git a/docs/API-Reference/command/Commands.md b/docs/API-Reference/command/Commands.md
index e188a3ba43..7b525d1a55 100644
--- a/docs/API-Reference/command/Commands.md
+++ b/docs/API-Reference/command/Commands.md
@@ -476,6 +476,18 @@ Zooms out the editor view
## VIEW\_ZOOM\_SUBMENU
Submenu for zoom options
+**Kind**: global variable
+
+
+## OPEN\_IN\_SUBMENU
+Submenu for Open in project context menu
+
+**Kind**: global variable
+
+
+## OPEN\_IN\_SUBMENU\_WS
+Submenu for Open in working set context menu
+
**Kind**: global variable
@@ -602,6 +614,24 @@ Shows current file in file tree
## NAVIGATE\_SHOW\_IN\_OS
Shows current file in OS file explorer
+**Kind**: global variable
+
+
+## NAVIGATE\_OPEN\_IN\_TERMINAL
+Shows current file in OS Terminal
+
+**Kind**: global variable
+
+
+## NAVIGATE\_OPEN\_IN\_POWERSHELL
+Shows current file in open powershell in Windows os
+
+**Kind**: global variable
+
+
+## NAVIGATE\_OPEN\_IN\_DEFAULT\_APP
+Open current file in the default associated app in the os
+
**Kind**: global variable
diff --git a/docs/API-Reference/utils/LocalizationUtils.md b/docs/API-Reference/utils/LocalizationUtils.md
new file mode 100644
index 0000000000..2b9cb0035d
--- /dev/null
+++ b/docs/API-Reference/utils/LocalizationUtils.md
@@ -0,0 +1,63 @@
+### Import :
+```js
+const LocalizationUtils = brackets.getModule("utils/LocalizationUtils")
+```
+
+
+
+## getLocalizedLabel(locale) ⇒ string
+Converts a language code to its written name, if possible.
+If not possible, the language code is simply returned.
+
+**Kind**: global function
+**Returns**: string - The language's name or the given language code
+
+| Param | Type | Description |
+| --- | --- | --- |
+| locale | string | The two-char language code |
+
+
+
+## getFormattedDateTime([date], [lang], [dateTimeFormat]) ⇒ string
+Formats a given date object into a locale-aware date and time string.
+
+**Kind**: global function
+**Returns**: string - - The formatted date and time string (e.g., "Dec 24, 2024, 10:30 AM").
+
+| Param | Type | Description |
+| --- | --- | --- |
+| [date] | Date | The date object to format. If not provided, the current date and time will be used. |
+| [lang] | string | Optional language code to use for formatting (e.g., 'en', 'fr'). If not provided, defaults to the application locale or 'en'. |
+| [dateTimeFormat] | Object | Optional object specifying the date and time formatting options. Defaults to { dateStyle: 'medium', timeStyle: 'short' }. |
+| [dateTimeFormat.dateStyle] | string | Specifies the date format style. One of: DATE_TIME_STYLE.* |
+| [dateTimeFormat.timeStyle] | string | Specifies the time format style. One of: DATE_TIME_STYLE.* |
+
+
+
+## dateTimeFromNow([date], [lang]) ⇒ string
+Returns a relative time string (e.g., "2 days ago", "in 3 hours") based on the difference between the given date and now.
+
+**Kind**: global function
+**Returns**: string - - A human-readable relative time string (e.g., "2 days ago", "in 3 hours").
+
+| Param | Type | Description |
+| --- | --- | --- |
+| [date] | Date | The date to compare with the current date and time. If not given, defaults to now. |
+| [lang] | string | Optional language code to use for formatting (e.g., 'en', 'fr'). If not provided, defaults to the application locale or 'en'. |
+
+
+
+## dateTimeFromNowFriendly(date, [lang]) ⇒ string
+Returns an intelligent date string.
+- For dates within the last 30 days or the future: relative time (e.g., "2 days ago", "in 3 hours").
+- For dates earlier this year: formatted date (e.g., "Jan 5").
+- For dates not in the current year: formatted date with year (e.g., "Jan 5, 2023").
+
+**Kind**: global function
+**Returns**: string - - An intelligently formatted date string.
+
+| Param | Type | Description |
+| --- | --- | --- |
+| date | Date | The date to compare and format. |
+| [lang] | string | Optional language code to use for formatting (e.g., 'en', 'fr'). |
+
diff --git a/docs/API-Reference/utils/NodeUtils.md b/docs/API-Reference/utils/NodeUtils.md
index c23cebc8ef..36a415cd6c 100644
--- a/docs/API-Reference/utils/NodeUtils.md
+++ b/docs/API-Reference/utils/NodeUtils.md
@@ -82,3 +82,28 @@ This is only available in the native app
| fullFilePath | string |
| projectFullPath | string |
+
+
+## openNativeTerminal(cwd, [usePowerShell])
+Runs ESLint on a file
+This is only available in the native app
+
+**Kind**: global function
+
+| Param | Type | Default | Description |
+| --- | --- | --- | --- |
+| cwd | string | | the working directory of terminal |
+| [usePowerShell] | boolean | false | |
+
+
+
+## openInDefaultApp(fullPath) ⇒ Promise.<void>
+Opens a file in the default application for its type on Windows, macOS, and Linux.
+
+**Kind**: global function
+**Returns**: Promise.<void> - - Resolves if the file/folder is opened successfully, rejects otherwise.
+
+| Param | Type | Description |
+| --- | --- | --- |
+| fullPath | string | The path to the file/folder to open. |
+
diff --git a/docs/API-Reference/widgets/PopUpManager.md b/docs/API-Reference/widgets/PopUpManager.md
index 5c28bdd673..1f9ce816f0 100644
--- a/docs/API-Reference/widgets/PopUpManager.md
+++ b/docs/API-Reference/widgets/PopUpManager.md
@@ -22,7 +22,8 @@ Add Esc key handling for a popup DOM element.
| removeHandler | function | Pop-up specific remove (e.g. display:none or DOM removal) |
| autoRemove | Boolean | Specify true to indicate the PopUpManager should remove the popup from the _popUps array when the popup is closed. Specify false when the popup is always persistant in the _popUps array. |
| options | object | |
-| options.popupManagesFocus | boolean | set to true if the popup manages focus restore on close |
+| [options.popupManagesFocus] | boolean | set to true if the popup manages focus restore on close |
+| [options.closeCurrentPopups] | boolean | set to true if you want to dismiss all exiting popups before adding this. Useful when this should be the only popup visible. |
diff --git a/src/brackets.js b/src/brackets.js
index 80ad195d78..75aabed6d2 100644
--- a/src/brackets.js
+++ b/src/brackets.js
@@ -138,6 +138,7 @@ define(function (require, exports, module) {
require("language/JSONUtils");
require("widgets/InlineMenu");
require("thirdparty/tinycolor");
+ require("utils/LocalizationUtils");
// DEPRECATED: In future we want to remove the global CodeMirror, but for now we
// expose our required CodeMirror globally so as to avoid breaking extensions in the
diff --git a/src/utils/LocalizationUtils.js b/src/utils/LocalizationUtils.js
index 60354722ff..19118b204a 100644
--- a/src/utils/LocalizationUtils.js
+++ b/src/utils/LocalizationUtils.js
@@ -19,15 +19,13 @@
*
*/
-/**
- * Utilities functions related to localization/i18n
- */
-define(function (require, exports, module) {
+// @INCLUDE_IN_API_DOCS
+define(function (require, exports, module) {
- var Strings = require("strings");
+ const Strings = require("strings");
- /*
+ /**
* Converts a language code to its written name, if possible.
* If not possible, the language code is simply returned.
*
@@ -41,7 +39,109 @@ define(function (require, exports, module) {
return i18n === undefined ? locale : i18n;
}
+ const DATE_TIME_STYLE = {
+ FULL: "full",
+ LONG: "long",
+ MEDIUM: "medium",
+ SHORT: "short"
+ };
+
+ /**
+ * Formats a given date object into a locale-aware date and time string.
+ *
+ * @param {Date} [date] - The date object to format. If not provided, the current date and time will be used.
+ * @param {string} [lang] - Optional language code to use for formatting (e.g., 'en', 'fr').
+ * If not provided, defaults to the application locale or 'en'.
+ * @param {Object} [dateTimeFormat] - Optional object specifying the date and time formatting options.
+ * Defaults to { dateStyle: 'medium', timeStyle: 'short' }.
+ * @param {string} [dateTimeFormat.dateStyle] - Specifies the date format style. One of: DATE_TIME_STYLE.*
+ * @param {string} [dateTimeFormat.timeStyle] - Specifies the time format style. One of: DATE_TIME_STYLE.*
+ * @returns {string} - The formatted date and time string (e.g., "Dec 24, 2024, 10:30 AM").
+ */
+ function getFormattedDateTime(date, lang, dateTimeFormat) {
+ if(!date){
+ date = new Date();
+ }
+ if(!dateTimeFormat){
+ dateTimeFormat = {
+ dateStyle: DATE_TIME_STYLE.MEDIUM,
+ timeStyle: DATE_TIME_STYLE.SHORT
+ };
+ }
+ return Intl.DateTimeFormat([lang || brackets.getLocale() || "en", "en"], dateTimeFormat).format(date);
+ }
+
+ /**
+ * Returns a relative time string (e.g., "2 days ago", "in 3 hours") based on the difference between the given date and now.
+ *
+ * @param {Date} [date] - The date to compare with the current date and time. If not given, defaults to now.
+ * @param {string} [lang] - Optional language code to use for formatting (e.g., 'en', 'fr').
+ * If not provided, defaults to the application locale or 'en'.
+ * @returns {string} - A human-readable relative time string (e.g., "2 days ago", "in 3 hours").
+ */
+ function dateTimeFromNow(date, lang) {
+ date = date || new Date();
+ const now = new Date();
+ const diffInSeconds = Math.floor((date - now) / 1000);
+
+ const rtf = new Intl.RelativeTimeFormat([lang || brackets.getLocale() || "en", "en"],
+ { numeric: 'auto' });
+
+ if (Math.abs(diffInSeconds) < 60) {
+ return rtf.format(diffInSeconds, 'second');
+ } else if (Math.abs(diffInSeconds) < 3600) {
+ return rtf.format(Math.floor(diffInSeconds / 60), 'minute');
+ } else if (Math.abs(diffInSeconds) < 86400) {
+ return rtf.format(Math.floor(diffInSeconds / 3600), 'hour');
+ } else if (Math.abs(diffInSeconds) < 2592000) {
+ return rtf.format(Math.floor(diffInSeconds / 86400), 'day');
+ } else if (Math.abs(diffInSeconds) < 31536000) {
+ return rtf.format(Math.floor(diffInSeconds / 2592000), 'month');
+ } else {
+ return rtf.format(Math.floor(diffInSeconds / 31536000), 'year');
+ }
+ }
+
+ /**
+ * Returns an intelligent date string.
+ * - For dates within the last 30 days or the future: relative time (e.g., "2 days ago", "in 3 hours").
+ * - For dates earlier this year: formatted date (e.g., "Jan 5").
+ * - For dates not in the current year: formatted date with year (e.g., "Jan 5, 2023").
+ *
+ * @param {Date} date - The date to compare and format.
+ * @param {string} [lang] - Optional language code to use for formatting (e.g., 'en', 'fr').
+ * @returns {string} - An intelligently formatted date string.
+ */
+ function dateTimeFromNowFriendly(date, lang) {
+ const now = new Date();
+ const diffInMilliseconds = date - now;
+ const diffInDays = Math.floor(diffInMilliseconds / (1000 * 60 * 60 * 24));
+
+ // If within the last 30 days or the future, use relative time
+ if (Math.abs(diffInDays) <= 30) {
+ return dateTimeFromNow(date, lang);
+ }
+
+ // If in the current year, format as "MMM DD"
+ const currentYear = now.getFullYear();
+ const dateYear = date.getFullYear();
+
+ const languageOption = [lang || brackets.getLocale() || "en", "en"];
+
+ if (currentYear === dateYear) {
+ return new Intl.DateTimeFormat(languageOption, { month: "short", day: "numeric" }).format(date);
+ }
+
+ // For dates in previous years, format as "MMM DD, YYYY"
+ return new Intl.DateTimeFormat(languageOption,
+ { month: "short", day: "numeric", year: "numeric" }).format(date);
+ }
// Define public API
exports.getLocalizedLabel = getLocalizedLabel;
+ exports.getFormattedDateTime = getFormattedDateTime;
+ exports.dateTimeFromNow = dateTimeFromNow;
+ exports.dateTimeFromNowFriendly = dateTimeFromNowFriendly;
+ // public constants
+ exports.DATE_TIME_STYLE = DATE_TIME_STYLE;
});
diff --git a/test/UnitTestSuite.js b/test/UnitTestSuite.js
index 8507739806..4819678336 100644
--- a/test/UnitTestSuite.js
+++ b/test/UnitTestSuite.js
@@ -118,6 +118,7 @@ define(function (require, exports, module) {
require("spec/TaskManager-integ-test");
require("spec/Generic-integ-test");
require("spec/spacing-auto-detect-integ-test");
+ require("spec/LocalizationUtils-test");
// Integrated extension tests
require("spec/Extn-InAppNotifications-integ-test");
require("spec/Extn-RemoteFileAdapter-integ-test");
diff --git a/test/spec/LocalizationUtils-test.js b/test/spec/LocalizationUtils-test.js
new file mode 100644
index 0000000000..34e2bf898a
--- /dev/null
+++ b/test/spec/LocalizationUtils-test.js
@@ -0,0 +1,271 @@
+/*
+ * GNU AGPL-3.0 License
+ *
+ * Copyright (c) 2021 - present core.ai . All rights reserved.
+ *
+ * This program is free software: you can redistribute it and/or modify it
+ * under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License
+ * for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see https://opensource.org/licenses/AGPL-3.0.
+ *
+ */
+
+/*global describe, it, expect*/
+
+define(function (require, exports, module) {
+ const LocalizationUtils = require("utils/LocalizationUtils");
+
+ describe("unit:LocalizationUtils", function () {
+
+ describe("getFormattedDateTime", function () {
+ it("should format date in default locale", function () {
+ const testDate = new Date(2024, 0, 1, 13, 30); // Jan 1, 2024, 1:30 PM
+ const formatted = LocalizationUtils.getFormattedDateTime(testDate);
+ expect(formatted).toMatch(/Jan(uary)? 1, 2024/);
+ expect(formatted).toMatch(/1:30 PM/);
+ });
+
+ it("should format in de locale", function () {
+ const testDate = new Date(2024, 0, 1, 13, 30); // Jan 1, 2024, 1:30 PM
+ const formatted = LocalizationUtils.getFormattedDateTime(testDate, "de"); // German
+ expect(formatted).toMatch(/01.01.2024, 13:30/);
+ });
+
+ it("should fallback to default locale if invalid locale specified", function () {
+ const testDate = new Date(2024, 0, 1, 13, 30); // Jan 1, 2024, 1:30 PM
+ const formatted = LocalizationUtils.getFormattedDateTime(testDate, "invalid-locale");
+ // Should still format in a valid way
+ expect(formatted).toBeTruthy();
+ expect(formatted.length).toBeGreaterThan(0);
+ });
+
+ it("should handle empty date input gracefully", function () {
+ const formattedNow = LocalizationUtils.getFormattedDateTime(); // No date provided
+ const now = new Date();
+ const expected = new Intl.DateTimeFormat('en', {
+ dateStyle: 'medium',
+ timeStyle: 'short'
+ }).format(now);
+
+ expect(formattedNow).toBe(expected);
+ });
+
+ it("should handle edge case dates", function () {
+ const epochDate = new Date(0); // Unix epoch
+ const formattedEpoch = LocalizationUtils.getFormattedDateTime(epochDate);
+ expect(formattedEpoch).toBeTruthy();
+
+ const farFutureDate = new Date(3000, 0, 1, 0, 0); // Jan 1, 3000
+ const formattedFuture = LocalizationUtils.getFormattedDateTime(farFutureDate);
+ expect(formattedFuture).toBeTruthy();
+ });
+
+ it("should correctly format using non-Latin locales", function () {
+ const testDate = new Date(2024, 0, 1, 13, 30); // Jan 1, 2024, 1:30 PM
+ const formatted = LocalizationUtils.getFormattedDateTime(testDate, "ja"); // Japanese
+ expect(formatted).toBe("2024/01/01 13:30");
+ });
+
+ it("should format using a custom dateStyle and timeStyle (FULL)", function () {
+ const testDate = new Date(2024, 0, 1, 13, 30); // Jan 1, 2024, 1:30 PM
+ const customFormat = {
+ dateStyle: LocalizationUtils.DATE_TIME_STYLE.FULL,
+ timeStyle: LocalizationUtils.DATE_TIME_STYLE.FULL
+ };
+ const formatted = LocalizationUtils.getFormattedDateTime(testDate, "en", customFormat);
+ // Example format: "Monday, January 1, 2024 at 1:30:00 PM GMT+1"
+ expect(formatted).toMatch(/Monday, January 1, 2024/);
+ expect(formatted).toMatch(/1:30:00 PM/);
+ });
+
+ it("should format using only dateStyle (SHORT)", function () {
+ const testDate = new Date(2024, 0, 1, 13, 30); // Jan 1, 2024, 1:30 PM
+ const customFormat = {
+ dateStyle: LocalizationUtils.DATE_TIME_STYLE.SHORT
+ };
+ const formatted = LocalizationUtils.getFormattedDateTime(testDate, "en", customFormat);
+ // Example format: "1/1/24"
+ expect(formatted).toBe("1/1/24");
+ });
+
+ it("should format using only timeStyle (LONG)", function () {
+ const testDate = new Date(2024, 0, 1, 13, 30); // Jan 1, 2024, 1:30 PM
+ const customFormat = {
+ timeStyle: LocalizationUtils.DATE_TIME_STYLE.LONG
+ };
+ const formatted = LocalizationUtils.getFormattedDateTime(testDate, "en", customFormat);
+ // Example format: "1:30:00 PM GMT+1"
+ expect(formatted).toMatch(/1:30:00 PM/);
+ });
+
+ it("should respect custom dateTimeFormat and locale", function () {
+ const testDate = new Date(2024, 0, 1, 13, 30); // Jan 1, 2024, 1:30 PM
+ const customFormat = {
+ dateStyle: LocalizationUtils.DATE_TIME_STYLE.LONG,
+ timeStyle: LocalizationUtils.DATE_TIME_STYLE.SHORT
+ };
+ const formatted = LocalizationUtils.getFormattedDateTime(testDate, "fr", customFormat);
+ // Example format: "1 janvier 2024 à 13:30"
+ expect(formatted.includes("1 janvier 2024")).toBeTrue();
+ expect(formatted.includes("13:30")).toBeTrue();
+ });
+
+ it("should default to current date with custom dateTimeFormat", function () {
+ const customFormat = {
+ dateStyle: LocalizationUtils.DATE_TIME_STYLE.MEDIUM,
+ timeStyle: LocalizationUtils.DATE_TIME_STYLE.MEDIUM
+ };
+ const formattedNow = LocalizationUtils.getFormattedDateTime(undefined, "en", customFormat); // No date provided
+ const now = new Date();
+ const expected = new Intl.DateTimeFormat("en", customFormat).format(now);
+
+ expect(formattedNow).toBe(expected);
+ });
+ });
+
+ describe("dateTimeFromNow", function () {
+ it("should return 'now' for current time", function () {
+ const now = new Date();
+ let result = LocalizationUtils.dateTimeFromNow(now, "en");
+ expect(result).toBe("now");
+ result = LocalizationUtils.dateTimeFromNow(now, "de");
+ expect(result).toBe("jetzt");
+ });
+
+ it("should handle future dates within seconds", function () {
+ const futureDate = new Date(Date.now() + 30 * 1000); // 30 seconds in the future
+ const result = LocalizationUtils.dateTimeFromNow(futureDate, "en");
+ expect(result).toBe("in 30 seconds");
+ });
+
+ it("should handle past dates within minutes", function () {
+ const pastDate = new Date(Date.now() - 90 * 1000); // 90 seconds in the past
+ const result = LocalizationUtils.dateTimeFromNow(pastDate, "en");
+ expect(result).toBe("2 minutes ago");
+ });
+
+ it("should handle future dates within hours", function () {
+ const futureDate = new Date(Date.now() + 2 * 60 * 60 * 1000); // 2 hours in the future
+ const result = LocalizationUtils.dateTimeFromNow(futureDate, "en");
+ expect(result).toBe("in 2 hours");
+ });
+
+ it("should handle past dates within days", function () {
+ const pastDate = new Date(Date.now() - 3 * 24 * 60 * 60 * 1000); // 3 days ago
+ const result = LocalizationUtils.dateTimeFromNow(pastDate, "en");
+ expect(result).toBe("3 days ago");
+ });
+
+ it("should handle future dates within months", function () {
+ const futureDate = new Date(Date.now() + 45 * 24 * 60 * 60 * 1000); // 45 days in the future
+ const result = LocalizationUtils.dateTimeFromNow(futureDate, "en");
+ expect(result).toBe("next month");
+ });
+
+ it("should handle past dates within years", function () {
+ const pastDate = new Date(Date.now() - 2 * 365 * 24 * 60 * 60 * 1000); // 2 years ago
+ const result = LocalizationUtils.dateTimeFromNow(pastDate, "en");
+ expect(result).toBe("2 years ago");
+ });
+
+ it("should return relative time in French locale", function () {
+ const pastDate = new Date(Date.now() - 3 * 24 * 60 * 60 * 1000); // 3 days ago
+ const result = LocalizationUtils.dateTimeFromNow(pastDate, "fr");
+ expect(result).toBe("il y a 3 jours");
+ });
+
+ it("should fallback to default locale if an invalid locale is specified", function () {
+ const futureDate = new Date(Date.now() + 2 * 60 * 60 * 1000); // 2 hours in the future
+ const result = LocalizationUtils.dateTimeFromNow(futureDate, "invalid-locale");
+ expect(result).toBe("in 2 hours");
+ });
+
+ it("should handle default date input (now) gracefully", function () {
+ const result = LocalizationUtils.dateTimeFromNow(undefined, "en");
+ expect(result).toBe("now");
+ });
+ });
+
+ describe("dateTimeFromNowFriendly", function () {
+ it("should use relative time for dates within the last 30 days", function () {
+ const testDate = new Date(Date.now() - 3 * 24 * 60 * 60 * 1000); // 3 days ago
+ const result = LocalizationUtils.dateTimeFromNowFriendly(testDate, "en");
+ expect(result).toBe("3 days ago");
+ });
+
+ it("should use relative time for dates in the future within 30 days", function () {
+ const testDate = new Date(Date.now() + 5 * 24 * 60 * 60 * 1000); // 5 days in the future
+ const result = LocalizationUtils.dateTimeFromNowFriendly(testDate, "en");
+ expect(result).toBe("in 5 days");
+ });
+
+ it("should use formatted date without year for dates earlier this year", function () {
+ const now = new Date();
+ const testDate = new Date(now.getFullYear(), 1, 15); // Feb 15 of current year
+ const result = LocalizationUtils.dateTimeFromNowFriendly(testDate, "en");
+ expect(result).toBe("Feb 15");
+ });
+
+ it("should use formatted date with year for dates in previous years", function () {
+ const testDate = new Date(2022, 6, 4); // July 4, 2022
+ const result = LocalizationUtils.dateTimeFromNowFriendly(testDate, "en");
+ expect(result).toBe("Jul 4, 2022");
+ });
+
+ it("should use formatted date with year for future dates in upcoming years", function () {
+ const testDate = new Date(new Date().getFullYear() + 2, 0, 1); // Jan 1, two years from now
+ const result = LocalizationUtils.dateTimeFromNowFriendly(testDate, "en");
+ expect(result).toBe("Jan 1, " + (new Date().getFullYear() + 2));
+ });
+
+ it("should handle relative time for today", function () {
+ const now = new Date();
+ const result = LocalizationUtils.dateTimeFromNowFriendly(now, "en");
+ expect(result).toBe("now");
+ });
+
+ // Relative time tests
+ it("should use relative time for dates within the last 30 days (de locale)", function () {
+ const testDate = new Date(Date.now() - 3 * 24 * 60 * 60 * 1000); // 3 days ago
+ const result = LocalizationUtils.dateTimeFromNowFriendly(testDate, "de");
+ expect(result).toBe("vor 3 Tagen");
+ });
+
+ it("should use relative time for dates in the future within 30 days (de locale)", function () {
+ const testDate = new Date(Date.now() + 5 * 24 * 60 * 60 * 1000); // 5 days in the future
+ const result = LocalizationUtils.dateTimeFromNowFriendly(testDate, "de");
+ expect(result).toBe("in 5 Tagen");
+ });
+
+ // Current year tests
+ it("should use formatted date without year for dates earlier this year (de locale)", function () {
+ const now = new Date();
+ const testDate = new Date(now.getFullYear(), 1, 15); // Feb 15 of current year
+ const result = LocalizationUtils.dateTimeFromNowFriendly(testDate, "de");
+ expect(result).toBe("15. Feb.");
+ });
+
+ // Non-current year tests
+ it("should use formatted date with year for dates in previous years (de locale)", function () {
+ const testDate = new Date(2022, 6, 4); // July 4, 2022
+ const result = LocalizationUtils.dateTimeFromNowFriendly(testDate, "de");
+ expect(result).toBe("4. Juli 2022");
+ });
+
+ it("should use formatted date with year for future dates in upcoming years (de locale)", function () {
+ const testDate = new Date(new Date().getFullYear() + 2, 0, 1); // Jan 1, two years from now
+ const result = LocalizationUtils.dateTimeFromNowFriendly(testDate, "de");
+ expect(result).toBe(`1. Jan. ${new Date().getFullYear() + 2}`);
+ });
+ });
+
+ });
+});
\ No newline at end of file