1010namespace MaplePHP \DTO \Format ;
1111
1212use Exception ;
13+ use IntlDateFormatter ;
1314use InvalidArgumentException ;
1415
1516final class Clock extends FormatAbstract implements FormatInterface
1617{
18+ static protected ?string $ defaultLocale = 'en ' ;
19+ protected ?string $ locale = null ;
20+ protected array $ parts = [];
21+
1722 /**
1823 * Input is mixed data type in the interface because I do not know the type before
1924 * The class constructor MUST handle the input validation
@@ -30,44 +35,106 @@ public function __construct(mixed $value)
3035 }
3136
3237 /**
33- * Add translation
38+ * Init format by adding data to modify/format/traverse
39+ * @param mixed $value
40+ * @return self
41+ * @throws Exception
42+ */
43+ public static function value (mixed $ value ): FormatInterface
44+ {
45+ return new Clock ((string )$ value );
46+ }
47+
48+ /**
49+ * Get current expected locale
3450 *
35- * @param string $langCode
51+ * @return string|null
52+ */
53+ public function getLocale (): ?string
54+ {
55+ return $ this ->locale ?? self ::$ defaultLocale ;
56+ }
57+
58+ /**
59+ * Set expected date language using locale
60+ *
61+ * @param string $localeCode
3662 * @return $this
3763 */
38- public function setLanguage (string $ langCode ): self
64+ public function setLanguage (string $ localeCode ): self
3965 {
66+ $ this ->locale = $ localeCode ;
4067 return $ this ;
4168 }
4269
70+ // Alias to setLanguage
71+ public function setLocale (string $ localeCode ): self
72+ {
73+ return $ this ->setLanguage ($ localeCode );
74+ }
75+
76+ /**
77+ * Set default date language using locale
78+ *
79+ * @param string $localeCode
80+ * @return void
81+ */
82+ static public function setDefaultLanguage (string $ localeCode ): void
83+ {
84+ self ::$ defaultLocale = $ localeCode ;
85+ }
86+
87+ // Alias to setDefaultLanguage
88+ static public function setDefaultLocale (string $ localeCode ): void
89+ {
90+ self ::setDefaultLocale ($ localeCode );
91+ }
92+
4393 /**
4494 * Format date data
95+ *
4596 * @param string $format
97+ * @param string|null $locale
4698 * @return object
4799 */
48- public function format (string $ format = 'Y-m-d H:i:s ' ): string
100+ public function format (string $ format = 'Y-m-d H:i:s ' , ? string $ locale = null ): string
49101 {
50- return $ this ->value ->format ($ format );
102+ $ locale = !is_null ($ locale ) ? $ locale : $ this ->getLocale ();
103+ $ translations = $ this ->getTranslationData ($ this ->raw , $ locale );
104+ if ($ translations ) {
105+ return str_replace ($ translations ['find ' ], $ translations ['replace ' ], $ this ->raw ->format ($ format ));
106+ }
107+ return $ this ->raw ->format ($ format );
51108 }
52109
53110 /**
54- * Get DTO value
55- * @return mixed
111+ * Get date and time
112+ *
113+ * @return string
56114 */
57- public function get (): mixed
115+ public function get (): string
58116 {
59- return $ this ->value -> format ( ' Y-m-d H:i:s ' );
117+ return $ this ->dateTime ( );
60118 }
61119
62120 /**
63- * Init format by adding data to modify/format/traverse
64- * @param mixed $value
65- * @return self
66- * @throws Exception
121+ * Get date and time
122+ *
123+ * @return string
67124 */
68- public static function value ( mixed $ value ): FormatInterface
125+ public function dateTime ( ): string
69126 {
70- return new Clock ((string )$ value );
127+ return $ this ->raw ->format ('Y-m-d H:i:s ' );
128+ }
129+
130+ /**
131+ * Get date
132+ *
133+ * @return string
134+ */
135+ public function date (): string
136+ {
137+ return $ this ->raw ->format ('Y-m-d ' );
71138 }
72139
73140 /**
@@ -76,6 +143,77 @@ public static function value(mixed $value): FormatInterface
76143 */
77144 public function __toString (): string
78145 {
79- return (string )$ this ->get ();
146+ return $ this ->get ();
147+ }
148+
149+ /**
150+ * Return translation find array
151+ *
152+ * @param \DateTime $date
153+ * @param string $locale
154+ * @return array
155+ */
156+ protected function getTranslationData (\DateTime $ date , string $ locale ): array
157+ {
158+
159+ if ($ locale !== "en " ) {
160+ $ translations = $ this ->getTranslation ($ locale );
161+ if ($ translations === false ) {
162+ $ formatters = $ this ->getLocaleTranslation ($ locale );
163+ }
164+ }
165+
166+ if (!isset ($ translations ) && !isset ($ formatters )) {
167+ return [];
168+ }
169+
170+ return [
171+ 'find ' => [$ date ->format ('F ' ), $ date ->format ('M ' ), $ date ->format ('l ' ), $ date ->format ('D ' )],
172+ 'replace ' => [
173+ $ translations ['months ' ][$ date ->format ('F ' )] ?? $ formatters ['monthFull ' ]->format ($ this ->raw ),
174+ $ translations ['monthsShort ' ][$ date ->format ('M ' )] ?? $ formatters ['monthShort ' ]->format ($ this ->raw ),
175+ $ translations ['weekdays ' ][$ date ->format ('l ' )] ?? $ formatters ['weekdayFull ' ]->format ($ this ->raw ),
176+ $ translations ['weekdaysShort ' ][$ date ->format ('D ' )] ?? $ formatters ['weekdayShort ' ]->format ($ this ->raw ),
177+ ]
178+ ];
179+ }
180+
181+ /**
182+ * Get static translation if exists
183+ *
184+ * @param string $locale
185+ * @return array|false
186+ */
187+ protected function getTranslation (string $ locale ): array |false
188+ {
189+ $ translationFile = realpath (__DIR__ . "/../lang/ $ locale.php " );
190+ if ($ translationFile !== false ) {
191+ return require $ translationFile ;
192+ }
193+ return false ;
194+ }
195+
196+ /**
197+ * Retrieves localized month and weekday names.
198+ *
199+ * Falls back to PHP’s IntlDateFormatter if a static translation is unavailable.
200+ * This depends on installed system locales, which may cause inconsistencies
201+ * and be slower than static translations. However, missing locales can be
202+ * installed for greater control.
203+ *
204+ * @param string $locale The locale code (e.g., 'sv_SE', 'en_US').
205+ * @return array IntlDateFormatter instances for full/short month and weekday names.
206+ */
207+ protected function getLocaleTranslation (string $ locale ): array
208+ {
209+ if (!isset ($ this ->parts [$ locale ])) {
210+ $ this ->parts [$ locale ] = [
211+ 'monthFull ' => new IntlDateFormatter ($ locale , IntlDateFormatter::LONG , IntlDateFormatter::NONE , null , null , 'MMMM ' ),
212+ 'monthShort ' => new IntlDateFormatter ($ locale , IntlDateFormatter::MEDIUM , IntlDateFormatter::NONE , null , null , 'MMM ' ),
213+ 'weekdayFull ' => new IntlDateFormatter ($ locale , IntlDateFormatter::FULL , IntlDateFormatter::NONE , null , null , 'EEEE ' ),
214+ 'weekdayShort ' => new IntlDateFormatter ($ locale , IntlDateFormatter::FULL , IntlDateFormatter::NONE , null , null , 'E ' )
215+ ];
216+ }
217+ return $ this ->parts [$ locale ];
80218 }
81219}
0 commit comments