Skip to content

Commit 50dc2ed

Browse files
committed
feat: add getFormattedDateTime localization util
1 parent 309a6c2 commit 50dc2ed

File tree

8 files changed

+271
-7
lines changed

8 files changed

+271
-7
lines changed

docs/API-Reference/command/Commands.md

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -476,6 +476,18 @@ Zooms out the editor view
476476
## VIEW\_ZOOM\_SUBMENU
477477
Submenu for zoom options
478478

479+
**Kind**: global variable
480+
<a name="OPEN_IN_SUBMENU"></a>
481+
482+
## OPEN\_IN\_SUBMENU
483+
Submenu for Open in project context menu
484+
485+
**Kind**: global variable
486+
<a name="OPEN_IN_SUBMENU_WS"></a>
487+
488+
## OPEN\_IN\_SUBMENU\_WS
489+
Submenu for Open in working set context menu
490+
479491
**Kind**: global variable
480492
<a name="VIEW_INCREASE_FONT_SIZE"></a>
481493

@@ -602,6 +614,24 @@ Shows current file in file tree
602614
## NAVIGATE\_SHOW\_IN\_OS
603615
Shows current file in OS file explorer
604616

617+
**Kind**: global variable
618+
<a name="NAVIGATE_OPEN_IN_TERMINAL"></a>
619+
620+
## NAVIGATE\_OPEN\_IN\_TERMINAL
621+
Shows current file in OS Terminal
622+
623+
**Kind**: global variable
624+
<a name="NAVIGATE_OPEN_IN_POWERSHELL"></a>
625+
626+
## NAVIGATE\_OPEN\_IN\_POWERSHELL
627+
Shows current file in open powershell in Windows os
628+
629+
**Kind**: global variable
630+
<a name="NAVIGATE_OPEN_IN_DEFAULT_APP"></a>
631+
632+
## NAVIGATE\_OPEN\_IN\_DEFAULT\_APP
633+
Open current file in the default associated app in the os
634+
605635
**Kind**: global variable
606636
<a name="NAVIGATE_QUICK_OPEN"></a>
607637

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
### Import :
2+
```js
3+
const LocalizationUtils = brackets.getModule("utils/LocalizationUtils")
4+
```
5+
6+
<a name="getLocalizedLabel"></a>
7+
8+
## getLocalizedLabel(locale) ⇒ <code>string</code>
9+
Converts a language code to its written name, if possible.
10+
If not possible, the language code is simply returned.
11+
12+
**Kind**: global function
13+
**Returns**: <code>string</code> - The language's name or the given language code
14+
15+
| Param | Type | Description |
16+
| --- | --- | --- |
17+
| locale | <code>string</code> | The two-char language code |
18+
19+
<a name="getFormattedDateTime"></a>
20+
21+
## getFormattedDateTime([date], [lang], [dateTimeFormat]) ⇒ <code>string</code>
22+
Formats a given date object into a locale-aware date and time string.
23+
24+
**Kind**: global function
25+
**Returns**: <code>string</code> - - The formatted date and time string (e.g., "Dec 24, 2024, 10:30 AM").
26+
27+
| Param | Type | Description |
28+
| --- | --- | --- |
29+
| [date] | <code>Date</code> | The date object to format. If not provided, the current date and time will be used. |
30+
| [lang] | <code>string</code> | Optional language code to use for formatting (e.g., 'en', 'fr'). If not provided, defaults to the application locale or 'en'. |
31+
| [dateTimeFormat] | <code>Object</code> | Optional object specifying the date and time formatting options. Defaults to { dateStyle: 'medium', timeStyle: 'short' }. |
32+
| [dateTimeFormat.dateStyle] | <code>string</code> | Specifies the date format style. One of: DATE_TIME_STYLE.* |
33+
| [dateTimeFormat.timeStyle] | <code>string</code> | Specifies the time format style. One of: DATE_TIME_STYLE.* |
34+

docs/API-Reference/utils/NodeUtils.md

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -82,3 +82,28 @@ This is only available in the native app
8282
| fullFilePath | <code>string</code> |
8383
| projectFullPath | <code>string</code> |
8484

85+
<a name="openNativeTerminal"></a>
86+
87+
## openNativeTerminal(cwd, [usePowerShell])
88+
Runs ESLint on a file
89+
This is only available in the native app
90+
91+
**Kind**: global function
92+
93+
| Param | Type | Default | Description |
94+
| --- | --- | --- | --- |
95+
| cwd | <code>string</code> | | the working directory of terminal |
96+
| [usePowerShell] | <code>boolean</code> | <code>false</code> | |
97+
98+
<a name="openInDefaultApp"></a>
99+
100+
## openInDefaultApp(fullPath) ⇒ <code>Promise.&lt;void&gt;</code>
101+
Opens a file in the default application for its type on Windows, macOS, and Linux.
102+
103+
**Kind**: global function
104+
**Returns**: <code>Promise.&lt;void&gt;</code> - - Resolves if the file/folder is opened successfully, rejects otherwise.
105+
106+
| Param | Type | Description |
107+
| --- | --- | --- |
108+
| fullPath | <code>string</code> | The path to the file/folder to open. |
109+

docs/API-Reference/widgets/PopUpManager.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,8 @@ Add Esc key handling for a popup DOM element.
2222
| removeHandler | <code>function</code> | Pop-up specific remove (e.g. display:none or DOM removal) |
2323
| autoRemove | <code>Boolean</code> | 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. |
2424
| options | <code>object</code> | |
25-
| options.popupManagesFocus | <code>boolean</code> | set to true if the popup manages focus restore on close |
25+
| [options.popupManagesFocus] | <code>boolean</code> | set to true if the popup manages focus restore on close |
26+
| [options.closeCurrentPopups] | <code>boolean</code> | set to true if you want to dismiss all exiting popups before adding this. Useful when this should be the only popup visible. |
2627

2728
<a name="removePopUp"></a>
2829

src/brackets.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -138,6 +138,7 @@ define(function (require, exports, module) {
138138
require("language/JSONUtils");
139139
require("widgets/InlineMenu");
140140
require("thirdparty/tinycolor");
141+
require("utils/LocalizationUtils");
141142

142143
// DEPRECATED: In future we want to remove the global CodeMirror, but for now we
143144
// expose our required CodeMirror globally so as to avoid breaking extensions in the

src/utils/LocalizationUtils.js

Lines changed: 38 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -19,15 +19,13 @@
1919
*
2020
*/
2121

22-
/**
23-
* Utilities functions related to localization/i18n
24-
*/
25-
define(function (require, exports, module) {
22+
// @INCLUDE_IN_API_DOCS
2623

24+
define(function (require, exports, module) {
2725

28-
var Strings = require("strings");
26+
const Strings = require("strings");
2927

30-
/*
28+
/**
3129
* Converts a language code to its written name, if possible.
3230
* If not possible, the language code is simply returned.
3331
*
@@ -41,7 +39,41 @@ define(function (require, exports, module) {
4139
return i18n === undefined ? locale : i18n;
4240
}
4341

42+
const DATE_TIME_STYLE = {
43+
FULL: "full",
44+
LONG: "long",
45+
MEDIUM: "medium",
46+
SHORT: "short"
47+
};
48+
49+
/**
50+
* Formats a given date object into a locale-aware date and time string.
51+
*
52+
* @param {Date} [date] - The date object to format. If not provided, the current date and time will be used.
53+
* @param {string} [lang] - Optional language code to use for formatting (e.g., 'en', 'fr').
54+
* If not provided, defaults to the application locale or 'en'.
55+
* @param {Object} [dateTimeFormat] - Optional object specifying the date and time formatting options.
56+
* Defaults to { dateStyle: 'medium', timeStyle: 'short' }.
57+
* @param {string} [dateTimeFormat.dateStyle] - Specifies the date format style. One of: DATE_TIME_STYLE.*
58+
* @param {string} [dateTimeFormat.timeStyle] - Specifies the time format style. One of: DATE_TIME_STYLE.*
59+
* @returns {string} - The formatted date and time string (e.g., "Dec 24, 2024, 10:30 AM").
60+
*/
61+
function getFormattedDateTime(date, lang, dateTimeFormat) {
62+
if(!date){
63+
date = new Date();
64+
}
65+
if(!dateTimeFormat){
66+
dateTimeFormat = {
67+
dateStyle: DATE_TIME_STYLE.MEDIUM,
68+
timeStyle: DATE_TIME_STYLE.SHORT
69+
};
70+
}
71+
return Intl.DateTimeFormat([lang || brackets.getLocale() || "en", "en"], dateTimeFormat).format(date);
72+
}
4473

4574
// Define public API
4675
exports.getLocalizedLabel = getLocalizedLabel;
76+
exports.getFormattedDateTime = getFormattedDateTime;
77+
// public constants
78+
exports.DATE_TIME_STYLE = DATE_TIME_STYLE;
4779
});

test/UnitTestSuite.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -118,6 +118,7 @@ define(function (require, exports, module) {
118118
require("spec/TaskManager-integ-test");
119119
require("spec/Generic-integ-test");
120120
require("spec/spacing-auto-detect-integ-test");
121+
require("spec/LocalizationUtils-test");
121122
// Integrated extension tests
122123
require("spec/Extn-InAppNotifications-integ-test");
123124
require("spec/Extn-RemoteFileAdapter-integ-test");
Lines changed: 140 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,140 @@
1+
/*
2+
* GNU AGPL-3.0 License
3+
*
4+
* Copyright (c) 2021 - present core.ai . All rights reserved.
5+
*
6+
* This program is free software: you can redistribute it and/or modify it
7+
* under the terms of the GNU Affero General Public License as published by
8+
* the Free Software Foundation, either version 3 of the License, or
9+
* (at your option) any later version.
10+
*
11+
* This program is distributed in the hope that it will be useful, but WITHOUT
12+
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13+
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License
14+
* for more details.
15+
*
16+
* You should have received a copy of the GNU Affero General Public License
17+
* along with this program. If not, see https://opensource.org/licenses/AGPL-3.0.
18+
*
19+
*/
20+
21+
/*global describe, it, expect*/
22+
23+
define(function (require, exports, module) {
24+
const LocalizationUtils = require("utils/LocalizationUtils");
25+
26+
describe("unit:LocalizationUtils", function () {
27+
28+
describe("getFormattedDateTime", function () {
29+
it("should format date in default locale", function () {
30+
const testDate = new Date(2024, 0, 1, 13, 30); // Jan 1, 2024, 1:30 PM
31+
const formatted = LocalizationUtils.getFormattedDateTime(testDate);
32+
expect(formatted).toMatch(/Jan(uary)? 1, 2024/);
33+
expect(formatted).toMatch(/1:30 PM/);
34+
});
35+
36+
it("should format date in specified locale fr", function () {
37+
const testDate = new Date(2024, 0, 1, 13, 30); // Jan 1, 2024, 1:30 PM
38+
const formatted = LocalizationUtils.getFormattedDateTime(testDate, "fr");
39+
// Explicit check for French date and time format
40+
expect(formatted).toBe("1 janv. 2024, 13:30");
41+
});
42+
43+
it("should format in de locale", function () {
44+
const testDate = new Date(2024, 0, 1, 13, 30); // Jan 1, 2024, 1:30 PM
45+
const formatted = LocalizationUtils.getFormattedDateTime(testDate, "de"); // German
46+
expect(formatted).toMatch(/01.01.2024, 13:30/);
47+
});
48+
49+
it("should fallback to default locale if invalid locale specified", function () {
50+
const testDate = new Date(2024, 0, 1, 13, 30); // Jan 1, 2024, 1:30 PM
51+
const formatted = LocalizationUtils.getFormattedDateTime(testDate, "invalid-locale");
52+
// Should still format in a valid way
53+
expect(formatted).toBeTruthy();
54+
expect(formatted.length).toBeGreaterThan(0);
55+
});
56+
57+
it("should handle empty date input gracefully", function () {
58+
const formattedNow = LocalizationUtils.getFormattedDateTime(); // No date provided
59+
const now = new Date();
60+
const expected = new Intl.DateTimeFormat('en', {
61+
dateStyle: 'medium',
62+
timeStyle: 'short'
63+
}).format(now);
64+
65+
expect(formattedNow).toBe(expected);
66+
});
67+
68+
it("should handle edge case dates", function () {
69+
const epochDate = new Date(0); // Unix epoch
70+
const formattedEpoch = LocalizationUtils.getFormattedDateTime(epochDate);
71+
expect(formattedEpoch).toBeTruthy();
72+
73+
const farFutureDate = new Date(3000, 0, 1, 0, 0); // Jan 1, 3000
74+
const formattedFuture = LocalizationUtils.getFormattedDateTime(farFutureDate);
75+
expect(formattedFuture).toBeTruthy();
76+
});
77+
78+
it("should correctly format using non-Latin locales", function () {
79+
const testDate = new Date(2024, 0, 1, 13, 30); // Jan 1, 2024, 1:30 PM
80+
const formatted = LocalizationUtils.getFormattedDateTime(testDate, "ja"); // Japanese
81+
expect(formatted).toBe("2024/01/01 13:30");
82+
});
83+
84+
it("should format using a custom dateStyle and timeStyle (FULL)", function () {
85+
const testDate = new Date(2024, 0, 1, 13, 30); // Jan 1, 2024, 1:30 PM
86+
const customFormat = {
87+
dateStyle: LocalizationUtils.DATE_TIME_STYLE.FULL,
88+
timeStyle: LocalizationUtils.DATE_TIME_STYLE.FULL
89+
};
90+
const formatted = LocalizationUtils.getFormattedDateTime(testDate, "en", customFormat);
91+
// Example format: "Monday, January 1, 2024 at 1:30:00 PM GMT+1"
92+
expect(formatted).toMatch(/Monday, January 1, 2024/);
93+
expect(formatted).toMatch(/1:30:00 PM/);
94+
});
95+
96+
it("should format using only dateStyle (SHORT)", function () {
97+
const testDate = new Date(2024, 0, 1, 13, 30); // Jan 1, 2024, 1:30 PM
98+
const customFormat = {
99+
dateStyle: LocalizationUtils.DATE_TIME_STYLE.SHORT
100+
};
101+
const formatted = LocalizationUtils.getFormattedDateTime(testDate, "en", customFormat);
102+
// Example format: "1/1/24"
103+
expect(formatted).toBe("1/1/24");
104+
});
105+
106+
it("should format using only timeStyle (LONG)", function () {
107+
const testDate = new Date(2024, 0, 1, 13, 30); // Jan 1, 2024, 1:30 PM
108+
const customFormat = {
109+
timeStyle: LocalizationUtils.DATE_TIME_STYLE.LONG
110+
};
111+
const formatted = LocalizationUtils.getFormattedDateTime(testDate, "en", customFormat);
112+
// Example format: "1:30:00 PM GMT+1"
113+
expect(formatted).toMatch(/1:30:00 PM/);
114+
});
115+
116+
it("should respect custom dateTimeFormat and locale", function () {
117+
const testDate = new Date(2024, 0, 1, 13, 30); // Jan 1, 2024, 1:30 PM
118+
const customFormat = {
119+
dateStyle: LocalizationUtils.DATE_TIME_STYLE.LONG,
120+
timeStyle: LocalizationUtils.DATE_TIME_STYLE.SHORT
121+
};
122+
const formatted = LocalizationUtils.getFormattedDateTime(testDate, "fr", customFormat);
123+
// Example format: "1 janvier 2024 à 13:30"
124+
expect(formatted).toBe("1 janvier 2024 à 13:30");
125+
});
126+
127+
it("should default to current date with custom dateTimeFormat", function () {
128+
const customFormat = {
129+
dateStyle: LocalizationUtils.DATE_TIME_STYLE.MEDIUM,
130+
timeStyle: LocalizationUtils.DATE_TIME_STYLE.MEDIUM
131+
};
132+
const formattedNow = LocalizationUtils.getFormattedDateTime(undefined, "en", customFormat); // No date provided
133+
const now = new Date();
134+
const expected = new Intl.DateTimeFormat("en", customFormat).format(now);
135+
136+
expect(formattedNow).toBe(expected);
137+
});
138+
});
139+
});
140+
});

0 commit comments

Comments
 (0)