@@ -180,24 +180,58 @@ private function safeProjectName(object $project): string {
180180
181181 /** Match the currency name from the known currencies. **/
182182 private function getCurrencyName (string $ name ): ?string {
183- $ name = strtolower ($ name );
184- foreach ($ this ->symbols as $ cur => $ currency ) {
185- // e.g. usd
186- $ id = strtolower ($ cur );
187- if (($ name ) === $ id ) {
183+ $ original = trim ($ name );
184+ $ lower = mb_strtolower ($ original , 'UTF-8 ' );
185+
186+ // Prefer explicit 3-letter code tokens (e.g., "US Dollar (USD)")
187+ foreach ($ this ->symbols as $ code => $ currency ) {
188+ $ id = mb_strtolower ((string )$ code , 'UTF-8 ' );
189+ if (preg_match ('/\b ' . preg_quote ($ id , '/ ' ) . '\b/u ' , $ lower )) {
188190 return $ id ;
189191 }
192+ }
190193
191- // e.g. $, $ USD
192- $ symbol = $ currency ['symbol ' ];
193- if (str_contains ($ name , $ symbol )) {
194- return $ id ;
194+ // Symbol-based detection (avoid matching symbols attached to letters unless
195+ // those letters are part of the *symbol itself*, e.g., "R$")
196+ // Collect all codes hit by symbols, then resolve with preference.
197+ $ hitsBySymbol = [];
198+
199+ foreach ($ this ->symbols as $ code => $ currency ) {
200+ if (empty ($ currency ['symbol ' ])) {
201+ continue ;
195202 }
196203
197- // e.g. US Dollar (USD)
198- preg_match ('/\b ' . $ id . '\b/ ' , $ name , $ matches );
199- if (count ($ matches ) > 0 ) {
200- return $ id ;
204+ $ sym = (string )$ currency ['symbol ' ];
205+ $ symQuoted = preg_quote ($ sym , '/ ' );
206+ $ hasLetters = preg_match ('/\p{L}/u ' , $ sym ) === 1 ;
207+
208+ if ($ hasLetters ) {
209+ // Multi-char symbol that includes letters (e.g., "R$", "C$", "zł")
210+ $ pattern = '/ ' . $ symQuoted . '/u ' ;
211+ } else {
212+ // Pure symbol (e.g., "$", "€", "₪") — require no letters adjacent
213+ // so "MN$" or "$U" won't match.
214+ $ pattern = '/(?<!\p{L}) ' . $ symQuoted . '(?!\p{L})/u ' ;
215+ }
216+
217+ if (preg_match ($ pattern , $ original )) {
218+ $ hitsBySymbol [$ sym ] ??= [];
219+ $ hitsBySymbol [$ sym ][] = mb_strtolower ((string )$ code , 'UTF-8 ' );
220+ }
221+ }
222+
223+ if (!empty ($ hitsBySymbol )) {
224+ // Resolve ambiguity by symbol preference, else first hit
225+ foreach ($ hitsBySymbol as $ sym => $ codes ) {
226+ $ preferred = $ this ->symbolPreference [$ sym ] ?? null ;
227+ if ($ preferred ) {
228+ $ preferredLower = mb_strtolower ($ preferred , 'UTF-8 ' );
229+ if (in_array ($ preferredLower , $ codes , true )) {
230+ return $ preferredLower ;
231+ }
232+ }
233+ // Fall back to first detected code for that symbol
234+ return $ codes [0 ];
201235 }
202236 }
203237
0 commit comments