def _set_translation(path_to_tx, resource, lang, path_to_file): """Reusable method to set translation file.""" proj, res = resource.split('.') if not project or not resource: raise Exception("\"%s\" is not a valid resource identifier. " "It should be in the following format " "project_slug.resource_slug." % resource) _go_to_dir(path_to_tx) # Warn the customuser if the file doesn't exist if not os.path.exists(path_to_file): logger.info("Warning: File '%s' doesn't exist." % path_to_file) # instantiate the project.Project prj = project.Project(path_to_tx) root_dir = os.path.abspath(path_to_tx) if root_dir not in os.path.normpath(os.path.abspath(path_to_file)): raise Exception("File must be under the project root directory.") if lang == prj.config.get("%s.%s" % (proj, res), "source_lang"): raise Exception("tx: You cannot set translation file for " "the source language. Source languages contain " "the strings which will be translated!") logger.info("Updating translations for resource %s ( %s -> %s )." % (resource, lang, path_to_file)) path_to_file = os.path.relpath(path_to_file, root_dir) prj.config.set("%s.%s" % (proj, res), "trans.%s" % lang, posix_path(path_to_file)) prj.save()
def regex_from_filefilter(file_filter, root_path=os.path.curdir): """Create proper regex from <lang> expression.""" # Force expr to be a valid regex expr (escaped) but keep <lang> intact expr_re = re.escape( posix_path(os.path.join(root_path, native_path(file_filter)))) expr_re = expr_re.replace("\\<lang\\>", '<lang>').replace( '<lang>', '([^%(sep)s]+)' % {'sep': re.escape(posix_sep)}) return "^%s$" % expr_re
def regex_from_filefilter(file_filter, root_path = os.path.curdir): """Create proper regex from <lang> expression.""" # Force expr to be a valid regex expr (escaped) but keep <lang> intact expr_re = re.escape( posix_path(os.path.join(root_path, native_path(file_filter))) ) expr_re = expr_re.replace("\\<lang\\>", '<lang>').replace( '<lang>', '([^%(sep)s]+)' % { 'sep': re.escape(posix_sep)}) return "^%s$" % expr_re
def get_resource_files(self, resource): """ Get a dict for all files assigned to a resource. First we calculate the files matching the file expression and then we apply all translation excpetions. The resulting dict will be in this format: { 'en': 'path/foo/en/bar.po', 'de': 'path/foo/de/bar.po', 'es': 'path/exceptions/es.po'} NOTE: All paths are relative to the root of the project """ tr_files = {} if self.config.has_section(resource): try: file_filter = self.config.get(resource, "file_filter") except ConfigParser.NoOptionError: file_filter = "$^" source_lang = self.config.get(resource, "source_lang") source_file = self.get_resource_option(resource, 'source_file') or None if source_file is not None: source_file = native_path(source_file) expr_re = regex_from_filefilter(file_filter, self.root) expr_rec = re.compile(expr_re) for f_path in files_in_project(self.root): match = expr_rec.match(posix_path(f_path)) if match: lang = match.group(1) if lang != source_lang: f_path = os.path.relpath(f_path, self.root) if f_path != source_file: tr_files.update({lang: f_path}) for (name, value) in self.config.items(resource): if name.startswith("trans."): value = native_path(value) lang = name.split('.')[1] # delete language which has same file if value in tr_files.values(): keys = [] for k, v in tr_files.iteritems(): if v == value: keys.append(k) if len(keys) == 1: del tr_files[keys[0]] else: raise Exception("Your configuration seems wrong."\ " You have multiple languages pointing to"\ " the same file.") # Add language with correct file tr_files.update({lang: value}) return tr_files return None
def get_resource_files(self, resource): """Get a dict for all files assigned to a resource. First we calculate the files matching the file expression and then we apply all translation excpetions. The resulting dict will be in this format: { 'en': 'path/foo/en/bar.po', 'de': 'path/foo/de/bar.po', 'es': 'path/exceptions/es.po'} NOTE: All paths are relative to the root of the project """ tr_files = {} if self.config.has_section(resource): try: file_filter = self.config.get(resource, "file_filter") except configparser.NoOptionError: file_filter = "$^" source_lang = self.config.get(resource, "source_lang") source_file = self.get_source_file(resource) expr_re = utils.regex_from_filefilter(file_filter, self.root) expr_rec = re.compile(expr_re) for f_path in utils.files_in_project(self.root): match = expr_rec.match(posix_path(f_path)) if match: lang = match.group(1) if lang != source_lang: f_path = os.path.relpath(f_path, self.root) if f_path != source_file: tr_files.update({lang: f_path}) for (name, value) in self.config.items(resource): if name.startswith("trans."): value = native_path(value) lang = name.split('.')[1] # delete language which has same file if value in list(tr_files.values()): keys = [] for k, v in six.iteritems(tr_files): if v == value: keys.append(k) if len(keys) == 1: del tr_files[keys[0]] else: raise Exception("Your configuration seems wrong. " "You have multiple languages " "pointing to the same file.") # Add language with correct file tr_files.update({lang: value}) return tr_files return None
def _set_source_file(path_to_tx, resource, lang, path_to_file): """Reusable method to set source file.""" proj, res = resource.split('.') if not proj or not res: raise Exception("\"%s.%s\" is not a valid resource identifier. " "It should be in the following format " "project_slug.resource_slug." % (proj, res)) if not lang: raise Exception("You haven't specified a source language.") try: _go_to_dir(path_to_tx) except UnInitializedError as e: utils.logger.error(e) return if not os.path.exists(path_to_file): raise Exception("tx: File ( %s ) does not exist." % os.path.join(path_to_tx, path_to_file)) # instantiate the project.Project prj = project.Project(path_to_tx) root_dir = os.path.abspath(path_to_tx) if root_dir not in os.path.normpath(os.path.abspath(path_to_file)): raise Exception("File must be under the project root directory.") logger.info("Setting source file for resource %s.%s ( %s -> %s )." % ( proj, res, lang, path_to_file)) path_to_file = os.path.relpath(path_to_file, root_dir) prj = project.Project(path_to_tx) # FIXME: Check also if the path to source file already exists. try: try: prj.config.get("%s.%s" % (proj, res), "source_file") except configparser.NoSectionError: prj.config.add_section("%s.%s" % (proj, res)) except configparser.NoOptionError: pass finally: prj.config.set( "%s.%s" % (proj, res), "source_file", posix_path(path_to_file) ) prj.config.set("%s.%s" % (proj, res), "source_lang", lang) prj.save()
def _set_source_file(path_to_tx, resource, lang, path_to_file): """Reusable method to set source file.""" proj, res = resource.split('.') if not proj or not res: raise Exception("\"%s.%s\" is not a valid resource identifier. " "It should be in the following format " "project_slug.resource_slug." % (proj, res)) if not lang: raise Exception("You haven't specified a source language.") try: _go_to_dir(path_to_tx) except UnInitializedError as e: utils.logger.error(e) return if not os.path.exists(path_to_file): raise Exception("tx: File ( %s ) does not exist." % os.path.join(path_to_tx, path_to_file)) # instantiate the project.Project prj = project.Project(path_to_tx) root_dir = os.path.abspath(path_to_tx) if root_dir not in os.path.normpath(os.path.abspath(path_to_file)): raise Exception("File must be under the project root directory.") logger.info("Setting source file for resource %s.%s ( %s -> %s )." % ( proj, res, lang, path_to_file)) path_to_file = os.path.relpath(path_to_file, root_dir) prj = project.Project(path_to_tx) # FIXME: Check also if the path to source file already exists. try: try: prj.config.get("%s.%s" % (proj, res), "source_file") except configparser.NoSectionError: prj.config.add_section("%s.%s" % (proj, res)) except configparser.NoOptionError: pass finally: prj.config.set( "%s.%s" % (proj, res), "source_file", posix_path(path_to_file) ) prj.config.set("%s.%s" % (proj, res), "source_lang", lang) prj.save()
def get_project_files(curpath, expression): """ Iterate over the files in the project that match the given expression. Return a tuple with the absolute file path and the language code of the language that is associated with it. """ # Strip the reference to the current directory, if it exists if expression.startswith(".{}".format(os.sep)): expression = expression[2:] # Split the expression into parts expression_parts = expression.split(os.sep) # Merge the expression's path into 'curpath' until the <lang> placeholder # is specified in order to reduce the search tree while _can_walk(curpath, expression_parts): curpath = os.path.realpath(os.path.join(curpath, expression_parts[0])) expression_parts = expression_parts[1:] expr_re = regex_from_filefilter(os.path.join(*expression_parts), curpath) expression_regex = re.compile(expr_re) visited = set() for root, dirs, files in os.walk(curpath, followlinks=True): root_realpath = os.path.realpath(root) # Don't visit any subdirectory if root_realpath in visited: del dirs[:] continue for file_path in files: full_path = os.path.realpath(os.path.join(root, file_path)) match = expression_regex.match(posix_path(full_path)) if match: try: lang = match.group(1) except IndexError: msg = ("File filter '{}' does not contain the '<lang>' " "placeholder".format(expression)) raise MalformedConfigFile(msg) yield full_path, lang visited.add(root_realpath) # Find which directories are already visited and remove them from # further processing removals = list(d for d in dirs if os.path.realpath(os.path.join(root, d)) in visited) for removal in removals: dirs.remove(removal)
def _set_translation(path_to_tx, resource, lang, path_to_file): """Reusable method to set translation file.""" proj, res = resource.split('.') if not project or not resource: raise Exception("\"%s\" is not a valid resource identifier. " "It should be in the following format " "project_slug.resource_slug." % resource) try: _go_to_dir(path_to_tx) except UnInitializedError as e: utils.logger.error(e) return # Warn the user if the file doesn't exist if not os.path.exists(path_to_file): logger.info("Warning: File '%s' doesn't exist." % path_to_file) # instantiate the project.Project prj = project.Project(path_to_tx) root_dir = os.path.abspath(path_to_tx) if root_dir not in os.path.normpath(os.path.abspath(path_to_file)): raise Exception("File must be under the project root directory.") if lang == prj.config.get("%s.%s" % (proj, res), "source_lang"): raise Exception("tx: You cannot set translation file for " "the source language. Source languages contain " "the strings which will be translated!") logger.info("Updating translations for resource %s ( %s -> %s )." % ( resource, lang, path_to_file)) path_to_file = os.path.relpath(path_to_file, root_dir) prj.config.set( "%s.%s" % (proj, res), "trans.%s" % lang, posix_path(path_to_file) ) prj.save()
def get_project_files(curpath, expression): """ Iterate over the files in the project that match the given expression. Return a tuple with the absolute file path and the language code of the language that is associated with it. """ # Strip the reference to the current directory, if it exists if expression.startswith(".{}".format(os.sep)): expression = expression[2:] # Split the expression into parts expression_parts = expression.split(os.sep) # Merge the expression's path into 'curpath' until the <lang> placeholder # is specified in order to reduce the search tree while _can_walk(curpath, expression_parts): curpath = os.path.realpath(os.path.join(curpath, expression_parts[0])) expression_parts = expression_parts[1:] expr_re = regex_from_filefilter(os.path.join(*expression_parts), curpath) expression_regex = re.compile(expr_re) initial_depth = curpath.count(os.sep) max_depth = 50 for root, dirs, files in os.walk(curpath, followlinks=True): # Don't visit any subdirectory if root.count(os.sep) > initial_depth + max_depth: del dirs[:] continue for file_path in files: path_to_match = os.path.join(root, file_path) full_path = os.path.realpath(os.path.join(root, file_path)) match = expression_regex.match(posix_path(path_to_match)) if match: try: lang = match.group(1) except IndexError: msg = ("File filter '{}' does not contain the '<lang>' " "placeholder".format(expression)) raise MalformedConfigFile(msg) yield full_path, lang
def test_posix_path_in_unix_does_nothing(self): orig_path = os.path.abspath(os.getcwd()) path = posix_path(orig_path) self.assertEqual(orig_path, path)
path_to_file = os.path.relpath(path_to_file, root_dir) prj = project.Project(path_to_tx) # FIXME: Check also if the path to source file already exists. try: try: prj.config.get("%s.%s" % (proj, res), "source_file") except ConfigParser.NoSectionError: prj.config.add_section("%s.%s" % (proj, res)) except ConfigParser.NoOptionError: pass finally: prj.config.set( "%s.%s" % (proj, res), "source_file", posix_path(path_to_file) ) prj.config.set("%s.%s" % (proj, res), "source_lang", lang) prj.save() def _set_translation(path_to_tx, resource, lang, path_to_file): """Reusable method to set translation file.""" proj, res = resource.split('.') if not project or not resource: raise Exception("\"%s\" is not a valid resource identifier. It should" " be in the following format project_slug.resource_slug." % resource)
def test_posix_path_in_unix_does_nothing(self): orig_path = os.path.abspath(os.getcwd()) path = posix_path(orig_path) self.assertEqual(orig_path, path)
def test_posix_path_in_windows_replaces_backslashes(self): orig_path = posix_path(os.path.abspath(os.getcwd())) expected_path = orig_path.replace('/', '\\') self.assertEqual(expected_path, native_path(orig_path))
path_to_file = os.path.relpath(path_to_file, root_dir) prj = project.Project(path_to_tx) # FIXME: Check also if the path to source file already exists. try: try: prj.config.get("%s.%s" % (proj, res), "source_file") except ConfigParser.NoSectionError: prj.config.add_section("%s.%s" % (proj, res)) except ConfigParser.NoOptionError: pass finally: prj.config.set( "%s.%s" % (proj, res), "source_file", posix_path(path_to_file) ) prj.config.set("%s.%s" % (proj, res), "source_lang", lang) prj.save() def _set_translation(path_to_tx, resource, lang, path_to_file): """Reusable method to set translation file.""" proj, res = resource.split('.') if not project or not resource: raise Exception("\"%s\" is not a valid resource identifier. It should" " be in the following format project_slug.resource_slug." % resource)
def test_posix_path_in_windows_replaces_backslashes(self): orig_path = os.path.abspath(os.getcwd()) path = orig_path.replace("/", "\\") self.assertEqual(orig_path, posix_path(path))
def test_posix_path_in_windows_replaces_backslashes(self): orig_path = posix_path(os.path.abspath(os.getcwd())) expected_path = orig_path.replace("/", "\\") self.assertEqual(expected_path, native_path(orig_path))
def _auto_local(path_to_tx, resource, source_language, expression, execute=False, source_file=None, regex=False): """Auto configure local project.""" # The path everything will be relative to curpath = os.path.abspath(os.curdir) # Force expr to be a valid regex expr (escaped) but keep <lang> intact if not expression: raise Exception("You need to specify an expression to define where " "translation files should be saved.") if not execute: logger.info("Only printing the commands which will be run if the " "--execute switch is specified.") # First, let's construct a dictionary of all matching files. # Note: Only the last matching file of a language will be stored. translation_files = {} for f_path, lang in utils.get_project_files(curpath, expression): if lang == source_language and not source_file: source_file = f_path else: translation_files[lang] = f_path if not source_file: raise Exception("Could not find a source language file. Please run " "set --source manually and then re-run this command " "or provide the source file with the -s flag.") if execute: logger.info("Updating source for resource %s ( %s -> %s )." % ( resource, source_language, os.path.relpath( source_file, path_to_tx) )) _set_source_file(path_to_tx, resource, source_language, os.path.relpath(source_file, path_to_tx)) else: logger.info('\ntx set --source -r %(res)s -l %(lang)s %(file)s\n' % { 'res': resource, 'lang': source_language, 'file': os.path.relpath(source_file, curpath)}) prj = project.Project(path_to_tx) if execute: try: prj.config.get("%s" % resource, "source_file") except configparser.NoSectionError: raise Exception("No resource with slug \"%s\" was found.\nRun " "'tx set auto-local -r %s \"expression\"' to " "do the initial configuration." % resource) # Now let's handle the translation files. if execute: logger.info("Updating file expression for resource %s ( %s )." % ( resource, expression)) # Evaluate file_filter relative to root dir file_filter = posix_path( os.path.relpath(os.path.join(curpath, expression), path_to_tx) ) prj.config.set("%s" % resource, "file_filter", file_filter) else: for (lang, f_path) in sorted(translation_files.items()): logger.info('tx set -r %(res)s -l %(lang)s %(file)s' % { 'res': resource, 'lang': lang, 'file': os.path.relpath(f_path, curpath)}) if execute: prj.save()
def test_posix_path_in_windows_replaces_backslashes(self): orig_path = os.path.abspath(os.getcwd()) path = orig_path.replace('/', '\\') self.assertEqual(orig_path, posix_path(path))