def pull(config, languages=None): """ Pulls translations from the POEditor API. """ assert config client = POEditorAPI(api_token=config.get("main", "apikey")) sections = config.sections() for s in sections: if s.startswith("project."): print("\nProject: {}".format(s)) project_id = config.get(s, "project_id") file_type = config.get(s, "type") options = config.options(s) for o in options: if o.startswith("trans."): language = o[6:] if languages and language not in languages: continue export_path = config.get(s, o) parent_dir = os.path.dirname(export_path) if not os.path.exists(parent_dir): os.makedirs(parent_dir) print("Language: {}".format(language)) client.export(project_id, language_code=language, file_type=file_type, local_file=export_path)
def status(config): """ Status is a simple task that displays the existing project configuration in a more human readable format. It lists all resources that have been initialized under the local project and all their associated translation files. """ assert config client = POEditorAPI(api_token=config.get("main", "apikey")) sections = config.sections() print("Api key: {}".format(config.get("main", "apikey"))) for s in sections: if s.startswith("project."): project_id = config.get(s, "project_id") details = client.view_project_details(project_id) terms = config.get(s, 'terms', None) if config.has_option(s, 'terms') else None options = config.options(s) print("\nProject: {0} ({1})".format(details['name'], details['id'])) print("Terms: {0}".format(terms)) for option in options: if option.startswith("trans."): import_path = config.get(s, option) language = option.split('.', 1)[-1] print(" - Language {0}: {1}".format(language, import_path))
def download_translation(file_path, project_id, api_token): client = POEditorAPI(api_token=api_token) os.makedirs(Path(file_path).absolute().parent, exist_ok=True) client.export( project_id=project_id, language_code='fr', file_type='mo', local_file=file_path, )
def pushTerms(config): """ Pushes new terms to POEditor """ assert config client = POEditorAPI(api_token=config.get("main", "apikey")) sections = config.sections() for s in sections: if s.startswith("project."): terms = config.get(s, 'terms', None) if config.has_option(s, 'terms') else None if terms: project_id = config.get(s, "project_id") print(" - Project: {0}, {1}\n".format(s, terms)) client.update_terms(project_id, terms)
def main(api_token, project_id, languages, tags, file_path, header_comment): client = POEditorAPI(api_token) # See also: https://github.com/sporteasy/python-poeditor/pull/15 client.FILTER_BY += 'proofread' done = 0 lock = threading.Lock() def export_worker(params): ((language_code, language_id), (tag, module)) = params output = file_path.format(language_id, module) (_, temp) = client.export(project_id, language_code, 'android_strings', 'proofread', tag) try: if os.path.getsize(temp) > 0: try: os.makedirs(os.path.dirname(output)) except FileExistsError: pass with open(output, 'w') as writer: with open(temp) as reader: head = reader.readline() assert head.startswith('<?xml') writer.write(head) writer.write(header_comment) for line in reader: writer.write(line) finally: os.remove(temp) with lock: nonlocal done done += 1 print("{}/{}: {}".format(done, len(languages) * len(tags), output)) pool = ThreadPool(max(64, multiprocessing.cpu_count())) pool.map(export_worker, itertools.product(languages.items(), tags.items()))
def pushTerms(config): """ Pushes new terms to POEditor """ assert config client = POEditorAPI(api_token=config.get("main", "apikey")) sections = config.sections() for s in sections: if s.startswith("project."): terms = config.get(s, 'terms', None) if config.has_option( s, 'terms') else None if terms: project_id = config.get(s, "project_id") print(" - Project: {0}, {1}\n".format(s, terms)) client.update_terms(project_id, terms)
def push(config, languages=None, overwrite=False, sync_terms=False): """ Push terms and languages """ assert config client = POEditorAPI(api_token=config.get("main", "apikey")) sections = config.sections() sleep_time = 32 for section in sections: if not section.startswith("project."): continue print("Project: {}".format(section)) project_id = config.get(section, "project_id") options = config.options(section) for option in options: if option.startswith("trans."): import_path = config.get(section, option) language = option.split('.', 1)[-1] if languages and language not in languages: continue if not os.path.exists(import_path): print("Error: {path} doesn't exist: ignoring language '{language}'" .format(path=import_path, language=language)) continue print(" Pushing language '{}'...".format(language)) for i in xrange(0, 100): try: client.update_terms_definitions( project_id, language_code=language, file_path=import_path, overwrite=overwrite, sync_terms=sync_terms ) print("language {} ok ".format(language)) break except Exception as e: if hasattr(e, 'error_code') and e.error_code == u'4048': #NOQA print(" error 4048 rety-Pushing {} in {}s language '{}'...".format(i+1, sleep_time, language)) #NOQA time.sleep(sleep_time) sleep_time = int(sleep_time + 1) continue
def init(config): """ Initializes the project on POEditor based on the configuration file. """ assert config client = POEditorAPI(api_token=config.get("main", "apikey")) sections = config.sections() for s in sections: if s.startswith("project."): print(" - Project: {}".format(s)) project_id = config.get(s, "project_id") # 1. Push terms terms = config.get(s, 'terms', None) if config.has_option( s, 'terms') else None if terms: project_id = config.get(s, "project_id") print(" - Terms: {0}".format(terms)) client.update_terms(project_id, terms) # 2. Create/add languages options = config.options(s) for o in options: if o.startswith("trans."): lang = o[6:] try: client.add_language_to_project(project_id, lang) except POEditorException: pass client.update_definitions(project_id=project_id, file_path=config.get(s, o), language_code=lang, overwrite=True) print(" - Language added: {}".format(lang))
def main(api_token, project_id, languages, tags, file_path): client = POEditorAPI(api_token) # See also: https://github.com/sporteasy/python-poeditor/pull/15 client.FILTER_BY += 'proofread' done = 0 lock = threading.Lock() def export_worker(params): ((language_code, language_id), (tag, module)) = params output = file_path.format(language_id, module) try: os.makedirs(os.path.dirname(output)) except FileExistsError: pass client.export(project_id, language_code, 'android_strings', 'proofread', tag, output) with lock: nonlocal done done += 1 print("{}/{}: {}".format(done, len(languages) * len(tags), output)) pool = ThreadPool(max(64, multiprocessing.cpu_count())) pool.map(export_worker, itertools.product(languages.items(), tags.items()))
def init(config): """ Initializes the project on POEditor based on the configuration file. """ assert config client = POEditorAPI(api_token=config.get("main", "apikey")) sections = config.sections() for s in sections: if s.startswith("project."): print(" - Project: {}".format(s)) project_id = config.get(s, "project_id") # 1. Push terms terms = config.get(s, 'terms', None) if config.has_option(s, 'terms') else None if terms: project_id = config.get(s, "project_id") print(" - Terms: {0}".format(terms)) client.update_terms(project_id, terms) # 2. Create/add languages options = config.options(s) for o in options: if o.startswith("trans."): lang = o[6:] try: client.add_language_to_project(project_id, lang) except POEditorException: pass client.update_definitions( project_id=project_id, file_path=config.get(s, o), language_code=lang, overwrite=True ) print(" - Language added: {}".format(lang))
#!/usr/bin/env python3 import os from dotenv import load_dotenv from poeditor import POEditorAPI os.chdir(os.path.dirname(os.path.realpath(__file__))) load_dotenv() mo_dir = "../i18n/mo" po_dir = "../i18n/po" if not os.path.isdir(mo_dir): os.mkdir(mo_dir) if not os.path.isdir(po_dir): os.mkdir(po_dir) client = POEditorAPI(os.getenv("POEDITOR_TOKEN")) projects = client.list_projects() project_id = [proj for proj in projects if proj["name"] == "WinDynamicDesktop"][0]["id"] languages = client.list_project_languages(project_id) language_codes = [lang["code"] for lang in languages if lang["percentage"] >= 50] for lc in language_codes: print(f"Downloading translation for {lc}") client.export(project_id, lc, "po", local_file=f"{po_dir}/{lc}.po") client.export(project_id, lc, "mo", local_file=f"{mo_dir}/{lc}.mo")
import os from poeditor import POEditorAPI project_id = os.environ['POEDITOR_PROJECT_ID'] api_token = os.environ['POEDITOR_API_TOKEN'] client = POEditorAPI(api_token=api_token) project = client.view_project_details(project_id) print(f"Before update, {project['name']} (id: {project['id']}) has {project['terms']} terms.") update_results = client.update_terms( project_id=project_id, file_path='_build/docs.pot', sync_terms=True, ) terms = update_results['terms'] print("Terms updated:") for k, v in terms.items(): print(f"\t{k}: {v}") project = client.view_project_details(project_id) print(f"After update, {project['name']} (id: {project['id']}) has {project['terms']} terms.")
import os import json import subprocess from poeditor import POEditorAPI from settings import PO_API_KEY, PROJECT_ID client = POEditorAPI(api_token=PO_API_KEY) languages = client.list_project_languages(PROJECT_ID) language_codes = [] for l in languages: if l['percentage'] > 50: language_codes.append(l['code']) for code in language_codes: print(f"Downloading {code}...") client.export(PROJECT_ID, code, file_type='json', local_file=f'lib/l10n/intl_{code}.json') print(f"Downloaded {code}") for fname in os.listdir('lib/l10n'): if fname.endswith('.json'): fname = f'lib/l10n/{fname}' out_file = fname.replace(".json", ".arb") ret = {} with open(fname) as json_file: data = json.load(json_file)
import os from poeditor import POEditorAPI if not os.path.isdir("mo"): os.mkdir("mo") if not os.path.isdir("po"): os.mkdir("po") with open("poeditor_token", 'r') as fileobj: api_token = fileobj.readline().strip() client = POEditorAPI(api_token) projects = client.list_projects() project_id = [p for p in projects if p["name"] == "WinDynamicDesktop"][0]["id"] languages = client.list_project_languages(project_id) language_codes = [l["code"] for l in languages if l["translations"]] for lc in language_codes: print(f"Downloading translation for {lc}") client.export(project_id, lc, "po", local_file=f"po/{lc}.po") client.export(project_id, lc, "mo", local_file=f"mo/{lc}.mo")
from pathlib import Path import json from babel.core import Locale from poeditor import POEditorAPI path_cwd = Path.cwd() with path_cwd.joinpath("script/poediter.json").open("r") as fp: args = json.load(fp) poe = POEditorAPI(args["api_token"]) domain = "py_sms_impl" for code in map(lambda d: d["code"], poe.list_project_languages(args["id"])): loc_ins = Locale.parse(code, sep="-") dist_path = path_cwd / "locale" / str(loc_ins) / "LC_MESSAGES/py_sms_impl.po" poe.export(args["id"], code, local_file=str(dist_path)) print("Downloaded: %s" % dist_path)
def cmd_poeditor_sync(args): opts = env.core.options.with_prefix('locale.poeditor') if 'project_id' not in opts: raise RuntimeError("POEditor project ID isn't set!") poeditor_project_id = opts['project_id'] if 'api_token' not in opts: raise RuntimeError("POEditor API token isn't set!") poeditor_api_token = opts['api_token'] client = POEditorAPI(api_token=poeditor_api_token) # Package versions are stored as part of the project's description in the POEditor description = POEditorDescription(client, poeditor_project_id) local_ver = pkginfo.packages[args.package].version if args.package in description.packages: remote_ver = description.packages[args.package] if pkg_version.parse(local_ver) < pkg_version.parse(remote_ver): raise RuntimeError( "Version of the '%s' package must be not lower than %s in order to use translations from the POEditor " "(current version: %s)." % (args.package, remote_ver, local_ver)) # Update local po-files poeditor_terms = {} reference_catalogs = {} for comp_id, locales in components_and_locales(args, work_in_progress=True): cmd_update( Namespace(package=pkginfo.comp_pkg(comp_id), component=[ comp_id, ], locale=[lc for lc in locales if lc != 'ru'], extract=args.extract, force=False)) for locale in locales: if locale == 'ru' and locales != ['ru']: po_path = catalog_filename(comp_id, locale) if po_path.exists(): ref_catalog = reference_catalogs.get(locale) if ref_catalog is None: ref_catalog = Catalog(locale=locale) reference_catalogs[locale] = ref_catalog with po_path.open('r') as po_fd: for m in read_po(po_fd, locale=locale): m.context = comp_id ref_catalog[m.id] = m continue terms = poeditor_terms.get(locale) if not terms: logger.debug( "Fetching translations from POEditor for locale [%s]...", locale) language_code = locale.replace('_', '-').lower() terms = client.view_project_terms(poeditor_project_id, language_code=language_code) poeditor_terms[locale] = terms # Filter terms by context terms = [term for term in terms if comp_id == term['context']] po_path = catalog_filename(comp_id, locale) if not po_path.exists(): continue with po_path.open('r') as po_fd: po = read_po(po_fd, locale=locale) updated = 0 for msg in po: for term in terms: if msg.id == term['term']: cur_tr = msg.string new_tr = term['translation']['content'] if cur_tr != new_tr: msg.string = new_tr updated += 1 break if updated != 0: with io.open(po_path, 'wb') as fd: write_po(fd, po, width=80, omit_header=True) logger.info( "%d messages translated for component [%s] locale [%s]", updated, comp_id, locale) # Synchronize terms remote_terms = defaultdict(set) target_state = defaultdict(dict) for item in client.view_project_terms(poeditor_project_id): remote_terms[item['term']].add(item['context']) comps = [] for comp_id, _ in components_and_locales(args, work_in_progress=True): pot_path = catalog_filename(comp_id, '', ext='pot') with pot_path.open('r') as fd: pot = read_po(fd) for msg in pot: if msg.id == '': continue target_state[msg.id][comp_id] = True comps.append(comp_id) for comp_id in comps: for msg_id in target_state: if comp_id not in target_state[msg_id]: target_state[msg_id][comp_id] = False terms_to_add = [] terms_to_del = [] for msg_id in target_state: if msg_id not in remote_terms: for comp_id in target_state[msg_id]: if target_state[msg_id][comp_id]: logger.debug("ADD (%s, %s)", comp_id, msg_id) terms_to_add.append(dict(term=msg_id, context=comp_id)) continue for comp_id in target_state[msg_id]: if target_state[msg_id][comp_id] and comp_id not in remote_terms[ msg_id]: logger.debug("ADD (%s, %s)", comp_id, msg_id) terms_to_add.append(dict(term=msg_id, context=comp_id)) if not target_state[msg_id][comp_id] and comp_id in remote_terms[ msg_id]: logger.debug("DEL (%s, %s)", comp_id, msg_id) terms_to_del.append(dict(term=msg_id, context=comp_id)) for msg_id, contexts in remote_terms.items(): if msg_id not in target_state: for comp_id in contexts: if comp_id in comps: logger.debug("DEL (%s, %s)", comp_id, msg_id) terms_to_del.append(dict(term=msg_id, context=comp_id)) if len(terms_to_add) > 0 and args.no_dry_run: client.add_terms(poeditor_project_id, terms_to_add) logger.info("%d messages added to the POEditor", len(terms_to_add)) if len(terms_to_del) > 0 and args.no_dry_run: client.delete_terms(poeditor_project_id, terms_to_del) logger.info("%d messages deleted from the POEditor", len(terms_to_del)) if len(reference_catalogs) > 0: logger.info( "Upload the following reference translations to POEditor: " + ", ".join(reference_catalogs.keys())) if args.no_dry_run: wait_for_rate_limit = False for locale, catalog in reference_catalogs.items(): with NamedTemporaryFile(suffix='.po') as fd: write_po(fd, catalog, width=80, omit_header=True) fd.flush() logger.debug("Uploading %s reference translation...", locale) # Free account allows doing 1 upload per 20 seconds sleep(20 if wait_for_rate_limit else 0) wait_for_rate_limit = True client.update_terms_translations( project_id=poeditor_project_id, language_code=locale.replace('-', '_'), overwrite=True, sync_terms=False, file_path=fd.name) if len(terms_to_add) > 0 or len(terms_to_del) > 0: if args.no_dry_run: description.update(args.package, local_ver) logger.info("Set '%s' package version to %s in the POEditor.", args.package, local_ver)