diff --git a/currency.currency.inc b/currency.currency.inc
index eb1c80a..51c3e46 100644
--- a/currency.currency.inc
+++ b/currency.currency.inc
@@ -52,6 +52,7 @@ function currency_currency_info() {
     )),
     new Currency(array(
       'code' => 'BHD',
+      'minor_unit' => 3,
       'sign' => '.د.ب',
       'title' => t('Bahraini Dinar'),
     )),
@@ -137,6 +138,7 @@ function currency_currency_info() {
     )),
     new Currency(array(
       'code' => 'CVE',
+      'minor_unit' => 0,
       'sign' => 'Esc',
       'title' => t('Cape Verdean Escudo'),
     )),
@@ -247,6 +249,7 @@ function currency_currency_info() {
     )),
     new Currency(array(
       'code' => 'EUR',
+      'minor_unit' => 2,
       'sign' => '€',
       'title' => t('Euro'),
     )),
@@ -358,7 +361,7 @@ function currency_currency_info() {
     )),
     new Currency(array(
       'code' => 'JPY',
-      'decimals' => 0,
+      'minor_unit' => 0,
       'sign' => '¥',
       'title' => t('Japanese Yen'),
     )),
@@ -546,6 +549,7 @@ function currency_currency_info() {
     )),
     new Currency(array(
       'code' => 'OMR',
+      'minor_unit' => 3,
       'sign' => '﷼',
       'title' => t('Omani Rial'),
     )),
@@ -771,6 +775,7 @@ function currency_currency_info() {
     )),
     new Currency(array(
       'code' => 'UAH',
+      'minor_unit' => 2,
       'sign' => 'грн.',
       'title' => t('Ukraine Hryvnia'),
     )),
diff --git a/currency.info b/currency.info
index 9f6abf8..1ee22ba 100755
--- a/currency.info
+++ b/currency.info
@@ -5,4 +5,6 @@ configure = admin/config/regional/currency
 dependencies[] = ctools
 files[] = includes/Currency.inc
 files[] = includes/CurrencyBaseAbstract.inc
+files[] = includes/CurrencyPattern.inc
 files[] = tests/CurrencyCRUDWebTestCase.test
+files[] = tests/CurrencyPatternWebTestCase.test
diff --git a/includes/Currency.inc b/includes/Currency.inc
index 83ef5b3..e5963a8 100644
--- a/includes/Currency.inc
+++ b/includes/Currency.inc
@@ -60,4 +60,26 @@ class Currency extends CurrencyBaseAbstract {
    * @var string
    */
   public $title = '';
+
+  /**
+   * Gets an amount's major unit.
+   *
+   * @param integer|string $amount
+   *
+   * @return string
+   */
+  function amountMajorUnit($amount) {
+    return $this->minor_unit > 0 ? substr($amount, 0, -$this->minor_unit) : $amount;
+  }
+
+  /**
+   * Gets an amount's minor unit.
+   *
+   * @param integer|string $amount
+   *
+   * @return string
+   */
+  function amountMinorUnit($amount) {
+    return $this->minor_unit > 0 ? substr($amount, -$this->minor_unit) : '';
+  }
 }
diff --git a/includes/CurrencyPattern.inc b/includes/CurrencyPattern.inc
new file mode 100644
index 0000000..10d08e3
--- /dev/null
+++ b/includes/CurrencyPattern.inc
@@ -0,0 +1,235 @@
+<?php
+
+/**
+ * @file
+ * Contains class CurrencyPattern.
+ */
+
+/**
+ * Describes a Unicode CLDR currency number pattern.
+ */
+class CurrencyPattern {
+
+  /**
+   * The pattern for a positive amount.
+   *
+   * @var integer
+   */
+  const POSITIVE = 0;
+
+  /**
+   * The pattern for a negative amount.
+   *
+   * @var integer
+   */
+  const NEGATIVE = 1;
+
+  /**
+   * The pattern for an amount's major unit.
+   *
+   * @var integer
+   */
+  const MAJOR = 0;
+
+  /**
+   * The pattern for an amount's minor unit.
+   *
+   * @var integer
+   */
+  const MINOR = 1;
+
+  /**
+   * A Unicode CLDR currency number pattern, without short number support.
+   *
+   * @see http://cldr.unicode.org/translation/number-patterns
+   *
+   * @var string
+   *   See the Unicode documentation for details. Additionally, the following
+   *   replacements will be made:
+   *   - [XXX] will be replaced by the ISO 4217 currency code.
+   *   - [999] will be replaced by the ISO 4217 currency number.
+   */
+  public $pattern = '';
+
+  /**
+   * The character(s) used as decimal separator.
+   *
+   * @var string
+   */
+  public $decimal_separator = '';
+
+  /**
+   * The character(s) used as grouping (aka thousands) separator.
+   *
+   * @var string
+   */
+  public $grouping_separator = '';
+
+  /**
+   * Implements __construct().
+   */
+  function __construct($pattern, $decimal_separator, $grouping_separator) {
+    self::validatePattern($pattern);
+    $this->pattern = $pattern;
+    $this->decimal_separator = $decimal_separator;
+    $this->grouping_separator = $grouping_separator;
+  }
+
+  /**
+   * Splits a string in a unicode-safe way.
+   *
+   * @param string
+   *
+   * @return array
+   */
+  static function str_split($string, $split_length = 1) {
+    preg_match_all('/.{' . $split_length . '}/u', $string, $matches);
+
+    return $matches[0];
+  }
+
+  /**
+   * Reverses a string in a unicode-safe way.
+   *
+   * @param string
+   *
+   * @return string
+   */
+  static function strrev($string) {
+    return implode(array_reverse(self::str_split($string)));
+  }
+
+  /**
+   * Split the pattern into its positive and negative fragments.
+   *
+   * @return array
+   */
+  static function patternSplitPositiveNegative($pattern) {
+    return preg_split("#(?<!');(?!')#", $pattern);
+  }
+
+  /**
+   * Split the pattern into its major and minor fragments.
+   *
+   * @param string $pattern
+   *
+   * @return array
+   */
+  static function patternSplitMajorMinor($pattern) {
+    return preg_split("#(?<!')\.(?!')#", $pattern);
+  }
+
+  /**
+   * Validate a pattern.
+   *
+   * @throws Exception
+   *
+   * @param string $pattern
+   */
+  static function validatePattern($pattern) {
+    if (empty($pattern)) {
+      throw new Exception(t('Empty currency pattern.'));
+    }
+    foreach (self::patternSplitPositiveNegative($pattern) as $fragment) {
+      $position = strpos($fragment, '.');
+      if (count(explode('.', $fragment)) != 2) {
+        throw new Exception(t('The pattern <code>@pattern/code> contains more or less than one period (<code>.</code>) as a decimal separator.', array(
+          '@pattern' => $pattern,
+        )));
+      }
+      if (strpos($fragment, '00', strpos($fragment, '.')) == FALSE) {
+        throw new Exception(t('The pattern <code>@pattern/code> does not contain a double zero (<code>00</code>) as a placeholder for decimal digits.', array(
+          '@pattern' => $pattern,
+        )));
+      }
+    }
+  }
+
+  /**
+   * Get a fragment of a pattern.
+   *
+   * @param integer $sign
+   *   self::POSITIVE or self::NEGATIVE.
+   * @param integer $majorminor
+   *   self::MAJOR or self::MINOR.
+   *
+   * @return string
+   */
+  function getPatternFragment($sign, $majorminor) {
+    $fragments = &drupal_static('CurrencyPattern' . $this->pattern);
+
+    if (is_null($fragments)) {
+      self::validatePattern($this->pattern);
+      $fragments = $this->patternSplitPositiveNegative($this->pattern);
+      if (count($fragments) == 1) {
+        $fragments[1] = '-' . $this->pattern;
+      }
+      foreach ($fragments as $sign => $fragment) {
+        $fragments[$sign] = self::patternSplitMajorMinor($fragment);
+      }
+    }
+
+    return $fragments[$sign][$majorminor];
+  }
+
+  /**
+   * Formats an amount.
+   *
+   * @throws Exception
+   *
+   * @param Currency $currency
+   * @param integer $amount
+   *
+   * @return string
+   */
+  function format(Currency $currency, $amount) {
+    $sign = (int) ($amount < 0);
+
+    $output = array(
+      self::MAJOR => '',
+      self::MINOR => '',
+    );
+
+    // Render the minor unit.
+    $output[self::MINOR] = str_replace('00', $currency->amountMinorUnit($amount), $this->getPatternFragment($sign, self::MINOR));
+
+    // Render the major unit.
+    // Split the pattern into its characters in a unicode-safe way.
+    $characters = array_reverse(preg_split('//u', $this->getPatternFragment($sign, self::MAJOR)));
+    // We keep track of the first replaceable character in the pattern, so if
+    // the amount has more digits than there are replaceable characters, we
+    // replace the first character by all remaining digits.
+    $digits = array_reverse(str_split($currency->amountMajorUnit($amount)));
+    foreach ($characters as $position => $character) {
+      // Replace replaceable characters, but only if we still have digits left
+      // to replace them with.
+      if ($digits && ($character == '#' || $character ==='0')) {
+        $output[self::MAJOR] .= array_shift($digits);
+      }
+      else {
+        $output[self::MAJOR] .= $character;
+      }
+    }
+    // Remove remaining hashes and whatever is between them and the last
+    // digit.
+    $output[self::MAJOR] = preg_replace('/(\D*)#/', '', $output[self::MAJOR]);
+    // Put the major unit back in its original order.
+    $output[self::MAJOR] = $this->strrev($output[self::MAJOR]);
+
+    foreach ($output as &$output_sign) {
+      // Insert the grouping separator, currency sign, code, and number.
+      $output_sign = str_replace(',', $this->grouping_separator, $output_sign);
+      $output_sign = str_replace('¤', $currency->sign, $output_sign);
+      $output_sign = str_replace('[XXX]', $currency->code, $output_sign);
+      $output_sign = str_replace('[999]', $currency->number, $output_sign);
+      // Unescape escaped characters.
+      $output_sign = preg_replace("#'(.)'#", "\\1", $output_sign);
+    }
+
+    if ($output[self::MINOR]) {
+      $output[self::MINOR] = $this->decimal_separator . $output[self::MINOR];
+    }
+
+    return implode($output);
+  }
+}
diff --git a/tests/CurrencyPatternWebTestCase.test b/tests/CurrencyPatternWebTestCase.test
new file mode 100644
index 0000000..6de53d0
--- /dev/null
+++ b/tests/CurrencyPatternWebTestCase.test
@@ -0,0 +1,101 @@
+<?php
+
+/**
+ * @file
+ * Contains class CurrencyPatternWebTestCase.
+ */
+
+/**
+ * Tests Currency's functionality.
+ */
+class CurrencyPatternWebTestCase extends DrupalWebTestCase {
+
+  static function getInfo() {
+    return array(
+      'name' => 'Currency formatting',
+      'group' => 'Currency',
+    );
+  }
+
+  function setUp(array $modules = array()) {
+    parent::setUp($modules + array('currency'));
+  }
+
+  /**
+   * Test amount formatting.
+   */
+  function testFormat() {
+    ctools_include('export');
+    $amounts = array(123456789, -123456789);
+    foreach (ctools_export_crud_load_all('currency') as $currency) {
+      if (!is_null($currency->minor_unit)) {
+        foreach ($amounts as $i => $amount) {
+          $minor_unit = $currency->amountMinorUnit($amount);
+          // Patterns are grouped by the number of currency decimals they
+          // should be tested with.
+          $patterns = array(
+            0 => array(
+              array(
+                'pattern' => new CurrencyPattern('¤###,###,###,##0.00;¤-###,###,###,##0.00', ',', '.'),
+                'results' => array(
+                  $currency->sign . '123.456.789',
+                  $currency->sign . '-123.456.789',
+                ),
+              ),
+            ),
+            // Because most currencies have 2 decimals, we will use those for
+            // more extensive testing.
+            2 => array(
+              // Test default negative patterns.
+              array(
+                'pattern' => new CurrencyPattern('¤##,####,###,##0.00', ',', '.'),
+                'results' => array(
+                  $currency->sign . '1.234.567,89',
+                  '-' . $currency->sign . '1.234.567,89',
+                ),
+              ),
+              array(
+                // Test identical decimal and grouping separators, and a custom negative format.
+                'pattern' => new CurrencyPattern('¤###,###,###,##0.00;¤-###,###,###,##0.00', '.', '.'),
+                'results' => array(
+                  $currency->sign . '1.234.567.89',
+                  $currency->sign . '-1.234.567.89',
+                ),
+              ),
+              array(
+                'pattern' => new CurrencyPattern('[XXX] ##0.00;[XXX] ##0.00', ',', '.'),
+                'results' => array(
+                  $currency->code . ' 567,89',
+                  $currency->code . ' 567,89',
+                ),
+              ),
+              // Test some unusual character combinations and positions, and an
+              // empty decimal separator.
+              array(
+                'pattern' => new CurrencyPattern("¤####000/@##0.<span style=\"text-transform: uppercase';'\">00[XXX]</span>--;-¤####000/@##0.<span style=\"text-transform: uppercase';'\">00[XXX]</span>--", '', '.'),
+                'results' => array(
+                  $currency->sign . '1234/@567<span style="text-transform: uppercase;">89' . $currency->code . '</span>--',
+                  '-' . $currency->sign . '1234/@567<span style="text-transform: uppercase;">89' . $currency->code . '</span>--',
+                ),
+              ),
+            ),
+            3 => array(
+              array(
+                'pattern' => new CurrencyPattern('¤###,###,###,##0.00;¤-###,###,###,##0.00', '.', ','),
+                'results' => array(
+                  $currency->sign . '123,456.789',
+                  $currency->sign . '-123,456.789',
+                ),
+              ),
+            ),
+          );
+          foreach ($patterns[$currency->minor_unit] as $pattern_info) {
+            $pattern = $pattern_info['pattern'];
+            $result = $pattern_info['results'][$i];
+            $this->assertEqual($pattern->format($currency, $amount), $result, '<code>CurrencyPattern::format()</code> formats amount <code>' . $currency->code . $amount . '</code> as <code>' . $result . '</code> using pattern <code>' . $pattern->pattern . '</code>.');
+          }
+        }
+      }
+    }
+  }
+}
\ No newline at end of file
