Skip to content

Commit 1d3cebf

Browse files
authored
Add currency mini-library (#8)
1 parent 5bb0bb0 commit 1d3cebf

File tree

16 files changed

+935
-1
lines changed

16 files changed

+935
-1
lines changed

README.md

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,12 @@ distinguish between cellular vs WiFi connection.
2323
![badge][badge-android]
2424
![badge][badge-ios]
2525

26+
[currency](currency/README.md): Format currency values.
27+
![badge][badge-android]
28+
![badge][badge-ios]
29+
![badge][badge-js]
30+
![badge][badge-jvm]
31+
2632
[ignore-ios](ignore-ios/README.md): Annotations to ignore iOS tests.
2733
![badge][badge-android]
2834
![badge][badge-ios]
@@ -54,7 +60,7 @@ language translations, unit/integration tests are welcomed.
5460

5561
[![License](https://img.shields.io/badge/License-Apache%202.0-blue.svg)](LICENSE)
5662

57-
Copyright 2021 Appmattus Limited
63+
Copyright 2021-2025 Appmattus Limited
5864

5965
Licensed under the Apache License, Version 2.0 (the "License"); you may not use
6066
this file except in compliance with the License. You may obtain a copy of the

currency/README.md

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
# currency
2+
3+
A Kotlin Multiplatform Mobile library to format currency values.
4+
5+
## Getting started
6+
7+
![badge][badge-android]
8+
![badge][badge-ios]
9+
![badge][badge-js]
10+
[![Maven Central](https://img.shields.io/maven-central/v/com.appmattus.mpu/currency)](https://search.maven.org/search?q=g:com.appmattus.mpu)
11+
12+
Include the following dependency in your *build.gradle.kts* file:
13+
14+
```kotlin
15+
commonMain {
16+
implementation("com.appmattus.mpu:currency:<latest-version>")
17+
}
18+
```
19+
20+
Format a currency value:
21+
22+
```kotlin
23+
Currency.format(value = 1345.23, currencyCode = "GBP", locale = "en-GB")
24+
```
25+
26+
[badge-android]: http://img.shields.io/badge/platform-android-6EDB8D.svg?style=flat
27+
[badge-ios]: http://img.shields.io/badge/platform-ios-CDCDCD.svg?style=flat
28+
[badge-js]: http://img.shields.io/badge/platform-js-F8DB5D.svg?style=flat
29+
[badge-jvm]: http://img.shields.io/badge/platform-jvm-DB413D.svg?style=flat
30+
[badge-linux]: http://img.shields.io/badge/platform-linux-2D3F6C.svg?style=flat
31+
[badge-windows]: http://img.shields.io/badge/platform-windows-4D76CD.svg?style=flat
32+
[badge-mac]: http://img.shields.io/badge/platform-macos-111111.svg?style=flat
33+
[badge-watchos]: http://img.shields.io/badge/platform-watchos-C0C0C0.svg?style=flat
34+
[badge-tvos]: http://img.shields.io/badge/platform-tvos-808080.svg?style=flat
35+
[badge-wasm]: https://img.shields.io/badge/platform-wasm-624FE8.svg?style=flat

currency/build.gradle.kts

Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,85 @@
1+
/*
2+
* Copyright 2025 Appmattus Limited
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
plugins {
18+
id("com.android.library")
19+
kotlin("multiplatform")
20+
alias(libs.plugins.gradleMavenPublishPlugin)
21+
alias(libs.plugins.dokkaPlugin)
22+
}
23+
24+
kotlin {
25+
androidTarget()
26+
27+
iosX64()
28+
iosArm64()
29+
iosSimulatorArm64()
30+
31+
js {
32+
browser()
33+
nodejs()
34+
}
35+
36+
listOf(
37+
iosX64(),
38+
iosArm64(),
39+
iosSimulatorArm64()
40+
).forEach { iosTarget ->
41+
iosTarget.binaries.framework {
42+
baseName = "multiplatformutils-currency"
43+
isStatic = true
44+
}
45+
}
46+
47+
// Apply the default hierarchy again. It'll create, for example, the iosMain source set:
48+
applyDefaultHierarchyTemplate()
49+
50+
sourceSets {
51+
commonMain.dependencies {
52+
implementation(libs.kotlinx.coroutines)
53+
}
54+
commonTest.dependencies {
55+
implementation(kotlin("test"))
56+
}
57+
androidUnitTest.dependencies {
58+
implementation(libs.robolectric)
59+
}
60+
}
61+
62+
compilerOptions {
63+
jvmToolchain(11)
64+
freeCompilerArgs.add("-Xexpect-actual-classes")
65+
}
66+
}
67+
68+
android {
69+
namespace = "com.appmattus.multiplatformutils.currency"
70+
compileSdk = 35
71+
72+
defaultConfig {
73+
minSdk = 21
74+
}
75+
buildTypes {
76+
getByName("release") {
77+
isMinifyEnabled = false
78+
}
79+
}
80+
81+
compileOptions {
82+
sourceCompatibility = JavaVersion.VERSION_11
83+
targetCompatibility = JavaVersion.VERSION_11
84+
}
85+
}

currency/gradle.properties

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
#
2+
# Copyright 2025 Appmattus Limited
3+
#
4+
# Licensed under the Apache License, Version 2.0 (the "License");
5+
# you may not use this file except in compliance with the License.
6+
# You may obtain a copy of the License at
7+
#
8+
# http://www.apache.org/licenses/LICENSE-2.0
9+
#
10+
# Unless required by applicable law or agreed to in writing, software
11+
# distributed under the License is distributed on an "AS IS" BASIS,
12+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
# See the License for the specific language governing permissions and
14+
# limitations under the License.
15+
#
16+
17+
POM_NAME=com.appmattus.mpu:currency
18+
POM_ARTIFACT_ID=currency
19+
POM_DESCRIPTION=Format currencies
20+
POM_INCEPTION_YEAR=2025
Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
/*
2+
* Copyright 2025 Appmattus Limited
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package com.appmattus.currency
18+
19+
import java.text.NumberFormat
20+
import java.util.Currency
21+
import java.util.Locale
22+
23+
actual object Currency {
24+
actual fun format(
25+
amount: Double,
26+
currencyCode: String,
27+
locale: String,
28+
showFractionDigits: Boolean,
29+
roundingMode: RoundingMode
30+
): String {
31+
return NumberFormat.getCurrencyInstance(Locale.forLanguageTag(locale)).apply {
32+
val currency = Currency.getInstance(currencyCode)
33+
this.currency = currency
34+
this.roundingMode = when (roundingMode) {
35+
RoundingMode.Up -> java.math.RoundingMode.UP
36+
RoundingMode.Down -> java.math.RoundingMode.DOWN
37+
RoundingMode.Ceiling -> java.math.RoundingMode.CEILING
38+
RoundingMode.Floor -> java.math.RoundingMode.FLOOR
39+
RoundingMode.HalfUp -> java.math.RoundingMode.HALF_UP
40+
RoundingMode.HalfDown -> java.math.RoundingMode.HALF_DOWN
41+
RoundingMode.HalfEven -> java.math.RoundingMode.HALF_EVEN
42+
}
43+
44+
// When showing decimal places use value from currency as NumberFormat defaults to device locale
45+
// TND is formatted with 2 dp and not 3 dp and JPY with 2 dp and not 0 dp
46+
(if (!showFractionDigits) 0 else currency.defaultFractionDigits).let { decimalPlaces ->
47+
minimumFractionDigits = decimalPlaces
48+
maximumFractionDigits = decimalPlaces
49+
}
50+
}.format(amount)
51+
}
52+
}
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
/*
2+
* Copyright 2025 Appmattus Limited
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package com.appmattus.currency
18+
19+
import org.junit.runner.RunWith
20+
import org.robolectric.RobolectricTestRunner
21+
22+
@Suppress("EXPECT_ACTUAL_CLASSIFIERS_ARE_IN_BETA_WARNING")
23+
@RunWith(RobolectricTestRunner::class)
24+
actual abstract class RobolectricTest actual constructor()
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
/*
2+
* Copyright 2025 Appmattus Limited
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package com.appmattus.currency
18+
19+
expect object Currency {
20+
21+
/**
22+
* Format a currency [amount] taking [locale] into account. Decimals are shown based on [showFractionDigits] and numbers are rounded based
23+
* on [roundingMode].
24+
* @param amount Currency amount to format
25+
* @param currencyCode ISO-4217 alphabetic currency code, i.e. CAD
26+
* @param locale IETF BCP 47 language tag, i.e. en-CA
27+
* @param showFractionDigits `true` to show fraction digits, `false` otherwise
28+
* @param roundingMode [RoundingMode] to use
29+
*/
30+
fun format(
31+
amount: Double,
32+
currencyCode: String,
33+
locale: String,
34+
showFractionDigits: Boolean = true,
35+
roundingMode: RoundingMode = RoundingMode.HalfEven
36+
): String
37+
}

0 commit comments

Comments
 (0)