docs: def relaxng(filename=None):
      """Generates a RelaxNG Schema for FoLiA. Optionally saves it to file.
  
      Args:
          filename (str): Save the schema to the following filename
  
      Returns:
          lxml.ElementTree: The schema
      """
      E = ElementMaker(namespace="http://relaxng.org/ns/structure/1.0",nsmap={None:'http://relaxng.org/ns/structure/1.0' , 'folia': NSFOLIA, 'xml' : "http://www.w3.org/XML/1998/namespace"})
      grammar = E.grammar( E.start( E.element( #FoLiA
                  E.attribute(name='id',ns="http://www.w3.org/XML/1998/namespace"),
                  E.optional( E.attribute(name='version') ),
                  E.optional( E.attribute(name='generator') ),
                  E.element( #metadata
                      E.optional(E.attribute(name='type')),
                      E.optional(E.attribute(name='src')),
                      E.element( E.zeroOrMore( E.choice( *relaxng_declarations() ) ) ,name='annotations'),
                      E.zeroOrMore(
                          E.element(E.attribute(name='id'), E.text(), name='meta'),
                      ),
                      E.zeroOrMore(
                          E.ref(name="foreign-data"),
                      ),
                      E.zeroOrMore(
                          E.element( #submetadata
                              E.attribute(name='id',ns="http://www.w3.org/XML/1998/namespace"),
                              E.optional(E.attribute(name='type')),
                              E.optional(E.attribute(name='src')),
                              E.zeroOrMore(
                                  E.element(E.attribute(name='id'), E.text(), name='meta'),
                              ),
                              E.zeroOrMore(
                                  E.ref(name="foreign-data"),
                              ),
                              name="submetadata"
                          )
                      ),
                      #E.optional(
                      #    E.ref(name='METATRANSCRIPT')
                      #),
                      name='metadata',
                      #ns=NSFOLIA,
                  ),
                  E.interleave(
                      E.zeroOrMore(
                          E.ref(name='text'),
                      ),
                      E.zeroOrMore(
                          E.ref(name='speech'),
                      ),
                  ),
                  name='FoLiA',
                  ns = NSFOLIA
              ) ),
              #definitions needed for ForeignData (allow any content) - see http://www.microhowto.info/howto/match_arbitrary_content_using_relax_ng.html
              E.define( E.interleave(E.zeroOrMore(E.ref(name="any_element")),E.text()), name="any_content"),
              E.define( E.element(E.anyName(), E.zeroOrMore(E.ref(name="any_attribute")), E.zeroOrMore(E.ref(name="any_content"))), name="any_element"),
              E.define( E.attribute(E.anyName()), name="any_attribute"),
              #Definition for allowing alien-namespace attributes on any element
              E.define( E.zeroOrMore(E.attribute(E.anyName(getattr(E,'except')(E.nsName(),E.nsName(ns=""),E.nsName(ns="http://www.w3.org/XML/1998/namespace"),E.nsName(ns="http://www.w3.org/1999/xlink"))))), name="allow_foreign_attributes"),
              datatypeLibrary="http://www.w3.org/2001/XMLSchema-datatypes",
              )
  
      done = {}
      for c in globals().values():
          if 'relaxng' in dir(c):
              if c.relaxng and c.XMLTAG and not c.XMLTAG in done:
                  done[c.XMLTAG] = True
                  definition = c.relaxng()
                  grammar.append( definition )
                  if c.XMLTAG == 'item': #nasty backward-compatibility hack to allow deprecated listitem element (actually called item)
                      definition_alias = c.relaxng()
                      definition_alias.set('name','listitem')
                      definition_alias[0].set('name','listitem')
                      grammar.append( definition_alias )
  
      #for e in relaxng_imdi():
      #    grammar.append(e)
      if filename:
          if sys.version < '3':
              f = io.open(filename,'w',encoding='utf-8')
          else:
              f = io.open(filename,'wb')
          if LXE:
              if sys.version < '3':
                  f.write( ElementTree.tostring(relaxng(),pretty_print=True).replace("</define>","</define>\n\n") )
              else:
                  f.write( ElementTree.tostring(relaxng(),pretty_print=True).replace(b"</define>",b"</define>\n\n") )
          else:
              f.write( ElementTree.tostring(relaxng()).replace("</define>","</define>\n\n") )
          f.close()
  
      return grammar