# Path: openformats/exceptions.py
# class ParseError(OpenformatsError):
#     pass
# 
# Path: openformats/handlers.py
# class Handler(object):
#     """
#     This class defines the interface you need to implement in order to create a
#     handler. Both the `parse` and `compile` methods must be implemented.
#     """
# 
#     name = None
#     extension = None
# 
#     _RULES_ATOI = {
#         'zero': 0,
#         'one': 1,
#         'two': 2,
#         'few': 3,
#         'many': 4,
#         'other': 5
#     }
# 
#     EXTRACTS_RAW = True
# 
#     # Use this flag for handlers whose the content should be processed as
#     # binary and not be converted to unicode
#     PROCESSES_BINARY = False
# 
#     _RULES_ITOA = {value: key for key, value in six.iteritems(_RULES_ATOI)}
# 
#     _RULE_ERROR_MSG = (
#         '{attempted} is not a valid rule value. Valid choices are {valid}'
#     )
# 
#     @classmethod
#     def get_rule_number(cls, string_value):
#         try:
#             return cls._RULES_ATOI[string_value]
#         except KeyError:
#             msg = cls._RULE_ERROR_MSG.format(
#                 attempted=string_value,
#                 valid=list(six.iterkeys(cls._RULES_ATOI))
#             )
#             raise RuleError(msg)
# 
#     @classmethod
#     def get_rule_string(cls, number_value):
#         try:
#             return cls._RULES_ITOA[number_value]
#         except KeyError:
#             msg = cls._RULE_ERROR_MSG.format(
#                 attempted=number_value,
#                 valid=list(six.iterkeys(cls._RULES_ITOA))
#             )
#             raise RuleError(msg)
# 
#     def parse(self, content, is_source=False):
#         """
#         Parses the content, extracts translatable strings into a stringset,
#         replaces them with hashes and returns a tuple of the template with the
#         stringset
# 
#         Typically this is done in the following way:
# 
#         * Use a library or your own code to segment (deserialize) the content
#           into translatable entities.
#         * Choose a key to uniquely identify the entity.
#         * Create an ``OpenString`` object representing the entity.
#         * Create a hash to replace the original content with.
#         * Create a stringset with the content.
#         * Use library or own code to serialize stringset back into a template.
#         """
# 
#         raise NotImplementedError('Abstract method')  # pragma: no cover
# 
#     def compile(self, template, stringset, **kwargs):
#         """
#         Parses the template, finds the hashes, replaces them with strings from
#         the stringset and returns the compiled file. If a hash in the template
#         isn't found in the stringset, it's a good practice to remove the whole
#         string section surrounding it
# 
#         Typically this is done in the following way:
# 
#         * Use a library or own code to segment (deserialize) the template into
#           translatable entities, as if assuming that the hashes are the
#           translatable entities.
#         * Make sure the hash matches the first string in the stringset.
#         * Replace the hash with the string.
#         * Use library or own code to serialize stringset back into a compiled
#           file.
# 
#         You can safely assume that the stringset will have strings in the
#         correct order for the above process and thus you will probably be able
#         to perform the whole compilation in a single pass.
#         """
# 
#         raise NotImplementedError('Abstract method')  # pragma: no cover
# 
# Path: openformats/utils/compat.py
# def ensure_unicode(pattern):
#     if isinstance(pattern, six.binary_type):
#         pattern = pattern.decode('utf-8')
#     return pattern


import re
import pyparsing
import six
from openformats.exceptions import ParseError
from openformats.handlers import Handler
from openformats.utils.compat import ensure_unicode
                'with key: "{key}", serialized translations: "{trans_str}". '
                'Could not parse the string at or near '
                'the following chunk: "{chunk}". '
                'It contains either invalid braces ("{{", "}}") '
                'or invalid characters.'.format(
                    key=key,
                    trans_str=serialized_strings,
                    chunk=remaining_str,
                )
            )

    def _handle_invalid_plural_format(self, serialized_strings,
                                      any_plural_item, key, value):
        """
        Raise a descriptive ParseError exception when the serialized
        translation string of a plural string is not properly formatted.

        :param serialized_strings:
        :param any_plural_item: a forgiving pyparsing element that matches all
            strings formatted like '<alphanumeric> {...}'

        :raise: ParseError
        """
        all_matches = any_plural_item.searchString(serialized_strings)
        all_keys = [match[0] for match in all_matches]

        invalid_rules = [
            rule for rule in all_keys
            if rule not in six.iterkeys(Handler._RULES_ATOI)
        ]

# Path: openformats/exceptions.py
# class ParseError(OpenformatsError):
#     pass
# 
# Path: openformats/handlers.py
# class Handler(object):
#     """
#     This class defines the interface you need to implement in order to create a
#     handler. Both the `parse` and `compile` methods must be implemented.
#     """
# 
#     name = None
#     extension = None
# 
#     _RULES_ATOI = {
#         'zero': 0,
#         'one': 1,
#         'two': 2,
#         'few': 3,
#         'many': 4,
#         'other': 5
#     }
# 
#     EXTRACTS_RAW = True
# 
#     # Use this flag for handlers whose the content should be processed as
#     # binary and not be converted to unicode
#     PROCESSES_BINARY = False
# 
#     _RULES_ITOA = {value: key for key, value in six.iteritems(_RULES_ATOI)}
# 
#     _RULE_ERROR_MSG = (
#         '{attempted} is not a valid rule value. Valid choices are {valid}'
#     )
# 
#     @classmethod
#     def get_rule_number(cls, string_value):
#         try:
#             return cls._RULES_ATOI[string_value]
#         except KeyError:
#             msg = cls._RULE_ERROR_MSG.format(
#                 attempted=string_value,
#                 valid=list(six.iterkeys(cls._RULES_ATOI))
#             )
#             raise RuleError(msg)
# 
#     @classmethod
#     def get_rule_string(cls, number_value):
#         try:
#             return cls._RULES_ITOA[number_value]
#         except KeyError:
#             msg = cls._RULE_ERROR_MSG.format(
#                 attempted=number_value,
#                 valid=list(six.iterkeys(cls._RULES_ITOA))
#             )
#             raise RuleError(msg)
# 
#     def parse(self, content, is_source=False):
#         """
#         Parses the content, extracts translatable strings into a stringset,
#         replaces them with hashes and returns a tuple of the template with the
#         stringset
# 
#         Typically this is done in the following way:
# 
#         * Use a library or your own code to segment (deserialize) the content
#           into translatable entities.
#         * Choose a key to uniquely identify the entity.
#         * Create an ``OpenString`` object representing the entity.
#         * Create a hash to replace the original content with.
#         * Create a stringset with the content.
#         * Use library or own code to serialize stringset back into a template.
#         """
# 
#         raise NotImplementedError('Abstract method')  # pragma: no cover
# 
#     def compile(self, template, stringset, **kwargs):
#         """
#         Parses the template, finds the hashes, replaces them with strings from
#         the stringset and returns the compiled file. If a hash in the template
#         isn't found in the stringset, it's a good practice to remove the whole
#         string section surrounding it
# 
#         Typically this is done in the following way:
# 
#         * Use a library or own code to segment (deserialize) the template into
#           translatable entities, as if assuming that the hashes are the
#           translatable entities.
#         * Make sure the hash matches the first string in the stringset.
#         * Replace the hash with the string.
#         * Use library or own code to serialize stringset back into a compiled
#           file.
# 
#         You can safely assume that the stringset will have strings in the
#         correct order for the above process and thus you will probably be able
#         to perform the whole compilation in a single pass.
#         """
# 
#         raise NotImplementedError('Abstract method')  # pragma: no cover
# 
# Path: openformats/utils/compat.py
# def ensure_unicode(pattern):
#     if isinstance(pattern, six.binary_type):
#         pattern = pattern.decode('utf-8')
#     return pattern


# Path: openformats/utils/icu.py
import re
import pyparsing
import six
from openformats.exceptions import ParseError
from openformats.handlers import Handler
from openformats.utils.compat import ensure_unicode

                'with key: "{key}", serialized translations: "{trans_str}". '
                'Could not parse the string at or near '
                'the following chunk: "{chunk}". '
                'It contains either invalid braces ("{{", "}}") '
                'or invalid characters.'.format(
                    key=key,
                    trans_str=serialized_strings,
                    chunk=remaining_str,
                )
            )

    def _handle_invalid_plural_format(self, serialized_strings,
                                      any_plural_item, key, value):
        """
        Raise a descriptive ParseError exception when the serialized
        translation string of a plural string is not properly formatted.

        :param serialized_strings:
        :param any_plural_item: a forgiving pyparsing element that matches all
            strings formatted like '<alphanumeric> {...}'

        :raise: ParseError
        """
        all_matches = any_plural_item.searchString(serialized_strings)
        all_keys = [match[0] for match in all_matches]

        invalid_rules = [
            rule for rule in all_keys
            if rule not in six.iterkeys(Handler._RULES_ATOI)
        ]

        raise ParseError(