diff --git a/currency.api.php b/currency.api.php
index 3feff6a..d0f2afe 100644
--- a/currency.api.php
+++ b/currency.api.php
@@ -34,3 +34,30 @@ function hook_currency_info_alter(array $currencies) {
   // Let's pretend the euro has 1000 subunits.
   $currencies['EUR']['minorUnit'] = 3;
 }
+
+/**
+ * Expose CLDR number patterns for locales.
+ *
+ * @return array
+ *   An array of CurrencyLocalePattern objects, keyed by their locales.
+ */
+function hook_locale_pattern_info() {
+  $locale_patterns['nl_NL'] = new CurrencyLocalePattern(array(
+    'locale' => 'nl_NL',
+    'pattern' => '¤#.##0,0#',
+  ));
+
+  return $locale_patterns;
+}
+
+/**
+ * Alter exposed currency locale patterns.
+ *
+ * @param array
+ *   An array of CurrencyLocalePattern objects, keyed by their locales.
+ *
+ * @return NULL
+ */
+function hook_locale_pattern_info_alter(array $locale_patterns) {
+  $locale_patterns['nl_NL']->pattern = '¤ #.##0,0#';
+}
diff --git a/currency.currency.inc b/currency.currency.inc
index 65626de..cc879c2 100644
--- a/currency.currency.inc
+++ b/currency.currency.inc
@@ -11,3 +11,51 @@
 function currency_currency_info() {
   return Currency::resourceLoadAll();
 }
+
+/**
+ * Implements hook_locale_pattern_info().
+ */
+function currency_currency_locale_pattern_info() {
+  $locale_patterns = array(
+    new CurrencyLocalePattern(array(
+      'locale' => 'de_DE',
+      'pattern' => 'XXX #,##0.00;XXX -#,##0.00',
+      'symbol_decimal_separator' => ',',
+      'symbol_grouping_separator' => '.',
+    )),
+    new CurrencyLocalePattern(array(
+      'locale' => 'fr_FR',
+      'pattern' => 'XXX #,##0.00;XXX -#,##0.00',
+      'symbol_decimal_separator' => ',',
+      'symbol_grouping_separator' => '.',
+    )),
+    new CurrencyLocalePattern(array(
+      'locale' => 'nl_BE',
+      'pattern' => '¤#,##0.00;¤-#,##0.00',
+      'symbol_decimal_separator' => ',',
+      'symbol_grouping_separator' => '.',
+    )),
+    new CurrencyLocalePattern(array(
+      'locale' => 'nl_NL',
+      'pattern' => '¤#,##0.00;¤-#,##0.00',
+      'symbol_decimal_separator' => ',',
+      'symbol_grouping_separator' => '.',
+    )),
+    new CurrencyLocalePattern(array(
+      'locale' => 'en_UK',
+      'pattern' => '¤#,##0.00;¤-#,##0.00',
+    )),
+    new CurrencyLocalePattern(array(
+      'locale' => 'en_US',
+      'pattern' => '¤#,##0.00;¤-#,##0.00',
+    )),
+  );
+
+  // Key patterns by their locales, which act as their machine names.
+  $locale_patterns_keyed = array();
+  foreach ($locale_patterns as $locale_pattern) {
+    $locale_patterns_keyed[$locale_pattern->locale] = $locale_pattern;
+  }
+
+  return $locale_patterns_keyed;
+}
diff --git a/currency.info b/currency.info
index 9a713ac..bb8f690 100755
--- a/currency.info
+++ b/currency.info
@@ -1,7 +1,13 @@
 name = Currency
 description = Provides currency information and allows users to add custom currencies.
 core = 7.x
+php = 5.3
 configure = admin/config/regional/currency
 dependencies[] = ctools
 files[] = includes/Currency.inc
+files[] = includes/CurrencyExportableInterface.inc
+files[] = includes/CurrencyLocalePattern.inc
 files[] = tests/CurrencyCRUDWebTestCase.test
+files[] = tests/CurrencyLocalePatternCRUDWebTestCase.test
+files[] = tests/CurrencyLocalePatternWebTestCase.test
+files[] = tests/CurrencyWebTestCase.test
diff --git a/currency.install b/currency.install
index b95dfd6..02fbd97 100644
--- a/currency.install
+++ b/currency.install
@@ -61,6 +61,42 @@ function currency_schema() {
       $schema['currency']['fields'][$property]['object default'] = $default_value;
     }
   }
+  $schema['currency_locale_pattern'] = array(
+    'description' => 'Locale-specific amount formatting.',
+    'export' => array(
+      'key' => 'locale',
+      'key name' => 'Locale',
+      'default hook' => 'currency_locale_pattern_info',
+      'object' => 'CurrencyLocalePattern',
+      'save callback' => 'currency_ctools_export_object_save',
+    ),
+    'fields' => array(
+      'export_module' => array(
+        'type' => 'varchar',
+        'length' => 255,
+      ),
+      'export_type' => array(
+        'type' => 'int',
+        'length' => 'tiny',
+      ),
+      'locale' => array(
+        'type' => 'varchar',
+        'length' => 5,
+      ),
+      'pattern' => array(
+        'type' => 'varchar',
+        'length' => 255,
+      ),
+    ),
+    'primary key' => array('locale'),
+  );
+  // Set Ctools object defaults based on CurrencyLocalePattern's default
+  // values.
+  foreach (get_class_vars('CurrencyLocalePattern') as $property => $default_value) {
+    if (isset($schema['currency_locale_pattern']['fields'][$property])) {
+      $schema['currency_locale_pattern']['fields'][$property]['object default'] = $default_value;
+    }
+  }
 
   return $schema;
 }
diff --git a/currency.module b/currency.module
index 6165b07..44fc42c 100644
--- a/currency.module
+++ b/currency.module
@@ -18,6 +18,11 @@ define('CURRENCY_INDICATOR_BEFORE', 1);
 define('CURRENCY_INDICATOR_AFTER', 2);
 
 /**
+ * The default locale.
+ */
+define('CURRENCY_DEFAULT_LOCALE', 'en_US');
+
+/**
  * Implements hook_hook_info().
  */
 function currency_hook_info() {
@@ -27,6 +32,12 @@ function currency_hook_info() {
   $hooks['currency_info_alter'] = array(
     'group' => 'currency',
   );
+  $hooks['locale_pattern_info'] = array(
+    'group' => 'currency',
+  );
+  $hooks['locale_pattern_info_alter'] = array(
+    'group' => 'currency',
+  );
 
   return $hooks;
 }
@@ -40,3 +51,26 @@ function currency_views_api() {
     'path' => drupal_get_path('module', 'currency') . '/views',
   );
 }
+
+/**
+ * Implements Ctools exportables save callback.
+ */
+function currency_ctools_export_object_save(CurrencyExportableInterface $exportable) {
+  $exportable->validate();
+  $exportable->export_type = $exportable->export_type | EXPORT_IN_DATABASE;
+  $schema = drupal_get_schema($exportable->table);
+    $fields = array();
+  foreach ($schema['fields'] as $field => $info) {
+    if (property_exists($exportable, $field)) {
+      $fields[$field] = $exportable->$field;
+    }
+  }
+  $primary_key = $schema['primary key'][0];
+
+  return db_merge($exportable->table)
+    ->key(array(
+      $primary_key => $exportable->$primary_key,
+    ))
+    ->fields($fields)
+    ->execute();
+}
diff --git a/includes/Currency.inc b/includes/Currency.inc
index 8a47100..09e4971 100644
--- a/includes/Currency.inc
+++ b/includes/Currency.inc
@@ -46,4 +46,19 @@ class Currency extends BartFeenstra\Currency\Currency {
       $this->$property = $value;
     }
   }
+
+  /**
+   * Format an amount using this currency and the environment's default locale
+   * pattern.
+   *
+   * This is a wrapper for CurrencyLocalePattern::format() in situations where
+   * the environment's default locale pattern should be used.
+   *
+   * @param float $amount
+   *
+   * @return string
+   */
+  function format($amount) {
+    return CurrencyLocalePattern::loadFromEnv()->format($this, $amount);
+  }
 }
diff --git a/includes/CurrencyExportableInterface.inc b/includes/CurrencyExportableInterface.inc
new file mode 100644
index 0000000..a65746e
--- /dev/null
+++ b/includes/CurrencyExportableInterface.inc
@@ -0,0 +1,15 @@
+<?php
+
+/**
+ * Contains interface CurrencyExportableInterface.
+ */
+
+interface CurrencyExportableInterface {
+
+  /**
+   * Validates the exportable.
+   *
+   * @throws Exception
+   */
+  function validate();
+}
\ No newline at end of file
diff --git a/includes/CurrencyLocalePattern.inc b/includes/CurrencyLocalePattern.inc
new file mode 100644
index 0000000..2150815
--- /dev/null
+++ b/includes/CurrencyLocalePattern.inc
@@ -0,0 +1,152 @@
+<?php
+
+/**
+ * @file
+ * Contains class CurrencyLocalePattern.
+ */
+
+use BartFeenstra\CLDR\CurrencyFormatter;
+
+/**
+ * A currency pattern for a locale.
+ */
+class CurrencyLocalePattern implements CurrencyExportableInterface {
+
+  /**
+   * The locale as a combination of an ISO 639-1 language code and an ISO 3166
+   * country code, separated by an underscore.
+   *
+   * @var string
+   */
+  public $locale = '';
+
+  /**
+   * Implements Chaos tools' exportable object "export_module" property.
+   */
+  public $export_module = 'currency';
+
+  /**
+   * Implements Chaos tools' exportable object "export_type" property.
+   */
+  public $export_type = 0;
+
+  /**
+   * The Unicode CLDR number pattern.
+   *
+   * @var string
+   */
+  public $pattern = '';
+
+  /**
+   * The decimal separator's replacement.
+   *
+   * @var string
+   */
+  public $symbol_decimal_separator = '.';
+
+  /**
+   * The grouping separator's replacement.
+   *
+   * @var string
+   */
+  public $symbol_grouping_separator = ',';
+
+  /**
+   * Implements Chaos tools' exportable object "table" property.
+   */
+  public $table = 'currency_locale_pattern';
+
+  /**
+   * Implements Ctools' exportables "type" property.
+   *
+   * @var string
+   */
+  public $type = 'Local';
+
+  /**
+   * Implements __construct().
+   */
+  function __construct(array $properties = array()) {
+    foreach ($properties as $property => $value) {
+      $this->$property = $value;
+    }
+  }
+
+  /**
+   * Implements CurrencyExportableInterface::validate().
+   */
+  function validate() {
+    $properties = array('locale', 'pattern');
+    foreach ($properties as $property) {
+      if (!strlen($this->$property)) {
+        throw new Exception(t('Missing value for %property.', array(
+          '%property' => $property,
+        )));
+      }
+    }
+  }
+
+  /**
+   * Formats an amount using this pattern.
+   *
+   * @param Currency $currency
+   * @param float $amount
+   *
+   * @return string
+   */
+  function format(Currency $currency, $amount) {
+    static $formatter = NULL;
+
+    if (is_null($formatter) || $formatter->pattern != $this->pattern) {
+      $formatter = new CurrencyFormatter($this->pattern, array(
+        CurrencyFormatter::SYMBOL_SPECIAL_DECIMAL_SEPARATOR => $this->symbol_decimal_separator,
+        CurrencyFormatter::SYMBOL_SPECIAL_GROUPING_SEPARATOR => $this->symbol_grouping_separator,
+      ));
+    }
+
+    $formatted = $formatter->format($amount, $currency->sign);
+    $formatted = str_replace(array('[XXX]', '[999]'), array($currency->ISO4217Code, $currency->ISO4217Number), $formatted);
+
+    return $formatted;
+  }
+
+  /**
+   * Loads a single CurrencyLocalePattern based on environment variables.
+   *
+   * If no country code is set in $language->currency_country_code, the
+   * "site_default_country" system variable will be used instead. If a
+   * CurrencyLocalePattern could not be loaded using these country sources and
+   * $language->language, the locale pattern for en_US will be loaded. This is
+   * consistent with Drupal's default language, which is US English.
+   *
+   * @throws RuntimeException
+   *
+   * @return CurrencyLocalePattern
+   */
+  static function loadFromEnv() {
+    global $language;
+
+    $locale_pattern = &drupal_static('currency_env_locale_pattern');
+
+    if (is_null($locale_pattern)) {
+      // Try this request's country code.
+      if (isset($language->currency_country_code)) {
+        $country_code = $language->currency_country_code;
+        $locale_pattern = ctools_export_crud_load('currency_locale_pattern', $language->language . '_' . $language->currency_country_code);
+      }
+      // Try the global default country code.
+      if (!$locale_pattern && $country_code = variable_get('site_default_country', '')) {
+        $locale_pattern = ctools_export_crud_load('currency_locale_pattern', $language->language . '_' . $country_code);
+      }
+      // Try the Currency default.
+      if (!$locale_pattern) {
+        $locale_pattern = ctools_export_crud_load('currency_locale_pattern', CURRENCY_DEFAULT_LOCALE);
+      }
+      if (!$locale_pattern) {
+        throw new RuntimeException(t('The CurrencyLocalePattern en_US could not be loaded.'));
+      }
+    }
+
+    return $locale_pattern;
+  }
+}
diff --git a/tests/CurrencyLocalePatternCRUDWebTestCase.test b/tests/CurrencyLocalePatternCRUDWebTestCase.test
new file mode 100644
index 0000000..c62fdb1
--- /dev/null
+++ b/tests/CurrencyLocalePatternCRUDWebTestCase.test
@@ -0,0 +1,76 @@
+<?php
+
+/**
+ * @file
+ * Contains class CurrencyLocalePatternCRUDWebTestCase.
+ */
+
+/**
+ * Tests CRUD.
+ */
+class CurrencyLocalePatternCRUDWebTestCase extends DrupalWebTestCase {
+
+  static function getInfo() {
+    return array(
+      'name' => 'Currency locale pattern CRUD',
+      'group' => 'Currency',
+    );
+  }
+
+  function setUp(array $modules = array()) {
+    parent::setUp($modules + array('currency'));
+  }
+
+  /**
+   * Checks if a locale pattern exists in the database.
+   *
+   * @param CurrencyLocalePattern $locale_pattern
+   *
+   * @return boolean
+   */
+  function localePatternExists(CurrencyLocalePattern $locale_pattern) {
+    $schema = drupal_get_schema('currency_locale_pattern');
+    $query = db_select('currency_locale_pattern');
+    foreach (array_keys($schema['fields']) as $property) {
+      $query->condition($property, $locale_pattern->$property);
+    }
+
+    return $query->countQuery()
+      ->execute()->fetchField();
+  }
+
+  /**
+   * Test CRUD functionality.
+   */
+  function testCRUD() {
+    // Test creating a new locale pattern.
+    $this->assertEqual(ctools_export_crud_new('currency_locale_pattern'), new CurrencyLocalePattern(), 'Chaos tools correctly creates a new currency locale pattern.');
+
+    // Test inserting a locale pattern.
+    $locale_pattern = new CurrencyLocalePattern(array(
+      'locale' => 'nl_NL',
+      'pattern' => 'foo',
+    ));
+    ctools_export_crud_save('currency_locale_pattern', $locale_pattern);
+    $this->assertTrue($this->localePatternExists($locale_pattern), 'Chaos tools correctly inserts a currency locale pattern.');
+
+    // Test loading a locale pattern.
+    $locale_pattern_loaded = ctools_export_crud_load('currency_locale_pattern', $locale_pattern->locale);
+    // The "export_type" and "type" properties are supposed to change.
+    $locale_pattern_original = clone $locale_pattern;
+    unset($locale_pattern_original->export_type);
+    unset($locale_pattern_original->type);
+    unset($locale_pattern_loaded->export_type);
+    unset($locale_pattern_loaded->type);
+    $this->assertEqual($locale_pattern_original, $locale_pattern_loaded, 'Chaos tools correctly loads a currency locale pattern.');
+
+    // Test updating a locale pattern.
+    $locale_pattern->pattern = 'bar';
+    ctools_export_crud_save('currency_locale_pattern', $locale_pattern);
+    $this->assertTrue($this->localePatternExists($locale_pattern), 'Chaos tools correctly updates a currency locale pattern.');
+
+    // Test deleting a locale pattern.
+    ctools_export_crud_delete('currency_locale_pattern', $locale_pattern->locale);
+    $this->assertFalse($this->localePatternExists($locale_pattern), 'Chaos tools correctly deletes a currency locale pattern.');
+  }
+}
diff --git a/tests/CurrencyLocalePatternWebTestCase.test b/tests/CurrencyLocalePatternWebTestCase.test
new file mode 100644
index 0000000..87f60f6
--- /dev/null
+++ b/tests/CurrencyLocalePatternWebTestCase.test
@@ -0,0 +1,39 @@
+<?php
+
+/**
+ * @file
+ * Contains class CurrencyLocalePatternWebTestCase.
+ */
+
+/**
+ * Tests class CurrencyLocalePattern.
+ */
+class CurrencyLocalePatternWebTestCase extends DrupalWebTestCase {
+
+  static function getInfo() {
+    return array(
+      'name' => 'CurrencyLocalePattern functionality',
+      'group' => 'Currency',
+    );
+  }
+
+  function setUp(array $modules = array()) {
+    parent::setUp($modules + array('currency'));
+  }
+
+  /**
+   * Test format().
+   */
+  function testFormat() {
+    $currency = ctools_export_crud_load('currency', 'EUR');
+    $locale_pattern = new CurrencyLocalePattern(array(
+      'pattern' => '¤-#,##0.00[XXX][999]',
+      'symbol_decimal_separator' => ',',
+      'symbol_grouping_separator' => '.',
+    ));
+    $amount = 12345.6789;
+    $formatted = $locale_pattern->format($currency, $amount);
+    $formatted_expected = '€-12.345,6789EUR978';
+    $this->assertEqual($formatted, $formatted_expected);
+  }
+}
diff --git a/tests/CurrencyWebTestCase.test b/tests/CurrencyWebTestCase.test
new file mode 100644
index 0000000..b6b6d2c
--- /dev/null
+++ b/tests/CurrencyWebTestCase.test
@@ -0,0 +1,34 @@
+<?php
+
+/**
+ * @file
+ * Contains class CurrencyWebTestCase.
+ */
+
+/**
+ * Tests class Currency.
+ */
+class CurrencyWebTestCase extends DrupalWebTestCase {
+
+  static function getInfo() {
+    return array(
+      'name' => 'Currency functionality',
+      'group' => 'Currency',
+    );
+  }
+
+  function setUp(array $modules = array()) {
+    parent::setUp($modules + array('currency'));
+  }
+
+  /**
+   * Test format().
+   */
+  function testFormat() {
+    $currency = ctools_export_crud_load('currency', 'EUR');
+    $amount = 12345.6789;
+    $formatted = $currency->format($amount);
+    $formatted_expected = '€12,345.6789';
+    $this->assertEqual($formatted, $formatted_expected, 'Currency::format() correctly formats an amount.');
+  }
+}
