Fix a misuse of a set as a dictionary in db loading.
[zzz-pokedex.git] / pokedex / db / tables.py
index 1db5898..b3001c6 100644 (file)
@@ -15,149 +15,251 @@ Columns have a info dictionary with these keys:
   - identifier: A fan-made identifier in the [-_a-z0-9]* format. Not intended
     for translation.
   - latex: A formula in LaTeX syntax.
   - identifier: A fan-made identifier in the [-_a-z0-9]* format. Not intended
     for translation.
   - latex: A formula in LaTeX syntax.
-- foreign: If set, the column contains foreign (non-English) text.
+- ripped: True for text that has been ripped from the games, and can be ripped
+  again for new versions or languages
 
 
+See `pokedex.db.multilang` for how localizable text columns work.  The session
+classes in that module can be used to change the default language.
 """
 # XXX: Check if "gametext" is set correctly everywhere
 
 """
 # XXX: Check if "gametext" is set correctly everywhere
 
-# XXX: Some columns paradoxically have official=True and markup='identifier'.
-# This is when one column is used as both the English name (lowercased) and
-# an identifier. This should be fixed.
+import collections
+from functools import partial
 
 
-from sqlalchemy import Column, ForeignKey, MetaData, PrimaryKeyConstraint, Table
-from sqlalchemy.ext.declarative import declarative_base
+from sqlalchemy import Column, ForeignKey, MetaData, PrimaryKeyConstraint, Table, UniqueConstraint
+from sqlalchemy.ext.declarative import declarative_base, DeclarativeMeta
 from sqlalchemy.ext.associationproxy import association_proxy
 from sqlalchemy.ext.associationproxy import association_proxy
-from sqlalchemy.orm import backref, eagerload_all, relation
+from sqlalchemy.orm import backref, relation
 from sqlalchemy.orm.session import Session
 from sqlalchemy.orm.session import Session
+from sqlalchemy.orm.interfaces import AttributeExtension
 from sqlalchemy.sql import and_
 from sqlalchemy.sql import and_
+from sqlalchemy.schema import ColumnDefault
 from sqlalchemy.types import *
 
 from sqlalchemy.types import *
 
-from pokedex.db import markdown
+from pokedex.db import markdown, multilang
+
+class TableSuperclass(object):
+    """Superclass for declarative tables, to give them some generic niceties
+    like stringification.
+    """
+    def __unicode__(self):
+        """Be as useful as possible.  Show the primary key, and an identifier
+        if we've got one.
+        """
+        typename = u'.'.join((__name__, type(self).__name__))
+
+        pk_constraint = self.__table__.primary_key
+        if not pk_constraint:
+            return u"<%s object at %x>" % (typename, id(self))
+
+        pk = u', '.join(unicode(getattr(self, column.name))
+            for column in pk_constraint.columns)
+        try:
+            return u"<%s object (%s): %s>" % (typename, pk, self.identifier)
+        except AttributeError:
+            return u"<%s object (%s)>" % (typename, pk)
+
+    def __str__(self):
+        return unicode(self).encode('utf8')
+
+    def __repr__(self):
+        return unicode(self).encode('utf8')
+
+mapped_classes = []
+class TableMetaclass(DeclarativeMeta):
+    def __init__(cls, name, bases, attrs):
+        super(TableMetaclass, cls).__init__(name, bases, attrs)
+        if hasattr(cls, '__tablename__'):
+            mapped_classes.append(cls)
+            cls.translation_classes = []
 
 metadata = MetaData()
 
 metadata = MetaData()
-TableBase = declarative_base(metadata=metadata)
+TableBase = declarative_base(metadata=metadata, cls=TableSuperclass, metaclass=TableMetaclass)
+
+
+### Need Language first, to create the partial() below
+
+class Language(TableBase):
+    u"""A language the Pokémon games have been translated into
+    """
+    __tablename__ = 'languages'
+    __singlename__ = 'language'
+    id = Column(Integer, primary_key=True, nullable=False,
+        info=dict(description="A numeric ID"))
+    iso639 = Column(Unicode(2), nullable=False,
+        info=dict(description="The two-letter code of the country where this language is spoken. Note that it is not unique.", format='identifier'))
+    iso3166 = Column(Unicode(2), nullable=False,
+        info=dict(description="The two-letter code of the language. Note that it is not unique.", format='identifier'))
+    identifier = Column(Unicode(16), nullable=False,
+        info=dict(description="An identifier", format='identifier'))
+    official = Column(Boolean, nullable=False, index=True,
+        info=dict(description=u"True iff games are produced in the language."))
+    order = Column(Integer, nullable=True,
+        info=dict(description=u"Order for sorting in foreign name lists."))
+
+create_translation_table = partial(multilang.create_translation_table, language_class=Language)
+
+create_translation_table('language_names', Language, 'names',
+    name = Column(Unicode(16), nullable=False, index=True,
+        info=dict(description="The name", format='plaintext', official=True)),
+)
+
+### The actual tables
 
 class Ability(TableBase):
 
 class Ability(TableBase):
-    u"""An ability a pokémon can have, such as Static or Pressure.
+    u"""An ability a Pokémon can have, such as Static or Pressure.
     """
     __tablename__ = 'abilities'
     __singlename__ = 'ability'
     id = Column(Integer, primary_key=True, nullable=False,
     """
     __tablename__ = 'abilities'
     __singlename__ = 'ability'
     id = Column(Integer, primary_key=True, nullable=False,
-        info=dict(description="A numeric ID"))
-    name = Column(Unicode(24), nullable=False,
-        info=dict(description="The official English name of this ability", official=True, format='plaintext'))
+        info=dict(description="This ability's unique ID; matches the games' internal ID"))
+    identifier = Column(Unicode(24), nullable=False,
+        info=dict(description="An identifier", format='identifier'))
     generation_id = Column(Integer, ForeignKey('generations.id'), nullable=False,
     generation_id = Column(Integer, ForeignKey('generations.id'), nullable=False,
-        info=dict(description="ID of the generation this ability was introduced in", detail=True))
-    effect = Column(markdown.MarkdownColumn(5120), nullable=False,
-        info=dict(description="Detailed description of this ability's effect", format='markdown'))
-    short_effect = Column(markdown.MarkdownColumn(255), nullable=False,
-        info=dict(description="Short summary of this ability's effect", format='markdown'))
+        info=dict(description="The ID of the generation this ability was introduced in", detail=True))
+
+create_translation_table('ability_names', Ability, 'names',
+    relation_lazy='joined',
+    name = Column(Unicode(24), nullable=False, index=True,
+        info=dict(description="The name", format='plaintext', official=True, ripped=True)),
+)
+create_translation_table('ability_prose', Ability, 'prose',
+    effect = Column(markdown.MarkdownColumn(5120), nullable=True,
+        info=dict(description="A detailed description of this ability's effect", format='markdown')),
+    short_effect = Column(markdown.MarkdownColumn(255), nullable=True,
+        info=dict(description="A short summary of this ability's effect", format='markdown')),
+)
+
+class AbilityChangelog(TableBase):
+    """History of changes to abilities across main game versions."""
+    __tablename__ = 'ability_changelog'
+    __singlename__ = 'ability_changelog'
+    id = Column(Integer, primary_key=True, nullable=False,
+        info=dict(description="This change's unique ID"))
+    ability_id = Column(Integer, ForeignKey('abilities.id'), nullable=False,
+        info=dict(description="The ID of the ability that changed"))
+    changed_in_version_group_id = Column(Integer, ForeignKey('version_groups.id'), nullable=False,
+        info=dict(description="The ID of the version group in which the ability changed"))
+
+create_translation_table('ability_changelog_prose', AbilityChangelog, 'prose',
+    effect = Column(markdown.MarkdownColumn(255), nullable=False,
+        info=dict(description="A description of the old behavior", format='markdown'))
+)
 
 class AbilityFlavorText(TableBase):
     u"""In-game flavor text of an ability
     """
     __tablename__ = 'ability_flavor_text'
     ability_id = Column(Integer, ForeignKey('abilities.id'), primary_key=True, nullable=False, autoincrement=False,
 
 class AbilityFlavorText(TableBase):
     u"""In-game flavor text of an ability
     """
     __tablename__ = 'ability_flavor_text'
     ability_id = Column(Integer, ForeignKey('abilities.id'), primary_key=True, nullable=False, autoincrement=False,
-        info=dict(description="A numeric ID"))
+        info=dict(description="The ID of the ability"))
     version_group_id = Column(Integer, ForeignKey('version_groups.id'), primary_key=True, nullable=False, autoincrement=False,
     version_group_id = Column(Integer, ForeignKey('version_groups.id'), primary_key=True, nullable=False, autoincrement=False,
-        info=dict(description="The versions this flavor text is shown in"))
+        info=dict(description="The ID of the version group this flavor text is taken from"))
+    language_id = Column(Integer, ForeignKey('languages.id'), primary_key=True, nullable=False,
+        info=dict(description="The language"))
     flavor_text = Column(Unicode(64), nullable=False,
         info=dict(description="The actual flavor text", official=True, format='gametext'))
 
     flavor_text = Column(Unicode(64), nullable=False,
         info=dict(description="The actual flavor text", official=True, format='gametext'))
 
-class AbilityName(TableBase):
-    u"""Non-English official name of an ability
-    """
-    __tablename__ = 'ability_names'
-    ability_id = Column(Integer, ForeignKey('abilities.id'), primary_key=True, nullable=False, autoincrement=False,
-        info=dict(description="ID of the ability"))
-    language_id = Column(Integer, ForeignKey('languages.id'), primary_key=True, nullable=False, autoincrement=False,
-        info=dict(description="ID of the language"))
-    name = Column(Unicode(16), nullable=False,
-        info=dict(description="ID of the language", official=True, foreign=True, format='plaintext'))
-
 class Berry(TableBase):
     u"""A Berry, consumable item that grows on trees
 
 class Berry(TableBase):
     u"""A Berry, consumable item that grows on trees
 
-    For data common to all Items, such as the name, see the corresponding Item entry.
+    For data common to all items, such as the name, see the corresponding item entry.
     """
     __tablename__ = 'berries'
     id = Column(Integer, primary_key=True, nullable=False,
     """
     __tablename__ = 'berries'
     id = Column(Integer, primary_key=True, nullable=False,
-        info=dict(description="A numeric ID"))
+        info=dict(description="This Berry's in-game number"))
     item_id = Column(Integer, ForeignKey('items.id'), nullable=False,
     item_id = Column(Integer, ForeignKey('items.id'), nullable=False,
-        info=dict(description="ID of the Item this Berry corresponds to"))
+        info=dict(description="The ID of the item that represents this Berry"))
     firmness_id = Column(Integer, ForeignKey('berry_firmness.id'), nullable=False,
     firmness_id = Column(Integer, ForeignKey('berry_firmness.id'), nullable=False,
-        info=dict(description="ID of this berry's firmness"))
+        info=dict(description="The ID of this Berry's firmness category"))
     natural_gift_power = Column(Integer, nullable=True,
     natural_gift_power = Column(Integer, nullable=True,
-        info=dict(description="Power of Natural Gift when that move is used with this Berry"))
+        info=dict(description="Natural Gift's power when used with this Berry"))
     natural_gift_type_id = Column(Integer, ForeignKey('types.id'), nullable=True,
     natural_gift_type_id = Column(Integer, ForeignKey('types.id'), nullable=True,
-        info=dict(description="ID of the Type that Natural Gift will have when used with this Berry"))
+        info=dict(description="The ID of the Type that Natural Gift has when used with this Berry"))
     size = Column(Integer, nullable=False,
     size = Column(Integer, nullable=False,
-        info=dict(description=u"Size of this Berry, in millimeters"))
+        info=dict(description=u"The size of this Berry, in millimeters"))
     max_harvest = Column(Integer, nullable=False,
     max_harvest = Column(Integer, nullable=False,
-        info=dict(description="Maximum number of these berries that can grow on one tree"))
+        info=dict(description="The maximum number of these berries that can grow on one tree in Generation IV"))
     growth_time = Column(Integer, nullable=False,
     growth_time = Column(Integer, nullable=False,
-        info=dict(description="Time it takes the tree to grow one stage, in hours. Multiply by four to get overall time."))
+        info=dict(description="Time it takes the tree to grow one stage, in hours.  Berry trees go through four of these growth stages before they can be picked."))
     soil_dryness = Column(Integer, nullable=False,
     soil_dryness = Column(Integer, nullable=False,
-        info=dict(description="The speed of soil drying the tree causes"))  # XXX: What's this exactly? I'm not a good farmer
+        info=dict(description="The speed at which this Berry dries out the soil as it grows.  A higher rate means the soil dries more quickly."))
     smoothness = Column(Integer, nullable=False,
     smoothness = Column(Integer, nullable=False,
-        info=dict(description="Smoothness of this Berry, a culinary attribute. Higher is better."))
+        info=dict(description="The smoothness of this Berry, used in making Pokéblocks or Poffins"))
 
 class BerryFirmness(TableBase):
     u"""A Berry firmness, such as "hard" or "very soft".
     """
     __tablename__ = 'berry_firmness'
 
 class BerryFirmness(TableBase):
     u"""A Berry firmness, such as "hard" or "very soft".
     """
     __tablename__ = 'berry_firmness'
+    __singlename__ = 'berry_firmness'
     id = Column(Integer, primary_key=True, nullable=False,
     id = Column(Integer, primary_key=True, nullable=False,
-        info=dict(description="A numeric ID"))
-    name = Column(Unicode(10), nullable=False,
-        info=dict(description="English name of the firmness level", official=True, format='plaintext'))
+        info=dict(description="A unique ID for this firmness"))
+    identifier = Column(Unicode(10), nullable=False,
+        info=dict(description="An identifier", format='identifier'))
+
+create_translation_table('berry_firmness_names', BerryFirmness, 'names',
+    relation_lazy='joined',
+    name = Column(Unicode(10), nullable=False, index=True,
+        info=dict(description="The name", format='plaintext', official=True)),
+)
 
 class BerryFlavor(TableBase):
     u"""A Berry flavor level.
     """
     __tablename__ = 'berry_flavors'
     berry_id = Column(Integer, ForeignKey('berries.id'), primary_key=True, nullable=False, autoincrement=False,
 
 class BerryFlavor(TableBase):
     u"""A Berry flavor level.
     """
     __tablename__ = 'berry_flavors'
     berry_id = Column(Integer, ForeignKey('berries.id'), primary_key=True, nullable=False, autoincrement=False,
-        info=dict(description="ID of the berry"))
+        info=dict(description="The ID of the berry"))
     contest_type_id = Column(Integer, ForeignKey('contest_types.id'), primary_key=True, nullable=False, autoincrement=False,
     contest_type_id = Column(Integer, ForeignKey('contest_types.id'), primary_key=True, nullable=False, autoincrement=False,
-        info=dict(description="ID of the flavor"))
+        info=dict(description="The ID of the flavor"))
     flavor = Column(Integer, nullable=False,
     flavor = Column(Integer, nullable=False,
-        info=dict(description="Level of the flavor in the berry"))
+        info=dict(description="The level of the flavor in the berry"))
 
 class ContestCombo(TableBase):
     u"""Combo of two moves in a Contest.
     """
     __tablename__ = 'contest_combos'
     first_move_id = Column(Integer, ForeignKey('moves.id'), primary_key=True, nullable=False, autoincrement=False,
 
 class ContestCombo(TableBase):
     u"""Combo of two moves in a Contest.
     """
     __tablename__ = 'contest_combos'
     first_move_id = Column(Integer, ForeignKey('moves.id'), primary_key=True, nullable=False, autoincrement=False,
-        info=dict(description="ID of the first move in the combo"))
+        info=dict(description="The ID of the first move in the combo"))
     second_move_id = Column(Integer, ForeignKey('moves.id'), primary_key=True, nullable=False, autoincrement=False,
     second_move_id = Column(Integer, ForeignKey('moves.id'), primary_key=True, nullable=False, autoincrement=False,
-        info=dict(description="ID of the second and final move in the combo"))
+        info=dict(description="The ID of the second and final move in the combo"))
 
 class ContestEffect(TableBase):
     u"""Effect of a move when used in a Contest.
     """
     __tablename__ = 'contest_effects'
 
 class ContestEffect(TableBase):
     u"""Effect of a move when used in a Contest.
     """
     __tablename__ = 'contest_effects'
+    __singlename__ = 'contest_effect'
     id = Column(Integer, primary_key=True, nullable=False,
     id = Column(Integer, primary_key=True, nullable=False,
-        info=dict(description="A numeric ID"))
+        info=dict(description="A unique ID for this effect"))
     appeal = Column(SmallInteger, nullable=False,
         info=dict(description="The base number of hearts the user of this move gets"))
     jam = Column(SmallInteger, nullable=False,
         info=dict(description="The base number of hearts the user's opponent loses"))
     appeal = Column(SmallInteger, nullable=False,
         info=dict(description="The base number of hearts the user of this move gets"))
     jam = Column(SmallInteger, nullable=False,
         info=dict(description="The base number of hearts the user's opponent loses"))
-    flavor_text = Column(Unicode(64), nullable=False,
-        info=dict(description="English in-game description of this effect", official=True, format='gametext'))
-    effect = Column(Unicode(255), nullable=False,
-        info=dict(description="Detailed description of the effect", format='markdown'))
+
+create_translation_table('contest_effect_prose', ContestEffect, 'prose',
+    flavor_text = Column(Unicode(64), nullable=True,
+        info=dict(description="The in-game description of this effect", official=True, format='gametext')),
+    effect = Column(Unicode(255), nullable=True,
+        info=dict(description="A detailed description of the effect", format='plaintext')),
+)
 
 class ContestType(TableBase):
 
 class ContestType(TableBase):
-    u"""A Contest type, such as "cool" or "smart". Also functions as Berry flavor and Pokéblock color.
+    u"""A Contest type, such as "cool" or "smart", and their associated Berry flavors and Pokéblock colors.
     """
     __tablename__ = 'contest_types'
     """
     __tablename__ = 'contest_types'
+    __singlename__ = 'contest_type'
     id = Column(Integer, primary_key=True, nullable=False,
     id = Column(Integer, primary_key=True, nullable=False,
-        info=dict(description="A numeric ID"))
-    name = Column(Unicode(6), nullable=False,
-        info=dict(description="The English name of the Contest type", official=True, format='identifier'))
-    flavor = Column(Unicode(6), nullable=False,
-        info=dict(description="The English name of the corresponding Berry flavor", official=True, format='identifier'))
-    color = Column(Unicode(6), nullable=False,
-        info=dict(description=u"The English name of the corresponding Pokéblock color", official=True, format='identifier'))
+        info=dict(description="A unique ID for this Contest type"))
+    identifier = Column(Unicode(6), nullable=False,
+        info=dict(description="An identifier", format='identifier'))
+
+create_translation_table('contest_type_names', ContestType, 'names',
+    relation_lazy='joined',
+    name = Column(Unicode(6), nullable=True, index=True,
+        info=dict(description="The name", format='plaintext', official=True)),
+    flavor = Column(Unicode(6), nullable=True,
+        info=dict(description="The name of the corresponding Berry flavor", official=True, format='plaintext')),
+    color = Column(Unicode(6), nullable=True,
+        info=dict(description=u"The name of the corresponding Pokéblock color", official=True, format='plaintext')),
+)
 
 class EggGroup(TableBase):
     u"""An Egg group. Usually, two Pokémon can breed if they share an Egg Group.
 
 class EggGroup(TableBase):
     u"""An Egg group. Usually, two Pokémon can breed if they share an Egg Group.
@@ -165,10 +267,17 @@ class EggGroup(TableBase):
     (exceptions are the Ditto and No Eggs groups)
     """
     __tablename__ = 'egg_groups'
     (exceptions are the Ditto and No Eggs groups)
     """
     __tablename__ = 'egg_groups'
+    __singlename__ = 'egg_group'
     id = Column(Integer, primary_key=True, nullable=False,
     id = Column(Integer, primary_key=True, nullable=False,
-        info=dict(description="A numeric ID"))
-    name = Column(Unicode(16), nullable=False,
-        info=dict(description=u'The English "official" name. One NPC in Stadium uses these names; they are pretty bad.', official=True, format='identifier'))
+        info=dict(description="A unique ID for this group"))
+    identifier = Column(Unicode(16), nullable=False,
+        info=dict(description=u"An identifier.", format='identifier'))
+
+create_translation_table('egg_group_prose', EggGroup, 'names',
+    relation_lazy='joined',
+    name = Column(Unicode(16), nullable=False, index=True,
+        info=dict(description="The name", format='plaintext', official=True)),
+)
 
 class Encounter(TableBase):
     u"""Encounters with wild Pokémon.
 
 class Encounter(TableBase):
     u"""Encounters with wild Pokémon.
@@ -179,10 +288,10 @@ class Encounter(TableBase):
     "slot" they are in and the state of the game world.
 
     What the player is doing to get an encounter, such as surfing or walking
     "slot" they are in and the state of the game world.
 
     What the player is doing to get an encounter, such as surfing or walking
-    through tall grass, is called terrain.  Each terrain has its own set of
+    through tall grass, is called a method.  Each method has its own set of
     encounter slots.
 
     encounter slots.
 
-    Within a terrain, slots are defined primarily by rarity.  Each slot can
+    Within a method, slots are defined primarily by rarity.  Each slot can
     also be affected by world conditions; for example, the 20% slot for walking
     in tall grass is affected by whether a swarm is in effect in that area.
     "Is there a swarm?" is a condition; "there is a swarm" and "there is not a
     also be affected by world conditions; for example, the 20% slot for walking
     in tall grass is affected by whether a swarm is in effect in that area.
     "Is there a swarm?" is a condition; "there is a swarm" and "there is not a
@@ -196,13 +305,13 @@ class Encounter(TableBase):
 
     __tablename__ = 'encounters'
     id = Column(Integer, primary_key=True, nullable=False,
 
     __tablename__ = 'encounters'
     id = Column(Integer, primary_key=True, nullable=False,
-        info=dict(description="A numeric ID"))
+        info=dict(description="A unique ID for this encounter"))
     version_id = Column(Integer, ForeignKey('versions.id'), nullable=False, autoincrement=False,
     version_id = Column(Integer, ForeignKey('versions.id'), nullable=False, autoincrement=False,
-        info=dict(description="The ID of the Version this applies to"))
+        info=dict(description="The ID of the version this applies to"))
     location_area_id = Column(Integer, ForeignKey('location_areas.id'), nullable=False, autoincrement=False,
     location_area_id = Column(Integer, ForeignKey('location_areas.id'), nullable=False, autoincrement=False,
-        info=dict(description="The ID of the Location of this encounter"))
+        info=dict(description="The ID of the location of this encounter"))
     encounter_slot_id = Column(Integer, ForeignKey('encounter_slots.id'), nullable=False, autoincrement=False,
     encounter_slot_id = Column(Integer, ForeignKey('encounter_slots.id'), nullable=False, autoincrement=False,
-        info=dict(description="The ID of the encounter slot, which determines terrain and rarity"))
+        info=dict(description="The ID of the encounter slot, which determines method and rarity"))
     pokemon_id = Column(Integer, ForeignKey('pokemon.id'), nullable=False, autoincrement=False,
         info=dict(description=u"The ID of the encountered Pokémon"))
     min_level = Column(Integer, nullable=False, autoincrement=False,
     pokemon_id = Column(Integer, ForeignKey('pokemon.id'), nullable=False, autoincrement=False,
         info=dict(description=u"The ID of the encountered Pokémon"))
     min_level = Column(Integer, nullable=False, autoincrement=False,
@@ -211,50 +320,68 @@ class Encounter(TableBase):
         info=dict(description=u"The maxmum level of the encountered Pokémon"))
 
 class EncounterCondition(TableBase):
         info=dict(description=u"The maxmum level of the encountered Pokémon"))
 
 class EncounterCondition(TableBase):
-    u"""A conditions in the game world that affects pokémon encounters, such as time of day.
+    u"""A conditions in the game world that affects Pokémon encounters, such as time of day.
     """
 
     __tablename__ = 'encounter_conditions'
     """
 
     __tablename__ = 'encounter_conditions'
+    __singlename__ = 'encounter_condition'
     id = Column(Integer, primary_key=True, nullable=False,
     id = Column(Integer, primary_key=True, nullable=False,
-        info=dict(description="A numeric ID"))
-    name = Column(Unicode(64), nullable=False,
-        info=dict(description="An English name of the condition", format='plaintext'))
+        info=dict(description="A unique ID for this condition"))
+    identifier = Column(Unicode(64), nullable=False,
+        info=dict(description="An identifier", format='identifier'))
+
+create_translation_table('encounter_condition_prose', EncounterCondition, 'prose',
+    name = Column(Unicode(64), nullable=False, index=True,
+        info=dict(description="The name", format='plaintext', official=False)),
+)
 
 class EncounterConditionValue(TableBase):
     u"""A possible state for a condition; for example, the state of 'swarm' could be 'swarm' or 'no swarm'.
     """
 
     __tablename__ = 'encounter_condition_values'
 
 class EncounterConditionValue(TableBase):
     u"""A possible state for a condition; for example, the state of 'swarm' could be 'swarm' or 'no swarm'.
     """
 
     __tablename__ = 'encounter_condition_values'
+    __singlename__ = 'encounter_condition_value'
     id = Column(Integer, primary_key=True, nullable=False,
         info=dict(description="A numeric ID"))
     encounter_condition_id = Column(Integer, ForeignKey('encounter_conditions.id'), primary_key=False, nullable=False, autoincrement=False,
         info=dict(description="The ID of the encounter condition this is a value of"))
     id = Column(Integer, primary_key=True, nullable=False,
         info=dict(description="A numeric ID"))
     encounter_condition_id = Column(Integer, ForeignKey('encounter_conditions.id'), primary_key=False, nullable=False, autoincrement=False,
         info=dict(description="The ID of the encounter condition this is a value of"))
-    name = Column(Unicode(64), nullable=False,
-        info=dict(description="An english name of this value", format='plaintext'))
+    identifier = Column(Unicode(64), nullable=False,
+        info=dict(description="An identifier", format='identifier'))
     is_default = Column(Boolean, nullable=False,
     is_default = Column(Boolean, nullable=False,
-        info=dict(description='Set if this value is "default" or "normal" in some sense'))
+        info=dict(description='Set if this value is the default state for the condition'))
+
+create_translation_table('encounter_condition_value_prose', EncounterConditionValue, 'prose',
+    name = Column(Unicode(64), nullable=False, index=True,
+        info=dict(description="The name", format='plaintext', official=False)),
+)
 
 class EncounterConditionValueMap(TableBase):
     u"""Maps encounters to the specific conditions under which they occur.
     """
     __tablename__ = 'encounter_condition_value_map'
     encounter_id = Column(Integer, ForeignKey('encounters.id'), primary_key=True, nullable=False, autoincrement=False,
 
 class EncounterConditionValueMap(TableBase):
     u"""Maps encounters to the specific conditions under which they occur.
     """
     __tablename__ = 'encounter_condition_value_map'
     encounter_id = Column(Integer, ForeignKey('encounters.id'), primary_key=True, nullable=False, autoincrement=False,
-        info=dict(description="ID of the encounter"))
+        info=dict(description="The ID of the encounter"))
     encounter_condition_value_id = Column(Integer, ForeignKey('encounter_condition_values.id'), primary_key=True, nullable=False, autoincrement=False,
     encounter_condition_value_id = Column(Integer, ForeignKey('encounter_condition_values.id'), primary_key=True, nullable=False, autoincrement=False,
-        info=dict(description="ID of the encounter condition value"))
+        info=dict(description="The ID of the encounter condition value"))
 
 
-class EncounterTerrain(TableBase):
+class EncounterMethod(TableBase):
     u"""A way the player can enter a wild encounter, e.g., surfing, fishing, or walking through tall grass.
     """
 
     u"""A way the player can enter a wild encounter, e.g., surfing, fishing, or walking through tall grass.
     """
 
-    __tablename__ = 'encounter_terrain'
+    __tablename__ = 'encounter_methods'
+    __singlename__ = 'encounter_method'
     id = Column(Integer, primary_key=True, nullable=False,
     id = Column(Integer, primary_key=True, nullable=False,
-        info=dict(description="A numeric ID"))
-    name = Column(Unicode(64), nullable=False,
-        info=dict(description="An english name of this terrain", format='plaintext'))
+        info=dict(description="A unique ID for the method"))
+    identifier = Column(Unicode(16), nullable=False, unique=True,
+        info=dict(description="An identifier", format='identifier'))
+
+create_translation_table('encounter_method_prose', EncounterMethod, 'prose',
+    name = Column(Unicode(64), nullable=False, index=True,
+        info=dict(description="The name", format='plaintext', official=False)),
+)
 
 class EncounterSlot(TableBase):
 
 class EncounterSlot(TableBase):
-    u"""An abstract "slot" within a terrain, associated with both some set of conditions and a rarity.
+    u"""An abstract "slot" within a method, associated with both some set of conditions and a rarity.
 
     Note that there are two encounters per slot, so the rarities will only add
     up to 50.
 
     Note that there are two encounters per slot, so the rarities will only add
     up to 50.
@@ -262,27 +389,18 @@ class EncounterSlot(TableBase):
 
     __tablename__ = 'encounter_slots'
     id = Column(Integer, primary_key=True, nullable=False,
 
     __tablename__ = 'encounter_slots'
     id = Column(Integer, primary_key=True, nullable=False,
-        info=dict(description="A numeric ID"))
+        info=dict(description="A unique ID for this slot"))
     version_group_id = Column(Integer, ForeignKey('version_groups.id'), nullable=False, autoincrement=False,
     version_group_id = Column(Integer, ForeignKey('version_groups.id'), nullable=False, autoincrement=False,
-        info=dict(description="The ID of the Version group this slot is in"))
-    encounter_terrain_id = Column(Integer, ForeignKey('encounter_terrain.id'), primary_key=False, nullable=False, autoincrement=False,
-        info=dict(description="The ID of the terrain"))
+        info=dict(description="The ID of the version group this slot is in"))
+    encounter_method_id = Column(Integer, ForeignKey('encounter_methods.id'), primary_key=False, nullable=False, autoincrement=False,
+        info=dict(description="The ID of the method"))
     slot = Column(Integer, nullable=True,
     slot = Column(Integer, nullable=True,
-        info=dict(description="The slot")) # XXX: What is this, exactly?
-    rarity = Column(Integer, nullable=False,
-        info=dict(description="The chance of the encounter, in percent"))  # XXX: It is in percent, right? I'm confused.
-
-class EncounterSlotCondition(TableBase):
-    u"""A condition that affects an encounter slot.
-    """
-    __tablename__ = 'encounter_slot_conditions'
-    encounter_slot_id = Column(Integer, ForeignKey('encounter_slots.id'), primary_key=True, nullable=False, autoincrement=False,
-        info=dict(description="The ID of the encounter slot"))
-    encounter_condition_id = Column(Integer, ForeignKey('encounter_conditions.id'), primary_key=True, nullable=False, autoincrement=False,
-        info=dict(description="The ID of the encounter condition"))
+        info=dict(description="This slot's order for the location and method"))
+    rarity = Column(Integer, nullable=True,
+        info=dict(description="The chance of the encounter as a percentage"))
 
 class EvolutionChain(TableBase):
 
 class EvolutionChain(TableBase):
-    u"""A family of pokémon that are linked by evolution
+    u"""A family of Pokémon that are linked by evolution
     """
     __tablename__ = 'evolution_chains'
     id = Column(Integer, primary_key=True, nullable=False,
     """
     __tablename__ = 'evolution_chains'
     id = Column(Integer, primary_key=True, nullable=False,
@@ -296,10 +414,16 @@ class EvolutionTrigger(TableBase):
     u"""An evolution type, such as "level" or "trade".
     """
     __tablename__ = 'evolution_triggers'
     u"""An evolution type, such as "level" or "trade".
     """
     __tablename__ = 'evolution_triggers'
+    __singlename__ = 'evolution_trigger'
     id = Column(Integer, primary_key=True, nullable=False,
         info=dict(description="A numeric ID"))
     identifier = Column(Unicode(16), nullable=False,
     id = Column(Integer, primary_key=True, nullable=False,
         info=dict(description="A numeric ID"))
     identifier = Column(Unicode(16), nullable=False,
-        info=dict(description="An English identifier", format='identifier'))
+        info=dict(description="An identifier", format='identifier'))
+
+create_translation_table('evolution_trigger_prose', EvolutionTrigger, 'prose',
+    name = Column(Unicode(16), nullable=False, index=True,
+        info=dict(description="The name", format='plaintext', official=False)),
+)
 
 class Experience(TableBase):
     u"""EXP needed for a certain level with a certain growth rate
 
 class Experience(TableBase):
     u"""EXP needed for a certain level with a certain growth rate
@@ -313,29 +437,42 @@ class Experience(TableBase):
         info=dict(description="The number of EXP points needed to get to that level"))
 
 class Generation(TableBase):
         info=dict(description="The number of EXP points needed to get to that level"))
 
 class Generation(TableBase):
-    u"""A Generation of the pokémon franchise
+    u"""A Generation of the Pokémon franchise
     """
     __tablename__ = 'generations'
     """
     __tablename__ = 'generations'
+    __singlename__ = 'generation'
     id = Column(Integer, primary_key=True, nullable=False,
         info=dict(description="A numeric ID"))
     id = Column(Integer, primary_key=True, nullable=False,
         info=dict(description="A numeric ID"))
-    main_region_id = Column(Integer, ForeignKey('regions.id'),
+    main_region_id = Column(Integer, ForeignKey('regions.id'), nullable=False,
         info=dict(description="ID of the region this generation's main games take place in"))
         info=dict(description="ID of the region this generation's main games take place in"))
-    canonical_pokedex_id = Column(Integer, ForeignKey('pokedexes.id'),
-        info=dict(description=u"ID of the pokédex this generation's main games use by default"))
-    name = Column(Unicode(16), nullable=False,
-        info=dict(description=u'An English name of this generation, such as "Generation IV"', format='plaintext'))
+    canonical_pokedex_id = Column(Integer, ForeignKey('pokedexes.id'), nullable=False,
+        info=dict(description=u"ID of the Pokédex this generation's main games use by default"))
+    identifier = Column(Unicode(16), nullable=False,
+        info=dict(description=u'An identifier', format='identifier'))
+
+create_translation_table('generation_names', Generation, 'names',
+    relation_lazy='joined',
+    name = Column(Unicode(16), nullable=False, index=True,
+        info=dict(description="The name", format='plaintext', official=True)),
+)
 
 class GrowthRate(TableBase):
 
 class GrowthRate(TableBase):
-    u"""Growth rate of a pokémon, i.e. the EXP → level function.
+    u"""Growth rate of a Pokémon, i.e. the EXP → level function.
     """
     __tablename__ = 'growth_rates'
     """
     __tablename__ = 'growth_rates'
+    __singlename__ = 'growth_rate'
     id = Column(Integer, primary_key=True, nullable=False,
         info=dict(description="A numeric ID"))
     id = Column(Integer, primary_key=True, nullable=False,
         info=dict(description="A numeric ID"))
-    name = Column(Unicode(20), nullable=False,
-        info=dict(description="A name for the", format='identifier'))
+    identifier = Column(Unicode(20), nullable=False,
+        info=dict(description="An identifier", format='identifier'))
     formula = Column(Unicode(500), nullable=False,
         info=dict(description="The formula", format='latex'))
 
     formula = Column(Unicode(500), nullable=False,
         info=dict(description="The formula", format='latex'))
 
+create_translation_table('growth_rate_prose', GrowthRate, 'prose',
+    name = Column(Unicode(20), nullable=False, index=True,
+        info=dict(description="The name", format='plaintext', official=False)),
+)
+
 class Item(TableBase):
     u"""An Item from the games, like "Poké Ball" or "Bicycle".
     """
 class Item(TableBase):
     u"""An Item from the games, like "Poké Ball" or "Bicycle".
     """
@@ -343,8 +480,8 @@ class Item(TableBase):
     __singlename__ = 'item'
     id = Column(Integer, primary_key=True, nullable=False,
         info=dict(description="A numeric ID"))
     __singlename__ = 'item'
     id = Column(Integer, primary_key=True, nullable=False,
         info=dict(description="A numeric ID"))
-    name = Column(Unicode(20), nullable=False,
-        info=dict(description="The English name of the item", official=True, format='plaintext'))
+    identifier = Column(Unicode(20), nullable=False,
+        info=dict(description="An identifier", format='identifier'))
     category_id = Column(Integer, ForeignKey('item_categories.id'), nullable=False,
         info=dict(description="ID of a category this item belongs to"))
     cost = Column(Integer, nullable=False,
     category_id = Column(Integer, ForeignKey('item_categories.id'), nullable=False,
         info=dict(description="ID of a category this item belongs to"))
     cost = Column(Integer, nullable=False,
@@ -353,8 +490,6 @@ class Item(TableBase):
         info=dict(description=u"Power of the move Fling when used with this item."))
     fling_effect_id = Column(Integer, ForeignKey('item_fling_effects.id'), nullable=True,
         info=dict(description=u"ID of the fling-effect of the move Fling when used with this item. Note that these are different from move effects."))
         info=dict(description=u"Power of the move Fling when used with this item."))
     fling_effect_id = Column(Integer, ForeignKey('item_fling_effects.id'), nullable=True,
         info=dict(description=u"ID of the fling-effect of the move Fling when used with this item. Note that these are different from move effects."))
-    effect = Column(markdown.MarkdownColumn(5120), nullable=False,
-        info=dict(description=u"Detailed English description of the item's effect.", format='markdown'))
 
     @property
     def appears_underground(self):
 
     @property
     def appears_underground(self):
@@ -362,28 +497,57 @@ class Item(TableBase):
         """
         return any(flag.identifier == u'underground' for flag in self.flags)
 
         """
         return any(flag.identifier == u'underground' for flag in self.flags)
 
+create_translation_table('item_names', Item, 'names',
+    relation_lazy='joined',
+    name = Column(Unicode(20), nullable=False, index=True,
+        info=dict(description="The name", format='plaintext', official=True, ripped=True)),
+)
+create_translation_table('item_prose', Item, 'prose',
+    short_effect = Column(markdown.MarkdownColumn(256), nullable=True,
+        info=dict(description="A short summary of the effect", format='markdown')),
+    effect = Column(markdown.MarkdownColumn(5120), nullable=True,
+        info=dict(description=u"Detailed description of the item's effect.", format='markdown')),
+)
+create_translation_table('item_flavor_summaries', Item, 'flavor_summaries',
+    flavor_summary = Column(Unicode(512), nullable=True,
+        info=dict(description=u"Text containing facts from all flavor texts, for languages without official game translations", official=False, format='plaintext', ripped=True)),
+)
+
 class ItemCategory(TableBase):
     u"""An item category
     """
     # XXX: This is fanon, right?
     __tablename__ = 'item_categories'
 class ItemCategory(TableBase):
     u"""An item category
     """
     # XXX: This is fanon, right?
     __tablename__ = 'item_categories'
+    __singlename__ = 'item_category'
     id = Column(Integer, primary_key=True, nullable=False,
         info=dict(description="A numeric ID"))
     pocket_id = Column(Integer, ForeignKey('item_pockets.id'), nullable=False,
         info=dict(description="ID of the pocket these items go to"))
     id = Column(Integer, primary_key=True, nullable=False,
         info=dict(description="A numeric ID"))
     pocket_id = Column(Integer, ForeignKey('item_pockets.id'), nullable=False,
         info=dict(description="ID of the pocket these items go to"))
-    name = Column(Unicode(16), nullable=False,
-        info=dict(description="English name of the category", format='plaintext'))
+    identifier = Column(Unicode(16), nullable=False,
+        info=dict(description="An identifier", format='identifier'))
+
+create_translation_table('item_category_prose', ItemCategory, 'prose',
+    relation_lazy='joined',
+    name = Column(Unicode(16), nullable=False, index=True,
+        info=dict(description="The name", format='plaintext', official=False)),
+)
 
 class ItemFlag(TableBase):
     u"""An item attribute such as "consumable" or "holdable".
     """
     __tablename__ = 'item_flags'
 
 class ItemFlag(TableBase):
     u"""An item attribute such as "consumable" or "holdable".
     """
     __tablename__ = 'item_flags'
+    __singlename__ = 'item_flag'
     id = Column(Integer, primary_key=True, nullable=False,
         info=dict(description="A numeric ID"))
     identifier = Column(Unicode(24), nullable=False,
         info=dict(description="Identifier of the flag", format='identifier'))
     id = Column(Integer, primary_key=True, nullable=False,
         info=dict(description="A numeric ID"))
     identifier = Column(Unicode(24), nullable=False,
         info=dict(description="Identifier of the flag", format='identifier'))
-    name = Column(Unicode(64), nullable=False,
-        info=dict(description="Short English description of the flag", format='plaintext'))
+
+create_translation_table('item_flag_prose', ItemFlag, 'prose',
+    name = Column(Unicode(24), nullable=True, index=True,
+        info=dict(description="The name", format='plaintext', official=False)),
+    description = Column(Unicode(64), nullable=True,
+        info=dict(description="Short description of the flag", format='plaintext')),
+)
 
 class ItemFlagMap(TableBase):
     u"""Maps an item flag to its item.
 
 class ItemFlagMap(TableBase):
     u"""Maps an item flag to its item.
@@ -398,10 +562,14 @@ class ItemFlavorText(TableBase):
     u"""An in-game description of an item
     """
     __tablename__ = 'item_flavor_text'
     u"""An in-game description of an item
     """
     __tablename__ = 'item_flavor_text'
+    __singlename__ = 'item_flavor_text'
+    summary_column = Item.flavor_summaries_table, 'flavor_summary'
     item_id = Column(Integer, ForeignKey('items.id'), primary_key=True, autoincrement=False, nullable=False,
         info=dict(description="The ID of the item"))
     version_group_id = Column(Integer, ForeignKey('version_groups.id'), primary_key=True, autoincrement=False, nullable=False,
         info=dict(description="ID of the version group that sports this text"))
     item_id = Column(Integer, ForeignKey('items.id'), primary_key=True, autoincrement=False, nullable=False,
         info=dict(description="The ID of the item"))
     version_group_id = Column(Integer, ForeignKey('version_groups.id'), primary_key=True, autoincrement=False, nullable=False,
         info=dict(description="ID of the version group that sports this text"))
+    language_id = Column(Integer, ForeignKey('languages.id'), primary_key=True, nullable=False,
+        info=dict(description="The language"))
     flavor_text = Column(Unicode(255), nullable=False,
         info=dict(description="The flavor text itself", official=True, format='gametext'))
 
     flavor_text = Column(Unicode(255), nullable=False,
         info=dict(description="The flavor text itself", official=True, format='gametext'))
 
@@ -409,56 +577,41 @@ class ItemFlingEffect(TableBase):
     u"""An effect of the move Fling when used with a specific item
     """
     __tablename__ = 'item_fling_effects'
     u"""An effect of the move Fling when used with a specific item
     """
     __tablename__ = 'item_fling_effects'
+    __singlename__ = 'item_fling_effect'
     id = Column(Integer, primary_key=True, nullable=False,
         info=dict(description="A numeric ID"))
     id = Column(Integer, primary_key=True, nullable=False,
         info=dict(description="A numeric ID"))
+
+create_translation_table('item_fling_effect_prose', ItemFlingEffect, 'prose',
     effect = Column(Unicode(255), nullable=False,
     effect = Column(Unicode(255), nullable=False,
-        info=dict(description="English description of the effect", format='plaintext'))
+        info=dict(description="Description of the effect", format='plaintext')),
+)
 
 
-class ItemInternalID(TableBase):
+class ItemGameIndex(TableBase):
     u"""The internal ID number a game uses for an item
     """
     u"""The internal ID number a game uses for an item
     """
-    __tablename__ = 'item_internal_ids'
+    __tablename__ = 'item_game_indices'
     item_id = Column(Integer, ForeignKey('items.id'), primary_key=True, autoincrement=False, nullable=False,
         info=dict(description="The database ID of the item"))
     generation_id = Column(Integer, ForeignKey('generations.id'), primary_key=True, autoincrement=False, nullable=False,
         info=dict(description="ID of the generation of games"))
     item_id = Column(Integer, ForeignKey('items.id'), primary_key=True, autoincrement=False, nullable=False,
         info=dict(description="The database ID of the item"))
     generation_id = Column(Integer, ForeignKey('generations.id'), primary_key=True, autoincrement=False, nullable=False,
         info=dict(description="ID of the generation of games"))
-    internal_id = Column(Integer, nullable=False,
+    game_index = Column(Integer, nullable=False,
         info=dict(description="Internal ID of the item in the generation"))
 
         info=dict(description="Internal ID of the item in the generation"))
 
-class ItemName(TableBase):
-    u"""A non-English name of an item
-    """
-    __tablename__ = 'item_names'
-    item_id = Column(Integer, ForeignKey('items.id'), primary_key=True, nullable=False, autoincrement=False,
-        info=dict(description="The ID of the item"))
-    language_id = Column(Integer, ForeignKey('languages.id'), primary_key=True, nullable=False, autoincrement=False,
-        info=dict(description="The ID of the language"))
-    name = Column(Unicode(16), nullable=False,
-        info=dict(description="The name of the item in this language", foreign=True, format='plaintext'))
-
 class ItemPocket(TableBase):
     u"""A pocket that categorizes items
     """
     __tablename__ = 'item_pockets'
 class ItemPocket(TableBase):
     u"""A pocket that categorizes items
     """
     __tablename__ = 'item_pockets'
+    __singlename__ = 'item_pocket'
     id = Column(Integer, primary_key=True, nullable=False,
         info=dict(description="A numeric ID"))
     identifier = Column(Unicode(16), nullable=False,
         info=dict(description="An identifier of this pocket", format='identifier'))
     id = Column(Integer, primary_key=True, nullable=False,
         info=dict(description="A numeric ID"))
     identifier = Column(Unicode(16), nullable=False,
         info=dict(description="An identifier of this pocket", format='identifier'))
-    name = Column(Unicode(16), nullable=False,
-        info=dict(description="A numeric ID", format='plaintext'))
 
 
-class Language(TableBase):
-    u"""A language the Pokémon games have been transleted into; except English
-    """
-    __tablename__ = 'languages'
-    id = Column(Integer, primary_key=True, nullable=False,
-        info=dict(description="A numeric ID"))
-    iso639 = Column(Unicode(2), nullable=False,
-        info=dict(description="The two-letter code of the country where this language is spoken. Note that it is not unique."))
-    iso3166 = Column(Unicode(2), nullable=False,
-        info=dict(description="The two-letter code of the language. Note that it is not unique."))
-    name = Column(Unicode(16), nullable=False,
-        info=dict(description="The English name of the language", format='plaintext'))
+create_translation_table('item_pocket_names', ItemPocket, 'names',
+    relation_lazy='joined',
+    name = Column(Unicode(16), nullable=False, index=True,
+        info=dict(description="The name", format='plaintext', official=True)),
+)
 
 class Location(TableBase):
     u"""A place in the Pokémon world
 
 class Location(TableBase):
     u"""A place in the Pokémon world
@@ -469,47 +622,60 @@ class Location(TableBase):
         info=dict(description="A numeric ID"))
     region_id = Column(Integer, ForeignKey('regions.id'),
         info=dict(description="ID of the region this location is in"))
         info=dict(description="A numeric ID"))
     region_id = Column(Integer, ForeignKey('regions.id'),
         info=dict(description="ID of the region this location is in"))
-    name = Column(Unicode(64), nullable=False,
-        info=dict(description="English name of the location", official=True, format='plaintext'))
+    identifier = Column(Unicode(64), nullable=False,
+        info=dict(description="An identifier", format='identifier'))
+
+create_translation_table('location_names', Location, 'names',
+    relation_lazy='joined',
+    name = Column(Unicode(64), nullable=False, index=True,
+        info=dict(description="The name", format='plaintext', official=True)),
+)
 
 class LocationArea(TableBase):
     u"""A sub-area of a location
     """
     __tablename__ = 'location_areas'
 
 class LocationArea(TableBase):
     u"""A sub-area of a location
     """
     __tablename__ = 'location_areas'
+    __singlename__ = 'location_area'
     id = Column(Integer, primary_key=True, nullable=False,
         info=dict(description="A numeric ID"))
     location_id = Column(Integer, ForeignKey('locations.id'), nullable=False,
         info=dict(description="ID of the location this area is part of"))
     id = Column(Integer, primary_key=True, nullable=False,
         info=dict(description="A numeric ID"))
     location_id = Column(Integer, ForeignKey('locations.id'), nullable=False,
         info=dict(description="ID of the location this area is part of"))
-    internal_id = Column(Integer, nullable=False,
+    game_index = Column(Integer, nullable=False,
         info=dict(description="ID the games ude for this area"))
         info=dict(description="ID the games ude for this area"))
-    name = Column(Unicode(64), nullable=True,
-        info=dict(description="An English name of the area, if applicable", format='plaintext'))
+    identifier = Column(Unicode(64), nullable=True,
+        info=dict(description="An identifier", format='identifier'))
+
+create_translation_table('location_area_prose', LocationArea, 'prose',
+    relation_lazy='joined',
+    name = Column(Unicode(64), nullable=False, index=True,
+        info=dict(description="The name", format='plaintext', official=False)),
+)
 
 class LocationAreaEncounterRate(TableBase):
     # XXX: What's this exactly? Someone add the docstring & revise the descriptions
     __tablename__ = 'location_area_encounter_rates'
     location_area_id = Column(Integer, ForeignKey('location_areas.id'), primary_key=True, nullable=False, autoincrement=False,
         info=dict(description="ID of the area"))
 
 class LocationAreaEncounterRate(TableBase):
     # XXX: What's this exactly? Someone add the docstring & revise the descriptions
     __tablename__ = 'location_area_encounter_rates'
     location_area_id = Column(Integer, ForeignKey('location_areas.id'), primary_key=True, nullable=False, autoincrement=False,
         info=dict(description="ID of the area"))
-    encounter_terrain_id = Column(Integer, ForeignKey('encounter_terrain.id'), primary_key=True, nullable=False, autoincrement=False,
-        info=dict(description="ID of the terrain"))
+    encounter_method_id = Column(Integer, ForeignKey('encounter_methods.id'), primary_key=True, nullable=False, autoincrement=False,
+        info=dict(description="ID of the method"))
     version_id = Column(Integer, ForeignKey('versions.id'), primary_key=True, autoincrement=False,
         info=dict(description="ID of the version"))
     rate = Column(Integer, nullable=True,
         info=dict(description="The encounter rate"))  # units?
 
     version_id = Column(Integer, ForeignKey('versions.id'), primary_key=True, autoincrement=False,
         info=dict(description="ID of the version"))
     rate = Column(Integer, nullable=True,
         info=dict(description="The encounter rate"))  # units?
 
-class LocationInternalID(TableBase):
+class LocationGameIndex(TableBase):
     u"""IDs the games use internally for locations
     """
     u"""IDs the games use internally for locations
     """
-    __tablename__ = 'location_internal_ids'
+    __tablename__ = 'location_game_indices'
     location_id = Column(Integer, ForeignKey('locations.id'), nullable=False, primary_key=True,
         info=dict(description="Database ID of the locaion"))
     generation_id = Column(Integer, ForeignKey('generations.id'), nullable=False, primary_key=True,
         info=dict(description="ID of the generation this entry to"))
     location_id = Column(Integer, ForeignKey('locations.id'), nullable=False, primary_key=True,
         info=dict(description="Database ID of the locaion"))
     generation_id = Column(Integer, ForeignKey('generations.id'), nullable=False, primary_key=True,
         info=dict(description="ID of the generation this entry to"))
-    internal_id = Column(Integer, nullable=False,
+    game_index = Column(Integer, nullable=False, primary_key=True, autoincrement=False,
         info=dict(description="Internal game ID of the location"))
 
 class Machine(TableBase):
         info=dict(description="Internal game ID of the location"))
 
 class Machine(TableBase):
-    u"""A TM or HM; numbered item that can teach a move to a pokémon
+    u"""A TM or HM; numbered item that can teach a move to a Pokémon
     """
     __tablename__ = 'machines'
     machine_number = Column(Integer, primary_key=True, nullable=False, autoincrement=False,
     """
     __tablename__ = 'machines'
     machine_number = Column(Integer, primary_key=True, nullable=False, autoincrement=False,
@@ -527,57 +693,141 @@ class Machine(TableBase):
         """
         return self.machine_number >= 100
 
         """
         return self.machine_number >= 100
 
+class Move(TableBase):
+    u"""A Move: technique or attack a Pokémon can learn to use
+    """
+    __tablename__ = 'moves'
+    __singlename__ = 'move'
+    id = Column(Integer, primary_key=True, nullable=False,
+        info=dict(description="A numeric ID"))
+    identifier = Column(Unicode(24), nullable=False,
+        info=dict(description="An identifier", format='identifier'))
+    generation_id = Column(Integer, ForeignKey('generations.id'), nullable=False,
+        info=dict(description="ID of the generation this move first appeared in"))
+    type_id = Column(Integer, ForeignKey('types.id'), nullable=False,
+        info=dict(description="ID of the move's elemental type"))
+    power = Column(SmallInteger, nullable=False,
+        info=dict(description="Base power of the move"))
+    pp = Column(SmallInteger, nullable=True,
+        info=dict(description="Base PP (Power Points) of the move, nullable if not applicable (e.g. Struggle and Shadow moves)."))
+    accuracy = Column(SmallInteger, nullable=True,
+        info=dict(description="Accuracy of the move; NULL means it never misses"))
+    priority = Column(SmallInteger, nullable=False,
+        info=dict(description="The move's priority bracket"))
+    target_id = Column(Integer, ForeignKey('move_targets.id'), nullable=False,
+        info=dict(description="ID of the target (range) of the move"))
+    damage_class_id = Column(Integer, ForeignKey('move_damage_classes.id'), nullable=False,
+        info=dict(description="ID of the damage class (physical/special) of the move"))
+    effect_id = Column(Integer, ForeignKey('move_effects.id'), nullable=False,
+        info=dict(description="ID of the move's effect"))
+    effect_chance = Column(Integer, nullable=True,
+        info=dict(description="The chance for a secondary effect. What this is a chance of is specified by the move's effect."))
+    contest_type_id = Column(Integer, ForeignKey('contest_types.id'), nullable=True,
+        info=dict(description="ID of the move's Contest type (e.g. cool or smart)"))
+    contest_effect_id = Column(Integer, ForeignKey('contest_effects.id'), nullable=True,
+        info=dict(description="ID of the move's Contest effect"))
+    super_contest_effect_id = Column(Integer, ForeignKey('super_contest_effects.id'), nullable=True,
+        info=dict(description="ID of the move's Super Contest effect"))
+
+create_translation_table('move_names', Move, 'names',
+    relation_lazy='joined',
+    name = Column(Unicode(24), nullable=False, index=True,
+        info=dict(description="The name", format='plaintext', official=True, ripped=True))
+)
+create_translation_table('move_flavor_summaries', Move, 'flavor_summaries',
+    flavor_summary = Column(Unicode(512), nullable=True,
+        info=dict(description=u"Text containing facts from all flavor texts, for languages without official game translations", official=False, format='plaintext', ripped=True)),
+)
+
 class MoveBattleStyle(TableBase):
     u"""A battle style of a move"""  # XXX: Explain better
     __tablename__ = 'move_battle_styles'
 class MoveBattleStyle(TableBase):
     u"""A battle style of a move"""  # XXX: Explain better
     __tablename__ = 'move_battle_styles'
+    __singlename__ = 'move_battle_style'
     id = Column(Integer, primary_key=True, nullable=False,
         info=dict(description="A numeric ID"))
     id = Column(Integer, primary_key=True, nullable=False,
         info=dict(description="A numeric ID"))
-    name = Column(Unicode(8), nullable=False,
-        info=dict(description="An English name for this battle style", format='plaintext'))
+    identifier = Column(Unicode(8), nullable=False,
+        info=dict(description="An identifier", format='identifier'))
 
 
-class MoveEffectCategory(TableBase):
-    u"""Category of a move effect
-    """
-    __tablename__ = 'move_effect_categories'
-    id = Column(Integer, primary_key=True, nullable=False,
-        info=dict(description="A numeric ID"))
-    name = Column(Unicode(64), nullable=False,
-        info=dict(description="English name of the category", format='plaintext'))
-    can_affect_user = Column(Boolean, nullable=False,
-        info=dict(description="Set if the user can be affected"))
-
-class MoveEffectCategoryMap(TableBase):
-    u"""Maps a move effect category to a move effect
-    """
-    __tablename__ = 'move_effect_category_map'
-    move_effect_id = Column(Integer, ForeignKey('move_effects.id'), primary_key=True, nullable=False,
-        info=dict(description="ID of the move effect"))
-    move_effect_category_id = Column(Integer, ForeignKey('move_effect_categories.id'), primary_key=True, nullable=False,
-        info=dict(description="ID of the category"))
-    affects_user = Column(Boolean, primary_key=True, nullable=False,
-        info=dict(description="Set if the user is affected"))
+create_translation_table('move_battle_style_prose', MoveBattleStyle, 'prose',
+    relation_lazy='joined',
+    name = Column(Unicode(8), nullable=False, index=True,
+        info=dict(description="The name", format='plaintext', official=False)),
+)
+
+class MoveChangelog(TableBase):
+    """History of changes to moves across main game versions."""
+    __tablename__ = 'move_changelog'
+    __singlename__ = 'move_changelog'
+    move_id = Column(Integer, ForeignKey('moves.id'), primary_key=True, nullable=False,
+        info=dict(description="ID of the move that changed"))
+    changed_in_version_group_id = Column(Integer, ForeignKey('version_groups.id'), primary_key=True, nullable=False,
+        info=dict(description="ID of the version group in which the move changed"))
+    type_id = Column(Integer, ForeignKey('types.id'), nullable=True,
+        info=dict(description="Prior type of the move, or NULL if unchanged"))
+    power = Column(SmallInteger, nullable=True,
+        info=dict(description="Prior base power of the move, or NULL if unchanged"))
+    pp = Column(SmallInteger, nullable=True,
+        info=dict(description="Prior base PP of the move, or NULL if unchanged"))
+    accuracy = Column(SmallInteger, nullable=True,
+        info=dict(description="Prior accuracy of the move, or NULL if unchanged"))
+    effect_id = Column(Integer, ForeignKey('move_effects.id'), nullable=True,
+        info=dict(description="Prior ID of the effect, or NULL if unchanged"))
+    effect_chance = Column(Integer, nullable=True,
+        info=dict(description="Prior effect chance, or NULL if unchanged"))
 
 class MoveDamageClass(TableBase):
 
 class MoveDamageClass(TableBase):
-    u"""Damage class of a move, i.e. "Physical", "Special, or "None".
+    u"""Any of the damage classes moves can have, i.e. physical, special, or non-damaging.
     """
     __tablename__ = 'move_damage_classes'
     """
     __tablename__ = 'move_damage_classes'
+    __singlename__ = 'move_damage_class'
     id = Column(Integer, primary_key=True, nullable=False,
         info=dict(description="A numeric ID"))
     id = Column(Integer, primary_key=True, nullable=False,
         info=dict(description="A numeric ID"))
-    name = Column(Unicode(8), nullable=False,
-        info=dict(description="An English name of the class", format='plaintext'))
-    description = Column(Unicode(64), nullable=False,
-        info=dict(description="An English description of the class", format='plaintext'))
+    identifier = Column(Unicode(16), nullable=False,
+        info=dict(description="An identifier", format='identifier'))
+
+create_translation_table('move_damage_class_prose', MoveDamageClass, 'prose',
+    relation_lazy='joined',
+    name = Column(Unicode(16), nullable=True, index=True,
+        info=dict(description="The name", format='plaintext', official=False)),
+    description = Column(Unicode(64), nullable=True,
+        info=dict(description="A description of the class", format='plaintext')),
+)
 
 class MoveEffect(TableBase):
     u"""An effect of a move
     """
     __tablename__ = 'move_effects'
 
 class MoveEffect(TableBase):
     u"""An effect of a move
     """
     __tablename__ = 'move_effects'
+    __singlename__ = 'move_effect'
+    id = Column(Integer, primary_key=True, nullable=False,
+        info=dict(description="A numeric ID"))
+
+create_translation_table('move_effect_prose', MoveEffect, 'prose',
+    short_effect = Column(Unicode(256), nullable=True,
+        info=dict(description="A short summary of the effect", format='plaintext')),
+    effect = Column(Unicode(5120), nullable=True,
+        info=dict(description="A detailed description of the effect", format='plaintext')),
+)
+
+class MoveEffectChangelog(TableBase):
+    """History of changes to move effects across main game versions."""
+    __tablename__ = 'move_effect_changelog'
+    __singlename__ = 'move_effect_changelog'
     id = Column(Integer, primary_key=True, nullable=False,
         info=dict(description="A numeric ID"))
     id = Column(Integer, primary_key=True, nullable=False,
         info=dict(description="A numeric ID"))
-    short_effect = Column(Unicode(256), nullable=False,
-        info=dict(description="A short summary of the effect", format='plaintext'))
-    effect = Column(Unicode(5120), nullable=False,
-        info=dict(description="A detailed description of the effect", format='plaintext'))
+    effect_id = Column(Integer, ForeignKey('move_effects.id'), nullable=False,
+        info=dict(description="The ID of the effect that changed"))
+    changed_in_version_group_id = Column(Integer, ForeignKey('version_groups.id'), nullable=False,
+        info=dict(description="The ID of the version group in which the effect changed"))
+
+    __table_args__ = (
+        UniqueConstraint(effect_id, changed_in_version_group_id),
+        {},
+    )
+
+create_translation_table('move_effect_changelog_prose', MoveEffectChangelog, 'prose',
+    effect = Column(markdown.MarkdownColumn(512), nullable=False,
+        info=dict(description="A description of the old behavior", format='markdown')),
+)
 
 class MoveFlag(TableBase):
     u"""Maps a move flag to a move
 
 class MoveFlag(TableBase):
     u"""Maps a move flag to a move
@@ -594,107 +844,133 @@ class MoveFlagType(TableBase):
     u"""A Move attribute such as "snatchable" or "contact".
     """
     __tablename__ = 'move_flag_types'
     u"""A Move attribute such as "snatchable" or "contact".
     """
     __tablename__ = 'move_flag_types'
+    __singlename__ = 'move_flag_type'
     id = Column(Integer, primary_key=True, nullable=False,
         info=dict(description="A numeric ID"))
     id = Column(Integer, primary_key=True, nullable=False,
         info=dict(description="A numeric ID"))
-    identifier = Column(Unicode(16), nullable=False,
+    identifier = Column(Unicode(32), nullable=False,
         info=dict(description="A short identifier for the flag", format='identifier'))
         info=dict(description="A short identifier for the flag", format='identifier'))
-    name = Column(Unicode(32), nullable=False,
-        info=dict(description="An English name for the flag", format='plaintext'))
-    description = Column(markdown.MarkdownColumn(128), nullable=False,
-        info=dict(description="A short English description of the flag", format='markdown'))
+
+create_translation_table('move_flag_type_prose', MoveFlagType, 'prose',
+    relation_lazy='joined',
+    name = Column(Unicode(32), nullable=True, index=True,
+        info=dict(description="The name", format='plaintext', official=False)),
+    description = Column(markdown.MarkdownColumn(128), nullable=True,
+        info=dict(description="A short description of the flag", format='markdown')),
+)
 
 class MoveFlavorText(TableBase):
     u"""In-game description of a move
     """
     __tablename__ = 'move_flavor_text'
 
 class MoveFlavorText(TableBase):
     u"""In-game description of a move
     """
     __tablename__ = 'move_flavor_text'
+    summary_column = Move.flavor_summaries_table, 'flavor_summary'
     move_id = Column(Integer, ForeignKey('moves.id'), primary_key=True, nullable=False, autoincrement=False,
         info=dict(description="ID of the move"))
     version_group_id = Column(Integer, ForeignKey('version_groups.id'), primary_key=True, nullable=False, autoincrement=False,
         info=dict(description="ID of the version group this text appears in"))
     move_id = Column(Integer, ForeignKey('moves.id'), primary_key=True, nullable=False, autoincrement=False,
         info=dict(description="ID of the move"))
     version_group_id = Column(Integer, ForeignKey('version_groups.id'), primary_key=True, nullable=False, autoincrement=False,
         info=dict(description="ID of the version group this text appears in"))
+    language_id = Column(Integer, ForeignKey('languages.id'), primary_key=True, nullable=False,
+        info=dict(description="The language"))
     flavor_text = Column(Unicode(255), nullable=False,
     flavor_text = Column(Unicode(255), nullable=False,
-        info=dict(description="The English flavor text", official=True, format='gametext'))
+        info=dict(description="The flavor text", official=True, format='gametext'))
 
 
-class MoveName(TableBase):
-    u"""Non-English name of a move
-    """
-    __tablename__ = 'move_names'
+class MoveMeta(TableBase):
+    u"""Metadata for move effects, sorta-kinda ripped straight from the game"""
+    __tablename__ = 'move_meta'
+    move_id = Column(Integer, ForeignKey('moves.id'), primary_key=True, nullable=False, autoincrement=False,
+        info=dict(description="A numeric ID"))
+    meta_category_id = Column(Integer, ForeignKey('move_meta_categories.id'), nullable=False,
+        info=dict(description="ID of the move category"))
+    meta_ailment_id = Column(Integer, ForeignKey('move_meta_ailments.id'), nullable=False,
+        info=dict(description="ID of the caused ailment"))
+    min_hits = Column(Integer, nullable=True, index=True,
+        info=dict(description="Minimum number of hits per use"))
+    max_hits = Column(Integer, nullable=True, index=True,
+        info=dict(description="Maximum number of hits per use"))
+    min_turns = Column(Integer, nullable=True, index=True,
+        info=dict(description="Minimum number of turns the user is forced to use the move"))
+    max_turns = Column(Integer, nullable=True, index=True,
+        info=dict(description="Maximum number of turns the user is forced to use the move"))
+    recoil = Column(Integer, nullable=False, index=True,
+        info=dict(description="Recoil damage, in percent of damage done"))
+    healing = Column(Integer, nullable=False, index=True,
+        info=dict(description="Healing, in percent of user's max HP"))
+    crit_rate = Column(Integer, nullable=False, index=True,
+        info=dict(description="Critical hit rate bonus"))
+    ailment_chance = Column(Integer, nullable=False, index=True,
+        info=dict(description="Chance to cause an ailment, in percent"))
+    flinch_chance = Column(Integer, nullable=False, index=True,
+        info=dict(description="Chance to cause flinching, in percent"))
+    stat_chance = Column(Integer, nullable=False, index=True,
+        info=dict(description="Chance to cause a stat change, in percent"))
+
+class MoveMetaAilment(TableBase):
+    u"""Common status ailments moves can inflict on a single Pokémon, including
+    major ailments like paralysis and minor ailments like trapping.
+    """
+    __tablename__ = 'move_meta_ailments'
+    __singlename__ = 'move_meta_ailment'
+    id = Column(Integer, primary_key=True, nullable=False, autoincrement=False,
+        info=dict(description="A numeric ID"))
+    identifier = Column(Unicode(24), nullable=False, index=True, unique=True,
+        info=dict(description="An identifier", format='identifier'))
+
+create_translation_table('move_meta_ailment_names', MoveMetaAilment, 'names',
+    relation_lazy='joined',
+    name = Column(Unicode(24), nullable=False, index=True,
+        info=dict(description="The name", format='plaintext', official=True)),
+)
+
+class MoveMetaCategory(TableBase):
+    u"""Very general categories that loosely group move effects."""
+    __tablename__ = 'move_meta_categories'
+    __singlename__ = 'move_meta_category'
+    id = Column(Integer, primary_key=True, nullable=False,
+        info=dict(description="A numeric ID"))
+    identifier = Column(Unicode(32), nullable=False, index=True, unique=True,
+        info=dict(description="An identifier", format='identifier'))
+
+create_translation_table('move_meta_category_prose', MoveMetaCategory, 'prose',
+    relation_lazy='joined',
+    description = Column(Unicode(64), nullable=False,
+        info=dict(description="A description of the category", format="plaintext", official=False)),
+)
+
+class MoveMetaStatChange(TableBase):
+    u"""Stat changes moves (may) make."""
+    __tablename__ = 'move_meta_stat_changes'
     move_id = Column(Integer, ForeignKey('moves.id'), primary_key=True, nullable=False, autoincrement=False,
         info=dict(description="ID of the move"))
     move_id = Column(Integer, ForeignKey('moves.id'), primary_key=True, nullable=False, autoincrement=False,
         info=dict(description="ID of the move"))
-    language_id = Column(Integer, ForeignKey('languages.id'), primary_key=True, nullable=False, autoincrement=False,
-        info=dict(description="ID of the language"))
-    name = Column(Unicode(16), nullable=False,
-        info=dict(description="ID of the language", foreign=True, format='plaintext'))
+    stat_id = Column(Integer, ForeignKey('stats.id'), primary_key=True, nullable=False, autoincrement=False,
+        info=dict(description="ID of the stat"))
+    change = Column(Integer, nullable=False, index=True,
+        info=dict(description="Amount of increase/decrease, in stages"))
 
 class MoveTarget(TableBase):
     u"""Targetting or "range" of a move, e.g. "Affects all opponents" or "Affects user".
     """
     __tablename__ = 'move_targets'
 
 class MoveTarget(TableBase):
     u"""Targetting or "range" of a move, e.g. "Affects all opponents" or "Affects user".
     """
     __tablename__ = 'move_targets'
+    __singlename__ = 'move_target'
     id = Column(Integer, primary_key=True, nullable=False,
         info=dict(description="A numeric ID"))
     id = Column(Integer, primary_key=True, nullable=False,
         info=dict(description="A numeric ID"))
-    name = Column(Unicode(32), nullable=False,
-        info=dict(description="An English name", format='plaintext'))
-    description = Column(Unicode(128), nullable=False,
-        info=dict(description="An English description", format='plaintext'))
-
-class Move(TableBase):
-    u"""A Move: technique or attack a Pokémon can learn to use
-    """
-    __tablename__ = 'moves'
-    __singlename__ = 'move'
-    id = Column(Integer, primary_key=True, nullable=False,
-        info=dict(description="A numeric ID"))
-    name = Column(Unicode(24), nullable=False,
-        info=dict(description="The English name of the move", official=True, format='plaintext'))
-    generation_id = Column(Integer, ForeignKey('generations.id'), nullable=False,
-        info=dict(description="ID of the generation this move first appeared in"))
-    type_id = Column(Integer, ForeignKey('types.id'), nullable=False,
-        info=dict(description="ID of the move's elemental type"))
-    power = Column(SmallInteger, nullable=False,
-        info=dict(description="Base power of the move"))
-    pp = Column(SmallInteger, nullable=False,
-        info=dict(description="Base PP (Power Points) of the move"))
-    accuracy = Column(SmallInteger, nullable=True,
-        info=dict(description="Accuracy of the move; NULL means it never misses"))
-    priority = Column(SmallInteger, nullable=False,
-        info=dict(description="The move's priority bracket"))
-    target_id = Column(Integer, ForeignKey('move_targets.id'), nullable=False,
-        info=dict(description="ID of the target (range) of the move"))
-    damage_class_id = Column(Integer, ForeignKey('move_damage_classes.id'), nullable=False,
-        info=dict(description="ID of the damage class (physical/special) of the move"))
-    effect_id = Column(Integer, ForeignKey('move_effects.id'), nullable=False,
-        info=dict(description="ID of the move's effect"))
-    effect_chance = Column(Integer,
-        info=dict(description="The chance for a secondary effect. What this is a chance of is specified by the move's effect."))
-    contest_type_id = Column(Integer, ForeignKey('contest_types.id'), nullable=True,
-        info=dict(description="ID of the move's Contest type (e.g. cool or smart)"))
-    contest_effect_id = Column(Integer, ForeignKey('contest_effects.id'), nullable=True,
-        info=dict(description="ID of the move's Contest effect"))
-    super_contest_effect_id = Column(Integer, ForeignKey('super_contest_effects.id'), nullable=True,
-        info=dict(description="ID of the move's Super Contest effect"))
-
-class MoveChangelog(TableBase):
-    """History of changes to moves across main game versions."""
-    __tablename__ = 'move_changelog'
-    move_id = Column(Integer, ForeignKey('moves.id'), primary_key=True, nullable=False,
-        info=dict(description="ID of the move that changed"))
-    changed_in_version_group_id = Column(Integer, ForeignKey('version_groups.id'), primary_key=True, nullable=False,
-        info=dict(description="ID of the version group in which the move changed"))
-    power = Column(SmallInteger, nullable=True,
-        info=dict(description="Prior base power of the move, or NULL if unchanged"))
-    pp = Column(SmallInteger, nullable=True,
-        info=dict(description="Prior base PP of the move, or NULL if unchanged"))
-    accuracy = Column(SmallInteger, nullable=True,
-        info=dict(description="Prior accuracy of the move, or NULL if unchanged"))
+    identifier = Column(Unicode(32), nullable=False,
+        info=dict(description="An identifier", format='identifier'))
+
+create_translation_table('move_target_prose', MoveTarget, 'prose',
+    relation_lazy='joined',
+    name = Column(Unicode(32), nullable=True, index=True,
+        info=dict(description="The name", format='plaintext', official=False)),
+    description = Column(Unicode(128), nullable=True,
+        info=dict(description="A description", format='plaintext')),
+)
 
 class Nature(TableBase):
 
 class Nature(TableBase):
-    u"""A nature a pokémon can have, such as Calm or Brave
+    u"""A nature a Pokémon can have, such as Calm or Brave
     """
     __tablename__ = 'natures'
     __singlename__ = 'nature'
     id = Column(Integer, primary_key=True, nullable=False,
         info=dict(description="A numeric ID"))
     """
     __tablename__ = 'natures'
     __singlename__ = 'nature'
     id = Column(Integer, primary_key=True, nullable=False,
         info=dict(description="A numeric ID"))
-    name = Column(Unicode(8), nullable=False,
-        info=dict(description="An English name of the nature", official=True, format='plaintext'))
+    identifier = Column(Unicode(8), nullable=False,
+        info=dict(description="An identifier", format='identifier'))
     decreased_stat_id = Column(Integer, ForeignKey('stats.id'), nullable=False,
         info=dict(description="ID of the stat that this nature decreases by 10% (if decreased_stat_id is the same, the effects cancel out)"))
     increased_stat_id = Column(Integer, ForeignKey('stats.id'), nullable=False,
     decreased_stat_id = Column(Integer, ForeignKey('stats.id'), nullable=False,
         info=dict(description="ID of the stat that this nature decreases by 10% (if decreased_stat_id is the same, the effects cancel out)"))
     increased_stat_id = Column(Integer, ForeignKey('stats.id'), nullable=False,
@@ -711,15 +987,21 @@ class Nature(TableBase):
         """
         return self.increased_stat_id == self.decreased_stat_id
 
         """
         return self.increased_stat_id == self.decreased_stat_id
 
+create_translation_table('nature_names', Nature, 'names',
+    relation_lazy='joined',
+    name = Column(Unicode(8), nullable=False, index=True,
+        info=dict(description="The name", format='plaintext', official=True, ripped=True)),
+)
+
 class NatureBattleStylePreference(TableBase):
     u"""Battle Palace move preference
 
 class NatureBattleStylePreference(TableBase):
     u"""Battle Palace move preference
 
-    Specifies how likely a pokémon with a specific Nature is to use a move of
+    Specifies how likely a Pokémon with a specific Nature is to use a move of
     a particular battl style in Battle Palace or Battle Tent
     """
     __tablename__ = 'nature_battle_style_preferences'
     nature_id = Column(Integer, ForeignKey('natures.id'), primary_key=True, nullable=False,
     a particular battl style in Battle Palace or Battle Tent
     """
     __tablename__ = 'nature_battle_style_preferences'
     nature_id = Column(Integer, ForeignKey('natures.id'), primary_key=True, nullable=False,
-        info=dict(description=u"ID of the pokémon's nature"))
+        info=dict(description=u"ID of the Pokémon's nature"))
     move_battle_style_id = Column(Integer, ForeignKey('move_battle_styles.id'), primary_key=True, nullable=False,
         info=dict(description="ID of the battle style"))
     low_hp_preference = Column(Integer, nullable=False,
     move_battle_style_id = Column(Integer, ForeignKey('move_battle_styles.id'), primary_key=True, nullable=False,
         info=dict(description="ID of the battle style"))
     low_hp_preference = Column(Integer, nullable=False,
@@ -727,17 +1009,6 @@ class NatureBattleStylePreference(TableBase):
     high_hp_preference = Column(Integer, nullable=False,
         info=dict(description=u"Chance of using the move, in percent, if HP is over ½"))
 
     high_hp_preference = Column(Integer, nullable=False,
         info=dict(description=u"Chance of using the move, in percent, if HP is over ½"))
 
-class NatureName(TableBase):
-    u"""Non-english name of a Nature
-    """
-    __tablename__ = 'nature_names'
-    nature_id = Column(Integer, ForeignKey('natures.id'), primary_key=True, nullable=False, autoincrement=False,
-        info=dict(description="ID of the nature"))
-    language_id = Column(Integer, ForeignKey('languages.id'), primary_key=True, nullable=False, autoincrement=False,
-        info=dict(description="ID of the language"))
-    name = Column(Unicode(8), nullable=False,
-        info=dict(description="The nature's foreign name", foreign=True, format='plaintext'))
-
 class NaturePokeathlonStat(TableBase):
     u"""Specifies how a Nature affects a Pokéathlon stat
     """
 class NaturePokeathlonStat(TableBase):
     u"""Specifies how a Nature affects a Pokéathlon stat
     """
@@ -753,23 +1024,36 @@ class PokeathlonStat(TableBase):
     u"""A Pokéathlon stat, such as "Stamina" or "Jump".
     """
     __tablename__ = 'pokeathlon_stats'
     u"""A Pokéathlon stat, such as "Stamina" or "Jump".
     """
     __tablename__ = 'pokeathlon_stats'
+    __singlename__ = 'pokeathlon_stat'
     id = Column(Integer, primary_key=True, nullable=False,
         info=dict(description="A numeric ID"))
     id = Column(Integer, primary_key=True, nullable=False,
         info=dict(description="A numeric ID"))
-    name = Column(Unicode(8), nullable=False,
-        info=dict(description="The English name of the stat", official=True, format='plaintext'))
+    identifier = Column(Unicode(8), nullable=False,
+        info=dict(description="An identifier", format='identifier'))
+
+create_translation_table('pokeathlon_stat_names', PokeathlonStat, 'names',
+    name = Column(Unicode(8), nullable=False, index=True,
+        info=dict(description="The name", format='plaintext', official=True)),
+)
 
 class Pokedex(TableBase):
 
 class Pokedex(TableBase):
-    u"""A collection of pokémon species ordered in a particular way
+    u"""A collection of Pokémon species ordered in a particular way
     """
     __tablename__ = 'pokedexes'
     """
     __tablename__ = 'pokedexes'
+    __singlename__ = 'pokedex'
     id = Column(Integer, primary_key=True, nullable=False,
         info=dict(description="A numeric ID"))
     region_id = Column(Integer, ForeignKey('regions.id'), nullable=True,
     id = Column(Integer, primary_key=True, nullable=False,
         info=dict(description="A numeric ID"))
     region_id = Column(Integer, ForeignKey('regions.id'), nullable=True,
-        info=dict(description=u"ID of the region this pokédex is used in, or None if it's global"))
-    name = Column(Unicode(16), nullable=False,
-        info=dict(description=u"An English name of the pokédex", format='plaintext'))
-    description = Column(Unicode(512),
-        info=dict(description=u"A longer description of the pokédex", format='plaintext'))
+        info=dict(description=u"ID of the region this Pokédex is used in, or None if it's global"))
+    identifier = Column(Unicode(16), nullable=False,
+        info=dict(description=u"An identifier", format='identifier'))
+
+create_translation_table('pokedex_prose', Pokedex, 'prose',
+    relation_lazy='joined',
+    name = Column(Unicode(16), nullable=True, index=True,
+        info=dict(description="The name", format='plaintext', official=False)),
+    description = Column(Unicode(512), nullable=True,
+        info=dict(description=u"A longer description of the Pokédex", format='plaintext')),
+)
 
 class Pokemon(TableBase):
     u"""A species of Pokémon.  The core to this whole mess.
 
 class Pokemon(TableBase):
     u"""A species of Pokémon.  The core to this whole mess.
@@ -778,45 +1062,50 @@ class Pokemon(TableBase):
     __singlename__ = 'pokemon'
     id = Column(Integer, primary_key=True, nullable=False,
         info=dict(description=u"A numeric ID"))
     __singlename__ = 'pokemon'
     id = Column(Integer, primary_key=True, nullable=False,
         info=dict(description=u"A numeric ID"))
-    name = Column(Unicode(20), nullable=False,
-        info=dict(description=u"The English name of the Pokémon", official=True, format='plaintext'))
+    identifier = Column(Unicode(20), nullable=False,
+        info=dict(description=u"An identifier", format='identifier'))
     generation_id = Column(Integer, ForeignKey('generations.id'),
         info=dict(description=u"ID of the generation this species first appeared in"))
     evolution_chain_id = Column(Integer, ForeignKey('evolution_chains.id'),
         info=dict(description=u"ID of the species' evolution chain (a.k.a. family)"))
     generation_id = Column(Integer, ForeignKey('generations.id'),
         info=dict(description=u"ID of the generation this species first appeared in"))
     evolution_chain_id = Column(Integer, ForeignKey('evolution_chains.id'),
         info=dict(description=u"ID of the species' evolution chain (a.k.a. family)"))
+    evolves_from_pokemon_id = Column(Integer, ForeignKey('pokemon.id'), nullable=True,
+        info=dict(description=u"The Pokémon species from which this one evolves"))
     height = Column(Integer, nullable=False,
     height = Column(Integer, nullable=False,
-        info=dict(description=u"The height of the pokémon, in decimeters (tenths of a meter)"))
+        info=dict(description=u"The height of the Pokémon, in decimeters (tenths of a meter)"))
     weight = Column(Integer, nullable=False,
     weight = Column(Integer, nullable=False,
-        info=dict(description=u"The weight of the pokémon, in tenths of a kilogram (decigrams)"))
-    species = Column(Unicode(16), nullable=False,
-        info=dict(description=u'The short English flavor text, such as "Seed" or "Lizard"; usually affixed with the word "Pokémon"',
-        official=True, format='plaintext'))
+        info=dict(description=u"The weight of the Pokémon, in tenths of a kilogram (decigrams)"))
     color_id = Column(Integer, ForeignKey('pokemon_colors.id'), nullable=False,
     color_id = Column(Integer, ForeignKey('pokemon_colors.id'), nullable=False,
-        info=dict(description=u"ID of this pokémon's pokédex color, as used for a gimmick search function in the games."))
-    pokemon_shape_id = Column(Integer, ForeignKey('pokemon_shapes.id'), nullable=True,
-        info=dict(description=u"ID of this pokémon's body shape, as used for a gimmick search function in the games."))
+        info=dict(description=u"ID of this Pokémon's Pokédex color, as used for a gimmick search function in the games."))
+    pokemon_shape_id = Column(Integer, ForeignKey('pokemon_shapes.id'), nullable=False,
+        info=dict(description=u"ID of this Pokémon's body shape, as used for a gimmick search function in the games."))
     habitat_id = Column(Integer, ForeignKey('pokemon_habitats.id'), nullable=True,
     habitat_id = Column(Integer, ForeignKey('pokemon_habitats.id'), nullable=True,
-        info=dict(description=u"ID of this pokémon's habitat, as used for a gimmick search function in the games."))
+        info=dict(description=u"ID of this Pokémon's habitat, as used for a gimmick search function in the games."))
     gender_rate = Column(Integer, nullable=False,
     gender_rate = Column(Integer, nullable=False,
-        info=dict(description=u"The chance of this pokémon being female, in eighths; or -1 for genderless"))
+        info=dict(description=u"The chance of this Pokémon being female, in eighths; or -1 for genderless"))
     capture_rate = Column(Integer, nullable=False,
         info=dict(description=u"The base capture rate; up to 255"))
     base_experience = Column(Integer, nullable=False,
     capture_rate = Column(Integer, nullable=False,
         info=dict(description=u"The base capture rate; up to 255"))
     base_experience = Column(Integer, nullable=False,
-        info=dict(description=u"The base EXP gained when defeating this pokémon"))  # XXX: Is this correct?
+        info=dict(description=u"The base EXP gained when defeating this Pokémon"))  # XXX: Is this correct?
     base_happiness = Column(Integer, nullable=False,
         info=dict(description=u"The tameness when caught by a normal ball"))
     is_baby = Column(Boolean, nullable=False,
     base_happiness = Column(Integer, nullable=False,
         info=dict(description=u"The tameness when caught by a normal ball"))
     is_baby = Column(Boolean, nullable=False,
-        info=dict(description=u"True iff the pokémon is a baby"))  # XXX: What exactly makes it a baby?
+        info=dict(description=u"True iff the Pokémon is a baby, i.e. a lowest-stage Pokémon that cannot breed but whose evolved form can."))
     hatch_counter = Column(Integer, nullable=False,
     hatch_counter = Column(Integer, nullable=False,
-        info=dict(description=u"Initial hatch counter: one must walk 255 × (hatch_counter + 1) steps before this pokémon's egg hatches, unless utilizing bonuses like Flame Body's"))
-    has_gen4_fem_sprite = Column(Boolean, nullable=False,
-        info=dict(description=u"Set iff the species' female front sprite is different from the male's in generation IV"))
-    has_gen4_fem_back_sprite = Column(Boolean, nullable=False,
-        info=dict(description=u"Set iff the species' female back sprite is different from the male's in generation IV"))
+        info=dict(description=u"Initial hatch counter: one must walk 255 × (hatch_counter + 1) steps before this Pokémon's egg hatches, unless utilizing bonuses like Flame Body's"))
+    has_gender_differences = Column(Boolean, nullable=False,
+        info=dict(description=u"Set iff the species exhibits enough sexual dimorphism to have separate sets of sprites in Gen IV and beyond."))
+    order = Column(Integer, nullable=False, index=True,
+        info=dict(description=u"Order for sorting. Almost national order, except families and forms are grouped together."))
 
     ### Stuff to handle alternate Pokémon forms
 
     @property
 
     ### Stuff to handle alternate Pokémon forms
 
     @property
+    def form(self):
+        u"""Returns the Pokémon's form, using its default form as fallback."""
+
+        return self.unique_form or self.default_form
+
+    @property
     def is_base_form(self):
         u"""Returns True iff the Pokémon is the base form for its species,
         e.g. Land Shaymin.
     def is_base_form(self):
         u"""Returns True iff the Pokémon is the base form for its species,
         e.g. Land Shaymin.
@@ -840,7 +1129,7 @@ class Pokemon(TableBase):
         u"""Returns the Pokémon's name, including its form if applicable."""
 
         if self.form_name:
         u"""Returns the Pokémon's name, including its form if applicable."""
 
         if self.form_name:
-            return '{0} {1}'.format(self.form_name, self.name)
+            return u'%s %s' % (self.form_name, self.name)
         else:
             return self.name
 
         else:
             return self.name
 
@@ -890,12 +1179,25 @@ class Pokemon(TableBase):
         else:
             return None
 
         else:
             return None
 
+create_translation_table('pokemon_names', Pokemon, 'names',
+    relation_lazy='joined',
+    name = Column(Unicode(20), nullable=True, index=True,
+        info=dict(description="The name", format='plaintext', official=True, ripped=True)),
+    species = Column(Unicode(16), nullable=True,
+        info=dict(description=u'The short flavor text, such as "Seed" or "Lizard"; usually affixed with the word "Pokémon"',
+        official=True, format='plaintext')),
+)
+create_translation_table('pokemon_flavor_summaries', Pokemon, 'flavor_summaries',
+    flavor_summary = Column(Unicode(512), nullable=True,
+        info=dict(description=u"Text containing facts from all flavor texts, for languages without official game translations", official=False, format='plaintext', ripped=True)),
+)
+
 class PokemonAbility(TableBase):
 class PokemonAbility(TableBase):
-    u"""Maps an ability to a pokémon that can have it
+    u"""Maps an ability to a Pokémon that can have it
     """
     __tablename__ = 'pokemon_abilities'
     pokemon_id = Column(Integer, ForeignKey('pokemon.id'), primary_key=True, nullable=False, autoincrement=False,
     """
     __tablename__ = 'pokemon_abilities'
     pokemon_id = Column(Integer, ForeignKey('pokemon.id'), primary_key=True, nullable=False, autoincrement=False,
-        info=dict(description=u"ID of the pokémon"))
+        info=dict(description=u"ID of the Pokémon"))
     ability_id = Column(Integer, ForeignKey('abilities.id'), nullable=False,
         info=dict(description=u"ID of the ability"))
     # XXX having both a method and a slot is kind of gross.  "slot" is a
     ability_id = Column(Integer, ForeignKey('abilities.id'), nullable=False,
         info=dict(description=u"ID of the ability"))
     # XXX having both a method and a slot is kind of gross.  "slot" is a
@@ -907,77 +1209,91 @@ class PokemonAbility(TableBase):
         info=dict(description=u"The ability slot, i.e. 1 or 2 for gen. IV"))
 
 class PokemonColor(TableBase):
         info=dict(description=u"The ability slot, i.e. 1 or 2 for gen. IV"))
 
 class PokemonColor(TableBase):
-    u"""The "pokédex color" of a pokémon species. Usually based on the pokémon's color.
+    u"""The "Pokédex color" of a Pokémon species. Usually based on the Pokémon's color.
     """
     __tablename__ = 'pokemon_colors'
     """
     __tablename__ = 'pokemon_colors'
+    __singlename__ = 'pokemon_color'
     id = Column(Integer, primary_key=True, nullable=False, autoincrement=False,
     id = Column(Integer, primary_key=True, nullable=False, autoincrement=False,
-        info=dict(description=u"ID of the pokémon"))
-    name = Column(Unicode(6), nullable=False,
-        info=dict(description=u"The English name of the color", official=True, format='identifier'))
+        info=dict(description=u"ID of the Pokémon"))
+    identifier = Column(Unicode(6), nullable=False,
+        info=dict(description=u"An identifier", format='identifier'))
+
+create_translation_table('pokemon_color_names', PokemonColor, 'names',
+    relation_lazy='joined',
+    name = Column(Unicode(6), nullable=False, index=True,
+        info=dict(description="The name", format='plaintext', official=True)),
+)
 
 class PokemonDexNumber(TableBase):
     u"""The number of a Pokémon in a particular Pokédex (e.g. Jigglypuff is #138 in Hoenn's 'dex)
     """
     __tablename__ = 'pokemon_dex_numbers'
     pokemon_id = Column(Integer, ForeignKey('pokemon.id'), primary_key=True, nullable=False, autoincrement=False,
 
 class PokemonDexNumber(TableBase):
     u"""The number of a Pokémon in a particular Pokédex (e.g. Jigglypuff is #138 in Hoenn's 'dex)
     """
     __tablename__ = 'pokemon_dex_numbers'
     pokemon_id = Column(Integer, ForeignKey('pokemon.id'), primary_key=True, nullable=False, autoincrement=False,
-        info=dict(description=u"ID of the pokémon"))
+        info=dict(description=u"ID of the Pokémon"))
     pokedex_id = Column(Integer, ForeignKey('pokedexes.id'), primary_key=True, nullable=False, autoincrement=False,
     pokedex_id = Column(Integer, ForeignKey('pokedexes.id'), primary_key=True, nullable=False, autoincrement=False,
-        info=dict(description=u"ID of the pokédex"))
+        info=dict(description=u"ID of the Pokédex"))
     pokedex_number = Column(Integer, nullable=False,
     pokedex_number = Column(Integer, nullable=False,
-        info=dict(description=u"Number of the pokémon in that the pokédex"))
+        info=dict(description=u"Number of the Pokémon in that the Pokédex"))
 
 class PokemonEggGroup(TableBase):
 
 class PokemonEggGroup(TableBase):
-    u"""Maps an Egg group to a pokémon; each pokémon belongs to one or two egg groups
+    u"""Maps an Egg group to a Pokémon; each Pokémon belongs to one or two egg groups
     """
     __tablename__ = 'pokemon_egg_groups'
     pokemon_id = Column(Integer, ForeignKey('pokemon.id'), primary_key=True, nullable=False, autoincrement=False,
     """
     __tablename__ = 'pokemon_egg_groups'
     pokemon_id = Column(Integer, ForeignKey('pokemon.id'), primary_key=True, nullable=False, autoincrement=False,
-        info=dict(description=u"ID of the pokémon"))
+        info=dict(description=u"ID of the Pokémon"))
     egg_group_id = Column(Integer, ForeignKey('egg_groups.id'), primary_key=True, nullable=False, autoincrement=False,
         info=dict(description=u"ID of the egg group"))
 
 class PokemonEvolution(TableBase):
     egg_group_id = Column(Integer, ForeignKey('egg_groups.id'), primary_key=True, nullable=False, autoincrement=False,
         info=dict(description=u"ID of the egg group"))
 
 class PokemonEvolution(TableBase):
-    u"""Specifies what causes a particular pokémon to evolve into another species.
+    u"""A required action ("trigger") and the conditions under which the trigger
+    must occur to cause a Pokémon to evolve.
+
+    Any condition may be null if it does not apply for a particular Pokémon.
     """
     __tablename__ = 'pokemon_evolution'
     """
     __tablename__ = 'pokemon_evolution'
-    from_pokemon_id = Column(Integer, ForeignKey('pokemon.id'), nullable=False,
-        info=dict(description=u"ID of the pre-evolution species"))
-    to_pokemon_id = Column(Integer, ForeignKey('pokemon.id'), primary_key=True, nullable=False, autoincrement=False,
-        info=dict(description=u"ID of the post-evolution species"))
+    id = Column(Integer, primary_key=True, nullable=False)
+    evolved_pokemon_id = Column(Integer, ForeignKey('pokemon.id'), nullable=False,
+        info=dict(description=u"The ID of the post-evolution Pokémon."))
     evolution_trigger_id = Column(Integer, ForeignKey('evolution_triggers.id'), nullable=False,
     evolution_trigger_id = Column(Integer, ForeignKey('evolution_triggers.id'), nullable=False,
-        info=dict(description=u"ID of the trigger type"))
+        info=dict(description=u"The ID of the evolution trigger."))
     trigger_item_id = Column(Integer, ForeignKey('items.id'), nullable=True,
     trigger_item_id = Column(Integer, ForeignKey('items.id'), nullable=True,
-        info=dict(description=u"ID of the item that triggers the evolution in a way defined by evolution_trigger_id"))
+        info=dict(description=u"The ID of the item that must be used on the Pokémon."))
     minimum_level = Column(Integer, nullable=True,
     minimum_level = Column(Integer, nullable=True,
-        info=dict(description=u"Minimum level, or None if level doean't matter"))
+        info=dict(description=u"The minimum level for the Pokémon."))
     gender = Column(Enum('male', 'female', name='pokemon_evolution_gender'), nullable=True,
     gender = Column(Enum('male', 'female', name='pokemon_evolution_gender'), nullable=True,
-        info=dict(description=u"Required gender, or None if gender doesn't matter"))
+        info=dict(description=u"The Pokémon's required gender, or None if gender doesn't matter"))
     location_id = Column(Integer, ForeignKey('locations.id'), nullable=True,
     location_id = Column(Integer, ForeignKey('locations.id'), nullable=True,
-        info=dict(description=u"Required location, or None if it doesn't matter"))
+        info=dict(description=u"The ID of the location the evolution must be triggered at."))
     held_item_id = Column(Integer, ForeignKey('items.id'), nullable=True,
     held_item_id = Column(Integer, ForeignKey('items.id'), nullable=True,
-        info=dict(description=u"An item the pokémon must hold, or None if it doesn't matter"))
-    time_of_day = Column(Enum('morning', 'day', 'night', name='pokemon_evolution_time_of_day'), nullable=True,
-        info=dict(description=u"Required time of day, or None if it doesn't matter"))
+        info=dict(description=u"The ID of the item the Pokémon must hold."))
+    time_of_day = Column(Enum('day', 'night', name='pokemon_evolution_time_of_day'), nullable=True,
+        info=dict(description=u"The required time of day."))
     known_move_id = Column(Integer, ForeignKey('moves.id'), nullable=True,
     known_move_id = Column(Integer, ForeignKey('moves.id'), nullable=True,
-        info=dict(description=u"ID of a move the pokémon must know, or None if it doesn't matter"))
+        info=dict(description=u"The ID of the move the Pokémon must know."))
     minimum_happiness = Column(Integer, nullable=True,
     minimum_happiness = Column(Integer, nullable=True,
-        info=dict(description=u"Minimum tameness value the pokémon must have, or None if it doesn't matter"))
+        info=dict(description=u"The minimum happiness value the Pokémon must have."))
     minimum_beauty = Column(Integer, nullable=True,
     minimum_beauty = Column(Integer, nullable=True,
-        info=dict(description=u"Minimum Beauty value the pokémon must have, or None if it doesn't matter"))
+        info=dict(description=u"The minimum Beauty value the Pokémon must have."))
     relative_physical_stats = Column(Integer, nullable=True,
     relative_physical_stats = Column(Integer, nullable=True,
-        info=dict(description=u"Relation of Attack and Defense stats the pokémon must have, as sgn(atk-def), or None if that doesn't matter"))
+        info=dict(description=u"The required relation between the Pokémon's Attack and Defense stats, as sgn(atk-def)."))
     party_pokemon_id = Column(Integer, ForeignKey('pokemon.id'), nullable=True,
     party_pokemon_id = Column(Integer, ForeignKey('pokemon.id'), nullable=True,
-        info=dict(description=u"ID of a pokémon that must be present in the party, or None if there's no such condition"))
+        info=dict(description=u"The ID of the Pokémon that must be present in the party."))
+    trade_pokemon_id = Column(Integer, ForeignKey('pokemon.id'), nullable=True,
+        info=dict(description=u"The ID of the Pokémon for which this Pokémon must be traded."))
 
 class PokemonFlavorText(TableBase):
 
 class PokemonFlavorText(TableBase):
-    u"""In-game pokédex descrption of a pokémon.
+    u"""In-game Pokédex descrption of a Pokémon.
     """
     __tablename__ = 'pokemon_flavor_text'
     """
     __tablename__ = 'pokemon_flavor_text'
+    summary_column = Pokemon.flavor_summaries_table, 'flavor_summary'
     pokemon_id = Column(Integer, ForeignKey('pokemon.id'), primary_key=True, nullable=False, autoincrement=False,
     pokemon_id = Column(Integer, ForeignKey('pokemon.id'), primary_key=True, nullable=False, autoincrement=False,
-        info=dict(description=u"ID of the pokémon"))
+        info=dict(description=u"ID of the Pokémon"))
     version_id = Column(Integer, ForeignKey('versions.id'), primary_key=True, nullable=False, autoincrement=False,
         info=dict(description=u"ID of the version that has this flavor text"))
     version_id = Column(Integer, ForeignKey('versions.id'), primary_key=True, nullable=False, autoincrement=False,
         info=dict(description=u"ID of the version that has this flavor text"))
+    language_id = Column(Integer, ForeignKey('languages.id'), primary_key=True, nullable=False,
+        info=dict(description="The language"))
     flavor_text = Column(Unicode(255), nullable=False,
     flavor_text = Column(Unicode(255), nullable=False,
-        info=dict(description=u"ID of the version that has this flavor text", official=True, format='gametext'))
+        info=dict(description=u"The flavor text", official=True, format='gametext'))
 
 class PokemonForm(TableBase):
     u"""An individual form of a Pokémon.
 
 class PokemonForm(TableBase):
     u"""An individual form of a Pokémon.
@@ -989,8 +1305,8 @@ class PokemonForm(TableBase):
     __singlename__ = 'pokemon_form'
     id = Column(Integer, primary_key=True, nullable=False,
         info=dict(description=u'A unique ID for this form.'))
     __singlename__ = 'pokemon_form'
     id = Column(Integer, primary_key=True, nullable=False,
         info=dict(description=u'A unique ID for this form.'))
-    name = Column(Unicode(16), nullable=True,
-        info=dict(description=u"This form's name, e.g. \"Plant\" for Plant Cloak Burmy.", official=True, format='plaintext'))
+    identifier = Column(Unicode(16), nullable=True,
+        info=dict(description=u"An identifier", format='identifier'))
     form_base_pokemon_id = Column(Integer, ForeignKey('pokemon.id'), nullable=False, autoincrement=False,
         info=dict(description=u'The ID of the base Pokémon for this form.'))
     unique_pokemon_id = Column(Integer, ForeignKey('pokemon.id'), autoincrement=False,
     form_base_pokemon_id = Column(Integer, ForeignKey('pokemon.id'), nullable=False, autoincrement=False,
         info=dict(description=u'The ID of the base Pokémon for this form.'))
     unique_pokemon_id = Column(Integer, ForeignKey('pokemon.id'), autoincrement=False,
@@ -1003,13 +1319,20 @@ class PokemonForm(TableBase):
         info=dict(description=u'The order in which forms should be sorted.  Multiple forms may have equal order, in which case they should fall back on sorting by name.'))
 
     @property
         info=dict(description=u'The order in which forms should be sorted.  Multiple forms may have equal order, in which case they should fall back on sorting by name.'))
 
     @property
+    def pokemon(self):
+        u"""Returns the Pokémon for this form, using the form base as fallback.
+        """
+
+        return self.unique_pokemon or self.form_base_pokemon
+
+    @property
     def full_name(self):
         u"""Returns the full name of this form, e.g. "Plant Cloak"."""
 
         if not self.name:
             return None
         elif self.form_group and self.form_group.term:
     def full_name(self):
         u"""Returns the full name of this form, e.g. "Plant Cloak"."""
 
         if not self.name:
             return None
         elif self.form_group and self.form_group.term:
-            return '{0} {1}'.format(self.name, self.form_group.term)
+            return u'%s %s' % (self.name, self.form_group.term)
         else:
             return self.name
 
         else:
             return self.name
 
@@ -1020,21 +1343,33 @@ class PokemonForm(TableBase):
         """
 
         if self.name:
         """
 
         if self.name:
-            return '{0} {1}'.format(self.name, self.form_base_pokemon.name)
+            return u'%s %s' % (self.name, self.form_base_pokemon.name)
         else:
             return self.form_base_pokemon.name
 
         else:
             return self.form_base_pokemon.name
 
+create_translation_table('pokemon_form_names', PokemonForm, 'names',
+    relation_lazy='joined',
+    name = Column(Unicode(16), nullable=False, index=True,
+        info=dict(description="The name", format='plaintext', official=True)),
+)
+
 class PokemonFormGroup(TableBase):
     u"""Information about a Pokémon's forms as a group."""
     __tablename__ = 'pokemon_form_groups'
 class PokemonFormGroup(TableBase):
     u"""Information about a Pokémon's forms as a group."""
     __tablename__ = 'pokemon_form_groups'
+    __singlename__ = 'pokemon_form_group'
     pokemon_id = Column(Integer, ForeignKey('pokemon.id'), primary_key=True, nullable=False, autoincrement=False,
         info=dict(description=u"ID of the base form Pokémon"))
     pokemon_id = Column(Integer, ForeignKey('pokemon.id'), primary_key=True, nullable=False, autoincrement=False,
         info=dict(description=u"ID of the base form Pokémon"))
-    term = Column(Unicode(16), nullable=True,
-        info=dict(description=u"The term for this Pokémon's forms, e.g. \"Cloak\" for Burmy or \"Forme\" for Deoxys.", official=True, format='plaintext'))
     is_battle_only = Column(Boolean, nullable=False,
         info=dict(description=u"Set iff the forms only change in battle"))
     is_battle_only = Column(Boolean, nullable=False,
         info=dict(description=u"Set iff the forms only change in battle"))
-    description = Column(markdown.MarkdownColumn(1024), nullable=False,
-        info=dict(description=u"English description of how the forms work", format='markdown'))
+# FIXME remooove
+PokemonFormGroup.id = PokemonFormGroup.pokemon_id
+
+create_translation_table('pokemon_form_group_prose', PokemonFormGroup, 'prose',
+    term = Column(Unicode(16), nullable=True,
+        info=dict(description=u"The term for this Pokémon's forms, e.g. \"Cloak\" for Burmy or \"Forme\" for Deoxys.", official=True, format='plaintext')),
+    description = Column(markdown.MarkdownColumn(1024), nullable=True,
+        info=dict(description=u"Description of how the forms work", format='markdown')),
+)
 
 class PokemonFormPokeathlonStat(TableBase):
     u"""A Pokémon form's performance in one Pokéathlon stat."""
 
 class PokemonFormPokeathlonStat(TableBase):
     u"""A Pokémon form's performance in one Pokéathlon stat."""
@@ -1050,45 +1385,52 @@ class PokemonFormPokeathlonStat(TableBase):
     maximum_stat = Column(Integer, nullable=False, autoincrement=False,
         info=dict(description=u'The maximum value for this stat for this Pokémon form.'))
 
     maximum_stat = Column(Integer, nullable=False, autoincrement=False,
         info=dict(description=u'The maximum value for this stat for this Pokémon form.'))
 
+class PokemonGameIndex(TableBase):
+    u"""The number of a Pokémon a game uses internally
+    """
+    __tablename__ = 'pokemon_game_indices'
+    pokemon_id = Column(Integer, ForeignKey('pokemon.id'), primary_key=True, autoincrement=False, nullable=False,
+        info=dict(description=u"Database ID of the Pokémon"))
+    generation_id = Column(Integer, ForeignKey('generations.id'), primary_key=True, autoincrement=False, nullable=False,
+        info=dict(description=u"Database ID of the generation"))
+    game_index = Column(Integer, nullable=False,
+        info=dict(description=u"Internal ID the generation's games use for the Pokémon"))
+
 class PokemonHabitat(TableBase):
 class PokemonHabitat(TableBase):
-    u"""The habitat of a pokémon, as given in the FireRed/LeafGreen version pokédex
+    u"""The habitat of a Pokémon, as given in the FireRed/LeafGreen version Pokédex
     """
     __tablename__ = 'pokemon_habitats'
     """
     __tablename__ = 'pokemon_habitats'
+    __singlename__ = 'pokemon_habitat'
     id = Column(Integer, primary_key=True, nullable=False, autoincrement=False,
         info=dict(description=u"A numeric ID"))
     id = Column(Integer, primary_key=True, nullable=False, autoincrement=False,
         info=dict(description=u"A numeric ID"))
-    name = Column(Unicode(16), nullable=False,
-        info=dict(description=u"The English name of the habitat", official=True, format='plaintext'))
+    identifier = Column(Unicode(16), nullable=False,
+        info=dict(description=u"An identifier", format='identifier'))
 
 
-class PokemonInternalID(TableBase):
-    u"""The number of a pokémon a game uses internally
-    """
-    __tablename__ = 'pokemon_internal_ids'
-    pokemon_id = Column(Integer, ForeignKey('pokemon.id'), primary_key=True, autoincrement=False, nullable=False,
-        info=dict(description=u"Database ID of the pokémon"))
-    generation_id = Column(Integer, ForeignKey('generations.id'), primary_key=True, autoincrement=False, nullable=False,
-        info=dict(description=u"Database ID of the generation"))
-    internal_id = Column(Integer, nullable=False,
-        info=dict(description=u"Internal ID the generation's games use for the pokémon"))
+create_translation_table('pokemon_habitat_names', PokemonHabitat, 'names',
+    relation_lazy='joined',
+    name = Column(Unicode(16), nullable=False, index=True,
+        info=dict(description="The name", format='plaintext', official=True)),
+)
 
 class PokemonItem(TableBase):
 
 class PokemonItem(TableBase):
-    u"""Record of an item a pokémon can hold in the wild
+    u"""Record of an item a Pokémon can hold in the wild
     """
     __tablename__ = 'pokemon_items'
     pokemon_id = Column(Integer, ForeignKey('pokemon.id'), primary_key=True, nullable=False, autoincrement=False,
     """
     __tablename__ = 'pokemon_items'
     pokemon_id = Column(Integer, ForeignKey('pokemon.id'), primary_key=True, nullable=False, autoincrement=False,
-        info=dict(description=u"ID of the pokémon"))
+        info=dict(description=u"ID of the Pokémon"))
     version_id = Column(Integer, ForeignKey('versions.id'), primary_key=True, nullable=False, autoincrement=False,
         info=dict(description=u"ID of the version this applies to"))
     item_id = Column(Integer, ForeignKey('items.id'), primary_key=True, nullable=False, autoincrement=False,
         info=dict(description=u"ID of the item"))
     rarity = Column(Integer, nullable=False,
     version_id = Column(Integer, ForeignKey('versions.id'), primary_key=True, nullable=False, autoincrement=False,
         info=dict(description=u"ID of the version this applies to"))
     item_id = Column(Integer, ForeignKey('items.id'), primary_key=True, nullable=False, autoincrement=False,
         info=dict(description=u"ID of the item"))
     rarity = Column(Integer, nullable=False,
-        info=dict(description=u"Chance of the pokémon holding the item, in percent"))
+        info=dict(description=u"Chance of the Pokémon holding the item, in percent"))
 
 class PokemonMove(TableBase):
 
 class PokemonMove(TableBase):
-    u"""Record of a move a pokémon can learn
+    u"""Record of a move a Pokémon can learn
     """
     __tablename__ = 'pokemon_moves'
     pokemon_id = Column(Integer, ForeignKey('pokemon.id'), nullable=False, index=True,
     """
     __tablename__ = 'pokemon_moves'
     pokemon_id = Column(Integer, ForeignKey('pokemon.id'), nullable=False, index=True,
-        info=dict(description=u"ID of the pokémon"))
+        info=dict(description=u"ID of the Pokémon"))
     version_group_id = Column(Integer, ForeignKey('version_groups.id'), nullable=False, index=True,
         info=dict(description=u"ID of the version group this applies to"))
     move_id = Column(Integer, ForeignKey('moves.id'), nullable=False, index=True,
     version_group_id = Column(Integer, ForeignKey('version_groups.id'), nullable=False, index=True,
         info=dict(description=u"ID of the version group this applies to"))
     move_id = Column(Integer, ForeignKey('moves.id'), nullable=False, index=True,
@@ -1109,54 +1451,57 @@ class PokemonMoveMethod(TableBase):
     u"""A method a move can be learned by, such as "Level up" or "Tutor".
     """
     __tablename__ = 'pokemon_move_methods'
     u"""A method a move can be learned by, such as "Level up" or "Tutor".
     """
     __tablename__ = 'pokemon_move_methods'
+    __singlename__ = 'pokemon_move_method'
     id = Column(Integer, primary_key=True, nullable=False, autoincrement=False,
         info=dict(description=u"A numeric ID"))
     id = Column(Integer, primary_key=True, nullable=False, autoincrement=False,
         info=dict(description=u"A numeric ID"))
-    name = Column(Unicode(64), nullable=False,
-        info=dict(description=u"An English name of the method", format='plaintext'))
-    description = Column(Unicode(255), nullable=False,
-        info=dict(description=u"A detailed description of how the method works", format='plaintext'))
-
-class PokemonName(TableBase):
-    u"""A non-English name of a pokémon.
-    """
-    __tablename__ = 'pokemon_names'
-    pokemon_id = Column(Integer, ForeignKey('pokemon.id'), primary_key=True, nullable=False, autoincrement=False,
-        info=dict(description=u"ID of the pokémon"))
-    language_id = Column(Integer, ForeignKey('languages.id'), primary_key=True, nullable=False, autoincrement=False,
-        info=dict(description=u"ID of the language"))
-    name = Column(Unicode(16), nullable=False,
-        info=dict(description=u"Name of the pokémon in the language", foreign=True, format='plaintext'))
+    identifier = Column(Unicode(64), nullable=False,
+        info=dict(description=u"An identifier", format='identifier'))
+
+create_translation_table('pokemon_move_method_prose', PokemonMoveMethod, 'prose',
+    relation_lazy='joined',
+    name = Column(Unicode(64), nullable=True, index=True,
+        info=dict(description="The name", format='plaintext', official=False)),
+    description = Column(Unicode(255), nullable=True,
+        info=dict(description=u"A detailed description of how the method works", format='plaintext')),
+)
 
 class PokemonShape(TableBase):
 
 class PokemonShape(TableBase):
-    u"""The shape of a pokémon's body, as used in generation IV pokédexes.
+    u"""The shape of a Pokémon's body, as used in generation IV Pokédexes.
     """
     __tablename__ = 'pokemon_shapes'
     """
     __tablename__ = 'pokemon_shapes'
+    __singlename__ = 'pokemon_shape'
     id = Column(Integer, primary_key=True, nullable=False,
         info=dict(description=u"A numeric ID"))
     id = Column(Integer, primary_key=True, nullable=False,
         info=dict(description=u"A numeric ID"))
-    name = Column(Unicode(24), nullable=False,
-        info=dict(description=u"A boring English name of the body shape", format='plaintext'))
-    awesome_name = Column(Unicode(16), nullable=False,
-        info=dict(description=u"A splendiferous, technically English, name of the body shape", format='plaintext'))
+    identifier = Column(Unicode(24), nullable=False,
+        info=dict(description=u"An identifier", format='identifier'))
+
+create_translation_table('pokemon_shape_prose', PokemonShape, 'prose',
+    relation_lazy='joined',
+    name = Column(Unicode(24), nullable=True, index=True,
+        info=dict(description="The name", format='plaintext', official=False)),
+    awesome_name = Column(Unicode(16), nullable=True,
+        info=dict(description=u"A splendiferous name of the body shape", format='plaintext')),
+)
 
 class PokemonStat(TableBase):
 
 class PokemonStat(TableBase):
-    u"""A stat value of a pokémon
+    u"""A stat value of a Pokémon
     """
     __tablename__ = 'pokemon_stats'
     pokemon_id = Column(Integer, ForeignKey('pokemon.id'), primary_key=True, nullable=False, autoincrement=False,
     """
     __tablename__ = 'pokemon_stats'
     pokemon_id = Column(Integer, ForeignKey('pokemon.id'), primary_key=True, nullable=False, autoincrement=False,
-        info=dict(description=u"ID of the pokémon"))
+        info=dict(description=u"ID of the Pokémon"))
     stat_id = Column(Integer, ForeignKey('stats.id'), primary_key=True, nullable=False, autoincrement=False,
         info=dict(description=u"ID of the stat"))
     base_stat = Column(Integer, nullable=False,
         info=dict(description=u"The base stat"))
     effort = Column(Integer, nullable=False,
     stat_id = Column(Integer, ForeignKey('stats.id'), primary_key=True, nullable=False, autoincrement=False,
         info=dict(description=u"ID of the stat"))
     base_stat = Column(Integer, nullable=False,
         info=dict(description=u"The base stat"))
     effort = Column(Integer, nullable=False,
-        info=dict(description=u"The effort increase in this stat gained when this pokémon is defeated"))
+        info=dict(description=u"The effort increase in this stat gained when this Pokémon is defeated"))
 
 class PokemonType(TableBase):
 
 class PokemonType(TableBase):
-    u"""Maps a type to a pokémon. Each pokémon has 1 or 2 types.
+    u"""Maps a type to a Pokémon. Each Pokémon has 1 or 2 types.
     """
     __tablename__ = 'pokemon_types'
     pokemon_id = Column(Integer, ForeignKey('pokemon.id'), primary_key=True, nullable=False, autoincrement=False,
     """
     __tablename__ = 'pokemon_types'
     pokemon_id = Column(Integer, ForeignKey('pokemon.id'), primary_key=True, nullable=False, autoincrement=False,
-        info=dict(description=u"ID of the pokémon"))
+        info=dict(description=u"ID of the Pokémon"))
     type_id = Column(Integer, ForeignKey('types.id'), nullable=False,
         info=dict(description=u"ID of the type"))
     slot = Column(Integer, primary_key=True, nullable=False, autoincrement=False,
     type_id = Column(Integer, ForeignKey('types.id'), nullable=False,
         info=dict(description=u"ID of the type"))
     slot = Column(Integer, primary_key=True, nullable=False, autoincrement=False,
@@ -1166,261 +1511,444 @@ class Region(TableBase):
     u"""Major areas of the world: Kanto, Johto, etc.
     """
     __tablename__ = 'regions'
     u"""Major areas of the world: Kanto, Johto, etc.
     """
     __tablename__ = 'regions'
+    __singlename__ = 'region'
     id = Column(Integer, primary_key=True, nullable=False,
         info=dict(description=u"A numeric ID"))
     id = Column(Integer, primary_key=True, nullable=False,
         info=dict(description=u"A numeric ID"))
-    name = Column(Unicode(16), nullable=False,
-        info=dict(description=u"The English name of the region", official=True, format='plaintext'))
+    identifier = Column(Unicode(16), nullable=False,
+        info=dict(description=u"An identifier", format='identifier'))
+
+create_translation_table('region_names', Region, 'names',
+    relation_lazy='joined',
+    name = Column(Unicode(16), nullable=False, index=True,
+        info=dict(description="The name", format='plaintext', official=True)),
+)
 
 class Stat(TableBase):
     u"""A Stat, such as Attack or Speed
     """
     __tablename__ = 'stats'
 
 class Stat(TableBase):
     u"""A Stat, such as Attack or Speed
     """
     __tablename__ = 'stats'
+    __singlename__ = 'stat'
     id = Column(Integer, primary_key=True, nullable=False,
         info=dict(description=u"A numeric ID"))
     damage_class_id = Column(Integer, ForeignKey('move_damage_classes.id'), nullable=True,
         info=dict(description=u"For offensive and defensive stats, the damage this stat relates to; otherwise None (the NULL value)"))
     id = Column(Integer, primary_key=True, nullable=False,
         info=dict(description=u"A numeric ID"))
     damage_class_id = Column(Integer, ForeignKey('move_damage_classes.id'), nullable=True,
         info=dict(description=u"For offensive and defensive stats, the damage this stat relates to; otherwise None (the NULL value)"))
-    name = Column(Unicode(16), nullable=False,
-        info=dict(description=u"The English name of the stat", official=True, format='plaintext'))
+    identifier = Column(Unicode(16), nullable=False,
+        info=dict(description=u"An identifier", format='identifier'))
+    is_battle_only = Column(Boolean, nullable=False,
+        info=dict(description=u"Whether this stat only exists within a battle"))
+
+create_translation_table('stat_names', Stat, 'names',
+    relation_lazy='joined',
+    name = Column(Unicode(16), nullable=False, index=True,
+        info=dict(description="The name", format='plaintext', official=True)),
+)
+
+class StatHint(TableBase):
+    u"""Flavor text for genes that appears in a Pokémon's summary.  Sometimes
+    called "characteristics".
+    """
+    __tablename__ = 'stat_hints'
+    __singlename__ = 'stat_hint'
+    id = Column(Integer, primary_key=True, nullable=False,
+        info=dict(description=u"A numeric ID"))
+    stat_id = Column(Integer, ForeignKey('stats.id'), nullable=False,
+        info=dict(description=u"ID of the highest stat"))
+    gene_mod_5 = Column(Integer, nullable=False, index=True,
+        info=dict(description=u"Value of the highest stat modulo 5"))
+
+create_translation_table('stat_hint_names', StatHint, 'names',
+    relation_lazy='joined',
+    message = Column(Unicode(24), nullable=False, index=True,
+        info=dict(description=u"The text displayed", official=True, format='plaintext')),
+)
 
 class SuperContestCombo(TableBase):
     u"""Combo of two moves in a Super Contest.
     """
     __tablename__ = 'super_contest_combos'
     first_move_id = Column(Integer, ForeignKey('moves.id'), primary_key=True, nullable=False, autoincrement=False,
 
 class SuperContestCombo(TableBase):
     u"""Combo of two moves in a Super Contest.
     """
     __tablename__ = 'super_contest_combos'
     first_move_id = Column(Integer, ForeignKey('moves.id'), primary_key=True, nullable=False, autoincrement=False,
-        info=dict(description=u"ID of the first move"))
+        info=dict(description=u"The ID of the first move in the combo."))
     second_move_id = Column(Integer, ForeignKey('moves.id'), primary_key=True, nullable=False, autoincrement=False,
     second_move_id = Column(Integer, ForeignKey('moves.id'), primary_key=True, nullable=False, autoincrement=False,
-        info=dict(description=u"ID of the second and last move"))
+        info=dict(description=u"The ID of the second and last move."))
 
 class SuperContestEffect(TableBase):
     u"""An effect a move can have when used in the Super Contest
     """
     __tablename__ = 'super_contest_effects'
 
 class SuperContestEffect(TableBase):
     u"""An effect a move can have when used in the Super Contest
     """
     __tablename__ = 'super_contest_effects'
+    __singlename__ = 'super_contest_effect'
     id = Column(Integer, primary_key=True, nullable=False,
     id = Column(Integer, primary_key=True, nullable=False,
-        info=dict(description=u"A numeric ID"))
+        info=dict(description=u"This effect's unique ID."))
     appeal = Column(SmallInteger, nullable=False,
     appeal = Column(SmallInteger, nullable=False,
-        info=dict(description=u"Number of hearts the user will get when executing a move with this effect"))
+        info=dict(description=u"The number of hearts the user gains."))
+
+create_translation_table('super_contest_effect_prose', SuperContestEffect, 'prose',
     flavor_text = Column(Unicode(64), nullable=False,
     flavor_text = Column(Unicode(64), nullable=False,
-        info=dict(description=u"An English description of the effect", format='plaintext'))
+        info=dict(description=u"A description of the effect.", format='plaintext', official=True)),
+)
+
+class Type(TableBase):
+    u"""Any of the elemental types Pokémon and moves can have."""
+    __tablename__ = 'types'
+    __singlename__ = 'type'
+    id = Column(Integer, primary_key=True, nullable=False,
+        info=dict(description=u"A unique ID for this type."))
+    identifier = Column(Unicode(12), nullable=False,
+        info=dict(description=u"An identifier", format='identifier'))
+    generation_id = Column(Integer, ForeignKey('generations.id'), nullable=False,
+        info=dict(description=u"The ID of the generation this type first appeared in."))
+    damage_class_id = Column(Integer, ForeignKey('move_damage_classes.id'), nullable=True,
+        info=dict(description=u"The ID of the damage class this type's moves had before Generation IV, null if not applicable (e.g. ???)."))
+
+create_translation_table('type_names', Type, 'names',
+    relation_lazy='joined',
+    name = Column(Unicode(12), nullable=False, index=True,
+        info=dict(description="The name", format='plaintext', official=True)),
+)
 
 class TypeEfficacy(TableBase):
 
 class TypeEfficacy(TableBase):
-    u"""The effectiveness of damage of one type against pokémon of another type
+    u"""The damage multiplier used when a move of a particular type damages a
+    Pokémon of a particular other type.
     """
     __tablename__ = 'type_efficacy'
     damage_type_id = Column(Integer, ForeignKey('types.id'), primary_key=True, nullable=False, autoincrement=False,
     """
     __tablename__ = 'type_efficacy'
     damage_type_id = Column(Integer, ForeignKey('types.id'), primary_key=True, nullable=False, autoincrement=False,
-        info=dict(description=u"ID of the damage type; most commonly this is the same as the attack type"))
+        info=dict(description=u"The ID of the damaging type."))
     target_type_id = Column(Integer, ForeignKey('types.id'), primary_key=True, nullable=False, autoincrement=False,
     target_type_id = Column(Integer, ForeignKey('types.id'), primary_key=True, nullable=False, autoincrement=False,
-        info=dict(description=u"ID of the defending pokémon's type"))
+        info=dict(description=u"The ID of the defending Pokémon's type."))
     damage_factor = Column(Integer, nullable=False,
     damage_factor = Column(Integer, nullable=False,
-        info=dict(description=u"The effectiveness, in percent"))
+        info=dict(description=u"The multiplier, as a percentage of damage inflicted."))
 
 
-class Type(TableBase):
-    u"""An elemental type, such as Grass or Steel
-    """
-    __tablename__ = 'types'
-    __singlename__ = 'type'
+class Version(TableBase):
+    u"""An individual main-series Pokémon game."""
+    __tablename__ = 'versions'
+    __singlename__ = 'version'
     id = Column(Integer, primary_key=True, nullable=False,
     id = Column(Integer, primary_key=True, nullable=False,
-        info=dict(description=u"A numeric ID"))
-    name = Column(Unicode(8), nullable=False,
-        info=dict(description=u"The English name.", format='plaintext'))  # XXX: Is this official? The games don't spell "Electric" in full...
-    abbreviation = Column(Unicode(3), nullable=False,
-        info=dict(description=u"An arbitrary 3-letter abbreviation of the type", format='plaintext'))  # XXX: Or is it not arbitrary?
-    generation_id = Column(Integer, ForeignKey('generations.id'), nullable=False,
-        info=dict(description=u"ID of the generation this type first appeared in"))
-    damage_class_id = Column(Integer, ForeignKey('move_damage_classes.id'), nullable=False,
-        info=dict(description=u"ID of the damage class this type's moves had before generation IV, or None for the ??? type"))
-
-class TypeName(TableBase):
-    u"""Non-English name of an elemental type
-    """
-    __tablename__ = 'type_names'
-    type_id = Column(Integer, ForeignKey('types.id'), primary_key=True, nullable=False, autoincrement=False,
-        info=dict(description=u"ID of the type"))
-    language_id = Column(Integer, ForeignKey('languages.id'), primary_key=True, nullable=False, autoincrement=False,
-        info=dict(description=u"ID of the language"))
-    name = Column(Unicode(16), nullable=False,
-        info=dict(description=u"Name of the type in that language", foreign=True, format='plaintext'))
+        info=dict(description=u"A unique ID for this version."))
+    version_group_id = Column(Integer, ForeignKey('version_groups.id'), nullable=False,
+        info=dict(description=u"The ID of the version group this game belongs to."))
+    identifier = Column(Unicode(32), nullable=False,
+        info=dict(description=u'And identifier', format='identifier'))
+
+create_translation_table('version_names', Version, 'names',
+    relation_lazy='joined',
+    name = Column(Unicode(32), nullable=False, index=True,
+        info=dict(description="The name", format='plaintext', official=True)),
+)
 
 class VersionGroup(TableBase):
 
 class VersionGroup(TableBase):
-    u"""A group of related game versions
+    u"""A group of versions, containing either two paired versions (such as Red
+    and Blue) or a single game (such as Yellow.)
     """
     __tablename__ = 'version_groups'
     id = Column(Integer, primary_key=True, nullable=False,
     """
     __tablename__ = 'version_groups'
     id = Column(Integer, primary_key=True, nullable=False,
-        info=dict(description=u"A numeric ID"))
+        info=dict(description=u"This version group's unique ID."))
     generation_id = Column(Integer, ForeignKey('generations.id'), nullable=False,
     generation_id = Column(Integer, ForeignKey('generations.id'), nullable=False,
-        info=dict(description=u"ID of the generation the games of this group belong to"))
+        info=dict(description=u"The ID of the generation the games in this group belong to."))
     pokedex_id = Column(Integer, ForeignKey('pokedexes.id'), nullable=False,
     pokedex_id = Column(Integer, ForeignKey('pokedexes.id'), nullable=False,
-        info=dict(description=u"ID of the regional Pokédex used in this version group."))
+        info=dict(description=u"The ID of the regional Pokédex used in this version group."))
 
 class VersionGroupRegion(TableBase):
 
 class VersionGroupRegion(TableBase):
-    u"""Maps a region to a game version group that features it
-    """
+    u"""Maps a version group to a region that appears in it."""
     __tablename__ = 'version_group_regions'
     version_group_id = Column(Integer, ForeignKey('version_groups.id'), primary_key=True, nullable=False,
     __tablename__ = 'version_group_regions'
     version_group_id = Column(Integer, ForeignKey('version_groups.id'), primary_key=True, nullable=False,
-        info=dict(description=u"ID of the version"))
+        info=dict(description=u"The ID of the version group."))
     region_id = Column(Integer, ForeignKey('regions.id'), primary_key=True, nullable=False,
     region_id = Column(Integer, ForeignKey('regions.id'), primary_key=True, nullable=False,
-        info=dict(description=u"ID of the region"))
+        info=dict(description=u"The ID of the region."))
 
 
-class Version(TableBase):
-    u"""A version of a mainline pokémon game
-    """
-    __tablename__ = 'versions'
-    id = Column(Integer, primary_key=True, nullable=False,
-        info=dict(description=u"A numeric ID"))
-    version_group_id = Column(Integer, ForeignKey('version_groups.id'), nullable=False,
-        info=dict(description=u"ID of the version group this game belongs to"))
-    name = Column(Unicode(32), nullable=False,
-        info=dict(description=u'The English name of the game, without the "Pokémon" prefix', official=True, format='plaintext'))
 
 
+### Relations down here, to avoid dependency ordering problems
 
 
-### Relations down here, to avoid ordering problems
-Ability.flavor_text = relation(AbilityFlavorText, order_by=AbilityFlavorText.version_group_id, backref='ability')
-Ability.foreign_names = relation(AbilityName, backref='ability')
-Ability.generation = relation(Generation, backref='abilities')
-Ability.pokemon = relation(Pokemon,
-    secondary=PokemonAbility.__table__,
-)
+Ability.changelog = relation(AbilityChangelog,
+    order_by=AbilityChangelog.changed_in_version_group_id.desc(),
+    backref=backref('ability', innerjoin=True, lazy='joined'))
+Ability.flavor_text = relation(AbilityFlavorText,
+    order_by=AbilityFlavorText.version_group_id,
+    backref=backref('ability', innerjoin=True, lazy='joined'))
+Ability.generation = relation(Generation,
+    innerjoin=True,
+    backref='abilities')
 
 
-AbilityFlavorText.version_group = relation(VersionGroup)
+AbilityChangelog.changed_in = relation(VersionGroup,
+    innerjoin=True, lazy='joined',
+    backref='ability_changelog')
 
 
-AbilityName.language = relation(Language)
+AbilityFlavorText.version_group = relation(VersionGroup,
+    innerjoin=True)
+AbilityFlavorText.language = relation(Language,
+    innerjoin=True, lazy='joined')
 
 
-Berry.berry_firmness = relation(BerryFirmness, backref='berries')
+
+Berry.berry_firmness = relation(BerryFirmness,
+    innerjoin=True,
+    backref='berries')
 Berry.firmness = association_proxy('berry_firmness', 'name')
 Berry.firmness = association_proxy('berry_firmness', 'name')
-Berry.flavors = relation(BerryFlavor, order_by=BerryFlavor.contest_type_id, backref='berry')
-Berry.natural_gift_type = relation(Type)
+Berry.flavors = relation(BerryFlavor,
+    order_by=BerryFlavor.contest_type_id,
+    backref=backref('berry', innerjoin=True))
+Berry.natural_gift_type = relation(Type, innerjoin=True)
 
 
-BerryFlavor.contest_type = relation(ContestType)
+BerryFlavor.contest_type = relation(ContestType, innerjoin=True)
 
 
-ContestCombo.first = relation(Move, primaryjoin=ContestCombo.first_move_id==Move.id,
-                                    backref='contest_combo_first')
-ContestCombo.second = relation(Move, primaryjoin=ContestCombo.second_move_id==Move.id,
-                                     backref='contest_combo_second')
 
 
-Encounter.location_area = relation(LocationArea, backref='encounters')
-Encounter.pokemon = relation(Pokemon, backref='encounters')
-Encounter.version = relation(Version, backref='encounters')
-Encounter.slot = relation(EncounterSlot, backref='encounters')
+ContestCombo.first = relation(Move,
+    primaryjoin=ContestCombo.first_move_id==Move.id,
+    innerjoin=True, lazy='joined',
+    backref='contest_combo_first')
+ContestCombo.second = relation(Move,
+    primaryjoin=ContestCombo.second_move_id==Move.id,
+    innerjoin=True, lazy='joined',
+    backref='contest_combo_second')
 
 
-EncounterConditionValue.condition = relation(EncounterCondition, backref='values')
 
 
-Encounter.condition_value_map = relation(EncounterConditionValueMap, backref='encounter')
+Encounter.condition_value_map = relation(EncounterConditionValueMap,
+    backref='encounter')
 Encounter.condition_values = association_proxy('condition_value_map', 'condition_value')
 Encounter.condition_values = association_proxy('condition_value_map', 'condition_value')
+Encounter.location_area = relation(LocationArea,
+    innerjoin=True, lazy='joined',
+    backref='encounters')
+Encounter.pokemon = relation(Pokemon,
+    innerjoin=True, lazy='joined',
+    backref='encounters')
+Encounter.version = relation(Version,
+    innerjoin=True, lazy='joined',
+    backref='encounters')
+Encounter.slot = relation(EncounterSlot,
+    innerjoin=True, lazy='joined',
+    backref='encounters')
+
+EncounterConditionValue.condition = relation(EncounterCondition,
+    innerjoin=True, lazy='joined',
+    backref='values')
 EncounterConditionValueMap.condition_value = relation(EncounterConditionValue,
 EncounterConditionValueMap.condition_value = relation(EncounterConditionValue,
-                                                      backref='encounter_map')
+    innerjoin=True, lazy='joined',
+    backref='encounter_map')
+
+EncounterSlot.method = relation(EncounterMethod,
+    innerjoin=True, lazy='joined',
+    backref='slots')
+EncounterSlot.version_group = relation(VersionGroup, innerjoin=True)
+
 
 
-EncounterSlot.terrain = relation(EncounterTerrain, backref='slots')
+EvolutionChain.growth_rate = relation(GrowthRate,
+    innerjoin=True,
+    backref='evolution_chains')
+EvolutionChain.baby_trigger_item = relation(Item,
+    backref='evolution_chains')
 
 
-EncounterSlot.condition_map = relation(EncounterSlotCondition, backref='slot')
-EncounterSlot.conditions = association_proxy('condition_map', 'condition')
-EncounterSlotCondition.condition = relation(EncounterCondition,
-                                            backref='slot_map')
 
 
-EvolutionChain.growth_rate = relation(GrowthRate, backref='evolution_chains')
-EvolutionChain.baby_trigger_item = relation(Item, backref='evolution_chains')
+Experience.growth_rate = relation(GrowthRate,
+    innerjoin=True, lazy='joined',
+    backref='experience_table')
 
 
-Experience.growth_rate = relation(GrowthRate, backref='experience_table')
 
 
-Generation.canonical_pokedex = relation(Pokedex, backref='canonical_for_generation')
-Generation.versions = relation(Version, secondary=VersionGroup.__table__)
-Generation.main_region = relation(Region)
+Generation.canonical_pokedex = relation(Pokedex,
+    backref='canonical_for_generation')
+Generation.versions = relation(Version,
+    secondary=VersionGroup.__table__,
+    innerjoin=True)
+Generation.main_region = relation(Region, innerjoin=True)
 
 
-GrowthRate.max_experience_obj = relation(Experience, primaryjoin=and_(Experience.growth_rate_id == GrowthRate.id, Experience.level == 100), uselist=False)
+
+GrowthRate.max_experience_obj = relation(Experience,
+    primaryjoin=and_(
+        Experience.growth_rate_id == GrowthRate.id,
+        Experience.level == 100),
+    uselist=False, innerjoin=True)
 GrowthRate.max_experience = association_proxy('max_experience_obj', 'experience')
 
 GrowthRate.max_experience = association_proxy('max_experience_obj', 'experience')
 
-Item.berry = relation(Berry, uselist=False, backref='item')
-Item.flags = relation(ItemFlag, secondary=ItemFlagMap.__table__)
-Item.flavor_text = relation(ItemFlavorText, order_by=ItemFlavorText.version_group_id.asc(), backref='item')
-Item.fling_effect = relation(ItemFlingEffect, backref='items')
-Item.foreign_names = relation(ItemName, backref='item')
-Item.machines = relation(Machine, order_by=Machine.version_group_id.asc())
-Item.category = relation(ItemCategory)
+
+Item.berry = relation(Berry,
+    uselist=False,
+    backref='item')
+Item.flags = relation(ItemFlag,
+    secondary=ItemFlagMap.__table__)
+Item.flavor_text = relation(ItemFlavorText,
+    order_by=ItemFlavorText.version_group_id.asc(),
+    backref=backref('item', innerjoin=True, lazy='joined'))
+Item.fling_effect = relation(ItemFlingEffect,
+    backref='items')
+Item.machines = relation(Machine,
+    order_by=Machine.version_group_id.asc())
+Item.category = relation(ItemCategory,
+    innerjoin=True,
+    backref=backref('items', order_by=Item.identifier.asc()))
 Item.pocket = association_proxy('category', 'pocket')
 
 Item.pocket = association_proxy('category', 'pocket')
 
-ItemCategory.items = relation(Item, order_by=Item.name)
-ItemCategory.pocket = relation(ItemPocket)
+ItemCategory.pocket = relation(ItemPocket, innerjoin=True)
+
+ItemFlavorText.version_group = relation(VersionGroup,
+    innerjoin=True, lazy='joined')
+ItemFlavorText.language = relation(Language,
+    innerjoin=True, lazy='joined')
+
+ItemGameIndex.item = relation(Item,
+    innerjoin=True, lazy='joined',
+    backref='game_indices')
+ItemGameIndex.generation = relation(Generation,
+    innerjoin=True, lazy='joined')
 
 
-ItemFlavorText.version_group = relation(VersionGroup)
+ItemPocket.categories = relation(ItemCategory,
+    innerjoin=True,
+    order_by=ItemCategory.identifier.asc())
 
 
-ItemInternalID.item = relation(Item, backref='internal_ids')
-ItemInternalID.generation = relation(Generation)
 
 
-ItemName.language = relation(Language)
+Location.region = relation(Region,
+    innerjoin=True,
+    backref='locations')
 
 
-ItemPocket.categories = relation(ItemCategory, order_by=ItemCategory.name)
+LocationArea.location = relation(Location,
+    innerjoin=True, lazy='joined',
+    backref='areas')
 
 
-Location.region = relation(Region, backref='locations')
+LocationAreaEncounterRate.location_area = relation(LocationArea,
+    innerjoin=True,
+    backref='encounter_rates')
+LocationAreaEncounterRate.method = relation(EncounterMethod,
+    innerjoin=True)
 
 
-LocationArea.location = relation(Location, backref='areas')
+LocationGameIndex.location = relation(Location,
+    innerjoin=True, lazy='joined',
+    backref='game_indices')
+LocationGameIndex.generation = relation(Generation,
+    innerjoin=True, lazy='joined')
 
 
-LocationInternalID.location = relation(Location, backref='internal_ids')
-LocationInternalID.generation = relation(Generation)
 
 Machine.item = relation(Item)
 
 Machine.item = relation(Item)
-Machine.version_group = relation(VersionGroup)
+Machine.version_group = relation(VersionGroup,
+    innerjoin=True, lazy='joined')
+
 
 Move.changelog = relation(MoveChangelog,
 
 Move.changelog = relation(MoveChangelog,
-    order_by=MoveChangelog.changed_in_version_group_id.asc(),
-    backref='move',
-)
-Move.contest_effect = relation(ContestEffect, backref='moves')
+    order_by=MoveChangelog.changed_in_version_group_id.desc(),
+    backref=backref('move', innerjoin=True, lazy='joined'))
+Move.contest_effect = relation(ContestEffect,
+    backref='moves')
 Move.contest_combo_next = association_proxy('contest_combo_first', 'second')
 Move.contest_combo_prev = association_proxy('contest_combo_second', 'first')
 Move.contest_combo_next = association_proxy('contest_combo_first', 'second')
 Move.contest_combo_prev = association_proxy('contest_combo_second', 'first')
-Move.contest_type = relation(ContestType, backref='moves')
-Move.damage_class = relation(MoveDamageClass, backref='moves')
+Move.contest_type = relation(ContestType,
+    backref='moves')
+Move.damage_class = relation(MoveDamageClass,
+    innerjoin=True,
+    backref='moves')
 Move.flags = association_proxy('move_flags', 'flag')
 Move.flags = association_proxy('move_flags', 'flag')
-Move.flavor_text = relation(MoveFlavorText, order_by=MoveFlavorText.version_group_id, backref='move')
-Move.foreign_names = relation(MoveName, backref='move')
-Move.generation = relation(Generation, backref='moves')
-Move.machines = relation(Machine, backref='move')
-Move.move_effect = relation(MoveEffect, backref='moves')
-Move.move_flags = relation(MoveFlag, backref='move')
-Move.super_contest_effect = relation(SuperContestEffect, backref='moves')
+Move.flavor_text = relation(MoveFlavorText,
+    order_by=MoveFlavorText.version_group_id, backref='move')
+Move.generation = relation(Generation,
+    innerjoin=True,
+    backref='moves')
+Move.machines = relation(Machine,
+    backref='move')
+Move.meta = relation(MoveMeta,
+    uselist=False, innerjoin=True,
+    backref='move')
+Move.meta_stat_changes = relation(MoveMetaStatChange)
+Move.move_effect = relation(MoveEffect,
+    innerjoin=True,
+    backref='moves')
+Move.move_flags = relation(MoveFlag,
+    backref='move')
+Move.super_contest_effect = relation(SuperContestEffect,
+    backref='moves')
 Move.super_contest_combo_next = association_proxy('super_contest_combo_first', 'second')
 Move.super_contest_combo_prev = association_proxy('super_contest_combo_second', 'first')
 Move.super_contest_combo_next = association_proxy('super_contest_combo_first', 'second')
 Move.super_contest_combo_prev = association_proxy('super_contest_combo_second', 'first')
-Move.target = relation(MoveTarget, backref='moves')
-Move.type = relation(Type, backref='moves')
+Move.target = relation(MoveTarget,
+    innerjoin=True,
+    backref='moves')
+Move.type = relation(Type,
+    innerjoin=True,
+    backref='moves')
 
 Move.effect = markdown.MoveEffectProperty('effect')
 
 Move.effect = markdown.MoveEffectProperty('effect')
+Move.effect_map = markdown.MoveEffectPropertyMap('effect_map')
 Move.short_effect = markdown.MoveEffectProperty('short_effect')
 Move.short_effect = markdown.MoveEffectProperty('short_effect')
-
-MoveChangelog.changed_in = relation(VersionGroup, backref='move_changelog')
-
-MoveEffect.category_map = relation(MoveEffectCategoryMap)
-MoveEffect.categories = association_proxy('category_map', 'category')
-MoveEffectCategoryMap.category = relation(MoveEffectCategory)
-
-MoveFlag.flag = relation(MoveFlagType)
-
-MoveFlavorText.version_group = relation(VersionGroup)
-
-MoveName.language = relation(Language)
-
-Nature.foreign_names = relation(NatureName, backref='nature')
-Nature.decreased_stat = relation(Stat, primaryjoin=Nature.decreased_stat_id==Stat.id,
-                                       backref='decreasing_natures')
-Nature.increased_stat = relation(Stat, primaryjoin=Nature.increased_stat_id==Stat.id,
-                                       backref='increasing_natures')
-Nature.hates_flavor = relation(ContestType, primaryjoin=Nature.hates_flavor_id==ContestType.id,
-                                       backref='hating_natures')
-Nature.likes_flavor = relation(ContestType, primaryjoin=Nature.likes_flavor_id==ContestType.id,
-                                       backref='liking_natures')
+Move.short_effect_map = markdown.MoveEffectPropertyMap('short_effect_map')
+
+MoveChangelog.changed_in = relation(VersionGroup,
+    innerjoin=True, lazy='joined',
+    backref='move_changelog')
+MoveChangelog.move_effect = relation(MoveEffect,
+    backref='move_changelog')
+MoveChangelog.type = relation(Type,
+    backref='move_changelog')
+
+MoveChangelog.effect = markdown.MoveEffectProperty('effect')
+MoveChangelog.effect_map = markdown.MoveEffectPropertyMap('effect_map')
+MoveChangelog.short_effect = markdown.MoveEffectProperty('short_effect')
+MoveChangelog.short_effect_map = markdown.MoveEffectPropertyMap('short_effect_map')
+
+MoveEffect.changelog = relation(MoveEffectChangelog,
+    order_by=MoveEffectChangelog.changed_in_version_group_id.desc(),
+    backref='move_effect')
+
+MoveEffectChangelog.changed_in = relation(VersionGroup,
+    innerjoin=True, lazy='joined',
+    backref='move_effect_changelog')
+
+MoveFlag.flag = relation(MoveFlagType, innerjoin=True, lazy='joined')
+
+MoveFlavorText.version_group = relation(VersionGroup,
+    innerjoin=True, lazy='joined')
+MoveFlavorText.language = relation(Language,
+    innerjoin=True, lazy='joined')
+
+MoveMeta.category = relation(MoveMetaCategory,
+    innerjoin=True, lazy='joined',
+    backref='move_meta')
+MoveMeta.ailment = relation(MoveMetaAilment,
+    innerjoin=True, lazy='joined',
+    backref='move_meta')
+
+MoveMetaStatChange.stat = relation(Stat,
+    innerjoin=True, lazy='joined',
+    backref='move_meta_stat_changes')
+
+
+Nature.decreased_stat = relation(Stat,
+    primaryjoin=Nature.decreased_stat_id==Stat.id,
+    innerjoin=True,
+    backref='decreasing_natures')
+Nature.increased_stat = relation(Stat,
+    primaryjoin=Nature.increased_stat_id==Stat.id,
+    innerjoin=True,
+    backref='increasing_natures')
+Nature.hates_flavor = relation(ContestType,
+    primaryjoin=Nature.hates_flavor_id==ContestType.id,
+    innerjoin=True,
+    backref='hating_natures')
+Nature.likes_flavor = relation(ContestType,
+    primaryjoin=Nature.likes_flavor_id==ContestType.id,
+    innerjoin=True,
+    backref='liking_natures')
 Nature.battle_style_preferences = relation(NatureBattleStylePreference,
 Nature.battle_style_preferences = relation(NatureBattleStylePreference,
-                                           order_by=NatureBattleStylePreference.move_battle_style_id,
-                                           backref='nature')
-Nature.pokeathlon_effects = relation(NaturePokeathlonStat, order_by=NaturePokeathlonStat.pokeathlon_stat_id)
+    order_by=NatureBattleStylePreference.move_battle_style_id.asc(),
+    backref='nature')
+Nature.pokeathlon_effects = relation(NaturePokeathlonStat,
+    order_by=NaturePokeathlonStat.pokeathlon_stat_id.asc())
+
+NatureBattleStylePreference.battle_style = relation(MoveBattleStyle,
+    innerjoin=True, lazy='joined',
+    backref='nature_preferences')
 
 
-NatureBattleStylePreference.battle_style = relation(MoveBattleStyle, backref='nature_preferences')
+NaturePokeathlonStat.pokeathlon_stat = relation(PokeathlonStat,
+    innerjoin=True, lazy='joined',
+    backref='nature_effects')
 
 
-NatureName.language = relation(Language)
 
 
-NaturePokeathlonStat.pokeathlon_stat = relation(PokeathlonStat, backref='nature_effects')
+Pokedex.region = relation(Region,
+    innerjoin=True,
+    backref='pokedexes')
+Pokedex.version_groups = relation(VersionGroup,
+    innerjoin=True,
+    order_by=VersionGroup.id.asc(),
+    backref='pokedex')
 
 
-Pokedex.region = relation(Region, backref='pokedexes')
-Pokedex.version_groups = relation(VersionGroup, order_by=VersionGroup.id, back_populates='pokedex')
 
 Pokemon.all_abilities = relation(Ability,
     secondary=PokemonAbility.__table__,
 
 Pokemon.all_abilities = relation(Ability,
     secondary=PokemonAbility.__table__,
-    order_by=PokemonAbility.slot,
+    order_by=PokemonAbility.slot.asc(),
+    innerjoin=True,
+    backref=backref('all_pokemon',
+        order_by=Pokemon.order.asc(),
+    ),
 )
 Pokemon.abilities = relation(Ability,
     secondary=PokemonAbility.__table__,
 )
 Pokemon.abilities = relation(Ability,
     secondary=PokemonAbility.__table__,
@@ -1428,7 +1956,11 @@ Pokemon.abilities = relation(Ability,
         Pokemon.id == PokemonAbility.pokemon_id,
         PokemonAbility.is_dream == False,
     ),
         Pokemon.id == PokemonAbility.pokemon_id,
         PokemonAbility.is_dream == False,
     ),
-    order_by=PokemonAbility.slot,
+    innerjoin=True,
+    order_by=PokemonAbility.slot.asc(),
+    backref=backref('pokemon',
+        order_by=Pokemon.order.asc(),
+    ),
 )
 Pokemon.dream_ability = relation(Ability,
     secondary=PokemonAbility.__table__,
 )
 Pokemon.dream_ability = relation(Ability,
     secondary=PokemonAbility.__table__,
@@ -1437,135 +1969,186 @@ Pokemon.dream_ability = relation(Ability,
         PokemonAbility.is_dream == True,
     ),
     uselist=False,
         PokemonAbility.is_dream == True,
     ),
     uselist=False,
+    backref=backref('dream_pokemon',
+        order_by=Pokemon.order,
+    ),
 )
 )
-Pokemon.pokemon_color = relation(PokemonColor, backref='pokemon')
+Pokemon.pokemon_color = relation(PokemonColor,
+    innerjoin=True,
+    backref='pokemon')
 Pokemon.color = association_proxy('pokemon_color', 'name')
 Pokemon.color = association_proxy('pokemon_color', 'name')
-Pokemon.dex_numbers = relation(PokemonDexNumber, order_by=PokemonDexNumber.pokedex_id.asc(), backref='pokemon')
-Pokemon.egg_groups = relation(EggGroup, secondary=PokemonEggGroup.__table__,
-                                        order_by=PokemonEggGroup.egg_group_id,
-                                        backref='pokemon')
-Pokemon.evolution_chain = relation(EvolutionChain, backref='pokemon')
-Pokemon.child_pokemon = relation(Pokemon,
-    primaryjoin=Pokemon.id==PokemonEvolution.from_pokemon_id,
-    secondary=PokemonEvolution.__table__,
-    secondaryjoin=PokemonEvolution.to_pokemon_id==Pokemon.id,
-    backref=backref('parent_pokemon', uselist=False),
-)
-Pokemon.flavor_text = relation(PokemonFlavorText, order_by=PokemonFlavorText.version_id.asc(), backref='pokemon')
-Pokemon.foreign_names = relation(PokemonName, backref='pokemon')
-Pokemon.forms = relation(PokemonForm, primaryjoin=Pokemon.id==PokemonForm.form_base_pokemon_id,
-                         order_by=(PokemonForm.order.asc(), PokemonForm.name.asc()))
+Pokemon.dex_numbers = relation(PokemonDexNumber,
+    innerjoin=True,
+    order_by=PokemonDexNumber.pokedex_id.asc(),
+    backref='pokemon')
+Pokemon.egg_groups = relation(EggGroup,
+    secondary=PokemonEggGroup.__table__,
+    innerjoin=True,
+    order_by=PokemonEggGroup.egg_group_id.asc(),
+    backref=backref('pokemon', order_by=Pokemon.order.asc()))
+Pokemon.evolution_chain = relation(EvolutionChain,
+    innerjoin=True,
+    backref=backref('pokemon', order_by=Pokemon.order.asc()))
+Pokemon.parent_pokemon = relation(Pokemon,
+    primaryjoin=Pokemon.evolves_from_pokemon_id==Pokemon.id,
+    remote_side=[Pokemon.id],
+    backref='child_pokemon')
+Pokemon.evolutions = relation(PokemonEvolution,
+    primaryjoin=Pokemon.id==PokemonEvolution.evolved_pokemon_id,
+    backref=backref('evolved_pokemon', innerjoin=True, lazy='joined'))
+Pokemon.flavor_text = relation(PokemonFlavorText,
+    order_by=PokemonFlavorText.version_id.asc(),
+    backref='pokemon')
+Pokemon.forms = relation(PokemonForm,
+    primaryjoin=Pokemon.id==PokemonForm.form_base_pokemon_id,
+    order_by=(PokemonForm.order.asc(), PokemonForm.identifier.asc()))
 Pokemon.default_form = relation(PokemonForm,
 Pokemon.default_form = relation(PokemonForm,
-    primaryjoin=and_(Pokemon.id==PokemonForm.form_base_pokemon_id, PokemonForm.is_default==True),
-    uselist=False,
-)
-Pokemon.pokemon_habitat = relation(PokemonHabitat, backref='pokemon')
+    primaryjoin=and_(
+        Pokemon.id==PokemonForm.form_base_pokemon_id,
+        PokemonForm.is_default==True),
+    uselist=False)
+Pokemon.pokemon_habitat = relation(PokemonHabitat,
+    backref='pokemon')
 Pokemon.habitat = association_proxy('pokemon_habitat', 'name')
 Pokemon.habitat = association_proxy('pokemon_habitat', 'name')
-Pokemon.items = relation(PokemonItem, backref='pokemon')
-Pokemon.generation = relation(Generation, backref='pokemon')
-Pokemon.shape = relation(PokemonShape, backref='pokemon')
-Pokemon.stats = relation(PokemonStat, backref='pokemon', order_by=PokemonStat.stat_id.asc())
-Pokemon.types = relation(Type, secondary=PokemonType.__table__, order_by=PokemonType.slot.asc(), backref='pokemon')
-
-PokemonDexNumber.pokedex = relation(Pokedex)
-
-PokemonEvolution.from_pokemon = relation(Pokemon,
-    primaryjoin=PokemonEvolution.from_pokemon_id==Pokemon.id,
-    backref='child_evolutions',
-)
-PokemonEvolution.to_pokemon = relation(Pokemon,
-    primaryjoin=PokemonEvolution.to_pokemon_id==Pokemon.id,
-    backref=backref('parent_evolution', uselist=False),
-)
-PokemonEvolution.child_evolutions = relation(PokemonEvolution,
-    primaryjoin=PokemonEvolution.from_pokemon_id==PokemonEvolution.to_pokemon_id,
-    foreign_keys=[PokemonEvolution.to_pokemon_id],
-    backref=backref('parent_evolution',
-        remote_side=[PokemonEvolution.from_pokemon_id],
-        uselist=False,
-    ),
-)
-PokemonEvolution.trigger = relation(EvolutionTrigger, backref='evolutions')
+Pokemon.items = relation(PokemonItem,
+    backref='pokemon')
+Pokemon.generation = relation(Generation,
+    innerjoin=True,
+    backref='pokemon')
+Pokemon.shape = relation(PokemonShape,
+    innerjoin=True,
+    backref='pokemon')
+Pokemon.stats = relation(PokemonStat,
+    innerjoin=True,
+    order_by=PokemonStat.stat_id.asc(),
+    backref='pokemon')
+Pokemon.types = relation(Type,
+    secondary=PokemonType.__table__,
+    innerjoin=True,
+    order_by=PokemonType.slot.asc(),
+    backref=backref('pokemon', order_by=Pokemon.order))
+
+PokemonDexNumber.pokedex = relation(Pokedex,
+    innerjoin=True, lazy='joined')
+
+PokemonEvolution.trigger = relation(EvolutionTrigger,
+    innerjoin=True, lazy='joined',
+    backref='evolutions')
 PokemonEvolution.trigger_item = relation(Item,
     primaryjoin=PokemonEvolution.trigger_item_id==Item.id,
 PokemonEvolution.trigger_item = relation(Item,
     primaryjoin=PokemonEvolution.trigger_item_id==Item.id,
-    backref='triggered_evolutions',
-)
+    backref='triggered_evolutions')
 PokemonEvolution.held_item = relation(Item,
     primaryjoin=PokemonEvolution.held_item_id==Item.id,
 PokemonEvolution.held_item = relation(Item,
     primaryjoin=PokemonEvolution.held_item_id==Item.id,
-    backref='required_for_evolutions',
-)
-PokemonEvolution.location = relation(Location, backref='triggered_evolutions')
-PokemonEvolution.known_move = relation(Move, backref='triggered_evolutions')
+    backref='required_for_evolutions')
+PokemonEvolution.location = relation(Location,
+    backref='triggered_evolutions')
+PokemonEvolution.known_move = relation(Move,
+    backref='triggered_evolutions')
 PokemonEvolution.party_pokemon = relation(Pokemon,
     primaryjoin=PokemonEvolution.party_pokemon_id==Pokemon.id,
 PokemonEvolution.party_pokemon = relation(Pokemon,
     primaryjoin=PokemonEvolution.party_pokemon_id==Pokemon.id,
-    backref='triggered_evolutions',
-)
-
-PokemonFlavorText.version = relation(Version)
-
-PokemonForm.form_base_pokemon = relation(Pokemon, primaryjoin=PokemonForm.form_base_pokemon_id==Pokemon.id)
-PokemonForm.unique_pokemon = relation(Pokemon, backref=backref('unique_form', uselist=False),
-                                      primaryjoin=PokemonForm.unique_pokemon_id==Pokemon.id)
-PokemonForm.version_group = relation(VersionGroup)
+    backref='triggered_evolutions')
+PokemonEvolution.trade_pokemon = relation(Pokemon,
+    primaryjoin=PokemonEvolution.trade_pokemon_id==Pokemon.id)
+
+PokemonFlavorText.version = relation(Version, innerjoin=True, lazy='joined')
+PokemonFlavorText.language = relation(Language, innerjoin=True, lazy='joined')
+
+PokemonForm.form_base_pokemon = relation(Pokemon,
+    primaryjoin=PokemonForm.form_base_pokemon_id==Pokemon.id,
+    innerjoin=True)
+PokemonForm.unique_pokemon = relation(Pokemon,
+    primaryjoin=PokemonForm.unique_pokemon_id==Pokemon.id,
+    backref=backref('unique_form', uselist=False))
+PokemonForm.version_group = relation(VersionGroup,
+    innerjoin=True)
 PokemonForm.form_group = association_proxy('form_base_pokemon', 'form_group')
 PokemonForm.pokeathlon_stats = relation(PokemonFormPokeathlonStat,
 PokemonForm.form_group = association_proxy('form_base_pokemon', 'form_group')
 PokemonForm.pokeathlon_stats = relation(PokemonFormPokeathlonStat,
-                                        order_by=PokemonFormPokeathlonStat.pokeathlon_stat_id,
-                                        backref='pokemon_form')
+    order_by=PokemonFormPokeathlonStat.pokeathlon_stat_id,
+    backref='pokemon_form')
+
+PokemonFormGroup.pokemon = relation(Pokemon,
+    innerjoin=True,
+    backref=backref('form_group', uselist=False))
+
+PokemonFormPokeathlonStat.pokeathlon_stat = relation(PokeathlonStat,
+    innerjoin=True, lazy='joined')
+
+PokemonItem.item = relation(Item,
+    innerjoin=True, lazy='joined',
+    backref='pokemon')
+PokemonItem.version = relation(Version,
+    innerjoin=True, lazy='joined')
+
+PokemonMove.pokemon = relation(Pokemon,
+    innerjoin=True, lazy='joined',
+    backref='pokemon_moves')
+PokemonMove.version_group = relation(VersionGroup,
+    innerjoin=True, lazy='joined')
+PokemonMove.machine = relation(Machine,
+    primaryjoin=and_(
+        Machine.version_group_id==PokemonMove.version_group_id,
+        Machine.move_id==PokemonMove.move_id),
+    foreign_keys=[Machine.version_group_id, Machine.move_id],
+    uselist=False,
+    backref='pokemon_moves')
+PokemonMove.move = relation(Move,
+    innerjoin=True, lazy='joined',
+    backref='pokemon_moves')
+PokemonMove.method = relation(PokemonMoveMethod,
+    innerjoin=True, lazy='joined')
 
 
-PokemonFormGroup.pokemon = relation(Pokemon, backref=backref('form_group',
-                                                             uselist=False))
+PokemonStat.stat = relation(Stat,
+    innerjoin=True, lazy='joined')
 
 
-PokemonFormPokeathlonStat.pokeathlon_stat = relation(PokeathlonStat)
 
 
-PokemonItem.item = relation(Item, backref='pokemon')
-PokemonItem.version = relation(Version)
+Region.generation = relation(Generation, uselist=False)
+Region.version_group_regions = relation(VersionGroupRegion,
+    order_by=VersionGroupRegion.version_group_id.asc(),
+    backref='region')
+Region.version_groups = association_proxy('version_group_regions', 'version_group')
 
 
-PokemonMove.pokemon = relation(Pokemon, backref='pokemon_moves')
-PokemonMove.version_group = relation(VersionGroup)
-PokemonMove.machine = relation(Machine, backref='pokemon_moves',
-                               primaryjoin=and_(Machine.version_group_id==PokemonMove.version_group_id,
-                                                Machine.move_id==PokemonMove.move_id),
-                                foreign_keys=[Machine.version_group_id, Machine.move_id],
-                                uselist=False)
-PokemonMove.move = relation(Move, backref='pokemon_moves')
-PokemonMove.method = relation(PokemonMoveMethod)
 
 
-PokemonName.language = relation(Language)
+Stat.damage_class = relation(MoveDamageClass,
+    backref='stats')
 
 
-PokemonStat.stat = relation(Stat)
+StatHint.stat = relation(Stat,
+    innerjoin=True,
+    backref='hints')
 
 
-# This is technically a has-many; Generation.main_region_id -> Region.id
-Region.generation = relation(Generation, uselist=False)
-Region.version_group_regions = relation(VersionGroupRegion, backref='region',
-                                        order_by='VersionGroupRegion.version_group_id')
-Region.version_groups = association_proxy('version_group_regions', 'version_group')
 
 
-Stat.damage_class = relation(MoveDamageClass, backref='stats')
+SuperContestCombo.first = relation(Move,
+    primaryjoin=SuperContestCombo.first_move_id==Move.id,
+    innerjoin=True, lazy='joined',
+    backref='super_contest_combo_first')
+SuperContestCombo.second = relation(Move,
+    primaryjoin=SuperContestCombo.second_move_id==Move.id,
+    innerjoin=True, lazy='joined',
+    backref='super_contest_combo_second')
 
 
-SuperContestCombo.first = relation(Move, primaryjoin=SuperContestCombo.first_move_id==Move.id,
-                                        backref='super_contest_combo_first')
-SuperContestCombo.second = relation(Move, primaryjoin=SuperContestCombo.second_move_id==Move.id,
-                                         backref='super_contest_combo_second')
 
 Type.damage_efficacies = relation(TypeEfficacy,
 
 Type.damage_efficacies = relation(TypeEfficacy,
-                                  primaryjoin=Type.id
-                                      ==TypeEfficacy.damage_type_id,
-                                  backref='damage_type')
+    primaryjoin=Type.id==TypeEfficacy.damage_type_id,
+    backref=backref('damage_type', innerjoin=True, lazy='joined'))
 Type.target_efficacies = relation(TypeEfficacy,
 Type.target_efficacies = relation(TypeEfficacy,
-                                  primaryjoin=Type.id
-                                      ==TypeEfficacy.target_type_id,
-                                  backref='target_type')
+    primaryjoin=Type.id==TypeEfficacy.target_type_id,
+    backref=backref('target_type', innerjoin=True, lazy='joined'))
 
 
-Type.generation = relation(Generation, backref='types')
-Type.damage_class = relation(MoveDamageClass, backref='types')
-Type.foreign_names = relation(TypeName, backref='type')
+Type.generation = relation(Generation,
+    innerjoin=True,
+    backref='types')
+Type.damage_class = relation(MoveDamageClass,
+    backref='types')
 
 
-TypeName.language = relation(Language)
 
 
-Version.version_group = relation(VersionGroup, backref='versions')
 Version.generation = association_proxy('version_group', 'generation')
 
 Version.generation = association_proxy('version_group', 'generation')
 
-VersionGroup.generation = relation(Generation, backref='version_groups')
-VersionGroup.version_group_regions = relation(VersionGroupRegion, backref='version_group')
+VersionGroup.versions = relation(Version,
+    innerjoin=True,
+    order_by=Version.id,
+    backref=backref('version_group', lazy='joined'))
+VersionGroup.generation = relation(Generation,
+    innerjoin=True, lazy='joined',
+    backref='version_groups')
+VersionGroup.version_group_regions = relation(VersionGroupRegion,
+    backref='version_group')
 VersionGroup.regions = association_proxy('version_group_regions', 'region')
 VersionGroup.regions = association_proxy('version_group_regions', 'region')
-VersionGroup.pokedex = relation(Pokedex, back_populates='version_groups')