fix: recognize yen in currency conversions

This commit is contained in:
2026-10-22 14:04:29 +02:00
parent 751f22f6b1
commit 0a85d21801
3 changed files with 19 additions and 0 deletions
+3
View File
@@ -100,6 +100,7 @@ sqrt(144)
32 f to c 32 f to c
1 GiB to MiB 1 GiB to MiB
100 USD to DKK 100 USD to DKK
1000 yen to DKK
0.5 BTC to ETH 0.5 BTC to ETH
``` ```
@@ -109,6 +110,8 @@ Convert length, mass, temperature, volume, duration, speed, area, and data with
Currency and crypto conversions require internet access and use [currency-api](https://github.com/fawazahmed0/exchange-api). These are **daily rates, not live market prices**; the snapshot date is shown with the answer. Treat them as estimates, not trading or payment quotes. Fetch failures show an error rather than silently using old rates; change the query to retry. Currency and crypto conversions require internet access and use [currency-api](https://github.com/fawazahmed0/exchange-api). These are **daily rates, not live market prices**; the snapshot date is shown with the answer. Treat them as estimates, not trading or payment quotes. Fetch failures show an error rather than silently using old rates; change the query to retry.
`yen` is accepted as an alias for `JPY` in either conversion direction, case-insensitively.
Rates are shared across queries and refreshed on the next currency query after the local day changes. While refreshing, a cached answer remains copyable and is labeled stale. Leaving the query or dismissing the launcher does not cancel that shared refresh. Rates are shared across queries and refreshed on the next currency query after the local day changes. While refreshing, a cached answer remains copyable and is labeled stale. Leaving the query or dismissing the launcher does not cancel that shared refresh.
`ConjureKit.CurrencyCalculation` owns this lifetime policy; the launcher renders its result. Its checks use an injected clock and scripted rate fetches. `ConjureKit.CurrencyCalculation` owns this lifetime policy; the launcher renders its result. Its checks use an injected clock and scripted rate fetches.
+1
View File
@@ -105,6 +105,7 @@ public enum Calculator {
private static func currencyCode(_ text: String, rates: CurrencyRates?) -> String? { private static func currencyCode(_ text: String, rates: CurrencyRates?) -> String? {
let code = text.lowercased() let code = text.lowercased()
if code == "yen" { return "jpy" }
guard (2...12).contains(code.utf8.count), guard (2...12).contains(code.utf8.count),
code.utf8.allSatisfy({ (97...122).contains($0) || (48...57).contains($0) }), code.utf8.allSatisfy({ (97...122).contains($0) || (48...57).contains($0) }),
ConversionUnit.parse(text) == nil else { return nil } ConversionUnit.parse(text) == nil else { return nil }
+15
View File
@@ -11,6 +11,21 @@ func calculatorCrossConvertsCurrencyExpressionsAndPreservesSnapshotDate() throws
#expect(result.detail == "2*50 USD → DKK · Daily snapshot 2026-01-02") #expect(result.detail == "2*50 USD → DKK · Daily snapshot 2026-01-02")
} }
@Test
func calculatorConvertsYenAsJPYInEitherDirection() {
let snapshot = CurrencyRates(date: "2026-01-02", rates: ["eur": 1, "jpy": 160, "dkk": 8])
for (query, expected) in [
("1000 yen to dkk", "50 DKK"),
("1000JPY to DKK", "50 DKK"),
("1000YeN in dkk", "50 DKK"),
("50 DKK as yen", "1000 JPY"),
("2*500 yen to JPY", "1000 JPY"),
] {
#expect(Calculator.isCurrencyQuery(query), "\(query)")
#expect(Calculator.evaluate(query, currencyRates: snapshot)?.copyValue == expected, "\(query)")
}
}
@Test @Test
func calculatorConvertsFiatAndCryptoWithCaseInsensitiveSeparators() { func calculatorConvertsFiatAndCryptoWithCaseInsensitiveSeparators() {
let snapshot = CurrencyRates(date: "2026-01-02", rates: [ let snapshot = CurrencyRates(date: "2026-01-02", rates: [