public function isValid( &$messages = array() )
    {
        $isValid = true;
        // missing identifier
        if ( !isset( $this->Identifier ) || $this->Identifier == '' )
        {
            $messages[] = ezpI18n::tr( 'kernel/state/edit', 'Identifier: input required' );
            $isValid = false;
        }
        else
        {
            // make sure the identifier contains only valid characters
            $trans = eZCharTransform::instance();
            $validIdentifier = $trans->transformByGroup( $this->Identifier, 'identifier' );
            if ( strcmp( $validIdentifier, $this->Identifier ) != 0 )
            {
                // invalid identifier
                $messages[] = ezpI18n::tr( 'kernel/state/edit', 'Identifier: invalid, it can only consist of characters in the range a-z, 0-9 and underscore.' );
                $isValid = false;
            }
            else if ( strlen( $this->Identifier ) > self::MAX_IDENTIFIER_LENGTH )
            {
                $messages[] = ezpI18n::tr( 'kernel/state/edit', 'Identifier: invalid, maximum %max characters allowed.',
                                      null, array( '%max' => self::MAX_IDENTIFIER_LENGTH ) );
                $isValid = false;
            }
            else if ( isset( $this->GroupID ) )
            {
                // check for existing identifier
                $existingState = self::fetchByIdentifier( $this->Identifier, $this->GroupID );
                if ( $existingState && ( !isset( $this->ID ) || $existingState->attribute( 'id' ) !== $this->ID ) )
                {
                    $messages[] = ezpI18n::tr( 'kernel/state/edit', 'Identifier: a content object state group with this identifier already exists, please give another identifier' );
                    $isValid = false;
                }
            }
        }

        $translationsWithData = 0;
        foreach ( $this->AllTranslations as $translation )
        {
            if ( $translation->hasData() )
            {
                $translationsWithData++;
                if ( !$translation->isValid( $messages ) )
                {
                    $isValid = false;
                }
            }
            else if ( ( $translation->attribute( 'language_id' ) & ~1 ) == $this->DefaultLanguageID )
            {
                // if name nor description are set but translation is specified as main language
                $isValid = false;
                $messages[] = ezpI18n::tr( 'kernel/state/edit', '%language_name: this language is the default but neither name or description were provided for this language', null, array( '%language_name' => $translation->attribute( 'language' )->attribute( 'locale_object' )->attribute( 'intl_language_name' ) ) );
            }
        }

        if ( $translationsWithData == 0 )
        {
            $isValid = false;
            $messages[] =  ezpI18n::tr( 'kernel/state/edit', 'Translations: you need to add at least one localization' );
        }
        else if ( empty( $this->DefaultLanguageID ) && $translationsWithData > 1 )
        {
            $isValid = false;
            $messages[] =  ezpI18n::tr( 'kernel/state/edit', 'Translations: there are multiple localizations but you did not specify which is the default one' );
        }

        return $isValid;
    }