Example #1
0
        continue

    LANGUAGE_DIR = os.path.join(SOURCE_DIR, "{}.lproj".format(OUTPUTLANGUAGE))

    if not os.path.exists(LANGUAGE_DIR):
        raise ValueError('Source file not found for {}'.format(OUTPUTLANGUAGE))

    IN_FILES = [
        os.path.join(root, name)
        for root, dirs, files in os.walk(LANGUAGE_DIR)
        for name in files
        if name.endswith(".strings")
    ]

    for IN_FILE in IN_FILES:
        strings = strparser.parse_strings(filename=IN_FILE)

        for item in strings:
            value = item['value']
            
            # escape the words that needs not to be translated
            if value in ['OK']:
                continue

            translated = translate(value, OUTPUTLANGUAGE, INPUTLANGUAGE).replace('\\ ', '\\').replace('\\ n ', '\\n').replace('\\n ', '\\n').replace('/ ', '/')
            inplace_change(IN_FILE, value, translated)
        
        print("Finished translating {}".format(IN_FILE))
        tmp_file = "{}.bak".format(IN_FILE)
        if os.path.exists(tmp_file):
            os.remove(tmp_file)
Example #2
0
    def synchronize(target_file,
                    lc):  #add,remove, update (translate or copy from base)
        # parse target file
        target_kv = {}
        target_kc = {}
        target_error_lines = []
        if not notexist_or_empty_file(target_file):
            parsed_strings = strparser.parse_strings(filename=target_file)
            for item in parsed_strings:
                k, e = item['key'], item['error']
                # line error
                if e:
                    target_error_lines.append(e)
                if not target_error_lines:
                    target_kv[k] = item['value']
                    target_kc[k] = item['comment']

        # parsing complete or return.
        if target_error_lines:
            print('(!) Syntax error - Skip')
            return False, None, None, target_error_lines

        # base
        base_content = base_dict[os.path.basename(target_file)]
        base_kv = {}
        base_kc = {}
        for item in base_content:
            k, e = item['key'], item['error']
            # line error
            if e:
                print('(!) WARNING : Syntax error from Base -> ', k, ':', e)
            base_kv[k] = item['value']
            base_kc[k] = item['comment']

        force_adding_keys = base_kv.keys(
        ) if __KEYS_FORCE_TRANSLATE_ALL__ else __KEYS_FORCE_TRANSLATE__
        adding_keys = list(
            ((set(base_kv.keys()) - set(target_kv.keys())) | (set(base_kv.keys()) & set(force_adding_keys))) \
            - set(base_kv.keys() if __FOLLOWING_ALL_KEYS_IFNOT_EXIST__ else __KEYS_FOLLOW_BASE__) \
        )
        removing_keys = list(set(target_kv.keys()) - set(base_kv.keys()))
        existing_keys = list(
            set(base_kv.keys()) - (set(adding_keys) | set(removing_keys)))

        # Filter if __ONLY_FOR_KEYS__ option activated
        if __ONLY_FOR_KEYS__:
            adding_keys = list(set(adding_keys) & set(__ONLY_FOR_KEYS__))
            removing_keys = list(set(removing_keys) & set(__ONLY_FOR_KEYS__))
            existing_keys = list(set(existing_keys) & set(__ONLY_FOR_KEYS__))

        updated_keys = []
        """
        perform translate
        """
        translated_kv = {}
        reversed_matched_kv = {}  # {"ratio":float, "ignored":True|False}
        reversed_translated_kv = {}

        if len(adding_keys):
            print('Translating...')
            translated_kv = dict(
                zip(
                    adding_keys,
                    strtrans.translate_strs([base_kv[k] for k in adding_keys],
                                            lc)))

            if __VERIFY_TRANS_RESULTS__:
                print('Reversing results and matching...')
                reversed_translated_kv = dict(
                    zip(
                        adding_keys,
                        strtrans.translate_strs(
                            [translated_kv[_ak] for _ak in adding_keys],
                            'en')))

                for bk in adding_keys:
                    if bk in reversed_translated_kv:
                        ratio = fuzz.partial_ratio(base_kv[bk],
                                                   reversed_translated_kv[bk])
                        should_ignore = __IGNORE_UNVERIFIED_RESULTS__ and ratio <= __RATIO_TO_IGNORE_UNVERIFIED_RESULTS__
                        if should_ignore:
                            translated_kv[bk] = base_kv[
                                bk]  # copy from base set
                        reversed_matched_kv[bk] = {
                            "ratio": ratio,
                            "ignored": should_ignore
                        }

        updated_content = []
        for item in base_content:
            k = item['key']
            newitem = dict.fromkeys(item.keys())
            newitem['key'] = k

            target_value, target_comment = target_kv.get(k), target_kc.get(k)
            newitem['value'] = target_value or item['value']
            newitem[
                'comment'] = target_comment if __IGNORE_COMMENTS__ else target_comment or base_kc[
                    k]
            needs_update_comment = False if __IGNORE_COMMENTS__ else not target_comment and base_kc[
                k]

            # added
            if k in adding_keys:
                if k in translated_kv:
                    newitem['value'] = translated_kv[k]
                    if not newitem['comment']:
                        newitem['comment'] = 'Translated from: {0}'.format(
                            base_kv[k])

                    reversed_matched_msg = ''
                    if k in reversed_matched_kv:
                        reversed_matched_msg = Fore.CYAN + "({}% Matched{}: \'{}\' <- \'{}\' <- \'{}\')".format(
                            reversed_matched_kv[k]["ratio"], ", So ignored [X]"
                            if reversed_matched_kv[k]["ignored"] else "",
                            reversed_translated_kv[k], newitem['value'],
                            base_kv[k]) + Style.RESET_ALL

                    print(
                        '[Add] "{0}" = "{1}" <- {2}'.format(
                            k, newitem['value'], base_kv[k]),
                        reversed_matched_msg)
                else:
                    newitem['value'] = target_kv[k]
                    if not newitem['comment']:
                        newitem[
                            'comment'] = 'Translate failed from: {0}'.format(
                                base_kv[k])

                    print(Fore.RED + '[Error] "{0}" = "{1}" X <- {2}'.format(
                        k, newitem['value'], base_kv[k]) + Style.RESET_ALL)

            # exists
            elif k in existing_keys:

                if k != "Base" and __CUTTING_LENGTH_RATIO__ > 0:

                    if target_value != base_kv[k] \
                            and len_unicode(target_value) > float(len_unicode(base_kv[k]))*__CUTTING_LENGTH_RATIO__ \
                            or needs_update_comment:

                        print(Fore.YELLOW + '(!) Length of "', target_value,
                              '" is longer than"', base_kv[k], '" as',
                              len(target_value), '>', len(base_kv[k]),
                              Style.RESET_ALL)
                        newitem['value'] = base_kv[k]
                        updated_keys.append(k)

                        if not lc in global_result_logs:
                            global_result_logs[lc] = {}
                        global_result_logs[lc][k] = (target_value, base_kv[k])
                    else:
                        newitem['value'] = target_value or base_kv[k]

                elif k in __KEYS_FOLLOW_BASE__:
                    newitem['value'] = base_kv[k]
                    if target_value != base_kv[k] or needs_update_comment:
                        updated_keys.append(k)

                else:
                    newitem['value'] = target_value or base_kv[k]
                    if not target_value or needs_update_comment:
                        updated_keys.append(k)

            updated_content.append(newitem)

        # removed or wrong
        for k in removing_keys:
            print(Fore.RED + '[Remove]', k, Style.RESET_ALL)

        if len(adding_keys) or len(updated_keys) or len(removing_keys):
            print(
                Fore.WHITE +
                '(i) Changed Keys: Added {0}, Updated {1}, Removed {2}'.format(
                    len(adding_keys), len(updated_keys), len(removing_keys)),
                Style.RESET_ALL)

        # check verification failed items
        target_verified_items = None
        if len(reversed_matched_kv):
            target_verified_items = {
                k: {
                    'ratio': reversed_matched_kv[k]["ratio"],
                    'original': base_kv[k],
                    'reversed': reversed_translated_kv[k],
                    'translated': translated_kv[k]
                }
                for k in reversed_matched_kv.keys()
            }

        return updated_content and (
            len(adding_keys) > 0 or len(updated_keys) > 0
            or len(removing_keys) > 0
        ), updated_content, translated_kv, target_error_lines, target_verified_items
Example #3
0
def main():
    parser = argparse.ArgumentParser(
        description=
        'Automatically translate and synchronize .strings files from defined base language.'
    )
    parser.add_argument(
        '-b',
        '--base-lang-name',
        help=
        'A base(or source) localizable resource name.(default=\'Base\'), (e.g. "Base" via \'Base.lproj\', "en" via \'en.lproj\')',
        default='Base',
        required=False)
    parser.add_argument(
        '-x',
        '--excluding-lang-names',
        type=str,
        help=
        'A localizable resource name that you want to exclude. (e.g. "Base" via \'Base.lproj\', "en" via \'en.lproj\')',
        default=[],
        required=False,
        nargs='+')
    parser.add_argument(
        '-f',
        '--force-translate-keys',
        type=str,
        help=
        'Keys in the strings to update and translate by force. (input nothing for all keys.)',
        default=[],
        required=False,
        nargs='*')
    parser.add_argument('-o',
                        '--following-base-keys',
                        type=str,
                        help='Keys in the strings to follow from "Base.',
                        default=[],
                        required=False,
                        nargs='+')
    parser.add_argument(
        '-w',
        '--following-base-if-not-exists',
        type=str,
        help=
        'With this option, all keys will be followed up with base values if they does not exist.',
        default=None,
        required=False,
        nargs='*')
    parser.add_argument(
        '-l',
        '--cutting-length-ratio-with-base',
        type=float,
        help='Keys in the float as the ratio to compare the length of "Base"',
        default=[],
        required=False,
        nargs='+')
    parser.add_argument('-c',
                        '--ignore-comments',
                        help='Allows ignoring comment synchronization.',
                        default=None,
                        required=False,
                        nargs='*')
    parser.add_argument('-v',
                        '--verify-results',
                        help='Verify translated results via reversed results',
                        default=None,
                        required=False,
                        nargs='*')
    parser.add_argument(
        '-s',
        '--include-secondary-languages',
        help='Include Additional Secondary Languages. (+63 language codes)',
        default=None,
        required=False,
        nargs='*')
    parser.add_argument(
        '-i',
        '--ignore-unverified-results',
        help='Allows ignoring unverified results when appending them.',
        default=None,
        required=False,
        nargs='*')
    parser.add_argument(
        'target path',
        help=
        'Target localization resource path. (root path of Base.lproj, default=./)',
        default='./',
        nargs='?')
    parser.add_argument(
        'only for keys',
        help=
        'Some specified keys for exclusive work. All operations will work for only that keys therefore other keys will be ignored. Not specified by default. (default=None)',
        default=None,
        nargs='*')
    args = vars(parser.parse_args())

    reload(sys)
    sys.setdefaultencoding('utf-8')

    # configure arguments
    __LOCALE_XCODE_BASE_LOWERCASE__ = 'base'

    __DIR_SUFFIX__ = ".lproj"
    __FILE_SUFFIX__ = ".strings"
    __FILE_INTENT_SUFFIX__ = ".intentdefinition"
    __FILE_DICT_SUFFIX__ = ".stringsdict"
    __RESOURCE_PATH__ = expanduser(args['target path'])
    __ONLY_FOR_KEYS__ = args['only for keys']
    __BASE_LANG__ = args['base_lang_name']
    __EXCLUDING_LANGS__ = args['excluding_lang_names']
    __KEYS_FORCE_TRANSLATE__ = args['force_translate_keys']
    __KEYS_FORCE_TRANSLATE_ALL__ = (
        '--force-translate-keys' in sys.argv
        or '-f' in sys.argv) and not __KEYS_FORCE_TRANSLATE__
    __KEYS_FOLLOW_BASE__ = args['following_base_keys']
    __CUTTING_LENGTH_RATIO__ = (args['cutting_length_ratio_with_base']
                                or [0])[0]
    __FOLLOWING_ALL_KEYS_IFNOT_EXIST__ = args[
        'following_base_if_not_exists'] is not None

    __IGNORE_COMMENTS__ = args['ignore_comments'] is not None
    __IGNORE_UNVERIFIED_RESULTS__ = args[
        'ignore_unverified_results'] is not None
    __RATIO_TO_IGNORE_UNVERIFIED_RESULTS__ = int(
        args['ignore_unverified_results']
        [0]) if __IGNORE_UNVERIFIED_RESULTS__ and len(
            args['ignore_unverified_results']) else 0
    __VERIFY_TRANS_RESULTS__ = __IGNORE_UNVERIFIED_RESULTS__ or args[
        'verify_results'] is not None
    __INCLUDE_SECONDARY_LANGUAGES__ = args[
        'include_secondary_languages'] is not None

    # Locale settings
    # [language designator] en, fr
    # [language designator]_[region designator] en_GB, zh_HK
    # [language designator]-[script designator] az-Arab, zh-Hans
    # [language designator]-[script designator]_[region designator] zh-Hans_HK
    print('(i) Initializing for supported languages ...')
    __lang_codes = strlocale.default_supporting_xcode_lang_codes()

    if __INCLUDE_SECONDARY_LANGUAGES__:
        __lang_codes += strlocale.secondary_supporting_xcode_lang_codes()

    __XCODE_LPROJ_SUPPORTED_LOCALES_MAP__ = strlocale.map_locale_codes(
        __lang_codes, strtrans.supported_locale_codes())
    __XCODE_LPROJ_SUPPORTED_LOCALES__ = __XCODE_LPROJ_SUPPORTED_LOCALES_MAP__.keys(
    )
    print(Fore.WHITE + '(i) Supported numbers of locale code :',
          str(len(__XCODE_LPROJ_SUPPORTED_LOCALES__)), Style.RESET_ALL)
    print(__XCODE_LPROJ_SUPPORTED_LOCALES__)

    # handle base
    if __BASE_LANG__.endswith(__DIR_SUFFIX__):
        __BASE_RESOUCE_DIR__ = __BASE_LANG__
        __BASE_LANG__ = __BASE_LANG__.split(__DIR_SUFFIX__)[0]
    else:
        __BASE_RESOUCE_DIR__ = __BASE_LANG__ + __DIR_SUFFIX__

    if not __BASE_LANG__.lower() == __LOCALE_XCODE_BASE_LOWERCASE__:
        __BASE_LANG__ = strlocale.lang(__BASE_LANG__)

    # setup Translator & langs

    # read ios langs
    print(Fore.WHITE + '(i) Fetching supported locale codes for ios9 ...',
          Style.RESET_ALL)
    __IOS9_CODES__ = [
        lang_row[0] for lang_row in csv.reader(
            open(resolve_file_path('lc_ios9.tsv'), 'rb'), delimiter='\t')
    ]
    print(Fore.WHITE + '(i) Supported numbers of locale code :',
          len(__IOS9_CODES__), Style.RESET_ALL)

    global_result_logs = {}

    def merge_two_dicts(x, y):
        '''Given two dicts, merge them into a new dict as a shallow copy.'''
        z = x.copy()
        z.update(y)
        return z

    # core function
    def synchronize(target_file,
                    lc):  #add,remove, update (translate or copy from base)
        # parse target file
        target_kv = {}
        target_kc = {}
        target_error_lines = []
        if not notexist_or_empty_file(target_file):
            parsed_strings = strparser.parse_strings(filename=target_file)
            for item in parsed_strings:
                k, e = item['key'], item['error']
                # line error
                if e:
                    target_error_lines.append(e)
                if not target_error_lines:
                    target_kv[k] = item['value']
                    target_kc[k] = item['comment']

        # parsing complete or return.
        if target_error_lines:
            print('(!) Syntax error - Skip')
            return False, None, None, target_error_lines

        # base
        base_content = base_dict[os.path.basename(target_file)]
        base_kv = {}
        base_kc = {}
        for item in base_content:
            k, e = item['key'], item['error']
            # line error
            if e:
                print('(!) WARNING : Syntax error from Base -> ', k, ':', e)
            base_kv[k] = item['value']
            base_kc[k] = item['comment']

        force_adding_keys = base_kv.keys(
        ) if __KEYS_FORCE_TRANSLATE_ALL__ else __KEYS_FORCE_TRANSLATE__
        adding_keys = list(
            ((set(base_kv.keys()) - set(target_kv.keys())) | (set(base_kv.keys()) & set(force_adding_keys))) \
            - set(base_kv.keys() if __FOLLOWING_ALL_KEYS_IFNOT_EXIST__ else __KEYS_FOLLOW_BASE__) \
        )
        removing_keys = list(set(target_kv.keys()) - set(base_kv.keys()))
        existing_keys = list(
            set(base_kv.keys()) - (set(adding_keys) | set(removing_keys)))

        # Filter if __ONLY_FOR_KEYS__ option activated
        if __ONLY_FOR_KEYS__:
            adding_keys = list(set(adding_keys) & set(__ONLY_FOR_KEYS__))
            removing_keys = list(set(removing_keys) & set(__ONLY_FOR_KEYS__))
            existing_keys = list(set(existing_keys) & set(__ONLY_FOR_KEYS__))

        updated_keys = []
        """
        perform translate
        """
        translated_kv = {}
        reversed_matched_kv = {}  # {"ratio":float, "ignored":True|False}
        reversed_translated_kv = {}

        if len(adding_keys):
            print('Translating...')
            translated_kv = dict(
                zip(
                    adding_keys,
                    strtrans.translate_strs([base_kv[k] for k in adding_keys],
                                            lc)))

            if __VERIFY_TRANS_RESULTS__:
                print('Reversing results and matching...')
                reversed_translated_kv = dict(
                    zip(
                        adding_keys,
                        strtrans.translate_strs(
                            [translated_kv[_ak] for _ak in adding_keys],
                            'en')))

                for bk in adding_keys:
                    if bk in reversed_translated_kv:
                        ratio = fuzz.partial_ratio(base_kv[bk],
                                                   reversed_translated_kv[bk])
                        should_ignore = __IGNORE_UNVERIFIED_RESULTS__ and ratio <= __RATIO_TO_IGNORE_UNVERIFIED_RESULTS__
                        if should_ignore:
                            translated_kv[bk] = base_kv[
                                bk]  # copy from base set
                        reversed_matched_kv[bk] = {
                            "ratio": ratio,
                            "ignored": should_ignore
                        }

        updated_content = []
        for item in base_content:
            k = item['key']
            newitem = dict.fromkeys(item.keys())
            newitem['key'] = k

            target_value, target_comment = target_kv.get(k), target_kc.get(k)
            newitem['value'] = target_value or item['value']
            newitem[
                'comment'] = target_comment if __IGNORE_COMMENTS__ else target_comment or base_kc[
                    k]
            needs_update_comment = False if __IGNORE_COMMENTS__ else not target_comment and base_kc[
                k]

            # added
            if k in adding_keys:
                if k in translated_kv:
                    newitem['value'] = translated_kv[k]
                    if not newitem['comment']:
                        newitem['comment'] = 'Translated from: {0}'.format(
                            base_kv[k])

                    reversed_matched_msg = ''
                    if k in reversed_matched_kv:
                        reversed_matched_msg = Fore.CYAN + "({}% Matched{}: \'{}\' <- \'{}\' <- \'{}\')".format(
                            reversed_matched_kv[k]["ratio"], ", So ignored [X]"
                            if reversed_matched_kv[k]["ignored"] else "",
                            reversed_translated_kv[k], newitem['value'],
                            base_kv[k]) + Style.RESET_ALL

                    print(
                        '[Add] "{0}" = "{1}" <- {2}'.format(
                            k, newitem['value'], base_kv[k]),
                        reversed_matched_msg)
                else:
                    newitem['value'] = target_kv[k]
                    if not newitem['comment']:
                        newitem[
                            'comment'] = 'Translate failed from: {0}'.format(
                                base_kv[k])

                    print(Fore.RED + '[Error] "{0}" = "{1}" X <- {2}'.format(
                        k, newitem['value'], base_kv[k]) + Style.RESET_ALL)

            # exists
            elif k in existing_keys:

                if k != "Base" and __CUTTING_LENGTH_RATIO__ > 0:

                    if target_value != base_kv[k] \
                            and len_unicode(target_value) > float(len_unicode(base_kv[k]))*__CUTTING_LENGTH_RATIO__ \
                            or needs_update_comment:

                        print(Fore.YELLOW + '(!) Length of "', target_value,
                              '" is longer than"', base_kv[k], '" as',
                              len(target_value), '>', len(base_kv[k]),
                              Style.RESET_ALL)
                        newitem['value'] = base_kv[k]
                        updated_keys.append(k)

                        if not lc in global_result_logs:
                            global_result_logs[lc] = {}
                        global_result_logs[lc][k] = (target_value, base_kv[k])
                    else:
                        newitem['value'] = target_value or base_kv[k]

                elif k in __KEYS_FOLLOW_BASE__:
                    newitem['value'] = base_kv[k]
                    if target_value != base_kv[k] or needs_update_comment:
                        updated_keys.append(k)

                else:
                    newitem['value'] = target_value or base_kv[k]
                    if not target_value or needs_update_comment:
                        updated_keys.append(k)

            updated_content.append(newitem)

        # removed or wrong
        for k in removing_keys:
            print(Fore.RED + '[Remove]', k, Style.RESET_ALL)

        if len(adding_keys) or len(updated_keys) or len(removing_keys):
            print(
                Fore.WHITE +
                '(i) Changed Keys: Added {0}, Updated {1}, Removed {2}'.format(
                    len(adding_keys), len(updated_keys), len(removing_keys)),
                Style.RESET_ALL)

        # check verification failed items
        target_verified_items = None
        if len(reversed_matched_kv):
            target_verified_items = {
                k: {
                    'ratio': reversed_matched_kv[k]["ratio"],
                    'original': base_kv[k],
                    'reversed': reversed_translated_kv[k],
                    'translated': translated_kv[k]
                }
                for k in reversed_matched_kv.keys()
            }

        return updated_content and (
            len(adding_keys) > 0 or len(updated_keys) > 0
            or len(removing_keys) > 0
        ), updated_content, translated_kv, target_error_lines, target_verified_items

    def write_file(target_file, parsed_list):
        suc = False
        try:
            f = codecs.open(target_file, "w", "utf-8")
            contents = ''
            for content in parsed_list:
                if content['comment']:
                    contents += '/*{0}*/'.format(content['comment']) + '\n'
                contents += '"{0}" = "{1}";'.format(content['key'],
                                                    content['value']) + '\n'
            f.write(contents)
            suc = True
        except IOError:
            print('IOError to open', target_file)
        finally:
            f.close()
        return suc

    def remove_file(target_file):
        try:
            os.rename(target_file, target_file + '.deleted')
            return True
        except IOError:
            print('IOError to rename', target_file)
            return False

    def create_file(target_file):
        open(target_file, 'a').close()

    def notexist_or_empty_file(target_file):
        return not os.path.exists(target_file) or os.path.getsize(
            target_file) == 0

    def resolve_file_names(target_file_names):
        return map(
            lambda f: f.decode('utf-8'),
            filter(
                lambda f: f.endswith(__FILE_SUFFIX__) or f.endswith(
                    __FILE_INTENT_SUFFIX__), target_file_names))

    base_dict = {}
    results_dict = {}

    # Get Base Language Specs
    walked = list(os.walk(__RESOURCE_PATH__, topdown=True))

    # Init with Base.lproj
    for dir, subdirs, files in walked:
        if os.path.basename(dir) == __BASE_RESOUCE_DIR__:
            for _file in resolve_file_names(files):
                f = os.path.join(dir, _file)

                if notexist_or_empty_file(f):
                    continue

                parsed_objs = None

                # parse .strings
                if f.endswith(__FILE_SUFFIX__):
                    parsed_objs = strparser.parse_strings(filename=f)

                # parse .intentdefinition
                elif f.endswith(__FILE_INTENT_SUFFIX__):
                    print('[i] Found "{0}" in {1}. Parse ....'.format(
                        os.path.basename(f), __BASE_RESOUCE_DIR__))
                    parsed_objs = strparser_intentdefinition.parse_strings(f)
                    # replace to dest extenstion .strings
                    _file = _file.replace(__FILE_INTENT_SUFFIX__,
                                          __FILE_SUFFIX__)
                    # write original .strings file to local
                    write_file(os.path.join(dir, _file), parsed_objs)

                if not parsed_objs:
                    continue

                base_dict[_file] = parsed_objs

    if not base_dict:
        print('[!] Not found "{0}" in target path "{1}"'.format(
            __BASE_RESOUCE_DIR__, __RESOURCE_PATH__))
        sys.exit(0)

    # Exist or Create supporting lproj dirs.
    print('Check and verifiy resources ...')
    current_lproj_names = [
        os.path.splitext(os.path.basename(lproj_path))[0]
        for lproj_path in filter(lambda d: d.endswith(__DIR_SUFFIX__),
                                 [dir for dir, subdirs, files in walked])
    ]
    notexisted_lproj_names = list(
        set(__XCODE_LPROJ_SUPPORTED_LOCALES__) - set(current_lproj_names))

    creating_lproj_dirs = [
        expanduser(os.path.join(__RESOURCE_PATH__, ln + __DIR_SUFFIX__))
        for ln in notexisted_lproj_names
    ]
    if creating_lproj_dirs:
        print('Following lproj dirs does not exists. Creating ...')
        for d in creating_lproj_dirs:
            print('Created', d)
            os.mkdir(d)

    # Start to sync localizable files.
    print('Start synchronizing...')
    for file in base_dict:
        print('Target:', file)

    for dir, subdirs, files in walked:
        files = resolve_file_names(files)

        if dir.endswith((__DIR_SUFFIX__)):
            lproj_name = os.path.basename(dir).split(__DIR_SUFFIX__)[0]

            if lproj_name == __BASE_LANG__:
                continue

            if not lproj_name in __XCODE_LPROJ_SUPPORTED_LOCALES_MAP__:
                print('Does not supported: ', lproj_name)
                continue

            lc = __XCODE_LPROJ_SUPPORTED_LOCALES_MAP__[lproj_name]

            if strlocale.matched_locale_code(lc, __EXCLUDING_LANGS__):
                print('Skip: ', lc)
                continue

            results_dict[lc] = {
                'deleted_files': [],
                'added_files': [],
                'updated_files': [],
                'skipped_files': [],
                'translated_files_lines': {},
                'error_lines_kv': {},
                'verified_result': {}
            }

            # if not supported_lang(lc):
            #     print('Does not supported: ', lc)
            #     results_dict[lc]['skipped_files'] = join_path_all(dir, files)
            #     continue

            print('\n',
                  'Analayzing localizables... {1} (at {0})'.format(dir, lc))

            added_files = list(set(base_dict.keys()) - set(files))
            removed_files = list(set(files) - set(base_dict.keys()))
            existing_files = list(
                set(files) - (set(added_files) | set(removed_files)))

            added_files = join_path_all(dir, added_files)
            removed_files = join_path_all(dir, removed_files)
            existing_files = join_path_all(dir, existing_files)

            added_cnt, updated_cnt, removed_cnt = 0, 0, 0
            translated_files_lines = results_dict[lc]['translated_files_lines']
            error_files = results_dict[lc]['error_lines_kv']

            # remove - file
            for removed_file in removed_files:
                print('Removing File... {0}'.format(removed_file))
                if remove_file(removed_file):
                    removed_cnt += 1

            # add - file
            for added_file in added_files:
                print('Adding File... {0}'.format(added_file))
                create_file(added_file)
                u, c, t, e, m = synchronize(added_file, lc)
                # error
                if e:
                    error_files[added_file] = e
                # normal
                elif u and write_file(added_file, c):
                    added_cnt += 1
                    translated_files_lines[added_file] = t

                # verify failed
                for k in (m or {}):
                    results_dict[lc]['verified_result'][k] = m[k]

            # exist - lookup lines
            for ext_file in existing_files:
                u, c, t, e, m = synchronize(ext_file, lc)
                # error
                if e:
                    error_files[ext_file] = e
                # normal
                elif u:
                    print('Updating File... {0}'.format(ext_file))
                    if write_file(ext_file, c):
                        updated_cnt = +1
                        translated_files_lines[ext_file] = t

                # verify failed
                for k in (m or {}):
                    results_dict[lc]['verified_result'][k] = m[k]

            if added_cnt or updated_cnt or removed_cnt or error_files:
                print(
                    Fore.WHITE +
                    '(i) Changed Files : Added {0}, Updated {1}, Removed {2}, Error {3}'
                    .format(added_cnt, updated_cnt, removed_cnt,
                            len(error_files.keys())), Style.RESET_ALL)
            else:
                print('Nothing to translate or add.')
            """
            Results
            """
            results_dict[lc]['deleted_files'] = removed_files
            results_dict[lc]['added_files'] = list(
                set(added_files) & set(translated_files_lines.keys()))
            results_dict[lc]['updated_files'] = list(
                set(existing_files) & set(translated_files_lines.keys()))
            if error_files:
                print(error_files)
            results_dict[lc]['error_lines_kv'] = error_files

    # print(total Results)
    print('')
    t_file_cnt = \
        t_line_cnt = \
        file_add_cnt = \
        file_add_cnt = \
        file_remove_cnt = \
        file_update_cnt = \
        file_skip_cnt = \
        0

    for lc in results_dict.keys():
        result_lc = results_dict[lc]

        file_add_cnt += len(result_lc['added_files'])
        file_remove_cnt += len(result_lc['deleted_files'])
        file_update_cnt += len(result_lc['updated_files'])
        file_skip_cnt += len(result_lc['skipped_files'])

        for f in result_lc['added_files']:
            print('Added', f)
        for f in result_lc['deleted_files']:
            print('Removed', f)
        for f in result_lc['updated_files']:
            print('Updated', f)
        for f in result_lc['skipped_files']:
            print('Skiped', f)

        tfiles = result_lc['translated_files_lines']
        if tfiles:
            # print('============ Results for langcode : {0} ============='.format(lc))
            for f in tfiles:
                t_file_cnt += 1
                if len(tfiles[f]):
                    # print('', f)
                    for key in tfiles[f]:
                        t_line_cnt += 1
                        # print(key, ' = ', tfiles[f][key])

    for lc in global_result_logs.keys():
        print(lc)
        for t in global_result_logs[lc].keys():
            o, b = global_result_logs[lc][t]
            print(o.decode('utf-8'), ' -> ', b)

    print('')
    # WARN
    found_warining = filter(lambda i: i or None,
                            rget(results_dict, 'error_lines_kv'))
    if found_warining:
        print(
            Fore.YELLOW +
            '\n[!] WARNING: Found strings that contains the syntax error. Please confirm.'
            + Style.RESET_ALL)
        for a in found_warining:
            for k in a:
                print('at', k)
                for i in a[k]:
                    print(' ', i)
    # VERIFY FAILED
    verified_results = filter(lambda i: i or None,
                              rget(results_dict, 'verified_result'))
    if verified_results and len(verified_results):
        print(
            Fore.GREEN +
            '\n[i] VERIFIED RESULTS: Matched ratio via reversed translation results. Please confirm.'
            + Style.RESET_ALL)
        for lc in results_dict:
            print(lc)
            vr = results_dict[lc]['verified_result']
            for k in vr:
                vd = vr[k]
                status_msg = Fore.RED + '(Ignored) ' + Style.RESET_ALL if __IGNORE_UNVERIFIED_RESULTS__ and vd[
                    'ratio'] <= __RATIO_TO_IGNORE_UNVERIFIED_RESULTS__ else ''

                print('  {}{}: {} -> {} -> {}, Matched: {}%'.format(
                    status_msg, k, vd['original'], vd['translated'],
                    vd['reversed'], str(vd['ratio'])))

    print('')
    if file_add_cnt or file_update_cnt or file_remove_cnt or file_skip_cnt:
        print('Total New Translated Strings : {0}'.format(t_line_cnt))
        print(
            'Changed Files Total : Added {0}, Updated {1}, Removed {2}, Skipped {3}'
            .format(file_add_cnt, file_update_cnt, file_remove_cnt,
                    file_skip_cnt))
        print("Synchronized.")
    else:
        print(
            "All strings are already synchronized. Nothing to translate or add."
        )

    return
Example #4
0
 def strings_obj_from_file(file):
     return strparser.parse_strings(filename=file)