def run(self): if not os.path.exists(self.path): os.makedirs(self.path) folders = ['content', 'templates', 'static', 'data'] for folder in folders: folder_path = os.path.join(self.path, folder) if os.path.exists(folder_path) and not os.path.isdir(folder_path): logger.error( 'content folder already exists and is not a folder') sys.exit(1) elif not os.path.exists(folder_path): os.mkdir(folder_path) routing_file_path = os.path.join(self.path, 'data/routing.json') if not os.path.exists(routing_file_path): with open(routing_file_path, 'w+') as routing_file: routing_file.write('[]') config_path = os.path.join(self.path, 'sitic.yml') config = { 'title': 'My Sitic site', 'base_url': 'www.example.org', } if not os.path.exists(config_path): with open(config_path, 'w+') as config_file: config_file.write(yaml.dump(config, default_flow_style=False))
def generate_po_file(self): for language in config.get_languages(): locale_path = os.path.join(config.locales_path, language, 'LC_MESSAGES') po_file_path = os.path.join(locale_path, self.po_filename) po_content = '' if not os.path.isdir(locale_path): os.makedirs(locale_path) if not os.path.exists(po_file_path): with open(self.pot_file_path, 'r') as f: po_content = f.read() else: msgmerge_args = ['msgmerge'] + self.msgmerge_options + [ po_file_path, self.pot_file_path ] po_content, errors, status = call_subprocess(msgmerge_args) if status != constants.STATUS_OK and errors: logger.error( "errors happened while running msgmerge\n{}".format( errors)) sys.exit(1) elif errors: logger.error(errors) with open(po_file_path, 'w', encoding='utf-8') as f: f.write(po_content) logger.info( '«.po» file generated for language «{}» at «{}»'.format( language, po_file_path))
def __init__(self, path): if os.path.isfile(path): logger.error('Not valid path, its a file') sys.exit(1) self.path = path
def __init__(self, filepath): self.filepath = filepath self.full_path = os.path.join(config.content_path, self.filepath) if not os.path.isfile(self.full_path): logger.error('File {} does no exists'.format(self.filepath)) sys.exit(1)
def __init__(self, filepath, title, frontmatter_format): self.filepath = filepath self.title = title self.frontmatter_format = frontmatter_format parts = os.path.split(self.filepath) self.filename = parts[-1] self.path = parts[0:-1] valid_extension = False for extension in VALID_CONTENT_EXTENSIONS: if self.filename.endswith(extension): valid_extension = True break if not valid_extension: logger.error('Not valid file extesion, valid ones: {}'.format( ', '.join(VALID_CONTENT_EXTENSIONS))) sys.exit(1) full_path = os.path.join(config.content_path, self.filepath) if os.path.isfile(full_path): logger.error('File {} already exists'.format(self.filepath)) sys.exit(1) while not self.title: self.title = click.prompt('Please enter a title', default=None, type=str)
def __init__(self, language): self.loader = FileSystemLoader( [config.templates_path, config.files_path]) self.environment = Environment(loader=self.loader, extensions=['jinja2.ext.i18n']) translations = self.get_translations(language) self.environment.install_gettext_translations(translations, True) for name, function in inspect.getmembers(filters, predicate=inspect.isfunction): self.environment.filters[name] = function self.environment.globals['get_search_url'] = functions.get_search_url # Get filters defined by user if config.custom_filters: try: spec = importlib.util.spec_from_file_location( "custom_filters", config.custom_filters) custom_filters = importlib.util.module_from_spec(spec) spec.loader.exec_module(custom_filters) for name, function in inspect.getmembers( custom_filters, predicate=inspect.isfunction): if not name.startswith('custom_'): name = '{}_{}'.format('custom', name) self.environment.filters[name] = function except: logger.error( 'Could not get custom filters, make sure it\'s a valid python file' )
def __init__(self): languages = config.get_languages() if len(languages) == 1 and languages[0] == constants.DEFAULT_LANG: logger.error( 'No languages configuration detected, can\'t create messages') sys.exit(1) self.po_filename = '{}.po'.format(constants.GETTEXT_DOMAIN) self.mo_filename = '{}.mo'.format(constants.GETTEXT_DOMAIN)
def force_text(s, encoding='utf-8', errors='strict'): if issubclass(type(s), str): return s try: if isinstance(s, bytes): s = str(s, encoding, errors) else: s = str(s) except UnicodeDecodeError as e: logger.error('Error: {}'.format(str(e))) sys.exit(1) return s
def call_subprocess(args, stdout_encoding='utf-8'): """ Friendly wrapper around Popen. Return stdout output, stderr output, and OS status code. """ try: p = Popen(args, shell=False, stdout=PIPE, stderr=PIPE, close_fds=os.name != 'nt') except OSError as err: logger.error('Error executing: {}'.format(args[0])) sys.exit(1) output, errors = p.communicate() return (force_text(output), force_text(errors), p.returncode)
def load_config(self, config_file_path): self.config_path = config_file_path self.base_path = os.path.dirname(os.path.abspath(self.config_path)) # Default self.content_path = os.path.join(self.base_path, constants.DEFAULT_CONTENT_PATH) self.public_path = os.path.join(self.base_path, constants.DEFAULT_PUBLIC_PATH) self.static_path = os.path.join(self.base_path, constants.DEFAULT_STATIC_PATH) self.templates_path = os.path.join(self.base_path, constants.DEFAULT_TEMPLATES_PATH) self.locales_path = os.path.join(self.base_path, constants.DEFAULT_LOCALES_PATH) self.routing_path = os.path.join(self.base_path, constants.DEFAULT_ROUTING_FILE_PATH) path_options = ['public_path', 'content_path', 'static_path', 'custom_filters'] with open(self.config_path, 'r') as config_file: parsed_config = yaml.load(config_file) if not parsed_config: logger.warning('Config file completely empty') parsed_config = [] for param in parsed_config: value = parsed_config[param] if param in path_options: value = value if os.path.isdir(value) else os.path.join(self.base_path, value) setattr(self, param, value) if not os.path.isdir(self.content_path): logger.error('Invalid path for content. Folder not found') sys.exit(1) if self.custom_filters and not os.path.isfile(self.custom_filters): logger.error('Specified filters file not found.') self.custom_filters = None return # TODO: make patterns configurables self.ignore_files_regex = [re.compile(i) for i in constants.IGNORE_FILES_PATTERN] self._format_languages() self.set_main_language() self.check_taxonomies_format() current_file_path = os.path.abspath(os.path.dirname(__file__)) self.files_path = os.path.join(current_file_path, 'files/')
def __init__(self): languages = config.get_languages() if len(languages) == 1 and languages[0] == constants.DEFAULT_LANG: logger.error( 'No languages configuration detected, can\'t create messages') sys.exit(1) loader = FileSystemLoader(config.files_path) self.env = Environment(extensions=['jinja2.ext.i18n'], loader=loader) self.env.filters['clean_msg'] = clean_msg self.translations = [] self.pot_filename = '{}.pot'.format(constants.GETTEXT_DOMAIN) self.po_filename = '{}.po'.format(constants.GETTEXT_DOMAIN) self.pot_file_path = os.path.join(config.locales_path, self.pot_filename)
def render(self, content, output_path, context, meta_tag, js_includes): template = self.get_content_template(content) if template: try: content = template.render(**context) content = content.replace('</head>', "{}\n\n</head>".format(meta_tag)) js_includes = '\n'.join(js_includes) content = content.replace('</body>', "{}\n\n</body>".format(js_includes)) with open(output_path, 'w') as output_file: output_file.write(content) except Exception as error: template_path = '/' + template.filename.replace( config.base_path, '').lstrip('/') logger.error('Rendering template {} for content {}: {}'.format( template_path, content, str(error))) else: logger.warning( 'No template found for content - {}'.format(content))
def generate_pot_file(self): pot_template = self.env.get_template('pot_template.jinja') pot_content = pot_template.render({'translations': self.translations}) if not os.path.isdir(config.locales_path): os.makedirs(config.locales_path) with open(self.pot_file_path, 'w', encoding='utf-8') as f: f.write(pot_content) msguniq_args = ['msguniq' ] + self.msguniq_options + [self.pot_file_path] output, errors, status = call_subprocess(msguniq_args) if status != constants.STATUS_OK and errors: logger.error( "errors happened while running msguniq\n{}".format(errors)) sys.exit(1) elif errors: logger.error(errors) # Replace pot file with msguniq output with open(self.pot_file_path, 'w', encoding='utf-8') as f: f.write(output)
def compile(self): for language in config.get_languages(): locale_path = os.path.join(config.locales_path, language, 'LC_MESSAGES') po_file_path = os.path.join(locale_path, self.po_filename) mo_file_path = os.path.join(locale_path, self.mo_filename) if has_bom(po_file_path): logger.error("The {} file has a BOM (Byte Order Mark). " "Sitic only supports .po files encoded in " "UTF-8 and without any BOM.".format(po_file_path)) if not os.path.exists(po_file_path): logger.warning( "Not .po file found for language «{}»." "Run «makemessages» to generate it.".format(language)) continue msgmt_args = ['msgfmt'] + self.msgfmt_options + [ '-o', mo_file_path, po_file_path ] output, errors, status = call_subprocess(msgmt_args) if status != constants.STATUS_OK and errors: logger.error( "errors happened while running msgmerge\n{}".format( errors)) sys.exit(1) elif errors: logger.error(errors) logger.info( 'Messages successfully compiled for language «{}»'.format( language)) logger.warning( 'Please, keep in mind that «msgfmt» won\'t generate any «.mo» file if no translation modified' )