Adding multilang fields

From EPrints Documentation
Jump to: navigation, search

EPrints' builtin fields are not multilingual, in the sense that there is only one version of each field. This page explains how to add multilingual versions of existing fields in EPrints and how they can be integrated with it.

Multilang fields and EPrints

EPrints supports the multilang field type (see Multilang for more details) which allows a user to insert different content for different languages. There are a few limitations with multilang fields though:

  • When its value is printed it is shown in all languages which contain content.
  • If a basic EPrints field's type is replaced to become multilang (like title and abstract), EPrints functionality breaks because it expects one output.

So this article explains how we can replace a basic field, the title field, with a multilingual one.

How to add a custom, multilingual field

Replacing an EPrints basic field, like the title field, involves a few steps. First, a new field needs to be created that will be able to store information for different languages; this field will be of type multilang. Next, the basic field's type needs to be replaced with one that is able to use our newly created field as its storage place. This field type will use a function wrapper for storing and retrieving information from the multilang field, hence the title field will become a calculated field.

So, in order to add multilingual support for the title field, the following actions need to take place:

  • A new field type needs to be created that will help our title field to implement some of EPrints logic. Our field type is called virtualwithvalue.
  • A new multilang field needs to be created that will store our multilingual information. We will call this field ml_title.
  • ml_title field and title field's functionality need to be introduced to the EPrints system via a configuration file located in ~eprints/archives/<reponame>/cfg/cfg.d/.
  • EPrints' database needs to be updated to include the new field. This is achieved when user eprints executes (from his home directory):
$ ./bin/epadmin update reponame
  • The appropriate phrases need to be added for the ml_title field on each supported language.
  • The title field needs to be replaced with ml_title field in the workflow.
  • ml_title field in the workflow will contain a custom lookup script.
  • The title field needs to be replaced with ml_title field in the simple and advanced search scripts.
  • The repository needs to be reloaded, by running:
$ ./bin/epadmin reload reponame

within eprints user's home directory.

The following sections explain each step in detail, where we are using ml_title as our example field. The code snippets are just for demonstration purposes (and proof of concept). If you want to see the final, working implementation, you should look at the source code of the plugin.

Adding a new field type (VirtualWithValue)

In order to create a multiple-language field we have to create an appropriate field type. EPrints' MetaField is a perfect candidate for this, and we need to extend it and override its get_value and set_value functions for the field to work properly with the rest of EPrints API, as well as its get_property_defaults function to sort out warnings for default values. The following code could be a rough implementation of such a field:

package EPrints::MetaField::Virtualwithvalue;

use strict;
use warnings;

use EPrints::MetaField;

our @ISA = qw( EPrints::MetaField );

use strict;

sub get_property_defaults
{
    my ( $self ) = @_;
    my %defaults = $self->SUPER::get_property_defaults;
    $defaults{get_value} = $EPrints::MetaField::UNDEF;
    $defaults{set_value} = $EPrints::MetaField::UNDEF;

    return %defaults;
}

sub get_value
{
    my( $self, $object ) = @_;
    if ( defined $self->get_property("get_value") )
    {
        return $self->call_property( "get_value", $object);
    }
    return undef;
}

sub set_value
{
     my( $self, $object, $value ) = @_;
     if ( defined $self->get_property("set_value") )
     {
         return $self->call_property( "set_value", $object, $value);
     }
     return undef;
}

We could save this file in ~eprints/lib/plugins/EPrints/MetaField/Virtualwithvalue.pm.

Introducing ml_title field in EPrints and replacing title field's type

To inform EPrints about our new field (that will be of type virtualwithvalue), we should create a configuration file, eg: ~eprints/archives/<reponame>/cfg/cfg.d/zz_multilang_field.pl with content like the following:

#define local fields
my $local_fields = [
{
    name => 'ml_title',
    type => 'multilang',
    multiple => 1,
    fields => [ { sub_name => "text", type => "longtext", input_rows => 3, make_single_value_orderkey => 'EPrints::Extras::english_title_orderkey' } ],
    input_add_boxes => 1,
},

{
    name => 'title',
    type => 'virtualwithvalue',
    virtual => 1,

    get_value => sub
    {
        my ($eprint) = @_;
        if ($eprint->is_set('ml_title'))
        {
            my $lang = $eprint->repository->get_langid;
            my $lang_set = 0;
            my $vals = $eprint->get_value('ml_title');
            my $title = '';
            if (!$lang)
            {
                $lang_set = 1;
            }
            else
            {
                # set the default lang's text as title
                foreach my $v1 (@{$vals})
                {
                    if ($v1->{lang} eq $lang)
                    {
                        $title = $v1->{text};
                    }
                }
            }
            # if the language is not set or I can't find an abstract in the 
            # user's language, get the first object's text as abstract
            if ($lang_set or $title eq '')
            {
                $title = $vals->[0]->{text};
            }
            return $title;

        }
        return undef;
    },

    set_value => sub
    {
        my ($eprint, $value) = @_;
        my $lang = 'en';
        #only use this on imports, NOT if the value is already set
        if ($eprint->is_set('ml_title'))
        {
            return;
        }
        if ($value)
        {
            $eprint->set_value('ml_title', [{lang=>$lang, text=>$value}]);
        }
    }
},
];

#create lookup hash of local field names
my $local_fieldnames = {};

foreach my $f (@{$local_fields})
{
    $local_fieldnames->{$f->{name}} = 1;
}

#merge in existing field configurations
foreach my $f (@{$c->{fields}->{eprint}})
{
    if (!$local_fieldnames->{$f->{name}})
    {
     push @{$local_fields}, $f;
    }
}

#overwrite original array of configured fields
$c->{fields}->{eprint} = $local_fields;

Where we can see that our new ml_title field is of type multilang and the title field's type has become virtualwithvalue. Moreover, the title field now implements two aforementioned functions: get_value and set_value. Both these functions are used by EPrints API, and their existence, as well as their return values, are critical for EPrints to work properly. Their names imply their functionality. In the end of our example code, one can see how a custom field can be added in the list of EPrints fields.

What has happened in effect is that the title field has become a calculated field that gets or sets its value in its corresponding multilingual field (ml_title). So, each new record now has a multilingual field which it accesses via the interface provided by the calculated field of type virtualwithvalue.

The reason we didn't set the title field to be of type multilang in the first place is that many EPrints builtin functions expect only one value from the title (and abstract) field, and multilang fields don't support such functionality, which causes EPrints to err. By using and extending calculated fields (like MetaFields), we can calculate and produce always a single output for the multilang field (based on some condition) via its get_value function; its set_value function is used for populating our ml_title field's values. Our sample code prints output based on the user's language settings, but the programmer can do whatever they wish in these functions, as long as they return values that comply with EPrints' API.

Adding the appropriate phrases

EPrints phrases are not aware of our new field (ml_title), so we need to update them. Our example updates phrases for languages en and el. We chose to add new files instead of changing the default ones so as to help EPrints upgrades. For the English language we add the file ~eprints/archives/reponame/cfg/lang/en/phrases/local.xml that contains the following information:

<epp:phrase id="eprint_fieldname_ml_title">Title</epp:phrase>
<epp:phrase id="eprint_fieldname_ml_title_text">Text</epp:phrase>
<epp:phrase id="eprint_fieldname_ml_title_lang">Language</epp:phrase>
<epp:phrase id="eprint_fieldhelp_ml_title">The title of the item. The title should not end with a full stop, but may end with a question mark. There is no way to make italic text, please enter it normally. If you have a subtitle, it should be preceded with a colon [:]. Use capitals only for the first word and for proper nouns.
    <br/>Example: <span class="ep_form_example">A brief history of time</span>
    <br/>Example: <span class="ep_form_example">Life: an unauthorised biography</span>
    <br/>Example: <span class="ep_form_example">Mathematics for engineers and scientists. 5th edition</span>
    <br/>Example: <span class="ep_form_example">Ecosystems of the world. Vol. 26. Estuaries of the world</span>
</epp:phrase>

Greek phrases are added in file ~eprints/archives/reponame/cfg/lang/el/phrases/local.xml':

<epp:phrase id="eprint_fieldname_ml_title">Τίτλος</epp:phrase>
<epp:phrase id="eprint_fieldname_ml_title_text">Κείμενο</epp:phrase>
<epp:phrase id="eprint_fieldname_ml_title_help">Το help τεξτ</epp:phrase>
<epp:phrase id="eprint_fieldname_ml_title_lang">Γλώσσα</epp:phrase>
<epp:phrase id="eprint_fieldhelp_ml_title">Ο τίτλος του τεκμηρίου. Ο τίτλος δεν πρέπει να τελειώνει με τελεία, αλλά μπορεί να τελειώνει με ερωτηματικό. Δεν υπάρχει τρόπος να γράψετε με πλάγια γράμματα, παρακαλώ χρησιμοποιήστε απλό κείμενο. Εάν έχετε έναν υπότιτλο, θα πρέπει να προηγείται η άνω και κάτω τελεία του υπότιτλου [:]. Χρησιμοποιήστε κεφαλαία γράμματα μόνο στην πρώτη λέξη και στα κύρια ονόματα.
    <br/>Παράδειγμα: <span class="ep_form_example">Μια σύντομη ιστορία</span>
    <br/>Παράδειγμα: <span class="ep_form_example">Καβάφης: η βιογραφία</span>
    <br/>Παράδειγμα: <span class="ep_form_example">Μαθηματικά για μηχανικούς και επιστήμονες. 5η έκδοση</span>
    <br/>Παράδειγμα: <span class="ep_form_example">Οικοσυστήματα του πλανήτη. Τόμ. 26. Εκβολές του πλανήτη.</span>
</epp:phrase>