| Index: cms/bin/translate.py | 
| =================================================================== | 
| --- a/cms/bin/translate.py | 
| +++ b/cms/bin/translate.py | 
| @@ -30,7 +30,7 @@ | 
| import cms.utils | 
| from cms.sources import FileSource | 
| -logger = logging.getLogger("cms.bin.translate") | 
| +logger = logging.getLogger('cms.bin.translate') | 
| class CrowdinAPI: | 
| @@ -39,23 +39,23 @@ | 
| def __init__(self, api_key, project_name): | 
| self.api_key = api_key | 
| self.project_name = project_name | 
| - self.connection = urllib3.connection_from_url("https://api.crowdin.com/") | 
| + self.connection = urllib3.connection_from_url('https://api.crowdin.com/') | 
| def raw_request(self, request_method, api_endpoint, query_params=(), **kwargs): | 
| - url = "/api/project/%s/%s?%s" % ( | 
| + url = '/api/project/%s/%s?%s' % ( | 
| urllib.quote(self.project_name), | 
| urllib.quote(api_endpoint), | 
| - urllib.urlencode((("key", self.api_key),) + query_params) | 
| + urllib.urlencode((('key', self.api_key),) + query_params) | 
| ) | 
| try: | 
| response = self.connection.request( | 
| request_method, str(url), **kwargs | 
| ) | 
| except urllib3.exceptions.HTTPError: | 
| - logger.error("Connection to API endpoint %s failed", url) | 
| + logger.error('Connection to API endpoint %s failed', url) | 
| raise | 
| if response.status < 200 or response.status >= 300: | 
| - logger.error("API call to %s failed:\n%s", url, response.data) | 
| + logger.error('API call to %s failed:\n%s', url, response.data) | 
| raise urllib3.exceptions.HTTPError(response.status) | 
| return response | 
| @@ -66,19 +66,19 @@ | 
| if isinstance(value, basestring): | 
| fields.append((name, value)) | 
| else: | 
| - fields.extend((name + "[]", v) for v in value) | 
| + fields.extend((name + '[]', v) for v in value) | 
| if files: | 
| - fields.extend(("files[%s]" % f[0], f) for f in files) | 
| + fields.extend(('files[%s]' % f[0], f) for f in files) | 
| response = self.raw_request( | 
| - request_method, api_endpoint, (("json", "1"),), | 
| + request_method, api_endpoint, (('json', '1'),), | 
| fields=fields, preload_content=False | 
| ) | 
| try: | 
| return json.load(response) | 
| except ValueError: | 
| - logger.error("Invalid response returned by API endpoint %s", url) | 
| + logger.error('Invalid response returned by API endpoint %s', url) | 
| raise | 
| @@ -92,7 +92,7 @@ | 
| def extract_strings(source, defaultlocale): | 
| - logger.info("Extracting page strings (please be patient)...") | 
| + logger.info('Extracting page strings (please be patient)...') | 
| page_strings = {} | 
| def record_string(page, locale, name, value, comment, fixed_strings): | 
| @@ -104,14 +104,14 @@ | 
| except KeyError: | 
| store = page_strings[page] = collections.OrderedDict() | 
| - store[name] = {"message": value} | 
| + store[name] = {'message': value} | 
| if fixed_strings: | 
| - comment = comment + "\n" if comment else "" | 
| - comment += ", ".join("{%d}: %s" % i_s | 
| + comment = comment + '\n' if comment else '' | 
| + comment += ', '.join('{%d}: %s' % i_s | 
| for i_s in enumerate(fixed_strings, 1)) | 
| if comment: | 
| - store[name]["description"] = comment | 
| + store[name]['description'] = comment | 
| for page, format in source.list_pages(): | 
| cms.utils.process_page(source, defaultlocale, page, | 
| @@ -121,21 +121,21 @@ | 
| def configure_locales(crowdin_api, local_locales, enabled_locales, | 
| defaultlocale): | 
| - logger.info("Checking which locales are supported by Crowdin...") | 
| - response = crowdin_api.request("GET", "supported-languages") | 
| + logger.info('Checking which locales are supported by Crowdin...') | 
| + response = crowdin_api.request('GET', 'supported-languages') | 
| - supported_locales = {l["crowdin_code"] for l in response} | 
| + supported_locales = {l['crowdin_code'] for l in response} | 
| # We need to map the locale names we use to the ones that Crowdin is expecting | 
| # and at the same time ensure that they are supported. | 
| required_locales = {} | 
| for locale in local_locales: | 
| - if "_" in locale: | 
| - crowdin_locale = locale.replace("_", "-") | 
| + if '_' in locale: | 
| + crowdin_locale = locale.replace('_', '-') | 
| elif locale in supported_locales: | 
| crowdin_locale = locale | 
| else: | 
| - crowdin_locale = "%s-%s" % (locale, locale.upper()) | 
| + crowdin_locale = '%s-%s' % (locale, locale.upper()) | 
| if crowdin_locale in supported_locales: | 
| required_locales[locale] = crowdin_locale | 
| @@ -145,28 +145,28 @@ | 
| required_crowdin_locales = set(required_locales.values()) | 
| if not required_crowdin_locales.issubset(enabled_locales): | 
| - logger.info("Enabling the required locales for the Crowdin project...") | 
| + logger.info('Enabling the required locales for the Crowdin project...') | 
| crowdin_api.request( | 
| - "POST", "edit-project", | 
| - data={"languages": enabled_locales | required_crowdin_locales} | 
| + 'POST', 'edit-project', | 
| + data={'languages': enabled_locales | required_crowdin_locales} | 
| ) | 
| return required_locales | 
| def list_remote_files(project_info): | 
| - def parse_file_node(node, path=""): | 
| - if node["node_type"] == "file": | 
| - remote_files.add(path + node["name"]) | 
| - elif node["node_type"] == "directory": | 
| - dir_name = path + node["name"] | 
| + def parse_file_node(node, path=''): | 
| + if node['node_type'] == 'file': | 
| + remote_files.add(path + node['name']) | 
| + elif node['node_type'] == 'directory': | 
| + dir_name = path + node['name'] | 
| remote_directories.add(dir_name) | 
| - for file in node.get("files", []): | 
| - parse_file_node(file, dir_name + "/") | 
| + for file in node.get('files', []): | 
| + parse_file_node(file, dir_name + '/') | 
| remote_files = set() | 
| remote_directories = set() | 
| - for node in project_info["files"]: | 
| + for node in project_info['files']: | 
| parse_file_node(node) | 
| return remote_files, remote_directories | 
| @@ -176,17 +176,17 @@ | 
| local_directories = set() | 
| for page, strings in page_strings.iteritems(): | 
| if strings: | 
| - local_files.add(page + ".json") | 
| - while "/" in page: | 
| - page = page.rsplit("/", 1)[0] | 
| + local_files.add(page + '.json') | 
| + while '/' in page: | 
| + page = page.rsplit('/', 1)[0] | 
| local_directories.add(page) | 
| return local_files, local_directories | 
| def create_directories(crowdin_api, directories): | 
| for directory in directories: | 
| - logger.info("Creating directory %s", directory) | 
| - crowdin_api.request("POST", "add-directory", data={"name": directory}) | 
| + logger.info('Creating directory %s', directory) | 
| + crowdin_api.request('POST', 'add-directory', data={'name': directory}) | 
| def add_update_files(crowdin_api, api_endpoint, message, files, page_strings): | 
| @@ -194,73 +194,73 @@ | 
| files = [] | 
| for file_name in group: | 
| page = os.path.splitext(file_name)[0] | 
| - files.append((file_name, json.dumps(page_strings[page]), "application/json")) | 
| + files.append((file_name, json.dumps(page_strings[page]), 'application/json')) | 
| del page_strings[page] | 
| logger.info(message, len(files)) | 
| - crowdin_api.request("POST", api_endpoint, files=files) | 
| + crowdin_api.request('POST', api_endpoint, files=files) | 
| def upload_new_files(crowdin_api, new_files, page_strings): | 
| - add_update_files(crowdin_api, "add-file", "Uploading %d new pages...", | 
| + add_update_files(crowdin_api, 'add-file', 'Uploading %d new pages...', | 
| new_files, page_strings) | 
| def update_existing_files(crowdin_api, existing_files, page_strings): | 
| - add_update_files(crowdin_api, "update-file", "Updating %d existing pages...", | 
| + add_update_files(crowdin_api, 'update-file', 'Updating %d existing pages...', | 
| existing_files, page_strings) | 
| def upload_translations(crowdin_api, source_dir, new_files, required_locales): | 
| def open_locale_files(locale, files): | 
| for file_name in files: | 
| - path = os.path.join(source_dir, "locales", locale, file_name) | 
| + path = os.path.join(source_dir, 'locales', locale, file_name) | 
| if os.path.isfile(path): | 
| - with open(path, "rb") as f: | 
| - yield (file_name, f.read(), "application/json") | 
| + with open(path, 'rb') as f: | 
| + yield (file_name, f.read(), 'application/json') | 
| if new_files: | 
| for locale, crowdin_locale in required_locales.iteritems(): | 
| for files in grouper(open_locale_files(locale, new_files), | 
| crowdin_api.FILES_PER_REQUEST): | 
| - logger.info("Uploading %d existing translation " | 
| - "files for locale %s...", len(files), locale) | 
| - crowdin_api.request("POST", "upload-translation", files=files, | 
| - data={"language": crowdin_locale}) | 
| + logger.info('Uploading %d existing translation ' | 
| + 'files for locale %s...', len(files), locale) | 
| + crowdin_api.request('POST', 'upload-translation', files=files, | 
| + data={'language': crowdin_locale}) | 
| def remove_old_files(crowdin_api, old_files): | 
| for file_name in old_files: | 
| - logger.info("Removing old file %s", file_name) | 
| - crowdin_api.request("POST", "delete-file", data={"file": file_name}) | 
| + logger.info('Removing old file %s', file_name) | 
| + crowdin_api.request('POST', 'delete-file', data={'file': file_name}) | 
| def remove_old_directories(crowdin_api, old_directories): | 
| for directory in reversed(sorted(old_directories, key=len)): | 
| - logger.info("Removing old directory %s", directory) | 
| - crowdin_api.request("POST", "delete-directory", data={"name": directory}) | 
| + logger.info('Removing old directory %s', directory) | 
| + crowdin_api.request('POST', 'delete-directory', data={'name': directory}) | 
| def download_translations(crowdin_api, source_dir, required_locales): | 
| - logger.info("Requesting generation of fresh translations archive...") | 
| - result = crowdin_api.request("GET", "export") | 
| - if result.get("success", {}).get("status") == "skipped": | 
| - logger.warning("Archive generation skipped, either " | 
| - "no changes or API usage excessive") | 
| + logger.info('Requesting generation of fresh translations archive...') | 
| + result = crowdin_api.request('GET', 'export') | 
| + if result.get('success', {}).get('status') == 'skipped': | 
| + logger.warning('Archive generation skipped, either ' | 
| + 'no changes or API usage excessive') | 
| - logger.info("Downloading translations archive...") | 
| - response = crowdin_api.raw_request("GET", "download/all.zip") | 
| + logger.info('Downloading translations archive...') | 
| + response = crowdin_api.raw_request('GET', 'download/all.zip') | 
| inverted_required_locales = {crowdin: local for local, crowdin in | 
| required_locales.iteritems()} | 
| - logger.info("Extracting translations archive...") | 
| - with zipfile.ZipFile(io.BytesIO(response.data), "r") as archive: | 
| - locale_path = os.path.join(source_dir, "locales") | 
| + logger.info('Extracting translations archive...') | 
| + with zipfile.ZipFile(io.BytesIO(response.data), 'r') as archive: | 
| + locale_path = os.path.join(source_dir, 'locales') | 
| # First clear existing translation files | 
| for root, dirs, files in os.walk(locale_path, topdown=True): | 
| if root == locale_path: | 
| dirs[:] = [d for d in dirs if d in required_locales] | 
| for f in files: | 
| - if f.lower().endswith(".json"): | 
| + if f.lower().endswith('.json'): | 
| os.remove(os.path.join(root, f)) | 
| # Then extract the new ones in place | 
| for member in archive.namelist(): | 
| @@ -268,7 +268,7 @@ | 
| ext = posixpath.splitext(file_name)[1] | 
| path_parts = path.split(posixpath.sep) | 
| locale, file_path = path_parts[0], path_parts[1:] | 
| - if ext.lower() == ".json" and locale in inverted_required_locales: | 
| + if ext.lower() == '.json' and locale in inverted_required_locales: | 
| output_path = os.path.join( | 
| locale_path, inverted_required_locales[locale], | 
| *file_path + [file_name] | 
| @@ -276,25 +276,25 @@ | 
| with archive.open(member) as source_file: | 
| locale_file_contents = json.load(source_file) | 
| if len(locale_file_contents): | 
| - with codecs.open(output_path, "wb", "utf-8") as target_file: | 
| + with codecs.open(output_path, 'wb', 'utf-8') as target_file: | 
| json.dump(locale_file_contents, target_file, ensure_ascii=False, | 
| - sort_keys=True, indent=2, separators=(",", ": ")) | 
| + sort_keys=True, indent=2, separators=(',', ': ')) | 
| def crowdin_sync(source_dir, crowdin_api_key): | 
| with FileSource(source_dir) as source: | 
| config = source.read_config() | 
| - defaultlocale = config.get("general", "defaultlocale") | 
| - crowdin_project_name = config.get("general", "crowdin-project-name") | 
| + defaultlocale = config.get('general', 'defaultlocale') | 
| + crowdin_project_name = config.get('general', 'crowdin-project-name') | 
| crowdin_api = CrowdinAPI(crowdin_api_key, crowdin_project_name) | 
| - logger.info("Requesting project information...") | 
| - project_info = crowdin_api.request("GET", "info") | 
| + logger.info('Requesting project information...') | 
| + project_info = crowdin_api.request('GET', 'info') | 
| page_strings = extract_strings(source, defaultlocale) | 
| local_locales = source.list_locales() - {defaultlocale} | 
| - enabled_locales = {l["code"] for l in project_info["languages"]} | 
| + enabled_locales = {l['code'] for l in project_info['languages']} | 
| required_locales = configure_locales(crowdin_api, local_locales, | 
| enabled_locales, defaultlocale) | 
| @@ -304,8 +304,8 @@ | 
| # Avoid deleting all remote content if there was a problem listing local files | 
| if not local_files: | 
| - logger.error("No existing strings found, maybe the project directory is " | 
| - "not set up correctly? Aborting!") | 
| + logger.error('No existing strings found, maybe the project directory is ' | 
| + 'not set up correctly? Aborting!') | 
| sys.exit(1) | 
| new_files = local_files - remote_files | 
| @@ -323,11 +323,11 @@ | 
| remove_old_directories(crowdin_api, old_directories) | 
| download_translations(crowdin_api, source_dir, required_locales) | 
| - logger.info("Crowdin sync completed.") | 
| + logger.info('Crowdin sync completed.') | 
| -if __name__ == "__main__": | 
| +if __name__ == '__main__': | 
| if len(sys.argv) < 3: | 
| - print >>sys.stderr, "Usage: python -m cms.bin.translate www_directory crowdin_project_api_key [logging_level]" | 
| + print >>sys.stderr, 'Usage: python -m cms.bin.translate www_directory crowdin_project_api_key [logging_level]' | 
| sys.exit(1) | 
| logging.basicConfig() |