diff --git a/commandLineTool/README.md b/commandLineTool/README.md
new file mode 100644
index 0000000..a0fb1f3
--- /dev/null
+++ b/commandLineTool/README.md
@@ -0,0 +1,238 @@
+Smartling Python Translation Tool
+=======
+
+Command line tool to upload, download, and import translation to Smartling 
+
+Usage
+----------
+
+```bash
+$ python smartlingTool.py -h
+usage: smartlingTool.py [-h] [-k APIKEY] [-p PROJECTID]
+                        [-c CONFIGFILE]
+                        {upload,download,import} ...
+
+Smartling Translation Tool to upload, download, and import translation files
+
+positional arguments:
+  {upload,download,import}   To see individual sub command help: 'subcommand -h'
+    upload              Upload the (English) source
+    download            Download the translations
+    import              Import translations
+
+optional arguments:
+  -h, --help            show this help message and exit
+  -k APIKEY, --apiKey APIKEY
+                        Smartling API key (overrides configuration file value)
+  -p PROJECTID, --projectId PROJECTID
+                        Smartling project ID (overrides configuration file
+                        value)
+  -c CONFIGFILE, --config CONFIGFILE
+                        Configuration file (default ./translation.cfg)
+```
+
+### Upload
+
+```bash
+$ python smartlingTool.py upload -h
+usage: smartlingTool.py upload [-h] -d DIR [-u URIPATH] [--run]
+
+optional arguments:
+  -h, --help            show this help message and exit
+  -d DIR, --dir DIR     Path to English source directory or file
+  -u URIPATH, --uriPath URIPATH
+                        File URI path used in Smartling system
+  --run                 Run for real (default is noop)
+```
+
+### Download
+
+Any translation that is not 100% complete will be skipped.
+Translation which are not 100% and are download will include English in their place.
+
+```bash
+$ python smartlingTool.py download -h
+usage: smartlingTool.py download [-h] -d DIR -o OUTPUTDIR [-u URIPATH]
+                                 [-l LOCALE] [-p] [-s] [--run]
+
+optional arguments:
+  -h, --help            show this help message and exit
+  -d DIR, --dir DIR     Path to English source directory of file
+  -o OUTPUTDIR, --outputDir OUTPUTDIR
+                        Output directory where to save translated files.
+                        Stores each translation in their own sub-directory.
+  -u URIPATH, --uriPath URIPATH
+                        File URI path used in Smartling system
+  -l LOCALE, --locale LOCALE
+                        Locale to download (default is all)
+  -p, --allowPartial    Allow translation not 100% complete (default is false)
+  -s, --pseudo          Download pseudo translations
+  --run                 Run for real (default is noop)
+```
+
+### Import
+
+```bash
+$ python smartlingTool.py import -h
+usage: smartlingTool.py import [-h] -d DIR [-u URIPATH] -l LOCALE
+                               [-o] [--run]
+
+optional arguments:
+  -h, --help            show this help message and exit
+  -d DIR, --dir DIR     Path to translations directory or file
+  -u URIPATH, --uriPath URIPATH
+                        File URI path used in Smartling system
+  -l LOCALE, --locale LOCALE
+                        Locale to import
+  -o, --overwrite       Overwrite previous translations
+  --run                 Run for real (default is noop)
+```
+
+Configuration File
+----------
+
+Default location for configuration file: `translation.cfg`
+
+```
+[smartling]
+# Project API Key
+apiKey = XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXX
+
+# Project ID
+projectId = XXXXXXXX
+
+# Custom URI path (default "/files")
+uriPath = /files/config-ui
+
+[directives]
+# Smartling directives
+# See: https://docs.smartling.com/display/docs/Supported+File+Types
+translate_mode = all
+source_key_paths = {*}
+placeholder_format_custom = __\w+__|\{{2,2}[\w\.]+\}{2,2}
+variants_enabled = true
+
+[locales]
+# Locale Mapping - Use to map differences between project and Smartling locale codes
+en = en-US
+de = de-DE
+es = es-ES
+fr = fr-FR
+ja = ja-JP
+pt = pt-PT
+pt_BR = pt-BR
+
+
+[filters]
+# Common delimited list of file extensions (default all files)
+file_extensions =
+
+
+[extensions]
+# Extension Mapping - Use to map differences between file types and Smartling file types
+
+# Gettext .pot and .po files
+gettext = pot,po
+
+# HTML files
+html = html,htm
+
+# Java Properties
+javaProperties = properties
+
+# Yaml files
+yaml = yml
+
+# Supports .xlf, .xliff, and .xml files that use the XML Localization Interchange File Format (XLIFF)
+xliff = xlf,xliff
+
+# Javascript files
+json = json,js
+
+# Qt Linguist TS format files
+qt = ts
+
+# MadCap Flare ZIP packages
+madcap = zip
+```
+
+Examples
+----------
+
+### Upload
+
+```bash
+$ python smartlingTool.py upload --dir /workspace/config-ui/static/locales/en
+2014-11-03 10:44:06,208 - INFO - Uploading source files from: /workspace/config-ui/static/locales/en
+2014-11-03 10:44:06,209 - INFO - Upload (noop): /workspace/config-ui/static/locales/en/badges.json -> /files/config-ui/badges.json
+2014-11-03 10:44:06,209 - INFO - Upload (noop): /workspace/config-ui/static/locales/en/common.json -> /files/config-ui/common.json
+2014-11-03 10:44:06,209 - INFO - Upload (noop): /workspace/config-ui/static/locales/en/container.json -> /files/config-ui/container.json
+2014-11-03 10:44:06,209 - INFO - Upload (noop): /workspace/config-ui/static/locales/en/editCopy.json -> /files/config-ui/editCopy.json
+2014-11-03 10:44:06,209 - INFO - Upload (noop): /workspace/config-ui/static/locales/en/preview.json -> /files/config-ui/preview.json
+2014-11-03 10:44:06,209 - INFO - Upload (noop): /workspace/config-ui/static/locales/en/submission.json -> /files/config-ui/submission.json
+```
+
+### Download
+
+```bash
+python smartlingTool.py download --dir /workspace/config-ui/static/locales/en --outputDir /workspace/config-ui/static/locales
+2014-11-03 10:44:28,670 - INFO - Downloading translated files for: /workspace/config-ui/static/locales/en
+2014-11-03 10:44:29,576 - INFO - Fetching translations (noop) for French (France) (fr-FR)
+2014-11-03 10:44:30,172 - INFO - Translated 100% (fr-FR): /files/config-ui/badges.json -> /workspace/config-ui/static/locales/fr/badges.json
+2014-11-03 10:44:30,734 - INFO - Translated 100% (fr-FR): /files/config-ui/common.json -> /workspace/config-ui/static/locales/fr/common.json
+2014-11-03 10:44:31,228 - INFO - Translated 100% (fr-FR): /files/config-ui/container.json -> /workspace/config-ui/static/locales/fr/container.json
+2014-11-03 10:44:32,506 - INFO - Translated 100% (fr-FR): /files/config-ui/editCopy.json -> /workspace/config-ui/static/locales/fr/editCopy.json
+2014-11-03 10:44:32,965 - INFO - Translated 100% (fr-FR): /files/config-ui/preview.json -> /workspace/config-ui/static/locales/fr/preview.json
+2014-11-03 10:44:33,774 - INFO - Translated 100% (fr-FR): /files/config-ui/submission.json -> /workspace/config-ui/static/locales/fr/submission.json
+2014-11-03 10:44:33,774 - INFO - Fetching translations (noop) for German (Germany) (de-DE)
+2014-11-03 10:44:34,455 - INFO - Translated 100% (de-DE): /files/config-ui/badges.json -> /workspace/config-ui/static/locales/de/badges.json
+2014-11-03 10:44:34,993 - INFO - Translated 100% (de-DE): /files/config-ui/common.json -> /workspace/config-ui/static/locales/de/common.json
+2014-11-03 10:44:35,563 - INFO - Translated 100% (de-DE): /files/config-ui/container.json -> /workspace/config-ui/static/locales/de/container.json
+2014-11-03 10:44:36,065 - INFO - Translated 100% (de-DE): /files/config-ui/editCopy.json -> /workspace/config-ui/static/locales/de/editCopy.json
+2014-11-03 10:44:36,488 - INFO - Translated 100% (de-DE): /files/config-ui/preview.json -> /workspace/config-ui/static/locales/de/preview.json
+2014-11-03 10:44:37,085 - INFO - Translated 100% (de-DE): /files/config-ui/submission.json -> /workspace/config-ui/static/locales/de/submission.json
+2014-11-03 10:44:37,085 - INFO - Fetching translations (noop) for Japanese (ja-JP)
+2014-11-03 10:44:37,564 - INFO - Translated 100% (ja-JP): /files/config-ui/badges.json -> /workspace/config-ui/static/locales/ja/badges.json
+2014-11-03 10:44:38,124 - INFO - Translated 100% (ja-JP): /files/config-ui/common.json -> /workspace/config-ui/static/locales/ja/common.json
+2014-11-03 10:44:38,670 - INFO - Translated 100% (ja-JP): /files/config-ui/container.json -> /workspace/config-ui/static/locales/ja/container.json
+2014-11-03 10:44:39,150 - INFO - Translated 100% (ja-JP): /files/config-ui/editCopy.json -> /workspace/config-ui/static/locales/ja/editCopy.json
+2014-11-03 10:44:39,666 - INFO - Translated 100% (ja-JP): /files/config-ui/preview.json -> /workspace/config-ui/static/locales/ja/preview.json
+2014-11-03 10:44:40,164 - INFO - Translated 100% (ja-JP): /files/config-ui/submission.json -> /workspace/config-ui/static/locales/ja/submission.json
+2014-11-03 10:44:40,164 - INFO - Fetching translations (noop) for Portuguese (Portugal) (pt-PT)
+2014-11-03 10:44:40,614 - INFO - Translated 100% (pt-PT): /files/config-ui/badges.json -> /workspace/config-ui/static/locales/pt/badges.json
+2014-11-03 10:44:41,375 - INFO - Translated 100% (pt-PT): /files/config-ui/common.json -> /workspace/config-ui/static/locales/pt/common.json
+2014-11-03 10:44:41,774 - INFO - Translated 100% (pt-PT): /files/config-ui/container.json -> /workspace/config-ui/static/locales/pt/container.json
+2014-11-03 10:44:42,218 - INFO - Translated 100% (pt-PT): /files/config-ui/editCopy.json -> /workspace/config-ui/static/locales/pt/editCopy.json
+2014-11-03 10:44:42,731 - INFO - Translated 100% (pt-PT): /files/config-ui/preview.json -> /workspace/config-ui/static/locales/pt/preview.json
+2014-11-03 10:44:43,180 - INFO - Translated 100% (pt-PT): /files/config-ui/submission.json -> /workspace/config-ui/static/locales/pt/submission.json
+2014-11-03 10:44:43,180 - INFO - Fetching translations (noop) for Spanish (Spain) (es-ES)
+2014-11-03 10:44:43,864 - INFO - Translated 100% (es-ES): /files/config-ui/badges.json -> /workspace/config-ui/static/locales/es/badges.json
+2014-11-03 10:44:44,288 - INFO - Translated 100% (es-ES): /files/config-ui/common.json -> /workspace/config-ui/static/locales/es/common.json
+2014-11-03 10:44:44,739 - INFO - Translated 100% (es-ES): /files/config-ui/container.json -> /workspace/config-ui/static/locales/es/container.json
+2014-11-03 10:44:45,142 - INFO - Translated 100% (es-ES): /files/config-ui/editCopy.json -> /workspace/config-ui/static/locales/es/editCopy.json
+2014-11-03 10:44:45,528 - INFO - Translated 100% (es-ES): /files/config-ui/preview.json -> /workspace/config-ui/static/locales/es/preview.json
+2014-11-03 10:44:45,968 - INFO - Translated 100% (es-ES): /files/config-ui/submission.json -> /workspace/config-ui/static/locales/es/submission.json
+2014-11-03 10:44:45,968 - INFO - Successfully - all source files translated!
+```
+
+### Import Directory
+
+```bash
+$ python smartlingTool.py import --dir /workspace/config-ui/static/locales/fr --locale fr
+2014-11-03 10:46:39,653 - INFO - Importing translation file(s) from: /workspace/config-ui/static/locales/fr
+2014-11-03 10:46:40,624 - INFO - Import translation (noop): /workspace/config-ui/static/locales/fr/badges.json -> /files/config-ui/badges.json
+2014-11-03 10:46:41,091 - INFO - Import translation (noop): /workspace/config-ui/static/locales/fr/common.json -> /files/config-ui/common.json
+2014-11-03 10:46:41,480 - INFO - Import translation (noop): /workspace/config-ui/static/locales/fr/container.json -> /files/config-ui/container.json
+2014-11-03 10:46:41,951 - INFO - Import translation (noop): /workspace/config-ui/static/locales/fr/editCopy.json -> /files/config-ui/editCopy.json
+2014-11-03 10:46:42,433 - INFO - Import translation (noop): /workspace/config-ui/static/locales/fr/preview.json -> /files/config-ui/preview.json
+2014-11-03 10:46:42,927 - INFO - Import translation (noop): /workspace/config-ui/static/locales/fr/submission.json -> /files/config-ui/submission.json
+```
+
+### Import File (with overwrite)
+
+```bash
+$ python smartlingTool.py import --dir /workspace/config-ui/static/locales/fr/badges.json --locale fr --overwrite
+2014-11-03 10:47:22,401 - INFO - Importing translation file(s) from: /workspace/config-ui/static/locales/fr/badges.json
+2014-11-03 10:47:22,910 - INFO - Import translation (noop): /workspace/config-ui/static/locales/fr/badges.json -> /files/config-ui/badges.json
+```
+
diff --git a/commandLineTool/smartlingTool.py b/commandLineTool/smartlingTool.py
new file mode 100644
index 0000000..354c8f2
--- /dev/null
+++ b/commandLineTool/smartlingTool.py
@@ -0,0 +1,457 @@
+#
+# Python command-line: Translation Tool
+# Contributed by: Bazaarvoice
+# Developers: Michael Goodnow
+#
+
+import os
+import sys
+import argparse
+import logging
+import ConfigParser
+
+# allow to import ../smartlingApiSdk/SmartlingFileApi
+lib_path = os.path.abspath(os.path.dirname(os.path.realpath(__file__)) + os.path.sep + os.path.pardir + os.path.sep)
+sys.path.append(lib_path)
+
+from smartlingApiSdk.SmartlingFileApi import SmartlingFileApiFactory
+from smartlingApiSdk.SmartlingDirective import SmartlingDirective
+from smartlingApiSdk.UploadData import UploadData
+from smartlingApiSdk.Constants import ReqMethod
+
+
+class SmartlingApi:
+    def __init__(self, apiKey, projectId):
+        self.file_api = SmartlingFileApiFactory().getSmartlingTranslationApi(apiKey, projectId)
+
+    # Upload a source file
+    def uploadFile(self, uploadData):
+        self.disableStdOut()
+        response, code = self.file_api.upload(uploadData)
+        self.enableStdOut()
+        if code == 200 and response.code == "SUCCESS":
+            return response.data
+        else:
+            raise IOError("Failed to upload ({0}), caused by: {1}".format(code, self._getMessages(response)))
+
+    # Import a translation
+    # ApiResponse: {"response":{"data":{"wordCount":10,"translationImportErrors":[{"contentFileId":238103,"stringHashcode":"851194c88c080f24ef257383841eb757","messages":["Information about import key was not found"],"importKey":null}],"stringCount":5},"code":"SUCCESS","messages":[]}}
+    def importFile(self, uploadData, locale, overwrite=False):
+        self.disableStdOut()
+        response, code = self.file_api.import_call(uploadData, locale, translationState="PUBLISHED", overwrite=str(overwrite).lower())
+        self.enableStdOut()
+        if code == 200 and response.code == "SUCCESS":
+            if response.data.translationImportErrors and len(response.data.translationImportErrors) > 0:
+                logging.error("Error Importing translation (%s): %s, Caused by:\n%s",
+                              locale,
+                              uploadData.uriPath + uploadData.name,
+                              self._getStringFromArray(response.data.translationImportErrors))
+                raise IOError("Failed to import translation file: {0}".format(uploadData.uriPath + uploadData.name))
+            return response.data
+        else:
+            raise IOError("Failed to import ({0}), caused by: {1}".format(code, self._getMessages(response)))
+
+    # Get a translated files
+    def getFile(self, fileUri, locale, isPseudo=False):
+        self.disableStdOut()
+        if isPseudo:
+            data, code = self.file_api.get(fileUri, locale, retrievalType="pseudo")
+        else:
+            data, code = self.file_api.get(fileUri, locale)
+        self.enableStdOut()
+        if code == 200:
+            return data
+        else:
+            raise IOError("Failed to get file ({0}): {1}".format(locale, fileUri))
+
+    # Get file status
+    # ApiResponse: {"response":{"data":{"fileUri":"common.json","wordCount":9,"fileType":"json","callbackUrl":null,"lastUploaded":"2014-10-30T19:09:36","stringCount":5,"approvedStringCount":0,"completedStringCount":0},"code":"SUCCESS","messages":[]}}
+    def getStatus(self, fileUri, locale):
+        self.disableStdOut()
+        response, code = self.file_api.status(fileUri, locale)
+        self.enableStdOut()
+        if code == 200 and response.code == "SUCCESS":
+            return response.data
+        else:
+            raise IOError("Failed to get file status ({0}): {1}, caused by: {2}".format(locale, fileUri, self._getMessages(response)))
+
+    # Get list of the project locales
+    # ApiResponse: [{'locale': 'de-DE', 'translated': 'Deutsch', 'name': 'German (Germany)'}, {'locale': 'pl-PL', 'translated': 'Polski', 'name': 'Polish (Poland)'}]
+    def getProjectLocales(self):
+        self.disableStdOut()
+        response, code = self.file_api.command(ReqMethod.GET, "/v1/project/locale/list", params={})
+        self.enableStdOut()
+        if code != 200 or response.code != "SUCCESS":
+            raise IOError("Failed to get project locales, caused by: {0}".format(self._getMessages(response)))
+        return response.data.locales
+
+    def _getMessages(self, response):
+        if response and response.messages:
+            return self._getStringFromArray(response.messages)
+
+    def _getStringFromArray(self, array):
+        message = ""
+        for m in array:
+            if len(message) > 0:
+                message += "\n"
+            message += str(m)
+        return message
+
+    def disableStdOut(self):
+        self._stdout = sys.stdout
+        null = open(os.devnull, 'wb')
+        sys.stdout = null
+
+    def enableStdOut(self):
+        if self._stdout:
+            sys.stdout = self._stdout
+
+
+class SmartlingTranslations:
+    def __init__(self, args):
+        self.args = args
+        self.api = SmartlingApi(args.apiKey, args.projectId)
+
+    #
+    # Upload Source
+    #
+    def uploadSource(self, directives=None):
+        args = self.args
+        logging.info("Uploading source file(s) from: %s", args.dir)
+        if not os.path.isdir(args.dir) and not os.path.isfile(args.dir):
+            raise ValueError("Invalid source directory/file: {0}".format(args.dir))
+
+        if os.path.isfile(args.dir):
+            # Upload single file
+            self._uploadSourceFile(os.path.dirname(args.dir), os.path.basename(args.dir), args.uriPath, directives)
+        else:
+            # Loop through files in directory recursively
+            for root, dirs, files in os.walk(args.dir):
+                for name in files:
+                    if self._processFile(name):
+                        relativeUri = args.uriPath + self._getRelativeUri(args.dir, root)
+                        self._uploadSourceFile(root, name, relativeUri, directives)
+
+    def _uploadSourceFile(self, path, fileName, uriPath, directives=None):
+        absFile = os.path.join(path, fileName)
+        logging.debug("Uploading: %s", absFile)
+
+        if not path.endswith(os.path.sep):
+            path += os.path.sep
+
+        if self.args.run:
+            uploadData = UploadData(path, fileName, self._getFileType(fileName))
+            uploadData.setUri(uriPath + fileName)
+
+            if directives:
+                for directive, value in directives:
+                    uploadData.addDirective(SmartlingDirective(directive, value))
+
+            try:
+                stats = self.api.uploadFile(uploadData)
+                logging.info("Uploaded: %s -> %s (Word count = %s  New source = %s)",
+                             absFile,
+                             uriPath + fileName,
+                             stats.wordCount,
+                             "No" if stats.overWritten else "Yes")
+            except IOError as ex:
+                if "No source strings found" in str(ex):
+                    logging.warn("No source strings found: %s", absFile)
+                else:
+                    raise ex
+        else:
+            logging.info("Upload (noop): %s -> %s", os.path.join(path, fileName), uriPath + fileName)
+
+    #
+    # Import Translations
+    #
+    def importTranslations(self, directives=None):
+        args = self.args
+        logging.info("Importing translation file(s) from: %s", args.dir)
+        if not os.path.isdir(args.dir) and not os.path.isfile(args.dir):
+            raise ValueError("Invalid translation directory/file: {0}".format(args.dir))
+
+        if os.path.isfile(args.dir):
+            # Upload single file
+            self._importTranslationFile(os.path.dirname(args.dir), os.path.basename(args.dir), args.uriPath, args.locale, directives, args.overwrite)
+        else:
+            # Loop through files in directory recursively
+            for root, dirs, files in os.walk(args.dir):
+                for name in files:
+                    if self._processFile(name):
+                        relativeUri = args.uriPath + self._getRelativeUri(args.dir, root)
+                        self._importTranslationFile(root, name, relativeUri, args.locale, directives, args.overwrite)
+
+    def _importTranslationFile(self, path, fileName, uriPath, locale, directives=None, overwrite=False):
+        smartlingLocale = self._getSmartlingLocale(locale)
+
+        # Test if English source exists
+        try:
+            self.api.getStatus(uriPath + fileName, smartlingLocale)
+        except IOError:
+            logging.error("Failed to import translation as English source has not been uploaded: %s", uriPath + fileName)
+            raise ValueError("No English source: {0}".format(uriPath + fileName))
+
+        absFile = os.path.join(path, fileName)
+        if self.args.run:
+            logging.debug("Importing translation (%s): %s", smartlingLocale, absFile)
+            if not path.endswith(os.path.sep):
+                path += os.path.sep
+            uploadData = UploadData(path, fileName, self._getFileType(fileName), uriPath)
+            uploadData.uri = uriPath + fileName
+            if directives:
+                for directive, value in directives:
+                    uploadData.addDirective(SmartlingDirective(directive, value))
+            stats = self.api.importFile(uploadData, smartlingLocale, overwrite)
+            logging.info("Imported translation (%s): %s -> %s (Word count = %s)",
+                         smartlingLocale,
+                         absFile,
+                         uriPath + fileName,
+                         stats.wordCount)
+        else:
+            logging.info("Import translation (noop): %s -> %s", absFile, uriPath + fileName)
+
+    #
+    # Download Translations
+    #
+    def downloadTranslations(self):
+        args = self.args
+        logging.info("Downloading translated file(s) for: %s", args.dir)
+        if not os.path.isdir(args.dir) and not os.path.isfile(args.dir):
+            raise ValueError("Invalid English source directory/file: {}".format(args.dir))
+
+        if not os.path.isdir(args.outputDir):
+            logging.debug("Creating output directory: %s", args.outputDir)
+            os.makedirs(args.outputDir)
+
+        projectLocales = self.api.getProjectLocales()
+
+        allComplete = True
+        for l in projectLocales:
+            smartlingLocale = l['locale']
+            if args.locale and args.locale != self._getLocaleFromSmartlingLocale(smartlingLocale):
+                logging.debug("Skipping locale: %s", smartlingLocale)
+                continue
+            logging.info("Fetching translations%s for %s (%s)", "" if args.run else " (noop)", l['name'], smartlingLocale)
+            if os.path.isfile(args.dir):
+                # Get single file
+                if not self._getTranslationsFile(args.outputDir, "", args.uriPath, os.path.basename(args.dir), smartlingLocale):
+                    allComplete = False
+            else:
+                # Loop through files in directory recursively and get their translations from the server respectfully
+                for root, dirs, files in os.walk(args.dir):
+                    for name in files:
+                        if self._processFile(name):
+                            relativePath = self._getRelativePath(args.dir, root)
+                            try:
+                                if not self._getTranslationsFile(args.outputDir, relativePath, args.uriPath, name, smartlingLocale):
+                                    allComplete = False
+                            except IOError:
+                                logging.warning("File hasn't been uploaded yet: " + args.uriPath + relativePath.replace('\\', '/') + name)
+                                allComplete = False
+
+        if allComplete:
+            logging.info("Successfully - all source files translated!")
+        else:
+            logging.warn("Not all source files are translated!")
+
+    def _getTranslationsFile(self, outputDir, relativePath, uriPath, fileName, smartlingLocale):
+        args = self.args
+        sourceFile = uriPath + relativePath.replace('\\', '/') + fileName
+        outputFile = outputDir + os.path.sep + self._getLocaleFromSmartlingLocale(smartlingLocale) + os.path.sep + relativePath + fileName
+
+        status = self.api.getStatus(sourceFile, smartlingLocale)
+        percentComplete = self._getPercent(status.completedStringCount, status.stringCount)
+
+        if percentComplete < 100 and not self.args.allowPartial:
+            logging.info("Translated %s%% (%s): %s -> %s (skipping)", percentComplete, smartlingLocale, sourceFile, outputFile)
+            return False
+
+        logging.info("Translated %s%% (%s): %s -> %s", percentComplete, smartlingLocale, sourceFile, outputFile)
+        if args.run:
+            fileData = self.api.getFile(sourceFile, smartlingLocale, args.pseudo)
+            if not os.path.isdir(os.path.dirname(outputFile)):
+                os.makedirs(os.path.dirname(outputFile))
+            f = open(outputFile, 'w')
+            f.write(fileData)
+
+        return percentComplete == 100
+
+    # Get percentage
+    def _getPercent(self, count, totalCount):
+        if count == 0 and totalCount == 0:
+            return 100
+        if totalCount > 0:
+            return int ((count / float(totalCount)) * 100)
+        return 0
+
+    # Get Smartling locale from locale
+    def _getSmartlingLocale(self, locale):
+        if hasattr(self.args, 'localeMap'):
+            if self.args.localeMap[locale]:
+                return self.args.localeMap[locale]
+        return locale
+
+    # Get locale from a Smartling locale
+    def _getLocaleFromSmartlingLocale(self, smartlingLocale):
+        if hasattr(self.args, 'localeMap'):
+            for locale, slLocale in self.args.localeMap.iteritems():
+                if slLocale == smartlingLocale:
+                    return locale
+        return smartlingLocale
+
+    # Get relative path
+    # rootDir = /root/path
+    # fileDir = /root/path/subdir/path/file
+    # returns subdir/path/file
+    def _getRelativePath(self, rootDir, filePath):
+        relativePath = filePath.replace(rootDir, "", 1)
+        if relativePath.startswith(os.path.sep):
+            relativePath = relativePath[1:]
+        if len(relativePath) > 0 and not relativePath.endswith(os.path.sep):
+            relativePath += os.path.sep
+        return relativePath
+
+    def _getRelativeUri(self, rootDir, filePath):
+        return self._getRelativePath(rootDir, filePath).replace('\\', '/')
+
+    # Determine if file should be processed based on file filters applied in configuration
+    # returns boolean
+    def _processFile(self, name):
+        if not hasattr(self.args, 'filterFileExtensions'):
+            return True
+        extension = os.path.splitext(name)[1][1:]
+        return extension in self.args.filterFileExtensions
+
+    # Determine Smartling file type
+    def _getFileType(self, filename):
+        extension = os.path.splitext(filename)[1][1:]
+        if hasattr(self.args, 'extensionMap'):
+            for key, value in self.args.extensionMap.iteritems():
+                if extension in value.split(","):
+                    return key
+        return extension
+
+
+def uploadSource(args, directives=None):
+    tool = SmartlingTranslations(args)
+    tool.uploadSource(directives)
+
+
+def downloadTranslations(args):
+    tool = SmartlingTranslations(args)
+    tool.downloadTranslations()
+
+
+def importTranslations(args, directives=None):
+    tool = SmartlingTranslations(args)
+    tool.importTranslations(directives)
+
+
+def main():
+    logging.basicConfig(format="%(asctime)s - %(levelname)s - %(message)s", level=logging.INFO)
+
+    parser = argparse.ArgumentParser(description="Smartling Translation Tool to upload, download, and import translation files")
+    parser.add_argument("-k", "--apiKey", dest="apiKey", help="Smartling API key (overrides configuration file value)")
+    parser.add_argument("-p", "--projectId", dest="projectId",
+                        help="Smartling project ID (overrides configuration file value)")
+    parser.add_argument("-c", "--config", dest="configFile", default="translation.cfg", help="Configuration file (default ./translation.cfg)")
+
+    subparsers = parser.add_subparsers(dest="sub_parser", help="To see individual sub command help: 'subcommand -h'")
+
+    parser_upload = subparsers.add_parser("upload", help="Upload the (English) source")
+    parser_upload.add_argument("-d", "--dir", dest="dir", required=True, help="Path to English source directory or file")
+    parser_upload.add_argument("-u", "--uriPath", dest="uriPath", help="File URI path used in Smartling system")
+    parser_upload.add_argument("--run", dest="run", action="store_true", help="Run for real (default is noop)")
+
+    parser_download = subparsers.add_parser("download", help="Download the translations")
+    parser_download.add_argument("-d", "--dir", dest="dir", required=True, help="Path to English source directory of file")
+    parser_download.add_argument("-o", "--outputDir", dest="outputDir", required=True,
+                            help="Output directory where to save translated files. Stores each translation in their own sub-directory.")
+    parser_download.add_argument("-u", "--uriPath", dest="uriPath", help="File URI path used in Smartling system")
+    parser_download.add_argument("-l", "--locale", dest="locale", help="Locale to download (default is all)")
+    parser_download.add_argument("-p", "--allowPartial", dest="allowPartial", action="store_true", help="Allow translation not 100%% complete (default is false)")
+    parser_download.add_argument("-s", "--pseudo", dest="pseudo", action="store_true", help="Download pseudo translations")
+    parser_download.add_argument("--run", dest="run", action="store_true", help="Run for real (default is noop)")
+
+    parser_import = subparsers.add_parser("import", help="Import translations")
+    parser_import.add_argument("-d", "--dir", dest="dir", required=True, help="Path to translations directory or file")
+    parser_import.add_argument("-u", "--uriPath", dest="uriPath", help="File URI path used in Smartling system")
+    parser_import.add_argument("-l", "--locale", dest="locale", required=True, help="Locale to import")
+    parser_import.add_argument("-o", "--overwrite", dest="overwrite", action="store_true", help="Overwrite previous translations")
+    parser_import.add_argument("--run", dest="run", action="store_true", help="Run for real (default is noop)")
+
+    args = parser.parse_args()
+
+    config = ConfigParser.ConfigParser()
+    # Allow case sensitive configuration keys
+    config.optionxform = str
+    config.read(args.configFile)
+
+    if not args.uriPath and config.has_section("smartling"):
+        args.uriPath = config.get("smartling", "uriPath")
+
+    # Default uriPath
+    if not args.uriPath:
+        args.uriPath = "/files"
+
+    # Fix URI path to include trailing slash (expected)
+    if not args.uriPath.endswith('/'):
+        args.uriPath += "/"
+
+    # Get API Key
+    if not args.apiKey and config.has_section("smartling"):
+        args.apiKey = config.get("smartling", "apiKey")
+
+    # Get Project ID
+    if not args.projectId and config.has_section("smartling"):
+        args.projectId = config.get("smartling", "projectId")
+    if not args.apiKey or not args.projectId:
+        raise ValueError("Smartling API Key and Project ID are required")
+
+    # Get Locales
+    if config.has_section("locales"):
+        locales = {}
+        for name, value in config.items("locales"):
+            locales[name] = value
+        args.localeMap = locales
+
+    if config.has_section("extensions"):
+        extensions = {}
+        for name, value in config.items("extensions"):
+            extensions[name] = value
+        args.extensionMap = extensions
+
+    # Get file extension filters
+    if config.has_section("filters") and config.has_option("filters", "file_extensions"):
+        fileExtensions = config.get("filters", "file_extensions")
+        if fileExtensions and len(fileExtensions) > 0:
+            fileExtensionArray = fileExtensions.split(",")
+            if len(fileExtensionArray) > 0:
+                args.filterFileExtensions = fileExtensionArray
+
+
+    # Upload Command
+    if args.sub_parser == "upload":
+        directives = None
+        if config.has_section("directives"):
+            directives = config.items("directives")
+        uploadSource(args, directives)
+
+    # Get Command
+    if args.sub_parser == "download":
+        # Pseudo argument
+        if args.pseudo:
+            args.allowPartial = True
+        downloadTranslations(args)
+
+    # Import Command
+    if args.sub_parser == "import":
+        directives = None
+        if config.has_section("directives"):
+            directives = config.items("directives")
+        importTranslations(args, directives)
+
+
+main()
+
diff --git a/commandLineTool/translation.cfg b/commandLineTool/translation.cfg
new file mode 100644
index 0000000..30cca2b
--- /dev/null
+++ b/commandLineTool/translation.cfg
@@ -0,0 +1,62 @@
+[smartling]
+# Project API Key
+apiKey = XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXX
+
+# Project ID
+projectId = XXXXXXXX
+
+# Custom URI path (default "/files")
+uriPath = /files/config-ui
+
+
+[directives]
+# Smartling directives
+# See: https://docs.smartling.com/display/docs/Supported+File+Types
+translate_mode = all
+source_key_paths = {*}
+placeholder_format_custom = __\w+__|\{{2,2}[\w\.]+\}{2,2}
+variants_enabled = true
+
+
+[locales]
+# Locale Mapping - Use to map differences between project and Smartling locale codes
+en = en-US
+de = de-DE
+es = es-ES
+fr = fr-FR
+ja = ja-JP
+pt = pt-PT
+pt_BR = pt-BR
+
+
+[filters]
+# Common delimited list of file extensions (default all files)
+file_extensions =
+
+
+[extensions]
+# Extension Mapping - Use to map differences between file types and Smartling file types
+
+# Gettext .pot and .po files
+gettext = pot,po
+
+# HTML files
+html = html,htm
+
+# Java Properties
+javaProperties = properties
+
+# Yaml files
+yaml = yml
+
+# Supports .xlf, .xliff, and .xml files that use the XML Localization Interchange File Format (XLIFF)
+xliff = xlf,xliff
+
+# Javascript files
+json = json,js
+
+# Qt Linguist TS format files
+qt = ts
+
+# MadCap Flare ZIP packages
+madcap = zip
diff --git a/smartlingApiSdk/FileApiBase.py b/smartlingApiSdk/FileApiBase.py
index 47e715c..094f732 100644
--- a/smartlingApiSdk/FileApiBase.py
+++ b/smartlingApiSdk/FileApiBase.py
@@ -118,6 +118,10 @@ def commandImport(self, uploadData, locale, **kw):
         kw[Params.LOCALE] = locale
         self.addApiKeys(kw)
 
+        if (uploadData.directives):
+            for index, directive in enumerate(uploadData.directives):
+                kw[directive.sl_prefix + directive.name] = directive.value
+
         return self.uploadMultipart(Uri.IMPORT, kw)
 
     def commandStatus(self, fileUri, locale, **kw):